@prisma-next/sql-contract-psl 0.12.0-dev.3 → 0.12.0-dev.31

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 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 = {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/psl-column-resolution.ts","../src/interpreter.ts"],"mappings":";;;;;;;;;;;KAyCY,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,iBAwyCjD,iCAAA,CACd,KAAA,EAAO,sCAAA,GACN,MAAA,CAAO,QAAA,EAAU,yBAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/psl-column-resolution.ts","../src/interpreter.ts"],"mappings":";;;;;;;;;;;KAyCY,gBAAA;EAAA,SACD,OAAA;EAAA,SACA,UAAA;EAAA,SACA,OAAA;EAAA,SACA,UAAA,GAAa,MAAM;AAAA;;;UC+Cb,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;EDtDzB;;;AAAmB;;;;AC+C9B;;ED/CW,SCgEA,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-QE6eZZof.mjs";
1
+ import { t as interpretPslDocumentToSqlContract } from "./interpreter-B75uZQde.mjs";
2
2
  export { interpretPslDocumentToSqlContract };
@@ -1,6 +1,5 @@
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";
@@ -279,6 +278,56 @@ function findDuplicateFieldName(fieldNames) {
279
278
  seen.add(name);
280
279
  }
281
280
  }
281
+ const CONTROL_POLICY_LITERAL_SET = new Set([
282
+ "managed",
283
+ "tolerated",
284
+ "external",
285
+ "observed"
286
+ ]);
287
+ function isControlPolicyLiteral(value) {
288
+ return CONTROL_POLICY_LITERAL_SET.has(value);
289
+ }
290
+ function parseControlPolicyAttribute(input) {
291
+ if (input.attribute.args.filter((arg) => arg.kind === "named").length > 0) {
292
+ input.diagnostics.push({
293
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
294
+ message: "`@@control` does not accept named arguments; pass the policy positionally as `@@control(external)`.",
295
+ sourceId: input.sourceId,
296
+ span: input.attribute.span
297
+ });
298
+ return;
299
+ }
300
+ const positionalArgs = getPositionalArguments(input.attribute);
301
+ if (positionalArgs.length === 0) {
302
+ input.diagnostics.push({
303
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
304
+ message: "`@@control` requires exactly one positional argument: `managed`, `tolerated`, `external`, or `observed`.",
305
+ sourceId: input.sourceId,
306
+ span: input.attribute.span
307
+ });
308
+ return;
309
+ }
310
+ if (positionalArgs.length > 1) {
311
+ input.diagnostics.push({
312
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
313
+ message: `\`@@control\` accepts exactly one positional argument; got ${positionalArgs.length}.`,
314
+ sourceId: input.sourceId,
315
+ span: input.attribute.span
316
+ });
317
+ return;
318
+ }
319
+ const token = unquoteStringLiteral(positionalArgs[0] ?? "").trim();
320
+ if (!isControlPolicyLiteral(token)) {
321
+ input.diagnostics.push({
322
+ code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
323
+ message: `\`@@control\` argument \`${token}\` is not a known policy. Allowed: \`managed\`, \`tolerated\`, \`external\`, \`observed\`.`,
324
+ sourceId: input.sourceId,
325
+ span: input.attribute.span
326
+ });
327
+ return;
328
+ }
329
+ return token;
330
+ }
282
331
  function mapFieldNamesToColumns(input) {
283
332
  const columns = [];
284
333
  for (const fieldName of input.fieldNames) {
@@ -1852,9 +1901,6 @@ const UNSPECIFIED_PSL_NAMESPACE_NAME = "__unspecified__";
1852
1901
  * slot empty (which means the late-bound default at the `StorageTable`
1853
1902
  * layer; emitted JSON omits the field).
1854
1903
  */
1855
- function defaultSqlNamespaceIdForTarget(targetId) {
1856
- return targetId === "postgres" ? "public" : UNBOUND_NAMESPACE_ID;
1857
- }
1858
1904
  function resolveNamespaceIdForSqlTarget(input) {
1859
1905
  if (input.targetId !== "postgres") return;
1860
1906
  if (input.bucketName === UNSPECIFIED_PSL_NAMESPACE_NAME) return "public";
@@ -2132,6 +2178,8 @@ function buildModelNodeFromPsl(input) {
2132
2178
  } : void 0;
2133
2179
  const hasInlinePrimaryKey = primaryKey !== void 0;
2134
2180
  let blockPrimaryKeyDeclared = false;
2181
+ let controlPolicyDeclared = false;
2182
+ let controlPolicy;
2135
2183
  const resultBackrelationCandidates = [];
2136
2184
  for (const field of model.fields) {
2137
2185
  if (!field.list || !input.modelNames.has(field.typeName)) continue;
@@ -2198,6 +2246,25 @@ function buildModelNodeFromPsl(input) {
2198
2246
  for (const modelAttribute of model.attributes) {
2199
2247
  if (modelAttribute.name === "map") continue;
2200
2248
  if (modelAttribute.name === "discriminator" || modelAttribute.name === "base") continue;
2249
+ if (modelAttribute.name === "control") {
2250
+ if (controlPolicyDeclared) {
2251
+ diagnostics.push({
2252
+ code: "PSL_DUPLICATE_ATTRIBUTE",
2253
+ message: `\`@@control\` declared more than once on model "${model.name}".`,
2254
+ sourceId,
2255
+ span: modelAttribute.span
2256
+ });
2257
+ continue;
2258
+ }
2259
+ controlPolicyDeclared = true;
2260
+ const parsed = parseControlPolicyAttribute({
2261
+ attribute: modelAttribute,
2262
+ sourceId,
2263
+ diagnostics
2264
+ });
2265
+ if (parsed !== void 0) controlPolicy = parsed;
2266
+ continue;
2267
+ }
2201
2268
  const attributeLabel = `Model "${model.name}" @@${modelAttribute.name}`;
2202
2269
  if (modelAttribute.name === "id") {
2203
2270
  if (blockPrimaryKeyDeclared) {
@@ -2520,7 +2587,8 @@ function buildModelNodeFromPsl(input) {
2520
2587
  ...ifDefined("id", primaryKey),
2521
2588
  ...uniqueConstraints.length > 0 ? { uniques: uniqueConstraints } : {},
2522
2589
  ...indexNodes.length > 0 ? { indexes: indexNodes } : {},
2523
- ...foreignKeyNodes.length > 0 ? { foreignKeys: foreignKeyNodes } : {}
2590
+ ...foreignKeyNodes.length > 0 ? { foreignKeys: foreignKeyNodes } : {},
2591
+ ...ifDefined("control", controlPolicy)
2524
2592
  },
2525
2593
  fkRelationMetadata: resultFkRelationMetadata,
2526
2594
  backrelationCandidates: resultBackrelationCandidates,
@@ -2688,7 +2756,7 @@ function collectPolymorphismDeclarations(models, sourceId, diagnostics) {
2688
2756
  baseDeclarations
2689
2757
  };
2690
2758
  }
2691
- function resolvePolymorphism(models, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, modelNamespaceIds, targetId, sourceId, diagnostics) {
2759
+ function resolvePolymorphism(models, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, modelNamespaceIds, defaultNamespaceId, sourceId, diagnostics) {
2692
2760
  let patched = models;
2693
2761
  for (const [modelName, decl] of discriminatorDeclarations) {
2694
2762
  if (baseDeclarations.has(modelName)) {
@@ -2775,7 +2843,7 @@ function resolvePolymorphism(models, discriminatorDeclarations, baseDeclarations
2775
2843
  ...patched,
2776
2844
  [variantName]: {
2777
2845
  ...variantModel,
2778
- base: crossRef(baseDecl.baseName, modelNamespaceIds.get(baseDecl.baseName) ?? defaultSqlNamespaceIdForTarget(targetId)),
2846
+ base: crossRef(baseDecl.baseName, modelNamespaceIds.get(baseDecl.baseName) ?? defaultNamespaceId),
2779
2847
  ...resolvedTable ? { storage: {
2780
2848
  ...variantModel.storage,
2781
2849
  table: resolvedTable
@@ -2963,7 +3031,7 @@ function interpretPslDocumentToSqlContract(input) {
2963
3031
  }
2964
3032
  let patchedModels = patchModelDomainFields(modelsForPatch, modelResolvedFields);
2965
3033
  const polyDiagnostics = [];
2966
- patchedModels = resolvePolymorphism(patchedModels, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, modelNamespaceIds, input.target.targetId, sourceId, polyDiagnostics);
3034
+ patchedModels = resolvePolymorphism(patchedModels, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, modelNamespaceIds, input.target.defaultNamespaceId, sourceId, polyDiagnostics);
2967
3035
  if (polyDiagnostics.length > 0) return notOk({
2968
3036
  summary: "PSL to SQL contract interpretation failed",
2969
3037
  diagnostics: polyDiagnostics
@@ -2976,11 +3044,11 @@ function interpretPslDocumentToSqlContract(input) {
2976
3044
  domain: { namespaces: Object.fromEntries(Object.entries(contract.domain.namespaces).map(([namespaceId, namespaceSlice]) => [namespaceId, {
2977
3045
  models: Object.fromEntries(Object.entries(namespaceSlice.models).map(([modelName, model]) => [modelName, patchedModels[modelName] ?? model])),
2978
3046
  ...namespaceSlice.valueObjects !== void 0 ? { valueObjects: namespaceSlice.valueObjects } : {},
2979
- ...namespaceId === defaultSqlNamespaceIdForTarget(input.target.targetId) && Object.keys(valueObjects).length > 0 ? { valueObjects } : {}
3047
+ ...namespaceId === input.target.defaultNamespaceId && Object.keys(valueObjects).length > 0 ? { valueObjects } : {}
2980
3048
  }])) }
2981
3049
  });
2982
3050
  }
2983
3051
  //#endregion
2984
3052
  export { interpretPslDocumentToSqlContract as t };
2985
3053
 
2986
- //# sourceMappingURL=interpreter-QE6eZZof.mjs.map
3054
+ //# sourceMappingURL=interpreter-B75uZQde.mjs.map