@prisma-next/sql-contract-psl 0.5.0-dev.6 → 0.5.0-dev.61
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.
- package/README.md +9 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{interpreter-iFCRN9nb.mjs → interpreter-g4FDWENY.mjs} +337 -54
- package/dist/interpreter-g4FDWENY.mjs.map +1 -0
- package/dist/provider.d.mts +2 -2
- package/dist/provider.d.mts.map +1 -1
- package/dist/provider.mjs +2 -4
- package/dist/provider.mjs.map +1 -1
- package/package.json +12 -11
- package/src/interpreter.ts +128 -28
- package/src/provider.ts +3 -5
- package/src/psl-attribute-parsing.ts +14 -5
- package/src/psl-column-resolution.ts +228 -37
- package/src/psl-field-resolution.ts +138 -17
- package/src/psl-relation-resolution.ts +3 -0
- package/dist/interpreter-iFCRN9nb.mjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
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";
|
|
@@ -149,7 +149,7 @@ function parseAttributeFieldList(input) {
|
|
|
149
149
|
if (!raw) {
|
|
150
150
|
input.diagnostics.push({
|
|
151
151
|
code: input.code,
|
|
152
|
-
message: `${input.
|
|
152
|
+
message: `${input.entityLabel} requires fields list argument`,
|
|
153
153
|
sourceId: input.sourceId,
|
|
154
154
|
span: input.attribute.span
|
|
155
155
|
});
|
|
@@ -159,7 +159,7 @@ function parseAttributeFieldList(input) {
|
|
|
159
159
|
if (!fields || fields.length === 0) {
|
|
160
160
|
input.diagnostics.push({
|
|
161
161
|
code: input.code,
|
|
162
|
-
message: `${input.
|
|
162
|
+
message: `${input.entityLabel} requires bracketed field list argument`,
|
|
163
163
|
sourceId: input.sourceId,
|
|
164
164
|
span: input.attribute.span
|
|
165
165
|
});
|
|
@@ -167,6 +167,13 @@ function parseAttributeFieldList(input) {
|
|
|
167
167
|
}
|
|
168
168
|
return fields;
|
|
169
169
|
}
|
|
170
|
+
function findDuplicateFieldName(fieldNames) {
|
|
171
|
+
const seen = /* @__PURE__ */ new Set();
|
|
172
|
+
for (const name of fieldNames) {
|
|
173
|
+
if (seen.has(name)) return name;
|
|
174
|
+
seen.add(name);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
170
177
|
function mapFieldNamesToColumns(input) {
|
|
171
178
|
const columns = [];
|
|
172
179
|
for (const fieldName of input.fieldNames) {
|
|
@@ -174,7 +181,7 @@ function mapFieldNamesToColumns(input) {
|
|
|
174
181
|
if (!columnName) {
|
|
175
182
|
input.diagnostics.push({
|
|
176
183
|
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
177
|
-
message: `${input.
|
|
184
|
+
message: `${input.entityLabel} references unknown field "${input.modelName}.${fieldName}"`,
|
|
178
185
|
sourceId: input.sourceId,
|
|
179
186
|
span: input.span
|
|
180
187
|
});
|
|
@@ -620,33 +627,40 @@ function getAuthoringTypeConstructor(contributions, path) {
|
|
|
620
627
|
return isAuthoringTypeConstructorDescriptor(current) ? current : void 0;
|
|
621
628
|
}
|
|
622
629
|
/**
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
*
|
|
630
|
+
* Walks `authoringContributions.field` segment-by-segment and returns the field-preset descriptor at the resolved path, or `undefined` if no descriptor is registered.
|
|
631
|
+
*
|
|
632
|
+
* 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`).
|
|
633
|
+
*/
|
|
634
|
+
function getAuthoringFieldPreset(contributions, path) {
|
|
635
|
+
let current = contributions?.field;
|
|
636
|
+
for (const segment of path) {
|
|
637
|
+
if (typeof current !== "object" || current === null || Array.isArray(current)) return;
|
|
638
|
+
current = current[segment];
|
|
639
|
+
}
|
|
640
|
+
return isAuthoringFieldPresetDescriptor(current) ? current : void 0;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Returns the namespace prefix of `attributeName` if it references an unrecognized extension namespace, otherwise `undefined`. A namespace is considered recognized when it is:
|
|
626
644
|
*
|
|
627
645
|
* - `db` (native-type spec, always allowed),
|
|
628
646
|
* - the active family id (e.g. `sql`),
|
|
629
647
|
* - the active target id (e.g. `postgres`),
|
|
648
|
+
* - a registered field-preset namespace (e.g. `temporal`),
|
|
630
649
|
* - present in `composedExtensions`.
|
|
631
650
|
*
|
|
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).
|
|
651
|
+
* 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
652
|
*/
|
|
636
653
|
function checkUncomposedNamespace(attributeName, composedExtensions, context) {
|
|
637
654
|
const dotIndex = attributeName.indexOf(".");
|
|
638
655
|
if (dotIndex <= 0 || dotIndex === attributeName.length - 1) return;
|
|
639
656
|
const namespace = attributeName.slice(0, dotIndex);
|
|
640
|
-
if (namespace === "db" || namespace === context?.familyId || namespace === context?.targetId || composedExtensions.has(namespace)) return;
|
|
657
|
+
if (namespace === "db" || namespace === context?.familyId || namespace === context?.targetId || hasRegisteredFieldNamespace(context?.authoringContributions, namespace) || composedExtensions.has(namespace)) return;
|
|
641
658
|
return namespace;
|
|
642
659
|
}
|
|
643
660
|
/**
|
|
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.
|
|
661
|
+
* 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
662
|
*
|
|
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.
|
|
663
|
+
* The `data` payload carries the missing namespace so machine consumers (agents, IDE extensions, CLI auto-fix) don't have to parse the prose.
|
|
650
664
|
*/
|
|
651
665
|
function reportUncomposedNamespace(input) {
|
|
652
666
|
input.diagnostics.push({
|
|
@@ -660,6 +674,21 @@ function reportUncomposedNamespace(input) {
|
|
|
660
674
|
}
|
|
661
675
|
});
|
|
662
676
|
}
|
|
677
|
+
/**
|
|
678
|
+
* 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.
|
|
679
|
+
*/
|
|
680
|
+
function reportUnknownFieldPreset(input) {
|
|
681
|
+
input.diagnostics.push({
|
|
682
|
+
code: "PSL_UNKNOWN_FIELD_PRESET",
|
|
683
|
+
message: `${input.entityLabel} references unknown field preset "${input.helperPath}". Check the spelling against the available presets in the "${input.namespace}" namespace.`,
|
|
684
|
+
sourceId: input.sourceId,
|
|
685
|
+
span: input.span,
|
|
686
|
+
data: {
|
|
687
|
+
namespace: input.namespace,
|
|
688
|
+
helperPath: input.helperPath
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
}
|
|
663
692
|
function instantiatePslTypeConstructor(input) {
|
|
664
693
|
const helperPath = input.call.path.join(".");
|
|
665
694
|
const args = mapPslHelperArgs({
|
|
@@ -697,11 +726,15 @@ function pushUnsupportedTypeConstructorDiagnostic(input) {
|
|
|
697
726
|
function resolvePslTypeConstructorDescriptor(input) {
|
|
698
727
|
const descriptor = getAuthoringTypeConstructor(input.authoringContributions, input.call.path);
|
|
699
728
|
if (descriptor) return descriptor;
|
|
700
|
-
const
|
|
701
|
-
|
|
729
|
+
const uncomposedNamespace = checkUncomposedNamespace(input.call.path.join("."), input.composedExtensions, {
|
|
730
|
+
familyId: input.familyId,
|
|
731
|
+
targetId: input.targetId,
|
|
732
|
+
authoringContributions: input.authoringContributions
|
|
733
|
+
});
|
|
734
|
+
if (uncomposedNamespace) {
|
|
702
735
|
reportUncomposedNamespace({
|
|
703
736
|
subjectLabel: `Type constructor "${input.call.path.join(".")}"`,
|
|
704
|
-
namespace,
|
|
737
|
+
namespace: uncomposedNamespace,
|
|
705
738
|
sourceId: input.sourceId,
|
|
706
739
|
span: input.call.span,
|
|
707
740
|
diagnostics: input.diagnostics
|
|
@@ -716,10 +749,95 @@ function resolvePslTypeConstructorDescriptor(input) {
|
|
|
716
749
|
message: input.unsupportedMessage
|
|
717
750
|
});
|
|
718
751
|
}
|
|
752
|
+
/**
|
|
753
|
+
* 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.
|
|
754
|
+
*
|
|
755
|
+
* 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.
|
|
756
|
+
*/
|
|
757
|
+
function instantiatePslFieldPreset(input) {
|
|
758
|
+
const helperPath = input.call.path.join(".");
|
|
759
|
+
const args = mapPslHelperArgs({
|
|
760
|
+
args: input.call.args,
|
|
761
|
+
descriptors: input.descriptor.args ?? [],
|
|
762
|
+
helperLabel: `preset "${helperPath}"`,
|
|
763
|
+
span: input.call.span,
|
|
764
|
+
diagnostics: input.diagnostics,
|
|
765
|
+
sourceId: input.sourceId,
|
|
766
|
+
entityLabel: input.entityLabel
|
|
767
|
+
});
|
|
768
|
+
if (!args) return;
|
|
769
|
+
try {
|
|
770
|
+
validateAuthoringHelperArguments(helperPath, input.descriptor.args, args);
|
|
771
|
+
const instantiated = instantiateAuthoringFieldPreset(input.descriptor, args);
|
|
772
|
+
return {
|
|
773
|
+
descriptor: {
|
|
774
|
+
codecId: instantiated.descriptor.codecId,
|
|
775
|
+
nativeType: instantiated.descriptor.nativeType,
|
|
776
|
+
...instantiated.descriptor.typeParams !== void 0 ? { typeParams: instantiated.descriptor.typeParams } : {}
|
|
777
|
+
},
|
|
778
|
+
nullable: instantiated.nullable,
|
|
779
|
+
...instantiated.default !== void 0 ? { default: instantiated.default } : {},
|
|
780
|
+
...instantiated.executionDefaults !== void 0 ? { executionDefaults: instantiated.executionDefaults } : {},
|
|
781
|
+
id: instantiated.id,
|
|
782
|
+
unique: instantiated.unique
|
|
783
|
+
};
|
|
784
|
+
} catch (error) {
|
|
785
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
786
|
+
input.diagnostics.push({
|
|
787
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
788
|
+
message: `${input.entityLabel} preset "${helperPath}" ${message}`,
|
|
789
|
+
sourceId: input.sourceId,
|
|
790
|
+
span: input.call.span
|
|
791
|
+
});
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
719
795
|
function resolveFieldTypeDescriptor(input) {
|
|
720
796
|
if (input.field.typeConstructor) {
|
|
797
|
+
const presetDescriptor = getAuthoringFieldPreset(input.authoringContributions, input.field.typeConstructor.path);
|
|
798
|
+
if (presetDescriptor) {
|
|
799
|
+
const instantiated$1 = instantiatePslFieldPreset({
|
|
800
|
+
call: input.field.typeConstructor,
|
|
801
|
+
descriptor: presetDescriptor,
|
|
802
|
+
diagnostics: input.diagnostics,
|
|
803
|
+
sourceId: input.sourceId,
|
|
804
|
+
entityLabel: input.entityLabel
|
|
805
|
+
});
|
|
806
|
+
if (!instantiated$1) return {
|
|
807
|
+
ok: false,
|
|
808
|
+
alreadyReported: true
|
|
809
|
+
};
|
|
810
|
+
const presetContributions = {
|
|
811
|
+
nullable: instantiated$1.nullable,
|
|
812
|
+
id: instantiated$1.id,
|
|
813
|
+
unique: instantiated$1.unique,
|
|
814
|
+
...instantiated$1.default !== void 0 ? { default: instantiated$1.default } : {},
|
|
815
|
+
...instantiated$1.executionDefaults !== void 0 ? { executionDefaults: instantiated$1.executionDefaults } : {}
|
|
816
|
+
};
|
|
817
|
+
return {
|
|
818
|
+
ok: true,
|
|
819
|
+
descriptor: instantiated$1.descriptor,
|
|
820
|
+
presetContributions
|
|
821
|
+
};
|
|
822
|
+
}
|
|
721
823
|
const helperPath = input.field.typeConstructor.path.join(".");
|
|
722
|
-
const
|
|
824
|
+
const namespacePrefix = input.field.typeConstructor.path.length > 1 ? input.field.typeConstructor.path[0] : void 0;
|
|
825
|
+
const typeDescriptor = getAuthoringTypeConstructor(input.authoringContributions, input.field.typeConstructor.path);
|
|
826
|
+
if (!typeDescriptor && namespacePrefix && hasRegisteredFieldNamespace(input.authoringContributions, namespacePrefix)) {
|
|
827
|
+
reportUnknownFieldPreset({
|
|
828
|
+
entityLabel: input.entityLabel,
|
|
829
|
+
namespace: namespacePrefix,
|
|
830
|
+
helperPath,
|
|
831
|
+
sourceId: input.sourceId,
|
|
832
|
+
span: input.field.typeConstructor.span,
|
|
833
|
+
diagnostics: input.diagnostics
|
|
834
|
+
});
|
|
835
|
+
return {
|
|
836
|
+
ok: false,
|
|
837
|
+
alreadyReported: true
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
const descriptor$1 = typeDescriptor ?? resolvePslTypeConstructorDescriptor({
|
|
723
841
|
call: input.field.typeConstructor,
|
|
724
842
|
authoringContributions: input.authoringContributions,
|
|
725
843
|
composedExtensions: input.composedExtensions,
|
|
@@ -985,6 +1103,15 @@ function lowerDefaultForField(input) {
|
|
|
985
1103
|
});
|
|
986
1104
|
return {};
|
|
987
1105
|
}
|
|
1106
|
+
if (generatorDescriptor.applicableCodecIds === void 0) {
|
|
1107
|
+
input.diagnostics.push({
|
|
1108
|
+
code: "PSL_INVALID_DEFAULT_APPLICABILITY",
|
|
1109
|
+
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.`,
|
|
1110
|
+
sourceId: input.sourceId,
|
|
1111
|
+
span: expressionEntry.span
|
|
1112
|
+
});
|
|
1113
|
+
return {};
|
|
1114
|
+
}
|
|
988
1115
|
if (!generatorDescriptor.applicableCodecIds.includes(input.columnDescriptor.codecId)) {
|
|
989
1116
|
input.diagnostics.push({
|
|
990
1117
|
code: "PSL_INVALID_DEFAULT_APPLICABILITY",
|
|
@@ -994,7 +1121,7 @@ function lowerDefaultForField(input) {
|
|
|
994
1121
|
});
|
|
995
1122
|
return {};
|
|
996
1123
|
}
|
|
997
|
-
return {
|
|
1124
|
+
return { executionDefaults: { onCreate: lowered.value.generated } };
|
|
998
1125
|
}
|
|
999
1126
|
function resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors) {
|
|
1000
1127
|
if (field.typeRef && namedTypeDescriptors.has(field.typeRef)) return namedTypeDescriptors.get(field.typeRef);
|
|
@@ -1012,12 +1139,21 @@ const BUILTIN_FIELD_ATTRIBUTE_NAMES = new Set([
|
|
|
1012
1139
|
"relation",
|
|
1013
1140
|
"map"
|
|
1014
1141
|
]);
|
|
1142
|
+
const REMOVED_ATTRIBUTE_RULES = new Map([["updatedAt", {
|
|
1143
|
+
hint: "Use `temporal.updatedAt()` as a field-preset call instead.",
|
|
1144
|
+
suppressWhen: (field) => field.typeConstructor?.path[0] === "temporal"
|
|
1145
|
+
}]]);
|
|
1146
|
+
{
|
|
1147
|
+
const overlap = [...REMOVED_ATTRIBUTE_RULES.keys()].filter((name) => BUILTIN_FIELD_ATTRIBUTE_NAMES.has(name));
|
|
1148
|
+
if (overlap.length > 0) throw new Error(`BUILTIN_FIELD_ATTRIBUTE_NAMES and REMOVED_ATTRIBUTE_RULES must not overlap. Names in both: ${overlap.join(", ")}`);
|
|
1149
|
+
}
|
|
1015
1150
|
function validateFieldAttributes(input) {
|
|
1016
1151
|
for (const attribute of input.field.attributes) {
|
|
1017
1152
|
if (BUILTIN_FIELD_ATTRIBUTE_NAMES.has(attribute.name)) continue;
|
|
1018
1153
|
const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
|
|
1019
1154
|
familyId: input.familyId,
|
|
1020
|
-
targetId: input.targetId
|
|
1155
|
+
targetId: input.targetId,
|
|
1156
|
+
authoringContributions: input.authoringContributions
|
|
1021
1157
|
});
|
|
1022
1158
|
if (uncomposedNamespace) {
|
|
1023
1159
|
reportUncomposedNamespace({
|
|
@@ -1029,9 +1165,12 @@ function validateFieldAttributes(input) {
|
|
|
1029
1165
|
});
|
|
1030
1166
|
continue;
|
|
1031
1167
|
}
|
|
1168
|
+
const baseMessage = `Field "${input.model.name}.${input.field.name}" uses unsupported attribute "@${attribute.name}"`;
|
|
1169
|
+
const removedRule = REMOVED_ATTRIBUTE_RULES.get(attribute.name);
|
|
1170
|
+
const message = removedRule && !removedRule.suppressWhen(input.field) ? `${baseMessage}. ${removedRule.hint}` : baseMessage;
|
|
1032
1171
|
input.diagnostics.push({
|
|
1033
1172
|
code: "PSL_UNSUPPORTED_FIELD_ATTRIBUTE",
|
|
1034
|
-
message
|
|
1173
|
+
message,
|
|
1035
1174
|
sourceId: input.sourceId,
|
|
1036
1175
|
span: attribute.span
|
|
1037
1176
|
});
|
|
@@ -1065,21 +1204,25 @@ function collectResolvedFields(input) {
|
|
|
1065
1204
|
const { model, mapping, enumTypeDescriptors, namedTypeDescriptors, modelNames, compositeTypeNames, composedExtensions, authoringContributions, familyId, targetId, defaultFunctionRegistry, generatorDescriptorById, diagnostics, sourceId, scalarTypeDescriptors } = input;
|
|
1066
1205
|
const resolvedFields = [];
|
|
1067
1206
|
for (const field of model.fields) {
|
|
1068
|
-
|
|
1207
|
+
const isModelField = modelNames.has(field.typeName);
|
|
1208
|
+
if (field.list && isModelField) continue;
|
|
1069
1209
|
validateFieldAttributes({
|
|
1070
1210
|
model,
|
|
1071
1211
|
field,
|
|
1072
1212
|
composedExtensions,
|
|
1213
|
+
authoringContributions,
|
|
1073
1214
|
diagnostics,
|
|
1074
1215
|
sourceId,
|
|
1075
1216
|
familyId,
|
|
1076
1217
|
targetId
|
|
1077
1218
|
});
|
|
1078
|
-
|
|
1219
|
+
const relationAttribute = getAttribute(field.attributes, "relation");
|
|
1220
|
+
if (isModelField && relationAttribute) continue;
|
|
1079
1221
|
const isValueObjectField = compositeTypeNames.has(field.typeName);
|
|
1080
1222
|
const isListField = field.list;
|
|
1081
1223
|
let descriptor;
|
|
1082
1224
|
let scalarCodecId;
|
|
1225
|
+
let presetContributions;
|
|
1083
1226
|
const resolveInput = {
|
|
1084
1227
|
field,
|
|
1085
1228
|
enumTypeDescriptors,
|
|
@@ -1105,6 +1248,15 @@ function collectResolvedFields(input) {
|
|
|
1105
1248
|
});
|
|
1106
1249
|
continue;
|
|
1107
1250
|
}
|
|
1251
|
+
if (resolved.presetContributions) {
|
|
1252
|
+
diagnostics.push({
|
|
1253
|
+
code: "PSL_PRESET_NOT_LIST",
|
|
1254
|
+
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.`,
|
|
1255
|
+
sourceId,
|
|
1256
|
+
span: field.span
|
|
1257
|
+
});
|
|
1258
|
+
continue;
|
|
1259
|
+
}
|
|
1108
1260
|
scalarCodecId = resolved.descriptor.codecId;
|
|
1109
1261
|
descriptor = scalarTypeDescriptors.get("Json");
|
|
1110
1262
|
} else {
|
|
@@ -1119,9 +1271,28 @@ function collectResolvedFields(input) {
|
|
|
1119
1271
|
continue;
|
|
1120
1272
|
}
|
|
1121
1273
|
descriptor = resolved.descriptor;
|
|
1274
|
+
presetContributions = resolved.presetContributions;
|
|
1122
1275
|
}
|
|
1123
1276
|
if (!descriptor) continue;
|
|
1277
|
+
if (presetContributions && field.optional) {
|
|
1278
|
+
diagnostics.push({
|
|
1279
|
+
code: "PSL_PRESET_NOT_OPTIONAL",
|
|
1280
|
+
message: `Field "${model.name}.${field.name}" uses a field-preset call and cannot be optional. Remove "?" or use a different field type.`,
|
|
1281
|
+
sourceId,
|
|
1282
|
+
span: field.span
|
|
1283
|
+
});
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
1124
1286
|
const defaultAttribute = getAttribute(field.attributes, "default");
|
|
1287
|
+
if (presetContributions && defaultAttribute) {
|
|
1288
|
+
diagnostics.push({
|
|
1289
|
+
code: "PSL_PRESET_AND_DEFAULT_CONFLICT",
|
|
1290
|
+
message: `Field "${model.name}.${field.name}" uses a field-preset call and cannot also declare @default(...). The preset already specifies the default value.`,
|
|
1291
|
+
sourceId,
|
|
1292
|
+
span: defaultAttribute.span
|
|
1293
|
+
});
|
|
1294
|
+
continue;
|
|
1295
|
+
}
|
|
1125
1296
|
const loweredDefault = defaultAttribute ? lowerDefaultForField({
|
|
1126
1297
|
modelName: model.name,
|
|
1127
1298
|
fieldName: field.name,
|
|
@@ -1132,8 +1303,9 @@ function collectResolvedFields(input) {
|
|
|
1132
1303
|
defaultFunctionRegistry,
|
|
1133
1304
|
diagnostics
|
|
1134
1305
|
}) : {};
|
|
1135
|
-
|
|
1136
|
-
|
|
1306
|
+
const loweredOnCreate = loweredDefault.executionDefaults?.onCreate;
|
|
1307
|
+
if (field.optional && loweredOnCreate) {
|
|
1308
|
+
const generatorDescription = loweredOnCreate.kind === "generator" ? `"${loweredOnCreate.id}"` : "for this field";
|
|
1137
1309
|
diagnostics.push({
|
|
1138
1310
|
code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
|
|
1139
1311
|
message: `Field "${model.name}.${field.name}" cannot be optional when using execution default ${generatorDescription}. Remove "?" or use a storage default.`,
|
|
@@ -1142,8 +1314,8 @@ function collectResolvedFields(input) {
|
|
|
1142
1314
|
});
|
|
1143
1315
|
continue;
|
|
1144
1316
|
}
|
|
1145
|
-
if (
|
|
1146
|
-
const generatedDescriptor = generatorDescriptorById.get(
|
|
1317
|
+
if (loweredOnCreate) {
|
|
1318
|
+
const generatedDescriptor = generatorDescriptorById.get(loweredOnCreate.id)?.resolveGeneratedColumnDescriptor?.({ generated: loweredOnCreate });
|
|
1147
1319
|
if (generatedDescriptor) descriptor = generatedDescriptor;
|
|
1148
1320
|
}
|
|
1149
1321
|
const mappedColumnName = mapping.fieldColumns.get(field.name) ?? field.name;
|
|
@@ -1153,14 +1325,35 @@ function collectResolvedFields(input) {
|
|
|
1153
1325
|
sourceId,
|
|
1154
1326
|
diagnostics
|
|
1155
1327
|
});
|
|
1328
|
+
let isIdField = Boolean(idAttribute);
|
|
1329
|
+
if (idAttribute && field.optional) {
|
|
1330
|
+
diagnostics.push({
|
|
1331
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1332
|
+
message: `Field "${model.name}.${field.name}" @id cannot be optional; primary key columns must be NOT NULL`,
|
|
1333
|
+
sourceId,
|
|
1334
|
+
span: idAttribute.span
|
|
1335
|
+
});
|
|
1336
|
+
isIdField = false;
|
|
1337
|
+
}
|
|
1338
|
+
if (presetContributions && idAttribute && !presetContributions.id) {
|
|
1339
|
+
diagnostics.push({
|
|
1340
|
+
code: "PSL_PRESET_AND_ID_CONFLICT",
|
|
1341
|
+
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.`,
|
|
1342
|
+
sourceId,
|
|
1343
|
+
span: idAttribute.span
|
|
1344
|
+
});
|
|
1345
|
+
continue;
|
|
1346
|
+
}
|
|
1347
|
+
const fieldExecutionDefaults = presetContributions?.executionDefaults ?? loweredDefault.executionDefaults;
|
|
1348
|
+
const fieldDefaultValue = presetContributions?.default ?? loweredDefault.defaultValue;
|
|
1156
1349
|
resolvedFields.push({
|
|
1157
1350
|
field,
|
|
1158
1351
|
columnName: mappedColumnName,
|
|
1159
1352
|
descriptor,
|
|
1160
|
-
...ifDefined("defaultValue",
|
|
1161
|
-
...ifDefined("
|
|
1162
|
-
isId: Boolean(
|
|
1163
|
-
isUnique: Boolean(uniqueAttribute),
|
|
1353
|
+
...ifDefined("defaultValue", fieldDefaultValue),
|
|
1354
|
+
...ifDefined("executionDefaults", fieldExecutionDefaults),
|
|
1355
|
+
isId: isIdField || Boolean(presetContributions?.id),
|
|
1356
|
+
isUnique: Boolean(uniqueAttribute) || Boolean(presetContributions?.unique),
|
|
1164
1357
|
...ifDefined("idName", idName),
|
|
1165
1358
|
...ifDefined("uniqueName", uniqueName),
|
|
1166
1359
|
...ifDefined("many", isListField ? true : void 0),
|
|
@@ -1418,7 +1611,8 @@ function validateNavigationListFieldAttributes(input) {
|
|
|
1418
1611
|
if (attribute.name === "relation") continue;
|
|
1419
1612
|
const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
|
|
1420
1613
|
familyId: input.familyId,
|
|
1421
|
-
targetId: input.targetId
|
|
1614
|
+
targetId: input.targetId,
|
|
1615
|
+
authoringContributions: input.authoringContributions
|
|
1422
1616
|
});
|
|
1423
1617
|
if (uncomposedNamespace) {
|
|
1424
1618
|
reportUncomposedNamespace({
|
|
@@ -1531,7 +1725,8 @@ function validateNamedTypeAttributes(input) {
|
|
|
1531
1725
|
if (input.allowDbNativeType && attribute.name.startsWith("db.")) continue;
|
|
1532
1726
|
const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
|
|
1533
1727
|
familyId: input.familyId,
|
|
1534
|
-
targetId: input.targetId
|
|
1728
|
+
targetId: input.targetId,
|
|
1729
|
+
authoringContributions: input.authoringContributions
|
|
1535
1730
|
});
|
|
1536
1731
|
if (uncomposedNamespace) {
|
|
1537
1732
|
reportUncomposedNamespace({
|
|
@@ -1567,6 +1762,7 @@ function resolveNamedTypeDeclarations(input) {
|
|
|
1567
1762
|
sourceId: input.sourceId,
|
|
1568
1763
|
diagnostics: input.diagnostics,
|
|
1569
1764
|
composedExtensions: input.composedExtensions,
|
|
1765
|
+
authoringContributions: input.authoringContributions,
|
|
1570
1766
|
allowDbNativeType: false,
|
|
1571
1767
|
familyId: input.familyId,
|
|
1572
1768
|
targetId: input.targetId
|
|
@@ -1626,6 +1822,7 @@ function resolveNamedTypeDeclarations(input) {
|
|
|
1626
1822
|
sourceId: input.sourceId,
|
|
1627
1823
|
diagnostics: input.diagnostics,
|
|
1628
1824
|
composedExtensions: input.composedExtensions,
|
|
1825
|
+
authoringContributions: input.authoringContributions,
|
|
1629
1826
|
allowDbNativeType: true,
|
|
1630
1827
|
familyId: input.familyId,
|
|
1631
1828
|
targetId: input.targetId
|
|
@@ -1682,16 +1879,20 @@ function buildModelNodeFromPsl(input) {
|
|
|
1682
1879
|
sourceId,
|
|
1683
1880
|
scalarTypeDescriptors: input.scalarTypeDescriptors
|
|
1684
1881
|
});
|
|
1685
|
-
const
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
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`,
|
|
1882
|
+
const inlineIdFields = resolvedFields.filter((field) => field.isId);
|
|
1883
|
+
if (inlineIdFields.length > 1) diagnostics.push({
|
|
1884
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1885
|
+
message: `Model "${model.name}" cannot declare inline @id on multiple fields; use model-level @@id([...]) for composite identity`,
|
|
1692
1886
|
sourceId,
|
|
1693
1887
|
span: model.span
|
|
1694
1888
|
});
|
|
1889
|
+
const singleInlineIdField = inlineIdFields.length === 1 ? inlineIdFields[0] : void 0;
|
|
1890
|
+
let primaryKey = singleInlineIdField ? {
|
|
1891
|
+
columns: [singleInlineIdField.columnName],
|
|
1892
|
+
...ifDefined("name", singleInlineIdField.idName)
|
|
1893
|
+
} : void 0;
|
|
1894
|
+
const hasInlinePrimaryKey = primaryKey !== void 0;
|
|
1895
|
+
let blockPrimaryKeyDeclared = false;
|
|
1695
1896
|
const resultBackrelationCandidates = [];
|
|
1696
1897
|
for (const field of model.fields) {
|
|
1697
1898
|
if (!field.list || !input.modelNames.has(field.typeName)) continue;
|
|
@@ -1700,6 +1901,7 @@ function buildModelNodeFromPsl(input) {
|
|
|
1700
1901
|
field,
|
|
1701
1902
|
sourceId,
|
|
1702
1903
|
composedExtensions: input.composedExtensions,
|
|
1904
|
+
authoringContributions: input.authoringContributions,
|
|
1703
1905
|
diagnostics,
|
|
1704
1906
|
familyId: input.familyId,
|
|
1705
1907
|
targetId: input.targetId
|
|
@@ -1757,15 +1959,98 @@ function buildModelNodeFromPsl(input) {
|
|
|
1757
1959
|
for (const modelAttribute of model.attributes) {
|
|
1758
1960
|
if (modelAttribute.name === "map") continue;
|
|
1759
1961
|
if (modelAttribute.name === "discriminator" || modelAttribute.name === "base") continue;
|
|
1962
|
+
const attributeLabel = `Model "${model.name}" @@${modelAttribute.name}`;
|
|
1963
|
+
if (modelAttribute.name === "id") {
|
|
1964
|
+
if (blockPrimaryKeyDeclared) {
|
|
1965
|
+
diagnostics.push({
|
|
1966
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1967
|
+
message: `Model "${model.name}" declares @@id more than once`,
|
|
1968
|
+
sourceId,
|
|
1969
|
+
span: modelAttribute.span
|
|
1970
|
+
});
|
|
1971
|
+
continue;
|
|
1972
|
+
}
|
|
1973
|
+
if (hasInlinePrimaryKey) {
|
|
1974
|
+
diagnostics.push({
|
|
1975
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1976
|
+
message: `Model "${model.name}" cannot declare both field-level @id and model-level @@id`,
|
|
1977
|
+
sourceId,
|
|
1978
|
+
span: modelAttribute.span
|
|
1979
|
+
});
|
|
1980
|
+
blockPrimaryKeyDeclared = true;
|
|
1981
|
+
continue;
|
|
1982
|
+
}
|
|
1983
|
+
const fieldNames = parseAttributeFieldList({
|
|
1984
|
+
attribute: modelAttribute,
|
|
1985
|
+
sourceId,
|
|
1986
|
+
diagnostics,
|
|
1987
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1988
|
+
entityLabel: attributeLabel
|
|
1989
|
+
});
|
|
1990
|
+
if (!fieldNames) continue;
|
|
1991
|
+
const duplicateFieldName = findDuplicateFieldName(fieldNames);
|
|
1992
|
+
if (duplicateFieldName !== void 0) {
|
|
1993
|
+
diagnostics.push({
|
|
1994
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1995
|
+
message: `${attributeLabel} list contains duplicate field "${duplicateFieldName}"`,
|
|
1996
|
+
sourceId,
|
|
1997
|
+
span: modelAttribute.span
|
|
1998
|
+
});
|
|
1999
|
+
continue;
|
|
2000
|
+
}
|
|
2001
|
+
const nullableFieldName = fieldNames.find((name) => model.fields.find((f) => f.name === name)?.optional === true);
|
|
2002
|
+
if (nullableFieldName !== void 0) {
|
|
2003
|
+
diagnostics.push({
|
|
2004
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
2005
|
+
message: `${attributeLabel} cannot include optional field "${nullableFieldName}"; primary key columns must be NOT NULL`,
|
|
2006
|
+
sourceId,
|
|
2007
|
+
span: modelAttribute.span
|
|
2008
|
+
});
|
|
2009
|
+
continue;
|
|
2010
|
+
}
|
|
2011
|
+
const columnNames = mapFieldNamesToColumns({
|
|
2012
|
+
modelName: model.name,
|
|
2013
|
+
fieldNames,
|
|
2014
|
+
mapping,
|
|
2015
|
+
sourceId,
|
|
2016
|
+
diagnostics,
|
|
2017
|
+
span: modelAttribute.span,
|
|
2018
|
+
entityLabel: attributeLabel
|
|
2019
|
+
});
|
|
2020
|
+
if (!columnNames) continue;
|
|
2021
|
+
primaryKey = {
|
|
2022
|
+
columns: columnNames,
|
|
2023
|
+
...ifDefined("name", parseConstraintMapArgument({
|
|
2024
|
+
attribute: modelAttribute,
|
|
2025
|
+
sourceId,
|
|
2026
|
+
diagnostics,
|
|
2027
|
+
entityLabel: attributeLabel,
|
|
2028
|
+
span: modelAttribute.span,
|
|
2029
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT"
|
|
2030
|
+
}))
|
|
2031
|
+
};
|
|
2032
|
+
blockPrimaryKeyDeclared = true;
|
|
2033
|
+
continue;
|
|
2034
|
+
}
|
|
1760
2035
|
if (modelAttribute.name === "unique" || modelAttribute.name === "index") {
|
|
1761
2036
|
const fieldNames = parseAttributeFieldList({
|
|
1762
2037
|
attribute: modelAttribute,
|
|
1763
2038
|
sourceId,
|
|
1764
2039
|
diagnostics,
|
|
1765
2040
|
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1766
|
-
|
|
2041
|
+
entityLabel: attributeLabel
|
|
1767
2042
|
});
|
|
1768
2043
|
if (!fieldNames) continue;
|
|
2044
|
+
const duplicateFieldName = findDuplicateFieldName(fieldNames);
|
|
2045
|
+
if (duplicateFieldName !== void 0) {
|
|
2046
|
+
diagnostics.push({
|
|
2047
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
2048
|
+
message: `${attributeLabel} list contains duplicate field "${duplicateFieldName}"`,
|
|
2049
|
+
sourceId,
|
|
2050
|
+
span: modelAttribute.span
|
|
2051
|
+
});
|
|
2052
|
+
continue;
|
|
2053
|
+
}
|
|
1769
2054
|
const columnNames = mapFieldNamesToColumns({
|
|
1770
2055
|
modelName: model.name,
|
|
1771
2056
|
fieldNames,
|
|
@@ -1773,14 +2058,14 @@ function buildModelNodeFromPsl(input) {
|
|
|
1773
2058
|
sourceId,
|
|
1774
2059
|
diagnostics,
|
|
1775
2060
|
span: modelAttribute.span,
|
|
1776
|
-
|
|
2061
|
+
entityLabel: attributeLabel
|
|
1777
2062
|
});
|
|
1778
2063
|
if (!columnNames) continue;
|
|
1779
2064
|
const constraintName = parseConstraintMapArgument({
|
|
1780
2065
|
attribute: modelAttribute,
|
|
1781
2066
|
sourceId,
|
|
1782
2067
|
diagnostics,
|
|
1783
|
-
entityLabel:
|
|
2068
|
+
entityLabel: attributeLabel,
|
|
1784
2069
|
span: modelAttribute.span,
|
|
1785
2070
|
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT"
|
|
1786
2071
|
});
|
|
@@ -1796,7 +2081,8 @@ function buildModelNodeFromPsl(input) {
|
|
|
1796
2081
|
}
|
|
1797
2082
|
const uncomposedNamespace = checkUncomposedNamespace(modelAttribute.name, input.composedExtensions, {
|
|
1798
2083
|
familyId: input.familyId,
|
|
1799
|
-
targetId: input.targetId
|
|
2084
|
+
targetId: input.targetId,
|
|
2085
|
+
authoringContributions: input.authoringContributions
|
|
1800
2086
|
});
|
|
1801
2087
|
if (uncomposedNamespace) {
|
|
1802
2088
|
reportUncomposedNamespace({
|
|
@@ -1861,7 +2147,7 @@ function buildModelNodeFromPsl(input) {
|
|
|
1861
2147
|
sourceId,
|
|
1862
2148
|
diagnostics,
|
|
1863
2149
|
span: relationAttribute.relation.span,
|
|
1864
|
-
|
|
2150
|
+
entityLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
|
|
1865
2151
|
});
|
|
1866
2152
|
if (!localColumns) continue;
|
|
1867
2153
|
const referencedColumns = mapFieldNamesToColumns({
|
|
@@ -1871,7 +2157,7 @@ function buildModelNodeFromPsl(input) {
|
|
|
1871
2157
|
sourceId,
|
|
1872
2158
|
diagnostics,
|
|
1873
2159
|
span: relationAttribute.relation.span,
|
|
1874
|
-
|
|
2160
|
+
entityLabel: `Relation field "${model.name}.${relationAttribute.field.name}"`
|
|
1875
2161
|
});
|
|
1876
2162
|
if (!referencedColumns) continue;
|
|
1877
2163
|
if (localColumns.length !== referencedColumns.length) {
|
|
@@ -1933,12 +2219,9 @@ function buildModelNodeFromPsl(input) {
|
|
|
1933
2219
|
descriptor: resolvedField.descriptor,
|
|
1934
2220
|
nullable: resolvedField.field.optional,
|
|
1935
2221
|
...ifDefined("default", resolvedField.defaultValue),
|
|
1936
|
-
...ifDefined("
|
|
2222
|
+
...ifDefined("executionDefaults", resolvedField.executionDefaults)
|
|
1937
2223
|
})),
|
|
1938
|
-
...
|
|
1939
|
-
columns: primaryKeyColumns,
|
|
1940
|
-
...ifDefined("name", primaryKeyName)
|
|
1941
|
-
} } : {},
|
|
2224
|
+
...ifDefined("id", primaryKey),
|
|
1942
2225
|
...uniqueConstraints.length > 0 ? { uniques: uniqueConstraints } : {},
|
|
1943
2226
|
...indexNodes.length > 0 ? { indexes: indexNodes } : {},
|
|
1944
2227
|
...foreignKeyNodes.length > 0 ? { foreignKeys: foreignKeyNodes } : {}
|
|
@@ -2340,4 +2623,4 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
2340
2623
|
|
|
2341
2624
|
//#endregion
|
|
2342
2625
|
export { interpretPslDocumentToSqlContract as t };
|
|
2343
|
-
//# sourceMappingURL=interpreter-
|
|
2626
|
+
//# sourceMappingURL=interpreter-g4FDWENY.mjs.map
|