@prisma-next/sql-contract-psl 0.0.1 → 0.3.0-dev.114

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.
@@ -0,0 +1,1412 @@
1
+ import { defineContract } from "@prisma-next/sql-contract-ts/contract-builder";
2
+ import { assertDefined, invariant } from "@prisma-next/utils/assertions";
3
+ import { ifDefined } from "@prisma-next/utils/defined";
4
+ import { notOk, ok } from "@prisma-next/utils/result";
5
+
6
+ //#region src/default-function-registry.ts
7
+ function resolveSpanPositionFromBase(base, text, offset) {
8
+ const safeOffset = Math.min(Math.max(0, offset), text.length);
9
+ let line = base.start.line;
10
+ let column = base.start.column;
11
+ for (let index = 0; index < safeOffset; index += 1) {
12
+ const character = text[index] ?? "";
13
+ if (character === "\r") {
14
+ if (text[index + 1] === "\n" && index + 1 < safeOffset) index += 1;
15
+ line += 1;
16
+ column = 1;
17
+ continue;
18
+ }
19
+ if (character === "\n") {
20
+ line += 1;
21
+ column = 1;
22
+ continue;
23
+ }
24
+ column += 1;
25
+ }
26
+ return {
27
+ offset: base.start.offset + safeOffset,
28
+ line,
29
+ column
30
+ };
31
+ }
32
+ function createSpanFromBase(base, startOffset, endOffset, text) {
33
+ const safeStart = Math.max(0, Math.min(startOffset, text.length));
34
+ const safeEnd = Math.max(safeStart, Math.min(endOffset, text.length));
35
+ return {
36
+ start: resolveSpanPositionFromBase(base, text, safeStart),
37
+ end: resolveSpanPositionFromBase(base, text, safeEnd)
38
+ };
39
+ }
40
+ function splitTopLevelArgs(raw) {
41
+ if (raw.trim().length === 0) return [];
42
+ const parts = [];
43
+ let depthParen = 0;
44
+ let depthBracket = 0;
45
+ let quote = null;
46
+ let start = 0;
47
+ for (let index = 0; index < raw.length; index += 1) {
48
+ const character = raw[index] ?? "";
49
+ if (quote) {
50
+ if (character === quote && raw[index - 1] !== "\\") quote = null;
51
+ continue;
52
+ }
53
+ if (character === "\"" || character === "'") {
54
+ quote = character;
55
+ continue;
56
+ }
57
+ if (character === "(") {
58
+ depthParen += 1;
59
+ continue;
60
+ }
61
+ if (character === ")") {
62
+ depthParen = Math.max(0, depthParen - 1);
63
+ continue;
64
+ }
65
+ if (character === "[") {
66
+ depthBracket += 1;
67
+ continue;
68
+ }
69
+ if (character === "]") {
70
+ depthBracket = Math.max(0, depthBracket - 1);
71
+ continue;
72
+ }
73
+ if (character === "," && depthParen === 0 && depthBracket === 0) {
74
+ parts.push({
75
+ raw: raw.slice(start, index),
76
+ start,
77
+ end: index
78
+ });
79
+ start = index + 1;
80
+ }
81
+ }
82
+ parts.push({
83
+ raw: raw.slice(start),
84
+ start,
85
+ end: raw.length
86
+ });
87
+ return parts;
88
+ }
89
+ function parseDefaultFunctionCall(expression, expressionSpan) {
90
+ const trimmed = expression.trim();
91
+ const leadingWhitespace = expression.length - expression.trimStart().length;
92
+ const trailingWhitespace = expression.length - expression.trimEnd().length;
93
+ const contentEnd = expression.length - trailingWhitespace;
94
+ const openParen = trimmed.indexOf("(");
95
+ const closeParen = trimmed.lastIndexOf(")");
96
+ if (openParen <= 0 || closeParen !== trimmed.length - 1) return;
97
+ const functionName = trimmed.slice(0, openParen).trim();
98
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(functionName)) return;
99
+ const parts = splitTopLevelArgs(trimmed.slice(openParen + 1, closeParen));
100
+ const args = [];
101
+ for (const part of parts) {
102
+ const raw = part.raw.trim();
103
+ if (raw.length === 0) return;
104
+ const leadingPartWhitespace = part.raw.length - part.raw.trimStart().length;
105
+ const argStart = leadingWhitespace + openParen + 1 + part.start + leadingPartWhitespace;
106
+ const argEnd = argStart + raw.length;
107
+ args.push({
108
+ raw,
109
+ span: createSpanFromBase(expressionSpan, argStart, argEnd, expression)
110
+ });
111
+ }
112
+ return {
113
+ name: functionName,
114
+ raw: trimmed,
115
+ args,
116
+ span: createSpanFromBase(expressionSpan, leadingWhitespace, contentEnd, expression)
117
+ };
118
+ }
119
+ function formatSupportedFunctionList(registry) {
120
+ const signatures = Array.from(registry.entries()).sort(([a], [b]) => a.localeCompare(b)).flatMap(([functionName, entry]) => {
121
+ const usageSignatures = entry.usageSignatures?.filter((signature) => signature.length > 0);
122
+ return usageSignatures && usageSignatures.length > 0 ? usageSignatures : [`${functionName}()`];
123
+ });
124
+ return signatures.length > 0 ? signatures.join(", ") : "none";
125
+ }
126
+ function lowerDefaultFunctionWithRegistry(input) {
127
+ const entry = input.registry.get(input.call.name);
128
+ if (entry) return entry.lower({
129
+ call: input.call,
130
+ context: input.context
131
+ });
132
+ const supportedFunctionList = formatSupportedFunctionList(input.registry);
133
+ return {
134
+ ok: false,
135
+ diagnostic: {
136
+ code: "PSL_UNKNOWN_DEFAULT_FUNCTION",
137
+ message: `Default function "${input.call.name}" is not supported in SQL PSL provider v1. Supported functions: ${supportedFunctionList}.`,
138
+ sourceId: input.context.sourceId,
139
+ span: input.call.span
140
+ }
141
+ };
142
+ }
143
+
144
+ //#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;
181
+ 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)
216
+ };
217
+ }
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 {};
228
+ }
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
+ };
406
+ }
407
+ const NATIVE_TYPE_SPECS = {
408
+ "db.VarChar": {
409
+ args: "optionalLength",
410
+ baseType: "String",
411
+ codecId: "sql/varchar@1",
412
+ nativeType: "character varying"
413
+ },
414
+ "db.Char": {
415
+ args: "optionalLength",
416
+ baseType: "String",
417
+ codecId: "sql/char@1",
418
+ nativeType: "character"
419
+ },
420
+ "db.Uuid": {
421
+ args: "noArgs",
422
+ baseType: "String",
423
+ codecId: null,
424
+ nativeType: "uuid"
425
+ },
426
+ "db.SmallInt": {
427
+ args: "noArgs",
428
+ baseType: "Int",
429
+ codecId: "pg/int2@1",
430
+ nativeType: "int2"
431
+ },
432
+ "db.Real": {
433
+ args: "noArgs",
434
+ baseType: "Float",
435
+ codecId: "pg/float4@1",
436
+ nativeType: "float4"
437
+ },
438
+ "db.Numeric": {
439
+ args: "optionalNumeric",
440
+ baseType: "Decimal",
441
+ codecId: "pg/numeric@1",
442
+ nativeType: "numeric"
443
+ },
444
+ "db.Timestamp": {
445
+ args: "optionalPrecision",
446
+ baseType: "DateTime",
447
+ codecId: "pg/timestamp@1",
448
+ nativeType: "timestamp"
449
+ },
450
+ "db.Timestamptz": {
451
+ args: "optionalPrecision",
452
+ baseType: "DateTime",
453
+ codecId: "pg/timestamptz@1",
454
+ nativeType: "timestamptz"
455
+ },
456
+ "db.Date": {
457
+ args: "noArgs",
458
+ baseType: "DateTime",
459
+ codecId: null,
460
+ nativeType: "date"
461
+ },
462
+ "db.Time": {
463
+ args: "optionalPrecision",
464
+ baseType: "DateTime",
465
+ codecId: "pg/time@1",
466
+ nativeType: "time"
467
+ },
468
+ "db.Timetz": {
469
+ args: "optionalPrecision",
470
+ baseType: "DateTime",
471
+ codecId: "pg/timetz@1",
472
+ nativeType: "timetz"
473
+ },
474
+ "db.Json": {
475
+ args: "noArgs",
476
+ baseType: "Json",
477
+ codecId: "pg/json@1",
478
+ nativeType: "json"
479
+ }
480
+ };
481
+ function resolveDbNativeTypeAttribute(input) {
482
+ const spec = NATIVE_TYPE_SPECS[input.attribute.name];
483
+ if (!spec) {
484
+ input.diagnostics.push({
485
+ code: "PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE",
486
+ message: `${input.entityLabel} uses unsupported attribute "@${input.attribute.name}"`,
487
+ sourceId: input.sourceId,
488
+ span: input.attribute.span
489
+ });
490
+ return;
491
+ }
492
+ if (input.baseType !== spec.baseType) return pushInvalidAttributeArgument({
493
+ diagnostics: input.diagnostics,
494
+ sourceId: input.sourceId,
495
+ span: input.attribute.span,
496
+ message: `${input.entityLabel} uses @${input.attribute.name} on unsupported base type "${input.baseType}". Expected "${spec.baseType}".`
497
+ });
498
+ switch (spec.args) {
499
+ case "noArgs":
500
+ if (getPositionalArguments(input.attribute).length > 0 || input.attribute.args.length > 0) return pushInvalidAttributeArgument({
501
+ diagnostics: input.diagnostics,
502
+ sourceId: input.sourceId,
503
+ span: input.attribute.span,
504
+ message: `${input.entityLabel} @${input.attribute.name} does not accept arguments.`
505
+ });
506
+ return {
507
+ codecId: spec.codecId ?? input.baseDescriptor.codecId,
508
+ nativeType: spec.nativeType
509
+ };
510
+ case "optionalLength": {
511
+ const length = parseOptionalSingleIntegerArgument({
512
+ attribute: input.attribute,
513
+ diagnostics: input.diagnostics,
514
+ sourceId: input.sourceId,
515
+ entityLabel: input.entityLabel,
516
+ minimum: 1,
517
+ valueLabel: "positive integer length"
518
+ });
519
+ if (length === void 0) return;
520
+ return {
521
+ codecId: spec.codecId,
522
+ nativeType: spec.nativeType,
523
+ ...length === null ? {} : { typeParams: { length } }
524
+ };
525
+ }
526
+ case "optionalPrecision": {
527
+ const precision = parseOptionalSingleIntegerArgument({
528
+ attribute: input.attribute,
529
+ diagnostics: input.diagnostics,
530
+ sourceId: input.sourceId,
531
+ entityLabel: input.entityLabel,
532
+ minimum: 0,
533
+ valueLabel: "non-negative integer precision"
534
+ });
535
+ if (precision === void 0) return;
536
+ return {
537
+ codecId: spec.codecId,
538
+ nativeType: spec.nativeType,
539
+ ...precision === null ? {} : { typeParams: { precision } }
540
+ };
541
+ }
542
+ case "optionalNumeric": {
543
+ const numeric = parseOptionalNumericArguments({
544
+ attribute: input.attribute,
545
+ diagnostics: input.diagnostics,
546
+ sourceId: input.sourceId,
547
+ entityLabel: input.entityLabel
548
+ });
549
+ if (numeric === void 0) return;
550
+ return {
551
+ codecId: spec.codecId,
552
+ nativeType: spec.nativeType,
553
+ ...numeric === null ? {} : { typeParams: numeric }
554
+ };
555
+ }
556
+ }
557
+ }
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
603
+ });
604
+ else if (!(field.typeName === "Bytes" || namedTypeBaseTypes.get(field.typeRef ?? field.typeName) === "Bytes")) diagnostics.push({
605
+ 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"))
665
+ });
666
+ }
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;
671
+ }
672
+ function compareStrings(left, right) {
673
+ if (left < right) return -1;
674
+ if (left > right) return 1;
675
+ return 0;
676
+ }
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
693
+ });
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);
701
+ }
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
745
+ });
746
+ }
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;
772
+ });
773
+ }
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
792
+ });
793
+ }
794
+ function parseAttributeFieldList(input) {
795
+ const raw = getNamedArgument(input.attribute, "fields") ?? getPositionalArgument(input.attribute);
796
+ if (!raw) {
797
+ input.diagnostics.push({
798
+ code: input.code,
799
+ message: `${input.messagePrefix} requires fields list argument`,
800
+ sourceId: input.sourceId,
801
+ span: input.attribute.span
802
+ });
803
+ return;
804
+ }
805
+ const fields = parseFieldList(raw);
806
+ if (!fields || fields.length === 0) {
807
+ input.diagnostics.push({
808
+ code: input.code,
809
+ message: `${input.messagePrefix} requires bracketed field list argument`,
810
+ sourceId: input.sourceId,
811
+ span: input.attribute.span
812
+ });
813
+ return;
814
+ }
815
+ return fields;
816
+ }
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({
823
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
824
+ message: `${input.contextLabel} references unknown field "${input.modelName}.${fieldName}"`,
825
+ sourceId: input.sourceId,
826
+ span: input.span
827
+ });
828
+ return;
829
+ }
830
+ columns.push(columnName);
831
+ }
832
+ return columns;
833
+ }
834
+ function buildModelMappings(models, diagnostics, sourceId) {
835
+ const result = /* @__PURE__ */ new Map();
836
+ for (const model of models) {
837
+ const tableName = parseMapName({
838
+ attribute: getAttribute(model.attributes, "map"),
839
+ defaultValue: lowerFirst(model.name),
840
+ sourceId,
841
+ diagnostics,
842
+ entityLabel: `Model "${model.name}"`,
843
+ span: model.span
844
+ });
845
+ const fieldColumns = /* @__PURE__ */ new Map();
846
+ for (const field of model.fields) {
847
+ const columnName = parseMapName({
848
+ attribute: getAttribute(field.attributes, "map"),
849
+ defaultValue: field.name,
850
+ sourceId,
851
+ diagnostics,
852
+ entityLabel: `Field "${model.name}.${field.name}"`,
853
+ span: field.span
854
+ });
855
+ fieldColumns.set(field.name, columnName);
856
+ }
857
+ result.set(model.name, {
858
+ model,
859
+ tableName,
860
+ fieldColumns
861
+ });
862
+ }
863
+ return result;
864
+ }
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;
888
+ }
889
+ function parseRelationAttribute(input) {
890
+ if (input.attribute.args.filter((arg) => arg.kind === "positional").length > 1) {
891
+ input.diagnostics.push({
892
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
893
+ message: `Relation field "${input.modelName}.${input.fieldName}" has too many positional arguments`,
894
+ sourceId: input.sourceId,
895
+ span: input.attribute.span
896
+ });
897
+ return;
898
+ }
899
+ let relationNameFromPositional;
900
+ const positionalNameEntry = getPositionalArgumentEntry(input.attribute);
901
+ if (positionalNameEntry) {
902
+ const parsedName = parseQuotedStringLiteral(positionalNameEntry.value);
903
+ if (!parsedName) {
904
+ input.diagnostics.push({
905
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
906
+ message: `Relation field "${input.modelName}.${input.fieldName}" positional relation name must be a quoted string literal`,
907
+ sourceId: input.sourceId,
908
+ span: positionalNameEntry.span
909
+ });
910
+ return;
911
+ }
912
+ relationNameFromPositional = parsedName;
913
+ }
914
+ for (const arg of input.attribute.args) {
915
+ if (arg.kind === "positional") continue;
916
+ if (arg.name !== "name" && arg.name !== "fields" && arg.name !== "references" && arg.name !== "map" && arg.name !== "onDelete" && arg.name !== "onUpdate") {
917
+ input.diagnostics.push({
918
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
919
+ message: `Relation field "${input.modelName}.${input.fieldName}" has unsupported argument "${arg.name}"`,
920
+ sourceId: input.sourceId,
921
+ span: arg.span
922
+ });
923
+ return;
924
+ }
925
+ }
926
+ const namedRelationNameRaw = getNamedArgument(input.attribute, "name");
927
+ const namedRelationName = namedRelationNameRaw ? parseQuotedStringLiteral(namedRelationNameRaw) : void 0;
928
+ if (namedRelationNameRaw && !namedRelationName) {
929
+ input.diagnostics.push({
930
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
931
+ message: `Relation field "${input.modelName}.${input.fieldName}" named relation name must be a quoted string literal`,
932
+ sourceId: input.sourceId,
933
+ span: input.attribute.span
934
+ });
935
+ return;
936
+ }
937
+ if (relationNameFromPositional && namedRelationName && relationNameFromPositional !== namedRelationName) {
938
+ input.diagnostics.push({
939
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
940
+ message: `Relation field "${input.modelName}.${input.fieldName}" has conflicting positional and named relation names`,
941
+ sourceId: input.sourceId,
942
+ span: input.attribute.span
943
+ });
944
+ return;
945
+ }
946
+ const relationName = namedRelationName ?? relationNameFromPositional;
947
+ const constraintNameRaw = getNamedArgument(input.attribute, "map");
948
+ const constraintName = constraintNameRaw ? parseQuotedStringLiteral(constraintNameRaw) : void 0;
949
+ if (constraintNameRaw && !constraintName) {
950
+ input.diagnostics.push({
951
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
952
+ message: `Relation field "${input.modelName}.${input.fieldName}" map argument must be a quoted string literal`,
953
+ sourceId: input.sourceId,
954
+ span: input.attribute.span
955
+ });
956
+ return;
957
+ }
958
+ const fieldsRaw = getNamedArgument(input.attribute, "fields");
959
+ const referencesRaw = getNamedArgument(input.attribute, "references");
960
+ if (fieldsRaw && !referencesRaw || !fieldsRaw && referencesRaw) {
961
+ input.diagnostics.push({
962
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
963
+ message: `Relation field "${input.modelName}.${input.fieldName}" requires fields and references arguments`,
964
+ sourceId: input.sourceId,
965
+ span: input.attribute.span
966
+ });
967
+ return;
968
+ }
969
+ let fields;
970
+ let references;
971
+ if (fieldsRaw && referencesRaw) {
972
+ const parsedFields = parseFieldList(fieldsRaw);
973
+ const parsedReferences = parseFieldList(referencesRaw);
974
+ if (!parsedFields || !parsedReferences || parsedFields.length === 0 || parsedReferences.length === 0) {
975
+ input.diagnostics.push({
976
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
977
+ message: `Relation field "${input.modelName}.${input.fieldName}" requires bracketed fields and references lists`,
978
+ sourceId: input.sourceId,
979
+ span: input.attribute.span
980
+ });
981
+ return;
982
+ }
983
+ fields = parsedFields;
984
+ references = parsedReferences;
985
+ }
986
+ const onDeleteArgument = getNamedArgument(input.attribute, "onDelete");
987
+ const onUpdateArgument = getNamedArgument(input.attribute, "onUpdate");
988
+ return {
989
+ ...ifDefined("relationName", relationName),
990
+ ...ifDefined("fields", fields),
991
+ ...ifDefined("references", references),
992
+ ...ifDefined("constraintName", constraintName),
993
+ ...ifDefined("onDelete", onDeleteArgument ? unquoteStringLiteral(onDeleteArgument) : void 0),
994
+ ...ifDefined("onUpdate", onUpdateArgument ? unquoteStringLiteral(onUpdateArgument) : void 0)
995
+ };
996
+ }
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);
1023
+ 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) {
1027
+ const nativeType = parseMapName({
1028
+ attribute: getAttribute(enumDeclaration.attributes, "map"),
1029
+ defaultValue: enumDeclaration.name,
1030
+ sourceId,
1031
+ diagnostics,
1032
+ entityLabel: `Enum "${enumDeclaration.name}"`,
1033
+ span: enumDeclaration.span
1034
+ });
1035
+ const descriptor = {
1036
+ codecId: "pg/enum@1",
1037
+ nativeType,
1038
+ typeRef: enumDeclaration.name
1039
+ };
1040
+ 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
+ });
1046
+ }
1047
+ for (const declaration of input.document.ast.types?.declarations ?? []) {
1048
+ const baseDescriptor = enumTypeDescriptors.get(declaration.baseType) ?? input.scalarTypeDescriptors.get(declaration.baseType);
1049
+ if (!baseDescriptor) {
1050
+ diagnostics.push({
1051
+ code: "PSL_UNSUPPORTED_NAMED_TYPE_BASE",
1052
+ message: `Named type "${declaration.name}" references unsupported base type "${declaration.baseType}"`,
1053
+ sourceId,
1054
+ span: declaration.span
1055
+ });
1056
+ continue;
1057
+ }
1058
+ namedTypeBaseTypes.set(declaration.name, declaration.baseType);
1059
+ const pgvectorAttribute = getAttribute(declaration.attributes, "pgvector.column");
1060
+ const dbNativeTypeAttribute = declaration.attributes.find((attribute) => attribute.name.startsWith("db."));
1061
+ const unsupportedNamedTypeAttribute = declaration.attributes.find((attribute) => attribute.name !== "pgvector.column" && !attribute.name.startsWith("db."));
1062
+ if (unsupportedNamedTypeAttribute) {
1063
+ diagnostics.push({
1064
+ code: "PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE",
1065
+ message: `Named type "${declaration.name}" uses unsupported attribute "${unsupportedNamedTypeAttribute.name}"`,
1066
+ sourceId,
1067
+ span: unsupportedNamedTypeAttribute.span
1068
+ });
1069
+ continue;
1070
+ }
1071
+ if (pgvectorAttribute && dbNativeTypeAttribute) {
1072
+ diagnostics.push({
1073
+ code: "PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE",
1074
+ message: `Named type "${declaration.name}" cannot combine @pgvector.column with @${dbNativeTypeAttribute.name}.`,
1075
+ sourceId,
1076
+ span: dbNativeTypeAttribute.span
1077
+ });
1078
+ continue;
1079
+ }
1080
+ if (pgvectorAttribute) {
1081
+ if (!composedExtensions.has("pgvector")) {
1082
+ diagnostics.push({
1083
+ code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
1084
+ message: "Attribute \"@pgvector.column\" uses unrecognized namespace \"pgvector\". Add extension pack \"pgvector\" to extensionPacks in prisma-next.config.ts.",
1085
+ sourceId,
1086
+ span: pgvectorAttribute.span
1087
+ });
1088
+ continue;
1089
+ }
1090
+ if (declaration.baseType !== "Bytes") {
1091
+ diagnostics.push({
1092
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1093
+ message: `Named type "${declaration.name}" uses @pgvector.column on unsupported base type "${declaration.baseType}"`,
1094
+ sourceId,
1095
+ span: pgvectorAttribute.span
1096
+ });
1097
+ continue;
1098
+ }
1099
+ const length = parsePgvectorLength({
1100
+ attribute: pgvectorAttribute,
1101
+ diagnostics,
1102
+ sourceId
1103
+ });
1104
+ 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, {
1111
+ codecId: "pg/vector@1",
1112
+ nativeType: "vector",
1113
+ typeParams: { length }
1114
+ });
1115
+ continue;
1116
+ }
1117
+ if (dbNativeTypeAttribute) {
1118
+ const descriptor$1 = resolveDbNativeTypeAttribute({
1119
+ attribute: dbNativeTypeAttribute,
1120
+ baseType: declaration.baseType,
1121
+ baseDescriptor,
1122
+ diagnostics,
1123
+ sourceId,
1124
+ entityLabel: `Named type "${declaration.name}"`
1125
+ });
1126
+ if (!descriptor$1) continue;
1127
+ namedTypeDescriptors.set(declaration.name, {
1128
+ ...descriptor$1,
1129
+ typeRef: declaration.name
1130
+ });
1131
+ builder = builder.storageType(declaration.name, {
1132
+ codecId: descriptor$1.codecId,
1133
+ nativeType: descriptor$1.nativeType,
1134
+ typeParams: descriptor$1.typeParams ?? {}
1135
+ });
1136
+ continue;
1137
+ }
1138
+ const descriptor = {
1139
+ codecId: baseDescriptor.codecId,
1140
+ nativeType: baseDescriptor.nativeType,
1141
+ typeRef: declaration.name
1142
+ };
1143
+ namedTypeDescriptors.set(declaration.name, descriptor);
1144
+ builder = builder.storageType(declaration.name, {
1145
+ codecId: baseDescriptor.codecId,
1146
+ nativeType: baseDescriptor.nativeType,
1147
+ typeParams: {}
1148
+ });
1149
+ }
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`,
1168
+ sourceId,
1169
+ span: model.span
1170
+ });
1171
+ for (const field of model.fields) {
1172
+ if (!field.list || !modelNames.has(field.typeName)) continue;
1173
+ const attributesValid = validateNavigationListFieldAttributes({
1174
+ modelName: model.name,
1175
+ field,
1176
+ sourceId,
1177
+ composedExtensions,
1178
+ diagnostics
1179
+ });
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,
1187
+ sourceId,
1188
+ diagnostics
1189
+ });
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;
1210
+ }
1211
+ if (!attributesValid) continue;
1212
+ backrelationCandidates.push({
1213
+ modelName: model.name,
1214
+ tableName,
1215
+ field,
1216
+ targetModelName: field.typeName,
1217
+ ...ifDefined("relationName", relationName)
1218
+ });
1219
+ }
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]);
1240
+ }
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
+ }
1276
+ diagnostics.push({
1277
+ code: "PSL_UNSUPPORTED_MODEL_ATTRIBUTE",
1278
+ message: `Model "${model.name}" uses unsupported attribute "@@${modelAttribute.name}"`,
1279
+ sourceId,
1280
+ span: modelAttribute.span
1281
+ });
1282
+ }
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;
1293
+ }
1294
+ const parsedRelation = parseRelationAttribute({
1295
+ attribute: relationAttribute.relation,
1296
+ modelName: model.name,
1297
+ fieldName: relationAttribute.field.name,
1298
+ sourceId,
1299
+ diagnostics
1300
+ });
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,
1325
+ sourceId,
1326
+ diagnostics,
1327
+ span: relationAttribute.relation.span,
1328
+ contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
1329
+ });
1330
+ if (!localColumns) continue;
1331
+ const referencedColumns = mapFieldNamesToColumns({
1332
+ modelName: targetMapping.model.name,
1333
+ fieldNames: parsedRelation.references,
1334
+ mapping: targetMapping,
1335
+ sourceId,
1336
+ diagnostics,
1337
+ span: relationAttribute.relation.span,
1338
+ contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
1339
+ });
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,
1364
+ 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)
1375
+ });
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
1385
+ });
1386
+ }
1387
+ return table;
1388
+ });
1389
+ }
1390
+ const { modelRelations, fkRelationsByPair } = indexFkRelations({ fkRelationMetadata });
1391
+ applyBackrelationCandidates({
1392
+ backrelationCandidates,
1393
+ fkRelationsByPair,
1394
+ modelRelations,
1395
+ diagnostics,
1396
+ sourceId
1397
+ });
1398
+ builder = emitModelsWithRelations({
1399
+ builder,
1400
+ resolvedModels,
1401
+ modelRelations
1402
+ });
1403
+ if (diagnostics.length > 0) return notOk({
1404
+ summary: "PSL to SQL Contract IR normalization failed",
1405
+ 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
+ });
1407
+ return ok(builder.build());
1408
+ }
1409
+
1410
+ //#endregion
1411
+ export { interpretPslDocumentToSqlContractIR as t };
1412
+ //# sourceMappingURL=interpreter-D7gLmaHz.mjs.map