@prisma-next/sql-contract-psl 0.12.0-dev.4 → 0.12.0-dev.41
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 +10 -2
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{interpreter-QE6eZZof.mjs → interpreter-BTfcjVFM.mjs} +82 -13
- package/dist/interpreter-BTfcjVFM.mjs.map +1 -0
- package/dist/provider.d.mts +5 -0
- package/dist/provider.d.mts.map +1 -1
- package/dist/provider.mjs +5 -3
- package/dist/provider.mjs.map +1 -1
- package/package.json +12 -12
- package/src/interpreter.ts +31 -10
- package/src/provider.ts +10 -1
- package/src/psl-attribute-parsing.ts +66 -0
- package/src/psl-column-resolution.ts +10 -3
- package/src/psl-relation-resolution.ts +3 -4
- package/dist/interpreter-QE6eZZof.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ This keeps core/CLI source-agnostic while giving PSL-first SQL users a one-line
|
|
|
14
14
|
## Responsibilities
|
|
15
15
|
|
|
16
16
|
- Interpret `ParsePslDocumentResult` into SQL `Contract`
|
|
17
|
-
- Interpret generic PSL attributes into SQL contract semantics (`@id`, `@unique`, `@default`, `@relation`, `@map`, `@@map`)
|
|
17
|
+
- Interpret generic PSL attributes into SQL contract semantics (`@id`, `@unique`, `@default`, `@relation`, `@map`, `@@map`, `@@control`)
|
|
18
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
|
|
19
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)?`)
|
|
20
20
|
- Lower supported default functions through composed registry inputs
|
|
@@ -71,12 +71,20 @@ Supported timestamp authoring surface:
|
|
|
71
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
72
|
- `@createdAt` is not supported as a PSL alias.
|
|
73
73
|
|
|
74
|
+
Model-level control policy:
|
|
75
|
+
|
|
76
|
+
- `@@control(<policy>)` lowers to the storage table's `control` field. The argument is one positional lowercase literal: `managed`, `tolerated`, `external`, or `observed`. Omit `@@control` to leave per-table control unset (the framework default applies at runtime).
|
|
77
|
+
|
|
78
|
+
Contract-level default (specifier options bag):
|
|
79
|
+
|
|
80
|
+
- `defaultControlPolicy` on `prismaContract(...)` sets `Contract.defaultControlPolicy` at load time when the interpreted contract does not already define one (source wins when both are present).
|
|
81
|
+
|
|
74
82
|
## Public API
|
|
75
83
|
|
|
76
84
|
- `@prisma-next/sql-contract-psl`
|
|
77
85
|
- `interpretPslDocumentToSqlContract({ document, target, scalarTypeDescriptors, authoringContributions?, controlMutationDefaults?, composedExtensionPacks? })`
|
|
78
86
|
- `@prisma-next/sql-contract-psl/provider`
|
|
79
|
-
- `prismaContract(schemaPath, { output?, target, scalarTypeDescriptors, authoringContributions?, controlMutationDefaults?, composedExtensionPacks? })`
|
|
87
|
+
- `prismaContract(schemaPath, { output?, target, defaultControlPolicy?, scalarTypeDescriptors, authoringContributions?, controlMutationDefaults?, composedExtensionPacks? })`
|
|
80
88
|
- Provider input is fully preassembled by composition layers (for example `@prisma-next/family-sql/control` helpers).
|
|
81
89
|
|
|
82
90
|
## Dependencies
|
package/dist/index.d.mts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Contract } from "@prisma-next/contract/types";
|
|
2
2
|
import { AuthoringContributions } from "@prisma-next/framework-components/authoring";
|
|
3
|
-
import { Namespace } from "@prisma-next/framework-components/ir";
|
|
4
3
|
import { SqlNamespaceTablesInput } from "@prisma-next/sql-contract/types";
|
|
5
4
|
import { Result } from "@prisma-next/utils/result";
|
|
6
5
|
import { ParsePslDocumentResult } from "@prisma-next/psl-parser";
|
|
7
6
|
import { ControlMutationDefaults, ControlMutationDefaults as ControlMutationDefaults$1, DefaultFunctionLoweringContext, DefaultFunctionLoweringHandler, DefaultFunctionRegistry, DefaultFunctionRegistryEntry, MutationDefaultGeneratorDescriptor } from "@prisma-next/framework-components/control";
|
|
8
7
|
import { ContractSourceDiagnostics } from "@prisma-next/config/config-types";
|
|
9
8
|
import { ExtensionPackRef, TargetPackRef } from "@prisma-next/framework-components/components";
|
|
9
|
+
import { Namespace } from "@prisma-next/framework-components/ir";
|
|
10
10
|
|
|
11
11
|
//#region src/psl-column-resolution.d.ts
|
|
12
12
|
type ColumnDescriptor = {
|
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"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/psl-column-resolution.ts","../src/interpreter.ts"],"mappings":";;;;;;;;;;;KA0CY,gBAAA;EAAA,SACD,OAAA;EAAA,SACA,UAAA;EAAA,SACA,OAAA;EAAA,SACA,UAAA,GAAa,MAAM;AAAA;;;UC8Cb,sCAAA;EAAA,SACN,QAAA,EAAU,sBAAA;EAAA,SACV,MAAA,EAAQ,aAAA;EAAA,SACR,qBAAA,EAAuB,WAAA,SAAoB,gBAAA;EAAA,SAC3C,sBAAA;EAAA,SACA,yBAAA,YAAqC,gBAAA;EAAA,SACrC,uBAAA,GAA0B,yBAAA;EAAA,SAC1B,sBAAA,GAAyB,sBAAA;EDrDzB;;;AAAmB;;;;AC8C9B;;ED9CW,SC+DA,eAAA,IAAmB,KAAA,EAAO,uBAAA,KAA4B,SAAA;AAAA;AAAA,iBA4zCjD,iCAAA,CACd,KAAA,EAAO,sCAAA,GACN,MAAA,CAAO,QAAA,EAAU,yBAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as interpretPslDocumentToSqlContract } from "./interpreter-
|
|
1
|
+
import { t as interpretPslDocumentToSqlContract } from "./interpreter-BTfcjVFM.mjs";
|
|
2
2
|
export { interpretPslDocumentToSqlContract };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { crossRef } from "@prisma-next/contract/types";
|
|
2
2
|
import { hasRegisteredFieldNamespace, instantiateAuthoringEntityType, instantiateAuthoringFieldPreset, instantiateAuthoringTypeConstructor, isAuthoringEntityTypeDescriptor, isAuthoringFieldPresetDescriptor, isAuthoringTypeConstructorDescriptor, validateAuthoringHelperArguments } from "@prisma-next/framework-components/authoring";
|
|
3
|
-
import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
|
|
4
3
|
import { isPostgresEnumStorageEntry } from "@prisma-next/sql-contract/types";
|
|
5
4
|
import { buildSqlContractFromDefinition } from "@prisma-next/sql-contract-ts/contract-builder";
|
|
6
5
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
7
6
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
8
7
|
import { getPositionalArgument, parseQuotedStringLiteral } from "@prisma-next/psl-parser";
|
|
8
|
+
import { blindCast } from "@prisma-next/utils/casts";
|
|
9
9
|
import { assertDefined, invariant } from "@prisma-next/utils/assertions";
|
|
10
10
|
//#region src/psl-attribute-parsing.ts
|
|
11
11
|
function lowerFirst(value) {
|
|
@@ -279,6 +279,56 @@ function findDuplicateFieldName(fieldNames) {
|
|
|
279
279
|
seen.add(name);
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
|
+
const CONTROL_POLICY_LITERAL_SET = new Set([
|
|
283
|
+
"managed",
|
|
284
|
+
"tolerated",
|
|
285
|
+
"external",
|
|
286
|
+
"observed"
|
|
287
|
+
]);
|
|
288
|
+
function isControlPolicyLiteral(value) {
|
|
289
|
+
return CONTROL_POLICY_LITERAL_SET.has(value);
|
|
290
|
+
}
|
|
291
|
+
function parseControlPolicyAttribute(input) {
|
|
292
|
+
if (input.attribute.args.filter((arg) => arg.kind === "named").length > 0) {
|
|
293
|
+
input.diagnostics.push({
|
|
294
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
295
|
+
message: "`@@control` does not accept named arguments; pass the policy positionally as `@@control(external)`.",
|
|
296
|
+
sourceId: input.sourceId,
|
|
297
|
+
span: input.attribute.span
|
|
298
|
+
});
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const positionalArgs = getPositionalArguments(input.attribute);
|
|
302
|
+
if (positionalArgs.length === 0) {
|
|
303
|
+
input.diagnostics.push({
|
|
304
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
305
|
+
message: "`@@control` requires exactly one positional argument: `managed`, `tolerated`, `external`, or `observed`.",
|
|
306
|
+
sourceId: input.sourceId,
|
|
307
|
+
span: input.attribute.span
|
|
308
|
+
});
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (positionalArgs.length > 1) {
|
|
312
|
+
input.diagnostics.push({
|
|
313
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
314
|
+
message: `\`@@control\` accepts exactly one positional argument; got ${positionalArgs.length}.`,
|
|
315
|
+
sourceId: input.sourceId,
|
|
316
|
+
span: input.attribute.span
|
|
317
|
+
});
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const token = unquoteStringLiteral(positionalArgs[0] ?? "").trim();
|
|
321
|
+
if (!isControlPolicyLiteral(token)) {
|
|
322
|
+
input.diagnostics.push({
|
|
323
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
324
|
+
message: `\`@@control\` argument \`${token}\` is not a known policy. Allowed: \`managed\`, \`tolerated\`, \`external\`, \`observed\`.`,
|
|
325
|
+
sourceId: input.sourceId,
|
|
326
|
+
span: input.attribute.span
|
|
327
|
+
});
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
return token;
|
|
331
|
+
}
|
|
282
332
|
function mapFieldNamesToColumns(input) {
|
|
283
333
|
const columns = [];
|
|
284
334
|
for (const fieldName of input.fieldNames) {
|
|
@@ -730,7 +780,7 @@ function getAuthoringTypeConstructor(contributions, path) {
|
|
|
730
780
|
let current = contributions?.type;
|
|
731
781
|
for (const segment of path) {
|
|
732
782
|
if (typeof current !== "object" || current === null || Array.isArray(current)) return;
|
|
733
|
-
current = current[segment];
|
|
783
|
+
current = blindCast(current)[segment];
|
|
734
784
|
}
|
|
735
785
|
return isAuthoringTypeConstructorDescriptor(current) ? current : void 0;
|
|
736
786
|
}
|
|
@@ -749,7 +799,7 @@ function getAuthoringEntity(contributions, path) {
|
|
|
749
799
|
let current = contributions?.entityTypes;
|
|
750
800
|
for (const segment of path) {
|
|
751
801
|
if (typeof current !== "object" || current === null || Array.isArray(current)) return;
|
|
752
|
-
current = current[segment];
|
|
802
|
+
current = blindCast(current)[segment];
|
|
753
803
|
}
|
|
754
804
|
return isAuthoringEntityTypeDescriptor(current) ? current : void 0;
|
|
755
805
|
}
|
|
@@ -762,7 +812,7 @@ function getAuthoringFieldPreset(contributions, path) {
|
|
|
762
812
|
let current = contributions?.field;
|
|
763
813
|
for (const segment of path) {
|
|
764
814
|
if (typeof current !== "object" || current === null || Array.isArray(current)) return;
|
|
765
|
-
current = current[segment];
|
|
815
|
+
current = blindCast(current)[segment];
|
|
766
816
|
}
|
|
767
817
|
return isAuthoringFieldPresetDescriptor(current) ? current : void 0;
|
|
768
818
|
}
|
|
@@ -1852,9 +1902,6 @@ const UNSPECIFIED_PSL_NAMESPACE_NAME = "__unspecified__";
|
|
|
1852
1902
|
* slot empty (which means the late-bound default at the `StorageTable`
|
|
1853
1903
|
* layer; emitted JSON omits the field).
|
|
1854
1904
|
*/
|
|
1855
|
-
function defaultSqlNamespaceIdForTarget(targetId) {
|
|
1856
|
-
return targetId === "postgres" ? "public" : UNBOUND_NAMESPACE_ID;
|
|
1857
|
-
}
|
|
1858
1905
|
function resolveNamespaceIdForSqlTarget(input) {
|
|
1859
1906
|
if (input.targetId !== "postgres") return;
|
|
1860
1907
|
if (input.bucketName === UNSPECIFIED_PSL_NAMESPACE_NAME) return "public";
|
|
@@ -2132,6 +2179,8 @@ function buildModelNodeFromPsl(input) {
|
|
|
2132
2179
|
} : void 0;
|
|
2133
2180
|
const hasInlinePrimaryKey = primaryKey !== void 0;
|
|
2134
2181
|
let blockPrimaryKeyDeclared = false;
|
|
2182
|
+
let controlPolicyDeclared = false;
|
|
2183
|
+
let controlPolicy;
|
|
2135
2184
|
const resultBackrelationCandidates = [];
|
|
2136
2185
|
for (const field of model.fields) {
|
|
2137
2186
|
if (!field.list || !input.modelNames.has(field.typeName)) continue;
|
|
@@ -2198,6 +2247,25 @@ function buildModelNodeFromPsl(input) {
|
|
|
2198
2247
|
for (const modelAttribute of model.attributes) {
|
|
2199
2248
|
if (modelAttribute.name === "map") continue;
|
|
2200
2249
|
if (modelAttribute.name === "discriminator" || modelAttribute.name === "base") continue;
|
|
2250
|
+
if (modelAttribute.name === "control") {
|
|
2251
|
+
if (controlPolicyDeclared) {
|
|
2252
|
+
diagnostics.push({
|
|
2253
|
+
code: "PSL_DUPLICATE_ATTRIBUTE",
|
|
2254
|
+
message: `\`@@control\` declared more than once on model "${model.name}".`,
|
|
2255
|
+
sourceId,
|
|
2256
|
+
span: modelAttribute.span
|
|
2257
|
+
});
|
|
2258
|
+
continue;
|
|
2259
|
+
}
|
|
2260
|
+
controlPolicyDeclared = true;
|
|
2261
|
+
const parsed = parseControlPolicyAttribute({
|
|
2262
|
+
attribute: modelAttribute,
|
|
2263
|
+
sourceId,
|
|
2264
|
+
diagnostics
|
|
2265
|
+
});
|
|
2266
|
+
if (parsed !== void 0) controlPolicy = parsed;
|
|
2267
|
+
continue;
|
|
2268
|
+
}
|
|
2201
2269
|
const attributeLabel = `Model "${model.name}" @@${modelAttribute.name}`;
|
|
2202
2270
|
if (modelAttribute.name === "id") {
|
|
2203
2271
|
if (blockPrimaryKeyDeclared) {
|
|
@@ -2520,7 +2588,8 @@ function buildModelNodeFromPsl(input) {
|
|
|
2520
2588
|
...ifDefined("id", primaryKey),
|
|
2521
2589
|
...uniqueConstraints.length > 0 ? { uniques: uniqueConstraints } : {},
|
|
2522
2590
|
...indexNodes.length > 0 ? { indexes: indexNodes } : {},
|
|
2523
|
-
...foreignKeyNodes.length > 0 ? { foreignKeys: foreignKeyNodes } : {}
|
|
2591
|
+
...foreignKeyNodes.length > 0 ? { foreignKeys: foreignKeyNodes } : {},
|
|
2592
|
+
...ifDefined("control", controlPolicy)
|
|
2524
2593
|
},
|
|
2525
2594
|
fkRelationMetadata: resultFkRelationMetadata,
|
|
2526
2595
|
backrelationCandidates: resultBackrelationCandidates,
|
|
@@ -2688,7 +2757,7 @@ function collectPolymorphismDeclarations(models, sourceId, diagnostics) {
|
|
|
2688
2757
|
baseDeclarations
|
|
2689
2758
|
};
|
|
2690
2759
|
}
|
|
2691
|
-
function resolvePolymorphism(models, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, modelNamespaceIds,
|
|
2760
|
+
function resolvePolymorphism(models, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, modelNamespaceIds, defaultNamespaceId, sourceId, diagnostics) {
|
|
2692
2761
|
let patched = models;
|
|
2693
2762
|
for (const [modelName, decl] of discriminatorDeclarations) {
|
|
2694
2763
|
if (baseDeclarations.has(modelName)) {
|
|
@@ -2775,7 +2844,7 @@ function resolvePolymorphism(models, discriminatorDeclarations, baseDeclarations
|
|
|
2775
2844
|
...patched,
|
|
2776
2845
|
[variantName]: {
|
|
2777
2846
|
...variantModel,
|
|
2778
|
-
base: crossRef(baseDecl.baseName, modelNamespaceIds.get(baseDecl.baseName) ??
|
|
2847
|
+
base: crossRef(baseDecl.baseName, modelNamespaceIds.get(baseDecl.baseName) ?? defaultNamespaceId),
|
|
2779
2848
|
...resolvedTable ? { storage: {
|
|
2780
2849
|
...variantModel.storage,
|
|
2781
2850
|
table: resolvedTable
|
|
@@ -2963,7 +3032,7 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
2963
3032
|
}
|
|
2964
3033
|
let patchedModels = patchModelDomainFields(modelsForPatch, modelResolvedFields);
|
|
2965
3034
|
const polyDiagnostics = [];
|
|
2966
|
-
patchedModels = resolvePolymorphism(patchedModels, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, modelNamespaceIds, input.target.
|
|
3035
|
+
patchedModels = resolvePolymorphism(patchedModels, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, modelNamespaceIds, input.target.defaultNamespaceId, sourceId, polyDiagnostics);
|
|
2967
3036
|
if (polyDiagnostics.length > 0) return notOk({
|
|
2968
3037
|
summary: "PSL to SQL contract interpretation failed",
|
|
2969
3038
|
diagnostics: polyDiagnostics
|
|
@@ -2976,11 +3045,11 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
2976
3045
|
domain: { namespaces: Object.fromEntries(Object.entries(contract.domain.namespaces).map(([namespaceId, namespaceSlice]) => [namespaceId, {
|
|
2977
3046
|
models: Object.fromEntries(Object.entries(namespaceSlice.models).map(([modelName, model]) => [modelName, patchedModels[modelName] ?? model])),
|
|
2978
3047
|
...namespaceSlice.valueObjects !== void 0 ? { valueObjects: namespaceSlice.valueObjects } : {},
|
|
2979
|
-
...namespaceId ===
|
|
3048
|
+
...namespaceId === input.target.defaultNamespaceId && Object.keys(valueObjects).length > 0 ? { valueObjects } : {}
|
|
2980
3049
|
}])) }
|
|
2981
3050
|
});
|
|
2982
3051
|
}
|
|
2983
3052
|
//#endregion
|
|
2984
3053
|
export { interpretPslDocumentToSqlContract as t };
|
|
2985
3054
|
|
|
2986
|
-
//# sourceMappingURL=interpreter-
|
|
3055
|
+
//# sourceMappingURL=interpreter-BTfcjVFM.mjs.map
|