@prisma/orm-family-mongo 8.0.0-rc.8-dev.2 → 8.0.0-rc.8-dev.3

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.
@@ -7,11 +7,11 @@ import { blindCast } from "@prisma/orm-framework/utils/casts";
7
7
  import { computeProfileHash, computeStorageHash } from "@prisma/orm-framework/contract/hashing";
8
8
  import { errorEnumCodecNotInPackStack } from "@prisma/orm-framework/errors/control";
9
9
  import { instantiateAuthoringEntityType, isAuthoringEntityTypeDescriptor } from "@prisma/orm-framework/components/authoring";
10
- import { nodePslSpan, parseQuotedStringLiteral } from "@prisma/orm-framework/psl-parser";
10
+ import { bool, entityRef, fieldAttribute, fieldRef, funcCall, identifier, int, interpretAttribute, json, list, modelAttribute, nodePslSpan, num, oneOf, optional, record, str } from "@prisma/orm-framework/psl-parser";
11
11
  import { assertDefined } from "@prisma/orm-framework/utils/assertions";
12
12
  import { ifDefined } from "@prisma/orm-framework/utils/defined";
13
13
  import { notOk, ok } from "@prisma/orm-framework/utils/result";
14
- //#region ../../../2-mongo-family/2-authoring/contract-psl/dist/interpreter-BxbUTQ3k.mjs
14
+ //#region ../../../2-mongo-family/2-authoring/contract-psl/dist/interpreter-O09WlHk7.mjs
15
15
  function resolveBsonType(codecId, codecLookup) {
16
16
  return codecLookup?.targetTypesFor(codecId)?.[0];
17
17
  }
@@ -113,91 +113,166 @@ function derivePolymorphicJsonSchema(baseFields, discriminatorField, variants, v
113
113
  validationAction: "error"
114
114
  });
115
115
  }
116
- function getPositionalArgument(attr, index = 0) {
117
- return attr.args.filter((arg) => arg.kind === "positional")[index]?.value;
116
+ function findModelAttributeNode(model, name) {
117
+ for (const attribute of model.node.attributes()) if (attribute.name()?.isSimpleName(name) === true) return attribute;
118
118
  }
119
- function getNamedArgument(attr, name) {
120
- return attr.args.find((a) => a.kind === "named" && a.name === name)?.value;
119
+ function findFieldAttributeNode(field, name) {
120
+ for (const attribute of field.node.attributes()) if (attribute.name()?.isSimpleName(name) === true) return attribute;
121
121
  }
122
- function parseFieldList(value) {
123
- const inner = value.replace(/^\[/, "").replace(/\]$/, "").trim();
124
- if (inner.length === 0) return [];
125
- return splitTopLevel(inner).map((s) => s.trim());
126
- }
127
- function parseIndexFieldList(value) {
128
- return parseFieldList(value).map(parseIndexFieldSegment);
129
- }
130
- function parseIndexFieldSegment(segment) {
131
- const wildcardMatch = segment.match(/^wildcard\(\s*(.*?)\s*\)$/);
132
- if (wildcardMatch) {
133
- const scope = wildcardMatch[1] ?? "";
134
- return {
135
- name: scope.length > 0 ? `${scope}.$**` : "$**",
136
- isWildcard: true
137
- };
138
- }
139
- const modifierMatch = segment.match(/^(\w+)\(\s*sort:\s*(\w+)\s*\)$/);
140
- if (modifierMatch) return {
141
- name: modifierMatch[1] ?? segment,
142
- isWildcard: false,
143
- direction: modifierMatch[2] === "Desc" ? -1 : 1
122
+ function buildModelInterpretCtx(input) {
123
+ return {
124
+ level: "model",
125
+ sourceId: input.sourceId,
126
+ sourceFile: input.sourceFile,
127
+ selfModel: input.selfModel,
128
+ resolveReferencedModel: () => void 0
144
129
  };
130
+ }
131
+ function buildFieldInterpretCtx(input) {
145
132
  return {
146
- name: segment,
147
- isWildcard: false
133
+ level: "field",
134
+ sourceId: input.sourceId,
135
+ sourceFile: input.sourceFile,
136
+ selfModel: input.selfModel,
137
+ resolveReferencedModel: input.resolveReferencedModel ?? (() => void 0),
138
+ field: input.field
148
139
  };
149
140
  }
150
- function splitTopLevel(input) {
151
- const parts = [];
152
- let depth = 0;
153
- let start = 0;
154
- for (let i = 0; i < input.length; i++) {
155
- const ch = input[i];
156
- if (ch === "(" || ch === "[" || ch === "{") depth++;
157
- else if (ch === ")" || ch === "]" || ch === "}") depth = Math.max(0, depth - 1);
158
- else if (ch === "," && depth === 0) {
159
- parts.push(input.slice(start, i));
160
- start = i + 1;
161
- }
141
+ function interpretModelAttribute(input) {
142
+ const result = interpretAttribute(input.node, input.spec, buildModelInterpretCtx({
143
+ selfModel: input.model,
144
+ sourceFile: input.sourceFile,
145
+ sourceId: input.sourceId
146
+ }));
147
+ if (!result.ok) {
148
+ for (const failure of result.failure) input.diagnostics.push(failure);
149
+ return;
162
150
  }
163
- parts.push(input.slice(start));
164
- return parts;
151
+ return result.value;
165
152
  }
166
- function lowerFirst(value) {
167
- if (value.length === 0) return value;
168
- return value[0]?.toLowerCase() + value.slice(1);
153
+ function interpretFieldAttribute(input) {
154
+ const result = interpretAttribute(input.node, input.spec, buildFieldInterpretCtx({
155
+ selfModel: input.model,
156
+ field: input.field,
157
+ sourceFile: input.sourceFile,
158
+ sourceId: input.sourceId,
159
+ resolveReferencedModel: input.resolveReferencedModel
160
+ }));
161
+ if (!result.ok) {
162
+ for (const failure of result.failure) input.diagnostics.push(failure);
163
+ return;
164
+ }
165
+ return result.value;
169
166
  }
170
- function getAttribute(attributes, name) {
171
- return attributes.find((attr) => attr.name === name);
167
+ const mapModelSpec = modelAttribute("map", { positional: [{
168
+ key: "name",
169
+ type: str()
170
+ }] });
171
+ const mapFieldSpec = fieldAttribute("map", { positional: [{
172
+ key: "name",
173
+ type: str()
174
+ }] });
175
+ const relationFieldSpec = fieldAttribute("relation", {
176
+ positional: [{
177
+ key: "name",
178
+ type: optional(str())
179
+ }],
180
+ named: {
181
+ name: optional(str()),
182
+ fields: optional(list(fieldRef("self"), {
183
+ nonEmpty: true,
184
+ unique: true
185
+ })),
186
+ references: optional(list(fieldRef("referenced"), {
187
+ nonEmpty: true,
188
+ unique: true
189
+ }))
190
+ }
191
+ });
192
+ const discriminatorModelSpec = modelAttribute("discriminator", { positional: [{
193
+ key: "field",
194
+ type: fieldRef("self")
195
+ }] });
196
+ const baseModelSpec = modelAttribute("base", { positional: [{
197
+ key: "base",
198
+ type: entityRef()
199
+ }, {
200
+ key: "value",
201
+ type: str()
202
+ }] });
203
+ const sortSig = { named: { sort: oneOf(identifier("Asc"), identifier("Desc")) } };
204
+ function indexFieldElement(fieldNames) {
205
+ return oneOf(...[
206
+ fieldRef("self"),
207
+ funcCall("wildcard", { positional: [{
208
+ key: "scope",
209
+ type: optional(entityRef())
210
+ }] }),
211
+ ...fieldNames.map((name) => funcCall(name, sortSig))
212
+ ]);
172
213
  }
173
- function getMapName(attributes) {
174
- const mapAttr = getAttribute(attributes, "map");
175
- if (!mapAttr) return void 0;
176
- const arg = mapAttr.args[0];
177
- if (!arg) return void 0;
178
- return stripQuotes(arg.value);
214
+ const collationNamedArgs = {
215
+ collationLocale: optional(str()),
216
+ collationStrength: optional(oneOf(num(1), num(2), num(3), num(4), num(5))),
217
+ collationCaseLevel: optional(bool()),
218
+ collationCaseFirst: optional(oneOf(str("upper"), str("lower"), str("off"))),
219
+ collationNumericOrdering: optional(bool()),
220
+ collationAlternate: optional(oneOf(str("non-ignorable"), str("shifted"))),
221
+ collationMaxVariable: optional(oneOf(str("punct"), str("space"))),
222
+ collationBackwards: optional(bool()),
223
+ collationNormalization: optional(bool())
224
+ };
225
+ function buildIndexModelSpec(name, fieldElement) {
226
+ return modelAttribute(name, {
227
+ positional: [{
228
+ key: "fields",
229
+ type: list(fieldElement, { nonEmpty: true })
230
+ }],
231
+ named: {
232
+ type: optional(oneOf(num(1), num(-1), str("text"), str("2dsphere"), str("2d"), str("hashed"))),
233
+ sparse: optional(bool()),
234
+ expireAfterSeconds: optional(int()),
235
+ filter: optional(json()),
236
+ include: optional(list(str())),
237
+ exclude: optional(list(str())),
238
+ default_language: optional(str()),
239
+ languageOverride: optional(str()),
240
+ ...collationNamedArgs
241
+ }
242
+ });
179
243
  }
180
- function parseRelationAttribute(attributes) {
181
- const relationAttr = getAttribute(attributes, "relation");
182
- if (!relationAttr) return void 0;
183
- let relationName;
184
- let fieldsArg;
185
- let referencesArg;
186
- for (const arg of relationAttr.args) if (arg.kind === "positional") relationName = stripQuotes(arg.value);
187
- else if (arg.name === "name") relationName = stripQuotes(arg.value);
188
- else if (arg.name === "fields") fieldsArg = arg;
189
- else if (arg.name === "references") referencesArg = arg;
190
- const fields = fieldsArg ? parseFieldList(fieldsArg.value) : void 0;
191
- const references = referencesArg ? parseFieldList(referencesArg.value) : void 0;
244
+ function buildTextIndexModelSpec(fieldElement) {
245
+ return modelAttribute("textIndex", {
246
+ positional: [{
247
+ key: "fields",
248
+ type: list(fieldElement, { nonEmpty: true })
249
+ }],
250
+ named: {
251
+ filter: optional(json()),
252
+ weights: optional(record(int({
253
+ min: 1,
254
+ max: 99999
255
+ }))),
256
+ language: optional(str()),
257
+ languageOverride: optional(str()),
258
+ ...collationNamedArgs
259
+ }
260
+ });
261
+ }
262
+ function buildIndexModelSpecs(fieldNames) {
263
+ const fieldElement = indexFieldElement(fieldNames);
192
264
  return {
193
- ...ifDefined("relationName", relationName),
194
- ...ifDefined("fields", fields),
195
- ...ifDefined("references", references)
265
+ index: buildIndexModelSpec("index", fieldElement),
266
+ unique: buildIndexModelSpec("unique", fieldElement),
267
+ textIndex: buildTextIndexModelSpec(fieldElement)
196
268
  };
197
269
  }
198
- function stripQuotes(value) {
199
- if (value.startsWith("\"") && value.endsWith("\"")) return value.slice(1, -1);
200
- return value;
270
+ function lowerFirst(value) {
271
+ if (value.length === 0) return value;
272
+ return value[0]?.toLowerCase() + value.slice(1);
273
+ }
274
+ function getAttribute(attributes, name) {
275
+ return attributes.find((attr) => attr.name === name);
201
276
  }
202
277
  /**
203
278
  * Encode an authored enum value to its codec-encoded JSON form via the codec resolved by id from the
@@ -226,79 +301,87 @@ function validateNamespaceBlocksForMongoTarget(input) {
226
301
  function fkRelationPairKey(declaringModel, targetModel) {
227
302
  return `${declaringModel}::${targetModel}`;
228
303
  }
229
- function resolveFieldMappings(model) {
304
+ function resolveFieldMappings(input) {
305
+ const { model, sourceFile, sourceId, diagnostics } = input;
230
306
  const pslNameToMapped = /* @__PURE__ */ new Map();
231
307
  for (const field of Object.values(model.fields)) {
232
- const mapped = getMapName(field.attributes) ?? field.name;
308
+ const mapNode = findFieldAttributeNode(field, "map");
309
+ const mapped = (mapNode ? interpretFieldAttribute({
310
+ node: mapNode,
311
+ spec: mapFieldSpec,
312
+ model,
313
+ field,
314
+ sourceFile,
315
+ sourceId,
316
+ diagnostics
317
+ })?.name : void 0) ?? field.name;
233
318
  pslNameToMapped.set(field.name, mapped);
234
319
  }
235
320
  return { pslNameToMapped };
236
321
  }
237
- function resolveCollectionName(model) {
238
- return getMapName(model.attributes) ?? lowerFirst(model.name);
322
+ function resolveCollectionName(input) {
323
+ const { model, sourceFile, sourceId, diagnostics } = input;
324
+ const mapNode = findModelAttributeNode(model, "map");
325
+ return (mapNode ? interpretModelAttribute({
326
+ node: mapNode,
327
+ spec: mapModelSpec,
328
+ model,
329
+ sourceFile,
330
+ sourceId,
331
+ diagnostics
332
+ })?.name : void 0) ?? lowerFirst(model.name);
239
333
  }
240
334
  function mongoCrossRef(modelName) {
241
335
  return crossRef(modelName, UNBOUND_NAMESPACE_ID);
242
336
  }
243
- function collectPolymorphismDeclarations(models, sourceId, diagnostics) {
337
+ function collectPolymorphismDeclarations(models, modelMetadataByName, sourceFile, sourceId, diagnostics) {
244
338
  const discriminatorDeclarations = /* @__PURE__ */ new Map();
245
339
  const baseDeclarations = /* @__PURE__ */ new Map();
246
- for (const model of models) for (const attr of model.attributes) {
247
- if (attr.name === "discriminator") {
248
- const fieldName = getPositionalArgument(attr);
249
- if (!fieldName) {
250
- diagnostics.push({
251
- code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
252
- message: `Model "${model.name}" @@discriminator requires a field name argument`,
253
- sourceId,
254
- span: attr.span
255
- });
256
- continue;
257
- }
258
- const discField = model.fields[fieldName];
259
- if (discField && discField.typeName !== "String") {
260
- diagnostics.push({
340
+ for (const model of models) {
341
+ const discNode = findModelAttributeNode(model, "discriminator");
342
+ if (discNode) {
343
+ const parsed = interpretModelAttribute({
344
+ node: discNode,
345
+ spec: discriminatorModelSpec,
346
+ model,
347
+ sourceFile,
348
+ sourceId,
349
+ diagnostics
350
+ });
351
+ if (parsed) {
352
+ const fieldName = parsed.field;
353
+ const discField = model.fields[fieldName];
354
+ if (discField && discField.typeName !== "String") diagnostics.push({
261
355
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
262
356
  message: `Discriminator field "${fieldName}" on model "${model.name}" must be of type String, but is "${discField.typeName}"`,
263
357
  sourceId,
264
- span: attr.span
358
+ span: nodePslSpan(discNode.syntax, sourceFile)
265
359
  });
266
- continue;
267
- }
268
- discriminatorDeclarations.set(model.name, {
269
- fieldName,
270
- span: attr.span
271
- });
272
- }
273
- if (attr.name === "base") {
274
- const baseName = getPositionalArgument(attr, 0);
275
- const rawValue = getPositionalArgument(attr, 1);
276
- if (!baseName || !rawValue) {
277
- diagnostics.push({
278
- code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
279
- message: `Model "${model.name}" @@base requires two arguments: base model name and discriminator value`,
280
- sourceId,
281
- span: attr.span
360
+ else discriminatorDeclarations.set(model.name, {
361
+ fieldName,
362
+ span: nodePslSpan(discNode.syntax, sourceFile)
282
363
  });
283
- continue;
284
364
  }
285
- const value = parseQuotedStringLiteral(rawValue);
286
- if (value === void 0) {
287
- diagnostics.push({
288
- code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
289
- message: `Model "${model.name}" @@base discriminator value must be a quoted string literal`,
290
- sourceId,
291
- span: attr.span
365
+ }
366
+ const baseNode = findModelAttributeNode(model, "base");
367
+ if (baseNode) {
368
+ const parsed = interpretModelAttribute({
369
+ node: baseNode,
370
+ spec: baseModelSpec,
371
+ model,
372
+ sourceFile,
373
+ sourceId,
374
+ diagnostics
375
+ });
376
+ if (parsed) {
377
+ const collectionName = modelMetadataByName.get(model.name)?.collectionName ?? lowerFirst(model.name);
378
+ baseDeclarations.set(model.name, {
379
+ baseName: parsed.base,
380
+ value: parsed.value,
381
+ collectionName,
382
+ span: nodePslSpan(baseNode.syntax, sourceFile)
292
383
  });
293
- continue;
294
384
  }
295
- const collectionName = resolveCollectionName(model);
296
- baseDeclarations.set(model.name, {
297
- baseName,
298
- value,
299
- collectionName,
300
- span: attr.span
301
- });
302
385
  }
303
386
  }
304
387
  return {
@@ -307,7 +390,7 @@ function collectPolymorphismDeclarations(models, sourceId, diagnostics) {
307
390
  };
308
391
  }
309
392
  function resolvePolymorphism(input) {
310
- const { discriminatorDeclarations, baseDeclarations, modelNames, sourceId, allModels: allModelViews, indexSpans, modelIndexesByName } = input;
393
+ const { discriminatorDeclarations, baseDeclarations, modelNames, modelMetadataByName, sourceId, allModels: allModelViews, indexSpans, modelIndexesByName } = input;
311
394
  let patched = input.models;
312
395
  let roots = input.roots;
313
396
  let collections = input.collections;
@@ -324,8 +407,7 @@ function resolvePolymorphism(input) {
324
407
  }
325
408
  const model = patched[modelName];
326
409
  if (!model) continue;
327
- const modelView = allModelViews.find((m) => m.name === modelName);
328
- const mappedDiscriminatorField = modelView ? resolveFieldMappings(modelView).pslNameToMapped.get(decl.fieldName) ?? decl.fieldName : decl.fieldName;
410
+ const mappedDiscriminatorField = modelMetadataByName.get(modelName)?.fieldMappings.pslNameToMapped.get(decl.fieldName) ?? decl.fieldName;
329
411
  if (!Object.hasOwn(model.fields, mappedDiscriminatorField)) {
330
412
  diagnostics.push({
331
413
  code: "PSL_DISCRIMINATOR_FIELD_NOT_FOUND",
@@ -381,7 +463,7 @@ function resolvePolymorphism(input) {
381
463
  const baseModel = patched[baseDecl.baseName];
382
464
  const variantModelView = allModelViews.find((m) => m.name === variantName);
383
465
  if (!variantModelView) continue;
384
- if (getMapName(variantModelView.attributes) !== void 0 && baseModel && baseDecl.collectionName !== baseModel.storage.collection) {
466
+ if (getAttribute(variantModelView.attributes, "map") !== void 0 && baseModel && baseDecl.collectionName !== baseModel.storage.collection) {
385
467
  diagnostics.push({
386
468
  code: "PSL_MONGO_VARIANT_SEPARATE_COLLECTION",
387
469
  message: `Mongo variant "${variantName}" cannot use a different collection than its base "${baseDecl.baseName}". Mongo only supports single-collection polymorphism.`,
@@ -400,7 +482,7 @@ function resolvePolymorphism(input) {
400
482
  storage: { collection: baseCollection }
401
483
  }
402
484
  };
403
- const variantCollectionName = resolveCollectionName(variantModelView);
485
+ const variantCollectionName = modelMetadataByName.get(variantName)?.collectionName ?? lowerFirst(variantName);
404
486
  if (roots[variantCollectionName]?.model === variantName) if (variantCollectionName === baseCollection && baseModel) roots = {
405
487
  ...roots,
406
488
  [variantCollectionName]: mongoCrossRef(baseDecl.baseName)
@@ -432,7 +514,7 @@ function resolvePolymorphism(input) {
432
514
  if (variantCollectionName !== baseCollection) {
433
515
  const filtered = Object.fromEntries(Object.entries(collections).filter(([key]) => key !== variantCollectionName));
434
516
  if (scopedVariantIndexes.length > 0 && baseColl) {
435
- const baseIndexes = baseColl["indexes"] ?? [];
517
+ const baseIndexes = collectionIndexes(baseColl);
436
518
  collections = {
437
519
  ...filtered,
438
520
  [baseCollection]: {
@@ -442,7 +524,7 @@ function resolvePolymorphism(input) {
442
524
  };
443
525
  } else collections = filtered;
444
526
  } else if (baseColl) {
445
- const existingIndexes = baseColl["indexes"] ?? [];
527
+ const existingIndexes = collectionIndexes(baseColl);
446
528
  const variantIndexSet = new Set(variantOwnIndexes);
447
529
  const withoutUnscopedVariants = existingIndexes.filter((idx) => !variantIndexSet.has(idx));
448
530
  const mergedIndexes = [...withoutUnscopedVariants];
@@ -468,84 +550,225 @@ function resolvePolymorphism(input) {
468
550
  diagnostics
469
551
  };
470
552
  }
553
+ function collectionIndexes(collection) {
554
+ return blindCast(collection["indexes"] ?? []);
555
+ }
471
556
  function canonicalJson(value) {
472
557
  if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
473
558
  if (value && typeof value === "object") return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`;
474
559
  return JSON.stringify(value);
475
560
  }
476
- function parseIndexDirection(raw) {
477
- if (!raw) return 1;
478
- const stripped = raw.replace(/^["']/, "").replace(/["']$/, "");
479
- const num = Number(stripped);
480
- if (num === 1 || num === -1) return num;
481
- if ([
482
- "text",
483
- "2dsphere",
484
- "2d",
485
- "hashed"
486
- ].includes(stripped)) return stripped;
487
- return 1;
488
- }
489
- function parseNumericArg(raw) {
490
- if (!raw) return void 0;
491
- const n = Number(raw);
492
- return Number.isFinite(n) ? n : void 0;
493
- }
494
- function parseBooleanArg(raw) {
495
- if (raw === "true") return true;
496
- if (raw === "false") return false;
497
- }
498
- function parseJsonArg(raw) {
499
- if (!raw) return void 0;
500
- const stripped = raw.replace(/^["']/, "").replace(/["']$/, "").replace(/\\"/g, "\"");
501
- try {
502
- const parsed = JSON.parse(stripped);
503
- if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) return parsed;
504
- } catch {}
561
+ function normalizeIndexField(element) {
562
+ if (typeof element === "string") return {
563
+ kind: "field",
564
+ name: element
565
+ };
566
+ if (element.fn === "wildcard" && element.args["sort"] === void 0) {
567
+ const scope = element.args["scope"];
568
+ return typeof scope === "string" ? {
569
+ kind: "wildcard",
570
+ scope
571
+ } : { kind: "wildcard" };
572
+ }
573
+ const sort = element.args["sort"];
574
+ return {
575
+ kind: "field",
576
+ name: element.fn,
577
+ direction: sort === "Desc" ? -1 : 1
578
+ };
505
579
  }
506
- function parseCollation(attr) {
507
- const locale = stripQuotesHelper(getNamedArgument(attr, "collationLocale"));
508
- if (!locale) return getNamedArgument(attr, "collationStrength") != null || getNamedArgument(attr, "collationCaseLevel") != null || getNamedArgument(attr, "collationCaseFirst") != null || getNamedArgument(attr, "collationNumericOrdering") != null || getNamedArgument(attr, "collationAlternate") != null || getNamedArgument(attr, "collationMaxVariable") != null || getNamedArgument(attr, "collationBackwards") != null || getNamedArgument(attr, "collationNormalization") != null ? null : void 0;
580
+ function buildCollationFromSpec(args) {
581
+ const locale = args.collationLocale;
582
+ if (locale === void 0) return args.collationStrength !== void 0 || args.collationCaseLevel !== void 0 || args.collationCaseFirst !== void 0 || args.collationNumericOrdering !== void 0 || args.collationAlternate !== void 0 || args.collationMaxVariable !== void 0 || args.collationBackwards !== void 0 || args.collationNormalization !== void 0 ? null : void 0;
509
583
  const collation = { locale };
510
- const strength = parseNumericArg(getNamedArgument(attr, "collationStrength"));
511
- if (strength != null) collation.strength = strength;
512
- const caseLevel = parseBooleanArg(getNamedArgument(attr, "collationCaseLevel"));
513
- if (caseLevel != null) collation.caseLevel = caseLevel;
514
- const caseFirst = stripQuotesHelper(getNamedArgument(attr, "collationCaseFirst"));
515
- if (caseFirst != null) collation.caseFirst = caseFirst;
516
- const numericOrdering = parseBooleanArg(getNamedArgument(attr, "collationNumericOrdering"));
517
- if (numericOrdering != null) collation.numericOrdering = numericOrdering;
518
- const alternate = stripQuotesHelper(getNamedArgument(attr, "collationAlternate"));
519
- if (alternate != null) collation.alternate = alternate;
520
- const maxVariable = stripQuotesHelper(getNamedArgument(attr, "collationMaxVariable"));
521
- if (maxVariable != null) collation.maxVariable = maxVariable;
522
- const backwards = parseBooleanArg(getNamedArgument(attr, "collationBackwards"));
523
- if (backwards != null) collation.backwards = backwards;
524
- const normalization = parseBooleanArg(getNamedArgument(attr, "collationNormalization"));
525
- if (normalization != null) collation.normalization = normalization;
584
+ if (args.collationStrength !== void 0) collation.strength = args.collationStrength;
585
+ if (args.collationCaseLevel !== void 0) collation.caseLevel = args.collationCaseLevel;
586
+ if (args.collationCaseFirst !== void 0) collation.caseFirst = args.collationCaseFirst;
587
+ if (args.collationNumericOrdering !== void 0) collation.numericOrdering = args.collationNumericOrdering;
588
+ if (args.collationAlternate !== void 0) collation.alternate = args.collationAlternate;
589
+ if (args.collationMaxVariable !== void 0) collation.maxVariable = args.collationMaxVariable;
590
+ if (args.collationBackwards !== void 0) collation.backwards = args.collationBackwards;
591
+ if (args.collationNormalization !== void 0) collation.normalization = args.collationNormalization;
526
592
  return collation;
527
593
  }
528
- function stripQuotesHelper(raw) {
529
- if (!raw) return void 0;
530
- return raw.replace(/^["']/, "").replace(/["']$/, "");
594
+ function resolveIndexKeys(parsedFields, defaultDirection, ctx) {
595
+ const wildcardCount = parsedFields.filter((field) => field.kind === "wildcard").length;
596
+ if (wildcardCount > 1) {
597
+ ctx.diagnostics.push({
598
+ code: "PSL_INVALID_INDEX",
599
+ message: "An index can contain at most one wildcard() field",
600
+ sourceId: ctx.sourceId,
601
+ span: ctx.span
602
+ });
603
+ return;
604
+ }
605
+ for (const field of parsedFields) {
606
+ const fieldName = field.kind === "wildcard" ? field.scope : field.name;
607
+ if (fieldName !== void 0 && !ctx.indexableFieldNames.has(fieldName)) {
608
+ ctx.diagnostics.push({
609
+ code: "PSL_INDEX_FIELD_NOT_FOUND",
610
+ message: `Index on model "${ctx.pslModel.name}" references unknown field "${fieldName}"`,
611
+ sourceId: ctx.sourceId,
612
+ span: ctx.span
613
+ });
614
+ return;
615
+ }
616
+ }
617
+ return {
618
+ keys: parsedFields.map((field) => {
619
+ if (field.kind === "wildcard") {
620
+ if (field.scope === void 0) return {
621
+ field: "$**",
622
+ direction: defaultDirection
623
+ };
624
+ return {
625
+ field: `${ctx.fieldMappings.pslNameToMapped.get(field.scope) ?? field.scope}.$**`,
626
+ direction: defaultDirection
627
+ };
628
+ }
629
+ return {
630
+ field: ctx.fieldMappings.pslNameToMapped.get(field.name) ?? field.name,
631
+ direction: field.direction ?? defaultDirection
632
+ };
633
+ }),
634
+ hasWildcard: wildcardCount === 1
635
+ };
531
636
  }
532
- function parseProjectionList(raw, value) {
533
- if (!raw) return void 0;
534
- const inner = raw.replace(/^["']/, "").replace(/["']$/, "").replace(/^\[/, "").replace(/\]$/, "").trim();
535
- if (inner.length === 0) return void 0;
536
- const fields = inner.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
537
- const result = {};
538
- for (const f of fields) result[f] = value;
539
- return result;
637
+ function buildProjection(include, exclude, hasWildcard, ctx) {
638
+ if (include !== void 0 && exclude !== void 0) {
639
+ ctx.diagnostics.push({
640
+ code: "PSL_INVALID_INDEX",
641
+ message: "Cannot specify both include and exclude on the same index",
642
+ sourceId: ctx.sourceId,
643
+ span: ctx.span
644
+ });
645
+ return null;
646
+ }
647
+ const fields = include ?? exclude;
648
+ if (fields === void 0) return void 0;
649
+ if (!hasWildcard) {
650
+ ctx.diagnostics.push({
651
+ code: "PSL_INVALID_INDEX",
652
+ message: "include/exclude options are only valid when the index contains a wildcard() field",
653
+ sourceId: ctx.sourceId,
654
+ span: ctx.span
655
+ });
656
+ return null;
657
+ }
658
+ if (fields.length === 0) return void 0;
659
+ const value = include === void 0 ? 0 : 1;
660
+ const projection = {};
661
+ for (const field of fields) projection[field] = value;
662
+ return projection;
663
+ }
664
+ function buildNormalIndex(parsed, unique, ctx) {
665
+ const parsedFields = parsed.fields.map(normalizeIndexField);
666
+ if (parsedFields.length === 0) return void 0;
667
+ const defaultDirection = parsed.type ?? 1;
668
+ const resolved = resolveIndexKeys(parsedFields, defaultDirection, ctx);
669
+ if (!resolved) return void 0;
670
+ if (unique && resolved.hasWildcard) {
671
+ ctx.diagnostics.push({
672
+ code: "PSL_INVALID_INDEX",
673
+ message: "Unique indexes cannot use wildcard() fields",
674
+ sourceId: ctx.sourceId,
675
+ span: ctx.span
676
+ });
677
+ return;
678
+ }
679
+ if (resolved.hasWildcard && typeof defaultDirection === "string" && [
680
+ "hashed",
681
+ "2dsphere",
682
+ "2d"
683
+ ].includes(defaultDirection)) {
684
+ ctx.diagnostics.push({
685
+ code: "PSL_INVALID_INDEX",
686
+ message: `wildcard() fields cannot be combined with type: ${defaultDirection}`,
687
+ sourceId: ctx.sourceId,
688
+ span: ctx.span
689
+ });
690
+ return;
691
+ }
692
+ if (defaultDirection === "hashed" && parsedFields.length > 1) {
693
+ ctx.diagnostics.push({
694
+ code: "PSL_INVALID_INDEX",
695
+ message: "Hashed indexes must have exactly one field",
696
+ sourceId: ctx.sourceId,
697
+ span: ctx.span
698
+ });
699
+ return;
700
+ }
701
+ if (resolved.hasWildcard && parsed.expireAfterSeconds !== void 0) {
702
+ ctx.diagnostics.push({
703
+ code: "PSL_INVALID_INDEX",
704
+ message: "expireAfterSeconds cannot be combined with wildcard() fields",
705
+ sourceId: ctx.sourceId,
706
+ span: ctx.span
707
+ });
708
+ return;
709
+ }
710
+ const wildcardProjection = buildProjection(parsed.include, parsed.exclude, resolved.hasWildcard, ctx);
711
+ if (wildcardProjection === null) return void 0;
712
+ const collation = buildCollationFromSpec(parsed);
713
+ if (collation === null) {
714
+ ctx.diagnostics.push({
715
+ code: "PSL_INVALID_INDEX",
716
+ message: "collationLocale is required when using collation options",
717
+ sourceId: ctx.sourceId,
718
+ span: ctx.span
719
+ });
720
+ return;
721
+ }
722
+ return new MongoIndex({
723
+ keys: resolved.keys,
724
+ ...unique && { unique: true },
725
+ ...parsed.sparse !== void 0 && { sparse: parsed.sparse },
726
+ ...parsed.expireAfterSeconds !== void 0 && { expireAfterSeconds: parsed.expireAfterSeconds },
727
+ ...parsed.filter !== void 0 && { partialFilterExpression: parsed.filter },
728
+ ...wildcardProjection !== void 0 && { wildcardProjection },
729
+ ...collation !== void 0 && { collation },
730
+ ...parsed.default_language !== void 0 && { default_language: parsed.default_language },
731
+ ...parsed.languageOverride !== void 0 && { language_override: parsed.languageOverride }
732
+ });
733
+ }
734
+ function buildTextIndex(parsed, ctx) {
735
+ const parsedFields = parsed.fields.map(normalizeIndexField);
736
+ if (parsedFields.length === 0) return void 0;
737
+ const resolved = resolveIndexKeys(parsedFields, "text", ctx);
738
+ if (!resolved) return void 0;
739
+ if (resolved.hasWildcard) {
740
+ ctx.diagnostics.push({
741
+ code: "PSL_INVALID_INDEX",
742
+ message: "wildcard() fields cannot be combined with type: hashed/2dsphere/2d or @@textIndex",
743
+ sourceId: ctx.sourceId,
744
+ span: ctx.span
745
+ });
746
+ return;
747
+ }
748
+ const collation = buildCollationFromSpec(parsed);
749
+ if (collation === null) {
750
+ ctx.diagnostics.push({
751
+ code: "PSL_INVALID_INDEX",
752
+ message: "collationLocale is required when using collation options",
753
+ sourceId: ctx.sourceId,
754
+ span: ctx.span
755
+ });
756
+ return;
757
+ }
758
+ return new MongoIndex({
759
+ keys: resolved.keys,
760
+ ...parsed.filter !== void 0 && { partialFilterExpression: parsed.filter },
761
+ ...collation !== void 0 && { collation },
762
+ ...ifDefined("weights", parsed.weights),
763
+ ...parsed.language !== void 0 && { default_language: parsed.language },
764
+ ...parsed.languageOverride !== void 0 && { language_override: parsed.languageOverride }
765
+ });
540
766
  }
541
- function collectIndexes(pslModel, fieldMappings, modelNames, sourceId, diagnostics, indexSpans) {
767
+ function collectIndexes(pslModel, fieldMappings, modelNames, sourceId, sourceFile, diagnostics, indexSpans) {
542
768
  const indexes = [];
543
769
  let textIndexCount = 0;
544
770
  const indexableFieldNames = /* @__PURE__ */ new Set();
545
- for (const f of Object.values(pslModel.fields)) {
546
- if (modelNames.has(f.typeName)) continue;
547
- indexableFieldNames.add(f.name);
548
- }
771
+ for (const field of Object.values(pslModel.fields)) if (!modelNames.has(field.typeName)) indexableFieldNames.add(field.name);
549
772
  for (const field of Object.values(pslModel.fields)) {
550
773
  if (modelNames.has(field.typeName)) continue;
551
774
  const uniqueAttr = getAttribute(field.attributes, "unique");
@@ -560,35 +783,31 @@ function collectIndexes(pslModel, fieldMappings, modelNames, sourceId, diagnosti
560
783
  indexes.push(fieldUniqueIndex);
561
784
  indexSpans.set(fieldUniqueIndex, uniqueAttr.span);
562
785
  }
563
- for (const attr of pslModel.attributes) {
564
- const isIndex = attr.name === "index";
565
- const isUnique = attr.name === "unique";
566
- const isTextIndex = attr.name === "textIndex";
567
- if (!isIndex && !isUnique && !isTextIndex) continue;
568
- const fieldsArg = getPositionalArgument(attr, 0);
569
- if (!fieldsArg) continue;
570
- const parsedFields = parseIndexFieldList(fieldsArg);
571
- if (parsedFields.length === 0) continue;
572
- const hasWildcard = parsedFields.some((f) => f.isWildcard);
573
- if (parsedFields.filter((f) => f.isWildcard).length > 1) {
574
- diagnostics.push({
575
- code: "PSL_INVALID_INDEX",
576
- message: "An index can contain at most one wildcard() field",
577
- sourceId,
578
- span: attr.span
579
- });
580
- continue;
581
- }
582
- if (isUnique && hasWildcard) {
583
- diagnostics.push({
584
- code: "PSL_INVALID_INDEX",
585
- message: "Unique indexes cannot use wildcard() fields",
786
+ const specs = buildIndexModelSpecs(Object.keys(pslModel.fields));
787
+ const attributeNodes = Array.from(pslModel.node.attributes());
788
+ for (const [attrIndex, attr] of pslModel.attributes.entries()) {
789
+ if (attr.name !== "index" && attr.name !== "unique" && attr.name !== "textIndex") continue;
790
+ const node = attributeNodes[attrIndex];
791
+ if (!node) continue;
792
+ const ctx = {
793
+ pslModel,
794
+ fieldMappings,
795
+ indexableFieldNames,
796
+ sourceId,
797
+ span: attr.span,
798
+ diagnostics
799
+ };
800
+ let index;
801
+ if (attr.name === "textIndex") {
802
+ const parsed = interpretModelAttribute({
803
+ node,
804
+ spec: specs.textIndex,
805
+ model: pslModel,
806
+ sourceFile,
586
807
  sourceId,
587
- span: attr.span
808
+ diagnostics
588
809
  });
589
- continue;
590
- }
591
- if (isTextIndex) {
810
+ if (!parsed || parsed.fields.length === 0) continue;
592
811
  textIndexCount++;
593
812
  if (textIndexCount > 1) {
594
813
  diagnostics.push({
@@ -599,135 +818,21 @@ function collectIndexes(pslModel, fieldMappings, modelNames, sourceId, diagnosti
599
818
  });
600
819
  continue;
601
820
  }
602
- if (hasWildcard) {
603
- diagnostics.push({
604
- code: "PSL_INVALID_INDEX",
605
- message: "wildcard() fields cannot be combined with type: hashed/2dsphere/2d or @@textIndex",
606
- sourceId,
607
- span: attr.span
608
- });
609
- continue;
610
- }
611
- }
612
- const typeArg = getNamedArgument(attr, "type");
613
- const defaultDirection = isTextIndex ? "text" : parseIndexDirection(typeArg);
614
- if (hasWildcard && typeof defaultDirection === "string" && [
615
- "hashed",
616
- "2dsphere",
617
- "2d"
618
- ].includes(defaultDirection)) {
619
- diagnostics.push({
620
- code: "PSL_INVALID_INDEX",
621
- message: `wildcard() fields cannot be combined with type: ${defaultDirection}`,
622
- sourceId,
623
- span: attr.span
624
- });
625
- continue;
626
- }
627
- if (defaultDirection === "hashed" && parsedFields.length > 1) {
628
- diagnostics.push({
629
- code: "PSL_INVALID_INDEX",
630
- message: "Hashed indexes must have exactly one field",
631
- sourceId,
632
- span: attr.span
633
- });
634
- continue;
635
- }
636
- let missingField;
637
- for (const pf of parsedFields) {
638
- let fieldNameForLookup;
639
- if (pf.isWildcard) {
640
- const wildcardMatch = pf.name.match(/^(.+)\.\$\*\*$/);
641
- fieldNameForLookup = wildcardMatch ? wildcardMatch[1] : void 0;
642
- } else fieldNameForLookup = pf.name;
643
- if (fieldNameForLookup === void 0 || fieldNameForLookup.length === 0) continue;
644
- if (!indexableFieldNames.has(fieldNameForLookup)) {
645
- missingField = fieldNameForLookup;
646
- break;
647
- }
648
- }
649
- if (missingField !== void 0) {
650
- diagnostics.push({
651
- code: "PSL_INDEX_FIELD_NOT_FOUND",
652
- message: `Index on model "${pslModel.name}" references unknown field "${missingField}"`,
653
- sourceId,
654
- span: attr.span
655
- });
656
- continue;
657
- }
658
- const keys = parsedFields.map((pf) => {
659
- return {
660
- field: pf.isWildcard ? pf.name.replace(/^(.+)\.\$\*\*$/, (_, prefix) => {
661
- const mapped = fieldMappings.pslNameToMapped.get(prefix);
662
- return mapped ? `${mapped}.$**` : `${prefix}.$**`;
663
- }) : fieldMappings.pslNameToMapped.get(pf.name) ?? pf.name,
664
- direction: pf.direction != null ? pf.direction : defaultDirection
665
- };
666
- });
667
- const unique = isUnique ? true : void 0;
668
- const sparse = isTextIndex ? void 0 : parseBooleanArg(getNamedArgument(attr, "sparse"));
669
- const expireAfterSeconds = isTextIndex ? void 0 : parseNumericArg(getNamedArgument(attr, "expireAfterSeconds"));
670
- if (hasWildcard && expireAfterSeconds != null) {
671
- diagnostics.push({
672
- code: "PSL_INVALID_INDEX",
673
- message: "expireAfterSeconds cannot be combined with wildcard() fields",
674
- sourceId,
675
- span: attr.span
676
- });
677
- continue;
678
- }
679
- const partialFilterExpression = parseJsonArg(getNamedArgument(attr, "filter"));
680
- const includeArg = getNamedArgument(attr, "include");
681
- const excludeArg = getNamedArgument(attr, "exclude");
682
- if (includeArg != null && excludeArg != null) {
683
- diagnostics.push({
684
- code: "PSL_INVALID_INDEX",
685
- message: "Cannot specify both include and exclude on the same index",
686
- sourceId,
687
- span: attr.span
688
- });
689
- continue;
690
- }
691
- if ((includeArg != null || excludeArg != null) && !hasWildcard) {
692
- diagnostics.push({
693
- code: "PSL_INVALID_INDEX",
694
- message: "include/exclude options are only valid when the index contains a wildcard() field",
821
+ index = buildTextIndex(parsed, ctx);
822
+ } else {
823
+ const unique = attr.name === "unique";
824
+ const parsed = interpretModelAttribute({
825
+ node,
826
+ spec: unique ? specs.unique : specs.index,
827
+ model: pslModel,
828
+ sourceFile,
695
829
  sourceId,
696
- span: attr.span
830
+ diagnostics
697
831
  });
698
- continue;
832
+ if (!parsed) continue;
833
+ index = buildNormalIndex(parsed, unique, ctx);
699
834
  }
700
- const wildcardProjection = includeArg != null ? parseProjectionList(includeArg, 1) : excludeArg != null ? parseProjectionList(excludeArg, 0) : void 0;
701
- const collation = parseCollation(attr);
702
- if (collation === null) {
703
- diagnostics.push({
704
- code: "PSL_INVALID_INDEX",
705
- message: "collationLocale is required when using collation options",
706
- sourceId,
707
- span: attr.span
708
- });
709
- continue;
710
- }
711
- const rawWeights = parseJsonArg(getNamedArgument(attr, "weights"));
712
- let weights;
713
- if (rawWeights) {
714
- weights = {};
715
- for (const [k, v] of Object.entries(rawWeights)) if (typeof v === "number") weights[k] = v;
716
- }
717
- const default_language = stripQuotesHelper(isTextIndex ? getNamedArgument(attr, "language") : getNamedArgument(attr, "default_language"));
718
- const language_override = stripQuotesHelper(getNamedArgument(attr, "languageOverride"));
719
- const index = new MongoIndex({
720
- keys,
721
- ...unique != null && { unique },
722
- ...sparse != null && { sparse },
723
- ...expireAfterSeconds != null && { expireAfterSeconds },
724
- ...partialFilterExpression != null && { partialFilterExpression },
725
- ...wildcardProjection != null && { wildcardProjection },
726
- ...collation != null && { collation },
727
- ...weights != null && { weights },
728
- ...default_language != null && { default_language },
729
- ...language_override != null && { language_override }
730
- });
835
+ if (!index) continue;
731
836
  indexes.push(index);
732
837
  indexSpans.set(index, attr.span);
733
838
  }
@@ -839,6 +944,21 @@ function interpretPslDocumentToMongoContract(input) {
839
944
  const allCompositeTypes = Object.values(topLevel.compositeTypes);
840
945
  const modelNames = new Set(allModels.map((m) => m.name));
841
946
  const compositeTypeNames = new Set(allCompositeTypes.map((ct) => ct.name));
947
+ const modelMetadataByName = /* @__PURE__ */ new Map();
948
+ for (const model of allModels) modelMetadataByName.set(model.name, {
949
+ collectionName: resolveCollectionName({
950
+ model,
951
+ sourceFile,
952
+ sourceId,
953
+ diagnostics
954
+ }),
955
+ fieldMappings: resolveFieldMappings({
956
+ model,
957
+ sourceFile,
958
+ sourceId,
959
+ diagnostics
960
+ })
961
+ });
842
962
  const builtEnums = processEnumDeclarations({
843
963
  enumBlocks: Object.values(topLevel.blocks).filter((b) => b.keyword === "enum").map((b) => b.block),
844
964
  sourceId,
@@ -864,19 +984,30 @@ function interpretPslDocumentToMongoContract(input) {
864
984
  const modelIndexesByName = /* @__PURE__ */ new Map();
865
985
  const backrelationCandidates = [];
866
986
  for (const pslModel of allModels) {
867
- const collectionName = resolveCollectionName(pslModel);
868
- const fieldMappings = resolveFieldMappings(pslModel);
987
+ const metadata = modelMetadataByName.get(pslModel.name);
988
+ if (!metadata) continue;
989
+ const { collectionName, fieldMappings } = metadata;
869
990
  const fields = {};
870
991
  const relations = {};
871
992
  for (const field of Object.values(pslModel.fields)) {
872
993
  if (isRelationField(field, modelNames)) {
873
- const relation = parseRelationAttribute(field.attributes);
994
+ const relationNode = findFieldAttributeNode(field, "relation");
995
+ const relation = relationNode ? interpretFieldAttribute({
996
+ node: relationNode,
997
+ spec: relationFieldSpec,
998
+ model: pslModel,
999
+ field,
1000
+ sourceFile,
1001
+ sourceId,
1002
+ diagnostics,
1003
+ resolveReferencedModel: () => allModels.find((m) => m.name === field.typeName)
1004
+ }) : void 0;
874
1005
  if (field.list || !(relation?.fields && relation?.references)) {
875
1006
  backrelationCandidates.push({
876
1007
  modelName: pslModel.name,
877
1008
  fieldName: field.name,
878
1009
  targetModelName: field.typeName,
879
- ...ifDefined("relationName", relation?.relationName),
1010
+ ...ifDefined("relationName", relation?.name),
880
1011
  cardinality: field.list ? "1:N" : "1:1",
881
1012
  field
882
1013
  });
@@ -884,8 +1015,7 @@ function interpretPslDocumentToMongoContract(input) {
884
1015
  }
885
1016
  if (relation?.fields && relation?.references) {
886
1017
  const localMapped = relation.fields.map((f) => fieldMappings.pslNameToMapped.get(f) ?? f);
887
- const targetModel = allModels.find((m) => m.name === field.typeName);
888
- const targetFieldMappings = targetModel ? resolveFieldMappings(targetModel) : void 0;
1018
+ const targetFieldMappings = modelMetadataByName.get(field.typeName)?.fieldMappings;
889
1019
  const targetMapped = relation.references.map((f) => targetFieldMappings?.pslNameToMapped.get(f) ?? f);
890
1020
  relations[field.name] = {
891
1021
  to: mongoCrossRef(field.typeName),
@@ -899,7 +1029,7 @@ function interpretPslDocumentToMongoContract(input) {
899
1029
  declaringModel: pslModel.name,
900
1030
  fieldName: field.name,
901
1031
  targetModel: field.typeName,
902
- ...ifDefined("relationName", relation.relationName),
1032
+ ...ifDefined("relationName", relation.name),
903
1033
  localFields: localMapped,
904
1034
  targetFields: targetMapped
905
1035
  });
@@ -932,10 +1062,10 @@ function interpretPslDocumentToMongoContract(input) {
932
1062
  relations,
933
1063
  storage: { collection: collectionName }
934
1064
  };
935
- const modelIndexes = collectIndexes(pslModel, fieldMappings, modelNames, sourceId, diagnostics, indexSpans);
1065
+ const modelIndexes = collectIndexes(pslModel, fieldMappings, modelNames, sourceId, sourceFile, diagnostics, indexSpans);
936
1066
  modelIndexesByName.set(pslModel.name, modelIndexes);
937
1067
  const existingColl = collections[collectionName];
938
- if (existingColl && modelIndexes.length > 0) collections[collectionName] = { indexes: [...existingColl["indexes"] ?? [], ...modelIndexes] };
1068
+ if (existingColl && modelIndexes.length > 0) collections[collectionName] = { indexes: [...collectionIndexes(existingColl), ...modelIndexes] };
939
1069
  else if (!existingColl) collections[collectionName] = modelIndexes.length > 0 ? { indexes: modelIndexes } : {};
940
1070
  roots[collectionName] = mongoCrossRef(pslModel.name);
941
1071
  }
@@ -991,7 +1121,7 @@ function interpretPslDocumentToMongoContract(input) {
991
1121
  }
992
1122
  };
993
1123
  }
994
- const { discriminatorDeclarations, baseDeclarations } = collectPolymorphismDeclarations(allModels, sourceId, diagnostics);
1124
+ const { discriminatorDeclarations, baseDeclarations } = collectPolymorphismDeclarations(allModels, modelMetadataByName, sourceFile, sourceId, diagnostics);
995
1125
  const polyResult = resolvePolymorphism({
996
1126
  models,
997
1127
  roots,
@@ -1002,6 +1132,7 @@ function interpretPslDocumentToMongoContract(input) {
1002
1132
  modelNames,
1003
1133
  indexSpans,
1004
1134
  modelIndexesByName,
1135
+ modelMetadataByName,
1005
1136
  sourceId
1006
1137
  });
1007
1138
  if (diagnostics.length > 0 || polyResult.diagnostics.length > 0) return notOk({
@@ -1048,7 +1179,7 @@ function interpretPslDocumentToMongoContract(input) {
1048
1179
  ...Object.keys(storageValueSets).length > 0 ? { valueSet: storageValueSets } : {}
1049
1180
  }
1050
1181
  });
1051
- const storage = new MongoStorage({
1182
+ const storage = blindCast(new MongoStorage({
1052
1183
  storageHash: computeStorageHash({
1053
1184
  target,
1054
1185
  targetFamily,
@@ -1062,7 +1193,7 @@ function interpretPslDocumentToMongoContract(input) {
1062
1193
  ...mongoContractCanonicalizationHooks
1063
1194
  }),
1064
1195
  namespaces: { [UNBOUND_NAMESPACE_ID]: unboundNamespace }
1065
- });
1196
+ }));
1066
1197
  const capabilities = {};
1067
1198
  const hasEnums = Object.keys(builtEnums).length > 0;
1068
1199
  return ok({
@@ -1088,4 +1219,4 @@ function interpretPslDocumentToMongoContract(input) {
1088
1219
  //#endregion
1089
1220
  export { derivePolymorphicJsonSchema as n, interpretPslDocumentToMongoContract as r, deriveJsonSchema as t };
1090
1221
 
1091
- //# sourceMappingURL=interpreter-BxbUTQ3k-ClR98Ned.mjs.map
1222
+ //# sourceMappingURL=interpreter-O09WlHk7-BiV9gBae.mjs.map