@prisma-next/mongo-contract-psl 0.14.0-dev.4 → 0.14.0-dev.40
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/dist/exports/provider.d.mts +5 -0
- package/dist/exports/provider.d.mts.map +1 -1
- package/dist/exports/provider.mjs +26 -8
- package/dist/exports/provider.mjs.map +1 -1
- package/dist/index.d.mts +33 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{interpreter-Cj1vigKn.mjs → interpreter-D0vfRJ7f.mjs} +183 -85
- package/dist/interpreter-D0vfRJ7f.mjs.map +1 -0
- package/package.json +12 -10
- package/src/derive-json-schema.ts +31 -19
- package/src/exports/index.ts +5 -0
- package/src/interpreter.ts +256 -87
- package/src/provider.ts +39 -6
- package/src/psl-helpers.ts +18 -13
- package/dist/interpreter-Cj1vigKn.mjs.map +0 -1
|
@@ -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
|
-
|
|
18
|
-
|
|
19
|
-
items
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
177
|
-
...fields
|
|
178
|
-
...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
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
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
|
-
|
|
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
|
|
195
|
-
*
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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(
|
|
246
|
+
function collectPolymorphismDeclarations(models, sourceId, diagnostics) {
|
|
231
247
|
const discriminatorDeclarations = /* @__PURE__ */ new Map();
|
|
232
248
|
const baseDeclarations = /* @__PURE__ */ new Map();
|
|
233
|
-
const
|
|
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 "${
|
|
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 =
|
|
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 "${
|
|
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(
|
|
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 "${
|
|
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 "${
|
|
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(
|
|
284
|
-
baseDeclarations.set(
|
|
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,
|
|
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
|
|
317
|
-
const mappedDiscriminatorField =
|
|
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
|
|
372
|
-
if (!
|
|
373
|
-
if (getMapName(
|
|
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(
|
|
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 {
|
|
770
|
-
const 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:
|
|
836
|
+
namespaces: Object.values(topLevel.namespaces),
|
|
774
837
|
sourceId,
|
|
838
|
+
sourceFile,
|
|
775
839
|
diagnostics
|
|
776
840
|
});
|
|
777
|
-
const allModels =
|
|
778
|
-
const allCompositeTypes =
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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(
|
|
997
|
+
const { discriminatorDeclarations, baseDeclarations } = collectPolymorphismDeclarations(allModels, sourceId, diagnostics);
|
|
917
998
|
const polyResult = resolvePolymorphism({
|
|
918
999
|
models,
|
|
919
1000
|
roots,
|
|
920
1001
|
collections,
|
|
921
|
-
|
|
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: {
|
|
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: {
|
|
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-
|
|
1094
|
+
//# sourceMappingURL=interpreter-D0vfRJ7f.mjs.map
|