@prisma-next/family-sql 0.14.0-dev.5 → 0.14.0-dev.50

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 (79) 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-BVOj4gXI.d.mts} +42 -8
  4. package/dist/control-adapter-BVOj4gXI.d.mts.map +1 -0
  5. package/dist/control-adapter.d.mts +1 -1
  6. package/dist/control.d.mts +238 -6
  7. package/dist/control.d.mts.map +1 -1
  8. package/dist/control.mjs +336 -942
  9. package/dist/control.mjs.map +1 -1
  10. package/dist/diff.d.mts +150 -0
  11. package/dist/diff.d.mts.map +1 -0
  12. package/dist/diff.mjs +188 -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 +101 -0
  24. package/dist/psl-infer.d.mts.map +1 -0
  25. package/dist/psl-infer.mjs +342 -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-BW7pzb2j.d.mts} +34 -139
  34. package/dist/types-BW7pzb2j.d.mts.map +1 -0
  35. package/package.json +23 -23
  36. package/src/core/authoring-entity-types.ts +12 -37
  37. package/src/core/control-adapter.ts +10 -5
  38. package/src/core/control-instance.ts +210 -42
  39. package/src/core/control-target-descriptor.ts +98 -0
  40. package/src/core/diff/diff-tree-normalization.ts +211 -0
  41. package/src/core/diff/schema-verify.ts +324 -0
  42. package/src/core/diff/sql-schema-diff.ts +77 -0
  43. package/src/core/diff/verifier-disposition.ts +30 -0
  44. package/src/core/ir/sql-contract-serializer-base.ts +70 -56
  45. package/src/core/ir/sql-contract-serializer.ts +5 -7
  46. package/src/core/ir/sql-schema-verifier-base.ts +5 -5
  47. package/src/core/migrations/contract-to-schema-ir.ts +110 -30
  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 +31 -34
  52. package/src/core/psl-contract-infer/printer-config.ts +12 -6
  53. package/src/core/psl-contract-infer/relation-inference.ts +9 -1
  54. package/src/core/sql-migration.ts +12 -1
  55. package/src/exports/control.ts +11 -1
  56. package/src/exports/diff.ts +38 -0
  57. package/src/exports/psl-infer.ts +39 -0
  58. package/dist/authoring-type-constructors-CjFfO6LM.mjs.map +0 -1
  59. package/dist/control-adapter-Cmw9LvEP.d.mts.map +0 -1
  60. package/dist/schema-verify.d.mts +0 -39
  61. package/dist/schema-verify.d.mts.map +0 -1
  62. package/dist/schema-verify.mjs +0 -2
  63. package/dist/sql-contract-serializer-BR2vC7Z-.mjs.map +0 -1
  64. package/dist/test-utils.d.mts +0 -2
  65. package/dist/test-utils.mjs +0 -2
  66. package/dist/types-kgstZ_Zd.d.mts.map +0 -1
  67. package/dist/verify-sql-schema-thU-jKpf.d.mts +0 -66
  68. package/dist/verify-sql-schema-thU-jKpf.d.mts.map +0 -1
  69. package/dist/verify-sql-schema-xT4udQLQ.mjs +0 -1501
  70. package/dist/verify-sql-schema-xT4udQLQ.mjs.map +0 -1
  71. package/src/core/psl-contract-infer/postgres-default-mapping.ts +0 -16
  72. package/src/core/psl-contract-infer/postgres-type-map.ts +0 -157
  73. package/src/core/psl-contract-infer/sql-schema-ir-to-psl-ast.ts +0 -795
  74. package/src/core/schema-verify/control-verify-emit.ts +0 -46
  75. package/src/core/schema-verify/verifier-disposition.ts +0 -58
  76. package/src/core/schema-verify/verify-helpers.ts +0 -820
  77. package/src/core/schema-verify/verify-sql-schema.ts +0 -1311
  78. package/src/exports/schema-verify.ts +0 -18
  79. package/src/exports/test-utils.ts +0 -9
@@ -0,0 +1,101 @@
1
+ import { ColumnDefault } from "@prisma-next/contract/types";
2
+ import { SqlForeignKeyIR, SqlTableIR } from "@prisma-next/sql-schema-ir/types";
3
+
4
+ //#region src/core/psl-contract-infer/default-mapping.d.ts
5
+ interface DefaultMappingOptions {
6
+ readonly functionAttributes?: Readonly<Record<string, string>>;
7
+ readonly fallbackFunctionAttribute?: ((expression: string) => string | undefined) | undefined;
8
+ }
9
+ type DefaultMappingResult = {
10
+ readonly attribute: string;
11
+ } | {
12
+ readonly comment: string;
13
+ };
14
+ declare function mapDefault(columnDefault: ColumnDefault, options?: DefaultMappingOptions): DefaultMappingResult;
15
+ //#endregion
16
+ //#region src/core/psl-contract-infer/name-transforms.d.ts
17
+ type NameResult = {
18
+ readonly name: string;
19
+ readonly map?: string;
20
+ };
21
+ declare function toModelName(tableName: string): NameResult;
22
+ declare function toFieldName(columnName: string): NameResult;
23
+ declare function toEnumName(pgTypeName: string): NameResult;
24
+ declare function pluralize(word: string): string;
25
+ declare function deriveRelationFieldName(fkColumns: readonly string[], referencedTableName: string): string;
26
+ declare function deriveBackRelationFieldName(childModelName: string, isOneToOne: boolean): string;
27
+ declare function toNamedTypeName(columnName: string): string;
28
+ //#endregion
29
+ //#region src/core/psl-contract-infer/printer-config.d.ts
30
+ type PslNativeTypeAttribute = {
31
+ readonly name: string;
32
+ readonly args?: readonly string[];
33
+ };
34
+ type PslTypeResolution = {
35
+ readonly pslType: string;
36
+ readonly nativeType: string;
37
+ readonly typeParams?: Record<string, unknown>;
38
+ readonly nativeTypeAttribute?: PslNativeTypeAttribute;
39
+ } | {
40
+ readonly unsupported: true;
41
+ readonly nativeType: string;
42
+ };
43
+ interface PslTypeMap {
44
+ resolve(nativeType: string, annotations?: Record<string, unknown>): PslTypeResolution;
45
+ }
46
+ interface EnumInfo {
47
+ readonly typeNames: ReadonlySet<string>;
48
+ readonly definitions: ReadonlyMap<string, readonly string[]>;
49
+ }
50
+ interface PslPrinterOptions {
51
+ readonly typeMap: PslTypeMap;
52
+ readonly defaultMapping?: DefaultMappingOptions;
53
+ readonly enumInfo?: EnumInfo;
54
+ readonly parseRawDefault?: (rawDefault: string, nativeType?: string) => ColumnDefault | undefined;
55
+ }
56
+ type RelationField = {
57
+ readonly fieldName: string;
58
+ readonly typeName: string;
59
+ /**
60
+ * Namespace qualifier for a cross-space relation (e.g. `"auth"` for a
61
+ * relation into `supabase:auth.AuthUser`). Absent for a same-namespace
62
+ * relation.
63
+ */
64
+ readonly typeNamespaceId?: string | undefined;
65
+ /**
66
+ * Contract-space qualifier for a relation into another stack extension
67
+ * pack's contract space (e.g. `"supabase"`). Absent for a same-space
68
+ * relation.
69
+ */
70
+ readonly typeContractSpaceId?: string | undefined;
71
+ readonly referencedTableName?: string | undefined;
72
+ readonly optional: boolean;
73
+ readonly list: boolean;
74
+ readonly relationName?: string | undefined;
75
+ readonly fkName?: string | undefined;
76
+ readonly fields?: readonly string[] | undefined;
77
+ readonly references?: readonly string[] | undefined;
78
+ readonly onDelete?: string | undefined;
79
+ readonly onUpdate?: string | undefined;
80
+ };
81
+ //#endregion
82
+ //#region src/core/psl-contract-infer/raw-default-parser.d.ts
83
+ declare function parseRawDefault(rawDefault: string, nativeType?: string): ColumnDefault | undefined;
84
+ //#endregion
85
+ //#region src/core/psl-contract-infer/relation-inference.d.ts
86
+ type InferredRelations = {
87
+ readonly relationsByTable: ReadonlyMap<string, readonly RelationField[]>;
88
+ };
89
+ declare function inferRelations(tables: Record<string, SqlTableIR>, modelNameMap: ReadonlyMap<string, string>): InferredRelations;
90
+ /**
91
+ * Builds the child-side {@link RelationField} for a single foreign key:
92
+ * `typeName` is the parent model name, `fields`/`references` are the FK's raw
93
+ * columns, and `onDelete`/`onUpdate` are normalized to their PSL spelling.
94
+ * Exported so a caller resolving a foreign key against a model outside
95
+ * `tables` (e.g. a cross-space reference into another contract) can reuse the
96
+ * same normalization instead of duplicating it.
97
+ */
98
+ declare function buildChildRelationField(fieldName: string, parentModelName: string, fk: SqlForeignKeyIR, optional: boolean, relationName?: string): RelationField;
99
+ //#endregion
100
+ export { type DefaultMappingOptions, type DefaultMappingResult, type EnumInfo, type InferredRelations, type PslNativeTypeAttribute, type PslPrinterOptions, type PslTypeMap, type PslTypeResolution, type RelationField, buildChildRelationField, deriveBackRelationFieldName, deriveRelationFieldName, inferRelations, mapDefault, parseRawDefault, pluralize, toEnumName, toFieldName, toModelName, toNamedTypeName };
101
+ //# sourceMappingURL=psl-infer.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"psl-infer.d.mts","names":[],"sources":["../src/core/psl-contract-infer/default-mapping.ts","../src/core/psl-contract-infer/name-transforms.ts","../src/core/psl-contract-infer/printer-config.ts","../src/core/psl-contract-infer/raw-default-parser.ts","../src/core/psl-contract-infer/relation-inference.ts"],"mappings":";;;;UAOiB,qBAAA;EAAA,SACN,kBAAA,GAAqB,QAAQ,CAAC,MAAA;EAAA,SAC9B,yBAAA,KAA8B,UAAA;AAAA;AAAA,KAG7B,oBAAA;EAAA,SAAkC,SAAA;AAAA;EAAA,SAAiC,OAAO;AAAA;AAAA,iBAEtE,UAAA,CACd,aAAA,EAAe,aAAA,EACf,OAAA,GAAU,qBAAA,GACT,oBAAA;;;KCbE,UAAA;EAAA,SACM,IAAA;EAAA,SACA,GAAG;AAAA;AAAA,iBA4DE,WAAA,CAAY,SAAA,WAAoB,UAAU;AAAA,iBAqB1C,WAAA,CAAY,UAAA,WAAqB,UAAU;AAAA,iBAqB3C,UAAA,CAAW,UAAA,WAAqB,UAAU;AAAA,iBAqB1C,SAAA,CAAU,IAAY;AAAA,iBAgBtB,uBAAA,CACd,SAAA,qBACA,mBAA2B;AAAA,iBAeb,2BAAA,CAA4B,cAAA,UAAwB,UAAmB;AAAA,iBAKvE,eAAA,CAAgB,UAAkB;;;KCpKtC,sBAAA;EAAA,SACD,IAAA;EAAA,SACA,IAAI;AAAA;AAAA,KAGH,iBAAA;EAAA,SAEG,OAAA;EAAA,SACA,UAAA;EAAA,SACA,UAAA,GAAa,MAAA;EAAA,SACb,mBAAA,GAAsB,sBAAsB;AAAA;EAAA,SAG5C,WAAA;EAAA,SACA,UAAA;AAAA;AAAA,UAGE,UAAA;EACf,OAAA,CAAQ,UAAA,UAAoB,WAAA,GAAc,MAAA,oBAA0B,iBAAiB;AAAA;AAAA,UAGtE,QAAA;EAAA,SACN,SAAA,EAAW,WAAA;EAAA,SACX,WAAA,EAAa,WAAW;AAAA;AAAA,UAGlB,iBAAA;EAAA,SACN,OAAA,EAAS,UAAA;EAAA,SACT,cAAA,GAAiB,qBAAA;EAAA,SACjB,QAAA,GAAW,QAAA;EAAA,SACX,eAAA,IAAmB,UAAA,UAAoB,UAAA,cAAwB,aAAA;AAAA;AAAA,KAG9D,aAAA;EAAA,SACD,SAAA;EAAA,SACA,QAAA;EFtBT;;;AACqB;;EADrB,SE4BS,eAAA;;;;;;WAMA,mBAAA;EAAA,SACA,mBAAA;EAAA,SACA,QAAA;EAAA,SACA,IAAA;EAAA,SACA,YAAA;EAAA,SACA,MAAA;EAAA,SACA,MAAA;EAAA,SACA,UAAA;EAAA,SACA,QAAA;EAAA,SACA,QAAA;AAAA;;;iBCtBK,eAAA,CACd,UAAA,UACA,UAAA,YACC,aAAa;;;KCzBJ,iBAAA;EAAA,SACD,gBAAA,EAAkB,WAAW,kBAAkB,aAAA;AAAA;AAAA,iBAG1C,cAAA,CACd,MAAA,EAAQ,MAAA,SAAe,UAAA,GACvB,YAAA,EAAc,WAAA,mBACb,iBAAA;;;;;;;;;iBA4Ha,uBAAA,CACd,SAAA,UACA,eAAA,UACA,EAAA,EAAI,eAAA,EACJ,QAAA,WACA,YAAA,YACC,aAAa"}
@@ -0,0 +1,342 @@
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
+ /**
302
+ * Builds the child-side {@link RelationField} for a single foreign key:
303
+ * `typeName` is the parent model name, `fields`/`references` are the FK's raw
304
+ * columns, and `onDelete`/`onUpdate` are normalized to their PSL spelling.
305
+ * Exported so a caller resolving a foreign key against a model outside
306
+ * `tables` (e.g. a cross-space reference into another contract) can reuse the
307
+ * same normalization instead of duplicating it.
308
+ */
309
+ function buildChildRelationField(fieldName, parentModelName, fk, optional, relationName) {
310
+ const onDelete = fk.onDelete && fk.onDelete !== DEFAULT_ON_DELETE ? fk.onDelete : void 0;
311
+ const onUpdate = fk.onUpdate && fk.onUpdate !== DEFAULT_ON_UPDATE ? fk.onUpdate : void 0;
312
+ return {
313
+ fieldName,
314
+ typeName: parentModelName,
315
+ referencedTableName: fk.referencedTable,
316
+ optional,
317
+ list: false,
318
+ relationName,
319
+ fkName: fk.name,
320
+ fields: fk.columns,
321
+ references: fk.referencedColumns,
322
+ onDelete: onDelete ? REFERENTIAL_ACTION_PSL[onDelete] : void 0,
323
+ onUpdate: onUpdate ? REFERENTIAL_ACTION_PSL[onUpdate] : void 0
324
+ };
325
+ }
326
+ function resolveUniqueFieldName(desired, usedNames, fallbackSuffix) {
327
+ if (!usedNames.has(desired)) return desired;
328
+ const withSuffix = `${desired}${fallbackSuffix}`;
329
+ if (!usedNames.has(withSuffix)) return withSuffix;
330
+ let counter = 2;
331
+ while (usedNames.has(`${desired}${counter}`)) counter++;
332
+ return `${desired}${counter}`;
333
+ }
334
+ function addRelationField(map, tableName, field) {
335
+ const existing = map.get(tableName);
336
+ if (existing) existing.push(field);
337
+ else map.set(tableName, [field]);
338
+ }
339
+ //#endregion
340
+ export { buildChildRelationField, deriveBackRelationFieldName, deriveRelationFieldName, inferRelations, mapDefault, parseRawDefault, pluralize, toEnumName, toFieldName, toModelName, toNamedTypeName };
341
+
342
+ //# 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\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 */\nexport function 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":";AAEA,MAAM,8BAAgE;CACpE,mBAAmB;CACnB,SAAS;AACX;AASA,SAAgB,WACd,eACA,SACsB;CACtB,QAAQ,cAAc,MAAtB;EACE,KAAK,WACH,OAAO,EAAE,WAAW,YAAY,mBAAmB,cAAc,KAAK,EAAE,GAAG;EAC7E,KAAK,YAAY;GACf,MAAM,YACJ,SAAS,qBAAqB,cAAc,eAC5C,4BAA4B,cAAc,eAC1C,SAAS,4BAA4B,cAAc,UAAU;GAC/D,OAAO,YACH,EAAE,UAAU,IACZ,EAAE,SAAS,mBAAmB,cAAc,WAAW,QAAQ,YAAY,GAAG,IAAI;EACxF;CACF;AACF;AAEA,SAAS,mBAAmB,OAAwB;CAClD,IAAI,UAAU,MACZ,OAAO;CAGT,QAAQ,OAAO,OAAf;EACE,KAAK;EACL,KAAK,UACH,OAAO,OAAO,KAAK;EACrB,KAAK,UACH,OAAO,YAAY,KAAK;EAC1B,SACE,OAAO,YAAY,KAAK,UAAU,KAAK,CAAC;CAC5C;AACF;AAEA,SAAS,YAAY,KAAqB;CACxC,OAAO,IAAI,aAAa,GAAG,EAAE;AAC/B;AAEA,SAAS,aAAa,KAAqB;CACzC,OAAO,KAAK,UAAU,GAAG,CAAC,CAAC,MAAM,GAAG,EAAE;AACxC;;;ACvDA,MAAM,qBAAqB,IAAI,IAAI;CAAC;CAAS;CAAQ;CAAS;CAAQ;CAAa;AAAY,CAAC;AAEhG,MAAM,0BAA0B;AAOhC,SAAS,cAAc,OAAwB;CAC7C,OAAO,eAAe,KAAK,KAAK;AAClC;AAEA,SAAS,uBAAuB,OAAyB;CACvD,OAAO,MAAM,MAAM,uBAAuB,KAAK,CAAC;AAClD;AAEA,SAAS,0BAA0B,OAAuB;CACxD,IAAI,OAAO;CAEX,KAAK,MAAM,QAAQ,OAAO;EACxB,QAAQ,KAAK,YAAY,CAAC,KAAK;EAC/B,OAAO,KAAK,KAAK,MAAM,QAAQ;CACjC;CAEA,OAAO,KAAK,SAAS,EAAA,CAAG,SAAS,EAAE;AACrC;AAEA,SAAS,6BAA6B,OAAuB;CAC3D,MAAM,YAAY,MAAM,QAAQ,UAAU,EAAE;CAC5C,OAAO,UAAU,SAAS,IAAI,YAAY,0BAA0B,KAAK;AAC3E;AAEA,SAAS,WAAW,MAAsB;CACxC,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC;AACpD;AAEA,SAAS,kBAAkB,OAAuB;CAChD,MAAM,QAAQ,uBAAuB,KAAK;CAC1C,IAAI,MAAM,WAAW,GACnB,OAAO,WAAW,6BAA6B,KAAK,CAAC;CAEvD,OAAO,MAAM,IAAI,UAAU,CAAC,CAAC,KAAK,EAAE;AACtC;AAEA,SAAS,iBAAiB,OAAuB;CAC/C,MAAM,QAAQ,uBAAuB,KAAK;CAC1C,IAAI,MAAM,WAAW,GACnB,OAAO,6BAA6B,KAAK;CAE3C,MAAM,CAAC,YAAY,OAAO,GAAG,QAAQ;CACrC,OAAO,UAAU,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,UAAU,MAAM,CAAC,IAAI,KAAK,IAAI,UAAU,CAAC,CAAC,KAAK,EAAE;AAC9F;AAEA,SAAS,cAAc,MAAuB;CAC5C,OAAO,mBAAmB,IAAI,KAAK,YAAY,CAAC,KAAK,MAAM,KAAK,IAAI;AACtE;AAEA,SAAS,WAAW,MAAsB;CACxC,OAAO,IAAI;AACb;AAEA,SAAS,eAAe,MAAsB;CAC5C,OAAO,cAAc,IAAI,IAAI,WAAW,IAAI,IAAI;AAClD;AAEA,SAAgB,YAAY,WAA+B;CACzD,IAAI;CAEJ,IAAI,cAAc,SAAS,GACzB,OAAO,kBAAkB,SAAS;MAElC,OAAO,UAAU,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,UAAU,MAAM,CAAC;CAG9D,IAAI,cAAc,IAAI,GAEpB,OAAO;EAAE,MADO,WAAW,IACN;EAAG,KAAK;CAAU;CAGzC,IAAI,SAAS,WACX,OAAO;EAAE;EAAM,KAAK;CAAU;CAGhC,OAAO,EAAE,KAAK;AAChB;AAEA,SAAgB,YAAY,YAAgC;CAC1D,IAAI;CAEJ,IAAI,cAAc,UAAU,GAC1B,OAAO,iBAAiB,UAAU;MAElC,OAAO,WAAW,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,WAAW,MAAM,CAAC;CAGhE,IAAI,cAAc,IAAI,GAEpB,OAAO;EAAE,MADO,WAAW,IACN;EAAG,KAAK;CAAW;CAG1C,IAAI,SAAS,YACX,OAAO;EAAE;EAAM,KAAK;CAAW;CAGjC,OAAO,EAAE,KAAK;AAChB;AAEA,SAAgB,WAAW,YAAgC;CACzD,IAAI;CAEJ,IAAI,cAAc,UAAU,GAC1B,OAAO,kBAAkB,UAAU;MAEnC,OAAO,WAAW,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,WAAW,MAAM,CAAC;CAGhE,IAAI,cAAc,IAAI,GAEpB,OAAO;EAAE,MADO,WAAW,IACN;EAAG,KAAK;CAAW;CAG1C,IAAI,SAAS,YACX,OAAO;EAAE;EAAM,KAAK;CAAW;CAGjC,OAAO,EAAE,KAAK;AAChB;AAEA,SAAgB,UAAU,MAAsB;CAC9C,IACE,KAAK,SAAS,GAAG,KACjB,KAAK,SAAS,GAAG,KACjB,KAAK,SAAS,GAAG,KACjB,KAAK,SAAS,IAAI,KAClB,KAAK,SAAS,IAAI,GAElB,OAAO,GAAG,KAAK;CAEjB,IAAI,KAAK,SAAS,GAAG,KAAK,CAAC,aAAa,KAAK,IAAI,GAC/C,OAAO,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE;CAE9B,OAAO,GAAG,KAAK;AACjB;AAEA,SAAgB,wBACd,WACA,qBACQ;CACR,IAAI,UAAU,WAAW,GAAG;EAC1B,MAAM,CAAC,MAAM,uBAAuB;EACpC,MAAM,WAAW,IAAI,QAAQ,SAAS,EAAE,CAAC,CAAC,QAAQ,OAAO,EAAE;EAE3D,IAAI,SAAS,SAAS,KAAK,aAAa,KACtC,OAAO,eAAe,iBAAiB,QAAQ,CAAC;EAElD,OAAO,eAAe,iBAAiB,mBAAmB,CAAC;CAC7D;CAEA,OAAO,eAAe,iBAAiB,mBAAmB,CAAC;AAC7D;AAEA,SAAgB,4BAA4B,gBAAwB,YAA6B;CAC/F,MAAM,OAAO,eAAe,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,eAAe,MAAM,CAAC;CAC5E,OAAO,aAAa,OAAO,UAAU,IAAI;AAC3C;AAEA,SAAgB,gBAAgB,YAA4B;CAC1D,IAAI;CAEJ,IAAI,cAAc,UAAU,GAC1B,OAAO,kBAAkB,UAAU;MAEnC,OAAO,WAAW,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,WAAW,MAAM,CAAC;CAGhE,OAAO,eAAe,IAAI;AAC5B;;;AC/KA,MAAM,kBAAkB;AACxB,MAAM,uBAAuB;AAC7B,MAAM,0BAA0B;AAChC,MAAM,wBAAwB;AAC9B,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAM,eAAe;AACrB,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,eAAe;AACrB,MAAM,gBAAgB;AACtB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AACzB,MAAM,yBAAyB;AAE/B,SAAS,6BAA6B,MAAkC;CACtE,IAAI,qBAAqB,KAAK,IAAI,GAAG,OAAO;CAC5C,IAAI,wBAAwB,KAAK,IAAI,GAAG,OAAO;CAE/C,IAAI,CAAC,sBAAsB,KAAK,IAAI,GAAG,OAAO,KAAA;CAE9C,IAAI,QAAQ,KAAK,QAAQ,uBAAuB,EAAE,CAAC,CAAC,KAAK;CACzD,IAAI,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,GAC7C,QAAQ,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK;CAGlC,IAAI,qBAAqB,KAAK,KAAK,GAAG,OAAO;CAC7C,IAAI,wBAAwB,KAAK,KAAK,GAAG,OAAO;CAEhD,QAAQ,MAAM,QAAQ,kBAAkB,EAAE,CAAC,CAAC,KAAK;CACjD,IAAI,oBAAoB,KAAK,KAAK,GAAG,OAAO;AAG9C;AAEA,SAAgB,gBACd,YACA,YAC2B;CAC3B,MAAM,UAAU,WAAW,KAAK;CAChC,MAAM,iBAAiB,YAAY,YAAY;CAE/C,IAAI,gBAAgB,KAAK,OAAO,GAC9B,OAAO;EAAE,MAAM;EAAY,YAAY;CAAkB;CAG3D,MAAM,qBAAqB,6BAA6B,OAAO;CAC/D,IAAI,oBACF,OAAO;EAAE,MAAM;EAAY,YAAY;CAAmB;CAG5D,IAAI,aAAa,KAAK,OAAO,KAAK,kBAAkB,KAAK,OAAO,GAC9D,OAAO;EAAE,MAAM;EAAY,YAAY;CAAoB;CAG7D,IAAI,aAAa,KAAK,OAAO,GAC3B,OAAO;EAAE,MAAM;EAAW,OAAO;CAAK;CAGxC,IAAI,aAAa,KAAK,OAAO,GAC3B,OAAO;EAAE,MAAM;EAAW,OAAO;CAAK;CAGxC,IAAI,cAAc,KAAK,OAAO,GAC5B,OAAO;EAAE,MAAM;EAAW,OAAO;CAAM;CAGzC,IAAI,gBAAgB,KAAK,OAAO,GAC9B,OAAO;EAAE,MAAM;EAAW,OAAO,OAAO,OAAO;CAAE;CAGnD,MAAM,cAAc,QAAQ,MAAM,sBAAsB;CACxD,IAAI,cAAc,OAAO,KAAA,GAAW;EAClC,MAAM,YAAY,YAAY,EAAE,CAAC,QAAQ,OAAO,GAAG;EACnD,IAAI,mBAAmB,UAAU,mBAAmB,SAAS;GAC3D,IAAI,iBAAiB,KAAK,OAAO,GAC/B,OAAO;IAAE,MAAM;IAAY,YAAY;GAAQ;GAEjD,IAAI;IACF,OAAO;KAAE,MAAM;KAAW,OAAO,KAAK,MAAM,SAAS;IAAE;GACzD,QAAQ,CAER;EACF;EACA,OAAO;GAAE,MAAM;GAAW,OAAO;EAAU;CAC7C;CAEA,OAAO;EAAE,MAAM;EAAY,YAAY;CAAQ;AACjD;;;ACtFA,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAE1B,MAAM,yBAAiD;CACrD,UAAU;CACV,UAAU;CACV,SAAS;CACT,SAAS;CACT,YAAY;AACd;AAMA,SAAgB,eACd,QACA,cACmB;CACnB,MAAM,mCAAmB,IAAI,IAA6B;CAE1D,MAAM,gCAAgB,IAAI,IAAoB;CAC9C,KAAK,MAAM,SAAS,OAAO,OAAO,MAAM,GACtC,KAAK,MAAM,MAAM,MAAM,aAAa;EAClC,MAAM,UAAU,GAAG,MAAM,KAAK,GAAG,GAAG;EACpC,cAAc,IAAI,UAAU,cAAc,IAAI,OAAO,KAAK,KAAK,CAAC;CAClE;CAGF,MAAM,iCAAiB,IAAI,IAAyB;CACpD,KAAK,MAAM,SAAS,OAAO,OAAO,MAAM,GAAG;EACzC,MAAM,wBAAQ,IAAI,IAAY;EAC9B,KAAK,MAAM,OAAO,OAAO,OAAO,MAAM,OAAO,GAC3C,MAAM,IAAI,IAAI,IAAI;EAEpB,eAAe,IAAI,MAAM,MAAM,KAAK;CACtC;CAEA,KAAK,MAAM,SAAS,OAAO,OAAO,MAAM,GACtC,KAAK,MAAM,MAAM,MAAM,aAAa;EAClC,MAAM,iBAAiB,MAAM;EAC7B,MAAM,kBAAkB,GAAG;EAC3B,MAAM,YAAY,eAAe,IAAI,cAAc;EACnD,MAAM,iBAAiB,aAAa,IAAI,cAAc,KAAK;EAC3D,MAAM,kBAAkB,aAAa,IAAI,eAAe,KAAK;EAC7D,MAAM,UAAU,GAAG,eAAe,GAAG;EACrC,MAAM,iBAAiB,mBAAmB;EAC1C,MAAM,oBAAqB,cAAc,IAAI,OAAO,IAAe,KAAK;EAExE,MAAM,aAAa,eAAe,IAAI,KAAK;EAE3C,MAAM,oBAAoB,uBACxB,wBAAwB,GAAG,SAAS,eAAe,GACnD,WACA,eACF;EACA,MAAM,eAAe,oBACjB,mBAAmB,IAAI,mBAAmB,iBAAiB,cAAc,IACzE,KAAA;EAaJ,iBAAiB,kBAAkB,gBARb,wBACpB,mBACA,iBACA,IAPoB,GAAG,QAAQ,MAC9B,eAAe,MAAM,QAAQ,WAAW,EAAE,YAAY,KAO3C,GACZ,YAG6D,CAAC;EAChE,UAAU,IAAI,iBAAiB;EAE/B,MAAM,aAAa,eAAe,IAAI,eAAe,qBAAK,IAAI,IAAI;EAClE,eAAe,IAAI,iBAAiB,UAAU;EAE9C,MAAM,mBAAmB,uBACvB,4BAA4B,gBAAgB,UAAU,GACtD,YACA,cACF;EAUA,iBAAiB,kBAAkB,iBAAiB;GAPlD,WAAW;GACX,UAAU;GACV,UAAU;GACV,MAAM,CAAC;GACP;EAG6D,CAAC;EAChE,WAAW,IAAI,gBAAgB;CACjC;CAGF,OAAO,EAAE,iBAAiB;AAC5B;AAEA,SAAS,eAAe,IAAqB,OAA4B;CACvE,MAAM,SAAS,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC,KAAK;CAEpC,IAAI,MAAM,YAAY;EACpB,MAAM,SAAS,CAAC,GAAG,MAAM,WAAW,OAAO,CAAC,CAAC,KAAK;EAClD,IAAI,OAAO,WAAW,OAAO,UAAU,OAAO,OAAO,GAAG,MAAM,MAAM,OAAO,EAAE,GAC3E,OAAO;CAEX;CAEA,KAAK,MAAM,UAAU,MAAM,SAAS;EAClC,MAAM,aAAa,CAAC,GAAG,OAAO,OAAO,CAAC,CAAC,KAAK;EAC5C,IAAI,WAAW,WAAW,OAAO,UAAU,WAAW,OAAO,GAAG,MAAM,MAAM,OAAO,EAAE,GACnF,OAAO;CAEX;CAEA,OAAO;AACT;AAEA,SAAS,mBACP,IACA,wBACA,iBACA,gBACQ;CACR,IAAI,GAAG,MACL,OAAO,GAAG;CAEZ,IAAI,gBACF,OAAO,GAAG,uBAAuB,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,uBAAuB,MAAM,CAAC,IAAI,UAAU,eAAe;CAExH,OAAO,GAAG,QAAQ,KAAK,GAAG;AAC5B;;;;;;;;;AAUA,SAAgB,wBACd,WACA,iBACA,IACA,UACA,cACe;CACf,MAAM,WAAW,GAAG,YAAY,GAAG,aAAa,oBAAoB,GAAG,WAAW,KAAA;CAClF,MAAM,WAAW,GAAG,YAAY,GAAG,aAAa,oBAAoB,GAAG,WAAW,KAAA;CAElF,OAAO;EACL;EACA,UAAU;EACV,qBAAqB,GAAG;EACxB;EACA,MAAM;EACN;EACA,QAAQ,GAAG;EACX,QAAQ,GAAG;EACX,YAAY,GAAG;EACf,UAAU,WAAW,uBAAuB,YAAY,KAAA;EACxD,UAAU,WAAW,uBAAuB,YAAY,KAAA;CAC1D;AACF;AAEA,SAAS,uBACP,SACA,WACA,gBACQ;CACR,IAAI,CAAC,UAAU,IAAI,OAAO,GACxB,OAAO;CAGT,MAAM,aAAa,GAAG,UAAU;CAChC,IAAI,CAAC,UAAU,IAAI,UAAU,GAC3B,OAAO;CAGT,IAAI,UAAU;CACd,OAAO,UAAU,IAAI,GAAG,UAAU,SAAS,GACzC;CAEF,OAAO,GAAG,UAAU;AACtB;AAEA,SAAS,iBACP,KACA,WACA,OACM;CACN,MAAM,WAAW,IAAI,IAAI,SAAS;CAClC,IAAI,UACF,SAAS,KAAK,KAAK;MAEnB,IAAI,IAAI,WAAW,CAAC,KAAK,CAAC;AAE9B"}
@@ -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"}