@prisma-next/sql-contract-psl 0.5.0-dev.51 → 0.5.0-dev.52
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 +2 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{interpreter-iFCRN9nb.mjs → interpreter-DJrrH8Ee.mjs} +234 -27
- package/dist/interpreter-DJrrH8Ee.mjs.map +1 -0
- package/dist/provider.d.mts +2 -2
- package/dist/provider.d.mts.map +1 -1
- package/dist/provider.mjs +1 -1
- package/dist/provider.mjs.map +1 -1
- package/package.json +11 -11
- package/src/interpreter.ts +17 -8
- package/src/provider.ts +2 -2
- package/src/psl-column-resolution.ts +253 -28
- package/src/psl-field-resolution.ts +128 -17
- package/src/psl-relation-resolution.ts +3 -0
- package/dist/interpreter-iFCRN9nb.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ This keeps core/CLI source-agnostic while giving PSL-first SQL users a one-line
|
|
|
15
15
|
|
|
16
16
|
- Interpret `ParsePslDocumentResult` into SQL `Contract`
|
|
17
17
|
- Interpret generic PSL attributes into SQL contract semantics (`@id`, `@unique`, `@default`, `@relation`, `@map`, `@@map`)
|
|
18
|
+
- Interpret SQL timestamp semantics: `DateTime @default(now())` (or the equivalent `temporal.createdAt()` field-preset call) as a storage default, and `temporal.updatedAt()` as an execution mutation default
|
|
18
19
|
- Lower shared constructor expressions in both `types {}` blocks and inline field positions (for example `ShortName = sql.String(length: 35)` and `embedding pgvector.Vector(length: 1536)?`)
|
|
19
20
|
- Lower supported default functions through composed registry inputs
|
|
20
21
|
- Support selected Postgres native-type attributes on named types for brownfield round-trips (`@db.Char`, `@db.VarChar`, `@db.Numeric`, `@db.Uuid`, `@db.SmallInt`, `@db.Real`, `@db.Timestamp`, `@db.Timestamptz`, `@db.Date`, `@db.Time`, `@db.Timetz`, `@db.Json`)
|
|
@@ -42,7 +43,7 @@ The **pure interpreter entrypoint** specifically excludes:
|
|
|
42
43
|
- Artifact emission (`contract.json`, `contract.d.ts`) and hashing
|
|
43
44
|
- CLI or ControlClient orchestration
|
|
44
45
|
|
|
45
|
-
Current scope is SQL
|
|
46
|
+
Current scope is SQL target-specific: callers pass scalar descriptors and target context assembled for the active SQL target.
|
|
46
47
|
|
|
47
48
|
Unsupported PSL constructs in v1 (strict errors):
|
|
48
49
|
|
|
@@ -63,6 +64,13 @@ Supported `@default(...)` surface in v1 when composed contributors provide handl
|
|
|
63
64
|
- Explicitly unsupported in v1: `cuid()` (diagnostic suggests `cuid(2)`)
|
|
64
65
|
- `dbgenerated("...")` preserves the parsed PSL string-literal contents as-is (escaped sequences are not normalized in v1).
|
|
65
66
|
|
|
67
|
+
Supported timestamp authoring surface:
|
|
68
|
+
|
|
69
|
+
- `createdAt DateTime @default(now())` and `createdAt temporal.createdAt()` both lower to the target storage default and do not create an execution mutation default.
|
|
70
|
+
- `updatedAt temporal.updatedAt()` lowers to `timestampNow` on create and on non-empty update mutations. This is application-side because update-time semantics are mutation-aware, not a database trigger.
|
|
71
|
+
- The Prisma-flavored `@updatedAt` attribute is not supported; references produce `PSL_UNSUPPORTED_FIELD_ATTRIBUTE` with a migration hint pointing at `temporal.updatedAt()`. The hint is suppressed when the field already declares any `temporal.*` preset.
|
|
72
|
+
- `@createdAt` is not supported as a PSL alias.
|
|
73
|
+
|
|
66
74
|
## Public API
|
|
67
75
|
|
|
68
76
|
- `@prisma-next/sql-contract-psl`
|
package/dist/index.d.mts
CHANGED
|
@@ -17,10 +17,10 @@ type ColumnDescriptor = {
|
|
|
17
17
|
//#region src/interpreter.d.ts
|
|
18
18
|
interface InterpretPslDocumentToSqlContractInput {
|
|
19
19
|
readonly document: ParsePslDocumentResult;
|
|
20
|
-
readonly target: TargetPackRef<'sql',
|
|
20
|
+
readonly target: TargetPackRef<'sql', string>;
|
|
21
21
|
readonly scalarTypeDescriptors: ReadonlyMap<string, ColumnDescriptor>;
|
|
22
22
|
readonly composedExtensionPacks?: readonly string[];
|
|
23
|
-
readonly composedExtensionPackRefs?: readonly ExtensionPackRef<'sql',
|
|
23
|
+
readonly composedExtensionPackRefs?: readonly ExtensionPackRef<'sql', string>[];
|
|
24
24
|
readonly controlMutationDefaults?: ControlMutationDefaults$1;
|
|
25
25
|
readonly authoringContributions?: AuthoringContributions;
|
|
26
26
|
}
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/psl-column-resolution.ts","../src/interpreter.ts"],"sourcesContent":[],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/psl-column-resolution.ts","../src/interpreter.ts"],"sourcesContent":[],"mappings":";;;;;;;;;KAuCY,gBAAA;;;;EAAA,SAAA,UAAgB,CAAA,EAIJ,MAJI,CAAA,MAIJ,EAAM,OAAA,CAAA;;;;UCkCb,sCAAA;EDtCL,SAAA,QAAA,ECuCS,sBDnCS;mBCoCX;kCACe,oBAAoB;;EAHrC,SAAA,yBAAA,CAAA,EAAA,SAK+B,gBALO,CAAA,KAAA,EAAA,MAAA,CAAA,EAAA;EAClC,SAAA,uBAAA,CAAA,EAKgB,yBALhB;EACF,SAAA,sBAAA,CAAA,EAKiB,sBALjB;;AACe,iBAggClB,iCAAA,CAhgCkB,KAAA,EAigCzB,sCAjgCyB,CAAA,EAkgC/B,MAlgC+B,CAkgCxB,QAlgCwB,EAkgCd,yBAlgCc,CAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -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";
|
|
@@ -620,6 +620,26 @@ function getAuthoringTypeConstructor(contributions, path) {
|
|
|
620
620
|
return isAuthoringTypeConstructorDescriptor(current) ? current : void 0;
|
|
621
621
|
}
|
|
622
622
|
/**
|
|
623
|
+
* Walks `authoringContributions.field` segment-by-segment and returns the
|
|
624
|
+
* field-preset descriptor at the resolved path, or `undefined` if no descriptor
|
|
625
|
+
* is registered.
|
|
626
|
+
*
|
|
627
|
+
* Symmetric with `getAuthoringTypeConstructor`. Field presets are strictly
|
|
628
|
+
* richer than type constructors — they can contribute `default` /
|
|
629
|
+
* `executionDefaults` / `id` / `unique` / `nullable` in addition to the
|
|
630
|
+
* `codecId` / `nativeType` / `typeParams` triple. PSL resolution tries field
|
|
631
|
+
* presets first, then falls back to type constructors on miss (see
|
|
632
|
+
* `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
|
+
/**
|
|
623
643
|
* Returns the namespace prefix of `attributeName` if it references an
|
|
624
644
|
* unrecognized extension namespace, otherwise `undefined`. A namespace is
|
|
625
645
|
* considered recognized when it is:
|
|
@@ -627,17 +647,19 @@ function getAuthoringTypeConstructor(contributions, path) {
|
|
|
627
647
|
* - `db` (native-type spec, always allowed),
|
|
628
648
|
* - the active family id (e.g. `sql`),
|
|
629
649
|
* - the active target id (e.g. `postgres`),
|
|
650
|
+
* - a registered field-preset namespace (e.g. `temporal`),
|
|
630
651
|
* - present in `composedExtensions`.
|
|
631
652
|
*
|
|
632
|
-
* Family/target namespaces are exempted so that e.g. `@sql.foo`
|
|
633
|
-
* PSL_UNSUPPORTED_*_ATTRIBUTE (the attribute isn't defined)
|
|
634
|
-
* PSL_EXTENSION_NAMESPACE_NOT_COMPOSED (the namespace is already
|
|
653
|
+
* Family/target/field-preset namespaces are exempted so that e.g. `@sql.foo`
|
|
654
|
+
* surfaces as PSL_UNSUPPORTED_*_ATTRIBUTE (the attribute isn't defined)
|
|
655
|
+
* rather than PSL_EXTENSION_NAMESPACE_NOT_COMPOSED (the namespace is already
|
|
656
|
+
* composed).
|
|
635
657
|
*/
|
|
636
658
|
function checkUncomposedNamespace(attributeName, composedExtensions, context) {
|
|
637
659
|
const dotIndex = attributeName.indexOf(".");
|
|
638
660
|
if (dotIndex <= 0 || dotIndex === attributeName.length - 1) return;
|
|
639
661
|
const namespace = attributeName.slice(0, dotIndex);
|
|
640
|
-
if (namespace === "db" || namespace === context?.familyId || namespace === context?.targetId || composedExtensions.has(namespace)) return;
|
|
662
|
+
if (namespace === "db" || namespace === context?.familyId || namespace === context?.targetId || hasRegisteredFieldNamespace(context?.authoringContributions, namespace) || composedExtensions.has(namespace)) return;
|
|
641
663
|
return namespace;
|
|
642
664
|
}
|
|
643
665
|
/**
|
|
@@ -660,6 +682,24 @@ function reportUncomposedNamespace(input) {
|
|
|
660
682
|
}
|
|
661
683
|
});
|
|
662
684
|
}
|
|
685
|
+
/**
|
|
686
|
+
* Pushes the canonical `PSL_UNKNOWN_FIELD_PRESET` diagnostic when a typoed
|
|
687
|
+
* preset name is referenced inside a registered field-preset namespace. The
|
|
688
|
+
* `data` payload exposes the namespace and full helper path so machine
|
|
689
|
+
* consumers (agents, IDE extensions) don't have to parse the prose.
|
|
690
|
+
*/
|
|
691
|
+
function reportUnknownFieldPreset(input) {
|
|
692
|
+
input.diagnostics.push({
|
|
693
|
+
code: "PSL_UNKNOWN_FIELD_PRESET",
|
|
694
|
+
message: `${input.entityLabel} references unknown field preset "${input.helperPath}". Check the spelling against the available presets in the "${input.namespace}" namespace.`,
|
|
695
|
+
sourceId: input.sourceId,
|
|
696
|
+
span: input.span,
|
|
697
|
+
data: {
|
|
698
|
+
namespace: input.namespace,
|
|
699
|
+
helperPath: input.helperPath
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
}
|
|
663
703
|
function instantiatePslTypeConstructor(input) {
|
|
664
704
|
const helperPath = input.call.path.join(".");
|
|
665
705
|
const args = mapPslHelperArgs({
|
|
@@ -697,11 +737,15 @@ function pushUnsupportedTypeConstructorDiagnostic(input) {
|
|
|
697
737
|
function resolvePslTypeConstructorDescriptor(input) {
|
|
698
738
|
const descriptor = getAuthoringTypeConstructor(input.authoringContributions, input.call.path);
|
|
699
739
|
if (descriptor) return descriptor;
|
|
700
|
-
const
|
|
701
|
-
|
|
740
|
+
const uncomposedNamespace = checkUncomposedNamespace(input.call.path.join("."), input.composedExtensions, {
|
|
741
|
+
familyId: input.familyId,
|
|
742
|
+
targetId: input.targetId,
|
|
743
|
+
authoringContributions: input.authoringContributions
|
|
744
|
+
});
|
|
745
|
+
if (uncomposedNamespace) {
|
|
702
746
|
reportUncomposedNamespace({
|
|
703
747
|
subjectLabel: `Type constructor "${input.call.path.join(".")}"`,
|
|
704
|
-
namespace,
|
|
748
|
+
namespace: uncomposedNamespace,
|
|
705
749
|
sourceId: input.sourceId,
|
|
706
750
|
span: input.call.span,
|
|
707
751
|
diagnostics: input.diagnostics
|
|
@@ -716,10 +760,102 @@ function resolvePslTypeConstructorDescriptor(input) {
|
|
|
716
760
|
message: input.unsupportedMessage
|
|
717
761
|
});
|
|
718
762
|
}
|
|
763
|
+
/**
|
|
764
|
+
* Instantiates a field-preset call against its descriptor, coercing PSL AST
|
|
765
|
+
* arguments into the descriptor's typed argument shape and returning the
|
|
766
|
+
* preset's full set of contract contributions.
|
|
767
|
+
*
|
|
768
|
+
* Symmetric with `instantiatePslTypeConstructor` but richer: a field preset
|
|
769
|
+
* can contribute `default`, `executionDefaults`, `id`, `unique`, and
|
|
770
|
+
* `nullable` in addition to the storage-type triple. PSL → typed-args
|
|
771
|
+
* coercion happens here (via `mapPslHelperArgs`) so that
|
|
772
|
+
* `instantiateAuthoringFieldPreset` itself stays typed-input-only and TS
|
|
773
|
+
* keeps its zero-runtime-validation cost.
|
|
774
|
+
*/
|
|
775
|
+
function instantiatePslFieldPreset(input) {
|
|
776
|
+
const helperPath = input.call.path.join(".");
|
|
777
|
+
const args = mapPslHelperArgs({
|
|
778
|
+
args: input.call.args,
|
|
779
|
+
descriptors: input.descriptor.args ?? [],
|
|
780
|
+
helperLabel: `preset "${helperPath}"`,
|
|
781
|
+
span: input.call.span,
|
|
782
|
+
diagnostics: input.diagnostics,
|
|
783
|
+
sourceId: input.sourceId,
|
|
784
|
+
entityLabel: input.entityLabel
|
|
785
|
+
});
|
|
786
|
+
if (!args) return;
|
|
787
|
+
try {
|
|
788
|
+
validateAuthoringHelperArguments(helperPath, input.descriptor.args, args);
|
|
789
|
+
const instantiated = instantiateAuthoringFieldPreset(input.descriptor, args);
|
|
790
|
+
return {
|
|
791
|
+
descriptor: {
|
|
792
|
+
codecId: instantiated.descriptor.codecId,
|
|
793
|
+
nativeType: instantiated.descriptor.nativeType,
|
|
794
|
+
...instantiated.descriptor.typeParams !== void 0 ? { typeParams: instantiated.descriptor.typeParams } : {}
|
|
795
|
+
},
|
|
796
|
+
nullable: instantiated.nullable,
|
|
797
|
+
...instantiated.default !== void 0 ? { default: instantiated.default } : {},
|
|
798
|
+
...instantiated.executionDefaults !== void 0 ? { executionDefaults: instantiated.executionDefaults } : {},
|
|
799
|
+
id: instantiated.id,
|
|
800
|
+
unique: instantiated.unique
|
|
801
|
+
};
|
|
802
|
+
} catch (error) {
|
|
803
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
804
|
+
input.diagnostics.push({
|
|
805
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
806
|
+
message: `${input.entityLabel} preset "${helperPath}" ${message}`,
|
|
807
|
+
sourceId: input.sourceId,
|
|
808
|
+
span: input.call.span
|
|
809
|
+
});
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
719
813
|
function resolveFieldTypeDescriptor(input) {
|
|
720
814
|
if (input.field.typeConstructor) {
|
|
815
|
+
const presetDescriptor = getAuthoringFieldPreset(input.authoringContributions, input.field.typeConstructor.path);
|
|
816
|
+
if (presetDescriptor) {
|
|
817
|
+
const instantiated$1 = instantiatePslFieldPreset({
|
|
818
|
+
call: input.field.typeConstructor,
|
|
819
|
+
descriptor: presetDescriptor,
|
|
820
|
+
diagnostics: input.diagnostics,
|
|
821
|
+
sourceId: input.sourceId,
|
|
822
|
+
entityLabel: input.entityLabel
|
|
823
|
+
});
|
|
824
|
+
if (!instantiated$1) return {
|
|
825
|
+
ok: false,
|
|
826
|
+
alreadyReported: true
|
|
827
|
+
};
|
|
828
|
+
const presetContributions = {
|
|
829
|
+
nullable: instantiated$1.nullable,
|
|
830
|
+
id: instantiated$1.id,
|
|
831
|
+
unique: instantiated$1.unique,
|
|
832
|
+
...instantiated$1.default !== void 0 ? { default: instantiated$1.default } : {},
|
|
833
|
+
...instantiated$1.executionDefaults !== void 0 ? { executionDefaults: instantiated$1.executionDefaults } : {}
|
|
834
|
+
};
|
|
835
|
+
return {
|
|
836
|
+
ok: true,
|
|
837
|
+
descriptor: instantiated$1.descriptor,
|
|
838
|
+
presetContributions
|
|
839
|
+
};
|
|
840
|
+
}
|
|
721
841
|
const helperPath = input.field.typeConstructor.path.join(".");
|
|
722
|
-
const
|
|
842
|
+
const namespacePrefix = input.field.typeConstructor.path.length > 1 ? input.field.typeConstructor.path[0] : void 0;
|
|
843
|
+
const typeDescriptor = getAuthoringTypeConstructor(input.authoringContributions, input.field.typeConstructor.path);
|
|
844
|
+
if (!typeDescriptor && namespacePrefix && hasRegisteredFieldNamespace(input.authoringContributions, namespacePrefix)) {
|
|
845
|
+
reportUnknownFieldPreset({
|
|
846
|
+
entityLabel: input.entityLabel,
|
|
847
|
+
namespace: namespacePrefix,
|
|
848
|
+
helperPath,
|
|
849
|
+
sourceId: input.sourceId,
|
|
850
|
+
span: input.field.typeConstructor.span,
|
|
851
|
+
diagnostics: input.diagnostics
|
|
852
|
+
});
|
|
853
|
+
return {
|
|
854
|
+
ok: false,
|
|
855
|
+
alreadyReported: true
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
const descriptor$1 = typeDescriptor ?? resolvePslTypeConstructorDescriptor({
|
|
723
859
|
call: input.field.typeConstructor,
|
|
724
860
|
authoringContributions: input.authoringContributions,
|
|
725
861
|
composedExtensions: input.composedExtensions,
|
|
@@ -985,6 +1121,15 @@ function lowerDefaultForField(input) {
|
|
|
985
1121
|
});
|
|
986
1122
|
return {};
|
|
987
1123
|
}
|
|
1124
|
+
if (generatorDescriptor.applicableCodecIds === void 0) {
|
|
1125
|
+
input.diagnostics.push({
|
|
1126
|
+
code: "PSL_INVALID_DEFAULT_APPLICABILITY",
|
|
1127
|
+
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.`,
|
|
1128
|
+
sourceId: input.sourceId,
|
|
1129
|
+
span: expressionEntry.span
|
|
1130
|
+
});
|
|
1131
|
+
return {};
|
|
1132
|
+
}
|
|
988
1133
|
if (!generatorDescriptor.applicableCodecIds.includes(input.columnDescriptor.codecId)) {
|
|
989
1134
|
input.diagnostics.push({
|
|
990
1135
|
code: "PSL_INVALID_DEFAULT_APPLICABILITY",
|
|
@@ -994,7 +1139,7 @@ function lowerDefaultForField(input) {
|
|
|
994
1139
|
});
|
|
995
1140
|
return {};
|
|
996
1141
|
}
|
|
997
|
-
return {
|
|
1142
|
+
return { executionDefaults: { onCreate: lowered.value.generated } };
|
|
998
1143
|
}
|
|
999
1144
|
function resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors) {
|
|
1000
1145
|
if (field.typeRef && namedTypeDescriptors.has(field.typeRef)) return namedTypeDescriptors.get(field.typeRef);
|
|
@@ -1012,12 +1157,21 @@ const BUILTIN_FIELD_ATTRIBUTE_NAMES = new Set([
|
|
|
1012
1157
|
"relation",
|
|
1013
1158
|
"map"
|
|
1014
1159
|
]);
|
|
1160
|
+
const REMOVED_ATTRIBUTE_RULES = new Map([["updatedAt", {
|
|
1161
|
+
hint: "Use `temporal.updatedAt()` as a field-preset call instead.",
|
|
1162
|
+
suppressWhen: (field) => field.typeConstructor?.path[0] === "temporal"
|
|
1163
|
+
}]]);
|
|
1164
|
+
{
|
|
1165
|
+
const overlap = [...REMOVED_ATTRIBUTE_RULES.keys()].filter((name) => BUILTIN_FIELD_ATTRIBUTE_NAMES.has(name));
|
|
1166
|
+
if (overlap.length > 0) throw new Error(`BUILTIN_FIELD_ATTRIBUTE_NAMES and REMOVED_ATTRIBUTE_RULES must not overlap. Names in both: ${overlap.join(", ")}`);
|
|
1167
|
+
}
|
|
1015
1168
|
function validateFieldAttributes(input) {
|
|
1016
1169
|
for (const attribute of input.field.attributes) {
|
|
1017
1170
|
if (BUILTIN_FIELD_ATTRIBUTE_NAMES.has(attribute.name)) continue;
|
|
1018
1171
|
const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
|
|
1019
1172
|
familyId: input.familyId,
|
|
1020
|
-
targetId: input.targetId
|
|
1173
|
+
targetId: input.targetId,
|
|
1174
|
+
authoringContributions: input.authoringContributions
|
|
1021
1175
|
});
|
|
1022
1176
|
if (uncomposedNamespace) {
|
|
1023
1177
|
reportUncomposedNamespace({
|
|
@@ -1029,9 +1183,12 @@ function validateFieldAttributes(input) {
|
|
|
1029
1183
|
});
|
|
1030
1184
|
continue;
|
|
1031
1185
|
}
|
|
1186
|
+
const baseMessage = `Field "${input.model.name}.${input.field.name}" uses unsupported attribute "@${attribute.name}"`;
|
|
1187
|
+
const removedRule = REMOVED_ATTRIBUTE_RULES.get(attribute.name);
|
|
1188
|
+
const message = removedRule && !removedRule.suppressWhen(input.field) ? `${baseMessage}. ${removedRule.hint}` : baseMessage;
|
|
1032
1189
|
input.diagnostics.push({
|
|
1033
1190
|
code: "PSL_UNSUPPORTED_FIELD_ATTRIBUTE",
|
|
1034
|
-
message
|
|
1191
|
+
message,
|
|
1035
1192
|
sourceId: input.sourceId,
|
|
1036
1193
|
span: attribute.span
|
|
1037
1194
|
});
|
|
@@ -1065,21 +1222,25 @@ function collectResolvedFields(input) {
|
|
|
1065
1222
|
const { model, mapping, enumTypeDescriptors, namedTypeDescriptors, modelNames, compositeTypeNames, composedExtensions, authoringContributions, familyId, targetId, defaultFunctionRegistry, generatorDescriptorById, diagnostics, sourceId, scalarTypeDescriptors } = input;
|
|
1066
1223
|
const resolvedFields = [];
|
|
1067
1224
|
for (const field of model.fields) {
|
|
1068
|
-
|
|
1225
|
+
const isModelField = modelNames.has(field.typeName);
|
|
1226
|
+
if (field.list && isModelField) continue;
|
|
1069
1227
|
validateFieldAttributes({
|
|
1070
1228
|
model,
|
|
1071
1229
|
field,
|
|
1072
1230
|
composedExtensions,
|
|
1231
|
+
authoringContributions,
|
|
1073
1232
|
diagnostics,
|
|
1074
1233
|
sourceId,
|
|
1075
1234
|
familyId,
|
|
1076
1235
|
targetId
|
|
1077
1236
|
});
|
|
1078
|
-
|
|
1237
|
+
const relationAttribute = getAttribute(field.attributes, "relation");
|
|
1238
|
+
if (isModelField && relationAttribute) continue;
|
|
1079
1239
|
const isValueObjectField = compositeTypeNames.has(field.typeName);
|
|
1080
1240
|
const isListField = field.list;
|
|
1081
1241
|
let descriptor;
|
|
1082
1242
|
let scalarCodecId;
|
|
1243
|
+
let presetContributions;
|
|
1083
1244
|
const resolveInput = {
|
|
1084
1245
|
field,
|
|
1085
1246
|
enumTypeDescriptors,
|
|
@@ -1105,6 +1266,15 @@ function collectResolvedFields(input) {
|
|
|
1105
1266
|
});
|
|
1106
1267
|
continue;
|
|
1107
1268
|
}
|
|
1269
|
+
if (resolved.presetContributions) {
|
|
1270
|
+
diagnostics.push({
|
|
1271
|
+
code: "PSL_PRESET_NOT_LIST",
|
|
1272
|
+
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.`,
|
|
1273
|
+
sourceId,
|
|
1274
|
+
span: field.span
|
|
1275
|
+
});
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1108
1278
|
scalarCodecId = resolved.descriptor.codecId;
|
|
1109
1279
|
descriptor = scalarTypeDescriptors.get("Json");
|
|
1110
1280
|
} else {
|
|
@@ -1119,9 +1289,28 @@ function collectResolvedFields(input) {
|
|
|
1119
1289
|
continue;
|
|
1120
1290
|
}
|
|
1121
1291
|
descriptor = resolved.descriptor;
|
|
1292
|
+
presetContributions = resolved.presetContributions;
|
|
1122
1293
|
}
|
|
1123
1294
|
if (!descriptor) continue;
|
|
1295
|
+
if (presetContributions && field.optional) {
|
|
1296
|
+
diagnostics.push({
|
|
1297
|
+
code: "PSL_PRESET_NOT_OPTIONAL",
|
|
1298
|
+
message: `Field "${model.name}.${field.name}" uses a field-preset call and cannot be optional. Remove "?" or use a different field type.`,
|
|
1299
|
+
sourceId,
|
|
1300
|
+
span: field.span
|
|
1301
|
+
});
|
|
1302
|
+
continue;
|
|
1303
|
+
}
|
|
1124
1304
|
const defaultAttribute = getAttribute(field.attributes, "default");
|
|
1305
|
+
if (presetContributions && defaultAttribute) {
|
|
1306
|
+
diagnostics.push({
|
|
1307
|
+
code: "PSL_PRESET_AND_DEFAULT_CONFLICT",
|
|
1308
|
+
message: `Field "${model.name}.${field.name}" uses a field-preset call and cannot also declare @default(...). The preset already specifies the default value.`,
|
|
1309
|
+
sourceId,
|
|
1310
|
+
span: defaultAttribute.span
|
|
1311
|
+
});
|
|
1312
|
+
continue;
|
|
1313
|
+
}
|
|
1125
1314
|
const loweredDefault = defaultAttribute ? lowerDefaultForField({
|
|
1126
1315
|
modelName: model.name,
|
|
1127
1316
|
fieldName: field.name,
|
|
@@ -1132,8 +1321,9 @@ function collectResolvedFields(input) {
|
|
|
1132
1321
|
defaultFunctionRegistry,
|
|
1133
1322
|
diagnostics
|
|
1134
1323
|
}) : {};
|
|
1135
|
-
|
|
1136
|
-
|
|
1324
|
+
const loweredOnCreate = loweredDefault.executionDefaults?.onCreate;
|
|
1325
|
+
if (field.optional && loweredOnCreate) {
|
|
1326
|
+
const generatorDescription = loweredOnCreate.kind === "generator" ? `"${loweredOnCreate.id}"` : "for this field";
|
|
1137
1327
|
diagnostics.push({
|
|
1138
1328
|
code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
|
|
1139
1329
|
message: `Field "${model.name}.${field.name}" cannot be optional when using execution default ${generatorDescription}. Remove "?" or use a storage default.`,
|
|
@@ -1142,8 +1332,8 @@ function collectResolvedFields(input) {
|
|
|
1142
1332
|
});
|
|
1143
1333
|
continue;
|
|
1144
1334
|
}
|
|
1145
|
-
if (
|
|
1146
|
-
const generatedDescriptor = generatorDescriptorById.get(
|
|
1335
|
+
if (loweredOnCreate) {
|
|
1336
|
+
const generatedDescriptor = generatorDescriptorById.get(loweredOnCreate.id)?.resolveGeneratedColumnDescriptor?.({ generated: loweredOnCreate });
|
|
1147
1337
|
if (generatedDescriptor) descriptor = generatedDescriptor;
|
|
1148
1338
|
}
|
|
1149
1339
|
const mappedColumnName = mapping.fieldColumns.get(field.name) ?? field.name;
|
|
@@ -1153,14 +1343,25 @@ function collectResolvedFields(input) {
|
|
|
1153
1343
|
sourceId,
|
|
1154
1344
|
diagnostics
|
|
1155
1345
|
});
|
|
1346
|
+
if (presetContributions && idAttribute && !presetContributions.id) {
|
|
1347
|
+
diagnostics.push({
|
|
1348
|
+
code: "PSL_PRESET_AND_ID_CONFLICT",
|
|
1349
|
+
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.`,
|
|
1350
|
+
sourceId,
|
|
1351
|
+
span: idAttribute.span
|
|
1352
|
+
});
|
|
1353
|
+
continue;
|
|
1354
|
+
}
|
|
1355
|
+
const fieldExecutionDefaults = presetContributions?.executionDefaults ?? loweredDefault.executionDefaults;
|
|
1356
|
+
const fieldDefaultValue = presetContributions?.default ?? loweredDefault.defaultValue;
|
|
1156
1357
|
resolvedFields.push({
|
|
1157
1358
|
field,
|
|
1158
1359
|
columnName: mappedColumnName,
|
|
1159
1360
|
descriptor,
|
|
1160
|
-
...ifDefined("defaultValue",
|
|
1161
|
-
...ifDefined("
|
|
1162
|
-
isId: Boolean(idAttribute),
|
|
1163
|
-
isUnique: Boolean(uniqueAttribute),
|
|
1361
|
+
...ifDefined("defaultValue", fieldDefaultValue),
|
|
1362
|
+
...ifDefined("executionDefaults", fieldExecutionDefaults),
|
|
1363
|
+
isId: Boolean(idAttribute) || Boolean(presetContributions?.id),
|
|
1364
|
+
isUnique: Boolean(uniqueAttribute) || Boolean(presetContributions?.unique),
|
|
1164
1365
|
...ifDefined("idName", idName),
|
|
1165
1366
|
...ifDefined("uniqueName", uniqueName),
|
|
1166
1367
|
...ifDefined("many", isListField ? true : void 0),
|
|
@@ -1418,7 +1619,8 @@ function validateNavigationListFieldAttributes(input) {
|
|
|
1418
1619
|
if (attribute.name === "relation") continue;
|
|
1419
1620
|
const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
|
|
1420
1621
|
familyId: input.familyId,
|
|
1421
|
-
targetId: input.targetId
|
|
1622
|
+
targetId: input.targetId,
|
|
1623
|
+
authoringContributions: input.authoringContributions
|
|
1422
1624
|
});
|
|
1423
1625
|
if (uncomposedNamespace) {
|
|
1424
1626
|
reportUncomposedNamespace({
|
|
@@ -1531,7 +1733,8 @@ function validateNamedTypeAttributes(input) {
|
|
|
1531
1733
|
if (input.allowDbNativeType && attribute.name.startsWith("db.")) continue;
|
|
1532
1734
|
const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
|
|
1533
1735
|
familyId: input.familyId,
|
|
1534
|
-
targetId: input.targetId
|
|
1736
|
+
targetId: input.targetId,
|
|
1737
|
+
authoringContributions: input.authoringContributions
|
|
1535
1738
|
});
|
|
1536
1739
|
if (uncomposedNamespace) {
|
|
1537
1740
|
reportUncomposedNamespace({
|
|
@@ -1567,6 +1770,7 @@ function resolveNamedTypeDeclarations(input) {
|
|
|
1567
1770
|
sourceId: input.sourceId,
|
|
1568
1771
|
diagnostics: input.diagnostics,
|
|
1569
1772
|
composedExtensions: input.composedExtensions,
|
|
1773
|
+
authoringContributions: input.authoringContributions,
|
|
1570
1774
|
allowDbNativeType: false,
|
|
1571
1775
|
familyId: input.familyId,
|
|
1572
1776
|
targetId: input.targetId
|
|
@@ -1626,6 +1830,7 @@ function resolveNamedTypeDeclarations(input) {
|
|
|
1626
1830
|
sourceId: input.sourceId,
|
|
1627
1831
|
diagnostics: input.diagnostics,
|
|
1628
1832
|
composedExtensions: input.composedExtensions,
|
|
1833
|
+
authoringContributions: input.authoringContributions,
|
|
1629
1834
|
allowDbNativeType: true,
|
|
1630
1835
|
familyId: input.familyId,
|
|
1631
1836
|
targetId: input.targetId
|
|
@@ -1700,6 +1905,7 @@ function buildModelNodeFromPsl(input) {
|
|
|
1700
1905
|
field,
|
|
1701
1906
|
sourceId,
|
|
1702
1907
|
composedExtensions: input.composedExtensions,
|
|
1908
|
+
authoringContributions: input.authoringContributions,
|
|
1703
1909
|
diagnostics,
|
|
1704
1910
|
familyId: input.familyId,
|
|
1705
1911
|
targetId: input.targetId
|
|
@@ -1796,7 +2002,8 @@ function buildModelNodeFromPsl(input) {
|
|
|
1796
2002
|
}
|
|
1797
2003
|
const uncomposedNamespace = checkUncomposedNamespace(modelAttribute.name, input.composedExtensions, {
|
|
1798
2004
|
familyId: input.familyId,
|
|
1799
|
-
targetId: input.targetId
|
|
2005
|
+
targetId: input.targetId,
|
|
2006
|
+
authoringContributions: input.authoringContributions
|
|
1800
2007
|
});
|
|
1801
2008
|
if (uncomposedNamespace) {
|
|
1802
2009
|
reportUncomposedNamespace({
|
|
@@ -1933,7 +2140,7 @@ function buildModelNodeFromPsl(input) {
|
|
|
1933
2140
|
descriptor: resolvedField.descriptor,
|
|
1934
2141
|
nullable: resolvedField.field.optional,
|
|
1935
2142
|
...ifDefined("default", resolvedField.defaultValue),
|
|
1936
|
-
...ifDefined("
|
|
2143
|
+
...ifDefined("executionDefaults", resolvedField.executionDefaults)
|
|
1937
2144
|
})),
|
|
1938
2145
|
...primaryKeyColumns.length > 0 ? { id: {
|
|
1939
2146
|
columns: primaryKeyColumns,
|
|
@@ -2340,4 +2547,4 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
2340
2547
|
|
|
2341
2548
|
//#endregion
|
|
2342
2549
|
export { interpretPslDocumentToSqlContract as t };
|
|
2343
|
-
//# sourceMappingURL=interpreter-
|
|
2550
|
+
//# sourceMappingURL=interpreter-DJrrH8Ee.mjs.map
|