@prisma-next/sql-contract-psl 0.12.0-dev.46 → 0.12.0-dev.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
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"}
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;;;UCiDb,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;EDxDzB;;;AAAmB;;;;ACiD9B;;EDjDW,SCkEA,eAAA,IAAmB,KAAA,EAAO,uBAAA,KAA4B,SAAA;AAAA;AAAA,iBAwgDjD,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-BTfcjVFM.mjs";
1
+ import { t as interpretPslDocumentToSqlContract } from "./interpreter-Rn_Vzvt-.mjs";
2
2
  export { interpretPslDocumentToSqlContract };
@@ -2,10 +2,10 @@ 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
3
  import { isPostgresEnumStorageEntry } from "@prisma-next/sql-contract/types";
4
4
  import { buildSqlContractFromDefinition } from "@prisma-next/sql-contract-ts/contract-builder";
5
+ import { blindCast } from "@prisma-next/utils/casts";
5
6
  import { ifDefined } from "@prisma-next/utils/defined";
6
7
  import { notOk, ok } from "@prisma-next/utils/result";
7
8
  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) {
@@ -2757,8 +2757,16 @@ function collectPolymorphismDeclarations(models, sourceId, diagnostics) {
2757
2757
  baseDeclarations
2758
2758
  };
2759
2759
  }
2760
- function resolvePolymorphism(models, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, modelNamespaceIds, defaultNamespaceId, sourceId, diagnostics) {
2760
+ function resolvePolymorphism(models, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, modelNamespaceIds, defaultNamespaceId, syntheticPkFieldsByVariant, stiBaseFieldsByBase, sourceId, diagnostics) {
2761
2761
  let patched = models;
2762
+ for (const [baseName, fieldNames] of stiBaseFieldsByBase) {
2763
+ const baseModel = patched[baseName];
2764
+ if (!baseModel || fieldNames.length === 0) continue;
2765
+ patched = {
2766
+ ...patched,
2767
+ [baseName]: stripStorageOnlyDomainFields(baseModel, fieldNames)
2768
+ };
2769
+ }
2762
2770
  for (const [modelName, decl] of discriminatorDeclarations) {
2763
2771
  if (baseDeclarations.has(modelName)) {
2764
2772
  diagnostics.push({
@@ -2840,20 +2848,168 @@ function resolvePolymorphism(models, discriminatorDeclarations, baseDeclarations
2840
2848
  const baseMapping = modelMappings.get(baseDecl.baseName);
2841
2849
  const variantMapping = modelMappings.get(variantName);
2842
2850
  const resolvedTable = variantMapping?.model.attributes.some((attr) => attr.name === "map") ?? false ? variantMapping?.tableName : baseMapping?.tableName;
2851
+ const patchedVariant = {
2852
+ ...variantModel,
2853
+ base: crossRef(baseDecl.baseName, modelNamespaceIds.get(baseDecl.baseName) ?? defaultNamespaceId),
2854
+ ...resolvedTable ? { storage: {
2855
+ ...variantModel.storage,
2856
+ table: resolvedTable
2857
+ } } : {}
2858
+ };
2843
2859
  patched = {
2844
2860
  ...patched,
2845
- [variantName]: {
2846
- ...variantModel,
2847
- base: crossRef(baseDecl.baseName, modelNamespaceIds.get(baseDecl.baseName) ?? defaultNamespaceId),
2848
- ...resolvedTable ? { storage: {
2849
- ...variantModel.storage,
2850
- table: resolvedTable
2851
- } } : {}
2852
- }
2861
+ [variantName]: stripStorageOnlyDomainFields(patchedVariant, syntheticPkFieldsByVariant.get(variantName) ?? [])
2853
2862
  };
2854
2863
  }
2855
2864
  return patched;
2856
2865
  }
2866
+ /**
2867
+ * Multi-table-inheritance variants (`@@base` + their own `@@map`) live in a
2868
+ * separate table from their base. The ORM joins that table to the base on the
2869
+ * shared primary key (`base.id = variant.id`), so the variant storage table
2870
+ * must carry the base PK column even though the variant domain model declares
2871
+ * only its own fields. This enriches each MTI variant's `ModelNode` with that
2872
+ * link column, a primary key on it, and a FK back to the base table.
2873
+ *
2874
+ * The link column is reported back per variant in `syntheticPkFieldsByVariant`
2875
+ * so the domain-model patch can drop it again — keeping the variant's domain
2876
+ * surface thin (its create/read inputs don't gain a redundant `id`) while the
2877
+ * storage table stays joinable. Single-table-inheritance variants (no own
2878
+ * table) are left untouched.
2879
+ */
2880
+ function materializeMtiVariantStorageLinks(modelNodes, baseDeclarations, stiVariantNames) {
2881
+ const nodeByModel = new Map(modelNodes.map((node) => [node.modelName, node]));
2882
+ const syntheticPkFieldsByVariant = /* @__PURE__ */ new Map();
2883
+ return {
2884
+ modelNodes: modelNodes.map((node) => {
2885
+ const baseDecl = baseDeclarations.get(node.modelName);
2886
+ if (!baseDecl) return node;
2887
+ const baseNode = nodeByModel.get(baseDecl.baseName);
2888
+ if (!baseNode) return node;
2889
+ if (stiVariantNames.has(node.modelName)) return node;
2890
+ const basePrimaryKey = baseNode.id;
2891
+ if (!basePrimaryKey || basePrimaryKey.columns.length === 0) return node;
2892
+ const existingColumns = new Set(node.fields.map((field) => field.columnName));
2893
+ const linkFields = [];
2894
+ for (const pkColumn of basePrimaryKey.columns) {
2895
+ if (existingColumns.has(pkColumn)) continue;
2896
+ const baseField = baseNode.fields.find((field) => "descriptor" in field && field.columnName === pkColumn);
2897
+ if (!baseField) continue;
2898
+ linkFields.push({
2899
+ fieldName: baseField.fieldName,
2900
+ columnName: pkColumn,
2901
+ descriptor: baseField.descriptor,
2902
+ nullable: false
2903
+ });
2904
+ }
2905
+ if (linkFields.length === 0) return node;
2906
+ syntheticPkFieldsByVariant.set(node.modelName, linkFields.map((field) => field.fieldName));
2907
+ const foreignKey = {
2908
+ columns: basePrimaryKey.columns,
2909
+ references: {
2910
+ model: baseNode.modelName,
2911
+ table: baseNode.tableName,
2912
+ columns: basePrimaryKey.columns,
2913
+ ...ifDefined("namespaceId", baseNode.namespaceId)
2914
+ },
2915
+ constraint: true,
2916
+ index: false,
2917
+ onDelete: "cascade"
2918
+ };
2919
+ return {
2920
+ ...node,
2921
+ fields: [...linkFields, ...node.fields],
2922
+ id: { columns: basePrimaryKey.columns },
2923
+ foreignKeys: [...node.foreignKeys ?? [], foreignKey]
2924
+ };
2925
+ }),
2926
+ syntheticPkFieldsByVariant
2927
+ };
2928
+ }
2929
+ /**
2930
+ * Single-table-inheritance variants (`@@base` with no own `@@map`) share the
2931
+ * base table: `resolvePolymorphism` points the variant's `storage.table` at the
2932
+ * base, and the ORM reads variant-declared fields straight off the base table.
2933
+ * For that to validate and round-trip, the base storage table must physically
2934
+ * carry every STI variant's declared columns. This enriches the base
2935
+ * `ModelNode` with those columns.
2936
+ *
2937
+ * The materialised columns are always nullable in storage: the base table hosts
2938
+ * every variant's rows, so a column a variant declares as required is still
2939
+ * NULL on sibling-variant rows. The variant's domain field keeps its declared
2940
+ * nullability — required-in-domain / nullable-in-storage is the intended STI
2941
+ * shape.
2942
+ *
2943
+ * Collisions (two variants declaring the same column, or a variant column name
2944
+ * clashing with a base column) are resolved skip-if-exists here, mirroring the
2945
+ * MTI link guard; surfacing them as diagnostics is tracked separately
2946
+ * (TML-2827).
2947
+ */
2948
+ function materializeStiVariantStorageColumns(modelNodes, baseDeclarations, stiVariantNames) {
2949
+ if (stiVariantNames.size === 0) return {
2950
+ modelNodes: [...modelNodes],
2951
+ stiBaseFieldsByBase: /* @__PURE__ */ new Map()
2952
+ };
2953
+ const nodeByModel = new Map(modelNodes.map((node) => [node.modelName, node]));
2954
+ const stiColumnsByBase = /* @__PURE__ */ new Map();
2955
+ for (const variantName of stiVariantNames) {
2956
+ const variantNode = nodeByModel.get(variantName);
2957
+ const baseDecl = baseDeclarations.get(variantName);
2958
+ if (!variantNode || !baseDecl) continue;
2959
+ const baseNode = nodeByModel.get(baseDecl.baseName);
2960
+ if (!baseNode) continue;
2961
+ const baseColumns = new Set(baseNode.fields.map((field) => field.columnName));
2962
+ const claimed = stiColumnsByBase.get(baseDecl.baseName) ?? [];
2963
+ const claimedColumns = new Set(claimed.map((field) => field.columnName));
2964
+ for (const field of variantNode.fields) {
2965
+ if (baseColumns.has(field.columnName) || claimedColumns.has(field.columnName)) continue;
2966
+ claimedColumns.add(field.columnName);
2967
+ claimed.push({
2968
+ ...field,
2969
+ nullable: true
2970
+ });
2971
+ }
2972
+ stiColumnsByBase.set(baseDecl.baseName, claimed);
2973
+ }
2974
+ const stiBaseFieldsByBase = /* @__PURE__ */ new Map();
2975
+ for (const [baseName, columns] of stiColumnsByBase) stiBaseFieldsByBase.set(baseName, columns.map((field) => field.fieldName));
2976
+ return {
2977
+ modelNodes: modelNodes.map((node) => {
2978
+ if (stiVariantNames.has(node.modelName)) return {
2979
+ ...node,
2980
+ sharesBaseTable: true
2981
+ };
2982
+ const stiColumns = stiColumnsByBase.get(node.modelName);
2983
+ if (!stiColumns || stiColumns.length === 0) return node;
2984
+ return {
2985
+ ...node,
2986
+ fields: [...node.fields, ...stiColumns]
2987
+ };
2988
+ }),
2989
+ stiBaseFieldsByBase
2990
+ };
2991
+ }
2992
+ /**
2993
+ * Drop the storage-only link fields (added by
2994
+ * {@link materializeMtiVariantStorageLinks}) from a variant's domain model, so
2995
+ * the domain surface stays thin while the storage table keeps the link column.
2996
+ */
2997
+ function stripStorageOnlyDomainFields(model, fieldNames) {
2998
+ if (fieldNames.length === 0) return model;
2999
+ const fields = { ...model.fields };
3000
+ for (const name of fieldNames) delete fields[name];
3001
+ const storage = blindCast(model.storage);
3002
+ const storageFields = { ...storage.fields };
3003
+ for (const name of fieldNames) delete storageFields[name];
3004
+ return {
3005
+ ...model,
3006
+ fields,
3007
+ storage: {
3008
+ ...storage,
3009
+ fields: storageFields
3010
+ }
3011
+ };
3012
+ }
2857
3013
  function interpretPslDocumentToSqlContract(input) {
2858
3014
  const sourceId = input.document.ast.sourceId;
2859
3015
  if (!input.target) return notOk({
@@ -2998,6 +3154,10 @@ function interpretPslDocumentToSqlContract(input) {
2998
3154
  sourceId
2999
3155
  });
3000
3156
  const { discriminatorDeclarations, baseDeclarations } = collectPolymorphismDeclarations(models, sourceId, diagnostics);
3157
+ const stiVariantNames = /* @__PURE__ */ new Set();
3158
+ for (const variantName of baseDeclarations.keys()) if (!(modelMappings.get(variantName)?.model.attributes.some((attr) => attr.name === "map") ?? false)) stiVariantNames.add(variantName);
3159
+ const { modelNodes: mtiLinkedModelNodes, syntheticPkFieldsByVariant } = materializeMtiVariantStorageLinks(modelNodes, baseDeclarations, stiVariantNames);
3160
+ const { modelNodes: stiColumnModelNodes, stiBaseFieldsByBase } = materializeStiVariantStorageColumns(mtiLinkedModelNodes, baseDeclarations, stiVariantNames);
3001
3161
  const valueObjects = buildValueObjects({
3002
3162
  compositeTypes,
3003
3163
  enumTypeDescriptors: allEnumTypeDescriptors,
@@ -3020,7 +3180,7 @@ function interpretPslDocumentToSqlContract(input) {
3020
3180
  ...Object.keys(storageTypes).length > 0 ? { storageTypes } : {},
3021
3181
  ...Object.keys(namespaceEnumStorageTypes).length > 0 ? { namespaceTypes: namespaceEnumStorageTypes } : {},
3022
3182
  ...ifDefined("createNamespace", input.createNamespace),
3023
- models: modelNodes.map((model) => ({
3183
+ models: stiColumnModelNodes.map((model) => ({
3024
3184
  ...model,
3025
3185
  ...modelRelations.has(model.modelName) ? { relations: [...modelRelations.get(model.modelName) ?? []].sort((left, right) => compareStrings(left.fieldName, right.fieldName)) } : {}
3026
3186
  }))
@@ -3032,7 +3192,7 @@ function interpretPslDocumentToSqlContract(input) {
3032
3192
  }
3033
3193
  let patchedModels = patchModelDomainFields(modelsForPatch, modelResolvedFields);
3034
3194
  const polyDiagnostics = [];
3035
- patchedModels = resolvePolymorphism(patchedModels, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, modelNamespaceIds, input.target.defaultNamespaceId, sourceId, polyDiagnostics);
3195
+ patchedModels = resolvePolymorphism(patchedModels, discriminatorDeclarations, baseDeclarations, modelNames, modelMappings, modelNamespaceIds, input.target.defaultNamespaceId, syntheticPkFieldsByVariant, stiBaseFieldsByBase, sourceId, polyDiagnostics);
3036
3196
  if (polyDiagnostics.length > 0) return notOk({
3037
3197
  summary: "PSL to SQL contract interpretation failed",
3038
3198
  diagnostics: polyDiagnostics
@@ -3052,4 +3212,4 @@ function interpretPslDocumentToSqlContract(input) {
3052
3212
  //#endregion
3053
3213
  export { interpretPslDocumentToSqlContract as t };
3054
3214
 
3055
- //# sourceMappingURL=interpreter-BTfcjVFM.mjs.map
3215
+ //# sourceMappingURL=interpreter-Rn_Vzvt-.mjs.map