@prisma-next/sql-contract-psl 0.5.0-dev.9 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,9 @@
1
- import { instantiateAuthoringTypeConstructor, isAuthoringTypeConstructorDescriptor, validateAuthoringHelperArguments } from "@prisma-next/framework-components/authoring";
1
+ import { hasRegisteredFieldNamespace, instantiateAuthoringFieldPreset, instantiateAuthoringTypeConstructor, isAuthoringFieldPresetDescriptor, isAuthoringTypeConstructorDescriptor, validateAuthoringHelperArguments } from "@prisma-next/framework-components/authoring";
2
2
  import { buildSqlContractFromDefinition } from "@prisma-next/sql-contract-ts/contract-builder";
3
3
  import { ifDefined } from "@prisma-next/utils/defined";
4
4
  import { notOk, ok } from "@prisma-next/utils/result";
5
5
  import { getPositionalArgument, parseQuotedStringLiteral } from "@prisma-next/psl-parser";
6
6
  import { assertDefined, invariant } from "@prisma-next/utils/assertions";
7
-
8
7
  //#region src/psl-attribute-parsing.ts
9
8
  function lowerFirst(value) {
10
9
  if (value.length === 0) return value;
@@ -77,6 +76,109 @@ function parseConstraintMapArgument(input) {
77
76
  function getPositionalArguments(attribute) {
78
77
  return attribute.args.filter((arg) => arg.kind === "positional").map((arg) => arg.kind === "positional" ? arg.value : "");
79
78
  }
79
+ /**
80
+ * Parses a PSL object-literal attribute argument value of the form
81
+ * `{ key1: "value1", key2: "value2" }` into a `Record<string, string>`.
82
+ *
83
+ * V1 admits string literals only as leaf values. Boolean and number
84
+ * literals are rejected. Trailing commas are allowed.
85
+ *
86
+ * Returns the parsed record, or pushes a diagnostic and returns undefined
87
+ * on malformed input or non-string leaves.
88
+ */
89
+ function parseObjectLiteralStringMap(input) {
90
+ const trimmed = input.raw.trim();
91
+ if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return pushInvalidAttributeArgument({
92
+ diagnostics: input.diagnostics,
93
+ sourceId: input.sourceId,
94
+ span: input.span,
95
+ message: `${input.entityLabel} expected an object literal value of the form { key: "value", ... }`
96
+ });
97
+ const body = trimmed.slice(1, -1).trim();
98
+ if (body.length === 0) return {};
99
+ const result = {};
100
+ for (const part of splitObjectLiteralEntries(body)) {
101
+ const colonAt = findTopLevelColon(part);
102
+ if (colonAt === -1) return pushInvalidAttributeArgument({
103
+ diagnostics: input.diagnostics,
104
+ sourceId: input.sourceId,
105
+ span: input.span,
106
+ message: `${input.entityLabel} object-literal entry "${part}" is missing a "key: value" colon`
107
+ });
108
+ const key = part.slice(0, colonAt).trim();
109
+ const rawValue = part.slice(colonAt + 1).trim();
110
+ if (key.length === 0 || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) return pushInvalidAttributeArgument({
111
+ diagnostics: input.diagnostics,
112
+ sourceId: input.sourceId,
113
+ span: input.span,
114
+ message: `${input.entityLabel} object-literal key "${key}" must be a bare identifier`
115
+ });
116
+ const parsedString = parseQuotedStringLiteral(rawValue);
117
+ if (parsedString === void 0) return pushInvalidAttributeArgument({
118
+ diagnostics: input.diagnostics,
119
+ sourceId: input.sourceId,
120
+ span: input.span,
121
+ message: `${input.entityLabel} object-literal value for "${key}" must be a quoted string literal (V1 PSL @@index options support string leaves only; use the TS authoring surface for non-string options)`
122
+ });
123
+ if (Object.hasOwn(result, key)) return pushInvalidAttributeArgument({
124
+ diagnostics: input.diagnostics,
125
+ sourceId: input.sourceId,
126
+ span: input.span,
127
+ message: `${input.entityLabel} object-literal key "${key}" appears more than once`
128
+ });
129
+ result[key] = parsedString;
130
+ }
131
+ return result;
132
+ }
133
+ function splitObjectLiteralEntries(body) {
134
+ const parts = [];
135
+ let depthBrace = 0;
136
+ let depthBracket = 0;
137
+ let depthParen = 0;
138
+ let quote = null;
139
+ let start = 0;
140
+ for (let index = 0; index < body.length; index += 1) {
141
+ const ch = body[index] ?? "";
142
+ if (quote) {
143
+ if (ch === quote && body[index - 1] !== "\\") quote = null;
144
+ continue;
145
+ }
146
+ if (ch === "\"" || ch === "'") {
147
+ quote = ch;
148
+ continue;
149
+ }
150
+ if (ch === "{") depthBrace += 1;
151
+ else if (ch === "}") depthBrace = Math.max(0, depthBrace - 1);
152
+ else if (ch === "[") depthBracket += 1;
153
+ else if (ch === "]") depthBracket = Math.max(0, depthBracket - 1);
154
+ else if (ch === "(") depthParen += 1;
155
+ else if (ch === ")") depthParen = Math.max(0, depthParen - 1);
156
+ else if (ch === "," && depthBrace === 0 && depthBracket === 0 && depthParen === 0) {
157
+ const segment = body.slice(start, index).trim();
158
+ if (segment.length > 0) parts.push(segment);
159
+ start = index + 1;
160
+ }
161
+ }
162
+ const tail = body.slice(start).trim();
163
+ if (tail.length > 0) parts.push(tail);
164
+ return parts;
165
+ }
166
+ function findTopLevelColon(entry) {
167
+ let quote = null;
168
+ for (let index = 0; index < entry.length; index += 1) {
169
+ const ch = entry[index] ?? "";
170
+ if (quote) {
171
+ if (ch === quote && entry[index - 1] !== "\\") quote = null;
172
+ continue;
173
+ }
174
+ if (ch === "\"" || ch === "'") {
175
+ quote = ch;
176
+ continue;
177
+ }
178
+ if (ch === ":") return index;
179
+ }
180
+ return -1;
181
+ }
80
182
  function pushInvalidAttributeArgument(input) {
81
183
  input.diagnostics.push({
82
184
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
@@ -149,7 +251,7 @@ function parseAttributeFieldList(input) {
149
251
  if (!raw) {
150
252
  input.diagnostics.push({
151
253
  code: input.code,
152
- message: `${input.messagePrefix} requires fields list argument`,
254
+ message: `${input.entityLabel} requires fields list argument`,
153
255
  sourceId: input.sourceId,
154
256
  span: input.attribute.span
155
257
  });
@@ -159,7 +261,7 @@ function parseAttributeFieldList(input) {
159
261
  if (!fields || fields.length === 0) {
160
262
  input.diagnostics.push({
161
263
  code: input.code,
162
- message: `${input.messagePrefix} requires bracketed field list argument`,
264
+ message: `${input.entityLabel} requires bracketed field list argument`,
163
265
  sourceId: input.sourceId,
164
266
  span: input.attribute.span
165
267
  });
@@ -167,6 +269,13 @@ function parseAttributeFieldList(input) {
167
269
  }
168
270
  return fields;
169
271
  }
272
+ function findDuplicateFieldName(fieldNames) {
273
+ const seen = /* @__PURE__ */ new Set();
274
+ for (const name of fieldNames) {
275
+ if (seen.has(name)) return name;
276
+ seen.add(name);
277
+ }
278
+ }
170
279
  function mapFieldNamesToColumns(input) {
171
280
  const columns = [];
172
281
  for (const fieldName of input.fieldNames) {
@@ -174,7 +283,7 @@ function mapFieldNamesToColumns(input) {
174
283
  if (!columnName) {
175
284
  input.diagnostics.push({
176
285
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
177
- message: `${input.contextLabel} references unknown field "${input.modelName}.${fieldName}"`,
286
+ message: `${input.entityLabel} references unknown field "${input.modelName}.${fieldName}"`,
178
287
  sourceId: input.sourceId,
179
288
  span: input.span
180
289
  });
@@ -184,7 +293,6 @@ function mapFieldNamesToColumns(input) {
184
293
  }
185
294
  return columns;
186
295
  }
187
-
188
296
  //#endregion
189
297
  //#region src/default-function-registry.ts
190
298
  function resolveSpanPositionFromBase(base, text, offset) {
@@ -323,7 +431,6 @@ function lowerDefaultFunctionWithRegistry(input) {
323
431
  }
324
432
  };
325
433
  }
326
-
327
434
  //#endregion
328
435
  //#region src/psl-authoring-arguments.ts
329
436
  const INVALID_AUTHORING_ARGUMENT = Symbol("invalidAuthoringArgument");
@@ -401,10 +508,10 @@ function parseJsLikeLiteral(value) {
401
508
  function parseNumber() {
402
509
  const raw = value.slice(index).match(/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/)?.[0];
403
510
  if (!raw) return INVALID_AUTHORING_ARGUMENT;
404
- const parsed$1 = Number(raw);
405
- if (!Number.isFinite(parsed$1)) return INVALID_AUTHORING_ARGUMENT;
511
+ const parsed = Number(raw);
512
+ if (!Number.isFinite(parsed)) return INVALID_AUTHORING_ARGUMENT;
406
513
  index += raw.length;
407
- return parsed$1;
514
+ return parsed;
408
515
  }
409
516
  function parseArray() {
410
517
  if (value[index] !== "[") return INVALID_AUTHORING_ARGUMENT;
@@ -511,6 +618,12 @@ function parsePslObjectLiteral(value) {
511
618
  function parsePslAuthoringArgumentValue(descriptor, rawValue) {
512
619
  switch (descriptor.kind) {
513
620
  case "string": return unquoteStringLiteral(rawValue);
621
+ case "boolean": {
622
+ const trimmed = rawValue.trim();
623
+ if (trimmed === "true") return true;
624
+ if (trimmed === "false") return false;
625
+ return INVALID_AUTHORING_ARGUMENT;
626
+ }
514
627
  case "number": {
515
628
  const parsed = Number(unquoteStringLiteral(rawValue));
516
629
  return Number.isNaN(parsed) ? INVALID_AUTHORING_ARGUMENT : parsed;
@@ -562,7 +675,7 @@ function mapPslHelperArgs(input) {
562
675
  mappedArgs[index] = value;
563
676
  }
564
677
  for (const argument of namedArgs) {
565
- const descriptorIndex = input.descriptors.findIndex((descriptor$1) => descriptor$1.name === argument.name);
678
+ const descriptorIndex = input.descriptors.findIndex((descriptor) => descriptor.name === argument.name);
566
679
  if (descriptorIndex < 0) return pushInvalidPslHelperArgument({
567
680
  diagnostics: input.diagnostics,
568
681
  sourceId: input.sourceId,
@@ -601,7 +714,6 @@ function mapPslHelperArgs(input) {
601
714
  }
602
715
  return mappedArgs;
603
716
  }
604
-
605
717
  //#endregion
606
718
  //#region src/psl-column-resolution.ts
607
719
  function toNamedTypeFieldDescriptor(typeRef, descriptor) {
@@ -620,33 +732,40 @@ function getAuthoringTypeConstructor(contributions, path) {
620
732
  return isAuthoringTypeConstructorDescriptor(current) ? current : void 0;
621
733
  }
622
734
  /**
623
- * Returns the namespace prefix of `attributeName` if it references an
624
- * unrecognized extension namespace, otherwise `undefined`. A namespace is
625
- * considered recognized when it is:
735
+ * Walks `authoringContributions.field` segment-by-segment and returns the field-preset descriptor at the resolved path, or `undefined` if no descriptor is registered.
736
+ *
737
+ * Symmetric with `getAuthoringTypeConstructor`. Field presets are strictly richer than type constructors — they can contribute `default` / `executionDefaults` / `id` / `unique` / `nullable` in addition to the `codecId` / `nativeType` / `typeParams` triple. PSL resolution tries field presets first, then falls back to type constructors on miss (see `resolveFieldTypeDescriptor`).
738
+ */
739
+ function getAuthoringFieldPreset(contributions, path) {
740
+ let current = contributions?.field;
741
+ for (const segment of path) {
742
+ if (typeof current !== "object" || current === null || Array.isArray(current)) return;
743
+ current = current[segment];
744
+ }
745
+ return isAuthoringFieldPresetDescriptor(current) ? current : void 0;
746
+ }
747
+ /**
748
+ * Returns the namespace prefix of `attributeName` if it references an unrecognized extension namespace, otherwise `undefined`. A namespace is considered recognized when it is:
626
749
  *
627
750
  * - `db` (native-type spec, always allowed),
628
751
  * - the active family id (e.g. `sql`),
629
752
  * - the active target id (e.g. `postgres`),
753
+ * - a registered field-preset namespace (e.g. `temporal`),
630
754
  * - present in `composedExtensions`.
631
755
  *
632
- * Family/target namespaces are exempted so that e.g. `@sql.foo` surfaces as
633
- * PSL_UNSUPPORTED_*_ATTRIBUTE (the attribute isn't defined) rather than
634
- * PSL_EXTENSION_NAMESPACE_NOT_COMPOSED (the namespace is already composed).
756
+ * Family/target/field-preset namespaces are exempted so that e.g. `@sql.foo` surfaces as PSL_UNSUPPORTED_*_ATTRIBUTE (the attribute isn't defined) rather than PSL_EXTENSION_NAMESPACE_NOT_COMPOSED (the namespace is already composed).
635
757
  */
636
758
  function checkUncomposedNamespace(attributeName, composedExtensions, context) {
637
759
  const dotIndex = attributeName.indexOf(".");
638
760
  if (dotIndex <= 0 || dotIndex === attributeName.length - 1) return;
639
761
  const namespace = attributeName.slice(0, dotIndex);
640
- if (namespace === "db" || namespace === context?.familyId || namespace === context?.targetId || composedExtensions.has(namespace)) return;
762
+ if (namespace === "db" || namespace === context?.familyId || namespace === context?.targetId || hasRegisteredFieldNamespace(context?.authoringContributions, namespace) || composedExtensions.has(namespace)) return;
641
763
  return namespace;
642
764
  }
643
765
  /**
644
- * Pushes the canonical `PSL_EXTENSION_NAMESPACE_NOT_COMPOSED` diagnostic for a
645
- * subject (attribute, model attribute, or type constructor) that references an
646
- * extension namespace which is not composed in the current contract.
766
+ * Pushes the canonical `PSL_EXTENSION_NAMESPACE_NOT_COMPOSED` diagnostic for a subject (attribute, model attribute, or type constructor) that references an extension namespace which is not composed in the current contract.
647
767
  *
648
- * The `data` payload carries the missing namespace so machine consumers
649
- * (agents, IDE extensions, CLI auto-fix) don't have to parse the prose.
768
+ * The `data` payload carries the missing namespace so machine consumers (agents, IDE extensions, CLI auto-fix) don't have to parse the prose.
650
769
  */
651
770
  function reportUncomposedNamespace(input) {
652
771
  input.diagnostics.push({
@@ -660,6 +779,21 @@ function reportUncomposedNamespace(input) {
660
779
  }
661
780
  });
662
781
  }
782
+ /**
783
+ * Pushes the canonical `PSL_UNKNOWN_FIELD_PRESET` diagnostic when a typoed preset name is referenced inside a registered field-preset namespace. The `data` payload exposes the namespace and full helper path so machine consumers (agents, IDE extensions) don't have to parse the prose.
784
+ */
785
+ function reportUnknownFieldPreset(input) {
786
+ input.diagnostics.push({
787
+ code: "PSL_UNKNOWN_FIELD_PRESET",
788
+ message: `${input.entityLabel} references unknown field preset "${input.helperPath}". Check the spelling against the available presets in the "${input.namespace}" namespace.`,
789
+ sourceId: input.sourceId,
790
+ span: input.span,
791
+ data: {
792
+ namespace: input.namespace,
793
+ helperPath: input.helperPath
794
+ }
795
+ });
796
+ }
663
797
  function instantiatePslTypeConstructor(input) {
664
798
  const helperPath = input.call.path.join(".");
665
799
  const args = mapPslHelperArgs({
@@ -697,11 +831,15 @@ function pushUnsupportedTypeConstructorDiagnostic(input) {
697
831
  function resolvePslTypeConstructorDescriptor(input) {
698
832
  const descriptor = getAuthoringTypeConstructor(input.authoringContributions, input.call.path);
699
833
  if (descriptor) return descriptor;
700
- const namespace = input.call.path.length > 1 ? input.call.path[0] : void 0;
701
- if (namespace && namespace !== "db" && namespace !== input.familyId && namespace !== input.targetId && !input.composedExtensions.has(namespace)) {
834
+ const uncomposedNamespace = checkUncomposedNamespace(input.call.path.join("."), input.composedExtensions, {
835
+ familyId: input.familyId,
836
+ targetId: input.targetId,
837
+ authoringContributions: input.authoringContributions
838
+ });
839
+ if (uncomposedNamespace) {
702
840
  reportUncomposedNamespace({
703
841
  subjectLabel: `Type constructor "${input.call.path.join(".")}"`,
704
- namespace,
842
+ namespace: uncomposedNamespace,
705
843
  sourceId: input.sourceId,
706
844
  span: input.call.span,
707
845
  diagnostics: input.diagnostics
@@ -716,10 +854,95 @@ function resolvePslTypeConstructorDescriptor(input) {
716
854
  message: input.unsupportedMessage
717
855
  });
718
856
  }
857
+ /**
858
+ * Instantiates a field-preset call against its descriptor, coercing PSL AST arguments into the descriptor's typed argument shape and returning the preset's full set of contract contributions.
859
+ *
860
+ * Symmetric with `instantiatePslTypeConstructor` but richer: a field preset can contribute `default`, `executionDefaults`, `id`, `unique`, and `nullable` in addition to the storage-type triple. PSL → typed-args coercion happens here (via `mapPslHelperArgs`) so that `instantiateAuthoringFieldPreset` itself stays typed-input-only and TS keeps its zero-runtime-validation cost.
861
+ */
862
+ function instantiatePslFieldPreset(input) {
863
+ const helperPath = input.call.path.join(".");
864
+ const args = mapPslHelperArgs({
865
+ args: input.call.args,
866
+ descriptors: input.descriptor.args ?? [],
867
+ helperLabel: `preset "${helperPath}"`,
868
+ span: input.call.span,
869
+ diagnostics: input.diagnostics,
870
+ sourceId: input.sourceId,
871
+ entityLabel: input.entityLabel
872
+ });
873
+ if (!args) return;
874
+ try {
875
+ validateAuthoringHelperArguments(helperPath, input.descriptor.args, args);
876
+ const instantiated = instantiateAuthoringFieldPreset(input.descriptor, args);
877
+ return {
878
+ descriptor: {
879
+ codecId: instantiated.descriptor.codecId,
880
+ nativeType: instantiated.descriptor.nativeType,
881
+ ...instantiated.descriptor.typeParams !== void 0 ? { typeParams: instantiated.descriptor.typeParams } : {}
882
+ },
883
+ nullable: instantiated.nullable,
884
+ ...instantiated.default !== void 0 ? { default: instantiated.default } : {},
885
+ ...instantiated.executionDefaults !== void 0 ? { executionDefaults: instantiated.executionDefaults } : {},
886
+ id: instantiated.id,
887
+ unique: instantiated.unique
888
+ };
889
+ } catch (error) {
890
+ const message = error instanceof Error ? error.message : String(error);
891
+ input.diagnostics.push({
892
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
893
+ message: `${input.entityLabel} preset "${helperPath}" ${message}`,
894
+ sourceId: input.sourceId,
895
+ span: input.call.span
896
+ });
897
+ return;
898
+ }
899
+ }
719
900
  function resolveFieldTypeDescriptor(input) {
720
901
  if (input.field.typeConstructor) {
902
+ const presetDescriptor = getAuthoringFieldPreset(input.authoringContributions, input.field.typeConstructor.path);
903
+ if (presetDescriptor) {
904
+ const instantiated = instantiatePslFieldPreset({
905
+ call: input.field.typeConstructor,
906
+ descriptor: presetDescriptor,
907
+ diagnostics: input.diagnostics,
908
+ sourceId: input.sourceId,
909
+ entityLabel: input.entityLabel
910
+ });
911
+ if (!instantiated) return {
912
+ ok: false,
913
+ alreadyReported: true
914
+ };
915
+ const presetContributions = {
916
+ nullable: instantiated.nullable,
917
+ id: instantiated.id,
918
+ unique: instantiated.unique,
919
+ ...instantiated.default !== void 0 ? { default: instantiated.default } : {},
920
+ ...instantiated.executionDefaults !== void 0 ? { executionDefaults: instantiated.executionDefaults } : {}
921
+ };
922
+ return {
923
+ ok: true,
924
+ descriptor: instantiated.descriptor,
925
+ presetContributions
926
+ };
927
+ }
721
928
  const helperPath = input.field.typeConstructor.path.join(".");
722
- const descriptor$1 = resolvePslTypeConstructorDescriptor({
929
+ const namespacePrefix = input.field.typeConstructor.path.length > 1 ? input.field.typeConstructor.path[0] : void 0;
930
+ const typeDescriptor = getAuthoringTypeConstructor(input.authoringContributions, input.field.typeConstructor.path);
931
+ if (!typeDescriptor && namespacePrefix && hasRegisteredFieldNamespace(input.authoringContributions, namespacePrefix)) {
932
+ reportUnknownFieldPreset({
933
+ entityLabel: input.entityLabel,
934
+ namespace: namespacePrefix,
935
+ helperPath,
936
+ sourceId: input.sourceId,
937
+ span: input.field.typeConstructor.span,
938
+ diagnostics: input.diagnostics
939
+ });
940
+ return {
941
+ ok: false,
942
+ alreadyReported: true
943
+ };
944
+ }
945
+ const descriptor = typeDescriptor ?? resolvePslTypeConstructorDescriptor({
723
946
  call: input.field.typeConstructor,
724
947
  authoringContributions: input.authoringContributions,
725
948
  composedExtensions: input.composedExtensions,
@@ -730,13 +953,13 @@ function resolveFieldTypeDescriptor(input) {
730
953
  unsupportedCode: "PSL_UNSUPPORTED_FIELD_TYPE",
731
954
  unsupportedMessage: `${input.entityLabel} type constructor "${helperPath}" is not supported in SQL PSL provider v1`
732
955
  });
733
- if (!descriptor$1) return {
956
+ if (!descriptor) return {
734
957
  ok: false,
735
958
  alreadyReported: true
736
959
  };
737
960
  const instantiated = instantiatePslTypeConstructor({
738
961
  call: input.field.typeConstructor,
739
- descriptor: descriptor$1,
962
+ descriptor,
740
963
  diagnostics: input.diagnostics,
741
964
  sourceId: input.sourceId,
742
965
  entityLabel: input.entityLabel
@@ -985,6 +1208,15 @@ function lowerDefaultForField(input) {
985
1208
  });
986
1209
  return {};
987
1210
  }
1211
+ if (generatorDescriptor.applicableCodecIds === void 0) {
1212
+ input.diagnostics.push({
1213
+ code: "PSL_INVALID_DEFAULT_APPLICABILITY",
1214
+ message: `Default generator "${generatorDescriptor.id}" is not applicable to "@default(...)" lowering. Use the corresponding field preset (e.g. \`temporal.${generatorDescriptor.id === "timestampNow" ? "updatedAt" : generatorDescriptor.id}()\`) instead.`,
1215
+ sourceId: input.sourceId,
1216
+ span: expressionEntry.span
1217
+ });
1218
+ return {};
1219
+ }
988
1220
  if (!generatorDescriptor.applicableCodecIds.includes(input.columnDescriptor.codecId)) {
989
1221
  input.diagnostics.push({
990
1222
  code: "PSL_INVALID_DEFAULT_APPLICABILITY",
@@ -994,7 +1226,7 @@ function lowerDefaultForField(input) {
994
1226
  });
995
1227
  return {};
996
1228
  }
997
- return { executionDefault: lowered.value.generated };
1229
+ return { executionDefaults: { onCreate: lowered.value.generated } };
998
1230
  }
999
1231
  function resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors) {
1000
1232
  if (field.typeRef && namedTypeDescriptors.has(field.typeRef)) return namedTypeDescriptors.get(field.typeRef);
@@ -1002,7 +1234,6 @@ function resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptor
1002
1234
  if (enumTypeDescriptors.has(field.typeName)) return enumTypeDescriptors.get(field.typeName);
1003
1235
  return scalarTypeDescriptors.get(field.typeName);
1004
1236
  }
1005
-
1006
1237
  //#endregion
1007
1238
  //#region src/psl-field-resolution.ts
1008
1239
  const BUILTIN_FIELD_ATTRIBUTE_NAMES = new Set([
@@ -1012,12 +1243,21 @@ const BUILTIN_FIELD_ATTRIBUTE_NAMES = new Set([
1012
1243
  "relation",
1013
1244
  "map"
1014
1245
  ]);
1246
+ const REMOVED_ATTRIBUTE_RULES = new Map([["updatedAt", {
1247
+ hint: "Use `temporal.updatedAt()` as a field-preset call instead.",
1248
+ suppressWhen: (field) => field.typeConstructor?.path[0] === "temporal"
1249
+ }]]);
1250
+ {
1251
+ const overlap = [...REMOVED_ATTRIBUTE_RULES.keys()].filter((name) => BUILTIN_FIELD_ATTRIBUTE_NAMES.has(name));
1252
+ if (overlap.length > 0) throw new Error(`BUILTIN_FIELD_ATTRIBUTE_NAMES and REMOVED_ATTRIBUTE_RULES must not overlap. Names in both: ${overlap.join(", ")}`);
1253
+ }
1015
1254
  function validateFieldAttributes(input) {
1016
1255
  for (const attribute of input.field.attributes) {
1017
1256
  if (BUILTIN_FIELD_ATTRIBUTE_NAMES.has(attribute.name)) continue;
1018
1257
  const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
1019
1258
  familyId: input.familyId,
1020
- targetId: input.targetId
1259
+ targetId: input.targetId,
1260
+ authoringContributions: input.authoringContributions
1021
1261
  });
1022
1262
  if (uncomposedNamespace) {
1023
1263
  reportUncomposedNamespace({
@@ -1029,9 +1269,12 @@ function validateFieldAttributes(input) {
1029
1269
  });
1030
1270
  continue;
1031
1271
  }
1272
+ const baseMessage = `Field "${input.model.name}.${input.field.name}" uses unsupported attribute "@${attribute.name}"`;
1273
+ const removedRule = REMOVED_ATTRIBUTE_RULES.get(attribute.name);
1274
+ const message = removedRule && !removedRule.suppressWhen(input.field) ? `${baseMessage}. ${removedRule.hint}` : baseMessage;
1032
1275
  input.diagnostics.push({
1033
1276
  code: "PSL_UNSUPPORTED_FIELD_ATTRIBUTE",
1034
- message: `Field "${input.model.name}.${input.field.name}" uses unsupported attribute "@${attribute.name}"`,
1277
+ message,
1035
1278
  sourceId: input.sourceId,
1036
1279
  span: attribute.span
1037
1280
  });
@@ -1065,21 +1308,25 @@ function collectResolvedFields(input) {
1065
1308
  const { model, mapping, enumTypeDescriptors, namedTypeDescriptors, modelNames, compositeTypeNames, composedExtensions, authoringContributions, familyId, targetId, defaultFunctionRegistry, generatorDescriptorById, diagnostics, sourceId, scalarTypeDescriptors } = input;
1066
1309
  const resolvedFields = [];
1067
1310
  for (const field of model.fields) {
1068
- if (field.list && modelNames.has(field.typeName)) continue;
1311
+ const isModelField = modelNames.has(field.typeName);
1312
+ if (field.list && isModelField) continue;
1069
1313
  validateFieldAttributes({
1070
1314
  model,
1071
1315
  field,
1072
1316
  composedExtensions,
1317
+ authoringContributions,
1073
1318
  diagnostics,
1074
1319
  sourceId,
1075
1320
  familyId,
1076
1321
  targetId
1077
1322
  });
1078
- if (getAttribute(field.attributes, "relation") && modelNames.has(field.typeName)) continue;
1323
+ const relationAttribute = getAttribute(field.attributes, "relation");
1324
+ if (isModelField && relationAttribute) continue;
1079
1325
  const isValueObjectField = compositeTypeNames.has(field.typeName);
1080
1326
  const isListField = field.list;
1081
1327
  let descriptor;
1082
1328
  let scalarCodecId;
1329
+ let presetContributions;
1083
1330
  const resolveInput = {
1084
1331
  field,
1085
1332
  enumTypeDescriptors,
@@ -1105,6 +1352,15 @@ function collectResolvedFields(input) {
1105
1352
  });
1106
1353
  continue;
1107
1354
  }
1355
+ if (resolved.presetContributions) {
1356
+ diagnostics.push({
1357
+ code: "PSL_PRESET_NOT_LIST",
1358
+ message: `Field "${model.name}.${field.name}" uses a field-preset call as a list element type. Presets cannot be list elements; remove "[]" or use a scalar type.`,
1359
+ sourceId,
1360
+ span: field.span
1361
+ });
1362
+ continue;
1363
+ }
1108
1364
  scalarCodecId = resolved.descriptor.codecId;
1109
1365
  descriptor = scalarTypeDescriptors.get("Json");
1110
1366
  } else {
@@ -1119,9 +1375,28 @@ function collectResolvedFields(input) {
1119
1375
  continue;
1120
1376
  }
1121
1377
  descriptor = resolved.descriptor;
1378
+ presetContributions = resolved.presetContributions;
1122
1379
  }
1123
1380
  if (!descriptor) continue;
1381
+ if (presetContributions && field.optional) {
1382
+ diagnostics.push({
1383
+ code: "PSL_PRESET_NOT_OPTIONAL",
1384
+ message: `Field "${model.name}.${field.name}" uses a field-preset call and cannot be optional. Remove "?" or use a different field type.`,
1385
+ sourceId,
1386
+ span: field.span
1387
+ });
1388
+ continue;
1389
+ }
1124
1390
  const defaultAttribute = getAttribute(field.attributes, "default");
1391
+ if (presetContributions && defaultAttribute) {
1392
+ diagnostics.push({
1393
+ code: "PSL_PRESET_AND_DEFAULT_CONFLICT",
1394
+ message: `Field "${model.name}.${field.name}" uses a field-preset call and cannot also declare @default(...). The preset already specifies the default value.`,
1395
+ sourceId,
1396
+ span: defaultAttribute.span
1397
+ });
1398
+ continue;
1399
+ }
1125
1400
  const loweredDefault = defaultAttribute ? lowerDefaultForField({
1126
1401
  modelName: model.name,
1127
1402
  fieldName: field.name,
@@ -1132,8 +1407,9 @@ function collectResolvedFields(input) {
1132
1407
  defaultFunctionRegistry,
1133
1408
  diagnostics
1134
1409
  }) : {};
1135
- if (field.optional && loweredDefault.executionDefault) {
1136
- const generatorDescription = loweredDefault.executionDefault.kind === "generator" ? `"${loweredDefault.executionDefault.id}"` : "for this field";
1410
+ const loweredOnCreate = loweredDefault.executionDefaults?.onCreate;
1411
+ if (field.optional && loweredOnCreate) {
1412
+ const generatorDescription = loweredOnCreate.kind === "generator" ? `"${loweredOnCreate.id}"` : "for this field";
1137
1413
  diagnostics.push({
1138
1414
  code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
1139
1415
  message: `Field "${model.name}.${field.name}" cannot be optional when using execution default ${generatorDescription}. Remove "?" or use a storage default.`,
@@ -1142,8 +1418,8 @@ function collectResolvedFields(input) {
1142
1418
  });
1143
1419
  continue;
1144
1420
  }
1145
- if (loweredDefault.executionDefault) {
1146
- const generatedDescriptor = generatorDescriptorById.get(loweredDefault.executionDefault.id)?.resolveGeneratedColumnDescriptor?.({ generated: loweredDefault.executionDefault });
1421
+ if (loweredOnCreate) {
1422
+ const generatedDescriptor = generatorDescriptorById.get(loweredOnCreate.id)?.resolveGeneratedColumnDescriptor?.({ generated: loweredOnCreate });
1147
1423
  if (generatedDescriptor) descriptor = generatedDescriptor;
1148
1424
  }
1149
1425
  const mappedColumnName = mapping.fieldColumns.get(field.name) ?? field.name;
@@ -1153,14 +1429,35 @@ function collectResolvedFields(input) {
1153
1429
  sourceId,
1154
1430
  diagnostics
1155
1431
  });
1432
+ let isIdField = Boolean(idAttribute);
1433
+ if (idAttribute && field.optional) {
1434
+ diagnostics.push({
1435
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1436
+ message: `Field "${model.name}.${field.name}" @id cannot be optional; primary key columns must be NOT NULL`,
1437
+ sourceId,
1438
+ span: idAttribute.span
1439
+ });
1440
+ isIdField = false;
1441
+ }
1442
+ if (presetContributions && idAttribute && !presetContributions.id) {
1443
+ diagnostics.push({
1444
+ code: "PSL_PRESET_AND_ID_CONFLICT",
1445
+ message: `Field "${model.name}.${field.name}" uses a field-preset call and cannot also declare @id. Use a preset that contributes id semantics, or drop @id.`,
1446
+ sourceId,
1447
+ span: idAttribute.span
1448
+ });
1449
+ continue;
1450
+ }
1451
+ const fieldExecutionDefaults = presetContributions?.executionDefaults ?? loweredDefault.executionDefaults;
1452
+ const fieldDefaultValue = presetContributions?.default ?? loweredDefault.defaultValue;
1156
1453
  resolvedFields.push({
1157
1454
  field,
1158
1455
  columnName: mappedColumnName,
1159
1456
  descriptor,
1160
- ...ifDefined("defaultValue", loweredDefault.defaultValue),
1161
- ...ifDefined("executionDefault", loweredDefault.executionDefault),
1162
- isId: Boolean(idAttribute),
1163
- isUnique: Boolean(uniqueAttribute),
1457
+ ...ifDefined("defaultValue", fieldDefaultValue),
1458
+ ...ifDefined("executionDefaults", fieldExecutionDefaults),
1459
+ isId: isIdField || Boolean(presetContributions?.id),
1460
+ isUnique: Boolean(uniqueAttribute) || Boolean(presetContributions?.unique),
1164
1461
  ...ifDefined("idName", idName),
1165
1462
  ...ifDefined("uniqueName", uniqueName),
1166
1463
  ...ifDefined("many", isListField ? true : void 0),
@@ -1201,7 +1498,6 @@ function buildModelMappings(models, diagnostics, sourceId) {
1201
1498
  }
1202
1499
  return result;
1203
1500
  }
1204
-
1205
1501
  //#endregion
1206
1502
  //#region src/psl-relation-resolution.ts
1207
1503
  const REFERENTIAL_ACTION_MAP = {
@@ -1418,7 +1714,8 @@ function validateNavigationListFieldAttributes(input) {
1418
1714
  if (attribute.name === "relation") continue;
1419
1715
  const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
1420
1716
  familyId: input.familyId,
1421
- targetId: input.targetId
1717
+ targetId: input.targetId,
1718
+ authoringContributions: input.authoringContributions
1422
1719
  });
1423
1720
  if (uncomposedNamespace) {
1424
1721
  reportUncomposedNamespace({
@@ -1441,7 +1738,6 @@ function validateNavigationListFieldAttributes(input) {
1441
1738
  }
1442
1739
  return valid;
1443
1740
  }
1444
-
1445
1741
  //#endregion
1446
1742
  //#region src/interpreter.ts
1447
1743
  function buildComposedExtensionPackRefs(target, extensionIds, extensionPackRefs = []) {
@@ -1531,7 +1827,8 @@ function validateNamedTypeAttributes(input) {
1531
1827
  if (input.allowDbNativeType && attribute.name.startsWith("db.")) continue;
1532
1828
  const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
1533
1829
  familyId: input.familyId,
1534
- targetId: input.targetId
1830
+ targetId: input.targetId,
1831
+ authoringContributions: input.authoringContributions
1535
1832
  });
1536
1833
  if (uncomposedNamespace) {
1537
1834
  reportUncomposedNamespace({
@@ -1562,16 +1859,17 @@ function resolveNamedTypeDeclarations(input) {
1562
1859
  const namedTypeDescriptors = /* @__PURE__ */ new Map();
1563
1860
  for (const declaration of input.declarations) {
1564
1861
  if (declaration.typeConstructor) {
1565
- const { hasUnsupportedNamedTypeAttribute: hasUnsupportedNamedTypeAttribute$1 } = validateNamedTypeAttributes({
1862
+ const { hasUnsupportedNamedTypeAttribute } = validateNamedTypeAttributes({
1566
1863
  declaration,
1567
1864
  sourceId: input.sourceId,
1568
1865
  diagnostics: input.diagnostics,
1569
1866
  composedExtensions: input.composedExtensions,
1867
+ authoringContributions: input.authoringContributions,
1570
1868
  allowDbNativeType: false,
1571
1869
  familyId: input.familyId,
1572
1870
  targetId: input.targetId
1573
1871
  });
1574
- if (hasUnsupportedNamedTypeAttribute$1) continue;
1872
+ if (hasUnsupportedNamedTypeAttribute) continue;
1575
1873
  const helperPath = declaration.typeConstructor.path.join(".");
1576
1874
  const typeConstructor = resolvePslTypeConstructorDescriptor({
1577
1875
  call: declaration.typeConstructor,
@@ -1626,13 +1924,14 @@ function resolveNamedTypeDeclarations(input) {
1626
1924
  sourceId: input.sourceId,
1627
1925
  diagnostics: input.diagnostics,
1628
1926
  composedExtensions: input.composedExtensions,
1927
+ authoringContributions: input.authoringContributions,
1629
1928
  allowDbNativeType: true,
1630
1929
  familyId: input.familyId,
1631
1930
  targetId: input.targetId
1632
1931
  });
1633
1932
  if (hasUnsupportedNamedTypeAttribute) continue;
1634
1933
  if (dbNativeTypeAttribute) {
1635
- const descriptor$1 = resolveDbNativeTypeAttribute({
1934
+ const descriptor = resolveDbNativeTypeAttribute({
1636
1935
  attribute: dbNativeTypeAttribute,
1637
1936
  baseType,
1638
1937
  baseDescriptor,
@@ -1640,12 +1939,12 @@ function resolveNamedTypeDeclarations(input) {
1640
1939
  sourceId: input.sourceId,
1641
1940
  entityLabel: `Named type "${declaration.name}"`
1642
1941
  });
1643
- if (!descriptor$1) continue;
1644
- namedTypeDescriptors.set(declaration.name, toNamedTypeFieldDescriptor(declaration.name, descriptor$1));
1942
+ if (!descriptor) continue;
1943
+ namedTypeDescriptors.set(declaration.name, toNamedTypeFieldDescriptor(declaration.name, descriptor));
1645
1944
  storageTypes[declaration.name] = {
1646
- codecId: descriptor$1.codecId,
1647
- nativeType: descriptor$1.nativeType,
1648
- typeParams: descriptor$1.typeParams ?? {}
1945
+ codecId: descriptor.codecId,
1946
+ nativeType: descriptor.nativeType,
1947
+ typeParams: descriptor.typeParams ?? {}
1649
1948
  };
1650
1949
  continue;
1651
1950
  }
@@ -1682,16 +1981,20 @@ function buildModelNodeFromPsl(input) {
1682
1981
  sourceId,
1683
1982
  scalarTypeDescriptors: input.scalarTypeDescriptors
1684
1983
  });
1685
- const primaryKeyFields = resolvedFields.filter((field) => field.isId);
1686
- const primaryKeyColumns = primaryKeyFields.map((field) => field.columnName);
1687
- const primaryKeyName = primaryKeyFields.length === 1 ? primaryKeyFields[0]?.idName : void 0;
1688
- const isVariantModel = model.attributes.some((attr) => attr.name === "base");
1689
- if (primaryKeyColumns.length === 0 && !isVariantModel) diagnostics.push({
1690
- code: "PSL_MISSING_PRIMARY_KEY",
1691
- message: `Model "${model.name}" must declare at least one @id field for SQL provider`,
1984
+ const inlineIdFields = resolvedFields.filter((field) => field.isId);
1985
+ if (inlineIdFields.length > 1) diagnostics.push({
1986
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1987
+ message: `Model "${model.name}" cannot declare inline @id on multiple fields; use model-level @@id([...]) for composite identity`,
1692
1988
  sourceId,
1693
1989
  span: model.span
1694
1990
  });
1991
+ const singleInlineIdField = inlineIdFields.length === 1 ? inlineIdFields[0] : void 0;
1992
+ let primaryKey = singleInlineIdField ? {
1993
+ columns: [singleInlineIdField.columnName],
1994
+ ...ifDefined("name", singleInlineIdField.idName)
1995
+ } : void 0;
1996
+ const hasInlinePrimaryKey = primaryKey !== void 0;
1997
+ let blockPrimaryKeyDeclared = false;
1695
1998
  const resultBackrelationCandidates = [];
1696
1999
  for (const field of model.fields) {
1697
2000
  if (!field.list || !input.modelNames.has(field.typeName)) continue;
@@ -1700,6 +2003,7 @@ function buildModelNodeFromPsl(input) {
1700
2003
  field,
1701
2004
  sourceId,
1702
2005
  composedExtensions: input.composedExtensions,
2006
+ authoringContributions: input.authoringContributions,
1703
2007
  diagnostics,
1704
2008
  familyId: input.familyId,
1705
2009
  targetId: input.targetId
@@ -1757,15 +2061,98 @@ function buildModelNodeFromPsl(input) {
1757
2061
  for (const modelAttribute of model.attributes) {
1758
2062
  if (modelAttribute.name === "map") continue;
1759
2063
  if (modelAttribute.name === "discriminator" || modelAttribute.name === "base") continue;
2064
+ const attributeLabel = `Model "${model.name}" @@${modelAttribute.name}`;
2065
+ if (modelAttribute.name === "id") {
2066
+ if (blockPrimaryKeyDeclared) {
2067
+ diagnostics.push({
2068
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
2069
+ message: `Model "${model.name}" declares @@id more than once`,
2070
+ sourceId,
2071
+ span: modelAttribute.span
2072
+ });
2073
+ continue;
2074
+ }
2075
+ if (hasInlinePrimaryKey) {
2076
+ diagnostics.push({
2077
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
2078
+ message: `Model "${model.name}" cannot declare both field-level @id and model-level @@id`,
2079
+ sourceId,
2080
+ span: modelAttribute.span
2081
+ });
2082
+ blockPrimaryKeyDeclared = true;
2083
+ continue;
2084
+ }
2085
+ const fieldNames = parseAttributeFieldList({
2086
+ attribute: modelAttribute,
2087
+ sourceId,
2088
+ diagnostics,
2089
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
2090
+ entityLabel: attributeLabel
2091
+ });
2092
+ if (!fieldNames) continue;
2093
+ const duplicateFieldName = findDuplicateFieldName(fieldNames);
2094
+ if (duplicateFieldName !== void 0) {
2095
+ diagnostics.push({
2096
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
2097
+ message: `${attributeLabel} list contains duplicate field "${duplicateFieldName}"`,
2098
+ sourceId,
2099
+ span: modelAttribute.span
2100
+ });
2101
+ continue;
2102
+ }
2103
+ const nullableFieldName = fieldNames.find((name) => model.fields.find((f) => f.name === name)?.optional === true);
2104
+ if (nullableFieldName !== void 0) {
2105
+ diagnostics.push({
2106
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
2107
+ message: `${attributeLabel} cannot include optional field "${nullableFieldName}"; primary key columns must be NOT NULL`,
2108
+ sourceId,
2109
+ span: modelAttribute.span
2110
+ });
2111
+ continue;
2112
+ }
2113
+ const columnNames = mapFieldNamesToColumns({
2114
+ modelName: model.name,
2115
+ fieldNames,
2116
+ mapping,
2117
+ sourceId,
2118
+ diagnostics,
2119
+ span: modelAttribute.span,
2120
+ entityLabel: attributeLabel
2121
+ });
2122
+ if (!columnNames) continue;
2123
+ primaryKey = {
2124
+ columns: columnNames,
2125
+ ...ifDefined("name", parseConstraintMapArgument({
2126
+ attribute: modelAttribute,
2127
+ sourceId,
2128
+ diagnostics,
2129
+ entityLabel: attributeLabel,
2130
+ span: modelAttribute.span,
2131
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT"
2132
+ }))
2133
+ };
2134
+ blockPrimaryKeyDeclared = true;
2135
+ continue;
2136
+ }
1760
2137
  if (modelAttribute.name === "unique" || modelAttribute.name === "index") {
1761
2138
  const fieldNames = parseAttributeFieldList({
1762
2139
  attribute: modelAttribute,
1763
2140
  sourceId,
1764
2141
  diagnostics,
1765
2142
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
1766
- messagePrefix: `Model "${model.name}" @@${modelAttribute.name}`
2143
+ entityLabel: attributeLabel
1767
2144
  });
1768
2145
  if (!fieldNames) continue;
2146
+ const duplicateFieldName = findDuplicateFieldName(fieldNames);
2147
+ if (duplicateFieldName !== void 0) {
2148
+ diagnostics.push({
2149
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
2150
+ message: `${attributeLabel} list contains duplicate field "${duplicateFieldName}"`,
2151
+ sourceId,
2152
+ span: modelAttribute.span
2153
+ });
2154
+ continue;
2155
+ }
1769
2156
  const columnNames = mapFieldNamesToColumns({
1770
2157
  modelName: model.name,
1771
2158
  fieldNames,
@@ -1773,14 +2160,14 @@ function buildModelNodeFromPsl(input) {
1773
2160
  sourceId,
1774
2161
  diagnostics,
1775
2162
  span: modelAttribute.span,
1776
- contextLabel: `Model "${model.name}" @@${modelAttribute.name}`
2163
+ entityLabel: attributeLabel
1777
2164
  });
1778
2165
  if (!columnNames) continue;
1779
2166
  const constraintName = parseConstraintMapArgument({
1780
2167
  attribute: modelAttribute,
1781
2168
  sourceId,
1782
2169
  diagnostics,
1783
- entityLabel: `Model "${model.name}" @@${modelAttribute.name}`,
2170
+ entityLabel: attributeLabel,
1784
2171
  span: modelAttribute.span,
1785
2172
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT"
1786
2173
  });
@@ -1788,15 +2175,58 @@ function buildModelNodeFromPsl(input) {
1788
2175
  columns: columnNames,
1789
2176
  ...ifDefined("name", constraintName)
1790
2177
  });
1791
- else indexNodes.push({
1792
- columns: columnNames,
1793
- ...ifDefined("name", constraintName)
1794
- });
2178
+ else {
2179
+ const indexEntityLabel = `Model "${model.name}" @@index`;
2180
+ const rawTypeArg = getNamedArgument(modelAttribute, "type");
2181
+ let indexType;
2182
+ if (rawTypeArg !== void 0) {
2183
+ const parsed = parseQuotedStringLiteral(rawTypeArg);
2184
+ if (parsed === void 0) {
2185
+ diagnostics.push({
2186
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
2187
+ message: `${indexEntityLabel} type argument must be a quoted string literal`,
2188
+ sourceId,
2189
+ span: modelAttribute.span
2190
+ });
2191
+ continue;
2192
+ }
2193
+ indexType = parsed;
2194
+ }
2195
+ const rawOptionsArg = getNamedArgument(modelAttribute, "options");
2196
+ let indexOptions;
2197
+ if (rawOptionsArg !== void 0) {
2198
+ if (indexType === void 0) {
2199
+ diagnostics.push({
2200
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
2201
+ message: `${indexEntityLabel} options argument requires a type argument`,
2202
+ sourceId,
2203
+ span: modelAttribute.span
2204
+ });
2205
+ continue;
2206
+ }
2207
+ const parsed = parseObjectLiteralStringMap({
2208
+ raw: rawOptionsArg,
2209
+ diagnostics,
2210
+ sourceId,
2211
+ span: modelAttribute.span,
2212
+ entityLabel: indexEntityLabel
2213
+ });
2214
+ if (parsed === void 0) continue;
2215
+ indexOptions = parsed;
2216
+ }
2217
+ indexNodes.push({
2218
+ columns: columnNames,
2219
+ ...ifDefined("name", constraintName),
2220
+ ...ifDefined("type", indexType),
2221
+ ...ifDefined("options", indexOptions)
2222
+ });
2223
+ }
1795
2224
  continue;
1796
2225
  }
1797
2226
  const uncomposedNamespace = checkUncomposedNamespace(modelAttribute.name, input.composedExtensions, {
1798
2227
  familyId: input.familyId,
1799
- targetId: input.targetId
2228
+ targetId: input.targetId,
2229
+ authoringContributions: input.authoringContributions
1800
2230
  });
1801
2231
  if (uncomposedNamespace) {
1802
2232
  reportUncomposedNamespace({
@@ -1861,7 +2291,7 @@ function buildModelNodeFromPsl(input) {
1861
2291
  sourceId,
1862
2292
  diagnostics,
1863
2293
  span: relationAttribute.relation.span,
1864
- contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
2294
+ entityLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
1865
2295
  });
1866
2296
  if (!localColumns) continue;
1867
2297
  const referencedColumns = mapFieldNamesToColumns({
@@ -1871,7 +2301,7 @@ function buildModelNodeFromPsl(input) {
1871
2301
  sourceId,
1872
2302
  diagnostics,
1873
2303
  span: relationAttribute.relation.span,
1874
- contextLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
2304
+ entityLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
1875
2305
  });
1876
2306
  if (!referencedColumns) continue;
1877
2307
  if (localColumns.length !== referencedColumns.length) {
@@ -1933,12 +2363,9 @@ function buildModelNodeFromPsl(input) {
1933
2363
  descriptor: resolvedField.descriptor,
1934
2364
  nullable: resolvedField.field.optional,
1935
2365
  ...ifDefined("default", resolvedField.defaultValue),
1936
- ...ifDefined("executionDefault", resolvedField.executionDefault)
2366
+ ...ifDefined("executionDefaults", resolvedField.executionDefaults)
1937
2367
  })),
1938
- ...primaryKeyColumns.length > 0 ? { id: {
1939
- columns: primaryKeyColumns,
1940
- ...ifDefined("name", primaryKeyName)
1941
- } } : {},
2368
+ ...ifDefined("id", primaryKey),
1942
2369
  ...uniqueConstraints.length > 0 ? { uniques: uniqueConstraints } : {},
1943
2370
  ...indexNodes.length > 0 ? { indexes: indexNodes } : {},
1944
2371
  ...foreignKeyNodes.length > 0 ? { foreignKeys: foreignKeyNodes } : {}
@@ -2337,7 +2764,7 @@ function interpretPslDocumentToSqlContract(input) {
2337
2764
  ...Object.keys(valueObjects).length > 0 ? { valueObjects } : {}
2338
2765
  });
2339
2766
  }
2340
-
2341
2767
  //#endregion
2342
2768
  export { interpretPslDocumentToSqlContract as t };
2343
- //# sourceMappingURL=interpreter-iFCRN9nb.mjs.map
2769
+
2770
+ //# sourceMappingURL=interpreter-ijCjxhaU.mjs.map