@prisma/orm-family-mongo 8.0.0-rc.20-dev.3 → 8.0.0-rc.20-dev.4
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-UA5sY_Xm-DeYf_cYq.mjs → authoring-entity-types-BEwwEsFI-J0WF36SE.mjs} +8 -6
- package/dist/authoring-entity-types-BEwwEsFI-J0WF36SE.mjs.map +1 -0
- package/dist/config-types-DYTCUpAs.mjs.map +1 -1
- package/dist/contract-builder-d5cf-u9p.mjs +1832 -0
- package/dist/contract-builder-d5cf-u9p.mjs.map +1 -0
- package/dist/{contract-builder-ClL-xMK2.d.mts → contract-builder-lqaM5w74.d.mts} +489 -94
- package/dist/contract-builder-lqaM5w74.d.mts.map +1 -0
- package/dist/contract-prisma6__provider.mjs +168 -107
- package/dist/contract-prisma6__provider.mjs.map +1 -1
- package/dist/contract-psl.d.mts +7 -4
- package/dist/contract-psl.d.mts.map +1 -1
- package/dist/contract-psl.mjs +1 -1
- package/dist/contract-psl__provider.d.mts +5 -1
- package/dist/contract-psl__provider.d.mts.map +1 -1
- package/dist/contract-psl__provider.mjs +17 -4
- package/dist/contract-psl__provider.mjs.map +1 -1
- package/dist/contract-ts.d.mts +2 -2
- package/dist/contract-ts.mjs +2 -2
- package/dist/contract-ts__contract-builder.d.mts +2 -2
- package/dist/contract-ts__contract-builder.mjs +2 -2
- package/dist/contract.d.mts +3 -3
- package/dist/contract.mjs +1 -1
- package/dist/contract__data-type.d.mts +2 -15
- package/dist/contract__data-type.d.mts.map +1 -1
- package/dist/contract__data-type.mjs +2 -2
- package/dist/contract__entity-kinds.d.mts +1 -1
- package/dist/{control-CvQSMuxa.d.mts → control-B5MQg_2b.d.mts} +5 -5
- package/dist/{control-CvQSMuxa.d.mts.map → control-B5MQg_2b.d.mts.map} +1 -1
- package/dist/{control-BvDzFRXB.mjs → control-Bo6DU4E_.mjs} +6 -6
- package/dist/{control-BvDzFRXB.mjs.map → control-Bo6DU4E_.mjs.map} +1 -1
- package/dist/{control-4pC8N2Rb.d.mts → control-DS-Fx1NE.d.mts} +3 -3
- package/dist/{control-4pC8N2Rb.d.mts.map → control-DS-Fx1NE.d.mts.map} +1 -1
- package/dist/{control-adapter-Bh_ZraGM--5JDb99k.d.mts → control-adapter-Bh_ZraGM-B4cpVawQ.d.mts} +4 -4
- package/dist/{control-adapter-Bh_ZraGM--5JDb99k.d.mts.map → control-adapter-Bh_ZraGM-B4cpVawQ.d.mts.map} +1 -1
- package/dist/control-adapter-CbqlnOk6.d.mts +1 -0
- package/dist/{dist-B8f-pfcT.mjs → dist-DgMGRQkm.mjs} +7 -7
- package/dist/dist-DgMGRQkm.mjs.map +1 -0
- package/dist/{dist-gmGbjhmk.mjs → dist-DmE6N0B3.mjs} +2 -2
- package/dist/{dist-gmGbjhmk.mjs.map → dist-DmE6N0B3.mjs.map} +1 -1
- package/dist/dist-xk_0xOR-.mjs.map +1 -1
- package/dist/family.d.mts +10 -10
- package/dist/family.mjs +8 -8
- package/dist/family__control-adapter.d.mts +2 -2
- package/dist/family__control.d.mts +1 -1
- package/dist/family__control.mjs +2 -2
- package/dist/family__ir.d.mts +2 -2
- package/dist/family__ir.mjs +3 -3
- package/dist/family__migration.d.mts +1 -1
- package/dist/family__migration.mjs +1 -1
- package/dist/family__pack.d.mts +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/family__schema-verify.mjs +1 -1
- package/dist/{field-accessor-CmxCw9xP-C6jMngbK.d.mts → field-accessor-CmxCw9xP-C7wBU7aM.d.mts} +3 -3
- package/dist/{field-accessor-CmxCw9xP-C6jMngbK.d.mts.map → field-accessor-CmxCw9xP-C7wBU7aM.d.mts.map} +1 -1
- package/dist/{index-FeVOOdZ0.d.mts → index-8ml_NVS_.d.mts} +3 -3
- package/dist/{index-FeVOOdZ0.d.mts.map → index-8ml_NVS_.d.mts.map} +1 -1
- package/dist/{index-BdXzdXGE.d.mts → index-BEE0UxUA.d.mts} +3 -3
- package/dist/{index-BdXzdXGE.d.mts.map → index-BEE0UxUA.d.mts.map} +1 -1
- package/dist/{index-YY1SU_7o.d.mts → index-CQ1JuHm5.d.mts} +13 -12
- package/dist/index-CQ1JuHm5.d.mts.map +1 -0
- package/dist/{index-rANtsIZG.d.mts → index-D62lu0tO.d.mts} +3 -3
- package/dist/{index-rANtsIZG.d.mts.map → index-D62lu0tO.d.mts.map} +1 -1
- package/dist/{index-BhIiYIXu.d.mts → index-Dfilr1Os.d.mts} +6 -6
- package/dist/{index-BhIiYIXu.d.mts.map → index-Dfilr1Os.d.mts.map} +1 -1
- package/dist/{interpreter-BfHdPd7T-DGqi2_Um.mjs → interpreter-DFYQNoyO--j8HG0mg.mjs} +620 -905
- package/dist/interpreter-DFYQNoyO--j8HG0mg.mjs.map +1 -0
- package/dist/{ir-DGm0HXbP.mjs → ir-DrSbC_hc.mjs} +2 -2
- package/dist/{ir-DGm0HXbP.mjs.map → ir-DrSbC_hc.mjs.map} +1 -1
- package/dist/{ir-BLC0t6_x.d.mts → ir-SLc3mVHt.d.mts} +4 -4
- package/dist/{ir-BLC0t6_x.d.mts.map → ir-SLc3mVHt.d.mts.map} +1 -1
- package/dist/lowering.d.mts +1 -1
- package/dist/{migration-YQVmgFU8.d.mts → migration-C9enejJh.d.mts} +5 -5
- package/dist/{migration-YQVmgFU8.d.mts.map → migration-C9enejJh.d.mts.map} +1 -1
- package/dist/{migration-DnQ7_A_B.mjs → migration-CKa85PF4.mjs} +2 -2
- package/dist/{migration-DnQ7_A_B.mjs.map → migration-CKa85PF4.mjs.map} +1 -1
- package/dist/{mongo-contract-serializer-Zx1rvil3-BywiKCpn.mjs → mongo-contract-serializer-DAPHJOHU-BjEbQIgp.mjs} +3 -3
- package/dist/{mongo-contract-serializer-Zx1rvil3-BywiKCpn.mjs.map → mongo-contract-serializer-DAPHJOHU-BjEbQIgp.mjs.map} +1 -1
- package/dist/{mongo-contract-view-AnejqfrQ-Wj_ulq2L.d.mts → mongo-contract-view-AnejqfrQ-DJJ6PwWt.d.mts} +3 -3
- package/dist/{mongo-contract-view-AnejqfrQ-Wj_ulq2L.d.mts.map → mongo-contract-view-AnejqfrQ-DJJ6PwWt.d.mts.map} +1 -1
- package/dist/{mongo-contract-view-D5yu2LrY-DCAZYJ4q.mjs → mongo-contract-view-x3OCGAk1-DvEhwf_e.mjs} +4 -4
- package/dist/{mongo-contract-view-D5yu2LrY-DCAZYJ4q.mjs.map → mongo-contract-view-x3OCGAk1-DvEhwf_e.mjs.map} +1 -1
- package/dist/mongo-data-type-Bv8Bd0zu-cnTiyyP0.mjs +33 -0
- package/dist/mongo-data-type-Bv8Bd0zu-cnTiyyP0.mjs.map +1 -0
- package/dist/{mongo-value-set-DEd7WLCT-B2Ar2GkJ.d.mts → mongo-value-set-BkGaKhbE-86TPJ91m.d.mts} +4 -5
- package/dist/mongo-value-set-BkGaKhbE-86TPJ91m.d.mts.map +1 -0
- package/dist/orm.d.mts +6 -6
- package/dist/orm.d.mts.map +1 -1
- package/dist/orm.mjs +2 -2
- package/dist/{pack-qw77Jt2G.d.mts → pack-WQox6nGF.d.mts} +4 -2
- package/dist/pack-WQox6nGF.d.mts.map +1 -0
- package/dist/query-ast.d.mts +2 -2
- package/dist/query-ast__control.d.mts +2 -2
- package/dist/query-builder.d.mts +2 -2
- package/dist/query-builder.mjs +1 -1
- package/dist/query-builder__contract-free.d.mts +3 -3
- package/dist/{runtime-yZA30qM2.d.mts → runtime-B1uT8PuG.d.mts} +2 -2
- package/dist/runtime-B1uT8PuG.d.mts.map +1 -0
- package/dist/runtime.d.mts +2 -2
- package/dist/schema-ir.d.mts +1 -1
- package/dist/{schema-verify-CVOG6EZx.d.mts → schema-verify-BiwnOU7s.d.mts} +4 -4
- package/dist/{schema-verify-CVOG6EZx.d.mts.map → schema-verify-BiwnOU7s.d.mts.map} +1 -1
- package/dist/{verify-mongo-schema-CeQP5N4k-CNOctppM.mjs → verify-mongo-schema-nsnAhPm8-BgFC6Aop.mjs} +3 -3
- package/dist/{verify-mongo-schema-CeQP5N4k-CNOctppM.mjs.map → verify-mongo-schema-nsnAhPm8-BgFC6Aop.mjs.map} +1 -1
- package/dist/wire.d.mts +1 -1
- package/package.json +20 -20
- package/dist/authoring-entity-types-UA5sY_Xm-DeYf_cYq.mjs.map +0 -1
- package/dist/contract-builder-BHzQQk70.mjs +0 -965
- package/dist/contract-builder-BHzQQk70.mjs.map +0 -1
- package/dist/contract-builder-ClL-xMK2.d.mts.map +0 -1
- package/dist/control-adapter-CndDiTXW.d.mts +0 -1
- package/dist/dist-B8f-pfcT.mjs.map +0 -1
- package/dist/index-YY1SU_7o.d.mts.map +0 -1
- package/dist/interpreter-BfHdPd7T-DGqi2_Um.mjs.map +0 -1
- package/dist/mongo-data-type-DzyzU4LF-C1ydFXgE.mjs +0 -53
- package/dist/mongo-data-type-DzyzU4LF-C1ydFXgE.mjs.map +0 -1
- package/dist/mongo-value-set-DEd7WLCT-B2Ar2GkJ.d.mts.map +0 -1
- package/dist/pack-qw77Jt2G.d.mts.map +0 -1
- package/dist/runtime-yZA30qM2.d.mts.map +0 -1
|
@@ -0,0 +1,1832 @@
|
|
|
1
|
+
import { i as MongoCollectionOptions, o as MongoIndex } from "./entity-kinds-BQBtfVet-B-Px7NHp.mjs";
|
|
2
|
+
import { n as codecTypeParamsProblem } from "./mongo-data-type-Bv8Bd0zu-cnTiyyP0.mjs";
|
|
3
|
+
import { M as unsupportedStoredNameReason, S as mongoContractRefusalsOf, g as deriveCollectionValidators, s as applyPolymorphicScopeToMongoIndex, u as buildMongoStorage, w as mongoValueSetsOf } from "./dist-DgMGRQkm.mjs";
|
|
4
|
+
import { t as contractError } from "./contract-errors-BCN6RVI5-kMurtAbI.mjs";
|
|
5
|
+
import { structuredError } from "@prisma/orm-framework/utils/structured-error";
|
|
6
|
+
import { blindCast } from "@prisma/orm-framework/utils/casts";
|
|
7
|
+
import { crossRef } from "@prisma/orm-framework/contract/types";
|
|
8
|
+
import { buildExecutionSection, computeProfileHash } from "@prisma/orm-framework/contract/hashing";
|
|
9
|
+
import { assertDefined } from "@prisma/orm-framework/utils/assertions";
|
|
10
|
+
import { notOk, ok } from "@prisma/orm-framework/utils/result";
|
|
11
|
+
import { bindEnumType, composePackAuthoringNamespace, createEntityHelpersFromNamespace, createFieldHelpersFromNamespace, enumMemberError, enumType as enumType$1, isEnumTypeHandle, member, resolveToOneRelationNullable } from "@prisma/orm-framework/contract-authoring";
|
|
12
|
+
import { applyDiscriminatorRule, assembleDataTypeSupport, assertNoCrossRegistryCollisions, assertTypeAliasesValid, encodeEnumMembers, flushAuthoringWarnings, instantiateAuthoringFieldPreset, validateAuthoringHelperArguments } from "@prisma/orm-framework/components/authoring";
|
|
13
|
+
import { assembleGeneratorFunctions, createGeneratorHelpers } from "@prisma/orm-framework/components/components";
|
|
14
|
+
import { ifDefined } from "@prisma/orm-framework/utils/defined";
|
|
15
|
+
import { canonicalStringify } from "@prisma/orm-framework/utils/canonical-stringify";
|
|
16
|
+
import { extractCodecLookup } from "@prisma/orm-framework/components/control";
|
|
17
|
+
//#region ../../../2-mongo-family/2-authoring/contract-ts/dist/contract-builder.mjs
|
|
18
|
+
function modelFieldKey(model, field) {
|
|
19
|
+
return {
|
|
20
|
+
kind: "field",
|
|
21
|
+
owner: {
|
|
22
|
+
kind: "model",
|
|
23
|
+
name: model
|
|
24
|
+
},
|
|
25
|
+
field
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function valueObjectFieldKey(valueObject, field) {
|
|
29
|
+
return {
|
|
30
|
+
kind: "field",
|
|
31
|
+
owner: {
|
|
32
|
+
kind: "valueObject",
|
|
33
|
+
name: valueObject
|
|
34
|
+
},
|
|
35
|
+
field
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** The key of a validator refusal: the part of the definition its subject names. */
|
|
39
|
+
function validatorRefusalKey(refusal) {
|
|
40
|
+
switch (refusal.kind) {
|
|
41
|
+
case "embeddedModelHasVariants": return {
|
|
42
|
+
kind: "relation",
|
|
43
|
+
model: refusal.modelName,
|
|
44
|
+
relation: refusal.relationName
|
|
45
|
+
};
|
|
46
|
+
case "enumBsonType":
|
|
47
|
+
case "enumNotListable": return {
|
|
48
|
+
kind: "enum",
|
|
49
|
+
enumName: refusal.enumName
|
|
50
|
+
};
|
|
51
|
+
case "enumMemberNotListable": return {
|
|
52
|
+
kind: "enumMember",
|
|
53
|
+
enumName: refusal.enumName,
|
|
54
|
+
member: refusal.member
|
|
55
|
+
};
|
|
56
|
+
case "fieldBsonType": return {
|
|
57
|
+
kind: "field",
|
|
58
|
+
owner: refusal.owner,
|
|
59
|
+
field: refusal.fieldName
|
|
60
|
+
};
|
|
61
|
+
case "documentId": return modelFieldKey(refusal.modelName, refusal.fieldName);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function storedNameOf(field) {
|
|
65
|
+
return field.storedName ?? field.fieldName;
|
|
66
|
+
}
|
|
67
|
+
/** The name documents store `fieldName` under, or `fieldName` itself when the fields do not declare it. */
|
|
68
|
+
function storedFieldNameIn(fields, fieldName) {
|
|
69
|
+
const field = fields.find((candidate) => candidate.fieldName === fieldName);
|
|
70
|
+
return field === void 0 ? fieldName : storedNameOf(field);
|
|
71
|
+
}
|
|
72
|
+
function buildContractField(field, namespaceId) {
|
|
73
|
+
const valueSet = field.enumName === void 0 ? void 0 : {
|
|
74
|
+
plane: "domain",
|
|
75
|
+
entityKind: "enum",
|
|
76
|
+
namespaceId,
|
|
77
|
+
entityName: field.enumName
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
type: field.type,
|
|
81
|
+
nullable: field.nullable,
|
|
82
|
+
many: field.many,
|
|
83
|
+
...ifDefined("valueSet", valueSet)
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function buildFields(fields, namespaceId) {
|
|
87
|
+
return Object.fromEntries(fields.map((field) => [field.fieldName, buildContractField(field, namespaceId)]));
|
|
88
|
+
}
|
|
89
|
+
function buildStorageFields(modelName, fields, refusals) {
|
|
90
|
+
const storageFields = {};
|
|
91
|
+
const fieldsByStoredName = /* @__PURE__ */ new Map();
|
|
92
|
+
for (const field of fields) {
|
|
93
|
+
const { fieldName } = field;
|
|
94
|
+
const key = modelFieldKey(modelName, fieldName);
|
|
95
|
+
const storedName = storedNameOf(field);
|
|
96
|
+
const unsupported = unsupportedStoredNameReason(storedName);
|
|
97
|
+
if (unsupported !== void 0) refusals.push({
|
|
98
|
+
kind: "storedNameUnsupported",
|
|
99
|
+
key,
|
|
100
|
+
reason: unsupported
|
|
101
|
+
});
|
|
102
|
+
const firstField = fieldsByStoredName.get(storedName);
|
|
103
|
+
if (firstField !== void 0) {
|
|
104
|
+
refusals.push({
|
|
105
|
+
kind: "storedNameDuplicate",
|
|
106
|
+
key,
|
|
107
|
+
storedName,
|
|
108
|
+
firstField
|
|
109
|
+
});
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
fieldsByStoredName.set(storedName, fieldName);
|
|
113
|
+
storageFields[fieldName] = { field: storedName };
|
|
114
|
+
}
|
|
115
|
+
return storageFields;
|
|
116
|
+
}
|
|
117
|
+
function toOneRelationNullable(key, relation, fields, refusals) {
|
|
118
|
+
const ownsForeignKey = relation.cardinality !== "1:1";
|
|
119
|
+
const localFields = [];
|
|
120
|
+
if (ownsForeignKey) for (const fieldName of relation.on.localFields) {
|
|
121
|
+
const localField = fields.find((field) => field.fieldName === fieldName);
|
|
122
|
+
if (localField === void 0) {
|
|
123
|
+
refusals.push({
|
|
124
|
+
kind: "relationLocalFieldUnknown",
|
|
125
|
+
key,
|
|
126
|
+
fieldName
|
|
127
|
+
});
|
|
128
|
+
return relation.nullable ?? false;
|
|
129
|
+
}
|
|
130
|
+
localFields.push(localField);
|
|
131
|
+
}
|
|
132
|
+
const { nullable, contradiction } = resolveToOneRelationNullable({
|
|
133
|
+
declaredNullable: relation.nullable,
|
|
134
|
+
localFieldNullability: localFields.map((localField) => localField.nullable === true),
|
|
135
|
+
ownsReference: ownsForeignKey
|
|
136
|
+
});
|
|
137
|
+
if (contradiction !== void 0) refusals.push({
|
|
138
|
+
kind: "relationNullability",
|
|
139
|
+
key,
|
|
140
|
+
ownsForeignKey,
|
|
141
|
+
contradiction
|
|
142
|
+
});
|
|
143
|
+
return nullable;
|
|
144
|
+
}
|
|
145
|
+
function buildRelation(model, relation, namespaceId, refusals) {
|
|
146
|
+
const to = crossRef(relation.toModel, namespaceId);
|
|
147
|
+
if (relation.on === void 0) return {
|
|
148
|
+
to,
|
|
149
|
+
cardinality: relation.cardinality
|
|
150
|
+
};
|
|
151
|
+
if (relation.cardinality === "1:N") return {
|
|
152
|
+
to,
|
|
153
|
+
cardinality: "1:N",
|
|
154
|
+
on: relation.on
|
|
155
|
+
};
|
|
156
|
+
const key = {
|
|
157
|
+
kind: "relation",
|
|
158
|
+
model: model.modelName,
|
|
159
|
+
relation: relation.relationName
|
|
160
|
+
};
|
|
161
|
+
return {
|
|
162
|
+
to,
|
|
163
|
+
cardinality: relation.cardinality,
|
|
164
|
+
nullable: toOneRelationNullable(key, relation, model.fields, refusals),
|
|
165
|
+
on: relation.on
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function buildValueObjects(valueObjects, namespaceId, refusals) {
|
|
169
|
+
const builtValueObjects = {};
|
|
170
|
+
for (const valueObject of valueObjects ?? []) {
|
|
171
|
+
if (valueObject.name in builtValueObjects) {
|
|
172
|
+
refusals.push({
|
|
173
|
+
kind: "duplicateValueObject",
|
|
174
|
+
key: {
|
|
175
|
+
kind: "valueObject",
|
|
176
|
+
valueObject: valueObject.name
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
for (const field of valueObject.fields) if (field.storedName !== void 0) refusals.push({
|
|
182
|
+
kind: "valueObjectFieldStoredName",
|
|
183
|
+
key: valueObjectFieldKey(valueObject.name, field.fieldName)
|
|
184
|
+
});
|
|
185
|
+
builtValueObjects[valueObject.name] = { fields: buildFields(valueObject.fields, namespaceId) };
|
|
186
|
+
}
|
|
187
|
+
return builtValueObjects;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Refuses a variant field stored under a name another field of its base is stored under. A variant's documents hold its base's fields too, so the two would overwrite each other, the discriminator included. A variant that restates a base field under the same name restates that field.
|
|
191
|
+
*/
|
|
192
|
+
function restatementDifferences(variantField, baseField) {
|
|
193
|
+
const differences = [];
|
|
194
|
+
if (storedNameOf(variantField) !== storedNameOf(baseField)) differences.push("storedName");
|
|
195
|
+
if (canonicalStringify([variantField.type, variantField.enumName]) !== canonicalStringify([baseField.type, baseField.enumName])) differences.push("type");
|
|
196
|
+
if (variantField.nullable !== baseField.nullable) differences.push("nullable");
|
|
197
|
+
if (canonicalStringify(variantField.many) !== canonicalStringify(baseField.many)) differences.push("many");
|
|
198
|
+
return differences;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* A variant's documents hold its base's fields too, and its collection's validator is written from the base. A variant field may restate a base field only exactly, and may not take a stored name a different base field uses.
|
|
202
|
+
*/
|
|
203
|
+
function checkFieldsAgainstBase(variant, models, refusals) {
|
|
204
|
+
if (variant.base === void 0) return;
|
|
205
|
+
const baseName = variant.base.model;
|
|
206
|
+
const base = models.find((model) => model.modelName === baseName);
|
|
207
|
+
if (base === void 0) return;
|
|
208
|
+
const baseFieldsByName = new Map(base.fields.map((field) => [field.fieldName, field]));
|
|
209
|
+
const baseFieldsByStoredName = new Map(base.fields.map((field) => [storedNameOf(field), field]));
|
|
210
|
+
for (const field of variant.fields) {
|
|
211
|
+
const key = modelFieldKey(variant.modelName, field.fieldName);
|
|
212
|
+
const restated = baseFieldsByName.get(field.fieldName);
|
|
213
|
+
if (restated !== void 0) {
|
|
214
|
+
const differences = restatementDifferences(field, restated);
|
|
215
|
+
if (differences.length > 0) refusals.push({
|
|
216
|
+
kind: "restatedBaseFieldDiffers",
|
|
217
|
+
key,
|
|
218
|
+
baseModel: baseName,
|
|
219
|
+
differences
|
|
220
|
+
});
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
const storedName = storedNameOf(field);
|
|
224
|
+
const baseField = baseFieldsByStoredName.get(storedName);
|
|
225
|
+
if (baseField === void 0) continue;
|
|
226
|
+
refusals.push({
|
|
227
|
+
kind: "storedNameSharedWithBase",
|
|
228
|
+
key,
|
|
229
|
+
storedName,
|
|
230
|
+
baseModel: baseName,
|
|
231
|
+
baseField: baseField.fieldName
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Each model of the contract. `variantValues` holds each accepted variant's discriminator value as its base's field stores it; a base lists those variants.
|
|
237
|
+
*/
|
|
238
|
+
function buildModels(input) {
|
|
239
|
+
const { models, variantValues, namespaceId, refusals } = input;
|
|
240
|
+
const builtModels = {};
|
|
241
|
+
for (const model of models) {
|
|
242
|
+
const { modelName } = model;
|
|
243
|
+
if (modelName in builtModels) {
|
|
244
|
+
refusals.push({
|
|
245
|
+
kind: "duplicateModel",
|
|
246
|
+
key: {
|
|
247
|
+
kind: "model",
|
|
248
|
+
model: modelName
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
checkFieldsAgainstBase(model, models, refusals);
|
|
254
|
+
const storage = {
|
|
255
|
+
...model.collection ? { collection: model.collection } : {},
|
|
256
|
+
fields: buildStorageFields(modelName, model.fields, refusals),
|
|
257
|
+
...model.storageRelations ? { relations: model.storageRelations } : {}
|
|
258
|
+
};
|
|
259
|
+
const variants = model.discriminator === void 0 ? void 0 : models.flatMap((variant) => {
|
|
260
|
+
const value = variantValues.get(variant.modelName);
|
|
261
|
+
return variant.base?.model === modelName && value !== void 0 ? [[value, crossRef(variant.modelName, namespaceId)]] : [];
|
|
262
|
+
});
|
|
263
|
+
builtModels[modelName] = {
|
|
264
|
+
fields: buildFields(model.fields, namespaceId),
|
|
265
|
+
relations: Object.fromEntries((model.relations ?? []).map((relation) => [relation.relationName, buildRelation(model, relation, namespaceId, refusals)])),
|
|
266
|
+
storage,
|
|
267
|
+
...model.owner ? { owner: model.owner } : {},
|
|
268
|
+
...model.base ? { base: crossRef(model.base.model, namespaceId) } : {},
|
|
269
|
+
...model.discriminator ? { discriminator: { field: model.discriminator.field } } : {},
|
|
270
|
+
...variants === void 0 ? {} : { variants: Object.fromEntries(variants) }
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return builtModels;
|
|
274
|
+
}
|
|
275
|
+
function buildRoots(definition, models, namespaceId) {
|
|
276
|
+
if (definition.roots !== void 0) return Object.fromEntries(Object.entries(definition.roots).map(([rootName, modelName]) => [rootName, crossRef(modelName, namespaceId)]));
|
|
277
|
+
const roots = {};
|
|
278
|
+
for (const model of models) {
|
|
279
|
+
if (!model.collection || model.owner || model.base) continue;
|
|
280
|
+
roots[model.collection] = crossRef(model.modelName, namespaceId);
|
|
281
|
+
}
|
|
282
|
+
return roots;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* A key-sorted signature of a storage index, used only to find duplicate indexes. `canonicalStringify` refuses class instances such as `MongoIndex`, so this reads their enumerable properties instead.
|
|
286
|
+
*/
|
|
287
|
+
function stableStringify(value) {
|
|
288
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
289
|
+
if (value && typeof value === "object") return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`).join(",")}}`;
|
|
290
|
+
return JSON.stringify(value);
|
|
291
|
+
}
|
|
292
|
+
function storedIndexKey(fields, key) {
|
|
293
|
+
const scope = key.match(/^(.+)\.\$\*\*$/)?.[1];
|
|
294
|
+
if (scope !== void 0) return `${storedFieldNameIn(fields, scope)}.$**`;
|
|
295
|
+
return key === "$**" ? key : storedFieldNameIn(fields, key);
|
|
296
|
+
}
|
|
297
|
+
function toStorageIndex(index, fields) {
|
|
298
|
+
const input = { keys: Object.entries(index.fields).map(([field, direction]) => ({
|
|
299
|
+
field: storedIndexKey(fields, field),
|
|
300
|
+
direction
|
|
301
|
+
})) };
|
|
302
|
+
if (index.options) {
|
|
303
|
+
for (const [key, value] of Object.entries(index.options)) if (value !== void 0) input[key] = value;
|
|
304
|
+
}
|
|
305
|
+
return new MongoIndex(blindCast(input));
|
|
306
|
+
}
|
|
307
|
+
function toStorageCollectionOptions(opts) {
|
|
308
|
+
return new MongoCollectionOptions({
|
|
309
|
+
...opts.capped ? { capped: {
|
|
310
|
+
size: opts.size ?? 0,
|
|
311
|
+
...opts.max != null && { max: opts.max }
|
|
312
|
+
} } : {},
|
|
313
|
+
...ifDefined("storageEngine", opts.storageEngine),
|
|
314
|
+
...ifDefined("indexOptionDefaults", opts.indexOptionDefaults),
|
|
315
|
+
...ifDefined("collation", opts.collation),
|
|
316
|
+
...ifDefined("timeseries", opts.timeseries),
|
|
317
|
+
...opts.clusteredIndex !== void 0 && { clusteredIndex: opts.clusteredIndex.name !== void 0 ? { name: opts.clusteredIndex.name } : {} },
|
|
318
|
+
...ifDefined("expireAfterSeconds", opts.expireAfterSeconds),
|
|
319
|
+
...ifDefined("changeStreamPreAndPostImages", opts.changeStreamPreAndPostImages)
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
function findMissingIndexField(index, fields) {
|
|
323
|
+
for (const fieldName of Object.keys(index.fields)) {
|
|
324
|
+
const wildcardMatch = fieldName.match(/^(.+)\.\$\*\*$/);
|
|
325
|
+
const lookupName = wildcardMatch ? wildcardMatch[1] : fieldName;
|
|
326
|
+
if (lookupName === void 0 || lookupName.length === 0) continue;
|
|
327
|
+
if (lookupName === "$**") continue;
|
|
328
|
+
if (!fields.some((field) => field.fieldName === lookupName)) return lookupName;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function isWildcardKey(key) {
|
|
332
|
+
return key === "$**" || key.endsWith(".$**");
|
|
333
|
+
}
|
|
334
|
+
function isTextIndex(index) {
|
|
335
|
+
return Object.values(index.fields).includes("text");
|
|
336
|
+
}
|
|
337
|
+
/** The first shape rule an index breaks, of those MongoDB enforces when it creates the index. */
|
|
338
|
+
function indexShapeProblem(index) {
|
|
339
|
+
const keys = Object.entries(index.fields);
|
|
340
|
+
const hasWildcard = keys.some(([key]) => isWildcardKey(key));
|
|
341
|
+
const directions = keys.map(([, direction]) => direction);
|
|
342
|
+
const options = index.options ?? {};
|
|
343
|
+
if (hasWildcard && options.unique) return "unique-wildcard";
|
|
344
|
+
if (hasWildcard && directions.some((direction) => [
|
|
345
|
+
"hashed",
|
|
346
|
+
"2d",
|
|
347
|
+
"2dsphere"
|
|
348
|
+
].includes(String(direction)))) return "wildcard-with-type";
|
|
349
|
+
if (directions.includes("hashed") && keys.length > 1) return "hashed-several-fields";
|
|
350
|
+
if (hasWildcard && options.expireAfterSeconds !== void 0) return "ttl-with-wildcard";
|
|
351
|
+
if (!hasWildcard && options.wildcardProjection !== void 0) return "projection-without-wildcard";
|
|
352
|
+
if (options.collation !== void 0 && !options.collation.locale) return "collation-without-locale";
|
|
353
|
+
}
|
|
354
|
+
function resolveVariantScope(model, modelsByName, variantValues) {
|
|
355
|
+
if (!model.base) return void 0;
|
|
356
|
+
const base = modelsByName.get(model.base.model);
|
|
357
|
+
const discriminatorField = base?.discriminator?.field;
|
|
358
|
+
const discriminatorValue = variantValues.get(model.modelName);
|
|
359
|
+
if (base === void 0 || !discriminatorField || discriminatorValue === void 0) return;
|
|
360
|
+
return {
|
|
361
|
+
discriminatorField: storedFieldNameIn(base.fields, discriminatorField),
|
|
362
|
+
discriminatorValue
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function checkNoCollectionSettings(model, refusals) {
|
|
366
|
+
const key = {
|
|
367
|
+
kind: "collectionSettings",
|
|
368
|
+
model: model.modelName
|
|
369
|
+
};
|
|
370
|
+
if (model.indexes && model.indexes.length > 0) refusals.push({
|
|
371
|
+
kind: "collectionSettingWithoutCollection",
|
|
372
|
+
key,
|
|
373
|
+
setting: "indexes"
|
|
374
|
+
});
|
|
375
|
+
if (model.collectionOptions) refusals.push({
|
|
376
|
+
kind: "collectionSettingWithoutCollection",
|
|
377
|
+
key,
|
|
378
|
+
setting: "collectionOptions"
|
|
379
|
+
});
|
|
380
|
+
if (model.controlPolicy) refusals.push({
|
|
381
|
+
kind: "collectionSettingWithoutCollection",
|
|
382
|
+
key,
|
|
383
|
+
setting: "controlPolicy"
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
/** Each collection the models are stored in, with its indexes, options and control policy. Validators are added later. */
|
|
387
|
+
function buildCollections(input) {
|
|
388
|
+
const { models, variantValues, refusals } = input;
|
|
389
|
+
const intermediate = {};
|
|
390
|
+
const declaredIndexOwners = /* @__PURE__ */ new Map();
|
|
391
|
+
const textIndexCollections = /* @__PURE__ */ new Set();
|
|
392
|
+
const modelsByName = new Map(models.map((model) => [model.modelName, model]));
|
|
393
|
+
for (const model of models) {
|
|
394
|
+
const { modelName, collection } = model;
|
|
395
|
+
if (!collection) {
|
|
396
|
+
checkNoCollectionSettings(model, refusals);
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const collectionKey = {
|
|
400
|
+
kind: "collectionSettings",
|
|
401
|
+
model: modelName
|
|
402
|
+
};
|
|
403
|
+
const existingCollection = intermediate[collection] ?? {};
|
|
404
|
+
const existingIndexes = existingCollection.indexes ?? [];
|
|
405
|
+
if (existingCollection.options && model.collectionOptions) refusals.push({
|
|
406
|
+
kind: "collectionSettingTwice",
|
|
407
|
+
key: collectionKey,
|
|
408
|
+
setting: "collectionOptions",
|
|
409
|
+
collection
|
|
410
|
+
});
|
|
411
|
+
if (existingCollection.control !== void 0 && model.controlPolicy) refusals.push({
|
|
412
|
+
kind: "collectionSettingTwice",
|
|
413
|
+
key: collectionKey,
|
|
414
|
+
setting: "controlPolicy",
|
|
415
|
+
collection
|
|
416
|
+
});
|
|
417
|
+
const polymorphicScope = resolveVariantScope(model, modelsByName, variantValues);
|
|
418
|
+
const storageIndexes = [];
|
|
419
|
+
(model.indexes ?? []).forEach((written, position) => {
|
|
420
|
+
const key = {
|
|
421
|
+
kind: "index",
|
|
422
|
+
model: modelName,
|
|
423
|
+
position
|
|
424
|
+
};
|
|
425
|
+
const signature = canonicalStringify(written);
|
|
426
|
+
const missingField = findMissingIndexField(written, model.fields);
|
|
427
|
+
if (missingField !== void 0) {
|
|
428
|
+
refusals.push({
|
|
429
|
+
kind: "indexFieldUnknown",
|
|
430
|
+
key,
|
|
431
|
+
fieldName: missingField,
|
|
432
|
+
signature
|
|
433
|
+
});
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
const shapeProblem = indexShapeProblem(written);
|
|
437
|
+
if (shapeProblem !== void 0) {
|
|
438
|
+
refusals.push({
|
|
439
|
+
kind: "indexShape",
|
|
440
|
+
key,
|
|
441
|
+
rule: shapeProblem,
|
|
442
|
+
signature
|
|
443
|
+
});
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (isTextIndex(written)) {
|
|
447
|
+
if (textIndexCollections.has(collection)) {
|
|
448
|
+
refusals.push({
|
|
449
|
+
kind: "indexShape",
|
|
450
|
+
key,
|
|
451
|
+
rule: "second-text-index",
|
|
452
|
+
signature
|
|
453
|
+
});
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
textIndexCollections.add(collection);
|
|
457
|
+
}
|
|
458
|
+
const storageIndex = toStorageIndex(written, model.fields);
|
|
459
|
+
const scoped = polymorphicScope === void 0 ? {
|
|
460
|
+
kind: "ok",
|
|
461
|
+
index: storageIndex
|
|
462
|
+
} : applyPolymorphicScopeToMongoIndex(storageIndex, polymorphicScope);
|
|
463
|
+
if (scoped.kind === "conflict") {
|
|
464
|
+
refusals.push({
|
|
465
|
+
kind: "indexScopeConflict",
|
|
466
|
+
key,
|
|
467
|
+
reason: scoped.reason,
|
|
468
|
+
signature
|
|
469
|
+
});
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const collectionIndexKey = `${collection}:${stableStringify(scoped.index)}`;
|
|
473
|
+
const firstModel = declaredIndexOwners.get(collectionIndexKey);
|
|
474
|
+
if (firstModel !== void 0) {
|
|
475
|
+
refusals.push({
|
|
476
|
+
kind: "indexDuplicate",
|
|
477
|
+
key,
|
|
478
|
+
collection,
|
|
479
|
+
firstModel,
|
|
480
|
+
signature
|
|
481
|
+
});
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
declaredIndexOwners.set(collectionIndexKey, modelName);
|
|
485
|
+
storageIndexes.push(scoped.index);
|
|
486
|
+
});
|
|
487
|
+
const storageOptions = model.collectionOptions ? toStorageCollectionOptions(model.collectionOptions) : void 0;
|
|
488
|
+
const controlPatch = model.controlPolicy !== void 0 ? { control: model.controlPolicy } : {};
|
|
489
|
+
intermediate[collection] = storageIndexes.length > 0 ? {
|
|
490
|
+
...existingCollection,
|
|
491
|
+
indexes: [...existingIndexes, ...storageIndexes],
|
|
492
|
+
...storageOptions ? { options: storageOptions } : {},
|
|
493
|
+
...controlPatch
|
|
494
|
+
} : storageOptions ? {
|
|
495
|
+
...existingCollection,
|
|
496
|
+
options: storageOptions,
|
|
497
|
+
...controlPatch
|
|
498
|
+
} : Object.keys(controlPatch).length > 0 ? {
|
|
499
|
+
...existingCollection,
|
|
500
|
+
...controlPatch
|
|
501
|
+
} : existingCollection;
|
|
502
|
+
}
|
|
503
|
+
return intermediate;
|
|
504
|
+
}
|
|
505
|
+
/** Each enum with its members as its codec stores them. An enum the builder refuses is left out. */
|
|
506
|
+
function buildEnums(definition, codecLookup, refusals) {
|
|
507
|
+
const builtEnums = {};
|
|
508
|
+
for (const [enumName, handle] of Object.entries(definition.enums ?? {})) {
|
|
509
|
+
const encoded = encodeEnumMembers(handle, codecLookup);
|
|
510
|
+
if (!encoded.ok) {
|
|
511
|
+
for (const problem of encoded.failure) refusals.push({
|
|
512
|
+
kind: "enumMemberProblem",
|
|
513
|
+
key: "member" in problem ? {
|
|
514
|
+
kind: "enumMember",
|
|
515
|
+
enumName,
|
|
516
|
+
member: problem.member
|
|
517
|
+
} : {
|
|
518
|
+
kind: "enum",
|
|
519
|
+
enumName
|
|
520
|
+
},
|
|
521
|
+
enumName: handle.enumName,
|
|
522
|
+
codecId: handle.codecId,
|
|
523
|
+
problem
|
|
524
|
+
});
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
builtEnums[enumName] = {
|
|
528
|
+
codecId: handle.codecId,
|
|
529
|
+
members: encoded.value
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
for (const [enumName, handle] of Object.entries(definition.enums ?? {})) if (enumName !== handle.enumName) refusals.push({
|
|
533
|
+
kind: "enumKeyMismatch",
|
|
534
|
+
key: {
|
|
535
|
+
kind: "enum",
|
|
536
|
+
enumName
|
|
537
|
+
},
|
|
538
|
+
enumName: handle.enumName
|
|
539
|
+
});
|
|
540
|
+
return builtEnums;
|
|
541
|
+
}
|
|
542
|
+
const PHASES = ["onCreate", "onUpdate"];
|
|
543
|
+
function hasExecutionDefaults(field) {
|
|
544
|
+
const phases = field.executionDefaults;
|
|
545
|
+
const calls = field.generatorCalls;
|
|
546
|
+
const values = field.generatedValues;
|
|
547
|
+
return phases?.onCreate !== void 0 || phases?.onUpdate !== void 0 || values?.onCreate !== void 0 || values?.onUpdate !== void 0 || calls?.onCreate !== void 0 || calls?.onUpdate !== void 0;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* The phases a field generates: its resolved execution defaults, with the generators its calls resolve to for its codec. Undefined when a call is refused.
|
|
551
|
+
*/
|
|
552
|
+
function executionDefaultsOf(field, key, generatorFunctions, refusals) {
|
|
553
|
+
const typeGiven = field.executionDefaults;
|
|
554
|
+
let refused = false;
|
|
555
|
+
for (const phase of PHASES) if (typeGiven?.[phase] !== void 0 && field.generatedValues?.[phase] !== void 0) {
|
|
556
|
+
refusals.push({
|
|
557
|
+
kind: "generatorPhaseSetTwice",
|
|
558
|
+
key,
|
|
559
|
+
phase
|
|
560
|
+
});
|
|
561
|
+
refused = true;
|
|
562
|
+
}
|
|
563
|
+
const written = {
|
|
564
|
+
...typeGiven,
|
|
565
|
+
...field.generatedValues
|
|
566
|
+
};
|
|
567
|
+
const calls = field.generatorCalls;
|
|
568
|
+
if (calls === void 0) return refused ? void 0 : written;
|
|
569
|
+
const type = field.type;
|
|
570
|
+
const codecId = type.kind === "scalar" ? type.codecId : void 0;
|
|
571
|
+
const phases = written;
|
|
572
|
+
for (const phase of PHASES) {
|
|
573
|
+
const call = calls[phase];
|
|
574
|
+
if (call === void 0) continue;
|
|
575
|
+
if (written[phase] !== void 0) {
|
|
576
|
+
refusals.push({
|
|
577
|
+
kind: "generatorPhaseSetTwice",
|
|
578
|
+
key,
|
|
579
|
+
phase
|
|
580
|
+
});
|
|
581
|
+
refused = true;
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
const resolution = type.kind === "scalar" ? generatorFunctions.resolve(call, {
|
|
585
|
+
codecId: type.codecId,
|
|
586
|
+
typeParams: type.typeParams,
|
|
587
|
+
length: void 0
|
|
588
|
+
}) : void 0;
|
|
589
|
+
if (resolution?.ok === true) {
|
|
590
|
+
phases[phase] = resolution.generator;
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
refused = true;
|
|
594
|
+
refusals.push(resolution?.reason === "unknown-function" ? {
|
|
595
|
+
kind: "generatorUnknown",
|
|
596
|
+
key,
|
|
597
|
+
phase,
|
|
598
|
+
functionName: call.name,
|
|
599
|
+
codecId
|
|
600
|
+
} : {
|
|
601
|
+
kind: "generatorNotApplicable",
|
|
602
|
+
key,
|
|
603
|
+
phase,
|
|
604
|
+
functionName: call.name,
|
|
605
|
+
codecId,
|
|
606
|
+
message: resolution?.reason === "refused" ? resolution.message : void 0
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
return refused ? void 0 : phases;
|
|
610
|
+
}
|
|
611
|
+
function checkValueObjectExecutionDefaults(valueObjects, refusals) {
|
|
612
|
+
for (const valueObject of valueObjects ?? []) for (const field of valueObject.fields) if (hasExecutionDefaults(field)) refusals.push({
|
|
613
|
+
kind: "generatedOnValueObject",
|
|
614
|
+
key: valueObjectFieldKey(valueObject.name, field.fieldName)
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
function buildExecutionDefaults(input) {
|
|
618
|
+
const { models, generatorFunctions, namespaceId, refusals } = input;
|
|
619
|
+
const collectionByModelName = new Map(models.map((model) => [model.modelName, model.collection]));
|
|
620
|
+
const byRef = /* @__PURE__ */ new Map();
|
|
621
|
+
const defaults = [];
|
|
622
|
+
for (const model of models) {
|
|
623
|
+
const { modelName } = model;
|
|
624
|
+
for (const field of model.fields) {
|
|
625
|
+
if (!hasExecutionDefaults(field)) continue;
|
|
626
|
+
const { fieldName } = field;
|
|
627
|
+
const key = modelFieldKey(modelName, fieldName);
|
|
628
|
+
const phases = executionDefaultsOf(field, key, generatorFunctions, refusals);
|
|
629
|
+
if (phases === void 0) continue;
|
|
630
|
+
if (field.many) {
|
|
631
|
+
refusals.push({
|
|
632
|
+
kind: "generatedList",
|
|
633
|
+
key
|
|
634
|
+
});
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
if (model.base !== void 0) {
|
|
638
|
+
refusals.push({
|
|
639
|
+
kind: "generatedOnVariant",
|
|
640
|
+
key,
|
|
641
|
+
baseModel: model.base.model,
|
|
642
|
+
collection: model.collection ?? collectionByModelName.get(model.base.model)
|
|
643
|
+
});
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
const entry = model.collection;
|
|
647
|
+
if (entry === void 0) {
|
|
648
|
+
refusals.push({
|
|
649
|
+
kind: "generatedWithoutCollection",
|
|
650
|
+
key
|
|
651
|
+
});
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
const storedName = storedNameOf(field);
|
|
655
|
+
const refKey = JSON.stringify([entry, storedName]);
|
|
656
|
+
const phasesKey = canonicalStringify(phases);
|
|
657
|
+
const existing = byRef.get(refKey);
|
|
658
|
+
if (existing !== void 0) {
|
|
659
|
+
if (existing.phasesKey !== phasesKey) refusals.push({
|
|
660
|
+
kind: "generatedConflict",
|
|
661
|
+
key,
|
|
662
|
+
otherModel: existing.modelName,
|
|
663
|
+
otherField: existing.fieldName,
|
|
664
|
+
collection: entry
|
|
665
|
+
});
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
byRef.set(refKey, {
|
|
669
|
+
modelName,
|
|
670
|
+
fieldName,
|
|
671
|
+
phasesKey
|
|
672
|
+
});
|
|
673
|
+
defaults.push({
|
|
674
|
+
ref: {
|
|
675
|
+
namespace: namespaceId,
|
|
676
|
+
entry,
|
|
677
|
+
field: storedName
|
|
678
|
+
},
|
|
679
|
+
...phases
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return defaults;
|
|
684
|
+
}
|
|
685
|
+
function generatesNothing(field) {
|
|
686
|
+
const contributed = field.presetContribution?.executionDefaults;
|
|
687
|
+
const calls = field.generatorCalls;
|
|
688
|
+
const values = field.generatedValues;
|
|
689
|
+
return values?.onCreate === void 0 && values?.onUpdate === void 0 && contributed?.onCreate === void 0 && contributed?.onUpdate === void 0 && calls?.onCreate === void 0 && calls?.onUpdate === void 0;
|
|
690
|
+
}
|
|
691
|
+
function checkField(field, key, input) {
|
|
692
|
+
const { type } = field;
|
|
693
|
+
if (field.enumName !== void 0 && !input.declaredEnums.has(field.enumName)) input.refusals.push({
|
|
694
|
+
kind: "enumUnknown",
|
|
695
|
+
key,
|
|
696
|
+
enumName: field.enumName
|
|
697
|
+
});
|
|
698
|
+
if (type.kind === "scalar" && type.typeParams !== void 0) {
|
|
699
|
+
const problem = codecTypeParamsProblem(type.codecId, type.typeParams, input.lookups);
|
|
700
|
+
if (problem !== void 0) input.refusals.push({
|
|
701
|
+
kind: "typeParamsInvalid",
|
|
702
|
+
key,
|
|
703
|
+
problem
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
if (field.presetContribution !== void 0 && type.kind === "scalar" && generatesNothing(field)) input.warnings.push({
|
|
707
|
+
kind: "presetWithoutEffect",
|
|
708
|
+
key,
|
|
709
|
+
preset: field.presetContribution.preset,
|
|
710
|
+
codecId: type.codecId
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Checks each field of the models and value objects on its own: its enum is declared, its data type accepts its type parameters, and a preset that made it generates a value.
|
|
715
|
+
*/
|
|
716
|
+
function checkFields(input) {
|
|
717
|
+
for (const model of input.models) for (const field of model.fields) checkField(field, modelFieldKey(model.modelName, field.fieldName), input);
|
|
718
|
+
for (const valueObject of input.valueObjects ?? []) for (const field of valueObject.fields) checkField(field, valueObjectFieldKey(valueObject.name, field.fieldName), input);
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* The models with each variant stored in its base's collection: a variant that names no collection takes its base's, and one that names another is refused.
|
|
722
|
+
*/
|
|
723
|
+
function inheritVariantCollections(models, refusals) {
|
|
724
|
+
const modelsByName = new Map(models.map((model) => [model.modelName, model]));
|
|
725
|
+
return models.map((model) => {
|
|
726
|
+
if (model.base === void 0) return model;
|
|
727
|
+
const baseCollection = modelsByName.get(model.base.model)?.collection;
|
|
728
|
+
if (baseCollection === void 0) return model;
|
|
729
|
+
if (model.collection === void 0) return {
|
|
730
|
+
...model,
|
|
731
|
+
collection: baseCollection
|
|
732
|
+
};
|
|
733
|
+
if (model.collection !== baseCollection) refusals.push({
|
|
734
|
+
kind: "variantCollectionDiffers",
|
|
735
|
+
key: {
|
|
736
|
+
kind: "collectionSettings",
|
|
737
|
+
model: model.modelName
|
|
738
|
+
},
|
|
739
|
+
collection: model.collection,
|
|
740
|
+
baseModel: model.base.model,
|
|
741
|
+
baseCollection
|
|
742
|
+
});
|
|
743
|
+
return model;
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
function checkVariantShape(model, modelsByName, refusals) {
|
|
747
|
+
if (model.base === void 0) return;
|
|
748
|
+
const key = {
|
|
749
|
+
kind: "base",
|
|
750
|
+
variant: model.modelName
|
|
751
|
+
};
|
|
752
|
+
const baseModel = model.base.model;
|
|
753
|
+
if (model.discriminator !== void 0) refusals.push({
|
|
754
|
+
kind: "discriminatorAndBase",
|
|
755
|
+
key: {
|
|
756
|
+
kind: "discriminator",
|
|
757
|
+
model: model.modelName
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
const base = modelsByName.get(baseModel);
|
|
761
|
+
if (base === void 0) {
|
|
762
|
+
refusals.push({
|
|
763
|
+
kind: "baseUnknown",
|
|
764
|
+
key,
|
|
765
|
+
baseModel
|
|
766
|
+
});
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
if (base.discriminator === void 0) {
|
|
770
|
+
refusals.push({
|
|
771
|
+
kind: "baseWithoutDiscriminator",
|
|
772
|
+
key,
|
|
773
|
+
baseModel
|
|
774
|
+
});
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
if (model.base.value === void 0) refusals.push({
|
|
778
|
+
kind: "variantWithoutValue",
|
|
779
|
+
key,
|
|
780
|
+
baseModel
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
/** Each variant of `base` with the value its source wrote, in the order of the models. */
|
|
784
|
+
function writtenVariantsOf(base, models) {
|
|
785
|
+
return models.flatMap((model) => model.base?.model === base.modelName && model.base.value !== void 0 ? [{
|
|
786
|
+
key: model.modelName,
|
|
787
|
+
written: model.base.value
|
|
788
|
+
}] : []);
|
|
789
|
+
}
|
|
790
|
+
function decideDiscriminator(input) {
|
|
791
|
+
const { base, models, refusals } = input;
|
|
792
|
+
const discriminator = base.discriminator;
|
|
793
|
+
if (discriminator === void 0 || base.base !== void 0) return /* @__PURE__ */ new Map();
|
|
794
|
+
const key = {
|
|
795
|
+
kind: "discriminator",
|
|
796
|
+
model: base.modelName
|
|
797
|
+
};
|
|
798
|
+
const variantNames = new Set(models.filter((model) => model.base?.model === base.modelName).map((model) => model.modelName));
|
|
799
|
+
for (const listed of discriminator.listedVariants ?? []) if (!variantNames.has(listed)) refusals.push({
|
|
800
|
+
kind: "listedVariantMissing",
|
|
801
|
+
key,
|
|
802
|
+
variant: listed
|
|
803
|
+
});
|
|
804
|
+
if (variantNames.size === 0) refusals.push({
|
|
805
|
+
kind: "discriminatorWithoutVariants",
|
|
806
|
+
key
|
|
807
|
+
});
|
|
808
|
+
const fieldName = discriminator.field;
|
|
809
|
+
const field = base.fields.find((candidate) => candidate.fieldName === fieldName);
|
|
810
|
+
if (field === void 0) {
|
|
811
|
+
refusals.push({
|
|
812
|
+
kind: "discriminatorFieldUnknown",
|
|
813
|
+
key,
|
|
814
|
+
fieldName
|
|
815
|
+
});
|
|
816
|
+
return /* @__PURE__ */ new Map();
|
|
817
|
+
}
|
|
818
|
+
const { type } = field;
|
|
819
|
+
const enumMembers = field.enumName === void 0 ? void 0 : input.enums[field.enumName]?.members.map((member) => member.value);
|
|
820
|
+
const decision = applyDiscriminatorRule({
|
|
821
|
+
field: {
|
|
822
|
+
ref: type.kind === "scalar" ? {
|
|
823
|
+
codecId: type.codecId,
|
|
824
|
+
...type.typeParams ? { typeParams: blindCast(type.typeParams) } : {}
|
|
825
|
+
} : void 0,
|
|
826
|
+
nullable: field.nullable,
|
|
827
|
+
many: field.many !== false,
|
|
828
|
+
enumMembers
|
|
829
|
+
},
|
|
830
|
+
variants: writtenVariantsOf(base, models),
|
|
831
|
+
subject: `Field "${base.modelName}.${fieldName}"`,
|
|
832
|
+
codecLookup: input.codecLookup,
|
|
833
|
+
dataTypes: input.dataTypes
|
|
834
|
+
});
|
|
835
|
+
for (const refusal of decision.fieldRefusals) refusals.push({
|
|
836
|
+
kind: "discriminatorField",
|
|
837
|
+
key,
|
|
838
|
+
fieldName,
|
|
839
|
+
refusal
|
|
840
|
+
});
|
|
841
|
+
for (const refusal of decision.variantRefusals) refusals.push({
|
|
842
|
+
kind: "discriminatorValue",
|
|
843
|
+
key: {
|
|
844
|
+
kind: "base",
|
|
845
|
+
variant: refusal.key
|
|
846
|
+
},
|
|
847
|
+
baseModel: base.modelName,
|
|
848
|
+
fieldName,
|
|
849
|
+
enumName: field.enumName,
|
|
850
|
+
refusal
|
|
851
|
+
});
|
|
852
|
+
const variantsByValue = /* @__PURE__ */ new Map();
|
|
853
|
+
for (const [variant, value] of decision.values) {
|
|
854
|
+
const firstVariant = variantsByValue.get(value);
|
|
855
|
+
if (firstVariant !== void 0) {
|
|
856
|
+
refusals.push({
|
|
857
|
+
kind: "discriminatorValueDuplicate",
|
|
858
|
+
key: {
|
|
859
|
+
kind: "base",
|
|
860
|
+
variant
|
|
861
|
+
},
|
|
862
|
+
baseModel: base.modelName,
|
|
863
|
+
value,
|
|
864
|
+
firstVariant
|
|
865
|
+
});
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
variantsByValue.set(value, variant);
|
|
869
|
+
}
|
|
870
|
+
return decision.values;
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Checks each base and variant, and decides each variant's discriminator value with the framework's discriminator rule. Returns each accepted variant's value as its base's discriminator field stores it.
|
|
874
|
+
*/
|
|
875
|
+
function decidePolymorphism(input) {
|
|
876
|
+
const { models, refusals } = input;
|
|
877
|
+
const modelsByName = new Map(models.map((model) => [model.modelName, model]));
|
|
878
|
+
const values = /* @__PURE__ */ new Map();
|
|
879
|
+
for (const model of models) {
|
|
880
|
+
checkVariantShape(model, modelsByName, refusals);
|
|
881
|
+
for (const [variant, value] of decideDiscriminator({
|
|
882
|
+
...input,
|
|
883
|
+
base: model
|
|
884
|
+
})) values.set(variant, value);
|
|
885
|
+
}
|
|
886
|
+
return values;
|
|
887
|
+
}
|
|
888
|
+
/** Whether a refusal is about listing an enum in a collection validator, which matters only when collections get validators. */
|
|
889
|
+
function isAboutEnumListing(refusal) {
|
|
890
|
+
return refusal.kind === "enumBsonType" || refusal.kind === "enumNotListable" || refusal.kind === "enumMemberNotListable";
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Builds a MongoDB contract from its plain-data definition, with the stack's codecs and data types. Every MongoDB family rule is checked here, and each refusal comes back as data keyed by the part of the definition it is about, for the source to report in its own terms.
|
|
894
|
+
*/
|
|
895
|
+
function buildMongoContractFromDefinition(definition, codecLookup, dataTypes) {
|
|
896
|
+
const refusals = [];
|
|
897
|
+
const warnings = (definition.warnings ?? []).map((warning) => ({
|
|
898
|
+
kind: "source",
|
|
899
|
+
warning
|
|
900
|
+
}));
|
|
901
|
+
const namespaceId = definition.target.defaultNamespaceId;
|
|
902
|
+
const lookups = {
|
|
903
|
+
codecLookup,
|
|
904
|
+
dataTypeLookup: dataTypes.lookup
|
|
905
|
+
};
|
|
906
|
+
const generatorFunctions = assembleGeneratorFunctions([definition.target, ...Object.values(definition.extensions ?? {})]);
|
|
907
|
+
const models = inheritVariantCollections(definition.models, refusals);
|
|
908
|
+
const builtEnums = buildEnums(definition, codecLookup, refusals);
|
|
909
|
+
const variantValues = decidePolymorphism({
|
|
910
|
+
models,
|
|
911
|
+
enums: builtEnums,
|
|
912
|
+
codecLookup,
|
|
913
|
+
dataTypes,
|
|
914
|
+
refusals
|
|
915
|
+
});
|
|
916
|
+
const builtModels = buildModels({
|
|
917
|
+
models,
|
|
918
|
+
variantValues,
|
|
919
|
+
namespaceId,
|
|
920
|
+
refusals
|
|
921
|
+
});
|
|
922
|
+
const builtValueObjects = buildValueObjects(definition.valueObjects, namespaceId, refusals);
|
|
923
|
+
checkFields({
|
|
924
|
+
models,
|
|
925
|
+
valueObjects: definition.valueObjects,
|
|
926
|
+
declaredEnums: new Set(Object.keys(definition.enums ?? {})),
|
|
927
|
+
lookups,
|
|
928
|
+
refusals,
|
|
929
|
+
warnings
|
|
930
|
+
});
|
|
931
|
+
const collections = buildCollections({
|
|
932
|
+
models,
|
|
933
|
+
variantValues,
|
|
934
|
+
refusals
|
|
935
|
+
});
|
|
936
|
+
checkValueObjectExecutionDefaults(definition.valueObjects, refusals);
|
|
937
|
+
const executionDefaults = buildExecutionDefaults({
|
|
938
|
+
models,
|
|
939
|
+
generatorFunctions,
|
|
940
|
+
namespaceId,
|
|
941
|
+
refusals
|
|
942
|
+
});
|
|
943
|
+
if (refusals.length > 0) return notOk({
|
|
944
|
+
refusals,
|
|
945
|
+
warnings
|
|
946
|
+
});
|
|
947
|
+
const controlPolicies = {
|
|
948
|
+
collections: Object.fromEntries(Object.entries(collections).map(([name, collection]) => [name, collection.control])),
|
|
949
|
+
defaultControlPolicy: definition.defaultControlPolicy
|
|
950
|
+
};
|
|
951
|
+
const validatorRefusals = mongoContractRefusalsOf({
|
|
952
|
+
models: builtModels,
|
|
953
|
+
valueObjects: builtValueObjects,
|
|
954
|
+
enums: builtEnums,
|
|
955
|
+
lookups,
|
|
956
|
+
controlPolicies
|
|
957
|
+
}).filter((refusal) => definition.writesCollectionValidators || !isAboutEnumListing(refusal));
|
|
958
|
+
if (validatorRefusals.length > 0) return notOk({
|
|
959
|
+
refusals: validatorRefusals.map((refusal) => ({
|
|
960
|
+
kind: "validator",
|
|
961
|
+
key: validatorRefusalKey(refusal),
|
|
962
|
+
refusal
|
|
963
|
+
})),
|
|
964
|
+
warnings
|
|
965
|
+
});
|
|
966
|
+
const storageValueSets = mongoValueSetsOf(builtEnums);
|
|
967
|
+
const validators = definition.writesCollectionValidators ? deriveCollectionValidators({
|
|
968
|
+
models: builtModels,
|
|
969
|
+
lookups,
|
|
970
|
+
valueObjects: builtValueObjects,
|
|
971
|
+
valueSets: storageValueSets,
|
|
972
|
+
controlPolicies
|
|
973
|
+
}) : {};
|
|
974
|
+
const storage = buildMongoStorage({
|
|
975
|
+
namespaceId,
|
|
976
|
+
collections: Object.fromEntries(Object.entries(collections).map(([name, collection]) => [name, {
|
|
977
|
+
...collection,
|
|
978
|
+
...ifDefined("validator", validators[name])
|
|
979
|
+
}])),
|
|
980
|
+
valueSets: storageValueSets
|
|
981
|
+
});
|
|
982
|
+
const capabilities = {};
|
|
983
|
+
const execution = buildExecutionSection({
|
|
984
|
+
target: definition.target.targetId,
|
|
985
|
+
targetFamily: definition.target.familyId,
|
|
986
|
+
defaults: executionDefaults
|
|
987
|
+
});
|
|
988
|
+
return ok({
|
|
989
|
+
contract: {
|
|
990
|
+
target: definition.target.targetId,
|
|
991
|
+
targetFamily: definition.target.familyId,
|
|
992
|
+
...ifDefined("defaultControlPolicy", definition.defaultControlPolicy),
|
|
993
|
+
roots: buildRoots(definition, models, namespaceId),
|
|
994
|
+
domain: { namespaces: { [namespaceId]: {
|
|
995
|
+
models: builtModels,
|
|
996
|
+
...Object.keys(builtValueObjects).length > 0 ? { valueObjects: builtValueObjects } : {},
|
|
997
|
+
...Object.keys(builtEnums).length > 0 ? { enum: builtEnums } : {}
|
|
998
|
+
} } },
|
|
999
|
+
storage,
|
|
1000
|
+
capabilities,
|
|
1001
|
+
extensions: definition.extensions ?? {},
|
|
1002
|
+
profileHash: computeProfileHash({
|
|
1003
|
+
target: definition.target.targetId,
|
|
1004
|
+
targetFamily: definition.target.familyId,
|
|
1005
|
+
capabilities
|
|
1006
|
+
}),
|
|
1007
|
+
meta: {},
|
|
1008
|
+
...ifDefined("execution", execution)
|
|
1009
|
+
},
|
|
1010
|
+
warnings
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
function resolveModelName(value) {
|
|
1014
|
+
return typeof value === "string" ? value : value.__name;
|
|
1015
|
+
}
|
|
1016
|
+
function validateTargetPackRef(family, target) {
|
|
1017
|
+
if (family.familyId !== "mongo") throw contractError("CONTRACT.PACK_FAMILY_MISMATCH", `defineContract only accepts Mongo family packs. Received family "${family.familyId}".`, { meta: {
|
|
1018
|
+
packId: family.id,
|
|
1019
|
+
packFamilyId: family.familyId,
|
|
1020
|
+
contractFamilyId: "mongo"
|
|
1021
|
+
} });
|
|
1022
|
+
if (target.familyId !== family.familyId) throw contractError("CONTRACT.PACK_FAMILY_MISMATCH", `target pack "${target.id}" targets family "${target.familyId}" but contract family is "${family.familyId}".`, { meta: {
|
|
1023
|
+
packId: target.id,
|
|
1024
|
+
packFamilyId: target.familyId,
|
|
1025
|
+
contractFamilyId: family.familyId
|
|
1026
|
+
} });
|
|
1027
|
+
}
|
|
1028
|
+
function validateExtensionPackRefs(target, extensions) {
|
|
1029
|
+
for (const packRef of Object.values(extensions ?? {})) {
|
|
1030
|
+
if (packRef.kind !== "extension") throw contractError("CONTRACT.PACK_REF_INVALID", `defineContract only accepts extension pack refs in extensions. Received kind "${packRef.kind}".`, { meta: {
|
|
1031
|
+
packId: packRef.id,
|
|
1032
|
+
kind: packRef.kind
|
|
1033
|
+
} });
|
|
1034
|
+
if (packRef.familyId !== target.familyId) throw contractError("CONTRACT.PACK_FAMILY_MISMATCH", `extension pack "${packRef.id}" targets family "${packRef.familyId}" but contract target family is "${target.familyId}".`, { meta: {
|
|
1035
|
+
packId: packRef.id,
|
|
1036
|
+
packFamilyId: packRef.familyId,
|
|
1037
|
+
contractFamilyId: target.familyId
|
|
1038
|
+
} });
|
|
1039
|
+
if (packRef.targetId && packRef.targetId !== target.targetId) throw contractError("CONTRACT.PACK_TARGET_MISMATCH", `extension pack "${packRef.id}" targets "${packRef.targetId}" but contract target is "${target.targetId}".`, { meta: {
|
|
1040
|
+
packId: packRef.id,
|
|
1041
|
+
packTargetId: packRef.targetId,
|
|
1042
|
+
contractTargetId: target.targetId
|
|
1043
|
+
} });
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
function fieldNodes(fields) {
|
|
1047
|
+
return Object.entries(fields).map(([fieldName, builder]) => ({
|
|
1048
|
+
fieldName,
|
|
1049
|
+
...ifDefined("storedName", builder.__storedName),
|
|
1050
|
+
type: builder.__type,
|
|
1051
|
+
nullable: builder.__nullable,
|
|
1052
|
+
many: builder.__many,
|
|
1053
|
+
...ifDefined("enumName", builder.__enumHandle?.enumName),
|
|
1054
|
+
...ifDefined("executionDefaults", builder.__executionDefaults),
|
|
1055
|
+
...ifDefined("generatorCalls", builder.__generatorCalls),
|
|
1056
|
+
...ifDefined("presetContribution", builder.__presetContribution)
|
|
1057
|
+
}));
|
|
1058
|
+
}
|
|
1059
|
+
function relationNodes(builder) {
|
|
1060
|
+
return Object.entries(builder.__relations).map(([relationName, relation]) => {
|
|
1061
|
+
const common = {
|
|
1062
|
+
relationName,
|
|
1063
|
+
toModel: relation.__to,
|
|
1064
|
+
nullable: relation.__nullable
|
|
1065
|
+
};
|
|
1066
|
+
return relation.__on === void 0 ? {
|
|
1067
|
+
...common,
|
|
1068
|
+
cardinality: relation.__cardinality
|
|
1069
|
+
} : {
|
|
1070
|
+
...common,
|
|
1071
|
+
cardinality: relation.__cardinality,
|
|
1072
|
+
on: relation.__on
|
|
1073
|
+
};
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
function modelNode(builder, buildersByName) {
|
|
1077
|
+
const base = builder.__base;
|
|
1078
|
+
const variantValue = base === void 0 ? void 0 : buildersByName.get(base)?.__variants?.[builder.__name]?.value;
|
|
1079
|
+
return {
|
|
1080
|
+
modelName: builder.__name,
|
|
1081
|
+
...ifDefined("collection", builder.__collection),
|
|
1082
|
+
...ifDefined("owner", builder.__owner),
|
|
1083
|
+
...ifDefined("storageRelations", builder.__storageRelations),
|
|
1084
|
+
...ifDefined("controlPolicy", builder.__controlPolicy),
|
|
1085
|
+
...ifDefined("collectionOptions", builder.__collectionOptions),
|
|
1086
|
+
...ifDefined("indexes", builder.__indexes),
|
|
1087
|
+
fields: fieldNodes(builder.__fields),
|
|
1088
|
+
relations: relationNodes(builder),
|
|
1089
|
+
...builder.__discriminator === void 0 ? {} : { discriminator: {
|
|
1090
|
+
field: builder.__discriminator.field,
|
|
1091
|
+
listedVariants: Object.keys(builder.__variants ?? {})
|
|
1092
|
+
} },
|
|
1093
|
+
...base === void 0 ? {} : { base: {
|
|
1094
|
+
model: base,
|
|
1095
|
+
value: variantValue === void 0 ? void 0 : {
|
|
1096
|
+
kind: "string",
|
|
1097
|
+
text: variantValue
|
|
1098
|
+
}
|
|
1099
|
+
} }
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
function valueObjectNode(builder) {
|
|
1103
|
+
return {
|
|
1104
|
+
name: builder.__name,
|
|
1105
|
+
fields: fieldNodes(builder.__fields)
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
/** Turns `defineContract`'s builder objects into the plain-data definition the contract is built from. */
|
|
1109
|
+
function buildContractDefinition(input) {
|
|
1110
|
+
const { target } = input;
|
|
1111
|
+
validateTargetPackRef(input.family, target);
|
|
1112
|
+
validateExtensionPackRefs(target, input.extensions);
|
|
1113
|
+
const builders = Object.values(input.models ?? {});
|
|
1114
|
+
const buildersByName = /* @__PURE__ */ new Map();
|
|
1115
|
+
for (const builder of builders) if (!buildersByName.has(builder.__name)) buildersByName.set(builder.__name, builder);
|
|
1116
|
+
return {
|
|
1117
|
+
target,
|
|
1118
|
+
...ifDefined("extensions", input.extensions),
|
|
1119
|
+
...ifDefined("defaultControlPolicy", input.defaultControlPolicy),
|
|
1120
|
+
models: builders.map((builder) => modelNode(builder, buildersByName)),
|
|
1121
|
+
valueObjects: Object.values(input.valueObjects ?? {}).map(valueObjectNode),
|
|
1122
|
+
...ifDefined("enums", input.enums),
|
|
1123
|
+
...ifDefined("roots", input.roots === void 0 ? void 0 : Object.fromEntries(Object.entries(input.roots).map(([rootName, model]) => [rootName, resolveModelName(model)]))),
|
|
1124
|
+
writesCollectionValidators: true,
|
|
1125
|
+
warnings: void 0
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
const LIST_THE_CODEC_PACK = "List the pack that provides the codec in `extensions` in defineContract.";
|
|
1129
|
+
function codecRefusalError(subject, meta, codecId, refusal) {
|
|
1130
|
+
if (refusal.kind === "unknownCodec") return contractError("CONTRACT.CODEC_DESCRIPTOR_MISSING", `${subject} ${refusal.reason}`, {
|
|
1131
|
+
fix: LIST_THE_CODEC_PACK,
|
|
1132
|
+
meta: {
|
|
1133
|
+
...meta,
|
|
1134
|
+
codecId
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
return contractError("CONTRACT.PACK_CONTRIBUTION_INVALID", `${subject} ${refusal.reason}`, {
|
|
1138
|
+
fix: "Use a codec whose data type is a MongoDB data type, declared with mongoDataType.",
|
|
1139
|
+
meta: {
|
|
1140
|
+
...meta,
|
|
1141
|
+
codecId,
|
|
1142
|
+
dataType: refusal.dataType,
|
|
1143
|
+
reason: "data-type-not-mongo"
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
function validatorRefusalError(refusal) {
|
|
1148
|
+
switch (refusal.kind) {
|
|
1149
|
+
case "embeddedModelHasVariants": return contractError("CONTRACT.RELATION_INVALID", `Relation "${refusal.modelName}.${refusal.relationName}" ${refusal.reason}`, { meta: {
|
|
1150
|
+
modelName: refusal.modelName,
|
|
1151
|
+
relationName: refusal.relationName,
|
|
1152
|
+
reason: "embedded-model-has-variants"
|
|
1153
|
+
} });
|
|
1154
|
+
case "enumBsonType": return codecRefusalError(`enumType("${refusal.enumName}")`, { enumName: refusal.enumName }, refusal.codecId, refusal.refusal);
|
|
1155
|
+
case "enumNotListable": return contractError("CONTRACT.ENUM_INVALID", `enumType("${refusal.enumName}") codec "${refusal.codecId}" ${refusal.reason}`, { meta: {
|
|
1156
|
+
enumName: refusal.enumName,
|
|
1157
|
+
codecId: refusal.codecId,
|
|
1158
|
+
reason: "not-listable-by-validator"
|
|
1159
|
+
} });
|
|
1160
|
+
case "enumMemberNotListable": return contractError("CONTRACT.ENUM_INVALID", `enumType("${refusal.enumName}") member "${refusal.member}" ${refusal.reason}`, { meta: {
|
|
1161
|
+
enumName: refusal.enumName,
|
|
1162
|
+
member: refusal.member,
|
|
1163
|
+
codecId: refusal.codecId,
|
|
1164
|
+
reason: "member-not-listable-by-validator"
|
|
1165
|
+
} });
|
|
1166
|
+
case "fieldBsonType": {
|
|
1167
|
+
const { owner } = refusal;
|
|
1168
|
+
const label = owner.kind === "model" ? "Model" : "Value object";
|
|
1169
|
+
const ownerMeta = owner.kind === "model" ? { modelName: owner.name } : { valueObjectName: owner.name };
|
|
1170
|
+
return codecRefusalError(`${label} "${owner.name}" field "${refusal.fieldName}"`, {
|
|
1171
|
+
...ownerMeta,
|
|
1172
|
+
fieldName: refusal.fieldName
|
|
1173
|
+
}, refusal.codecId, refusal.refusal);
|
|
1174
|
+
}
|
|
1175
|
+
case "documentId": return contractError("CONTRACT.COLLECTION_INVALID", `Model "${refusal.modelName}" field "${refusal.fieldName}" ${refusal.reason}`, { meta: {
|
|
1176
|
+
modelName: refusal.modelName,
|
|
1177
|
+
fieldName: refusal.fieldName,
|
|
1178
|
+
collection: refusal.collection,
|
|
1179
|
+
codecId: refusal.codecId,
|
|
1180
|
+
bsonTypes: refusal.forbidden,
|
|
1181
|
+
reason: "document-id-bson-type"
|
|
1182
|
+
} });
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
function defaultError(key, reason, message) {
|
|
1186
|
+
return contractError("CONTRACT.DEFAULT_INVALID", `Field "${`${key.owner.name}.${key.field}`}" ${message}`, { meta: {
|
|
1187
|
+
modelName: key.owner.name,
|
|
1188
|
+
fieldName: key.field,
|
|
1189
|
+
reason
|
|
1190
|
+
} });
|
|
1191
|
+
}
|
|
1192
|
+
function ownerMetaOf(key) {
|
|
1193
|
+
return key.owner.kind === "model" ? {
|
|
1194
|
+
modelName: key.owner.name,
|
|
1195
|
+
fieldName: key.field
|
|
1196
|
+
} : {
|
|
1197
|
+
valueObjectName: key.owner.name,
|
|
1198
|
+
fieldName: key.field
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
function ownerLabelOf(key) {
|
|
1202
|
+
return `${key.owner.kind === "model" ? "Model" : "Value object"} "${key.owner.name}" field "${key.field}"`;
|
|
1203
|
+
}
|
|
1204
|
+
const INDEX_SHAPE_MESSAGES = {
|
|
1205
|
+
"unique-wildcard": "is unique and has a wildcard key; a unique index cannot use a wildcard.",
|
|
1206
|
+
"wildcard-with-type": "combines a wildcard key with a hashed, 2d or 2dsphere key, which MongoDB does not allow.",
|
|
1207
|
+
"hashed-several-fields": "is hashed on several fields; a hashed index has exactly one field.",
|
|
1208
|
+
"ttl-with-wildcard": "sets expireAfterSeconds on a wildcard index, which MongoDB does not allow.",
|
|
1209
|
+
"projection-without-wildcard": "sets wildcardProjection but has no wildcard key; a wildcard projection needs a wildcard index.",
|
|
1210
|
+
"collation-without-locale": "sets a collation without a locale; a collation needs a locale.",
|
|
1211
|
+
"second-text-index": "is a second text index in its collection; MongoDB allows one text index per collection."
|
|
1212
|
+
};
|
|
1213
|
+
function discriminatorFieldError(refusal) {
|
|
1214
|
+
const field = `Discriminator field "${refusal.key.model}.${refusal.fieldName}"`;
|
|
1215
|
+
const meta = {
|
|
1216
|
+
modelName: refusal.key.model,
|
|
1217
|
+
fieldName: refusal.fieldName
|
|
1218
|
+
};
|
|
1219
|
+
switch (refusal.refusal.kind) {
|
|
1220
|
+
case "optional": return contractError("CONTRACT.ARGUMENT_INVALID", `${field} must be required, but it is optional. Remove .optional().`, { meta: {
|
|
1221
|
+
...meta,
|
|
1222
|
+
reason: "discriminator-optional"
|
|
1223
|
+
} });
|
|
1224
|
+
case "list": return contractError("CONTRACT.ARGUMENT_INVALID", `${field} must hold one value, but it is a list. Remove .many().`, { meta: {
|
|
1225
|
+
...meta,
|
|
1226
|
+
reason: "discriminator-list"
|
|
1227
|
+
} });
|
|
1228
|
+
case "not-text": {
|
|
1229
|
+
const { dataType } = refusal.refusal;
|
|
1230
|
+
return contractError("CONTRACT.ARGUMENT_INVALID", `${field} must be text, such as field.string(), but its data type is ${dataType === void 0 ? "not a scalar" : `"${dataType}"`}.`, { meta: {
|
|
1231
|
+
...meta,
|
|
1232
|
+
dataType,
|
|
1233
|
+
reason: "discriminator-not-text"
|
|
1234
|
+
} });
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
/** The error `defineContract` throws for a refusal of the builder. */
|
|
1239
|
+
function refusalError(refusal) {
|
|
1240
|
+
switch (refusal.kind) {
|
|
1241
|
+
case "duplicateModel": return contractError("CONTRACT.NAME_DUPLICATE", `Duplicate model name "${refusal.key.model}" in defineContract().`, { meta: {
|
|
1242
|
+
kind: "model",
|
|
1243
|
+
name: refusal.key.model
|
|
1244
|
+
} });
|
|
1245
|
+
case "duplicateValueObject": return contractError("CONTRACT.NAME_DUPLICATE", `Duplicate value object name "${refusal.key.valueObject}" in defineContract().`, { meta: {
|
|
1246
|
+
kind: "valueObject",
|
|
1247
|
+
name: refusal.key.valueObject
|
|
1248
|
+
} });
|
|
1249
|
+
case "storedNameUnsupported": return contractError("CONTRACT.ARGUMENT_INVALID", `Model "${refusal.key.owner.name}" field "${refusal.key.field}" ${refusal.reason}. Give it another stored name.`, { meta: {
|
|
1250
|
+
modelName: refusal.key.owner.name,
|
|
1251
|
+
fieldName: refusal.key.field,
|
|
1252
|
+
reason: "stored-name-unsupported"
|
|
1253
|
+
} });
|
|
1254
|
+
case "storedNameDuplicate": return contractError("CONTRACT.NAME_DUPLICATE", `Model "${refusal.key.owner.name}" stores both "${refusal.firstField}" and "${refusal.key.field}" as "${refusal.storedName}".`, { meta: {
|
|
1255
|
+
kind: "field",
|
|
1256
|
+
name: refusal.storedName,
|
|
1257
|
+
first: refusal.firstField,
|
|
1258
|
+
second: refusal.key.field
|
|
1259
|
+
} });
|
|
1260
|
+
case "restatedBaseFieldDiffers": return contractError("CONTRACT.ARGUMENT_INVALID", `Variant model "${refusal.key.owner.name}" restates field "${refusal.key.field}" of its base "${refusal.baseModel}" with a different ${refusal.differences.map(differenceWording).join(" and ")}. A variant's documents are checked against the base's field, so restate it exactly or remove it from the variant.`, { meta: {
|
|
1261
|
+
modelName: refusal.key.owner.name,
|
|
1262
|
+
fieldName: refusal.key.field,
|
|
1263
|
+
baseModel: refusal.baseModel,
|
|
1264
|
+
differences: refusal.differences,
|
|
1265
|
+
reason: "restated-base-field-differs"
|
|
1266
|
+
} });
|
|
1267
|
+
case "storedNameSharedWithBase": return contractError("CONTRACT.NAME_DUPLICATE", `Variant model "${refusal.key.owner.name}" field "${refusal.key.field}" is stored as "${refusal.storedName}", which its base "${refusal.baseModel}" stores field "${refusal.baseField}" under. A variant's documents hold its base's fields too, so give the field another stored name.`, { meta: {
|
|
1268
|
+
kind: "field",
|
|
1269
|
+
name: refusal.storedName,
|
|
1270
|
+
first: `${refusal.baseModel}.${refusal.baseField}`,
|
|
1271
|
+
second: `${refusal.key.owner.name}.${refusal.key.field}`,
|
|
1272
|
+
reason: "stored-name-shared-with-base"
|
|
1273
|
+
} });
|
|
1274
|
+
case "valueObjectFieldStoredName": return contractError("CONTRACT.ARGUMENT_INVALID", `Value object "${refusal.key.owner.name}" field "${refusal.key.field}" sets .mongo({ field }), but a field of a value object is stored under its own name.`, { meta: {
|
|
1275
|
+
valueObjectName: refusal.key.owner.name,
|
|
1276
|
+
fieldName: refusal.key.field,
|
|
1277
|
+
reason: "value-object-field-stored-name"
|
|
1278
|
+
} });
|
|
1279
|
+
case "relationLocalFieldUnknown": {
|
|
1280
|
+
const { model, relation } = refusal.key;
|
|
1281
|
+
return contractError("CONTRACT.RELATION_INVALID", `Relation "${model}.${relation}" joins on local field "${refusal.fieldName}", which model "${model}" does not declare`, { meta: {
|
|
1282
|
+
modelName: model,
|
|
1283
|
+
relationName: relation,
|
|
1284
|
+
fieldName: refusal.fieldName,
|
|
1285
|
+
reason: "local-field-unknown"
|
|
1286
|
+
} });
|
|
1287
|
+
}
|
|
1288
|
+
case "relationNullability": {
|
|
1289
|
+
const { model, relation } = refusal.key;
|
|
1290
|
+
const location = `Relation "${model}.${relation}"`;
|
|
1291
|
+
return contractError("CONTRACT.RELATION_INVALID", !refusal.ownsForeignKey ? `${location} is required but does not own the foreign key, so nothing in storage guarantees the related document exists` : refusal.contradiction === "declared-optional" ? `${location} is optional but every local field it joins on is required` : `${location} is required but a local field it joins on is nullable`, { meta: {
|
|
1292
|
+
modelName: model,
|
|
1293
|
+
relationName: relation,
|
|
1294
|
+
reason: "to-one-nullability-mismatch"
|
|
1295
|
+
} });
|
|
1296
|
+
}
|
|
1297
|
+
case "collectionSettingWithoutCollection": {
|
|
1298
|
+
const modelName = refusal.key.model;
|
|
1299
|
+
const what = refusal.setting === "controlPolicy" ? "it" : "them";
|
|
1300
|
+
return contractError("CONTRACT.COLLECTION_INVALID", `Model "${modelName}" defines ${refusal.setting} but has no collection to attach ${what} to.`, { meta: {
|
|
1301
|
+
modelName,
|
|
1302
|
+
reason: `${refusal.setting}-without-collection`
|
|
1303
|
+
} });
|
|
1304
|
+
}
|
|
1305
|
+
case "collectionSettingTwice": return contractError("CONTRACT.COLLECTION_INVALID", `Collection "${refusal.collection}" has ${refusal.setting} declared by multiple models. Author ${refusal.setting} on a single model per collection.`, { meta: {
|
|
1306
|
+
modelName: refusal.key.model,
|
|
1307
|
+
collection: refusal.collection,
|
|
1308
|
+
reason: `${refusal.setting}-declared-by-multiple-models`
|
|
1309
|
+
} });
|
|
1310
|
+
case "variantCollectionDiffers": return contractError("CONTRACT.COLLECTION_INVALID", `Variant model "${refusal.key.model}" is stored in collection "${refusal.collection}", but its base "${refusal.baseModel}" is stored in "${refusal.baseCollection}". A variant is stored in its base's collection; leave its collection out.`, { meta: {
|
|
1311
|
+
modelName: refusal.key.model,
|
|
1312
|
+
collection: refusal.collection,
|
|
1313
|
+
baseModel: refusal.baseModel,
|
|
1314
|
+
baseCollection: refusal.baseCollection,
|
|
1315
|
+
reason: "variant-collection-differs"
|
|
1316
|
+
} });
|
|
1317
|
+
case "indexFieldUnknown": return contractError("CONTRACT.FIELD_UNKNOWN", `Model "${refusal.key.model}" index ${refusal.signature} references unknown field "${refusal.fieldName}".`, { meta: {
|
|
1318
|
+
modelName: refusal.key.model,
|
|
1319
|
+
fieldName: refusal.fieldName,
|
|
1320
|
+
indexSignature: refusal.signature
|
|
1321
|
+
} });
|
|
1322
|
+
case "indexShape": return contractError("CONTRACT.INDEX_INVALID", `Model "${refusal.key.model}" index ${refusal.signature} ${INDEX_SHAPE_MESSAGES[refusal.rule]}`, { meta: {
|
|
1323
|
+
modelName: refusal.key.model,
|
|
1324
|
+
indexSignature: refusal.signature,
|
|
1325
|
+
reason: refusal.rule
|
|
1326
|
+
} });
|
|
1327
|
+
case "indexScopeConflict": return contractError("CONTRACT.INDEX_INVALID", `Variant model "${refusal.key.model}" index ${refusal.signature} conflicts with discriminator scope: ${refusal.reason}`, { meta: {
|
|
1328
|
+
variantName: refusal.key.model,
|
|
1329
|
+
indexLabel: refusal.signature,
|
|
1330
|
+
reason: refusal.reason
|
|
1331
|
+
} });
|
|
1332
|
+
case "indexDuplicate": return contractError("CONTRACT.NAME_DUPLICATE", `Collection "${refusal.collection}" defines duplicate index ${refusal.signature}. First declared on model "${refusal.firstModel}" and duplicated on model "${refusal.key.model}".`, { meta: {
|
|
1333
|
+
kind: "index",
|
|
1334
|
+
name: refusal.signature,
|
|
1335
|
+
collection: refusal.collection,
|
|
1336
|
+
first: refusal.firstModel,
|
|
1337
|
+
second: refusal.key.model
|
|
1338
|
+
} });
|
|
1339
|
+
case "generatedOnValueObject": return defaultError(refusal.key, "executionDefaults-on-value-object", "has executionDefaults, but it belongs to a value object. Generated values are only supported on model fields.");
|
|
1340
|
+
case "generatorUnknown": {
|
|
1341
|
+
const { owner, field } = refusal.key;
|
|
1342
|
+
return contractError("CONTRACT.DEFAULT_INVALID", `Field "${owner.name}.${field}": no pack in the contract declares the generator function "${refusal.functionName}".`, { meta: {
|
|
1343
|
+
modelName: owner.name,
|
|
1344
|
+
fieldName: field,
|
|
1345
|
+
codecId: refusal.codecId,
|
|
1346
|
+
function: refusal.functionName,
|
|
1347
|
+
reason: "generator-unknown"
|
|
1348
|
+
} });
|
|
1349
|
+
}
|
|
1350
|
+
case "generatorNotApplicable": {
|
|
1351
|
+
const { owner, field } = refusal.key;
|
|
1352
|
+
const subject = `Field "${owner.name}.${field}"`;
|
|
1353
|
+
return contractError("CONTRACT.DEFAULT_INVALID", refusal.message !== void 0 ? `${subject}: ${refusal.message}` : `${subject}: ${refusal.functionName}() does not apply to ${refusal.codecId === void 0 ? "the field's type" : `codec "${refusal.codecId}"`}.`, { meta: {
|
|
1354
|
+
modelName: owner.name,
|
|
1355
|
+
fieldName: field,
|
|
1356
|
+
codecId: refusal.codecId,
|
|
1357
|
+
function: refusal.functionName,
|
|
1358
|
+
reason: "generator-not-applicable"
|
|
1359
|
+
} });
|
|
1360
|
+
}
|
|
1361
|
+
case "generatorPhaseSetTwice": return contractError("CONTRACT.DEFAULT_INVALID", `.${refusal.phase}() is called on a field that already generates a value ${refusal.phase === "onCreate" ? "on create" : "on update"}. Set each phase once, and do not combine .${refusal.phase}() with a field preset, type alias or alias constant that sets it.`, { meta: {
|
|
1362
|
+
modelName: refusal.key.owner.name,
|
|
1363
|
+
fieldName: refusal.key.field,
|
|
1364
|
+
reason: "generator-phase-set-twice",
|
|
1365
|
+
phase: refusal.phase
|
|
1366
|
+
} });
|
|
1367
|
+
case "generatedList": return defaultError(refusal.key, "many-with-executionDefaults", "generates a value on write, so it cannot be a list; remove .many().");
|
|
1368
|
+
case "generatedOnVariant": return defaultError(refusal.key, "executionDefaults-on-variant", `has executionDefaults, but "${refusal.key.owner.name}" is a variant of "${refusal.baseModel}". Execution defaults apply to every document in collection "${refusal.collection}", so declare them on the base model.`);
|
|
1369
|
+
case "generatedWithoutCollection": return defaultError(refusal.key, "executionDefaults-without-collection", "has executionDefaults, but its model has no collection. Generated values are only supported on fields of a model stored in a collection.");
|
|
1370
|
+
case "generatedConflict": return defaultError(refusal.key, "executionDefaults-conflict", `has different executionDefaults than "${refusal.otherModel}.${refusal.otherField}", which is stored in the same collection "${refusal.collection}". Use the same executionDefaults on both models.`);
|
|
1371
|
+
case "typeParamsInvalid": return structuredError("CONTRACT.TYPE_PARAMS_INVALID", refusal.problem.message, { meta: {
|
|
1372
|
+
dataType: refusal.problem.dataType,
|
|
1373
|
+
parameters: refusal.problem.parameters,
|
|
1374
|
+
...ownerMetaOf(refusal.key)
|
|
1375
|
+
} });
|
|
1376
|
+
case "enumKeyMismatch": return contractError("CONTRACT.ENUM_INVALID", `enum declaration key "${refusal.key.enumName}" must match enumType name "${refusal.enumName}". Aliases are not supported.`, { meta: {
|
|
1377
|
+
enumName: refusal.enumName,
|
|
1378
|
+
declaredKey: refusal.key.enumName,
|
|
1379
|
+
reason: "declaration-key-mismatch"
|
|
1380
|
+
} });
|
|
1381
|
+
case "enumMemberProblem": return enumMemberError({
|
|
1382
|
+
enumName: refusal.enumName,
|
|
1383
|
+
codecId: refusal.codecId
|
|
1384
|
+
}, refusal.problem, "the field");
|
|
1385
|
+
case "enumUnknown": return contractError("CONTRACT.ENUM_UNKNOWN", `${ownerLabelOf(refusal.key)} references enum "${refusal.enumName}" which is not declared in defineContract({ enums: { ... } }).`, { meta: {
|
|
1386
|
+
...ownerMetaOf(refusal.key),
|
|
1387
|
+
enumName: refusal.enumName
|
|
1388
|
+
} });
|
|
1389
|
+
case "discriminatorValueDuplicate": return contractError("CONTRACT.ARGUMENT_INVALID", `Discriminator value "${refusal.value}" is used by both "${refusal.firstVariant}" and "${refusal.key.variant}" on base model "${refusal.baseModel}".`, { meta: {
|
|
1390
|
+
modelName: refusal.baseModel,
|
|
1391
|
+
value: refusal.value,
|
|
1392
|
+
variants: [refusal.firstVariant, refusal.key.variant],
|
|
1393
|
+
reason: "duplicate-discriminator-value"
|
|
1394
|
+
} });
|
|
1395
|
+
case "discriminatorFieldUnknown": return contractError("CONTRACT.FIELD_UNKNOWN", `Model "${refusal.key.model}" names "${refusal.fieldName}" as its discriminator, but declares no field "${refusal.fieldName}".`, { meta: {
|
|
1396
|
+
modelName: refusal.key.model,
|
|
1397
|
+
fieldName: refusal.fieldName,
|
|
1398
|
+
reason: "discriminator-field-unknown"
|
|
1399
|
+
} });
|
|
1400
|
+
case "discriminatorField": return discriminatorFieldError(refusal);
|
|
1401
|
+
case "discriminatorValue": {
|
|
1402
|
+
const variant = refusal.key.variant;
|
|
1403
|
+
const meta = {
|
|
1404
|
+
modelName: variant,
|
|
1405
|
+
baseModel: refusal.baseModel,
|
|
1406
|
+
fieldName: refusal.fieldName
|
|
1407
|
+
};
|
|
1408
|
+
if (refusal.refusal.kind === "unreadable") return contractError("CONTRACT.ARGUMENT_INVALID", `Variant model "${variant}" has a discriminator value that discriminator field "${refusal.baseModel}.${refusal.fieldName}" cannot hold: ${refusal.refusal.refusal.message}`, { meta: {
|
|
1409
|
+
...meta,
|
|
1410
|
+
reason: "discriminator-value-unreadable"
|
|
1411
|
+
} });
|
|
1412
|
+
return contractError("CONTRACT.ARGUMENT_INVALID", `Variant model "${variant}" has discriminator value ${JSON.stringify(refusal.refusal.stored)}, which is not a member of enum "${refusal.enumName}", the type of discriminator field "${refusal.baseModel}.${refusal.fieldName}". Use one of: ${refusal.refusal.members.map((member) => JSON.stringify(member)).join(", ")}.`, { meta: {
|
|
1413
|
+
...meta,
|
|
1414
|
+
reason: "discriminator-value-not-a-member"
|
|
1415
|
+
} });
|
|
1416
|
+
}
|
|
1417
|
+
case "discriminatorAndBase": return contractError("CONTRACT.ARGUMENT_INVALID", `Model "${refusal.key.model}" has both a discriminator and a base. A model is either a base with variants or a variant of one base.`, { meta: {
|
|
1418
|
+
modelName: refusal.key.model,
|
|
1419
|
+
reason: "discriminator-and-base"
|
|
1420
|
+
} });
|
|
1421
|
+
case "discriminatorWithoutVariants": return contractError("CONTRACT.ARGUMENT_INVALID", `Model "${refusal.key.model}" has a discriminator, but no model names it as its base.`, { meta: {
|
|
1422
|
+
modelName: refusal.key.model,
|
|
1423
|
+
reason: "discriminator-without-variants"
|
|
1424
|
+
} });
|
|
1425
|
+
case "listedVariantMissing": return contractError("CONTRACT.ARGUMENT_INVALID", `Model "${refusal.key.model}" lists variant "${refusal.variant}", but no model "${refusal.variant}" names "${refusal.key.model}" as its base.`, { meta: {
|
|
1426
|
+
modelName: refusal.key.model,
|
|
1427
|
+
variant: refusal.variant,
|
|
1428
|
+
reason: "listed-variant-missing"
|
|
1429
|
+
} });
|
|
1430
|
+
case "baseWithoutDiscriminator": return contractError("CONTRACT.ARGUMENT_INVALID", `Model "${refusal.key.variant}" names "${refusal.baseModel}" as its base, but "${refusal.baseModel}" has no discriminator.`, { meta: {
|
|
1431
|
+
modelName: refusal.key.variant,
|
|
1432
|
+
baseModel: refusal.baseModel,
|
|
1433
|
+
reason: "base-without-discriminator"
|
|
1434
|
+
} });
|
|
1435
|
+
case "baseUnknown": return contractError("CONTRACT.ARGUMENT_INVALID", `Model "${refusal.key.variant}" names "${refusal.baseModel}" as its base, but the contract has no model "${refusal.baseModel}".`, { meta: {
|
|
1436
|
+
modelName: refusal.key.variant,
|
|
1437
|
+
baseModel: refusal.baseModel,
|
|
1438
|
+
reason: "base-unknown"
|
|
1439
|
+
} });
|
|
1440
|
+
case "variantWithoutValue": return contractError("CONTRACT.ARGUMENT_INVALID", `Model "${refusal.key.variant}" names "${refusal.baseModel}" as its base, but "${refusal.baseModel}" lists no discriminator value for it.`, { meta: {
|
|
1441
|
+
modelName: refusal.key.variant,
|
|
1442
|
+
baseModel: refusal.baseModel,
|
|
1443
|
+
reason: "variant-without-value"
|
|
1444
|
+
} });
|
|
1445
|
+
case "validator": return validatorRefusalError(refusal.refusal);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
/** A builder warning as `defineContract` reports it. */
|
|
1449
|
+
function authoringWarningOf(warning) {
|
|
1450
|
+
if (warning.kind === "source") return warning.warning;
|
|
1451
|
+
const item = `field "${warning.key.owner.name}.${warning.key.field}"`;
|
|
1452
|
+
return {
|
|
1453
|
+
code: "PN_PRESET_WITHOUT_EFFECT",
|
|
1454
|
+
message: `Field "${warning.key.owner.name}.${warning.key.field}" uses ${warning.preset}(), which generates no value, so the field is stored like any field of codec "${warning.codecId}".`,
|
|
1455
|
+
item,
|
|
1456
|
+
summary: "fields use a preset that generates no value"
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
function differenceWording(difference) {
|
|
1460
|
+
switch (difference) {
|
|
1461
|
+
case "storedName": return "stored name";
|
|
1462
|
+
case "type": return "type";
|
|
1463
|
+
case "nullable": return "nullability";
|
|
1464
|
+
case "many": return "list shape";
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
const MONGO_RESERVED_HELPER_KEYS = [
|
|
1468
|
+
"field",
|
|
1469
|
+
"gen",
|
|
1470
|
+
"index",
|
|
1471
|
+
"model",
|
|
1472
|
+
"rel",
|
|
1473
|
+
"valueObject"
|
|
1474
|
+
];
|
|
1475
|
+
function buildFieldPreset(helperPath, descriptor, args) {
|
|
1476
|
+
validateAuthoringHelperArguments(helperPath, descriptor.args, args);
|
|
1477
|
+
const preset = instantiateAuthoringFieldPreset(descriptor, args);
|
|
1478
|
+
const unsupported = [
|
|
1479
|
+
...preset.default !== void 0 ? ["default"] : [],
|
|
1480
|
+
...preset.id ? ["id"] : [],
|
|
1481
|
+
...preset.unique ? ["unique"] : []
|
|
1482
|
+
];
|
|
1483
|
+
if (unsupported.length > 0) throw contractError("CONTRACT.PACK_CONTRIBUTION_INVALID", `Field preset "${helperPath}" contributes ${unsupported.join(", ")}, which Mongo does not support. Mongo supports presets that set a codec and execution defaults only.`, { meta: {
|
|
1484
|
+
helperPath,
|
|
1485
|
+
reason: "preset-contribution-unsupported",
|
|
1486
|
+
contribution: unsupported
|
|
1487
|
+
} });
|
|
1488
|
+
return createFieldBuilder({
|
|
1489
|
+
type: {
|
|
1490
|
+
kind: "scalar",
|
|
1491
|
+
codecId: preset.descriptor.codecId,
|
|
1492
|
+
...normalizeOptionalTypeParams(preset.descriptor.typeParams)
|
|
1493
|
+
},
|
|
1494
|
+
nullable: preset.nullable,
|
|
1495
|
+
many: false
|
|
1496
|
+
}, void 0, preset.executionDefaults, void 0, void 0, {
|
|
1497
|
+
preset: helperPath,
|
|
1498
|
+
executionDefaults: preset.executionDefaults
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
function composeMongoFieldHelpers(fieldNamespace) {
|
|
1502
|
+
const presetHelpers = createFieldHelpersFromNamespace(fieldNamespace, ({ helperPath, descriptor }) => (...args) => buildFieldPreset(helperPath, descriptor, args));
|
|
1503
|
+
const collisions = Object.keys(presetHelpers).filter((name) => Object.hasOwn(field, name));
|
|
1504
|
+
if (collisions.length > 0) throw contractError("CONTRACT.PACK_CONTRIBUTION_INVALID", `Pack-contributed field preset(s) ${collisions.map((c) => `"${c}"`).join(", ")} collide with the built-in Mongo field helper(s) of the same name.`, { meta: {
|
|
1505
|
+
reason: "core-field-helper-collision",
|
|
1506
|
+
contribution: collisions
|
|
1507
|
+
} });
|
|
1508
|
+
return {
|
|
1509
|
+
...field,
|
|
1510
|
+
...presetHelpers
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
function stackLookupsOf(components) {
|
|
1514
|
+
const codecLookup = extractCodecLookup(components);
|
|
1515
|
+
assertTypeAliasesValid(composePackAuthoringNamespace(components, "type"), {
|
|
1516
|
+
generatorFunctions: assembleGeneratorFunctions(components),
|
|
1517
|
+
paramsSchemaOf: (codecId) => codecLookup.descriptorFor(codecId)?.paramsSchema,
|
|
1518
|
+
fieldLength: () => void 0
|
|
1519
|
+
});
|
|
1520
|
+
const { support } = assembleDataTypeSupport(components, (codecId) => codecLookup.descriptorFor(codecId)?.dataType);
|
|
1521
|
+
return {
|
|
1522
|
+
codecLookup,
|
|
1523
|
+
dataTypes: support
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
function composeMongoAuthoringHelpers(family, target, extensions, lookups) {
|
|
1527
|
+
const components = [
|
|
1528
|
+
family,
|
|
1529
|
+
target,
|
|
1530
|
+
...Object.values(extensions ?? {})
|
|
1531
|
+
];
|
|
1532
|
+
const entityNamespace = composePackAuthoringNamespace(components, "entityTypes");
|
|
1533
|
+
const fieldNamespace = composePackAuthoringNamespace(components, "field");
|
|
1534
|
+
assertNoCrossRegistryCollisions({}, fieldNamespace, entityNamespace);
|
|
1535
|
+
const collisions = Object.keys(entityNamespace).filter((name) => MONGO_RESERVED_HELPER_KEYS.includes(name));
|
|
1536
|
+
if (collisions.length > 0) throw contractError("CONTRACT.PACK_CONTRIBUTION_INVALID", `Pack-contributed entity type(s) ${collisions.map((c) => `"${c}"`).join(", ")} collide with the reserved built-in helper key(s) on the Mongo composed helpers surface. Reserved keys: ${MONGO_RESERVED_HELPER_KEYS.map((k) => `"${k}"`).join(", ")}.`, { meta: {
|
|
1537
|
+
reason: "reserved-helper-key-collision",
|
|
1538
|
+
contribution: collisions
|
|
1539
|
+
} });
|
|
1540
|
+
return blindCast({
|
|
1541
|
+
...createEntityHelpersFromNamespace(entityNamespace, { ctx: {
|
|
1542
|
+
family: family.familyId,
|
|
1543
|
+
target: target.targetId,
|
|
1544
|
+
codecLookup: lookups.codecLookup,
|
|
1545
|
+
dataTypes: lookups.dataTypes
|
|
1546
|
+
} }),
|
|
1547
|
+
field: composeMongoFieldHelpers(fieldNamespace),
|
|
1548
|
+
gen: createGeneratorHelpers(components),
|
|
1549
|
+
index,
|
|
1550
|
+
model,
|
|
1551
|
+
rel,
|
|
1552
|
+
valueObject
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
function createFieldBuilder(spec, enumHandle, executionDefaults, storedName, generatorCalls, presetContribution) {
|
|
1556
|
+
function many(options) {
|
|
1557
|
+
return createFieldBuilder({
|
|
1558
|
+
type: spec.type,
|
|
1559
|
+
nullable: spec.nullable,
|
|
1560
|
+
many: { elementNullable: options?.elementsNullable ?? false }
|
|
1561
|
+
}, enumHandle, executionDefaults, storedName, generatorCalls, presetContribution);
|
|
1562
|
+
}
|
|
1563
|
+
function withGeneratorCall(phase, call) {
|
|
1564
|
+
if (generatorCalls?.[phase] !== void 0 || executionDefaults?.[phase] !== void 0) throw contractError("CONTRACT.DEFAULT_INVALID", `.${phase}() is called on a field that already generates a value ${phase === "onCreate" ? "on create" : "on update"}. Set each phase once, and do not combine .${phase}() with a field preset, type alias or alias constant that sets it.`, { meta: {
|
|
1565
|
+
reason: "generator-phase-set-twice",
|
|
1566
|
+
phase
|
|
1567
|
+
} });
|
|
1568
|
+
return createFieldBuilder(spec, enumHandle, blindCast(executionDefaults), storedName, {
|
|
1569
|
+
...generatorCalls,
|
|
1570
|
+
[phase]: call
|
|
1571
|
+
}, presetContribution);
|
|
1572
|
+
}
|
|
1573
|
+
return {
|
|
1574
|
+
__kind: "field",
|
|
1575
|
+
...ifDefined("__executionDefaults", executionDefaults),
|
|
1576
|
+
...ifDefined("__generatorCalls", generatorCalls),
|
|
1577
|
+
...ifDefined("__presetContribution", presetContribution),
|
|
1578
|
+
onCreate: (call) => withGeneratorCall("onCreate", call),
|
|
1579
|
+
onUpdate: (call) => withGeneratorCall("onUpdate", call),
|
|
1580
|
+
__type: spec.type,
|
|
1581
|
+
__nullable: spec.nullable,
|
|
1582
|
+
__many: spec.many,
|
|
1583
|
+
__enumHandle: blindCast(enumHandle),
|
|
1584
|
+
__storedName: blindCast(storedName),
|
|
1585
|
+
optional: () => createFieldBuilder({
|
|
1586
|
+
type: spec.type,
|
|
1587
|
+
nullable: true,
|
|
1588
|
+
many: spec.many
|
|
1589
|
+
}, enumHandle, executionDefaults, storedName, generatorCalls, presetContribution),
|
|
1590
|
+
many: blindCast(many),
|
|
1591
|
+
mongo: ({ field }) => createFieldBuilder(spec, enumHandle, executionDefaults, field, generatorCalls, presetContribution)
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
function normalizeOptionalTypeParams(typeParams) {
|
|
1595
|
+
if (!typeParams) return {};
|
|
1596
|
+
return { typeParams };
|
|
1597
|
+
}
|
|
1598
|
+
function createScalarFieldBuilder(codecId, options) {
|
|
1599
|
+
return createFieldBuilder({
|
|
1600
|
+
type: blindCast({
|
|
1601
|
+
kind: "scalar",
|
|
1602
|
+
codecId,
|
|
1603
|
+
...normalizeOptionalTypeParams(options?.typeParams)
|
|
1604
|
+
}),
|
|
1605
|
+
nullable: false,
|
|
1606
|
+
many: false
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
function vectorField(options) {
|
|
1610
|
+
return createScalarFieldBuilder("mongo/vector@1", options);
|
|
1611
|
+
}
|
|
1612
|
+
const field = {
|
|
1613
|
+
scalar: createScalarFieldBuilder,
|
|
1614
|
+
objectId() {
|
|
1615
|
+
return createScalarFieldBuilder("mongo/objectId@1");
|
|
1616
|
+
},
|
|
1617
|
+
string() {
|
|
1618
|
+
return createScalarFieldBuilder("mongo/string@1");
|
|
1619
|
+
},
|
|
1620
|
+
double() {
|
|
1621
|
+
return createScalarFieldBuilder("mongo/double@1");
|
|
1622
|
+
},
|
|
1623
|
+
int32() {
|
|
1624
|
+
return createScalarFieldBuilder("mongo/int32@1");
|
|
1625
|
+
},
|
|
1626
|
+
bool() {
|
|
1627
|
+
return createScalarFieldBuilder("mongo/bool@1");
|
|
1628
|
+
},
|
|
1629
|
+
date() {
|
|
1630
|
+
return createScalarFieldBuilder("mongo/date@1");
|
|
1631
|
+
},
|
|
1632
|
+
int64() {
|
|
1633
|
+
return createScalarFieldBuilder("mongo/int64@1");
|
|
1634
|
+
},
|
|
1635
|
+
int64Number() {
|
|
1636
|
+
return createScalarFieldBuilder("mongo/int64Number@1");
|
|
1637
|
+
},
|
|
1638
|
+
decimal128() {
|
|
1639
|
+
return createScalarFieldBuilder("mongo/decimal128@1");
|
|
1640
|
+
},
|
|
1641
|
+
binary() {
|
|
1642
|
+
return createScalarFieldBuilder("mongo/binary@1");
|
|
1643
|
+
},
|
|
1644
|
+
json() {
|
|
1645
|
+
return createScalarFieldBuilder("mongo/json@1");
|
|
1646
|
+
},
|
|
1647
|
+
bson() {
|
|
1648
|
+
return createScalarFieldBuilder("mongo/bson@1");
|
|
1649
|
+
},
|
|
1650
|
+
vector: vectorField,
|
|
1651
|
+
valueObject(valueObjectName) {
|
|
1652
|
+
return createFieldBuilder({
|
|
1653
|
+
type: blindCast({
|
|
1654
|
+
kind: "valueObject",
|
|
1655
|
+
name: resolveValueObjectName(valueObjectName)
|
|
1656
|
+
}),
|
|
1657
|
+
nullable: false,
|
|
1658
|
+
many: false
|
|
1659
|
+
});
|
|
1660
|
+
},
|
|
1661
|
+
namedType(handle) {
|
|
1662
|
+
return createFieldBuilder({
|
|
1663
|
+
type: blindCast({
|
|
1664
|
+
kind: "scalar",
|
|
1665
|
+
codecId: handle.codecId
|
|
1666
|
+
}),
|
|
1667
|
+
nullable: false,
|
|
1668
|
+
many: false
|
|
1669
|
+
}, handle);
|
|
1670
|
+
}
|
|
1671
|
+
};
|
|
1672
|
+
function index(fields, options) {
|
|
1673
|
+
return {
|
|
1674
|
+
fields,
|
|
1675
|
+
...options ? { options } : {}
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
function createFieldReference(modelName, fieldName) {
|
|
1679
|
+
return {
|
|
1680
|
+
__kind: "fieldRef",
|
|
1681
|
+
modelName,
|
|
1682
|
+
fieldName
|
|
1683
|
+
};
|
|
1684
|
+
}
|
|
1685
|
+
function isFieldReference(value) {
|
|
1686
|
+
return typeof value === "object" && value !== null && "__kind" in value && value.__kind === "fieldRef";
|
|
1687
|
+
}
|
|
1688
|
+
function resolveValueObjectName(value) {
|
|
1689
|
+
return typeof value === "string" ? value : value.__name;
|
|
1690
|
+
}
|
|
1691
|
+
function normalizeStringList(value) {
|
|
1692
|
+
return typeof value === "string" ? [value] : [...value];
|
|
1693
|
+
}
|
|
1694
|
+
function normalizeTargetField(targetModelName, value) {
|
|
1695
|
+
if (!isFieldReference(value)) return value;
|
|
1696
|
+
if (value.modelName !== targetModelName) throw contractError("CONTRACT.RELATION_INVALID", `Relation target "${targetModelName}" cannot reference field "${value.modelName}.${value.fieldName}".`, { meta: {
|
|
1697
|
+
modelName: targetModelName,
|
|
1698
|
+
reason: "cross-model-field-reference",
|
|
1699
|
+
referencedModel: value.modelName,
|
|
1700
|
+
referencedField: value.fieldName
|
|
1701
|
+
} });
|
|
1702
|
+
return value.fieldName;
|
|
1703
|
+
}
|
|
1704
|
+
function normalizeTargetFields(targetModelName, value) {
|
|
1705
|
+
if (typeof value === "string") return [value];
|
|
1706
|
+
if (isFieldReference(value)) return [normalizeTargetField(targetModelName, value)];
|
|
1707
|
+
return value.map((entry) => normalizeTargetField(targetModelName, entry));
|
|
1708
|
+
}
|
|
1709
|
+
function createRelationBuilder(spec) {
|
|
1710
|
+
return {
|
|
1711
|
+
__kind: "relation",
|
|
1712
|
+
__to: spec.to,
|
|
1713
|
+
__cardinality: spec.cardinality,
|
|
1714
|
+
__on: spec.on,
|
|
1715
|
+
__nullable: spec.nullable
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
function createReferenceRelationBuilder(target, cardinality, options, nullable) {
|
|
1719
|
+
const targetModelName = resolveModelName(target);
|
|
1720
|
+
return createRelationBuilder({
|
|
1721
|
+
to: blindCast(targetModelName),
|
|
1722
|
+
cardinality,
|
|
1723
|
+
on: {
|
|
1724
|
+
localFields: blindCast(normalizeStringList(options.from)),
|
|
1725
|
+
targetFields: blindCast(normalizeTargetFields(targetModelName, options.to))
|
|
1726
|
+
},
|
|
1727
|
+
nullable
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
function createEmbedRelationBuilder(target, cardinality) {
|
|
1731
|
+
return createRelationBuilder({
|
|
1732
|
+
to: blindCast(resolveModelName(target)),
|
|
1733
|
+
cardinality,
|
|
1734
|
+
on: void 0,
|
|
1735
|
+
nullable: void 0
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1738
|
+
function hasOne(target, options) {
|
|
1739
|
+
if (!options) return createEmbedRelationBuilder(target, "1:1");
|
|
1740
|
+
return createReferenceRelationBuilder(target, "1:1", options, true);
|
|
1741
|
+
}
|
|
1742
|
+
function hasMany(target, options) {
|
|
1743
|
+
if (!options) return createEmbedRelationBuilder(target, "1:N");
|
|
1744
|
+
return createReferenceRelationBuilder(target, "1:N", options, void 0);
|
|
1745
|
+
}
|
|
1746
|
+
function belongsTo(target, options) {
|
|
1747
|
+
return createReferenceRelationBuilder(target, "N:1", options, options.optional);
|
|
1748
|
+
}
|
|
1749
|
+
const rel = {
|
|
1750
|
+
belongsTo,
|
|
1751
|
+
hasMany,
|
|
1752
|
+
hasOne
|
|
1753
|
+
};
|
|
1754
|
+
function valueObject(name, input) {
|
|
1755
|
+
return {
|
|
1756
|
+
__kind: "valueObject",
|
|
1757
|
+
__name: name,
|
|
1758
|
+
__fields: input.fields
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
function model(name, input) {
|
|
1762
|
+
return {
|
|
1763
|
+
__kind: "model",
|
|
1764
|
+
__name: name,
|
|
1765
|
+
__fields: input.fields,
|
|
1766
|
+
__relations: blindCast(input.relations ?? {}),
|
|
1767
|
+
__indexes: input.indexes,
|
|
1768
|
+
__collectionOptions: input.collectionOptions,
|
|
1769
|
+
__controlPolicy: input.controlPolicy,
|
|
1770
|
+
__collection: blindCast(input.collection),
|
|
1771
|
+
__owner: blindCast(input.owner ? resolveModelName(input.owner) : void 0),
|
|
1772
|
+
__base: blindCast(input.base ? resolveModelName(input.base) : void 0),
|
|
1773
|
+
__storageRelations: blindCast(input.storageRelations),
|
|
1774
|
+
__discriminator: blindCast(input.discriminator ? { field: input.discriminator.field } : void 0),
|
|
1775
|
+
__variants: blindCast(input.discriminator?.variants),
|
|
1776
|
+
ref(fieldName) {
|
|
1777
|
+
return createFieldReference(name, fieldName);
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
function isContractScaffold(value) {
|
|
1782
|
+
if (typeof value !== "object" || value === null) return false;
|
|
1783
|
+
return "family" in value && "target" in value;
|
|
1784
|
+
}
|
|
1785
|
+
function buildContractFromDsl(input, lookups) {
|
|
1786
|
+
const definition = buildContractDefinition(input);
|
|
1787
|
+
const { codecLookup, dataTypes } = lookups ?? stackLookupsOf([
|
|
1788
|
+
input.family,
|
|
1789
|
+
input.target,
|
|
1790
|
+
...Object.values(input.extensions ?? {})
|
|
1791
|
+
]);
|
|
1792
|
+
const built = buildMongoContractFromDefinition(definition, codecLookup, dataTypes);
|
|
1793
|
+
flushAuthoringWarnings((built.ok ? built.value.warnings : built.failure.warnings).map(authoringWarningOf));
|
|
1794
|
+
if (!built.ok) {
|
|
1795
|
+
const [first] = built.failure.refusals;
|
|
1796
|
+
assertDefined(first, "a refused build has at least one refusal");
|
|
1797
|
+
throw refusalError(first);
|
|
1798
|
+
}
|
|
1799
|
+
return blindCast(built.value.contract);
|
|
1800
|
+
}
|
|
1801
|
+
/** Implementation. */
|
|
1802
|
+
function buildBoundContract(family, target, definition, factory) {
|
|
1803
|
+
const full = {
|
|
1804
|
+
...definition,
|
|
1805
|
+
family,
|
|
1806
|
+
target
|
|
1807
|
+
};
|
|
1808
|
+
if (factory !== void 0) {
|
|
1809
|
+
const lookups = stackLookupsOf([
|
|
1810
|
+
family,
|
|
1811
|
+
target,
|
|
1812
|
+
...Object.values(definition.extensions ?? {})
|
|
1813
|
+
]);
|
|
1814
|
+
const built = factory(composeMongoAuthoringHelpers(family, target, definition.extensions, lookups));
|
|
1815
|
+
return buildContractFromDsl({
|
|
1816
|
+
...full,
|
|
1817
|
+
...ifDefined("models", built.models),
|
|
1818
|
+
...ifDefined("valueObjects", built.valueObjects),
|
|
1819
|
+
...ifDefined("roots", built.roots)
|
|
1820
|
+
}, lookups);
|
|
1821
|
+
}
|
|
1822
|
+
return buildContractFromDsl(full);
|
|
1823
|
+
}
|
|
1824
|
+
function defineContract(definition, factory) {
|
|
1825
|
+
if (!isContractScaffold(definition)) throw contractError("CONTRACT.ARGUMENT_INVALID", "defineContract expects a contract definition object. Define your contract with defineContract({ family, target, models, ... }).");
|
|
1826
|
+
if (factory !== void 0) return buildBoundContract(definition.family, definition.target, definition, factory);
|
|
1827
|
+
return buildBoundContract(definition.family, definition.target, definition);
|
|
1828
|
+
}
|
|
1829
|
+
//#endregion
|
|
1830
|
+
export { enumType$1 as a, isEnumTypeHandle as c, rel as d, valueObject as f, defineContract as i, member as l, buildBoundContract as n, field as o, buildMongoContractFromDefinition as r, index as s, bindEnumType as t, model as u };
|
|
1831
|
+
|
|
1832
|
+
//# sourceMappingURL=contract-builder-d5cf-u9p.mjs.map
|