@prisma-next/sql-contract-psl 0.3.0-dev.128 → 0.3.0-dev.146

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.
@@ -1,8 +1,191 @@
1
- import { defineContract } from "@prisma-next/sql-contract-ts/contract-builder";
2
- import { assertDefined, invariant } from "@prisma-next/utils/assertions";
1
+ import { instantiateAuthoringTypeConstructor, isAuthoringTypeConstructorDescriptor } from "@prisma-next/framework-components/authoring";
2
+ import { buildSqlContractFromDefinition } from "@prisma-next/sql-contract-ts/contract-builder";
3
3
  import { ifDefined } from "@prisma-next/utils/defined";
4
4
  import { notOk, ok } from "@prisma-next/utils/result";
5
+ import { getPositionalArgument, parseQuotedStringLiteral } from "@prisma-next/psl-parser";
6
+ import { assertDefined, invariant } from "@prisma-next/utils/assertions";
7
+
8
+ //#region src/psl-attribute-parsing.ts
9
+ function lowerFirst(value) {
10
+ if (value.length === 0) return value;
11
+ return value[0]?.toLowerCase() + value.slice(1);
12
+ }
13
+ function getAttribute(attributes, name) {
14
+ return attributes?.find((attribute) => attribute.name === name);
15
+ }
16
+ function getNamedArgument(attribute, name) {
17
+ const entry = attribute.args.find((arg) => arg.kind === "named" && arg.name === name);
18
+ if (!entry || entry.kind !== "named") return;
19
+ return entry.value;
20
+ }
21
+ function getPositionalArgumentEntry(attribute, index = 0) {
22
+ const entry = attribute.args.filter((arg) => arg.kind === "positional")[index];
23
+ if (!entry || entry.kind !== "positional") return;
24
+ return {
25
+ value: entry.value,
26
+ span: entry.span
27
+ };
28
+ }
29
+ function unquoteStringLiteral(value) {
30
+ const trimmed = value.trim();
31
+ const match = trimmed.match(/^(['"])(.*)\1$/);
32
+ if (!match) return trimmed;
33
+ return match[2] ?? "";
34
+ }
35
+ function parseFieldList(value) {
36
+ const trimmed = value.trim();
37
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return;
38
+ return trimmed.slice(1, -1).split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
39
+ }
40
+ function parseMapName(input) {
41
+ if (!input.attribute) return input.defaultValue;
42
+ const value = getPositionalArgument(input.attribute);
43
+ if (!value) {
44
+ input.diagnostics.push({
45
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
46
+ message: `${input.entityLabel} @map requires a positional quoted string literal argument`,
47
+ sourceId: input.sourceId,
48
+ span: input.attribute.span
49
+ });
50
+ return input.defaultValue;
51
+ }
52
+ const parsed = parseQuotedStringLiteral(value);
53
+ if (parsed === void 0) {
54
+ input.diagnostics.push({
55
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
56
+ message: `${input.entityLabel} @map requires a positional quoted string literal argument`,
57
+ sourceId: input.sourceId,
58
+ span: input.attribute.span
59
+ });
60
+ return input.defaultValue;
61
+ }
62
+ return parsed;
63
+ }
64
+ function parseConstraintMapArgument(input) {
65
+ if (!input.attribute) return;
66
+ const raw = getNamedArgument(input.attribute, "map");
67
+ if (!raw) return;
68
+ const parsed = parseQuotedStringLiteral(raw);
69
+ if (parsed !== void 0) return parsed;
70
+ input.diagnostics.push({
71
+ code: input.code,
72
+ message: `${input.entityLabel} map argument must be a quoted string literal`,
73
+ sourceId: input.sourceId,
74
+ span: input.span
75
+ });
76
+ }
77
+ function getPositionalArguments(attribute) {
78
+ return attribute.args.filter((arg) => arg.kind === "positional").map((arg) => arg.kind === "positional" ? arg.value : "");
79
+ }
80
+ function pushInvalidAttributeArgument(input) {
81
+ input.diagnostics.push({
82
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
83
+ message: input.message,
84
+ sourceId: input.sourceId,
85
+ span: input.span
86
+ });
87
+ }
88
+ function parseOptionalSingleIntegerArgument(input) {
89
+ if (input.attribute.args.some((arg) => arg.kind === "named")) return pushInvalidAttributeArgument({
90
+ diagnostics: input.diagnostics,
91
+ sourceId: input.sourceId,
92
+ span: input.attribute.span,
93
+ message: `${input.entityLabel} @${input.attribute.name} accepts zero or one positional integer argument.`
94
+ });
95
+ const positionalArguments = getPositionalArguments(input.attribute);
96
+ if (positionalArguments.length > 1) return pushInvalidAttributeArgument({
97
+ diagnostics: input.diagnostics,
98
+ sourceId: input.sourceId,
99
+ span: input.attribute.span,
100
+ message: `${input.entityLabel} @${input.attribute.name} accepts zero or one positional integer argument.`
101
+ });
102
+ if (positionalArguments.length === 0) return null;
103
+ const parsed = Number(unquoteStringLiteral(positionalArguments[0] ?? ""));
104
+ if (!Number.isInteger(parsed) || parsed < input.minimum) return pushInvalidAttributeArgument({
105
+ diagnostics: input.diagnostics,
106
+ sourceId: input.sourceId,
107
+ span: input.attribute.span,
108
+ message: `${input.entityLabel} @${input.attribute.name} requires a ${input.valueLabel}.`
109
+ });
110
+ return parsed;
111
+ }
112
+ function parseOptionalNumericArguments(input) {
113
+ if (input.attribute.args.some((arg) => arg.kind === "named")) return pushInvalidAttributeArgument({
114
+ diagnostics: input.diagnostics,
115
+ sourceId: input.sourceId,
116
+ span: input.attribute.span,
117
+ message: `${input.entityLabel} @${input.attribute.name} accepts zero, one, or two positional integer arguments.`
118
+ });
119
+ const positionalArguments = getPositionalArguments(input.attribute);
120
+ if (positionalArguments.length > 2) return pushInvalidAttributeArgument({
121
+ diagnostics: input.diagnostics,
122
+ sourceId: input.sourceId,
123
+ span: input.attribute.span,
124
+ message: `${input.entityLabel} @${input.attribute.name} accepts zero, one, or two positional integer arguments.`
125
+ });
126
+ if (positionalArguments.length === 0) return null;
127
+ const precision = Number(unquoteStringLiteral(positionalArguments[0] ?? ""));
128
+ if (!Number.isInteger(precision) || precision < 1) return pushInvalidAttributeArgument({
129
+ diagnostics: input.diagnostics,
130
+ sourceId: input.sourceId,
131
+ span: input.attribute.span,
132
+ message: `${input.entityLabel} @${input.attribute.name} requires a positive integer precision.`
133
+ });
134
+ if (positionalArguments.length === 1) return { precision };
135
+ const scale = Number(unquoteStringLiteral(positionalArguments[1] ?? ""));
136
+ if (!Number.isInteger(scale) || scale < 0) return pushInvalidAttributeArgument({
137
+ diagnostics: input.diagnostics,
138
+ sourceId: input.sourceId,
139
+ span: input.attribute.span,
140
+ message: `${input.entityLabel} @${input.attribute.name} requires a non-negative integer scale.`
141
+ });
142
+ return {
143
+ precision,
144
+ scale
145
+ };
146
+ }
147
+ function parseAttributeFieldList(input) {
148
+ const raw = getNamedArgument(input.attribute, "fields") ?? getPositionalArgument(input.attribute);
149
+ if (!raw) {
150
+ input.diagnostics.push({
151
+ code: input.code,
152
+ message: `${input.messagePrefix} requires fields list argument`,
153
+ sourceId: input.sourceId,
154
+ span: input.attribute.span
155
+ });
156
+ return;
157
+ }
158
+ const fields = parseFieldList(raw);
159
+ if (!fields || fields.length === 0) {
160
+ input.diagnostics.push({
161
+ code: input.code,
162
+ message: `${input.messagePrefix} requires bracketed field list argument`,
163
+ sourceId: input.sourceId,
164
+ span: input.attribute.span
165
+ });
166
+ return;
167
+ }
168
+ return fields;
169
+ }
170
+ function mapFieldNamesToColumns(input) {
171
+ const columns = [];
172
+ for (const fieldName of input.fieldNames) {
173
+ const columnName = input.mapping.fieldColumns.get(fieldName);
174
+ if (!columnName) {
175
+ input.diagnostics.push({
176
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
177
+ message: `${input.contextLabel} references unknown field "${input.modelName}.${fieldName}"`,
178
+ sourceId: input.sourceId,
179
+ span: input.span
180
+ });
181
+ return;
182
+ }
183
+ columns.push(columnName);
184
+ }
185
+ return columns;
186
+ }
5
187
 
188
+ //#endregion
6
189
  //#region src/default-function-registry.ts
7
190
  function resolveSpanPositionFromBase(base, text, offset) {
8
191
  const safeOffset = Math.min(Math.max(0, offset), text.length);
@@ -142,267 +325,21 @@ function lowerDefaultFunctionWithRegistry(input) {
142
325
  }
143
326
 
144
327
  //#endregion
145
- //#region src/interpreter.ts
146
- const REFERENTIAL_ACTION_MAP = {
147
- NoAction: "noAction",
148
- Restrict: "restrict",
149
- Cascade: "cascade",
150
- SetNull: "setNull",
151
- SetDefault: "setDefault",
152
- noAction: "noAction",
153
- restrict: "restrict",
154
- cascade: "cascade",
155
- setNull: "setNull",
156
- setDefault: "setDefault"
157
- };
158
- function fkRelationPairKey(declaringModelName, targetModelName) {
159
- return `${declaringModelName}::${targetModelName}`;
160
- }
161
- function lowerFirst(value) {
162
- if (value.length === 0) return value;
163
- return value[0]?.toLowerCase() + value.slice(1);
164
- }
165
- function getAttribute(attributes, name) {
166
- return attributes?.find((attribute) => attribute.name === name);
167
- }
168
- function getNamedArgument(attribute, name) {
169
- const entry = attribute.args.find((arg) => arg.kind === "named" && arg.name === name);
170
- if (!entry || entry.kind !== "named") return;
171
- return entry.value;
172
- }
173
- function getPositionalArgument(attribute, index = 0) {
174
- const entry = attribute.args.filter((arg) => arg.kind === "positional")[index];
175
- if (!entry || entry.kind !== "positional") return;
176
- return entry.value;
177
- }
178
- function getPositionalArgumentEntry(attribute, index = 0) {
179
- const entry = attribute.args.filter((arg) => arg.kind === "positional")[index];
180
- if (!entry || entry.kind !== "positional") return;
328
+ //#region src/psl-column-resolution.ts
329
+ function toNamedTypeFieldDescriptor(typeRef, descriptor) {
181
330
  return {
182
- value: entry.value,
183
- span: entry.span
184
- };
185
- }
186
- function unquoteStringLiteral(value) {
187
- const trimmed = value.trim();
188
- const match = trimmed.match(/^(['"])(.*)\1$/);
189
- if (!match) return trimmed;
190
- return match[2] ?? "";
191
- }
192
- function parseQuotedStringLiteral(value) {
193
- const match = value.trim().match(/^(['"])(.*)\1$/);
194
- if (!match) return;
195
- return match[2] ?? "";
196
- }
197
- function parseFieldList(value) {
198
- const trimmed = value.trim();
199
- if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return;
200
- return trimmed.slice(1, -1).split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
201
- }
202
- function parseDefaultLiteralValue(expression) {
203
- const trimmed = expression.trim();
204
- if (trimmed === "true" || trimmed === "false") return {
205
- kind: "literal",
206
- value: trimmed === "true"
207
- };
208
- const numericValue = Number(trimmed);
209
- if (!Number.isNaN(numericValue) && trimmed.length > 0 && !/^(['"]).*\1$/.test(trimmed)) return {
210
- kind: "literal",
211
- value: numericValue
212
- };
213
- if (/^(['"]).*\1$/.test(trimmed)) return {
214
- kind: "literal",
215
- value: unquoteStringLiteral(trimmed)
331
+ codecId: descriptor.codecId,
332
+ nativeType: descriptor.nativeType,
333
+ typeRef
216
334
  };
217
335
  }
218
- function lowerDefaultForField(input) {
219
- const positionalEntries = input.defaultAttribute.args.filter((arg) => arg.kind === "positional");
220
- if (input.defaultAttribute.args.filter((arg) => arg.kind === "named").length > 0 || positionalEntries.length !== 1) {
221
- input.diagnostics.push({
222
- code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
223
- message: `Field "${input.modelName}.${input.fieldName}" requires exactly one positional @default(...) expression.`,
224
- sourceId: input.sourceId,
225
- span: input.defaultAttribute.span
226
- });
227
- return {};
336
+ function getAuthoringTypeConstructor(contributions, path) {
337
+ let current = contributions?.type;
338
+ for (const segment of path) {
339
+ if (typeof current !== "object" || current === null || Array.isArray(current)) return;
340
+ current = current[segment];
228
341
  }
229
- const expressionEntry = getPositionalArgumentEntry(input.defaultAttribute);
230
- if (!expressionEntry) {
231
- input.diagnostics.push({
232
- code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
233
- message: `Field "${input.modelName}.${input.fieldName}" requires a positional @default(...) expression.`,
234
- sourceId: input.sourceId,
235
- span: input.defaultAttribute.span
236
- });
237
- return {};
238
- }
239
- const literalDefault = parseDefaultLiteralValue(expressionEntry.value);
240
- if (literalDefault) return { defaultValue: literalDefault };
241
- const defaultFunctionCall = parseDefaultFunctionCall(expressionEntry.value, expressionEntry.span);
242
- if (!defaultFunctionCall) {
243
- input.diagnostics.push({
244
- code: "PSL_INVALID_DEFAULT_VALUE",
245
- message: `Unsupported default value "${expressionEntry.value}"`,
246
- sourceId: input.sourceId,
247
- span: input.defaultAttribute.span
248
- });
249
- return {};
250
- }
251
- const lowered = lowerDefaultFunctionWithRegistry({
252
- call: defaultFunctionCall,
253
- registry: input.defaultFunctionRegistry,
254
- context: {
255
- sourceId: input.sourceId,
256
- modelName: input.modelName,
257
- fieldName: input.fieldName,
258
- columnCodecId: input.columnDescriptor.codecId
259
- }
260
- });
261
- if (!lowered.ok) {
262
- input.diagnostics.push(lowered.diagnostic);
263
- return {};
264
- }
265
- if (lowered.value.kind === "storage") return { defaultValue: lowered.value.defaultValue };
266
- const generatorDescriptor = input.generatorDescriptorById.get(lowered.value.generated.id);
267
- if (!generatorDescriptor) {
268
- input.diagnostics.push({
269
- code: "PSL_INVALID_DEFAULT_APPLICABILITY",
270
- message: `Default generator "${lowered.value.generated.id}" is not available in the composed mutation default registry.`,
271
- sourceId: input.sourceId,
272
- span: expressionEntry.span
273
- });
274
- return {};
275
- }
276
- if (!generatorDescriptor.applicableCodecIds.includes(input.columnDescriptor.codecId)) {
277
- input.diagnostics.push({
278
- code: "PSL_INVALID_DEFAULT_APPLICABILITY",
279
- message: `Default generator "${generatorDescriptor.id}" is not applicable to "${input.modelName}.${input.fieldName}" with codecId "${input.columnDescriptor.codecId}".`,
280
- sourceId: input.sourceId,
281
- span: expressionEntry.span
282
- });
283
- return {};
284
- }
285
- return { executionDefault: lowered.value.generated };
286
- }
287
- function parseMapName(input) {
288
- if (!input.attribute) return input.defaultValue;
289
- const value = getPositionalArgument(input.attribute);
290
- if (!value) {
291
- input.diagnostics.push({
292
- code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
293
- message: `${input.entityLabel} @map requires a positional quoted string literal argument`,
294
- sourceId: input.sourceId,
295
- span: input.attribute.span
296
- });
297
- return input.defaultValue;
298
- }
299
- const parsed = parseQuotedStringLiteral(value);
300
- if (parsed === void 0) {
301
- input.diagnostics.push({
302
- code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
303
- message: `${input.entityLabel} @map requires a positional quoted string literal argument`,
304
- sourceId: input.sourceId,
305
- span: input.attribute.span
306
- });
307
- return input.defaultValue;
308
- }
309
- return parsed;
310
- }
311
- function parsePgvectorLength(input) {
312
- const namedLength = getNamedArgument(input.attribute, "length");
313
- const namedDim = getNamedArgument(input.attribute, "dim");
314
- const positional = getPositionalArgument(input.attribute);
315
- const raw = namedLength ?? namedDim ?? positional;
316
- if (!raw) {
317
- input.diagnostics.push({
318
- code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
319
- message: "@pgvector.column requires length/dim argument",
320
- sourceId: input.sourceId,
321
- span: input.attribute.span
322
- });
323
- return;
324
- }
325
- const parsed = Number(unquoteStringLiteral(raw));
326
- if (!Number.isInteger(parsed) || parsed < 1) {
327
- input.diagnostics.push({
328
- code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
329
- message: "@pgvector.column length/dim must be a positive integer",
330
- sourceId: input.sourceId,
331
- span: input.attribute.span
332
- });
333
- return;
334
- }
335
- return parsed;
336
- }
337
- function getPositionalArguments(attribute) {
338
- return attribute.args.filter((arg) => arg.kind === "positional").map((arg) => arg.kind === "positional" ? arg.value : "");
339
- }
340
- function pushInvalidAttributeArgument(input) {
341
- input.diagnostics.push({
342
- code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
343
- message: input.message,
344
- sourceId: input.sourceId,
345
- span: input.span
346
- });
347
- }
348
- function parseOptionalSingleIntegerArgument(input) {
349
- if (input.attribute.args.some((arg) => arg.kind === "named")) return pushInvalidAttributeArgument({
350
- diagnostics: input.diagnostics,
351
- sourceId: input.sourceId,
352
- span: input.attribute.span,
353
- message: `${input.entityLabel} @${input.attribute.name} accepts zero or one positional integer argument.`
354
- });
355
- const positionalArguments = getPositionalArguments(input.attribute);
356
- if (positionalArguments.length > 1) return pushInvalidAttributeArgument({
357
- diagnostics: input.diagnostics,
358
- sourceId: input.sourceId,
359
- span: input.attribute.span,
360
- message: `${input.entityLabel} @${input.attribute.name} accepts zero or one positional integer argument.`
361
- });
362
- if (positionalArguments.length === 0) return null;
363
- const parsed = Number(unquoteStringLiteral(positionalArguments[0] ?? ""));
364
- if (!Number.isInteger(parsed) || parsed < input.minimum) return pushInvalidAttributeArgument({
365
- diagnostics: input.diagnostics,
366
- sourceId: input.sourceId,
367
- span: input.attribute.span,
368
- message: `${input.entityLabel} @${input.attribute.name} requires a ${input.valueLabel}.`
369
- });
370
- return parsed;
371
- }
372
- function parseOptionalNumericArguments(input) {
373
- if (input.attribute.args.some((arg) => arg.kind === "named")) return pushInvalidAttributeArgument({
374
- diagnostics: input.diagnostics,
375
- sourceId: input.sourceId,
376
- span: input.attribute.span,
377
- message: `${input.entityLabel} @${input.attribute.name} accepts zero, one, or two positional integer arguments.`
378
- });
379
- const positionalArguments = getPositionalArguments(input.attribute);
380
- if (positionalArguments.length > 2) return pushInvalidAttributeArgument({
381
- diagnostics: input.diagnostics,
382
- sourceId: input.sourceId,
383
- span: input.attribute.span,
384
- message: `${input.entityLabel} @${input.attribute.name} accepts zero, one, or two positional integer arguments.`
385
- });
386
- if (positionalArguments.length === 0) return null;
387
- const precision = Number(unquoteStringLiteral(positionalArguments[0] ?? ""));
388
- if (!Number.isInteger(precision) || precision < 1) return pushInvalidAttributeArgument({
389
- diagnostics: input.diagnostics,
390
- sourceId: input.sourceId,
391
- span: input.attribute.span,
392
- message: `${input.entityLabel} @${input.attribute.name} requires a positive integer precision.`
393
- });
394
- if (positionalArguments.length === 1) return { precision };
395
- const scale = Number(unquoteStringLiteral(positionalArguments[1] ?? ""));
396
- if (!Number.isInteger(scale) || scale < 0) return pushInvalidAttributeArgument({
397
- diagnostics: input.diagnostics,
398
- sourceId: input.sourceId,
399
- span: input.attribute.span,
400
- message: `${input.entityLabel} @${input.attribute.name} requires a non-negative integer scale.`
401
- });
402
- return {
403
- precision,
404
- scale
405
- };
342
+ return isAuthoringTypeConstructorDescriptor(current) ? current : void 0;
406
343
  }
407
344
  const NATIVE_TYPE_SPECS = {
408
345
  "db.VarChar": {
@@ -555,281 +492,275 @@ function resolveDbNativeTypeAttribute(input) {
555
492
  }
556
493
  }
557
494
  }
558
- function resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors) {
559
- if (field.typeRef && namedTypeDescriptors.has(field.typeRef)) return namedTypeDescriptors.get(field.typeRef);
560
- if (namedTypeDescriptors.has(field.typeName)) return namedTypeDescriptors.get(field.typeName);
561
- if (enumTypeDescriptors.has(field.typeName)) return enumTypeDescriptors.get(field.typeName);
562
- return scalarTypeDescriptors.get(field.typeName);
563
- }
564
- function collectResolvedFields(model, mapping, enumTypeDescriptors, namedTypeDescriptors, namedTypeBaseTypes, modelNames, composedExtensions, defaultFunctionRegistry, generatorDescriptorById, diagnostics, sourceId, scalarTypeDescriptors) {
565
- const resolvedFields = [];
566
- for (const field of model.fields) {
567
- if (field.list) {
568
- if (modelNames.has(field.typeName)) continue;
569
- diagnostics.push({
570
- code: "PSL_UNSUPPORTED_FIELD_LIST",
571
- message: `Field "${model.name}.${field.name}" uses a scalar/storage list type, which is not supported in SQL PSL provider v1. Model-typed lists are only supported as backrelation navigation fields when they match an FK-side relation.`,
572
- sourceId,
573
- span: field.span
574
- });
575
- continue;
576
- }
577
- for (const attribute of field.attributes) {
578
- if (attribute.name === "id" || attribute.name === "unique" || attribute.name === "default" || attribute.name === "relation" || attribute.name === "map" || attribute.name === "pgvector.column") continue;
579
- if (attribute.name.startsWith("pgvector.") && !composedExtensions.has("pgvector")) {
580
- diagnostics.push({
581
- code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
582
- message: `Attribute "@${attribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
583
- sourceId,
584
- span: attribute.span
585
- });
586
- continue;
587
- }
588
- diagnostics.push({
589
- code: "PSL_UNSUPPORTED_FIELD_ATTRIBUTE",
590
- message: `Field "${model.name}.${field.name}" uses unsupported attribute "@${attribute.name}"`,
591
- sourceId,
592
- span: attribute.span
593
- });
594
- }
595
- if (getAttribute(field.attributes, "relation") && modelNames.has(field.typeName)) continue;
596
- let descriptor = resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors);
597
- const pgvectorColumnAttribute = getAttribute(field.attributes, "pgvector.column");
598
- if (pgvectorColumnAttribute) if (!composedExtensions.has("pgvector")) diagnostics.push({
599
- code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
600
- message: "Attribute \"@pgvector.column\" uses unrecognized namespace \"pgvector\". Add extension pack \"pgvector\" to extensionPacks in prisma-next.config.ts.",
601
- sourceId,
602
- span: pgvectorColumnAttribute.span
495
+ function parsePgvectorLength(input) {
496
+ const namedLength = getNamedArgument(input.attribute, "length");
497
+ const namedDim = getNamedArgument(input.attribute, "dim");
498
+ const positional = getPositionalArgument(input.attribute);
499
+ const raw = namedLength ?? namedDim ?? positional;
500
+ if (!raw) {
501
+ input.diagnostics.push({
502
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
503
+ message: "@pgvector.column requires length/dim argument",
504
+ sourceId: input.sourceId,
505
+ span: input.attribute.span
603
506
  });
604
- else if (!(field.typeName === "Bytes" || namedTypeBaseTypes.get(field.typeRef ?? field.typeName) === "Bytes")) diagnostics.push({
507
+ return;
508
+ }
509
+ const parsed = Number(unquoteStringLiteral(raw));
510
+ if (!Number.isInteger(parsed) || parsed < 1) {
511
+ input.diagnostics.push({
605
512
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
606
- message: `Field "${model.name}.${field.name}" uses @pgvector.column on unsupported base type "${field.typeName}"`,
607
- sourceId,
608
- span: pgvectorColumnAttribute.span
609
- });
610
- else {
611
- const length = parsePgvectorLength({
612
- attribute: pgvectorColumnAttribute,
613
- diagnostics,
614
- sourceId
615
- });
616
- if (length !== void 0) descriptor = {
617
- codecId: "pg/vector@1",
618
- nativeType: "vector",
619
- typeParams: { length }
620
- };
621
- }
622
- if (!descriptor) {
623
- diagnostics.push({
624
- code: "PSL_UNSUPPORTED_FIELD_TYPE",
625
- message: `Field "${model.name}.${field.name}" type "${field.typeName}" is not supported in SQL PSL provider v1`,
626
- sourceId,
627
- span: field.span
628
- });
629
- continue;
630
- }
631
- const defaultAttribute = getAttribute(field.attributes, "default");
632
- const loweredDefault = defaultAttribute ? lowerDefaultForField({
633
- modelName: model.name,
634
- fieldName: field.name,
635
- defaultAttribute,
636
- columnDescriptor: descriptor,
637
- generatorDescriptorById,
638
- sourceId,
639
- defaultFunctionRegistry,
640
- diagnostics
641
- }) : {};
642
- if (field.optional && loweredDefault.executionDefault) {
643
- const generatorDescription = loweredDefault.executionDefault.kind === "generator" ? `"${loweredDefault.executionDefault.id}"` : "for this field";
644
- diagnostics.push({
645
- code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
646
- message: `Field "${model.name}.${field.name}" cannot be optional when using execution default ${generatorDescription}. Remove "?" or use a storage default.`,
647
- sourceId,
648
- span: defaultAttribute?.span ?? field.span
649
- });
650
- continue;
651
- }
652
- if (loweredDefault.executionDefault) {
653
- const generatedDescriptor = generatorDescriptorById.get(loweredDefault.executionDefault.id)?.resolveGeneratedColumnDescriptor?.({ generated: loweredDefault.executionDefault });
654
- if (generatedDescriptor) descriptor = generatedDescriptor;
655
- }
656
- const mappedColumnName = mapping.fieldColumns.get(field.name) ?? field.name;
657
- resolvedFields.push({
658
- field,
659
- columnName: mappedColumnName,
660
- descriptor,
661
- ...ifDefined("defaultValue", loweredDefault.defaultValue),
662
- ...ifDefined("executionDefault", loweredDefault.executionDefault),
663
- isId: Boolean(getAttribute(field.attributes, "id")),
664
- isUnique: Boolean(getAttribute(field.attributes, "unique"))
513
+ message: "@pgvector.column length/dim must be a positive integer",
514
+ sourceId: input.sourceId,
515
+ span: input.attribute.span
665
516
  });
517
+ return;
666
518
  }
667
- return resolvedFields;
668
- }
669
- function hasSameSpan(a, b) {
670
- return a.start.offset === b.start.offset && a.end.offset === b.end.offset && a.start.line === b.start.line && a.end.line === b.end.line;
519
+ return parsed;
671
520
  }
672
- function compareStrings(left, right) {
673
- if (left < right) return -1;
674
- if (left > right) return 1;
675
- return 0;
521
+ function parseDefaultLiteralValue(expression) {
522
+ const trimmed = expression.trim();
523
+ if (trimmed === "true" || trimmed === "false") return {
524
+ kind: "literal",
525
+ value: trimmed === "true"
526
+ };
527
+ const numericValue = Number(trimmed);
528
+ if (!Number.isNaN(numericValue) && trimmed.length > 0 && !/^(['"]).*\1$/.test(trimmed)) return {
529
+ kind: "literal",
530
+ value: numericValue
531
+ };
532
+ if (/^(['"]).*\1$/.test(trimmed)) return {
533
+ kind: "literal",
534
+ value: unquoteStringLiteral(trimmed)
535
+ };
676
536
  }
677
- function indexFkRelations(input) {
678
- const modelRelations = /* @__PURE__ */ new Map();
679
- const fkRelationsByPair = /* @__PURE__ */ new Map();
680
- for (const relation of input.fkRelationMetadata) {
681
- const existing = modelRelations.get(relation.declaringModelName);
682
- const current = existing ?? [];
683
- if (!existing) modelRelations.set(relation.declaringModelName, current);
684
- current.push({
685
- fieldName: relation.declaringFieldName,
686
- toModel: relation.targetModelName,
687
- toTable: relation.targetTableName,
688
- cardinality: "N:1",
689
- parentTable: relation.declaringTableName,
690
- parentColumns: relation.localColumns,
691
- childTable: relation.targetTableName,
692
- childColumns: relation.referencedColumns
537
+ function lowerDefaultForField(input) {
538
+ const positionalEntries = input.defaultAttribute.args.filter((arg) => arg.kind === "positional");
539
+ if (input.defaultAttribute.args.filter((arg) => arg.kind === "named").length > 0 || positionalEntries.length !== 1) {
540
+ input.diagnostics.push({
541
+ code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
542
+ message: `Field "${input.modelName}.${input.fieldName}" requires exactly one positional @default(...) expression.`,
543
+ sourceId: input.sourceId,
544
+ span: input.defaultAttribute.span
693
545
  });
694
- const pairKey = fkRelationPairKey(relation.declaringModelName, relation.targetModelName);
695
- const pairRelations = fkRelationsByPair.get(pairKey);
696
- if (!pairRelations) {
697
- fkRelationsByPair.set(pairKey, [relation]);
698
- continue;
699
- }
700
- pairRelations.push(relation);
546
+ return {};
701
547
  }
702
- return {
703
- modelRelations,
704
- fkRelationsByPair
705
- };
706
- }
707
- function applyBackrelationCandidates(input) {
708
- for (const candidate of input.backrelationCandidates) {
709
- const pairKey = fkRelationPairKey(candidate.targetModelName, candidate.modelName);
710
- const pairMatches = input.fkRelationsByPair.get(pairKey) ?? [];
711
- const matches = candidate.relationName ? pairMatches.filter((relation) => relation.relationName === candidate.relationName) : [...pairMatches];
712
- if (matches.length === 0) {
713
- input.diagnostics.push({
714
- code: "PSL_ORPHANED_BACKRELATION_LIST",
715
- message: `Backrelation list field "${candidate.modelName}.${candidate.field.name}" has no matching FK-side relation on model "${candidate.targetModelName}". Add @relation(fields: [...], references: [...]) on the FK-side relation or use an explicit join model for many-to-many.`,
716
- sourceId: input.sourceId,
717
- span: candidate.field.span
718
- });
719
- continue;
720
- }
721
- if (matches.length > 1) {
722
- input.diagnostics.push({
723
- code: "PSL_AMBIGUOUS_BACKRELATION_LIST",
724
- message: `Backrelation list field "${candidate.modelName}.${candidate.field.name}" matches multiple FK-side relations on model "${candidate.targetModelName}". Add @relation(name: "...") (or @relation("...")) to both sides to disambiguate.`,
725
- sourceId: input.sourceId,
726
- span: candidate.field.span
727
- });
728
- continue;
729
- }
730
- invariant(matches.length === 1, "Backrelation matching requires exactly one match");
731
- const matched = matches[0];
732
- assertDefined(matched, "Backrelation matching requires a defined relation match");
733
- const existing = input.modelRelations.get(candidate.modelName);
734
- const current = existing ?? [];
735
- if (!existing) input.modelRelations.set(candidate.modelName, current);
736
- current.push({
737
- fieldName: candidate.field.name,
738
- toModel: matched.declaringModelName,
739
- toTable: matched.declaringTableName,
740
- cardinality: "1:N",
741
- parentTable: candidate.tableName,
742
- parentColumns: matched.referencedColumns,
743
- childTable: matched.declaringTableName,
744
- childColumns: matched.localColumns
548
+ const expressionEntry = getPositionalArgumentEntry(input.defaultAttribute);
549
+ if (!expressionEntry) {
550
+ input.diagnostics.push({
551
+ code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
552
+ message: `Field "${input.modelName}.${input.fieldName}" requires a positional @default(...) expression.`,
553
+ sourceId: input.sourceId,
554
+ span: input.defaultAttribute.span
745
555
  });
556
+ return {};
746
557
  }
747
- }
748
- function emitModelsWithRelations(input) {
749
- let nextBuilder = input.builder;
750
- const sortedModels = input.resolvedModels.sort((left, right) => {
751
- const tableComparison = compareStrings(left.mapping.tableName, right.mapping.tableName);
752
- if (tableComparison === 0) return compareStrings(left.model.name, right.model.name);
753
- return tableComparison;
754
- });
755
- for (const entry of sortedModels) {
756
- const relationEntries = [...input.modelRelations.get(entry.model.name) ?? []].sort((left, right) => compareStrings(left.fieldName, right.fieldName));
757
- nextBuilder = nextBuilder.model(entry.model.name, entry.mapping.tableName, (modelBuilder) => {
758
- let next = modelBuilder;
759
- for (const resolvedField of entry.resolvedFields) next = next.field(resolvedField.field.name, resolvedField.columnName);
760
- for (const relation of relationEntries) next = next.relation(relation.fieldName, {
761
- toModel: relation.toModel,
762
- toTable: relation.toTable,
763
- cardinality: relation.cardinality,
764
- on: {
765
- parentTable: relation.parentTable,
766
- parentColumns: relation.parentColumns,
767
- childTable: relation.childTable,
768
- childColumns: relation.childColumns
769
- }
770
- });
771
- return next;
558
+ const literalDefault = parseDefaultLiteralValue(expressionEntry.value);
559
+ if (literalDefault) return { defaultValue: literalDefault };
560
+ const defaultFunctionCall = parseDefaultFunctionCall(expressionEntry.value, expressionEntry.span);
561
+ if (!defaultFunctionCall) {
562
+ input.diagnostics.push({
563
+ code: "PSL_INVALID_DEFAULT_VALUE",
564
+ message: `Unsupported default value "${expressionEntry.value}"`,
565
+ sourceId: input.sourceId,
566
+ span: input.defaultAttribute.span
772
567
  });
568
+ return {};
773
569
  }
774
- return nextBuilder;
775
- }
776
- function mapParserDiagnostics(document) {
777
- return document.diagnostics.map((diagnostic) => ({
778
- code: diagnostic.code,
779
- message: diagnostic.message,
780
- sourceId: diagnostic.sourceId,
781
- span: diagnostic.span
782
- }));
783
- }
784
- function normalizeReferentialAction(input) {
785
- const normalized = REFERENTIAL_ACTION_MAP[input.actionToken];
786
- if (normalized) return normalized;
787
- input.diagnostics.push({
788
- code: "PSL_UNSUPPORTED_REFERENTIAL_ACTION",
789
- message: `Relation field "${input.modelName}.${input.fieldName}" has unsupported ${input.actionName} action "${input.actionToken}"`,
790
- sourceId: input.sourceId,
791
- span: input.span
570
+ const lowered = lowerDefaultFunctionWithRegistry({
571
+ call: defaultFunctionCall,
572
+ registry: input.defaultFunctionRegistry,
573
+ context: {
574
+ sourceId: input.sourceId,
575
+ modelName: input.modelName,
576
+ fieldName: input.fieldName,
577
+ columnCodecId: input.columnDescriptor.codecId
578
+ }
792
579
  });
793
- }
794
- function parseAttributeFieldList(input) {
795
- const raw = getNamedArgument(input.attribute, "fields") ?? getPositionalArgument(input.attribute);
796
- if (!raw) {
580
+ if (!lowered.ok) {
581
+ input.diagnostics.push(lowered.diagnostic);
582
+ return {};
583
+ }
584
+ if (lowered.value.kind === "storage") return { defaultValue: lowered.value.defaultValue };
585
+ const generatorDescriptor = input.generatorDescriptorById.get(lowered.value.generated.id);
586
+ if (!generatorDescriptor) {
797
587
  input.diagnostics.push({
798
- code: input.code,
799
- message: `${input.messagePrefix} requires fields list argument`,
588
+ code: "PSL_INVALID_DEFAULT_APPLICABILITY",
589
+ message: `Default generator "${lowered.value.generated.id}" is not available in the composed mutation default registry.`,
800
590
  sourceId: input.sourceId,
801
- span: input.attribute.span
591
+ span: expressionEntry.span
802
592
  });
803
- return;
593
+ return {};
804
594
  }
805
- const fields = parseFieldList(raw);
806
- if (!fields || fields.length === 0) {
595
+ if (!generatorDescriptor.applicableCodecIds.includes(input.columnDescriptor.codecId)) {
807
596
  input.diagnostics.push({
808
- code: input.code,
809
- message: `${input.messagePrefix} requires bracketed field list argument`,
597
+ code: "PSL_INVALID_DEFAULT_APPLICABILITY",
598
+ message: `Default generator "${generatorDescriptor.id}" is not applicable to "${input.modelName}.${input.fieldName}" with codecId "${input.columnDescriptor.codecId}".`,
810
599
  sourceId: input.sourceId,
811
- span: input.attribute.span
600
+ span: expressionEntry.span
812
601
  });
813
- return;
602
+ return {};
814
603
  }
815
- return fields;
604
+ return { executionDefault: lowered.value.generated };
816
605
  }
817
- function mapFieldNamesToColumns(input) {
818
- const columns = [];
819
- for (const fieldName of input.fieldNames) {
820
- const columnName = input.mapping.fieldColumns.get(fieldName);
821
- if (!columnName) {
822
- input.diagnostics.push({
606
+ function resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors) {
607
+ if (field.typeRef && namedTypeDescriptors.has(field.typeRef)) return namedTypeDescriptors.get(field.typeRef);
608
+ if (namedTypeDescriptors.has(field.typeName)) return namedTypeDescriptors.get(field.typeName);
609
+ if (enumTypeDescriptors.has(field.typeName)) return enumTypeDescriptors.get(field.typeName);
610
+ return scalarTypeDescriptors.get(field.typeName);
611
+ }
612
+
613
+ //#endregion
614
+ //#region src/psl-field-resolution.ts
615
+ function collectResolvedFields(model, mapping, enumTypeDescriptors, namedTypeDescriptors, namedTypeBaseTypes, modelNames, compositeTypeNames, composedExtensions, authoringContributions, defaultFunctionRegistry, generatorDescriptorById, diagnostics, sourceId, scalarTypeDescriptors) {
616
+ const resolvedFields = [];
617
+ const pgvectorVectorConstructor = getAuthoringTypeConstructor(authoringContributions, ["pgvector", "vector"]);
618
+ for (const field of model.fields) {
619
+ if (field.list && modelNames.has(field.typeName)) continue;
620
+ for (const attribute of field.attributes) {
621
+ if (attribute.name === "id" || attribute.name === "unique" || attribute.name === "default" || attribute.name === "relation" || attribute.name === "map" || attribute.name === "pgvector.column") continue;
622
+ if (attribute.name.startsWith("pgvector.") && !composedExtensions.has("pgvector")) {
623
+ diagnostics.push({
624
+ code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
625
+ message: `Attribute "@${attribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
626
+ sourceId,
627
+ span: attribute.span
628
+ });
629
+ continue;
630
+ }
631
+ diagnostics.push({
632
+ code: "PSL_UNSUPPORTED_FIELD_ATTRIBUTE",
633
+ message: `Field "${model.name}.${field.name}" uses unsupported attribute "@${attribute.name}"`,
634
+ sourceId,
635
+ span: attribute.span
636
+ });
637
+ }
638
+ if (getAttribute(field.attributes, "relation") && modelNames.has(field.typeName)) continue;
639
+ const isValueObjectField = compositeTypeNames.has(field.typeName);
640
+ const isListField = field.list;
641
+ const pgvectorOnJsonField = getAttribute(field.attributes, "pgvector.column");
642
+ if (pgvectorOnJsonField && (isValueObjectField || isListField)) {
643
+ diagnostics.push({
823
644
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
824
- message: `${input.contextLabel} references unknown field "${input.modelName}.${fieldName}"`,
825
- sourceId: input.sourceId,
826
- span: input.span
645
+ message: `Field "${model.name}.${field.name}" uses @pgvector.column on a JSON-backed field (${isValueObjectField ? "value object" : "list"}). @pgvector.column is only supported on scalar Bytes fields.`,
646
+ sourceId,
647
+ span: pgvectorOnJsonField.span
827
648
  });
828
- return;
649
+ continue;
829
650
  }
830
- columns.push(columnName);
651
+ let descriptor;
652
+ let scalarCodecId;
653
+ if (isValueObjectField) descriptor = scalarTypeDescriptors.get("Json");
654
+ else if (isListField) {
655
+ const originalDescriptor = resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors);
656
+ if (!originalDescriptor) {
657
+ diagnostics.push({
658
+ code: "PSL_UNSUPPORTED_FIELD_TYPE",
659
+ message: `Field "${model.name}.${field.name}" type "${field.typeName}" is not supported in SQL PSL provider v1`,
660
+ sourceId,
661
+ span: field.span
662
+ });
663
+ continue;
664
+ }
665
+ scalarCodecId = originalDescriptor.codecId;
666
+ descriptor = scalarTypeDescriptors.get("Json");
667
+ } else {
668
+ descriptor = resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors);
669
+ const pgvectorColumnAttribute = getAttribute(field.attributes, "pgvector.column");
670
+ if (pgvectorColumnAttribute) if (!composedExtensions.has("pgvector")) diagnostics.push({
671
+ code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
672
+ message: "Attribute \"@pgvector.column\" uses unrecognized namespace \"pgvector\". Add extension pack \"pgvector\" to extensionPacks in prisma-next.config.ts.",
673
+ sourceId,
674
+ span: pgvectorColumnAttribute.span
675
+ });
676
+ else if (!(field.typeName === "Bytes" || namedTypeBaseTypes.get(field.typeRef ?? field.typeName) === "Bytes")) diagnostics.push({
677
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
678
+ message: `Field "${model.name}.${field.name}" uses @pgvector.column on unsupported base type "${field.typeName}"`,
679
+ sourceId,
680
+ span: pgvectorColumnAttribute.span
681
+ });
682
+ else {
683
+ const length = parsePgvectorLength({
684
+ attribute: pgvectorColumnAttribute,
685
+ diagnostics,
686
+ sourceId
687
+ });
688
+ if (length !== void 0) descriptor = pgvectorVectorConstructor ? instantiateAuthoringTypeConstructor(pgvectorVectorConstructor, [length]) : {
689
+ codecId: "pg/vector@1",
690
+ nativeType: "vector",
691
+ typeParams: { length }
692
+ };
693
+ }
694
+ }
695
+ if (!descriptor) {
696
+ diagnostics.push({
697
+ code: "PSL_UNSUPPORTED_FIELD_TYPE",
698
+ message: `Field "${model.name}.${field.name}" type "${field.typeName}" is not supported in SQL PSL provider v1`,
699
+ sourceId,
700
+ span: field.span
701
+ });
702
+ continue;
703
+ }
704
+ const defaultAttribute = getAttribute(field.attributes, "default");
705
+ const loweredDefault = defaultAttribute ? lowerDefaultForField({
706
+ modelName: model.name,
707
+ fieldName: field.name,
708
+ defaultAttribute,
709
+ columnDescriptor: descriptor,
710
+ generatorDescriptorById,
711
+ sourceId,
712
+ defaultFunctionRegistry,
713
+ diagnostics
714
+ }) : {};
715
+ if (field.optional && loweredDefault.executionDefault) {
716
+ const generatorDescription = loweredDefault.executionDefault.kind === "generator" ? `"${loweredDefault.executionDefault.id}"` : "for this field";
717
+ diagnostics.push({
718
+ code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
719
+ message: `Field "${model.name}.${field.name}" cannot be optional when using execution default ${generatorDescription}. Remove "?" or use a storage default.`,
720
+ sourceId,
721
+ span: defaultAttribute?.span ?? field.span
722
+ });
723
+ continue;
724
+ }
725
+ if (loweredDefault.executionDefault) {
726
+ const generatedDescriptor = generatorDescriptorById.get(loweredDefault.executionDefault.id)?.resolveGeneratedColumnDescriptor?.({ generated: loweredDefault.executionDefault });
727
+ if (generatedDescriptor) descriptor = generatedDescriptor;
728
+ }
729
+ const mappedColumnName = mapping.fieldColumns.get(field.name) ?? field.name;
730
+ const idAttribute = getAttribute(field.attributes, "id");
731
+ const uniqueAttribute = getAttribute(field.attributes, "unique");
732
+ const idName = parseConstraintMapArgument({
733
+ attribute: idAttribute,
734
+ sourceId,
735
+ diagnostics,
736
+ entityLabel: `Field "${model.name}.${field.name}" @id`,
737
+ span: field.span,
738
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT"
739
+ });
740
+ const uniqueName = parseConstraintMapArgument({
741
+ attribute: uniqueAttribute,
742
+ sourceId,
743
+ diagnostics,
744
+ entityLabel: `Field "${model.name}.${field.name}" @unique`,
745
+ span: field.span,
746
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT"
747
+ });
748
+ resolvedFields.push({
749
+ field,
750
+ columnName: mappedColumnName,
751
+ descriptor,
752
+ ...ifDefined("defaultValue", loweredDefault.defaultValue),
753
+ ...ifDefined("executionDefault", loweredDefault.executionDefault),
754
+ isId: Boolean(idAttribute),
755
+ isUnique: Boolean(uniqueAttribute),
756
+ ...ifDefined("idName", idName),
757
+ ...ifDefined("uniqueName", uniqueName),
758
+ ...ifDefined("many", isListField ? true : void 0),
759
+ ...ifDefined("valueObjectTypeName", isValueObjectField ? field.typeName : void 0),
760
+ ...ifDefined("scalarCodecId", scalarCodecId)
761
+ });
831
762
  }
832
- return columns;
763
+ return resolvedFields;
833
764
  }
834
765
  function buildModelMappings(models, diagnostics, sourceId) {
835
766
  const result = /* @__PURE__ */ new Map();
@@ -862,29 +793,33 @@ function buildModelMappings(models, diagnostics, sourceId) {
862
793
  }
863
794
  return result;
864
795
  }
865
- function validateNavigationListFieldAttributes(input) {
866
- let valid = true;
867
- for (const attribute of input.field.attributes) {
868
- if (attribute.name === "relation") continue;
869
- if (attribute.name.startsWith("pgvector.") && !input.composedExtensions.has("pgvector")) {
870
- input.diagnostics.push({
871
- code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
872
- message: `Attribute "@${attribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
873
- sourceId: input.sourceId,
874
- span: attribute.span
875
- });
876
- valid = false;
877
- continue;
878
- }
879
- input.diagnostics.push({
880
- code: "PSL_UNSUPPORTED_FIELD_ATTRIBUTE",
881
- message: `Field "${input.modelName}.${input.field.name}" uses unsupported attribute "@${attribute.name}"`,
882
- sourceId: input.sourceId,
883
- span: attribute.span
884
- });
885
- valid = false;
886
- }
887
- return valid;
796
+
797
+ //#endregion
798
+ //#region src/psl-relation-resolution.ts
799
+ const REFERENTIAL_ACTION_MAP = {
800
+ NoAction: "noAction",
801
+ Restrict: "restrict",
802
+ Cascade: "cascade",
803
+ SetNull: "setNull",
804
+ SetDefault: "setDefault",
805
+ noAction: "noAction",
806
+ restrict: "restrict",
807
+ cascade: "cascade",
808
+ setNull: "setNull",
809
+ setDefault: "setDefault"
810
+ };
811
+ function fkRelationPairKey(declaringModelName, targetModelName) {
812
+ return `${declaringModelName}::${targetModelName}`;
813
+ }
814
+ function normalizeReferentialAction(input) {
815
+ const normalized = REFERENTIAL_ACTION_MAP[input.actionToken];
816
+ if (normalized) return normalized;
817
+ input.diagnostics.push({
818
+ code: "PSL_UNSUPPORTED_REFERENTIAL_ACTION",
819
+ message: `Relation field "${input.modelName}.${input.fieldName}" has unsupported ${input.actionName} action "${input.actionToken}"`,
820
+ sourceId: input.sourceId,
821
+ span: input.span
822
+ });
888
823
  }
889
824
  function parseRelationAttribute(input) {
890
825
  if (input.attribute.args.filter((arg) => arg.kind === "positional").length > 1) {
@@ -994,63 +929,180 @@ function parseRelationAttribute(input) {
994
929
  ...ifDefined("onUpdate", onUpdateArgument ? unquoteStringLiteral(onUpdateArgument) : void 0)
995
930
  };
996
931
  }
997
- function interpretPslDocumentToSqlContractIR(input) {
998
- const sourceId = input.document.ast.sourceId;
999
- if (!input.target) return notOk({
1000
- summary: "PSL to SQL Contract IR normalization failed",
1001
- diagnostics: [{
1002
- code: "PSL_TARGET_CONTEXT_REQUIRED",
1003
- message: "PSL interpretation requires an explicit target context from composition.",
1004
- sourceId
1005
- }]
1006
- });
1007
- if (!input.scalarTypeDescriptors) return notOk({
1008
- summary: "PSL to SQL Contract IR normalization failed",
1009
- diagnostics: [{
1010
- code: "PSL_SCALAR_TYPE_CONTEXT_REQUIRED",
1011
- message: "PSL interpretation requires composed scalar type descriptors.",
1012
- sourceId
1013
- }]
1014
- });
1015
- const diagnostics = mapParserDiagnostics(input.document);
1016
- const modelNames = new Set(input.document.ast.models.map((model) => model.name));
1017
- const composedExtensions = new Set(input.composedExtensionPacks ?? []);
1018
- const defaultFunctionRegistry = input.controlMutationDefaults?.defaultFunctionRegistry ?? /* @__PURE__ */ new Map();
1019
- const generatorDescriptors = input.controlMutationDefaults?.generatorDescriptors ?? [];
1020
- const generatorDescriptorById = /* @__PURE__ */ new Map();
1021
- for (const descriptor of generatorDescriptors) generatorDescriptorById.set(descriptor.id, descriptor);
1022
- let builder = defineContract().target(input.target);
932
+ function indexFkRelations(input) {
933
+ const modelRelations = /* @__PURE__ */ new Map();
934
+ const fkRelationsByPair = /* @__PURE__ */ new Map();
935
+ for (const relation of input.fkRelationMetadata) {
936
+ const existing = modelRelations.get(relation.declaringModelName);
937
+ const current = existing ?? [];
938
+ if (!existing) modelRelations.set(relation.declaringModelName, current);
939
+ current.push({
940
+ fieldName: relation.declaringFieldName,
941
+ toModel: relation.targetModelName,
942
+ toTable: relation.targetTableName,
943
+ cardinality: "N:1",
944
+ on: {
945
+ parentTable: relation.declaringTableName,
946
+ parentColumns: relation.localColumns,
947
+ childTable: relation.targetTableName,
948
+ childColumns: relation.referencedColumns
949
+ }
950
+ });
951
+ const pairKey = fkRelationPairKey(relation.declaringModelName, relation.targetModelName);
952
+ const pairRelations = fkRelationsByPair.get(pairKey);
953
+ if (!pairRelations) {
954
+ fkRelationsByPair.set(pairKey, [relation]);
955
+ continue;
956
+ }
957
+ pairRelations.push(relation);
958
+ }
959
+ return {
960
+ modelRelations,
961
+ fkRelationsByPair
962
+ };
963
+ }
964
+ function applyBackrelationCandidates(input) {
965
+ for (const candidate of input.backrelationCandidates) {
966
+ const pairKey = fkRelationPairKey(candidate.targetModelName, candidate.modelName);
967
+ const pairMatches = input.fkRelationsByPair.get(pairKey) ?? [];
968
+ const matches = candidate.relationName ? pairMatches.filter((relation) => relation.relationName === candidate.relationName) : [...pairMatches];
969
+ if (matches.length === 0) {
970
+ input.diagnostics.push({
971
+ code: "PSL_ORPHANED_BACKRELATION_LIST",
972
+ message: `Backrelation list field "${candidate.modelName}.${candidate.field.name}" has no matching FK-side relation on model "${candidate.targetModelName}". Add @relation(fields: [...], references: [...]) on the FK-side relation or use an explicit join model for many-to-many.`,
973
+ sourceId: input.sourceId,
974
+ span: candidate.field.span
975
+ });
976
+ continue;
977
+ }
978
+ if (matches.length > 1) {
979
+ input.diagnostics.push({
980
+ code: "PSL_AMBIGUOUS_BACKRELATION_LIST",
981
+ message: `Backrelation list field "${candidate.modelName}.${candidate.field.name}" matches multiple FK-side relations on model "${candidate.targetModelName}". Add @relation(name: "...") (or @relation("...")) to both sides to disambiguate.`,
982
+ sourceId: input.sourceId,
983
+ span: candidate.field.span
984
+ });
985
+ continue;
986
+ }
987
+ invariant(matches.length === 1, "Backrelation matching requires exactly one match");
988
+ const matched = matches[0];
989
+ assertDefined(matched, "Backrelation matching requires a defined relation match");
990
+ const existing = input.modelRelations.get(candidate.modelName);
991
+ const current = existing ?? [];
992
+ if (!existing) input.modelRelations.set(candidate.modelName, current);
993
+ current.push({
994
+ fieldName: candidate.field.name,
995
+ toModel: matched.declaringModelName,
996
+ toTable: matched.declaringTableName,
997
+ cardinality: "1:N",
998
+ on: {
999
+ parentTable: candidate.tableName,
1000
+ parentColumns: matched.referencedColumns,
1001
+ childTable: matched.declaringTableName,
1002
+ childColumns: matched.localColumns
1003
+ }
1004
+ });
1005
+ }
1006
+ }
1007
+ function validateNavigationListFieldAttributes(input) {
1008
+ let valid = true;
1009
+ for (const attribute of input.field.attributes) {
1010
+ if (attribute.name === "relation") continue;
1011
+ if (attribute.name.startsWith("pgvector.") && !input.composedExtensions.has("pgvector")) {
1012
+ input.diagnostics.push({
1013
+ code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
1014
+ message: `Attribute "@${attribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
1015
+ sourceId: input.sourceId,
1016
+ span: attribute.span
1017
+ });
1018
+ valid = false;
1019
+ continue;
1020
+ }
1021
+ input.diagnostics.push({
1022
+ code: "PSL_UNSUPPORTED_FIELD_ATTRIBUTE",
1023
+ message: `Field "${input.modelName}.${input.field.name}" uses unsupported attribute "@${attribute.name}"`,
1024
+ sourceId: input.sourceId,
1025
+ span: attribute.span
1026
+ });
1027
+ valid = false;
1028
+ }
1029
+ return valid;
1030
+ }
1031
+
1032
+ //#endregion
1033
+ //#region src/interpreter.ts
1034
+ function buildComposedExtensionPackRefs(target, extensionIds, extensionPackRefs = []) {
1035
+ if (extensionIds.length === 0) return;
1036
+ const extensionPackRefById = new Map(extensionPackRefs.map((packRef) => [packRef.id, packRef]));
1037
+ return Object.fromEntries(extensionIds.map((extensionId) => [extensionId, extensionPackRefById.get(extensionId) ?? {
1038
+ kind: "extension",
1039
+ id: extensionId,
1040
+ familyId: target.familyId,
1041
+ targetId: target.targetId,
1042
+ version: "0.0.1"
1043
+ }]));
1044
+ }
1045
+ function hasSameSpan(a, b) {
1046
+ return a.start.offset === b.start.offset && a.end.offset === b.end.offset && a.start.line === b.start.line && a.end.line === b.end.line;
1047
+ }
1048
+ function compareStrings(left, right) {
1049
+ if (left < right) return -1;
1050
+ if (left > right) return 1;
1051
+ return 0;
1052
+ }
1053
+ function mapParserDiagnostics(document) {
1054
+ return document.diagnostics.map((diagnostic) => ({
1055
+ code: diagnostic.code,
1056
+ message: diagnostic.message,
1057
+ sourceId: diagnostic.sourceId,
1058
+ span: diagnostic.span
1059
+ }));
1060
+ }
1061
+ function processEnumDeclarations(input) {
1062
+ const storageTypes = {};
1023
1063
  const enumTypeDescriptors = /* @__PURE__ */ new Map();
1024
- const namedTypeDescriptors = /* @__PURE__ */ new Map();
1025
- const namedTypeBaseTypes = /* @__PURE__ */ new Map();
1026
- for (const enumDeclaration of input.document.ast.enums) {
1064
+ for (const enumDeclaration of input.enums) {
1027
1065
  const nativeType = parseMapName({
1028
1066
  attribute: getAttribute(enumDeclaration.attributes, "map"),
1029
1067
  defaultValue: enumDeclaration.name,
1030
- sourceId,
1031
- diagnostics,
1068
+ sourceId: input.sourceId,
1069
+ diagnostics: input.diagnostics,
1032
1070
  entityLabel: `Enum "${enumDeclaration.name}"`,
1033
1071
  span: enumDeclaration.span
1034
1072
  });
1035
- const descriptor = {
1073
+ const enumStorageType = input.enumTypeConstructor ? instantiateAuthoringTypeConstructor(input.enumTypeConstructor, [nativeType, enumDeclaration.values.map((value) => value.name)]) : {
1036
1074
  codecId: "pg/enum@1",
1037
1075
  nativeType,
1076
+ typeParams: { values: enumDeclaration.values.map((value) => value.name) }
1077
+ };
1078
+ const descriptor = {
1079
+ codecId: enumStorageType.codecId,
1080
+ nativeType: enumStorageType.nativeType,
1038
1081
  typeRef: enumDeclaration.name
1039
1082
  };
1040
1083
  enumTypeDescriptors.set(enumDeclaration.name, descriptor);
1041
- builder = builder.storageType(enumDeclaration.name, {
1042
- codecId: "pg/enum@1",
1043
- nativeType,
1044
- typeParams: { values: enumDeclaration.values.map((value) => value.name) }
1045
- });
1084
+ storageTypes[enumDeclaration.name] = {
1085
+ codecId: enumStorageType.codecId,
1086
+ nativeType: enumStorageType.nativeType,
1087
+ typeParams: enumStorageType.typeParams ?? { values: enumDeclaration.values.map((value) => value.name) }
1088
+ };
1046
1089
  }
1047
- for (const declaration of input.document.ast.types?.declarations ?? []) {
1048
- const baseDescriptor = enumTypeDescriptors.get(declaration.baseType) ?? input.scalarTypeDescriptors.get(declaration.baseType);
1090
+ return {
1091
+ storageTypes,
1092
+ enumTypeDescriptors
1093
+ };
1094
+ }
1095
+ function resolveNamedTypeDeclarations(input) {
1096
+ const storageTypes = {};
1097
+ const namedTypeDescriptors = /* @__PURE__ */ new Map();
1098
+ const namedTypeBaseTypes = /* @__PURE__ */ new Map();
1099
+ for (const declaration of input.declarations) {
1100
+ const baseDescriptor = input.enumTypeDescriptors.get(declaration.baseType) ?? input.scalarTypeDescriptors.get(declaration.baseType);
1049
1101
  if (!baseDescriptor) {
1050
- diagnostics.push({
1102
+ input.diagnostics.push({
1051
1103
  code: "PSL_UNSUPPORTED_NAMED_TYPE_BASE",
1052
1104
  message: `Named type "${declaration.name}" references unsupported base type "${declaration.baseType}"`,
1053
- sourceId,
1105
+ sourceId: input.sourceId,
1054
1106
  span: declaration.span
1055
1107
  });
1056
1108
  continue;
@@ -1060,58 +1112,59 @@ function interpretPslDocumentToSqlContractIR(input) {
1060
1112
  const dbNativeTypeAttribute = declaration.attributes.find((attribute) => attribute.name.startsWith("db."));
1061
1113
  const unsupportedNamedTypeAttribute = declaration.attributes.find((attribute) => attribute.name !== "pgvector.column" && !attribute.name.startsWith("db."));
1062
1114
  if (unsupportedNamedTypeAttribute) {
1063
- diagnostics.push({
1115
+ input.diagnostics.push({
1064
1116
  code: "PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE",
1065
1117
  message: `Named type "${declaration.name}" uses unsupported attribute "${unsupportedNamedTypeAttribute.name}"`,
1066
- sourceId,
1118
+ sourceId: input.sourceId,
1067
1119
  span: unsupportedNamedTypeAttribute.span
1068
1120
  });
1069
1121
  continue;
1070
1122
  }
1071
1123
  if (pgvectorAttribute && dbNativeTypeAttribute) {
1072
- diagnostics.push({
1124
+ input.diagnostics.push({
1073
1125
  code: "PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE",
1074
1126
  message: `Named type "${declaration.name}" cannot combine @pgvector.column with @${dbNativeTypeAttribute.name}.`,
1075
- sourceId,
1127
+ sourceId: input.sourceId,
1076
1128
  span: dbNativeTypeAttribute.span
1077
1129
  });
1078
1130
  continue;
1079
1131
  }
1080
1132
  if (pgvectorAttribute) {
1081
- if (!composedExtensions.has("pgvector")) {
1082
- diagnostics.push({
1133
+ if (!input.composedExtensions.has("pgvector")) {
1134
+ input.diagnostics.push({
1083
1135
  code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
1084
1136
  message: "Attribute \"@pgvector.column\" uses unrecognized namespace \"pgvector\". Add extension pack \"pgvector\" to extensionPacks in prisma-next.config.ts.",
1085
- sourceId,
1137
+ sourceId: input.sourceId,
1086
1138
  span: pgvectorAttribute.span
1087
1139
  });
1088
1140
  continue;
1089
1141
  }
1090
1142
  if (declaration.baseType !== "Bytes") {
1091
- diagnostics.push({
1143
+ input.diagnostics.push({
1092
1144
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1093
1145
  message: `Named type "${declaration.name}" uses @pgvector.column on unsupported base type "${declaration.baseType}"`,
1094
- sourceId,
1146
+ sourceId: input.sourceId,
1095
1147
  span: pgvectorAttribute.span
1096
1148
  });
1097
1149
  continue;
1098
1150
  }
1099
1151
  const length = parsePgvectorLength({
1100
1152
  attribute: pgvectorAttribute,
1101
- diagnostics,
1102
- sourceId
1153
+ diagnostics: input.diagnostics,
1154
+ sourceId: input.sourceId
1103
1155
  });
1104
1156
  if (length === void 0) continue;
1105
- namedTypeDescriptors.set(declaration.name, {
1106
- codecId: "pg/vector@1",
1107
- nativeType: "vector",
1108
- typeParams: { length }
1109
- });
1110
- builder = builder.storageType(declaration.name, {
1157
+ const pgvectorStorageType = input.pgvectorVectorConstructor ? instantiateAuthoringTypeConstructor(input.pgvectorVectorConstructor, [length]) : {
1111
1158
  codecId: "pg/vector@1",
1112
1159
  nativeType: "vector",
1113
1160
  typeParams: { length }
1114
- });
1161
+ };
1162
+ namedTypeDescriptors.set(declaration.name, toNamedTypeFieldDescriptor(declaration.name, pgvectorStorageType));
1163
+ storageTypes[declaration.name] = {
1164
+ codecId: pgvectorStorageType.codecId,
1165
+ nativeType: pgvectorStorageType.nativeType,
1166
+ typeParams: pgvectorStorageType.typeParams ?? { length }
1167
+ };
1115
1168
  continue;
1116
1169
  }
1117
1170
  if (dbNativeTypeAttribute) {
@@ -1119,273 +1172,614 @@ function interpretPslDocumentToSqlContractIR(input) {
1119
1172
  attribute: dbNativeTypeAttribute,
1120
1173
  baseType: declaration.baseType,
1121
1174
  baseDescriptor,
1122
- diagnostics,
1123
- sourceId,
1175
+ diagnostics: input.diagnostics,
1176
+ sourceId: input.sourceId,
1124
1177
  entityLabel: `Named type "${declaration.name}"`
1125
1178
  });
1126
1179
  if (!descriptor$1) continue;
1127
- namedTypeDescriptors.set(declaration.name, {
1128
- ...descriptor$1,
1129
- typeRef: declaration.name
1130
- });
1131
- builder = builder.storageType(declaration.name, {
1180
+ namedTypeDescriptors.set(declaration.name, toNamedTypeFieldDescriptor(declaration.name, descriptor$1));
1181
+ storageTypes[declaration.name] = {
1132
1182
  codecId: descriptor$1.codecId,
1133
1183
  nativeType: descriptor$1.nativeType,
1134
1184
  typeParams: descriptor$1.typeParams ?? {}
1135
- });
1185
+ };
1136
1186
  continue;
1137
1187
  }
1138
- const descriptor = {
1139
- codecId: baseDescriptor.codecId,
1140
- nativeType: baseDescriptor.nativeType,
1141
- typeRef: declaration.name
1142
- };
1188
+ const descriptor = toNamedTypeFieldDescriptor(declaration.name, baseDescriptor);
1143
1189
  namedTypeDescriptors.set(declaration.name, descriptor);
1144
- builder = builder.storageType(declaration.name, {
1190
+ storageTypes[declaration.name] = {
1145
1191
  codecId: baseDescriptor.codecId,
1146
1192
  nativeType: baseDescriptor.nativeType,
1147
1193
  typeParams: {}
1148
- });
1194
+ };
1149
1195
  }
1150
- const modelMappings = buildModelMappings(input.document.ast.models, diagnostics, sourceId);
1151
- const resolvedModels = [];
1152
- const fkRelationMetadata = [];
1153
- const backrelationCandidates = [];
1154
- for (const model of input.document.ast.models) {
1155
- const mapping = modelMappings.get(model.name);
1156
- if (!mapping) continue;
1157
- const tableName = mapping.tableName;
1158
- const resolvedFields = collectResolvedFields(model, mapping, enumTypeDescriptors, namedTypeDescriptors, namedTypeBaseTypes, modelNames, composedExtensions, defaultFunctionRegistry, generatorDescriptorById, diagnostics, sourceId, input.scalarTypeDescriptors);
1159
- resolvedModels.push({
1160
- model,
1161
- mapping,
1162
- resolvedFields
1163
- });
1164
- const primaryKeyColumns = resolvedFields.filter((field) => field.isId).map((field) => field.columnName);
1165
- if (primaryKeyColumns.length === 0) diagnostics.push({
1166
- code: "PSL_MISSING_PRIMARY_KEY",
1167
- message: `Model "${model.name}" must declare at least one @id field for SQL provider`,
1196
+ return {
1197
+ storageTypes,
1198
+ namedTypeDescriptors,
1199
+ namedTypeBaseTypes
1200
+ };
1201
+ }
1202
+ function buildModelNodeFromPsl(input) {
1203
+ const { model, mapping, sourceId, diagnostics } = input;
1204
+ const tableName = mapping.tableName;
1205
+ const resolvedFields = collectResolvedFields(model, mapping, input.enumTypeDescriptors, input.namedTypeDescriptors, input.namedTypeBaseTypes, input.modelNames, input.compositeTypeNames, input.composedExtensions, input.authoringContributions, input.defaultFunctionRegistry, input.generatorDescriptorById, diagnostics, sourceId, input.scalarTypeDescriptors);
1206
+ const primaryKeyFields = resolvedFields.filter((field) => field.isId);
1207
+ const primaryKeyColumns = primaryKeyFields.map((field) => field.columnName);
1208
+ const primaryKeyName = primaryKeyFields.length === 1 ? primaryKeyFields[0]?.idName : void 0;
1209
+ const isVariantModel = model.attributes.some((attr) => attr.name === "base");
1210
+ if (primaryKeyColumns.length === 0 && !isVariantModel) diagnostics.push({
1211
+ code: "PSL_MISSING_PRIMARY_KEY",
1212
+ message: `Model "${model.name}" must declare at least one @id field for SQL provider`,
1213
+ sourceId,
1214
+ span: model.span
1215
+ });
1216
+ const resultBackrelationCandidates = [];
1217
+ for (const field of model.fields) {
1218
+ if (!field.list || !input.modelNames.has(field.typeName)) continue;
1219
+ const attributesValid = validateNavigationListFieldAttributes({
1220
+ modelName: model.name,
1221
+ field,
1168
1222
  sourceId,
1169
- span: model.span
1223
+ composedExtensions: input.composedExtensions,
1224
+ diagnostics
1170
1225
  });
1171
- for (const field of model.fields) {
1172
- if (!field.list || !modelNames.has(field.typeName)) continue;
1173
- const attributesValid = validateNavigationListFieldAttributes({
1226
+ const relationAttribute = getAttribute(field.attributes, "relation");
1227
+ let relationName;
1228
+ if (relationAttribute) {
1229
+ const parsedRelation = parseRelationAttribute({
1230
+ attribute: relationAttribute,
1174
1231
  modelName: model.name,
1175
- field,
1232
+ fieldName: field.name,
1176
1233
  sourceId,
1177
- composedExtensions,
1178
1234
  diagnostics
1179
1235
  });
1180
- const relationAttribute = getAttribute(field.attributes, "relation");
1181
- let relationName;
1182
- if (relationAttribute) {
1183
- const parsedRelation = parseRelationAttribute({
1184
- attribute: relationAttribute,
1185
- modelName: model.name,
1186
- fieldName: field.name,
1236
+ if (!parsedRelation) continue;
1237
+ if (parsedRelation.fields || parsedRelation.references) {
1238
+ diagnostics.push({
1239
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
1240
+ message: `Backrelation list field "${model.name}.${field.name}" cannot declare fields/references; define them on the FK-side relation field`,
1187
1241
  sourceId,
1188
- diagnostics
1242
+ span: relationAttribute.span
1189
1243
  });
1190
- if (!parsedRelation) continue;
1191
- if (parsedRelation.fields || parsedRelation.references) {
1192
- diagnostics.push({
1193
- code: "PSL_INVALID_RELATION_ATTRIBUTE",
1194
- message: `Backrelation list field "${model.name}.${field.name}" cannot declare fields/references; define them on the FK-side relation field`,
1195
- sourceId,
1196
- span: relationAttribute.span
1197
- });
1198
- continue;
1199
- }
1200
- if (parsedRelation.onDelete || parsedRelation.onUpdate) {
1201
- diagnostics.push({
1202
- code: "PSL_INVALID_RELATION_ATTRIBUTE",
1203
- message: `Backrelation list field "${model.name}.${field.name}" cannot declare onDelete/onUpdate; define referential actions on the FK-side relation field`,
1204
- sourceId,
1205
- span: relationAttribute.span
1206
- });
1207
- continue;
1208
- }
1209
- relationName = parsedRelation.relationName;
1244
+ continue;
1245
+ }
1246
+ if (parsedRelation.onDelete || parsedRelation.onUpdate) {
1247
+ diagnostics.push({
1248
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
1249
+ message: `Backrelation list field "${model.name}.${field.name}" cannot declare onDelete/onUpdate; define referential actions on the FK-side relation field`,
1250
+ sourceId,
1251
+ span: relationAttribute.span
1252
+ });
1253
+ continue;
1210
1254
  }
1211
- if (!attributesValid) continue;
1212
- backrelationCandidates.push({
1255
+ relationName = parsedRelation.relationName;
1256
+ }
1257
+ if (!attributesValid) continue;
1258
+ resultBackrelationCandidates.push({
1259
+ modelName: model.name,
1260
+ tableName,
1261
+ field,
1262
+ targetModelName: field.typeName,
1263
+ ...ifDefined("relationName", relationName)
1264
+ });
1265
+ }
1266
+ const relationAttributes = model.fields.map((field) => ({
1267
+ field,
1268
+ relation: getAttribute(field.attributes, "relation")
1269
+ })).filter((entry) => Boolean(entry.relation));
1270
+ const uniqueConstraints = resolvedFields.filter((field) => field.isUnique).map((field) => ({
1271
+ columns: [field.columnName],
1272
+ ...ifDefined("name", field.uniqueName)
1273
+ }));
1274
+ const indexNodes = [];
1275
+ const foreignKeyNodes = [];
1276
+ for (const modelAttribute of model.attributes) {
1277
+ if (modelAttribute.name === "map") continue;
1278
+ if (modelAttribute.name === "discriminator" || modelAttribute.name === "base") continue;
1279
+ if (modelAttribute.name === "unique" || modelAttribute.name === "index") {
1280
+ const fieldNames = parseAttributeFieldList({
1281
+ attribute: modelAttribute,
1282
+ sourceId,
1283
+ diagnostics,
1284
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1285
+ messagePrefix: `Model "${model.name}" @@${modelAttribute.name}`
1286
+ });
1287
+ if (!fieldNames) continue;
1288
+ const columnNames = mapFieldNamesToColumns({
1213
1289
  modelName: model.name,
1214
- tableName,
1215
- field,
1216
- targetModelName: field.typeName,
1217
- ...ifDefined("relationName", relationName)
1290
+ fieldNames,
1291
+ mapping,
1292
+ sourceId,
1293
+ diagnostics,
1294
+ span: modelAttribute.span,
1295
+ contextLabel: `Model "${model.name}" @@${modelAttribute.name}`
1296
+ });
1297
+ if (!columnNames) continue;
1298
+ const constraintName = parseConstraintMapArgument({
1299
+ attribute: modelAttribute,
1300
+ sourceId,
1301
+ diagnostics,
1302
+ entityLabel: `Model "${model.name}" @@${modelAttribute.name}`,
1303
+ span: modelAttribute.span,
1304
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT"
1305
+ });
1306
+ if (modelAttribute.name === "unique") uniqueConstraints.push({
1307
+ columns: columnNames,
1308
+ ...ifDefined("name", constraintName)
1218
1309
  });
1310
+ else indexNodes.push({
1311
+ columns: columnNames,
1312
+ ...ifDefined("name", constraintName)
1313
+ });
1314
+ continue;
1219
1315
  }
1220
- const relationAttributes = model.fields.map((field) => ({
1221
- field,
1222
- relation: getAttribute(field.attributes, "relation")
1223
- })).filter((entry) => Boolean(entry.relation));
1224
- builder = builder.table(tableName, (tableBuilder) => {
1225
- let table = tableBuilder;
1226
- for (const resolvedField of resolvedFields) {
1227
- if (resolvedField.executionDefault) table = table.generated(resolvedField.columnName, {
1228
- type: resolvedField.descriptor,
1229
- generated: resolvedField.executionDefault
1230
- });
1231
- else {
1232
- const options = {
1233
- type: resolvedField.descriptor,
1234
- ...ifDefined("nullable", resolvedField.field.optional ? true : void 0),
1235
- ...ifDefined("default", resolvedField.defaultValue)
1236
- };
1237
- table = table.column(resolvedField.columnName, options);
1238
- }
1239
- if (resolvedField.isUnique) table = table.unique([resolvedField.columnName]);
1316
+ if (modelAttribute.name.startsWith("pgvector.") && !input.composedExtensions.has("pgvector")) {
1317
+ diagnostics.push({
1318
+ code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
1319
+ message: `Attribute "@@${modelAttribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
1320
+ sourceId,
1321
+ span: modelAttribute.span
1322
+ });
1323
+ continue;
1324
+ }
1325
+ diagnostics.push({
1326
+ code: "PSL_UNSUPPORTED_MODEL_ATTRIBUTE",
1327
+ message: `Model "${model.name}" uses unsupported attribute "@@${modelAttribute.name}"`,
1328
+ sourceId,
1329
+ span: modelAttribute.span
1330
+ });
1331
+ }
1332
+ const resultFkRelationMetadata = [];
1333
+ for (const relationAttribute of relationAttributes) {
1334
+ if (relationAttribute.field.list) continue;
1335
+ if (!input.modelNames.has(relationAttribute.field.typeName)) {
1336
+ diagnostics.push({
1337
+ code: "PSL_INVALID_RELATION_TARGET",
1338
+ message: `Relation field "${model.name}.${relationAttribute.field.name}" references unknown model "${relationAttribute.field.typeName}"`,
1339
+ sourceId,
1340
+ span: relationAttribute.field.span
1341
+ });
1342
+ continue;
1343
+ }
1344
+ const parsedRelation = parseRelationAttribute({
1345
+ attribute: relationAttribute.relation,
1346
+ modelName: model.name,
1347
+ fieldName: relationAttribute.field.name,
1348
+ sourceId,
1349
+ diagnostics
1350
+ });
1351
+ if (!parsedRelation) continue;
1352
+ if (!parsedRelation.fields || !parsedRelation.references) {
1353
+ diagnostics.push({
1354
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
1355
+ message: `Relation field "${model.name}.${relationAttribute.field.name}" requires fields and references arguments`,
1356
+ sourceId,
1357
+ span: relationAttribute.relation.span
1358
+ });
1359
+ continue;
1360
+ }
1361
+ const targetMapping = input.modelMappings.get(relationAttribute.field.typeName);
1362
+ if (!targetMapping) {
1363
+ diagnostics.push({
1364
+ code: "PSL_INVALID_RELATION_TARGET",
1365
+ message: `Relation field "${model.name}.${relationAttribute.field.name}" references unknown model "${relationAttribute.field.typeName}"`,
1366
+ sourceId,
1367
+ span: relationAttribute.field.span
1368
+ });
1369
+ continue;
1370
+ }
1371
+ const localColumns = mapFieldNamesToColumns({
1372
+ modelName: model.name,
1373
+ fieldNames: parsedRelation.fields,
1374
+ mapping,
1375
+ sourceId,
1376
+ diagnostics,
1377
+ span: relationAttribute.relation.span,
1378
+ contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
1379
+ });
1380
+ if (!localColumns) continue;
1381
+ const referencedColumns = mapFieldNamesToColumns({
1382
+ modelName: targetMapping.model.name,
1383
+ fieldNames: parsedRelation.references,
1384
+ mapping: targetMapping,
1385
+ sourceId,
1386
+ diagnostics,
1387
+ span: relationAttribute.relation.span,
1388
+ contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
1389
+ });
1390
+ if (!referencedColumns) continue;
1391
+ if (localColumns.length !== referencedColumns.length) {
1392
+ diagnostics.push({
1393
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
1394
+ message: `Relation field "${model.name}.${relationAttribute.field.name}" must provide the same number of fields and references`,
1395
+ sourceId,
1396
+ span: relationAttribute.relation.span
1397
+ });
1398
+ continue;
1399
+ }
1400
+ const onDelete = parsedRelation.onDelete ? normalizeReferentialAction({
1401
+ modelName: model.name,
1402
+ fieldName: relationAttribute.field.name,
1403
+ actionName: "onDelete",
1404
+ actionToken: parsedRelation.onDelete,
1405
+ sourceId,
1406
+ span: relationAttribute.field.span,
1407
+ diagnostics
1408
+ }) : void 0;
1409
+ const onUpdate = parsedRelation.onUpdate ? normalizeReferentialAction({
1410
+ modelName: model.name,
1411
+ fieldName: relationAttribute.field.name,
1412
+ actionName: "onUpdate",
1413
+ actionToken: parsedRelation.onUpdate,
1414
+ sourceId,
1415
+ span: relationAttribute.field.span,
1416
+ diagnostics
1417
+ }) : void 0;
1418
+ foreignKeyNodes.push({
1419
+ columns: localColumns,
1420
+ references: {
1421
+ model: targetMapping.model.name,
1422
+ table: targetMapping.tableName,
1423
+ columns: referencedColumns
1424
+ },
1425
+ ...ifDefined("name", parsedRelation.constraintName),
1426
+ ...ifDefined("onDelete", onDelete),
1427
+ ...ifDefined("onUpdate", onUpdate)
1428
+ });
1429
+ resultFkRelationMetadata.push({
1430
+ declaringModelName: model.name,
1431
+ declaringFieldName: relationAttribute.field.name,
1432
+ declaringTableName: tableName,
1433
+ targetModelName: targetMapping.model.name,
1434
+ targetTableName: targetMapping.tableName,
1435
+ ...ifDefined("relationName", parsedRelation.relationName),
1436
+ localColumns,
1437
+ referencedColumns
1438
+ });
1439
+ }
1440
+ return {
1441
+ modelNode: {
1442
+ modelName: model.name,
1443
+ tableName,
1444
+ fields: resolvedFields.map((resolvedField) => ({
1445
+ fieldName: resolvedField.field.name,
1446
+ columnName: resolvedField.columnName,
1447
+ descriptor: resolvedField.descriptor,
1448
+ nullable: resolvedField.field.optional,
1449
+ ...ifDefined("default", resolvedField.defaultValue),
1450
+ ...ifDefined("executionDefault", resolvedField.executionDefault)
1451
+ })),
1452
+ ...primaryKeyColumns.length > 0 ? { id: {
1453
+ columns: primaryKeyColumns,
1454
+ ...ifDefined("name", primaryKeyName)
1455
+ } } : {},
1456
+ ...uniqueConstraints.length > 0 ? { uniques: uniqueConstraints } : {},
1457
+ ...indexNodes.length > 0 ? { indexes: indexNodes } : {},
1458
+ ...foreignKeyNodes.length > 0 ? { foreignKeys: foreignKeyNodes } : {}
1459
+ },
1460
+ fkRelationMetadata: resultFkRelationMetadata,
1461
+ backrelationCandidates: resultBackrelationCandidates,
1462
+ resolvedFields
1463
+ };
1464
+ }
1465
+ function buildValueObjects(compositeTypes, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors, diagnostics, sourceId) {
1466
+ const valueObjects = {};
1467
+ const compositeTypeNames = new Set(compositeTypes.map((ct) => ct.name));
1468
+ for (const compositeType of compositeTypes) {
1469
+ const fields = {};
1470
+ for (const field of compositeType.fields) {
1471
+ if (compositeTypeNames.has(field.typeName)) {
1472
+ const result = {
1473
+ type: {
1474
+ kind: "valueObject",
1475
+ name: field.typeName
1476
+ },
1477
+ nullable: field.optional
1478
+ };
1479
+ fields[field.name] = field.list ? {
1480
+ ...result,
1481
+ many: true
1482
+ } : result;
1483
+ continue;
1240
1484
  }
1241
- if (primaryKeyColumns.length > 0) table = table.primaryKey(primaryKeyColumns);
1242
- for (const modelAttribute of model.attributes) {
1243
- if (modelAttribute.name === "map") continue;
1244
- if (modelAttribute.name === "unique" || modelAttribute.name === "index") {
1245
- const fieldNames = parseAttributeFieldList({
1246
- attribute: modelAttribute,
1247
- sourceId,
1248
- diagnostics,
1249
- code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1250
- messagePrefix: `Model "${model.name}" @@${modelAttribute.name}`
1251
- });
1252
- if (!fieldNames) continue;
1253
- const columnNames = mapFieldNamesToColumns({
1254
- modelName: model.name,
1255
- fieldNames,
1256
- mapping,
1257
- sourceId,
1258
- diagnostics,
1259
- span: modelAttribute.span,
1260
- contextLabel: `Model "${model.name}" @@${modelAttribute.name}`
1261
- });
1262
- if (!columnNames) continue;
1263
- if (modelAttribute.name === "unique") table = table.unique(columnNames);
1264
- else table = table.index(columnNames);
1265
- continue;
1266
- }
1267
- if (modelAttribute.name.startsWith("pgvector.") && !composedExtensions.has("pgvector")) {
1268
- diagnostics.push({
1269
- code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
1270
- message: `Attribute "@@${modelAttribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
1271
- sourceId,
1272
- span: modelAttribute.span
1273
- });
1274
- continue;
1275
- }
1485
+ const descriptor = resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors);
1486
+ if (!descriptor) {
1276
1487
  diagnostics.push({
1277
- code: "PSL_UNSUPPORTED_MODEL_ATTRIBUTE",
1278
- message: `Model "${model.name}" uses unsupported attribute "@@${modelAttribute.name}"`,
1488
+ code: "PSL_UNSUPPORTED_FIELD_TYPE",
1489
+ message: `Field "${compositeType.name}.${field.name}" type "${field.typeName}" is not supported`,
1279
1490
  sourceId,
1280
- span: modelAttribute.span
1491
+ span: field.span
1281
1492
  });
1493
+ continue;
1282
1494
  }
1283
- for (const relationAttribute of relationAttributes) {
1284
- if (relationAttribute.field.list) continue;
1285
- if (!modelNames.has(relationAttribute.field.typeName)) {
1286
- diagnostics.push({
1287
- code: "PSL_INVALID_RELATION_TARGET",
1288
- message: `Relation field "${model.name}.${relationAttribute.field.name}" references unknown model "${relationAttribute.field.typeName}"`,
1289
- sourceId,
1290
- span: relationAttribute.field.span
1291
- });
1292
- continue;
1495
+ fields[field.name] = {
1496
+ nullable: field.optional,
1497
+ type: {
1498
+ kind: "scalar",
1499
+ codecId: descriptor.codecId
1293
1500
  }
1294
- const parsedRelation = parseRelationAttribute({
1295
- attribute: relationAttribute.relation,
1296
- modelName: model.name,
1297
- fieldName: relationAttribute.field.name,
1501
+ };
1502
+ }
1503
+ valueObjects[compositeType.name] = { fields };
1504
+ }
1505
+ return valueObjects;
1506
+ }
1507
+ function patchModelDomainFields(models, modelResolvedFields) {
1508
+ let patched = models;
1509
+ for (const [modelName, resolvedFields] of modelResolvedFields) {
1510
+ const model = patched[modelName];
1511
+ if (!model) continue;
1512
+ let needsPatch = false;
1513
+ const patchedFields = { ...model.fields };
1514
+ for (const rf of resolvedFields) if (rf.valueObjectTypeName) {
1515
+ needsPatch = true;
1516
+ patchedFields[rf.field.name] = {
1517
+ nullable: rf.field.optional,
1518
+ type: {
1519
+ kind: "valueObject",
1520
+ name: rf.valueObjectTypeName
1521
+ },
1522
+ ...rf.many ? { many: true } : {}
1523
+ };
1524
+ } else if (rf.many && rf.scalarCodecId) {
1525
+ needsPatch = true;
1526
+ patchedFields[rf.field.name] = {
1527
+ nullable: rf.field.optional,
1528
+ type: {
1529
+ kind: "scalar",
1530
+ codecId: rf.scalarCodecId
1531
+ },
1532
+ many: true
1533
+ };
1534
+ }
1535
+ if (needsPatch) patched = {
1536
+ ...patched,
1537
+ [modelName]: {
1538
+ ...model,
1539
+ fields: patchedFields
1540
+ }
1541
+ };
1542
+ }
1543
+ return patched;
1544
+ }
1545
+ function collectPolymorphismDeclarations(models, sourceId, diagnostics) {
1546
+ const discriminatorDeclarations = /* @__PURE__ */ new Map();
1547
+ const baseDeclarations = /* @__PURE__ */ new Map();
1548
+ for (const model of models) for (const attr of model.attributes) {
1549
+ if (attr.name === "discriminator") {
1550
+ const fieldName = getPositionalArgument(attr);
1551
+ if (!fieldName) {
1552
+ diagnostics.push({
1553
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1554
+ message: `Model "${model.name}" @@discriminator requires a field name argument`,
1298
1555
  sourceId,
1299
- diagnostics
1556
+ span: attr.span
1300
1557
  });
1301
- if (!parsedRelation) continue;
1302
- if (!parsedRelation.fields || !parsedRelation.references) {
1303
- diagnostics.push({
1304
- code: "PSL_INVALID_RELATION_ATTRIBUTE",
1305
- message: `Relation field "${model.name}.${relationAttribute.field.name}" requires fields and references arguments`,
1306
- sourceId,
1307
- span: relationAttribute.relation.span
1308
- });
1309
- continue;
1310
- }
1311
- const targetMapping = modelMappings.get(relationAttribute.field.typeName);
1312
- if (!targetMapping) {
1313
- diagnostics.push({
1314
- code: "PSL_INVALID_RELATION_TARGET",
1315
- message: `Relation field "${model.name}.${relationAttribute.field.name}" references unknown model "${relationAttribute.field.typeName}"`,
1316
- sourceId,
1317
- span: relationAttribute.field.span
1318
- });
1319
- continue;
1320
- }
1321
- const localColumns = mapFieldNamesToColumns({
1322
- modelName: model.name,
1323
- fieldNames: parsedRelation.fields,
1324
- mapping,
1558
+ continue;
1559
+ }
1560
+ const discField = model.fields.find((f) => f.name === fieldName);
1561
+ if (discField && discField.typeName !== "String") {
1562
+ diagnostics.push({
1563
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1564
+ message: `Discriminator field "${fieldName}" on model "${model.name}" must be of type String, but is "${discField.typeName}"`,
1325
1565
  sourceId,
1326
- diagnostics,
1327
- span: relationAttribute.relation.span,
1328
- contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
1566
+ span: attr.span
1329
1567
  });
1330
- if (!localColumns) continue;
1331
- const referencedColumns = mapFieldNamesToColumns({
1332
- modelName: targetMapping.model.name,
1333
- fieldNames: parsedRelation.references,
1334
- mapping: targetMapping,
1568
+ continue;
1569
+ }
1570
+ discriminatorDeclarations.set(model.name, {
1571
+ fieldName,
1572
+ span: attr.span
1573
+ });
1574
+ }
1575
+ if (attr.name === "base") {
1576
+ const baseName = getPositionalArgument(attr, 0);
1577
+ const rawValue = getPositionalArgument(attr, 1);
1578
+ if (!baseName || !rawValue) {
1579
+ diagnostics.push({
1580
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1581
+ message: `Model "${model.name}" @@base requires two arguments: base model name and discriminator value`,
1335
1582
  sourceId,
1336
- diagnostics,
1337
- span: relationAttribute.relation.span,
1338
- contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
1583
+ span: attr.span
1339
1584
  });
1340
- if (!referencedColumns) continue;
1341
- if (localColumns.length !== referencedColumns.length) {
1342
- diagnostics.push({
1343
- code: "PSL_INVALID_RELATION_ATTRIBUTE",
1344
- message: `Relation field "${model.name}.${relationAttribute.field.name}" must provide the same number of fields and references`,
1345
- sourceId,
1346
- span: relationAttribute.relation.span
1347
- });
1348
- continue;
1349
- }
1350
- const onDelete = parsedRelation.onDelete ? normalizeReferentialAction({
1351
- modelName: model.name,
1352
- fieldName: relationAttribute.field.name,
1353
- actionName: "onDelete",
1354
- actionToken: parsedRelation.onDelete,
1355
- sourceId,
1356
- span: relationAttribute.field.span,
1357
- diagnostics
1358
- }) : void 0;
1359
- const onUpdate = parsedRelation.onUpdate ? normalizeReferentialAction({
1360
- modelName: model.name,
1361
- fieldName: relationAttribute.field.name,
1362
- actionName: "onUpdate",
1363
- actionToken: parsedRelation.onUpdate,
1585
+ continue;
1586
+ }
1587
+ const value = parseQuotedStringLiteral(rawValue);
1588
+ if (value === void 0) {
1589
+ diagnostics.push({
1590
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1591
+ message: `Model "${model.name}" @@base discriminator value must be a quoted string literal`,
1364
1592
  sourceId,
1365
- span: relationAttribute.field.span,
1366
- diagnostics
1367
- }) : void 0;
1368
- table = table.foreignKey(localColumns, {
1369
- table: targetMapping.tableName,
1370
- columns: referencedColumns
1371
- }, {
1372
- ...ifDefined("name", parsedRelation.constraintName),
1373
- ...ifDefined("onDelete", onDelete),
1374
- ...ifDefined("onUpdate", onUpdate)
1593
+ span: attr.span
1375
1594
  });
1376
- fkRelationMetadata.push({
1377
- declaringModelName: model.name,
1378
- declaringFieldName: relationAttribute.field.name,
1379
- declaringTableName: tableName,
1380
- targetModelName: targetMapping.model.name,
1381
- targetTableName: targetMapping.tableName,
1382
- ...ifDefined("relationName", parsedRelation.relationName),
1383
- localColumns,
1384
- referencedColumns
1595
+ continue;
1596
+ }
1597
+ baseDeclarations.set(model.name, {
1598
+ baseName,
1599
+ value,
1600
+ span: attr.span
1601
+ });
1602
+ }
1603
+ }
1604
+ return {
1605
+ discriminatorDeclarations,
1606
+ baseDeclarations
1607
+ };
1608
+ }
1609
+ function resolvePolymorphism(models, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, sourceId, diagnostics) {
1610
+ let patched = models;
1611
+ for (const [modelName, decl] of discriminatorDeclarations) {
1612
+ if (baseDeclarations.has(modelName)) {
1613
+ diagnostics.push({
1614
+ code: "PSL_DISCRIMINATOR_AND_BASE",
1615
+ message: `Model "${modelName}" cannot have both @@discriminator and @@base`,
1616
+ sourceId,
1617
+ span: decl.span
1618
+ });
1619
+ continue;
1620
+ }
1621
+ const model = patched[modelName];
1622
+ if (!model) continue;
1623
+ if (!Object.hasOwn(model.fields, decl.fieldName)) {
1624
+ diagnostics.push({
1625
+ code: "PSL_DISCRIMINATOR_FIELD_NOT_FOUND",
1626
+ message: `Discriminator field "${decl.fieldName}" is not a field on model "${modelName}"`,
1627
+ sourceId,
1628
+ span: decl.span
1629
+ });
1630
+ continue;
1631
+ }
1632
+ const variants = {};
1633
+ const seenValues = /* @__PURE__ */ new Map();
1634
+ for (const [variantName, baseDecl] of baseDeclarations) {
1635
+ if (baseDecl.baseName !== modelName) continue;
1636
+ const existingVariant = seenValues.get(baseDecl.value);
1637
+ if (existingVariant) {
1638
+ diagnostics.push({
1639
+ code: "PSL_DUPLICATE_DISCRIMINATOR_VALUE",
1640
+ message: `Discriminator value "${baseDecl.value}" is used by both "${existingVariant}" and "${variantName}" on base model "${modelName}"`,
1641
+ sourceId,
1642
+ span: baseDecl.span
1385
1643
  });
1644
+ continue;
1645
+ }
1646
+ seenValues.set(baseDecl.value, variantName);
1647
+ variants[variantName] = { value: baseDecl.value };
1648
+ }
1649
+ if (Object.keys(variants).length === 0) {
1650
+ diagnostics.push({
1651
+ code: "PSL_ORPHANED_DISCRIMINATOR",
1652
+ message: `Model "${modelName}" has @@discriminator but no variant models declare @@base(${modelName}, ...)`,
1653
+ sourceId,
1654
+ span: decl.span
1655
+ });
1656
+ continue;
1657
+ }
1658
+ patched = {
1659
+ ...patched,
1660
+ [modelName]: {
1661
+ ...model,
1662
+ discriminator: { field: decl.fieldName },
1663
+ variants
1664
+ }
1665
+ };
1666
+ }
1667
+ for (const [variantName, baseDecl] of baseDeclarations) {
1668
+ if (!modelNames.has(baseDecl.baseName)) {
1669
+ diagnostics.push({
1670
+ code: "PSL_BASE_TARGET_NOT_FOUND",
1671
+ message: `Model "${variantName}" @@base references non-existent model "${baseDecl.baseName}"`,
1672
+ sourceId,
1673
+ span: baseDecl.span
1674
+ });
1675
+ continue;
1676
+ }
1677
+ if (!discriminatorDeclarations.has(baseDecl.baseName)) {
1678
+ diagnostics.push({
1679
+ code: "PSL_ORPHANED_BASE",
1680
+ message: `Model "${variantName}" declares @@base(${baseDecl.baseName}, ...) but "${baseDecl.baseName}" has no @@discriminator`,
1681
+ sourceId,
1682
+ span: baseDecl.span
1683
+ });
1684
+ continue;
1685
+ }
1686
+ if (discriminatorDeclarations.has(variantName)) continue;
1687
+ const variantModel = patched[variantName];
1688
+ if (!variantModel) continue;
1689
+ const baseMapping = modelMappings.get(baseDecl.baseName);
1690
+ const variantMapping = modelMappings.get(variantName);
1691
+ const resolvedTable = variantMapping?.model.attributes.some((attr) => attr.name === "map") ?? false ? variantMapping?.tableName : baseMapping?.tableName;
1692
+ patched = {
1693
+ ...patched,
1694
+ [variantName]: {
1695
+ ...variantModel,
1696
+ base: baseDecl.baseName,
1697
+ ...resolvedTable ? { storage: {
1698
+ ...variantModel.storage,
1699
+ table: resolvedTable
1700
+ } } : {}
1386
1701
  }
1387
- return table;
1702
+ };
1703
+ }
1704
+ return patched;
1705
+ }
1706
+ function interpretPslDocumentToSqlContract(input) {
1707
+ const sourceId = input.document.ast.sourceId;
1708
+ if (!input.target) return notOk({
1709
+ summary: "PSL to SQL contract interpretation failed",
1710
+ diagnostics: [{
1711
+ code: "PSL_TARGET_CONTEXT_REQUIRED",
1712
+ message: "PSL interpretation requires an explicit target context from composition.",
1713
+ sourceId
1714
+ }]
1715
+ });
1716
+ if (!input.scalarTypeDescriptors) return notOk({
1717
+ summary: "PSL to SQL contract interpretation failed",
1718
+ diagnostics: [{
1719
+ code: "PSL_SCALAR_TYPE_CONTEXT_REQUIRED",
1720
+ message: "PSL interpretation requires composed scalar type descriptors.",
1721
+ sourceId
1722
+ }]
1723
+ });
1724
+ const diagnostics = mapParserDiagnostics(input.document);
1725
+ const models = input.document.ast.models ?? [];
1726
+ const enums = input.document.ast.enums ?? [];
1727
+ const compositeTypes = input.document.ast.compositeTypes ?? [];
1728
+ const modelNames = new Set(models.map((model) => model.name));
1729
+ const compositeTypeNames = new Set(compositeTypes.map((ct) => ct.name));
1730
+ const composedExtensions = new Set(input.composedExtensionPacks ?? []);
1731
+ const defaultFunctionRegistry = input.controlMutationDefaults?.defaultFunctionRegistry ?? /* @__PURE__ */ new Map();
1732
+ const generatorDescriptors = input.controlMutationDefaults?.generatorDescriptors ?? [];
1733
+ const generatorDescriptorById = /* @__PURE__ */ new Map();
1734
+ for (const descriptor of generatorDescriptors) generatorDescriptorById.set(descriptor.id, descriptor);
1735
+ const enumResult = processEnumDeclarations({
1736
+ enums,
1737
+ sourceId,
1738
+ enumTypeConstructor: getAuthoringTypeConstructor(input.authoringContributions, ["enum"]),
1739
+ diagnostics
1740
+ });
1741
+ const namedTypeResult = resolveNamedTypeDeclarations({
1742
+ declarations: input.document.ast.types?.declarations ?? [],
1743
+ sourceId,
1744
+ enumTypeDescriptors: enumResult.enumTypeDescriptors,
1745
+ scalarTypeDescriptors: input.scalarTypeDescriptors,
1746
+ composedExtensions,
1747
+ pgvectorVectorConstructor: getAuthoringTypeConstructor(input.authoringContributions, ["pgvector", "vector"]),
1748
+ diagnostics
1749
+ });
1750
+ const storageTypes = {
1751
+ ...enumResult.storageTypes,
1752
+ ...namedTypeResult.storageTypes
1753
+ };
1754
+ const modelMappings = buildModelMappings(models, diagnostics, sourceId);
1755
+ const modelNodes = [];
1756
+ const fkRelationMetadata = [];
1757
+ const backrelationCandidates = [];
1758
+ const modelResolvedFields = /* @__PURE__ */ new Map();
1759
+ for (const model of models) {
1760
+ const mapping = modelMappings.get(model.name);
1761
+ if (!mapping) continue;
1762
+ const result = buildModelNodeFromPsl({
1763
+ model,
1764
+ mapping,
1765
+ modelMappings,
1766
+ modelNames,
1767
+ compositeTypeNames,
1768
+ enumTypeDescriptors: enumResult.enumTypeDescriptors,
1769
+ namedTypeDescriptors: namedTypeResult.namedTypeDescriptors,
1770
+ namedTypeBaseTypes: namedTypeResult.namedTypeBaseTypes,
1771
+ composedExtensions,
1772
+ authoringContributions: input.authoringContributions,
1773
+ defaultFunctionRegistry,
1774
+ generatorDescriptorById,
1775
+ scalarTypeDescriptors: input.scalarTypeDescriptors,
1776
+ sourceId,
1777
+ diagnostics
1388
1778
  });
1779
+ modelNodes.push(result.modelNode);
1780
+ fkRelationMetadata.push(...result.fkRelationMetadata);
1781
+ backrelationCandidates.push(...result.backrelationCandidates);
1782
+ modelResolvedFields.set(model.name, result.resolvedFields);
1389
1783
  }
1390
1784
  const { modelRelations, fkRelationsByPair } = indexFkRelations({ fkRelationMetadata });
1391
1785
  applyBackrelationCandidates({
@@ -1395,18 +1789,38 @@ function interpretPslDocumentToSqlContractIR(input) {
1395
1789
  diagnostics,
1396
1790
  sourceId
1397
1791
  });
1398
- builder = emitModelsWithRelations({
1399
- builder,
1400
- resolvedModels,
1401
- modelRelations
1402
- });
1792
+ const { discriminatorDeclarations, baseDeclarations } = collectPolymorphismDeclarations(models, sourceId, diagnostics);
1403
1793
  if (diagnostics.length > 0) return notOk({
1404
- summary: "PSL to SQL Contract IR normalization failed",
1794
+ summary: "PSL to SQL contract interpretation failed",
1405
1795
  diagnostics: diagnostics.filter((diagnostic, index, allDiagnostics) => allDiagnostics.findIndex((candidate) => candidate.code === diagnostic.code && candidate.message === diagnostic.message && candidate.sourceId === diagnostic.sourceId && (candidate.span && diagnostic.span && hasSameSpan(candidate.span, diagnostic.span) || !candidate.span && !diagnostic.span)) === index)
1406
1796
  });
1407
- return ok(builder.build());
1797
+ const contract = buildSqlContractFromDefinition({
1798
+ target: input.target,
1799
+ ...ifDefined("extensionPacks", buildComposedExtensionPackRefs(input.target, [...composedExtensions].sort(compareStrings), input.composedExtensionPackRefs)),
1800
+ ...Object.keys(storageTypes).length > 0 ? { storageTypes } : {},
1801
+ models: modelNodes.map((model) => ({
1802
+ ...model,
1803
+ ...modelRelations.has(model.modelName) ? { relations: [...modelRelations.get(model.modelName) ?? []].sort((left, right) => compareStrings(left.fieldName, right.fieldName)) } : {}
1804
+ }))
1805
+ });
1806
+ const valueObjects = buildValueObjects(compositeTypes, enumResult.enumTypeDescriptors, namedTypeResult.namedTypeDescriptors, input.scalarTypeDescriptors, diagnostics, sourceId);
1807
+ let patchedModels = patchModelDomainFields(contract.models, modelResolvedFields);
1808
+ const polyDiagnostics = [];
1809
+ patchedModels = resolvePolymorphism(patchedModels, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, sourceId, polyDiagnostics);
1810
+ if (polyDiagnostics.length > 0) return notOk({
1811
+ summary: "PSL to SQL contract interpretation failed",
1812
+ diagnostics: polyDiagnostics
1813
+ });
1814
+ const variantModelNames = new Set(baseDeclarations.keys());
1815
+ const filteredRoots = Object.fromEntries(Object.entries(contract.roots).filter(([, modelName]) => !variantModelNames.has(modelName)));
1816
+ return ok({
1817
+ ...contract,
1818
+ roots: filteredRoots,
1819
+ models: patchedModels,
1820
+ ...Object.keys(valueObjects).length > 0 ? { valueObjects } : {}
1821
+ });
1408
1822
  }
1409
1823
 
1410
1824
  //#endregion
1411
- export { interpretPslDocumentToSqlContractIR as t };
1412
- //# sourceMappingURL=interpreter-D7gLmaHz.mjs.map
1825
+ export { interpretPslDocumentToSqlContract as t };
1826
+ //# sourceMappingURL=interpreter-qjtea3zY.mjs.map