0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-30 22:03:56 -05:00

feat: prevent optional on text pk

This commit is contained in:
bholmesdev 2024-02-08 13:57:09 -05:00
parent b29208e156
commit 2ff473572e

View file

@ -1,7 +1,7 @@
import type { SQLiteInsertValue } from 'drizzle-orm/sqlite-core'; import type { SQLiteInsertValue } from 'drizzle-orm/sqlite-core';
import type { InferSelectModel } from 'drizzle-orm'; import type { InferSelectModel } from 'drizzle-orm';
import type { SqliteDB, Table } from '../runtime/index.js'; import type { SqliteDB, Table } from '../runtime/index.js';
import { z, type ZodTypeDef } from 'zod'; import { z } from 'zod';
import { getTableName, SQL } from 'drizzle-orm'; import { getTableName, SQL } from 'drizzle-orm';
export type MaybePromise<T> = T | Promise<T>; export type MaybePromise<T> = T | Promise<T>;
@ -60,13 +60,29 @@ const numberFieldSchema = numberFieldOptsSchema.and(
}) })
); );
const textFieldBaseSchema = baseFieldSchema.extend({ const textFieldBaseSchema = baseFieldSchema
type: z.literal('text'), .omit({ optional: true })
primaryKey: z.boolean().optional(), .extend({
default: z.union([z.string(), z.instanceof(SQL<any>)]).optional(), default: z.union([z.string(), z.instanceof(SQL<any>)]).optional(),
}); })
.and(
z.union([
z.object({
primaryKey: z.literal(false).optional(),
optional: z.boolean().optional(),
}),
z.object({
// text primary key allows NULL values.
// NULL values bypass unique checks, which could
// lead to duplicate URLs per record in Astro Studio.
// disable `optional` for primary keys.
primaryKey: z.literal(true),
optional: z.literal(false).optional(),
}),
])
);
const textFieldSchema: z.ZodType< const textFieldOptsSchema: z.ZodType<
z.infer<typeof textFieldBaseSchema> & { z.infer<typeof textFieldBaseSchema> & {
// ReferenceableField creates a circular type. Define ZodType to resolve. // ReferenceableField creates a circular type. Define ZodType to resolve.
references?: () => TextField; references?: () => TextField;
@ -75,11 +91,17 @@ const textFieldSchema: z.ZodType<
z.object({ z.object({
references: z references: z
.function() .function()
.returns(z.lazy(() => textFieldBaseSchema)) .returns(z.lazy(() => textFieldSchema))
.optional(), .optional(),
}) })
); );
const textFieldSchema = textFieldOptsSchema.and(
z.object({
type: z.literal('text'),
})
);
const dateFieldSchema = baseFieldSchema.extend({ const dateFieldSchema = baseFieldSchema.extend({
type: z.literal('date'), type: z.literal('date'),
default: z default: z
@ -292,24 +314,25 @@ export function defineWritableCollection<TFields extends FieldsConfig>(
export type AstroConfigWithDB = z.infer<typeof astroConfigWithDbSchema>; export type AstroConfigWithDB = z.infer<typeof astroConfigWithDbSchema>;
type FieldOpts<T extends DBFieldInput> = Omit<T, 'type'>; type FieldOpts<T extends DBFieldInput> = Omit<T, 'type'>;
// We cannot use `Omit<NumberField, 'type'>`, // We cannot use `Omit<NumberField | TextField, 'type'>`,
// since Omit collapses our union type on primary key. // since Omit collapses our union type on primary key.
type NumberFieldOpts = z.input<typeof numberFieldOptsSchema>; type NumberFieldOpts = z.input<typeof numberFieldOptsSchema>;
type TextFieldOpts = z.input<typeof textFieldOptsSchema>;
export const field = { export const field = {
number: <T extends NumberFieldOpts>(opts: T = {} as T) => { number: <T extends NumberFieldOpts>(opts: T = {} as T) => {
return { type: 'number', ...opts } satisfies NumberField; return { type: 'number', ...opts } satisfies T & { type: 'number' };
}, },
boolean: <T extends FieldOpts<BooleanField>>(opts: T = {} as T) => { boolean: <T extends FieldOpts<BooleanField>>(opts: T = {} as T) => {
return { type: 'boolean', ...opts } satisfies BooleanField; return { type: 'boolean', ...opts } satisfies T & { type: 'boolean' };
}, },
text: <T extends FieldOpts<TextField>>(opts: T = {} as T) => { text: <T extends TextFieldOpts>(opts: T = {} as T) => {
return { type: 'text', ...opts } satisfies TextField; return { type: 'text', ...opts } satisfies T & { type: 'text' };
}, },
date<T extends FieldOpts<DateFieldInput>>(opts: T) { date<T extends FieldOpts<DateFieldInput>>(opts: T) {
return { type: 'date', ...opts } satisfies DateFieldInput; return { type: 'date', ...opts } satisfies T & { type: 'date' };
}, },
json<T extends FieldOpts<JsonField>>(opts: T) { json<T extends FieldOpts<JsonField>>(opts: T) {
return { type: 'json', ...opts } satisfies JsonField; return { type: 'json', ...opts } satisfies T & { type: 'json' };
}, },
}; };