@prisma-next/family-mongo 0.5.0-dev.4 → 0.5.0-dev.6
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/README.md +2 -0
- package/dist/control.d.mts +1 -1
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +18 -345
- package/dist/control.mjs.map +1 -1
- package/dist/schema-verify.d.mts +2 -0
- package/dist/schema-verify.mjs +3 -0
- package/package.json +18 -17
- package/src/core/control-instance.ts +12 -30
- package/src/core/mongo-target-descriptor.ts +20 -5
- package/src/exports/schema-verify.ts +2 -0
- package/src/core/schema-diff.ts +0 -402
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ This package is the Mongo family integration point for both control-plane assemb
|
|
|
23
23
|
- `./control`: control-plane entrypoint exporting `mongoFamilyDescriptor`, `mongoTargetDescriptor`, `createMongoFamilyInstance`, and `MongoControlFamilyInstance`
|
|
24
24
|
- `./migration`: migration authoring — `Migration` class, factory functions, and strategies (re-exported from `@prisma-next/target-mongo/migration`)
|
|
25
25
|
- `./pack`: pure pack ref for TypeScript authoring flows such as `@prisma-next/mongo-contract-ts/contract-builder`
|
|
26
|
+
- `./schema-verify`: re-exports the pure `verifyMongoSchema(...)` from `@prisma-next/target-mongo/schema-verify`. `MongoFamilyInstance.schemaVerify` (i.e. `db verify --schema-only`) and the `MongoMigrationRunner` post-apply verify step both call into this shared verifier, so both surfaces agree on "matches the contract" by construction
|
|
26
27
|
|
|
27
28
|
## Usage
|
|
28
29
|
|
|
@@ -134,6 +135,7 @@ Run `node migration.ts` to produce `ops.json` and `migration.json`. Use `--dry-r
|
|
|
134
135
|
- `src/exports/control.ts`: control-plane entrypoint
|
|
135
136
|
- `src/exports/migration.ts`: migration authoring entrypoint
|
|
136
137
|
- `src/exports/pack.ts`: authoring-time family pack ref
|
|
138
|
+
- `src/exports/schema-verify.ts`: schema-verify entrypoint that re-exports from `@prisma-next/target-mongo/schema-verify`
|
|
137
139
|
|
|
138
140
|
## Dependencies
|
|
139
141
|
|
package/dist/control.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ControlFamilyDescriptor, ControlFamilyInstance, ControlStack, MigratableTargetDescriptor, SchemaViewCapable } from "@prisma-next/framework-components/control";
|
|
2
|
-
import { MongoSchemaIR } from "@prisma-next/mongo-schema-ir";
|
|
3
2
|
import { Contract } from "@prisma-next/contract/types";
|
|
3
|
+
import { MongoSchemaIR } from "@prisma-next/mongo-schema-ir";
|
|
4
4
|
|
|
5
5
|
//#region src/core/control-instance.d.ts
|
|
6
6
|
interface MongoControlFamilyInstance extends ControlFamilyInstance<'mongo', MongoSchemaIR>, SchemaViewCapable<MongoSchemaIR> {
|
package/dist/control.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/control-instance.ts","../src/core/control-descriptor.ts","../src/core/mongo-target-descriptor.ts"],"sourcesContent":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/control-instance.ts","../src/core/control-descriptor.ts","../src/core/mongo-target-descriptor.ts"],"sourcesContent":[],"mappings":";;;;;UA2BiB,0BAAA,SACP,+BAA+B,gBACrC,kBAAkB;2CACqB;AAH3C;AACyC,iBAqRzB,yBAAA,CArRyB,aAAA,EAqRgB,YArRhB,CAAA,EAqR+B,0BArR/B;;;cCL5B,uBAAuB,iCAAiC;;;;;;ADIrE;;;;;;;AAsRgB,cEtRH,qBFsR4B,EEtRL,0BFsRoC,CAAA,OAAA,EAAA,OAA0B,EEnRhG,0BFmRgG,CAAA"}
|
package/dist/control.mjs
CHANGED
|
@@ -1,329 +1,13 @@
|
|
|
1
1
|
import { mongoEmission } from "@prisma-next/mongo-emitter";
|
|
2
2
|
import { createMongoRunnerDeps, extractDb, introspectSchema } from "@prisma-next/adapter-mongo/control";
|
|
3
|
-
import { SchemaTreeNode, VERIFY_CODE_HASH_MISMATCH, VERIFY_CODE_MARKER_MISSING,
|
|
3
|
+
import { SchemaTreeNode, VERIFY_CODE_HASH_MISMATCH, VERIFY_CODE_MARKER_MISSING, VERIFY_CODE_TARGET_MISMATCH } from "@prisma-next/framework-components/control";
|
|
4
4
|
import { validateMongoContract } from "@prisma-next/mongo-contract";
|
|
5
5
|
import { MongoMigrationPlanner, MongoMigrationRunner, contractToMongoSchemaIR, initMarker, readMarker, updateMarker } from "@prisma-next/target-mongo/control";
|
|
6
|
+
import { verifyMongoSchema } from "@prisma-next/target-mongo/schema-verify";
|
|
6
7
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
7
|
-
import { canonicalize, deepEqual } from "@prisma-next/mongo-schema-ir";
|
|
8
8
|
import { MongoDriverImpl } from "@prisma-next/driver-mongo";
|
|
9
9
|
import mongoTargetDescriptorMeta from "@prisma-next/target-mongo/pack";
|
|
10
10
|
|
|
11
|
-
//#region src/core/schema-diff.ts
|
|
12
|
-
function diffMongoSchemas(live, expected, strict) {
|
|
13
|
-
const issues = [];
|
|
14
|
-
const collectionChildren = [];
|
|
15
|
-
let pass = 0;
|
|
16
|
-
let warn = 0;
|
|
17
|
-
let fail = 0;
|
|
18
|
-
const allNames = new Set([...live.collectionNames, ...expected.collectionNames]);
|
|
19
|
-
for (const name of [...allNames].sort()) {
|
|
20
|
-
const liveColl = live.collection(name);
|
|
21
|
-
const expectedColl = expected.collection(name);
|
|
22
|
-
if (!liveColl && expectedColl) {
|
|
23
|
-
issues.push({
|
|
24
|
-
kind: "missing_table",
|
|
25
|
-
table: name,
|
|
26
|
-
message: `Collection "${name}" is missing from the database`
|
|
27
|
-
});
|
|
28
|
-
collectionChildren.push({
|
|
29
|
-
status: "fail",
|
|
30
|
-
kind: "collection",
|
|
31
|
-
name,
|
|
32
|
-
contractPath: `storage.collections.${name}`,
|
|
33
|
-
code: "MISSING_COLLECTION",
|
|
34
|
-
message: `Collection "${name}" is missing`,
|
|
35
|
-
expected: name,
|
|
36
|
-
actual: null,
|
|
37
|
-
children: []
|
|
38
|
-
});
|
|
39
|
-
fail++;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (liveColl && !expectedColl) {
|
|
43
|
-
const status = strict ? "fail" : "warn";
|
|
44
|
-
issues.push({
|
|
45
|
-
kind: "extra_table",
|
|
46
|
-
table: name,
|
|
47
|
-
message: `Extra collection "${name}" exists in the database but not in the contract`
|
|
48
|
-
});
|
|
49
|
-
collectionChildren.push({
|
|
50
|
-
status,
|
|
51
|
-
kind: "collection",
|
|
52
|
-
name,
|
|
53
|
-
contractPath: `storage.collections.${name}`,
|
|
54
|
-
code: "EXTRA_COLLECTION",
|
|
55
|
-
message: `Extra collection "${name}" found`,
|
|
56
|
-
expected: null,
|
|
57
|
-
actual: name,
|
|
58
|
-
children: []
|
|
59
|
-
});
|
|
60
|
-
if (status === "fail") fail++;
|
|
61
|
-
else warn++;
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
const lc = liveColl;
|
|
65
|
-
const ec = expectedColl;
|
|
66
|
-
const indexChildren = diffIndexes(name, lc, ec, strict, issues);
|
|
67
|
-
const validatorChildren = diffValidator(name, lc, ec, strict, issues);
|
|
68
|
-
const optionsChildren = diffOptions(name, lc, ec, strict, issues);
|
|
69
|
-
const children = [
|
|
70
|
-
...indexChildren,
|
|
71
|
-
...validatorChildren,
|
|
72
|
-
...optionsChildren
|
|
73
|
-
];
|
|
74
|
-
const worstStatus = children.reduce((s, c) => c.status === "fail" ? "fail" : c.status === "warn" && s !== "fail" ? "warn" : s, "pass");
|
|
75
|
-
for (const c of children) if (c.status === "pass") pass++;
|
|
76
|
-
else if (c.status === "warn") warn++;
|
|
77
|
-
else fail++;
|
|
78
|
-
if (children.length === 0) pass++;
|
|
79
|
-
collectionChildren.push({
|
|
80
|
-
status: worstStatus,
|
|
81
|
-
kind: "collection",
|
|
82
|
-
name,
|
|
83
|
-
contractPath: `storage.collections.${name}`,
|
|
84
|
-
code: worstStatus === "pass" ? "MATCH" : "DRIFT",
|
|
85
|
-
message: worstStatus === "pass" ? `Collection "${name}" matches` : `Collection "${name}" has drift`,
|
|
86
|
-
expected: name,
|
|
87
|
-
actual: name,
|
|
88
|
-
children
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
const rootStatus = fail > 0 ? "fail" : warn > 0 ? "warn" : "pass";
|
|
92
|
-
const totalNodes = pass + warn + fail + collectionChildren.length;
|
|
93
|
-
return {
|
|
94
|
-
root: {
|
|
95
|
-
status: rootStatus,
|
|
96
|
-
kind: "root",
|
|
97
|
-
name: "mongo-schema",
|
|
98
|
-
contractPath: "storage",
|
|
99
|
-
code: rootStatus === "pass" ? "MATCH" : "DRIFT",
|
|
100
|
-
message: rootStatus === "pass" ? "Schema matches" : "Schema has drift",
|
|
101
|
-
expected: null,
|
|
102
|
-
actual: null,
|
|
103
|
-
children: collectionChildren
|
|
104
|
-
},
|
|
105
|
-
issues,
|
|
106
|
-
counts: {
|
|
107
|
-
pass,
|
|
108
|
-
warn,
|
|
109
|
-
fail,
|
|
110
|
-
totalNodes
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
function buildIndexLookupKey(index) {
|
|
115
|
-
const keys = index.keys.map((k) => `${k.field}:${k.direction}`).join(",");
|
|
116
|
-
const opts = [
|
|
117
|
-
index.unique ? "unique" : "",
|
|
118
|
-
index.sparse ? "sparse" : "",
|
|
119
|
-
index.expireAfterSeconds != null ? `ttl:${index.expireAfterSeconds}` : "",
|
|
120
|
-
index.partialFilterExpression ? `pfe:${canonicalize(index.partialFilterExpression)}` : "",
|
|
121
|
-
index.wildcardProjection ? `wp:${canonicalize(index.wildcardProjection)}` : "",
|
|
122
|
-
index.collation ? `col:${canonicalize(index.collation)}` : "",
|
|
123
|
-
index.weights ? `wt:${canonicalize(index.weights)}` : "",
|
|
124
|
-
index.default_language ? `dl:${index.default_language}` : "",
|
|
125
|
-
index.language_override ? `lo:${index.language_override}` : ""
|
|
126
|
-
].filter(Boolean).join(";");
|
|
127
|
-
return opts ? `${keys}|${opts}` : keys;
|
|
128
|
-
}
|
|
129
|
-
function formatIndexName(index) {
|
|
130
|
-
return index.keys.map((k) => `${k.field}:${k.direction}`).join(", ");
|
|
131
|
-
}
|
|
132
|
-
function diffIndexes(collName, live, expected, strict, issues) {
|
|
133
|
-
const nodes = [];
|
|
134
|
-
const liveLookup = /* @__PURE__ */ new Map();
|
|
135
|
-
for (const idx of live.indexes) liveLookup.set(buildIndexLookupKey(idx), idx);
|
|
136
|
-
const expectedLookup = /* @__PURE__ */ new Map();
|
|
137
|
-
for (const idx of expected.indexes) expectedLookup.set(buildIndexLookupKey(idx), idx);
|
|
138
|
-
for (const [key, idx] of expectedLookup) if (liveLookup.has(key)) nodes.push({
|
|
139
|
-
status: "pass",
|
|
140
|
-
kind: "index",
|
|
141
|
-
name: formatIndexName(idx),
|
|
142
|
-
contractPath: `storage.collections.${collName}.indexes`,
|
|
143
|
-
code: "MATCH",
|
|
144
|
-
message: `Index ${formatIndexName(idx)} matches`,
|
|
145
|
-
expected: key,
|
|
146
|
-
actual: key,
|
|
147
|
-
children: []
|
|
148
|
-
});
|
|
149
|
-
else {
|
|
150
|
-
issues.push({
|
|
151
|
-
kind: "index_mismatch",
|
|
152
|
-
table: collName,
|
|
153
|
-
indexOrConstraint: formatIndexName(idx),
|
|
154
|
-
message: `Index ${formatIndexName(idx)} missing on collection "${collName}"`
|
|
155
|
-
});
|
|
156
|
-
nodes.push({
|
|
157
|
-
status: "fail",
|
|
158
|
-
kind: "index",
|
|
159
|
-
name: formatIndexName(idx),
|
|
160
|
-
contractPath: `storage.collections.${collName}.indexes`,
|
|
161
|
-
code: "MISSING_INDEX",
|
|
162
|
-
message: `Index ${formatIndexName(idx)} missing`,
|
|
163
|
-
expected: key,
|
|
164
|
-
actual: null,
|
|
165
|
-
children: []
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
for (const [key, idx] of liveLookup) if (!expectedLookup.has(key)) {
|
|
169
|
-
const status = strict ? "fail" : "warn";
|
|
170
|
-
issues.push({
|
|
171
|
-
kind: "extra_index",
|
|
172
|
-
table: collName,
|
|
173
|
-
indexOrConstraint: formatIndexName(idx),
|
|
174
|
-
message: `Extra index ${formatIndexName(idx)} on collection "${collName}"`
|
|
175
|
-
});
|
|
176
|
-
nodes.push({
|
|
177
|
-
status,
|
|
178
|
-
kind: "index",
|
|
179
|
-
name: formatIndexName(idx),
|
|
180
|
-
contractPath: `storage.collections.${collName}.indexes`,
|
|
181
|
-
code: "EXTRA_INDEX",
|
|
182
|
-
message: `Extra index ${formatIndexName(idx)}`,
|
|
183
|
-
expected: null,
|
|
184
|
-
actual: key,
|
|
185
|
-
children: []
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
return nodes;
|
|
189
|
-
}
|
|
190
|
-
function diffValidator(collName, live, expected, strict, issues) {
|
|
191
|
-
if (!live.validator && !expected.validator) return [];
|
|
192
|
-
if (expected.validator && !live.validator) {
|
|
193
|
-
issues.push({
|
|
194
|
-
kind: "type_missing",
|
|
195
|
-
table: collName,
|
|
196
|
-
message: `Validator missing on collection "${collName}"`
|
|
197
|
-
});
|
|
198
|
-
return [{
|
|
199
|
-
status: "fail",
|
|
200
|
-
kind: "validator",
|
|
201
|
-
name: "validator",
|
|
202
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
203
|
-
code: "MISSING_VALIDATOR",
|
|
204
|
-
message: "Validator missing",
|
|
205
|
-
expected: canonicalize(expected.validator.jsonSchema),
|
|
206
|
-
actual: null,
|
|
207
|
-
children: []
|
|
208
|
-
}];
|
|
209
|
-
}
|
|
210
|
-
if (!expected.validator && live.validator) {
|
|
211
|
-
const status = strict ? "fail" : "warn";
|
|
212
|
-
issues.push({
|
|
213
|
-
kind: "extra_validator",
|
|
214
|
-
table: collName,
|
|
215
|
-
message: `Extra validator on collection "${collName}"`
|
|
216
|
-
});
|
|
217
|
-
return [{
|
|
218
|
-
status,
|
|
219
|
-
kind: "validator",
|
|
220
|
-
name: "validator",
|
|
221
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
222
|
-
code: "EXTRA_VALIDATOR",
|
|
223
|
-
message: "Extra validator found",
|
|
224
|
-
expected: null,
|
|
225
|
-
actual: canonicalize(live.validator.jsonSchema),
|
|
226
|
-
children: []
|
|
227
|
-
}];
|
|
228
|
-
}
|
|
229
|
-
const liveVal = live.validator;
|
|
230
|
-
const expectedVal = expected.validator;
|
|
231
|
-
const liveSchema = canonicalize(liveVal.jsonSchema);
|
|
232
|
-
const expectedSchema = canonicalize(expectedVal.jsonSchema);
|
|
233
|
-
if (liveSchema !== expectedSchema || liveVal.validationLevel !== expectedVal.validationLevel || liveVal.validationAction !== expectedVal.validationAction) {
|
|
234
|
-
issues.push({
|
|
235
|
-
kind: "type_mismatch",
|
|
236
|
-
table: collName,
|
|
237
|
-
expected: expectedSchema,
|
|
238
|
-
actual: liveSchema,
|
|
239
|
-
message: `Validator mismatch on collection "${collName}"`
|
|
240
|
-
});
|
|
241
|
-
return [{
|
|
242
|
-
status: "fail",
|
|
243
|
-
kind: "validator",
|
|
244
|
-
name: "validator",
|
|
245
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
246
|
-
code: "VALIDATOR_MISMATCH",
|
|
247
|
-
message: "Validator mismatch",
|
|
248
|
-
expected: {
|
|
249
|
-
jsonSchema: expectedVal.jsonSchema,
|
|
250
|
-
validationLevel: expectedVal.validationLevel,
|
|
251
|
-
validationAction: expectedVal.validationAction
|
|
252
|
-
},
|
|
253
|
-
actual: {
|
|
254
|
-
jsonSchema: liveVal.jsonSchema,
|
|
255
|
-
validationLevel: liveVal.validationLevel,
|
|
256
|
-
validationAction: liveVal.validationAction
|
|
257
|
-
},
|
|
258
|
-
children: []
|
|
259
|
-
}];
|
|
260
|
-
}
|
|
261
|
-
return [{
|
|
262
|
-
status: "pass",
|
|
263
|
-
kind: "validator",
|
|
264
|
-
name: "validator",
|
|
265
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
266
|
-
code: "MATCH",
|
|
267
|
-
message: "Validator matches",
|
|
268
|
-
expected: expectedSchema,
|
|
269
|
-
actual: liveSchema,
|
|
270
|
-
children: []
|
|
271
|
-
}];
|
|
272
|
-
}
|
|
273
|
-
function diffOptions(collName, live, expected, strict, issues) {
|
|
274
|
-
if (!live.options && !expected.options) return [];
|
|
275
|
-
if (!expected.options && live.options) {
|
|
276
|
-
const status = strict ? "fail" : "warn";
|
|
277
|
-
issues.push({
|
|
278
|
-
kind: "type_mismatch",
|
|
279
|
-
table: collName,
|
|
280
|
-
actual: canonicalize(live.options),
|
|
281
|
-
message: `Extra collection options on "${collName}"`
|
|
282
|
-
});
|
|
283
|
-
return [{
|
|
284
|
-
status,
|
|
285
|
-
kind: "options",
|
|
286
|
-
name: "options",
|
|
287
|
-
contractPath: `storage.collections.${collName}.options`,
|
|
288
|
-
code: "EXTRA_OPTIONS",
|
|
289
|
-
message: "Extra collection options found",
|
|
290
|
-
expected: null,
|
|
291
|
-
actual: live.options,
|
|
292
|
-
children: []
|
|
293
|
-
}];
|
|
294
|
-
}
|
|
295
|
-
if (deepEqual(live.options, expected.options)) return [{
|
|
296
|
-
status: "pass",
|
|
297
|
-
kind: "options",
|
|
298
|
-
name: "options",
|
|
299
|
-
contractPath: `storage.collections.${collName}.options`,
|
|
300
|
-
code: "MATCH",
|
|
301
|
-
message: "Collection options match",
|
|
302
|
-
expected: canonicalize(expected.options),
|
|
303
|
-
actual: canonicalize(live.options),
|
|
304
|
-
children: []
|
|
305
|
-
}];
|
|
306
|
-
issues.push({
|
|
307
|
-
kind: "type_mismatch",
|
|
308
|
-
table: collName,
|
|
309
|
-
expected: canonicalize(expected.options),
|
|
310
|
-
actual: canonicalize(live.options),
|
|
311
|
-
message: `Collection options mismatch on "${collName}"`
|
|
312
|
-
});
|
|
313
|
-
return [{
|
|
314
|
-
status: "fail",
|
|
315
|
-
kind: "options",
|
|
316
|
-
name: "options",
|
|
317
|
-
contractPath: `storage.collections.${collName}.options`,
|
|
318
|
-
code: "OPTIONS_MISMATCH",
|
|
319
|
-
message: "Collection options mismatch",
|
|
320
|
-
expected: expected.options,
|
|
321
|
-
actual: live.options,
|
|
322
|
-
children: []
|
|
323
|
-
}];
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
//#endregion
|
|
327
11
|
//#region src/core/schema-to-view.ts
|
|
328
12
|
function mongoSchemaToView(schema) {
|
|
329
13
|
const collectionNodes = schema.collections.map((collection) => collectionToSchemaNode(collection.name, collection));
|
|
@@ -484,31 +168,17 @@ var MongoFamilyInstance = class {
|
|
|
484
168
|
}
|
|
485
169
|
async schemaVerify(options) {
|
|
486
170
|
const { driver, contract: rawContract, strict, contractPath, configPath } = options;
|
|
487
|
-
const startTime = Date.now();
|
|
488
171
|
const contract = validateMongoContract(rawContract).contract;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
target: { expected: contract.target },
|
|
500
|
-
schema: {
|
|
501
|
-
issues,
|
|
502
|
-
root,
|
|
503
|
-
counts
|
|
504
|
-
},
|
|
505
|
-
meta: {
|
|
506
|
-
...ifDefined("contractPath", contractPath),
|
|
507
|
-
...ifDefined("configPath", configPath),
|
|
508
|
-
strict
|
|
509
|
-
},
|
|
510
|
-
timings: { total: Date.now() - startTime }
|
|
511
|
-
};
|
|
172
|
+
return verifyMongoSchema({
|
|
173
|
+
contract,
|
|
174
|
+
schema: await introspectSchema(extractDb$1(driver)),
|
|
175
|
+
strict,
|
|
176
|
+
frameworkComponents: options.frameworkComponents,
|
|
177
|
+
context: {
|
|
178
|
+
contractPath,
|
|
179
|
+
...ifDefined("configPath", configPath)
|
|
180
|
+
}
|
|
181
|
+
});
|
|
512
182
|
}
|
|
513
183
|
async sign(options) {
|
|
514
184
|
const { driver, contract: rawContract, contractPath, configPath } = options;
|
|
@@ -639,12 +309,15 @@ const mongoTargetDescriptor = {
|
|
|
639
309
|
createPlanner(_family) {
|
|
640
310
|
return new MongoMigrationPlanner();
|
|
641
311
|
},
|
|
642
|
-
createRunner(
|
|
312
|
+
createRunner(family) {
|
|
643
313
|
let cachedDeps;
|
|
644
314
|
return { async execute(options) {
|
|
645
|
-
cachedDeps ??= createMongoRunnerDeps(options.driver, MongoDriverImpl.fromDb(extractDb(options.driver)));
|
|
315
|
+
cachedDeps ??= createMongoRunnerDeps(options.driver, MongoDriverImpl.fromDb(extractDb(options.driver)), family);
|
|
646
316
|
const { driver: _, ...runnerOptions } = options;
|
|
647
|
-
return new MongoMigrationRunner(cachedDeps).execute(
|
|
317
|
+
return new MongoMigrationRunner(cachedDeps).execute({
|
|
318
|
+
...runnerOptions,
|
|
319
|
+
destinationContract: runnerOptions.destinationContract
|
|
320
|
+
});
|
|
648
321
|
} };
|
|
649
322
|
},
|
|
650
323
|
contractToSchema(contract) {
|
package/dist/control.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.mjs","names":["issues: SchemaIssue[]","collectionChildren: SchemaVerificationNode[]","nodes: SchemaVerificationNode[]","children: SchemaTreeNode[]","options: string[]","validatorChildren: SchemaTreeNode[]","optLabels: string[]","extractDb","previousHashes: { storageHash?: string; profileHash?: string } | undefined","summary: string","mongoFamilyDescriptor: ControlFamilyDescriptor<'mongo', MongoControlFamilyInstance>","mongoTargetDescriptor: MigratableTargetDescriptor<\n 'mongo',\n 'mongo',\n MongoControlFamilyInstance\n>","cachedDeps: MongoRunnerDependencies | undefined"],"sources":["../src/core/schema-diff.ts","../src/core/schema-to-view.ts","../src/core/control-instance.ts","../src/core/control-descriptor.ts","../src/core/mongo-target-descriptor.ts"],"sourcesContent":["import type {\n SchemaIssue,\n SchemaVerificationNode,\n} from '@prisma-next/framework-components/control';\nimport type {\n MongoSchemaCollection,\n MongoSchemaIndex,\n MongoSchemaIR,\n} from '@prisma-next/mongo-schema-ir';\nimport { canonicalize, deepEqual } from '@prisma-next/mongo-schema-ir';\n\nexport function diffMongoSchemas(\n live: MongoSchemaIR,\n expected: MongoSchemaIR,\n strict: boolean,\n): {\n root: SchemaVerificationNode;\n issues: SchemaIssue[];\n counts: { pass: number; warn: number; fail: number; totalNodes: number };\n} {\n const issues: SchemaIssue[] = [];\n const collectionChildren: SchemaVerificationNode[] = [];\n let pass = 0;\n let warn = 0;\n let fail = 0;\n\n const allNames = new Set([...live.collectionNames, ...expected.collectionNames]);\n\n for (const name of [...allNames].sort()) {\n const liveColl = live.collection(name);\n const expectedColl = expected.collection(name);\n\n if (!liveColl && expectedColl) {\n issues.push({\n kind: 'missing_table',\n table: name,\n message: `Collection \"${name}\" is missing from the database`,\n });\n collectionChildren.push({\n status: 'fail',\n kind: 'collection',\n name,\n contractPath: `storage.collections.${name}`,\n code: 'MISSING_COLLECTION',\n message: `Collection \"${name}\" is missing`,\n expected: name,\n actual: null,\n children: [],\n });\n fail++;\n continue;\n }\n\n if (liveColl && !expectedColl) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'extra_table',\n table: name,\n message: `Extra collection \"${name}\" exists in the database but not in the contract`,\n });\n collectionChildren.push({\n status,\n kind: 'collection',\n name,\n contractPath: `storage.collections.${name}`,\n code: 'EXTRA_COLLECTION',\n message: `Extra collection \"${name}\" found`,\n expected: null,\n actual: name,\n children: [],\n });\n if (status === 'fail') fail++;\n else warn++;\n continue;\n }\n\n const lc = liveColl as MongoSchemaCollection;\n const ec = expectedColl as MongoSchemaCollection;\n const indexChildren = diffIndexes(name, lc, ec, strict, issues);\n const validatorChildren = diffValidator(name, lc, ec, strict, issues);\n const optionsChildren = diffOptions(name, lc, ec, strict, issues);\n const children = [...indexChildren, ...validatorChildren, ...optionsChildren];\n\n const worstStatus = children.reduce<'pass' | 'warn' | 'fail'>(\n (s, c) => (c.status === 'fail' ? 'fail' : c.status === 'warn' && s !== 'fail' ? 'warn' : s),\n 'pass',\n );\n\n for (const c of children) {\n if (c.status === 'pass') pass++;\n else if (c.status === 'warn') warn++;\n else fail++;\n }\n\n if (children.length === 0) {\n pass++;\n }\n\n collectionChildren.push({\n status: worstStatus,\n kind: 'collection',\n name,\n contractPath: `storage.collections.${name}`,\n code: worstStatus === 'pass' ? 'MATCH' : 'DRIFT',\n message:\n worstStatus === 'pass' ? `Collection \"${name}\" matches` : `Collection \"${name}\" has drift`,\n expected: name,\n actual: name,\n children,\n });\n }\n\n const rootStatus = fail > 0 ? 'fail' : warn > 0 ? 'warn' : 'pass';\n const totalNodes = pass + warn + fail + collectionChildren.length;\n\n const root: SchemaVerificationNode = {\n status: rootStatus,\n kind: 'root',\n name: 'mongo-schema',\n contractPath: 'storage',\n code: rootStatus === 'pass' ? 'MATCH' : 'DRIFT',\n message: rootStatus === 'pass' ? 'Schema matches' : 'Schema has drift',\n expected: null,\n actual: null,\n children: collectionChildren,\n };\n\n return { root, issues, counts: { pass, warn, fail, totalNodes } };\n}\n\nfunction buildIndexLookupKey(index: MongoSchemaIndex): string {\n const keys = index.keys.map((k) => `${k.field}:${k.direction}`).join(',');\n const opts = [\n index.unique ? 'unique' : '',\n index.sparse ? 'sparse' : '',\n index.expireAfterSeconds != null ? `ttl:${index.expireAfterSeconds}` : '',\n index.partialFilterExpression ? `pfe:${canonicalize(index.partialFilterExpression)}` : '',\n index.wildcardProjection ? `wp:${canonicalize(index.wildcardProjection)}` : '',\n index.collation ? `col:${canonicalize(index.collation)}` : '',\n index.weights ? `wt:${canonicalize(index.weights)}` : '',\n index.default_language ? `dl:${index.default_language}` : '',\n index.language_override ? `lo:${index.language_override}` : '',\n ]\n .filter(Boolean)\n .join(';');\n return opts ? `${keys}|${opts}` : keys;\n}\n\nfunction formatIndexName(index: MongoSchemaIndex): string {\n return index.keys.map((k) => `${k.field}:${k.direction}`).join(', ');\n}\n\nfunction diffIndexes(\n collName: string,\n live: MongoSchemaCollection,\n expected: MongoSchemaCollection,\n strict: boolean,\n issues: SchemaIssue[],\n): SchemaVerificationNode[] {\n const nodes: SchemaVerificationNode[] = [];\n const liveLookup = new Map<string, MongoSchemaIndex>();\n for (const idx of live.indexes) liveLookup.set(buildIndexLookupKey(idx), idx);\n\n const expectedLookup = new Map<string, MongoSchemaIndex>();\n for (const idx of expected.indexes) expectedLookup.set(buildIndexLookupKey(idx), idx);\n\n for (const [key, idx] of expectedLookup) {\n if (liveLookup.has(key)) {\n nodes.push({\n status: 'pass',\n kind: 'index',\n name: formatIndexName(idx),\n contractPath: `storage.collections.${collName}.indexes`,\n code: 'MATCH',\n message: `Index ${formatIndexName(idx)} matches`,\n expected: key,\n actual: key,\n children: [],\n });\n } else {\n issues.push({\n kind: 'index_mismatch',\n table: collName,\n indexOrConstraint: formatIndexName(idx),\n message: `Index ${formatIndexName(idx)} missing on collection \"${collName}\"`,\n });\n nodes.push({\n status: 'fail',\n kind: 'index',\n name: formatIndexName(idx),\n contractPath: `storage.collections.${collName}.indexes`,\n code: 'MISSING_INDEX',\n message: `Index ${formatIndexName(idx)} missing`,\n expected: key,\n actual: null,\n children: [],\n });\n }\n }\n\n for (const [key, idx] of liveLookup) {\n if (!expectedLookup.has(key)) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'extra_index',\n table: collName,\n indexOrConstraint: formatIndexName(idx),\n message: `Extra index ${formatIndexName(idx)} on collection \"${collName}\"`,\n });\n nodes.push({\n status,\n kind: 'index',\n name: formatIndexName(idx),\n contractPath: `storage.collections.${collName}.indexes`,\n code: 'EXTRA_INDEX',\n message: `Extra index ${formatIndexName(idx)}`,\n expected: null,\n actual: key,\n children: [],\n });\n }\n }\n\n return nodes;\n}\n\nfunction diffValidator(\n collName: string,\n live: MongoSchemaCollection,\n expected: MongoSchemaCollection,\n strict: boolean,\n issues: SchemaIssue[],\n): SchemaVerificationNode[] {\n if (!live.validator && !expected.validator) return [];\n\n if (expected.validator && !live.validator) {\n issues.push({\n kind: 'type_missing',\n table: collName,\n message: `Validator missing on collection \"${collName}\"`,\n });\n return [\n {\n status: 'fail',\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.collections.${collName}.validator`,\n code: 'MISSING_VALIDATOR',\n message: 'Validator missing',\n expected: canonicalize(expected.validator.jsonSchema),\n actual: null,\n children: [],\n },\n ];\n }\n\n if (!expected.validator && live.validator) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'extra_validator',\n table: collName,\n message: `Extra validator on collection \"${collName}\"`,\n });\n return [\n {\n status,\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.collections.${collName}.validator`,\n code: 'EXTRA_VALIDATOR',\n message: 'Extra validator found',\n expected: null,\n actual: canonicalize(live.validator.jsonSchema),\n children: [],\n },\n ];\n }\n\n const liveVal = live.validator as NonNullable<typeof live.validator>;\n const expectedVal = expected.validator as NonNullable<typeof expected.validator>;\n const liveSchema = canonicalize(liveVal.jsonSchema);\n const expectedSchema = canonicalize(expectedVal.jsonSchema);\n\n if (\n liveSchema !== expectedSchema ||\n liveVal.validationLevel !== expectedVal.validationLevel ||\n liveVal.validationAction !== expectedVal.validationAction\n ) {\n issues.push({\n kind: 'type_mismatch',\n table: collName,\n expected: expectedSchema,\n actual: liveSchema,\n message: `Validator mismatch on collection \"${collName}\"`,\n });\n return [\n {\n status: 'fail',\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.collections.${collName}.validator`,\n code: 'VALIDATOR_MISMATCH',\n message: 'Validator mismatch',\n expected: {\n jsonSchema: expectedVal.jsonSchema,\n validationLevel: expectedVal.validationLevel,\n validationAction: expectedVal.validationAction,\n },\n actual: {\n jsonSchema: liveVal.jsonSchema,\n validationLevel: liveVal.validationLevel,\n validationAction: liveVal.validationAction,\n },\n children: [],\n },\n ];\n }\n\n return [\n {\n status: 'pass',\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.collections.${collName}.validator`,\n code: 'MATCH',\n message: 'Validator matches',\n expected: expectedSchema,\n actual: liveSchema,\n children: [],\n },\n ];\n}\n\nfunction diffOptions(\n collName: string,\n live: MongoSchemaCollection,\n expected: MongoSchemaCollection,\n strict: boolean,\n issues: SchemaIssue[],\n): SchemaVerificationNode[] {\n if (!live.options && !expected.options) return [];\n\n if (!expected.options && live.options) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'type_mismatch',\n table: collName,\n actual: canonicalize(live.options),\n message: `Extra collection options on \"${collName}\"`,\n });\n return [\n {\n status,\n kind: 'options',\n name: 'options',\n contractPath: `storage.collections.${collName}.options`,\n code: 'EXTRA_OPTIONS',\n message: 'Extra collection options found',\n expected: null,\n actual: live.options,\n children: [],\n },\n ];\n }\n\n if (deepEqual(live.options, expected.options)) {\n return [\n {\n status: 'pass',\n kind: 'options',\n name: 'options',\n contractPath: `storage.collections.${collName}.options`,\n code: 'MATCH',\n message: 'Collection options match',\n expected: canonicalize(expected.options),\n actual: canonicalize(live.options),\n children: [],\n },\n ];\n }\n\n issues.push({\n kind: 'type_mismatch',\n table: collName,\n expected: canonicalize(expected.options),\n actual: canonicalize(live.options),\n message: `Collection options mismatch on \"${collName}\"`,\n });\n return [\n {\n status: 'fail',\n kind: 'options',\n name: 'options',\n contractPath: `storage.collections.${collName}.options`,\n code: 'OPTIONS_MISMATCH',\n message: 'Collection options mismatch',\n expected: expected.options,\n actual: live.options,\n children: [],\n },\n ];\n}\n","import type { CoreSchemaView } from '@prisma-next/framework-components/control';\nimport { SchemaTreeNode } from '@prisma-next/framework-components/control';\nimport type { MongoSchemaCollection, MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport { ifDefined } from '@prisma-next/utils/defined';\n\nexport function mongoSchemaToView(schema: MongoSchemaIR): CoreSchemaView {\n const collectionNodes = schema.collections.map((collection) =>\n collectionToSchemaNode(collection.name, collection),\n );\n\n return {\n root: new SchemaTreeNode({\n kind: 'root',\n id: 'mongo-schema',\n label: 'database',\n ...ifDefined('children', collectionNodes.length > 0 ? collectionNodes : undefined),\n }),\n };\n}\n\nfunction collectionToSchemaNode(name: string, collection: MongoSchemaCollection): SchemaTreeNode {\n const children: SchemaTreeNode[] = [];\n\n for (const index of collection.indexes) {\n const keysSummary = index.keys\n .map((k) => {\n if (k.direction === 1) return k.field;\n if (k.direction === -1) return `${k.field} desc`;\n return `${k.field} ${k.direction}`;\n })\n .join(', ');\n const prefix = index.unique ? 'unique index' : 'index';\n const options: string[] = [];\n if (index.sparse) options.push('sparse');\n if (index.expireAfterSeconds != null) options.push(`ttl: ${index.expireAfterSeconds}s`);\n if (index.partialFilterExpression) options.push('partial');\n const optsSuffix = options.length > 0 ? ` (${options.join(', ')})` : '';\n\n children.push(\n new SchemaTreeNode({\n kind: 'index',\n id: `index-${name}-${index.keys.map((k) => `${k.field}_${k.direction}`).join('_')}`,\n label: `${prefix} (${keysSummary})${optsSuffix}`,\n meta: {\n keys: index.keys,\n unique: index.unique,\n ...ifDefined('sparse', index.sparse || undefined),\n ...ifDefined('expireAfterSeconds', index.expireAfterSeconds ?? undefined),\n ...ifDefined('partialFilterExpression', index.partialFilterExpression ?? undefined),\n },\n }),\n );\n }\n\n if (collection.validator) {\n const validatorChildren: SchemaTreeNode[] = [];\n const jsonSchema = collection.validator.jsonSchema as Record<string, unknown>;\n const properties = jsonSchema['properties'] as\n | Record<string, Record<string, unknown>>\n | undefined;\n const required = new Set((jsonSchema['required'] as string[] | undefined) ?? []);\n\n if (properties) {\n for (const [propName, propDef] of Object.entries(properties)) {\n const bsonType = (propDef['bsonType'] as string) ?? 'unknown';\n const suffix = required.has(propName) ? ' (required)' : '';\n validatorChildren.push(\n new SchemaTreeNode({\n kind: 'field',\n id: `field-${name}-${propName}`,\n label: `${propName}: ${bsonType}${suffix}`,\n }),\n );\n }\n }\n\n children.push(\n new SchemaTreeNode({\n kind: 'field',\n id: `validator-${name}`,\n label: `validator (level: ${collection.validator.validationLevel}, action: ${collection.validator.validationAction})`,\n meta: {\n validationLevel: collection.validator.validationLevel,\n validationAction: collection.validator.validationAction,\n jsonSchema: collection.validator.jsonSchema,\n },\n ...ifDefined('children', validatorChildren.length > 0 ? validatorChildren : undefined),\n }),\n );\n }\n\n if (collection.options) {\n const opts = collection.options;\n const optLabels: string[] = [];\n if (opts.capped) optLabels.push('capped');\n if (opts.timeseries) optLabels.push('timeseries');\n if (opts.collation) optLabels.push('collation');\n if (opts.changeStreamPreAndPostImages) optLabels.push('changeStreamPreAndPostImages');\n if (opts.clusteredIndex) optLabels.push('clusteredIndex');\n\n if (optLabels.length > 0) {\n children.push(\n new SchemaTreeNode({\n kind: 'field',\n id: `options-${name}`,\n label: `options (${optLabels.join(', ')})`,\n meta: {\n ...ifDefined('capped', opts.capped ?? undefined),\n ...ifDefined('timeseries', opts.timeseries ?? undefined),\n ...ifDefined('collation', opts.collation ?? undefined),\n ...ifDefined(\n 'changeStreamPreAndPostImages',\n opts.changeStreamPreAndPostImages ?? undefined,\n ),\n ...ifDefined('clusteredIndex', opts.clusteredIndex ?? undefined),\n },\n }),\n );\n }\n }\n\n return new SchemaTreeNode({\n kind: 'collection',\n id: `collection-${name}`,\n label: `collection ${name}`,\n ...ifDefined('children', children.length > 0 ? children : undefined),\n });\n}\n","import { introspectSchema } from '@prisma-next/adapter-mongo/control';\nimport type { Contract, ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type {\n ControlDriverInstance,\n ControlFamilyInstance,\n ControlStack,\n CoreSchemaView,\n SchemaViewCapable,\n SignDatabaseResult,\n VerifyDatabaseResult,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport {\n VERIFY_CODE_HASH_MISMATCH,\n VERIFY_CODE_MARKER_MISSING,\n VERIFY_CODE_SCHEMA_FAILURE,\n VERIFY_CODE_TARGET_MISMATCH,\n} from '@prisma-next/framework-components/control';\nimport type { MongoContract } from '@prisma-next/mongo-contract';\nimport { validateMongoContract } from '@prisma-next/mongo-contract';\nimport type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport {\n contractToMongoSchemaIR,\n initMarker,\n readMarker,\n updateMarker,\n} from '@prisma-next/target-mongo/control';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { Db } from 'mongodb';\nimport { diffMongoSchemas } from './schema-diff';\nimport { mongoSchemaToView } from './schema-to-view';\n\nexport interface MongoControlFamilyInstance\n extends ControlFamilyInstance<'mongo', MongoSchemaIR>,\n SchemaViewCapable<MongoSchemaIR> {\n validateContract(contractJson: unknown): Contract;\n}\n\nfunction extractDb(driver: ControlDriverInstance<'mongo', string>): Db {\n const mongoDriver = driver as ControlDriverInstance<'mongo', string> & { db?: Db };\n if (!mongoDriver.db) {\n throw new Error(\n 'Mongo control driver does not expose a db property. ' +\n 'Use createMongoControlDriver() from @prisma-next/adapter-mongo/control.',\n );\n }\n return mongoDriver.db;\n}\n\nclass MongoFamilyInstance implements MongoControlFamilyInstance {\n readonly familyId = 'mongo' as const;\n\n validateContract(contractJson: unknown): Contract {\n const validated = validateMongoContract<MongoContract>(contractJson);\n // MongoContract and Contract share structure but are typed independently;\n // validateMongoContract guarantees the shape, so the double cast is safe.\n return validated.contract as unknown as Contract;\n }\n\n async verify(options: {\n readonly driver: ControlDriverInstance<'mongo', string>;\n readonly contract: unknown;\n readonly expectedTargetId: string;\n readonly contractPath: string;\n readonly configPath?: string;\n }): Promise<VerifyDatabaseResult> {\n const { driver, contract: rawContract, expectedTargetId, contractPath, configPath } = options;\n const startTime = Date.now();\n\n const validated = validateMongoContract<MongoContract>(rawContract);\n const contract = validated.contract;\n\n const contractStorageHash = contract.storage.storageHash;\n const contractProfileHash = contract.profileHash;\n const contractTarget = contract.target;\n\n const baseOpts = {\n contractStorageHash,\n contractProfileHash,\n expectedTargetId,\n contractPath,\n ...ifDefined('configPath', configPath),\n };\n\n if (contractTarget !== expectedTargetId) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_TARGET_MISMATCH,\n summary: 'Target mismatch',\n actualTargetId: contractTarget,\n totalTime: Date.now() - startTime,\n });\n }\n\n const db = extractDb(driver);\n const marker = await readMarker(db);\n\n if (!marker) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_MARKER_MISSING,\n summary: 'Marker missing',\n totalTime: Date.now() - startTime,\n });\n }\n\n if (marker.storageHash !== contractStorageHash) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_HASH_MISMATCH,\n summary: 'Hash mismatch',\n marker,\n totalTime: Date.now() - startTime,\n });\n }\n\n if (contractProfileHash && marker.profileHash !== contractProfileHash) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_HASH_MISMATCH,\n summary: 'Hash mismatch',\n marker,\n totalTime: Date.now() - startTime,\n });\n }\n\n return buildVerifyResult({\n ...baseOpts,\n ok: true,\n summary: 'Database matches contract',\n marker,\n totalTime: Date.now() - startTime,\n });\n }\n\n async schemaVerify(options: {\n readonly driver: ControlDriverInstance<'mongo', string>;\n readonly contract: unknown;\n readonly strict: boolean;\n readonly contractPath: string;\n readonly configPath?: string;\n readonly frameworkComponents: ReadonlyArray<unknown>;\n }): Promise<VerifyDatabaseSchemaResult> {\n const { driver, contract: rawContract, strict, contractPath, configPath } = options;\n const startTime = Date.now();\n\n const validated = validateMongoContract<MongoContract>(rawContract);\n const contract = validated.contract;\n\n const db = extractDb(driver);\n const liveIR = await introspectSchema(db);\n const expectedIR = contractToMongoSchemaIR(contract);\n\n const { root, issues, counts } = diffMongoSchemas(liveIR, expectedIR, strict);\n\n const ok = counts.fail === 0;\n\n return {\n ok,\n ...ifDefined('code', ok ? undefined : VERIFY_CODE_SCHEMA_FAILURE),\n summary: ok ? 'Schema matches contract' : `Schema verification found ${counts.fail} issue(s)`,\n contract: {\n storageHash: contract.storage.storageHash,\n ...ifDefined('profileHash', contract.profileHash),\n },\n target: { expected: contract.target },\n schema: { issues, root, counts },\n meta: {\n ...ifDefined('contractPath', contractPath),\n ...ifDefined('configPath', configPath),\n strict,\n },\n timings: { total: Date.now() - startTime },\n };\n }\n\n async sign(options: {\n readonly driver: ControlDriverInstance<'mongo', string>;\n readonly contract: unknown;\n readonly contractPath: string;\n readonly configPath?: string;\n }): Promise<SignDatabaseResult> {\n const { driver, contract: rawContract, contractPath, configPath } = options;\n const startTime = Date.now();\n\n const validated = validateMongoContract<MongoContract>(rawContract);\n const contract = validated.contract;\n\n const contractStorageHash = contract.storage.storageHash;\n const contractProfileHash = contract.profileHash;\n\n const db = extractDb(driver);\n\n const existingMarker = await readMarker(db);\n\n let markerCreated = false;\n let markerUpdated = false;\n let previousHashes: { storageHash?: string; profileHash?: string } | undefined;\n\n if (!existingMarker) {\n await initMarker(db, {\n storageHash: contractStorageHash,\n profileHash: contractProfileHash,\n });\n markerCreated = true;\n } else {\n const storageHashMatches = existingMarker.storageHash === contractStorageHash;\n const profileHashMatches = existingMarker.profileHash === contractProfileHash;\n\n if (!storageHashMatches || !profileHashMatches) {\n previousHashes = {\n storageHash: existingMarker.storageHash,\n profileHash: existingMarker.profileHash,\n };\n const updated = await updateMarker(db, existingMarker.storageHash, {\n storageHash: contractStorageHash,\n profileHash: contractProfileHash,\n });\n if (!updated) {\n throw new Error('CAS conflict: marker was modified by another process during sign');\n }\n markerUpdated = true;\n }\n }\n\n let summary: string;\n if (markerCreated) {\n summary = 'Database signed (marker created)';\n } else if (markerUpdated) {\n summary = `Database signed (marker updated from ${previousHashes?.storageHash ?? 'unknown'})`;\n } else {\n summary = 'Database already signed with this contract';\n }\n\n return {\n ok: true,\n summary,\n contract: {\n storageHash: contractStorageHash,\n profileHash: contractProfileHash,\n },\n target: {\n expected: contract.target,\n actual: contract.target,\n },\n marker: {\n created: markerCreated,\n updated: markerUpdated,\n ...ifDefined('previous', previousHashes),\n },\n meta: {\n contractPath,\n ...ifDefined('configPath', configPath),\n },\n timings: {\n total: Date.now() - startTime,\n },\n };\n }\n\n async readMarker(options: {\n readonly driver: ControlDriverInstance<'mongo', string>;\n }): Promise<ContractMarkerRecord | null> {\n const db = extractDb(options.driver);\n return readMarker(db);\n }\n\n async introspect(options: {\n readonly driver: ControlDriverInstance<'mongo', string>;\n readonly contract?: unknown;\n }): Promise<MongoSchemaIR> {\n const db = extractDb(options.driver);\n return introspectSchema(db);\n }\n\n toSchemaView(schema: MongoSchemaIR): CoreSchemaView {\n return mongoSchemaToView(schema);\n }\n}\n\nfunction buildVerifyResult(opts: {\n ok: boolean;\n code?: string;\n summary: string;\n contractStorageHash: string;\n contractProfileHash?: string;\n marker?: ContractMarkerRecord;\n expectedTargetId: string;\n actualTargetId?: string;\n contractPath: string;\n configPath?: string;\n totalTime: number;\n}): VerifyDatabaseResult {\n return {\n ok: opts.ok,\n ...ifDefined('code', opts.code),\n summary: opts.summary,\n contract: {\n storageHash: opts.contractStorageHash,\n ...ifDefined('profileHash', opts.contractProfileHash),\n },\n ...ifDefined(\n 'marker',\n opts.marker\n ? { storageHash: opts.marker.storageHash, profileHash: opts.marker.profileHash }\n : undefined,\n ),\n target: {\n expected: opts.expectedTargetId,\n ...ifDefined('actual', opts.actualTargetId),\n },\n meta: {\n contractPath: opts.contractPath,\n ...ifDefined('configPath', opts.configPath),\n },\n timings: { total: opts.totalTime },\n };\n}\n\nexport function createMongoFamilyInstance(_controlStack: ControlStack): MongoControlFamilyInstance {\n return new MongoFamilyInstance();\n}\n","import type {\n ControlFamilyDescriptor,\n ControlStack,\n} from '@prisma-next/framework-components/control';\nimport { mongoEmission } from '@prisma-next/mongo-emitter';\nimport { createMongoFamilyInstance, type MongoControlFamilyInstance } from './control-instance';\n\nclass MongoFamilyDescriptor\n implements ControlFamilyDescriptor<'mongo', MongoControlFamilyInstance>\n{\n readonly kind = 'family' as const;\n readonly id = 'mongo';\n readonly familyId = 'mongo' as const;\n readonly version = '0.0.1';\n readonly emission = mongoEmission;\n\n create<TTargetId extends string>(\n stack: ControlStack<'mongo', TTargetId>,\n ): MongoControlFamilyInstance {\n return createMongoFamilyInstance(stack);\n }\n}\n\nexport const mongoFamilyDescriptor: ControlFamilyDescriptor<'mongo', MongoControlFamilyInstance> =\n new MongoFamilyDescriptor();\n","import { createMongoRunnerDeps, extractDb } from '@prisma-next/adapter-mongo/control';\nimport type { Contract } from '@prisma-next/contract/types';\nimport { MongoDriverImpl } from '@prisma-next/driver-mongo';\nimport type { MigratableTargetDescriptor } from '@prisma-next/framework-components/control';\nimport type { MongoContract } from '@prisma-next/mongo-contract';\nimport {\n contractToMongoSchemaIR,\n MongoMigrationPlanner,\n MongoMigrationRunner,\n type MongoRunnerDependencies,\n} from '@prisma-next/target-mongo/control';\nimport mongoTargetDescriptorMeta from '@prisma-next/target-mongo/pack';\nimport type { MongoControlFamilyInstance } from './control-instance';\n\n/**\n * `migration.ts` default-exports a `Migration` subclass whose `operations`\n * getter returns the ordered list of operations and whose `describe()`\n * returns the manifest identity metadata. `MongoMigrationPlanner.plan()`\n * returns a `MigrationPlanWithAuthoringSurface` that knows how to render\n * itself back to such a file; `MongoMigrationPlanner.emptyMigration()`\n * returns the same shape for `migration new`. Users run the scaffolded\n * `migration.ts` directly (via `node migration.ts`) to self-emit\n * `ops.json` and attest the `migrationId`.\n */\nexport const mongoTargetDescriptor: MigratableTargetDescriptor<\n 'mongo',\n 'mongo',\n MongoControlFamilyInstance\n> = {\n ...mongoTargetDescriptorMeta,\n migrations: {\n createPlanner(_family: MongoControlFamilyInstance) {\n return new MongoMigrationPlanner();\n },\n createRunner(_family: MongoControlFamilyInstance) {\n // Deps are bound to the first driver passed to execute() and cached for\n // subsequent calls. Callers must not change the driver between calls.\n let cachedDeps: MongoRunnerDependencies | undefined;\n return {\n async execute(options) {\n cachedDeps ??= createMongoRunnerDeps(\n options.driver,\n MongoDriverImpl.fromDb(extractDb(options.driver)),\n );\n const { driver: _, ...runnerOptions } = options;\n const runner = new MongoMigrationRunner(cachedDeps);\n return runner.execute(runnerOptions);\n },\n };\n },\n contractToSchema(contract: Contract | null) {\n return contractToMongoSchemaIR(contract as MongoContract | null);\n },\n },\n create() {\n return { familyId: 'mongo' as const, targetId: 'mongo' as const };\n },\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"control.mjs","names":["children: SchemaTreeNode[]","options: string[]","validatorChildren: SchemaTreeNode[]","optLabels: string[]","extractDb","previousHashes: { storageHash?: string; profileHash?: string } | undefined","summary: string","mongoFamilyDescriptor: ControlFamilyDescriptor<'mongo', MongoControlFamilyInstance>","mongoTargetDescriptor: MigratableTargetDescriptor<\n 'mongo',\n 'mongo',\n MongoControlFamilyInstance\n>","cachedDeps: MongoRunnerDependencies | undefined"],"sources":["../src/core/schema-to-view.ts","../src/core/control-instance.ts","../src/core/control-descriptor.ts","../src/core/mongo-target-descriptor.ts"],"sourcesContent":["import type { CoreSchemaView } from '@prisma-next/framework-components/control';\nimport { SchemaTreeNode } from '@prisma-next/framework-components/control';\nimport type { MongoSchemaCollection, MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport { ifDefined } from '@prisma-next/utils/defined';\n\nexport function mongoSchemaToView(schema: MongoSchemaIR): CoreSchemaView {\n const collectionNodes = schema.collections.map((collection) =>\n collectionToSchemaNode(collection.name, collection),\n );\n\n return {\n root: new SchemaTreeNode({\n kind: 'root',\n id: 'mongo-schema',\n label: 'database',\n ...ifDefined('children', collectionNodes.length > 0 ? collectionNodes : undefined),\n }),\n };\n}\n\nfunction collectionToSchemaNode(name: string, collection: MongoSchemaCollection): SchemaTreeNode {\n const children: SchemaTreeNode[] = [];\n\n for (const index of collection.indexes) {\n const keysSummary = index.keys\n .map((k) => {\n if (k.direction === 1) return k.field;\n if (k.direction === -1) return `${k.field} desc`;\n return `${k.field} ${k.direction}`;\n })\n .join(', ');\n const prefix = index.unique ? 'unique index' : 'index';\n const options: string[] = [];\n if (index.sparse) options.push('sparse');\n if (index.expireAfterSeconds != null) options.push(`ttl: ${index.expireAfterSeconds}s`);\n if (index.partialFilterExpression) options.push('partial');\n const optsSuffix = options.length > 0 ? ` (${options.join(', ')})` : '';\n\n children.push(\n new SchemaTreeNode({\n kind: 'index',\n id: `index-${name}-${index.keys.map((k) => `${k.field}_${k.direction}`).join('_')}`,\n label: `${prefix} (${keysSummary})${optsSuffix}`,\n meta: {\n keys: index.keys,\n unique: index.unique,\n ...ifDefined('sparse', index.sparse || undefined),\n ...ifDefined('expireAfterSeconds', index.expireAfterSeconds ?? undefined),\n ...ifDefined('partialFilterExpression', index.partialFilterExpression ?? undefined),\n },\n }),\n );\n }\n\n if (collection.validator) {\n const validatorChildren: SchemaTreeNode[] = [];\n const jsonSchema = collection.validator.jsonSchema as Record<string, unknown>;\n const properties = jsonSchema['properties'] as\n | Record<string, Record<string, unknown>>\n | undefined;\n const required = new Set((jsonSchema['required'] as string[] | undefined) ?? []);\n\n if (properties) {\n for (const [propName, propDef] of Object.entries(properties)) {\n const bsonType = (propDef['bsonType'] as string) ?? 'unknown';\n const suffix = required.has(propName) ? ' (required)' : '';\n validatorChildren.push(\n new SchemaTreeNode({\n kind: 'field',\n id: `field-${name}-${propName}`,\n label: `${propName}: ${bsonType}${suffix}`,\n }),\n );\n }\n }\n\n children.push(\n new SchemaTreeNode({\n kind: 'field',\n id: `validator-${name}`,\n label: `validator (level: ${collection.validator.validationLevel}, action: ${collection.validator.validationAction})`,\n meta: {\n validationLevel: collection.validator.validationLevel,\n validationAction: collection.validator.validationAction,\n jsonSchema: collection.validator.jsonSchema,\n },\n ...ifDefined('children', validatorChildren.length > 0 ? validatorChildren : undefined),\n }),\n );\n }\n\n if (collection.options) {\n const opts = collection.options;\n const optLabels: string[] = [];\n if (opts.capped) optLabels.push('capped');\n if (opts.timeseries) optLabels.push('timeseries');\n if (opts.collation) optLabels.push('collation');\n if (opts.changeStreamPreAndPostImages) optLabels.push('changeStreamPreAndPostImages');\n if (opts.clusteredIndex) optLabels.push('clusteredIndex');\n\n if (optLabels.length > 0) {\n children.push(\n new SchemaTreeNode({\n kind: 'field',\n id: `options-${name}`,\n label: `options (${optLabels.join(', ')})`,\n meta: {\n ...ifDefined('capped', opts.capped ?? undefined),\n ...ifDefined('timeseries', opts.timeseries ?? undefined),\n ...ifDefined('collation', opts.collation ?? undefined),\n ...ifDefined(\n 'changeStreamPreAndPostImages',\n opts.changeStreamPreAndPostImages ?? undefined,\n ),\n ...ifDefined('clusteredIndex', opts.clusteredIndex ?? undefined),\n },\n }),\n );\n }\n }\n\n return new SchemaTreeNode({\n kind: 'collection',\n id: `collection-${name}`,\n label: `collection ${name}`,\n ...ifDefined('children', children.length > 0 ? children : undefined),\n });\n}\n","import { introspectSchema } from '@prisma-next/adapter-mongo/control';\nimport type { Contract, ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlDriverInstance,\n ControlFamilyInstance,\n ControlStack,\n CoreSchemaView,\n SchemaViewCapable,\n SignDatabaseResult,\n VerifyDatabaseResult,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport {\n VERIFY_CODE_HASH_MISMATCH,\n VERIFY_CODE_MARKER_MISSING,\n VERIFY_CODE_TARGET_MISMATCH,\n} from '@prisma-next/framework-components/control';\nimport type { MongoContract } from '@prisma-next/mongo-contract';\nimport { validateMongoContract } from '@prisma-next/mongo-contract';\nimport type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport { initMarker, readMarker, updateMarker } from '@prisma-next/target-mongo/control';\nimport { verifyMongoSchema } from '@prisma-next/target-mongo/schema-verify';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport type { Db } from 'mongodb';\nimport { mongoSchemaToView } from './schema-to-view';\n\nexport interface MongoControlFamilyInstance\n extends ControlFamilyInstance<'mongo', MongoSchemaIR>,\n SchemaViewCapable<MongoSchemaIR> {\n validateContract(contractJson: unknown): Contract;\n}\n\nfunction extractDb(driver: ControlDriverInstance<'mongo', string>): Db {\n const mongoDriver = driver as ControlDriverInstance<'mongo', string> & { db?: Db };\n if (!mongoDriver.db) {\n throw new Error(\n 'Mongo control driver does not expose a db property. ' +\n 'Use createMongoControlDriver() from @prisma-next/adapter-mongo/control.',\n );\n }\n return mongoDriver.db;\n}\n\nclass MongoFamilyInstance implements MongoControlFamilyInstance {\n readonly familyId = 'mongo' as const;\n\n validateContract(contractJson: unknown): Contract {\n const validated = validateMongoContract<MongoContract>(contractJson);\n // MongoContract and Contract share structure but are typed independently;\n // validateMongoContract guarantees the shape, so the double cast is safe.\n return validated.contract as unknown as Contract;\n }\n\n async verify(options: {\n readonly driver: ControlDriverInstance<'mongo', string>;\n readonly contract: unknown;\n readonly expectedTargetId: string;\n readonly contractPath: string;\n readonly configPath?: string;\n }): Promise<VerifyDatabaseResult> {\n const { driver, contract: rawContract, expectedTargetId, contractPath, configPath } = options;\n const startTime = Date.now();\n\n const validated = validateMongoContract<MongoContract>(rawContract);\n const contract = validated.contract;\n\n const contractStorageHash = contract.storage.storageHash;\n const contractProfileHash = contract.profileHash;\n const contractTarget = contract.target;\n\n const baseOpts = {\n contractStorageHash,\n contractProfileHash,\n expectedTargetId,\n contractPath,\n ...ifDefined('configPath', configPath),\n };\n\n if (contractTarget !== expectedTargetId) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_TARGET_MISMATCH,\n summary: 'Target mismatch',\n actualTargetId: contractTarget,\n totalTime: Date.now() - startTime,\n });\n }\n\n const db = extractDb(driver);\n const marker = await readMarker(db);\n\n if (!marker) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_MARKER_MISSING,\n summary: 'Marker missing',\n totalTime: Date.now() - startTime,\n });\n }\n\n if (marker.storageHash !== contractStorageHash) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_HASH_MISMATCH,\n summary: 'Hash mismatch',\n marker,\n totalTime: Date.now() - startTime,\n });\n }\n\n if (contractProfileHash && marker.profileHash !== contractProfileHash) {\n return buildVerifyResult({\n ...baseOpts,\n ok: false,\n code: VERIFY_CODE_HASH_MISMATCH,\n summary: 'Hash mismatch',\n marker,\n totalTime: Date.now() - startTime,\n });\n }\n\n return buildVerifyResult({\n ...baseOpts,\n ok: true,\n summary: 'Database matches contract',\n marker,\n totalTime: Date.now() - startTime,\n });\n }\n\n async schemaVerify(options: {\n readonly driver: ControlDriverInstance<'mongo', string>;\n readonly contract: unknown;\n readonly strict: boolean;\n readonly contractPath: string;\n readonly configPath?: string;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', string>>;\n }): Promise<VerifyDatabaseSchemaResult> {\n const { driver, contract: rawContract, strict, contractPath, configPath } = options;\n\n const validated = validateMongoContract<MongoContract>(rawContract);\n const contract = validated.contract;\n\n const db = extractDb(driver);\n const liveIR = await introspectSchema(db);\n\n return verifyMongoSchema({\n contract,\n schema: liveIR,\n strict,\n frameworkComponents: options.frameworkComponents,\n context: {\n contractPath,\n ...ifDefined('configPath', configPath),\n },\n });\n }\n\n async sign(options: {\n readonly driver: ControlDriverInstance<'mongo', string>;\n readonly contract: unknown;\n readonly contractPath: string;\n readonly configPath?: string;\n }): Promise<SignDatabaseResult> {\n const { driver, contract: rawContract, contractPath, configPath } = options;\n const startTime = Date.now();\n\n const validated = validateMongoContract<MongoContract>(rawContract);\n const contract = validated.contract;\n\n const contractStorageHash = contract.storage.storageHash;\n const contractProfileHash = contract.profileHash;\n\n const db = extractDb(driver);\n\n const existingMarker = await readMarker(db);\n\n let markerCreated = false;\n let markerUpdated = false;\n let previousHashes: { storageHash?: string; profileHash?: string } | undefined;\n\n if (!existingMarker) {\n await initMarker(db, {\n storageHash: contractStorageHash,\n profileHash: contractProfileHash,\n });\n markerCreated = true;\n } else {\n const storageHashMatches = existingMarker.storageHash === contractStorageHash;\n const profileHashMatches = existingMarker.profileHash === contractProfileHash;\n\n if (!storageHashMatches || !profileHashMatches) {\n previousHashes = {\n storageHash: existingMarker.storageHash,\n profileHash: existingMarker.profileHash,\n };\n const updated = await updateMarker(db, existingMarker.storageHash, {\n storageHash: contractStorageHash,\n profileHash: contractProfileHash,\n });\n if (!updated) {\n throw new Error('CAS conflict: marker was modified by another process during sign');\n }\n markerUpdated = true;\n }\n }\n\n let summary: string;\n if (markerCreated) {\n summary = 'Database signed (marker created)';\n } else if (markerUpdated) {\n summary = `Database signed (marker updated from ${previousHashes?.storageHash ?? 'unknown'})`;\n } else {\n summary = 'Database already signed with this contract';\n }\n\n return {\n ok: true,\n summary,\n contract: {\n storageHash: contractStorageHash,\n profileHash: contractProfileHash,\n },\n target: {\n expected: contract.target,\n actual: contract.target,\n },\n marker: {\n created: markerCreated,\n updated: markerUpdated,\n ...ifDefined('previous', previousHashes),\n },\n meta: {\n contractPath,\n ...ifDefined('configPath', configPath),\n },\n timings: {\n total: Date.now() - startTime,\n },\n };\n }\n\n async readMarker(options: {\n readonly driver: ControlDriverInstance<'mongo', string>;\n }): Promise<ContractMarkerRecord | null> {\n const db = extractDb(options.driver);\n return readMarker(db);\n }\n\n async introspect(options: {\n readonly driver: ControlDriverInstance<'mongo', string>;\n readonly contract?: unknown;\n }): Promise<MongoSchemaIR> {\n const db = extractDb(options.driver);\n return introspectSchema(db);\n }\n\n toSchemaView(schema: MongoSchemaIR): CoreSchemaView {\n return mongoSchemaToView(schema);\n }\n}\n\nfunction buildVerifyResult(opts: {\n ok: boolean;\n code?: string;\n summary: string;\n contractStorageHash: string;\n contractProfileHash?: string;\n marker?: ContractMarkerRecord;\n expectedTargetId: string;\n actualTargetId?: string;\n contractPath: string;\n configPath?: string;\n totalTime: number;\n}): VerifyDatabaseResult {\n return {\n ok: opts.ok,\n ...ifDefined('code', opts.code),\n summary: opts.summary,\n contract: {\n storageHash: opts.contractStorageHash,\n ...ifDefined('profileHash', opts.contractProfileHash),\n },\n ...ifDefined(\n 'marker',\n opts.marker\n ? { storageHash: opts.marker.storageHash, profileHash: opts.marker.profileHash }\n : undefined,\n ),\n target: {\n expected: opts.expectedTargetId,\n ...ifDefined('actual', opts.actualTargetId),\n },\n meta: {\n contractPath: opts.contractPath,\n ...ifDefined('configPath', opts.configPath),\n },\n timings: { total: opts.totalTime },\n };\n}\n\nexport function createMongoFamilyInstance(_controlStack: ControlStack): MongoControlFamilyInstance {\n return new MongoFamilyInstance();\n}\n","import type {\n ControlFamilyDescriptor,\n ControlStack,\n} from '@prisma-next/framework-components/control';\nimport { mongoEmission } from '@prisma-next/mongo-emitter';\nimport { createMongoFamilyInstance, type MongoControlFamilyInstance } from './control-instance';\n\nclass MongoFamilyDescriptor\n implements ControlFamilyDescriptor<'mongo', MongoControlFamilyInstance>\n{\n readonly kind = 'family' as const;\n readonly id = 'mongo';\n readonly familyId = 'mongo' as const;\n readonly version = '0.0.1';\n readonly emission = mongoEmission;\n\n create<TTargetId extends string>(\n stack: ControlStack<'mongo', TTargetId>,\n ): MongoControlFamilyInstance {\n return createMongoFamilyInstance(stack);\n }\n}\n\nexport const mongoFamilyDescriptor: ControlFamilyDescriptor<'mongo', MongoControlFamilyInstance> =\n new MongoFamilyDescriptor();\n","import { createMongoRunnerDeps, extractDb } from '@prisma-next/adapter-mongo/control';\nimport type { Contract } from '@prisma-next/contract/types';\nimport { MongoDriverImpl } from '@prisma-next/driver-mongo';\nimport type {\n MigratableTargetDescriptor,\n MigrationRunner,\n} from '@prisma-next/framework-components/control';\nimport type { MongoContract } from '@prisma-next/mongo-contract';\nimport {\n contractToMongoSchemaIR,\n MongoMigrationPlanner,\n MongoMigrationRunner,\n type MongoRunnerDependencies,\n} from '@prisma-next/target-mongo/control';\nimport mongoTargetDescriptorMeta from '@prisma-next/target-mongo/pack';\nimport type { MongoControlFamilyInstance } from './control-instance';\n\n/**\n * `migration.ts` default-exports a `Migration` subclass whose `operations`\n * getter returns the ordered list of operations and whose `describe()`\n * returns the manifest identity metadata. `MongoMigrationPlanner.plan()`\n * returns a `MigrationPlanWithAuthoringSurface` that knows how to render\n * itself back to such a file; `MongoMigrationPlanner.emptyMigration()`\n * returns the same shape for `migration new`. Users run the scaffolded\n * `migration.ts` directly (via `node migration.ts`) to self-emit\n * `ops.json` and attest the `migrationId`.\n */\nexport const mongoTargetDescriptor: MigratableTargetDescriptor<\n 'mongo',\n 'mongo',\n MongoControlFamilyInstance\n> = {\n ...mongoTargetDescriptorMeta,\n migrations: {\n createPlanner(_family: MongoControlFamilyInstance) {\n return new MongoMigrationPlanner();\n },\n createRunner(family: MongoControlFamilyInstance) {\n // Deps are bound to the first driver passed to execute() and cached for\n // subsequent calls. Callers must not change the driver between calls.\n let cachedDeps: MongoRunnerDependencies | undefined;\n const runner: MigrationRunner<'mongo', 'mongo'> = {\n async execute(options) {\n cachedDeps ??= createMongoRunnerDeps(\n options.driver,\n MongoDriverImpl.fromDb(extractDb(options.driver)),\n family,\n );\n const { driver: _, ...runnerOptions } = options;\n // The framework `MigrationRunner` interface types `destinationContract`\n // as `unknown`; the Mongo runner narrows to `MongoContract`. Validation\n // happens upstream — `migration apply` calls\n // `familyInstance.validateContract(migration.toContract)` before\n // routing the contract here (see\n // `packages/1-framework/3-tooling/cli/src/control-api/operations/migration-apply.ts`),\n // so this cast simply preserves the framework signature without\n // weakening the runner's typed surface or duplicating validation.\n return new MongoMigrationRunner(cachedDeps).execute({\n ...runnerOptions,\n destinationContract: runnerOptions.destinationContract as MongoContract,\n });\n },\n };\n return runner;\n },\n contractToSchema(contract: Contract | null) {\n return contractToMongoSchemaIR(contract as MongoContract | null);\n },\n },\n create() {\n return { familyId: 'mongo' as const, targetId: 'mongo' as const };\n },\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/family-mongo",
|
|
3
|
-
"version": "0.5.0-dev.
|
|
3
|
+
"version": "0.5.0-dev.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "Mongo family descriptor for Prisma Next",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"mongodb": "^6.16.0",
|
|
9
9
|
"pathe": "^2.0.3",
|
|
10
|
-
"@prisma-next/
|
|
11
|
-
"@prisma-next/
|
|
12
|
-
"@prisma-next/errors": "0.5.0-dev.
|
|
13
|
-
"@prisma-next/driver-mongo": "0.5.0-dev.
|
|
14
|
-
"@prisma-next/framework-components": "0.5.0-dev.
|
|
15
|
-
"@prisma-next/
|
|
16
|
-
"@prisma-next/
|
|
17
|
-
"@prisma-next/mongo
|
|
18
|
-
"@prisma-next/
|
|
19
|
-
"@prisma-next/mongo-query-ast": "0.5.0-dev.
|
|
20
|
-
"@prisma-next/mongo-schema-ir": "0.5.0-dev.
|
|
21
|
-
"@prisma-next/target-mongo": "0.5.0-dev.
|
|
22
|
-
"@prisma-next/utils": "0.5.0-dev.
|
|
10
|
+
"@prisma-next/contract": "0.5.0-dev.6",
|
|
11
|
+
"@prisma-next/emitter": "0.5.0-dev.6",
|
|
12
|
+
"@prisma-next/errors": "0.5.0-dev.6",
|
|
13
|
+
"@prisma-next/driver-mongo": "0.5.0-dev.6",
|
|
14
|
+
"@prisma-next/framework-components": "0.5.0-dev.6",
|
|
15
|
+
"@prisma-next/migration-tools": "0.5.0-dev.6",
|
|
16
|
+
"@prisma-next/mongo-contract": "0.5.0-dev.6",
|
|
17
|
+
"@prisma-next/adapter-mongo": "0.5.0-dev.6",
|
|
18
|
+
"@prisma-next/mongo-emitter": "0.5.0-dev.6",
|
|
19
|
+
"@prisma-next/mongo-query-ast": "0.5.0-dev.6",
|
|
20
|
+
"@prisma-next/mongo-schema-ir": "0.5.0-dev.6",
|
|
21
|
+
"@prisma-next/target-mongo": "0.5.0-dev.6",
|
|
22
|
+
"@prisma-next/utils": "0.5.0-dev.6"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"tsdown": "0.18.4",
|
|
26
26
|
"typescript": "5.9.3",
|
|
27
27
|
"vitest": "4.0.17",
|
|
28
|
-
"@prisma-next/
|
|
29
|
-
"@prisma-next/mongo-contract-ts": "0.5.0-dev.
|
|
30
|
-
"@prisma-next/
|
|
28
|
+
"@prisma-next/tsdown": "0.0.0",
|
|
29
|
+
"@prisma-next/mongo-contract-ts": "0.5.0-dev.6",
|
|
30
|
+
"@prisma-next/tsconfig": "0.0.0"
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
33
|
"dist",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"./control": "./dist/control.mjs",
|
|
46
46
|
"./migration": "./dist/migration.mjs",
|
|
47
47
|
"./pack": "./dist/pack.mjs",
|
|
48
|
+
"./schema-verify": "./dist/schema-verify.mjs",
|
|
48
49
|
"./package.json": "./package.json"
|
|
49
50
|
},
|
|
50
51
|
"scripts": {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { introspectSchema } from '@prisma-next/adapter-mongo/control';
|
|
2
2
|
import type { Contract, ContractMarkerRecord } from '@prisma-next/contract/types';
|
|
3
|
+
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
3
4
|
import type {
|
|
4
5
|
ControlDriverInstance,
|
|
5
6
|
ControlFamilyInstance,
|
|
@@ -13,21 +14,15 @@ import type {
|
|
|
13
14
|
import {
|
|
14
15
|
VERIFY_CODE_HASH_MISMATCH,
|
|
15
16
|
VERIFY_CODE_MARKER_MISSING,
|
|
16
|
-
VERIFY_CODE_SCHEMA_FAILURE,
|
|
17
17
|
VERIFY_CODE_TARGET_MISMATCH,
|
|
18
18
|
} from '@prisma-next/framework-components/control';
|
|
19
19
|
import type { MongoContract } from '@prisma-next/mongo-contract';
|
|
20
20
|
import { validateMongoContract } from '@prisma-next/mongo-contract';
|
|
21
21
|
import type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
initMarker,
|
|
25
|
-
readMarker,
|
|
26
|
-
updateMarker,
|
|
27
|
-
} from '@prisma-next/target-mongo/control';
|
|
22
|
+
import { initMarker, readMarker, updateMarker } from '@prisma-next/target-mongo/control';
|
|
23
|
+
import { verifyMongoSchema } from '@prisma-next/target-mongo/schema-verify';
|
|
28
24
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
29
25
|
import type { Db } from 'mongodb';
|
|
30
|
-
import { diffMongoSchemas } from './schema-diff';
|
|
31
26
|
import { mongoSchemaToView } from './schema-to-view';
|
|
32
27
|
|
|
33
28
|
export interface MongoControlFamilyInstance
|
|
@@ -143,39 +138,26 @@ class MongoFamilyInstance implements MongoControlFamilyInstance {
|
|
|
143
138
|
readonly strict: boolean;
|
|
144
139
|
readonly contractPath: string;
|
|
145
140
|
readonly configPath?: string;
|
|
146
|
-
readonly frameworkComponents: ReadonlyArray<
|
|
141
|
+
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', string>>;
|
|
147
142
|
}): Promise<VerifyDatabaseSchemaResult> {
|
|
148
143
|
const { driver, contract: rawContract, strict, contractPath, configPath } = options;
|
|
149
|
-
const startTime = Date.now();
|
|
150
144
|
|
|
151
145
|
const validated = validateMongoContract<MongoContract>(rawContract);
|
|
152
146
|
const contract = validated.contract;
|
|
153
147
|
|
|
154
148
|
const db = extractDb(driver);
|
|
155
149
|
const liveIR = await introspectSchema(db);
|
|
156
|
-
const expectedIR = contractToMongoSchemaIR(contract);
|
|
157
|
-
|
|
158
|
-
const { root, issues, counts } = diffMongoSchemas(liveIR, expectedIR, strict);
|
|
159
150
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
storageHash: contract.storage.storageHash,
|
|
168
|
-
...ifDefined('profileHash', contract.profileHash),
|
|
169
|
-
},
|
|
170
|
-
target: { expected: contract.target },
|
|
171
|
-
schema: { issues, root, counts },
|
|
172
|
-
meta: {
|
|
173
|
-
...ifDefined('contractPath', contractPath),
|
|
151
|
+
return verifyMongoSchema({
|
|
152
|
+
contract,
|
|
153
|
+
schema: liveIR,
|
|
154
|
+
strict,
|
|
155
|
+
frameworkComponents: options.frameworkComponents,
|
|
156
|
+
context: {
|
|
157
|
+
contractPath,
|
|
174
158
|
...ifDefined('configPath', configPath),
|
|
175
|
-
strict,
|
|
176
159
|
},
|
|
177
|
-
|
|
178
|
-
};
|
|
160
|
+
});
|
|
179
161
|
}
|
|
180
162
|
|
|
181
163
|
async sign(options: {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { createMongoRunnerDeps, extractDb } from '@prisma-next/adapter-mongo/control';
|
|
2
2
|
import type { Contract } from '@prisma-next/contract/types';
|
|
3
3
|
import { MongoDriverImpl } from '@prisma-next/driver-mongo';
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
MigratableTargetDescriptor,
|
|
6
|
+
MigrationRunner,
|
|
7
|
+
} from '@prisma-next/framework-components/control';
|
|
5
8
|
import type { MongoContract } from '@prisma-next/mongo-contract';
|
|
6
9
|
import {
|
|
7
10
|
contractToMongoSchemaIR,
|
|
@@ -32,21 +35,33 @@ export const mongoTargetDescriptor: MigratableTargetDescriptor<
|
|
|
32
35
|
createPlanner(_family: MongoControlFamilyInstance) {
|
|
33
36
|
return new MongoMigrationPlanner();
|
|
34
37
|
},
|
|
35
|
-
createRunner(
|
|
38
|
+
createRunner(family: MongoControlFamilyInstance) {
|
|
36
39
|
// Deps are bound to the first driver passed to execute() and cached for
|
|
37
40
|
// subsequent calls. Callers must not change the driver between calls.
|
|
38
41
|
let cachedDeps: MongoRunnerDependencies | undefined;
|
|
39
|
-
|
|
42
|
+
const runner: MigrationRunner<'mongo', 'mongo'> = {
|
|
40
43
|
async execute(options) {
|
|
41
44
|
cachedDeps ??= createMongoRunnerDeps(
|
|
42
45
|
options.driver,
|
|
43
46
|
MongoDriverImpl.fromDb(extractDb(options.driver)),
|
|
47
|
+
family,
|
|
44
48
|
);
|
|
45
49
|
const { driver: _, ...runnerOptions } = options;
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
// The framework `MigrationRunner` interface types `destinationContract`
|
|
51
|
+
// as `unknown`; the Mongo runner narrows to `MongoContract`. Validation
|
|
52
|
+
// happens upstream — `migration apply` calls
|
|
53
|
+
// `familyInstance.validateContract(migration.toContract)` before
|
|
54
|
+
// routing the contract here (see
|
|
55
|
+
// `packages/1-framework/3-tooling/cli/src/control-api/operations/migration-apply.ts`),
|
|
56
|
+
// so this cast simply preserves the framework signature without
|
|
57
|
+
// weakening the runner's typed surface or duplicating validation.
|
|
58
|
+
return new MongoMigrationRunner(cachedDeps).execute({
|
|
59
|
+
...runnerOptions,
|
|
60
|
+
destinationContract: runnerOptions.destinationContract as MongoContract,
|
|
61
|
+
});
|
|
48
62
|
},
|
|
49
63
|
};
|
|
64
|
+
return runner;
|
|
50
65
|
},
|
|
51
66
|
contractToSchema(contract: Contract | null) {
|
|
52
67
|
return contractToMongoSchemaIR(contract as MongoContract | null);
|
package/src/core/schema-diff.ts
DELETED
|
@@ -1,402 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
SchemaIssue,
|
|
3
|
-
SchemaVerificationNode,
|
|
4
|
-
} from '@prisma-next/framework-components/control';
|
|
5
|
-
import type {
|
|
6
|
-
MongoSchemaCollection,
|
|
7
|
-
MongoSchemaIndex,
|
|
8
|
-
MongoSchemaIR,
|
|
9
|
-
} from '@prisma-next/mongo-schema-ir';
|
|
10
|
-
import { canonicalize, deepEqual } from '@prisma-next/mongo-schema-ir';
|
|
11
|
-
|
|
12
|
-
export function diffMongoSchemas(
|
|
13
|
-
live: MongoSchemaIR,
|
|
14
|
-
expected: MongoSchemaIR,
|
|
15
|
-
strict: boolean,
|
|
16
|
-
): {
|
|
17
|
-
root: SchemaVerificationNode;
|
|
18
|
-
issues: SchemaIssue[];
|
|
19
|
-
counts: { pass: number; warn: number; fail: number; totalNodes: number };
|
|
20
|
-
} {
|
|
21
|
-
const issues: SchemaIssue[] = [];
|
|
22
|
-
const collectionChildren: SchemaVerificationNode[] = [];
|
|
23
|
-
let pass = 0;
|
|
24
|
-
let warn = 0;
|
|
25
|
-
let fail = 0;
|
|
26
|
-
|
|
27
|
-
const allNames = new Set([...live.collectionNames, ...expected.collectionNames]);
|
|
28
|
-
|
|
29
|
-
for (const name of [...allNames].sort()) {
|
|
30
|
-
const liveColl = live.collection(name);
|
|
31
|
-
const expectedColl = expected.collection(name);
|
|
32
|
-
|
|
33
|
-
if (!liveColl && expectedColl) {
|
|
34
|
-
issues.push({
|
|
35
|
-
kind: 'missing_table',
|
|
36
|
-
table: name,
|
|
37
|
-
message: `Collection "${name}" is missing from the database`,
|
|
38
|
-
});
|
|
39
|
-
collectionChildren.push({
|
|
40
|
-
status: 'fail',
|
|
41
|
-
kind: 'collection',
|
|
42
|
-
name,
|
|
43
|
-
contractPath: `storage.collections.${name}`,
|
|
44
|
-
code: 'MISSING_COLLECTION',
|
|
45
|
-
message: `Collection "${name}" is missing`,
|
|
46
|
-
expected: name,
|
|
47
|
-
actual: null,
|
|
48
|
-
children: [],
|
|
49
|
-
});
|
|
50
|
-
fail++;
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (liveColl && !expectedColl) {
|
|
55
|
-
const status = strict ? 'fail' : 'warn';
|
|
56
|
-
issues.push({
|
|
57
|
-
kind: 'extra_table',
|
|
58
|
-
table: name,
|
|
59
|
-
message: `Extra collection "${name}" exists in the database but not in the contract`,
|
|
60
|
-
});
|
|
61
|
-
collectionChildren.push({
|
|
62
|
-
status,
|
|
63
|
-
kind: 'collection',
|
|
64
|
-
name,
|
|
65
|
-
contractPath: `storage.collections.${name}`,
|
|
66
|
-
code: 'EXTRA_COLLECTION',
|
|
67
|
-
message: `Extra collection "${name}" found`,
|
|
68
|
-
expected: null,
|
|
69
|
-
actual: name,
|
|
70
|
-
children: [],
|
|
71
|
-
});
|
|
72
|
-
if (status === 'fail') fail++;
|
|
73
|
-
else warn++;
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const lc = liveColl as MongoSchemaCollection;
|
|
78
|
-
const ec = expectedColl as MongoSchemaCollection;
|
|
79
|
-
const indexChildren = diffIndexes(name, lc, ec, strict, issues);
|
|
80
|
-
const validatorChildren = diffValidator(name, lc, ec, strict, issues);
|
|
81
|
-
const optionsChildren = diffOptions(name, lc, ec, strict, issues);
|
|
82
|
-
const children = [...indexChildren, ...validatorChildren, ...optionsChildren];
|
|
83
|
-
|
|
84
|
-
const worstStatus = children.reduce<'pass' | 'warn' | 'fail'>(
|
|
85
|
-
(s, c) => (c.status === 'fail' ? 'fail' : c.status === 'warn' && s !== 'fail' ? 'warn' : s),
|
|
86
|
-
'pass',
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
for (const c of children) {
|
|
90
|
-
if (c.status === 'pass') pass++;
|
|
91
|
-
else if (c.status === 'warn') warn++;
|
|
92
|
-
else fail++;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (children.length === 0) {
|
|
96
|
-
pass++;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
collectionChildren.push({
|
|
100
|
-
status: worstStatus,
|
|
101
|
-
kind: 'collection',
|
|
102
|
-
name,
|
|
103
|
-
contractPath: `storage.collections.${name}`,
|
|
104
|
-
code: worstStatus === 'pass' ? 'MATCH' : 'DRIFT',
|
|
105
|
-
message:
|
|
106
|
-
worstStatus === 'pass' ? `Collection "${name}" matches` : `Collection "${name}" has drift`,
|
|
107
|
-
expected: name,
|
|
108
|
-
actual: name,
|
|
109
|
-
children,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const rootStatus = fail > 0 ? 'fail' : warn > 0 ? 'warn' : 'pass';
|
|
114
|
-
const totalNodes = pass + warn + fail + collectionChildren.length;
|
|
115
|
-
|
|
116
|
-
const root: SchemaVerificationNode = {
|
|
117
|
-
status: rootStatus,
|
|
118
|
-
kind: 'root',
|
|
119
|
-
name: 'mongo-schema',
|
|
120
|
-
contractPath: 'storage',
|
|
121
|
-
code: rootStatus === 'pass' ? 'MATCH' : 'DRIFT',
|
|
122
|
-
message: rootStatus === 'pass' ? 'Schema matches' : 'Schema has drift',
|
|
123
|
-
expected: null,
|
|
124
|
-
actual: null,
|
|
125
|
-
children: collectionChildren,
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
return { root, issues, counts: { pass, warn, fail, totalNodes } };
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function buildIndexLookupKey(index: MongoSchemaIndex): string {
|
|
132
|
-
const keys = index.keys.map((k) => `${k.field}:${k.direction}`).join(',');
|
|
133
|
-
const opts = [
|
|
134
|
-
index.unique ? 'unique' : '',
|
|
135
|
-
index.sparse ? 'sparse' : '',
|
|
136
|
-
index.expireAfterSeconds != null ? `ttl:${index.expireAfterSeconds}` : '',
|
|
137
|
-
index.partialFilterExpression ? `pfe:${canonicalize(index.partialFilterExpression)}` : '',
|
|
138
|
-
index.wildcardProjection ? `wp:${canonicalize(index.wildcardProjection)}` : '',
|
|
139
|
-
index.collation ? `col:${canonicalize(index.collation)}` : '',
|
|
140
|
-
index.weights ? `wt:${canonicalize(index.weights)}` : '',
|
|
141
|
-
index.default_language ? `dl:${index.default_language}` : '',
|
|
142
|
-
index.language_override ? `lo:${index.language_override}` : '',
|
|
143
|
-
]
|
|
144
|
-
.filter(Boolean)
|
|
145
|
-
.join(';');
|
|
146
|
-
return opts ? `${keys}|${opts}` : keys;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function formatIndexName(index: MongoSchemaIndex): string {
|
|
150
|
-
return index.keys.map((k) => `${k.field}:${k.direction}`).join(', ');
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function diffIndexes(
|
|
154
|
-
collName: string,
|
|
155
|
-
live: MongoSchemaCollection,
|
|
156
|
-
expected: MongoSchemaCollection,
|
|
157
|
-
strict: boolean,
|
|
158
|
-
issues: SchemaIssue[],
|
|
159
|
-
): SchemaVerificationNode[] {
|
|
160
|
-
const nodes: SchemaVerificationNode[] = [];
|
|
161
|
-
const liveLookup = new Map<string, MongoSchemaIndex>();
|
|
162
|
-
for (const idx of live.indexes) liveLookup.set(buildIndexLookupKey(idx), idx);
|
|
163
|
-
|
|
164
|
-
const expectedLookup = new Map<string, MongoSchemaIndex>();
|
|
165
|
-
for (const idx of expected.indexes) expectedLookup.set(buildIndexLookupKey(idx), idx);
|
|
166
|
-
|
|
167
|
-
for (const [key, idx] of expectedLookup) {
|
|
168
|
-
if (liveLookup.has(key)) {
|
|
169
|
-
nodes.push({
|
|
170
|
-
status: 'pass',
|
|
171
|
-
kind: 'index',
|
|
172
|
-
name: formatIndexName(idx),
|
|
173
|
-
contractPath: `storage.collections.${collName}.indexes`,
|
|
174
|
-
code: 'MATCH',
|
|
175
|
-
message: `Index ${formatIndexName(idx)} matches`,
|
|
176
|
-
expected: key,
|
|
177
|
-
actual: key,
|
|
178
|
-
children: [],
|
|
179
|
-
});
|
|
180
|
-
} else {
|
|
181
|
-
issues.push({
|
|
182
|
-
kind: 'index_mismatch',
|
|
183
|
-
table: collName,
|
|
184
|
-
indexOrConstraint: formatIndexName(idx),
|
|
185
|
-
message: `Index ${formatIndexName(idx)} missing on collection "${collName}"`,
|
|
186
|
-
});
|
|
187
|
-
nodes.push({
|
|
188
|
-
status: 'fail',
|
|
189
|
-
kind: 'index',
|
|
190
|
-
name: formatIndexName(idx),
|
|
191
|
-
contractPath: `storage.collections.${collName}.indexes`,
|
|
192
|
-
code: 'MISSING_INDEX',
|
|
193
|
-
message: `Index ${formatIndexName(idx)} missing`,
|
|
194
|
-
expected: key,
|
|
195
|
-
actual: null,
|
|
196
|
-
children: [],
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
for (const [key, idx] of liveLookup) {
|
|
202
|
-
if (!expectedLookup.has(key)) {
|
|
203
|
-
const status = strict ? 'fail' : 'warn';
|
|
204
|
-
issues.push({
|
|
205
|
-
kind: 'extra_index',
|
|
206
|
-
table: collName,
|
|
207
|
-
indexOrConstraint: formatIndexName(idx),
|
|
208
|
-
message: `Extra index ${formatIndexName(idx)} on collection "${collName}"`,
|
|
209
|
-
});
|
|
210
|
-
nodes.push({
|
|
211
|
-
status,
|
|
212
|
-
kind: 'index',
|
|
213
|
-
name: formatIndexName(idx),
|
|
214
|
-
contractPath: `storage.collections.${collName}.indexes`,
|
|
215
|
-
code: 'EXTRA_INDEX',
|
|
216
|
-
message: `Extra index ${formatIndexName(idx)}`,
|
|
217
|
-
expected: null,
|
|
218
|
-
actual: key,
|
|
219
|
-
children: [],
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return nodes;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function diffValidator(
|
|
228
|
-
collName: string,
|
|
229
|
-
live: MongoSchemaCollection,
|
|
230
|
-
expected: MongoSchemaCollection,
|
|
231
|
-
strict: boolean,
|
|
232
|
-
issues: SchemaIssue[],
|
|
233
|
-
): SchemaVerificationNode[] {
|
|
234
|
-
if (!live.validator && !expected.validator) return [];
|
|
235
|
-
|
|
236
|
-
if (expected.validator && !live.validator) {
|
|
237
|
-
issues.push({
|
|
238
|
-
kind: 'type_missing',
|
|
239
|
-
table: collName,
|
|
240
|
-
message: `Validator missing on collection "${collName}"`,
|
|
241
|
-
});
|
|
242
|
-
return [
|
|
243
|
-
{
|
|
244
|
-
status: 'fail',
|
|
245
|
-
kind: 'validator',
|
|
246
|
-
name: 'validator',
|
|
247
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
248
|
-
code: 'MISSING_VALIDATOR',
|
|
249
|
-
message: 'Validator missing',
|
|
250
|
-
expected: canonicalize(expected.validator.jsonSchema),
|
|
251
|
-
actual: null,
|
|
252
|
-
children: [],
|
|
253
|
-
},
|
|
254
|
-
];
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (!expected.validator && live.validator) {
|
|
258
|
-
const status = strict ? 'fail' : 'warn';
|
|
259
|
-
issues.push({
|
|
260
|
-
kind: 'extra_validator',
|
|
261
|
-
table: collName,
|
|
262
|
-
message: `Extra validator on collection "${collName}"`,
|
|
263
|
-
});
|
|
264
|
-
return [
|
|
265
|
-
{
|
|
266
|
-
status,
|
|
267
|
-
kind: 'validator',
|
|
268
|
-
name: 'validator',
|
|
269
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
270
|
-
code: 'EXTRA_VALIDATOR',
|
|
271
|
-
message: 'Extra validator found',
|
|
272
|
-
expected: null,
|
|
273
|
-
actual: canonicalize(live.validator.jsonSchema),
|
|
274
|
-
children: [],
|
|
275
|
-
},
|
|
276
|
-
];
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const liveVal = live.validator as NonNullable<typeof live.validator>;
|
|
280
|
-
const expectedVal = expected.validator as NonNullable<typeof expected.validator>;
|
|
281
|
-
const liveSchema = canonicalize(liveVal.jsonSchema);
|
|
282
|
-
const expectedSchema = canonicalize(expectedVal.jsonSchema);
|
|
283
|
-
|
|
284
|
-
if (
|
|
285
|
-
liveSchema !== expectedSchema ||
|
|
286
|
-
liveVal.validationLevel !== expectedVal.validationLevel ||
|
|
287
|
-
liveVal.validationAction !== expectedVal.validationAction
|
|
288
|
-
) {
|
|
289
|
-
issues.push({
|
|
290
|
-
kind: 'type_mismatch',
|
|
291
|
-
table: collName,
|
|
292
|
-
expected: expectedSchema,
|
|
293
|
-
actual: liveSchema,
|
|
294
|
-
message: `Validator mismatch on collection "${collName}"`,
|
|
295
|
-
});
|
|
296
|
-
return [
|
|
297
|
-
{
|
|
298
|
-
status: 'fail',
|
|
299
|
-
kind: 'validator',
|
|
300
|
-
name: 'validator',
|
|
301
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
302
|
-
code: 'VALIDATOR_MISMATCH',
|
|
303
|
-
message: 'Validator mismatch',
|
|
304
|
-
expected: {
|
|
305
|
-
jsonSchema: expectedVal.jsonSchema,
|
|
306
|
-
validationLevel: expectedVal.validationLevel,
|
|
307
|
-
validationAction: expectedVal.validationAction,
|
|
308
|
-
},
|
|
309
|
-
actual: {
|
|
310
|
-
jsonSchema: liveVal.jsonSchema,
|
|
311
|
-
validationLevel: liveVal.validationLevel,
|
|
312
|
-
validationAction: liveVal.validationAction,
|
|
313
|
-
},
|
|
314
|
-
children: [],
|
|
315
|
-
},
|
|
316
|
-
];
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return [
|
|
320
|
-
{
|
|
321
|
-
status: 'pass',
|
|
322
|
-
kind: 'validator',
|
|
323
|
-
name: 'validator',
|
|
324
|
-
contractPath: `storage.collections.${collName}.validator`,
|
|
325
|
-
code: 'MATCH',
|
|
326
|
-
message: 'Validator matches',
|
|
327
|
-
expected: expectedSchema,
|
|
328
|
-
actual: liveSchema,
|
|
329
|
-
children: [],
|
|
330
|
-
},
|
|
331
|
-
];
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function diffOptions(
|
|
335
|
-
collName: string,
|
|
336
|
-
live: MongoSchemaCollection,
|
|
337
|
-
expected: MongoSchemaCollection,
|
|
338
|
-
strict: boolean,
|
|
339
|
-
issues: SchemaIssue[],
|
|
340
|
-
): SchemaVerificationNode[] {
|
|
341
|
-
if (!live.options && !expected.options) return [];
|
|
342
|
-
|
|
343
|
-
if (!expected.options && live.options) {
|
|
344
|
-
const status = strict ? 'fail' : 'warn';
|
|
345
|
-
issues.push({
|
|
346
|
-
kind: 'type_mismatch',
|
|
347
|
-
table: collName,
|
|
348
|
-
actual: canonicalize(live.options),
|
|
349
|
-
message: `Extra collection options on "${collName}"`,
|
|
350
|
-
});
|
|
351
|
-
return [
|
|
352
|
-
{
|
|
353
|
-
status,
|
|
354
|
-
kind: 'options',
|
|
355
|
-
name: 'options',
|
|
356
|
-
contractPath: `storage.collections.${collName}.options`,
|
|
357
|
-
code: 'EXTRA_OPTIONS',
|
|
358
|
-
message: 'Extra collection options found',
|
|
359
|
-
expected: null,
|
|
360
|
-
actual: live.options,
|
|
361
|
-
children: [],
|
|
362
|
-
},
|
|
363
|
-
];
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
if (deepEqual(live.options, expected.options)) {
|
|
367
|
-
return [
|
|
368
|
-
{
|
|
369
|
-
status: 'pass',
|
|
370
|
-
kind: 'options',
|
|
371
|
-
name: 'options',
|
|
372
|
-
contractPath: `storage.collections.${collName}.options`,
|
|
373
|
-
code: 'MATCH',
|
|
374
|
-
message: 'Collection options match',
|
|
375
|
-
expected: canonicalize(expected.options),
|
|
376
|
-
actual: canonicalize(live.options),
|
|
377
|
-
children: [],
|
|
378
|
-
},
|
|
379
|
-
];
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
issues.push({
|
|
383
|
-
kind: 'type_mismatch',
|
|
384
|
-
table: collName,
|
|
385
|
-
expected: canonicalize(expected.options),
|
|
386
|
-
actual: canonicalize(live.options),
|
|
387
|
-
message: `Collection options mismatch on "${collName}"`,
|
|
388
|
-
});
|
|
389
|
-
return [
|
|
390
|
-
{
|
|
391
|
-
status: 'fail',
|
|
392
|
-
kind: 'options',
|
|
393
|
-
name: 'options',
|
|
394
|
-
contractPath: `storage.collections.${collName}.options`,
|
|
395
|
-
code: 'OPTIONS_MISMATCH',
|
|
396
|
-
message: 'Collection options mismatch',
|
|
397
|
-
expected: expected.options,
|
|
398
|
-
actual: live.options,
|
|
399
|
-
children: [],
|
|
400
|
-
},
|
|
401
|
-
];
|
|
402
|
-
}
|