@prisma-next/sql-contract-emitter 0.9.0 → 0.10.0

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.mts CHANGED
@@ -1,27 +1,6 @@
1
1
  import { Contract, ContractModel } from "@prisma-next/contract/types";
2
+ import { GenerateContractTypesOptions, ValidationContext } from "@prisma-next/framework-components/emission";
2
3
 
3
- //#region ../../../1-framework/1-core/framework-components/dist/types-import-spec-BxI5cSQy.d.mts
4
- //#region src/shared/types-import-spec.d.ts
5
- /**
6
- * Specifies how to import TypeScript types from a package.
7
- * Used in extension pack manifests to declare codec and operation type imports.
8
- */
9
- interface TypesImportSpec {
10
- readonly package: string;
11
- readonly named: string;
12
- readonly alias: string;
13
- } //#endregion
14
- //#endregion
15
- //#region ../../../1-framework/1-core/framework-components/dist/emission-types-CMv_053d.d.mts
16
- //#region src/control/emission-types.d.ts
17
- interface GenerateContractTypesOptions {
18
- readonly queryOperationTypeImports?: ReadonlyArray<TypesImportSpec>;
19
- }
20
- interface ValidationContext {
21
- readonly codecTypeImports?: ReadonlyArray<TypesImportSpec>;
22
- readonly extensionIds?: ReadonlyArray<string>;
23
- }
24
- //#endregion
25
4
  //#region src/index.d.ts
26
5
  declare const sqlEmission: {
27
6
  readonly id: "sql";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":["TypesImportSpec","package","named","alias","t","t","TypesImportSpec","Contract","ContractModel","GenerateContractTypesOptions","ReadonlyArray","queryOperationTypeImports","ValidationContext","codecTypeImports","extensionIds","EmissionSpi","Record","id","generateStorageType","contract","storageHashTypeName","generateModelStorageType","modelName","model","getFamilyImports","getFamilyTypeAliases","options","getTypeMapsExpression","getContractWrapper","contractBaseName","typeMapsName","resolveFieldTypeParams","fieldName","n","r"],"sources":["../../../../1-framework/1-core/framework-components/dist/types-import-spec-BxI5cSQy.d.mts","../../../../1-framework/1-core/framework-components/dist/emission-types-CMv_053d.d.mts","../src/index.ts"],"mappings":";;;;;;;;UAKUA,eAAAA;EAAAA,SACCC,OAAAA;EAAAA,SACAC,KAAAA;EAAAA,SACAC,KAAAA;AAAAA;;;;UCJDM,4BAAAA;EAAAA,SACCE,yBAAAA,GAA4BD,aAAAA,CAAcJ,eAAAA;AAAAA;AAAAA,UAE3CM,iBAAAA;EAAAA,SACCC,gBAAAA,GAAmBH,aAAAA,CAAcJ,eAAAA;EAAAA,SACjCQ,YAAAA,GAAeJ,aAAAA;AAAAA;;;cCmBb,WAAA;EAAA;qCAGa,QAAA,EAAQ,IAAA,EAAQ,iBAAA;EAAA,uCA2BZ,QAAA;EAAA,yCA8IE,QAAA,EAAQ,mBAAA;EAAA,wDAkFK,KAAA,EAAS,aAAA;EAAA,sDAoBhC,SAAA,UACD,KAAA,EACV,aAAA,EAAa,QAAA,EACV,QAAA,KACT,MAAA;EAAA;4CAwC4B,4BAAA;EAAA;0DAwBY,YAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;cAkEa,WAAA;EAAA;qCAGa,QAAA,EAAQ,IAAA,EAAQ,iBAAA;EAAA,uCA6BZ,QAAA;EAAA,yCAmJE,QAAA,EAAQ,mBAAA;EAAA,wDAQK,KAAA,EAAS,aAAA;EAAA,sDAoBhC,SAAA,UACD,KAAA,EACV,aAAA,EAAa,QAAA,EACV,QAAA,KACT,MAAA;EAAA;4CA+C4B,4BAAA;EAAA;0DAwBY,YAAA;AAAA"}
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { serializeObjectKey, serializeValue } from "@prisma-next/emitter/domain-type-generation";
2
+ import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
2
3
  import { isPostgresEnumStorageEntry } from "@prisma-next/sql-contract/types";
3
- import { assertDefined } from "@prisma-next/utils/assertions";
4
4
  //#region src/index.ts
5
5
  function serializeTypeParamsLiteral(params) {
6
6
  if (!params || Object.keys(params).length === 0) return "Record<string, never>";
@@ -8,13 +8,39 @@ function serializeTypeParamsLiteral(params) {
8
8
  for (const [key, value] of Object.entries(params)) entries.push(`readonly ${serializeObjectKey(key)}: ${serializeValue(value)}`);
9
9
  return `{ ${entries.join("; ")} }`;
10
10
  }
11
+ /**
12
+ * Name-only lookup that walks every namespace until it finds a table
13
+ * with the given name. Survives this slice only for the two model-side
14
+ * call sites whose `SqlModelStorage.table` is still a bare name without
15
+ * a namespace coordinate; both call sites — and this helper — are
16
+ * deleted by TML-2584, which promotes `SqlModelStorage` to carry the
17
+ * model's namespace id so model→table resolution can use explicit
18
+ * coordinates like the FK-ref site already does.
19
+ */
20
+ function findSqlTable(storage, tableName) {
21
+ for (const [namespaceId, ns] of Object.entries(storage.namespaces)) {
22
+ const table = ns.tables[tableName];
23
+ if (table !== void 0) return {
24
+ table,
25
+ namespaceId
26
+ };
27
+ }
28
+ }
29
+ function assertUniqueSqlTableNames(storage) {
30
+ const seen = /* @__PURE__ */ new Map();
31
+ for (const [namespaceId, ns] of Object.entries(storage.namespaces)) for (const tableName of Object.keys(ns.tables)) {
32
+ const existing = seen.get(tableName);
33
+ if (existing !== void 0 && existing !== namespaceId) throw new Error(`Duplicate table name "${tableName}" in namespaces "${existing}" and "${namespaceId}"`);
34
+ seen.set(tableName, namespaceId);
35
+ }
36
+ }
11
37
  const sqlEmission = {
12
38
  id: "sql",
13
39
  validateTypes(contract, _ctx) {
14
40
  const storage = contract.storage;
15
- if (!storage?.tables) return;
41
+ if (!storage?.namespaces) return;
16
42
  const typeIdRegex = /^([^/]+)\/([^@]+)@(\d+)$/;
17
- for (const [tableName, tableUnknown] of Object.entries(storage.tables)) {
43
+ for (const ns of Object.values(storage.namespaces)) for (const [tableName, tableUnknown] of Object.entries(ns.tables)) {
18
44
  const table = tableUnknown;
19
45
  for (const [colName, colUnknown] of Object.entries(table.columns)) {
20
46
  const codecId = colUnknown.codecId;
@@ -26,15 +52,17 @@ const sqlEmission = {
26
52
  validateStructure(contract) {
27
53
  if (contract.targetFamily !== "sql") throw new Error(`Expected targetFamily "sql", got "${contract.targetFamily}"`);
28
54
  const storage = contract.storage;
29
- if (!storage?.tables) throw new Error("SQL contract must have storage.tables");
55
+ if (!storage?.namespaces) throw new Error("SQL contract must have storage.namespaces");
56
+ assertUniqueSqlTableNames(storage);
30
57
  const models = contract.models;
31
- const tableNames = new Set(Object.keys(storage.tables));
58
+ const tableNames = /* @__PURE__ */ new Set();
59
+ for (const ns of Object.values(storage.namespaces)) for (const t of Object.keys(ns.tables)) tableNames.add(t);
32
60
  if (models) for (const [modelName, model] of Object.entries(models)) {
33
61
  if (!model.storage?.table) throw new Error(`Model "${modelName}" is missing storage.table`);
34
62
  const tableName = model.storage.table;
35
- if (!tableNames.has(tableName)) throw new Error(`Model "${modelName}" references non-existent table "${tableName}"`);
36
- const table = storage.tables[tableName];
37
- assertDefined(table, `Model "${modelName}" references non-existent table "${tableName}"`);
63
+ const located = findSqlTable(storage, tableName);
64
+ if (!located) throw new Error(`Model "${modelName}" references non-existent table "${tableName}"`);
65
+ const { table } = located;
38
66
  const columnNames = new Set(Object.keys(table.columns));
39
67
  const storageFields = model.storage.fields;
40
68
  if (!storageFields || Object.keys(storageFields).length === 0) throw new Error(`Model "${modelName}" is missing storage.fields`);
@@ -44,7 +72,7 @@ const sqlEmission = {
44
72
  }
45
73
  if (!model.relations || typeof model.relations !== "object") throw new Error(`Model "${modelName}" is missing required field "relations" (must be an object)`);
46
74
  }
47
- for (const [tableName, tableUnknown] of Object.entries(storage.tables)) {
75
+ for (const ns of Object.values(storage.namespaces)) for (const [tableName, tableUnknown] of Object.entries(ns.tables)) {
48
76
  const table = tableUnknown;
49
77
  const columnNames = new Set(Object.keys(table.columns));
50
78
  if (!Array.isArray(table.uniques)) throw new Error(`Table "${tableName}" is missing required field "uniques" (must be an array)`);
@@ -56,56 +84,20 @@ const sqlEmission = {
56
84
  for (const unique of table.uniques) for (const colName of unique.columns) if (!columnNames.has(colName)) throw new Error(`Table "${tableName}" unique constraint references non-existent column "${colName}"`);
57
85
  for (const index of table.indexes) for (const colName of index.columns) if (!columnNames.has(colName)) throw new Error(`Table "${tableName}" index references non-existent column "${colName}"`);
58
86
  for (const fk of table.foreignKeys) {
59
- for (const colName of fk.columns) if (!columnNames.has(colName)) throw new Error(`Table "${tableName}" foreignKey references non-existent column "${colName}"`);
60
- if (!tableNames.has(fk.references.table)) throw new Error(`Table "${tableName}" foreignKey references non-existent table "${fk.references.table}"`);
61
- const referencedTable = storage.tables[fk.references.table];
62
- assertDefined(referencedTable, `Table "${tableName}" foreignKey references non-existent table "${fk.references.table}"`);
87
+ for (const colName of fk.source.columns) if (!columnNames.has(colName)) throw new Error(`Table "${tableName}" foreignKey references non-existent column "${colName}"`);
88
+ const referencedTable = storage.namespaces[fk.target.namespaceId]?.tables[fk.target.tableName];
89
+ if (!referencedTable) throw new Error(`Table "${tableName}" foreignKey references non-existent table "${fk.target.tableName}" in namespace "${fk.target.namespaceId}"`);
63
90
  const referencedColumnNames = new Set(Object.keys(referencedTable.columns));
64
- for (const colName of fk.references.columns) if (!referencedColumnNames.has(colName)) throw new Error(`Table "${tableName}" foreignKey references non-existent column "${colName}" in table "${fk.references.table}"`);
65
- if (fk.columns.length !== fk.references.columns.length) throw new Error(`Table "${tableName}" foreignKey column count (${fk.columns.length}) does not match referenced column count (${fk.references.columns.length})`);
91
+ for (const colName of fk.target.columns) if (!referencedColumnNames.has(colName)) throw new Error(`Table "${tableName}" foreignKey references non-existent column "${colName}" in table "${fk.target.tableName}"`);
92
+ if (fk.source.columns.length !== fk.target.columns.length) throw new Error(`Table "${tableName}" foreignKey column count (${fk.source.columns.length}) does not match referenced column count (${fk.target.columns.length})`);
66
93
  }
67
94
  }
68
95
  },
69
96
  generateStorageType(contract, storageHashTypeName) {
70
97
  const storage = contract.storage;
71
- const tables = [];
72
- for (const [tableName, table] of Object.entries(storage.tables).sort(([a], [b]) => a.localeCompare(b))) {
73
- const columns = [];
74
- for (const [colName, col] of Object.entries(table.columns)) {
75
- const nullable = col.nullable ? "true" : "false";
76
- const nativeType = serializeValue(col.nativeType);
77
- const codecId = serializeValue(col.codecId);
78
- const defaultSpec = col.default ? col.default.kind === "literal" ? `; readonly default: { readonly kind: 'literal'; readonly value: DefaultLiteralValue<${codecId}, ${serializeValue(col.default.value)}> }` : `; readonly default: { readonly kind: 'function'; readonly expression: ${serializeValue(col.default.expression)} }` : "";
79
- const typeParamsSpec = col.typeParams && Object.keys(col.typeParams).length > 0 ? `; readonly typeParams: ${serializeTypeParamsLiteral(col.typeParams)}` : "";
80
- const typeRefSpec = col.typeRef ? `; readonly typeRef: ${serializeValue(col.typeRef)}` : "";
81
- columns.push(`readonly ${colName}: { readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly nullable: ${nullable}${defaultSpec}${typeParamsSpec}${typeRefSpec} }`);
82
- }
83
- const tableParts = [`columns: { ${columns.join("; ")} }`];
84
- if (table.primaryKey) {
85
- const pkCols = table.primaryKey.columns.map((c) => serializeValue(c)).join(", ");
86
- const pkName = table.primaryKey.name ? `; readonly name: ${serializeValue(table.primaryKey.name)}` : "";
87
- tableParts.push(`primaryKey: { readonly columns: readonly [${pkCols}]${pkName} }`);
88
- }
89
- const uniques = table.uniques.map((u) => {
90
- return `{ readonly columns: readonly [${u.columns.map((c) => serializeValue(c)).join(", ")}]${u.name ? `; readonly name: ${serializeValue(u.name)}` : ""} }`;
91
- }).join(", ");
92
- tableParts.push(`uniques: readonly [${uniques}]`);
93
- const indexes = table.indexes.map((i) => {
94
- return `{ readonly columns: readonly [${i.columns.map((c) => serializeValue(c)).join(", ")}]${i.name !== void 0 ? `; readonly name: ${serializeValue(i.name)}` : ""}${i.type !== void 0 ? `; readonly type: ${serializeValue(i.type)}` : ""}${i.options !== void 0 ? `; readonly options: ${serializeValue(i.options)}` : ""} }`;
95
- }).join(", ");
96
- tableParts.push(`indexes: readonly [${indexes}]`);
97
- const fks = table.foreignKeys.map((fk) => {
98
- const cols = fk.columns.map((c) => serializeValue(c)).join(", ");
99
- const refCols = fk.references.columns.map((c) => serializeValue(c)).join(", ");
100
- const name = fk.name ? `; readonly name: ${serializeValue(fk.name)}` : "";
101
- return `{ readonly columns: readonly [${cols}]; readonly references: { readonly table: ${serializeValue(fk.references.table)}; readonly columns: readonly [${refCols}] }${name}; readonly constraint: ${fk.constraint}; readonly index: ${fk.index} }`;
102
- }).join(", ");
103
- tableParts.push(`foreignKeys: readonly [${fks}]`);
104
- tables.push(`readonly ${tableName}: { ${tableParts.join("; ")} }`);
105
- }
106
- const typesType = generateStorageTypesType(storage.types);
107
98
  const namespacesType = generateStorageNamespacesType(storage.namespaces);
108
- return `{ readonly tables: { ${tables.join("; ")} }; readonly types: ${typesType}; readonly namespaces: ${namespacesType}; readonly storageHash: ${storageHashTypeName} }`;
99
+ const docTypes = generateDocumentScopedStorageTypesType(storage.types);
100
+ return `{ readonly namespaces: ${namespacesType}${docTypes === void 0 ? "" : `; readonly types: ${docTypes}`}; readonly storageHash: ${storageHashTypeName} }`;
109
101
  },
110
102
  generateModelStorageType(_modelName, model) {
111
103
  const sqlModel = model;
@@ -126,12 +118,15 @@ const sqlEmission = {
126
118
  const storage = contract.storage;
127
119
  if (!storage) return void 0;
128
120
  const tableName = sqlModel.storage.table;
129
- const table = storage.tables[tableName];
130
- if (!table) return void 0;
131
- const column = table.columns[storageField.column];
121
+ const located = findSqlTable(storage, tableName);
122
+ if (!located) return void 0;
123
+ const column = located.table.columns[storageField.column];
132
124
  if (!column) return void 0;
133
125
  if (column.typeRef) {
134
- const typeInstance = storage.types?.[column.typeRef];
126
+ const ns = storage.namespaces[located.namespaceId];
127
+ const fromNamespace = (ns !== void 0 && "types" in ns ? ns.types : void 0)?.[column.typeRef];
128
+ const fromDocument = storage.types?.[column.typeRef];
129
+ const typeInstance = fromNamespace ?? fromDocument;
135
130
  if (typeInstance === void 0) return void 0;
136
131
  if (isPostgresEnumStorageEntry(typeInstance)) return { values: typeInstance.values };
137
132
  return typeInstance.typeParams;
@@ -164,24 +159,18 @@ const sqlEmission = {
164
159
  return [
165
160
  `export type Contract = ContractWithTypeMaps<${contractBaseName}, ${typeMapsName}>;`,
166
161
  "",
167
- "export type Tables = Contract['storage']['tables'];",
162
+ "export type Namespaces = Contract['storage']['namespaces'];",
168
163
  "export type Models = Contract['models'];"
169
164
  ].join("\n");
170
165
  }
171
166
  };
172
- function generateStorageTypesType(types) {
173
- if (!types || Object.keys(types).length === 0) return "Record<string, never>";
167
+ function generateDocumentScopedStorageTypesType(types) {
168
+ if (!types || Object.keys(types).length === 0) return;
174
169
  const typeEntries = [];
175
170
  for (const [typeName, typeInstance] of Object.entries(types)) {
176
- if (isPostgresEnumStorageEntry(typeInstance)) {
177
- const codecId = serializeValue(typeInstance.codecId);
178
- const nativeType = serializeValue(typeInstance.nativeType);
179
- const typeParamsStr = serializeTypeParamsLiteral({ values: typeInstance.values });
180
- typeEntries.push(`readonly ${typeName}: { readonly kind: 'codec-instance'; readonly codecId: ${codecId}; readonly nativeType: ${nativeType}; readonly typeParams: ${typeParamsStr} }`);
181
- continue;
182
- }
171
+ if (isPostgresEnumStorageEntry(typeInstance)) throw new Error(`Document-scoped storage.types entry "${typeName}" is a postgres-enum; enums belong under storage.namespaces[namespaceId].types`);
183
172
  const codecInstanceShape = typeInstance;
184
- if (typeof codecInstanceShape.codecId !== "string" || typeof codecInstanceShape.nativeType !== "string") throw new Error(`Unknown storage type kind for "${typeName}"; expected a codec-instance triple or a known IR-class kind.`);
173
+ if (typeof codecInstanceShape.codecId !== "string" || typeof codecInstanceShape.nativeType !== "string") throw new Error(`Unknown storage type kind for "${typeName}" in document-scoped storage.types; expected a codec-instance triple.`);
185
174
  const codecId = serializeValue(codecInstanceShape.codecId);
186
175
  const nativeType = serializeValue(codecInstanceShape.nativeType);
187
176
  const typeParamsStr = serializeTypeParamsLiteral(codecInstanceShape.typeParams);
@@ -189,11 +178,81 @@ function generateStorageTypesType(types) {
189
178
  }
190
179
  return `{ ${typeEntries.join("; ")} }`;
191
180
  }
181
+ function generatePostgresNamespaceTypesType(types) {
182
+ if (Object.keys(types).length === 0) return "Record<string, never>";
183
+ const typeEntries = [];
184
+ for (const [typeName, typeInstance] of Object.entries(types)) {
185
+ if (isPostgresEnumStorageEntry(typeInstance)) {
186
+ const codecId = serializeValue(typeInstance.codecId);
187
+ const nativeType = serializeValue(typeInstance.nativeType);
188
+ const name = serializeValue(typeInstance.name);
189
+ const valuesLiteral = typeInstance.values.map((v) => serializeValue(v)).join(", ");
190
+ typeEntries.push(`readonly ${serializeObjectKey(typeName)}: { readonly kind: 'postgres-enum'; readonly name: ${name}; readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly values: readonly [${valuesLiteral}] }`);
191
+ continue;
192
+ }
193
+ throw new Error(`Unknown namespace storage type kind for "${typeName}"; expected postgres-enum in namespace.types.`);
194
+ }
195
+ return `{ ${typeEntries.join("; ")} }`;
196
+ }
197
+ function isPostgresSchemaNamespace(ns) {
198
+ return ns.kind === "schema" && "types" in ns && typeof ns.types === "object" && ns.types !== null;
199
+ }
200
+ function namespaceSerializedKind(ns) {
201
+ const kind = ns.kind;
202
+ if (kind === "schema") return `readonly kind: '${ns.id === UNBOUND_NAMESPACE_ID ? "postgres-unbound-schema" : "postgres-schema"}'`;
203
+ if (typeof kind === "string") return `readonly kind: ${serializeValue(kind)}`;
204
+ }
205
+ function generateTableLiteralType(table) {
206
+ const columns = [];
207
+ for (const [colName, col] of Object.entries(table.columns)) {
208
+ const nullable = col.nullable ? "true" : "false";
209
+ const nativeType = serializeValue(col.nativeType);
210
+ const codecId = serializeValue(col.codecId);
211
+ const defaultSpec = col.default ? col.default.kind === "literal" ? `; readonly default: { readonly kind: 'literal'; readonly value: DefaultLiteralValue<${codecId}, ${serializeValue(col.default.value)}> }` : `; readonly default: { readonly kind: 'function'; readonly expression: ${serializeValue(col.default.expression)} }` : "";
212
+ const typeParamsSpec = col.typeParams && Object.keys(col.typeParams).length > 0 ? `; readonly typeParams: ${serializeTypeParamsLiteral(col.typeParams)}` : "";
213
+ const typeRefSpec = col.typeRef ? `; readonly typeRef: ${serializeValue(col.typeRef)}` : "";
214
+ columns.push(`readonly ${colName}: { readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly nullable: ${nullable}${defaultSpec}${typeParamsSpec}${typeRefSpec} }`);
215
+ }
216
+ const tableParts = [`columns: { ${columns.join("; ")} }`];
217
+ if (table.primaryKey) {
218
+ const pkCols = table.primaryKey.columns.map((c) => serializeValue(c)).join(", ");
219
+ const pkName = table.primaryKey.name ? `; readonly name: ${serializeValue(table.primaryKey.name)}` : "";
220
+ tableParts.push(`primaryKey: { readonly columns: readonly [${pkCols}]${pkName} }`);
221
+ }
222
+ const uniques = table.uniques.map((u) => {
223
+ return `{ readonly columns: readonly [${u.columns.map((c) => serializeValue(c)).join(", ")}]${u.name ? `; readonly name: ${serializeValue(u.name)}` : ""} }`;
224
+ }).join(", ");
225
+ tableParts.push(`uniques: readonly [${uniques}]`);
226
+ const indexes = table.indexes.map((i) => {
227
+ return `{ readonly columns: readonly [${i.columns.map((c) => serializeValue(c)).join(", ")}]${i.name !== void 0 ? `; readonly name: ${serializeValue(i.name)}` : ""}${i.type !== void 0 ? `; readonly type: ${serializeValue(i.type)}` : ""}${i.options !== void 0 ? `; readonly options: ${serializeValue(i.options)}` : ""} }`;
228
+ }).join(", ");
229
+ tableParts.push(`indexes: readonly [${indexes}]`);
230
+ const fks = table.foreignKeys.map((fk) => {
231
+ const srcCols = fk.source.columns.map((c) => serializeValue(c)).join(", ");
232
+ const tgtCols = fk.target.columns.map((c) => serializeValue(c)).join(", ");
233
+ const name = fk.name ? `; readonly name: ${serializeValue(fk.name)}` : "";
234
+ return `{ readonly source: ${`{ readonly namespaceId: ${serializeValue(fk.source.namespaceId)}; readonly tableName: ${serializeValue(fk.source.tableName)}; readonly columns: readonly [${srcCols}] }`}; readonly target: ${`{ readonly namespaceId: ${serializeValue(fk.target.namespaceId)}; readonly tableName: ${serializeValue(fk.target.tableName)}; readonly columns: readonly [${tgtCols}] }`}${name}; readonly constraint: ${fk.constraint}; readonly index: ${fk.index} }`;
235
+ }).join(", ");
236
+ tableParts.push(`foreignKeys: readonly [${fks}]`);
237
+ return `{ ${tableParts.join("; ")} }`;
238
+ }
239
+ function generateTablesMapType(tables) {
240
+ const tableEntries = [];
241
+ for (const [tableName, table] of Object.entries(tables).sort(([a], [b]) => a.localeCompare(b))) tableEntries.push(`readonly ${tableName}: ${generateTableLiteralType(table)}`);
242
+ if (tableEntries.length === 0) return "{}";
243
+ return `{ ${tableEntries.join("; ")} }`;
244
+ }
192
245
  function generateStorageNamespacesType(namespaces) {
193
246
  const entries = Object.entries(namespaces ?? {}).sort(([a], [b]) => a.localeCompare(b));
194
247
  if (entries.length === 0) return "Record<string, never>";
195
248
  const parts = [];
196
- for (const [name, ns] of entries) parts.push(`readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)} }`);
249
+ for (const [name, ns] of entries) {
250
+ const kindPart = namespaceSerializedKind(ns);
251
+ const kindSuffix = kindPart ? `; ${kindPart}` : "";
252
+ const tablesType = generateTablesMapType(ns.tables);
253
+ const typesClause = isPostgresSchemaNamespace(ns) ? `; readonly types: ${generatePostgresNamespaceTypesType(ns.types)}` : "";
254
+ parts.push(`readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}${kindSuffix}; readonly tables: ${tablesType}${typesClause} }`);
255
+ }
197
256
  return `{ ${parts.join("; ")} }`;
198
257
  }
199
258
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["col","codecShape"],"sources":["../src/index.ts"],"sourcesContent":["import type { Contract, ContractModel } from '@prisma-next/contract/types';\nimport { serializeObjectKey, serializeValue } from '@prisma-next/emitter/domain-type-generation';\nimport type {\n GenerateContractTypesOptions,\n ValidationContext,\n} from '@prisma-next/framework-components/emission';\nimport {\n isPostgresEnumStorageEntry,\n type SqlModelStorage,\n type SqlStorage,\n type StorageTable,\n type StorageTypeInstance,\n} from '@prisma-next/sql-contract/types';\nimport { assertDefined } from '@prisma-next/utils/assertions';\n\nfunction serializeTypeParamsLiteral(params: Record<string, unknown> | undefined): 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 entries.push(`readonly ${serializeObjectKey(key)}: ${serializeValue(value)}`);\n }\n\n return `{ ${entries.join('; ')} }`;\n}\n\nexport const sqlEmission = {\n id: 'sql',\n\n validateTypes(contract: Contract, _ctx: ValidationContext): void {\n const storage = contract.storage as unknown as SqlStorage | undefined;\n if (!storage?.tables) {\n return;\n }\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?.[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(contract: Contract): void {\n if (contract.targetFamily !== 'sql') {\n throw new Error(`Expected targetFamily \"sql\", got \"${contract.targetFamily}\"`);\n }\n\n const storage = contract.storage as unknown as SqlStorage | undefined;\n if (!storage?.tables) {\n throw new Error('SQL contract must have storage.tables');\n }\n\n const models = contract.models as Record<string, ContractModel<SqlModelStorage>>;\n const tableNames = new Set(Object.keys(storage.tables));\n\n if (models) {\n for (const [modelName, model] of Object.entries(models)) {\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 const columnNames = new Set(Object.keys(table.columns));\n const storageFields = model.storage.fields;\n if (!storageFields || Object.keys(storageFields).length === 0) {\n throw new Error(`Model \"${modelName}\" is missing storage.fields`);\n }\n\n for (const [fieldName, field] of Object.entries(storageFields)) {\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 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: 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 generateStorageType(contract: Contract, storageHashTypeName: string): string {\n const storage = contract.storage as unknown as SqlStorage;\n const tables: string[] = [];\n for (const [tableName, table] of Object.entries(storage.tables).sort(([a], [b]) =>\n a.localeCompare(b),\n )) {\n const columns: string[] = [];\n for (const [colName, col] of Object.entries(table.columns)) {\n const nullable = col.nullable ? 'true' : 'false';\n const nativeType = serializeValue(col.nativeType);\n const codecId = serializeValue(col.codecId);\n const defaultSpec = col.default\n ? col.default.kind === 'literal'\n ? `; readonly default: { readonly kind: 'literal'; readonly value: DefaultLiteralValue<${codecId}, ${serializeValue(\n col.default.value,\n )}> }`\n : `; readonly default: { readonly kind: 'function'; readonly expression: ${serializeValue(\n col.default.expression,\n )} }`\n : '';\n const typeParamsSpec =\n col.typeParams && Object.keys(col.typeParams).length > 0\n ? `; readonly typeParams: ${serializeTypeParamsLiteral(col.typeParams)}`\n : '';\n const typeRefSpec = col.typeRef ? `; readonly typeRef: ${serializeValue(col.typeRef)}` : '';\n columns.push(\n `readonly ${colName}: { readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly nullable: ${nullable}${defaultSpec}${typeParamsSpec}${typeRefSpec} }`,\n );\n }\n\n const tableParts: string[] = [`columns: { ${columns.join('; ')} }`];\n\n if (table.primaryKey) {\n const pkCols = table.primaryKey.columns.map((c) => serializeValue(c)).join(', ');\n const pkName = table.primaryKey.name\n ? `; readonly name: ${serializeValue(table.primaryKey.name)}`\n : '';\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) => serializeValue(c)).join(', ');\n const name = u.name ? `; readonly name: ${serializeValue(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) => serializeValue(c)).join(', ');\n const name = i.name !== undefined ? `; readonly name: ${serializeValue(i.name)}` : '';\n const indexType =\n i.type !== undefined ? `; readonly type: ${serializeValue(i.type)}` : '';\n const indexOptions =\n i.options !== undefined ? `; readonly options: ${serializeValue(i.options)}` : '';\n return `{ readonly columns: readonly [${cols}]${name}${indexType}${indexOptions} }`;\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) => serializeValue(c)).join(', ');\n const refCols = fk.references.columns.map((c: string) => serializeValue(c)).join(', ');\n const name = fk.name ? `; readonly name: ${serializeValue(fk.name)}` : '';\n return `{ readonly columns: readonly [${cols}]; readonly references: { readonly table: ${serializeValue(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 = generateStorageTypesType(storage.types);\n\n const namespacesType = generateStorageNamespacesType(storage.namespaces);\n\n return `{ readonly tables: { ${tables.join('; ')} }; readonly types: ${typesType}; readonly namespaces: ${namespacesType}; readonly storageHash: ${storageHashTypeName} }`;\n },\n\n generateModelStorageType(_modelName: string, model: ContractModel): string {\n const sqlModel = model as ContractModel<SqlModelStorage>;\n const tableName = sqlModel.storage.table;\n const storageFields = sqlModel.storage.fields;\n\n const storageParts = [`readonly table: ${serializeValue(tableName)}`];\n if (Object.keys(storageFields).length > 0) {\n const fieldParts: string[] = [];\n for (const [fieldName, field] of Object.entries(storageFields)) {\n fieldParts.push(\n `readonly ${serializeObjectKey(fieldName)}: { readonly column: ${serializeValue(field.column)} }`,\n );\n }\n storageParts.push(`readonly fields: { ${fieldParts.join('; ')} }`);\n }\n\n return `{ ${storageParts.join('; ')} }`;\n },\n\n resolveFieldTypeParams(\n _modelName: string,\n fieldName: string,\n model: ContractModel,\n contract: Contract,\n ): Record<string, unknown> | undefined {\n const sqlModel = model as ContractModel<SqlModelStorage>;\n const storageField = sqlModel.storage?.fields?.[fieldName];\n if (!storageField) return undefined;\n\n const storage = contract.storage as unknown as SqlStorage | undefined;\n if (!storage) return undefined;\n\n const tableName = sqlModel.storage.table;\n const table: StorageTable | undefined = storage.tables[tableName];\n if (!table) return undefined;\n\n const column = table.columns[storageField.column];\n if (!column) return undefined;\n\n if (column.typeRef) {\n const typeInstance = storage.types?.[column.typeRef];\n if (typeInstance === undefined) return undefined;\n if (isPostgresEnumStorageEntry(typeInstance)) {\n return { values: typeInstance.values };\n }\n // Fall back to structural codec-triple access when the literal\n // bypasses the runtime normaliser (e.g. test fixtures or\n // hand-written descriptor inputs that omit the\n // `kind: 'codec-instance'` discriminator).\n const codecShape = typeInstance as Partial<StorageTypeInstance>;\n return codecShape.typeParams;\n }\n return column.typeParams;\n },\n\n getFamilyImports(): string[] {\n return [\n 'import type {',\n ' ContractWithTypeMaps,',\n ' TypeMaps as TypeMapsType,',\n \"} from '@prisma-next/sql-contract/types';\",\n ];\n },\n\n getFamilyTypeAliases(options?: GenerateContractTypesOptions): string {\n const queryOperationTypeImports = options?.queryOperationTypeImports ?? [];\n const queryOperationAliases = queryOperationTypeImports\n .filter((imp) => imp.named === 'QueryOperationTypes')\n .map((imp) => `${imp.alias}<CodecTypes>`);\n const queryOperationTypes =\n queryOperationAliases.length > 0\n ? queryOperationAliases.join(' & ')\n : 'Record<string, never>';\n\n return [\n 'export type LaneCodecTypes = CodecTypes;',\n `export type QueryOperationTypes = ${queryOperationTypes};`,\n 'type DefaultLiteralValue<CodecId extends string, _Encoded> =',\n ' CodecId extends keyof CodecTypes',\n \" ? CodecTypes[CodecId]['output']\",\n ' : _Encoded;',\n ].join('\\n');\n },\n\n getTypeMapsExpression(): string {\n return 'TypeMapsType<CodecTypes, QueryOperationTypes, FieldOutputTypes, FieldInputTypes>';\n },\n\n getContractWrapper(contractBaseName: string, typeMapsName: string): string {\n return [\n `export type Contract = ContractWithTypeMaps<${contractBaseName}, ${typeMapsName}>;`,\n '',\n \"export type Tables = Contract['storage']['tables'];\",\n \"export type Models = Contract['models'];\",\n ].join('\\n');\n },\n} as const;\n\nfunction 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 if (isPostgresEnumStorageEntry(typeInstance)) {\n const codecId = serializeValue(\n // `codecBinding.codecId` lives on the live IR-class instance;\n // raw JSON envelopes carry `codecId` as an enumerable own\n // property. Read the structural-shape field so the emitter\n // works against both runtime forms.\n typeInstance.codecId,\n );\n const nativeType = serializeValue(typeInstance.nativeType);\n const typeParamsStr = serializeTypeParamsLiteral({\n values: typeInstance.values as unknown as readonly unknown[],\n });\n // Emit the resolved codec view (kind: 'codec-instance') so the\n // emitted .d.ts shape stays uniform across slot variants and\n // satisfies the polymorphic slot's structural alphabet. The\n // persisted JSON envelope still carries the IR's narrower kind\n // discriminator; the d.ts is the type-level codec-resolved view.\n typeEntries.push(\n `readonly ${typeName}: { readonly kind: 'codec-instance'; readonly codecId: ${codecId}; readonly nativeType: ${nativeType}; readonly typeParams: ${typeParamsStr} }`,\n );\n continue;\n }\n // The slot is polymorphic at the framework level; codec-instance\n // entries are the only non-IR-class kind today. The runtime\n // `SqlStorage` constructor stamps `kind: 'codec-instance'` on\n // plain codec triples; the emitter is forgiving about literal\n // inputs that bypass the constructor (test fixtures, hand-written\n // descriptors) and treats anything with the codec-triple shape as\n // a codec-instance.\n const codecInstanceShape = typeInstance as Partial<StorageTypeInstance>;\n if (\n typeof codecInstanceShape.codecId !== 'string' ||\n typeof codecInstanceShape.nativeType !== 'string'\n ) {\n throw new Error(\n `Unknown storage type kind for \"${typeName}\"; expected a codec-instance triple or a known IR-class kind.`,\n );\n }\n const codecId = serializeValue(codecInstanceShape.codecId);\n const nativeType = serializeValue(codecInstanceShape.nativeType);\n const typeParamsStr = serializeTypeParamsLiteral(codecInstanceShape.typeParams);\n typeEntries.push(\n `readonly ${typeName}: { readonly kind: 'codec-instance'; readonly codecId: ${codecId}; readonly nativeType: ${nativeType}; readonly typeParams: ${typeParamsStr} }`,\n );\n }\n\n return `{ ${typeEntries.join('; ')} }`;\n}\n\nfunction generateStorageNamespacesType(namespaces: SqlStorage['namespaces']): string {\n const entries = Object.entries(namespaces ?? {}).sort(([a], [b]) => a.localeCompare(b));\n if (entries.length === 0) {\n return 'Record<string, never>';\n }\n const parts: string[] = [];\n for (const [name, ns] of entries) {\n parts.push(`readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)} }`);\n }\n return `{ ${parts.join('; ')} }`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["col","codecShape"],"sources":["../src/index.ts"],"sourcesContent":["import type { Contract, ContractModel } from '@prisma-next/contract/types';\nimport { serializeObjectKey, serializeValue } from '@prisma-next/emitter/domain-type-generation';\nimport type {\n GenerateContractTypesOptions,\n ValidationContext,\n} from '@prisma-next/framework-components/emission';\nimport { type Namespace, UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport {\n isPostgresEnumStorageEntry,\n type PostgresEnumStorageEntry,\n type SqlModelStorage,\n type SqlStorage,\n type StorageTable,\n type StorageTypeInstance,\n} from '@prisma-next/sql-contract/types';\n\nfunction serializeTypeParamsLiteral(params: Record<string, unknown> | undefined): 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 entries.push(`readonly ${serializeObjectKey(key)}: ${serializeValue(value)}`);\n }\n\n return `{ ${entries.join('; ')} }`;\n}\n\n/**\n * Name-only lookup that walks every namespace until it finds a table\n * with the given name. Survives this slice only for the two model-side\n * call sites whose `SqlModelStorage.table` is still a bare name without\n * a namespace coordinate; both call sites — and this helper — are\n * deleted by TML-2584, which promotes `SqlModelStorage` to carry the\n * model's namespace id so model→table resolution can use explicit\n * coordinates like the FK-ref site already does.\n */\nfunction findSqlTable(\n storage: SqlStorage,\n tableName: string,\n): { readonly table: StorageTable; readonly namespaceId: string } | undefined {\n for (const [namespaceId, ns] of Object.entries(storage.namespaces)) {\n const table = ns.tables[tableName] as StorageTable | undefined;\n if (table !== undefined) {\n return { table, namespaceId };\n }\n }\n return undefined;\n}\n\nfunction assertUniqueSqlTableNames(storage: SqlStorage): void {\n const seen = new Map<string, string>();\n for (const [namespaceId, ns] of Object.entries(storage.namespaces)) {\n for (const tableName of Object.keys(ns.tables)) {\n const existing = seen.get(tableName);\n if (existing !== undefined && existing !== namespaceId) {\n throw new Error(\n `Duplicate table name \"${tableName}\" in namespaces \"${existing}\" and \"${namespaceId}\"`,\n );\n }\n seen.set(tableName, namespaceId);\n }\n }\n}\n\nexport const sqlEmission = {\n id: 'sql',\n\n validateTypes(contract: Contract, _ctx: ValidationContext): void {\n const storage = contract.storage as unknown as SqlStorage | undefined;\n if (!storage?.namespaces) {\n return;\n }\n\n const typeIdRegex = /^([^/]+)\\/([^@]+)@(\\d+)$/;\n\n for (const ns of Object.values(storage.namespaces)) {\n for (const [tableName, tableUnknown] of Object.entries(ns.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?.[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\n validateStructure(contract: Contract): void {\n if (contract.targetFamily !== 'sql') {\n throw new Error(`Expected targetFamily \"sql\", got \"${contract.targetFamily}\"`);\n }\n\n const storage = contract.storage as unknown as SqlStorage | undefined;\n if (!storage?.namespaces) {\n throw new Error('SQL contract must have storage.namespaces');\n }\n\n assertUniqueSqlTableNames(storage);\n\n const models = contract.models as Record<string, ContractModel<SqlModelStorage>>;\n const tableNames = new Set<string>();\n for (const ns of Object.values(storage.namespaces)) {\n for (const t of Object.keys(ns.tables)) {\n tableNames.add(t);\n }\n }\n\n if (models) {\n for (const [modelName, model] of Object.entries(models)) {\n if (!model.storage?.table) {\n throw new Error(`Model \"${modelName}\" is missing storage.table`);\n }\n\n const tableName = model.storage.table;\n const located = findSqlTable(storage, tableName);\n if (!located) {\n throw new Error(`Model \"${modelName}\" references non-existent table \"${tableName}\"`);\n }\n\n const { table } = located;\n const columnNames = new Set(Object.keys(table.columns));\n const storageFields = model.storage.fields;\n if (!storageFields || Object.keys(storageFields).length === 0) {\n throw new Error(`Model \"${modelName}\" is missing storage.fields`);\n }\n\n for (const [fieldName, field] of Object.entries(storageFields)) {\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 ns of Object.values(storage.namespaces)) {\n for (const [tableName, tableUnknown] of Object.entries(ns.tables)) {\n const table = tableUnknown as StorageTable;\n const columnNames = new Set(Object.keys(table.columns));\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.source.columns) {\n if (!columnNames.has(colName)) {\n throw new Error(\n `Table \"${tableName}\" foreignKey references non-existent column \"${colName}\"`,\n );\n }\n }\n\n const referencedTable = storage.namespaces[fk.target.namespaceId]?.tables[\n fk.target.tableName\n ] as StorageTable | undefined;\n if (!referencedTable) {\n throw new Error(\n `Table \"${tableName}\" foreignKey references non-existent table \"${fk.target.tableName}\" in namespace \"${fk.target.namespaceId}\"`,\n );\n }\n\n const referencedColumnNames = new Set(Object.keys(referencedTable.columns));\n for (const colName of fk.target.columns) {\n if (!referencedColumnNames.has(colName)) {\n throw new Error(\n `Table \"${tableName}\" foreignKey references non-existent column \"${colName}\" in table \"${fk.target.tableName}\"`,\n );\n }\n }\n\n if (fk.source.columns.length !== fk.target.columns.length) {\n throw new Error(\n `Table \"${tableName}\" foreignKey column count (${fk.source.columns.length}) does not match referenced column count (${fk.target.columns.length})`,\n );\n }\n }\n }\n }\n },\n\n generateStorageType(contract: Contract, storageHashTypeName: string): string {\n const storage = contract.storage as unknown as SqlStorage;\n const namespacesType = generateStorageNamespacesType(storage.namespaces);\n const docTypes = generateDocumentScopedStorageTypesType(storage.types);\n const typesClause = docTypes === undefined ? '' : `; readonly types: ${docTypes}`;\n return `{ readonly namespaces: ${namespacesType}${typesClause}; readonly storageHash: ${storageHashTypeName} }`;\n },\n\n generateModelStorageType(_modelName: string, model: ContractModel): string {\n const sqlModel = model as ContractModel<SqlModelStorage>;\n const tableName = sqlModel.storage.table;\n const storageFields = sqlModel.storage.fields;\n\n const storageParts = [`readonly table: ${serializeValue(tableName)}`];\n if (Object.keys(storageFields).length > 0) {\n const fieldParts: string[] = [];\n for (const [fieldName, field] of Object.entries(storageFields)) {\n fieldParts.push(\n `readonly ${serializeObjectKey(fieldName)}: { readonly column: ${serializeValue(field.column)} }`,\n );\n }\n storageParts.push(`readonly fields: { ${fieldParts.join('; ')} }`);\n }\n\n return `{ ${storageParts.join('; ')} }`;\n },\n\n resolveFieldTypeParams(\n _modelName: string,\n fieldName: string,\n model: ContractModel,\n contract: Contract,\n ): Record<string, unknown> | undefined {\n const sqlModel = model as ContractModel<SqlModelStorage>;\n const storageField = sqlModel.storage?.fields?.[fieldName];\n if (!storageField) return undefined;\n\n const storage = contract.storage as unknown as SqlStorage | undefined;\n if (!storage) return undefined;\n\n const tableName = sqlModel.storage.table;\n const located = findSqlTable(storage, tableName);\n if (!located) return undefined;\n\n const column = located.table.columns[storageField.column];\n if (!column) return undefined;\n\n if (column.typeRef) {\n const ns = storage.namespaces[located.namespaceId];\n const nsTypes =\n ns !== undefined && 'types' in ns\n ? (\n ns as {\n types?: Readonly<Record<string, PostgresEnumStorageEntry | StorageTypeInstance>>;\n }\n ).types\n : undefined;\n const fromNamespace = nsTypes?.[column.typeRef];\n const fromDocument = storage.types?.[column.typeRef];\n const typeInstance = fromNamespace ?? fromDocument;\n if (typeInstance === undefined) return undefined;\n if (isPostgresEnumStorageEntry(typeInstance)) {\n return { values: typeInstance.values };\n }\n const codecShape = typeInstance as Partial<StorageTypeInstance>;\n return codecShape.typeParams;\n }\n return column.typeParams;\n },\n\n getFamilyImports(): string[] {\n return [\n 'import type {',\n ' ContractWithTypeMaps,',\n ' TypeMaps as TypeMapsType,',\n \"} from '@prisma-next/sql-contract/types';\",\n ];\n },\n\n getFamilyTypeAliases(options?: GenerateContractTypesOptions): string {\n const queryOperationTypeImports = options?.queryOperationTypeImports ?? [];\n const queryOperationAliases = queryOperationTypeImports\n .filter((imp) => imp.named === 'QueryOperationTypes')\n .map((imp) => `${imp.alias}<CodecTypes>`);\n const queryOperationTypes =\n queryOperationAliases.length > 0\n ? queryOperationAliases.join(' & ')\n : 'Record<string, never>';\n\n return [\n 'export type LaneCodecTypes = CodecTypes;',\n `export type QueryOperationTypes = ${queryOperationTypes};`,\n 'type DefaultLiteralValue<CodecId extends string, _Encoded> =',\n ' CodecId extends keyof CodecTypes',\n \" ? CodecTypes[CodecId]['output']\",\n ' : _Encoded;',\n ].join('\\n');\n },\n\n getTypeMapsExpression(): string {\n return 'TypeMapsType<CodecTypes, QueryOperationTypes, FieldOutputTypes, FieldInputTypes>';\n },\n\n getContractWrapper(contractBaseName: string, typeMapsName: string): string {\n return [\n `export type Contract = ContractWithTypeMaps<${contractBaseName}, ${typeMapsName}>;`,\n '',\n \"export type Namespaces = Contract['storage']['namespaces'];\",\n \"export type Models = Contract['models'];\",\n ].join('\\n');\n },\n} as const;\n\nfunction generateDocumentScopedStorageTypesType(types: SqlStorage['types']): string | undefined {\n if (!types || Object.keys(types).length === 0) {\n return undefined;\n }\n\n const typeEntries: string[] = [];\n for (const [typeName, typeInstance] of Object.entries(types)) {\n if (isPostgresEnumStorageEntry(typeInstance)) {\n throw new Error(\n `Document-scoped storage.types entry \"${typeName}\" is a postgres-enum; enums belong under storage.namespaces[namespaceId].types`,\n );\n }\n const codecInstanceShape = typeInstance as Partial<StorageTypeInstance>;\n if (\n typeof codecInstanceShape.codecId !== 'string' ||\n typeof codecInstanceShape.nativeType !== 'string'\n ) {\n throw new Error(\n `Unknown storage type kind for \"${typeName}\" in document-scoped storage.types; expected a codec-instance triple.`,\n );\n }\n const codecId = serializeValue(codecInstanceShape.codecId);\n const nativeType = serializeValue(codecInstanceShape.nativeType);\n const typeParamsStr = serializeTypeParamsLiteral(codecInstanceShape.typeParams);\n typeEntries.push(\n `readonly ${typeName}: { readonly kind: 'codec-instance'; readonly codecId: ${codecId}; readonly nativeType: ${nativeType}; readonly typeParams: ${typeParamsStr} }`,\n );\n }\n\n return `{ ${typeEntries.join('; ')} }`;\n}\n\nfunction generatePostgresNamespaceTypesType(\n types: Readonly<Record<string, PostgresEnumStorageEntry | StorageTypeInstance>>,\n): string {\n if (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 if (isPostgresEnumStorageEntry(typeInstance)) {\n const codecId = serializeValue(typeInstance.codecId);\n const nativeType = serializeValue(typeInstance.nativeType);\n const name = serializeValue(typeInstance.name);\n const valuesLiteral = typeInstance.values.map((v) => serializeValue(v)).join(', ');\n typeEntries.push(\n `readonly ${serializeObjectKey(typeName)}: { readonly kind: 'postgres-enum'; readonly name: ${name}; readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly values: readonly [${valuesLiteral}] }`,\n );\n continue;\n }\n throw new Error(\n `Unknown namespace storage type kind for \"${typeName}\"; expected postgres-enum in namespace.types.`,\n );\n }\n return `{ ${typeEntries.join('; ')} }`;\n}\n\nfunction isPostgresSchemaNamespace(ns: Namespace): ns is Namespace & {\n readonly types: Readonly<Record<string, PostgresEnumStorageEntry | StorageTypeInstance>>;\n} {\n return (\n (ns as { kind?: unknown }).kind === 'schema' &&\n 'types' in ns &&\n typeof (ns as { types?: unknown }).types === 'object' &&\n (ns as { types?: unknown }).types !== null\n );\n}\n\nfunction namespaceSerializedKind(ns: Namespace): string | undefined {\n const kind = (ns as { kind?: unknown }).kind;\n if (kind === 'schema') {\n const id = ns.id;\n const lit = id === UNBOUND_NAMESPACE_ID ? 'postgres-unbound-schema' : 'postgres-schema';\n return `readonly kind: '${lit}'`;\n }\n if (typeof kind === 'string') {\n return `readonly kind: ${serializeValue(kind)}`;\n }\n return undefined;\n}\n\nfunction generateTableLiteralType(table: StorageTable): string {\n const columns: string[] = [];\n for (const [colName, col] of Object.entries(table.columns)) {\n const nullable = col.nullable ? 'true' : 'false';\n const nativeType = serializeValue(col.nativeType);\n const codecId = serializeValue(col.codecId);\n const defaultSpec = col.default\n ? col.default.kind === 'literal'\n ? `; readonly default: { readonly kind: 'literal'; readonly value: DefaultLiteralValue<${codecId}, ${serializeValue(\n col.default.value,\n )}> }`\n : `; readonly default: { readonly kind: 'function'; readonly expression: ${serializeValue(\n col.default.expression,\n )} }`\n : '';\n const typeParamsSpec =\n col.typeParams && Object.keys(col.typeParams).length > 0\n ? `; readonly typeParams: ${serializeTypeParamsLiteral(col.typeParams)}`\n : '';\n const typeRefSpec = col.typeRef ? `; readonly typeRef: ${serializeValue(col.typeRef)}` : '';\n columns.push(\n `readonly ${colName}: { readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly nullable: ${nullable}${defaultSpec}${typeParamsSpec}${typeRefSpec} }`,\n );\n }\n\n const tableParts: string[] = [`columns: { ${columns.join('; ')} }`];\n\n if (table.primaryKey) {\n const pkCols = table.primaryKey.columns.map((c) => serializeValue(c)).join(', ');\n const pkName = table.primaryKey.name\n ? `; readonly name: ${serializeValue(table.primaryKey.name)}`\n : '';\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) => serializeValue(c)).join(', ');\n const name = u.name ? `; readonly name: ${serializeValue(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) => serializeValue(c)).join(', ');\n const name = i.name !== undefined ? `; readonly name: ${serializeValue(i.name)}` : '';\n const indexType = i.type !== undefined ? `; readonly type: ${serializeValue(i.type)}` : '';\n const indexOptions =\n i.options !== undefined ? `; readonly options: ${serializeValue(i.options)}` : '';\n return `{ readonly columns: readonly [${cols}]${name}${indexType}${indexOptions} }`;\n })\n .join(', ');\n tableParts.push(`indexes: readonly [${indexes}]`);\n\n const fks = table.foreignKeys\n .map((fk) => {\n const srcCols = fk.source.columns.map((c: string) => serializeValue(c)).join(', ');\n const tgtCols = fk.target.columns.map((c: string) => serializeValue(c)).join(', ');\n const name = fk.name ? `; readonly name: ${serializeValue(fk.name)}` : '';\n const srcRef = `{ readonly namespaceId: ${serializeValue(fk.source.namespaceId)}; readonly tableName: ${serializeValue(fk.source.tableName)}; readonly columns: readonly [${srcCols}] }`;\n const tgtRef = `{ readonly namespaceId: ${serializeValue(fk.target.namespaceId)}; readonly tableName: ${serializeValue(fk.target.tableName)}; readonly columns: readonly [${tgtCols}] }`;\n return `{ readonly source: ${srcRef}; readonly target: ${tgtRef}${name}; readonly constraint: ${fk.constraint}; readonly index: ${fk.index} }`;\n })\n .join(', ');\n tableParts.push(`foreignKeys: readonly [${fks}]`);\n\n return `{ ${tableParts.join('; ')} }`;\n}\n\nfunction generateTablesMapType(tables: Readonly<Record<string, StorageTable>>): string {\n const tableEntries: string[] = [];\n for (const [tableName, table] of Object.entries(tables).sort(([a], [b]) => a.localeCompare(b))) {\n tableEntries.push(`readonly ${tableName}: ${generateTableLiteralType(table)}`);\n }\n if (tableEntries.length === 0) {\n // Empty namespaces must emit `{}` (whose `keyof` is `never`), not\n // `Record<string, never>` (whose `keyof` is `string`). The latter\n // collapses `Db<C>` to a string-indexed shape and erases literal\n // table-name inference at every consumer site that walks all\n // namespaces (e.g. `db.sql.<tableName>`).\n return '{}';\n }\n return `{ ${tableEntries.join('; ')} }`;\n}\n\nfunction generateStorageNamespacesType(namespaces: SqlStorage['namespaces']): string {\n const entries = Object.entries(namespaces ?? {}).sort(([a], [b]) => a.localeCompare(b));\n if (entries.length === 0) {\n return 'Record<string, never>';\n }\n const parts: string[] = [];\n for (const [name, ns] of entries) {\n const kindPart = namespaceSerializedKind(ns);\n const kindSuffix = kindPart ? `; ${kindPart}` : '';\n const tablesType = generateTablesMapType(ns.tables as Readonly<Record<string, StorageTable>>);\n const isPg = isPostgresSchemaNamespace(ns);\n const typesClause = isPg\n ? `; readonly types: ${generatePostgresNamespaceTypesType(ns.types)}`\n : '';\n parts.push(\n `readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}${kindSuffix}; readonly tables: ${tablesType}${typesClause} }`,\n );\n }\n return `{ ${parts.join('; ')} }`;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "name": "@prisma-next/sql-contract-emitter",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "SQL emitter hook for Prisma Next",
8
8
  "dependencies": {
9
- "@prisma-next/contract": "0.9.0",
10
- "@prisma-next/emitter": "0.9.0",
11
- "@prisma-next/sql-contract": "0.9.0",
12
- "@prisma-next/utils": "0.9.0"
9
+ "@prisma-next/contract": "0.10.0",
10
+ "@prisma-next/emitter": "0.10.0",
11
+ "@prisma-next/framework-components": "0.10.0",
12
+ "@prisma-next/sql-contract": "0.10.0",
13
+ "@prisma-next/utils": "0.10.0"
13
14
  },
14
15
  "devDependencies": {
15
- "@prisma-next/framework-components": "0.9.0",
16
- "@prisma-next/test-utils": "0.9.0",
17
- "@prisma-next/tsconfig": "0.9.0",
18
- "@prisma-next/tsdown": "0.9.0",
16
+ "@prisma-next/test-utils": "0.10.0",
17
+ "@prisma-next/tsconfig": "0.10.0",
18
+ "@prisma-next/tsdown": "0.10.0",
19
19
  "tsdown": "0.22.0",
20
20
  "typescript": "5.9.3",
21
21
  "vitest": "4.1.6"
package/src/index.ts CHANGED
@@ -4,14 +4,15 @@ import type {
4
4
  GenerateContractTypesOptions,
5
5
  ValidationContext,
6
6
  } from '@prisma-next/framework-components/emission';
7
+ import { type Namespace, UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
7
8
  import {
8
9
  isPostgresEnumStorageEntry,
10
+ type PostgresEnumStorageEntry,
9
11
  type SqlModelStorage,
10
12
  type SqlStorage,
11
13
  type StorageTable,
12
14
  type StorageTypeInstance,
13
15
  } from '@prisma-next/sql-contract/types';
14
- import { assertDefined } from '@prisma-next/utils/assertions';
15
16
 
16
17
  function serializeTypeParamsLiteral(params: Record<string, unknown> | undefined): string {
17
18
  if (!params || Object.keys(params).length === 0) {
@@ -26,31 +27,70 @@ function serializeTypeParamsLiteral(params: Record<string, unknown> | undefined)
26
27
  return `{ ${entries.join('; ')} }`;
27
28
  }
28
29
 
30
+ /**
31
+ * Name-only lookup that walks every namespace until it finds a table
32
+ * with the given name. Survives this slice only for the two model-side
33
+ * call sites whose `SqlModelStorage.table` is still a bare name without
34
+ * a namespace coordinate; both call sites — and this helper — are
35
+ * deleted by TML-2584, which promotes `SqlModelStorage` to carry the
36
+ * model's namespace id so model→table resolution can use explicit
37
+ * coordinates like the FK-ref site already does.
38
+ */
39
+ function findSqlTable(
40
+ storage: SqlStorage,
41
+ tableName: string,
42
+ ): { readonly table: StorageTable; readonly namespaceId: string } | undefined {
43
+ for (const [namespaceId, ns] of Object.entries(storage.namespaces)) {
44
+ const table = ns.tables[tableName] as StorageTable | undefined;
45
+ if (table !== undefined) {
46
+ return { table, namespaceId };
47
+ }
48
+ }
49
+ return undefined;
50
+ }
51
+
52
+ function assertUniqueSqlTableNames(storage: SqlStorage): void {
53
+ const seen = new Map<string, string>();
54
+ for (const [namespaceId, ns] of Object.entries(storage.namespaces)) {
55
+ for (const tableName of Object.keys(ns.tables)) {
56
+ const existing = seen.get(tableName);
57
+ if (existing !== undefined && existing !== namespaceId) {
58
+ throw new Error(
59
+ `Duplicate table name "${tableName}" in namespaces "${existing}" and "${namespaceId}"`,
60
+ );
61
+ }
62
+ seen.set(tableName, namespaceId);
63
+ }
64
+ }
65
+ }
66
+
29
67
  export const sqlEmission = {
30
68
  id: 'sql',
31
69
 
32
70
  validateTypes(contract: Contract, _ctx: ValidationContext): void {
33
71
  const storage = contract.storage as unknown as SqlStorage | undefined;
34
- if (!storage?.tables) {
72
+ if (!storage?.namespaces) {
35
73
  return;
36
74
  }
37
75
 
38
76
  const typeIdRegex = /^([^/]+)\/([^@]+)@(\d+)$/;
39
77
 
40
- for (const [tableName, tableUnknown] of Object.entries(storage.tables)) {
41
- const table = tableUnknown as StorageTable;
42
- for (const [colName, colUnknown] of Object.entries(table.columns)) {
43
- const col = colUnknown as { codecId?: string };
44
- const codecId = col.codecId;
45
- if (!codecId) {
46
- throw new Error(`Column "${colName}" in table "${tableName}" is missing codecId`);
47
- }
78
+ for (const ns of Object.values(storage.namespaces)) {
79
+ for (const [tableName, tableUnknown] of Object.entries(ns.tables)) {
80
+ const table = tableUnknown as StorageTable;
81
+ for (const [colName, colUnknown] of Object.entries(table.columns)) {
82
+ const col = colUnknown as { codecId?: string };
83
+ const codecId = col.codecId;
84
+ if (!codecId) {
85
+ throw new Error(`Column "${colName}" in table "${tableName}" is missing codecId`);
86
+ }
48
87
 
49
- const match = codecId.match(typeIdRegex);
50
- if (!match?.[1]) {
51
- throw new Error(
52
- `Column "${colName}" in table "${tableName}" has invalid codec ID format "${codecId}". Expected format: ns/name@version`,
53
- );
88
+ const match = codecId.match(typeIdRegex);
89
+ if (!match?.[1]) {
90
+ throw new Error(
91
+ `Column "${colName}" in table "${tableName}" has invalid codec ID format "${codecId}". Expected format: ns/name@version`,
92
+ );
93
+ }
54
94
  }
55
95
  }
56
96
  }
@@ -62,12 +102,19 @@ export const sqlEmission = {
62
102
  }
63
103
 
64
104
  const storage = contract.storage as unknown as SqlStorage | undefined;
65
- if (!storage?.tables) {
66
- throw new Error('SQL contract must have storage.tables');
105
+ if (!storage?.namespaces) {
106
+ throw new Error('SQL contract must have storage.namespaces');
67
107
  }
68
108
 
109
+ assertUniqueSqlTableNames(storage);
110
+
69
111
  const models = contract.models as Record<string, ContractModel<SqlModelStorage>>;
70
- const tableNames = new Set(Object.keys(storage.tables));
112
+ const tableNames = new Set<string>();
113
+ for (const ns of Object.values(storage.namespaces)) {
114
+ for (const t of Object.keys(ns.tables)) {
115
+ tableNames.add(t);
116
+ }
117
+ }
71
118
 
72
119
  if (models) {
73
120
  for (const [modelName, model] of Object.entries(models)) {
@@ -76,13 +123,12 @@ export const sqlEmission = {
76
123
  }
77
124
 
78
125
  const tableName = model.storage.table;
79
- if (!tableNames.has(tableName)) {
126
+ const located = findSqlTable(storage, tableName);
127
+ if (!located) {
80
128
  throw new Error(`Model "${modelName}" references non-existent table "${tableName}"`);
81
129
  }
82
130
 
83
- const table: StorageTable | undefined = storage.tables[tableName];
84
- assertDefined(table, `Model "${modelName}" references non-existent table "${tableName}"`);
85
-
131
+ const { table } = located;
86
132
  const columnNames = new Set(Object.keys(table.columns));
87
133
  const storageFields = model.storage.fields;
88
134
  if (!storageFields || Object.keys(storageFields).length === 0) {
@@ -109,175 +155,100 @@ export const sqlEmission = {
109
155
  }
110
156
  }
111
157
 
112
- for (const [tableName, tableUnknown] of Object.entries(storage.tables)) {
113
- const table = tableUnknown as StorageTable;
114
- const columnNames = new Set(Object.keys(table.columns));
158
+ for (const ns of Object.values(storage.namespaces)) {
159
+ for (const [tableName, tableUnknown] of Object.entries(ns.tables)) {
160
+ const table = tableUnknown as StorageTable;
161
+ const columnNames = new Set(Object.keys(table.columns));
115
162
 
116
- if (!Array.isArray(table.uniques)) {
117
- throw new Error(
118
- `Table "${tableName}" is missing required field "uniques" (must be an array)`,
119
- );
120
- }
121
- if (!Array.isArray(table.indexes)) {
122
- throw new Error(
123
- `Table "${tableName}" is missing required field "indexes" (must be an array)`,
124
- );
125
- }
126
- if (!Array.isArray(table.foreignKeys)) {
127
- throw new Error(
128
- `Table "${tableName}" is missing required field "foreignKeys" (must be an array)`,
129
- );
130
- }
163
+ if (!Array.isArray(table.uniques)) {
164
+ throw new Error(
165
+ `Table "${tableName}" is missing required field "uniques" (must be an array)`,
166
+ );
167
+ }
168
+ if (!Array.isArray(table.indexes)) {
169
+ throw new Error(
170
+ `Table "${tableName}" is missing required field "indexes" (must be an array)`,
171
+ );
172
+ }
173
+ if (!Array.isArray(table.foreignKeys)) {
174
+ throw new Error(
175
+ `Table "${tableName}" is missing required field "foreignKeys" (must be an array)`,
176
+ );
177
+ }
131
178
 
132
- if (table.primaryKey) {
133
- for (const colName of table.primaryKey.columns) {
134
- if (!columnNames.has(colName)) {
135
- throw new Error(
136
- `Table "${tableName}" primaryKey references non-existent column "${colName}"`,
137
- );
179
+ if (table.primaryKey) {
180
+ for (const colName of table.primaryKey.columns) {
181
+ if (!columnNames.has(colName)) {
182
+ throw new Error(
183
+ `Table "${tableName}" primaryKey references non-existent column "${colName}"`,
184
+ );
185
+ }
138
186
  }
139
187
  }
140
- }
141
188
 
142
- for (const unique of table.uniques) {
143
- for (const colName of unique.columns) {
144
- if (!columnNames.has(colName)) {
145
- throw new Error(
146
- `Table "${tableName}" unique constraint references non-existent column "${colName}"`,
147
- );
189
+ for (const unique of table.uniques) {
190
+ for (const colName of unique.columns) {
191
+ if (!columnNames.has(colName)) {
192
+ throw new Error(
193
+ `Table "${tableName}" unique constraint references non-existent column "${colName}"`,
194
+ );
195
+ }
148
196
  }
149
197
  }
150
- }
151
198
 
152
- for (const index of table.indexes) {
153
- for (const colName of index.columns) {
154
- if (!columnNames.has(colName)) {
155
- throw new Error(
156
- `Table "${tableName}" index references non-existent column "${colName}"`,
157
- );
199
+ for (const index of table.indexes) {
200
+ for (const colName of index.columns) {
201
+ if (!columnNames.has(colName)) {
202
+ throw new Error(
203
+ `Table "${tableName}" index references non-existent column "${colName}"`,
204
+ );
205
+ }
158
206
  }
159
207
  }
160
- }
161
208
 
162
- for (const fk of table.foreignKeys) {
163
- for (const colName of fk.columns) {
164
- if (!columnNames.has(colName)) {
209
+ for (const fk of table.foreignKeys) {
210
+ for (const colName of fk.source.columns) {
211
+ if (!columnNames.has(colName)) {
212
+ throw new Error(
213
+ `Table "${tableName}" foreignKey references non-existent column "${colName}"`,
214
+ );
215
+ }
216
+ }
217
+
218
+ const referencedTable = storage.namespaces[fk.target.namespaceId]?.tables[
219
+ fk.target.tableName
220
+ ] as StorageTable | undefined;
221
+ if (!referencedTable) {
165
222
  throw new Error(
166
- `Table "${tableName}" foreignKey references non-existent column "${colName}"`,
223
+ `Table "${tableName}" foreignKey references non-existent table "${fk.target.tableName}" in namespace "${fk.target.namespaceId}"`,
167
224
  );
168
225
  }
169
- }
170
-
171
- if (!tableNames.has(fk.references.table)) {
172
- throw new Error(
173
- `Table "${tableName}" foreignKey references non-existent table "${fk.references.table}"`,
174
- );
175
- }
176
226
 
177
- const referencedTable: StorageTable | undefined = storage.tables[fk.references.table];
178
- assertDefined(
179
- referencedTable,
180
- `Table "${tableName}" foreignKey references non-existent table "${fk.references.table}"`,
181
- );
227
+ const referencedColumnNames = new Set(Object.keys(referencedTable.columns));
228
+ for (const colName of fk.target.columns) {
229
+ if (!referencedColumnNames.has(colName)) {
230
+ throw new Error(
231
+ `Table "${tableName}" foreignKey references non-existent column "${colName}" in table "${fk.target.tableName}"`,
232
+ );
233
+ }
234
+ }
182
235
 
183
- const referencedColumnNames = new Set(Object.keys(referencedTable.columns));
184
- for (const colName of fk.references.columns) {
185
- if (!referencedColumnNames.has(colName)) {
236
+ if (fk.source.columns.length !== fk.target.columns.length) {
186
237
  throw new Error(
187
- `Table "${tableName}" foreignKey references non-existent column "${colName}" in table "${fk.references.table}"`,
238
+ `Table "${tableName}" foreignKey column count (${fk.source.columns.length}) does not match referenced column count (${fk.target.columns.length})`,
188
239
  );
189
240
  }
190
241
  }
191
-
192
- if (fk.columns.length !== fk.references.columns.length) {
193
- throw new Error(
194
- `Table "${tableName}" foreignKey column count (${fk.columns.length}) does not match referenced column count (${fk.references.columns.length})`,
195
- );
196
- }
197
242
  }
198
243
  }
199
244
  },
200
245
 
201
246
  generateStorageType(contract: Contract, storageHashTypeName: string): string {
202
247
  const storage = contract.storage as unknown as SqlStorage;
203
- const tables: string[] = [];
204
- for (const [tableName, table] of Object.entries(storage.tables).sort(([a], [b]) =>
205
- a.localeCompare(b),
206
- )) {
207
- const columns: string[] = [];
208
- for (const [colName, col] of Object.entries(table.columns)) {
209
- const nullable = col.nullable ? 'true' : 'false';
210
- const nativeType = serializeValue(col.nativeType);
211
- const codecId = serializeValue(col.codecId);
212
- const defaultSpec = col.default
213
- ? col.default.kind === 'literal'
214
- ? `; readonly default: { readonly kind: 'literal'; readonly value: DefaultLiteralValue<${codecId}, ${serializeValue(
215
- col.default.value,
216
- )}> }`
217
- : `; readonly default: { readonly kind: 'function'; readonly expression: ${serializeValue(
218
- col.default.expression,
219
- )} }`
220
- : '';
221
- const typeParamsSpec =
222
- col.typeParams && Object.keys(col.typeParams).length > 0
223
- ? `; readonly typeParams: ${serializeTypeParamsLiteral(col.typeParams)}`
224
- : '';
225
- const typeRefSpec = col.typeRef ? `; readonly typeRef: ${serializeValue(col.typeRef)}` : '';
226
- columns.push(
227
- `readonly ${colName}: { readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly nullable: ${nullable}${defaultSpec}${typeParamsSpec}${typeRefSpec} }`,
228
- );
229
- }
230
-
231
- const tableParts: string[] = [`columns: { ${columns.join('; ')} }`];
232
-
233
- if (table.primaryKey) {
234
- const pkCols = table.primaryKey.columns.map((c) => serializeValue(c)).join(', ');
235
- const pkName = table.primaryKey.name
236
- ? `; readonly name: ${serializeValue(table.primaryKey.name)}`
237
- : '';
238
- tableParts.push(`primaryKey: { readonly columns: readonly [${pkCols}]${pkName} }`);
239
- }
240
-
241
- const uniques = table.uniques
242
- .map((u) => {
243
- const cols = u.columns.map((c: string) => serializeValue(c)).join(', ');
244
- const name = u.name ? `; readonly name: ${serializeValue(u.name)}` : '';
245
- return `{ readonly columns: readonly [${cols}]${name} }`;
246
- })
247
- .join(', ');
248
- tableParts.push(`uniques: readonly [${uniques}]`);
249
-
250
- const indexes = table.indexes
251
- .map((i) => {
252
- const cols = i.columns.map((c: string) => serializeValue(c)).join(', ');
253
- const name = i.name !== undefined ? `; readonly name: ${serializeValue(i.name)}` : '';
254
- const indexType =
255
- i.type !== undefined ? `; readonly type: ${serializeValue(i.type)}` : '';
256
- const indexOptions =
257
- i.options !== undefined ? `; readonly options: ${serializeValue(i.options)}` : '';
258
- return `{ readonly columns: readonly [${cols}]${name}${indexType}${indexOptions} }`;
259
- })
260
- .join(', ');
261
- tableParts.push(`indexes: readonly [${indexes}]`);
262
-
263
- const fks = table.foreignKeys
264
- .map((fk) => {
265
- const cols = fk.columns.map((c: string) => serializeValue(c)).join(', ');
266
- const refCols = fk.references.columns.map((c: string) => serializeValue(c)).join(', ');
267
- const name = fk.name ? `; readonly name: ${serializeValue(fk.name)}` : '';
268
- return `{ readonly columns: readonly [${cols}]; readonly references: { readonly table: ${serializeValue(fk.references.table)}; readonly columns: readonly [${refCols}] }${name}; readonly constraint: ${fk.constraint}; readonly index: ${fk.index} }`;
269
- })
270
- .join(', ');
271
- tableParts.push(`foreignKeys: readonly [${fks}]`);
272
-
273
- tables.push(`readonly ${tableName}: { ${tableParts.join('; ')} }`);
274
- }
275
-
276
- const typesType = generateStorageTypesType(storage.types);
277
-
278
248
  const namespacesType = generateStorageNamespacesType(storage.namespaces);
279
-
280
- return `{ readonly tables: { ${tables.join('; ')} }; readonly types: ${typesType}; readonly namespaces: ${namespacesType}; readonly storageHash: ${storageHashTypeName} }`;
249
+ const docTypes = generateDocumentScopedStorageTypesType(storage.types);
250
+ const typesClause = docTypes === undefined ? '' : `; readonly types: ${docTypes}`;
251
+ return `{ readonly namespaces: ${namespacesType}${typesClause}; readonly storageHash: ${storageHashTypeName} }`;
281
252
  },
282
253
 
283
254
  generateModelStorageType(_modelName: string, model: ContractModel): string {
@@ -313,22 +284,29 @@ export const sqlEmission = {
313
284
  if (!storage) return undefined;
314
285
 
315
286
  const tableName = sqlModel.storage.table;
316
- const table: StorageTable | undefined = storage.tables[tableName];
317
- if (!table) return undefined;
287
+ const located = findSqlTable(storage, tableName);
288
+ if (!located) return undefined;
318
289
 
319
- const column = table.columns[storageField.column];
290
+ const column = located.table.columns[storageField.column];
320
291
  if (!column) return undefined;
321
292
 
322
293
  if (column.typeRef) {
323
- const typeInstance = storage.types?.[column.typeRef];
294
+ const ns = storage.namespaces[located.namespaceId];
295
+ const nsTypes =
296
+ ns !== undefined && 'types' in ns
297
+ ? (
298
+ ns as {
299
+ types?: Readonly<Record<string, PostgresEnumStorageEntry | StorageTypeInstance>>;
300
+ }
301
+ ).types
302
+ : undefined;
303
+ const fromNamespace = nsTypes?.[column.typeRef];
304
+ const fromDocument = storage.types?.[column.typeRef];
305
+ const typeInstance = fromNamespace ?? fromDocument;
324
306
  if (typeInstance === undefined) return undefined;
325
307
  if (isPostgresEnumStorageEntry(typeInstance)) {
326
308
  return { values: typeInstance.values };
327
309
  }
328
- // Fall back to structural codec-triple access when the literal
329
- // bypasses the runtime normaliser (e.g. test fixtures or
330
- // hand-written descriptor inputs that omit the
331
- // `kind: 'codec-instance'` discriminator).
332
310
  const codecShape = typeInstance as Partial<StorageTypeInstance>;
333
311
  return codecShape.typeParams;
334
312
  }
@@ -372,55 +350,31 @@ export const sqlEmission = {
372
350
  return [
373
351
  `export type Contract = ContractWithTypeMaps<${contractBaseName}, ${typeMapsName}>;`,
374
352
  '',
375
- "export type Tables = Contract['storage']['tables'];",
353
+ "export type Namespaces = Contract['storage']['namespaces'];",
376
354
  "export type Models = Contract['models'];",
377
355
  ].join('\n');
378
356
  },
379
357
  } as const;
380
358
 
381
- function generateStorageTypesType(types: SqlStorage['types']): string {
359
+ function generateDocumentScopedStorageTypesType(types: SqlStorage['types']): string | undefined {
382
360
  if (!types || Object.keys(types).length === 0) {
383
- return 'Record<string, never>';
361
+ return undefined;
384
362
  }
385
363
 
386
364
  const typeEntries: string[] = [];
387
365
  for (const [typeName, typeInstance] of Object.entries(types)) {
388
366
  if (isPostgresEnumStorageEntry(typeInstance)) {
389
- const codecId = serializeValue(
390
- // `codecBinding.codecId` lives on the live IR-class instance;
391
- // raw JSON envelopes carry `codecId` as an enumerable own
392
- // property. Read the structural-shape field so the emitter
393
- // works against both runtime forms.
394
- typeInstance.codecId,
395
- );
396
- const nativeType = serializeValue(typeInstance.nativeType);
397
- const typeParamsStr = serializeTypeParamsLiteral({
398
- values: typeInstance.values as unknown as readonly unknown[],
399
- });
400
- // Emit the resolved codec view (kind: 'codec-instance') so the
401
- // emitted .d.ts shape stays uniform across slot variants and
402
- // satisfies the polymorphic slot's structural alphabet. The
403
- // persisted JSON envelope still carries the IR's narrower kind
404
- // discriminator; the d.ts is the type-level codec-resolved view.
405
- typeEntries.push(
406
- `readonly ${typeName}: { readonly kind: 'codec-instance'; readonly codecId: ${codecId}; readonly nativeType: ${nativeType}; readonly typeParams: ${typeParamsStr} }`,
367
+ throw new Error(
368
+ `Document-scoped storage.types entry "${typeName}" is a postgres-enum; enums belong under storage.namespaces[namespaceId].types`,
407
369
  );
408
- continue;
409
370
  }
410
- // The slot is polymorphic at the framework level; codec-instance
411
- // entries are the only non-IR-class kind today. The runtime
412
- // `SqlStorage` constructor stamps `kind: 'codec-instance'` on
413
- // plain codec triples; the emitter is forgiving about literal
414
- // inputs that bypass the constructor (test fixtures, hand-written
415
- // descriptors) and treats anything with the codec-triple shape as
416
- // a codec-instance.
417
371
  const codecInstanceShape = typeInstance as Partial<StorageTypeInstance>;
418
372
  if (
419
373
  typeof codecInstanceShape.codecId !== 'string' ||
420
374
  typeof codecInstanceShape.nativeType !== 'string'
421
375
  ) {
422
376
  throw new Error(
423
- `Unknown storage type kind for "${typeName}"; expected a codec-instance triple or a known IR-class kind.`,
377
+ `Unknown storage type kind for "${typeName}" in document-scoped storage.types; expected a codec-instance triple.`,
424
378
  );
425
379
  }
426
380
  const codecId = serializeValue(codecInstanceShape.codecId);
@@ -434,6 +388,143 @@ function generateStorageTypesType(types: SqlStorage['types']): string {
434
388
  return `{ ${typeEntries.join('; ')} }`;
435
389
  }
436
390
 
391
+ function generatePostgresNamespaceTypesType(
392
+ types: Readonly<Record<string, PostgresEnumStorageEntry | StorageTypeInstance>>,
393
+ ): string {
394
+ if (Object.keys(types).length === 0) {
395
+ return 'Record<string, never>';
396
+ }
397
+
398
+ const typeEntries: string[] = [];
399
+ for (const [typeName, typeInstance] of Object.entries(types)) {
400
+ if (isPostgresEnumStorageEntry(typeInstance)) {
401
+ const codecId = serializeValue(typeInstance.codecId);
402
+ const nativeType = serializeValue(typeInstance.nativeType);
403
+ const name = serializeValue(typeInstance.name);
404
+ const valuesLiteral = typeInstance.values.map((v) => serializeValue(v)).join(', ');
405
+ typeEntries.push(
406
+ `readonly ${serializeObjectKey(typeName)}: { readonly kind: 'postgres-enum'; readonly name: ${name}; readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly values: readonly [${valuesLiteral}] }`,
407
+ );
408
+ continue;
409
+ }
410
+ throw new Error(
411
+ `Unknown namespace storage type kind for "${typeName}"; expected postgres-enum in namespace.types.`,
412
+ );
413
+ }
414
+ return `{ ${typeEntries.join('; ')} }`;
415
+ }
416
+
417
+ function isPostgresSchemaNamespace(ns: Namespace): ns is Namespace & {
418
+ readonly types: Readonly<Record<string, PostgresEnumStorageEntry | StorageTypeInstance>>;
419
+ } {
420
+ return (
421
+ (ns as { kind?: unknown }).kind === 'schema' &&
422
+ 'types' in ns &&
423
+ typeof (ns as { types?: unknown }).types === 'object' &&
424
+ (ns as { types?: unknown }).types !== null
425
+ );
426
+ }
427
+
428
+ function namespaceSerializedKind(ns: Namespace): string | undefined {
429
+ const kind = (ns as { kind?: unknown }).kind;
430
+ if (kind === 'schema') {
431
+ const id = ns.id;
432
+ const lit = id === UNBOUND_NAMESPACE_ID ? 'postgres-unbound-schema' : 'postgres-schema';
433
+ return `readonly kind: '${lit}'`;
434
+ }
435
+ if (typeof kind === 'string') {
436
+ return `readonly kind: ${serializeValue(kind)}`;
437
+ }
438
+ return undefined;
439
+ }
440
+
441
+ function generateTableLiteralType(table: StorageTable): string {
442
+ const columns: string[] = [];
443
+ for (const [colName, col] of Object.entries(table.columns)) {
444
+ const nullable = col.nullable ? 'true' : 'false';
445
+ const nativeType = serializeValue(col.nativeType);
446
+ const codecId = serializeValue(col.codecId);
447
+ const defaultSpec = col.default
448
+ ? col.default.kind === 'literal'
449
+ ? `; readonly default: { readonly kind: 'literal'; readonly value: DefaultLiteralValue<${codecId}, ${serializeValue(
450
+ col.default.value,
451
+ )}> }`
452
+ : `; readonly default: { readonly kind: 'function'; readonly expression: ${serializeValue(
453
+ col.default.expression,
454
+ )} }`
455
+ : '';
456
+ const typeParamsSpec =
457
+ col.typeParams && Object.keys(col.typeParams).length > 0
458
+ ? `; readonly typeParams: ${serializeTypeParamsLiteral(col.typeParams)}`
459
+ : '';
460
+ const typeRefSpec = col.typeRef ? `; readonly typeRef: ${serializeValue(col.typeRef)}` : '';
461
+ columns.push(
462
+ `readonly ${colName}: { readonly nativeType: ${nativeType}; readonly codecId: ${codecId}; readonly nullable: ${nullable}${defaultSpec}${typeParamsSpec}${typeRefSpec} }`,
463
+ );
464
+ }
465
+
466
+ const tableParts: string[] = [`columns: { ${columns.join('; ')} }`];
467
+
468
+ if (table.primaryKey) {
469
+ const pkCols = table.primaryKey.columns.map((c) => serializeValue(c)).join(', ');
470
+ const pkName = table.primaryKey.name
471
+ ? `; readonly name: ${serializeValue(table.primaryKey.name)}`
472
+ : '';
473
+ tableParts.push(`primaryKey: { readonly columns: readonly [${pkCols}]${pkName} }`);
474
+ }
475
+
476
+ const uniques = table.uniques
477
+ .map((u) => {
478
+ const cols = u.columns.map((c: string) => serializeValue(c)).join(', ');
479
+ const name = u.name ? `; readonly name: ${serializeValue(u.name)}` : '';
480
+ return `{ readonly columns: readonly [${cols}]${name} }`;
481
+ })
482
+ .join(', ');
483
+ tableParts.push(`uniques: readonly [${uniques}]`);
484
+
485
+ const indexes = table.indexes
486
+ .map((i) => {
487
+ const cols = i.columns.map((c: string) => serializeValue(c)).join(', ');
488
+ const name = i.name !== undefined ? `; readonly name: ${serializeValue(i.name)}` : '';
489
+ const indexType = i.type !== undefined ? `; readonly type: ${serializeValue(i.type)}` : '';
490
+ const indexOptions =
491
+ i.options !== undefined ? `; readonly options: ${serializeValue(i.options)}` : '';
492
+ return `{ readonly columns: readonly [${cols}]${name}${indexType}${indexOptions} }`;
493
+ })
494
+ .join(', ');
495
+ tableParts.push(`indexes: readonly [${indexes}]`);
496
+
497
+ const fks = table.foreignKeys
498
+ .map((fk) => {
499
+ const srcCols = fk.source.columns.map((c: string) => serializeValue(c)).join(', ');
500
+ const tgtCols = fk.target.columns.map((c: string) => serializeValue(c)).join(', ');
501
+ const name = fk.name ? `; readonly name: ${serializeValue(fk.name)}` : '';
502
+ const srcRef = `{ readonly namespaceId: ${serializeValue(fk.source.namespaceId)}; readonly tableName: ${serializeValue(fk.source.tableName)}; readonly columns: readonly [${srcCols}] }`;
503
+ const tgtRef = `{ readonly namespaceId: ${serializeValue(fk.target.namespaceId)}; readonly tableName: ${serializeValue(fk.target.tableName)}; readonly columns: readonly [${tgtCols}] }`;
504
+ return `{ readonly source: ${srcRef}; readonly target: ${tgtRef}${name}; readonly constraint: ${fk.constraint}; readonly index: ${fk.index} }`;
505
+ })
506
+ .join(', ');
507
+ tableParts.push(`foreignKeys: readonly [${fks}]`);
508
+
509
+ return `{ ${tableParts.join('; ')} }`;
510
+ }
511
+
512
+ function generateTablesMapType(tables: Readonly<Record<string, StorageTable>>): string {
513
+ const tableEntries: string[] = [];
514
+ for (const [tableName, table] of Object.entries(tables).sort(([a], [b]) => a.localeCompare(b))) {
515
+ tableEntries.push(`readonly ${tableName}: ${generateTableLiteralType(table)}`);
516
+ }
517
+ if (tableEntries.length === 0) {
518
+ // Empty namespaces must emit `{}` (whose `keyof` is `never`), not
519
+ // `Record<string, never>` (whose `keyof` is `string`). The latter
520
+ // collapses `Db<C>` to a string-indexed shape and erases literal
521
+ // table-name inference at every consumer site that walks all
522
+ // namespaces (e.g. `db.sql.<tableName>`).
523
+ return '{}';
524
+ }
525
+ return `{ ${tableEntries.join('; ')} }`;
526
+ }
527
+
437
528
  function generateStorageNamespacesType(namespaces: SqlStorage['namespaces']): string {
438
529
  const entries = Object.entries(namespaces ?? {}).sort(([a], [b]) => a.localeCompare(b));
439
530
  if (entries.length === 0) {
@@ -441,7 +532,16 @@ function generateStorageNamespacesType(namespaces: SqlStorage['namespaces']): st
441
532
  }
442
533
  const parts: string[] = [];
443
534
  for (const [name, ns] of entries) {
444
- parts.push(`readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)} }`);
535
+ const kindPart = namespaceSerializedKind(ns);
536
+ const kindSuffix = kindPart ? `; ${kindPart}` : '';
537
+ const tablesType = generateTablesMapType(ns.tables as Readonly<Record<string, StorageTable>>);
538
+ const isPg = isPostgresSchemaNamespace(ns);
539
+ const typesClause = isPg
540
+ ? `; readonly types: ${generatePostgresNamespaceTypesType(ns.types)}`
541
+ : '';
542
+ parts.push(
543
+ `readonly ${serializeObjectKey(name)}: { readonly id: ${serializeValue(ns.id)}${kindSuffix}; readonly tables: ${tablesType}${typesClause} }`,
544
+ );
445
545
  }
446
546
  return `{ ${parts.join('; ')} }`;
447
547
  }