@prisma-next/sql-contract-emitter 0.3.0-dev.11 → 0.3.0-dev.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["table: StorageTable | undefined","referencedTable: StorageTable | undefined","allImports: TypesImportSpec[]","uniqueImports: TypesImportSpec[]","tables: string[]","columns: string[]","tableParts: string[]","typeEntries: string[]","entries: string[]","renderCtx: TypeRenderContext","modelTypes: string[]","fields: string[]","relations: string[]","modelParts: string[]","tableEntries: string[]","relationEntries: string[]","parts: string[]","modelToTable: string[]","tableToModel: string[]","fieldToColumn: string[]","columnToField: string[]","fieldMap: string[]","colMap: string[]"],"sources":["../src/index.ts"],"sourcesContent":["import type { ContractIR } from '@prisma-next/contract/ir';\nimport type {\n GenerateContractTypesOptions,\n TypeRenderContext,\n TypeRenderEntry,\n TypesImportSpec,\n ValidationContext,\n} from '@prisma-next/contract/types';\nimport type {\n ModelDefinition,\n ModelField,\n SqlStorage,\n StorageColumn,\n StorageTable,\n StorageTypeInstance,\n} from '@prisma-next/sql-contract/types';\nimport { assertDefined } from '@prisma-next/utils/assertions';\n\n/**\n * Resolves the typeParams for a column, either from inline typeParams or from typeRef.\n * Returns undefined if no typeParams are available.\n */\nfunction resolveColumnTypeParams(\n column: StorageColumn,\n storage: SqlStorage,\n): Record<string, unknown> | undefined {\n // Inline typeParams take precedence\n if (column.typeParams && Object.keys(column.typeParams).length > 0) {\n return column.typeParams;\n }\n // Check typeRef\n if (column.typeRef && storage.types) {\n const typeInstance = storage.types[column.typeRef] as StorageTypeInstance | undefined;\n if (typeInstance?.typeParams) {\n return typeInstance.typeParams;\n }\n }\n return undefined;\n}\n\nexport const sqlTargetFamilyHook = {\n id: 'sql',\n\n validateTypes(ir: ContractIR, _ctx: ValidationContext): void {\n const storage = ir.storage as SqlStorage | undefined;\n if (!storage || !storage.tables) {\n return;\n }\n\n // Validate codec ID format (ns/name@version). Adapter-provided codecs are available regardless of contract.extensionPacks (which is for framework extensions); TypeScript prevents invalid usage and runtime validates availability.\n\n const typeIdRegex = /^([^/]+)\\/([^@]+)@(\\d+)$/;\n\n for (const [tableName, tableUnknown] of Object.entries(storage.tables)) {\n const table = tableUnknown as StorageTable;\n for (const [colName, colUnknown] of Object.entries(table.columns)) {\n const col = colUnknown as { codecId?: string };\n const codecId = col.codecId;\n if (!codecId) {\n throw new Error(`Column \"${colName}\" in table \"${tableName}\" is missing codecId`);\n }\n\n const match = codecId.match(typeIdRegex);\n if (!match || !match[1]) {\n throw new Error(\n `Column \"${colName}\" in table \"${tableName}\" has invalid codec ID format \"${codecId}\". Expected format: ns/name@version`,\n );\n }\n }\n }\n },\n\n validateStructure(ir: ContractIR): void {\n if (ir.targetFamily !== 'sql') {\n throw new Error(`Expected targetFamily \"sql\", got \"${ir.targetFamily}\"`);\n }\n\n const storage = ir.storage as SqlStorage | undefined;\n if (!storage || !storage.tables) {\n throw new Error('SQL contract must have storage.tables');\n }\n\n const models = ir.models as Record<string, ModelDefinition> | undefined;\n const tableNames = new Set(Object.keys(storage.tables));\n\n if (models) {\n for (const [modelName, modelUnknown] of Object.entries(models)) {\n const model = modelUnknown as ModelDefinition;\n if (!model.storage?.table) {\n throw new Error(`Model \"${modelName}\" is missing storage.table`);\n }\n\n const tableName = model.storage.table;\n if (!tableNames.has(tableName)) {\n throw new Error(`Model \"${modelName}\" references non-existent table \"${tableName}\"`);\n }\n\n const table: StorageTable | undefined = storage.tables[tableName];\n assertDefined(table, `Model \"${modelName}\" references non-existent table \"${tableName}\"`);\n\n if (!table.primaryKey) {\n throw new Error(`Model \"${modelName}\" table \"${tableName}\" is missing a primary key`);\n }\n\n const columnNames = new Set(Object.keys(table.columns));\n if (!model.fields || Object.keys(model.fields).length === 0) {\n throw new Error(`Model \"${modelName}\" is missing fields`);\n }\n\n for (const [fieldName, fieldUnknown] of Object.entries(model.fields)) {\n const field = fieldUnknown as ModelField;\n if (!field.column) {\n throw new Error(`Model \"${modelName}\" field \"${fieldName}\" is missing column property`);\n }\n\n if (!columnNames.has(field.column)) {\n throw new Error(\n `Model \"${modelName}\" field \"${fieldName}\" references non-existent column \"${field.column}\" in table \"${tableName}\"`,\n );\n }\n }\n\n if (!model.relations || typeof model.relations !== 'object') {\n throw new Error(\n `Model \"${modelName}\" is missing required field \"relations\" (must be an object)`,\n );\n }\n }\n }\n\n for (const [tableName, tableUnknown] of Object.entries(storage.tables)) {\n const table = tableUnknown as StorageTable;\n const columnNames = new Set(Object.keys(table.columns));\n\n // Column structure (nullable, nativeType, codecId) and table arrays (uniques, indexes, foreignKeys)\n // are validated by Arktype schema validation - no need to re-check here.\n // We only validate logical consistency (foreign key references, model references, etc.)\n\n if (!Array.isArray(table.uniques)) {\n throw new Error(\n `Table \"${tableName}\" is missing required field \"uniques\" (must be an array)`,\n );\n }\n if (!Array.isArray(table.indexes)) {\n throw new Error(\n `Table \"${tableName}\" is missing required field \"indexes\" (must be an array)`,\n );\n }\n if (!Array.isArray(table.foreignKeys)) {\n throw new Error(\n `Table \"${tableName}\" is missing required field \"foreignKeys\" (must be an array)`,\n );\n }\n\n if (table.primaryKey) {\n for (const colName of table.primaryKey.columns) {\n if (!columnNames.has(colName)) {\n throw new Error(\n `Table \"${tableName}\" primaryKey references non-existent column \"${colName}\"`,\n );\n }\n }\n }\n\n for (const unique of table.uniques) {\n for (const colName of unique.columns) {\n if (!columnNames.has(colName)) {\n throw new Error(\n `Table \"${tableName}\" unique constraint references non-existent column \"${colName}\"`,\n );\n }\n }\n }\n\n for (const index of table.indexes) {\n for (const colName of index.columns) {\n if (!columnNames.has(colName)) {\n throw new Error(\n `Table \"${tableName}\" index references non-existent column \"${colName}\"`,\n );\n }\n }\n }\n\n for (const fk of table.foreignKeys) {\n for (const colName of fk.columns) {\n if (!columnNames.has(colName)) {\n throw new Error(\n `Table \"${tableName}\" foreignKey references non-existent column \"${colName}\"`,\n );\n }\n }\n\n if (!tableNames.has(fk.references.table)) {\n throw new Error(\n `Table \"${tableName}\" foreignKey references non-existent table \"${fk.references.table}\"`,\n );\n }\n\n // Table existence guaranteed by Set.has() check above\n const referencedTable: StorageTable | undefined = storage.tables[fk.references.table];\n assertDefined(\n referencedTable,\n `Table \"${tableName}\" foreignKey references non-existent table \"${fk.references.table}\"`,\n );\n\n const referencedColumnNames = new Set(Object.keys(referencedTable.columns));\n for (const colName of fk.references.columns) {\n if (!referencedColumnNames.has(colName)) {\n throw new Error(\n `Table \"${tableName}\" foreignKey references non-existent column \"${colName}\" in table \"${fk.references.table}\"`,\n );\n }\n }\n\n if (fk.columns.length !== fk.references.columns.length) {\n throw new Error(\n `Table \"${tableName}\" foreignKey column count (${fk.columns.length}) does not match referenced column count (${fk.references.columns.length})`,\n );\n }\n }\n }\n },\n\n generateContractTypes(\n ir: ContractIR,\n codecTypeImports: ReadonlyArray<TypesImportSpec>,\n operationTypeImports: ReadonlyArray<TypesImportSpec>,\n hashes: {\n readonly storageHash: string;\n readonly executionHash?: string;\n readonly profileHash: string;\n },\n options?: GenerateContractTypesOptions,\n ): string {\n const parameterizedRenderers = options?.parameterizedRenderers;\n const parameterizedTypeImports = options?.parameterizedTypeImports;\n const storage = ir.storage as SqlStorage;\n const models = ir.models as Record<string, ModelDefinition>;\n\n // Collect all type imports from three sources:\n // 1. Codec type imports (from adapters, targets, and extensions)\n // 2. Operation type imports (from adapters, targets, and extensions)\n // 3. Parameterized type imports (for parameterized codec renderers, may contain duplicates)\n const allImports: TypesImportSpec[] = [...codecTypeImports, ...operationTypeImports];\n\n if (parameterizedTypeImports) {\n allImports.push(...parameterizedTypeImports);\n }\n\n const queryOperationTypeImports = options?.queryOperationTypeImports ?? [];\n if (queryOperationTypeImports.length > 0) {\n allImports.push(...queryOperationTypeImports);\n }\n\n // Deduplicate imports by package+named to avoid duplicate import statements.\n // Strategy: When the same package::named appears multiple times, keep the first\n // occurrence (and its alias); later duplicates with different aliases are silently ignored.\n //\n // Note: uniqueImports must be an array (not a Set) because:\n // - We need to preserve the full TypesImportSpec objects (package, named, alias)\n // - We need to preserve insertion order (first occurrence wins)\n // - seenImportKeys is a Set used only for O(1) duplicate detection\n const seenImportKeys = new Set<string>();\n const uniqueImports: TypesImportSpec[] = [];\n for (const imp of allImports) {\n const key = `${imp.package}::${imp.named}`;\n if (!seenImportKeys.has(key)) {\n seenImportKeys.add(key);\n uniqueImports.push(imp);\n }\n }\n\n // Generate import statements, omitting redundant \"as Alias\" when named === alias\n const importLines = uniqueImports.map((imp) => {\n // Simplify import when named === alias (e.g., `import type { Vector }` instead of `{ Vector as Vector }`)\n const importClause = imp.named === imp.alias ? imp.named : `${imp.named} as ${imp.alias}`;\n return `import type { ${importClause} } from '${imp.package}';`;\n });\n\n // Only intersect actual codec/operation type maps. Extra type-only imports (e.g. Vector<N>) are\n // included in importLines via codecTypeImports but must not be intersected into CodecTypes.\n const codecTypes = codecTypeImports\n .filter((imp) => imp.named === 'CodecTypes')\n .map((imp) => imp.alias)\n .join(' & ');\n const operationTypes = operationTypeImports\n .filter((imp) => imp.named === 'OperationTypes')\n .map((imp) => imp.alias)\n .join(' & ');\n const queryOperationTypes = queryOperationTypeImports\n .filter((imp) => imp.named === 'QueryOperationTypes')\n .map((imp) => imp.alias)\n .join(' & ');\n\n const storageType = this.generateStorageType(storage);\n const modelsType = this.generateModelsType(models, storage, parameterizedRenderers);\n const relationsType = this.generateRelationsType(ir.relations);\n const mappingsType = this.generateMappingsType(models, storage);\n\n const executionHashType = hashes.executionHash\n ? `ExecutionHashBase<'${hashes.executionHash}'>`\n : 'ExecutionHashBase<string>';\n\n return `// ⚠️ GENERATED FILE - DO NOT EDIT\n // This file is automatically generated by 'prisma-next contract emit'.\n // To regenerate, run: prisma-next contract emit\n ${importLines.join('\\n')}\n\n import type {\n ExecutionHashBase,\n ProfileHashBase,\n StorageHashBase,\n } from '@prisma-next/contract/types';\n import type {\n SqlContract,\n SqlStorage,\n SqlMappings,\n ModelDefinition,\n ContractWithTypeMaps,\n TypeMaps as TypeMapsType,\n } from '@prisma-next/sql-contract/types';\n\n export type StorageHash = StorageHashBase<'${hashes.storageHash}'>;\n export type ExecutionHash = ${executionHashType};\n export type ProfileHash = ProfileHashBase<'${hashes.profileHash}'>;\n\n export type CodecTypes = ${codecTypes || 'Record<string, never>'};\n export type LaneCodecTypes = CodecTypes;\n export type OperationTypes = ${operationTypes || 'Record<string, never>'};\n export type QueryOperationTypes = ${queryOperationTypes || 'Record<string, never>'};\n type DefaultLiteralValue<CodecId extends string, Encoded> =\n CodecId extends keyof CodecTypes\n ? CodecTypes[CodecId] extends { readonly output: infer O }\n ? O extends Date | bigint ? O : Encoded\n : Encoded\n : Encoded;\n\n export type TypeMaps = TypeMapsType<CodecTypes, OperationTypes, QueryOperationTypes>;\n\n type ContractBase = SqlContract<\n ${storageType},\n ${modelsType},\n ${relationsType},\n ${mappingsType},\n StorageHash,\n ExecutionHash,\n ProfileHash\n > & {\n readonly target: ${this.serializeValue(ir.target)};\n readonly capabilities: ${this.serializeValue(ir.capabilities)};\n readonly extensionPacks: ${this.serializeValue(ir.extensionPacks)};\n };\n\n export type Contract = ContractWithTypeMaps<ContractBase, TypeMaps>;\n\n export type Tables = Contract['storage']['tables'];\n export type Models = Contract['models'];\n export type Relations = Contract['relations'];\n `;\n },\n\n generateStorageType(storage: SqlStorage): string {\n const tables: string[] = [];\n for (const [tableName, table] of Object.entries(storage.tables)) {\n const columns: string[] = [];\n for (const [colName, col] of Object.entries(table.columns)) {\n const nullable = col.nullable ? 'true' : 'false';\n const nativeType = `'${col.nativeType}'`;\n const codecId = `'${col.codecId}'`;\n const defaultSpec = col.default\n ? col.default.kind === 'literal'\n ? `; readonly default: { readonly kind: 'literal'; readonly value: DefaultLiteralValue<${codecId}, ${this.serializeValue(\n col.default.value,\n )}> }`\n : `; readonly default: { readonly kind: 'function'; readonly expression: ${this.serializeValue(\n col.default.expression,\n )} }`\n : '';\n columns.push(\n `readonly ${colName}: { readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly nullable: ${nullable}${defaultSpec} }`,\n );\n }\n\n const tableParts: string[] = [`columns: { ${columns.join('; ')} }`];\n\n if (table.primaryKey) {\n const pkCols = table.primaryKey.columns.map((c) => `'${c}'`).join(', ');\n const pkName = table.primaryKey.name ? `; readonly name: '${table.primaryKey.name}'` : '';\n tableParts.push(`primaryKey: { readonly columns: readonly [${pkCols}]${pkName} }`);\n }\n\n const uniques = table.uniques\n .map((u) => {\n const cols = u.columns.map((c: string) => `'${c}'`).join(', ');\n const name = u.name ? `; readonly name: '${u.name}'` : '';\n return `{ readonly columns: readonly [${cols}]${name} }`;\n })\n .join(', ');\n tableParts.push(`uniques: readonly [${uniques}]`);\n\n const indexes = table.indexes\n .map((i) => {\n const cols = i.columns.map((c: string) => `'${c}'`).join(', ');\n const name = i.name ? `; readonly name: '${i.name}'` : '';\n const using =\n i.using !== undefined ? `; readonly using: ${this.serializeValue(i.using)}` : '';\n const config =\n i.config !== undefined ? `; readonly config: ${this.serializeValue(i.config)}` : '';\n return `{ readonly columns: readonly [${cols}]${name}${using}${config} }`;\n })\n .join(', ');\n tableParts.push(`indexes: readonly [${indexes}]`);\n\n const fks = table.foreignKeys\n .map((fk) => {\n const cols = fk.columns.map((c: string) => `'${c}'`).join(', ');\n const refCols = fk.references.columns.map((c: string) => `'${c}'`).join(', ');\n const name = fk.name ? `; readonly name: '${fk.name}'` : '';\n return `{ readonly columns: readonly [${cols}]; readonly references: { readonly table: '${fk.references.table}'; readonly columns: readonly [${refCols}] }${name}; readonly constraint: ${fk.constraint}; readonly index: ${fk.index} }`;\n })\n .join(', ');\n tableParts.push(`foreignKeys: readonly [${fks}]`);\n\n tables.push(`readonly ${tableName}: { ${tableParts.join('; ')} }`);\n }\n\n const typesType = this.generateStorageTypesType(storage.types);\n\n return `{ readonly tables: { ${tables.join('; ')} }; readonly types: ${typesType} }`;\n },\n\n /**\n * Generates the TypeScript type for storage.types with literal types.\n * This preserves type params as literal values for precise typing.\n */\n generateStorageTypesType(types: SqlStorage['types']): string {\n if (!types || Object.keys(types).length === 0) {\n return 'Record<string, never>';\n }\n\n const typeEntries: string[] = [];\n for (const [typeName, typeInstance] of Object.entries(types)) {\n const codecId = `'${typeInstance.codecId}'`;\n const nativeType = `'${typeInstance.nativeType}'`;\n const typeParamsStr = this.serializeTypeParamsLiteral(typeInstance.typeParams);\n typeEntries.push(\n `readonly ${typeName}: { readonly codecId: ${codecId}; readonly nativeType: ${nativeType}; readonly typeParams: ${typeParamsStr} }`,\n );\n }\n\n return `{ ${typeEntries.join('; ')} }`;\n },\n\n /**\n * Serializes a typeParams object to a TypeScript literal type.\n * Converts { length: 1536 } to \"{ readonly length: 1536 }\".\n */\n serializeTypeParamsLiteral(params: Record<string, unknown>): string {\n if (!params || Object.keys(params).length === 0) {\n return 'Record<string, never>';\n }\n\n const entries: string[] = [];\n for (const [key, value] of Object.entries(params)) {\n const serialized = this.serializeValue(value);\n entries.push(`readonly ${this.serializeObjectKey(key)}: ${serialized}`);\n }\n\n return `{ ${entries.join('; ')} }`;\n },\n\n /**\n * Serializes a value to a TypeScript literal type expression.\n */\n serializeValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return 'undefined';\n }\n if (typeof value === 'string') {\n // Escape backslashes first, then single quotes\n const escaped = value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n return `'${escaped}'`;\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'bigint') {\n return `${value}n`;\n }\n if (Array.isArray(value)) {\n const items = value.map((v) => this.serializeValue(v)).join(', ');\n return `readonly [${items}]`;\n }\n if (typeof value === 'object') {\n const entries: string[] = [];\n for (const [k, v] of Object.entries(value)) {\n entries.push(`readonly ${this.serializeObjectKey(k)}: ${this.serializeValue(v)}`);\n }\n return `{ ${entries.join('; ')} }`;\n }\n return 'unknown';\n },\n\n serializeObjectKey(key: string): string {\n if (/^[$A-Z_a-z][$\\w]*$/.test(key)) {\n return key;\n }\n return this.serializeValue(key);\n },\n\n generateModelsType(\n models: Record<string, ModelDefinition> | undefined,\n storage: SqlStorage,\n parameterizedRenderers?: Map<string, TypeRenderEntry>,\n ): string {\n if (!models) {\n return 'Record<string, never>';\n }\n\n const renderCtx: TypeRenderContext = { codecTypesName: 'CodecTypes' };\n\n const modelTypes: string[] = [];\n for (const [modelName, model] of Object.entries(models)) {\n const fields: string[] = [];\n const tableName = model.storage.table;\n const table = storage.tables[tableName];\n\n if (table) {\n for (const [fieldName, field] of Object.entries(model.fields)) {\n const column = table.columns[field.column];\n if (!column) {\n fields.push(`readonly ${fieldName}: { readonly column: '${field.column}' }`);\n continue;\n }\n\n const jsType = this.generateColumnType(\n column,\n storage,\n parameterizedRenderers,\n renderCtx,\n );\n fields.push(`readonly ${fieldName}: ${jsType}`);\n }\n } else {\n for (const [fieldName, field] of Object.entries(model.fields)) {\n fields.push(`readonly ${fieldName}: { readonly column: '${field.column}' }`);\n }\n }\n\n const relations: string[] = [];\n for (const [relName, rel] of Object.entries(model.relations)) {\n if (typeof rel === 'object' && rel !== null && 'on' in rel) {\n const on = rel.on as { parentCols?: string[]; childCols?: string[] };\n if (on.parentCols && on.childCols) {\n const parentCols = on.parentCols.map((c) => `'${c}'`).join(', ');\n const childCols = on.childCols.map((c) => `'${c}'`).join(', ');\n relations.push(\n `readonly ${relName}: { readonly on: { readonly parentCols: readonly [${parentCols}]; readonly childCols: readonly [${childCols}] } }`,\n );\n }\n }\n }\n\n const modelParts: string[] = [\n `storage: { readonly table: '${tableName}' }`,\n `fields: { ${fields.join('; ')} }`,\n ];\n\n if (relations.length > 0) {\n modelParts.push(`relations: { ${relations.join('; ')} }`);\n }\n\n modelTypes.push(`readonly ${modelName}: { ${modelParts.join('; ')} }`);\n }\n\n return `{ ${modelTypes.join('; ')} }`;\n },\n\n /**\n * Generates the TypeScript type expression for a column.\n * Uses parameterized renderer if the column has typeParams and a matching renderer exists,\n * otherwise falls back to CodecTypes[codecId]['output'].\n */\n generateColumnType(\n column: StorageColumn,\n storage: SqlStorage,\n parameterizedRenderers: Map<string, TypeRenderEntry> | undefined,\n renderCtx: TypeRenderContext,\n ): string {\n const typeParams = resolveColumnTypeParams(column, storage);\n const nullable = column.nullable ?? false;\n const fallbackType = `CodecTypes['${column.codecId}']['output']`;\n const renderer = typeParams && parameterizedRenderers?.get(column.codecId);\n const baseType = renderer ? renderer.render(typeParams, renderCtx) : fallbackType;\n\n return nullable ? `${baseType} | null` : baseType;\n },\n\n generateRelationsType(relations: Record<string, unknown> | undefined): string {\n if (!relations || Object.keys(relations).length === 0) {\n return 'Record<string, never>';\n }\n\n const tableEntries: string[] = [];\n for (const [tableName, relsValue] of Object.entries(relations)) {\n if (typeof relsValue !== 'object' || relsValue === null) {\n continue;\n }\n const rels = relsValue as Record<string, unknown>;\n const relationEntries: string[] = [];\n for (const [relName, relValue] of Object.entries(rels)) {\n if (typeof relValue !== 'object' || relValue === null) {\n relationEntries.push(`readonly ${relName}: unknown`);\n continue;\n }\n const { to, cardinality, on, through } = relValue as {\n readonly to?: string;\n readonly cardinality?: string;\n readonly on?: {\n readonly parentCols?: readonly string[];\n readonly childCols?: readonly string[];\n };\n readonly through?: {\n readonly table: string;\n readonly parentCols: readonly string[];\n readonly childCols: readonly string[];\n };\n };\n\n const parts: string[] = [];\n if (to) {\n parts.push(`readonly to: '${to}'`);\n }\n if (cardinality) {\n parts.push(`readonly cardinality: '${cardinality}'`);\n }\n if (on?.parentCols && on.childCols) {\n const parentCols = on.parentCols.map((c) => `'${c}'`).join(', ');\n const childCols = on.childCols.map((c) => `'${c}'`).join(', ');\n parts.push(\n `readonly on: { readonly parentCols: readonly [${parentCols}]; readonly childCols: readonly [${childCols}] }`,\n );\n }\n if (through) {\n const parentCols = through.parentCols.map((c) => `'${c}'`).join(', ');\n const childCols = through.childCols.map((c) => `'${c}'`).join(', ');\n parts.push(\n `readonly through: { readonly table: '${through.table}'; readonly parentCols: readonly [${parentCols}]; readonly childCols: readonly [${childCols}] }`,\n );\n }\n\n relationEntries.push(\n parts.length > 0\n ? `readonly ${relName}: { ${parts.join('; ')} }`\n : `readonly ${relName}: unknown`,\n );\n }\n tableEntries.push(`readonly ${tableName}: { ${relationEntries.join('; ')} }`);\n }\n\n return `{ ${tableEntries.join('; ')} }`;\n },\n\n generateMappingsType(\n models: Record<string, ModelDefinition> | undefined,\n storage: SqlStorage,\n ): string {\n if (!models) {\n return 'SqlMappings';\n }\n\n const modelToTable: string[] = [];\n const tableToModel: string[] = [];\n const fieldToColumn: string[] = [];\n const columnToField: string[] = [];\n\n for (const [modelName, model] of Object.entries(models)) {\n const tableName = model.storage.table;\n modelToTable.push(`readonly ${modelName}: '${tableName}'`);\n tableToModel.push(`readonly ${tableName}: '${modelName}'`);\n\n const fieldMap: string[] = [];\n for (const [fieldName, field] of Object.entries(model.fields)) {\n fieldMap.push(`readonly ${fieldName}: '${field.column}'`);\n }\n\n if (fieldMap.length > 0) {\n fieldToColumn.push(`readonly ${modelName}: { ${fieldMap.join('; ')} }`);\n }\n\n if (storage.tables[tableName]) {\n const colMap: string[] = [];\n for (const [fieldName, field] of Object.entries(model.fields)) {\n colMap.push(`readonly ${field.column}: '${fieldName}'`);\n }\n\n if (colMap.length > 0) {\n columnToField.push(`readonly ${tableName}: { ${colMap.join('; ')} }`);\n }\n }\n }\n\n const parts: string[] = [];\n if (modelToTable.length > 0) {\n parts.push(`modelToTable: { ${modelToTable.join('; ')} }`);\n }\n if (tableToModel.length > 0) {\n parts.push(`tableToModel: { ${tableToModel.join('; ')} }`);\n }\n if (fieldToColumn.length > 0) {\n parts.push(`fieldToColumn: { ${fieldToColumn.join('; ')} }`);\n }\n if (columnToField.length > 0) {\n parts.push(`columnToField: { ${columnToField.join('; ')} }`);\n }\n\n return parts.length > 0 ? `{ ${parts.join('; ')} }` : 'SqlMappings';\n },\n} as const;\n"],"mappings":";;;;;;;AAsBA,SAAS,wBACP,QACA,SACqC;AAErC,KAAI,OAAO,cAAc,OAAO,KAAK,OAAO,WAAW,CAAC,SAAS,EAC/D,QAAO,OAAO;AAGhB,KAAI,OAAO,WAAW,QAAQ,OAAO;EACnC,MAAM,eAAe,QAAQ,MAAM,OAAO;AAC1C,MAAI,cAAc,WAChB,QAAO,aAAa;;;AAM1B,MAAa,sBAAsB;CACjC,IAAI;CAEJ,cAAc,IAAgB,MAA+B;EAC3D,MAAM,UAAU,GAAG;AACnB,MAAI,CAAC,WAAW,CAAC,QAAQ,OACvB;EAKF,MAAM,cAAc;AAEpB,OAAK,MAAM,CAAC,WAAW,iBAAiB,OAAO,QAAQ,QAAQ,OAAO,EAAE;GACtE,MAAM,QAAQ;AACd,QAAK,MAAM,CAAC,SAAS,eAAe,OAAO,QAAQ,MAAM,QAAQ,EAAE;IAEjE,MAAM,UADM,WACQ;AACpB,QAAI,CAAC,QACH,OAAM,IAAI,MAAM,WAAW,QAAQ,cAAc,UAAU,sBAAsB;IAGnF,MAAM,QAAQ,QAAQ,MAAM,YAAY;AACxC,QAAI,CAAC,SAAS,CAAC,MAAM,GACnB,OAAM,IAAI,MACR,WAAW,QAAQ,cAAc,UAAU,iCAAiC,QAAQ,qCACrF;;;;CAMT,kBAAkB,IAAsB;AACtC,MAAI,GAAG,iBAAiB,MACtB,OAAM,IAAI,MAAM,qCAAqC,GAAG,aAAa,GAAG;EAG1E,MAAM,UAAU,GAAG;AACnB,MAAI,CAAC,WAAW,CAAC,QAAQ,OACvB,OAAM,IAAI,MAAM,wCAAwC;EAG1D,MAAM,SAAS,GAAG;EAClB,MAAM,aAAa,IAAI,IAAI,OAAO,KAAK,QAAQ,OAAO,CAAC;AAEvD,MAAI,OACF,MAAK,MAAM,CAAC,WAAW,iBAAiB,OAAO,QAAQ,OAAO,EAAE;GAC9D,MAAM,QAAQ;AACd,OAAI,CAAC,MAAM,SAAS,MAClB,OAAM,IAAI,MAAM,UAAU,UAAU,4BAA4B;GAGlE,MAAM,YAAY,MAAM,QAAQ;AAChC,OAAI,CAAC,WAAW,IAAI,UAAU,CAC5B,OAAM,IAAI,MAAM,UAAU,UAAU,mCAAmC,UAAU,GAAG;GAGtF,MAAMA,QAAkC,QAAQ,OAAO;AACvD,iBAAc,OAAO,UAAU,UAAU,mCAAmC,UAAU,GAAG;AAEzF,OAAI,CAAC,MAAM,WACT,OAAM,IAAI,MAAM,UAAU,UAAU,WAAW,UAAU,4BAA4B;GAGvF,MAAM,cAAc,IAAI,IAAI,OAAO,KAAK,MAAM,QAAQ,CAAC;AACvD,OAAI,CAAC,MAAM,UAAU,OAAO,KAAK,MAAM,OAAO,CAAC,WAAW,EACxD,OAAM,IAAI,MAAM,UAAU,UAAU,qBAAqB;AAG3D,QAAK,MAAM,CAAC,WAAW,iBAAiB,OAAO,QAAQ,MAAM,OAAO,EAAE;IACpE,MAAM,QAAQ;AACd,QAAI,CAAC,MAAM,OACT,OAAM,IAAI,MAAM,UAAU,UAAU,WAAW,UAAU,8BAA8B;AAGzF,QAAI,CAAC,YAAY,IAAI,MAAM,OAAO,CAChC,OAAM,IAAI,MACR,UAAU,UAAU,WAAW,UAAU,oCAAoC,MAAM,OAAO,cAAc,UAAU,GACnH;;AAIL,OAAI,CAAC,MAAM,aAAa,OAAO,MAAM,cAAc,SACjD,OAAM,IAAI,MACR,UAAU,UAAU,6DACrB;;AAKP,OAAK,MAAM,CAAC,WAAW,iBAAiB,OAAO,QAAQ,QAAQ,OAAO,EAAE;GACtE,MAAM,QAAQ;GACd,MAAM,cAAc,IAAI,IAAI,OAAO,KAAK,MAAM,QAAQ,CAAC;AAMvD,OAAI,CAAC,MAAM,QAAQ,MAAM,QAAQ,CAC/B,OAAM,IAAI,MACR,UAAU,UAAU,0DACrB;AAEH,OAAI,CAAC,MAAM,QAAQ,MAAM,QAAQ,CAC/B,OAAM,IAAI,MACR,UAAU,UAAU,0DACrB;AAEH,OAAI,CAAC,MAAM,QAAQ,MAAM,YAAY,CACnC,OAAM,IAAI,MACR,UAAU,UAAU,8DACrB;AAGH,OAAI,MAAM,YACR;SAAK,MAAM,WAAW,MAAM,WAAW,QACrC,KAAI,CAAC,YAAY,IAAI,QAAQ,CAC3B,OAAM,IAAI,MACR,UAAU,UAAU,+CAA+C,QAAQ,GAC5E;;AAKP,QAAK,MAAM,UAAU,MAAM,QACzB,MAAK,MAAM,WAAW,OAAO,QAC3B,KAAI,CAAC,YAAY,IAAI,QAAQ,CAC3B,OAAM,IAAI,MACR,UAAU,UAAU,sDAAsD,QAAQ,GACnF;AAKP,QAAK,MAAM,SAAS,MAAM,QACxB,MAAK,MAAM,WAAW,MAAM,QAC1B,KAAI,CAAC,YAAY,IAAI,QAAQ,CAC3B,OAAM,IAAI,MACR,UAAU,UAAU,0CAA0C,QAAQ,GACvE;AAKP,QAAK,MAAM,MAAM,MAAM,aAAa;AAClC,SAAK,MAAM,WAAW,GAAG,QACvB,KAAI,CAAC,YAAY,IAAI,QAAQ,CAC3B,OAAM,IAAI,MACR,UAAU,UAAU,+CAA+C,QAAQ,GAC5E;AAIL,QAAI,CAAC,WAAW,IAAI,GAAG,WAAW,MAAM,CACtC,OAAM,IAAI,MACR,UAAU,UAAU,8CAA8C,GAAG,WAAW,MAAM,GACvF;IAIH,MAAMC,kBAA4C,QAAQ,OAAO,GAAG,WAAW;AAC/E,kBACE,iBACA,UAAU,UAAU,8CAA8C,GAAG,WAAW,MAAM,GACvF;IAED,MAAM,wBAAwB,IAAI,IAAI,OAAO,KAAK,gBAAgB,QAAQ,CAAC;AAC3E,SAAK,MAAM,WAAW,GAAG,WAAW,QAClC,KAAI,CAAC,sBAAsB,IAAI,QAAQ,CACrC,OAAM,IAAI,MACR,UAAU,UAAU,+CAA+C,QAAQ,cAAc,GAAG,WAAW,MAAM,GAC9G;AAIL,QAAI,GAAG,QAAQ,WAAW,GAAG,WAAW,QAAQ,OAC9C,OAAM,IAAI,MACR,UAAU,UAAU,6BAA6B,GAAG,QAAQ,OAAO,4CAA4C,GAAG,WAAW,QAAQ,OAAO,GAC7I;;;;CAMT,sBACE,IACA,kBACA,sBACA,QAKA,SACQ;EACR,MAAM,yBAAyB,SAAS;EACxC,MAAM,2BAA2B,SAAS;EAC1C,MAAM,UAAU,GAAG;EACnB,MAAM,SAAS,GAAG;EAMlB,MAAMC,aAAgC,CAAC,GAAG,kBAAkB,GAAG,qBAAqB;AAEpF,MAAI,yBACF,YAAW,KAAK,GAAG,yBAAyB;EAG9C,MAAM,4BAA4B,SAAS,6BAA6B,EAAE;AAC1E,MAAI,0BAA0B,SAAS,EACrC,YAAW,KAAK,GAAG,0BAA0B;EAW/C,MAAM,iCAAiB,IAAI,KAAa;EACxC,MAAMC,gBAAmC,EAAE;AAC3C,OAAK,MAAM,OAAO,YAAY;GAC5B,MAAM,MAAM,GAAG,IAAI,QAAQ,IAAI,IAAI;AACnC,OAAI,CAAC,eAAe,IAAI,IAAI,EAAE;AAC5B,mBAAe,IAAI,IAAI;AACvB,kBAAc,KAAK,IAAI;;;EAK3B,MAAM,cAAc,cAAc,KAAK,QAAQ;AAG7C,UAAO,iBADc,IAAI,UAAU,IAAI,QAAQ,IAAI,QAAQ,GAAG,IAAI,MAAM,MAAM,IAAI,QAC7C,WAAW,IAAI,QAAQ;IAC5D;EAIF,MAAM,aAAa,iBAChB,QAAQ,QAAQ,IAAI,UAAU,aAAa,CAC3C,KAAK,QAAQ,IAAI,MAAM,CACvB,KAAK,MAAM;EACd,MAAM,iBAAiB,qBACpB,QAAQ,QAAQ,IAAI,UAAU,iBAAiB,CAC/C,KAAK,QAAQ,IAAI,MAAM,CACvB,KAAK,MAAM;EACd,MAAM,sBAAsB,0BACzB,QAAQ,QAAQ,IAAI,UAAU,sBAAsB,CACpD,KAAK,QAAQ,IAAI,MAAM,CACvB,KAAK,MAAM;EAEd,MAAM,cAAc,KAAK,oBAAoB,QAAQ;EACrD,MAAM,aAAa,KAAK,mBAAmB,QAAQ,SAAS,uBAAuB;EACnF,MAAM,gBAAgB,KAAK,sBAAsB,GAAG,UAAU;EAC9D,MAAM,eAAe,KAAK,qBAAqB,QAAQ,QAAQ;EAE/D,MAAM,oBAAoB,OAAO,gBAC7B,sBAAsB,OAAO,cAAc,MAC3C;AAEJ,SAAO;;;IAGP,YAAY,KAAK,KAAK,CAAC;;;;;;;;;;;;;;;;+CAgBoB,OAAO,YAAY;gCAClC,kBAAkB;+CACH,OAAO,YAAY;;6BAErC,cAAc,wBAAwB;;iCAElC,kBAAkB,wBAAwB;sCACrC,uBAAuB,wBAAwB;;;;;;;;;;;IAWjF,YAAY;IACZ,WAAW;IACX,cAAc;IACd,aAAa;;;;;uBAKM,KAAK,eAAe,GAAG,OAAO,CAAC;6BACzB,KAAK,eAAe,GAAG,aAAa,CAAC;+BACnC,KAAK,eAAe,GAAG,eAAe,CAAC;;;;;;;;;;CAWpE,oBAAoB,SAA6B;EAC/C,MAAMC,SAAmB,EAAE;AAC3B,OAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,QAAQ,OAAO,EAAE;GAC/D,MAAMC,UAAoB,EAAE;AAC5B,QAAK,MAAM,CAAC,SAAS,QAAQ,OAAO,QAAQ,MAAM,QAAQ,EAAE;IAC1D,MAAM,WAAW,IAAI,WAAW,SAAS;IACzC,MAAM,aAAa,IAAI,IAAI,WAAW;IACtC,MAAM,UAAU,IAAI,IAAI,QAAQ;IAChC,MAAM,cAAc,IAAI,UACpB,IAAI,QAAQ,SAAS,YACnB,uFAAuF,QAAQ,IAAI,KAAK,eACtG,IAAI,QAAQ,MACb,CAAC,OACF,yEAAyE,KAAK,eAC5E,IAAI,QAAQ,WACb,CAAC,MACJ;AACJ,YAAQ,KACN,YAAY,QAAQ,2BAA2B,WAAW,sBAAsB,QAAQ,uBAAuB,WAAW,YAAY,IACvI;;GAGH,MAAMC,aAAuB,CAAC,cAAc,QAAQ,KAAK,KAAK,CAAC,IAAI;AAEnE,OAAI,MAAM,YAAY;IACpB,MAAM,SAAS,MAAM,WAAW,QAAQ,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK;IACvE,MAAM,SAAS,MAAM,WAAW,OAAO,qBAAqB,MAAM,WAAW,KAAK,KAAK;AACvF,eAAW,KAAK,6CAA6C,OAAO,GAAG,OAAO,IAAI;;GAGpF,MAAM,UAAU,MAAM,QACnB,KAAK,MAAM;AAGV,WAAO,iCAFM,EAAE,QAAQ,KAAK,MAAc,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAEjB,GADhC,EAAE,OAAO,qBAAqB,EAAE,KAAK,KAAK,GACF;KACrD,CACD,KAAK,KAAK;AACb,cAAW,KAAK,sBAAsB,QAAQ,GAAG;GAEjD,MAAM,UAAU,MAAM,QACnB,KAAK,MAAM;AAOV,WAAO,iCANM,EAAE,QAAQ,KAAK,MAAc,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAMjB,GALhC,EAAE,OAAO,qBAAqB,EAAE,KAAK,KAAK,KAErD,EAAE,UAAU,SAAY,qBAAqB,KAAK,eAAe,EAAE,MAAM,KAAK,KAE9E,EAAE,WAAW,SAAY,sBAAsB,KAAK,eAAe,EAAE,OAAO,KAAK,GACb;KACtE,CACD,KAAK,KAAK;AACb,cAAW,KAAK,sBAAsB,QAAQ,GAAG;GAEjD,MAAM,MAAM,MAAM,YACf,KAAK,OAAO;IACX,MAAM,OAAO,GAAG,QAAQ,KAAK,MAAc,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK;IAC/D,MAAM,UAAU,GAAG,WAAW,QAAQ,KAAK,MAAc,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK;IAC7E,MAAM,OAAO,GAAG,OAAO,qBAAqB,GAAG,KAAK,KAAK;AACzD,WAAO,iCAAiC,KAAK,6CAA6C,GAAG,WAAW,MAAM,iCAAiC,QAAQ,KAAK,KAAK,yBAAyB,GAAG,WAAW,oBAAoB,GAAG,MAAM;KACrO,CACD,KAAK,KAAK;AACb,cAAW,KAAK,0BAA0B,IAAI,GAAG;AAEjD,UAAO,KAAK,YAAY,UAAU,MAAM,WAAW,KAAK,KAAK,CAAC,IAAI;;EAGpE,MAAM,YAAY,KAAK,yBAAyB,QAAQ,MAAM;AAE9D,SAAO,wBAAwB,OAAO,KAAK,KAAK,CAAC,sBAAsB,UAAU;;CAOnF,yBAAyB,OAAoC;AAC3D,MAAI,CAAC,SAAS,OAAO,KAAK,MAAM,CAAC,WAAW,EAC1C,QAAO;EAGT,MAAMC,cAAwB,EAAE;AAChC,OAAK,MAAM,CAAC,UAAU,iBAAiB,OAAO,QAAQ,MAAM,EAAE;GAC5D,MAAM,UAAU,IAAI,aAAa,QAAQ;GACzC,MAAM,aAAa,IAAI,aAAa,WAAW;GAC/C,MAAM,gBAAgB,KAAK,2BAA2B,aAAa,WAAW;AAC9E,eAAY,KACV,YAAY,SAAS,wBAAwB,QAAQ,yBAAyB,WAAW,yBAAyB,cAAc,IACjI;;AAGH,SAAO,KAAK,YAAY,KAAK,KAAK,CAAC;;CAOrC,2BAA2B,QAAyC;AAClE,MAAI,CAAC,UAAU,OAAO,KAAK,OAAO,CAAC,WAAW,EAC5C,QAAO;EAGT,MAAMC,UAAoB,EAAE;AAC5B,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,EAAE;GACjD,MAAM,aAAa,KAAK,eAAe,MAAM;AAC7C,WAAQ,KAAK,YAAY,KAAK,mBAAmB,IAAI,CAAC,IAAI,aAAa;;AAGzE,SAAO,KAAK,QAAQ,KAAK,KAAK,CAAC;;CAMjC,eAAe,OAAwB;AACrC,MAAI,UAAU,KACZ,QAAO;AAET,MAAI,UAAU,OACZ,QAAO;AAET,MAAI,OAAO,UAAU,SAGnB,QAAO,IADS,MAAM,QAAQ,OAAO,OAAO,CAAC,QAAQ,MAAM,MAAM,CAC9C;AAErB,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAChD,QAAO,OAAO,MAAM;AAEtB,MAAI,OAAO,UAAU,SACnB,QAAO,GAAG,MAAM;AAElB,MAAI,MAAM,QAAQ,MAAM,CAEtB,QAAO,aADO,MAAM,KAAK,MAAM,KAAK,eAAe,EAAE,CAAC,CAAC,KAAK,KAAK,CACvC;AAE5B,MAAI,OAAO,UAAU,UAAU;GAC7B,MAAMA,UAAoB,EAAE;AAC5B,QAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,CACxC,SAAQ,KAAK,YAAY,KAAK,mBAAmB,EAAE,CAAC,IAAI,KAAK,eAAe,EAAE,GAAG;AAEnF,UAAO,KAAK,QAAQ,KAAK,KAAK,CAAC;;AAEjC,SAAO;;CAGT,mBAAmB,KAAqB;AACtC,MAAI,qBAAqB,KAAK,IAAI,CAChC,QAAO;AAET,SAAO,KAAK,eAAe,IAAI;;CAGjC,mBACE,QACA,SACA,wBACQ;AACR,MAAI,CAAC,OACH,QAAO;EAGT,MAAMC,YAA+B,EAAE,gBAAgB,cAAc;EAErE,MAAMC,aAAuB,EAAE;AAC/B,OAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,OAAO,EAAE;GACvD,MAAMC,SAAmB,EAAE;GAC3B,MAAM,YAAY,MAAM,QAAQ;GAChC,MAAM,QAAQ,QAAQ,OAAO;AAE7B,OAAI,MACF,MAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,MAAM,OAAO,EAAE;IAC7D,MAAM,SAAS,MAAM,QAAQ,MAAM;AACnC,QAAI,CAAC,QAAQ;AACX,YAAO,KAAK,YAAY,UAAU,wBAAwB,MAAM,OAAO,KAAK;AAC5E;;IAGF,MAAM,SAAS,KAAK,mBAClB,QACA,SACA,wBACA,UACD;AACD,WAAO,KAAK,YAAY,UAAU,IAAI,SAAS;;OAGjD,MAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,MAAM,OAAO,CAC3D,QAAO,KAAK,YAAY,UAAU,wBAAwB,MAAM,OAAO,KAAK;GAIhF,MAAMC,YAAsB,EAAE;AAC9B,QAAK,MAAM,CAAC,SAAS,QAAQ,OAAO,QAAQ,MAAM,UAAU,CAC1D,KAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,QAAQ,KAAK;IAC1D,MAAM,KAAK,IAAI;AACf,QAAI,GAAG,cAAc,GAAG,WAAW;KACjC,MAAM,aAAa,GAAG,WAAW,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK;KAChE,MAAM,YAAY,GAAG,UAAU,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK;AAC9D,eAAU,KACR,YAAY,QAAQ,oDAAoD,WAAW,mCAAmC,UAAU,OACjI;;;GAKP,MAAMC,aAAuB,CAC3B,+BAA+B,UAAU,MACzC,aAAa,OAAO,KAAK,KAAK,CAAC,IAChC;AAED,OAAI,UAAU,SAAS,EACrB,YAAW,KAAK,gBAAgB,UAAU,KAAK,KAAK,CAAC,IAAI;AAG3D,cAAW,KAAK,YAAY,UAAU,MAAM,WAAW,KAAK,KAAK,CAAC,IAAI;;AAGxE,SAAO,KAAK,WAAW,KAAK,KAAK,CAAC;;CAQpC,mBACE,QACA,SACA,wBACA,WACQ;EACR,MAAM,aAAa,wBAAwB,QAAQ,QAAQ;EAC3D,MAAM,WAAW,OAAO,YAAY;EACpC,MAAM,eAAe,eAAe,OAAO,QAAQ;EACnD,MAAM,WAAW,cAAc,wBAAwB,IAAI,OAAO,QAAQ;EAC1E,MAAM,WAAW,WAAW,SAAS,OAAO,YAAY,UAAU,GAAG;AAErE,SAAO,WAAW,GAAG,SAAS,WAAW;;CAG3C,sBAAsB,WAAwD;AAC5E,MAAI,CAAC,aAAa,OAAO,KAAK,UAAU,CAAC,WAAW,EAClD,QAAO;EAGT,MAAMC,eAAyB,EAAE;AACjC,OAAK,MAAM,CAAC,WAAW,cAAc,OAAO,QAAQ,UAAU,EAAE;AAC9D,OAAI,OAAO,cAAc,YAAY,cAAc,KACjD;GAEF,MAAM,OAAO;GACb,MAAMC,kBAA4B,EAAE;AACpC,QAAK,MAAM,CAAC,SAAS,aAAa,OAAO,QAAQ,KAAK,EAAE;AACtD,QAAI,OAAO,aAAa,YAAY,aAAa,MAAM;AACrD,qBAAgB,KAAK,YAAY,QAAQ,WAAW;AACpD;;IAEF,MAAM,EAAE,IAAI,aAAa,IAAI,YAAY;IAczC,MAAMC,QAAkB,EAAE;AAC1B,QAAI,GACF,OAAM,KAAK,iBAAiB,GAAG,GAAG;AAEpC,QAAI,YACF,OAAM,KAAK,0BAA0B,YAAY,GAAG;AAEtD,QAAI,IAAI,cAAc,GAAG,WAAW;KAClC,MAAM,aAAa,GAAG,WAAW,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK;KAChE,MAAM,YAAY,GAAG,UAAU,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK;AAC9D,WAAM,KACJ,iDAAiD,WAAW,mCAAmC,UAAU,KAC1G;;AAEH,QAAI,SAAS;KACX,MAAM,aAAa,QAAQ,WAAW,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK;KACrE,MAAM,YAAY,QAAQ,UAAU,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK;AACnE,WAAM,KACJ,wCAAwC,QAAQ,MAAM,oCAAoC,WAAW,mCAAmC,UAAU,KACnJ;;AAGH,oBAAgB,KACd,MAAM,SAAS,IACX,YAAY,QAAQ,MAAM,MAAM,KAAK,KAAK,CAAC,MAC3C,YAAY,QAAQ,WACzB;;AAEH,gBAAa,KAAK,YAAY,UAAU,MAAM,gBAAgB,KAAK,KAAK,CAAC,IAAI;;AAG/E,SAAO,KAAK,aAAa,KAAK,KAAK,CAAC;;CAGtC,qBACE,QACA,SACQ;AACR,MAAI,CAAC,OACH,QAAO;EAGT,MAAMC,eAAyB,EAAE;EACjC,MAAMC,eAAyB,EAAE;EACjC,MAAMC,gBAA0B,EAAE;EAClC,MAAMC,gBAA0B,EAAE;AAElC,OAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,OAAO,EAAE;GACvD,MAAM,YAAY,MAAM,QAAQ;AAChC,gBAAa,KAAK,YAAY,UAAU,KAAK,UAAU,GAAG;AAC1D,gBAAa,KAAK,YAAY,UAAU,KAAK,UAAU,GAAG;GAE1D,MAAMC,WAAqB,EAAE;AAC7B,QAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,MAAM,OAAO,CAC3D,UAAS,KAAK,YAAY,UAAU,KAAK,MAAM,OAAO,GAAG;AAG3D,OAAI,SAAS,SAAS,EACpB,eAAc,KAAK,YAAY,UAAU,MAAM,SAAS,KAAK,KAAK,CAAC,IAAI;AAGzE,OAAI,QAAQ,OAAO,YAAY;IAC7B,MAAMC,SAAmB,EAAE;AAC3B,SAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,MAAM,OAAO,CAC3D,QAAO,KAAK,YAAY,MAAM,OAAO,KAAK,UAAU,GAAG;AAGzD,QAAI,OAAO,SAAS,EAClB,eAAc,KAAK,YAAY,UAAU,MAAM,OAAO,KAAK,KAAK,CAAC,IAAI;;;EAK3E,MAAMN,QAAkB,EAAE;AAC1B,MAAI,aAAa,SAAS,EACxB,OAAM,KAAK,mBAAmB,aAAa,KAAK,KAAK,CAAC,IAAI;AAE5D,MAAI,aAAa,SAAS,EACxB,OAAM,KAAK,mBAAmB,aAAa,KAAK,KAAK,CAAC,IAAI;AAE5D,MAAI,cAAc,SAAS,EACzB,OAAM,KAAK,oBAAoB,cAAc,KAAK,KAAK,CAAC,IAAI;AAE9D,MAAI,cAAc,SAAS,EACzB,OAAM,KAAK,oBAAoB,cAAc,KAAK,KAAK,CAAC,IAAI;AAG9D,SAAO,MAAM,SAAS,IAAI,KAAK,MAAM,KAAK,KAAK,CAAC,MAAM;;CAEzD"}
package/package.json CHANGED
@@ -1,39 +1,48 @@
1
1
  {
2
2
  "name": "@prisma-next/sql-contract-emitter",
3
- "version": "0.3.0-dev.11",
3
+ "version": "0.3.0-dev.114",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "SQL emitter hook for Prisma Next",
7
7
  "dependencies": {
8
- "@prisma-next/contract": "0.3.0-dev.11",
9
- "@prisma-next/emitter": "0.3.0-dev.11",
10
- "@prisma-next/sql-contract": "0.3.0-dev.11"
8
+ "@prisma-next/contract": "0.3.0-dev.114",
9
+ "@prisma-next/emitter": "0.3.0-dev.114",
10
+ "@prisma-next/sql-contract": "0.3.0-dev.114",
11
+ "@prisma-next/utils": "0.3.0-dev.114"
11
12
  },
12
13
  "devDependencies": {
13
- "@vitest/coverage-v8": "4.0.16",
14
- "tsup": "8.5.1",
14
+ "tsdown": "0.18.4",
15
15
  "typescript": "5.9.3",
16
- "vitest": "4.0.16",
17
- "@prisma-next/test-utils": "0.0.1"
16
+ "vitest": "4.0.17",
17
+ "@prisma-next/test-utils": "0.0.1",
18
+ "@prisma-next/core-control-plane": "0.3.0-dev.114",
19
+ "@prisma-next/tsconfig": "0.0.0",
20
+ "@prisma-next/tsdown": "0.0.0"
18
21
  },
19
22
  "files": [
20
23
  "dist",
21
24
  "src"
22
25
  ],
23
26
  "exports": {
24
- ".": {
25
- "types": "./dist/index.d.ts",
26
- "import": "./dist/index.js"
27
- }
27
+ ".": "./dist/index.mjs",
28
+ "./package.json": "./package.json"
29
+ },
30
+ "main": "./dist/index.mjs",
31
+ "module": "./dist/index.mjs",
32
+ "types": "./dist/index.d.mts",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/prisma/prisma-next.git",
36
+ "directory": "packages/2-sql/3-tooling/emitter"
28
37
  },
29
38
  "scripts": {
30
- "build": "tsup --config tsup.config.ts && tsc --project tsconfig.build.json",
39
+ "build": "tsdown",
31
40
  "test": "vitest run --passWithNoTests",
32
41
  "test:coverage": "vitest run --coverage --passWithNoTests",
33
42
  "typecheck": "tsc --project tsconfig.json --noEmit",
34
- "lint": "biome check . --config-path ../../../../biome.json --error-on-warnings",
35
- "lint:fix": "biome check --write . --config-path ../../../../biome.json",
36
- "lint:fix:unsafe": "biome check --write --unsafe . --config-path ../../../../biome.json",
37
- "clean": "node ../../../../scripts/clean.mjs"
43
+ "lint": "biome check . --error-on-warnings",
44
+ "lint:fix": "biome check --write .",
45
+ "lint:fix:unsafe": "biome check --write --unsafe .",
46
+ "clean": "rm -rf dist dist-tsc dist-tsc-prod coverage .tmp-output"
38
47
  }
39
48
  }
package/src/index.ts CHANGED
@@ -1,11 +1,42 @@
1
1
  import type { ContractIR } from '@prisma-next/contract/ir';
2
- import type { TypesImportSpec, ValidationContext } from '@prisma-next/contract/types';
2
+ import type {
3
+ GenerateContractTypesOptions,
4
+ TypeRenderContext,
5
+ TypeRenderEntry,
6
+ TypesImportSpec,
7
+ ValidationContext,
8
+ } from '@prisma-next/contract/types';
3
9
  import type {
4
10
  ModelDefinition,
5
11
  ModelField,
6
12
  SqlStorage,
13
+ StorageColumn,
7
14
  StorageTable,
15
+ StorageTypeInstance,
8
16
  } from '@prisma-next/sql-contract/types';
17
+ import { assertDefined } from '@prisma-next/utils/assertions';
18
+
19
+ /**
20
+ * Resolves the typeParams for a column, either from inline typeParams or from typeRef.
21
+ * Returns undefined if no typeParams are available.
22
+ */
23
+ function resolveColumnTypeParams(
24
+ column: StorageColumn,
25
+ storage: SqlStorage,
26
+ ): Record<string, unknown> | undefined {
27
+ // Inline typeParams take precedence
28
+ if (column.typeParams && Object.keys(column.typeParams).length > 0) {
29
+ return column.typeParams;
30
+ }
31
+ // Check typeRef
32
+ if (column.typeRef && storage.types) {
33
+ const typeInstance = storage.types[column.typeRef] as StorageTypeInstance | undefined;
34
+ if (typeInstance?.typeParams) {
35
+ return typeInstance.typeParams;
36
+ }
37
+ }
38
+ return undefined;
39
+ }
9
40
 
10
41
  export const sqlTargetFamilyHook = {
11
42
  id: 'sql',
@@ -64,10 +95,8 @@ export const sqlTargetFamilyHook = {
64
95
  throw new Error(`Model "${modelName}" references non-existent table "${tableName}"`);
65
96
  }
66
97
 
67
- const table = storage.tables[tableName];
68
- if (!table) {
69
- throw new Error(`Model "${modelName}" references non-existent table "${tableName}"`);
70
- }
98
+ const table: StorageTable | undefined = storage.tables[tableName];
99
+ assertDefined(table, `Model "${modelName}" references non-existent table "${tableName}"`);
71
100
 
72
101
  if (!table.primaryKey) {
73
102
  throw new Error(`Model "${modelName}" table "${tableName}" is missing a primary key`);
@@ -168,12 +197,12 @@ export const sqlTargetFamilyHook = {
168
197
  );
169
198
  }
170
199
 
171
- const referencedTable = storage.tables[fk.references.table];
172
- if (!referencedTable) {
173
- throw new Error(
174
- `Table "${tableName}" foreignKey references non-existent table "${fk.references.table}"`,
175
- );
176
- }
200
+ // Table existence guaranteed by Set.has() check above
201
+ const referencedTable: StorageTable | undefined = storage.tables[fk.references.table];
202
+ assertDefined(
203
+ referencedTable,
204
+ `Table "${tableName}" foreignKey references non-existent table "${fk.references.table}"`,
205
+ );
177
206
 
178
207
  const referencedColumnNames = new Set(Object.keys(referencedTable.columns));
179
208
  for (const colName of fk.references.columns) {
@@ -197,45 +226,138 @@ export const sqlTargetFamilyHook = {
197
226
  ir: ContractIR,
198
227
  codecTypeImports: ReadonlyArray<TypesImportSpec>,
199
228
  operationTypeImports: ReadonlyArray<TypesImportSpec>,
229
+ hashes: {
230
+ readonly storageHash: string;
231
+ readonly executionHash?: string;
232
+ readonly profileHash: string;
233
+ },
234
+ options?: GenerateContractTypesOptions,
200
235
  ): string {
201
- const allImports = [...codecTypeImports, ...operationTypeImports];
202
- const importLines = allImports.map(
203
- (imp) => `import type { ${imp.named} as ${imp.alias} } from '${imp.package}';`,
204
- );
205
-
206
- const codecTypes = codecTypeImports.map((imp) => imp.alias).join(' & ');
207
- const operationTypes = operationTypeImports.map((imp) => imp.alias).join(' & ');
208
-
236
+ const parameterizedRenderers = options?.parameterizedRenderers;
237
+ const parameterizedTypeImports = options?.parameterizedTypeImports;
209
238
  const storage = ir.storage as SqlStorage;
210
239
  const models = ir.models as Record<string, ModelDefinition>;
211
240
 
212
- const storageType = this.generateStorageType(storage);
213
- const modelsType = this.generateModelsType(models, storage);
214
- const relationsType = this.generateRelationsType(ir.relations);
215
- const mappingsType = this.generateMappingsType(models, storage, codecTypes, operationTypes);
241
+ // Collect all type imports from three sources:
242
+ // 1. Codec type imports (from adapters, targets, and extensions)
243
+ // 2. Operation type imports (from adapters, targets, and extensions)
244
+ // 3. Parameterized type imports (for parameterized codec renderers, may contain duplicates)
245
+ const allImports: TypesImportSpec[] = [...codecTypeImports, ...operationTypeImports];
216
246
 
217
- return `// ⚠️ GENERATED FILE - DO NOT EDIT
218
- // This file is automatically generated by 'prisma-next emit'.
219
- // To regenerate, run: prisma-next emit
220
- ${importLines.join('\n')}
247
+ if (parameterizedTypeImports) {
248
+ allImports.push(...parameterizedTypeImports);
249
+ }
221
250
 
222
- import type { SqlContract, SqlStorage, SqlMappings, ModelDefinition } from '@prisma-next/sql-contract/types';
251
+ const queryOperationTypeImports = options?.queryOperationTypeImports ?? [];
252
+ if (queryOperationTypeImports.length > 0) {
253
+ allImports.push(...queryOperationTypeImports);
254
+ }
223
255
 
224
- export type CodecTypes = ${codecTypes || 'Record<string, never>'};
225
- export type LaneCodecTypes = CodecTypes;
226
- export type OperationTypes = ${operationTypes || 'Record<string, never>'};
256
+ // Deduplicate imports by package+named to avoid duplicate import statements.
257
+ // Strategy: When the same package::named appears multiple times, keep the first
258
+ // occurrence (and its alias); later duplicates with different aliases are silently ignored.
259
+ //
260
+ // Note: uniqueImports must be an array (not a Set) because:
261
+ // - We need to preserve the full TypesImportSpec objects (package, named, alias)
262
+ // - We need to preserve insertion order (first occurrence wins)
263
+ // - seenImportKeys is a Set used only for O(1) duplicate detection
264
+ const seenImportKeys = new Set<string>();
265
+ const uniqueImports: TypesImportSpec[] = [];
266
+ for (const imp of allImports) {
267
+ const key = `${imp.package}::${imp.named}`;
268
+ if (!seenImportKeys.has(key)) {
269
+ seenImportKeys.add(key);
270
+ uniqueImports.push(imp);
271
+ }
272
+ }
273
+
274
+ // Generate import statements, omitting redundant "as Alias" when named === alias
275
+ const importLines = uniqueImports.map((imp) => {
276
+ // Simplify import when named === alias (e.g., `import type { Vector }` instead of `{ Vector as Vector }`)
277
+ const importClause = imp.named === imp.alias ? imp.named : `${imp.named} as ${imp.alias}`;
278
+ return `import type { ${importClause} } from '${imp.package}';`;
279
+ });
280
+
281
+ // Only intersect actual codec/operation type maps. Extra type-only imports (e.g. Vector<N>) are
282
+ // included in importLines via codecTypeImports but must not be intersected into CodecTypes.
283
+ const codecTypes = codecTypeImports
284
+ .filter((imp) => imp.named === 'CodecTypes')
285
+ .map((imp) => imp.alias)
286
+ .join(' & ');
287
+ const operationTypes = operationTypeImports
288
+ .filter((imp) => imp.named === 'OperationTypes')
289
+ .map((imp) => imp.alias)
290
+ .join(' & ');
291
+ const queryOperationTypes = queryOperationTypeImports
292
+ .filter((imp) => imp.named === 'QueryOperationTypes')
293
+ .map((imp) => imp.alias)
294
+ .join(' & ');
295
+
296
+ const storageType = this.generateStorageType(storage);
297
+ const modelsType = this.generateModelsType(models, storage, parameterizedRenderers);
298
+ const relationsType = this.generateRelationsType(ir.relations);
299
+ const mappingsType = this.generateMappingsType(models, storage);
227
300
 
228
- export type Contract = SqlContract<
301
+ const executionHashType = hashes.executionHash
302
+ ? `ExecutionHashBase<'${hashes.executionHash}'>`
303
+ : 'ExecutionHashBase<string>';
304
+
305
+ return `// ⚠️ GENERATED FILE - DO NOT EDIT
306
+ // This file is automatically generated by 'prisma-next contract emit'.
307
+ // To regenerate, run: prisma-next contract emit
308
+ ${importLines.join('\n')}
309
+
310
+ import type {
311
+ ExecutionHashBase,
312
+ ProfileHashBase,
313
+ StorageHashBase,
314
+ } from '@prisma-next/contract/types';
315
+ import type {
316
+ SqlContract,
317
+ SqlStorage,
318
+ SqlMappings,
319
+ ModelDefinition,
320
+ ContractWithTypeMaps,
321
+ TypeMaps as TypeMapsType,
322
+ } from '@prisma-next/sql-contract/types';
323
+
324
+ export type StorageHash = StorageHashBase<'${hashes.storageHash}'>;
325
+ export type ExecutionHash = ${executionHashType};
326
+ export type ProfileHash = ProfileHashBase<'${hashes.profileHash}'>;
327
+
328
+ export type CodecTypes = ${codecTypes || 'Record<string, never>'};
329
+ export type LaneCodecTypes = CodecTypes;
330
+ export type OperationTypes = ${operationTypes || 'Record<string, never>'};
331
+ export type QueryOperationTypes = ${queryOperationTypes || 'Record<string, never>'};
332
+ type DefaultLiteralValue<CodecId extends string, Encoded> =
333
+ CodecId extends keyof CodecTypes
334
+ ? CodecTypes[CodecId] extends { readonly output: infer O }
335
+ ? O extends Date | bigint ? O : Encoded
336
+ : Encoded
337
+ : Encoded;
338
+
339
+ export type TypeMaps = TypeMapsType<CodecTypes, OperationTypes, QueryOperationTypes>;
340
+
341
+ type ContractBase = SqlContract<
229
342
  ${storageType},
230
343
  ${modelsType},
231
344
  ${relationsType},
232
- ${mappingsType}
233
- >;
234
-
235
- export type Tables = Contract['storage']['tables'];
236
- export type Models = Contract['models'];
237
- export type Relations = Contract['relations'];
238
- `;
345
+ ${mappingsType},
346
+ StorageHash,
347
+ ExecutionHash,
348
+ ProfileHash
349
+ > & {
350
+ readonly target: ${this.serializeValue(ir.target)};
351
+ readonly capabilities: ${this.serializeValue(ir.capabilities)};
352
+ readonly extensionPacks: ${this.serializeValue(ir.extensionPacks)};
353
+ };
354
+
355
+ export type Contract = ContractWithTypeMaps<ContractBase, TypeMaps>;
356
+
357
+ export type Tables = Contract['storage']['tables'];
358
+ export type Models = Contract['models'];
359
+ export type Relations = Contract['relations'];
360
+ `;
239
361
  },
240
362
 
241
363
  generateStorageType(storage: SqlStorage): string {
@@ -246,8 +368,17 @@ export type Relations = Contract['relations'];
246
368
  const nullable = col.nullable ? 'true' : 'false';
247
369
  const nativeType = `'${col.nativeType}'`;
248
370
  const codecId = `'${col.codecId}'`;
371
+ const defaultSpec = col.default
372
+ ? col.default.kind === 'literal'
373
+ ? `; readonly default: { readonly kind: 'literal'; readonly value: DefaultLiteralValue<${codecId}, ${this.serializeValue(
374
+ col.default.value,
375
+ )}> }`
376
+ : `; readonly default: { readonly kind: 'function'; readonly expression: ${this.serializeValue(
377
+ col.default.expression,
378
+ )} }`
379
+ : '';
249
380
  columns.push(
250
- `readonly ${colName}: { readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly nullable: ${nullable} }`,
381
+ `readonly ${colName}: { readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly nullable: ${nullable}${defaultSpec} }`,
251
382
  );
252
383
  }
253
384
 
@@ -272,7 +403,11 @@ export type Relations = Contract['relations'];
272
403
  .map((i) => {
273
404
  const cols = i.columns.map((c: string) => `'${c}'`).join(', ');
274
405
  const name = i.name ? `; readonly name: '${i.name}'` : '';
275
- return `{ readonly columns: readonly [${cols}]${name} }`;
406
+ const using =
407
+ i.using !== undefined ? `; readonly using: ${this.serializeValue(i.using)}` : '';
408
+ const config =
409
+ i.config !== undefined ? `; readonly config: ${this.serializeValue(i.config)}` : '';
410
+ return `{ readonly columns: readonly [${cols}]${name}${using}${config} }`;
276
411
  })
277
412
  .join(', ');
278
413
  tableParts.push(`indexes: readonly [${indexes}]`);
@@ -282,7 +417,7 @@ export type Relations = Contract['relations'];
282
417
  const cols = fk.columns.map((c: string) => `'${c}'`).join(', ');
283
418
  const refCols = fk.references.columns.map((c: string) => `'${c}'`).join(', ');
284
419
  const name = fk.name ? `; readonly name: '${fk.name}'` : '';
285
- return `{ readonly columns: readonly [${cols}]; readonly references: { readonly table: '${fk.references.table}'; readonly columns: readonly [${refCols}] }${name} }`;
420
+ return `{ readonly columns: readonly [${cols}]; readonly references: { readonly table: '${fk.references.table}'; readonly columns: readonly [${refCols}] }${name}; readonly constraint: ${fk.constraint}; readonly index: ${fk.index} }`;
286
421
  })
287
422
  .join(', ');
288
423
  tableParts.push(`foreignKeys: readonly [${fks}]`);
@@ -290,17 +425,104 @@ export type Relations = Contract['relations'];
290
425
  tables.push(`readonly ${tableName}: { ${tableParts.join('; ')} }`);
291
426
  }
292
427
 
293
- return `{ readonly tables: { ${tables.join('; ')} } }`;
428
+ const typesType = this.generateStorageTypesType(storage.types);
429
+
430
+ return `{ readonly tables: { ${tables.join('; ')} }; readonly types: ${typesType} }`;
431
+ },
432
+
433
+ /**
434
+ * Generates the TypeScript type for storage.types with literal types.
435
+ * This preserves type params as literal values for precise typing.
436
+ */
437
+ generateStorageTypesType(types: SqlStorage['types']): string {
438
+ if (!types || Object.keys(types).length === 0) {
439
+ return 'Record<string, never>';
440
+ }
441
+
442
+ const typeEntries: string[] = [];
443
+ for (const [typeName, typeInstance] of Object.entries(types)) {
444
+ const codecId = `'${typeInstance.codecId}'`;
445
+ const nativeType = `'${typeInstance.nativeType}'`;
446
+ const typeParamsStr = this.serializeTypeParamsLiteral(typeInstance.typeParams);
447
+ typeEntries.push(
448
+ `readonly ${typeName}: { readonly codecId: ${codecId}; readonly nativeType: ${nativeType}; readonly typeParams: ${typeParamsStr} }`,
449
+ );
450
+ }
451
+
452
+ return `{ ${typeEntries.join('; ')} }`;
453
+ },
454
+
455
+ /**
456
+ * Serializes a typeParams object to a TypeScript literal type.
457
+ * Converts { length: 1536 } to "{ readonly length: 1536 }".
458
+ */
459
+ serializeTypeParamsLiteral(params: Record<string, unknown>): string {
460
+ if (!params || Object.keys(params).length === 0) {
461
+ return 'Record<string, never>';
462
+ }
463
+
464
+ const entries: string[] = [];
465
+ for (const [key, value] of Object.entries(params)) {
466
+ const serialized = this.serializeValue(value);
467
+ entries.push(`readonly ${this.serializeObjectKey(key)}: ${serialized}`);
468
+ }
469
+
470
+ return `{ ${entries.join('; ')} }`;
471
+ },
472
+
473
+ /**
474
+ * Serializes a value to a TypeScript literal type expression.
475
+ */
476
+ serializeValue(value: unknown): string {
477
+ if (value === null) {
478
+ return 'null';
479
+ }
480
+ if (value === undefined) {
481
+ return 'undefined';
482
+ }
483
+ if (typeof value === 'string') {
484
+ // Escape backslashes first, then single quotes
485
+ const escaped = value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
486
+ return `'${escaped}'`;
487
+ }
488
+ if (typeof value === 'number' || typeof value === 'boolean') {
489
+ return String(value);
490
+ }
491
+ if (typeof value === 'bigint') {
492
+ return `${value}n`;
493
+ }
494
+ if (Array.isArray(value)) {
495
+ const items = value.map((v) => this.serializeValue(v)).join(', ');
496
+ return `readonly [${items}]`;
497
+ }
498
+ if (typeof value === 'object') {
499
+ const entries: string[] = [];
500
+ for (const [k, v] of Object.entries(value)) {
501
+ entries.push(`readonly ${this.serializeObjectKey(k)}: ${this.serializeValue(v)}`);
502
+ }
503
+ return `{ ${entries.join('; ')} }`;
504
+ }
505
+ return 'unknown';
506
+ },
507
+
508
+ serializeObjectKey(key: string): string {
509
+ if (/^[$A-Z_a-z][$\w]*$/.test(key)) {
510
+ return key;
511
+ }
512
+ return this.serializeValue(key);
294
513
  },
295
514
 
296
515
  generateModelsType(
297
516
  models: Record<string, ModelDefinition> | undefined,
298
517
  storage: SqlStorage,
518
+ parameterizedRenderers?: Map<string, TypeRenderEntry>,
299
519
  ): string {
300
520
  if (!models) {
301
521
  return 'Record<string, never>';
302
522
  }
303
523
 
524
+ const renderCtx: TypeRenderContext = { codecTypesName: 'CodecTypes' };
525
+
304
526
  const modelTypes: string[] = [];
305
527
  for (const [modelName, model] of Object.entries(models)) {
306
528
  const fields: string[] = [];
@@ -315,12 +537,12 @@ export type Relations = Contract['relations'];
315
537
  continue;
316
538
  }
317
539
 
318
- const typeId = column.codecId;
319
- const nullable = column.nullable ?? false;
320
- const jsType = nullable
321
- ? `CodecTypes['${typeId}']['output'] | null`
322
- : `CodecTypes['${typeId}']['output']`;
323
-
540
+ const jsType = this.generateColumnType(
541
+ column,
542
+ storage,
543
+ parameterizedRenderers,
544
+ renderCtx,
545
+ );
324
546
  fields.push(`readonly ${fieldName}: ${jsType}`);
325
547
  }
326
548
  } else {
@@ -358,6 +580,26 @@ export type Relations = Contract['relations'];
358
580
  return `{ ${modelTypes.join('; ')} }`;
359
581
  },
360
582
 
583
+ /**
584
+ * Generates the TypeScript type expression for a column.
585
+ * Uses parameterized renderer if the column has typeParams and a matching renderer exists,
586
+ * otherwise falls back to CodecTypes[codecId]['output'].
587
+ */
588
+ generateColumnType(
589
+ column: StorageColumn,
590
+ storage: SqlStorage,
591
+ parameterizedRenderers: Map<string, TypeRenderEntry> | undefined,
592
+ renderCtx: TypeRenderContext,
593
+ ): string {
594
+ const typeParams = resolveColumnTypeParams(column, storage);
595
+ const nullable = column.nullable ?? false;
596
+ const fallbackType = `CodecTypes['${column.codecId}']['output']`;
597
+ const renderer = typeParams && parameterizedRenderers?.get(column.codecId);
598
+ const baseType = renderer ? renderer.render(typeParams, renderCtx) : fallbackType;
599
+
600
+ return nullable ? `${baseType} | null` : baseType;
601
+ },
602
+
361
603
  generateRelationsType(relations: Record<string, unknown> | undefined): string {
362
604
  if (!relations || Object.keys(relations).length === 0) {
363
605
  return 'Record<string, never>';
@@ -426,11 +668,9 @@ export type Relations = Contract['relations'];
426
668
  generateMappingsType(
427
669
  models: Record<string, ModelDefinition> | undefined,
428
670
  storage: SqlStorage,
429
- codecTypes: string,
430
- operationTypes: string,
431
671
  ): string {
432
672
  if (!models) {
433
- return `SqlMappings & { readonly codecTypes: ${codecTypes || 'Record<string, never>'}; readonly operationTypes: ${operationTypes || 'Record<string, never>'}; }`;
673
+ return 'SqlMappings';
434
674
  }
435
675
 
436
676
  const modelToTable: string[] = [];
@@ -477,9 +717,7 @@ export type Relations = Contract['relations'];
477
717
  if (columnToField.length > 0) {
478
718
  parts.push(`columnToField: { ${columnToField.join('; ')} }`);
479
719
  }
480
- parts.push(`codecTypes: ${codecTypes || 'Record<string, never>'}`);
481
- parts.push(`operationTypes: ${operationTypes || 'Record<string, never>'}`);
482
720
 
483
- return `{ ${parts.join('; ')} }`;
721
+ return parts.length > 0 ? `{ ${parts.join('; ')} }` : 'SqlMappings';
484
722
  },
485
723
  } as const;
package/dist/index.d.ts DELETED
@@ -1,14 +0,0 @@
1
- import type { ContractIR } from '@prisma-next/contract/ir';
2
- import type { TypesImportSpec, ValidationContext } from '@prisma-next/contract/types';
3
- import type { ModelDefinition, SqlStorage } from '@prisma-next/sql-contract/types';
4
- export declare const sqlTargetFamilyHook: {
5
- readonly id: "sql";
6
- readonly validateTypes: (ir: ContractIR, _ctx: ValidationContext) => void;
7
- readonly validateStructure: (ir: ContractIR) => void;
8
- readonly generateContractTypes: (ir: ContractIR, codecTypeImports: ReadonlyArray<TypesImportSpec>, operationTypeImports: ReadonlyArray<TypesImportSpec>) => string;
9
- readonly generateStorageType: (storage: SqlStorage) => string;
10
- readonly generateModelsType: (models: Record<string, ModelDefinition> | undefined, storage: SqlStorage) => string;
11
- readonly generateRelationsType: (relations: Record<string, unknown> | undefined) => string;
12
- readonly generateMappingsType: (models: Record<string, ModelDefinition> | undefined, storage: SqlStorage, codecTypes: string, operationTypes: string) => string;
13
- };
14
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACtF,OAAO,KAAK,EACV,eAAe,EAEf,UAAU,EAEX,MAAM,iCAAiC,CAAC;AAEzC,eAAO,MAAM,mBAAmB;;iCAGZ,UAAU,QAAQ,iBAAiB,KAAG,IAAI;qCA6BtC,UAAU,KAAG,IAAI;yCA2JjC,UAAU,oBACI,aAAa,CAAC,eAAe,CAAC,wBAC1B,aAAa,CAAC,eAAe,CAAC,KACnD,MAAM;4CAyCoB,UAAU,KAAG,MAAM;0CAwDtC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,SAAS,WAC1C,UAAU,KAClB,MAAM;gDA8DwB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,KAAG,MAAM;4CAkEnE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,SAAS,WAC1C,UAAU,cACP,MAAM,kBACF,MAAM,KACrB,MAAM;CAsDD,CAAC"}