@prisma-next/adapter-mongo 0.4.0-dev.1 → 0.4.0-dev.3
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/{codecs-9xSaT_DN.mjs → codecs-DGN3pWRt.mjs} +2 -2
- package/dist/codecs-DGN3pWRt.mjs.map +1 -0
- package/dist/control.d.mts +6 -45
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +30 -795
- package/dist/control.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +14 -14
- package/src/core/codecs.ts +1 -1
- package/src/core/runner-deps.ts +37 -0
- package/src/exports/control.ts +9 -11
- package/dist/codecs-9xSaT_DN.mjs.map +0 -1
- package/src/core/contract-to-schema.ts +0 -63
- package/src/core/ddl-formatter.ts +0 -112
- package/src/core/filter-evaluator.ts +0 -84
- package/src/core/mongo-ops-serializer.ts +0 -275
- package/src/core/mongo-planner.ts +0 -470
- package/src/core/mongo-runner.ts +0 -277
package/dist/control.mjs
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { a as mongoObjectIdCodec, i as mongoInt32Codec, n as mongoDateCodec, o as mongoStringCodec, r as mongoDoubleCodec, s as mongoVectorCodec, t as mongoBooleanCodec } from "./codecs-
|
|
1
|
+
import { a as mongoObjectIdCodec, i as mongoInt32Codec, n as mongoDateCodec, o as mongoStringCodec, r as mongoDoubleCodec, s as mongoVectorCodec, t as mongoBooleanCodec } from "./codecs-DGN3pWRt.mjs";
|
|
2
2
|
import { MongoServerError } from "mongodb";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { type } from "arktype";
|
|
7
|
-
import { notOk, ok } from "@prisma-next/utils/result";
|
|
3
|
+
import { keysToKeySpec } from "@prisma-next/mongo-query-ast/control";
|
|
4
|
+
import { MongoSchemaCollection, MongoSchemaCollectionOptions, MongoSchemaIR, MongoSchemaIndex, MongoSchemaValidator } from "@prisma-next/mongo-schema-ir";
|
|
5
|
+
import { initMarker, readMarker, updateMarker, writeLedgerEntry } from "@prisma-next/target-mongo/control";
|
|
8
6
|
|
|
9
7
|
//#region src/core/command-executor.ts
|
|
10
8
|
var MongoCommandExecutor = class {
|
|
@@ -72,116 +70,6 @@ var MongoInspectionExecutor = class {
|
|
|
72
70
|
}
|
|
73
71
|
};
|
|
74
72
|
|
|
75
|
-
//#endregion
|
|
76
|
-
//#region src/core/contract-to-schema.ts
|
|
77
|
-
function convertIndex(index) {
|
|
78
|
-
return new MongoSchemaIndex({
|
|
79
|
-
keys: index.keys,
|
|
80
|
-
unique: index.unique,
|
|
81
|
-
sparse: index.sparse,
|
|
82
|
-
expireAfterSeconds: index.expireAfterSeconds,
|
|
83
|
-
partialFilterExpression: index.partialFilterExpression,
|
|
84
|
-
wildcardProjection: index.wildcardProjection,
|
|
85
|
-
collation: index.collation,
|
|
86
|
-
weights: index.weights,
|
|
87
|
-
default_language: index.default_language,
|
|
88
|
-
language_override: index.language_override
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
function convertValidator(v) {
|
|
92
|
-
return new MongoSchemaValidator({
|
|
93
|
-
jsonSchema: v.jsonSchema,
|
|
94
|
-
validationLevel: v.validationLevel,
|
|
95
|
-
validationAction: v.validationAction
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
function convertOptions(o) {
|
|
99
|
-
return new MongoSchemaCollectionOptions(o);
|
|
100
|
-
}
|
|
101
|
-
function convertCollection(name, def) {
|
|
102
|
-
return new MongoSchemaCollection({
|
|
103
|
-
name,
|
|
104
|
-
indexes: (def.indexes ?? []).map(convertIndex),
|
|
105
|
-
...def.validator != null && { validator: convertValidator(def.validator) },
|
|
106
|
-
...def.options != null && { options: convertOptions(def.options) }
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
function contractToMongoSchemaIR(contract) {
|
|
110
|
-
if (!contract) return new MongoSchemaIR([]);
|
|
111
|
-
return new MongoSchemaIR(Object.entries(contract.storage.collections).map(([name, def]) => convertCollection(name, def)));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
//#endregion
|
|
115
|
-
//#region src/core/ddl-formatter.ts
|
|
116
|
-
function formatKeySpec(keys) {
|
|
117
|
-
return `{ ${keys.map((k) => `${JSON.stringify(k.field)}: ${JSON.stringify(k.direction)}`).join(", ")} }`;
|
|
118
|
-
}
|
|
119
|
-
function formatOptions(cmd) {
|
|
120
|
-
const parts = [];
|
|
121
|
-
if (cmd.unique) parts.push("unique: true");
|
|
122
|
-
if (cmd.sparse) parts.push("sparse: true");
|
|
123
|
-
if (cmd.expireAfterSeconds !== void 0) parts.push(`expireAfterSeconds: ${cmd.expireAfterSeconds}`);
|
|
124
|
-
if (cmd.name) parts.push(`name: ${JSON.stringify(cmd.name)}`);
|
|
125
|
-
if (cmd.collation) parts.push(`collation: ${JSON.stringify(cmd.collation)}`);
|
|
126
|
-
if (cmd.weights) parts.push(`weights: ${JSON.stringify(cmd.weights)}`);
|
|
127
|
-
if (cmd.default_language) parts.push(`default_language: ${JSON.stringify(cmd.default_language)}`);
|
|
128
|
-
if (cmd.language_override) parts.push(`language_override: ${JSON.stringify(cmd.language_override)}`);
|
|
129
|
-
if (cmd.wildcardProjection) parts.push(`wildcardProjection: ${JSON.stringify(cmd.wildcardProjection)}`);
|
|
130
|
-
if (cmd.partialFilterExpression) parts.push(`partialFilterExpression: ${JSON.stringify(cmd.partialFilterExpression)}`);
|
|
131
|
-
if (parts.length === 0) return void 0;
|
|
132
|
-
return `{ ${parts.join(", ")} }`;
|
|
133
|
-
}
|
|
134
|
-
function formatCreateCollectionOptions(cmd) {
|
|
135
|
-
const parts = [];
|
|
136
|
-
if (cmd.capped) parts.push("capped: true");
|
|
137
|
-
if (cmd.size !== void 0) parts.push(`size: ${cmd.size}`);
|
|
138
|
-
if (cmd.max !== void 0) parts.push(`max: ${cmd.max}`);
|
|
139
|
-
if (cmd.timeseries) parts.push(`timeseries: ${JSON.stringify(cmd.timeseries)}`);
|
|
140
|
-
if (cmd.collation) parts.push(`collation: ${JSON.stringify(cmd.collation)}`);
|
|
141
|
-
if (cmd.clusteredIndex) parts.push(`clusteredIndex: ${JSON.stringify(cmd.clusteredIndex)}`);
|
|
142
|
-
if (cmd.validator) parts.push(`validator: ${JSON.stringify(cmd.validator)}`);
|
|
143
|
-
if (cmd.validationLevel) parts.push(`validationLevel: ${JSON.stringify(cmd.validationLevel)}`);
|
|
144
|
-
if (cmd.validationAction) parts.push(`validationAction: ${JSON.stringify(cmd.validationAction)}`);
|
|
145
|
-
if (cmd.changeStreamPreAndPostImages) parts.push(`changeStreamPreAndPostImages: ${JSON.stringify(cmd.changeStreamPreAndPostImages)}`);
|
|
146
|
-
if (parts.length === 0) return void 0;
|
|
147
|
-
return `{ ${parts.join(", ")} }`;
|
|
148
|
-
}
|
|
149
|
-
var MongoDdlCommandFormatter = class {
|
|
150
|
-
createIndex(cmd) {
|
|
151
|
-
const keySpec = formatKeySpec(cmd.keys);
|
|
152
|
-
const opts = formatOptions(cmd);
|
|
153
|
-
return opts ? `db.${cmd.collection}.createIndex(${keySpec}, ${opts})` : `db.${cmd.collection}.createIndex(${keySpec})`;
|
|
154
|
-
}
|
|
155
|
-
dropIndex(cmd) {
|
|
156
|
-
return `db.${cmd.collection}.dropIndex(${JSON.stringify(cmd.name)})`;
|
|
157
|
-
}
|
|
158
|
-
createCollection(cmd) {
|
|
159
|
-
const opts = formatCreateCollectionOptions(cmd);
|
|
160
|
-
return opts ? `db.createCollection(${JSON.stringify(cmd.collection)}, ${opts})` : `db.createCollection(${JSON.stringify(cmd.collection)})`;
|
|
161
|
-
}
|
|
162
|
-
dropCollection(cmd) {
|
|
163
|
-
return `db.${cmd.collection}.drop()`;
|
|
164
|
-
}
|
|
165
|
-
collMod(cmd) {
|
|
166
|
-
const parts = [`collMod: ${JSON.stringify(cmd.collection)}`];
|
|
167
|
-
if (cmd.validator) parts.push(`validator: ${JSON.stringify(cmd.validator)}`);
|
|
168
|
-
if (cmd.validationLevel) parts.push(`validationLevel: ${JSON.stringify(cmd.validationLevel)}`);
|
|
169
|
-
if (cmd.validationAction) parts.push(`validationAction: ${JSON.stringify(cmd.validationAction)}`);
|
|
170
|
-
if (cmd.changeStreamPreAndPostImages) parts.push(`changeStreamPreAndPostImages: ${JSON.stringify(cmd.changeStreamPreAndPostImages)}`);
|
|
171
|
-
return `db.runCommand({ ${parts.join(", ")} })`;
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
const formatter = new MongoDdlCommandFormatter();
|
|
175
|
-
function formatMongoOperations(operations) {
|
|
176
|
-
const statements = [];
|
|
177
|
-
for (const operation of operations) {
|
|
178
|
-
const candidate = operation;
|
|
179
|
-
if (!("execute" in candidate) || !Array.isArray(candidate["execute"])) continue;
|
|
180
|
-
for (const step of candidate["execute"]) if (step.command && typeof step.command.accept === "function") statements.push(step.command.accept(formatter));
|
|
181
|
-
}
|
|
182
|
-
return statements;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
73
|
//#endregion
|
|
186
74
|
//#region src/core/introspect-schema.ts
|
|
187
75
|
const PRISMA_MIGRATIONS_COLLECTION = "_prisma_migrations";
|
|
@@ -252,10 +140,10 @@ async function introspectSchema(db) {
|
|
|
252
140
|
const collections = [];
|
|
253
141
|
for (const info of collectionInfos) {
|
|
254
142
|
const name = info["name"];
|
|
255
|
-
const type
|
|
143
|
+
const type = info["type"];
|
|
256
144
|
if (name === PRISMA_MIGRATIONS_COLLECTION) continue;
|
|
257
145
|
if (name.startsWith("system.")) continue;
|
|
258
|
-
if (type
|
|
146
|
+
if (type === "view") continue;
|
|
259
147
|
const indexes = (await db.collection(name).listIndexes().toArray()).filter((doc) => !isDefaultIdIndex(doc)).map(parseIndex);
|
|
260
148
|
const validator = parseValidator("options" in info ? info["options"] : {});
|
|
261
149
|
const options = parseCollectionOptions(info);
|
|
@@ -292,686 +180,25 @@ function createMongoControlDriver(db, client) {
|
|
|
292
180
|
}
|
|
293
181
|
|
|
294
182
|
//#endregion
|
|
295
|
-
//#region src/core/
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
field: "string",
|
|
301
|
-
direction: type("1 | -1 | \"text\" | \"2dsphere\" | \"2d\" | \"hashed\"")
|
|
302
|
-
}).array().atLeastLength(1),
|
|
303
|
-
"unique?": "boolean",
|
|
304
|
-
"sparse?": "boolean",
|
|
305
|
-
"expireAfterSeconds?": "number",
|
|
306
|
-
"partialFilterExpression?": "Record<string, unknown>",
|
|
307
|
-
"name?": "string",
|
|
308
|
-
"wildcardProjection?": "Record<string, unknown>",
|
|
309
|
-
"collation?": "Record<string, unknown>",
|
|
310
|
-
"weights?": "Record<string, unknown>",
|
|
311
|
-
"default_language?": "string",
|
|
312
|
-
"language_override?": "string"
|
|
313
|
-
});
|
|
314
|
-
const DropIndexJson = type({
|
|
315
|
-
kind: "\"dropIndex\"",
|
|
316
|
-
collection: "string",
|
|
317
|
-
name: "string"
|
|
318
|
-
});
|
|
319
|
-
const CreateCollectionJson = type({
|
|
320
|
-
kind: "\"createCollection\"",
|
|
321
|
-
collection: "string",
|
|
322
|
-
"validator?": "Record<string, unknown>",
|
|
323
|
-
"validationLevel?": "\"strict\" | \"moderate\"",
|
|
324
|
-
"validationAction?": "\"error\" | \"warn\"",
|
|
325
|
-
"capped?": "boolean",
|
|
326
|
-
"size?": "number",
|
|
327
|
-
"max?": "number",
|
|
328
|
-
"timeseries?": "Record<string, unknown>",
|
|
329
|
-
"collation?": "Record<string, unknown>",
|
|
330
|
-
"changeStreamPreAndPostImages?": "Record<string, unknown>",
|
|
331
|
-
"clusteredIndex?": "Record<string, unknown>"
|
|
332
|
-
});
|
|
333
|
-
const DropCollectionJson = type({
|
|
334
|
-
kind: "\"dropCollection\"",
|
|
335
|
-
collection: "string"
|
|
336
|
-
});
|
|
337
|
-
const CollModJson = type({
|
|
338
|
-
kind: "\"collMod\"",
|
|
339
|
-
collection: "string",
|
|
340
|
-
"validator?": "Record<string, unknown>",
|
|
341
|
-
"validationLevel?": "\"strict\" | \"moderate\"",
|
|
342
|
-
"validationAction?": "\"error\" | \"warn\"",
|
|
343
|
-
"changeStreamPreAndPostImages?": "Record<string, unknown>"
|
|
344
|
-
});
|
|
345
|
-
const ListIndexesJson = type({
|
|
346
|
-
kind: "\"listIndexes\"",
|
|
347
|
-
collection: "string"
|
|
348
|
-
});
|
|
349
|
-
const ListCollectionsJson = type({ kind: "\"listCollections\"" });
|
|
350
|
-
const FieldFilterJson = type({
|
|
351
|
-
kind: "\"field\"",
|
|
352
|
-
field: "string",
|
|
353
|
-
op: "string",
|
|
354
|
-
value: "unknown"
|
|
355
|
-
});
|
|
356
|
-
const ExistsFilterJson = type({
|
|
357
|
-
kind: "\"exists\"",
|
|
358
|
-
field: "string",
|
|
359
|
-
exists: "boolean"
|
|
360
|
-
});
|
|
361
|
-
const CheckJson = type({
|
|
362
|
-
description: "string",
|
|
363
|
-
source: "Record<string, unknown>",
|
|
364
|
-
filter: "Record<string, unknown>",
|
|
365
|
-
expect: "\"exists\" | \"notExists\""
|
|
366
|
-
});
|
|
367
|
-
const StepJson = type({
|
|
368
|
-
description: "string",
|
|
369
|
-
command: "Record<string, unknown>"
|
|
370
|
-
});
|
|
371
|
-
const OperationJson = type({
|
|
372
|
-
id: "string",
|
|
373
|
-
label: "string",
|
|
374
|
-
operationClass: "\"additive\" | \"widening\" | \"destructive\"",
|
|
375
|
-
precheck: "Record<string, unknown>[]",
|
|
376
|
-
execute: "Record<string, unknown>[]",
|
|
377
|
-
postcheck: "Record<string, unknown>[]"
|
|
378
|
-
});
|
|
379
|
-
function validate(schema, data, context) {
|
|
380
|
-
try {
|
|
381
|
-
return schema.assert(data);
|
|
382
|
-
} catch (error) {
|
|
383
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
384
|
-
throw new Error(`Invalid ${context}: ${message}`);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
function deserializeFilterExpr(json) {
|
|
388
|
-
const record = json;
|
|
389
|
-
const kind = record["kind"];
|
|
390
|
-
switch (kind) {
|
|
391
|
-
case "field": {
|
|
392
|
-
const data = validate(FieldFilterJson, json, "field filter");
|
|
393
|
-
return MongoFieldFilter.of(data.field, data.op, data.value);
|
|
394
|
-
}
|
|
395
|
-
case "and": {
|
|
396
|
-
const exprs = record["exprs"];
|
|
397
|
-
if (!Array.isArray(exprs)) throw new Error("Invalid and filter: missing exprs array");
|
|
398
|
-
return MongoAndExpr.of(exprs.map(deserializeFilterExpr));
|
|
399
|
-
}
|
|
400
|
-
case "or": {
|
|
401
|
-
const exprs = record["exprs"];
|
|
402
|
-
if (!Array.isArray(exprs)) throw new Error("Invalid or filter: missing exprs array");
|
|
403
|
-
return MongoOrExpr.of(exprs.map(deserializeFilterExpr));
|
|
404
|
-
}
|
|
405
|
-
case "not": {
|
|
406
|
-
const expr = record["expr"];
|
|
407
|
-
if (!expr || typeof expr !== "object") throw new Error("Invalid not filter: missing expr");
|
|
408
|
-
return new MongoNotExpr(deserializeFilterExpr(expr));
|
|
409
|
-
}
|
|
410
|
-
case "exists": {
|
|
411
|
-
const data = validate(ExistsFilterJson, json, "exists filter");
|
|
412
|
-
return new MongoExistsExpr(data.field, data.exists);
|
|
413
|
-
}
|
|
414
|
-
default: throw new Error(`Unknown filter expression kind: ${kind}`);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
function deserializeDdlCommand(json) {
|
|
418
|
-
const kind = json["kind"];
|
|
419
|
-
switch (kind) {
|
|
420
|
-
case "createIndex": {
|
|
421
|
-
const data = validate(CreateIndexJson, json, "createIndex command");
|
|
422
|
-
return new CreateIndexCommand(data.collection, data.keys, {
|
|
423
|
-
unique: data.unique,
|
|
424
|
-
sparse: data.sparse,
|
|
425
|
-
expireAfterSeconds: data.expireAfterSeconds,
|
|
426
|
-
partialFilterExpression: data.partialFilterExpression,
|
|
427
|
-
name: data.name,
|
|
428
|
-
wildcardProjection: data.wildcardProjection,
|
|
429
|
-
collation: data.collation,
|
|
430
|
-
weights: data.weights,
|
|
431
|
-
default_language: data.default_language,
|
|
432
|
-
language_override: data.language_override
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
case "dropIndex": {
|
|
436
|
-
const data = validate(DropIndexJson, json, "dropIndex command");
|
|
437
|
-
return new DropIndexCommand(data.collection, data.name);
|
|
438
|
-
}
|
|
439
|
-
case "createCollection": {
|
|
440
|
-
const data = validate(CreateCollectionJson, json, "createCollection command");
|
|
441
|
-
return new CreateCollectionCommand(data.collection, {
|
|
442
|
-
validator: data.validator,
|
|
443
|
-
validationLevel: data.validationLevel,
|
|
444
|
-
validationAction: data.validationAction,
|
|
445
|
-
capped: data.capped,
|
|
446
|
-
size: data.size,
|
|
447
|
-
max: data.max,
|
|
448
|
-
timeseries: data.timeseries,
|
|
449
|
-
collation: data.collation,
|
|
450
|
-
changeStreamPreAndPostImages: data.changeStreamPreAndPostImages,
|
|
451
|
-
clusteredIndex: data.clusteredIndex
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
case "dropCollection": return new DropCollectionCommand(validate(DropCollectionJson, json, "dropCollection command").collection);
|
|
455
|
-
case "collMod": {
|
|
456
|
-
const data = validate(CollModJson, json, "collMod command");
|
|
457
|
-
return new CollModCommand(data.collection, {
|
|
458
|
-
validator: data.validator,
|
|
459
|
-
validationLevel: data.validationLevel,
|
|
460
|
-
validationAction: data.validationAction,
|
|
461
|
-
changeStreamPreAndPostImages: data.changeStreamPreAndPostImages
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
default: throw new Error(`Unknown DDL command kind: ${kind}`);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
function deserializeInspectionCommand(json) {
|
|
468
|
-
const kind = json["kind"];
|
|
469
|
-
switch (kind) {
|
|
470
|
-
case "listIndexes": return new ListIndexesCommand(validate(ListIndexesJson, json, "listIndexes command").collection);
|
|
471
|
-
case "listCollections":
|
|
472
|
-
validate(ListCollectionsJson, json, "listCollections command");
|
|
473
|
-
return new ListCollectionsCommand();
|
|
474
|
-
default: throw new Error(`Unknown inspection command kind: ${kind}`);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
function deserializeCheck(json) {
|
|
478
|
-
const data = validate(CheckJson, json, "migration check");
|
|
479
|
-
return {
|
|
480
|
-
description: data.description,
|
|
481
|
-
source: deserializeInspectionCommand(data.source),
|
|
482
|
-
filter: deserializeFilterExpr(data.filter),
|
|
483
|
-
expect: data.expect
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
function deserializeStep(json) {
|
|
487
|
-
const data = validate(StepJson, json, "migration step");
|
|
488
|
-
return {
|
|
489
|
-
description: data.description,
|
|
490
|
-
command: deserializeDdlCommand(data.command)
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
function deserializeMongoOp(json) {
|
|
494
|
-
const data = validate(OperationJson, json, "migration operation");
|
|
495
|
-
return {
|
|
496
|
-
id: data.id,
|
|
497
|
-
label: data.label,
|
|
498
|
-
operationClass: data.operationClass,
|
|
499
|
-
precheck: data.precheck.map(deserializeCheck),
|
|
500
|
-
execute: data.execute.map(deserializeStep),
|
|
501
|
-
postcheck: data.postcheck.map(deserializeCheck)
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
function deserializeMongoOps(json) {
|
|
505
|
-
return json.map(deserializeMongoOp);
|
|
506
|
-
}
|
|
507
|
-
function serializeMongoOps(ops) {
|
|
508
|
-
return JSON.stringify(ops, null, 2);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
//#endregion
|
|
512
|
-
//#region src/core/mongo-planner.ts
|
|
513
|
-
function buildIndexLookupKey(index) {
|
|
514
|
-
const keys = index.keys.map((k) => `${k.field}:${k.direction}`).join(",");
|
|
515
|
-
const opts = [
|
|
516
|
-
index.unique ? "unique" : "",
|
|
517
|
-
index.sparse ? "sparse" : "",
|
|
518
|
-
index.expireAfterSeconds != null ? `ttl:${index.expireAfterSeconds}` : "",
|
|
519
|
-
index.partialFilterExpression ? `pfe:${canonicalize(index.partialFilterExpression)}` : "",
|
|
520
|
-
index.wildcardProjection ? `wp:${canonicalize(index.wildcardProjection)}` : "",
|
|
521
|
-
index.collation ? `col:${canonicalize(index.collation)}` : "",
|
|
522
|
-
index.weights ? `wt:${canonicalize(index.weights)}` : "",
|
|
523
|
-
index.default_language ? `dl:${index.default_language}` : "",
|
|
524
|
-
index.language_override ? `lo:${index.language_override}` : ""
|
|
525
|
-
].filter(Boolean).join(";");
|
|
526
|
-
return opts ? `${keys}|${opts}` : keys;
|
|
527
|
-
}
|
|
528
|
-
function formatKeys(keys) {
|
|
529
|
-
return keys.map((k) => `${k.field}:${k.direction}`).join(", ");
|
|
530
|
-
}
|
|
531
|
-
function isTextIndex(keys) {
|
|
532
|
-
return keys.some((k) => k.direction === "text");
|
|
533
|
-
}
|
|
534
|
-
function planCreateIndex(collection, index) {
|
|
535
|
-
const { keys } = index;
|
|
536
|
-
const name = defaultMongoIndexName(keys);
|
|
537
|
-
const keyFilter = isTextIndex(keys) ? MongoFieldFilter.eq("key._fts", "text") : MongoFieldFilter.eq("key", keysToKeySpec(keys));
|
|
538
|
-
const fullFilter = index.unique ? MongoAndExpr.of([keyFilter, MongoFieldFilter.eq("unique", true)]) : keyFilter;
|
|
539
|
-
return {
|
|
540
|
-
id: buildIndexOpId("create", collection, keys),
|
|
541
|
-
label: `Create index on ${collection} (${formatKeys(keys)})`,
|
|
542
|
-
operationClass: "additive",
|
|
543
|
-
precheck: [{
|
|
544
|
-
description: `index does not already exist on ${collection}`,
|
|
545
|
-
source: new ListIndexesCommand(collection),
|
|
546
|
-
filter: keyFilter,
|
|
547
|
-
expect: "notExists"
|
|
548
|
-
}],
|
|
549
|
-
execute: [{
|
|
550
|
-
description: `create index on ${collection}`,
|
|
551
|
-
command: new CreateIndexCommand(collection, keys, {
|
|
552
|
-
unique: index.unique || void 0,
|
|
553
|
-
sparse: index.sparse,
|
|
554
|
-
expireAfterSeconds: index.expireAfterSeconds,
|
|
555
|
-
partialFilterExpression: index.partialFilterExpression,
|
|
556
|
-
wildcardProjection: index.wildcardProjection,
|
|
557
|
-
collation: index.collation,
|
|
558
|
-
weights: index.weights,
|
|
559
|
-
default_language: index.default_language,
|
|
560
|
-
language_override: index.language_override,
|
|
561
|
-
name
|
|
562
|
-
})
|
|
563
|
-
}],
|
|
564
|
-
postcheck: [{
|
|
565
|
-
description: `index exists on ${collection}`,
|
|
566
|
-
source: new ListIndexesCommand(collection),
|
|
567
|
-
filter: fullFilter,
|
|
568
|
-
expect: "exists"
|
|
569
|
-
}]
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
function planDropIndex(collection, index) {
|
|
573
|
-
const { keys } = index;
|
|
574
|
-
const indexName = defaultMongoIndexName(keys);
|
|
575
|
-
const keyFilter = isTextIndex(keys) ? MongoFieldFilter.eq("key._fts", "text") : MongoFieldFilter.eq("key", keysToKeySpec(keys));
|
|
576
|
-
return {
|
|
577
|
-
id: buildIndexOpId("drop", collection, keys),
|
|
578
|
-
label: `Drop index on ${collection} (${formatKeys(keys)})`,
|
|
579
|
-
operationClass: "destructive",
|
|
580
|
-
precheck: [{
|
|
581
|
-
description: `index exists on ${collection}`,
|
|
582
|
-
source: new ListIndexesCommand(collection),
|
|
583
|
-
filter: keyFilter,
|
|
584
|
-
expect: "exists"
|
|
585
|
-
}],
|
|
586
|
-
execute: [{
|
|
587
|
-
description: `drop index on ${collection}`,
|
|
588
|
-
command: new DropIndexCommand(collection, indexName)
|
|
589
|
-
}],
|
|
590
|
-
postcheck: [{
|
|
591
|
-
description: `index no longer exists on ${collection}`,
|
|
592
|
-
source: new ListIndexesCommand(collection),
|
|
593
|
-
filter: keyFilter,
|
|
594
|
-
expect: "notExists"
|
|
595
|
-
}]
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
function validatorsEqual(a, b) {
|
|
599
|
-
if (!a && !b) return true;
|
|
600
|
-
if (!a || !b) return false;
|
|
601
|
-
return a.validationLevel === b.validationLevel && a.validationAction === b.validationAction && canonicalize(a.jsonSchema) === canonicalize(b.jsonSchema);
|
|
602
|
-
}
|
|
603
|
-
function classifyValidatorUpdate(origin, dest) {
|
|
604
|
-
let hasDestructive = false;
|
|
605
|
-
if (canonicalize(origin.jsonSchema) !== canonicalize(dest.jsonSchema)) hasDestructive = true;
|
|
606
|
-
if (origin.validationAction !== dest.validationAction) {
|
|
607
|
-
if (dest.validationAction === "error") hasDestructive = true;
|
|
608
|
-
}
|
|
609
|
-
if (origin.validationLevel !== dest.validationLevel) {
|
|
610
|
-
if (dest.validationLevel === "strict") hasDestructive = true;
|
|
611
|
-
}
|
|
612
|
-
return hasDestructive ? "destructive" : "widening";
|
|
613
|
-
}
|
|
614
|
-
function planValidatorDiff(collName, originValidator, destValidator) {
|
|
615
|
-
if (validatorsEqual(originValidator, destValidator)) return void 0;
|
|
616
|
-
const collExistsPrecheck = {
|
|
617
|
-
description: `collection ${collName} exists`,
|
|
618
|
-
source: new ListCollectionsCommand(),
|
|
619
|
-
filter: MongoFieldFilter.eq("name", collName),
|
|
620
|
-
expect: "exists"
|
|
621
|
-
};
|
|
622
|
-
if (destValidator) {
|
|
623
|
-
const operationClass = originValidator ? classifyValidatorUpdate(originValidator, destValidator) : "destructive";
|
|
624
|
-
return {
|
|
625
|
-
id: `validator.${collName}.${originValidator ? "update" : "add"}`,
|
|
626
|
-
label: `${originValidator ? "Update" : "Add"} validator on ${collName}`,
|
|
627
|
-
operationClass,
|
|
628
|
-
precheck: [collExistsPrecheck],
|
|
629
|
-
execute: [{
|
|
630
|
-
description: `set validator on ${collName}`,
|
|
631
|
-
command: new CollModCommand(collName, {
|
|
632
|
-
validator: { $jsonSchema: destValidator.jsonSchema },
|
|
633
|
-
validationLevel: destValidator.validationLevel,
|
|
634
|
-
validationAction: destValidator.validationAction
|
|
635
|
-
})
|
|
636
|
-
}],
|
|
637
|
-
postcheck: [{
|
|
638
|
-
description: `validator applied on ${collName}`,
|
|
639
|
-
source: new ListCollectionsCommand(),
|
|
640
|
-
filter: MongoAndExpr.of([
|
|
641
|
-
MongoFieldFilter.eq("name", collName),
|
|
642
|
-
MongoFieldFilter.eq("options.validationLevel", destValidator.validationLevel),
|
|
643
|
-
MongoFieldFilter.eq("options.validationAction", destValidator.validationAction)
|
|
644
|
-
]),
|
|
645
|
-
expect: "exists"
|
|
646
|
-
}]
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
return {
|
|
650
|
-
id: `validator.${collName}.remove`,
|
|
651
|
-
label: `Remove validator on ${collName}`,
|
|
652
|
-
operationClass: "widening",
|
|
653
|
-
precheck: [collExistsPrecheck],
|
|
654
|
-
execute: [{
|
|
655
|
-
description: `remove validator on ${collName}`,
|
|
656
|
-
command: new CollModCommand(collName, {
|
|
657
|
-
validator: {},
|
|
658
|
-
validationLevel: "strict",
|
|
659
|
-
validationAction: "error"
|
|
660
|
-
})
|
|
661
|
-
}],
|
|
662
|
-
postcheck: []
|
|
663
|
-
};
|
|
664
|
-
}
|
|
665
|
-
function hasImmutableOptionChange(origin, dest) {
|
|
666
|
-
if (canonicalize(origin?.capped) !== canonicalize(dest?.capped)) return "capped";
|
|
667
|
-
if (canonicalize(origin?.timeseries) !== canonicalize(dest?.timeseries)) return "timeseries";
|
|
668
|
-
if (canonicalize(origin?.collation) !== canonicalize(dest?.collation)) return "collation";
|
|
669
|
-
if (canonicalize(origin?.clusteredIndex) !== canonicalize(dest?.clusteredIndex)) return "clusteredIndex";
|
|
670
|
-
}
|
|
671
|
-
function planCreateCollection(collName, dest) {
|
|
672
|
-
const opts = dest.options;
|
|
673
|
-
const validator = dest.validator;
|
|
674
|
-
return {
|
|
675
|
-
id: `collection.${collName}.create`,
|
|
676
|
-
label: `Create collection ${collName}`,
|
|
677
|
-
operationClass: "additive",
|
|
678
|
-
precheck: [{
|
|
679
|
-
description: `collection ${collName} does not exist`,
|
|
680
|
-
source: new ListCollectionsCommand(),
|
|
681
|
-
filter: MongoFieldFilter.eq("name", collName),
|
|
682
|
-
expect: "notExists"
|
|
683
|
-
}],
|
|
684
|
-
execute: [{
|
|
685
|
-
description: `create collection ${collName}`,
|
|
686
|
-
command: new CreateCollectionCommand(collName, {
|
|
687
|
-
capped: opts?.capped ? true : void 0,
|
|
688
|
-
size: opts?.capped?.size,
|
|
689
|
-
max: opts?.capped?.max,
|
|
690
|
-
timeseries: opts?.timeseries,
|
|
691
|
-
collation: opts?.collation,
|
|
692
|
-
clusteredIndex: opts?.clusteredIndex ? {
|
|
693
|
-
key: { _id: 1 },
|
|
694
|
-
unique: true,
|
|
695
|
-
...opts.clusteredIndex.name != null ? { name: opts.clusteredIndex.name } : {}
|
|
696
|
-
} : void 0,
|
|
697
|
-
validator: validator ? { $jsonSchema: validator.jsonSchema } : void 0,
|
|
698
|
-
validationLevel: validator?.validationLevel,
|
|
699
|
-
validationAction: validator?.validationAction,
|
|
700
|
-
changeStreamPreAndPostImages: opts?.changeStreamPreAndPostImages
|
|
701
|
-
})
|
|
702
|
-
}],
|
|
703
|
-
postcheck: []
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
function planDropCollection(collName) {
|
|
707
|
-
return {
|
|
708
|
-
id: `collection.${collName}.drop`,
|
|
709
|
-
label: `Drop collection ${collName}`,
|
|
710
|
-
operationClass: "destructive",
|
|
711
|
-
precheck: [],
|
|
712
|
-
execute: [{
|
|
713
|
-
description: `drop collection ${collName}`,
|
|
714
|
-
command: new DropCollectionCommand(collName)
|
|
715
|
-
}],
|
|
716
|
-
postcheck: []
|
|
717
|
-
};
|
|
183
|
+
//#region src/core/runner-deps.ts
|
|
184
|
+
function extractDb(driver) {
|
|
185
|
+
const mongoDriver = driver;
|
|
186
|
+
if (!mongoDriver.db) throw new Error("Mongo control driver does not expose a db property. Use mongoControlDriver.create() from `@prisma-next/driver-mongo/control`.");
|
|
187
|
+
return mongoDriver.db;
|
|
718
188
|
}
|
|
719
|
-
function
|
|
720
|
-
const
|
|
721
|
-
const destCSPPI = dest?.changeStreamPreAndPostImages;
|
|
722
|
-
if (deepEqual(originCSPPI, destCSPPI)) return void 0;
|
|
189
|
+
function createMongoRunnerDeps(driver) {
|
|
190
|
+
const db = extractDb(driver);
|
|
723
191
|
return {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
}],
|
|
732
|
-
postcheck: []
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
function collectionHasOptions(coll) {
|
|
736
|
-
return !!(coll.options || coll.validator);
|
|
737
|
-
}
|
|
738
|
-
var MongoMigrationPlanner = class {
|
|
739
|
-
plan(options) {
|
|
740
|
-
const contract = options.contract;
|
|
741
|
-
const originIR = options.schema;
|
|
742
|
-
const destinationIR = contractToMongoSchemaIR(contract);
|
|
743
|
-
const collCreates = [];
|
|
744
|
-
const drops = [];
|
|
745
|
-
const creates = [];
|
|
746
|
-
const validatorOps = [];
|
|
747
|
-
const mutableOptionOps = [];
|
|
748
|
-
const collDrops = [];
|
|
749
|
-
const conflicts = [];
|
|
750
|
-
const allCollectionNames = new Set([...originIR.collectionNames, ...destinationIR.collectionNames]);
|
|
751
|
-
for (const collName of [...allCollectionNames].sort()) {
|
|
752
|
-
const originColl = originIR.collection(collName);
|
|
753
|
-
const destColl = destinationIR.collection(collName);
|
|
754
|
-
if (!originColl && destColl) {
|
|
755
|
-
if (collectionHasOptions(destColl)) collCreates.push(planCreateCollection(collName, destColl));
|
|
756
|
-
} else if (originColl && !destColl) collDrops.push(planDropCollection(collName));
|
|
757
|
-
else if (originColl && destColl) {
|
|
758
|
-
const immutableChange = hasImmutableOptionChange(originColl.options, destColl.options);
|
|
759
|
-
if (immutableChange) conflicts.push({
|
|
760
|
-
kind: "policy-violation",
|
|
761
|
-
summary: `Cannot change immutable collection option '${immutableChange}' on ${collName}`,
|
|
762
|
-
why: `MongoDB does not support modifying the '${immutableChange}' option after collection creation`
|
|
763
|
-
});
|
|
764
|
-
const mutableOp = planMutableOptionsDiff(collName, originColl.options, destColl.options);
|
|
765
|
-
if (mutableOp) mutableOptionOps.push(mutableOp);
|
|
766
|
-
const validatorOp = planValidatorDiff(collName, originColl.validator, destColl.validator);
|
|
767
|
-
if (validatorOp) validatorOps.push(validatorOp);
|
|
768
|
-
}
|
|
769
|
-
const originLookup = /* @__PURE__ */ new Map();
|
|
770
|
-
if (originColl) for (const idx of originColl.indexes) originLookup.set(buildIndexLookupKey(idx), idx);
|
|
771
|
-
const destLookup = /* @__PURE__ */ new Map();
|
|
772
|
-
if (destColl) for (const idx of destColl.indexes) destLookup.set(buildIndexLookupKey(idx), idx);
|
|
773
|
-
for (const [lookupKey, idx] of originLookup) if (!destLookup.has(lookupKey)) drops.push(planDropIndex(collName, idx));
|
|
774
|
-
for (const [lookupKey, idx] of destLookup) if (!originLookup.has(lookupKey)) creates.push(planCreateIndex(collName, idx));
|
|
192
|
+
commandExecutor: new MongoCommandExecutor(db),
|
|
193
|
+
inspectionExecutor: new MongoInspectionExecutor(db),
|
|
194
|
+
markerOps: {
|
|
195
|
+
readMarker: () => readMarker(db),
|
|
196
|
+
initMarker: (dest) => initMarker(db, dest),
|
|
197
|
+
updateMarker: (expectedFrom, dest) => updateMarker(db, expectedFrom, dest),
|
|
198
|
+
writeLedgerEntry: (entry) => writeLedgerEntry(db, entry)
|
|
775
199
|
}
|
|
776
|
-
|
|
777
|
-
kind: "failure",
|
|
778
|
-
conflicts
|
|
779
|
-
};
|
|
780
|
-
const allOps = [
|
|
781
|
-
...collCreates,
|
|
782
|
-
...drops,
|
|
783
|
-
...creates,
|
|
784
|
-
...validatorOps,
|
|
785
|
-
...mutableOptionOps,
|
|
786
|
-
...collDrops
|
|
787
|
-
];
|
|
788
|
-
for (const op of allOps) if (!options.policy.allowedOperationClasses.includes(op.operationClass)) conflicts.push({
|
|
789
|
-
kind: "policy-violation",
|
|
790
|
-
summary: `${op.operationClass} operation disallowed: ${op.label}`,
|
|
791
|
-
why: `Policy does not allow '${op.operationClass}' operations`
|
|
792
|
-
});
|
|
793
|
-
if (conflicts.length > 0) return {
|
|
794
|
-
kind: "failure",
|
|
795
|
-
conflicts
|
|
796
|
-
};
|
|
797
|
-
return {
|
|
798
|
-
kind: "success",
|
|
799
|
-
plan: {
|
|
800
|
-
targetId: "mongo",
|
|
801
|
-
destination: { storageHash: contract.storage.storageHash },
|
|
802
|
-
operations: allOps
|
|
803
|
-
}
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
};
|
|
807
|
-
|
|
808
|
-
//#endregion
|
|
809
|
-
//#region src/core/filter-evaluator.ts
|
|
810
|
-
function getNestedField(doc, path) {
|
|
811
|
-
const parts = path.split(".");
|
|
812
|
-
let current = doc;
|
|
813
|
-
for (const part of parts) {
|
|
814
|
-
if (current === null || current === void 0 || typeof current !== "object") return;
|
|
815
|
-
const record = current;
|
|
816
|
-
if (!Object.hasOwn(record, part)) return;
|
|
817
|
-
current = record[part];
|
|
818
|
-
}
|
|
819
|
-
return current;
|
|
820
|
-
}
|
|
821
|
-
function evaluateFieldOp(op, actual, expected) {
|
|
822
|
-
switch (op) {
|
|
823
|
-
case "$eq": return deepEqual(actual, expected);
|
|
824
|
-
case "$ne": return !deepEqual(actual, expected);
|
|
825
|
-
case "$gt": return typeof actual === typeof expected && actual > expected;
|
|
826
|
-
case "$gte": return typeof actual === typeof expected && actual >= expected;
|
|
827
|
-
case "$lt": return typeof actual === typeof expected && actual < expected;
|
|
828
|
-
case "$lte": return typeof actual === typeof expected && actual <= expected;
|
|
829
|
-
case "$in": return Array.isArray(expected) && expected.some((v) => deepEqual(actual, v));
|
|
830
|
-
default: throw new Error(`Unsupported filter operator in migration check: ${op}`);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
var FilterEvaluator = class {
|
|
834
|
-
doc = {};
|
|
835
|
-
evaluate(filter, doc) {
|
|
836
|
-
this.doc = doc;
|
|
837
|
-
return filter.accept(this);
|
|
838
|
-
}
|
|
839
|
-
field(expr) {
|
|
840
|
-
const value = getNestedField(this.doc, expr.field);
|
|
841
|
-
return evaluateFieldOp(expr.op, value, expr.value);
|
|
842
|
-
}
|
|
843
|
-
and(expr) {
|
|
844
|
-
return expr.exprs.every((child) => child.accept(this));
|
|
845
|
-
}
|
|
846
|
-
or(expr) {
|
|
847
|
-
return expr.exprs.some((child) => child.accept(this));
|
|
848
|
-
}
|
|
849
|
-
not(expr) {
|
|
850
|
-
return !expr.expr.accept(this);
|
|
851
|
-
}
|
|
852
|
-
exists(expr) {
|
|
853
|
-
const has = getNestedField(this.doc, expr.field) !== void 0;
|
|
854
|
-
return expr.exists ? has : !has;
|
|
855
|
-
}
|
|
856
|
-
expr(_expr) {
|
|
857
|
-
throw new Error("Aggregation expression filters are not supported in migration checks");
|
|
858
|
-
}
|
|
859
|
-
};
|
|
860
|
-
|
|
861
|
-
//#endregion
|
|
862
|
-
//#region src/core/mongo-runner.ts
|
|
863
|
-
function runnerFailure(code, summary, opts) {
|
|
864
|
-
return notOk({
|
|
865
|
-
code,
|
|
866
|
-
summary,
|
|
867
|
-
...opts
|
|
868
|
-
});
|
|
869
|
-
}
|
|
870
|
-
function isMongoControlDriverInstance(driver) {
|
|
871
|
-
return "db" in driver && driver.db != null;
|
|
872
|
-
}
|
|
873
|
-
function extractDb(driver) {
|
|
874
|
-
if (!isMongoControlDriverInstance(driver)) throw new Error("Mongo control driver does not expose a db property. Use mongoControlDriver.create() from `@prisma-next/driver-mongo/control`.");
|
|
875
|
-
return driver.db;
|
|
200
|
+
};
|
|
876
201
|
}
|
|
877
|
-
var MongoMigrationRunner = class {
|
|
878
|
-
async execute(options) {
|
|
879
|
-
const db = extractDb(options.driver);
|
|
880
|
-
const operations = deserializeMongoOps(options.plan.operations);
|
|
881
|
-
const policyCheck = this.enforcePolicyCompatibility(options.policy, operations);
|
|
882
|
-
if (policyCheck) return policyCheck;
|
|
883
|
-
const existingMarker = await readMarker$1(db);
|
|
884
|
-
const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);
|
|
885
|
-
if (markerCheck) return markerCheck;
|
|
886
|
-
const checks = options.executionChecks;
|
|
887
|
-
const runPrechecks = checks?.prechecks !== false;
|
|
888
|
-
const runPostchecks = checks?.postchecks !== false;
|
|
889
|
-
const runIdempotency = checks?.idempotencyChecks !== false;
|
|
890
|
-
const commandExecutor = new MongoCommandExecutor(db);
|
|
891
|
-
const inspectionExecutor = new MongoInspectionExecutor(db);
|
|
892
|
-
const filterEvaluator = new FilterEvaluator();
|
|
893
|
-
let operationsExecuted = 0;
|
|
894
|
-
for (const operation of operations) {
|
|
895
|
-
options.callbacks?.onOperationStart?.(operation);
|
|
896
|
-
try {
|
|
897
|
-
if (runPostchecks && runIdempotency) {
|
|
898
|
-
if (await this.allChecksSatisfied(operation.postcheck, inspectionExecutor, filterEvaluator)) continue;
|
|
899
|
-
}
|
|
900
|
-
if (runPrechecks) {
|
|
901
|
-
if (!await this.evaluateChecks(operation.precheck, inspectionExecutor, filterEvaluator)) return runnerFailure("PRECHECK_FAILED", `Operation ${operation.id} failed during precheck`, { meta: { operationId: operation.id } });
|
|
902
|
-
}
|
|
903
|
-
for (const step of operation.execute) await step.command.accept(commandExecutor);
|
|
904
|
-
if (runPostchecks) {
|
|
905
|
-
if (!await this.evaluateChecks(operation.postcheck, inspectionExecutor, filterEvaluator)) return runnerFailure("POSTCHECK_FAILED", `Operation ${operation.id} failed during postcheck`, { meta: { operationId: operation.id } });
|
|
906
|
-
}
|
|
907
|
-
operationsExecuted += 1;
|
|
908
|
-
} finally {
|
|
909
|
-
options.callbacks?.onOperationComplete?.(operation);
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
const destination = options.plan.destination;
|
|
913
|
-
const profileHash = options.destinationContract.profileHash ?? destination.storageHash;
|
|
914
|
-
if (operationsExecuted === 0 && existingMarker?.storageHash === destination.storageHash && existingMarker.profileHash === profileHash) return ok({
|
|
915
|
-
operationsPlanned: operations.length,
|
|
916
|
-
operationsExecuted
|
|
917
|
-
});
|
|
918
|
-
if (existingMarker) {
|
|
919
|
-
if (!await updateMarker$1(db, existingMarker.storageHash, {
|
|
920
|
-
storageHash: destination.storageHash,
|
|
921
|
-
profileHash
|
|
922
|
-
})) return runnerFailure("MARKER_CAS_FAILURE", "Marker was modified by another process during migration execution.", { meta: {
|
|
923
|
-
expectedStorageHash: existingMarker.storageHash,
|
|
924
|
-
destinationStorageHash: destination.storageHash
|
|
925
|
-
} });
|
|
926
|
-
} else await initMarker$1(db, {
|
|
927
|
-
storageHash: destination.storageHash,
|
|
928
|
-
profileHash
|
|
929
|
-
});
|
|
930
|
-
const originHash = existingMarker?.storageHash ?? "";
|
|
931
|
-
await writeLedgerEntry$1(db, {
|
|
932
|
-
edgeId: `${originHash}->${destination.storageHash}`,
|
|
933
|
-
from: originHash,
|
|
934
|
-
to: destination.storageHash
|
|
935
|
-
});
|
|
936
|
-
return ok({
|
|
937
|
-
operationsPlanned: operations.length,
|
|
938
|
-
operationsExecuted
|
|
939
|
-
});
|
|
940
|
-
}
|
|
941
|
-
async evaluateChecks(checks, inspectionExecutor, filterEvaluator) {
|
|
942
|
-
for (const check of checks) {
|
|
943
|
-
const matchFound = (await check.source.accept(inspectionExecutor)).some((doc) => filterEvaluator.evaluate(check.filter, doc));
|
|
944
|
-
if (!(check.expect === "exists" ? matchFound : !matchFound)) return false;
|
|
945
|
-
}
|
|
946
|
-
return true;
|
|
947
|
-
}
|
|
948
|
-
async allChecksSatisfied(checks, inspectionExecutor, filterEvaluator) {
|
|
949
|
-
if (checks.length === 0) return false;
|
|
950
|
-
return this.evaluateChecks(checks, inspectionExecutor, filterEvaluator);
|
|
951
|
-
}
|
|
952
|
-
enforcePolicyCompatibility(policy, operations) {
|
|
953
|
-
const allowedClasses = new Set(policy.allowedOperationClasses);
|
|
954
|
-
for (const operation of operations) if (!allowedClasses.has(operation.operationClass)) return runnerFailure("POLICY_VIOLATION", `Operation ${operation.id} has class "${operation.operationClass}" which is not allowed by policy.`, {
|
|
955
|
-
why: `Policy only allows: ${[...allowedClasses].join(", ")}.`,
|
|
956
|
-
meta: {
|
|
957
|
-
operationId: operation.id,
|
|
958
|
-
operationClass: operation.operationClass
|
|
959
|
-
}
|
|
960
|
-
});
|
|
961
|
-
}
|
|
962
|
-
ensureMarkerCompatibility(marker, plan) {
|
|
963
|
-
const origin = plan.origin ?? null;
|
|
964
|
-
if (!origin) {
|
|
965
|
-
if (marker) return runnerFailure("MARKER_ORIGIN_MISMATCH", "Database already has a contract marker but the plan has no origin. This would silently overwrite the existing marker.", { meta: { markerStorageHash: marker.storageHash } });
|
|
966
|
-
return;
|
|
967
|
-
}
|
|
968
|
-
if (!marker) return runnerFailure("MARKER_ORIGIN_MISMATCH", `Missing contract marker: expected origin storage hash ${origin.storageHash}.`, { meta: { expectedOriginStorageHash: origin.storageHash } });
|
|
969
|
-
if (marker.storageHash !== origin.storageHash) return runnerFailure("MARKER_ORIGIN_MISMATCH", `Existing contract marker (${marker.storageHash}) does not match plan origin (${origin.storageHash}).`, { meta: {
|
|
970
|
-
markerStorageHash: marker.storageHash,
|
|
971
|
-
expectedOriginStorageHash: origin.storageHash
|
|
972
|
-
} });
|
|
973
|
-
}
|
|
974
|
-
};
|
|
975
202
|
|
|
976
203
|
//#endregion
|
|
977
204
|
//#region src/exports/control.ts
|
|
@@ -981,6 +208,14 @@ const mongoAdapterDescriptor = {
|
|
|
981
208
|
familyId: "mongo",
|
|
982
209
|
targetId: "mongo",
|
|
983
210
|
version: "0.0.1",
|
|
211
|
+
scalarTypeDescriptors: new Map([
|
|
212
|
+
["String", "mongo/string@1"],
|
|
213
|
+
["Int", "mongo/int32@1"],
|
|
214
|
+
["Boolean", "mongo/bool@1"],
|
|
215
|
+
["DateTime", "mongo/date@1"],
|
|
216
|
+
["ObjectId", "mongo/objectId@1"],
|
|
217
|
+
["Float", "mongo/double@1"]
|
|
218
|
+
]),
|
|
984
219
|
types: { codecTypes: {
|
|
985
220
|
codecInstances: [
|
|
986
221
|
mongoObjectIdCodec,
|
|
@@ -1012,5 +247,5 @@ const mongoAdapterDescriptor = {
|
|
|
1012
247
|
var control_default = mongoAdapterDescriptor;
|
|
1013
248
|
|
|
1014
249
|
//#endregion
|
|
1015
|
-
export { MongoCommandExecutor, MongoInspectionExecutor,
|
|
250
|
+
export { MongoCommandExecutor, MongoInspectionExecutor, createMongoControlDriver, createMongoRunnerDeps, control_default as default, introspectSchema };
|
|
1016
251
|
//# sourceMappingURL=control.mjs.map
|