@prisma/orm-family-mongo 0.16.0-dev.36
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/LICENSE +201 -0
- package/README.md +27 -0
- package/dist/authoring-entity-types-BGtkMyoN-D32kzx28.mjs +176 -0
- package/dist/authoring-entity-types-BGtkMyoN-D32kzx28.mjs.map +1 -0
- package/dist/canonicalization-hooks-DF3Z6sjO.mjs +22 -0
- package/dist/canonicalization-hooks-DF3Z6sjO.mjs.map +1 -0
- package/dist/codec.d.mts +2 -0
- package/dist/codec.mjs +2 -0
- package/dist/config-types-CFDf9ivE.mjs +37 -0
- package/dist/config-types-CFDf9ivE.mjs.map +1 -0
- package/dist/config-types-CQOf5N7R.d.mts +12 -0
- package/dist/config-types-CQOf5N7R.d.mts.map +1 -0
- package/dist/contract-builder-BFq0gOMV.mjs +634 -0
- package/dist/contract-builder-BFq0gOMV.mjs.map +1 -0
- package/dist/contract-builder-DB8nguDE.d.mts +445 -0
- package/dist/contract-builder-DB8nguDE.d.mts.map +1 -0
- package/dist/contract-errors-BCN6RVI5-kMurtAbI.mjs +9 -0
- package/dist/contract-errors-BCN6RVI5-kMurtAbI.mjs.map +1 -0
- package/dist/contract-psl.d.mts +45 -0
- package/dist/contract-psl.d.mts.map +1 -0
- package/dist/contract-psl.mjs +2 -0
- package/dist/contract-psl__provider.d.mts +15 -0
- package/dist/contract-psl__provider.d.mts.map +1 -0
- package/dist/contract-psl__provider.mjs +82 -0
- package/dist/contract-psl__provider.mjs.map +1 -0
- package/dist/contract-ts.d.mts +3 -0
- package/dist/contract-ts.mjs +3 -0
- package/dist/contract-ts__config-types.d.mts +2 -0
- package/dist/contract-ts__config-types.mjs +2 -0
- package/dist/contract-ts__contract-builder.d.mts +2 -0
- package/dist/contract-ts__contract-builder.mjs +2 -0
- package/dist/contract.d.mts +3 -0
- package/dist/contract.mjs +3 -0
- package/dist/contract__canonicalization-hooks.d.mts +9 -0
- package/dist/contract__canonicalization-hooks.d.mts.map +1 -0
- package/dist/contract__canonicalization-hooks.mjs +2 -0
- package/dist/contract__entity-kinds.d.mts +17 -0
- package/dist/contract__entity-kinds.d.mts.map +1 -0
- package/dist/contract__entity-kinds.mjs +2 -0
- package/dist/control-C7-kthr1.d.mts +193 -0
- package/dist/control-C7-kthr1.d.mts.map +1 -0
- package/dist/control-CClR5JMr.mjs +127 -0
- package/dist/control-CClR5JMr.mjs.map +1 -0
- package/dist/control-CFr3tYsg.mjs +398 -0
- package/dist/control-CFr3tYsg.mjs.map +1 -0
- package/dist/control-Cj81sPbj.d.mts +101 -0
- package/dist/control-Cj81sPbj.d.mts.map +1 -0
- package/dist/control-adapter-Db5pT77b.d.mts +83 -0
- package/dist/control-adapter-Db5pT77b.d.mts.map +1 -0
- package/dist/dist-C7Ykq48Q.mjs +19 -0
- package/dist/dist-C7Ykq48Q.mjs.map +1 -0
- package/dist/dist-Czwqx_i3.mjs +58 -0
- package/dist/dist-Czwqx_i3.mjs.map +1 -0
- package/dist/dist-DHrEJnhK.mjs +236 -0
- package/dist/dist-DHrEJnhK.mjs.map +1 -0
- package/dist/dist-Dyp05u64.mjs +1546 -0
- package/dist/dist-Dyp05u64.mjs.map +1 -0
- package/dist/emitter.d.mts +22 -0
- package/dist/emitter.d.mts.map +1 -0
- package/dist/emitter.mjs +2 -0
- package/dist/entity-kinds-B4KNTF8z-DPIuUuuB.mjs +603 -0
- package/dist/entity-kinds-B4KNTF8z-DPIuUuuB.mjs.map +1 -0
- package/dist/execution-CKnUiOfm.d.mts +48 -0
- package/dist/execution-CKnUiOfm.d.mts.map +1 -0
- package/dist/execution-D5XyZ5XS.mjs +1269 -0
- package/dist/execution-D5XyZ5XS.mjs.map +1 -0
- package/dist/exports-Bx-hlnZ3.mjs +209 -0
- package/dist/exports-Bx-hlnZ3.mjs.map +1 -0
- package/dist/exports-CJMh0VTU.mjs +156 -0
- package/dist/exports-CJMh0VTU.mjs.map +1 -0
- package/dist/family.d.mts +10 -0
- package/dist/family.mjs +9 -0
- package/dist/family__control-adapter.d.mts +2 -0
- package/dist/family__control-adapter.mjs +1 -0
- package/dist/family__control.d.mts +2 -0
- package/dist/family__control.mjs +3 -0
- package/dist/family__ir.d.mts +3 -0
- package/dist/family__ir.mjs +4 -0
- package/dist/family__migration.d.mts +2 -0
- package/dist/family__migration.mjs +2 -0
- package/dist/family__pack.d.mts +2 -0
- package/dist/family__pack.mjs +16 -0
- package/dist/family__pack.mjs.map +1 -0
- package/dist/family__runtime.d.mts +3 -0
- package/dist/family__runtime.mjs +2 -0
- package/dist/family__schema-verify.d.mts +2 -0
- package/dist/family__schema-verify.mjs +2 -0
- package/dist/field-accessor-0oUt6RZC-B_Z30uAA.mjs +234 -0
- package/dist/field-accessor-0oUt6RZC-B_Z30uAA.mjs.map +1 -0
- package/dist/field-accessor-DHsAPiW3-BdYCspQC.d.mts +588 -0
- package/dist/field-accessor-DHsAPiW3-BdYCspQC.d.mts.map +1 -0
- package/dist/filter-expressions-Uo8ZdzwB-53qnqceS.mjs +181 -0
- package/dist/filter-expressions-Uo8ZdzwB-53qnqceS.mjs.map +1 -0
- package/dist/index-BR0ue13b.d.mts +136 -0
- package/dist/index-BR0ue13b.d.mts.map +1 -0
- package/dist/index-Cd2zLiU6.d.mts +128 -0
- package/dist/index-Cd2zLiU6.d.mts.map +1 -0
- package/dist/index-DAOkcTCK.d.mts +151 -0
- package/dist/index-DAOkcTCK.d.mts.map +1 -0
- package/dist/index-Dv2PNMiG.d.mts +53 -0
- package/dist/index-Dv2PNMiG.d.mts.map +1 -0
- package/dist/index-DxoO-ywI.d.mts +178 -0
- package/dist/index-DxoO-ywI.d.mts.map +1 -0
- package/dist/index-wKMm41HC.d.mts +30 -0
- package/dist/index-wKMm41HC.d.mts.map +1 -0
- package/dist/interpreter-BxbUTQ3k-ClR98Ned.mjs +1091 -0
- package/dist/interpreter-BxbUTQ3k-ClR98Ned.mjs.map +1 -0
- package/dist/ir-CQrenTYt.d.mts +147 -0
- package/dist/ir-CQrenTYt.d.mts.map +1 -0
- package/dist/ir-Ch2aPr7a.mjs +62 -0
- package/dist/ir-Ch2aPr7a.mjs.map +1 -0
- package/dist/lowering.d.mts +2 -0
- package/dist/lowering.mjs +1 -0
- package/dist/migration-CNDQv5yB.d.mts +50 -0
- package/dist/migration-CNDQv5yB.d.mts.map +1 -0
- package/dist/migration-dcM7u4HG.mjs +52 -0
- package/dist/migration-dcM7u4HG.mjs.map +1 -0
- package/dist/mongo-contract-serializer-Cx7Fae8e-C8ZYPXpc.mjs +119 -0
- package/dist/mongo-contract-serializer-Cx7Fae8e-C8ZYPXpc.mjs.map +1 -0
- package/dist/mongo-contract-view-AnejqfrQ-BaCFT0xm.d.mts +27 -0
- package/dist/mongo-contract-view-AnejqfrQ-BaCFT0xm.d.mts.map +1 -0
- package/dist/mongo-contract-view-rcWzby6f-wRBCUnBt.mjs +17 -0
- package/dist/mongo-contract-view-rcWzby6f-wRBCUnBt.mjs.map +1 -0
- package/dist/mongo-value-set-tNn9-pun-dJYx_rVD.d.mts +536 -0
- package/dist/mongo-value-set-tNn9-pun-dJYx_rVD.d.mts.map +1 -0
- package/dist/mongodb-types-B4HmhF1S.d.mts +2 -0
- package/dist/orm.d.mts +311 -0
- package/dist/orm.d.mts.map +1 -0
- package/dist/orm.mjs +664 -0
- package/dist/orm.mjs.map +1 -0
- package/dist/pack-D9aLY50F.d.mts +44 -0
- package/dist/pack-D9aLY50F.d.mts.map +1 -0
- package/dist/query-ast.d.mts +5 -0
- package/dist/query-ast.mjs +4 -0
- package/dist/query-ast__control.d.mts +4 -0
- package/dist/query-ast__control.mjs +3 -0
- package/dist/query-ast__execution.d.mts +3 -0
- package/dist/query-ast__execution.mjs +3 -0
- package/dist/query-builder.d.mts +836 -0
- package/dist/query-builder.d.mts.map +1 -0
- package/dist/query-builder.mjs +3 -0
- package/dist/query-builder__contract-free.d.mts +83 -0
- package/dist/query-builder__contract-free.d.mts.map +1 -0
- package/dist/query-builder__contract-free.mjs +107 -0
- package/dist/query-builder__contract-free.mjs.map +1 -0
- package/dist/query-plan-C0-Yu8hf-DdilEdW6.d.mts +822 -0
- package/dist/query-plan-C0-Yu8hf-DdilEdW6.d.mts.map +1 -0
- package/dist/runtime-DPkRW4iu.d.mts +1 -0
- package/dist/runtime.d.mts +286 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +532 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/schema-ir.d.mts +2 -0
- package/dist/schema-ir.mjs +2 -0
- package/dist/schema-verify-DJGr28XW.d.mts +23 -0
- package/dist/schema-verify-DJGr28XW.d.mts.map +1 -0
- package/dist/value.d.mts +2 -0
- package/dist/value.mjs +2 -0
- package/dist/value__mongodb-types.d.mts +2 -0
- package/dist/value__mongodb-types.mjs +1 -0
- package/dist/verify-mongo-schema-De0FG2Rm-CCzwnNPY.mjs +447 -0
- package/dist/verify-mongo-schema-De0FG2Rm-CCzwnNPY.mjs.map +1 -0
- package/dist/wire.d.mts +3 -0
- package/dist/wire.mjs +190 -0
- package/dist/wire.mjs.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,1091 @@
|
|
|
1
|
+
import { l as MongoValidator, o as MongoIndex } from "./entity-kinds-B4KNTF8z-DPIuUuuB.mjs";
|
|
2
|
+
import { a as applyPolymorphicScopeToMongoIndex, r as MongoStorage, s as buildMongoNamespace } from "./dist-DHrEJnhK.mjs";
|
|
3
|
+
import { t as mongoContractCanonicalizationHooks } from "./canonicalization-hooks-DF3Z6sjO.mjs";
|
|
4
|
+
import { crossRef } from "@prisma/orm-framework/contract/types";
|
|
5
|
+
import { UNBOUND_NAMESPACE_ID } from "@prisma/orm-framework/components/ir";
|
|
6
|
+
import { blindCast } from "@prisma/orm-framework/utils/casts";
|
|
7
|
+
import { computeProfileHash, computeStorageHash } from "@prisma/orm-framework/contract/hashing";
|
|
8
|
+
import { errorEnumCodecNotInPackStack } from "@prisma/orm-framework/errors/control";
|
|
9
|
+
import { instantiateAuthoringEntityType, isAuthoringEntityTypeDescriptor } from "@prisma/orm-framework/components/authoring";
|
|
10
|
+
import { nodePslSpan, parseQuotedStringLiteral } from "@prisma/orm-framework/psl-parser";
|
|
11
|
+
import { assertDefined } from "@prisma/orm-framework/utils/assertions";
|
|
12
|
+
import { ifDefined } from "@prisma/orm-framework/utils/defined";
|
|
13
|
+
import { notOk, ok } from "@prisma/orm-framework/utils/result";
|
|
14
|
+
//#region ../../../2-mongo-family/2-authoring/contract-psl/dist/interpreter-BxbUTQ3k.mjs
|
|
15
|
+
function resolveBsonType(codecId, codecLookup) {
|
|
16
|
+
return codecLookup?.targetTypesFor(codecId)?.[0];
|
|
17
|
+
}
|
|
18
|
+
function fieldToBsonSchema(field, valueObjects, codecLookup, valueSets) {
|
|
19
|
+
if (field.type.kind === "scalar") {
|
|
20
|
+
const bsonType = resolveBsonType(field.type.codecId, codecLookup);
|
|
21
|
+
if (!bsonType) return void 0;
|
|
22
|
+
const enumValues = field.valueSet !== void 0 ? valueSets?.[field.valueSet.entityName]?.values ?? null : null;
|
|
23
|
+
if ("many" in field && field.many) {
|
|
24
|
+
const items = { bsonType };
|
|
25
|
+
if (enumValues) items["enum"] = enumValues;
|
|
26
|
+
return {
|
|
27
|
+
bsonType: "array",
|
|
28
|
+
items
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (field.nullable) {
|
|
32
|
+
const s = { bsonType: ["null", bsonType] };
|
|
33
|
+
if (enumValues) s["enum"] = [...enumValues, null];
|
|
34
|
+
return s;
|
|
35
|
+
}
|
|
36
|
+
const s = { bsonType };
|
|
37
|
+
if (enumValues) s["enum"] = enumValues;
|
|
38
|
+
return s;
|
|
39
|
+
}
|
|
40
|
+
if (field.type.kind === "valueObject") {
|
|
41
|
+
const vo = valueObjects?.[field.type.name];
|
|
42
|
+
if (!vo) return void 0;
|
|
43
|
+
const voSchema = deriveObjectSchema(vo.fields, valueObjects, codecLookup, valueSets);
|
|
44
|
+
if ("many" in field && field.many) return {
|
|
45
|
+
bsonType: "array",
|
|
46
|
+
items: voSchema
|
|
47
|
+
};
|
|
48
|
+
if (field.nullable) return { oneOf: [{ bsonType: "null" }, voSchema] };
|
|
49
|
+
return voSchema;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function deriveObjectSchema(fields, valueObjects, codecLookup, valueSets) {
|
|
53
|
+
const properties = {};
|
|
54
|
+
const required = [];
|
|
55
|
+
for (const [fieldName, field] of Object.entries(fields)) {
|
|
56
|
+
const schema = fieldToBsonSchema(field, valueObjects, codecLookup, valueSets);
|
|
57
|
+
if (schema) {
|
|
58
|
+
properties[fieldName] = schema;
|
|
59
|
+
if (!field.nullable) required.push(fieldName);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const result = {
|
|
63
|
+
bsonType: "object",
|
|
64
|
+
properties,
|
|
65
|
+
additionalProperties: false
|
|
66
|
+
};
|
|
67
|
+
if (required.length > 0) result["required"] = required.sort();
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
function isRecord(value) {
|
|
71
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
72
|
+
}
|
|
73
|
+
function deriveJsonSchema(fields, valueObjects, codecLookup, valueSets) {
|
|
74
|
+
return new MongoValidator({
|
|
75
|
+
jsonSchema: deriveObjectSchema(fields, valueObjects, codecLookup, valueSets),
|
|
76
|
+
validationLevel: "strict",
|
|
77
|
+
validationAction: "error"
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function derivePolymorphicJsonSchema(baseFields, discriminatorField, variants, valueObjects, codecLookup, valueSets) {
|
|
81
|
+
const baseSchema = deriveObjectSchema(baseFields, valueObjects, codecLookup, valueSets);
|
|
82
|
+
const baseProperties = isRecord(baseSchema["properties"]) ? baseSchema["properties"] : {};
|
|
83
|
+
const oneOf = [];
|
|
84
|
+
for (const variant of variants) {
|
|
85
|
+
const variantOnlyFields = {};
|
|
86
|
+
for (const [name, field] of Object.entries(variant.fields)) if (!(name in baseFields)) variantOnlyFields[name] = field;
|
|
87
|
+
const variantProperties = {};
|
|
88
|
+
const variantRequired = [discriminatorField];
|
|
89
|
+
for (const [name, field] of Object.entries(variantOnlyFields)) {
|
|
90
|
+
const schema = fieldToBsonSchema(field, valueObjects, codecLookup, valueSets);
|
|
91
|
+
if (schema) {
|
|
92
|
+
variantProperties[name] = schema;
|
|
93
|
+
if (!field.nullable) variantRequired.push(name);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const entry = {
|
|
97
|
+
properties: {
|
|
98
|
+
...baseProperties,
|
|
99
|
+
[discriminatorField]: { enum: [variant.discriminatorValue] },
|
|
100
|
+
...variantProperties
|
|
101
|
+
},
|
|
102
|
+
required: variantRequired.sort(),
|
|
103
|
+
additionalProperties: false
|
|
104
|
+
};
|
|
105
|
+
oneOf.push(entry);
|
|
106
|
+
}
|
|
107
|
+
const jsonSchema = { ...baseSchema };
|
|
108
|
+
delete jsonSchema["additionalProperties"];
|
|
109
|
+
if (oneOf.length > 0) jsonSchema["oneOf"] = oneOf;
|
|
110
|
+
return new MongoValidator({
|
|
111
|
+
jsonSchema,
|
|
112
|
+
validationLevel: "strict",
|
|
113
|
+
validationAction: "error"
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function getPositionalArgument(attr, index = 0) {
|
|
117
|
+
return attr.args.filter((arg) => arg.kind === "positional")[index]?.value;
|
|
118
|
+
}
|
|
119
|
+
function getNamedArgument(attr, name) {
|
|
120
|
+
return attr.args.find((a) => a.kind === "named" && a.name === name)?.value;
|
|
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
|
|
144
|
+
};
|
|
145
|
+
return {
|
|
146
|
+
name: segment,
|
|
147
|
+
isWildcard: false
|
|
148
|
+
};
|
|
149
|
+
}
|
|
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
|
+
}
|
|
162
|
+
}
|
|
163
|
+
parts.push(input.slice(start));
|
|
164
|
+
return parts;
|
|
165
|
+
}
|
|
166
|
+
function lowerFirst(value) {
|
|
167
|
+
if (value.length === 0) return value;
|
|
168
|
+
return value[0]?.toLowerCase() + value.slice(1);
|
|
169
|
+
}
|
|
170
|
+
function getAttribute(attributes, name) {
|
|
171
|
+
return attributes.find((attr) => attr.name === name);
|
|
172
|
+
}
|
|
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);
|
|
179
|
+
}
|
|
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;
|
|
192
|
+
return {
|
|
193
|
+
...ifDefined("relationName", relationName),
|
|
194
|
+
...ifDefined("fields", fields),
|
|
195
|
+
...ifDefined("references", references)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function stripQuotes(value) {
|
|
199
|
+
if (value.startsWith("\"") && value.endsWith("\"")) return value.slice(1, -1);
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Encode an authored enum value to its codec-encoded JSON form via the codec resolved by id from the
|
|
204
|
+
* contract's codec lookup, so a non-identity `encodeJson` (permitted by the `mongoCodec` factory) is
|
|
205
|
+
* respected. Matches the TS builder's `encodeEnumValue`: the lookup is always threaded in production,
|
|
206
|
+
* and a codecId the lookup cannot resolve is a hard error — the enum uses a codec that is not part of
|
|
207
|
+
* the contract's pack stack.
|
|
208
|
+
*/
|
|
209
|
+
function encodeEnumValue(value, codecId, codecLookup) {
|
|
210
|
+
const codec = codecLookup.get(codecId);
|
|
211
|
+
if (!codec) throw errorEnumCodecNotInPackStack({ codecId });
|
|
212
|
+
return codec.encodeJson(value);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Mongo's PSL surface binds the database from the connection string, so every
|
|
216
|
+
* explicit namespace block is invalid, including `namespace unbound { … }`.
|
|
217
|
+
*/
|
|
218
|
+
function validateNamespaceBlocksForMongoTarget(input) {
|
|
219
|
+
for (const namespace of input.namespaces) input.diagnostics.push({
|
|
220
|
+
code: "PSL_UNSUPPORTED_NAMESPACE_BLOCK",
|
|
221
|
+
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).`,
|
|
222
|
+
sourceId: input.sourceId,
|
|
223
|
+
span: nodePslSpan(namespace.node.syntax, input.sourceFile)
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
function fkRelationPairKey(declaringModel, targetModel) {
|
|
227
|
+
return `${declaringModel}::${targetModel}`;
|
|
228
|
+
}
|
|
229
|
+
function resolveFieldMappings(model) {
|
|
230
|
+
const pslNameToMapped = /* @__PURE__ */ new Map();
|
|
231
|
+
for (const field of Object.values(model.fields)) {
|
|
232
|
+
const mapped = getMapName(field.attributes) ?? field.name;
|
|
233
|
+
pslNameToMapped.set(field.name, mapped);
|
|
234
|
+
}
|
|
235
|
+
return { pslNameToMapped };
|
|
236
|
+
}
|
|
237
|
+
function resolveCollectionName(model) {
|
|
238
|
+
return getMapName(model.attributes) ?? lowerFirst(model.name);
|
|
239
|
+
}
|
|
240
|
+
function mongoCrossRef(modelName) {
|
|
241
|
+
return crossRef(modelName, UNBOUND_NAMESPACE_ID);
|
|
242
|
+
}
|
|
243
|
+
function collectPolymorphismDeclarations(models, sourceId, diagnostics) {
|
|
244
|
+
const discriminatorDeclarations = /* @__PURE__ */ new Map();
|
|
245
|
+
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({
|
|
261
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
262
|
+
message: `Discriminator field "${fieldName}" on model "${model.name}" must be of type String, but is "${discField.typeName}"`,
|
|
263
|
+
sourceId,
|
|
264
|
+
span: attr.span
|
|
265
|
+
});
|
|
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
|
|
282
|
+
});
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
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
|
|
292
|
+
});
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const collectionName = resolveCollectionName(model);
|
|
296
|
+
baseDeclarations.set(model.name, {
|
|
297
|
+
baseName,
|
|
298
|
+
value,
|
|
299
|
+
collectionName,
|
|
300
|
+
span: attr.span
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
discriminatorDeclarations,
|
|
306
|
+
baseDeclarations
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function resolvePolymorphism(input) {
|
|
310
|
+
const { discriminatorDeclarations, baseDeclarations, modelNames, sourceId, allModels: allModelViews, indexSpans, modelIndexesByName } = input;
|
|
311
|
+
let patched = input.models;
|
|
312
|
+
let roots = input.roots;
|
|
313
|
+
let collections = input.collections;
|
|
314
|
+
const diagnostics = [];
|
|
315
|
+
for (const [modelName, decl] of discriminatorDeclarations) {
|
|
316
|
+
if (baseDeclarations.has(modelName)) {
|
|
317
|
+
diagnostics.push({
|
|
318
|
+
code: "PSL_DISCRIMINATOR_AND_BASE",
|
|
319
|
+
message: `Model "${modelName}" cannot have both @@discriminator and @@base`,
|
|
320
|
+
sourceId,
|
|
321
|
+
span: decl.span
|
|
322
|
+
});
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const model = patched[modelName];
|
|
326
|
+
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;
|
|
329
|
+
if (!Object.hasOwn(model.fields, mappedDiscriminatorField)) {
|
|
330
|
+
diagnostics.push({
|
|
331
|
+
code: "PSL_DISCRIMINATOR_FIELD_NOT_FOUND",
|
|
332
|
+
message: `Discriminator field "${decl.fieldName}" is not a field on model "${modelName}"`,
|
|
333
|
+
sourceId,
|
|
334
|
+
span: decl.span
|
|
335
|
+
});
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
const variants = {};
|
|
339
|
+
for (const [variantName, baseDecl] of baseDeclarations) {
|
|
340
|
+
if (baseDecl.baseName !== modelName) continue;
|
|
341
|
+
variants[variantName] = { value: baseDecl.value };
|
|
342
|
+
}
|
|
343
|
+
if (Object.keys(variants).length === 0) {
|
|
344
|
+
diagnostics.push({
|
|
345
|
+
code: "PSL_ORPHANED_DISCRIMINATOR",
|
|
346
|
+
message: `Model "${modelName}" has @@discriminator but no variant models declare @@base(${modelName}, ...)`,
|
|
347
|
+
sourceId,
|
|
348
|
+
span: decl.span
|
|
349
|
+
});
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
patched = {
|
|
353
|
+
...patched,
|
|
354
|
+
[modelName]: {
|
|
355
|
+
...model,
|
|
356
|
+
discriminator: { field: mappedDiscriminatorField },
|
|
357
|
+
variants
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
for (const [variantName, baseDecl] of baseDeclarations) {
|
|
362
|
+
if (!modelNames.has(baseDecl.baseName)) {
|
|
363
|
+
diagnostics.push({
|
|
364
|
+
code: "PSL_BASE_TARGET_NOT_FOUND",
|
|
365
|
+
message: `Model "${variantName}" @@base references non-existent model "${baseDecl.baseName}"`,
|
|
366
|
+
sourceId,
|
|
367
|
+
span: baseDecl.span
|
|
368
|
+
});
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if (!discriminatorDeclarations.has(baseDecl.baseName)) {
|
|
372
|
+
diagnostics.push({
|
|
373
|
+
code: "PSL_ORPHANED_BASE",
|
|
374
|
+
message: `Model "${variantName}" declares @@base(${baseDecl.baseName}, ...) but "${baseDecl.baseName}" has no @@discriminator`,
|
|
375
|
+
sourceId,
|
|
376
|
+
span: baseDecl.span
|
|
377
|
+
});
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (discriminatorDeclarations.has(variantName)) continue;
|
|
381
|
+
const baseModel = patched[baseDecl.baseName];
|
|
382
|
+
const variantModelView = allModelViews.find((m) => m.name === variantName);
|
|
383
|
+
if (!variantModelView) continue;
|
|
384
|
+
if (getMapName(variantModelView.attributes) !== void 0 && baseModel && baseDecl.collectionName !== baseModel.storage.collection) {
|
|
385
|
+
diagnostics.push({
|
|
386
|
+
code: "PSL_MONGO_VARIANT_SEPARATE_COLLECTION",
|
|
387
|
+
message: `Mongo variant "${variantName}" cannot use a different collection than its base "${baseDecl.baseName}". Mongo only supports single-collection polymorphism.`,
|
|
388
|
+
sourceId,
|
|
389
|
+
span: baseDecl.span
|
|
390
|
+
});
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
const baseCollection = baseModel?.storage.collection ?? baseDecl.collectionName;
|
|
394
|
+
const variantModel = patched[variantName];
|
|
395
|
+
if (variantModel) patched = {
|
|
396
|
+
...patched,
|
|
397
|
+
[variantName]: {
|
|
398
|
+
...variantModel,
|
|
399
|
+
base: mongoCrossRef(baseDecl.baseName),
|
|
400
|
+
storage: { collection: baseCollection }
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
const variantCollectionName = resolveCollectionName(variantModelView);
|
|
404
|
+
if (roots[variantCollectionName]?.model === variantName) if (variantCollectionName === baseCollection && baseModel) roots = {
|
|
405
|
+
...roots,
|
|
406
|
+
[variantCollectionName]: mongoCrossRef(baseDecl.baseName)
|
|
407
|
+
};
|
|
408
|
+
else roots = Object.fromEntries(Object.entries(roots).filter(([key]) => key !== variantCollectionName));
|
|
409
|
+
const variantOwnIndexes = modelIndexesByName.get(variantName) ?? [];
|
|
410
|
+
const baseColl = collections[baseCollection];
|
|
411
|
+
const discriminatorField = patched[baseDecl.baseName]?.discriminator?.field;
|
|
412
|
+
const scopedVariantIndexes = [];
|
|
413
|
+
if (discriminatorField) for (const idx of variantOwnIndexes) {
|
|
414
|
+
const result = applyPolymorphicScopeToMongoIndex(idx, {
|
|
415
|
+
discriminatorField,
|
|
416
|
+
discriminatorValue: baseDecl.value
|
|
417
|
+
});
|
|
418
|
+
if (result.kind === "conflict") {
|
|
419
|
+
const span = indexSpans.get(idx) ?? baseDecl.span;
|
|
420
|
+
diagnostics.push({
|
|
421
|
+
code: "PSL_INVALID_INDEX",
|
|
422
|
+
message: `Variant "${variantName}" index conflicts with discriminator scope: ${result.reason}`,
|
|
423
|
+
sourceId,
|
|
424
|
+
span
|
|
425
|
+
});
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (result.index !== idx) indexSpans.set(result.index, indexSpans.get(idx) ?? baseDecl.span);
|
|
429
|
+
scopedVariantIndexes.push(result.index);
|
|
430
|
+
}
|
|
431
|
+
else scopedVariantIndexes.push(...variantOwnIndexes);
|
|
432
|
+
if (variantCollectionName !== baseCollection) {
|
|
433
|
+
const filtered = Object.fromEntries(Object.entries(collections).filter(([key]) => key !== variantCollectionName));
|
|
434
|
+
if (scopedVariantIndexes.length > 0 && baseColl) {
|
|
435
|
+
const baseIndexes = baseColl["indexes"] ?? [];
|
|
436
|
+
collections = {
|
|
437
|
+
...filtered,
|
|
438
|
+
[baseCollection]: {
|
|
439
|
+
...baseColl,
|
|
440
|
+
indexes: [...baseIndexes, ...scopedVariantIndexes]
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
} else collections = filtered;
|
|
444
|
+
} else if (baseColl) {
|
|
445
|
+
const existingIndexes = baseColl["indexes"] ?? [];
|
|
446
|
+
const variantIndexSet = new Set(variantOwnIndexes);
|
|
447
|
+
const withoutUnscopedVariants = existingIndexes.filter((idx) => !variantIndexSet.has(idx));
|
|
448
|
+
const mergedIndexes = [...withoutUnscopedVariants];
|
|
449
|
+
for (const idx of scopedVariantIndexes) {
|
|
450
|
+
const idxKey = canonicalJson(idx);
|
|
451
|
+
if (!withoutUnscopedVariants.some((existing) => canonicalJson(existing) === idxKey)) mergedIndexes.push(idx);
|
|
452
|
+
}
|
|
453
|
+
if (mergedIndexes.length !== existingIndexes.length || mergedIndexes.some((idx, i) => idx !== existingIndexes[i])) {
|
|
454
|
+
const next = { ...baseColl };
|
|
455
|
+
if (mergedIndexes.length > 0) next["indexes"] = mergedIndexes;
|
|
456
|
+
else delete next["indexes"];
|
|
457
|
+
collections = {
|
|
458
|
+
...collections,
|
|
459
|
+
[baseCollection]: next
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
models: patched,
|
|
466
|
+
roots,
|
|
467
|
+
collections,
|
|
468
|
+
diagnostics
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
function canonicalJson(value) {
|
|
472
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
473
|
+
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
|
+
return JSON.stringify(value);
|
|
475
|
+
}
|
|
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 {}
|
|
505
|
+
}
|
|
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;
|
|
509
|
+
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;
|
|
526
|
+
return collation;
|
|
527
|
+
}
|
|
528
|
+
function stripQuotesHelper(raw) {
|
|
529
|
+
if (!raw) return void 0;
|
|
530
|
+
return raw.replace(/^["']/, "").replace(/["']$/, "");
|
|
531
|
+
}
|
|
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;
|
|
540
|
+
}
|
|
541
|
+
function collectIndexes(pslModel, fieldMappings, modelNames, sourceId, diagnostics, indexSpans) {
|
|
542
|
+
const indexes = [];
|
|
543
|
+
let textIndexCount = 0;
|
|
544
|
+
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
|
+
}
|
|
549
|
+
for (const field of Object.values(pslModel.fields)) {
|
|
550
|
+
if (modelNames.has(field.typeName)) continue;
|
|
551
|
+
const uniqueAttr = getAttribute(field.attributes, "unique");
|
|
552
|
+
if (!uniqueAttr) continue;
|
|
553
|
+
const fieldUniqueIndex = new MongoIndex({
|
|
554
|
+
keys: [{
|
|
555
|
+
field: fieldMappings.pslNameToMapped.get(field.name) ?? field.name,
|
|
556
|
+
direction: 1
|
|
557
|
+
}],
|
|
558
|
+
unique: true
|
|
559
|
+
});
|
|
560
|
+
indexes.push(fieldUniqueIndex);
|
|
561
|
+
indexSpans.set(fieldUniqueIndex, uniqueAttr.span);
|
|
562
|
+
}
|
|
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",
|
|
586
|
+
sourceId,
|
|
587
|
+
span: attr.span
|
|
588
|
+
});
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
if (isTextIndex) {
|
|
592
|
+
textIndexCount++;
|
|
593
|
+
if (textIndexCount > 1) {
|
|
594
|
+
diagnostics.push({
|
|
595
|
+
code: "PSL_INVALID_INDEX",
|
|
596
|
+
message: `Only one @@textIndex is allowed per collection (model "${pslModel.name}")`,
|
|
597
|
+
sourceId,
|
|
598
|
+
span: attr.span
|
|
599
|
+
});
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
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",
|
|
695
|
+
sourceId,
|
|
696
|
+
span: attr.span
|
|
697
|
+
});
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
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
|
+
});
|
|
731
|
+
indexes.push(index);
|
|
732
|
+
indexSpans.set(index, attr.span);
|
|
733
|
+
}
|
|
734
|
+
return indexes;
|
|
735
|
+
}
|
|
736
|
+
function isRelationField(field, modelNames) {
|
|
737
|
+
return modelNames.has(field.typeName);
|
|
738
|
+
}
|
|
739
|
+
const MONGO_OBJECT_ID_PSL_TYPE = "ObjectId";
|
|
740
|
+
function resolveFieldCodecId(field, scalarTypeCodecIds) {
|
|
741
|
+
return scalarTypeCodecIds.get(field.typeName);
|
|
742
|
+
}
|
|
743
|
+
function resolveNonRelationField(field, ownerName, compositeTypeNames, scalarTypeCodecIds, codecIdByEnumName, sourceId, diagnostics) {
|
|
744
|
+
if (compositeTypeNames.has(field.typeName)) {
|
|
745
|
+
const result = {
|
|
746
|
+
type: {
|
|
747
|
+
kind: "valueObject",
|
|
748
|
+
name: field.typeName
|
|
749
|
+
},
|
|
750
|
+
nullable: field.optional
|
|
751
|
+
};
|
|
752
|
+
return field.list ? {
|
|
753
|
+
...result,
|
|
754
|
+
many: true
|
|
755
|
+
} : result;
|
|
756
|
+
}
|
|
757
|
+
const enumCodecId = codecIdByEnumName.get(field.typeName);
|
|
758
|
+
if (enumCodecId !== void 0) {
|
|
759
|
+
const valueSet = {
|
|
760
|
+
plane: "domain",
|
|
761
|
+
entityKind: "enum",
|
|
762
|
+
namespaceId: UNBOUND_NAMESPACE_ID,
|
|
763
|
+
entityName: field.typeName
|
|
764
|
+
};
|
|
765
|
+
const result = {
|
|
766
|
+
type: {
|
|
767
|
+
kind: "scalar",
|
|
768
|
+
codecId: enumCodecId
|
|
769
|
+
},
|
|
770
|
+
nullable: field.optional,
|
|
771
|
+
valueSet
|
|
772
|
+
};
|
|
773
|
+
return field.list ? {
|
|
774
|
+
...result,
|
|
775
|
+
many: true
|
|
776
|
+
} : result;
|
|
777
|
+
}
|
|
778
|
+
if (field.malformedType) return;
|
|
779
|
+
const codecId = resolveFieldCodecId(field, scalarTypeCodecIds);
|
|
780
|
+
if (!codecId) {
|
|
781
|
+
diagnostics.push({
|
|
782
|
+
code: "PSL_UNSUPPORTED_FIELD_TYPE",
|
|
783
|
+
message: `Field "${ownerName}.${field.name}" type "${field.typeName}" is not supported in Mongo PSL interpreter`,
|
|
784
|
+
sourceId,
|
|
785
|
+
span: field.span
|
|
786
|
+
});
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
const result = {
|
|
790
|
+
type: {
|
|
791
|
+
kind: "scalar",
|
|
792
|
+
codecId
|
|
793
|
+
},
|
|
794
|
+
nullable: field.optional
|
|
795
|
+
};
|
|
796
|
+
return field.list ? {
|
|
797
|
+
...result,
|
|
798
|
+
many: true
|
|
799
|
+
} : result;
|
|
800
|
+
}
|
|
801
|
+
function processEnumDeclarations(input) {
|
|
802
|
+
const builtEnums = {};
|
|
803
|
+
if (input.enumBlocks.length === 0) return builtEnums;
|
|
804
|
+
const enumDescriptor = input.authoringContributions?.entityTypes?.["enum"] !== void 0 && isAuthoringEntityTypeDescriptor(input.authoringContributions.entityTypes["enum"]) ? input.authoringContributions.entityTypes["enum"] : void 0;
|
|
805
|
+
if (!enumDescriptor) {
|
|
806
|
+
for (const decl of input.enumBlocks) input.diagnostics.push({
|
|
807
|
+
code: "PSL_ENUM_MISSING_FACTORY",
|
|
808
|
+
message: `enum "${decl.name}" requires an "enum" entityType factory in the active authoring contributions`,
|
|
809
|
+
sourceId: input.sourceId,
|
|
810
|
+
span: decl.span
|
|
811
|
+
});
|
|
812
|
+
return builtEnums;
|
|
813
|
+
}
|
|
814
|
+
for (const decl of input.enumBlocks) {
|
|
815
|
+
const handle = instantiateAuthoringEntityType("enum", enumDescriptor, [decl], input.entityContext);
|
|
816
|
+
if (handle === void 0 || handle === null) continue;
|
|
817
|
+
builtEnums[decl.name] = {
|
|
818
|
+
codecId: handle.codecId,
|
|
819
|
+
members: handle.enumMembers.map((m) => ({
|
|
820
|
+
name: m.name,
|
|
821
|
+
value: blindCast(m.value)
|
|
822
|
+
}))
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
return builtEnums;
|
|
826
|
+
}
|
|
827
|
+
function interpretPslDocumentToMongoContract(input) {
|
|
828
|
+
const { symbolTable, sourceFile, scalarTypeCodecIds, codecLookup } = input;
|
|
829
|
+
const sourceId = input.sourceId;
|
|
830
|
+
const diagnostics = [...input.seedDiagnostics ?? []];
|
|
831
|
+
const topLevel = symbolTable.topLevel;
|
|
832
|
+
validateNamespaceBlocksForMongoTarget({
|
|
833
|
+
namespaces: Object.values(topLevel.namespaces),
|
|
834
|
+
sourceId,
|
|
835
|
+
sourceFile,
|
|
836
|
+
diagnostics
|
|
837
|
+
});
|
|
838
|
+
const allModels = Object.values(topLevel.models);
|
|
839
|
+
const allCompositeTypes = Object.values(topLevel.compositeTypes);
|
|
840
|
+
const modelNames = new Set(allModels.map((m) => m.name));
|
|
841
|
+
const compositeTypeNames = new Set(allCompositeTypes.map((ct) => ct.name));
|
|
842
|
+
const builtEnums = processEnumDeclarations({
|
|
843
|
+
enumBlocks: Object.values(topLevel.blocks).filter((b) => b.keyword === "enum").map((b) => b.block),
|
|
844
|
+
sourceId,
|
|
845
|
+
authoringContributions: input.authoringContributions,
|
|
846
|
+
entityContext: {
|
|
847
|
+
family: "mongo",
|
|
848
|
+
target: "mongo",
|
|
849
|
+
...ifDefined("enumInferenceCodecs", input.enumInferenceCodecs),
|
|
850
|
+
...ifDefined("codecLookup", codecLookup),
|
|
851
|
+
sourceId,
|
|
852
|
+
diagnostics: { push: (d) => {
|
|
853
|
+
diagnostics.push(blindCast(d));
|
|
854
|
+
} }
|
|
855
|
+
},
|
|
856
|
+
diagnostics
|
|
857
|
+
});
|
|
858
|
+
const codecIdByEnumName = new Map(Object.entries(builtEnums).map(([name, e]) => [name, e.codecId]));
|
|
859
|
+
const models = {};
|
|
860
|
+
const collections = {};
|
|
861
|
+
const roots = {};
|
|
862
|
+
const allFkRelations = [];
|
|
863
|
+
const indexSpans = /* @__PURE__ */ new Map();
|
|
864
|
+
const modelIndexesByName = /* @__PURE__ */ new Map();
|
|
865
|
+
const backrelationCandidates = [];
|
|
866
|
+
for (const pslModel of allModels) {
|
|
867
|
+
const collectionName = resolveCollectionName(pslModel);
|
|
868
|
+
const fieldMappings = resolveFieldMappings(pslModel);
|
|
869
|
+
const fields = {};
|
|
870
|
+
const relations = {};
|
|
871
|
+
for (const field of Object.values(pslModel.fields)) {
|
|
872
|
+
if (isRelationField(field, modelNames)) {
|
|
873
|
+
const relation = parseRelationAttribute(field.attributes);
|
|
874
|
+
if (field.list || !(relation?.fields && relation?.references)) {
|
|
875
|
+
backrelationCandidates.push({
|
|
876
|
+
modelName: pslModel.name,
|
|
877
|
+
fieldName: field.name,
|
|
878
|
+
targetModelName: field.typeName,
|
|
879
|
+
...ifDefined("relationName", relation?.relationName),
|
|
880
|
+
cardinality: field.list ? "1:N" : "1:1",
|
|
881
|
+
field
|
|
882
|
+
});
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
if (relation?.fields && relation?.references) {
|
|
886
|
+
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;
|
|
889
|
+
const targetMapped = relation.references.map((f) => targetFieldMappings?.pslNameToMapped.get(f) ?? f);
|
|
890
|
+
relations[field.name] = {
|
|
891
|
+
to: mongoCrossRef(field.typeName),
|
|
892
|
+
cardinality: "N:1",
|
|
893
|
+
on: {
|
|
894
|
+
localFields: localMapped,
|
|
895
|
+
targetFields: targetMapped
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
allFkRelations.push({
|
|
899
|
+
declaringModel: pslModel.name,
|
|
900
|
+
fieldName: field.name,
|
|
901
|
+
targetModel: field.typeName,
|
|
902
|
+
...ifDefined("relationName", relation.relationName),
|
|
903
|
+
localFields: localMapped,
|
|
904
|
+
targetFields: targetMapped
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
const resolved = resolveNonRelationField(field, pslModel.name, compositeTypeNames, scalarTypeCodecIds, codecIdByEnumName, sourceId, diagnostics);
|
|
910
|
+
if (!resolved) continue;
|
|
911
|
+
const mappedName = fieldMappings.pslNameToMapped.get(field.name) ?? field.name;
|
|
912
|
+
fields[mappedName] = resolved;
|
|
913
|
+
}
|
|
914
|
+
const isVariantModel = pslModel.attributes.some((attr) => attr.name === "base");
|
|
915
|
+
const hasIdField = Object.values(pslModel.fields).some((f) => getAttribute(f.attributes, "id") !== void 0);
|
|
916
|
+
if (!isVariantModel) if (!hasIdField) diagnostics.push({
|
|
917
|
+
code: "PSL_MISSING_ID_FIELD",
|
|
918
|
+
message: `Model "${pslModel.name}" has no field with @id attribute. Every model must have exactly one @id field.`,
|
|
919
|
+
sourceId
|
|
920
|
+
});
|
|
921
|
+
else {
|
|
922
|
+
const objectIdCodecId = scalarTypeCodecIds.get(MONGO_OBJECT_ID_PSL_TYPE);
|
|
923
|
+
const idField = fields["_id"];
|
|
924
|
+
if (!(idField !== void 0 && idField.type.kind === "scalar" && objectIdCodecId !== void 0 && idField.type.codecId === objectIdCodecId)) diagnostics.push({
|
|
925
|
+
code: "PSL_MONGO_ID_REQUIRED",
|
|
926
|
+
message: `Model "${pslModel.name}" must declare an _id field of type ObjectId (e.g. \`id ObjectId @id @map("_id")\`).`,
|
|
927
|
+
sourceId
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
models[pslModel.name] = {
|
|
931
|
+
fields,
|
|
932
|
+
relations,
|
|
933
|
+
storage: { collection: collectionName }
|
|
934
|
+
};
|
|
935
|
+
const modelIndexes = collectIndexes(pslModel, fieldMappings, modelNames, sourceId, diagnostics, indexSpans);
|
|
936
|
+
modelIndexesByName.set(pslModel.name, modelIndexes);
|
|
937
|
+
const existingColl = collections[collectionName];
|
|
938
|
+
if (existingColl && modelIndexes.length > 0) collections[collectionName] = { indexes: [...existingColl["indexes"] ?? [], ...modelIndexes] };
|
|
939
|
+
else if (!existingColl) collections[collectionName] = modelIndexes.length > 0 ? { indexes: modelIndexes } : {};
|
|
940
|
+
roots[collectionName] = mongoCrossRef(pslModel.name);
|
|
941
|
+
}
|
|
942
|
+
const valueObjects = {};
|
|
943
|
+
for (const compositeType of allCompositeTypes) {
|
|
944
|
+
const fields = {};
|
|
945
|
+
for (const field of Object.values(compositeType.fields)) {
|
|
946
|
+
const resolved = resolveNonRelationField(field, compositeType.name, compositeTypeNames, scalarTypeCodecIds, codecIdByEnumName, sourceId, diagnostics);
|
|
947
|
+
if (!resolved) continue;
|
|
948
|
+
fields[field.name] = resolved;
|
|
949
|
+
}
|
|
950
|
+
valueObjects[compositeType.name] = { fields };
|
|
951
|
+
}
|
|
952
|
+
const fkRelationsByPair = /* @__PURE__ */ new Map();
|
|
953
|
+
for (const fk of allFkRelations) {
|
|
954
|
+
const key = fkRelationPairKey(fk.declaringModel, fk.targetModel);
|
|
955
|
+
const existing = fkRelationsByPair.get(key);
|
|
956
|
+
if (existing) existing.push(fk);
|
|
957
|
+
else fkRelationsByPair.set(key, [fk]);
|
|
958
|
+
}
|
|
959
|
+
for (const candidate of backrelationCandidates) {
|
|
960
|
+
const pairKey = fkRelationPairKey(candidate.targetModelName, candidate.modelName);
|
|
961
|
+
const pairMatches = fkRelationsByPair.get(pairKey) ?? [];
|
|
962
|
+
const matches = candidate.relationName ? pairMatches.filter((r) => r.relationName === candidate.relationName) : [...pairMatches];
|
|
963
|
+
if (matches.length === 0) {
|
|
964
|
+
diagnostics.push({
|
|
965
|
+
code: "PSL_ORPHANED_BACKRELATION",
|
|
966
|
+
message: `Backrelation list field "${candidate.modelName}.${candidate.fieldName}" has no matching FK-side relation on model "${candidate.targetModelName}". Add @relation(fields: [...], references: [...]) on the FK-side relation or use an explicit join model for many-to-many.`,
|
|
967
|
+
sourceId,
|
|
968
|
+
span: candidate.field.span
|
|
969
|
+
});
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
if (matches.length > 1) {
|
|
973
|
+
diagnostics.push({
|
|
974
|
+
code: "PSL_AMBIGUOUS_BACKRELATION",
|
|
975
|
+
message: `Backrelation list field "${candidate.modelName}.${candidate.fieldName}" matches multiple FK-side relations on model "${candidate.targetModelName}". Add @relation("...") to both sides to disambiguate.`,
|
|
976
|
+
sourceId,
|
|
977
|
+
span: candidate.field.span
|
|
978
|
+
});
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
const fk = matches[0];
|
|
982
|
+
if (!fk) continue;
|
|
983
|
+
const modelEntry = models[candidate.modelName];
|
|
984
|
+
if (!modelEntry) continue;
|
|
985
|
+
modelEntry.relations[candidate.fieldName] = {
|
|
986
|
+
to: mongoCrossRef(candidate.targetModelName),
|
|
987
|
+
cardinality: candidate.cardinality,
|
|
988
|
+
on: {
|
|
989
|
+
localFields: fk.targetFields,
|
|
990
|
+
targetFields: fk.localFields
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
const { discriminatorDeclarations, baseDeclarations } = collectPolymorphismDeclarations(allModels, sourceId, diagnostics);
|
|
995
|
+
const polyResult = resolvePolymorphism({
|
|
996
|
+
models,
|
|
997
|
+
roots,
|
|
998
|
+
collections,
|
|
999
|
+
allModels,
|
|
1000
|
+
discriminatorDeclarations,
|
|
1001
|
+
baseDeclarations,
|
|
1002
|
+
modelNames,
|
|
1003
|
+
indexSpans,
|
|
1004
|
+
modelIndexesByName,
|
|
1005
|
+
sourceId
|
|
1006
|
+
});
|
|
1007
|
+
if (diagnostics.length > 0 || polyResult.diagnostics.length > 0) return notOk({
|
|
1008
|
+
summary: "PSL to Mongo contract interpretation failed",
|
|
1009
|
+
diagnostics: [...diagnostics, ...polyResult.diagnostics]
|
|
1010
|
+
});
|
|
1011
|
+
const resolvedModels = polyResult.models;
|
|
1012
|
+
const resolvedCollections = polyResult.collections;
|
|
1013
|
+
const storageValueSets = {};
|
|
1014
|
+
const enumEntries = Object.entries(builtEnums);
|
|
1015
|
+
if (enumEntries.length > 0) {
|
|
1016
|
+
assertDefined(codecLookup, "Mongo PSL interpretation requires a codec lookup to encode enum values");
|
|
1017
|
+
for (const [enumName, builtEnum] of enumEntries) storageValueSets[enumName] = {
|
|
1018
|
+
kind: "valueSet",
|
|
1019
|
+
values: builtEnum.members.map((m) => encodeEnumValue(m.value, builtEnum.codecId, codecLookup))
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
for (const [, modelEntry] of Object.entries(resolvedModels)) {
|
|
1023
|
+
if (modelEntry.base) continue;
|
|
1024
|
+
const coll = resolvedCollections[modelEntry.storage.collection];
|
|
1025
|
+
if (!coll) continue;
|
|
1026
|
+
if (modelEntry.discriminator && modelEntry.variants) {
|
|
1027
|
+
const variantEntries = Object.entries(modelEntry.variants).map(([variantName, { value }]) => ({
|
|
1028
|
+
discriminatorValue: value,
|
|
1029
|
+
fields: resolvedModels[variantName]?.fields ?? {}
|
|
1030
|
+
}));
|
|
1031
|
+
coll["validator"] = derivePolymorphicJsonSchema(modelEntry.fields, modelEntry.discriminator.field, variantEntries, valueObjects, codecLookup, storageValueSets);
|
|
1032
|
+
} else coll["validator"] = deriveJsonSchema(modelEntry.fields, valueObjects, codecLookup, storageValueSets);
|
|
1033
|
+
}
|
|
1034
|
+
const target = "mongo";
|
|
1035
|
+
const targetFamily = "mongo";
|
|
1036
|
+
const collectionInputs = {};
|
|
1037
|
+
for (const [name, coll] of Object.entries(resolvedCollections)) {
|
|
1038
|
+
const raw = {};
|
|
1039
|
+
if (coll["indexes"] != null) raw["indexes"] = coll["indexes"];
|
|
1040
|
+
if (coll["validator"] != null) raw["validator"] = coll["validator"];
|
|
1041
|
+
if (coll["options"] != null) raw["options"] = coll["options"];
|
|
1042
|
+
collectionInputs[name] = blindCast(raw);
|
|
1043
|
+
}
|
|
1044
|
+
const unboundNamespace = buildMongoNamespace({
|
|
1045
|
+
id: UNBOUND_NAMESPACE_ID,
|
|
1046
|
+
entries: {
|
|
1047
|
+
collection: collectionInputs,
|
|
1048
|
+
...Object.keys(storageValueSets).length > 0 ? { valueSet: storageValueSets } : {}
|
|
1049
|
+
}
|
|
1050
|
+
});
|
|
1051
|
+
const storage = new MongoStorage({
|
|
1052
|
+
storageHash: computeStorageHash({
|
|
1053
|
+
target,
|
|
1054
|
+
targetFamily,
|
|
1055
|
+
storage: { namespaces: { [UNBOUND_NAMESPACE_ID]: {
|
|
1056
|
+
id: UNBOUND_NAMESPACE_ID,
|
|
1057
|
+
entries: {
|
|
1058
|
+
collection: unboundNamespace.entries.collection,
|
|
1059
|
+
...unboundNamespace.entries.valueSet !== void 0 ? { valueSet: unboundNamespace.entries.valueSet } : {}
|
|
1060
|
+
}
|
|
1061
|
+
} } },
|
|
1062
|
+
...mongoContractCanonicalizationHooks
|
|
1063
|
+
}),
|
|
1064
|
+
namespaces: { [UNBOUND_NAMESPACE_ID]: unboundNamespace }
|
|
1065
|
+
});
|
|
1066
|
+
const capabilities = {};
|
|
1067
|
+
const hasEnums = Object.keys(builtEnums).length > 0;
|
|
1068
|
+
return ok({
|
|
1069
|
+
targetFamily,
|
|
1070
|
+
target,
|
|
1071
|
+
roots: polyResult.roots,
|
|
1072
|
+
domain: { namespaces: { [UNBOUND_NAMESPACE_ID]: {
|
|
1073
|
+
models: polyResult.models,
|
|
1074
|
+
...Object.keys(valueObjects).length > 0 ? { valueObjects } : {},
|
|
1075
|
+
...hasEnums ? { enum: builtEnums } : {}
|
|
1076
|
+
} } },
|
|
1077
|
+
storage,
|
|
1078
|
+
extensions: {},
|
|
1079
|
+
capabilities,
|
|
1080
|
+
profileHash: computeProfileHash({
|
|
1081
|
+
target,
|
|
1082
|
+
targetFamily,
|
|
1083
|
+
capabilities
|
|
1084
|
+
}),
|
|
1085
|
+
meta: {}
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
//#endregion
|
|
1089
|
+
export { derivePolymorphicJsonSchema as n, interpretPslDocumentToMongoContract as r, deriveJsonSchema as t };
|
|
1090
|
+
|
|
1091
|
+
//# sourceMappingURL=interpreter-BxbUTQ3k-ClR98Ned.mjs.map
|