mirror of
https://github.com/withastro/astro.git
synced 2025-02-10 22:38:53 -05:00
fix: transform collection config to be JSON serializable
This commit is contained in:
parent
79c5c1eacf
commit
ad70ca403a
3 changed files with 68 additions and 28 deletions
|
@ -1,6 +1,6 @@
|
|||
import deepDiff from 'deep-diff';
|
||||
import { mkdir, readFile, readdir, writeFile } from 'fs/promises';
|
||||
import type { DBSnapshot } from '../types.js';
|
||||
import { collectionsSchema, type DBSnapshot } from '../types.js';
|
||||
import type { AstroConfig } from 'astro';
|
||||
import { cyan, green, yellow } from 'kleur/colors';
|
||||
const { applyChange, diff: generateDiff } = deepDiff;
|
||||
|
@ -82,7 +82,9 @@ export async function getMigrations(): Promise<string[]> {
|
|||
return migrationFiles;
|
||||
}
|
||||
|
||||
export async function loadMigration(migration: string): Promise<{ diff: any[]; db: string[], confirm?: string[] }> {
|
||||
export async function loadMigration(
|
||||
migration: string
|
||||
): Promise<{ diff: any[]; db: string[]; confirm?: string[] }> {
|
||||
return JSON.parse(await readFile(`./migrations/${migration}`, 'utf-8'));
|
||||
}
|
||||
|
||||
|
@ -117,7 +119,9 @@ export async function initializeFromMigrations(allMigrationFiles: string[]): Pro
|
|||
}
|
||||
|
||||
export function createCurrentSnapshot(config: AstroConfig): DBSnapshot {
|
||||
const schema = JSON.parse(JSON.stringify(config.db?.collections ?? {}));
|
||||
// Parse to resolve non-serializable types like () => references
|
||||
const collectionsConfig = collectionsSchema.parse(config.db?.collections ?? {});
|
||||
const schema = JSON.parse(JSON.stringify(collectionsConfig));
|
||||
return { experimentalVersion: 1, schema };
|
||||
}
|
||||
export function createEmptySnapshot(): DBSnapshot {
|
||||
|
|
|
@ -114,7 +114,9 @@ export function getCreateForeignKeyQueries(collectionName: string, collection: D
|
|||
let queries: string[] = [];
|
||||
for (const foreignKey of collection.foreignKeys ?? []) {
|
||||
const fields = asArray(foreignKey.fields);
|
||||
const references = asArray(foreignKey.references());
|
||||
const references = asArray(foreignKey.references);
|
||||
|
||||
console.log(references[0]);
|
||||
|
||||
if (fields.length !== references.length) {
|
||||
throw new Error(
|
||||
|
@ -184,7 +186,7 @@ export function getModifiers(fieldName: string, field: DBField) {
|
|||
export function getReferencesConfig(field: DBField) {
|
||||
const canHaveReferences = field.type === 'number' || field.type === 'text';
|
||||
if (!canHaveReferences) return undefined;
|
||||
return field.references?.();
|
||||
return field.references;
|
||||
}
|
||||
|
||||
// Using `DBField` will not narrow `default` based on the column `type`
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
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 { getTableName, SQL } from 'drizzle-orm';
|
||||
import { z, type ZodTypeDef } from 'zod';
|
||||
import { SQL } from 'drizzle-orm';
|
||||
import { errorMap } from './integration/error-map.js';
|
||||
|
||||
export type MaybePromise<T> = T | Promise<T>;
|
||||
export type MaybeArray<T> = T | T[];
|
||||
|
@ -43,14 +44,19 @@ const numberFieldBaseSchema = baseFieldSchema.omit({ optional: true }).and(
|
|||
const numberFieldOptsSchema: z.ZodType<
|
||||
z.infer<typeof numberFieldBaseSchema> & {
|
||||
// ReferenceableField creates a circular type. Define ZodType to resolve.
|
||||
references?: () => NumberField;
|
||||
references?: NumberField;
|
||||
},
|
||||
ZodTypeDef,
|
||||
z.input<typeof numberFieldBaseSchema> & {
|
||||
references?: () => NumberFieldInput;
|
||||
}
|
||||
> = numberFieldBaseSchema.and(
|
||||
z.object({
|
||||
references: z
|
||||
.function()
|
||||
.returns(z.lazy(() => numberFieldSchema))
|
||||
.optional(),
|
||||
.optional()
|
||||
.transform((fn) => fn?.()),
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -86,14 +92,19 @@ const textFieldBaseSchema = baseFieldSchema
|
|||
const textFieldOptsSchema: z.ZodType<
|
||||
z.infer<typeof textFieldBaseSchema> & {
|
||||
// ReferenceableField creates a circular type. Define ZodType to resolve.
|
||||
references?: () => TextField;
|
||||
references?: TextField;
|
||||
},
|
||||
ZodTypeDef,
|
||||
z.input<typeof textFieldBaseSchema> & {
|
||||
references?: () => TextFieldInput;
|
||||
}
|
||||
> = textFieldBaseSchema.and(
|
||||
z.object({
|
||||
references: z
|
||||
.function()
|
||||
.returns(z.lazy(() => textFieldSchema))
|
||||
.optional(),
|
||||
.optional()
|
||||
.transform((fn) => fn?.()),
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -136,14 +147,22 @@ export const indexSchema = z.object({
|
|||
unique: z.boolean().optional(),
|
||||
});
|
||||
|
||||
const foreignKeysSchema: z.ZodType<{
|
||||
type ForeignKeysInput = {
|
||||
fields: MaybeArray<string>;
|
||||
references: () => MaybeArray<ReferenceableField>;
|
||||
}> = z.object({
|
||||
references: () => MaybeArray<Omit<ReferenceableField, 'references'>>;
|
||||
};
|
||||
|
||||
type ForeignKeysOutput = Omit<ForeignKeysInput, 'references'> & {
|
||||
// reference fn called in `transform`. Ensures output is JSON serializable.
|
||||
references: MaybeArray<Omit<ReferenceableField, 'references'>>;
|
||||
};
|
||||
|
||||
const foreignKeysSchema: z.ZodType<ForeignKeysOutput, ZodTypeDef, ForeignKeysInput> = z.object({
|
||||
fields: z.string().or(z.array(z.string())),
|
||||
references: z
|
||||
.function()
|
||||
.returns(z.lazy(() => referenceableFieldSchema.or(z.array(referenceableFieldSchema)))),
|
||||
.returns(z.lazy(() => referenceableFieldSchema.or(z.array(referenceableFieldSchema))))
|
||||
.transform((fn) => fn()),
|
||||
});
|
||||
|
||||
export type Indexes = Record<string, z.infer<typeof indexSchema>>;
|
||||
|
@ -165,11 +184,32 @@ export const writableCollectionSchema = baseCollectionSchema.extend({
|
|||
});
|
||||
|
||||
export const collectionSchema = z.union([readableCollectionSchema, writableCollectionSchema]);
|
||||
export const collectionsSchema = z.record(collectionSchema);
|
||||
export const collectionsSchema = z.preprocess((rawCollections) => {
|
||||
// Preprocess collections to append collection and field names to fields.
|
||||
// Used to track collection info for references.
|
||||
|
||||
// Use minimum parsing to ensure collection has a `fields` record.
|
||||
const collections = z
|
||||
.record(
|
||||
z.object({
|
||||
fields: z.record(z.any()),
|
||||
})
|
||||
)
|
||||
.parse(rawCollections, { errorMap });
|
||||
for (const [collectionName, collection] of Object.entries(collections)) {
|
||||
for (const [fieldName, field] of Object.entries(collection.fields)) {
|
||||
field.name = fieldName;
|
||||
field.collection = collectionName;
|
||||
}
|
||||
}
|
||||
return rawCollections;
|
||||
}, z.record(collectionSchema));
|
||||
|
||||
export type BooleanField = z.infer<typeof booleanFieldSchema>;
|
||||
export type NumberField = z.infer<typeof numberFieldSchema>;
|
||||
export type NumberFieldInput = z.input<typeof numberFieldSchema>;
|
||||
export type TextField = z.infer<typeof textFieldSchema>;
|
||||
export type TextFieldInput = z.input<typeof textFieldSchema>;
|
||||
export type DateField = z.infer<typeof dateFieldSchema>;
|
||||
// Type `Date` is the config input, `string` is the output for D1 storage
|
||||
export type DateFieldInput = z.input<typeof dateFieldSchema>;
|
||||
|
@ -183,7 +223,12 @@ export type FieldType =
|
|||
| JsonField['type'];
|
||||
|
||||
export type DBField = z.infer<typeof fieldSchema>;
|
||||
export type DBFieldInput = DateFieldInput | BooleanField | NumberField | TextField | JsonField;
|
||||
export type DBFieldInput =
|
||||
| DateFieldInput
|
||||
| BooleanField
|
||||
| NumberFieldInput
|
||||
| TextFieldInput
|
||||
| JsonField;
|
||||
export type DBFields = z.infer<typeof fieldsSchema>;
|
||||
export type DBCollection = z.infer<
|
||||
typeof readableCollectionSchema | typeof writableCollectionSchema
|
||||
|
@ -268,11 +313,6 @@ function baseDefineCollection<TFields extends FieldsConfig, TWritable extends bo
|
|||
userConfig: CollectionConfig<TFields>,
|
||||
writable: TWritable
|
||||
): ResolvedCollectionConfig<TFields, TWritable> {
|
||||
for (const fieldName in userConfig.fields) {
|
||||
const field = userConfig.fields[fieldName];
|
||||
// Store field name within the field itself to track references
|
||||
field.name = fieldName;
|
||||
}
|
||||
const meta: { table: Table<string, TFields> } = { table: null! };
|
||||
/**
|
||||
* We need to attach the Drizzle `table` at runtime using `_setMeta`.
|
||||
|
@ -281,12 +321,6 @@ function baseDefineCollection<TFields extends FieldsConfig, TWritable extends bo
|
|||
*/
|
||||
const _setMeta = (values: { table: Table<string, TFields> }) => {
|
||||
Object.assign(meta, values);
|
||||
|
||||
const tableName = getTableName(meta.table);
|
||||
for (const fieldName in userConfig.fields) {
|
||||
const field = userConfig.fields[fieldName];
|
||||
field.collection = tableName;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
Loading…
Add table
Reference in a new issue