@prisma/orm-family-mongo 8.0.0-rc.18-dev.11 → 8.0.0-rc.18-dev.13
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/{authoring-entity-types-8yNXQKSJ-COs9oUJY.mjs → authoring-entity-types-ooM-r2td-r5JwBDd-.mjs} +11 -43
- package/dist/authoring-entity-types-ooM-r2td-r5JwBDd-.mjs.map +1 -0
- package/dist/{contract-builder-DWne7sJK.mjs → contract-builder-C_-FfTnK.mjs} +3 -3
- package/dist/{contract-builder-DWne7sJK.mjs.map → contract-builder-C_-FfTnK.mjs.map} +1 -1
- package/dist/{contract-builder-CHodgHxn.d.mts → contract-builder-CyB-7-Eq.d.mts} +2 -2
- package/dist/{contract-builder-CHodgHxn.d.mts.map → contract-builder-CyB-7-Eq.d.mts.map} +1 -1
- package/dist/contract-prisma6__provider.mjs +2 -2
- package/dist/contract-psl.d.mts +2 -21
- package/dist/contract-psl.d.mts.map +1 -1
- package/dist/contract-psl.mjs +2 -2
- package/dist/contract-psl__provider.mjs +2 -2
- package/dist/contract-psl__provider.mjs.map +1 -1
- package/dist/contract-ts.d.mts +1 -1
- package/dist/contract-ts.mjs +1 -1
- package/dist/contract-ts__contract-builder.d.mts +1 -1
- package/dist/contract-ts__contract-builder.mjs +1 -1
- package/dist/contract.d.mts +2 -2
- package/dist/contract.mjs +2 -2
- package/dist/contract__data-type.d.mts +1 -1
- package/dist/contract__data-type.mjs +1 -1
- package/dist/{control-BYbEjuQ5.d.mts → control-4JTCKNgO.d.mts} +2 -2
- package/dist/{control-BYbEjuQ5.d.mts.map → control-4JTCKNgO.d.mts.map} +1 -1
- package/dist/{control-Df3-4ayL.d.mts → control-SoPc-6DA.d.mts} +4 -4
- package/dist/{control-Df3-4ayL.d.mts.map → control-SoPc-6DA.d.mts.map} +1 -1
- package/dist/{control-BYnFmj1v.mjs → control-Yu_IYcIP.mjs} +4 -4
- package/dist/{control-BYnFmj1v.mjs.map → control-Yu_IYcIP.mjs.map} +1 -1
- package/dist/{control-adapter-Bh_ZraGM-Djo--Ygg.d.mts → control-adapter-Bh_ZraGM-D3xSqwxa.d.mts} +4 -4
- package/dist/{control-adapter-Bh_ZraGM-Djo--Ygg.d.mts.map → control-adapter-Bh_ZraGM-D3xSqwxa.d.mts.map} +1 -1
- package/dist/control-adapter-DriGOlGH.d.mts +1 -0
- package/dist/dist-BbfUvuqQ.mjs +629 -0
- package/dist/dist-BbfUvuqQ.mjs.map +1 -0
- package/dist/dist-xk_0xOR-.mjs.map +1 -1
- package/dist/family.d.mts +9 -9
- package/dist/family.mjs +6 -6
- package/dist/family__control-adapter.d.mts +2 -2
- package/dist/family__control.d.mts +1 -1
- package/dist/family__control.mjs +1 -1
- package/dist/family__ir.d.mts +2 -2
- package/dist/family__ir.mjs +2 -2
- package/dist/family__migration.d.mts +1 -1
- package/dist/family__migration.mjs +1 -1
- package/dist/family__pack.mjs +2 -2
- package/dist/family__pack.mjs.map +1 -1
- package/dist/family__runtime.d.mts +2 -2
- package/dist/family__runtime.mjs +1 -1
- package/dist/family__schema-verify.d.mts +1 -1
- package/dist/{field-accessor-CE4JbDIa-CemAdCzH.d.mts → field-accessor-CE4JbDIa-DCI8nD_s.d.mts} +2 -2
- package/dist/{field-accessor-CE4JbDIa-CemAdCzH.d.mts.map → field-accessor-CE4JbDIa-DCI8nD_s.d.mts.map} +1 -1
- package/dist/{index-D8_oAhb-.d.mts → index-B8HcPJrE.d.mts} +3 -3
- package/dist/{index-D8_oAhb-.d.mts.map → index-B8HcPJrE.d.mts.map} +1 -1
- package/dist/{index-XFSLz1g-.d.mts → index-BFhyIsHU.d.mts} +2 -2
- package/dist/{index-XFSLz1g-.d.mts.map → index-BFhyIsHU.d.mts.map} +1 -1
- package/dist/{index-mWfX7vkw.d.mts → index-CMd3fTIb.d.mts} +2 -2
- package/dist/{index-mWfX7vkw.d.mts.map → index-CMd3fTIb.d.mts.map} +1 -1
- package/dist/{index-C9aUMsc0.d.mts → index-hQxjnY-x.d.mts} +3 -3
- package/dist/{index-C9aUMsc0.d.mts.map → index-hQxjnY-x.d.mts.map} +1 -1
- package/dist/{index-Dg5hCMsu.d.mts → index-p1mHGKB5.d.mts} +57 -4
- package/dist/index-p1mHGKB5.d.mts.map +1 -0
- package/dist/{interpreter-Bhe8FSI4-uGNm4vAs.mjs → interpreter-BKBSURkK-B9QVhiLs.mjs} +23 -144
- package/dist/interpreter-BKBSURkK-B9QVhiLs.mjs.map +1 -0
- package/dist/{ir-iCpQRIJM.d.mts → ir-Cs8SNS0n.d.mts} +3 -3
- package/dist/{ir-iCpQRIJM.d.mts.map → ir-Cs8SNS0n.d.mts.map} +1 -1
- package/dist/ir-vZWjElPY.mjs.map +1 -1
- package/dist/lowering.d.mts +1 -1
- package/dist/{migration-BT1gIMTZ.mjs → migration-Bd8GWTeP.mjs} +2 -2
- package/dist/{migration-BT1gIMTZ.mjs.map → migration-Bd8GWTeP.mjs.map} +1 -1
- package/dist/{migration-BDqwISt7.d.mts → migration-DMXCxDu-.d.mts} +4 -4
- package/dist/{migration-BDqwISt7.d.mts.map → migration-DMXCxDu-.d.mts.map} +1 -1
- package/dist/{mongo-contract-serializer-DC_DM9_Y-CVJfr1pm.mjs → mongo-contract-serializer-5OphMWx2-DkWnm3kW.mjs} +3 -3
- package/dist/{mongo-contract-serializer-DC_DM9_Y-CVJfr1pm.mjs.map → mongo-contract-serializer-5OphMWx2-DkWnm3kW.mjs.map} +1 -1
- package/dist/{mongo-contract-view-AnejqfrQ-DYyueW8J.d.mts → mongo-contract-view-AnejqfrQ-CXymmojn.d.mts} +2 -2
- package/dist/{mongo-contract-view-AnejqfrQ-DYyueW8J.d.mts.map → mongo-contract-view-AnejqfrQ-CXymmojn.d.mts.map} +1 -1
- package/dist/{mongo-contract-view-JZOa0Ry--BZ2GUZQB.mjs → mongo-contract-view-OSrxTeL0-IBirxnYy.mjs} +4 -4
- package/dist/{mongo-contract-view-JZOa0Ry--BZ2GUZQB.mjs.map → mongo-contract-view-OSrxTeL0-IBirxnYy.mjs.map} +1 -1
- package/dist/{data-type-DuiFEJxB.d.mts → mongo-data-type-BMIRhoZC-COGrnC0Z.d.mts} +2 -2
- package/dist/mongo-data-type-BMIRhoZC-COGrnC0Z.d.mts.map +1 -0
- package/dist/{data-type-Cl0vtmfh.mjs → mongo-data-type-DJ7W2ota-BnUN7ydW.mjs} +2 -2
- package/dist/mongo-data-type-DJ7W2ota-BnUN7ydW.mjs.map +1 -0
- package/dist/orm.d.mts +2 -2
- package/dist/query-ast.d.mts +1 -1
- package/dist/query-ast__control.d.mts +1 -1
- package/dist/query-builder.d.mts +2 -2
- package/dist/query-builder__contract-free.d.mts +2 -2
- package/dist/{runtime-BIOlGVaz.d.mts → runtime-CQUQkK0P.d.mts} +2 -2
- package/dist/runtime-CQUQkK0P.d.mts.map +1 -0
- package/dist/runtime.d.mts +2 -2
- package/dist/schema-ir.d.mts +1 -1
- package/dist/{schema-verify-BP6zabY1.d.mts → schema-verify-Bzmg6whi.d.mts} +3 -3
- package/dist/{schema-verify-BP6zabY1.d.mts.map → schema-verify-Bzmg6whi.d.mts.map} +1 -1
- package/dist/wire.d.mts +1 -1
- package/package.json +20 -20
- package/dist/authoring-entity-types-8yNXQKSJ-COs9oUJY.mjs.map +0 -1
- package/dist/control-adapter-8uWiylj9.d.mts +0 -1
- package/dist/data-type-Cl0vtmfh.mjs.map +0 -1
- package/dist/data-type-DuiFEJxB.d.mts.map +0 -1
- package/dist/dist-CYYrUE3W.mjs +0 -310
- package/dist/dist-CYYrUE3W.mjs.map +0 -1
- package/dist/index-Dg5hCMsu.d.mts.map +0 -1
- package/dist/interpreter-Bhe8FSI4-uGNm4vAs.mjs.map +0 -1
- package/dist/runtime-BIOlGVaz.d.mts.map +0 -1
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
import { t as mongoContractCanonicalizationHooks } from "./canonicalization-hooks-CRVM-Kqh-Qi-VmEVg.mjs";
|
|
2
|
+
import { l as MongoValidator, n as MongoCollationOptions, o as MongoIndex, p as composeMongoEntityKinds } from "./entity-kinds-RooUvcN8-B1AF_ppw.mjs";
|
|
3
|
+
import { t as bsonTypesOfCodec } from "./mongo-data-type-DJ7W2ota-BnUN7ydW.mjs";
|
|
4
|
+
import { structuredError } from "@prisma/orm-framework/utils/structured-error";
|
|
5
|
+
import { IRNodeBase, NamespaceBase, UNBOUND_NAMESPACE_ID, buildNamespacedEntities, buildSingleNamespaceView, freezeNode, hydrateNamespaceEntities } from "@prisma/orm-framework/components/ir";
|
|
6
|
+
import { RelationKeys } from "@prisma/orm-framework/components/runtime";
|
|
7
|
+
import { computeStorageHash } from "@prisma/orm-framework/contract/hashing";
|
|
8
|
+
import { blindCast } from "@prisma/orm-framework/utils/casts";
|
|
9
|
+
import { UNBOUND_DOMAIN_NAMESPACE_ID } from "@prisma/orm-framework/contract/default-namespace";
|
|
10
|
+
import { assertDefined } from "@prisma/orm-framework/utils/assertions";
|
|
11
|
+
//#region ../../../2-mongo-family/1-foundation/mongo-contract/dist/index.mjs
|
|
12
|
+
var MongoUnboundNamespace = class MongoUnboundNamespace extends NamespaceBase {
|
|
13
|
+
static instance = new MongoUnboundNamespace();
|
|
14
|
+
id = UNBOUND_NAMESPACE_ID;
|
|
15
|
+
entries = Object.freeze({ collection: Object.freeze({}) });
|
|
16
|
+
constructor() {
|
|
17
|
+
super();
|
|
18
|
+
Object.defineProperty(this, "kind", {
|
|
19
|
+
value: "mongo-namespace",
|
|
20
|
+
writable: false,
|
|
21
|
+
enumerable: false,
|
|
22
|
+
configurable: true
|
|
23
|
+
});
|
|
24
|
+
freezeNode(this);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const MONGO_NAMESPACE_KIND = "mongo-namespace";
|
|
28
|
+
var MongoBoundNamespace = class MongoBoundNamespace extends NamespaceBase {
|
|
29
|
+
id;
|
|
30
|
+
entries;
|
|
31
|
+
static fromCollectionsInput(input) {
|
|
32
|
+
const collectionMap = input.entries["collection"];
|
|
33
|
+
const collectionCount = collectionMap !== void 0 ? Object.keys(collectionMap).length : 0;
|
|
34
|
+
const hasUnknownKinds = Object.keys(input.entries).some((kind) => kind !== "collection");
|
|
35
|
+
if (input.id === UNBOUND_NAMESPACE_ID && collectionCount === 0 && !hasUnknownKinds) return MongoUnboundNamespace.instance;
|
|
36
|
+
return new MongoBoundNamespace(input);
|
|
37
|
+
}
|
|
38
|
+
constructor(input) {
|
|
39
|
+
super();
|
|
40
|
+
this.id = input.id;
|
|
41
|
+
const rawEntries = {
|
|
42
|
+
collection: {},
|
|
43
|
+
...input.entries
|
|
44
|
+
};
|
|
45
|
+
this.entries = Object.freeze(blindCast(hydrateNamespaceEntities(rawEntries, composeMongoEntityKinds(), "carry")));
|
|
46
|
+
Object.defineProperty(this, "kind", {
|
|
47
|
+
value: MONGO_NAMESPACE_KIND,
|
|
48
|
+
writable: false,
|
|
49
|
+
enumerable: false,
|
|
50
|
+
configurable: true
|
|
51
|
+
});
|
|
52
|
+
freezeNode(this);
|
|
53
|
+
}
|
|
54
|
+
get collection() {
|
|
55
|
+
return this.entries.collection ?? Object.freeze({});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
function buildMongoNamespace(input) {
|
|
59
|
+
return MongoBoundNamespace.fromCollectionsInput(input);
|
|
60
|
+
}
|
|
61
|
+
var MongoStorage = class extends IRNodeBase {
|
|
62
|
+
storageHash;
|
|
63
|
+
namespaces;
|
|
64
|
+
constructor(input) {
|
|
65
|
+
super();
|
|
66
|
+
Object.defineProperty(this, "kind", {
|
|
67
|
+
value: "mongo-storage",
|
|
68
|
+
writable: false,
|
|
69
|
+
enumerable: false,
|
|
70
|
+
configurable: true
|
|
71
|
+
});
|
|
72
|
+
this.storageHash = input.storageHash;
|
|
73
|
+
this.namespaces = Object.freeze(input.namespaces);
|
|
74
|
+
freezeNode(this);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Builds a Mongo contract's storage: the unbound namespace holding the collections and value sets, and the storage hash that `db sign` and `db verify` compare. Every Mongo contract source calls this, so they hash storage the same way.
|
|
79
|
+
*/
|
|
80
|
+
function buildMongoStorage(input) {
|
|
81
|
+
const hasValueSets = Object.keys(input.valueSets).length > 0;
|
|
82
|
+
const namespace = buildMongoNamespace({
|
|
83
|
+
id: UNBOUND_NAMESPACE_ID,
|
|
84
|
+
entries: {
|
|
85
|
+
collection: input.collections,
|
|
86
|
+
...hasValueSets ? { valueSet: input.valueSets } : {}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
return new MongoStorage({
|
|
90
|
+
storageHash: computeStorageHash({
|
|
91
|
+
target: "mongo",
|
|
92
|
+
targetFamily: "mongo",
|
|
93
|
+
storage: { namespaces: { [UNBOUND_NAMESPACE_ID]: {
|
|
94
|
+
id: UNBOUND_NAMESPACE_ID,
|
|
95
|
+
entries: {
|
|
96
|
+
collection: namespace.entries.collection,
|
|
97
|
+
...namespace.entries.valueSet !== void 0 ? { valueSet: namespace.entries.valueSet } : {}
|
|
98
|
+
}
|
|
99
|
+
} } },
|
|
100
|
+
...mongoContractCanonicalizationHooks
|
|
101
|
+
}),
|
|
102
|
+
namespaces: { [UNBOUND_NAMESPACE_ID]: namespace }
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const MONGO_BUILTIN_KINDS = ["collection"];
|
|
106
|
+
/**
|
|
107
|
+
* Builds the Mongo view: unwraps the sole namespace's built-in `collection` kind
|
|
108
|
+
* to the root, attaches the namespace-keyed `namespace` map, and layers both
|
|
109
|
+
* over the deserialized contract so the result is a structural superset of the
|
|
110
|
+
* contract.
|
|
111
|
+
*/
|
|
112
|
+
function buildMongoContractView(contract) {
|
|
113
|
+
const rootAccessors = buildSingleNamespaceView(contract.storage, MONGO_BUILTIN_KINDS);
|
|
114
|
+
const namespace = buildNamespacedEntities(contract.storage, MONGO_BUILTIN_KINDS);
|
|
115
|
+
return {
|
|
116
|
+
...contract,
|
|
117
|
+
...rootAccessors,
|
|
118
|
+
namespace
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/** Default storage namespace for Mongo-family contracts at runtime. */
|
|
122
|
+
const defaultMongoStorageNamespaceId = UNBOUND_NAMESPACE_ID;
|
|
123
|
+
/** Default domain namespace for Mongo-family contracts at runtime. */
|
|
124
|
+
const defaultMongoDomainNamespaceId = UNBOUND_DOMAIN_NAMESPACE_ID;
|
|
125
|
+
const DOCUMENT_ID_FIELD = "_id";
|
|
126
|
+
/**
|
|
127
|
+
* The model facts behind a same-space to-one relation's nullability: which side owns the
|
|
128
|
+
* foreign key, and whether each local field is nullable.
|
|
129
|
+
*/
|
|
130
|
+
function resolveMongoToOneRelationFields(model, relation) {
|
|
131
|
+
const fields = relation.on.localFields.map((name) => ({
|
|
132
|
+
name,
|
|
133
|
+
nullable: model.fields[name]?.nullable ?? true
|
|
134
|
+
}));
|
|
135
|
+
return {
|
|
136
|
+
ownsForeignKey: !(relation.cardinality === "1:1" && fields.every((field) => field.name === DOCUMENT_ID_FIELD)),
|
|
137
|
+
fields
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/** Whether `relation` stores the related model inside the document, rather than referring to documents elsewhere. */
|
|
141
|
+
function isEmbedRelation(relation) {
|
|
142
|
+
return !("on" in relation) && !("through" in relation);
|
|
143
|
+
}
|
|
144
|
+
function typeKey(kind, name) {
|
|
145
|
+
return `${kind}:${name}`;
|
|
146
|
+
}
|
|
147
|
+
function referencesOf(fields) {
|
|
148
|
+
return Object.values(fields).flatMap((field) => field.type.kind === "valueObject" ? [typeKey("valueObject", field.type.name)] : []);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The recursive groups of value objects and embedded models: each set of types that can reach each other through value-object fields and embed relations (a strongly connected component of that graph), and each type that refers to itself. Maps the {@link typeKey} of every type in such a group to the group's number; a type in no recursive group is absent.
|
|
152
|
+
*/
|
|
153
|
+
function recursiveGroupsOf(valueObjects, models) {
|
|
154
|
+
const edges = /* @__PURE__ */ new Map();
|
|
155
|
+
for (const [name, valueObject] of Object.entries(valueObjects)) edges.set(typeKey("valueObject", name), referencesOf(valueObject.fields));
|
|
156
|
+
for (const [name, model] of Object.entries(models)) {
|
|
157
|
+
const embeds = Object.values(model.relations).flatMap((relation) => isEmbedRelation(relation) ? [typeKey("model", relation.to.model)] : []);
|
|
158
|
+
edges.set(typeKey("model", name), [...referencesOf(model.fields), ...embeds]);
|
|
159
|
+
}
|
|
160
|
+
const groups = /* @__PURE__ */ new Map();
|
|
161
|
+
const index = /* @__PURE__ */ new Map();
|
|
162
|
+
const lowLink = /* @__PURE__ */ new Map();
|
|
163
|
+
const stack = [];
|
|
164
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
165
|
+
let nextIndex = 0;
|
|
166
|
+
let nextGroup = 0;
|
|
167
|
+
const visit = (node) => {
|
|
168
|
+
index.set(node, nextIndex);
|
|
169
|
+
lowLink.set(node, nextIndex);
|
|
170
|
+
nextIndex++;
|
|
171
|
+
stack.push(node);
|
|
172
|
+
onStack.add(node);
|
|
173
|
+
for (const target of edges.get(node) ?? []) {
|
|
174
|
+
if (!edges.has(target)) continue;
|
|
175
|
+
if (!index.has(target)) {
|
|
176
|
+
visit(target);
|
|
177
|
+
lowLink.set(node, Math.min(lowLink.get(node) ?? 0, lowLink.get(target) ?? 0));
|
|
178
|
+
} else if (onStack.has(target)) lowLink.set(node, Math.min(lowLink.get(node) ?? 0, index.get(target) ?? 0));
|
|
179
|
+
}
|
|
180
|
+
if (lowLink.get(node) !== index.get(node)) return;
|
|
181
|
+
const members = [];
|
|
182
|
+
let member;
|
|
183
|
+
do {
|
|
184
|
+
member = stack.pop();
|
|
185
|
+
if (member === void 0) break;
|
|
186
|
+
onStack.delete(member);
|
|
187
|
+
members.push(member);
|
|
188
|
+
} while (member !== node);
|
|
189
|
+
const refersToItself = (edges.get(node) ?? []).includes(node);
|
|
190
|
+
if (members.length > 1 || refersToItself) {
|
|
191
|
+
for (const each of members) groups.set(each, nextGroup);
|
|
192
|
+
nextGroup++;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
for (const node of edges.keys()) if (!index.has(node)) visit(node);
|
|
196
|
+
return groups;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* `$jsonSchema` has no references, so a type that contains itself, directly or through other types, cannot be described at every depth. The validator describes the type through which a path enters a recursive group in full, and accepts any object for each field below it that refers back into that group. So each group is written out once for each place a document enters it, and no valid document is refused.
|
|
200
|
+
*/
|
|
201
|
+
const OPEN_OBJECT = { bsonType: "object" };
|
|
202
|
+
function sourcesFor(input, rootKey) {
|
|
203
|
+
const groups = recursiveGroupsOf(input.valueObjects, input.models);
|
|
204
|
+
const rootGroup = groups.get(rootKey);
|
|
205
|
+
return {
|
|
206
|
+
...input,
|
|
207
|
+
groups,
|
|
208
|
+
enteredGroups: new Set(rootGroup === void 0 ? [] : [rootGroup])
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function objectSchemaOf(kind, name, shape, sources) {
|
|
212
|
+
const key = typeKey(kind, name);
|
|
213
|
+
const group = sources.groups.get(key);
|
|
214
|
+
if (group !== void 0 && sources.enteredGroups.has(group)) return OPEN_OBJECT;
|
|
215
|
+
return deriveObjectSchema(shape, entering(key, sources));
|
|
216
|
+
}
|
|
217
|
+
/** `sources` with the recursive group of the type `key` entered, if it has one. */
|
|
218
|
+
function entering(key, sources) {
|
|
219
|
+
const group = sources.groups.get(key);
|
|
220
|
+
if (group === void 0 || sources.enteredGroups.has(group)) return sources;
|
|
221
|
+
return {
|
|
222
|
+
...sources,
|
|
223
|
+
enteredGroups: /* @__PURE__ */ new Set([...sources.enteredGroups, group])
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const NO_RELATIONS = {};
|
|
227
|
+
const NOTHING_INHERITED = {
|
|
228
|
+
fields: {},
|
|
229
|
+
relations: NO_RELATIONS
|
|
230
|
+
};
|
|
231
|
+
/**
|
|
232
|
+
* The `bsonType` keyword for a list of BSON type names: the name itself for one entry, the list for several.
|
|
233
|
+
*/
|
|
234
|
+
function bsonTypeKeyword(bsonTypes) {
|
|
235
|
+
const [only, ...rest] = bsonTypes;
|
|
236
|
+
return only !== void 0 && rest.length === 0 ? only : [...bsonTypes];
|
|
237
|
+
}
|
|
238
|
+
function withNull(bsonTypes) {
|
|
239
|
+
return bsonTypes.includes("null") ? bsonTypes : ["null", ...bsonTypes];
|
|
240
|
+
}
|
|
241
|
+
function anyValueSchema(field) {
|
|
242
|
+
if ("many" in field && field.many) return {
|
|
243
|
+
bsonType: field.nullable ? ["null", "array"] : "array",
|
|
244
|
+
items: {}
|
|
245
|
+
};
|
|
246
|
+
return {};
|
|
247
|
+
}
|
|
248
|
+
function fieldToBsonSchema(field, sources) {
|
|
249
|
+
const { lookups, valueObjects, valueSets } = sources;
|
|
250
|
+
if (field.type.kind === "scalar") {
|
|
251
|
+
const bsonTypes = bsonTypesOfCodec(field.type.codecId, lookups);
|
|
252
|
+
if (bsonTypes === void 0) return void 0;
|
|
253
|
+
if (bsonTypes.length === 0) return anyValueSchema(field);
|
|
254
|
+
const bsonType = bsonTypeKeyword(bsonTypes);
|
|
255
|
+
const enumValues = field.valueSet !== void 0 ? valueSets[field.valueSet.entityName]?.values ?? null : null;
|
|
256
|
+
if (field.many) {
|
|
257
|
+
const items = { bsonType: field.many.elementNullable ? ["null", bsonType] : bsonType };
|
|
258
|
+
if (enumValues) items["enum"] = field.many.elementNullable ? [...enumValues, null] : enumValues;
|
|
259
|
+
return {
|
|
260
|
+
bsonType: field.nullable ? ["null", "array"] : "array",
|
|
261
|
+
items
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (field.nullable) {
|
|
265
|
+
const s = { bsonType: bsonTypeKeyword(withNull(bsonTypes)) };
|
|
266
|
+
if (enumValues) s["enum"] = [...enumValues, null];
|
|
267
|
+
return s;
|
|
268
|
+
}
|
|
269
|
+
const s = { bsonType };
|
|
270
|
+
if (enumValues) s["enum"] = enumValues;
|
|
271
|
+
return s;
|
|
272
|
+
}
|
|
273
|
+
if (field.type.kind === "valueObject") {
|
|
274
|
+
const vo = valueObjects[field.type.name];
|
|
275
|
+
if (!vo) return void 0;
|
|
276
|
+
const voSchema = objectSchemaOf("valueObject", field.type.name, {
|
|
277
|
+
fields: vo.fields,
|
|
278
|
+
relations: NO_RELATIONS
|
|
279
|
+
}, sources);
|
|
280
|
+
if (field.many) return {
|
|
281
|
+
bsonType: field.nullable ? ["null", "array"] : "array",
|
|
282
|
+
items: field.many.elementNullable ? { oneOf: [{ bsonType: "null" }, voSchema] } : voSchema
|
|
283
|
+
};
|
|
284
|
+
if (field.nullable) return { oneOf: [{ bsonType: "null" }, voSchema] };
|
|
285
|
+
return voSchema;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* An embedded model is stored inside its owner's document as a closed object (a list of them for `1:N`). The contract does not say whether one must be present, and the ORM creates documents without them, so the property is never required.
|
|
290
|
+
*/
|
|
291
|
+
function embedToBsonSchema(relation, sources) {
|
|
292
|
+
const embeddedName = relation.to.model;
|
|
293
|
+
const embedded = sources.models[embeddedName];
|
|
294
|
+
assertDefined(embedded, `Embedded model "${embeddedName}" is missing`);
|
|
295
|
+
const schema = objectSchemaOf("model", embeddedName, embedded, sources);
|
|
296
|
+
return relation.cardinality === "1:N" ? {
|
|
297
|
+
bsonType: "array",
|
|
298
|
+
items: schema
|
|
299
|
+
} : schema;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* The properties of `shape`, leaving out the fields and relations `inherited` already describes.
|
|
303
|
+
*/
|
|
304
|
+
function propertiesOf(shape, sources, inherited) {
|
|
305
|
+
const properties = {};
|
|
306
|
+
const required = [];
|
|
307
|
+
for (const [fieldName, field] of Object.entries(shape.fields)) {
|
|
308
|
+
if (Object.hasOwn(inherited.fields, fieldName)) continue;
|
|
309
|
+
const schema = fieldToBsonSchema(field, sources);
|
|
310
|
+
if (schema) {
|
|
311
|
+
properties[fieldName] = schema;
|
|
312
|
+
if (!field.nullable) required.push(fieldName);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
for (const [relationName, relation] of Object.entries(shape.relations)) {
|
|
316
|
+
if (Object.hasOwn(inherited.relations, relationName) || !isEmbedRelation(relation)) continue;
|
|
317
|
+
properties[relationName] = embedToBsonSchema(relation, sources);
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
properties,
|
|
321
|
+
required
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function deriveObjectSchema(shape, sources) {
|
|
325
|
+
const { properties, required } = propertiesOf(shape, sources, NOTHING_INHERITED);
|
|
326
|
+
const result = {
|
|
327
|
+
bsonType: "object",
|
|
328
|
+
properties,
|
|
329
|
+
additionalProperties: false
|
|
330
|
+
};
|
|
331
|
+
if (required.length > 0) result["required"] = required.sort();
|
|
332
|
+
return result;
|
|
333
|
+
}
|
|
334
|
+
function isRecord(value) {
|
|
335
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
336
|
+
}
|
|
337
|
+
function validatorOf(jsonSchema) {
|
|
338
|
+
return new MongoValidator({
|
|
339
|
+
jsonSchema,
|
|
340
|
+
validationLevel: "strict",
|
|
341
|
+
validationAction: "error"
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function polymorphicSchema(base, discriminatorField, variants, sources) {
|
|
345
|
+
const baseSchema = deriveObjectSchema(base, sources);
|
|
346
|
+
const baseProperties = isRecord(baseSchema["properties"]) ? baseSchema["properties"] : {};
|
|
347
|
+
const oneOf = [];
|
|
348
|
+
for (const variant of variants) {
|
|
349
|
+
const { properties: variantProperties, required } = propertiesOf(variant, entering(typeKey("model", variant.modelName), sources), base);
|
|
350
|
+
oneOf.push({
|
|
351
|
+
properties: {
|
|
352
|
+
...baseProperties,
|
|
353
|
+
[discriminatorField]: { enum: [variant.discriminatorValue] },
|
|
354
|
+
...variantProperties
|
|
355
|
+
},
|
|
356
|
+
required: [discriminatorField, ...required].sort(),
|
|
357
|
+
additionalProperties: false
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
const jsonSchema = { ...baseSchema };
|
|
361
|
+
delete jsonSchema["additionalProperties"];
|
|
362
|
+
if (oneOf.length > 0) jsonSchema["oneOf"] = oneOf;
|
|
363
|
+
return jsonSchema;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* The validator of a root model's collection: the model's fields and embedded models, and for a model with variants one `oneOf` branch per variant.
|
|
367
|
+
*/
|
|
368
|
+
function deriveModelValidator(modelName, model, sources) {
|
|
369
|
+
const schemaSources = sourcesFor(sources, typeKey("model", modelName));
|
|
370
|
+
if (!model.discriminator || !model.variants) return validatorOf(deriveObjectSchema(model, schemaSources));
|
|
371
|
+
const variants = Object.entries(model.variants).map(([discriminatorValue, variant]) => {
|
|
372
|
+
const variantModel = sources.models[variant.model];
|
|
373
|
+
assertDefined(variantModel, `Variant model "${variant.model}" of "${modelName}" is missing`);
|
|
374
|
+
return {
|
|
375
|
+
discriminatorValue,
|
|
376
|
+
modelName: variant.model,
|
|
377
|
+
...variantModel
|
|
378
|
+
};
|
|
379
|
+
});
|
|
380
|
+
return validatorOf(polymorphicSchema(model, model.discriminator.field, variants, schemaSources));
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* The validator of each collection in `roots`, keyed by collection name, derived from the collection's root model. A root model with variants gets one polymorphic validator covering every variant.
|
|
384
|
+
*/
|
|
385
|
+
function deriveCollectionValidators(input) {
|
|
386
|
+
const validators = {};
|
|
387
|
+
for (const [collectionName, root] of Object.entries(input.roots)) {
|
|
388
|
+
const model = input.models[root.model];
|
|
389
|
+
assertDefined(model, `Root model "${root.model}" of collection "${collectionName}" is missing`);
|
|
390
|
+
validators[collectionName] = deriveModelValidator(root.model, model, input);
|
|
391
|
+
}
|
|
392
|
+
return validators;
|
|
393
|
+
}
|
|
394
|
+
const notObjectId = {
|
|
395
|
+
kind: "notObjectId",
|
|
396
|
+
reason: "has no _id field stored as BSON type objectId. MongoDB adds an objectId _id to every document it inserts without one, so a collection validator without it rejects every insert."
|
|
397
|
+
};
|
|
398
|
+
/**
|
|
399
|
+
* Why a root model with `fields` cannot be stored in a collection with a validator, or undefined when it can. The reason leaves out its subject so each authoring surface can lead with its own, as in `Model "User" has no _id field ...`.
|
|
400
|
+
*/
|
|
401
|
+
function documentIdRefusalOf(fields, lookups) {
|
|
402
|
+
const id = Object.hasOwn(fields, "_id") ? fields["_id"] : void 0;
|
|
403
|
+
if (id?.type.kind !== "scalar") return notObjectId;
|
|
404
|
+
const bsonTypes = bsonTypesOfCodec(id.type.codecId, lookups);
|
|
405
|
+
if (bsonTypes === void 0) return {
|
|
406
|
+
kind: "unknownCodec",
|
|
407
|
+
reason: `has an _id field whose codec "${id.type.codecId}" has no MongoDB data type, so its BSON type is unknown.`
|
|
408
|
+
};
|
|
409
|
+
return bsonTypes.length === 1 && bsonTypes[0] === "objectId" ? void 0 : notObjectId;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Clustered-collection options (the `clusteredIndex` collection-creation
|
|
413
|
+
* field). Lifted from a `type =` data shape to an AST class extending
|
|
414
|
+
* `IRNodeBase` per FR18.
|
|
415
|
+
*
|
|
416
|
+
* MongoDB requires `key` and `unique` for any clustered collection; the
|
|
417
|
+
* constructor enforces presence by type signature.
|
|
418
|
+
*/
|
|
419
|
+
var MongoClusteredCollectionOptions = class extends IRNodeBase {
|
|
420
|
+
kind = "mongo-clustered-collection-options";
|
|
421
|
+
key;
|
|
422
|
+
unique;
|
|
423
|
+
constructor(options) {
|
|
424
|
+
super();
|
|
425
|
+
if (options.name !== void 0) this.name = options.name;
|
|
426
|
+
this.key = options.key;
|
|
427
|
+
this.unique = options.unique;
|
|
428
|
+
freezeNode(this);
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
/**
|
|
432
|
+
* Mongo Contract IR node for the per-index option vocabulary (the second
|
|
433
|
+
* argument to `db.collection.createIndex(keys, options)` minus the keys
|
|
434
|
+
* themselves). Lifted from a `type =` data shape to an AST class
|
|
435
|
+
* extending `IRNodeBase` per FR18.
|
|
436
|
+
*
|
|
437
|
+
* Nested `collation` is itself an IR class (`MongoCollationOptions`); the
|
|
438
|
+
* constructor accepts either a class instance or a data literal and
|
|
439
|
+
* normalises to the class form so downstream walks see a uniform IR tree.
|
|
440
|
+
*/
|
|
441
|
+
var MongoIndexOptions = class extends IRNodeBase {
|
|
442
|
+
kind = "mongo-index-options";
|
|
443
|
+
constructor(options = {}) {
|
|
444
|
+
super();
|
|
445
|
+
if (options.unique !== void 0) this.unique = options.unique;
|
|
446
|
+
if (options.name !== void 0) this.name = options.name;
|
|
447
|
+
if (options.partialFilterExpression !== void 0) this.partialFilterExpression = options.partialFilterExpression;
|
|
448
|
+
if (options.sparse !== void 0) this.sparse = options.sparse;
|
|
449
|
+
if (options.expireAfterSeconds !== void 0) this.expireAfterSeconds = options.expireAfterSeconds;
|
|
450
|
+
if (options.weights !== void 0) this.weights = options.weights;
|
|
451
|
+
if (options.default_language !== void 0) this.default_language = options.default_language;
|
|
452
|
+
if (options.language_override !== void 0) this.language_override = options.language_override;
|
|
453
|
+
if (options.textIndexVersion !== void 0) this.textIndexVersion = options.textIndexVersion;
|
|
454
|
+
if (options["2dsphereIndexVersion"] !== void 0) this["2dsphereIndexVersion"] = options["2dsphereIndexVersion"];
|
|
455
|
+
if (options.bits !== void 0) this.bits = options.bits;
|
|
456
|
+
if (options.min !== void 0) this.min = options.min;
|
|
457
|
+
if (options.max !== void 0) this.max = options.max;
|
|
458
|
+
if (options.bucketSize !== void 0) this.bucketSize = options.bucketSize;
|
|
459
|
+
if (options.hidden !== void 0) this.hidden = options.hidden;
|
|
460
|
+
if (options.collation !== void 0) this.collation = options.collation instanceof MongoCollationOptions ? options.collation : new MongoCollationOptions(options.collation);
|
|
461
|
+
if (options.wildcardProjection !== void 0) this.wildcardProjection = options.wildcardProjection;
|
|
462
|
+
freezeNode(this);
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
/**
|
|
466
|
+
* The storage value set of each enum: its members as the contract holds them, in the form their codec stores.
|
|
467
|
+
*/
|
|
468
|
+
function mongoValueSetsOf(enums) {
|
|
469
|
+
const valueSets = {};
|
|
470
|
+
for (const [enumName, { members }] of Object.entries(enums)) valueSets[enumName] = {
|
|
471
|
+
kind: "valueSet",
|
|
472
|
+
values: members.map((member) => member.value)
|
|
473
|
+
};
|
|
474
|
+
return valueSets;
|
|
475
|
+
}
|
|
476
|
+
function isScalarDiscriminatorValue(value) {
|
|
477
|
+
const t = typeof value;
|
|
478
|
+
return t === "string" || t === "number" || t === "boolean";
|
|
479
|
+
}
|
|
480
|
+
function formatValue(value) {
|
|
481
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
482
|
+
if (value === null) return "null";
|
|
483
|
+
if (Array.isArray(value)) return JSON.stringify(value);
|
|
484
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
485
|
+
return String(value);
|
|
486
|
+
}
|
|
487
|
+
function applyPolymorphicScopeToMongoIndex(index, scope) {
|
|
488
|
+
if (!isScalarDiscriminatorValue(scope.discriminatorValue)) return {
|
|
489
|
+
kind: "conflict",
|
|
490
|
+
reason: `Variant-scoped indexes require a scalar (string, number, or boolean) discriminator value for field "${scope.discriminatorField}", but received ${formatValue(scope.discriminatorValue)}.`
|
|
491
|
+
};
|
|
492
|
+
const existing = index.partialFilterExpression;
|
|
493
|
+
if (existing && Object.hasOwn(existing, scope.discriminatorField)) {
|
|
494
|
+
const existingValue = existing[scope.discriminatorField];
|
|
495
|
+
if (existingValue === scope.discriminatorValue) return {
|
|
496
|
+
kind: "ok",
|
|
497
|
+
index
|
|
498
|
+
};
|
|
499
|
+
return {
|
|
500
|
+
kind: "conflict",
|
|
501
|
+
reason: `Index partialFilterExpression sets "${scope.discriminatorField}" to ${formatValue(existingValue)}, which conflicts with the variant's discriminator value ${formatValue(scope.discriminatorValue)}.`
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
const merged = {
|
|
505
|
+
...existing ?? {},
|
|
506
|
+
[scope.discriminatorField]: scope.discriminatorValue
|
|
507
|
+
};
|
|
508
|
+
return {
|
|
509
|
+
kind: "ok",
|
|
510
|
+
index: new MongoIndex({
|
|
511
|
+
keys: index.keys,
|
|
512
|
+
...index.unique !== void 0 && { unique: index.unique },
|
|
513
|
+
...index.sparse !== void 0 && { sparse: index.sparse },
|
|
514
|
+
...index.expireAfterSeconds !== void 0 && { expireAfterSeconds: index.expireAfterSeconds },
|
|
515
|
+
partialFilterExpression: merged,
|
|
516
|
+
...index.wildcardProjection !== void 0 && { wildcardProjection: index.wildcardProjection },
|
|
517
|
+
...index.collation !== void 0 && { collation: index.collation },
|
|
518
|
+
...index.weights !== void 0 && { weights: index.weights },
|
|
519
|
+
...index.default_language !== void 0 && { default_language: index.default_language },
|
|
520
|
+
...index.language_override !== void 0 && { language_override: index.language_override }
|
|
521
|
+
})
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
function formatCrossRef(crossRef) {
|
|
525
|
+
return `${crossRef.namespace}.${crossRef.model}`;
|
|
526
|
+
}
|
|
527
|
+
function storageDeclaresCollection(storage, collectionName) {
|
|
528
|
+
for (const ns of Object.values(storage.namespaces)) if (Object.hasOwn(ns.entries["collection"] ?? {}, collectionName)) return true;
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
function validateMongoStorage(contract) {
|
|
532
|
+
const errors = [];
|
|
533
|
+
for (const [namespaceId, namespace] of Object.entries(contract.domain.namespaces)) {
|
|
534
|
+
const models = namespace.models;
|
|
535
|
+
for (const [modelName, model] of Object.entries(models)) {
|
|
536
|
+
const qualifiedName = `${namespaceId}:${modelName}`;
|
|
537
|
+
if (model.storage.collection && !storageDeclaresCollection(contract.storage, model.storage.collection)) errors.push(`Model "${qualifiedName}" references collection "${model.storage.collection}" which is not declared under any namespace's collections map`);
|
|
538
|
+
if (model.base) {
|
|
539
|
+
const baseModel = models[model.base.model];
|
|
540
|
+
if (baseModel) {
|
|
541
|
+
const variantCollection = model.storage.collection;
|
|
542
|
+
const baseCollection = baseModel.storage.collection;
|
|
543
|
+
if (variantCollection !== baseCollection) errors.push(`Mongo does not support multi-table inheritance; variant "${qualifiedName}" must share its base's collection ("${baseCollection ?? "(none)"}"), but has "${variantCollection ?? "(none)"}"`);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
for (const [relName, relation] of Object.entries(model.relations ?? {})) {
|
|
547
|
+
const targetModel = models[relation.to.model];
|
|
548
|
+
const targetLabel = formatCrossRef(relation.to);
|
|
549
|
+
if (targetModel?.owner) {
|
|
550
|
+
if (targetModel.owner !== modelName) errors.push(`Embed relation "${relName}" targets "${targetLabel}" which is owned by "${targetModel.owner}", not "${qualifiedName}"`);
|
|
551
|
+
if (targetModel.storage.collection) errors.push(`Embed relation "${relName}" targets "${targetLabel}" which must not have a collection`);
|
|
552
|
+
} else if ("on" in relation && relation.on) {
|
|
553
|
+
for (const localField of relation.on.localFields) if (!(localField in model.fields)) errors.push(`Reference relation "${relName}": localField "${localField}" is not a field on model "${qualifiedName}"`);
|
|
554
|
+
if (targetModel) {
|
|
555
|
+
for (const targetField of relation.on.targetFields) if (!(targetField in targetModel.fields)) errors.push(`Reference relation "${relName}": targetField "${targetField}" is not a field on model "${targetLabel}"`);
|
|
556
|
+
}
|
|
557
|
+
const nullabilityError = toOneRelationNullabilityError(qualifiedName, relName, relation, model);
|
|
558
|
+
if (nullabilityError !== void 0) errors.push(nullabilityError);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (errors.length > 0) throw structuredError("CONTRACT.VALIDATION_FAILED", `Contract storage validation failed:\n- ${errors.join("\n- ")}`);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* A same-space to-one relation that states `nullable` must agree with its local fields: nullable
|
|
567
|
+
* exactly when one of them is nullable (a field that is not declared counts as nullable). A
|
|
568
|
+
* relation without the flag is left to hydration, which derives it the same way.
|
|
569
|
+
*/
|
|
570
|
+
function toOneRelationNullabilityError(qualifiedName, relationName, relation, model) {
|
|
571
|
+
if (!("on" in relation && (relation.cardinality === "1:1" || relation.cardinality === "N:1") && relation.to.space === void 0) || relation.nullable === void 0) return void 0;
|
|
572
|
+
const location = `Relation "${relationName}" on model "${qualifiedName}"`;
|
|
573
|
+
const { ownsForeignKey, fields } = resolveMongoToOneRelationFields(model, relation);
|
|
574
|
+
if (!ownsForeignKey) return relation.nullable ? void 0 : `${location} is required but does not own the foreign key, so nothing in storage guarantees the related document exists`;
|
|
575
|
+
const anyNullable = fields.some((field) => field.nullable);
|
|
576
|
+
if (relation.nullable === anyNullable) return void 0;
|
|
577
|
+
const fieldList = fields.filter((field) => field.nullable !== relation.nullable).map((field) => `"${field.name}"`).join(", ");
|
|
578
|
+
return relation.nullable ? `${location} is nullable but every local field is required (fields ${fieldList})` : `${location} is required but a local field is nullable (fields ${fieldList})`;
|
|
579
|
+
}
|
|
580
|
+
const STORED_JSON_TYPE_NAMES = {
|
|
581
|
+
string: "a string",
|
|
582
|
+
number: "a finite number",
|
|
583
|
+
boolean: "a boolean",
|
|
584
|
+
object: "a JSON object",
|
|
585
|
+
array: "a JSON array"
|
|
586
|
+
};
|
|
587
|
+
/**
|
|
588
|
+
* A collection validator lists an enum's members in their stored forms, so an enum's BSON type must be one whose values JSON holds. Maps each such type to the JSON type of a member's stored form.
|
|
589
|
+
*/
|
|
590
|
+
const VALIDATOR_LISTABLE_BSON_TYPES = {
|
|
591
|
+
string: "string",
|
|
592
|
+
int: "number",
|
|
593
|
+
double: "number",
|
|
594
|
+
bool: "boolean",
|
|
595
|
+
object: "object",
|
|
596
|
+
array: "array"
|
|
597
|
+
};
|
|
598
|
+
function storedJsonTypeOf(stored) {
|
|
599
|
+
if (stored === null) return "null";
|
|
600
|
+
if (Array.isArray(stored)) return "array";
|
|
601
|
+
if (typeof stored === "number" && !Number.isFinite(stored)) return "non-finite number";
|
|
602
|
+
return typeof stored;
|
|
603
|
+
}
|
|
604
|
+
function storedText(stored) {
|
|
605
|
+
return typeof stored === "number" || typeof stored === "bigint" ? String(stored) : JSON.stringify(stored);
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Why a collection validator cannot list the members of an enum whose codec stores `bsonTypes`, or undefined when it can. Like {@link validatorEnumMemberRefusalOf}, the reason leaves out its subject so each authoring surface can lead with its own, as in `enum "Role" @@type codec "mongo/int64@1" stores BSON type long, ...`.
|
|
609
|
+
*/
|
|
610
|
+
function validatorEnumRefusalOf(bsonTypes) {
|
|
611
|
+
const [bsonType, ...otherBsonTypes] = bsonTypes;
|
|
612
|
+
if (bsonType === void 0 || otherBsonTypes.length > 0) return `declares ${bsonTypes.length} BSON types; an enum needs exactly one. Use a codec with one BSON type, such as mongo/string@1.`;
|
|
613
|
+
if (VALIDATOR_LISTABLE_BSON_TYPES[bsonType] === void 0) return `stores BSON type ${bsonType}, which a collection validator cannot list as an enum value. Use a codec whose BSON type is string, int, double, bool, object or array.`;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Why a collection validator cannot list an enum member stored as `stored`, for an enum whose codec stores `bsonTypes`, or undefined when it can. The reason leaves out its subject, as in `enum "Role" member "Admin" is stored as ...`.
|
|
617
|
+
*/
|
|
618
|
+
function validatorEnumMemberRefusalOf(bsonTypes, stored) {
|
|
619
|
+
const enumRefusal = validatorEnumRefusalOf(bsonTypes);
|
|
620
|
+
if (enumRefusal !== void 0) return enumRefusal;
|
|
621
|
+
const [bsonType] = bsonTypes;
|
|
622
|
+
const storedJsonType = bsonType === void 0 ? void 0 : VALIDATOR_LISTABLE_BSON_TYPES[bsonType];
|
|
623
|
+
if (storedJsonType === void 0 || storedJsonTypeOf(stored) === storedJsonType) return void 0;
|
|
624
|
+
return `is stored as ${storedText(stored)}, which a collection validator cannot list as a ${bsonType}. A member of a ${bsonType} enum must be ${STORED_JSON_TYPE_NAMES[storedJsonType]}.`;
|
|
625
|
+
}
|
|
626
|
+
//#endregion
|
|
627
|
+
export { validatorEnumMemberRefusalOf as _, RelationKeys as a, buildMongoNamespace as c, defaultMongoStorageNamespaceId as d, deriveCollectionValidators as f, validateMongoStorage as g, resolveMongoToOneRelationFields as h, MongoUnboundNamespace as i, buildMongoStorage as l, mongoValueSetsOf as m, MongoIndexOptions as n, applyPolymorphicScopeToMongoIndex as o, documentIdRefusalOf as p, MongoStorage as r, buildMongoContractView as s, MongoClusteredCollectionOptions as t, defaultMongoDomainNamespaceId as u, validatorEnumRefusalOf as v };
|
|
628
|
+
|
|
629
|
+
//# sourceMappingURL=dist-BbfUvuqQ.mjs.map
|