From 6c22e8fcf1b1977a7bcf01990dd8d4db2d47c523 Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Thu, 8 Feb 2024 13:04:55 -0500 Subject: [PATCH] feat: ignore `optional` and `default` when pk is present --- packages/db/src/core/types.ts | 112 ++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 47 deletions(-) diff --git a/packages/db/src/core/types.ts b/packages/db/src/core/types.ts index e324471e28..39cb446a61 100644 --- a/packages/db/src/core/types.ts +++ b/packages/db/src/core/types.ts @@ -1,7 +1,7 @@ import type { SQLiteInsertValue } from 'drizzle-orm/sqlite-core'; import type { InferSelectModel } from 'drizzle-orm'; import type { SqliteDB, Table } from '../runtime/index.js'; -import { z } from 'zod'; +import { z, type ZodTypeDef } from 'zod'; import { getTableName, SQL } from 'drizzle-orm'; export type MaybePromise = T | Promise; @@ -19,55 +19,72 @@ const baseFieldSchema = z.object({ const booleanFieldSchema = baseFieldSchema.extend({ type: z.literal('boolean'), - default: z.union([ - z.boolean(), - z.instanceof(SQL), - ]).optional(), + default: z.union([z.boolean(), z.instanceof(SQL)]).optional(), }); -const numberFieldSchema: z.ZodType< - { +const numberFieldBaseSchema = baseFieldSchema.omit({ optional: true }).and( + z.union([ + z.object({ + primaryKey: z.literal(false).optional().default(false), + optional: z.boolean().optional(), + default: z.union([z.number(), z.instanceof(SQL)]).optional(), + }), + z + .object({ + // `integer primary key` uses ROWID as the default value. + // `optional` and `default` do not have an effect, + // so omit these config options for primary keys. + primaryKey: z.literal(true), + }) + .transform((val) => ({ ...val, optional: false, default: undefined })), + ]) +); + +const numberFieldOptsSchema: z.ZodType< + z.output & { // ReferenceableField creates a circular type. Define ZodType to resolve. - type: 'number'; - default?: number | SQL | undefined; - references?: () => ReferenceableField | undefined; - primaryKey?: boolean | undefined; - } & z.infer -> = baseFieldSchema.extend({ - type: z.literal('number'), - default: z.union([ - z.number(), - z.instanceof(SQL), - ]).optional(), - references: z - .function() - .returns(z.lazy(() => referenceableFieldSchema)) - .optional(), + references?: () => NumberFieldInput; + }, + ZodTypeDef, + z.input & { + references?: () => NumberFieldInput; + } +> = numberFieldBaseSchema.and( + z.object({ + references: z + .function() + .returns(z.lazy(() => numberFieldSchema)) + .optional(), + }) +); + +export type NumberFieldOpts = z.input; + +const numberFieldSchema = numberFieldOptsSchema.and( + z.object({ + type: z.literal('number'), + }) +); + +const textFieldBaseSchema = baseFieldSchema.extend({ + type: z.literal('text'), primaryKey: z.boolean().optional(), + default: z.union([z.string(), z.instanceof(SQL)]).optional(), }); const textFieldSchema: z.ZodType< - { + z.infer & { // ReferenceableField creates a circular type. Define ZodType to resolve. - type: 'text'; - multiline?: boolean | undefined; - default?: string | SQL | undefined; - references?: () => ReferenceableField | undefined; - primaryKey?: boolean | undefined; - } & z.infer -> = baseFieldSchema.extend({ - type: z.literal('text'), - multiline: z.boolean().optional(), - default: z.union([ - z.string(), - z.instanceof(SQL), - ]).optional(), - references: z - .function() - .returns(z.lazy(() => referenceableFieldSchema)) - .optional(), - primaryKey: z.boolean().optional(), -}); + references?: () => TextField; + } +> = textFieldBaseSchema.and( + z.object({ + references: z + .function() + .returns(z.lazy(() => textFieldBaseSchema)) + .optional(), + }) +); const dateFieldSchema = baseFieldSchema.extend({ type: z.literal('date'), @@ -93,8 +110,8 @@ const fieldSchema = z.union([ dateFieldSchema, jsonFieldSchema, ]); -export const referenceableFieldSchema = z.union([textFieldSchema, numberFieldSchema]); -export type ReferenceableField = z.infer; +export const referenceableFieldSchema = z.union([textFieldBaseSchema, numberFieldBaseSchema]); +export type ReferenceableField = z.input; const fieldsSchema = z.record(fieldSchema); export const indexSchema = z.object({ @@ -134,6 +151,7 @@ export const collectionSchema = z.union([readableCollectionSchema, writableColle export const collectionsSchema = z.record(collectionSchema); export type BooleanField = z.infer; +export type NumberFieldInput = z.input; export type NumberField = z.infer; export type TextField = z.infer; export type DateField = z.infer; @@ -149,7 +167,7 @@ export type FieldType = | JsonField['type']; export type DBField = z.infer; -export type DBFieldInput = DateFieldInput | BooleanField | NumberField | TextField | JsonField; +export type DBFieldInput = DateFieldInput | BooleanField | NumberFieldInput | TextField | JsonField; export type DBFields = z.infer; export type DBCollection = z.infer< typeof readableCollectionSchema | typeof writableCollectionSchema @@ -283,8 +301,8 @@ export type AstroConfigWithDB = z.infer; type FieldOpts = Omit; export const field = { - number: >(opts: T = {} as T) => { - return { type: 'number', ...opts } satisfies NumberField; + number: (opts: T = {} as T) => { + return { type: 'number', ...opts } satisfies NumberFieldInput; }, boolean: >(opts: T = {} as T) => { return { type: 'boolean', ...opts } satisfies BooleanField;