diff --git a/packages/db/src/internal.ts b/packages/db/src/internal.ts index df94f28093..db675fb3de 100644 --- a/packages/db/src/internal.ts +++ b/packages/db/src/internal.ts @@ -35,15 +35,7 @@ import { nanoid } from 'nanoid'; import type { AstroIntegrationLogger } from 'astro'; export type SqliteDB = SqliteRemoteDatabase; -export type { - AstroTable, - AstroText, - AstroDate, - AstroBoolean, - AstroNumber, - AstroJson, - AstroId, -} from './types.js'; +export type { Table } from './types.js'; const sqlite = new SQLiteAsyncDialect(); diff --git a/packages/db/src/typegen.ts b/packages/db/src/typegen.ts index 057b1fcee0..a0e9ca6a75 100644 --- a/packages/db/src/typegen.ts +++ b/packages/db/src/typegen.ts @@ -25,40 +25,21 @@ ${Object.entries(collections) } function generateTableType(name: string, collection: DBCollection): string { - let tableType = ` export const ${name}: import(${INTERNAL_MOD_IMPORT}).AstroTable<{ - name: ${JSON.stringify(name)}; - columns: { - id: import(${INTERNAL_MOD_IMPORT}).AstroId<{ - tableName: ${JSON.stringify(name)}; - }>;`; - - for (const [fieldName, field] of Object.entries(collection.fields)) { - const drizzleInterface = schemaTypeToDrizzleInterface(field.type); - tableType += ` - ${fieldName}: import(${INTERNAL_MOD_IMPORT}).${drizzleInterface}<{ - tableName: ${JSON.stringify(name)}; - name: ${JSON.stringify(fieldName)}; - notNull: ${field.optional ? 'false' : 'true'}; - hasDefault: ${typeof field.default !== 'undefined' ? 'true' : 'false'}; - }>;`; - } - tableType += ` - }; - }>;`; + let tableType = ` export const ${name}: import(${INTERNAL_MOD_IMPORT}).Table< + ${JSON.stringify(name)}, + ${JSON.stringify( + Object.fromEntries( + Object.entries(collection.fields).map(([fieldName, field]) => [ + fieldName, + { + // Only select fields Drizzle needs for inference + type: field.type, + optional: field.optional, + default: field.default, + }, + ]) + ) + )} + >;`; return tableType; } - -function schemaTypeToDrizzleInterface(type: FieldType) { - switch (type) { - case 'text': - return 'AstroText'; - case 'number': - return 'AstroNumber'; - case 'boolean': - return 'AstroBoolean'; - case 'date': - return 'AstroDate'; - case 'json': - return 'AstroJson'; - } -} diff --git a/packages/db/src/types.ts b/packages/db/src/types.ts index 7d1ae78131..157b3a9595 100644 --- a/packages/db/src/types.ts +++ b/packages/db/src/types.ts @@ -1,5 +1,5 @@ import type { ColumnDataType, ColumnBaseConfig } from 'drizzle-orm'; -import type { SQLiteColumn, SQLiteTableWithColumns, TableConfig } from 'drizzle-orm/sqlite-core'; +import type { SQLiteColumn, SQLiteTableWithColumns } from 'drizzle-orm/sqlite-core'; import { z } from 'zod'; const baseFieldSchema = z.object({ @@ -98,14 +98,7 @@ export type DBCollections = Record; export type ReadableDBCollection = z.infer; export type WritableDBCollection = z.infer; -export type AstroTable> = SQLiteTableWithColumns< - T & { - schema: undefined; - dialect: 'sqlite'; - } ->; - -type GeneratedConfig = Pick< +type GeneratedConfig = Pick< ColumnBaseConfig, 'name' | 'tableName' | 'notNull' | 'hasDefault' >; @@ -180,3 +173,38 @@ export type AstroId, 'tableName'>> = SQ >; export type MaybePromise = T | Promise; + +export type Column = T extends 'boolean' + ? AstroBoolean + : T extends 'number' + ? AstroNumber + : T extends 'text' + ? AstroText + : T extends 'date' + ? AstroDate + : T extends 'json' + ? AstroJson + : never; + +export type Table< + TTableName extends string, + TFields extends Record>, +> = SQLiteTableWithColumns<{ + name: TTableName; + schema: undefined; + dialect: 'sqlite'; + columns: { + id: AstroId<{ tableName: TTableName }>; + } & { + [K in keyof TFields]: Column< + TFields[K]['type'], + { + tableName: TTableName; + // TODO: narrow K to string + name: string; + hasDefault: TFields[K]['default'] extends undefined ? false : true; + notNull: TFields[K]['optional'] extends true ? false : true; + } + >; + }; +}>;