@prisma-next/family-sql 0.14.0-dev.4 → 0.14.0-dev.40

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.
Files changed (66) hide show
  1. package/dist/{authoring-type-constructors-CjFfO6LM.mjs → authoring-type-constructors-CXd-8ydc.mjs} +7 -30
  2. package/dist/authoring-type-constructors-CXd-8ydc.mjs.map +1 -0
  3. package/dist/contract-to-schema-ir-S-evq8E6.mjs +264 -0
  4. package/dist/contract-to-schema-ir-S-evq8E6.mjs.map +1 -0
  5. package/dist/{control-adapter-Cmw9LvEP.d.mts → control-adapter-DHYFuOBy.d.mts} +12 -7
  6. package/dist/{control-adapter-Cmw9LvEP.d.mts.map → control-adapter-DHYFuOBy.d.mts.map} +1 -1
  7. package/dist/control-adapter.d.mts +1 -1
  8. package/dist/control.d.mts +18 -3
  9. package/dist/control.d.mts.map +1 -1
  10. package/dist/control.mjs +81 -943
  11. package/dist/control.mjs.map +1 -1
  12. package/dist/{schema-verify.d.mts → diff.d.mts} +5 -5
  13. package/dist/diff.d.mts.map +1 -0
  14. package/dist/{verify-sql-schema-xT4udQLQ.mjs → diff.mjs} +169 -253
  15. package/dist/diff.mjs.map +1 -0
  16. package/dist/ir.d.mts +9 -6
  17. package/dist/ir.d.mts.map +1 -1
  18. package/dist/ir.mjs +1 -1
  19. package/dist/migration.d.mts +11 -2
  20. package/dist/migration.d.mts.map +1 -1
  21. package/dist/migration.mjs +7 -0
  22. package/dist/migration.mjs.map +1 -1
  23. package/dist/pack.mjs +1 -1
  24. package/dist/psl-infer.d.mts +80 -0
  25. package/dist/psl-infer.d.mts.map +1 -0
  26. package/dist/psl-infer.mjs +334 -0
  27. package/dist/psl-infer.mjs.map +1 -0
  28. package/dist/{sql-contract-serializer-BR2vC7Z-.mjs → sql-contract-serializer-CYtXg_zs.mjs} +17 -20
  29. package/dist/sql-contract-serializer-CYtXg_zs.mjs.map +1 -0
  30. package/dist/{verify-sql-schema-thU-jKpf.d.mts → sql-schema-diff-6z36dZt6.d.mts} +44 -5
  31. package/dist/sql-schema-diff-6z36dZt6.d.mts.map +1 -0
  32. package/dist/{types-kgstZ_Zd.d.mts → types-CkOIJXxU.d.mts} +84 -17
  33. package/dist/types-CkOIJXxU.d.mts.map +1 -0
  34. package/package.json +23 -23
  35. package/src/core/authoring-entity-types.ts +12 -37
  36. package/src/core/control-adapter.ts +10 -5
  37. package/src/core/control-instance.ts +174 -41
  38. package/src/core/{schema-verify/verify-sql-schema.ts → diff/sql-schema-diff.ts} +208 -16
  39. package/src/core/{schema-verify → diff}/verify-helpers.ts +12 -0
  40. package/src/core/ir/sql-contract-serializer-base.ts +23 -55
  41. package/src/core/ir/sql-contract-serializer.ts +5 -7
  42. package/src/core/migrations/contract-to-schema-ir.ts +49 -12
  43. package/src/core/migrations/field-event-planner.ts +2 -2
  44. package/src/core/migrations/schema-differ.ts +40 -0
  45. package/src/core/migrations/types.ts +43 -2
  46. package/src/core/psl-contract-infer/printer-config.ts +0 -6
  47. package/src/core/sql-migration.ts +12 -1
  48. package/src/exports/control.ts +1 -0
  49. package/src/exports/diff.ts +18 -0
  50. package/src/exports/psl-infer.ts +36 -0
  51. package/dist/authoring-type-constructors-CjFfO6LM.mjs.map +0 -1
  52. package/dist/schema-verify.d.mts.map +0 -1
  53. package/dist/schema-verify.mjs +0 -2
  54. package/dist/sql-contract-serializer-BR2vC7Z-.mjs.map +0 -1
  55. package/dist/test-utils.d.mts +0 -2
  56. package/dist/test-utils.mjs +0 -2
  57. package/dist/types-kgstZ_Zd.d.mts.map +0 -1
  58. package/dist/verify-sql-schema-thU-jKpf.d.mts.map +0 -1
  59. package/dist/verify-sql-schema-xT4udQLQ.mjs.map +0 -1
  60. package/src/core/psl-contract-infer/postgres-default-mapping.ts +0 -16
  61. package/src/core/psl-contract-infer/postgres-type-map.ts +0 -157
  62. package/src/core/psl-contract-infer/sql-schema-ir-to-psl-ast.ts +0 -795
  63. package/src/exports/schema-verify.ts +0 -18
  64. package/src/exports/test-utils.ts +0 -9
  65. /package/src/core/{schema-verify → diff}/control-verify-emit.ts +0 -0
  66. /package/src/core/{schema-verify → diff}/verifier-disposition.ts +0 -0
@@ -0,0 +1,334 @@
1
+ //#region src/core/psl-contract-infer/default-mapping.ts
2
+ const DEFAULT_FUNCTION_ATTRIBUTES = {
3
+ "autoincrement()": "@default(autoincrement())",
4
+ "now()": "@default(now())"
5
+ };
6
+ function mapDefault(columnDefault, options) {
7
+ switch (columnDefault.kind) {
8
+ case "literal": return { attribute: `@default(${formatLiteralValue(columnDefault.value)})` };
9
+ case "function": {
10
+ const attribute = options?.functionAttributes?.[columnDefault.expression] ?? DEFAULT_FUNCTION_ATTRIBUTES[columnDefault.expression] ?? options?.fallbackFunctionAttribute?.(columnDefault.expression);
11
+ return attribute ? { attribute } : { comment: `// Raw default: ${columnDefault.expression.replace(/[\r\n]+/g, " ")}` };
12
+ }
13
+ }
14
+ }
15
+ function formatLiteralValue(value) {
16
+ if (value === null) return "null";
17
+ switch (typeof value) {
18
+ case "boolean":
19
+ case "number": return String(value);
20
+ case "string": return quoteString(value);
21
+ default: return quoteString(JSON.stringify(value));
22
+ }
23
+ }
24
+ function quoteString(str) {
25
+ return `"${escapeString(str)}"`;
26
+ }
27
+ function escapeString(str) {
28
+ return JSON.stringify(str).slice(1, -1);
29
+ }
30
+ //#endregion
31
+ //#region src/core/psl-contract-infer/name-transforms.ts
32
+ const PSL_RESERVED_WORDS = new Set([
33
+ "model",
34
+ "enum",
35
+ "types",
36
+ "type",
37
+ "generator",
38
+ "datasource"
39
+ ]);
40
+ const IDENTIFIER_PART_PATTERN = /[A-Za-z0-9]+/g;
41
+ function hasSeparators(input) {
42
+ return /[^A-Za-z0-9]/.test(input);
43
+ }
44
+ function extractIdentifierParts(input) {
45
+ return input.match(IDENTIFIER_PART_PATTERN) ?? [];
46
+ }
47
+ function createSyntheticIdentifier(input) {
48
+ let hash = 2166136261;
49
+ for (const char of input) {
50
+ hash ^= char.codePointAt(0) ?? 0;
51
+ hash = Math.imul(hash, 16777619);
52
+ }
53
+ return `x${(hash >>> 0).toString(16)}`;
54
+ }
55
+ function sanitizeIdentifierCharacters(input) {
56
+ const sanitized = input.replace(/[^\w]/g, "");
57
+ return sanitized.length > 0 ? sanitized : createSyntheticIdentifier(input);
58
+ }
59
+ function capitalize(word) {
60
+ return word.charAt(0).toUpperCase() + word.slice(1);
61
+ }
62
+ function snakeToPascalCase(input) {
63
+ const parts = extractIdentifierParts(input);
64
+ if (parts.length === 0) return capitalize(sanitizeIdentifierCharacters(input));
65
+ return parts.map(capitalize).join("");
66
+ }
67
+ function snakeToCamelCase(input) {
68
+ const parts = extractIdentifierParts(input);
69
+ if (parts.length === 0) return sanitizeIdentifierCharacters(input);
70
+ const [firstPart = input, ...rest] = parts;
71
+ return firstPart.charAt(0).toLowerCase() + firstPart.slice(1) + rest.map(capitalize).join("");
72
+ }
73
+ function needsEscaping(name) {
74
+ return PSL_RESERVED_WORDS.has(name.toLowerCase()) || /^\d/.test(name);
75
+ }
76
+ function escapeName(name) {
77
+ return `_${name}`;
78
+ }
79
+ function escapeIfNeeded(name) {
80
+ return needsEscaping(name) ? escapeName(name) : name;
81
+ }
82
+ function toModelName(tableName) {
83
+ let name;
84
+ if (hasSeparators(tableName)) name = snakeToPascalCase(tableName);
85
+ else name = tableName.charAt(0).toUpperCase() + tableName.slice(1);
86
+ if (needsEscaping(name)) return {
87
+ name: escapeName(name),
88
+ map: tableName
89
+ };
90
+ if (name !== tableName) return {
91
+ name,
92
+ map: tableName
93
+ };
94
+ return { name };
95
+ }
96
+ function toFieldName(columnName) {
97
+ let name;
98
+ if (hasSeparators(columnName)) name = snakeToCamelCase(columnName);
99
+ else name = columnName.charAt(0).toLowerCase() + columnName.slice(1);
100
+ if (needsEscaping(name)) return {
101
+ name: escapeName(name),
102
+ map: columnName
103
+ };
104
+ if (name !== columnName) return {
105
+ name,
106
+ map: columnName
107
+ };
108
+ return { name };
109
+ }
110
+ function toEnumName(pgTypeName) {
111
+ let name;
112
+ if (hasSeparators(pgTypeName)) name = snakeToPascalCase(pgTypeName);
113
+ else name = pgTypeName.charAt(0).toUpperCase() + pgTypeName.slice(1);
114
+ if (needsEscaping(name)) return {
115
+ name: escapeName(name),
116
+ map: pgTypeName
117
+ };
118
+ if (name !== pgTypeName) return {
119
+ name,
120
+ map: pgTypeName
121
+ };
122
+ return { name };
123
+ }
124
+ function pluralize(word) {
125
+ if (word.endsWith("s") || word.endsWith("x") || word.endsWith("z") || word.endsWith("ch") || word.endsWith("sh")) return `${word}es`;
126
+ if (word.endsWith("y") && !/[aeiou]y$/i.test(word)) return `${word.slice(0, -1)}ies`;
127
+ return `${word}s`;
128
+ }
129
+ function deriveRelationFieldName(fkColumns, referencedTableName) {
130
+ if (fkColumns.length === 1) {
131
+ const [col = referencedTableName] = fkColumns;
132
+ const stripped = col.replace(/_id$/i, "").replace(/Id$/, "");
133
+ if (stripped.length > 0 && stripped !== col) return escapeIfNeeded(snakeToCamelCase(stripped));
134
+ return escapeIfNeeded(snakeToCamelCase(referencedTableName));
135
+ }
136
+ return escapeIfNeeded(snakeToCamelCase(referencedTableName));
137
+ }
138
+ function deriveBackRelationFieldName(childModelName, isOneToOne) {
139
+ const base = childModelName.charAt(0).toLowerCase() + childModelName.slice(1);
140
+ return isOneToOne ? base : pluralize(base);
141
+ }
142
+ function toNamedTypeName(columnName) {
143
+ let name;
144
+ if (hasSeparators(columnName)) name = snakeToPascalCase(columnName);
145
+ else name = columnName.charAt(0).toUpperCase() + columnName.slice(1);
146
+ return escapeIfNeeded(name);
147
+ }
148
+ //#endregion
149
+ //#region src/core/psl-contract-infer/raw-default-parser.ts
150
+ const NEXTVAL_PATTERN = /^nextval\s*\(/i;
151
+ const NOW_FUNCTION_PATTERN = /^(now\s*\(\s*\)|CURRENT_TIMESTAMP)$/i;
152
+ const CLOCK_TIMESTAMP_PATTERN = /^clock_timestamp\s*\(\s*\)$/i;
153
+ const TIMESTAMP_CAST_SUFFIX = /::timestamp(?:tz|\s+(?:with|without)\s+time\s+zone)?$/i;
154
+ const TEXT_CAST_SUFFIX = /::text$/i;
155
+ const NOW_LITERAL_PATTERN = /^'now'$/i;
156
+ const UUID_PATTERN = /^gen_random_uuid\s*\(\s*\)$/i;
157
+ const UUID_OSSP_PATTERN = /^uuid_generate_v4\s*\(\s*\)$/i;
158
+ const NULL_PATTERN = /^NULL(?:::.+)?$/i;
159
+ const TRUE_PATTERN = /^true$/i;
160
+ const FALSE_PATTERN = /^false$/i;
161
+ const NUMERIC_PATTERN = /^-?\d+(\.\d+)?$/;
162
+ const JSON_CAST_SUFFIX = /::jsonb?$/i;
163
+ const STRING_LITERAL_PATTERN = /^'((?:[^']|'')*)'(?:::(?:"[^"]+"|[\w\s]+)(?:\(\d+\))?)?$/;
164
+ function canonicalizeTimestampDefault(expr) {
165
+ if (NOW_FUNCTION_PATTERN.test(expr)) return "now()";
166
+ if (CLOCK_TIMESTAMP_PATTERN.test(expr)) return "clock_timestamp()";
167
+ if (!TIMESTAMP_CAST_SUFFIX.test(expr)) return void 0;
168
+ let inner = expr.replace(TIMESTAMP_CAST_SUFFIX, "").trim();
169
+ if (inner.startsWith("(") && inner.endsWith(")")) inner = inner.slice(1, -1).trim();
170
+ if (NOW_FUNCTION_PATTERN.test(inner)) return "now()";
171
+ if (CLOCK_TIMESTAMP_PATTERN.test(inner)) return "clock_timestamp()";
172
+ inner = inner.replace(TEXT_CAST_SUFFIX, "").trim();
173
+ if (NOW_LITERAL_PATTERN.test(inner)) return "now()";
174
+ }
175
+ function parseRawDefault(rawDefault, nativeType) {
176
+ const trimmed = rawDefault.trim();
177
+ const normalizedType = nativeType?.toLowerCase();
178
+ if (NEXTVAL_PATTERN.test(trimmed)) return {
179
+ kind: "function",
180
+ expression: "autoincrement()"
181
+ };
182
+ const canonicalTimestamp = canonicalizeTimestampDefault(trimmed);
183
+ if (canonicalTimestamp) return {
184
+ kind: "function",
185
+ expression: canonicalTimestamp
186
+ };
187
+ if (UUID_PATTERN.test(trimmed) || UUID_OSSP_PATTERN.test(trimmed)) return {
188
+ kind: "function",
189
+ expression: "gen_random_uuid()"
190
+ };
191
+ if (NULL_PATTERN.test(trimmed)) return {
192
+ kind: "literal",
193
+ value: null
194
+ };
195
+ if (TRUE_PATTERN.test(trimmed)) return {
196
+ kind: "literal",
197
+ value: true
198
+ };
199
+ if (FALSE_PATTERN.test(trimmed)) return {
200
+ kind: "literal",
201
+ value: false
202
+ };
203
+ if (NUMERIC_PATTERN.test(trimmed)) return {
204
+ kind: "literal",
205
+ value: Number(trimmed)
206
+ };
207
+ const stringMatch = trimmed.match(STRING_LITERAL_PATTERN);
208
+ if (stringMatch?.[1] !== void 0) {
209
+ const unescaped = stringMatch[1].replace(/''/g, "'");
210
+ if (normalizedType === "json" || normalizedType === "jsonb") {
211
+ if (JSON_CAST_SUFFIX.test(trimmed)) return {
212
+ kind: "function",
213
+ expression: trimmed
214
+ };
215
+ try {
216
+ return {
217
+ kind: "literal",
218
+ value: JSON.parse(unescaped)
219
+ };
220
+ } catch {}
221
+ }
222
+ return {
223
+ kind: "literal",
224
+ value: unescaped
225
+ };
226
+ }
227
+ return {
228
+ kind: "function",
229
+ expression: trimmed
230
+ };
231
+ }
232
+ //#endregion
233
+ //#region src/core/psl-contract-infer/relation-inference.ts
234
+ const DEFAULT_ON_DELETE = "noAction";
235
+ const DEFAULT_ON_UPDATE = "noAction";
236
+ const REFERENTIAL_ACTION_PSL = {
237
+ noAction: "NoAction",
238
+ restrict: "Restrict",
239
+ cascade: "Cascade",
240
+ setNull: "SetNull",
241
+ setDefault: "SetDefault"
242
+ };
243
+ function inferRelations(tables, modelNameMap) {
244
+ const relationsByTable = /* @__PURE__ */ new Map();
245
+ const fkCountByPair = /* @__PURE__ */ new Map();
246
+ for (const table of Object.values(tables)) for (const fk of table.foreignKeys) {
247
+ const pairKey = `${table.name}→${fk.referencedTable}`;
248
+ fkCountByPair.set(pairKey, (fkCountByPair.get(pairKey) ?? 0) + 1);
249
+ }
250
+ const usedFieldNames = /* @__PURE__ */ new Map();
251
+ for (const table of Object.values(tables)) {
252
+ const names = /* @__PURE__ */ new Set();
253
+ for (const col of Object.values(table.columns)) names.add(col.name);
254
+ usedFieldNames.set(table.name, names);
255
+ }
256
+ for (const table of Object.values(tables)) for (const fk of table.foreignKeys) {
257
+ const childTableName = table.name;
258
+ const parentTableName = fk.referencedTable;
259
+ const childUsed = usedFieldNames.get(childTableName);
260
+ const childModelName = modelNameMap.get(childTableName) ?? childTableName;
261
+ const parentModelName = modelNameMap.get(parentTableName) ?? parentTableName;
262
+ const pairKey = `${childTableName}→${parentTableName}`;
263
+ const isSelfRelation = childTableName === parentTableName;
264
+ const needsRelationName = fkCountByPair.get(pairKey) > 1 || isSelfRelation;
265
+ const isOneToOne = detectOneToOne(fk, table);
266
+ const childRelFieldName = resolveUniqueFieldName(deriveRelationFieldName(fk.columns, parentTableName), childUsed, parentModelName);
267
+ const relationName = needsRelationName ? deriveRelationName(fk, childRelFieldName, parentModelName, isSelfRelation) : void 0;
268
+ addRelationField(relationsByTable, childTableName, buildChildRelationField(childRelFieldName, parentModelName, fk, fk.columns.some((columnName) => table.columns[columnName]?.nullable ?? false), relationName));
269
+ childUsed.add(childRelFieldName);
270
+ const parentUsed = usedFieldNames.get(parentTableName) ?? /* @__PURE__ */ new Set();
271
+ usedFieldNames.set(parentTableName, parentUsed);
272
+ const backRelFieldName = resolveUniqueFieldName(deriveBackRelationFieldName(childModelName, isOneToOne), parentUsed, childModelName);
273
+ addRelationField(relationsByTable, parentTableName, {
274
+ fieldName: backRelFieldName,
275
+ typeName: childModelName,
276
+ optional: isOneToOne,
277
+ list: !isOneToOne,
278
+ relationName
279
+ });
280
+ parentUsed.add(backRelFieldName);
281
+ }
282
+ return { relationsByTable };
283
+ }
284
+ function detectOneToOne(fk, table) {
285
+ const fkCols = [...fk.columns].sort();
286
+ if (table.primaryKey) {
287
+ const pkCols = [...table.primaryKey.columns].sort();
288
+ if (pkCols.length === fkCols.length && pkCols.every((c, i) => c === fkCols[i])) return true;
289
+ }
290
+ for (const unique of table.uniques) {
291
+ const uniqueCols = [...unique.columns].sort();
292
+ if (uniqueCols.length === fkCols.length && uniqueCols.every((c, i) => c === fkCols[i])) return true;
293
+ }
294
+ return false;
295
+ }
296
+ function deriveRelationName(fk, childRelationFieldName, parentModelName, isSelfRelation) {
297
+ if (fk.name) return fk.name;
298
+ if (isSelfRelation) return `${childRelationFieldName.charAt(0).toUpperCase() + childRelationFieldName.slice(1)}${pluralize(parentModelName)}`;
299
+ return fk.columns.join("_");
300
+ }
301
+ function buildChildRelationField(fieldName, parentModelName, fk, optional, relationName) {
302
+ const onDelete = fk.onDelete && fk.onDelete !== DEFAULT_ON_DELETE ? fk.onDelete : void 0;
303
+ const onUpdate = fk.onUpdate && fk.onUpdate !== DEFAULT_ON_UPDATE ? fk.onUpdate : void 0;
304
+ return {
305
+ fieldName,
306
+ typeName: parentModelName,
307
+ referencedTableName: fk.referencedTable,
308
+ optional,
309
+ list: false,
310
+ relationName,
311
+ fkName: fk.name,
312
+ fields: fk.columns,
313
+ references: fk.referencedColumns,
314
+ onDelete: onDelete ? REFERENTIAL_ACTION_PSL[onDelete] : void 0,
315
+ onUpdate: onUpdate ? REFERENTIAL_ACTION_PSL[onUpdate] : void 0
316
+ };
317
+ }
318
+ function resolveUniqueFieldName(desired, usedNames, fallbackSuffix) {
319
+ if (!usedNames.has(desired)) return desired;
320
+ const withSuffix = `${desired}${fallbackSuffix}`;
321
+ if (!usedNames.has(withSuffix)) return withSuffix;
322
+ let counter = 2;
323
+ while (usedNames.has(`${desired}${counter}`)) counter++;
324
+ return `${desired}${counter}`;
325
+ }
326
+ function addRelationField(map, tableName, field) {
327
+ const existing = map.get(tableName);
328
+ if (existing) existing.push(field);
329
+ else map.set(tableName, [field]);
330
+ }
331
+ //#endregion
332
+ export { deriveBackRelationFieldName, deriveRelationFieldName, inferRelations, mapDefault, parseRawDefault, pluralize, toEnumName, toFieldName, toModelName, toNamedTypeName };
333
+
334
+ //# sourceMappingURL=psl-infer.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"psl-infer.mjs","names":[],"sources":["../src/core/psl-contract-infer/default-mapping.ts","../src/core/psl-contract-infer/name-transforms.ts","../src/core/psl-contract-infer/raw-default-parser.ts","../src/core/psl-contract-infer/relation-inference.ts"],"sourcesContent":["import type { ColumnDefault } from '@prisma-next/contract/types';\n\nconst DEFAULT_FUNCTION_ATTRIBUTES: Readonly<Record<string, string>> = {\n 'autoincrement()': '@default(autoincrement())',\n 'now()': '@default(now())',\n};\n\nexport interface DefaultMappingOptions {\n readonly functionAttributes?: Readonly<Record<string, string>>;\n readonly fallbackFunctionAttribute?: ((expression: string) => string | undefined) | undefined;\n}\n\nexport type DefaultMappingResult = { readonly attribute: string } | { readonly comment: string };\n\nexport function mapDefault(\n columnDefault: ColumnDefault,\n options?: DefaultMappingOptions,\n): DefaultMappingResult {\n switch (columnDefault.kind) {\n case 'literal':\n return { attribute: `@default(${formatLiteralValue(columnDefault.value)})` };\n case 'function': {\n const attribute =\n options?.functionAttributes?.[columnDefault.expression] ??\n DEFAULT_FUNCTION_ATTRIBUTES[columnDefault.expression] ??\n options?.fallbackFunctionAttribute?.(columnDefault.expression);\n return attribute\n ? { attribute }\n : { comment: `// Raw default: ${columnDefault.expression.replace(/[\\r\\n]+/g, ' ')}` };\n }\n }\n}\n\nfunction formatLiteralValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n\n switch (typeof value) {\n case 'boolean':\n case 'number':\n return String(value);\n case 'string':\n return quoteString(value);\n default:\n return quoteString(JSON.stringify(value));\n }\n}\n\nfunction quoteString(str: string): string {\n return `\"${escapeString(str)}\"`;\n}\n\nfunction escapeString(str: string): string {\n return JSON.stringify(str).slice(1, -1);\n}\n","const PSL_RESERVED_WORDS = new Set(['model', 'enum', 'types', 'type', 'generator', 'datasource']);\n\nconst IDENTIFIER_PART_PATTERN = /[A-Za-z0-9]+/g;\n\ntype NameResult = {\n readonly name: string;\n readonly map?: string;\n};\n\nfunction hasSeparators(input: string): boolean {\n return /[^A-Za-z0-9]/.test(input);\n}\n\nfunction extractIdentifierParts(input: string): string[] {\n return input.match(IDENTIFIER_PART_PATTERN) ?? [];\n}\n\nfunction createSyntheticIdentifier(input: string): string {\n let hash = 2166136261;\n\n for (const char of input) {\n hash ^= char.codePointAt(0) ?? 0;\n hash = Math.imul(hash, 16777619);\n }\n\n return `x${(hash >>> 0).toString(16)}`;\n}\n\nfunction sanitizeIdentifierCharacters(input: string): string {\n const sanitized = input.replace(/[^\\w]/g, '');\n return sanitized.length > 0 ? sanitized : createSyntheticIdentifier(input);\n}\n\nfunction capitalize(word: string): string {\n return word.charAt(0).toUpperCase() + word.slice(1);\n}\n\nfunction snakeToPascalCase(input: string): string {\n const parts = extractIdentifierParts(input);\n if (parts.length === 0) {\n return capitalize(sanitizeIdentifierCharacters(input));\n }\n return parts.map(capitalize).join('');\n}\n\nfunction snakeToCamelCase(input: string): string {\n const parts = extractIdentifierParts(input);\n if (parts.length === 0) {\n return sanitizeIdentifierCharacters(input);\n }\n const [firstPart = input, ...rest] = parts;\n return firstPart.charAt(0).toLowerCase() + firstPart.slice(1) + rest.map(capitalize).join('');\n}\n\nfunction needsEscaping(name: string): boolean {\n return PSL_RESERVED_WORDS.has(name.toLowerCase()) || /^\\d/.test(name);\n}\n\nfunction escapeName(name: string): string {\n return `_${name}`;\n}\n\nfunction escapeIfNeeded(name: string): string {\n return needsEscaping(name) ? escapeName(name) : name;\n}\n\nexport function toModelName(tableName: string): NameResult {\n let name: string;\n\n if (hasSeparators(tableName)) {\n name = snakeToPascalCase(tableName);\n } else {\n name = tableName.charAt(0).toUpperCase() + tableName.slice(1);\n }\n\n if (needsEscaping(name)) {\n const escaped = escapeName(name);\n return { name: escaped, map: tableName };\n }\n\n if (name !== tableName) {\n return { name, map: tableName };\n }\n\n return { name };\n}\n\nexport function toFieldName(columnName: string): NameResult {\n let name: string;\n\n if (hasSeparators(columnName)) {\n name = snakeToCamelCase(columnName);\n } else {\n name = columnName.charAt(0).toLowerCase() + columnName.slice(1);\n }\n\n if (needsEscaping(name)) {\n const escaped = escapeName(name);\n return { name: escaped, map: columnName };\n }\n\n if (name !== columnName) {\n return { name, map: columnName };\n }\n\n return { name };\n}\n\nexport function toEnumName(pgTypeName: string): NameResult {\n let name: string;\n\n if (hasSeparators(pgTypeName)) {\n name = snakeToPascalCase(pgTypeName);\n } else {\n name = pgTypeName.charAt(0).toUpperCase() + pgTypeName.slice(1);\n }\n\n if (needsEscaping(name)) {\n const escaped = escapeName(name);\n return { name: escaped, map: pgTypeName };\n }\n\n if (name !== pgTypeName) {\n return { name, map: pgTypeName };\n }\n\n return { name };\n}\n\nexport function pluralize(word: string): string {\n if (\n word.endsWith('s') ||\n word.endsWith('x') ||\n word.endsWith('z') ||\n word.endsWith('ch') ||\n word.endsWith('sh')\n ) {\n return `${word}es`;\n }\n if (word.endsWith('y') && !/[aeiou]y$/i.test(word)) {\n return `${word.slice(0, -1)}ies`;\n }\n return `${word}s`;\n}\n\nexport function deriveRelationFieldName(\n fkColumns: readonly string[],\n referencedTableName: string,\n): string {\n if (fkColumns.length === 1) {\n const [col = referencedTableName] = fkColumns;\n const stripped = col.replace(/_id$/i, '').replace(/Id$/, '');\n\n if (stripped.length > 0 && stripped !== col) {\n return escapeIfNeeded(snakeToCamelCase(stripped));\n }\n return escapeIfNeeded(snakeToCamelCase(referencedTableName));\n }\n\n return escapeIfNeeded(snakeToCamelCase(referencedTableName));\n}\n\nexport function deriveBackRelationFieldName(childModelName: string, isOneToOne: boolean): string {\n const base = childModelName.charAt(0).toLowerCase() + childModelName.slice(1);\n return isOneToOne ? base : pluralize(base);\n}\n\nexport function toNamedTypeName(columnName: string): string {\n let name: string;\n\n if (hasSeparators(columnName)) {\n name = snakeToPascalCase(columnName);\n } else {\n name = columnName.charAt(0).toUpperCase() + columnName.slice(1);\n }\n\n return escapeIfNeeded(name);\n}\n","import type { ColumnDefault } from '@prisma-next/contract/types';\n\nconst NEXTVAL_PATTERN = /^nextval\\s*\\(/i;\nconst NOW_FUNCTION_PATTERN = /^(now\\s*\\(\\s*\\)|CURRENT_TIMESTAMP)$/i;\nconst CLOCK_TIMESTAMP_PATTERN = /^clock_timestamp\\s*\\(\\s*\\)$/i;\nconst TIMESTAMP_CAST_SUFFIX = /::timestamp(?:tz|\\s+(?:with|without)\\s+time\\s+zone)?$/i;\nconst TEXT_CAST_SUFFIX = /::text$/i;\nconst NOW_LITERAL_PATTERN = /^'now'$/i;\nconst UUID_PATTERN = /^gen_random_uuid\\s*\\(\\s*\\)$/i;\nconst UUID_OSSP_PATTERN = /^uuid_generate_v4\\s*\\(\\s*\\)$/i;\nconst NULL_PATTERN = /^NULL(?:::.+)?$/i;\nconst TRUE_PATTERN = /^true$/i;\nconst FALSE_PATTERN = /^false$/i;\nconst NUMERIC_PATTERN = /^-?\\d+(\\.\\d+)?$/;\nconst JSON_CAST_SUFFIX = /::jsonb?$/i;\nconst STRING_LITERAL_PATTERN = /^'((?:[^']|'')*)'(?:::(?:\"[^\"]+\"|[\\w\\s]+)(?:\\(\\d+\\))?)?$/;\n\nfunction canonicalizeTimestampDefault(expr: string): string | undefined {\n if (NOW_FUNCTION_PATTERN.test(expr)) return 'now()';\n if (CLOCK_TIMESTAMP_PATTERN.test(expr)) return 'clock_timestamp()';\n\n if (!TIMESTAMP_CAST_SUFFIX.test(expr)) return undefined;\n\n let inner = expr.replace(TIMESTAMP_CAST_SUFFIX, '').trim();\n if (inner.startsWith('(') && inner.endsWith(')')) {\n inner = inner.slice(1, -1).trim();\n }\n\n if (NOW_FUNCTION_PATTERN.test(inner)) return 'now()';\n if (CLOCK_TIMESTAMP_PATTERN.test(inner)) return 'clock_timestamp()';\n\n inner = inner.replace(TEXT_CAST_SUFFIX, '').trim();\n if (NOW_LITERAL_PATTERN.test(inner)) return 'now()';\n\n return undefined;\n}\n\nexport function parseRawDefault(\n rawDefault: string,\n nativeType?: string,\n): ColumnDefault | undefined {\n const trimmed = rawDefault.trim();\n const normalizedType = nativeType?.toLowerCase();\n\n if (NEXTVAL_PATTERN.test(trimmed)) {\n return { kind: 'function', expression: 'autoincrement()' };\n }\n\n const canonicalTimestamp = canonicalizeTimestampDefault(trimmed);\n if (canonicalTimestamp) {\n return { kind: 'function', expression: canonicalTimestamp };\n }\n\n if (UUID_PATTERN.test(trimmed) || UUID_OSSP_PATTERN.test(trimmed)) {\n return { kind: 'function', expression: 'gen_random_uuid()' };\n }\n\n if (NULL_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: null };\n }\n\n if (TRUE_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: true };\n }\n\n if (FALSE_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: false };\n }\n\n if (NUMERIC_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: Number(trimmed) };\n }\n\n const stringMatch = trimmed.match(STRING_LITERAL_PATTERN);\n if (stringMatch?.[1] !== undefined) {\n const unescaped = stringMatch[1].replace(/''/g, \"'\");\n if (normalizedType === 'json' || normalizedType === 'jsonb') {\n if (JSON_CAST_SUFFIX.test(trimmed)) {\n return { kind: 'function', expression: trimmed };\n }\n try {\n return { kind: 'literal', value: JSON.parse(unescaped) };\n } catch {\n // Fall through to the string form for malformed/non-JSON values.\n }\n }\n return { kind: 'literal', value: unescaped };\n }\n\n return { kind: 'function', expression: trimmed };\n}\n","import type { SqlForeignKeyIR, SqlTableIR } from '@prisma-next/sql-schema-ir/types';\nimport { deriveBackRelationFieldName, deriveRelationFieldName, pluralize } from './name-transforms';\nimport type { RelationField } from './printer-config';\n\nconst DEFAULT_ON_DELETE = 'noAction';\nconst DEFAULT_ON_UPDATE = 'noAction';\n\nconst REFERENTIAL_ACTION_PSL: Record<string, string> = {\n noAction: 'NoAction',\n restrict: 'Restrict',\n cascade: 'Cascade',\n setNull: 'SetNull',\n setDefault: 'SetDefault',\n};\n\nexport type InferredRelations = {\n readonly relationsByTable: ReadonlyMap<string, readonly RelationField[]>;\n};\n\nexport function inferRelations(\n tables: Record<string, SqlTableIR>,\n modelNameMap: ReadonlyMap<string, string>,\n): InferredRelations {\n const relationsByTable = new Map<string, RelationField[]>();\n\n const fkCountByPair = new Map<string, number>();\n for (const table of Object.values(tables)) {\n for (const fk of table.foreignKeys) {\n const pairKey = `${table.name}→${fk.referencedTable}`;\n fkCountByPair.set(pairKey, (fkCountByPair.get(pairKey) ?? 0) + 1);\n }\n }\n\n const usedFieldNames = new Map<string, Set<string>>();\n for (const table of Object.values(tables)) {\n const names = new Set<string>();\n for (const col of Object.values(table.columns)) {\n names.add(col.name);\n }\n usedFieldNames.set(table.name, names);\n }\n\n for (const table of Object.values(tables)) {\n for (const fk of table.foreignKeys) {\n const childTableName = table.name;\n const parentTableName = fk.referencedTable;\n const childUsed = usedFieldNames.get(childTableName) as Set<string>;\n const childModelName = modelNameMap.get(childTableName) ?? childTableName;\n const parentModelName = modelNameMap.get(parentTableName) ?? parentTableName;\n const pairKey = `${childTableName}→${parentTableName}`;\n const isSelfRelation = childTableName === parentTableName;\n const needsRelationName = (fkCountByPair.get(pairKey) as number) > 1 || isSelfRelation;\n\n const isOneToOne = detectOneToOne(fk, table);\n\n const childRelFieldName = resolveUniqueFieldName(\n deriveRelationFieldName(fk.columns, parentTableName),\n childUsed,\n parentModelName,\n );\n const relationName = needsRelationName\n ? deriveRelationName(fk, childRelFieldName, parentModelName, isSelfRelation)\n : undefined;\n const childOptional = fk.columns.some(\n (columnName) => table.columns[columnName]?.nullable ?? false,\n );\n\n const childRelField = buildChildRelationField(\n childRelFieldName,\n parentModelName,\n fk,\n childOptional,\n relationName,\n );\n\n addRelationField(relationsByTable, childTableName, childRelField);\n childUsed.add(childRelFieldName);\n\n const parentUsed = usedFieldNames.get(parentTableName) ?? new Set();\n usedFieldNames.set(parentTableName, parentUsed);\n\n const backRelFieldName = resolveUniqueFieldName(\n deriveBackRelationFieldName(childModelName, isOneToOne),\n parentUsed,\n childModelName,\n );\n\n const backRelField: RelationField = {\n fieldName: backRelFieldName,\n typeName: childModelName,\n optional: isOneToOne,\n list: !isOneToOne,\n relationName,\n };\n\n addRelationField(relationsByTable, parentTableName, backRelField);\n parentUsed.add(backRelFieldName);\n }\n }\n\n return { relationsByTable };\n}\n\nfunction detectOneToOne(fk: SqlForeignKeyIR, table: SqlTableIR): boolean {\n const fkCols = [...fk.columns].sort();\n\n if (table.primaryKey) {\n const pkCols = [...table.primaryKey.columns].sort();\n if (pkCols.length === fkCols.length && pkCols.every((c, i) => c === fkCols[i])) {\n return true;\n }\n }\n\n for (const unique of table.uniques) {\n const uniqueCols = [...unique.columns].sort();\n if (uniqueCols.length === fkCols.length && uniqueCols.every((c, i) => c === fkCols[i])) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction deriveRelationName(\n fk: SqlForeignKeyIR,\n childRelationFieldName: string,\n parentModelName: string,\n isSelfRelation: boolean,\n): string {\n if (fk.name) {\n return fk.name;\n }\n if (isSelfRelation) {\n return `${childRelationFieldName.charAt(0).toUpperCase() + childRelationFieldName.slice(1)}${pluralize(parentModelName)}`;\n }\n return fk.columns.join('_');\n}\n\nfunction buildChildRelationField(\n fieldName: string,\n parentModelName: string,\n fk: SqlForeignKeyIR,\n optional: boolean,\n relationName?: string,\n): RelationField {\n const onDelete = fk.onDelete && fk.onDelete !== DEFAULT_ON_DELETE ? fk.onDelete : undefined;\n const onUpdate = fk.onUpdate && fk.onUpdate !== DEFAULT_ON_UPDATE ? fk.onUpdate : undefined;\n\n return {\n fieldName,\n typeName: parentModelName,\n referencedTableName: fk.referencedTable,\n optional,\n list: false,\n relationName,\n fkName: fk.name,\n fields: fk.columns,\n references: fk.referencedColumns,\n onDelete: onDelete ? REFERENTIAL_ACTION_PSL[onDelete] : undefined,\n onUpdate: onUpdate ? REFERENTIAL_ACTION_PSL[onUpdate] : undefined,\n };\n}\n\nfunction resolveUniqueFieldName(\n desired: string,\n usedNames: ReadonlySet<string>,\n fallbackSuffix: string,\n): string {\n if (!usedNames.has(desired)) {\n return desired;\n }\n\n const withSuffix = `${desired}${fallbackSuffix}`;\n if (!usedNames.has(withSuffix)) {\n return withSuffix;\n }\n\n let counter = 2;\n while (usedNames.has(`${desired}${counter}`)) {\n counter++;\n }\n return `${desired}${counter}`;\n}\n\nfunction addRelationField(\n map: Map<string, RelationField[]>,\n tableName: string,\n field: RelationField,\n): void {\n const existing = map.get(tableName);\n if (existing) {\n existing.push(field);\n } else {\n map.set(tableName, [field]);\n }\n}\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import { blindCast } from "@prisma-next/utils/casts";
2
+ import { hydrateNamespaceEntities } from "@prisma-next/framework-components/ir";
2
3
  import { sqlContractCanonicalizationHooks } from "@prisma-next/sql-contract/canonicalization-hooks";
3
4
  import { ifDefined } from "@prisma-next/utils/defined";
4
- import { NamespaceBase, UNBOUND_NAMESPACE_ID, hydrateNamespaceEntities } from "@prisma-next/framework-components/ir";
5
5
  import { ContractValidationError } from "@prisma-next/contract/contract-validation-error";
6
- import { isPlainRecord } from "@prisma-next/contract/is-plain-record";
6
+ import { isPlainRecord as isPlainRecord$1 } from "@prisma-next/contract/is-plain-record";
7
7
  import { composeSqlEntityKinds } from "@prisma-next/sql-contract/entity-kinds";
8
- import { SqlStorage, SqlUnboundNamespace, buildSqlNamespace } from "@prisma-next/sql-contract/types";
8
+ import { SqlStorage, isMaterializedSqlNamespace } from "@prisma-next/sql-contract/types";
9
9
  import { createSqlContractSchema, validateSqlContractFully } from "@prisma-next/sql-contract/validators";
10
10
  import { type } from "arktype";
11
11
  //#region src/core/ir/sql-contract-serializer-base.ts
@@ -65,39 +65,34 @@ var SqlContractSerializerBase = class {
65
65
  const hydratedTypes = types !== void 0 ? Object.fromEntries(Object.entries(types).map(([name, entry]) => [name, this.hydrateStorageTypeEntry(entry)])) : void 0;
66
66
  const rawNamespaces = validated.storage.namespaces;
67
67
  if (rawNamespaces === void 0) throw new ContractValidationError("Contract storage.namespaces is required after structural validation", "structural");
68
- const hydratedNamespaces = this.hydrateSqlNamespaceMap(rawNamespaces);
69
- const withInjectedUnbound = this.defaultNamespaceId === UNBOUND_NAMESPACE_ID ? {
70
- ...hydratedNamespaces,
71
- [UNBOUND_NAMESPACE_ID]: hydratedNamespaces[UNBOUND_NAMESPACE_ID] ?? SqlUnboundNamespace.instance
72
- } : hydratedNamespaces;
68
+ const hydratedNamespaces = this.hydrateSqlNamespaceMap(blindCast(rawNamespaces));
73
69
  return {
74
70
  ...validated,
75
71
  storage: new SqlStorage({
76
72
  storageHash: validated.storage.storageHash,
77
73
  ...ifDefined("types", hydratedTypes),
78
- namespaces: blindCast(withInjectedUnbound)
74
+ namespaces: blindCast(hydratedNamespaces)
79
75
  })
80
76
  };
81
77
  }
82
78
  hydrateSqlNamespaceMap(namespaces) {
83
79
  return Object.fromEntries(Object.entries(namespaces).map(([nsId, namespaceEntryRaw]) => {
84
80
  const namespaceHydrated = this.hydrateSqlNamespaceEntry(nsId, namespaceEntryRaw);
85
- return [nsId, namespaceHydrated instanceof NamespaceBase ? namespaceHydrated : buildSqlNamespace(blindCast(namespaceHydrated))];
81
+ if (!isMaterializedSqlNamespace(namespaceHydrated)) throw new Error(`Target serializer bug: hydrateSqlNamespaceEntry for namespace "${nsId}" returned a non-NamespaceBase value. Override hydrateSqlNamespaceEntry to produce a target namespace concretion.`);
82
+ return [nsId, namespaceHydrated];
86
83
  }));
87
84
  }
88
85
  hydrateSqlNamespaceEntry(nsId, raw) {
89
- if (raw instanceof NamespaceBase) return raw;
90
- const rawRecord = isPlainRecord(raw) ? raw : {};
91
- const id = typeof rawRecord["id"] === "string" ? rawRecord["id"] : nsId;
86
+ const id = typeof raw["id"] === "string" ? raw["id"] : nsId;
92
87
  const parsed = NamespaceRawSchema({
93
- ...rawRecord,
88
+ ...raw,
94
89
  id
95
90
  });
96
91
  if (parsed instanceof type.errors) throw new ContractValidationError(`Namespace hydration failed: ${parsed.map((p) => p.message).join("; ")}`, "structural");
97
92
  const entriesRaw = parsed.entries;
98
- const rawEntriesMap = isPlainRecord(entriesRaw) ? entriesRaw : {};
93
+ const rawEntriesMap = isPlainRecord$1(entriesRaw) ? entriesRaw : {};
99
94
  const entriesInput = {};
100
- for (const [key, innerMap] of Object.entries(rawEntriesMap)) entriesInput[key] = isPlainRecord(innerMap) ? innerMap : Object.freeze({});
95
+ for (const [key, innerMap] of Object.entries(rawEntriesMap)) entriesInput[key] = isPlainRecord$1(innerMap) ? innerMap : Object.freeze({});
101
96
  const entriesOutput = hydrateNamespaceEntities(entriesInput, this.entryKinds, "fail", id);
102
97
  if (!Object.hasOwn(entriesOutput, "table")) entriesOutput["table"] = {};
103
98
  return blindCast({
@@ -127,16 +122,18 @@ var SqlContractSerializerBase = class {
127
122
  * codec-typed envelope wire a target-specific subclass with a populated
128
123
  * registry (see Postgres). Family-level call sites instantiate this
129
124
  * default directly when no target serializer is supplied.
125
+ *
126
+ * Because this serializer has no target concretion, deserialization of
127
+ * contracts that include namespace entries from JSON will throw unless
128
+ * the caller provides pre-hydrated `NamespaceBase` instances. Production
129
+ * paths always supply a target-specific serializer.
130
130
  */
131
131
  var SqlContractSerializer = class extends SqlContractSerializerBase {
132
132
  constructor() {
133
133
  super(/* @__PURE__ */ new Map());
134
134
  }
135
- get defaultNamespaceId() {
136
- return UNBOUND_NAMESPACE_ID;
137
- }
138
135
  };
139
136
  //#endregion
140
137
  export { SqlContractSerializerBase as n, SqlContractSerializer as t };
141
138
 
142
- //# sourceMappingURL=sql-contract-serializer-BR2vC7Z-.mjs.map
139
+ //# sourceMappingURL=sql-contract-serializer-CYtXg_zs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-contract-serializer-CYtXg_zs.mjs","names":["isPlainRecord"],"sources":["../src/core/ir/sql-contract-serializer-base.ts","../src/core/ir/sql-contract-serializer.ts"],"sourcesContent":["import { ContractValidationError } from '@prisma-next/contract/contract-validation-error';\nimport { isPlainRecord } from '@prisma-next/contract/is-plain-record';\nimport type { Contract } from '@prisma-next/contract/types';\nimport type { ContractSerializer } from '@prisma-next/framework-components/control';\nimport {\n type AnyEntityKindDescriptor,\n hydrateNamespaceEntities,\n type Namespace,\n} from '@prisma-next/framework-components/ir';\nimport { sqlContractCanonicalizationHooks } from '@prisma-next/sql-contract/canonicalization-hooks';\nimport { composeSqlEntityKinds } from '@prisma-next/sql-contract/entity-kinds';\nimport {\n isMaterializedSqlNamespace,\n type SqlNamespaceInput,\n SqlStorage,\n type SqlStorageInput,\n type SqlStorageTypeEntry,\n} from '@prisma-next/sql-contract/types';\nimport {\n createSqlContractSchema,\n validateSqlContractFully,\n} from '@prisma-next/sql-contract/validators';\nimport { blindCast } from '@prisma-next/utils/casts';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { JsonObject } from '@prisma-next/utils/json';\nimport { type Type, type } from 'arktype';\n\nconst NamespaceRawSchema = type({\n id: 'string',\n 'kind?': 'string',\n entries: type({\n '+': 'ignore',\n }),\n});\n\nexport type SqlEntityHydrationFactory = (entry: unknown) => unknown;\n\n/**\n * SQL family `ContractSerializer` abstract base. Carries the SQL-shared\n * deserialization pipeline:\n *\n * 1. `parseSqlContractStructure` validates the on-disk JSON envelope\n * against the SQL contract arktype schema (`validateSqlContractFully`)\n * and returns the validated flat-data shape.\n * 2. `hydrateSqlStorage` walks the validated `storage` subtree and\n * constructs the family-shared SQL Contract IR class hierarchy\n * (`SqlStorage` -> `StorageTable` -> `StorageColumn` / `PrimaryKey`\n * / …). The rest of the contract envelope is JSON-clean primitive\n * data and passes through unchanged.\n * 3. `constructTargetContract` is the target-specific extension hook;\n * defaults to identity. Targets that need to attach target-only\n * fields (e.g. target-specific derived storage fields) override it.\n *\n * Default `serializeContract` is identity over the contract — concrete\n * SQL targets ship JSON-clean class instances, so the contract value\n * can be stringified directly. The non-enumerable family-level `kind`\n * discriminator on `SqlNode` instances stays out of the persisted\n * envelope automatically. Targets that need to canonicalize on the way\n * out (key ordering, dropping computed-only fields) override\n * `serializeContract` directly.\n */\nexport abstract class SqlContractSerializerBase<TContract extends Contract<SqlStorage>>\n implements ContractSerializer<TContract>\n{\n private readonly contractSchema: Type<unknown> | undefined;\n private readonly entryKinds: ReadonlyMap<string, AnyEntityKindDescriptor>;\n\n constructor(\n protected readonly entityHydrationRegistry: ReadonlyMap<\n string,\n SqlEntityHydrationFactory\n > = new Map(),\n packEntityKinds: readonly AnyEntityKindDescriptor[] = [],\n ) {\n this.entryKinds = composeSqlEntityKinds(packEntityKinds);\n this.contractSchema =\n packEntityKinds.length > 0 ? createSqlContractSchema(this.entryKinds) : undefined;\n }\n\n deserializeContract<T extends TContract = TContract>(json: unknown): T {\n const validated = this.parseSqlContractStructure(json);\n const hydrated = this.hydrateSqlStorage(validated);\n return this.constructTargetContract(hydrated) as T;\n }\n\n serializeContract(contract: TContract): JsonObject {\n return contract as unknown as JsonObject;\n }\n\n shouldPreserveEmpty = sqlContractCanonicalizationHooks.shouldPreserveEmpty;\n\n sortStorage = sqlContractCanonicalizationHooks.sortStorage;\n\n protected parseSqlContractStructure(json: unknown): Contract<SqlStorage> {\n return validateSqlContractFully<Contract<SqlStorage>>(\n json,\n this.contractSchema !== undefined ? { contractSchema: this.contractSchema } : undefined,\n );\n }\n\n protected hydrateSqlStorage(validated: Contract<SqlStorage>): Contract<SqlStorage> {\n const types = validated.storage.types;\n const hydratedTypes =\n types !== undefined\n ? Object.fromEntries(\n Object.entries(types).map(([name, entry]) => [\n name,\n this.hydrateStorageTypeEntry(entry),\n ]),\n )\n : undefined;\n\n const rawNamespaces = validated.storage.namespaces;\n if (rawNamespaces === undefined) {\n throw new ContractValidationError(\n 'Contract storage.namespaces is required after structural validation',\n 'structural',\n );\n }\n const hydratedNamespaces = this.hydrateSqlNamespaceMap(\n blindCast<\n Readonly<Record<string, Record<string, unknown>>>,\n 'parseSqlContractStructure validated raw JSON; namespace entries are plain objects, not SqlNamespace instances.'\n >(rawNamespaces),\n );\n\n return {\n ...validated,\n storage: new SqlStorage({\n storageHash: validated.storage.storageHash,\n ...ifDefined('types', hydratedTypes),\n namespaces: blindCast<\n SqlStorageInput['namespaces'],\n 'hydrateSqlNamespaceMap builds each namespace through the target serializer override, so every value is a SqlNamespace; the framework return type only promises the base Namespace.'\n >(hydratedNamespaces),\n }),\n };\n }\n\n protected hydrateSqlNamespaceMap(\n namespaces: Readonly<Record<string, Record<string, unknown>>>,\n ): Readonly<Record<string, Namespace>> {\n return Object.fromEntries(\n Object.entries(namespaces).map(([nsId, namespaceEntryRaw]) => {\n const namespaceHydrated = this.hydrateSqlNamespaceEntry(nsId, namespaceEntryRaw);\n if (!isMaterializedSqlNamespace(namespaceHydrated)) {\n throw new Error(\n `Target serializer bug: hydrateSqlNamespaceEntry for namespace \"${nsId}\" returned a non-NamespaceBase value. Override hydrateSqlNamespaceEntry to produce a target namespace concretion.`,\n );\n }\n return [nsId, namespaceHydrated];\n }),\n );\n }\n\n protected hydrateSqlNamespaceEntry(\n nsId: string,\n raw: Record<string, unknown>,\n ): Namespace | SqlNamespaceInput {\n const id = typeof raw['id'] === 'string' ? raw['id'] : nsId;\n const parsed = NamespaceRawSchema({ ...raw, id });\n if (parsed instanceof type.errors) {\n const messages = parsed.map((p: { message: string }) => p.message).join('; ');\n throw new ContractValidationError(`Namespace hydration failed: ${messages}`, 'structural');\n }\n const entriesRaw = parsed.entries;\n const rawEntriesMap = isPlainRecord(entriesRaw) ? entriesRaw : {};\n\n const entriesInput: Record<string, Readonly<Record<string, unknown>>> = {};\n for (const [key, innerMap] of Object.entries(rawEntriesMap)) {\n entriesInput[key] = isPlainRecord(innerMap) ? innerMap : Object.freeze({});\n }\n\n const entriesOutput = hydrateNamespaceEntities(entriesInput, this.entryKinds, 'fail', id);\n\n // Always ensure a 'table' key is present (may be empty).\n if (!Object.hasOwn(entriesOutput, 'table')) {\n entriesOutput['table'] = {};\n }\n\n return blindCast<\n SqlNamespaceInput,\n 'entriesOutput holds the hydrated SQL entity-kind maps (table always present); this wraps them as the SqlNamespaceInput the target createNamespace consumes.'\n >({\n id,\n entries: entriesOutput,\n });\n }\n\n protected hydrateStorageTypeEntry(entry: SqlStorageTypeEntry): SqlStorageTypeEntry {\n if (typeof entry !== 'object' || entry === null) {\n return entry;\n }\n const kind = (entry as { kind?: unknown }).kind;\n if (typeof kind !== 'string') {\n return entry;\n }\n const factory = this.entityHydrationRegistry.get(kind);\n if (factory === undefined) {\n return entry;\n }\n return blindCast<\n SqlStorageTypeEntry,\n 'entity registry factory returns SqlStorageTypeEntry for storage.types entries'\n >(factory(entry));\n }\n\n protected constructTargetContract(hydrated: Contract<SqlStorage>): TContract {\n return hydrated as TContract;\n }\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport { SqlContractSerializerBase } from './sql-contract-serializer-base';\n\n/**\n * Default SQL family `ContractSerializer` concretion. Inherits the\n * full SQL-shared deserialization pipeline (structural validation +\n * IR-class hydration) without pack-registered `storage.types`\n * hydration factories — targets that emit polymorphic JSON outside the\n * codec-typed envelope wire a target-specific subclass with a populated\n * registry (see Postgres). Family-level call sites instantiate this\n * default directly when no target serializer is supplied.\n *\n * Because this serializer has no target concretion, deserialization of\n * contracts that include namespace entries from JSON will throw unless\n * the caller provides pre-hydrated `NamespaceBase` instances. Production\n * paths always supply a target-specific serializer.\n */\nexport class SqlContractSerializer extends SqlContractSerializerBase<Contract<SqlStorage>> {\n constructor() {\n super(new Map());\n }\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
+ import { ColumnDefault, Contract } from "@prisma-next/contract/types";
1
2
  import { OperationContext, VerifyDatabaseSchemaResult } from "@prisma-next/framework-components/control";
2
3
  import { SqlStorage } from "@prisma-next/sql-contract/types";
3
- import { ColumnDefault, Contract } from "@prisma-next/contract/types";
4
- import { SqlSchemaIR } from "@prisma-next/sql-schema-ir/types";
4
+ import { SqlSchemaIR, SqlSchemaIRNode } from "@prisma-next/sql-schema-ir/types";
5
5
  import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
6
6
 
7
- //#region src/core/schema-verify/verify-sql-schema.d.ts
7
+ //#region src/core/diff/sql-schema-diff.d.ts
8
8
  /**
9
9
  * Function type for normalizing raw database default expressions into ColumnDefault.
10
10
  * Target-specific implementations handle database dialect differences.
@@ -49,6 +49,15 @@ interface VerifySqlSchemaOptions {
49
49
  * with contract native types (e.g., Postgres 'varchar' → 'character varying').
50
50
  */
51
51
  readonly normalizeNativeType?: NativeTypeNormalizer;
52
+ /**
53
+ * When set, only the contract tables in these namespace ids are checked
54
+ * against `schema` (the matching actual namespace node). The full contract is
55
+ * still consulted for cross-namespace value-set / control-policy resolution.
56
+ * Used by the multi-schema verify, which pairs each contract namespace to its
57
+ * own actual node. Absent ⇒ all contract namespaces are checked against the
58
+ * single (flat) `schema` — the single-schema / SQLite path.
59
+ */
60
+ readonly restrictToNamespaceIds?: ReadonlySet<string>;
52
61
  }
53
62
  /**
54
63
  * Verifies that a SqlSchemaIR matches a Contract.
@@ -61,6 +70,36 @@ interface VerifySqlSchemaOptions {
61
70
  * @returns VerifyDatabaseSchemaResult with verification tree and issues
62
71
  */
63
72
  declare function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabaseSchemaResult;
73
+ /**
74
+ * The single per-namespace-paired relational verify shared by the migration
75
+ * planner and the family schema verify — there is exactly one such operation.
76
+ *
77
+ * Each contract namespace is paired to the introspected namespace node holding
78
+ * the same DDL schema, then `verifySqlSchema` checks that namespace's tables
79
+ * against the matching actual node (a contract table under `auth` is only ever
80
+ * looked up in the `auth` actual node, so a multi-schema database no longer
81
+ * reports tables in other schemas as missing). The full contract is passed every
82
+ * time — `restrictToNamespaceIds` scopes only which tables are checked, so
83
+ * cross-namespace value-set / control-policy resolution is unaffected.
84
+ *
85
+ * The DDL schema of each contract namespace is read from a single-namespace
86
+ * expected projection (`buildExpectedSchema`), which both callers already build
87
+ * the same way. Empty contract namespaces verify nothing and are skipped.
88
+ * Single-schema (one namespace) and SQLite's flat schema are one pairing —
89
+ * byte-identical to the prior per-node verify.
90
+ */
91
+ declare function verifySqlSchemaTree(options: {
92
+ readonly contract: Contract<SqlStorage>;
93
+ readonly actualSchema: SqlSchemaIRNode;
94
+ readonly buildExpectedSchema: (contract: Contract<SqlStorage>) => SqlSchemaIRNode;
95
+ readonly strict: boolean;
96
+ readonly typeMetadataRegistry: ReadonlyMap<string, {
97
+ nativeType?: string;
98
+ }>;
99
+ readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
100
+ readonly normalizeDefault?: DefaultNormalizer;
101
+ readonly normalizeNativeType?: NativeTypeNormalizer;
102
+ }): VerifyDatabaseSchemaResult;
64
103
  //#endregion
65
- export { verifySqlSchema as i, NativeTypeNormalizer as n, VerifySqlSchemaOptions as r, DefaultNormalizer as t };
66
- //# sourceMappingURL=verify-sql-schema-thU-jKpf.d.mts.map
104
+ export { verifySqlSchemaTree as a, verifySqlSchema as i, NativeTypeNormalizer as n, VerifySqlSchemaOptions as r, DefaultNormalizer as t };
105
+ //# sourceMappingURL=sql-schema-diff-6z36dZt6.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-schema-diff-6z36dZt6.d.mts","names":[],"sources":["../src/core/diff/sql-schema-diff.ts"],"mappings":";;;;;;;;;;AA4EkB;KAHN,iBAAA,IACV,UAAA,UACA,UAAA,aACG,aAAa;;;;AAOoC;AAKtD;KALY,oBAAA,IAAwB,UAAkB;;;;UAKrC,sBAAA;EAQI;EAAA,SANV,QAAA,EAAU,QAAA,CAAS,UAAA;EAagB;EAAA,SAXnC,MAAA,EAAQ,WAAA;EAiBW;EAAA,SAfnB,MAAA;EA8ByB;EAAA,SA5BzB,OAAA,GAAU,gBAAA;EA4B0B;EAAA,SA1BpC,oBAAA,EAAsB,WAAA;IAAsB,UAAA;EAAA;EAN5C;;;;EAAA,SAWA,mBAAA,EAAqB,aAAA,CAAc,8BAAA;EALnC;;;;;EAAA,SAWA,gBAAA,GAAmB,iBAAA;EAAnB;;;;;EAAA,SAMA,mBAAA,GAAsB,oBAAA;EASc;AAAA;AAa/C;;;;;;EAb+C,SAApC,sBAAA,GAAyB,WAAA;AAAA;AAawD;AAkyC5F;;;;;;;;;AAlyC4F,iBAA5E,eAAA,CAAgB,OAAA,EAAS,sBAAA,GAAyB,0BAA0B;;;;;;;;;;;;;;;;;;;iBAkyC5E,mBAAA,CAAoB,OAAA;EAAA,SACzB,QAAA,EAAU,QAAA,CAAS,UAAA;EAAA,SACnB,YAAA,EAAc,eAAA;EAAA,SACd,mBAAA,GAAsB,QAAA,EAAU,QAAA,CAAS,UAAA,MAAgB,eAAA;EAAA,SACzD,MAAA;EAAA,SACA,oBAAA,EAAsB,WAAA;IAAsB,UAAA;EAAA;EAAA,SAC5C,mBAAA,EAAqB,aAAA,CAAc,8BAAA;EAAA,SACnC,gBAAA,GAAmB,iBAAA;EAAA,SACnB,mBAAA,GAAsB,oBAAA;AAAA,IAC7B,0BAAA"}