@prisma-next/family-sql 0.14.0 → 0.15.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) 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/{control-adapter-Cmw9LvEP.d.mts → control-adapter-BQgad8Zc.d.mts} +30 -8
  4. package/dist/control-adapter-BQgad8Zc.d.mts.map +1 -0
  5. package/dist/control-adapter.d.mts +1 -1
  6. package/dist/control.d.mts +279 -34
  7. package/dist/control.d.mts.map +1 -1
  8. package/dist/control.mjs +364 -1005
  9. package/dist/control.mjs.map +1 -1
  10. package/dist/diff.d.mts +102 -0
  11. package/dist/diff.d.mts.map +1 -0
  12. package/dist/diff.mjs +14 -0
  13. package/dist/diff.mjs.map +1 -0
  14. package/dist/ir.d.mts +26 -9
  15. package/dist/ir.d.mts.map +1 -1
  16. package/dist/ir.mjs +2 -2
  17. package/dist/ir.mjs.map +1 -1
  18. package/dist/migration.d.mts +11 -2
  19. package/dist/migration.d.mts.map +1 -1
  20. package/dist/migration.mjs +7 -0
  21. package/dist/migration.mjs.map +1 -1
  22. package/dist/pack.mjs +1 -1
  23. package/dist/psl-infer.d.mts +119 -0
  24. package/dist/psl-infer.d.mts.map +1 -0
  25. package/dist/psl-infer.mjs +366 -0
  26. package/dist/psl-infer.mjs.map +1 -0
  27. package/dist/schema-differ-DnoopSXm.d.mts +45 -0
  28. package/dist/schema-differ-DnoopSXm.d.mts.map +1 -0
  29. package/dist/schema-verify-W3r631Jh.mjs +226 -0
  30. package/dist/schema-verify-W3r631Jh.mjs.map +1 -0
  31. package/dist/{sql-contract-serializer-BR2vC7Z-.mjs → sql-contract-serializer-C75cfMSS.mjs} +46 -20
  32. package/dist/sql-contract-serializer-C75cfMSS.mjs.map +1 -0
  33. package/dist/{types-kgstZ_Zd.d.mts → types-BPv_y7iS.d.mts} +36 -142
  34. package/dist/types-BPv_y7iS.d.mts.map +1 -0
  35. package/package.json +26 -26
  36. package/src/core/authoring-entity-types.ts +12 -37
  37. package/src/core/control-adapter.ts +11 -5
  38. package/src/core/control-instance.ts +212 -42
  39. package/src/core/control-target-descriptor.ts +98 -0
  40. package/src/core/diff/schema-verify.ts +324 -0
  41. package/src/core/diff/sql-schema-diff.ts +41 -0
  42. package/src/core/diff/verifier-disposition.ts +30 -0
  43. package/src/core/ir/sql-contract-serializer-base.ts +70 -56
  44. package/src/core/ir/sql-contract-serializer.ts +5 -7
  45. package/src/core/ir/sql-schema-verifier-base.ts +5 -5
  46. package/src/core/migrations/contract-to-schema-ir.ts +137 -51
  47. package/src/core/migrations/control-policy.ts +66 -115
  48. package/src/core/migrations/field-event-planner.ts +2 -2
  49. package/src/core/migrations/native-type-expander.ts +28 -0
  50. package/src/core/migrations/schema-differ.ts +41 -0
  51. package/src/core/migrations/types.ts +33 -37
  52. package/src/core/psl-contract-infer/name-transforms.ts +15 -0
  53. package/src/core/psl-contract-infer/printer-config.ts +14 -6
  54. package/src/core/psl-contract-infer/relation-inference.ts +30 -1
  55. package/src/core/sql-migration.ts +12 -1
  56. package/src/exports/control.ts +12 -2
  57. package/src/exports/diff.ts +25 -0
  58. package/src/exports/psl-infer.ts +40 -0
  59. package/dist/authoring-type-constructors-CjFfO6LM.mjs.map +0 -1
  60. package/dist/control-adapter-Cmw9LvEP.d.mts.map +0 -1
  61. package/dist/schema-verify.d.mts +0 -39
  62. package/dist/schema-verify.d.mts.map +0 -1
  63. package/dist/schema-verify.mjs +0 -2
  64. package/dist/sql-contract-serializer-BR2vC7Z-.mjs.map +0 -1
  65. package/dist/test-utils.d.mts +0 -2
  66. package/dist/test-utils.mjs +0 -2
  67. package/dist/types-kgstZ_Zd.d.mts.map +0 -1
  68. package/dist/verify-sql-schema-thU-jKpf.d.mts +0 -66
  69. package/dist/verify-sql-schema-thU-jKpf.d.mts.map +0 -1
  70. package/dist/verify-sql-schema-xT4udQLQ.mjs +0 -1501
  71. package/dist/verify-sql-schema-xT4udQLQ.mjs.map +0 -1
  72. package/src/core/psl-contract-infer/postgres-default-mapping.ts +0 -16
  73. package/src/core/psl-contract-infer/postgres-type-map.ts +0 -157
  74. package/src/core/psl-contract-infer/sql-schema-ir-to-psl-ast.ts +0 -795
  75. package/src/core/schema-verify/control-verify-emit.ts +0 -46
  76. package/src/core/schema-verify/verifier-disposition.ts +0 -58
  77. package/src/core/schema-verify/verify-helpers.ts +0 -820
  78. package/src/core/schema-verify/verify-sql-schema.ts +0 -1311
  79. package/src/exports/schema-verify.ts +0 -18
  80. package/src/exports/test-utils.ts +0 -9
@@ -0,0 +1,366 @@
1
+ import { backingIndexColumnKeys, isBackedByColumnKeys } from "@prisma-next/sql-contract/foreign-key-materialization";
2
+ //#region src/core/psl-contract-infer/default-mapping.ts
3
+ const DEFAULT_FUNCTION_ATTRIBUTES = {
4
+ "autoincrement()": "@default(autoincrement())",
5
+ "now()": "@default(now())"
6
+ };
7
+ function mapDefault(columnDefault, options) {
8
+ switch (columnDefault.kind) {
9
+ case "literal": return { attribute: `@default(${formatLiteralValue(columnDefault.value)})` };
10
+ case "function": {
11
+ const attribute = options?.functionAttributes?.[columnDefault.expression] ?? DEFAULT_FUNCTION_ATTRIBUTES[columnDefault.expression] ?? options?.fallbackFunctionAttribute?.(columnDefault.expression);
12
+ return attribute ? { attribute } : { comment: `// Raw default: ${columnDefault.expression.replace(/[\r\n]+/g, " ")}` };
13
+ }
14
+ }
15
+ }
16
+ function formatLiteralValue(value) {
17
+ if (value === null) return "null";
18
+ switch (typeof value) {
19
+ case "boolean":
20
+ case "number": return String(value);
21
+ case "string": return quoteString(value);
22
+ default: return quoteString(JSON.stringify(value));
23
+ }
24
+ }
25
+ function quoteString(str) {
26
+ return `"${escapeString(str)}"`;
27
+ }
28
+ function escapeString(str) {
29
+ return JSON.stringify(str).slice(1, -1);
30
+ }
31
+ //#endregion
32
+ //#region src/core/psl-contract-infer/name-transforms.ts
33
+ const PSL_RESERVED_WORDS = /* @__PURE__ */ new Set([
34
+ "model",
35
+ "enum",
36
+ "types",
37
+ "type",
38
+ "generator",
39
+ "datasource"
40
+ ]);
41
+ const IDENTIFIER_PART_PATTERN = /[A-Za-z0-9]+/g;
42
+ function hasSeparators(input) {
43
+ return /[^A-Za-z0-9]/.test(input);
44
+ }
45
+ function extractIdentifierParts(input) {
46
+ return input.match(IDENTIFIER_PART_PATTERN) ?? [];
47
+ }
48
+ function createSyntheticIdentifier(input) {
49
+ let hash = 2166136261;
50
+ for (const char of input) {
51
+ hash ^= char.codePointAt(0) ?? 0;
52
+ hash = Math.imul(hash, 16777619);
53
+ }
54
+ return `x${(hash >>> 0).toString(16)}`;
55
+ }
56
+ function sanitizeIdentifierCharacters(input) {
57
+ const sanitized = input.replace(/[^\w]/g, "");
58
+ return sanitized.length > 0 ? sanitized : createSyntheticIdentifier(input);
59
+ }
60
+ function capitalize(word) {
61
+ return word.charAt(0).toUpperCase() + word.slice(1);
62
+ }
63
+ function snakeToPascalCase(input) {
64
+ const parts = extractIdentifierParts(input);
65
+ if (parts.length === 0) return capitalize(sanitizeIdentifierCharacters(input));
66
+ return parts.map(capitalize).join("");
67
+ }
68
+ function snakeToCamelCase(input) {
69
+ const parts = extractIdentifierParts(input);
70
+ if (parts.length === 0) return sanitizeIdentifierCharacters(input);
71
+ const [firstPart = input, ...rest] = parts;
72
+ return firstPart.charAt(0).toLowerCase() + firstPart.slice(1) + rest.map(capitalize).join("");
73
+ }
74
+ function needsEscaping(name) {
75
+ return PSL_RESERVED_WORDS.has(name.toLowerCase()) || /^\d/.test(name);
76
+ }
77
+ function escapeName(name) {
78
+ return `_${name}`;
79
+ }
80
+ function escapeIfNeeded(name) {
81
+ return needsEscaping(name) ? escapeName(name) : name;
82
+ }
83
+ function toModelName(tableName) {
84
+ let name;
85
+ if (hasSeparators(tableName)) name = snakeToPascalCase(tableName);
86
+ else name = tableName.charAt(0).toUpperCase() + tableName.slice(1);
87
+ if (needsEscaping(name)) return {
88
+ name: escapeName(name),
89
+ map: tableName
90
+ };
91
+ if (name !== tableName) return {
92
+ name,
93
+ map: tableName
94
+ };
95
+ return { name };
96
+ }
97
+ function toFieldName(columnName) {
98
+ let name;
99
+ if (hasSeparators(columnName)) name = snakeToCamelCase(columnName);
100
+ else name = columnName.charAt(0).toLowerCase() + columnName.slice(1);
101
+ if (needsEscaping(name)) return {
102
+ name: escapeName(name),
103
+ map: columnName
104
+ };
105
+ if (name !== columnName) return {
106
+ name,
107
+ map: columnName
108
+ };
109
+ return { name };
110
+ }
111
+ function toEnumName(pgTypeName) {
112
+ let name;
113
+ if (hasSeparators(pgTypeName)) name = snakeToPascalCase(pgTypeName);
114
+ else name = pgTypeName.charAt(0).toUpperCase() + pgTypeName.slice(1);
115
+ if (needsEscaping(name)) return {
116
+ name: escapeName(name),
117
+ map: pgTypeName
118
+ };
119
+ if (name !== pgTypeName) return {
120
+ name,
121
+ map: pgTypeName
122
+ };
123
+ return { name };
124
+ }
125
+ const VALID_IDENTIFIER_PATTERN = /^[A-Za-z_]\w*$/;
126
+ /**
127
+ * PSL member name for a native-enum value. The value itself always prints
128
+ * explicitly (`member = "value"`), so the returned name never needs a map:
129
+ * a value that already is a valid, non-reserved identifier is kept verbatim
130
+ * (case included); anything else is camelCased/escaped like a field name.
131
+ */
132
+ function toEnumMemberName(value) {
133
+ if (VALID_IDENTIFIER_PATTERN.test(value) && !needsEscaping(value)) return value;
134
+ return escapeIfNeeded(snakeToCamelCase(value));
135
+ }
136
+ function pluralize(word) {
137
+ if (word.endsWith("s") || word.endsWith("x") || word.endsWith("z") || word.endsWith("ch") || word.endsWith("sh")) return `${word}es`;
138
+ if (word.endsWith("y") && !/[aeiou]y$/i.test(word)) return `${word.slice(0, -1)}ies`;
139
+ return `${word}s`;
140
+ }
141
+ function deriveRelationFieldName(fkColumns, referencedTableName) {
142
+ if (fkColumns.length === 1) {
143
+ const [col = referencedTableName] = fkColumns;
144
+ const stripped = col.replace(/_id$/i, "").replace(/Id$/, "");
145
+ if (stripped.length > 0 && stripped !== col) return escapeIfNeeded(snakeToCamelCase(stripped));
146
+ return escapeIfNeeded(snakeToCamelCase(referencedTableName));
147
+ }
148
+ return escapeIfNeeded(snakeToCamelCase(referencedTableName));
149
+ }
150
+ function deriveBackRelationFieldName(childModelName, isOneToOne) {
151
+ const base = childModelName.charAt(0).toLowerCase() + childModelName.slice(1);
152
+ return isOneToOne ? base : pluralize(base);
153
+ }
154
+ function toNamedTypeName(columnName) {
155
+ let name;
156
+ if (hasSeparators(columnName)) name = snakeToPascalCase(columnName);
157
+ else name = columnName.charAt(0).toUpperCase() + columnName.slice(1);
158
+ return escapeIfNeeded(name);
159
+ }
160
+ //#endregion
161
+ //#region src/core/psl-contract-infer/raw-default-parser.ts
162
+ const NEXTVAL_PATTERN = /^nextval\s*\(/i;
163
+ const NOW_FUNCTION_PATTERN = /^(now\s*\(\s*\)|CURRENT_TIMESTAMP)$/i;
164
+ const CLOCK_TIMESTAMP_PATTERN = /^clock_timestamp\s*\(\s*\)$/i;
165
+ const TIMESTAMP_CAST_SUFFIX = /::timestamp(?:tz|\s+(?:with|without)\s+time\s+zone)?$/i;
166
+ const TEXT_CAST_SUFFIX = /::text$/i;
167
+ const NOW_LITERAL_PATTERN = /^'now'$/i;
168
+ const UUID_PATTERN = /^gen_random_uuid\s*\(\s*\)$/i;
169
+ const UUID_OSSP_PATTERN = /^uuid_generate_v4\s*\(\s*\)$/i;
170
+ const NULL_PATTERN = /^NULL(?:::.+)?$/i;
171
+ const TRUE_PATTERN = /^true$/i;
172
+ const FALSE_PATTERN = /^false$/i;
173
+ const NUMERIC_PATTERN = /^-?\d+(\.\d+)?$/;
174
+ const JSON_CAST_SUFFIX = /::jsonb?$/i;
175
+ const STRING_LITERAL_PATTERN = /^'((?:[^']|'')*)'(?:::(?:"[^"]+"|[\w\s]+)(?:\(\d+\))?)?$/;
176
+ function canonicalizeTimestampDefault(expr) {
177
+ if (NOW_FUNCTION_PATTERN.test(expr)) return "now()";
178
+ if (CLOCK_TIMESTAMP_PATTERN.test(expr)) return "clock_timestamp()";
179
+ if (!TIMESTAMP_CAST_SUFFIX.test(expr)) return void 0;
180
+ let inner = expr.replace(TIMESTAMP_CAST_SUFFIX, "").trim();
181
+ if (inner.startsWith("(") && inner.endsWith(")")) inner = inner.slice(1, -1).trim();
182
+ if (NOW_FUNCTION_PATTERN.test(inner)) return "now()";
183
+ if (CLOCK_TIMESTAMP_PATTERN.test(inner)) return "clock_timestamp()";
184
+ inner = inner.replace(TEXT_CAST_SUFFIX, "").trim();
185
+ if (NOW_LITERAL_PATTERN.test(inner)) return "now()";
186
+ }
187
+ function parseRawDefault(rawDefault, nativeType) {
188
+ const trimmed = rawDefault.trim();
189
+ const normalizedType = nativeType?.toLowerCase();
190
+ if (NEXTVAL_PATTERN.test(trimmed)) return {
191
+ kind: "function",
192
+ expression: "autoincrement()"
193
+ };
194
+ const canonicalTimestamp = canonicalizeTimestampDefault(trimmed);
195
+ if (canonicalTimestamp) return {
196
+ kind: "function",
197
+ expression: canonicalTimestamp
198
+ };
199
+ if (UUID_PATTERN.test(trimmed) || UUID_OSSP_PATTERN.test(trimmed)) return {
200
+ kind: "function",
201
+ expression: "gen_random_uuid()"
202
+ };
203
+ if (NULL_PATTERN.test(trimmed)) return {
204
+ kind: "literal",
205
+ value: null
206
+ };
207
+ if (TRUE_PATTERN.test(trimmed)) return {
208
+ kind: "literal",
209
+ value: true
210
+ };
211
+ if (FALSE_PATTERN.test(trimmed)) return {
212
+ kind: "literal",
213
+ value: false
214
+ };
215
+ if (NUMERIC_PATTERN.test(trimmed)) return {
216
+ kind: "literal",
217
+ value: Number(trimmed)
218
+ };
219
+ const stringMatch = trimmed.match(STRING_LITERAL_PATTERN);
220
+ if (stringMatch?.[1] !== void 0) {
221
+ const unescaped = stringMatch[1].replace(/''/g, "'");
222
+ if (normalizedType === "json" || normalizedType === "jsonb") {
223
+ if (JSON_CAST_SUFFIX.test(trimmed)) return {
224
+ kind: "function",
225
+ expression: trimmed
226
+ };
227
+ try {
228
+ return {
229
+ kind: "literal",
230
+ value: JSON.parse(unescaped)
231
+ };
232
+ } catch {}
233
+ }
234
+ return {
235
+ kind: "literal",
236
+ value: unescaped
237
+ };
238
+ }
239
+ return {
240
+ kind: "function",
241
+ expression: trimmed
242
+ };
243
+ }
244
+ //#endregion
245
+ //#region src/core/psl-contract-infer/relation-inference.ts
246
+ const DEFAULT_ON_DELETE = "noAction";
247
+ const DEFAULT_ON_UPDATE = "noAction";
248
+ const REFERENTIAL_ACTION_PSL = {
249
+ noAction: "NoAction",
250
+ restrict: "Restrict",
251
+ cascade: "Cascade",
252
+ setNull: "SetNull",
253
+ setDefault: "SetDefault"
254
+ };
255
+ function inferRelations(tables, modelNameMap) {
256
+ const relationsByTable = /* @__PURE__ */ new Map();
257
+ const fkCountByPair = /* @__PURE__ */ new Map();
258
+ for (const table of Object.values(tables)) for (const fk of table.foreignKeys) {
259
+ const pairKey = `${table.name}→${fk.referencedTable}`;
260
+ fkCountByPair.set(pairKey, (fkCountByPair.get(pairKey) ?? 0) + 1);
261
+ }
262
+ const usedFieldNames = /* @__PURE__ */ new Map();
263
+ for (const table of Object.values(tables)) {
264
+ const names = /* @__PURE__ */ new Set();
265
+ for (const col of Object.values(table.columns)) names.add(col.name);
266
+ usedFieldNames.set(table.name, names);
267
+ }
268
+ for (const table of Object.values(tables)) for (const fk of table.foreignKeys) {
269
+ const childTableName = table.name;
270
+ const parentTableName = fk.referencedTable;
271
+ const childUsed = usedFieldNames.get(childTableName);
272
+ const childModelName = modelNameMap.get(childTableName) ?? childTableName;
273
+ const parentModelName = modelNameMap.get(parentTableName) ?? parentTableName;
274
+ const pairKey = `${childTableName}→${parentTableName}`;
275
+ const isSelfRelation = childTableName === parentTableName;
276
+ const needsRelationName = fkCountByPair.get(pairKey) > 1 || isSelfRelation;
277
+ const isOneToOne = detectOneToOne(fk, table);
278
+ const childRelFieldName = resolveUniqueFieldName(deriveRelationFieldName(fk.columns, parentTableName), childUsed, parentModelName);
279
+ const relationName = needsRelationName ? deriveRelationName(fk, childRelFieldName, parentModelName, isSelfRelation) : void 0;
280
+ addRelationField(relationsByTable, childTableName, buildChildRelationField(childRelFieldName, parentModelName, fk, fk.columns.some((columnName) => table.columns[columnName]?.nullable ?? false), relationName, table));
281
+ childUsed.add(childRelFieldName);
282
+ const parentUsed = usedFieldNames.get(parentTableName) ?? /* @__PURE__ */ new Set();
283
+ usedFieldNames.set(parentTableName, parentUsed);
284
+ const backRelFieldName = resolveUniqueFieldName(deriveBackRelationFieldName(childModelName, isOneToOne), parentUsed, childModelName);
285
+ addRelationField(relationsByTable, parentTableName, {
286
+ fieldName: backRelFieldName,
287
+ typeName: childModelName,
288
+ optional: isOneToOne,
289
+ list: !isOneToOne,
290
+ relationName
291
+ });
292
+ parentUsed.add(backRelFieldName);
293
+ }
294
+ return { relationsByTable };
295
+ }
296
+ function detectOneToOne(fk, table) {
297
+ const fkCols = [...fk.columns].sort();
298
+ if (table.primaryKey) {
299
+ const pkCols = [...table.primaryKey.columns].sort();
300
+ if (pkCols.length === fkCols.length && pkCols.every((c, i) => c === fkCols[i])) return true;
301
+ }
302
+ for (const unique of table.uniques) {
303
+ const uniqueCols = [...unique.columns].sort();
304
+ if (uniqueCols.length === fkCols.length && uniqueCols.every((c, i) => c === fkCols[i])) return true;
305
+ }
306
+ return false;
307
+ }
308
+ function deriveRelationName(fk, childRelationFieldName, parentModelName, isSelfRelation) {
309
+ if (fk.name) return fk.name;
310
+ if (isSelfRelation) return `${childRelationFieldName.charAt(0).toUpperCase() + childRelationFieldName.slice(1)}${pluralize(parentModelName)}`;
311
+ return fk.columns.join("_");
312
+ }
313
+ /**
314
+ * Builds the child-side {@link RelationField} for a single foreign key:
315
+ * `typeName` is the parent model name, `fields`/`references` are the FK's raw
316
+ * columns, and `onDelete`/`onUpdate` are normalized to their PSL spelling.
317
+ * Exported so a caller resolving a foreign key against a model outside
318
+ * `tables` (e.g. a cross-space reference into another contract) can reuse the
319
+ * same normalization instead of duplicating it.
320
+ *
321
+ * `hostTable` is the table the FK is declared on (i.e. `table`, not the
322
+ * referenced parent) — its own indexes/uniques/primary key are what a
323
+ * backing index for this FK would be. When none of them exactly match the
324
+ * FK's source columns (in order), `index: false` is stamped so the emitted
325
+ * `@relation` opts out of the framework's default derived backing-index
326
+ * expectation, matching what `db verify` will find live. Passing the table
327
+ * is optional so existing single-FK callers (e.g. cross-space resolution
328
+ * before a host table reference is threaded through) can omit it and keep
329
+ * the framework default.
330
+ */
331
+ function buildChildRelationField(fieldName, parentModelName, fk, optional, relationName, hostTable) {
332
+ const onDelete = fk.onDelete && fk.onDelete !== DEFAULT_ON_DELETE ? fk.onDelete : void 0;
333
+ const onUpdate = fk.onUpdate && fk.onUpdate !== DEFAULT_ON_UPDATE ? fk.onUpdate : void 0;
334
+ const index = hostTable && !isBackedByColumnKeys(fk.columns, backingIndexColumnKeys(hostTable)) ? false : void 0;
335
+ return {
336
+ fieldName,
337
+ typeName: parentModelName,
338
+ referencedTableName: fk.referencedTable,
339
+ optional,
340
+ list: false,
341
+ relationName,
342
+ fkName: fk.name,
343
+ fields: fk.columns,
344
+ references: fk.referencedColumns,
345
+ onDelete: onDelete ? REFERENTIAL_ACTION_PSL[onDelete] : void 0,
346
+ onUpdate: onUpdate ? REFERENTIAL_ACTION_PSL[onUpdate] : void 0,
347
+ index
348
+ };
349
+ }
350
+ function resolveUniqueFieldName(desired, usedNames, fallbackSuffix) {
351
+ if (!usedNames.has(desired)) return desired;
352
+ const withSuffix = `${desired}${fallbackSuffix}`;
353
+ if (!usedNames.has(withSuffix)) return withSuffix;
354
+ let counter = 2;
355
+ while (usedNames.has(`${desired}${counter}`)) counter++;
356
+ return `${desired}${counter}`;
357
+ }
358
+ function addRelationField(map, tableName, field) {
359
+ const existing = map.get(tableName);
360
+ if (existing) existing.push(field);
361
+ else map.set(tableName, [field]);
362
+ }
363
+ //#endregion
364
+ export { buildChildRelationField, deriveBackRelationFieldName, deriveRelationFieldName, inferRelations, mapDefault, parseRawDefault, pluralize, toEnumMemberName, toEnumName, toFieldName, toModelName, toNamedTypeName };
365
+
366
+ //# 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\nconst VALID_IDENTIFIER_PATTERN = /^[A-Za-z_]\\w*$/;\n\n/**\n * PSL member name for a native-enum value. The value itself always prints\n * explicitly (`member = \"value\"`), so the returned name never needs a map:\n * a value that already is a valid, non-reserved identifier is kept verbatim\n * (case included); anything else is camelCased/escaped like a field name.\n */\nexport function toEnumMemberName(value: string): string {\n if (VALID_IDENTIFIER_PATTERN.test(value) && !needsEscaping(value)) {\n return value;\n }\n return escapeIfNeeded(snakeToCamelCase(value));\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 {\n backingIndexColumnKeys,\n isBackedByColumnKeys,\n} from '@prisma-next/sql-contract/foreign-key-materialization';\nimport 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 table,\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\n/**\n * Builds the child-side {@link RelationField} for a single foreign key:\n * `typeName` is the parent model name, `fields`/`references` are the FK's raw\n * columns, and `onDelete`/`onUpdate` are normalized to their PSL spelling.\n * Exported so a caller resolving a foreign key against a model outside\n * `tables` (e.g. a cross-space reference into another contract) can reuse the\n * same normalization instead of duplicating it.\n *\n * `hostTable` is the table the FK is declared on (i.e. `table`, not the\n * referenced parent) — its own indexes/uniques/primary key are what a\n * backing index for this FK would be. When none of them exactly match the\n * FK's source columns (in order), `index: false` is stamped so the emitted\n * `@relation` opts out of the framework's default derived backing-index\n * expectation, matching what `db verify` will find live. Passing the table\n * is optional so existing single-FK callers (e.g. cross-space resolution\n * before a host table reference is threaded through) can omit it and keep\n * the framework default.\n */\nexport function buildChildRelationField(\n fieldName: string,\n parentModelName: string,\n fk: SqlForeignKeyIR,\n optional: boolean,\n relationName?: string,\n hostTable?: SqlTableIR,\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 const index =\n hostTable && !isBackedByColumnKeys(fk.columns, backingIndexColumnKeys(hostTable))\n ? false\n : 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 index,\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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"}
@@ -0,0 +1,45 @@
1
+ import { SchemaDiffIssue } from "@prisma-next/framework-components/control";
2
+ import { Contract, ControlPolicy } from "@prisma-next/contract/types";
3
+ import { SqlStorage } from "@prisma-next/sql-contract/types";
4
+ import { SqlSchemaIRNode } from "@prisma-next/sql-schema-ir/types";
5
+ import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
6
+
7
+ //#region src/core/migrations/schema-differ.d.ts
8
+ /**
9
+ * The full-tree node diff a SQL target produces for the family verify
10
+ * verdict: the target derives the expected tree from the contract, applies
11
+ * the pre-diff normalizations (semantic satisfaction, FK schema-segment
12
+ * resolution), runs the generic differ, and ownership-scopes the result.
13
+ * Strict gating, control-policy disposition, and the verdict itself are the
14
+ * family's post-diff filters over this output.
15
+ */
16
+ interface SqlSchemaDiffResult {
17
+ /** The full, ownership-scoped diff issue list. */
18
+ readonly issues: readonly SchemaDiffIssue[];
19
+ /**
20
+ * Resolves a diff issue's subject table's declared control policy directly
21
+ * from the contract (Decision 5's own-layer-per-concern discipline extends
22
+ * here too: control policy is a contract concern, resolved by the target
23
+ * at disposition time — never stamped on the diff node). `undefined`
24
+ * when the issue's path resolves to no contract table (a genuine orphan,
25
+ * or a non-table subject).
26
+ */
27
+ readonly resolveControlPolicy: (issue: SchemaDiffIssue) => ControlPolicy | undefined;
28
+ /**
29
+ * The expected/actual namespace-node pairs the codec `verifyType` hooks
30
+ * run over — one per contract namespace with tables, paired by DDL
31
+ * schema; a flat target repeats its sole actual root per such namespace.
32
+ */
33
+ readonly namespacePairs: ReadonlyArray<{
34
+ readonly actual: SqlSchemaIRNode | undefined;
35
+ }>;
36
+ }
37
+ interface SqlSchemaDiffInput {
38
+ readonly contract: Contract<SqlStorage>;
39
+ readonly schema: SqlSchemaIRNode;
40
+ readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
41
+ }
42
+ type SqlSchemaDiffFn = (input: SqlSchemaDiffInput) => SqlSchemaDiffResult;
43
+ //#endregion
44
+ export { SqlSchemaDiffInput as n, SqlSchemaDiffResult as r, SqlSchemaDiffFn as t };
45
+ //# sourceMappingURL=schema-differ-DnoopSXm.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-differ-DnoopSXm.d.mts","names":[],"sources":["../src/core/migrations/schema-differ.ts"],"mappings":";;;;;;;;;AAcA;;;;;;UAAiB,mBAAA;EAiBU;EAAA,SAfhB,MAAA,WAAiB,eAAA;EAeY;;;;;;;;EAAA,SAN7B,oBAAA,GAAuB,KAAA,EAAO,eAAA,KAAoB,aAAA;EAMT;;;AAAuB;AAG3E;EAHoD,SAAzC,cAAA,EAAgB,aAAA;IAAA,SAAyB,MAAA,EAAQ,eAAA;EAAA;AAAA;AAAA,UAG3C,kBAAA;EAAA,SACN,QAAA,EAAU,QAAA,CAAS,UAAA;EAAA,SACnB,MAAA,EAAQ,eAAA;EAAA,SACR,mBAAA,EAAqB,aAAA,CAAc,8BAAA;AAAA;AAAA,KAGlC,eAAA,IAAmB,KAAA,EAAO,kBAAA,KAAuB,mBAAmB"}