@prisma-next/mongo-contract-psl 0.14.0-dev.5 → 0.14.0-dev.51

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,30 +1,45 @@
1
+ import { MongoIndex, MongoStorage, MongoValidator, applyPolymorphicScopeToMongoIndex, buildMongoNamespace } from "@prisma-next/mongo-contract";
1
2
  import { computeProfileHash, computeStorageHash } from "@prisma-next/contract/hashing";
2
3
  import { crossRef } from "@prisma-next/contract/types";
4
+ import { errorEnumCodecNotInPackStack } from "@prisma-next/errors/control";
5
+ import { instantiateAuthoringEntityType, isAuthoringEntityTypeDescriptor } from "@prisma-next/framework-components/authoring";
3
6
  import { UNBOUND_NAMESPACE_ID } from "@prisma-next/framework-components/ir";
4
- import { MongoIndex, MongoStorage, MongoValidator, applyPolymorphicScopeToMongoIndex, buildMongoNamespace } from "@prisma-next/mongo-contract";
5
7
  import { mongoContractCanonicalizationHooks } from "@prisma-next/mongo-contract/canonicalization-hooks";
8
+ import { nodePslSpan, parseQuotedStringLiteral } from "@prisma-next/psl-parser";
9
+ import { assertDefined } from "@prisma-next/utils/assertions";
6
10
  import { blindCast } from "@prisma-next/utils/casts";
11
+ import { ifDefined } from "@prisma-next/utils/defined";
7
12
  import { notOk, ok } from "@prisma-next/utils/result";
8
- import { getPositionalArgument, parseQuotedStringLiteral } from "@prisma-next/psl-parser";
9
13
  //#region src/derive-json-schema.ts
10
14
  function resolveBsonType(codecId, codecLookup) {
11
15
  return codecLookup?.targetTypesFor(codecId)?.[0];
12
16
  }
13
- function fieldToBsonSchema(field, valueObjects, codecLookup) {
17
+ function fieldToBsonSchema(field, valueObjects, codecLookup, valueSets) {
14
18
  if (field.type.kind === "scalar") {
15
19
  const bsonType = resolveBsonType(field.type.codecId, codecLookup);
16
20
  if (!bsonType) return void 0;
17
- if ("many" in field && field.many) return {
18
- bsonType: "array",
19
- items: { bsonType }
20
- };
21
- if (field.nullable) return { bsonType: ["null", bsonType] };
22
- return { bsonType };
21
+ const enumValues = field.valueSet !== void 0 ? valueSets?.[field.valueSet.entityName]?.values ?? null : null;
22
+ if ("many" in field && field.many) {
23
+ const items = { bsonType };
24
+ if (enumValues) items["enum"] = enumValues;
25
+ return {
26
+ bsonType: "array",
27
+ items
28
+ };
29
+ }
30
+ if (field.nullable) {
31
+ const s = { bsonType: ["null", bsonType] };
32
+ if (enumValues) s["enum"] = [...enumValues, null];
33
+ return s;
34
+ }
35
+ const s = { bsonType };
36
+ if (enumValues) s["enum"] = enumValues;
37
+ return s;
23
38
  }
24
39
  if (field.type.kind === "valueObject") {
25
40
  const vo = valueObjects?.[field.type.name];
26
41
  if (!vo) return void 0;
27
- const voSchema = deriveObjectSchema(vo.fields, valueObjects, codecLookup);
42
+ const voSchema = deriveObjectSchema(vo.fields, valueObjects, codecLookup, valueSets);
28
43
  if ("many" in field && field.many) return {
29
44
  bsonType: "array",
30
45
  items: voSchema
@@ -33,11 +48,11 @@ function fieldToBsonSchema(field, valueObjects, codecLookup) {
33
48
  return voSchema;
34
49
  }
35
50
  }
36
- function deriveObjectSchema(fields, valueObjects, codecLookup) {
51
+ function deriveObjectSchema(fields, valueObjects, codecLookup, valueSets) {
37
52
  const properties = {};
38
53
  const required = [];
39
54
  for (const [fieldName, field] of Object.entries(fields)) {
40
- const schema = fieldToBsonSchema(field, valueObjects, codecLookup);
55
+ const schema = fieldToBsonSchema(field, valueObjects, codecLookup, valueSets);
41
56
  if (schema) {
42
57
  properties[fieldName] = schema;
43
58
  if (!field.nullable) required.push(fieldName);
@@ -54,15 +69,15 @@ function deriveObjectSchema(fields, valueObjects, codecLookup) {
54
69
  function isRecord(value) {
55
70
  return typeof value === "object" && value !== null && !Array.isArray(value);
56
71
  }
57
- function deriveJsonSchema(fields, valueObjects, codecLookup) {
72
+ function deriveJsonSchema(fields, valueObjects, codecLookup, valueSets) {
58
73
  return new MongoValidator({
59
- jsonSchema: deriveObjectSchema(fields, valueObjects, codecLookup),
74
+ jsonSchema: deriveObjectSchema(fields, valueObjects, codecLookup, valueSets),
60
75
  validationLevel: "strict",
61
76
  validationAction: "error"
62
77
  });
63
78
  }
64
- function derivePolymorphicJsonSchema(baseFields, discriminatorField, variants, valueObjects, codecLookup) {
65
- const baseSchema = deriveObjectSchema(baseFields, valueObjects, codecLookup);
79
+ function derivePolymorphicJsonSchema(baseFields, discriminatorField, variants, valueObjects, codecLookup, valueSets) {
80
+ const baseSchema = deriveObjectSchema(baseFields, valueObjects, codecLookup, valueSets);
66
81
  const baseProperties = isRecord(baseSchema["properties"]) ? baseSchema["properties"] : {};
67
82
  const oneOf = [];
68
83
  for (const variant of variants) {
@@ -71,7 +86,7 @@ function derivePolymorphicJsonSchema(baseFields, discriminatorField, variants, v
71
86
  const variantProperties = {};
72
87
  const variantRequired = [discriminatorField];
73
88
  for (const [name, field] of Object.entries(variantOnlyFields)) {
74
- const schema = fieldToBsonSchema(field, valueObjects, codecLookup);
89
+ const schema = fieldToBsonSchema(field, valueObjects, codecLookup, valueSets);
75
90
  if (schema) {
76
91
  variantProperties[name] = schema;
77
92
  if (!field.nullable) variantRequired.push(name);
@@ -99,6 +114,9 @@ function derivePolymorphicJsonSchema(baseFields, discriminatorField, variants, v
99
114
  }
100
115
  //#endregion
101
116
  //#region src/psl-helpers.ts
117
+ function getPositionalArgument(attr, index = 0) {
118
+ return attr.args.filter((arg) => arg.kind === "positional")[index]?.value;
119
+ }
102
120
  function getNamedArgument(attr, name) {
103
121
  return attr.args.find((a) => a.kind === "named" && a.name === name)?.value;
104
122
  }
@@ -173,9 +191,9 @@ function parseRelationAttribute(attributes) {
173
191
  const fields = fieldsArg ? parseFieldList(fieldsArg.value) : void 0;
174
192
  const references = referencesArg ? parseFieldList(referencesArg.value) : void 0;
175
193
  return {
176
- ...relationName !== void 0 ? { relationName } : {},
177
- ...fields !== void 0 ? { fields } : {},
178
- ...references !== void 0 ? { references } : {}
194
+ ...ifDefined("relationName", relationName),
195
+ ...ifDefined("fields", fields),
196
+ ...ifDefined("references", references)
179
197
  };
180
198
  }
181
199
  function stripQuotes(value) {
@@ -185,37 +203,35 @@ function stripQuotes(value) {
185
203
  //#endregion
186
204
  //#region src/interpreter.ts
187
205
  /**
188
- * Name of the framework-parser synthesised bucket for top-level
189
- * declarations. Re-declared locally so the interpreter does not have to
190
- * import from `@prisma-next/framework-components/psl-ast`.
206
+ * Encode an authored enum value to its codec-encoded JSON form via the codec resolved by id from the
207
+ * contract's codec lookup, so a non-identity `encodeJson` (permitted by the `mongoCodec` factory) is
208
+ * respected. Matches the TS builder's `encodeEnumValue`: the lookup is always threaded in production,
209
+ * and a codecId the lookup cannot resolve is a hard error — the enum uses a codec that is not part of
210
+ * the contract's pack stack.
191
211
  */
192
- const UNSPECIFIED_PSL_NAMESPACE_NAME = "__unspecified__";
212
+ function encodeEnumValue(value, codecId, codecLookup) {
213
+ const codec = codecLookup.get(codecId);
214
+ if (!codec) throw errorEnumCodecNotInPackStack({ codecId });
215
+ return codec.encodeJson(value);
216
+ }
193
217
  /**
194
- * Mongo FR16c validation: Mongo's authoring DSL exposes the connection's
195
- * database as the only namespace surface today, so the PSL interpreter
196
- * rejects every explicit `namespace { … }` block. The implicit
197
- * `__unspecified__` bucket (top-level declarations) is the only
198
- * namespace Mongo accepts. `namespace unbound { … }` is rejected too —
199
- * Mongo has no late-binding namespace concept on the PSL surface (the
200
- * database name comes from the connection string, not from PSL).
218
+ * Mongo's PSL surface binds the database from the connection string, so every
219
+ * explicit namespace block is invalid, including `namespace unbound { }`.
201
220
  */
202
221
  function validateNamespaceBlocksForMongoTarget(input) {
203
- for (const namespace of input.namespaces) {
204
- if (namespace.name === UNSPECIFIED_PSL_NAMESPACE_NAME) continue;
205
- input.diagnostics.push({
206
- code: "PSL_UNSUPPORTED_NAMESPACE_BLOCK",
207
- message: `Mongo does not support \`namespace ${namespace.name} { … }\` blocks (the database is bound by the connection string; declare models at the document top level instead).`,
208
- sourceId: input.sourceId,
209
- span: namespace.span
210
- });
211
- }
222
+ for (const namespace of input.namespaces) input.diagnostics.push({
223
+ code: "PSL_UNSUPPORTED_NAMESPACE_BLOCK",
224
+ message: `Mongo does not support \`namespace ${namespace.name} { … }\` blocks (the database is bound by the connection string; declare models at the document top level instead).`,
225
+ sourceId: input.sourceId,
226
+ span: nodePslSpan(namespace.node.syntax, input.sourceFile)
227
+ });
212
228
  }
213
229
  function fkRelationPairKey(declaringModel, targetModel) {
214
230
  return `${declaringModel}::${targetModel}`;
215
231
  }
216
232
  function resolveFieldMappings(model) {
217
233
  const pslNameToMapped = /* @__PURE__ */ new Map();
218
- for (const field of model.fields) {
234
+ for (const field of Object.values(model.fields)) {
219
235
  const mapped = getMapName(field.attributes) ?? field.name;
220
236
  pslNameToMapped.set(field.name, mapped);
221
237
  }
@@ -227,33 +243,32 @@ function resolveCollectionName(model) {
227
243
  function mongoCrossRef(modelName) {
228
244
  return crossRef(modelName, UNBOUND_NAMESPACE_ID);
229
245
  }
230
- function collectPolymorphismDeclarations(document, sourceId, diagnostics) {
246
+ function collectPolymorphismDeclarations(models, sourceId, diagnostics) {
231
247
  const discriminatorDeclarations = /* @__PURE__ */ new Map();
232
248
  const baseDeclarations = /* @__PURE__ */ new Map();
233
- const allPslModels = document.ast.namespaces.flatMap((ns) => ns.models);
234
- for (const pslModel of allPslModels) for (const attr of pslModel.attributes) {
249
+ for (const model of models) for (const attr of model.attributes) {
235
250
  if (attr.name === "discriminator") {
236
251
  const fieldName = getPositionalArgument(attr);
237
252
  if (!fieldName) {
238
253
  diagnostics.push({
239
254
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
240
- message: `Model "${pslModel.name}" @@discriminator requires a field name argument`,
255
+ message: `Model "${model.name}" @@discriminator requires a field name argument`,
241
256
  sourceId,
242
257
  span: attr.span
243
258
  });
244
259
  continue;
245
260
  }
246
- const discField = pslModel.fields.find((f) => f.name === fieldName);
261
+ const discField = model.fields[fieldName];
247
262
  if (discField && discField.typeName !== "String") {
248
263
  diagnostics.push({
249
264
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
250
- message: `Discriminator field "${fieldName}" on model "${pslModel.name}" must be of type String, but is "${discField.typeName}"`,
265
+ message: `Discriminator field "${fieldName}" on model "${model.name}" must be of type String, but is "${discField.typeName}"`,
251
266
  sourceId,
252
267
  span: attr.span
253
268
  });
254
269
  continue;
255
270
  }
256
- discriminatorDeclarations.set(pslModel.name, {
271
+ discriminatorDeclarations.set(model.name, {
257
272
  fieldName,
258
273
  span: attr.span
259
274
  });
@@ -264,7 +279,7 @@ function collectPolymorphismDeclarations(document, sourceId, diagnostics) {
264
279
  if (!baseName || !rawValue) {
265
280
  diagnostics.push({
266
281
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
267
- message: `Model "${pslModel.name}" @@base requires two arguments: base model name and discriminator value`,
282
+ message: `Model "${model.name}" @@base requires two arguments: base model name and discriminator value`,
268
283
  sourceId,
269
284
  span: attr.span
270
285
  });
@@ -274,14 +289,14 @@ function collectPolymorphismDeclarations(document, sourceId, diagnostics) {
274
289
  if (value === void 0) {
275
290
  diagnostics.push({
276
291
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
277
- message: `Model "${pslModel.name}" @@base discriminator value must be a quoted string literal`,
292
+ message: `Model "${model.name}" @@base discriminator value must be a quoted string literal`,
278
293
  sourceId,
279
294
  span: attr.span
280
295
  });
281
296
  continue;
282
297
  }
283
- const collectionName = resolveCollectionName(pslModel);
284
- baseDeclarations.set(pslModel.name, {
298
+ const collectionName = resolveCollectionName(model);
299
+ baseDeclarations.set(model.name, {
285
300
  baseName,
286
301
  value,
287
302
  collectionName,
@@ -295,8 +310,7 @@ function collectPolymorphismDeclarations(document, sourceId, diagnostics) {
295
310
  };
296
311
  }
297
312
  function resolvePolymorphism(input) {
298
- const { discriminatorDeclarations, baseDeclarations, modelNames, sourceId, document, indexSpans, modelIndexesByName } = input;
299
- const allPslModels = document.ast.namespaces.flatMap((ns) => ns.models);
313
+ const { discriminatorDeclarations, baseDeclarations, modelNames, sourceId, allModels: allModelViews, indexSpans, modelIndexesByName } = input;
300
314
  let patched = input.models;
301
315
  let roots = input.roots;
302
316
  let collections = input.collections;
@@ -313,8 +327,8 @@ function resolvePolymorphism(input) {
313
327
  }
314
328
  const model = patched[modelName];
315
329
  if (!model) continue;
316
- const pslModel = allPslModels.find((m) => m.name === modelName);
317
- const mappedDiscriminatorField = pslModel ? resolveFieldMappings(pslModel).pslNameToMapped.get(decl.fieldName) ?? decl.fieldName : decl.fieldName;
330
+ const modelView = allModelViews.find((m) => m.name === modelName);
331
+ const mappedDiscriminatorField = modelView ? resolveFieldMappings(modelView).pslNameToMapped.get(decl.fieldName) ?? decl.fieldName : decl.fieldName;
318
332
  if (!Object.hasOwn(model.fields, mappedDiscriminatorField)) {
319
333
  diagnostics.push({
320
334
  code: "PSL_DISCRIMINATOR_FIELD_NOT_FOUND",
@@ -368,9 +382,9 @@ function resolvePolymorphism(input) {
368
382
  }
369
383
  if (discriminatorDeclarations.has(variantName)) continue;
370
384
  const baseModel = patched[baseDecl.baseName];
371
- const variantPslModel = allPslModels.find((m) => m.name === variantName);
372
- if (!variantPslModel) continue;
373
- if (getMapName(variantPslModel.attributes) !== void 0 && baseModel && baseDecl.collectionName !== baseModel.storage.collection) {
385
+ const variantModelView = allModelViews.find((m) => m.name === variantName);
386
+ if (!variantModelView) continue;
387
+ if (getMapName(variantModelView.attributes) !== void 0 && baseModel && baseDecl.collectionName !== baseModel.storage.collection) {
374
388
  diagnostics.push({
375
389
  code: "PSL_MONGO_VARIANT_SEPARATE_COLLECTION",
376
390
  message: `Mongo variant "${variantName}" cannot use a different collection than its base "${baseDecl.baseName}". Mongo only supports single-collection polymorphism.`,
@@ -389,7 +403,7 @@ function resolvePolymorphism(input) {
389
403
  storage: { collection: baseCollection }
390
404
  }
391
405
  };
392
- const variantCollectionName = resolveCollectionName(variantPslModel);
406
+ const variantCollectionName = resolveCollectionName(variantModelView);
393
407
  if (roots[variantCollectionName]?.model === variantName) if (variantCollectionName === baseCollection && baseModel) roots = {
394
408
  ...roots,
395
409
  [variantCollectionName]: mongoCrossRef(baseDecl.baseName)
@@ -531,11 +545,11 @@ function collectIndexes(pslModel, fieldMappings, modelNames, sourceId, diagnosti
531
545
  const indexes = [];
532
546
  let textIndexCount = 0;
533
547
  const indexableFieldNames = /* @__PURE__ */ new Set();
534
- for (const f of pslModel.fields) {
548
+ for (const f of Object.values(pslModel.fields)) {
535
549
  if (modelNames.has(f.typeName)) continue;
536
550
  indexableFieldNames.add(f.name);
537
551
  }
538
- for (const field of pslModel.fields) {
552
+ for (const field of Object.values(pslModel.fields)) {
539
553
  if (modelNames.has(field.typeName)) continue;
540
554
  const uniqueAttr = getAttribute(field.attributes, "unique");
541
555
  if (!uniqueAttr) continue;
@@ -729,7 +743,7 @@ const MONGO_OBJECT_ID_PSL_TYPE = "ObjectId";
729
743
  function resolveFieldCodecId(field, scalarTypeDescriptors) {
730
744
  return scalarTypeDescriptors.get(field.typeName);
731
745
  }
732
- function resolveNonRelationField(field, ownerName, compositeTypeNames, scalarTypeDescriptors, sourceId, diagnostics) {
746
+ function resolveNonRelationField(field, ownerName, compositeTypeNames, scalarTypeDescriptors, codecIdByEnumName, sourceId, diagnostics) {
733
747
  if (compositeTypeNames.has(field.typeName)) {
734
748
  const result = {
735
749
  type: {
@@ -743,6 +757,28 @@ function resolveNonRelationField(field, ownerName, compositeTypeNames, scalarTyp
743
757
  many: true
744
758
  } : result;
745
759
  }
760
+ const enumCodecId = codecIdByEnumName.get(field.typeName);
761
+ if (enumCodecId !== void 0) {
762
+ const valueSet = {
763
+ plane: "domain",
764
+ entityKind: "enum",
765
+ namespaceId: UNBOUND_NAMESPACE_ID,
766
+ entityName: field.typeName
767
+ };
768
+ const result = {
769
+ type: {
770
+ kind: "scalar",
771
+ codecId: enumCodecId
772
+ },
773
+ nullable: field.optional,
774
+ valueSet
775
+ };
776
+ return field.list ? {
777
+ ...result,
778
+ many: true
779
+ } : result;
780
+ }
781
+ if (field.malformedType) return;
746
782
  const codecId = resolveFieldCodecId(field, scalarTypeDescriptors);
747
783
  if (!codecId) {
748
784
  diagnostics.push({
@@ -765,19 +801,64 @@ function resolveNonRelationField(field, ownerName, compositeTypeNames, scalarTyp
765
801
  many: true
766
802
  } : result;
767
803
  }
804
+ function processEnumDeclarations(input) {
805
+ const builtEnums = {};
806
+ if (input.enumBlocks.length === 0) return builtEnums;
807
+ const enumDescriptor = input.authoringContributions?.entityTypes?.["enum"] !== void 0 && isAuthoringEntityTypeDescriptor(input.authoringContributions.entityTypes["enum"]) ? input.authoringContributions.entityTypes["enum"] : void 0;
808
+ if (!enumDescriptor) {
809
+ for (const decl of input.enumBlocks) input.diagnostics.push({
810
+ code: "PSL_ENUM_MISSING_FACTORY",
811
+ message: `enum "${decl.name}" requires an "enum" entityType factory in the active authoring contributions`,
812
+ sourceId: input.sourceId,
813
+ span: decl.span
814
+ });
815
+ return builtEnums;
816
+ }
817
+ for (const decl of input.enumBlocks) {
818
+ const handle = instantiateAuthoringEntityType("enum", enumDescriptor, [decl], input.entityContext);
819
+ if (handle === void 0 || handle === null) continue;
820
+ builtEnums[decl.name] = {
821
+ codecId: handle.codecId,
822
+ members: handle.enumMembers.map((m) => ({
823
+ name: m.name,
824
+ value: blindCast(m.value)
825
+ }))
826
+ };
827
+ }
828
+ return builtEnums;
829
+ }
768
830
  function interpretPslDocumentToMongoContract(input) {
769
- const { document, scalarTypeDescriptors, codecLookup } = input;
770
- const sourceId = document.ast.sourceId;
771
- const diagnostics = [];
831
+ const { symbolTable, sourceFile, scalarTypeDescriptors, codecLookup } = input;
832
+ const sourceId = input.sourceId;
833
+ const diagnostics = [...input.seedDiagnostics ?? []];
834
+ const topLevel = symbolTable.topLevel;
772
835
  validateNamespaceBlocksForMongoTarget({
773
- namespaces: document.ast.namespaces,
836
+ namespaces: Object.values(topLevel.namespaces),
774
837
  sourceId,
838
+ sourceFile,
775
839
  diagnostics
776
840
  });
777
- const allModels = document.ast.namespaces.flatMap((ns) => ns.models);
778
- const allCompositeTypes = document.ast.namespaces.flatMap((ns) => ns.compositeTypes);
841
+ const allModels = Object.values(topLevel.models);
842
+ const allCompositeTypes = Object.values(topLevel.compositeTypes);
779
843
  const modelNames = new Set(allModels.map((m) => m.name));
780
844
  const compositeTypeNames = new Set(allCompositeTypes.map((ct) => ct.name));
845
+ const builtEnums = processEnumDeclarations({
846
+ enumBlocks: Object.values(topLevel.blocks).filter((b) => b.keyword === "enum").map((b) => b.block),
847
+ sourceId,
848
+ authoringContributions: input.authoringContributions,
849
+ entityContext: {
850
+ family: "mongo",
851
+ target: "mongo",
852
+ ...ifDefined("enumInferenceCodecs", input.enumInferenceCodecs),
853
+ ...ifDefined("codecLookup", codecLookup),
854
+ sourceId,
855
+ diagnostics: { push: (d) => {
856
+ diagnostics.push(blindCast(d));
857
+ } }
858
+ },
859
+ diagnostics
860
+ });
861
+ const codecIdByEnumName = new Map(Object.entries(builtEnums).map(([name, e]) => [name, e.codecId]));
781
862
  const models = {};
782
863
  const collections = {};
783
864
  const roots = {};
@@ -790,7 +871,7 @@ function interpretPslDocumentToMongoContract(input) {
790
871
  const fieldMappings = resolveFieldMappings(pslModel);
791
872
  const fields = {};
792
873
  const relations = {};
793
- for (const field of pslModel.fields) {
874
+ for (const field of Object.values(pslModel.fields)) {
794
875
  if (isRelationField(field, modelNames)) {
795
876
  const relation = parseRelationAttribute(field.attributes);
796
877
  if (field.list || !(relation?.fields && relation?.references)) {
@@ -798,7 +879,7 @@ function interpretPslDocumentToMongoContract(input) {
798
879
  modelName: pslModel.name,
799
880
  fieldName: field.name,
800
881
  targetModelName: field.typeName,
801
- ...relation?.relationName !== void 0 ? { relationName: relation.relationName } : {},
882
+ ...ifDefined("relationName", relation?.relationName),
802
883
  cardinality: field.list ? "1:N" : "1:1",
803
884
  field
804
885
  });
@@ -821,20 +902,20 @@ function interpretPslDocumentToMongoContract(input) {
821
902
  declaringModel: pslModel.name,
822
903
  fieldName: field.name,
823
904
  targetModel: field.typeName,
824
- ...relation.relationName !== void 0 ? { relationName: relation.relationName } : {},
905
+ ...ifDefined("relationName", relation.relationName),
825
906
  localFields: localMapped,
826
907
  targetFields: targetMapped
827
908
  });
828
909
  }
829
910
  continue;
830
911
  }
831
- const resolved = resolveNonRelationField(field, pslModel.name, compositeTypeNames, scalarTypeDescriptors, sourceId, diagnostics);
912
+ const resolved = resolveNonRelationField(field, pslModel.name, compositeTypeNames, scalarTypeDescriptors, codecIdByEnumName, sourceId, diagnostics);
832
913
  if (!resolved) continue;
833
914
  const mappedName = fieldMappings.pslNameToMapped.get(field.name) ?? field.name;
834
915
  fields[mappedName] = resolved;
835
916
  }
836
917
  const isVariantModel = pslModel.attributes.some((attr) => attr.name === "base");
837
- const hasIdField = pslModel.fields.some((f) => getAttribute(f.attributes, "id") !== void 0);
918
+ const hasIdField = Object.values(pslModel.fields).some((f) => getAttribute(f.attributes, "id") !== void 0);
838
919
  if (!isVariantModel) if (!hasIdField) diagnostics.push({
839
920
  code: "PSL_MISSING_ID_FIELD",
840
921
  message: `Model "${pslModel.name}" has no field with @id attribute. Every model must have exactly one @id field.`,
@@ -864,8 +945,8 @@ function interpretPslDocumentToMongoContract(input) {
864
945
  const valueObjects = {};
865
946
  for (const compositeType of allCompositeTypes) {
866
947
  const fields = {};
867
- for (const field of compositeType.fields) {
868
- const resolved = resolveNonRelationField(field, compositeType.name, compositeTypeNames, scalarTypeDescriptors, sourceId, diagnostics);
948
+ for (const field of Object.values(compositeType.fields)) {
949
+ const resolved = resolveNonRelationField(field, compositeType.name, compositeTypeNames, scalarTypeDescriptors, codecIdByEnumName, sourceId, diagnostics);
869
950
  if (!resolved) continue;
870
951
  fields[field.name] = resolved;
871
952
  }
@@ -913,12 +994,12 @@ function interpretPslDocumentToMongoContract(input) {
913
994
  }
914
995
  };
915
996
  }
916
- const { discriminatorDeclarations, baseDeclarations } = collectPolymorphismDeclarations(document, sourceId, diagnostics);
997
+ const { discriminatorDeclarations, baseDeclarations } = collectPolymorphismDeclarations(allModels, sourceId, diagnostics);
917
998
  const polyResult = resolvePolymorphism({
918
999
  models,
919
1000
  roots,
920
1001
  collections,
921
- document,
1002
+ allModels,
922
1003
  discriminatorDeclarations,
923
1004
  baseDeclarations,
924
1005
  modelNames,
@@ -932,6 +1013,15 @@ function interpretPslDocumentToMongoContract(input) {
932
1013
  });
933
1014
  const resolvedModels = polyResult.models;
934
1015
  const resolvedCollections = polyResult.collections;
1016
+ const storageValueSets = {};
1017
+ const enumEntries = Object.entries(builtEnums);
1018
+ if (enumEntries.length > 0) {
1019
+ assertDefined(codecLookup, "Mongo PSL interpretation requires a codec lookup to encode enum values");
1020
+ for (const [enumName, builtEnum] of enumEntries) storageValueSets[enumName] = {
1021
+ kind: "valueSet",
1022
+ values: builtEnum.members.map((m) => encodeEnumValue(m.value, builtEnum.codecId, codecLookup))
1023
+ };
1024
+ }
935
1025
  for (const [, modelEntry] of Object.entries(resolvedModels)) {
936
1026
  if (modelEntry.base) continue;
937
1027
  const coll = resolvedCollections[modelEntry.storage.collection];
@@ -941,8 +1031,8 @@ function interpretPslDocumentToMongoContract(input) {
941
1031
  discriminatorValue: value,
942
1032
  fields: resolvedModels[variantName]?.fields ?? {}
943
1033
  }));
944
- coll["validator"] = derivePolymorphicJsonSchema(modelEntry.fields, modelEntry.discriminator.field, variantEntries, valueObjects, codecLookup);
945
- } else coll["validator"] = deriveJsonSchema(modelEntry.fields, valueObjects, codecLookup);
1034
+ coll["validator"] = derivePolymorphicJsonSchema(modelEntry.fields, modelEntry.discriminator.field, variantEntries, valueObjects, codecLookup, storageValueSets);
1035
+ } else coll["validator"] = deriveJsonSchema(modelEntry.fields, valueObjects, codecLookup, storageValueSets);
946
1036
  }
947
1037
  const target = "mongo";
948
1038
  const targetFamily = "mongo";
@@ -956,7 +1046,10 @@ function interpretPslDocumentToMongoContract(input) {
956
1046
  }
957
1047
  const unboundNamespace = buildMongoNamespace({
958
1048
  id: UNBOUND_NAMESPACE_ID,
959
- entries: { collection: collectionInputs }
1049
+ entries: {
1050
+ collection: collectionInputs,
1051
+ ...Object.keys(storageValueSets).length > 0 ? { valueSet: storageValueSets } : {}
1052
+ }
960
1053
  });
961
1054
  const storage = new MongoStorage({
962
1055
  storageHash: computeStorageHash({
@@ -964,20 +1057,25 @@ function interpretPslDocumentToMongoContract(input) {
964
1057
  targetFamily,
965
1058
  storage: { namespaces: { [UNBOUND_NAMESPACE_ID]: {
966
1059
  id: UNBOUND_NAMESPACE_ID,
967
- entries: { collection: unboundNamespace.entries.collection }
1060
+ entries: {
1061
+ collection: unboundNamespace.entries.collection,
1062
+ ...unboundNamespace.entries.valueSet !== void 0 ? { valueSet: unboundNamespace.entries.valueSet } : {}
1063
+ }
968
1064
  } } },
969
1065
  ...mongoContractCanonicalizationHooks
970
1066
  }),
971
1067
  namespaces: { [UNBOUND_NAMESPACE_ID]: unboundNamespace }
972
1068
  });
973
1069
  const capabilities = {};
1070
+ const hasEnums = Object.keys(builtEnums).length > 0;
974
1071
  return ok({
975
1072
  targetFamily,
976
1073
  target,
977
1074
  roots: polyResult.roots,
978
1075
  domain: { namespaces: { [UNBOUND_NAMESPACE_ID]: {
979
1076
  models: polyResult.models,
980
- ...Object.keys(valueObjects).length > 0 ? { valueObjects } : {}
1077
+ ...Object.keys(valueObjects).length > 0 ? { valueObjects } : {},
1078
+ ...hasEnums ? { enum: builtEnums } : {}
981
1079
  } } },
982
1080
  storage,
983
1081
  extensionPacks: {},
@@ -991,6 +1089,6 @@ function interpretPslDocumentToMongoContract(input) {
991
1089
  });
992
1090
  }
993
1091
  //#endregion
994
- export { interpretPslDocumentToMongoContract as t };
1092
+ export { deriveJsonSchema as n, derivePolymorphicJsonSchema as r, interpretPslDocumentToMongoContract as t };
995
1093
 
996
- //# sourceMappingURL=interpreter-Cj1vigKn.mjs.map
1094
+ //# sourceMappingURL=interpreter-D0vfRJ7f.mjs.map