@prisma-next/sql-contract-emitter 0.3.0-pr.100.1 → 0.3.0-pr.101.1
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.
- package/dist/index.d.ts +4 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -116
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +17 -167
package/dist/index.d.ts
CHANGED
|
@@ -1,33 +1,13 @@
|
|
|
1
1
|
import type { ContractIR } from '@prisma-next/contract/ir';
|
|
2
|
-
import type {
|
|
3
|
-
import type { ModelDefinition, SqlStorage
|
|
2
|
+
import type { TypesImportSpec, ValidationContext } from '@prisma-next/contract/types';
|
|
3
|
+
import type { ModelDefinition, SqlStorage } from '@prisma-next/sql-contract/types';
|
|
4
4
|
export declare const sqlTargetFamilyHook: {
|
|
5
5
|
readonly id: "sql";
|
|
6
6
|
readonly validateTypes: (ir: ContractIR, _ctx: ValidationContext) => void;
|
|
7
7
|
readonly validateStructure: (ir: ContractIR) => void;
|
|
8
|
-
readonly generateContractTypes: (ir: ContractIR, codecTypeImports: ReadonlyArray<TypesImportSpec>, operationTypeImports: ReadonlyArray<TypesImportSpec
|
|
8
|
+
readonly generateContractTypes: (ir: ContractIR, codecTypeImports: ReadonlyArray<TypesImportSpec>, operationTypeImports: ReadonlyArray<TypesImportSpec>) => string;
|
|
9
9
|
readonly generateStorageType: (storage: SqlStorage) => string;
|
|
10
|
-
|
|
11
|
-
* Generates the TypeScript type for storage.types with literal types.
|
|
12
|
-
* This preserves type params as literal values for precise typing.
|
|
13
|
-
*/
|
|
14
|
-
readonly generateStorageTypesType: (types: SqlStorage["types"]) => string;
|
|
15
|
-
/**
|
|
16
|
-
* Serializes a typeParams object to a TypeScript literal type.
|
|
17
|
-
* Converts { length: 1536 } to "{ readonly length: 1536 }".
|
|
18
|
-
*/
|
|
19
|
-
readonly serializeTypeParamsLiteral: (params: Record<string, unknown>) => string;
|
|
20
|
-
/**
|
|
21
|
-
* Serializes a value to a TypeScript literal type expression.
|
|
22
|
-
*/
|
|
23
|
-
readonly serializeValue: (value: unknown) => string;
|
|
24
|
-
readonly generateModelsType: (models: Record<string, ModelDefinition> | undefined, storage: SqlStorage, parameterizedRenderers?: Map<string, TypeRenderEntry>) => string;
|
|
25
|
-
/**
|
|
26
|
-
* Generates the TypeScript type expression for a column.
|
|
27
|
-
* Uses parameterized renderer if the column has typeParams and a matching renderer exists,
|
|
28
|
-
* otherwise falls back to CodecTypes[codecId]['output'].
|
|
29
|
-
*/
|
|
30
|
-
readonly generateColumnType: (column: StorageColumn, storage: SqlStorage, parameterizedRenderers: Map<string, TypeRenderEntry> | undefined, renderCtx: TypeRenderContext) => string;
|
|
10
|
+
readonly generateModelsType: (models: Record<string, ModelDefinition> | undefined, storage: SqlStorage) => string;
|
|
31
11
|
readonly generateRelationsType: (relations: Record<string, unknown> | undefined) => string;
|
|
32
12
|
readonly generateMappingsType: (models: Record<string, ModelDefinition> | undefined, storage: SqlStorage, codecTypes: string, operationTypes: string) => string;
|
|
33
13
|
};
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,
|
|
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"}
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
function resolveColumnTypeParams(column, storage) {
|
|
3
|
-
if (column.typeParams && Object.keys(column.typeParams).length > 0) {
|
|
4
|
-
return column.typeParams;
|
|
5
|
-
}
|
|
6
|
-
if (column.typeRef && storage.types) {
|
|
7
|
-
const typeInstance = storage.types[column.typeRef];
|
|
8
|
-
if (typeInstance?.typeParams) {
|
|
9
|
-
return typeInstance.typeParams;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
return void 0;
|
|
13
|
-
}
|
|
14
2
|
var sqlTargetFamilyHook = {
|
|
15
3
|
id: "sql",
|
|
16
4
|
validateTypes(ir, _ctx) {
|
|
@@ -165,19 +153,17 @@ var sqlTargetFamilyHook = {
|
|
|
165
153
|
}
|
|
166
154
|
}
|
|
167
155
|
},
|
|
168
|
-
generateContractTypes(ir, codecTypeImports, operationTypeImports
|
|
169
|
-
const
|
|
156
|
+
generateContractTypes(ir, codecTypeImports, operationTypeImports) {
|
|
157
|
+
const allImports = [...codecTypeImports, ...operationTypeImports];
|
|
158
|
+
const importLines = allImports.map(
|
|
159
|
+
(imp) => `import type { ${imp.named} as ${imp.alias} } from '${imp.package}';`
|
|
160
|
+
);
|
|
161
|
+
const codecTypes = codecTypeImports.map((imp) => imp.alias).join(" & ");
|
|
162
|
+
const operationTypes = operationTypeImports.map((imp) => imp.alias).join(" & ");
|
|
170
163
|
const storage = ir.storage;
|
|
171
164
|
const models = ir.models;
|
|
172
|
-
const allImports = [...codecTypeImports, ...operationTypeImports];
|
|
173
|
-
const importLines = allImports.map((imp) => {
|
|
174
|
-
const importClause = imp.named === imp.alias ? imp.named : `${imp.named} as ${imp.alias}`;
|
|
175
|
-
return `import type { ${importClause} } from '${imp.package}';`;
|
|
176
|
-
});
|
|
177
|
-
const codecTypes = codecTypeImports.filter((imp) => imp.named === "CodecTypes").map((imp) => imp.alias).join(" & ");
|
|
178
|
-
const operationTypes = operationTypeImports.filter((imp) => imp.named === "OperationTypes").map((imp) => imp.alias).join(" & ");
|
|
179
165
|
const storageType = this.generateStorageType(storage);
|
|
180
|
-
const modelsType = this.generateModelsType(models, storage
|
|
166
|
+
const modelsType = this.generateModelsType(models, storage);
|
|
181
167
|
const relationsType = this.generateRelationsType(ir.relations);
|
|
182
168
|
const mappingsType = this.generateMappingsType(models, storage, codecTypes, operationTypes);
|
|
183
169
|
return `// \u26A0\uFE0F GENERATED FILE - DO NOT EDIT
|
|
@@ -242,77 +228,12 @@ export type Relations = Contract['relations'];
|
|
|
242
228
|
tableParts.push(`foreignKeys: readonly [${fks}]`);
|
|
243
229
|
tables.push(`readonly ${tableName}: { ${tableParts.join("; ")} }`);
|
|
244
230
|
}
|
|
245
|
-
|
|
246
|
-
return `{ readonly tables: { ${tables.join("; ")} }; readonly types: ${typesType} }`;
|
|
247
|
-
},
|
|
248
|
-
/**
|
|
249
|
-
* Generates the TypeScript type for storage.types with literal types.
|
|
250
|
-
* This preserves type params as literal values for precise typing.
|
|
251
|
-
*/
|
|
252
|
-
generateStorageTypesType(types) {
|
|
253
|
-
if (!types || Object.keys(types).length === 0) {
|
|
254
|
-
return "Record<string, never>";
|
|
255
|
-
}
|
|
256
|
-
const typeEntries = [];
|
|
257
|
-
for (const [typeName, typeInstance] of Object.entries(types)) {
|
|
258
|
-
const codecId = `'${typeInstance.codecId}'`;
|
|
259
|
-
const nativeType = `'${typeInstance.nativeType}'`;
|
|
260
|
-
const typeParamsStr = this.serializeTypeParamsLiteral(typeInstance.typeParams);
|
|
261
|
-
typeEntries.push(
|
|
262
|
-
`readonly ${typeName}: { readonly codecId: ${codecId}; readonly nativeType: ${nativeType}; readonly typeParams: ${typeParamsStr} }`
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
return `{ ${typeEntries.join("; ")} }`;
|
|
266
|
-
},
|
|
267
|
-
/**
|
|
268
|
-
* Serializes a typeParams object to a TypeScript literal type.
|
|
269
|
-
* Converts { length: 1536 } to "{ readonly length: 1536 }".
|
|
270
|
-
*/
|
|
271
|
-
serializeTypeParamsLiteral(params) {
|
|
272
|
-
if (!params || Object.keys(params).length === 0) {
|
|
273
|
-
return "Record<string, never>";
|
|
274
|
-
}
|
|
275
|
-
const entries = [];
|
|
276
|
-
for (const [key, value] of Object.entries(params)) {
|
|
277
|
-
const serialized = this.serializeValue(value);
|
|
278
|
-
entries.push(`readonly ${key}: ${serialized}`);
|
|
279
|
-
}
|
|
280
|
-
return `{ ${entries.join("; ")} }`;
|
|
231
|
+
return `{ readonly tables: { ${tables.join("; ")} } }`;
|
|
281
232
|
},
|
|
282
|
-
|
|
283
|
-
* Serializes a value to a TypeScript literal type expression.
|
|
284
|
-
*/
|
|
285
|
-
serializeValue(value) {
|
|
286
|
-
if (value === null) {
|
|
287
|
-
return "null";
|
|
288
|
-
}
|
|
289
|
-
if (value === void 0) {
|
|
290
|
-
return "undefined";
|
|
291
|
-
}
|
|
292
|
-
if (typeof value === "string") {
|
|
293
|
-
return `'${value}'`;
|
|
294
|
-
}
|
|
295
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
296
|
-
return String(value);
|
|
297
|
-
}
|
|
298
|
-
if (Array.isArray(value)) {
|
|
299
|
-
const items = value.map((v) => this.serializeValue(v)).join(", ");
|
|
300
|
-
return `readonly [${items}]`;
|
|
301
|
-
}
|
|
302
|
-
if (typeof value === "object") {
|
|
303
|
-
const entries = [];
|
|
304
|
-
for (const [k, v] of Object.entries(value)) {
|
|
305
|
-
entries.push(`readonly ${k}: ${this.serializeValue(v)}`);
|
|
306
|
-
}
|
|
307
|
-
return `{ ${entries.join("; ")} }`;
|
|
308
|
-
}
|
|
309
|
-
return "unknown";
|
|
310
|
-
},
|
|
311
|
-
generateModelsType(models, storage, parameterizedRenderers) {
|
|
233
|
+
generateModelsType(models, storage) {
|
|
312
234
|
if (!models) {
|
|
313
235
|
return "Record<string, never>";
|
|
314
236
|
}
|
|
315
|
-
const renderCtx = { codecTypesName: "CodecTypes" };
|
|
316
237
|
const modelTypes = [];
|
|
317
238
|
for (const [modelName, model] of Object.entries(models)) {
|
|
318
239
|
const fields = [];
|
|
@@ -325,12 +246,9 @@ export type Relations = Contract['relations'];
|
|
|
325
246
|
fields.push(`readonly ${fieldName}: { readonly column: '${field.column}' }`);
|
|
326
247
|
continue;
|
|
327
248
|
}
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
parameterizedRenderers,
|
|
332
|
-
renderCtx
|
|
333
|
-
);
|
|
249
|
+
const typeId = column.codecId;
|
|
250
|
+
const nullable = column.nullable ?? false;
|
|
251
|
+
const jsType = nullable ? `CodecTypes['${typeId}']['output'] | null` : `CodecTypes['${typeId}']['output']`;
|
|
334
252
|
fields.push(`readonly ${fieldName}: ${jsType}`);
|
|
335
253
|
}
|
|
336
254
|
} else {
|
|
@@ -362,27 +280,6 @@ export type Relations = Contract['relations'];
|
|
|
362
280
|
}
|
|
363
281
|
return `{ ${modelTypes.join("; ")} }`;
|
|
364
282
|
},
|
|
365
|
-
/**
|
|
366
|
-
* Generates the TypeScript type expression for a column.
|
|
367
|
-
* Uses parameterized renderer if the column has typeParams and a matching renderer exists,
|
|
368
|
-
* otherwise falls back to CodecTypes[codecId]['output'].
|
|
369
|
-
*/
|
|
370
|
-
generateColumnType(column, storage, parameterizedRenderers, renderCtx) {
|
|
371
|
-
const typeParams = resolveColumnTypeParams(column, storage);
|
|
372
|
-
const nullable = column.nullable ?? false;
|
|
373
|
-
let baseType;
|
|
374
|
-
if (typeParams && parameterizedRenderers) {
|
|
375
|
-
const renderer = parameterizedRenderers.get(column.codecId);
|
|
376
|
-
if (renderer) {
|
|
377
|
-
baseType = renderer.render(typeParams, renderCtx);
|
|
378
|
-
} else {
|
|
379
|
-
baseType = `CodecTypes['${column.codecId}']['output']`;
|
|
380
|
-
}
|
|
381
|
-
} else {
|
|
382
|
-
baseType = `CodecTypes['${column.codecId}']['output']`;
|
|
383
|
-
}
|
|
384
|
-
return nullable ? `${baseType} | null` : baseType;
|
|
385
|
-
},
|
|
386
283
|
generateRelationsType(relations) {
|
|
387
284
|
if (!relations || Object.keys(relations).length === 0) {
|
|
388
285
|
return "Record<string, never>";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"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';\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 = storage.tables[tableName];\n if (!table) {\n throw new Error(`Model \"${modelName}\" references non-existent table \"${tableName}\"`);\n }\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 const referencedTable = storage.tables[fk.references.table];\n if (!referencedTable) {\n throw new Error(\n `Table \"${tableName}\" foreignKey references non-existent table \"${fk.references.table}\"`,\n );\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 options?: GenerateContractTypesOptions,\n ): string {\n const parameterizedRenderers = options?.parameterizedRenderers;\n const storage = ir.storage as SqlStorage;\n const models = ir.models as Record<string, ModelDefinition>;\n\n // Note: Parameterized type imports (e.g., Vector<N>) are expected to be included\n // in codecTypeImports by the assembly layer. The renderer only generates types.\n const allImports = [...codecTypeImports, ...operationTypeImports];\n const importLines = allImports.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\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, codecTypes, operationTypes);\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\nimport type { SqlContract, SqlStorage, SqlMappings, ModelDefinition } from '@prisma-next/sql-contract/types';\n\nexport type CodecTypes = ${codecTypes || 'Record<string, never>'};\nexport type LaneCodecTypes = CodecTypes;\nexport type OperationTypes = ${operationTypes || 'Record<string, never>'};\n\nexport type Contract = SqlContract<\n ${storageType},\n ${modelsType},\n ${relationsType},\n ${mappingsType}\n>;\n\nexport type Tables = Contract['storage']['tables'];\nexport type Models = Contract['models'];\nexport 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 columns.push(\n `readonly ${colName}: { readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly nullable: ${nullable} }`,\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 return `{ readonly columns: readonly [${cols}]${name} }`;\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} }`;\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 ${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 return `'${value}'`;\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\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 ${k}: ${this.serializeValue(v)}`);\n }\n return `{ ${entries.join('; ')} }`;\n }\n return 'unknown';\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 let baseType: string;\n\n if (typeParams && parameterizedRenderers) {\n const renderer = parameterizedRenderers.get(column.codecId);\n if (renderer) {\n baseType = renderer.render(typeParams, renderCtx);\n } else {\n // No renderer for this codecId, fall back to standard lookup\n baseType = `CodecTypes['${column.codecId}']['output']`;\n }\n } else {\n // No typeParams, use standard codec lookup\n baseType = `CodecTypes['${column.codecId}']['output']`;\n }\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 codecTypes: string,\n operationTypes: string,\n ): string {\n if (!models) {\n return `SqlMappings & { readonly codecTypes: ${codecTypes || 'Record<string, never>'}; readonly operationTypes: ${operationTypes || 'Record<string, never>'}; }`;\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 parts.push(`codecTypes: ${codecTypes || 'Record<string, never>'}`);\n parts.push(`operationTypes: ${operationTypes || 'Record<string, never>'}`);\n\n return `{ ${parts.join('; ')} }`;\n },\n} as const;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { ContractIR } from '@prisma-next/contract/ir';\nimport type { TypesImportSpec, ValidationContext } from '@prisma-next/contract/types';\nimport type {\n ModelDefinition,\n ModelField,\n SqlStorage,\n StorageTable,\n} from '@prisma-next/sql-contract/types';\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 = storage.tables[tableName];\n if (!table) {\n throw new Error(`Model \"${modelName}\" references non-existent table \"${tableName}\"`);\n }\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 const referencedTable = storage.tables[fk.references.table];\n if (!referencedTable) {\n throw new Error(\n `Table \"${tableName}\" foreignKey references non-existent table \"${fk.references.table}\"`,\n );\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 ): string {\n const allImports = [...codecTypeImports, ...operationTypeImports];\n const importLines = allImports.map(\n (imp) => `import type { ${imp.named} as ${imp.alias} } from '${imp.package}';`,\n );\n\n const codecTypes = codecTypeImports.map((imp) => imp.alias).join(' & ');\n const operationTypes = operationTypeImports.map((imp) => imp.alias).join(' & ');\n\n const storage = ir.storage as SqlStorage;\n const models = ir.models as Record<string, ModelDefinition>;\n\n const storageType = this.generateStorageType(storage);\n const modelsType = this.generateModelsType(models, storage);\n const relationsType = this.generateRelationsType(ir.relations);\n const mappingsType = this.generateMappingsType(models, storage, codecTypes, operationTypes);\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\nimport type { SqlContract, SqlStorage, SqlMappings, ModelDefinition } from '@prisma-next/sql-contract/types';\n\nexport type CodecTypes = ${codecTypes || 'Record<string, never>'};\nexport type LaneCodecTypes = CodecTypes;\nexport type OperationTypes = ${operationTypes || 'Record<string, never>'};\n\nexport type Contract = SqlContract<\n ${storageType},\n ${modelsType},\n ${relationsType},\n ${mappingsType}\n>;\n\nexport type Tables = Contract['storage']['tables'];\nexport type Models = Contract['models'];\nexport 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 columns.push(\n `readonly ${colName}: { readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly nullable: ${nullable} }`,\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 return `{ readonly columns: readonly [${cols}]${name} }`;\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} }`;\n })\n .join(', ');\n tableParts.push(`foreignKeys: readonly [${fks}]`);\n\n tables.push(`readonly ${tableName}: { ${tableParts.join('; ')} }`);\n }\n\n return `{ readonly tables: { ${tables.join('; ')} } }`;\n },\n\n generateModelsType(\n models: Record<string, ModelDefinition> | undefined,\n storage: SqlStorage,\n ): string {\n if (!models) {\n return 'Record<string, never>';\n }\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 typeId = column.codecId;\n const nullable = column.nullable ?? false;\n const jsType = nullable\n ? `CodecTypes['${typeId}']['output'] | null`\n : `CodecTypes['${typeId}']['output']`;\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 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 codecTypes: string,\n operationTypes: string,\n ): string {\n if (!models) {\n return `SqlMappings & { readonly codecTypes: ${codecTypes || 'Record<string, never>'}; readonly operationTypes: ${operationTypes || 'Record<string, never>'}; }`;\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 parts.push(`codecTypes: ${codecTypes || 'Record<string, never>'}`);\n parts.push(`operationTypes: ${operationTypes || 'Record<string, never>'}`);\n\n return `{ ${parts.join('; ')} }`;\n },\n} as const;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/sql-contract-emitter",
|
|
3
|
-
"version": "0.3.0-pr.
|
|
3
|
+
"version": "0.3.0-pr.101.1",
|
|
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-pr.
|
|
9
|
-
"@prisma-next/emitter": "0.3.0-pr.
|
|
10
|
-
"@prisma-next/sql-contract": "0.3.0-pr.
|
|
8
|
+
"@prisma-next/contract": "0.3.0-pr.101.1",
|
|
9
|
+
"@prisma-next/emitter": "0.3.0-pr.101.1",
|
|
10
|
+
"@prisma-next/sql-contract": "0.3.0-pr.101.1"
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"tsup": "8.5.1",
|
package/src/index.ts
CHANGED
|
@@ -1,42 +1,12 @@
|
|
|
1
1
|
import type { ContractIR } from '@prisma-next/contract/ir';
|
|
2
|
-
import type {
|
|
3
|
-
GenerateContractTypesOptions,
|
|
4
|
-
TypeRenderContext,
|
|
5
|
-
TypeRenderEntry,
|
|
6
|
-
TypesImportSpec,
|
|
7
|
-
ValidationContext,
|
|
8
|
-
} from '@prisma-next/contract/types';
|
|
2
|
+
import type { TypesImportSpec, ValidationContext } from '@prisma-next/contract/types';
|
|
9
3
|
import type {
|
|
10
4
|
ModelDefinition,
|
|
11
5
|
ModelField,
|
|
12
6
|
SqlStorage,
|
|
13
|
-
StorageColumn,
|
|
14
7
|
StorageTable,
|
|
15
|
-
StorageTypeInstance,
|
|
16
8
|
} from '@prisma-next/sql-contract/types';
|
|
17
9
|
|
|
18
|
-
/**
|
|
19
|
-
* Resolves the typeParams for a column, either from inline typeParams or from typeRef.
|
|
20
|
-
* Returns undefined if no typeParams are available.
|
|
21
|
-
*/
|
|
22
|
-
function resolveColumnTypeParams(
|
|
23
|
-
column: StorageColumn,
|
|
24
|
-
storage: SqlStorage,
|
|
25
|
-
): Record<string, unknown> | undefined {
|
|
26
|
-
// Inline typeParams take precedence
|
|
27
|
-
if (column.typeParams && Object.keys(column.typeParams).length > 0) {
|
|
28
|
-
return column.typeParams;
|
|
29
|
-
}
|
|
30
|
-
// Check typeRef
|
|
31
|
-
if (column.typeRef && storage.types) {
|
|
32
|
-
const typeInstance = storage.types[column.typeRef] as StorageTypeInstance | undefined;
|
|
33
|
-
if (typeInstance?.typeParams) {
|
|
34
|
-
return typeInstance.typeParams;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
10
|
export const sqlTargetFamilyHook = {
|
|
41
11
|
id: 'sql',
|
|
42
12
|
|
|
@@ -227,34 +197,20 @@ export const sqlTargetFamilyHook = {
|
|
|
227
197
|
ir: ContractIR,
|
|
228
198
|
codecTypeImports: ReadonlyArray<TypesImportSpec>,
|
|
229
199
|
operationTypeImports: ReadonlyArray<TypesImportSpec>,
|
|
230
|
-
options?: GenerateContractTypesOptions,
|
|
231
200
|
): string {
|
|
232
|
-
const
|
|
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
|
+
|
|
233
209
|
const storage = ir.storage as SqlStorage;
|
|
234
210
|
const models = ir.models as Record<string, ModelDefinition>;
|
|
235
211
|
|
|
236
|
-
// Note: Parameterized type imports (e.g., Vector<N>) are expected to be included
|
|
237
|
-
// in codecTypeImports by the assembly layer. The renderer only generates types.
|
|
238
|
-
const allImports = [...codecTypeImports, ...operationTypeImports];
|
|
239
|
-
const importLines = allImports.map((imp) => {
|
|
240
|
-
// Simplify import when named === alias (e.g., `import type { Vector }` instead of `{ Vector as Vector }`)
|
|
241
|
-
const importClause = imp.named === imp.alias ? imp.named : `${imp.named} as ${imp.alias}`;
|
|
242
|
-
return `import type { ${importClause} } from '${imp.package}';`;
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
// Only intersect actual codec/operation type maps. Extra type-only imports (e.g. Vector<N>) are
|
|
246
|
-
// included in importLines via codecTypeImports but must not be intersected into CodecTypes.
|
|
247
|
-
const codecTypes = codecTypeImports
|
|
248
|
-
.filter((imp) => imp.named === 'CodecTypes')
|
|
249
|
-
.map((imp) => imp.alias)
|
|
250
|
-
.join(' & ');
|
|
251
|
-
const operationTypes = operationTypeImports
|
|
252
|
-
.filter((imp) => imp.named === 'OperationTypes')
|
|
253
|
-
.map((imp) => imp.alias)
|
|
254
|
-
.join(' & ');
|
|
255
|
-
|
|
256
212
|
const storageType = this.generateStorageType(storage);
|
|
257
|
-
const modelsType = this.generateModelsType(models, storage
|
|
213
|
+
const modelsType = this.generateModelsType(models, storage);
|
|
258
214
|
const relationsType = this.generateRelationsType(ir.relations);
|
|
259
215
|
const mappingsType = this.generateMappingsType(models, storage, codecTypes, operationTypes);
|
|
260
216
|
|
|
@@ -334,92 +290,17 @@ export type Relations = Contract['relations'];
|
|
|
334
290
|
tables.push(`readonly ${tableName}: { ${tableParts.join('; ')} }`);
|
|
335
291
|
}
|
|
336
292
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
return `{ readonly tables: { ${tables.join('; ')} }; readonly types: ${typesType} }`;
|
|
340
|
-
},
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Generates the TypeScript type for storage.types with literal types.
|
|
344
|
-
* This preserves type params as literal values for precise typing.
|
|
345
|
-
*/
|
|
346
|
-
generateStorageTypesType(types: SqlStorage['types']): string {
|
|
347
|
-
if (!types || Object.keys(types).length === 0) {
|
|
348
|
-
return 'Record<string, never>';
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
const typeEntries: string[] = [];
|
|
352
|
-
for (const [typeName, typeInstance] of Object.entries(types)) {
|
|
353
|
-
const codecId = `'${typeInstance.codecId}'`;
|
|
354
|
-
const nativeType = `'${typeInstance.nativeType}'`;
|
|
355
|
-
const typeParamsStr = this.serializeTypeParamsLiteral(typeInstance.typeParams);
|
|
356
|
-
typeEntries.push(
|
|
357
|
-
`readonly ${typeName}: { readonly codecId: ${codecId}; readonly nativeType: ${nativeType}; readonly typeParams: ${typeParamsStr} }`,
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
return `{ ${typeEntries.join('; ')} }`;
|
|
362
|
-
},
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Serializes a typeParams object to a TypeScript literal type.
|
|
366
|
-
* Converts { length: 1536 } to "{ readonly length: 1536 }".
|
|
367
|
-
*/
|
|
368
|
-
serializeTypeParamsLiteral(params: Record<string, unknown>): string {
|
|
369
|
-
if (!params || Object.keys(params).length === 0) {
|
|
370
|
-
return 'Record<string, never>';
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const entries: string[] = [];
|
|
374
|
-
for (const [key, value] of Object.entries(params)) {
|
|
375
|
-
const serialized = this.serializeValue(value);
|
|
376
|
-
entries.push(`readonly ${key}: ${serialized}`);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
return `{ ${entries.join('; ')} }`;
|
|
380
|
-
},
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Serializes a value to a TypeScript literal type expression.
|
|
384
|
-
*/
|
|
385
|
-
serializeValue(value: unknown): string {
|
|
386
|
-
if (value === null) {
|
|
387
|
-
return 'null';
|
|
388
|
-
}
|
|
389
|
-
if (value === undefined) {
|
|
390
|
-
return 'undefined';
|
|
391
|
-
}
|
|
392
|
-
if (typeof value === 'string') {
|
|
393
|
-
return `'${value}'`;
|
|
394
|
-
}
|
|
395
|
-
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
396
|
-
return String(value);
|
|
397
|
-
}
|
|
398
|
-
if (Array.isArray(value)) {
|
|
399
|
-
const items = value.map((v) => this.serializeValue(v)).join(', ');
|
|
400
|
-
return `readonly [${items}]`;
|
|
401
|
-
}
|
|
402
|
-
if (typeof value === 'object') {
|
|
403
|
-
const entries: string[] = [];
|
|
404
|
-
for (const [k, v] of Object.entries(value)) {
|
|
405
|
-
entries.push(`readonly ${k}: ${this.serializeValue(v)}`);
|
|
406
|
-
}
|
|
407
|
-
return `{ ${entries.join('; ')} }`;
|
|
408
|
-
}
|
|
409
|
-
return 'unknown';
|
|
293
|
+
return `{ readonly tables: { ${tables.join('; ')} } }`;
|
|
410
294
|
},
|
|
411
295
|
|
|
412
296
|
generateModelsType(
|
|
413
297
|
models: Record<string, ModelDefinition> | undefined,
|
|
414
298
|
storage: SqlStorage,
|
|
415
|
-
parameterizedRenderers?: Map<string, TypeRenderEntry>,
|
|
416
299
|
): string {
|
|
417
300
|
if (!models) {
|
|
418
301
|
return 'Record<string, never>';
|
|
419
302
|
}
|
|
420
303
|
|
|
421
|
-
const renderCtx: TypeRenderContext = { codecTypesName: 'CodecTypes' };
|
|
422
|
-
|
|
423
304
|
const modelTypes: string[] = [];
|
|
424
305
|
for (const [modelName, model] of Object.entries(models)) {
|
|
425
306
|
const fields: string[] = [];
|
|
@@ -434,12 +315,12 @@ export type Relations = Contract['relations'];
|
|
|
434
315
|
continue;
|
|
435
316
|
}
|
|
436
317
|
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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
|
+
|
|
443
324
|
fields.push(`readonly ${fieldName}: ${jsType}`);
|
|
444
325
|
}
|
|
445
326
|
} else {
|
|
@@ -477,37 +358,6 @@ export type Relations = Contract['relations'];
|
|
|
477
358
|
return `{ ${modelTypes.join('; ')} }`;
|
|
478
359
|
},
|
|
479
360
|
|
|
480
|
-
/**
|
|
481
|
-
* Generates the TypeScript type expression for a column.
|
|
482
|
-
* Uses parameterized renderer if the column has typeParams and a matching renderer exists,
|
|
483
|
-
* otherwise falls back to CodecTypes[codecId]['output'].
|
|
484
|
-
*/
|
|
485
|
-
generateColumnType(
|
|
486
|
-
column: StorageColumn,
|
|
487
|
-
storage: SqlStorage,
|
|
488
|
-
parameterizedRenderers: Map<string, TypeRenderEntry> | undefined,
|
|
489
|
-
renderCtx: TypeRenderContext,
|
|
490
|
-
): string {
|
|
491
|
-
const typeParams = resolveColumnTypeParams(column, storage);
|
|
492
|
-
const nullable = column.nullable ?? false;
|
|
493
|
-
let baseType: string;
|
|
494
|
-
|
|
495
|
-
if (typeParams && parameterizedRenderers) {
|
|
496
|
-
const renderer = parameterizedRenderers.get(column.codecId);
|
|
497
|
-
if (renderer) {
|
|
498
|
-
baseType = renderer.render(typeParams, renderCtx);
|
|
499
|
-
} else {
|
|
500
|
-
// No renderer for this codecId, fall back to standard lookup
|
|
501
|
-
baseType = `CodecTypes['${column.codecId}']['output']`;
|
|
502
|
-
}
|
|
503
|
-
} else {
|
|
504
|
-
// No typeParams, use standard codec lookup
|
|
505
|
-
baseType = `CodecTypes['${column.codecId}']['output']`;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
return nullable ? `${baseType} | null` : baseType;
|
|
509
|
-
},
|
|
510
|
-
|
|
511
361
|
generateRelationsType(relations: Record<string, unknown> | undefined): string {
|
|
512
362
|
if (!relations || Object.keys(relations).length === 0) {
|
|
513
363
|
return 'Record<string, never>';
|