@prisma-next/sql-contract-psl 0.3.0-dev.54 → 0.3.0-dev.63

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,1376 @@
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 invalidArgumentDiagnostic(input) {
120
+ return {
121
+ ok: false,
122
+ diagnostic: {
123
+ code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
124
+ message: input.message,
125
+ sourceId: input.context.sourceId,
126
+ span: input.span
127
+ }
128
+ };
129
+ }
130
+ function executionGenerator(id, params) {
131
+ return {
132
+ ok: true,
133
+ value: {
134
+ kind: "execution",
135
+ generated: {
136
+ kind: "generator",
137
+ id,
138
+ ...params ? { params } : {}
139
+ }
140
+ }
141
+ };
142
+ }
143
+ function expectNoArgs(input) {
144
+ if (input.call.args.length === 0) return;
145
+ return invalidArgumentDiagnostic({
146
+ context: input.context,
147
+ span: input.call.span,
148
+ message: `Default function "${input.call.name}" does not accept arguments. Use ${input.usage}.`
149
+ });
150
+ }
151
+ function parseIntegerArgument(raw) {
152
+ const trimmed = raw.trim();
153
+ if (!/^-?\d+$/.test(trimmed)) return;
154
+ const value = Number(trimmed);
155
+ if (!Number.isInteger(value)) return;
156
+ return value;
157
+ }
158
+ function parseStringLiteral(raw) {
159
+ const match = raw.trim().match(/^(['"])(.*)\1$/s);
160
+ if (!match) return;
161
+ return match[2] ?? "";
162
+ }
163
+ function lowerAutoincrement(input) {
164
+ const maybeNoArgs = expectNoArgs({
165
+ call: input.call,
166
+ context: input.context,
167
+ usage: "`autoincrement()`"
168
+ });
169
+ if (maybeNoArgs) return maybeNoArgs;
170
+ return {
171
+ ok: true,
172
+ value: {
173
+ kind: "storage",
174
+ defaultValue: {
175
+ kind: "function",
176
+ expression: "autoincrement()"
177
+ }
178
+ }
179
+ };
180
+ }
181
+ function lowerNow(input) {
182
+ const maybeNoArgs = expectNoArgs({
183
+ call: input.call,
184
+ context: input.context,
185
+ usage: "`now()`"
186
+ });
187
+ if (maybeNoArgs) return maybeNoArgs;
188
+ return {
189
+ ok: true,
190
+ value: {
191
+ kind: "storage",
192
+ defaultValue: {
193
+ kind: "function",
194
+ expression: "now()"
195
+ }
196
+ }
197
+ };
198
+ }
199
+ function lowerUuid(input) {
200
+ if (input.call.args.length === 0) return executionGenerator("uuidv4");
201
+ if (input.call.args.length !== 1) return invalidArgumentDiagnostic({
202
+ context: input.context,
203
+ span: input.call.span,
204
+ message: "Default function \"uuid\" accepts at most one version argument: `uuid()`, `uuid(4)`, or `uuid(7)`."
205
+ });
206
+ const version = parseIntegerArgument(input.call.args[0]?.raw ?? "");
207
+ if (version === 4) return executionGenerator("uuidv4");
208
+ if (version === 7) return executionGenerator("uuidv7");
209
+ return invalidArgumentDiagnostic({
210
+ context: input.context,
211
+ span: input.call.args[0]?.span ?? input.call.span,
212
+ message: "Default function \"uuid\" supports only `uuid()`, `uuid(4)`, or `uuid(7)` in SQL PSL provider v1."
213
+ });
214
+ }
215
+ function lowerCuid(input) {
216
+ if (input.call.args.length === 0) return {
217
+ ok: false,
218
+ diagnostic: {
219
+ code: "PSL_UNKNOWN_DEFAULT_FUNCTION",
220
+ message: "Default function \"cuid()\" is not supported in SQL PSL provider v1. Use `cuid(2)` instead.",
221
+ sourceId: input.context.sourceId,
222
+ span: input.call.span
223
+ }
224
+ };
225
+ if (input.call.args.length !== 1) return invalidArgumentDiagnostic({
226
+ context: input.context,
227
+ span: input.call.span,
228
+ message: "Default function \"cuid\" accepts exactly one version argument: `cuid(2)`."
229
+ });
230
+ if (parseIntegerArgument(input.call.args[0]?.raw ?? "") === 2) return executionGenerator("cuid2");
231
+ return invalidArgumentDiagnostic({
232
+ context: input.context,
233
+ span: input.call.args[0]?.span ?? input.call.span,
234
+ message: "Default function \"cuid\" supports only `cuid(2)` in SQL PSL provider v1."
235
+ });
236
+ }
237
+ function lowerUlid(input) {
238
+ const maybeNoArgs = expectNoArgs({
239
+ call: input.call,
240
+ context: input.context,
241
+ usage: "`ulid()`"
242
+ });
243
+ if (maybeNoArgs) return maybeNoArgs;
244
+ return executionGenerator("ulid");
245
+ }
246
+ function lowerNanoid(input) {
247
+ if (input.call.args.length === 0) return executionGenerator("nanoid");
248
+ if (input.call.args.length !== 1) return invalidArgumentDiagnostic({
249
+ context: input.context,
250
+ span: input.call.span,
251
+ message: "Default function \"nanoid\" accepts at most one size argument: `nanoid()` or `nanoid(<2-255>)`."
252
+ });
253
+ const size = parseIntegerArgument(input.call.args[0]?.raw ?? "");
254
+ if (size !== void 0 && size >= 2 && size <= 255) return executionGenerator("nanoid", { size });
255
+ return invalidArgumentDiagnostic({
256
+ context: input.context,
257
+ span: input.call.args[0]?.span ?? input.call.span,
258
+ message: "Default function \"nanoid\" size argument must be an integer between 2 and 255."
259
+ });
260
+ }
261
+ function lowerDbgenerated(input) {
262
+ if (input.call.args.length !== 1) return invalidArgumentDiagnostic({
263
+ context: input.context,
264
+ span: input.call.span,
265
+ message: "Default function \"dbgenerated\" requires exactly one string argument: `dbgenerated(\"...\")`."
266
+ });
267
+ const rawExpression = parseStringLiteral(input.call.args[0]?.raw ?? "");
268
+ if (rawExpression === void 0) return invalidArgumentDiagnostic({
269
+ context: input.context,
270
+ span: input.call.args[0]?.span ?? input.call.span,
271
+ message: "Default function \"dbgenerated\" argument must be a string literal."
272
+ });
273
+ if (rawExpression.trim().length === 0) return invalidArgumentDiagnostic({
274
+ context: input.context,
275
+ span: input.call.args[0]?.span ?? input.call.span,
276
+ message: "Default function \"dbgenerated\" argument cannot be empty."
277
+ });
278
+ return {
279
+ ok: true,
280
+ value: {
281
+ kind: "storage",
282
+ defaultValue: {
283
+ kind: "function",
284
+ expression: rawExpression
285
+ }
286
+ }
287
+ };
288
+ }
289
+ const supportedFunctionUsageByName = {
290
+ autoincrement: ["autoincrement()"],
291
+ now: ["now()"],
292
+ uuid: [
293
+ "uuid()",
294
+ "uuid(4)",
295
+ "uuid(7)"
296
+ ],
297
+ cuid: ["cuid(2)"],
298
+ ulid: ["ulid()"],
299
+ nanoid: ["nanoid()", "nanoid(n)"],
300
+ dbgenerated: ["dbgenerated(\"...\")"]
301
+ };
302
+ const unknownFunctionSuggestionsByName = {
303
+ cuid2: "Use `cuid(2)`.",
304
+ uuidv4: "Use `uuid()` or `uuid(4)`.",
305
+ uuidv7: "Use `uuid(7)`."
306
+ };
307
+ function formatSupportedFunctionList(registry) {
308
+ const signatures = Array.from(registry.keys()).sort().flatMap((functionName) => supportedFunctionUsageByName[functionName] ?? [`${functionName}()`]);
309
+ return signatures.length > 0 ? signatures.join(", ") : "none";
310
+ }
311
+ function createBuiltinDefaultFunctionRegistry() {
312
+ return new Map([
313
+ ["autoincrement", lowerAutoincrement],
314
+ ["now", lowerNow],
315
+ ["uuid", lowerUuid],
316
+ ["cuid", lowerCuid],
317
+ ["ulid", lowerUlid],
318
+ ["nanoid", lowerNanoid],
319
+ ["dbgenerated", lowerDbgenerated]
320
+ ]);
321
+ }
322
+ function lowerDefaultFunctionWithRegistry(input) {
323
+ const handler = input.registry.get(input.call.name);
324
+ if (handler) return handler({
325
+ call: input.call,
326
+ context: input.context
327
+ });
328
+ const supportedFunctionList = formatSupportedFunctionList(input.registry);
329
+ const suggestion = unknownFunctionSuggestionsByName[input.call.name];
330
+ return {
331
+ ok: false,
332
+ diagnostic: {
333
+ code: "PSL_UNKNOWN_DEFAULT_FUNCTION",
334
+ message: `Default function "${input.call.name}" is not supported in SQL PSL provider v1. Supported functions: ${supportedFunctionList}.${suggestion ? ` ${suggestion}` : ""}`,
335
+ sourceId: input.context.sourceId,
336
+ span: input.call.span
337
+ }
338
+ };
339
+ }
340
+
341
+ //#endregion
342
+ //#region src/interpreter.ts
343
+ const DEFAULT_POSTGRES_TARGET = {
344
+ kind: "target",
345
+ familyId: "sql",
346
+ targetId: "postgres",
347
+ id: "postgres",
348
+ version: "0.0.1",
349
+ capabilities: {}
350
+ };
351
+ const SCALAR_COLUMN_MAP = {
352
+ String: {
353
+ codecId: "pg/text@1",
354
+ nativeType: "text"
355
+ },
356
+ Boolean: {
357
+ codecId: "pg/bool@1",
358
+ nativeType: "bool"
359
+ },
360
+ Int: {
361
+ codecId: "pg/int4@1",
362
+ nativeType: "int4"
363
+ },
364
+ BigInt: {
365
+ codecId: "pg/int8@1",
366
+ nativeType: "int8"
367
+ },
368
+ Float: {
369
+ codecId: "pg/float8@1",
370
+ nativeType: "float8"
371
+ },
372
+ Decimal: {
373
+ codecId: "pg/numeric@1",
374
+ nativeType: "numeric"
375
+ },
376
+ DateTime: {
377
+ codecId: "pg/timestamptz@1",
378
+ nativeType: "timestamptz"
379
+ },
380
+ Json: {
381
+ codecId: "pg/jsonb@1",
382
+ nativeType: "jsonb"
383
+ },
384
+ Bytes: {
385
+ codecId: "pg/bytea@1",
386
+ nativeType: "bytea"
387
+ }
388
+ };
389
+ const GENERATED_ID_COLUMN_MAP = {
390
+ ulid: {
391
+ codecId: "sql/char@1",
392
+ nativeType: "character",
393
+ typeParams: { length: 26 }
394
+ },
395
+ uuidv7: {
396
+ codecId: "sql/char@1",
397
+ nativeType: "character",
398
+ typeParams: { length: 36 }
399
+ },
400
+ uuidv4: {
401
+ codecId: "sql/char@1",
402
+ nativeType: "character",
403
+ typeParams: { length: 36 }
404
+ },
405
+ cuid2: {
406
+ codecId: "sql/char@1",
407
+ nativeType: "character",
408
+ typeParams: { length: 24 }
409
+ }
410
+ };
411
+ function resolveGeneratedColumnDescriptor(executionDefault) {
412
+ if (executionDefault.kind !== "generator") return;
413
+ if (executionDefault.id === "nanoid") {
414
+ const rawSize = executionDefault.params?.["size"];
415
+ return {
416
+ codecId: "sql/char@1",
417
+ nativeType: "character",
418
+ typeParams: { length: typeof rawSize === "number" && Number.isInteger(rawSize) && rawSize >= 2 && rawSize <= 255 ? rawSize : 21 }
419
+ };
420
+ }
421
+ return GENERATED_ID_COLUMN_MAP[executionDefault.id];
422
+ }
423
+ const REFERENTIAL_ACTION_MAP = {
424
+ NoAction: "noAction",
425
+ Restrict: "restrict",
426
+ Cascade: "cascade",
427
+ SetNull: "setNull",
428
+ SetDefault: "setDefault",
429
+ noAction: "noAction",
430
+ restrict: "restrict",
431
+ cascade: "cascade",
432
+ setNull: "setNull",
433
+ setDefault: "setDefault"
434
+ };
435
+ function fkRelationPairKey(declaringModelName, targetModelName) {
436
+ return `${declaringModelName}::${targetModelName}`;
437
+ }
438
+ function lowerFirst(value) {
439
+ if (value.length === 0) return value;
440
+ return value[0]?.toLowerCase() + value.slice(1);
441
+ }
442
+ function getAttribute(attributes, name) {
443
+ return attributes.find((attribute) => attribute.name === name);
444
+ }
445
+ function getNamedArgument(attribute, name) {
446
+ const entry = attribute.args.find((arg) => arg.kind === "named" && arg.name === name);
447
+ if (!entry || entry.kind !== "named") return;
448
+ return entry.value;
449
+ }
450
+ function getPositionalArgument(attribute, index = 0) {
451
+ const entry = attribute.args.filter((arg) => arg.kind === "positional")[index];
452
+ if (!entry || entry.kind !== "positional") return;
453
+ return entry.value;
454
+ }
455
+ function getPositionalArgumentEntry(attribute, index = 0) {
456
+ const entry = attribute.args.filter((arg) => arg.kind === "positional")[index];
457
+ if (!entry || entry.kind !== "positional") return;
458
+ return {
459
+ value: entry.value,
460
+ span: entry.span
461
+ };
462
+ }
463
+ function unquoteStringLiteral(value) {
464
+ const trimmed = value.trim();
465
+ const match = trimmed.match(/^(['"])(.*)\1$/);
466
+ if (!match) return trimmed;
467
+ return match[2] ?? "";
468
+ }
469
+ function parseQuotedStringLiteral(value) {
470
+ const match = value.trim().match(/^(['"])(.*)\1$/);
471
+ if (!match) return;
472
+ return match[2] ?? "";
473
+ }
474
+ function parseFieldList(value) {
475
+ const trimmed = value.trim();
476
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return;
477
+ return trimmed.slice(1, -1).split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
478
+ }
479
+ function parseDefaultLiteralValue(expression) {
480
+ const trimmed = expression.trim();
481
+ if (trimmed === "true" || trimmed === "false") return {
482
+ kind: "literal",
483
+ value: trimmed === "true"
484
+ };
485
+ const numericValue = Number(trimmed);
486
+ if (!Number.isNaN(numericValue) && trimmed.length > 0 && !/^(['"]).*\1$/.test(trimmed)) return {
487
+ kind: "literal",
488
+ value: numericValue
489
+ };
490
+ if (/^(['"]).*\1$/.test(trimmed)) return {
491
+ kind: "literal",
492
+ value: unquoteStringLiteral(trimmed)
493
+ };
494
+ }
495
+ function lowerDefaultForField(input) {
496
+ const positionalEntries = input.defaultAttribute.args.filter((arg) => arg.kind === "positional");
497
+ if (input.defaultAttribute.args.filter((arg) => arg.kind === "named").length > 0 || positionalEntries.length !== 1) {
498
+ input.diagnostics.push({
499
+ code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
500
+ message: `Field "${input.modelName}.${input.fieldName}" requires exactly one positional @default(...) expression.`,
501
+ sourceId: input.sourceId,
502
+ span: input.defaultAttribute.span
503
+ });
504
+ return {};
505
+ }
506
+ const expressionEntry = getPositionalArgumentEntry(input.defaultAttribute);
507
+ if (!expressionEntry) {
508
+ input.diagnostics.push({
509
+ code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
510
+ message: `Field "${input.modelName}.${input.fieldName}" requires a positional @default(...) expression.`,
511
+ sourceId: input.sourceId,
512
+ span: input.defaultAttribute.span
513
+ });
514
+ return {};
515
+ }
516
+ const literalDefault = parseDefaultLiteralValue(expressionEntry.value);
517
+ if (literalDefault) return { defaultValue: literalDefault };
518
+ const defaultFunctionCall = parseDefaultFunctionCall(expressionEntry.value, expressionEntry.span);
519
+ if (!defaultFunctionCall) {
520
+ input.diagnostics.push({
521
+ code: "PSL_INVALID_DEFAULT_VALUE",
522
+ message: `Unsupported default value "${expressionEntry.value}"`,
523
+ sourceId: input.sourceId,
524
+ span: input.defaultAttribute.span
525
+ });
526
+ return {};
527
+ }
528
+ const lowered = lowerDefaultFunctionWithRegistry({
529
+ call: defaultFunctionCall,
530
+ registry: input.defaultFunctionRegistry,
531
+ context: {
532
+ sourceId: input.sourceId,
533
+ modelName: input.modelName,
534
+ fieldName: input.fieldName
535
+ }
536
+ });
537
+ if (!lowered.ok) {
538
+ input.diagnostics.push(lowered.diagnostic);
539
+ return {};
540
+ }
541
+ if (lowered.value.kind === "storage") return { defaultValue: lowered.value.defaultValue };
542
+ return { executionDefault: lowered.value.generated };
543
+ }
544
+ function parseMapName(input) {
545
+ if (!input.attribute) return input.defaultValue;
546
+ const value = getPositionalArgument(input.attribute);
547
+ if (!value) {
548
+ input.diagnostics.push({
549
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
550
+ message: `${input.entityLabel} @map requires a positional quoted string literal argument`,
551
+ sourceId: input.sourceId,
552
+ span: input.attribute.span
553
+ });
554
+ return input.defaultValue;
555
+ }
556
+ const parsed = parseQuotedStringLiteral(value);
557
+ if (parsed === void 0) {
558
+ input.diagnostics.push({
559
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
560
+ message: `${input.entityLabel} @map requires a positional quoted string literal argument`,
561
+ sourceId: input.sourceId,
562
+ span: input.attribute.span
563
+ });
564
+ return input.defaultValue;
565
+ }
566
+ return parsed;
567
+ }
568
+ function parsePgvectorLength(input) {
569
+ const namedLength = getNamedArgument(input.attribute, "length");
570
+ const namedDim = getNamedArgument(input.attribute, "dim");
571
+ const positional = getPositionalArgument(input.attribute);
572
+ const raw = namedLength ?? namedDim ?? positional;
573
+ if (!raw) {
574
+ input.diagnostics.push({
575
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
576
+ message: "@pgvector.column requires length/dim argument",
577
+ sourceId: input.sourceId,
578
+ span: input.attribute.span
579
+ });
580
+ return;
581
+ }
582
+ const parsed = Number(unquoteStringLiteral(raw));
583
+ if (!Number.isInteger(parsed) || parsed < 1) {
584
+ input.diagnostics.push({
585
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
586
+ message: "@pgvector.column length/dim must be a positive integer",
587
+ sourceId: input.sourceId,
588
+ span: input.attribute.span
589
+ });
590
+ return;
591
+ }
592
+ return parsed;
593
+ }
594
+ function resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors) {
595
+ if (field.typeRef && namedTypeDescriptors.has(field.typeRef)) return namedTypeDescriptors.get(field.typeRef);
596
+ if (namedTypeDescriptors.has(field.typeName)) return namedTypeDescriptors.get(field.typeName);
597
+ if (enumTypeDescriptors.has(field.typeName)) return enumTypeDescriptors.get(field.typeName);
598
+ return SCALAR_COLUMN_MAP[field.typeName];
599
+ }
600
+ function collectResolvedFields(model, mapping, enumTypeDescriptors, namedTypeDescriptors, namedTypeBaseTypes, modelNames, composedExtensions, defaultFunctionRegistry, diagnostics, sourceId) {
601
+ const resolvedFields = [];
602
+ for (const field of model.fields) {
603
+ if (field.list) {
604
+ if (modelNames.has(field.typeName)) continue;
605
+ diagnostics.push({
606
+ code: "PSL_UNSUPPORTED_FIELD_LIST",
607
+ 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.`,
608
+ sourceId,
609
+ span: field.span
610
+ });
611
+ continue;
612
+ }
613
+ for (const attribute of field.attributes) {
614
+ if (attribute.name === "id" || attribute.name === "unique" || attribute.name === "default" || attribute.name === "relation" || attribute.name === "map" || attribute.name === "pgvector.column") continue;
615
+ if (attribute.name.startsWith("pgvector.") && !composedExtensions.has("pgvector")) {
616
+ diagnostics.push({
617
+ code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
618
+ message: `Attribute "@${attribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
619
+ sourceId,
620
+ span: attribute.span
621
+ });
622
+ continue;
623
+ }
624
+ diagnostics.push({
625
+ code: "PSL_UNSUPPORTED_FIELD_ATTRIBUTE",
626
+ message: `Field "${model.name}.${field.name}" uses unsupported attribute "@${attribute.name}"`,
627
+ sourceId,
628
+ span: attribute.span
629
+ });
630
+ }
631
+ if (getAttribute(field.attributes, "relation") && modelNames.has(field.typeName)) continue;
632
+ let descriptor = resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors);
633
+ const pgvectorColumnAttribute = getAttribute(field.attributes, "pgvector.column");
634
+ if (pgvectorColumnAttribute) if (!composedExtensions.has("pgvector")) diagnostics.push({
635
+ code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
636
+ message: "Attribute \"@pgvector.column\" uses unrecognized namespace \"pgvector\". Add extension pack \"pgvector\" to extensionPacks in prisma-next.config.ts.",
637
+ sourceId,
638
+ span: pgvectorColumnAttribute.span
639
+ });
640
+ else if (!(field.typeName === "Bytes" || namedTypeBaseTypes.get(field.typeRef ?? field.typeName) === "Bytes")) diagnostics.push({
641
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
642
+ message: `Field "${model.name}.${field.name}" uses @pgvector.column on unsupported base type "${field.typeName}"`,
643
+ sourceId,
644
+ span: pgvectorColumnAttribute.span
645
+ });
646
+ else {
647
+ const length = parsePgvectorLength({
648
+ attribute: pgvectorColumnAttribute,
649
+ diagnostics,
650
+ sourceId
651
+ });
652
+ if (length !== void 0) descriptor = {
653
+ codecId: "pg/vector@1",
654
+ nativeType: `vector(${length})`,
655
+ typeParams: { length }
656
+ };
657
+ }
658
+ if (!descriptor) {
659
+ diagnostics.push({
660
+ code: "PSL_UNSUPPORTED_FIELD_TYPE",
661
+ message: `Field "${model.name}.${field.name}" type "${field.typeName}" is not supported in SQL PSL provider v1`,
662
+ sourceId,
663
+ span: field.span
664
+ });
665
+ continue;
666
+ }
667
+ const defaultAttribute = getAttribute(field.attributes, "default");
668
+ const loweredDefault = defaultAttribute ? lowerDefaultForField({
669
+ modelName: model.name,
670
+ fieldName: field.name,
671
+ defaultAttribute,
672
+ sourceId,
673
+ defaultFunctionRegistry,
674
+ diagnostics
675
+ }) : {};
676
+ if (field.optional && loweredDefault.executionDefault) {
677
+ const generatorDescription = loweredDefault.executionDefault.kind === "generator" ? `"${loweredDefault.executionDefault.id}"` : "for this field";
678
+ diagnostics.push({
679
+ code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
680
+ message: `Field "${model.name}.${field.name}" cannot be optional when using execution default ${generatorDescription}. Remove "?" or use a storage default.`,
681
+ sourceId,
682
+ span: defaultAttribute?.span ?? field.span
683
+ });
684
+ continue;
685
+ }
686
+ if (loweredDefault.executionDefault) {
687
+ const generatedDescriptor = resolveGeneratedColumnDescriptor(loweredDefault.executionDefault);
688
+ if (generatedDescriptor) descriptor = generatedDescriptor;
689
+ }
690
+ const mappedColumnName = mapping.fieldColumns.get(field.name) ?? field.name;
691
+ resolvedFields.push({
692
+ field,
693
+ columnName: mappedColumnName,
694
+ descriptor,
695
+ ...ifDefined("defaultValue", loweredDefault.defaultValue),
696
+ ...ifDefined("executionDefault", loweredDefault.executionDefault),
697
+ isId: Boolean(getAttribute(field.attributes, "id")),
698
+ isUnique: Boolean(getAttribute(field.attributes, "unique"))
699
+ });
700
+ }
701
+ return resolvedFields;
702
+ }
703
+ function hasSameSpan(a, b) {
704
+ 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;
705
+ }
706
+ function compareStrings(left, right) {
707
+ if (left < right) return -1;
708
+ if (left > right) return 1;
709
+ return 0;
710
+ }
711
+ function indexFkRelations(input) {
712
+ const modelRelations = /* @__PURE__ */ new Map();
713
+ const fkRelationsByPair = /* @__PURE__ */ new Map();
714
+ for (const relation of input.fkRelationMetadata) {
715
+ const existing = modelRelations.get(relation.declaringModelName);
716
+ const current = existing ?? [];
717
+ if (!existing) modelRelations.set(relation.declaringModelName, current);
718
+ current.push({
719
+ fieldName: relation.declaringFieldName,
720
+ toModel: relation.targetModelName,
721
+ toTable: relation.targetTableName,
722
+ cardinality: "N:1",
723
+ parentTable: relation.declaringTableName,
724
+ parentColumns: relation.localColumns,
725
+ childTable: relation.targetTableName,
726
+ childColumns: relation.referencedColumns
727
+ });
728
+ const pairKey = fkRelationPairKey(relation.declaringModelName, relation.targetModelName);
729
+ const pairRelations = fkRelationsByPair.get(pairKey);
730
+ if (!pairRelations) {
731
+ fkRelationsByPair.set(pairKey, [relation]);
732
+ continue;
733
+ }
734
+ pairRelations.push(relation);
735
+ }
736
+ return {
737
+ modelRelations,
738
+ fkRelationsByPair
739
+ };
740
+ }
741
+ function applyBackrelationCandidates(input) {
742
+ for (const candidate of input.backrelationCandidates) {
743
+ const pairKey = fkRelationPairKey(candidate.targetModelName, candidate.modelName);
744
+ const pairMatches = input.fkRelationsByPair.get(pairKey) ?? [];
745
+ const matches = candidate.relationName ? pairMatches.filter((relation) => relation.relationName === candidate.relationName) : [...pairMatches];
746
+ if (matches.length === 0) {
747
+ input.diagnostics.push({
748
+ code: "PSL_ORPHANED_BACKRELATION_LIST",
749
+ 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.`,
750
+ sourceId: input.sourceId,
751
+ span: candidate.field.span
752
+ });
753
+ continue;
754
+ }
755
+ if (matches.length > 1) {
756
+ input.diagnostics.push({
757
+ code: "PSL_AMBIGUOUS_BACKRELATION_LIST",
758
+ 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.`,
759
+ sourceId: input.sourceId,
760
+ span: candidate.field.span
761
+ });
762
+ continue;
763
+ }
764
+ invariant(matches.length === 1, "Backrelation matching requires exactly one match");
765
+ const matched = matches[0];
766
+ assertDefined(matched, "Backrelation matching requires a defined relation match");
767
+ const existing = input.modelRelations.get(candidate.modelName);
768
+ const current = existing ?? [];
769
+ if (!existing) input.modelRelations.set(candidate.modelName, current);
770
+ current.push({
771
+ fieldName: candidate.field.name,
772
+ toModel: matched.declaringModelName,
773
+ toTable: matched.declaringTableName,
774
+ cardinality: "1:N",
775
+ parentTable: candidate.tableName,
776
+ parentColumns: matched.referencedColumns,
777
+ childTable: matched.declaringTableName,
778
+ childColumns: matched.localColumns
779
+ });
780
+ }
781
+ }
782
+ function emitModelsWithRelations(input) {
783
+ let nextBuilder = input.builder;
784
+ const sortedModels = input.resolvedModels.sort((left, right) => {
785
+ const tableComparison = compareStrings(left.mapping.tableName, right.mapping.tableName);
786
+ if (tableComparison === 0) return compareStrings(left.model.name, right.model.name);
787
+ return tableComparison;
788
+ });
789
+ for (const entry of sortedModels) {
790
+ const relationEntries = [...input.modelRelations.get(entry.model.name) ?? []].sort((left, right) => compareStrings(left.fieldName, right.fieldName));
791
+ nextBuilder = nextBuilder.model(entry.model.name, entry.mapping.tableName, (modelBuilder) => {
792
+ let next = modelBuilder;
793
+ for (const resolvedField of entry.resolvedFields) next = next.field(resolvedField.field.name, resolvedField.columnName);
794
+ for (const relation of relationEntries) next = next.relation(relation.fieldName, {
795
+ toModel: relation.toModel,
796
+ toTable: relation.toTable,
797
+ cardinality: relation.cardinality,
798
+ on: {
799
+ parentTable: relation.parentTable,
800
+ parentColumns: relation.parentColumns,
801
+ childTable: relation.childTable,
802
+ childColumns: relation.childColumns
803
+ }
804
+ });
805
+ return next;
806
+ });
807
+ }
808
+ return nextBuilder;
809
+ }
810
+ function mapParserDiagnostics(document) {
811
+ return document.diagnostics.map((diagnostic) => ({
812
+ code: diagnostic.code,
813
+ message: diagnostic.message,
814
+ sourceId: diagnostic.sourceId,
815
+ span: diagnostic.span
816
+ }));
817
+ }
818
+ function normalizeReferentialAction(input) {
819
+ const normalized = REFERENTIAL_ACTION_MAP[input.actionToken];
820
+ if (normalized) return normalized;
821
+ input.diagnostics.push({
822
+ code: "PSL_UNSUPPORTED_REFERENTIAL_ACTION",
823
+ message: `Relation field "${input.modelName}.${input.fieldName}" has unsupported ${input.actionName} action "${input.actionToken}"`,
824
+ sourceId: input.sourceId,
825
+ span: input.span
826
+ });
827
+ }
828
+ function parseAttributeFieldList(input) {
829
+ const raw = getNamedArgument(input.attribute, "fields") ?? getPositionalArgument(input.attribute);
830
+ if (!raw) {
831
+ input.diagnostics.push({
832
+ code: input.code,
833
+ message: `${input.messagePrefix} requires fields list argument`,
834
+ sourceId: input.sourceId,
835
+ span: input.attribute.span
836
+ });
837
+ return;
838
+ }
839
+ const fields = parseFieldList(raw);
840
+ if (!fields || fields.length === 0) {
841
+ input.diagnostics.push({
842
+ code: input.code,
843
+ message: `${input.messagePrefix} requires bracketed field list argument`,
844
+ sourceId: input.sourceId,
845
+ span: input.attribute.span
846
+ });
847
+ return;
848
+ }
849
+ return fields;
850
+ }
851
+ function mapFieldNamesToColumns(input) {
852
+ const columns = [];
853
+ for (const fieldName of input.fieldNames) {
854
+ const columnName = input.mapping.fieldColumns.get(fieldName);
855
+ if (!columnName) {
856
+ input.diagnostics.push({
857
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
858
+ message: `${input.contextLabel} references unknown field "${input.modelName}.${fieldName}"`,
859
+ sourceId: input.sourceId,
860
+ span: input.span
861
+ });
862
+ return;
863
+ }
864
+ columns.push(columnName);
865
+ }
866
+ return columns;
867
+ }
868
+ function buildModelMappings(models, diagnostics, sourceId) {
869
+ const result = /* @__PURE__ */ new Map();
870
+ for (const model of models) {
871
+ const tableName = parseMapName({
872
+ attribute: getAttribute(model.attributes, "map"),
873
+ defaultValue: lowerFirst(model.name),
874
+ sourceId,
875
+ diagnostics,
876
+ entityLabel: `Model "${model.name}"`,
877
+ span: model.span
878
+ });
879
+ const fieldColumns = /* @__PURE__ */ new Map();
880
+ for (const field of model.fields) {
881
+ const columnName = parseMapName({
882
+ attribute: getAttribute(field.attributes, "map"),
883
+ defaultValue: field.name,
884
+ sourceId,
885
+ diagnostics,
886
+ entityLabel: `Field "${model.name}.${field.name}"`,
887
+ span: field.span
888
+ });
889
+ fieldColumns.set(field.name, columnName);
890
+ }
891
+ result.set(model.name, {
892
+ model,
893
+ tableName,
894
+ fieldColumns
895
+ });
896
+ }
897
+ return result;
898
+ }
899
+ function validateNavigationListFieldAttributes(input) {
900
+ let valid = true;
901
+ for (const attribute of input.field.attributes) {
902
+ if (attribute.name === "relation") continue;
903
+ if (attribute.name.startsWith("pgvector.") && !input.composedExtensions.has("pgvector")) {
904
+ input.diagnostics.push({
905
+ code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
906
+ message: `Attribute "@${attribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
907
+ sourceId: input.sourceId,
908
+ span: attribute.span
909
+ });
910
+ valid = false;
911
+ continue;
912
+ }
913
+ input.diagnostics.push({
914
+ code: "PSL_UNSUPPORTED_FIELD_ATTRIBUTE",
915
+ message: `Field "${input.modelName}.${input.field.name}" uses unsupported attribute "@${attribute.name}"`,
916
+ sourceId: input.sourceId,
917
+ span: attribute.span
918
+ });
919
+ valid = false;
920
+ }
921
+ return valid;
922
+ }
923
+ function parseRelationAttribute(input) {
924
+ if (input.attribute.args.filter((arg) => arg.kind === "positional").length > 1) {
925
+ input.diagnostics.push({
926
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
927
+ message: `Relation field "${input.modelName}.${input.fieldName}" has too many positional arguments`,
928
+ sourceId: input.sourceId,
929
+ span: input.attribute.span
930
+ });
931
+ return;
932
+ }
933
+ let relationNameFromPositional;
934
+ const positionalNameEntry = getPositionalArgumentEntry(input.attribute);
935
+ if (positionalNameEntry) {
936
+ const parsedName = parseQuotedStringLiteral(positionalNameEntry.value);
937
+ if (!parsedName) {
938
+ input.diagnostics.push({
939
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
940
+ message: `Relation field "${input.modelName}.${input.fieldName}" positional relation name must be a quoted string literal`,
941
+ sourceId: input.sourceId,
942
+ span: positionalNameEntry.span
943
+ });
944
+ return;
945
+ }
946
+ relationNameFromPositional = parsedName;
947
+ }
948
+ for (const arg of input.attribute.args) {
949
+ if (arg.kind === "positional") continue;
950
+ if (arg.name !== "name" && arg.name !== "fields" && arg.name !== "references" && arg.name !== "onDelete" && arg.name !== "onUpdate") {
951
+ input.diagnostics.push({
952
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
953
+ message: `Relation field "${input.modelName}.${input.fieldName}" has unsupported argument "${arg.name}"`,
954
+ sourceId: input.sourceId,
955
+ span: arg.span
956
+ });
957
+ return;
958
+ }
959
+ }
960
+ const namedRelationNameRaw = getNamedArgument(input.attribute, "name");
961
+ const namedRelationName = namedRelationNameRaw ? parseQuotedStringLiteral(namedRelationNameRaw) : void 0;
962
+ if (namedRelationNameRaw && !namedRelationName) {
963
+ input.diagnostics.push({
964
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
965
+ message: `Relation field "${input.modelName}.${input.fieldName}" named relation name must be a quoted string literal`,
966
+ sourceId: input.sourceId,
967
+ span: input.attribute.span
968
+ });
969
+ return;
970
+ }
971
+ if (relationNameFromPositional && namedRelationName && relationNameFromPositional !== namedRelationName) {
972
+ input.diagnostics.push({
973
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
974
+ message: `Relation field "${input.modelName}.${input.fieldName}" has conflicting positional and named relation names`,
975
+ sourceId: input.sourceId,
976
+ span: input.attribute.span
977
+ });
978
+ return;
979
+ }
980
+ const relationName = namedRelationName ?? relationNameFromPositional;
981
+ const fieldsRaw = getNamedArgument(input.attribute, "fields");
982
+ const referencesRaw = getNamedArgument(input.attribute, "references");
983
+ if (fieldsRaw && !referencesRaw || !fieldsRaw && referencesRaw) {
984
+ input.diagnostics.push({
985
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
986
+ message: `Relation field "${input.modelName}.${input.fieldName}" requires fields and references arguments`,
987
+ sourceId: input.sourceId,
988
+ span: input.attribute.span
989
+ });
990
+ return;
991
+ }
992
+ let fields;
993
+ let references;
994
+ if (fieldsRaw && referencesRaw) {
995
+ const parsedFields = parseFieldList(fieldsRaw);
996
+ const parsedReferences = parseFieldList(referencesRaw);
997
+ if (!parsedFields || !parsedReferences || parsedFields.length === 0 || parsedReferences.length === 0) {
998
+ input.diagnostics.push({
999
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
1000
+ message: `Relation field "${input.modelName}.${input.fieldName}" requires bracketed fields and references lists`,
1001
+ sourceId: input.sourceId,
1002
+ span: input.attribute.span
1003
+ });
1004
+ return;
1005
+ }
1006
+ fields = parsedFields;
1007
+ references = parsedReferences;
1008
+ }
1009
+ const onDeleteArgument = getNamedArgument(input.attribute, "onDelete");
1010
+ const onUpdateArgument = getNamedArgument(input.attribute, "onUpdate");
1011
+ return {
1012
+ ...ifDefined("relationName", relationName),
1013
+ ...ifDefined("fields", fields),
1014
+ ...ifDefined("references", references),
1015
+ ...ifDefined("onDelete", onDeleteArgument ? unquoteStringLiteral(onDeleteArgument) : void 0),
1016
+ ...ifDefined("onUpdate", onUpdateArgument ? unquoteStringLiteral(onUpdateArgument) : void 0)
1017
+ };
1018
+ }
1019
+ function interpretPslDocumentToSqlContractIR(input) {
1020
+ const diagnostics = mapParserDiagnostics(input.document);
1021
+ const modelNames = new Set(input.document.ast.models.map((model) => model.name));
1022
+ const sourceId = input.document.ast.sourceId;
1023
+ const composedExtensions = new Set(input.composedExtensionPacks ?? []);
1024
+ const defaultFunctionRegistry = input.defaultFunctionRegistry ?? createBuiltinDefaultFunctionRegistry();
1025
+ let builder = defineContract().target(input.target ?? DEFAULT_POSTGRES_TARGET);
1026
+ const enumTypeDescriptors = /* @__PURE__ */ new Map();
1027
+ const namedTypeDescriptors = /* @__PURE__ */ new Map();
1028
+ const namedTypeBaseTypes = /* @__PURE__ */ new Map();
1029
+ for (const enumDeclaration of input.document.ast.enums) {
1030
+ const nativeType = enumDeclaration.name.toLowerCase();
1031
+ const descriptor = {
1032
+ codecId: "pg/enum@1",
1033
+ nativeType,
1034
+ typeRef: enumDeclaration.name
1035
+ };
1036
+ enumTypeDescriptors.set(enumDeclaration.name, descriptor);
1037
+ builder = builder.storageType(enumDeclaration.name, {
1038
+ codecId: "pg/enum@1",
1039
+ nativeType,
1040
+ typeParams: { values: enumDeclaration.values.map((value) => value.name) }
1041
+ });
1042
+ }
1043
+ for (const declaration of input.document.ast.types?.declarations ?? []) {
1044
+ const baseDescriptor = enumTypeDescriptors.get(declaration.baseType) ?? SCALAR_COLUMN_MAP[declaration.baseType];
1045
+ if (!baseDescriptor) {
1046
+ diagnostics.push({
1047
+ code: "PSL_UNSUPPORTED_NAMED_TYPE_BASE",
1048
+ message: `Named type "${declaration.name}" references unsupported base type "${declaration.baseType}"`,
1049
+ sourceId,
1050
+ span: declaration.span
1051
+ });
1052
+ continue;
1053
+ }
1054
+ namedTypeBaseTypes.set(declaration.name, declaration.baseType);
1055
+ const pgvectorAttribute = getAttribute(declaration.attributes, "pgvector.column");
1056
+ const unsupportedNamedTypeAttribute = declaration.attributes.find((attribute) => attribute.name !== "pgvector.column");
1057
+ if (unsupportedNamedTypeAttribute) {
1058
+ diagnostics.push({
1059
+ code: "PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE",
1060
+ message: `Named type "${declaration.name}" uses unsupported attribute "${unsupportedNamedTypeAttribute.name}"`,
1061
+ sourceId,
1062
+ span: unsupportedNamedTypeAttribute.span
1063
+ });
1064
+ continue;
1065
+ }
1066
+ if (pgvectorAttribute) {
1067
+ if (!composedExtensions.has("pgvector")) {
1068
+ diagnostics.push({
1069
+ code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
1070
+ message: "Attribute \"@pgvector.column\" uses unrecognized namespace \"pgvector\". Add extension pack \"pgvector\" to extensionPacks in prisma-next.config.ts.",
1071
+ sourceId,
1072
+ span: pgvectorAttribute.span
1073
+ });
1074
+ continue;
1075
+ }
1076
+ if (declaration.baseType !== "Bytes") {
1077
+ diagnostics.push({
1078
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1079
+ message: `Named type "${declaration.name}" uses @pgvector.column on unsupported base type "${declaration.baseType}"`,
1080
+ sourceId,
1081
+ span: pgvectorAttribute.span
1082
+ });
1083
+ continue;
1084
+ }
1085
+ const length = parsePgvectorLength({
1086
+ attribute: pgvectorAttribute,
1087
+ diagnostics,
1088
+ sourceId
1089
+ });
1090
+ if (length === void 0) continue;
1091
+ namedTypeDescriptors.set(declaration.name, {
1092
+ codecId: "pg/vector@1",
1093
+ nativeType: `vector(${length})`,
1094
+ typeRef: declaration.name
1095
+ });
1096
+ builder = builder.storageType(declaration.name, {
1097
+ codecId: "pg/vector@1",
1098
+ nativeType: `vector(${length})`,
1099
+ typeParams: { length }
1100
+ });
1101
+ continue;
1102
+ }
1103
+ const descriptor = {
1104
+ codecId: baseDescriptor.codecId,
1105
+ nativeType: baseDescriptor.nativeType,
1106
+ typeRef: declaration.name
1107
+ };
1108
+ namedTypeDescriptors.set(declaration.name, descriptor);
1109
+ builder = builder.storageType(declaration.name, {
1110
+ codecId: baseDescriptor.codecId,
1111
+ nativeType: baseDescriptor.nativeType,
1112
+ typeParams: {}
1113
+ });
1114
+ }
1115
+ const modelMappings = buildModelMappings(input.document.ast.models, diagnostics, sourceId);
1116
+ const resolvedModels = [];
1117
+ const fkRelationMetadata = [];
1118
+ const backrelationCandidates = [];
1119
+ for (const model of input.document.ast.models) {
1120
+ const mapping = modelMappings.get(model.name);
1121
+ if (!mapping) continue;
1122
+ const tableName = mapping.tableName;
1123
+ const resolvedFields = collectResolvedFields(model, mapping, enumTypeDescriptors, namedTypeDescriptors, namedTypeBaseTypes, modelNames, composedExtensions, defaultFunctionRegistry, diagnostics, sourceId);
1124
+ resolvedModels.push({
1125
+ model,
1126
+ mapping,
1127
+ resolvedFields
1128
+ });
1129
+ const primaryKeyColumns = resolvedFields.filter((field) => field.isId).map((field) => field.columnName);
1130
+ if (primaryKeyColumns.length === 0) diagnostics.push({
1131
+ code: "PSL_MISSING_PRIMARY_KEY",
1132
+ message: `Model "${model.name}" must declare at least one @id field for SQL provider`,
1133
+ sourceId,
1134
+ span: model.span
1135
+ });
1136
+ for (const field of model.fields) {
1137
+ if (!field.list || !modelNames.has(field.typeName)) continue;
1138
+ const attributesValid = validateNavigationListFieldAttributes({
1139
+ modelName: model.name,
1140
+ field,
1141
+ sourceId,
1142
+ composedExtensions,
1143
+ diagnostics
1144
+ });
1145
+ const relationAttribute = getAttribute(field.attributes, "relation");
1146
+ let relationName;
1147
+ if (relationAttribute) {
1148
+ const parsedRelation = parseRelationAttribute({
1149
+ attribute: relationAttribute,
1150
+ modelName: model.name,
1151
+ fieldName: field.name,
1152
+ sourceId,
1153
+ diagnostics
1154
+ });
1155
+ if (!parsedRelation) continue;
1156
+ if (parsedRelation.fields || parsedRelation.references) {
1157
+ diagnostics.push({
1158
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
1159
+ message: `Backrelation list field "${model.name}.${field.name}" cannot declare fields/references; define them on the FK-side relation field`,
1160
+ sourceId,
1161
+ span: relationAttribute.span
1162
+ });
1163
+ continue;
1164
+ }
1165
+ if (parsedRelation.onDelete || parsedRelation.onUpdate) {
1166
+ diagnostics.push({
1167
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
1168
+ message: `Backrelation list field "${model.name}.${field.name}" cannot declare onDelete/onUpdate; define referential actions on the FK-side relation field`,
1169
+ sourceId,
1170
+ span: relationAttribute.span
1171
+ });
1172
+ continue;
1173
+ }
1174
+ relationName = parsedRelation.relationName;
1175
+ }
1176
+ if (!attributesValid) continue;
1177
+ backrelationCandidates.push({
1178
+ modelName: model.name,
1179
+ tableName,
1180
+ field,
1181
+ targetModelName: field.typeName,
1182
+ ...ifDefined("relationName", relationName)
1183
+ });
1184
+ }
1185
+ const relationAttributes = model.fields.map((field) => ({
1186
+ field,
1187
+ relation: getAttribute(field.attributes, "relation")
1188
+ })).filter((entry) => Boolean(entry.relation));
1189
+ builder = builder.table(tableName, (tableBuilder) => {
1190
+ let table = tableBuilder;
1191
+ for (const resolvedField of resolvedFields) {
1192
+ if (resolvedField.executionDefault) table = table.generated(resolvedField.columnName, {
1193
+ type: resolvedField.descriptor,
1194
+ generated: resolvedField.executionDefault
1195
+ });
1196
+ else {
1197
+ const options = {
1198
+ type: resolvedField.descriptor,
1199
+ ...ifDefined("nullable", resolvedField.field.optional ? true : void 0),
1200
+ ...ifDefined("default", resolvedField.defaultValue)
1201
+ };
1202
+ table = table.column(resolvedField.columnName, options);
1203
+ }
1204
+ if (resolvedField.isUnique) table = table.unique([resolvedField.columnName]);
1205
+ }
1206
+ if (primaryKeyColumns.length > 0) table = table.primaryKey(primaryKeyColumns);
1207
+ for (const modelAttribute of model.attributes) {
1208
+ if (modelAttribute.name === "map") continue;
1209
+ if (modelAttribute.name === "unique" || modelAttribute.name === "index") {
1210
+ const fieldNames = parseAttributeFieldList({
1211
+ attribute: modelAttribute,
1212
+ sourceId,
1213
+ diagnostics,
1214
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1215
+ messagePrefix: `Model "${model.name}" @@${modelAttribute.name}`
1216
+ });
1217
+ if (!fieldNames) continue;
1218
+ const columnNames = mapFieldNamesToColumns({
1219
+ modelName: model.name,
1220
+ fieldNames,
1221
+ mapping,
1222
+ sourceId,
1223
+ diagnostics,
1224
+ span: modelAttribute.span,
1225
+ contextLabel: `Model "${model.name}" @@${modelAttribute.name}`
1226
+ });
1227
+ if (!columnNames) continue;
1228
+ if (modelAttribute.name === "unique") table = table.unique(columnNames);
1229
+ else table = table.index(columnNames);
1230
+ continue;
1231
+ }
1232
+ if (modelAttribute.name.startsWith("pgvector.") && !composedExtensions.has("pgvector")) {
1233
+ diagnostics.push({
1234
+ code: "PSL_EXTENSION_NAMESPACE_NOT_COMPOSED",
1235
+ message: `Attribute "@@${modelAttribute.name}" uses unrecognized namespace "pgvector". Add extension pack "pgvector" to extensionPacks in prisma-next.config.ts.`,
1236
+ sourceId,
1237
+ span: modelAttribute.span
1238
+ });
1239
+ continue;
1240
+ }
1241
+ diagnostics.push({
1242
+ code: "PSL_UNSUPPORTED_MODEL_ATTRIBUTE",
1243
+ message: `Model "${model.name}" uses unsupported attribute "@@${modelAttribute.name}"`,
1244
+ sourceId,
1245
+ span: modelAttribute.span
1246
+ });
1247
+ }
1248
+ for (const relationAttribute of relationAttributes) {
1249
+ if (relationAttribute.field.list) continue;
1250
+ if (!modelNames.has(relationAttribute.field.typeName)) {
1251
+ diagnostics.push({
1252
+ code: "PSL_INVALID_RELATION_TARGET",
1253
+ message: `Relation field "${model.name}.${relationAttribute.field.name}" references unknown model "${relationAttribute.field.typeName}"`,
1254
+ sourceId,
1255
+ span: relationAttribute.field.span
1256
+ });
1257
+ continue;
1258
+ }
1259
+ const parsedRelation = parseRelationAttribute({
1260
+ attribute: relationAttribute.relation,
1261
+ modelName: model.name,
1262
+ fieldName: relationAttribute.field.name,
1263
+ sourceId,
1264
+ diagnostics
1265
+ });
1266
+ if (!parsedRelation) continue;
1267
+ if (!parsedRelation.fields || !parsedRelation.references) {
1268
+ diagnostics.push({
1269
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
1270
+ message: `Relation field "${model.name}.${relationAttribute.field.name}" requires fields and references arguments`,
1271
+ sourceId,
1272
+ span: relationAttribute.relation.span
1273
+ });
1274
+ continue;
1275
+ }
1276
+ const targetMapping = modelMappings.get(relationAttribute.field.typeName);
1277
+ if (!targetMapping) {
1278
+ diagnostics.push({
1279
+ code: "PSL_INVALID_RELATION_TARGET",
1280
+ message: `Relation field "${model.name}.${relationAttribute.field.name}" references unknown model "${relationAttribute.field.typeName}"`,
1281
+ sourceId,
1282
+ span: relationAttribute.field.span
1283
+ });
1284
+ continue;
1285
+ }
1286
+ const localColumns = mapFieldNamesToColumns({
1287
+ modelName: model.name,
1288
+ fieldNames: parsedRelation.fields,
1289
+ mapping,
1290
+ sourceId,
1291
+ diagnostics,
1292
+ span: relationAttribute.relation.span,
1293
+ contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
1294
+ });
1295
+ if (!localColumns) continue;
1296
+ const referencedColumns = mapFieldNamesToColumns({
1297
+ modelName: targetMapping.model.name,
1298
+ fieldNames: parsedRelation.references,
1299
+ mapping: targetMapping,
1300
+ sourceId,
1301
+ diagnostics,
1302
+ span: relationAttribute.relation.span,
1303
+ contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
1304
+ });
1305
+ if (!referencedColumns) continue;
1306
+ if (localColumns.length !== referencedColumns.length) {
1307
+ diagnostics.push({
1308
+ code: "PSL_INVALID_RELATION_ATTRIBUTE",
1309
+ message: `Relation field "${model.name}.${relationAttribute.field.name}" must provide the same number of fields and references`,
1310
+ sourceId,
1311
+ span: relationAttribute.relation.span
1312
+ });
1313
+ continue;
1314
+ }
1315
+ const onDelete = parsedRelation.onDelete ? normalizeReferentialAction({
1316
+ modelName: model.name,
1317
+ fieldName: relationAttribute.field.name,
1318
+ actionName: "onDelete",
1319
+ actionToken: parsedRelation.onDelete,
1320
+ sourceId,
1321
+ span: relationAttribute.field.span,
1322
+ diagnostics
1323
+ }) : void 0;
1324
+ const onUpdate = parsedRelation.onUpdate ? normalizeReferentialAction({
1325
+ modelName: model.name,
1326
+ fieldName: relationAttribute.field.name,
1327
+ actionName: "onUpdate",
1328
+ actionToken: parsedRelation.onUpdate,
1329
+ sourceId,
1330
+ span: relationAttribute.field.span,
1331
+ diagnostics
1332
+ }) : void 0;
1333
+ table = table.foreignKey(localColumns, {
1334
+ table: targetMapping.tableName,
1335
+ columns: referencedColumns
1336
+ }, {
1337
+ ...ifDefined("onDelete", onDelete),
1338
+ ...ifDefined("onUpdate", onUpdate)
1339
+ });
1340
+ fkRelationMetadata.push({
1341
+ declaringModelName: model.name,
1342
+ declaringFieldName: relationAttribute.field.name,
1343
+ declaringTableName: tableName,
1344
+ targetModelName: targetMapping.model.name,
1345
+ targetTableName: targetMapping.tableName,
1346
+ ...ifDefined("relationName", parsedRelation.relationName),
1347
+ localColumns,
1348
+ referencedColumns
1349
+ });
1350
+ }
1351
+ return table;
1352
+ });
1353
+ }
1354
+ const { modelRelations, fkRelationsByPair } = indexFkRelations({ fkRelationMetadata });
1355
+ applyBackrelationCandidates({
1356
+ backrelationCandidates,
1357
+ fkRelationsByPair,
1358
+ modelRelations,
1359
+ diagnostics,
1360
+ sourceId
1361
+ });
1362
+ builder = emitModelsWithRelations({
1363
+ builder,
1364
+ resolvedModels,
1365
+ modelRelations
1366
+ });
1367
+ if (diagnostics.length > 0) return notOk({
1368
+ summary: "PSL to SQL Contract IR normalization failed",
1369
+ 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)
1370
+ });
1371
+ return ok(builder.build());
1372
+ }
1373
+
1374
+ //#endregion
1375
+ export { createBuiltinDefaultFunctionRegistry as n, interpretPslDocumentToSqlContractIR as t };
1376
+ //# sourceMappingURL=interpreter-IXr5c7s7.mjs.map