@prisma-next/adapter-mongo 0.12.0-dev.4 → 0.12.0-dev.40

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.
@@ -2,8 +2,8 @@ import { t as createMongoAdapter } from "./mongo-adapter-JuKx_-h9.mjs";
2
2
  import { Db, Document, MongoClient } from "mongodb";
3
3
  import { CollModCommand, CreateCollectionCommand, CreateIndexCommand, DropCollectionCommand, DropIndexCommand, ListCollectionsCommand, ListIndexesCommand, MongoDdlCommandVisitor, MongoInspectionCommandVisitor } from "@prisma-next/mongo-query-ast/control";
4
4
  import { MongoSchemaIR } from "@prisma-next/mongo-schema-ir";
5
+ import { ContractMarkerRecord, LedgerEntryRecord } from "@prisma-next/contract/types";
5
6
  import { MongoControlAdapter, MongoControlAdapterDescriptor } from "@prisma-next/family-mongo/control-adapter";
6
- import { ContractMarkerRecord } from "@prisma-next/contract/types";
7
7
  import { ControlDriverInstance, ControlFamilyInstance } from "@prisma-next/framework-components/control";
8
8
  import { MongoAdapter, MongoDriver } from "@prisma-next/mongo-lowering";
9
9
 
@@ -27,68 +27,12 @@ declare class MongoInspectionExecutor implements MongoInspectionCommandVisitor<P
27
27
  //#region src/core/introspect-schema.d.ts
28
28
  declare function introspectSchema(db: Db): Promise<MongoSchemaIR>;
29
29
  //#endregion
30
- //#region src/core/marker-ledger.d.ts
31
- /**
32
- * Reads the marker document for the given contract space, or returns
33
- * `null` if no marker has been written for that space yet. Each space
34
- * owns one row keyed by `_id: <space>` — see ADR 212 for the per-space
35
- * mechanism this enables.
36
- */
37
- declare function readMarker(db: Db, space: string): Promise<ContractMarkerRecord | null>;
38
- /**
39
- * Reads every marker doc in the collection (one per contract space)
40
- * and returns them keyed by `space`. Used by the per-space verifier
41
- * to detect marker-vs-on-disk drift and orphan marker rows. Returns
42
- * an empty map when no marker docs have been written yet.
43
- *
44
- * Marker docs are keyed by `_id: <space>` (string); ledger entries
45
- * live in the same collection but use a driver-generated `ObjectId`
46
- * `_id` plus `type: 'ledger'`. The filter selects string-keyed docs
47
- * with a `space` field, which excludes ledger entries by construction.
48
- */
49
- declare function readAllMarkers(db: Db): Promise<ReadonlyMap<string, ContractMarkerRecord>>;
50
- declare function initMarker(db: Db, space: string, destination: {
51
- readonly storageHash: string;
52
- readonly profileHash: string;
53
- readonly invariants?: readonly string[];
54
- }): Promise<void>;
55
- /**
56
- * Updates the marker doc for the given space atomically (CAS on
57
- * `expectedFrom`).
58
- *
59
- * `destination.invariants`:
60
- * - `undefined` → existing field left untouched.
61
- * - explicit value → merged into the existing field server-side via an
62
- * aggregation pipeline (`$setUnion + $sortArray`), atomic at the
63
- * document level. `[]` is a no-op merge.
64
- */
65
- declare function updateMarker(db: Db, space: string, expectedFrom: string, destination: {
66
- readonly storageHash: string;
67
- readonly profileHash: string;
68
- readonly invariants?: readonly string[];
69
- }): Promise<boolean>;
70
- /**
71
- * Appends a ledger entry for the given space. Ledger entries co-exist
72
- * with marker docs in the same collection; marker docs use `_id: <space>`
73
- * (string), ledger entries use `type: 'ledger'` plus a driver-generated
74
- * ObjectId. Reads partition the two by filter shape.
75
- *
76
- * The same `edgeId` may legitimately recur across different spaces (e.g.
77
- * a synthetic ∅→head edge on first apply), so the ledger key is
78
- * `(space, edgeId)` — the doc carries `space` for partitioned reads.
79
- */
80
- declare function writeLedgerEntry(db: Db, space: string, entry: {
81
- readonly edgeId: string;
82
- readonly from: string;
83
- readonly to: string;
84
- }): Promise<void>;
85
- //#endregion
86
30
  //#region src/core/mongo-control-adapter.d.ts
87
31
  /**
88
32
  * Mongo control adapter for control-plane operations like introspection
89
33
  * and marker-ledger CAS. Implements the family-level `MongoControlAdapter`
90
34
  * SPI by extracting the underlying `Db` from the framework-shaped driver
91
- * and forwarding to the wire-level helpers in this package.
35
+ * per call.
92
36
  */
93
37
  declare class MongoControlAdapterImpl implements MongoControlAdapter<'mongo'> {
94
38
  readonly familyId: "mongo";
@@ -109,7 +53,11 @@ declare class MongoControlAdapterImpl implements MongoControlAdapter<'mongo'> {
109
53
  readonly edgeId: string;
110
54
  readonly from: string;
111
55
  readonly to: string;
56
+ readonly migrationName: string;
57
+ readonly migrationHash: string;
58
+ readonly operations: readonly unknown[];
112
59
  }): Promise<void>;
60
+ readLedger(driver: ControlDriverInstance<'mongo', 'mongo'>, space?: string): Promise<readonly LedgerEntryRecord[]>;
113
61
  introspectSchema(driver: ControlDriverInstance<'mongo', 'mongo'>): Promise<MongoSchemaIR>;
114
62
  }
115
63
  //#endregion
@@ -143,6 +91,9 @@ interface MarkerOperations {
143
91
  readonly edgeId: string;
144
92
  readonly from: string;
145
93
  readonly to: string;
94
+ readonly migrationName: string;
95
+ readonly migrationHash: string;
96
+ readonly operations: readonly unknown[];
146
97
  }): Promise<void>;
147
98
  }
148
99
  interface MongoRunnerDependencies {
@@ -165,7 +116,28 @@ interface MongoRunnerDependencies {
165
116
  declare function createMongoRunnerDeps(controlDriver: ControlDriverInstance<'mongo', 'mongo'>, driver: MongoDriver, _family: ControlFamilyInstance<'mongo', MongoSchemaIR>, controlAdapter?: MongoControlAdapter<'mongo'>): MongoRunnerDependencies;
166
117
  //#endregion
167
118
  //#region src/exports/control.d.ts
119
+ declare function readMarker(db: Db, space: string): Promise<ContractMarkerRecord | null>;
120
+ declare function readAllMarkers(db: Db): Promise<ReadonlyMap<string, ContractMarkerRecord>>;
121
+ declare function initMarker(db: Db, space: string, destination: {
122
+ readonly storageHash: string;
123
+ readonly profileHash: string;
124
+ readonly invariants?: readonly string[];
125
+ }): Promise<void>;
126
+ declare function updateMarker(db: Db, space: string, expectedFrom: string, destination: {
127
+ readonly storageHash: string;
128
+ readonly profileHash: string;
129
+ readonly invariants?: readonly string[];
130
+ }): Promise<boolean>;
131
+ declare function readLedger(db: Db, space?: string): Promise<readonly LedgerEntryRecord[]>;
132
+ declare function writeLedgerEntry(db: Db, space: string, entry: {
133
+ readonly edgeId: string;
134
+ readonly from: string;
135
+ readonly to: string;
136
+ readonly migrationName: string;
137
+ readonly migrationHash: string;
138
+ readonly operations: readonly unknown[];
139
+ }): Promise<void>;
168
140
  declare const mongoAdapterDescriptor: MongoControlAdapterDescriptor<'mongo'>;
169
141
  //#endregion
170
- export { type MarkerOperations, MongoCommandExecutor, MongoControlAdapterImpl, type MongoControlDriverInstance, MongoInspectionExecutor, type MongoRunnerDependencies, createMongoAdapter, createMongoControlDriver, createMongoRunnerDeps, mongoAdapterDescriptor as default, mongoAdapterDescriptor, extractDb, initMarker, introspectSchema, readAllMarkers, readMarker, updateMarker, writeLedgerEntry };
142
+ export { type MarkerOperations, MongoCommandExecutor, MongoControlAdapterImpl, type MongoControlDriverInstance, MongoInspectionExecutor, type MongoRunnerDependencies, createMongoAdapter, createMongoControlDriver, createMongoRunnerDeps, mongoAdapterDescriptor as default, mongoAdapterDescriptor, extractDb, initMarker, introspectSchema, readAllMarkers, readLedger, readMarker, updateMarker, writeLedgerEntry };
171
143
  //# sourceMappingURL=control.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/command-executor.ts","../src/core/introspect-schema.ts","../src/core/marker-ledger.ts","../src/core/mongo-control-adapter.ts","../src/core/mongo-control-driver.ts","../src/core/runner-deps.ts","../src/exports/control.ts"],"mappings":"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"}
1
+ {"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/command-executor.ts","../src/core/introspect-schema.ts","../src/core/mongo-control-adapter.ts","../src/core/mongo-control-driver.ts","../src/core/runner-deps.ts","../src/exports/control.ts"],"mappings":"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"}
package/dist/control.mjs CHANGED
@@ -4,6 +4,7 @@ import { MongoServerError } from "mongodb";
4
4
  import { keysToKeySpec } from "@prisma-next/mongo-query-ast/control";
5
5
  import { MongoSchemaCollection, MongoSchemaCollectionOptions, MongoSchemaIR, MongoSchemaIndex, MongoSchemaValidator } from "@prisma-next/mongo-schema-ir";
6
6
  import { parseMarkerRowSafely, withMarkerReadErrorHandling } from "@prisma-next/errors/execution";
7
+ import { ledgerOriginFromStored } from "@prisma-next/migration-tools/ledger-origin";
7
8
  import { type } from "arktype";
8
9
  //#region src/core/command-executor.ts
9
10
  var MongoCommandExecutor = class {
@@ -167,17 +168,7 @@ async function introspectSchema(db) {
167
168
  //#region src/core/marker-ledger.ts
168
169
  const COLLECTION = "_prisma_migrations";
169
170
  const MONGO_MARKER_COLLECTION = `_prisma_migrations marker documents in ${COLLECTION}`;
170
- /**
171
- * Marker doc shape.
172
- *
173
- * Same fields as the SQL marker row but camelCase + Mongo-native types:
174
- * `Date` is BSON-hydrated, `meta` is a native object (not JSON-stringified),
175
- * `_id` and any extension fields are tolerated. `invariants?` is optional —
176
- * absent reads as `[]` (schemaless default); present-but-malformed throws.
177
- *
178
- * `space` is required: every marker doc is keyed by its space id (`_id`)
179
- * and stamped with a matching `space` field for partitioned reads.
180
- */
171
+ const MONGO_LEDGER_COLLECTION = `_prisma_migrations ledger documents in ${COLLECTION}`;
181
172
  const MongoMarkerDocSchema = type({
182
173
  space: "string",
183
174
  storageHash: "string",
@@ -219,115 +210,6 @@ async function executeInsertOne(db, cmd) {
219
210
  async function executeFindOneAndUpdate(db, cmd) {
220
211
  return db.collection(cmd.collection).findOneAndUpdate(cmd.filter, cmd.update, { upsert: cmd.upsert });
221
212
  }
222
- /**
223
- * Reads the marker document for the given contract space, or returns
224
- * `null` if no marker has been written for that space yet. Each space
225
- * owns one row keyed by `_id: <space>` — see ADR 212 for the per-space
226
- * mechanism this enables.
227
- */
228
- async function readMarker(db, space) {
229
- const doc = (await withMarkerReadErrorHandling(() => executeAggregate(db, new RawAggregateCommand(COLLECTION, [{ $match: {
230
- _id: space,
231
- space
232
- } }, { $limit: 1 }])), {
233
- space,
234
- markerLocation: MONGO_MARKER_COLLECTION
235
- }))[0];
236
- if (!doc) return null;
237
- return parseMongoMarkerDocSafely(doc, space);
238
- }
239
- /**
240
- * Reads every marker doc in the collection (one per contract space)
241
- * and returns them keyed by `space`. Used by the per-space verifier
242
- * to detect marker-vs-on-disk drift and orphan marker rows. Returns
243
- * an empty map when no marker docs have been written yet.
244
- *
245
- * Marker docs are keyed by `_id: <space>` (string); ledger entries
246
- * live in the same collection but use a driver-generated `ObjectId`
247
- * `_id` plus `type: 'ledger'`. The filter selects string-keyed docs
248
- * with a `space` field, which excludes ledger entries by construction.
249
- */
250
- async function readAllMarkers(db) {
251
- const docs = await withMarkerReadErrorHandling(() => executeAggregate(db, new RawAggregateCommand(COLLECTION, [{ $match: {
252
- _id: { $type: "string" },
253
- space: { $type: "string" },
254
- $expr: { $eq: ["$_id", "$space"] }
255
- } }])), {
256
- space: "app",
257
- markerLocation: MONGO_MARKER_COLLECTION
258
- });
259
- const out = /* @__PURE__ */ new Map();
260
- for (const doc of docs) {
261
- const space = doc["space"];
262
- /* v8 ignore next -- @preserve type-narrowing guard: the $match stage above filters on `space: { $type: 'string' }`, so this branch is unreachable at runtime. The check exists so the `out.set(space, ...)` call below can accept `string`. */
263
- if (typeof space !== "string") continue;
264
- out.set(space, parseMongoMarkerDocSafely(doc, space));
265
- }
266
- return out;
267
- }
268
- async function initMarker(db, space, destination) {
269
- await executeInsertOne(db, new RawInsertOneCommand(COLLECTION, {
270
- _id: space,
271
- space,
272
- storageHash: destination.storageHash,
273
- profileHash: destination.profileHash,
274
- contractJson: null,
275
- canonicalVersion: null,
276
- updatedAt: /* @__PURE__ */ new Date(),
277
- appTag: null,
278
- meta: {},
279
- invariants: destination.invariants ?? []
280
- }));
281
- }
282
- /**
283
- * Updates the marker doc for the given space atomically (CAS on
284
- * `expectedFrom`).
285
- *
286
- * `destination.invariants`:
287
- * - `undefined` → existing field left untouched.
288
- * - explicit value → merged into the existing field server-side via an
289
- * aggregation pipeline (`$setUnion + $sortArray`), atomic at the
290
- * document level. `[]` is a no-op merge.
291
- */
292
- async function updateMarker(db, space, expectedFrom, destination) {
293
- const setBase = {
294
- storageHash: destination.storageHash,
295
- profileHash: destination.profileHash,
296
- updatedAt: /* @__PURE__ */ new Date()
297
- };
298
- const update = destination.invariants === void 0 ? { $set: setBase } : [{ $set: {
299
- ...setBase,
300
- invariants: { $sortArray: {
301
- input: { $setUnion: [{ $ifNull: ["$invariants", []] }, destination.invariants] },
302
- sortBy: 1
303
- } }
304
- } }];
305
- return await executeFindOneAndUpdate(db, new RawFindOneAndUpdateCommand(COLLECTION, {
306
- _id: space,
307
- space,
308
- storageHash: expectedFrom
309
- }, update, false)) !== null;
310
- }
311
- /**
312
- * Appends a ledger entry for the given space. Ledger entries co-exist
313
- * with marker docs in the same collection; marker docs use `_id: <space>`
314
- * (string), ledger entries use `type: 'ledger'` plus a driver-generated
315
- * ObjectId. Reads partition the two by filter shape.
316
- *
317
- * The same `edgeId` may legitimately recur across different spaces (e.g.
318
- * a synthetic ∅→head edge on first apply), so the ledger key is
319
- * `(space, edgeId)` — the doc carries `space` for partitioned reads.
320
- */
321
- async function writeLedgerEntry(db, space, entry) {
322
- await executeInsertOne(db, new RawInsertOneCommand(COLLECTION, {
323
- type: "ledger",
324
- space,
325
- edgeId: entry.edgeId,
326
- from: entry.from,
327
- to: entry.to,
328
- appliedAt: /* @__PURE__ */ new Date()
329
- }));
330
- }
331
213
  //#endregion
332
214
  //#region src/core/runner-deps.ts
333
215
  function extractDb(driver) {
@@ -365,25 +247,123 @@ function createMongoRunnerDeps(controlDriver, driver, _family, controlAdapter =
365
247
  * Mongo control adapter for control-plane operations like introspection
366
248
  * and marker-ledger CAS. Implements the family-level `MongoControlAdapter`
367
249
  * SPI by extracting the underlying `Db` from the framework-shaped driver
368
- * and forwarding to the wire-level helpers in this package.
250
+ * per call.
369
251
  */
370
252
  var MongoControlAdapterImpl = class {
371
253
  familyId = "mongo";
372
254
  targetId = "mongo";
373
255
  async readMarker(driver, space) {
374
- return readMarker(extractDb(driver), space);
256
+ const db = extractDb(driver);
257
+ const doc = (await withMarkerReadErrorHandling(() => executeAggregate(db, new RawAggregateCommand(COLLECTION, [{ $match: {
258
+ _id: space,
259
+ space
260
+ } }, { $limit: 1 }])), {
261
+ space,
262
+ markerLocation: MONGO_MARKER_COLLECTION
263
+ }))[0];
264
+ if (!doc) return null;
265
+ return parseMongoMarkerDocSafely(doc, space);
375
266
  }
376
267
  async readAllMarkers(driver) {
377
- return readAllMarkers(extractDb(driver));
268
+ const db = extractDb(driver);
269
+ const docs = await withMarkerReadErrorHandling(() => executeAggregate(db, new RawAggregateCommand(COLLECTION, [{ $match: {
270
+ _id: { $type: "string" },
271
+ space: { $type: "string" },
272
+ $expr: { $eq: ["$_id", "$space"] }
273
+ } }])), {
274
+ space: "app",
275
+ markerLocation: MONGO_MARKER_COLLECTION
276
+ });
277
+ const out = /* @__PURE__ */ new Map();
278
+ for (const doc of docs) {
279
+ const space = doc["space"];
280
+ /* v8 ignore next -- @preserve type-narrowing guard: the $match stage above filters on `space: { $type: 'string' }`, so this branch is unreachable at runtime. The check exists so the `out.set(space, ...)` call below can accept `string`. */
281
+ if (typeof space !== "string") continue;
282
+ out.set(space, parseMongoMarkerDocSafely(doc, space));
283
+ }
284
+ return out;
378
285
  }
379
286
  async initMarker(driver, space, destination) {
380
- await initMarker(extractDb(driver), space, destination);
287
+ await executeInsertOne(extractDb(driver), new RawInsertOneCommand(COLLECTION, {
288
+ _id: space,
289
+ space,
290
+ storageHash: destination.storageHash,
291
+ profileHash: destination.profileHash,
292
+ contractJson: null,
293
+ canonicalVersion: null,
294
+ updatedAt: /* @__PURE__ */ new Date(),
295
+ appTag: null,
296
+ meta: {},
297
+ invariants: destination.invariants ?? []
298
+ }));
381
299
  }
382
300
  async updateMarker(driver, space, expectedFrom, destination) {
383
- return updateMarker(extractDb(driver), space, expectedFrom, destination);
301
+ const db = extractDb(driver);
302
+ const setBase = {
303
+ storageHash: destination.storageHash,
304
+ profileHash: destination.profileHash,
305
+ updatedAt: /* @__PURE__ */ new Date()
306
+ };
307
+ const update = destination.invariants === void 0 ? { $set: setBase } : [{ $set: {
308
+ ...setBase,
309
+ invariants: { $sortArray: {
310
+ input: { $setUnion: [{ $ifNull: ["$invariants", []] }, destination.invariants] },
311
+ sortBy: 1
312
+ } }
313
+ } }];
314
+ return await executeFindOneAndUpdate(db, new RawFindOneAndUpdateCommand(COLLECTION, {
315
+ _id: space,
316
+ space,
317
+ storageHash: expectedFrom
318
+ }, update, false)) !== null;
384
319
  }
385
320
  async writeLedgerEntry(driver, space, entry) {
386
- await writeLedgerEntry(extractDb(driver), space, entry);
321
+ await executeInsertOne(extractDb(driver), new RawInsertOneCommand(COLLECTION, {
322
+ type: "ledger",
323
+ space,
324
+ edgeId: entry.edgeId,
325
+ from: entry.from,
326
+ to: entry.to,
327
+ migrationName: entry.migrationName,
328
+ migrationHash: entry.migrationHash,
329
+ operations: entry.operations,
330
+ appliedAt: /* @__PURE__ */ new Date()
331
+ }));
332
+ }
333
+ async readLedger(driver, space) {
334
+ const db = extractDb(driver);
335
+ const ledgerContext = {
336
+ space: space ?? "*",
337
+ markerLocation: MONGO_LEDGER_COLLECTION
338
+ };
339
+ const matchStage = { type: "ledger" };
340
+ if (space !== void 0) matchStage["space"] = space;
341
+ const docs = await withMarkerReadErrorHandling(() => executeAggregate(db, new RawAggregateCommand(COLLECTION, [{ $match: matchStage }, { $sort: { _id: 1 } }])), ledgerContext);
342
+ const entries = [];
343
+ for (const doc of docs) {
344
+ const migrationName = doc["migrationName"];
345
+ const migrationHash = doc["migrationHash"];
346
+ const from = doc["from"];
347
+ const to = doc["to"];
348
+ const docSpace = doc["space"];
349
+ if (typeof migrationName !== "string" || typeof migrationHash !== "string") continue;
350
+ if (typeof from !== "string" || typeof to !== "string") continue;
351
+ if (typeof docSpace !== "string") continue;
352
+ const appliedAt = doc["appliedAt"];
353
+ const appliedAtDate = appliedAt instanceof Date ? appliedAt : appliedAt !== void 0 ? new Date(String(appliedAt)) : /* @__PURE__ */ new Date();
354
+ const operations = doc["operations"];
355
+ const opList = Array.isArray(operations) ? operations : [];
356
+ entries.push({
357
+ space: docSpace,
358
+ migrationName,
359
+ migrationHash,
360
+ from: ledgerOriginFromStored(from),
361
+ to,
362
+ appliedAt: appliedAtDate,
363
+ operationCount: opList.length
364
+ });
365
+ }
366
+ return entries;
387
367
  }
388
368
  async introspectSchema(driver) {
389
369
  return introspectSchema(extractDb(driver));
@@ -412,6 +392,34 @@ function createMongoControlDriver(db, client) {
412
392
  }
413
393
  //#endregion
414
394
  //#region src/exports/control.ts
395
+ const defaultControlAdapter = new MongoControlAdapterImpl();
396
+ function controlDriverFromDb(db) {
397
+ return {
398
+ familyId: "mongo",
399
+ targetId: "mongo",
400
+ db,
401
+ query: () => Promise.reject(/* @__PURE__ */ new Error("MongoDB control driver does not support SQL queries")),
402
+ close: async () => {}
403
+ };
404
+ }
405
+ async function readMarker(db, space) {
406
+ return defaultControlAdapter.readMarker(controlDriverFromDb(db), space);
407
+ }
408
+ async function readAllMarkers(db) {
409
+ return defaultControlAdapter.readAllMarkers(controlDriverFromDb(db));
410
+ }
411
+ async function initMarker(db, space, destination) {
412
+ await defaultControlAdapter.initMarker(controlDriverFromDb(db), space, destination);
413
+ }
414
+ async function updateMarker(db, space, expectedFrom, destination) {
415
+ return defaultControlAdapter.updateMarker(controlDriverFromDb(db), space, expectedFrom, destination);
416
+ }
417
+ async function readLedger(db, space) {
418
+ return defaultControlAdapter.readLedger(controlDriverFromDb(db), space);
419
+ }
420
+ async function writeLedgerEntry(db, space, entry) {
421
+ await defaultControlAdapter.writeLedgerEntry(controlDriverFromDb(db), space, entry);
422
+ }
415
423
  const mongoAdapterDescriptor = {
416
424
  kind: "adapter",
417
425
  id: "mongo",
@@ -444,6 +452,6 @@ const mongoAdapterDescriptor = {
444
452
  }
445
453
  };
446
454
  //#endregion
447
- export { MongoCommandExecutor, MongoControlAdapterImpl, MongoInspectionExecutor, createMongoAdapter, createMongoControlDriver, createMongoRunnerDeps, mongoAdapterDescriptor as default, mongoAdapterDescriptor, extractDb, initMarker, introspectSchema, readAllMarkers, readMarker, updateMarker, writeLedgerEntry };
455
+ export { MongoCommandExecutor, MongoControlAdapterImpl, MongoInspectionExecutor, createMongoAdapter, createMongoControlDriver, createMongoRunnerDeps, mongoAdapterDescriptor as default, mongoAdapterDescriptor, extractDb, initMarker, introspectSchema, readAllMarkers, readLedger, readMarker, updateMarker, writeLedgerEntry };
448
456
 
449
457
  //# sourceMappingURL=control.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"control.mjs","names":["#client"],"sources":["../src/core/command-executor.ts","../src/core/introspect-schema.ts","../src/core/marker-ledger.ts","../src/core/runner-deps.ts","../src/core/mongo-control-adapter.ts","../src/core/mongo-control-driver.ts","../src/exports/control.ts"],"sourcesContent":["import type {\n CollModCommand,\n CreateCollectionCommand,\n CreateIndexCommand,\n DropCollectionCommand,\n DropIndexCommand,\n ListCollectionsCommand,\n ListIndexesCommand,\n MongoDdlCommandVisitor,\n MongoInspectionCommandVisitor,\n} from '@prisma-next/mongo-query-ast/control';\nimport { keysToKeySpec } from '@prisma-next/mongo-query-ast/control';\nimport { type Db, type Document, MongoServerError } from 'mongodb';\n\nexport class MongoCommandExecutor implements MongoDdlCommandVisitor<Promise<void>> {\n constructor(private readonly db: Db) {}\n\n async createIndex(cmd: CreateIndexCommand): Promise<void> {\n const keySpec: Document = keysToKeySpec(cmd.keys);\n const options: Record<string, unknown> = {};\n if (cmd.unique !== undefined) options['unique'] = cmd.unique;\n if (cmd.sparse !== undefined) options['sparse'] = cmd.sparse;\n if (cmd.expireAfterSeconds !== undefined)\n options['expireAfterSeconds'] = cmd.expireAfterSeconds;\n if (cmd.partialFilterExpression !== undefined)\n options['partialFilterExpression'] = cmd.partialFilterExpression;\n if (cmd.name !== undefined) options['name'] = cmd.name;\n if (cmd.wildcardProjection !== undefined)\n options['wildcardProjection'] = cmd.wildcardProjection;\n if (cmd.collation !== undefined) options['collation'] = cmd.collation;\n if (cmd.weights !== undefined) options['weights'] = cmd.weights;\n if (cmd.default_language !== undefined) options['default_language'] = cmd.default_language;\n if (cmd.language_override !== undefined) options['language_override'] = cmd.language_override;\n await this.db.collection(cmd.collection).createIndex(keySpec, options);\n }\n\n async dropIndex(cmd: DropIndexCommand): Promise<void> {\n await this.db.collection(cmd.collection).dropIndex(cmd.name);\n }\n\n async createCollection(cmd: CreateCollectionCommand): Promise<void> {\n const options: Record<string, unknown> = {};\n if (cmd.capped !== undefined) options['capped'] = cmd.capped;\n if (cmd.size !== undefined) options['size'] = cmd.size;\n if (cmd.max !== undefined) options['max'] = cmd.max;\n if (cmd.timeseries !== undefined) options['timeseries'] = cmd.timeseries;\n if (cmd.collation !== undefined) options['collation'] = cmd.collation;\n if (cmd.clusteredIndex !== undefined) options['clusteredIndex'] = cmd.clusteredIndex;\n if (cmd.validator !== undefined) options['validator'] = cmd.validator;\n if (cmd.validationLevel !== undefined) options['validationLevel'] = cmd.validationLevel;\n if (cmd.validationAction !== undefined) options['validationAction'] = cmd.validationAction;\n if (cmd.changeStreamPreAndPostImages !== undefined)\n options['changeStreamPreAndPostImages'] = cmd.changeStreamPreAndPostImages;\n await this.db.createCollection(cmd.collection, options);\n }\n\n async dropCollection(cmd: DropCollectionCommand): Promise<void> {\n await this.db.collection(cmd.collection).drop();\n }\n\n async collMod(cmd: CollModCommand): Promise<void> {\n const command: Record<string, unknown> = { collMod: cmd.collection };\n if (cmd.validator !== undefined) command['validator'] = cmd.validator;\n if (cmd.validationLevel !== undefined) command['validationLevel'] = cmd.validationLevel;\n if (cmd.validationAction !== undefined) command['validationAction'] = cmd.validationAction;\n if (cmd.changeStreamPreAndPostImages !== undefined)\n command['changeStreamPreAndPostImages'] = cmd.changeStreamPreAndPostImages;\n await this.db.command(command);\n }\n}\n\nexport class MongoInspectionExecutor implements MongoInspectionCommandVisitor<Promise<Document[]>> {\n constructor(private readonly db: Db) {}\n\n async listIndexes(cmd: ListIndexesCommand): Promise<Document[]> {\n try {\n return await this.db.collection(cmd.collection).listIndexes().toArray();\n } catch (error: unknown) {\n if (error instanceof MongoServerError && error.code === 26) {\n return [];\n }\n throw error;\n }\n }\n\n async listCollections(_cmd: ListCollectionsCommand): Promise<Document[]> {\n return this.db.listCollections().toArray();\n }\n}\n","import type { MongoIndexKey, MongoIndexKeyDirection } from '@prisma-next/mongo-contract';\nimport {\n MongoSchemaCollection,\n MongoSchemaCollectionOptions,\n MongoSchemaIndex,\n MongoSchemaIR,\n MongoSchemaValidator,\n} from '@prisma-next/mongo-schema-ir';\nimport type { Db, Document } from 'mongodb';\n\nconst PRISMA_MIGRATIONS_COLLECTION = '_prisma_migrations';\n\nfunction parseIndexKeys(keySpec: Record<string, unknown>): MongoIndexKey[] {\n const keys: MongoIndexKey[] = [];\n for (const [field, direction] of Object.entries(keySpec)) {\n keys.push({ field, direction: direction as MongoIndexKeyDirection });\n }\n return keys;\n}\n\n/**\n * Exported for unit tests to exercise the defensive `!key` guard; not part of\n * the public API. Callers in this package use it via the `introspectSchema`\n * pipeline only.\n */\nexport function isDefaultIdIndex(doc: Document): boolean {\n const key = doc['key'] as Record<string, unknown> | undefined;\n if (!key) return false;\n const entries = Object.entries(key);\n return entries.length === 1 && entries[0]?.[0] === '_id' && entries[0]?.[1] === 1;\n}\n\nfunction parseIndex(doc: Document): MongoSchemaIndex {\n const keySpec = doc['key'] as Record<string, unknown>;\n return new MongoSchemaIndex({\n keys: parseIndexKeys(keySpec),\n unique: doc['unique'] as boolean | undefined,\n sparse: doc['sparse'] as boolean | undefined,\n expireAfterSeconds: doc['expireAfterSeconds'] as number | undefined,\n partialFilterExpression: doc['partialFilterExpression'] as Record<string, unknown> | undefined,\n wildcardProjection: doc['wildcardProjection'] as Record<string, 0 | 1> | undefined,\n collation: doc['collation'] as Record<string, unknown> | undefined,\n weights: doc['weights'] as Record<string, number> | undefined,\n default_language: doc['default_language'] as string | undefined,\n language_override: doc['language_override'] as string | undefined,\n });\n}\n\nfunction parseValidator(options: Document): MongoSchemaValidator | undefined {\n const validator = options['validator'] as Record<string, unknown> | undefined;\n if (!validator) return undefined;\n\n const jsonSchema = validator['$jsonSchema'] as Record<string, unknown> | undefined;\n if (!jsonSchema) return undefined;\n\n return new MongoSchemaValidator({\n jsonSchema,\n validationLevel: (options['validationLevel'] as 'strict' | 'moderate') ?? 'strict',\n validationAction: (options['validationAction'] as 'error' | 'warn') ?? 'error',\n });\n}\n\nfunction parseCollectionOptions(info: Document): MongoSchemaCollectionOptions | undefined {\n const options = info['options'] as Record<string, unknown> | undefined;\n if (!options) return undefined;\n\n const capped = options['capped'] as boolean | undefined;\n const size = options['size'] as number | undefined;\n const max = options['max'] as number | undefined;\n const timeseries = options['timeseries'] as\n | { timeField: string; metaField?: string; granularity?: 'seconds' | 'minutes' | 'hours' }\n | undefined;\n const collation = options['collation'] as Record<string, unknown> | undefined;\n const changeStreamPreAndPostImages = options['changeStreamPreAndPostImages'] as\n | { enabled: boolean }\n | undefined;\n const clusteredIndex = options['clusteredIndex'] as { name?: string } | undefined;\n\n const hasMeaningfulOptions =\n capped || timeseries || collation || changeStreamPreAndPostImages || clusteredIndex;\n if (!hasMeaningfulOptions) return undefined;\n\n return new MongoSchemaCollectionOptions({\n ...(capped ? { capped: { size: size ?? 0, ...(max != null ? { max } : {}) } } : {}),\n ...(timeseries ? { timeseries } : {}),\n ...(collation ? { collation } : {}),\n ...(changeStreamPreAndPostImages ? { changeStreamPreAndPostImages } : {}),\n ...(clusteredIndex ? { clusteredIndex } : {}),\n });\n}\n\nexport async function introspectSchema(db: Db): Promise<MongoSchemaIR> {\n const collectionInfos = await db.listCollections().toArray();\n\n const collections: MongoSchemaCollection[] = [];\n\n for (const info of collectionInfos) {\n const name = info['name'] as string;\n const type = info['type'] as string | undefined;\n\n if (name === PRISMA_MIGRATIONS_COLLECTION) continue;\n if (name.startsWith('system.')) continue;\n if (type === 'view') continue;\n\n const indexDocs = await db.collection(name).listIndexes().toArray();\n const indexes = indexDocs.filter((doc) => !isDefaultIdIndex(doc)).map(parseIndex);\n\n const infoOptions = 'options' in info ? (info['options'] as Record<string, unknown>) : {};\n const validator = parseValidator(infoOptions);\n const options = parseCollectionOptions(info);\n\n collections.push(\n new MongoSchemaCollection({\n name,\n indexes,\n ...(validator ? { validator } : {}),\n ...(options ? { options } : {}),\n }),\n );\n }\n\n return new MongoSchemaIR(collections);\n}\n","import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport { parseMarkerRowSafely, withMarkerReadErrorHandling } from '@prisma-next/errors/execution';\nimport {\n RawAggregateCommand,\n RawFindOneAndUpdateCommand,\n RawInsertOneCommand,\n} from '@prisma-next/mongo-query-ast/execution';\nimport { type } from 'arktype';\nimport type { Db, Document, UpdateFilter } from 'mongodb';\n\nconst COLLECTION = '_prisma_migrations';\nconst MONGO_MARKER_COLLECTION = `_prisma_migrations marker documents in ${COLLECTION}`;\n\n/**\n * Marker doc shape.\n *\n * Same fields as the SQL marker row but camelCase + Mongo-native types:\n * `Date` is BSON-hydrated, `meta` is a native object (not JSON-stringified),\n * `_id` and any extension fields are tolerated. `invariants?` is optional —\n * absent reads as `[]` (schemaless default); present-but-malformed throws.\n *\n * `space` is required: every marker doc is keyed by its space id (`_id`)\n * and stamped with a matching `space` field for partitioned reads.\n */\nconst MongoMarkerDocSchema = type({\n space: 'string',\n storageHash: 'string',\n profileHash: 'string',\n 'contractJson?': 'unknown | null',\n 'canonicalVersion?': 'number | null',\n 'updatedAt?': 'Date',\n 'appTag?': 'string | null',\n 'meta?': type({ '[string]': 'unknown' }).or('null'),\n 'invariants?': type('string').array(),\n '+': 'delete',\n});\n\nfunction parseMongoMarkerDoc(doc: unknown): ContractMarkerRecord {\n const result = MongoMarkerDocSchema(doc);\n if (result instanceof type.errors) {\n throw new Error(`Invalid marker doc on ${COLLECTION}: ${result.summary}`);\n }\n return {\n storageHash: result.storageHash,\n profileHash: result.profileHash,\n contractJson: result.contractJson ?? null,\n canonicalVersion: result.canonicalVersion ?? null,\n updatedAt: result.updatedAt ?? new Date(),\n appTag: result.appTag ?? null,\n meta: (result.meta as Record<string, unknown> | null) ?? {},\n invariants: result.invariants ?? [],\n };\n}\n\nfunction parseMongoMarkerDocSafely(doc: unknown, space: string): ContractMarkerRecord {\n return parseMarkerRowSafely(doc, parseMongoMarkerDoc, {\n space,\n markerLocation: MONGO_MARKER_COLLECTION,\n });\n}\n\nasync function executeAggregate(db: Db, cmd: RawAggregateCommand): Promise<Document[]> {\n return db\n .collection(cmd.collection)\n .aggregate(cmd.pipeline as Record<string, unknown>[])\n .toArray();\n}\n\nasync function executeInsertOne(db: Db, cmd: RawInsertOneCommand): Promise<void> {\n await db.collection(cmd.collection).insertOne(cmd.document);\n}\n\nasync function executeFindOneAndUpdate(\n db: Db,\n cmd: RawFindOneAndUpdateCommand,\n): Promise<Document | null> {\n // `cmd.update` is `Document | ReadonlyArray<Document>` per the AST. The\n // MongoDB driver's `findOneAndUpdate` accepts the same shape under the\n // type `UpdateFilter<T> | Document[]`. The driver's runtime path handles\n // both forms identically — pipelines (array) and update docs (object).\n // One cast to that union keeps the call single-arm.\n return db\n .collection(cmd.collection)\n .findOneAndUpdate(cmd.filter, cmd.update as UpdateFilter<Document> | Document[], {\n upsert: cmd.upsert,\n });\n}\n\n/**\n * Reads the marker document for the given contract space, or returns\n * `null` if no marker has been written for that space yet. Each space\n * owns one row keyed by `_id: <space>` — see ADR 212 for the per-space\n * mechanism this enables.\n */\nexport async function readMarker(db: Db, space: string): Promise<ContractMarkerRecord | null> {\n const markerContext = { space, markerLocation: MONGO_MARKER_COLLECTION };\n const docs = await withMarkerReadErrorHandling(\n () =>\n executeAggregate(\n db,\n new RawAggregateCommand(COLLECTION, [{ $match: { _id: space, space } }, { $limit: 1 }]),\n ),\n markerContext,\n );\n const doc = docs[0];\n if (!doc) return null;\n return parseMongoMarkerDocSafely(doc, space);\n}\n\n/**\n * Reads every marker doc in the collection (one per contract space)\n * and returns them keyed by `space`. Used by the per-space verifier\n * to detect marker-vs-on-disk drift and orphan marker rows. Returns\n * an empty map when no marker docs have been written yet.\n *\n * Marker docs are keyed by `_id: <space>` (string); ledger entries\n * live in the same collection but use a driver-generated `ObjectId`\n * `_id` plus `type: 'ledger'`. The filter selects string-keyed docs\n * with a `space` field, which excludes ledger entries by construction.\n */\nexport async function readAllMarkers(db: Db): Promise<ReadonlyMap<string, ContractMarkerRecord>> {\n const markerContext = { space: 'app', markerLocation: MONGO_MARKER_COLLECTION };\n const docs = await withMarkerReadErrorHandling(\n () =>\n executeAggregate(\n db,\n new RawAggregateCommand(COLLECTION, [\n {\n $match: {\n _id: { $type: 'string' },\n space: { $type: 'string' },\n $expr: { $eq: ['$_id', '$space'] },\n },\n },\n ]),\n ),\n markerContext,\n );\n const out = new Map<string, ContractMarkerRecord>();\n for (const doc of docs) {\n const space = doc['space'];\n /* v8 ignore next -- @preserve type-narrowing guard: the $match stage above filters on `space: { $type: 'string' }`, so this branch is unreachable at runtime. The check exists so the `out.set(space, ...)` call below can accept `string`. */\n if (typeof space !== 'string') continue;\n out.set(space, parseMongoMarkerDocSafely(doc, space));\n }\n return out;\n}\n\nexport async function initMarker(\n db: Db,\n space: string,\n destination: {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly invariants?: readonly string[];\n },\n): Promise<void> {\n const cmd = new RawInsertOneCommand(COLLECTION, {\n _id: space,\n space,\n storageHash: destination.storageHash,\n profileHash: destination.profileHash,\n contractJson: null,\n canonicalVersion: null,\n updatedAt: new Date(),\n appTag: null,\n meta: {},\n invariants: destination.invariants ?? [],\n });\n await executeInsertOne(db, cmd);\n}\n\n/**\n * Updates the marker doc for the given space atomically (CAS on\n * `expectedFrom`).\n *\n * `destination.invariants`:\n * - `undefined` → existing field left untouched.\n * - explicit value → merged into the existing field server-side via an\n * aggregation pipeline (`$setUnion + $sortArray`), atomic at the\n * document level. `[]` is a no-op merge.\n */\nexport async function updateMarker(\n db: Db,\n space: string,\n expectedFrom: string,\n destination: {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly invariants?: readonly string[];\n },\n): Promise<boolean> {\n const setBase: Record<string, unknown> = {\n storageHash: destination.storageHash,\n profileHash: destination.profileHash,\n updatedAt: new Date(),\n };\n // When invariants is supplied, use an aggregation pipeline so the\n // merge runs server-side against the doc's current value (atomic, no\n // read-then-write window). When omitted, a regular update doc keeps\n // the field untouched.\n const update: Document | Document[] =\n destination.invariants === undefined\n ? { $set: setBase }\n : [\n {\n $set: {\n ...setBase,\n invariants: {\n $sortArray: {\n input: { $setUnion: [{ $ifNull: ['$invariants', []] }, destination.invariants] },\n sortBy: 1,\n },\n },\n },\n },\n ];\n const cmd = new RawFindOneAndUpdateCommand(\n COLLECTION,\n { _id: space, space, storageHash: expectedFrom },\n update,\n false,\n );\n const result = await executeFindOneAndUpdate(db, cmd);\n return result !== null;\n}\n\n/**\n * Appends a ledger entry for the given space. Ledger entries co-exist\n * with marker docs in the same collection; marker docs use `_id: <space>`\n * (string), ledger entries use `type: 'ledger'` plus a driver-generated\n * ObjectId. Reads partition the two by filter shape.\n *\n * The same `edgeId` may legitimately recur across different spaces (e.g.\n * a synthetic ∅→head edge on first apply), so the ledger key is\n * `(space, edgeId)` — the doc carries `space` for partitioned reads.\n */\nexport async function writeLedgerEntry(\n db: Db,\n space: string,\n entry: { readonly edgeId: string; readonly from: string; readonly to: string },\n): Promise<void> {\n const cmd = new RawInsertOneCommand(COLLECTION, {\n type: 'ledger',\n space,\n edgeId: entry.edgeId,\n from: entry.from,\n to: entry.to,\n appliedAt: new Date(),\n });\n await executeInsertOne(db, cmd);\n}\n","import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type { MongoControlAdapter } from '@prisma-next/family-mongo/control-adapter';\nimport type {\n ControlDriverInstance,\n ControlFamilyInstance,\n} from '@prisma-next/framework-components/control';\nimport type { MongoAdapter, MongoDriver } from '@prisma-next/mongo-lowering';\nimport type {\n MongoDdlCommandVisitor,\n MongoInspectionCommandVisitor,\n} from '@prisma-next/mongo-query-ast/control';\nimport type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport type { Db } from 'mongodb';\nimport { createMongoAdapter } from '../mongo-adapter';\nimport { MongoCommandExecutor, MongoInspectionExecutor } from './command-executor';\nimport { MongoControlAdapterImpl } from './mongo-control-adapter';\n\nexport function extractDb(driver: ControlDriverInstance<'mongo', 'mongo'>): Db {\n const mongoDriver = driver as ControlDriverInstance<'mongo', 'mongo'> & { db?: Db };\n if (!mongoDriver.db) {\n throw new Error(\n 'Mongo control driver does not expose a db property. ' +\n 'Use mongoControlDriver.create() from `@prisma-next/driver-mongo/control`.',\n );\n }\n return mongoDriver.db;\n}\n\n/**\n * Marker / ledger operations the Mongo runner depends on. Every method\n * takes a `space` parameter so each loaded contract space addresses its\n * own marker row independently — see ADR 212 for the per-space\n * mechanism.\n */\nexport interface MarkerOperations {\n readMarker(space: string): Promise<ContractMarkerRecord | null>;\n initMarker(\n space: string,\n destination: {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly invariants?: readonly string[];\n },\n ): Promise<void>;\n updateMarker(\n space: string,\n expectedFrom: string,\n destination: {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly invariants?: readonly string[];\n },\n ): Promise<boolean>;\n writeLedgerEntry(\n space: string,\n entry: {\n readonly edgeId: string;\n readonly from: string;\n readonly to: string;\n },\n ): Promise<void>;\n}\n\nexport interface MongoRunnerDependencies {\n readonly commandExecutor: MongoDdlCommandVisitor<Promise<void>>;\n readonly inspectionExecutor: MongoInspectionCommandVisitor<Promise<Record<string, unknown>[]>>;\n readonly adapter: MongoAdapter;\n readonly driver: MongoDriver;\n readonly markerOps: MarkerOperations;\n readonly introspectSchema: () => Promise<MongoSchemaIR>;\n}\n\n/**\n * Build the runner-dependencies envelope. `controlAdapter` is the\n * dispatch surface for wire-level Mongo CAS operations (marker reads,\n * marker advances, ledger appends, introspection); the envelope's\n * `markerOps` shim simply forwards each call through it. When the\n * caller already has a `MongoControlAdapter` on the control stack it\n * can pass it in; otherwise a default `MongoControlAdapterImpl` is\n * constructed locally.\n */\nexport function createMongoRunnerDeps(\n controlDriver: ControlDriverInstance<'mongo', 'mongo'>,\n driver: MongoDriver,\n // Vestigial after the M2.5 family→adapter SPI dispatch refactor: the runner\n // dependencies now route every wire-level call through `controlAdapter`, so\n // the `family` instance is no longer consulted. Kept on the signature to\n // avoid rippling through ~14 call sites mid-orchestration; a follow-up that\n // already touches this factory should drop the parameter outright.\n _family: ControlFamilyInstance<'mongo', MongoSchemaIR>,\n controlAdapter: MongoControlAdapter<'mongo'> = new MongoControlAdapterImpl(),\n): MongoRunnerDependencies {\n return {\n commandExecutor: new MongoCommandExecutor(extractDb(controlDriver)),\n inspectionExecutor: new MongoInspectionExecutor(extractDb(controlDriver)),\n adapter: createMongoAdapter(),\n driver,\n markerOps: {\n readMarker: (space) => controlAdapter.readMarker(controlDriver, space),\n initMarker: (space, dest) => controlAdapter.initMarker(controlDriver, space, dest),\n updateMarker: (space, expectedFrom, dest) =>\n controlAdapter.updateMarker(controlDriver, space, expectedFrom, dest),\n writeLedgerEntry: (space, entry) =>\n controlAdapter.writeLedgerEntry(controlDriver, space, entry),\n },\n introspectSchema: () => controlAdapter.introspectSchema(controlDriver),\n };\n}\n","import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type { MongoControlAdapter } from '@prisma-next/family-mongo/control-adapter';\nimport type { ControlDriverInstance } from '@prisma-next/framework-components/control';\nimport type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport { introspectSchema } from './introspect-schema';\nimport {\n initMarker,\n readAllMarkers,\n readMarker,\n updateMarker,\n writeLedgerEntry,\n} from './marker-ledger';\nimport { extractDb } from './runner-deps';\n\n/**\n * Mongo control adapter for control-plane operations like introspection\n * and marker-ledger CAS. Implements the family-level `MongoControlAdapter`\n * SPI by extracting the underlying `Db` from the framework-shaped driver\n * and forwarding to the wire-level helpers in this package.\n */\nexport class MongoControlAdapterImpl implements MongoControlAdapter<'mongo'> {\n readonly familyId = 'mongo' as const;\n readonly targetId = 'mongo' as const;\n\n async readMarker(\n driver: ControlDriverInstance<'mongo', 'mongo'>,\n space: string,\n ): Promise<ContractMarkerRecord | null> {\n return readMarker(extractDb(driver), space);\n }\n\n async readAllMarkers(\n driver: ControlDriverInstance<'mongo', 'mongo'>,\n ): Promise<ReadonlyMap<string, ContractMarkerRecord>> {\n return readAllMarkers(extractDb(driver));\n }\n\n async initMarker(\n driver: ControlDriverInstance<'mongo', 'mongo'>,\n space: string,\n destination: {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly invariants?: readonly string[];\n },\n ): Promise<void> {\n await initMarker(extractDb(driver), space, destination);\n }\n\n async updateMarker(\n driver: ControlDriverInstance<'mongo', 'mongo'>,\n space: string,\n expectedFrom: string,\n destination: {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly invariants?: readonly string[];\n },\n ): Promise<boolean> {\n return updateMarker(extractDb(driver), space, expectedFrom, destination);\n }\n\n async writeLedgerEntry(\n driver: ControlDriverInstance<'mongo', 'mongo'>,\n space: string,\n entry: { readonly edgeId: string; readonly from: string; readonly to: string },\n ): Promise<void> {\n await writeLedgerEntry(extractDb(driver), space, entry);\n }\n\n async introspectSchema(driver: ControlDriverInstance<'mongo', 'mongo'>): Promise<MongoSchemaIR> {\n return introspectSchema(extractDb(driver));\n }\n}\n","import type { ControlDriverInstance } from '@prisma-next/framework-components/control';\nimport type { Db, MongoClient } from 'mongodb';\n\nexport interface MongoControlDriverInstance extends ControlDriverInstance<'mongo', 'mongo'> {\n readonly db: Db;\n}\n\nclass MongoControlDriverImpl implements MongoControlDriverInstance {\n readonly familyId = 'mongo' as const;\n readonly targetId = 'mongo' as const;\n readonly db: Db;\n readonly #client: MongoClient;\n\n constructor(db: Db, client: MongoClient) {\n this.db = db;\n this.#client = client;\n }\n\n query(): Promise<never> {\n throw new Error('MongoDB control driver does not support SQL queries');\n }\n\n async close(): Promise<void> {\n await this.#client.close();\n }\n}\n\nexport function createMongoControlDriver(db: Db, client: MongoClient): MongoControlDriverInstance {\n return new MongoControlDriverImpl(db, client);\n}\n","import type { MongoControlAdapterDescriptor } from '@prisma-next/family-mongo/control-adapter';\n\nexport { MongoCommandExecutor, MongoInspectionExecutor } from '../core/command-executor';\nexport { introspectSchema } from '../core/introspect-schema';\nexport {\n initMarker,\n readAllMarkers,\n readMarker,\n updateMarker,\n writeLedgerEntry,\n} from '../core/marker-ledger';\nexport { MongoControlAdapterImpl } from '../core/mongo-control-adapter';\nexport {\n createMongoControlDriver,\n type MongoControlDriverInstance,\n} from '../core/mongo-control-driver';\nexport {\n createMongoRunnerDeps,\n extractDb,\n type MarkerOperations,\n type MongoRunnerDependencies,\n} from '../core/runner-deps';\nexport { createMongoAdapter } from '../mongo-adapter';\n\nimport { mongoCodecDescriptors } from '../core/codecs';\nimport { MongoControlAdapterImpl } from '../core/mongo-control-adapter';\n\nexport const mongoAdapterDescriptor: MongoControlAdapterDescriptor<'mongo'> = {\n kind: 'adapter',\n id: 'mongo',\n familyId: 'mongo',\n targetId: 'mongo',\n version: '0.0.1',\n scalarTypeDescriptors: new Map([\n ['String', 'mongo/string@1'],\n ['Int', 'mongo/int32@1'],\n ['Boolean', 'mongo/bool@1'],\n ['DateTime', 'mongo/date@1'],\n ['ObjectId', 'mongo/objectId@1'],\n ['Float', 'mongo/double@1'],\n ]),\n types: {\n codecTypes: {\n codecDescriptors: mongoCodecDescriptors,\n import: {\n package: '@prisma-next/adapter-mongo/codec-types',\n named: 'CodecTypes',\n alias: 'MongoCodecTypes',\n },\n typeImports: [\n {\n package: '@prisma-next/adapter-mongo/codec-types',\n named: 'Vector',\n alias: 'Vector',\n },\n ],\n },\n },\n create(_stack) {\n return new MongoControlAdapterImpl();\n },\n};\n\nexport default mongoAdapterDescriptor;\n"],"mappings":";;;;;;;;AAcA,IAAa,uBAAb,MAAmF;CACpD;CAA7B,YAAY,IAAyB;EAAR,KAAA,KAAA;CAAS;CAEtC,MAAM,YAAY,KAAwC;EACxD,MAAM,UAAoB,cAAc,IAAI,IAAI;EAChD,MAAM,UAAmC,CAAC;EAC1C,IAAI,IAAI,WAAW,KAAA,GAAW,QAAQ,YAAY,IAAI;EACtD,IAAI,IAAI,WAAW,KAAA,GAAW,QAAQ,YAAY,IAAI;EACtD,IAAI,IAAI,uBAAuB,KAAA,GAC7B,QAAQ,wBAAwB,IAAI;EACtC,IAAI,IAAI,4BAA4B,KAAA,GAClC,QAAQ,6BAA6B,IAAI;EAC3C,IAAI,IAAI,SAAS,KAAA,GAAW,QAAQ,UAAU,IAAI;EAClD,IAAI,IAAI,uBAAuB,KAAA,GAC7B,QAAQ,wBAAwB,IAAI;EACtC,IAAI,IAAI,cAAc,KAAA,GAAW,QAAQ,eAAe,IAAI;EAC5D,IAAI,IAAI,YAAY,KAAA,GAAW,QAAQ,aAAa,IAAI;EACxD,IAAI,IAAI,qBAAqB,KAAA,GAAW,QAAQ,sBAAsB,IAAI;EAC1E,IAAI,IAAI,sBAAsB,KAAA,GAAW,QAAQ,uBAAuB,IAAI;EAC5E,MAAM,KAAK,GAAG,WAAW,IAAI,UAAU,EAAE,YAAY,SAAS,OAAO;CACvE;CAEA,MAAM,UAAU,KAAsC;EACpD,MAAM,KAAK,GAAG,WAAW,IAAI,UAAU,EAAE,UAAU,IAAI,IAAI;CAC7D;CAEA,MAAM,iBAAiB,KAA6C;EAClE,MAAM,UAAmC,CAAC;EAC1C,IAAI,IAAI,WAAW,KAAA,GAAW,QAAQ,YAAY,IAAI;EACtD,IAAI,IAAI,SAAS,KAAA,GAAW,QAAQ,UAAU,IAAI;EAClD,IAAI,IAAI,QAAQ,KAAA,GAAW,QAAQ,SAAS,IAAI;EAChD,IAAI,IAAI,eAAe,KAAA,GAAW,QAAQ,gBAAgB,IAAI;EAC9D,IAAI,IAAI,cAAc,KAAA,GAAW,QAAQ,eAAe,IAAI;EAC5D,IAAI,IAAI,mBAAmB,KAAA,GAAW,QAAQ,oBAAoB,IAAI;EACtE,IAAI,IAAI,cAAc,KAAA,GAAW,QAAQ,eAAe,IAAI;EAC5D,IAAI,IAAI,oBAAoB,KAAA,GAAW,QAAQ,qBAAqB,IAAI;EACxE,IAAI,IAAI,qBAAqB,KAAA,GAAW,QAAQ,sBAAsB,IAAI;EAC1E,IAAI,IAAI,iCAAiC,KAAA,GACvC,QAAQ,kCAAkC,IAAI;EAChD,MAAM,KAAK,GAAG,iBAAiB,IAAI,YAAY,OAAO;CACxD;CAEA,MAAM,eAAe,KAA2C;EAC9D,MAAM,KAAK,GAAG,WAAW,IAAI,UAAU,EAAE,KAAK;CAChD;CAEA,MAAM,QAAQ,KAAoC;EAChD,MAAM,UAAmC,EAAE,SAAS,IAAI,WAAW;EACnE,IAAI,IAAI,cAAc,KAAA,GAAW,QAAQ,eAAe,IAAI;EAC5D,IAAI,IAAI,oBAAoB,KAAA,GAAW,QAAQ,qBAAqB,IAAI;EACxE,IAAI,IAAI,qBAAqB,KAAA,GAAW,QAAQ,sBAAsB,IAAI;EAC1E,IAAI,IAAI,iCAAiC,KAAA,GACvC,QAAQ,kCAAkC,IAAI;EAChD,MAAM,KAAK,GAAG,QAAQ,OAAO;CAC/B;AACF;AAEA,IAAa,0BAAb,MAAmG;CACpE;CAA7B,YAAY,IAAyB;EAAR,KAAA,KAAA;CAAS;CAEtC,MAAM,YAAY,KAA8C;EAC9D,IAAI;GACF,OAAO,MAAM,KAAK,GAAG,WAAW,IAAI,UAAU,EAAE,YAAY,EAAE,QAAQ;EACxE,SAAS,OAAgB;GACvB,IAAI,iBAAiB,oBAAoB,MAAM,SAAS,IACtD,OAAO,CAAC;GAEV,MAAM;EACR;CACF;CAEA,MAAM,gBAAgB,MAAmD;EACvE,OAAO,KAAK,GAAG,gBAAgB,EAAE,QAAQ;CAC3C;AACF;;;AC9EA,MAAM,+BAA+B;AAErC,SAAS,eAAe,SAAmD;CACzE,MAAM,OAAwB,CAAC;CAC/B,KAAK,MAAM,CAAC,OAAO,cAAc,OAAO,QAAQ,OAAO,GACrD,KAAK,KAAK;EAAE;EAAkB;CAAoC,CAAC;CAErE,OAAO;AACT;;;;;;AAOA,SAAgB,iBAAiB,KAAwB;CACvD,MAAM,MAAM,IAAI;CAChB,IAAI,CAAC,KAAK,OAAO;CACjB,MAAM,UAAU,OAAO,QAAQ,GAAG;CAClC,OAAO,QAAQ,WAAW,KAAK,QAAQ,KAAK,OAAO,SAAS,QAAQ,KAAK,OAAO;AAClF;AAEA,SAAS,WAAW,KAAiC;CACnD,MAAM,UAAU,IAAI;CACpB,OAAO,IAAI,iBAAiB;EAC1B,MAAM,eAAe,OAAO;EAC5B,QAAQ,IAAI;EACZ,QAAQ,IAAI;EACZ,oBAAoB,IAAI;EACxB,yBAAyB,IAAI;EAC7B,oBAAoB,IAAI;EACxB,WAAW,IAAI;EACf,SAAS,IAAI;EACb,kBAAkB,IAAI;EACtB,mBAAmB,IAAI;CACzB,CAAC;AACH;AAEA,SAAS,eAAe,SAAqD;CAC3E,MAAM,YAAY,QAAQ;CAC1B,IAAI,CAAC,WAAW,OAAO,KAAA;CAEvB,MAAM,aAAa,UAAU;CAC7B,IAAI,CAAC,YAAY,OAAO,KAAA;CAExB,OAAO,IAAI,qBAAqB;EAC9B;EACA,iBAAkB,QAAQ,sBAAgD;EAC1E,kBAAmB,QAAQ,uBAA4C;CACzE,CAAC;AACH;AAEA,SAAS,uBAAuB,MAA0D;CACxF,MAAM,UAAU,KAAK;CACrB,IAAI,CAAC,SAAS,OAAO,KAAA;CAErB,MAAM,SAAS,QAAQ;CACvB,MAAM,OAAO,QAAQ;CACrB,MAAM,MAAM,QAAQ;CACpB,MAAM,aAAa,QAAQ;CAG3B,MAAM,YAAY,QAAQ;CAC1B,MAAM,+BAA+B,QAAQ;CAG7C,MAAM,iBAAiB,QAAQ;CAI/B,IAAI,EADF,UAAU,cAAc,aAAa,gCAAgC,iBAC5C,OAAO,KAAA;CAElC,OAAO,IAAI,6BAA6B;EACtC,GAAI,SAAS,EAAE,QAAQ;GAAE,MAAM,QAAQ;GAAG,GAAI,OAAO,OAAO,EAAE,IAAI,IAAI,CAAC;EAAG,EAAE,IAAI,CAAC;EACjF,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;EACnC,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;EACjC,GAAI,+BAA+B,EAAE,6BAA6B,IAAI,CAAC;EACvE,GAAI,iBAAiB,EAAE,eAAe,IAAI,CAAC;CAC7C,CAAC;AACH;AAEA,eAAsB,iBAAiB,IAAgC;CACrE,MAAM,kBAAkB,MAAM,GAAG,gBAAgB,EAAE,QAAQ;CAE3D,MAAM,cAAuC,CAAC;CAE9C,KAAK,MAAM,QAAQ,iBAAiB;EAClC,MAAM,OAAO,KAAK;EAClB,MAAM,OAAO,KAAK;EAElB,IAAI,SAAS,8BAA8B;EAC3C,IAAI,KAAK,WAAW,SAAS,GAAG;EAChC,IAAI,SAAS,QAAQ;EAGrB,MAAM,WAAU,MADQ,GAAG,WAAW,IAAI,EAAE,YAAY,EAAE,QAAQ,GACxC,QAAQ,QAAQ,CAAC,iBAAiB,GAAG,CAAC,EAAE,IAAI,UAAU;EAGhF,MAAM,YAAY,eADE,aAAa,OAAQ,KAAK,aAAyC,CAAC,CAC5C;EAC5C,MAAM,UAAU,uBAAuB,IAAI;EAE3C,YAAY,KACV,IAAI,sBAAsB;GACxB;GACA;GACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;GACjC,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC/B,CAAC,CACH;CACF;CAEA,OAAO,IAAI,cAAc,WAAW;AACtC;;;AChHA,MAAM,aAAa;AACnB,MAAM,0BAA0B,0CAA0C;;;;;;;;;;;;AAa1E,MAAM,uBAAuB,KAAK;CAChC,OAAO;CACP,aAAa;CACb,aAAa;CACb,iBAAiB;CACjB,qBAAqB;CACrB,cAAc;CACd,WAAW;CACX,SAAS,KAAK,EAAE,YAAY,UAAU,CAAC,EAAE,GAAG,MAAM;CAClD,eAAe,KAAK,QAAQ,EAAE,MAAM;CACpC,KAAK;AACP,CAAC;AAED,SAAS,oBAAoB,KAAoC;CAC/D,MAAM,SAAS,qBAAqB,GAAG;CACvC,IAAI,kBAAkB,KAAK,QACzB,MAAM,IAAI,MAAM,yBAAyB,WAAW,IAAI,OAAO,SAAS;CAE1E,OAAO;EACL,aAAa,OAAO;EACpB,aAAa,OAAO;EACpB,cAAc,OAAO,gBAAgB;EACrC,kBAAkB,OAAO,oBAAoB;EAC7C,WAAW,OAAO,6BAAa,IAAI,KAAK;EACxC,QAAQ,OAAO,UAAU;EACzB,MAAO,OAAO,QAA2C,CAAC;EAC1D,YAAY,OAAO,cAAc,CAAC;CACpC;AACF;AAEA,SAAS,0BAA0B,KAAc,OAAqC;CACpF,OAAO,qBAAqB,KAAK,qBAAqB;EACpD;EACA,gBAAgB;CAClB,CAAC;AACH;AAEA,eAAe,iBAAiB,IAAQ,KAA+C;CACrF,OAAO,GACJ,WAAW,IAAI,UAAU,EACzB,UAAU,IAAI,QAAqC,EACnD,QAAQ;AACb;AAEA,eAAe,iBAAiB,IAAQ,KAAyC;CAC/E,MAAM,GAAG,WAAW,IAAI,UAAU,EAAE,UAAU,IAAI,QAAQ;AAC5D;AAEA,eAAe,wBACb,IACA,KAC0B;CAM1B,OAAO,GACJ,WAAW,IAAI,UAAU,EACzB,iBAAiB,IAAI,QAAQ,IAAI,QAA+C,EAC/E,QAAQ,IAAI,OACd,CAAC;AACL;;;;;;;AAQA,eAAsB,WAAW,IAAQ,OAAqD;CAU5F,MAAM,OAAM,MARO,kCAEf,iBACE,IACA,IAAI,oBAAoB,YAAY,CAAC,EAAE,QAAQ;EAAE,KAAK;EAAO;CAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,CACxF,GACF;EAPsB;EAAO,gBAAgB;CAOjC,CACd,GACiB;CACjB,IAAI,CAAC,KAAK,OAAO;CACjB,OAAO,0BAA0B,KAAK,KAAK;AAC7C;;;;;;;;;;;;AAaA,eAAsB,eAAe,IAA4D;CAE/F,MAAM,OAAO,MAAM,kCAEf,iBACE,IACA,IAAI,oBAAoB,YAAY,CAClC,EACE,QAAQ;EACN,KAAK,EAAE,OAAO,SAAS;EACvB,OAAO,EAAE,OAAO,SAAS;EACzB,OAAO,EAAE,KAAK,CAAC,QAAQ,QAAQ,EAAE;CACnC,EACF,CACF,CAAC,CACH,GACF;EAfsB,OAAO;EAAO,gBAAgB;CAexC,CACd;CACA,MAAM,sBAAM,IAAI,IAAkC;CAClD,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,IAAI;;EAElB,IAAI,OAAO,UAAU,UAAU;EAC/B,IAAI,IAAI,OAAO,0BAA0B,KAAK,KAAK,CAAC;CACtD;CACA,OAAO;AACT;AAEA,eAAsB,WACpB,IACA,OACA,aAKe;CAaf,MAAM,iBAAiB,IAAI,IAZX,oBAAoB,YAAY;EAC9C,KAAK;EACL;EACA,aAAa,YAAY;EACzB,aAAa,YAAY;EACzB,cAAc;EACd,kBAAkB;EAClB,2BAAW,IAAI,KAAK;EACpB,QAAQ;EACR,MAAM,CAAC;EACP,YAAY,YAAY,cAAc,CAAC;CACzC,CAC6B,CAAC;AAChC;;;;;;;;;;;AAYA,eAAsB,aACpB,IACA,OACA,cACA,aAKkB;CAClB,MAAM,UAAmC;EACvC,aAAa,YAAY;EACzB,aAAa,YAAY;EACzB,2BAAW,IAAI,KAAK;CACtB;CAKA,MAAM,SACJ,YAAY,eAAe,KAAA,IACvB,EAAE,MAAM,QAAQ,IAChB,CACE,EACE,MAAM;EACJ,GAAG;EACH,YAAY,EACV,YAAY;GACV,OAAO,EAAE,WAAW,CAAC,EAAE,SAAS,CAAC,eAAe,CAAC,CAAC,EAAE,GAAG,YAAY,UAAU,EAAE;GAC/E,QAAQ;EACV,EACF;CACF,EACF,CACF;CAQN,OAAO,MADc,wBAAwB,IAAI,IANjC,2BACd,YACA;EAAE,KAAK;EAAO;EAAO,aAAa;CAAa,GAC/C,QACA,KAEiD,CAAC,MAClC;AACpB;;;;;;;;;;;AAYA,eAAsB,iBACpB,IACA,OACA,OACe;CASf,MAAM,iBAAiB,IAAI,IARX,oBAAoB,YAAY;EAC9C,MAAM;EACN;EACA,QAAQ,MAAM;EACd,MAAM,MAAM;EACZ,IAAI,MAAM;EACV,2BAAW,IAAI,KAAK;CACtB,CAC6B,CAAC;AAChC;;;AC1OA,SAAgB,UAAU,QAAqD;CAC7E,MAAM,cAAc;CACpB,IAAI,CAAC,YAAY,IACf,MAAM,IAAI,MACR,+HAEF;CAEF,OAAO,YAAY;AACrB;;;;;;;;;;AAuDA,SAAgB,sBACd,eACA,QAMA,SACA,iBAA+C,IAAI,wBAAwB,GAClD;CACzB,OAAO;EACL,iBAAiB,IAAI,qBAAqB,UAAU,aAAa,CAAC;EAClE,oBAAoB,IAAI,wBAAwB,UAAU,aAAa,CAAC;EACxE,SAAS,mBAAmB;EAC5B;EACA,WAAW;GACT,aAAa,UAAU,eAAe,WAAW,eAAe,KAAK;GACrE,aAAa,OAAO,SAAS,eAAe,WAAW,eAAe,OAAO,IAAI;GACjF,eAAe,OAAO,cAAc,SAClC,eAAe,aAAa,eAAe,OAAO,cAAc,IAAI;GACtE,mBAAmB,OAAO,UACxB,eAAe,iBAAiB,eAAe,OAAO,KAAK;EAC/D;EACA,wBAAwB,eAAe,iBAAiB,aAAa;CACvE;AACF;;;;;;;;;ACvFA,IAAa,0BAAb,MAA6E;CAC3E,WAAoB;CACpB,WAAoB;CAEpB,MAAM,WACJ,QACA,OACsC;EACtC,OAAO,WAAW,UAAU,MAAM,GAAG,KAAK;CAC5C;CAEA,MAAM,eACJ,QACoD;EACpD,OAAO,eAAe,UAAU,MAAM,CAAC;CACzC;CAEA,MAAM,WACJ,QACA,OACA,aAKe;EACf,MAAM,WAAW,UAAU,MAAM,GAAG,OAAO,WAAW;CACxD;CAEA,MAAM,aACJ,QACA,OACA,cACA,aAKkB;EAClB,OAAO,aAAa,UAAU,MAAM,GAAG,OAAO,cAAc,WAAW;CACzE;CAEA,MAAM,iBACJ,QACA,OACA,OACe;EACf,MAAM,iBAAiB,UAAU,MAAM,GAAG,OAAO,KAAK;CACxD;CAEA,MAAM,iBAAiB,QAAyE;EAC9F,OAAO,iBAAiB,UAAU,MAAM,CAAC;CAC3C;AACF;;;AClEA,IAAM,yBAAN,MAAmE;CACjE,WAAoB;CACpB,WAAoB;CACpB;CACA;CAEA,YAAY,IAAQ,QAAqB;EACvC,KAAK,KAAK;EACV,KAAKA,UAAU;CACjB;CAEA,QAAwB;EACtB,MAAM,IAAI,MAAM,qDAAqD;CACvE;CAEA,MAAM,QAAuB;EAC3B,MAAM,KAAKA,QAAQ,MAAM;CAC3B;AACF;AAEA,SAAgB,yBAAyB,IAAQ,QAAiD;CAChG,OAAO,IAAI,uBAAuB,IAAI,MAAM;AAC9C;;;ACFA,MAAa,yBAAiE;CAC5E,MAAM;CACN,IAAI;CACJ,UAAU;CACV,UAAU;CACV,SAAS;CACT,uBAAuB,IAAI,IAAI;EAC7B,CAAC,UAAU,gBAAgB;EAC3B,CAAC,OAAO,eAAe;EACvB,CAAC,WAAW,cAAc;EAC1B,CAAC,YAAY,cAAc;EAC3B,CAAC,YAAY,kBAAkB;EAC/B,CAAC,SAAS,gBAAgB;CAC5B,CAAC;CACD,OAAO,EACL,YAAY;EACV,kBAAkB;EAClB,QAAQ;GACN,SAAS;GACT,OAAO;GACP,OAAO;EACT;EACA,aAAa,CACX;GACE,SAAS;GACT,OAAO;GACP,OAAO;EACT,CACF;CACF,EACF;CACA,OAAO,QAAQ;EACb,OAAO,IAAI,wBAAwB;CACrC;AACF"}
1
+ {"version":3,"file":"control.mjs","names":["#client"],"sources":["../src/core/command-executor.ts","../src/core/introspect-schema.ts","../src/core/marker-ledger.ts","../src/core/runner-deps.ts","../src/core/mongo-control-adapter.ts","../src/core/mongo-control-driver.ts","../src/exports/control.ts"],"sourcesContent":["import type {\n CollModCommand,\n CreateCollectionCommand,\n CreateIndexCommand,\n DropCollectionCommand,\n DropIndexCommand,\n ListCollectionsCommand,\n ListIndexesCommand,\n MongoDdlCommandVisitor,\n MongoInspectionCommandVisitor,\n} from '@prisma-next/mongo-query-ast/control';\nimport { keysToKeySpec } from '@prisma-next/mongo-query-ast/control';\nimport { type Db, type Document, MongoServerError } from 'mongodb';\n\nexport class MongoCommandExecutor implements MongoDdlCommandVisitor<Promise<void>> {\n constructor(private readonly db: Db) {}\n\n async createIndex(cmd: CreateIndexCommand): Promise<void> {\n const keySpec: Document = keysToKeySpec(cmd.keys);\n const options: Record<string, unknown> = {};\n if (cmd.unique !== undefined) options['unique'] = cmd.unique;\n if (cmd.sparse !== undefined) options['sparse'] = cmd.sparse;\n if (cmd.expireAfterSeconds !== undefined)\n options['expireAfterSeconds'] = cmd.expireAfterSeconds;\n if (cmd.partialFilterExpression !== undefined)\n options['partialFilterExpression'] = cmd.partialFilterExpression;\n if (cmd.name !== undefined) options['name'] = cmd.name;\n if (cmd.wildcardProjection !== undefined)\n options['wildcardProjection'] = cmd.wildcardProjection;\n if (cmd.collation !== undefined) options['collation'] = cmd.collation;\n if (cmd.weights !== undefined) options['weights'] = cmd.weights;\n if (cmd.default_language !== undefined) options['default_language'] = cmd.default_language;\n if (cmd.language_override !== undefined) options['language_override'] = cmd.language_override;\n await this.db.collection(cmd.collection).createIndex(keySpec, options);\n }\n\n async dropIndex(cmd: DropIndexCommand): Promise<void> {\n await this.db.collection(cmd.collection).dropIndex(cmd.name);\n }\n\n async createCollection(cmd: CreateCollectionCommand): Promise<void> {\n const options: Record<string, unknown> = {};\n if (cmd.capped !== undefined) options['capped'] = cmd.capped;\n if (cmd.size !== undefined) options['size'] = cmd.size;\n if (cmd.max !== undefined) options['max'] = cmd.max;\n if (cmd.timeseries !== undefined) options['timeseries'] = cmd.timeseries;\n if (cmd.collation !== undefined) options['collation'] = cmd.collation;\n if (cmd.clusteredIndex !== undefined) options['clusteredIndex'] = cmd.clusteredIndex;\n if (cmd.validator !== undefined) options['validator'] = cmd.validator;\n if (cmd.validationLevel !== undefined) options['validationLevel'] = cmd.validationLevel;\n if (cmd.validationAction !== undefined) options['validationAction'] = cmd.validationAction;\n if (cmd.changeStreamPreAndPostImages !== undefined)\n options['changeStreamPreAndPostImages'] = cmd.changeStreamPreAndPostImages;\n await this.db.createCollection(cmd.collection, options);\n }\n\n async dropCollection(cmd: DropCollectionCommand): Promise<void> {\n await this.db.collection(cmd.collection).drop();\n }\n\n async collMod(cmd: CollModCommand): Promise<void> {\n const command: Record<string, unknown> = { collMod: cmd.collection };\n if (cmd.validator !== undefined) command['validator'] = cmd.validator;\n if (cmd.validationLevel !== undefined) command['validationLevel'] = cmd.validationLevel;\n if (cmd.validationAction !== undefined) command['validationAction'] = cmd.validationAction;\n if (cmd.changeStreamPreAndPostImages !== undefined)\n command['changeStreamPreAndPostImages'] = cmd.changeStreamPreAndPostImages;\n await this.db.command(command);\n }\n}\n\nexport class MongoInspectionExecutor implements MongoInspectionCommandVisitor<Promise<Document[]>> {\n constructor(private readonly db: Db) {}\n\n async listIndexes(cmd: ListIndexesCommand): Promise<Document[]> {\n try {\n return await this.db.collection(cmd.collection).listIndexes().toArray();\n } catch (error: unknown) {\n if (error instanceof MongoServerError && error.code === 26) {\n return [];\n }\n throw error;\n }\n }\n\n async listCollections(_cmd: ListCollectionsCommand): Promise<Document[]> {\n return this.db.listCollections().toArray();\n }\n}\n","import type { MongoIndexKey, MongoIndexKeyDirection } from '@prisma-next/mongo-contract';\nimport {\n MongoSchemaCollection,\n MongoSchemaCollectionOptions,\n MongoSchemaIndex,\n MongoSchemaIR,\n MongoSchemaValidator,\n} from '@prisma-next/mongo-schema-ir';\nimport type { Db, Document } from 'mongodb';\n\nconst PRISMA_MIGRATIONS_COLLECTION = '_prisma_migrations';\n\nfunction parseIndexKeys(keySpec: Record<string, unknown>): MongoIndexKey[] {\n const keys: MongoIndexKey[] = [];\n for (const [field, direction] of Object.entries(keySpec)) {\n keys.push({ field, direction: direction as MongoIndexKeyDirection });\n }\n return keys;\n}\n\n/**\n * Exported for unit tests to exercise the defensive `!key` guard; not part of\n * the public API. Callers in this package use it via the `introspectSchema`\n * pipeline only.\n */\nexport function isDefaultIdIndex(doc: Document): boolean {\n const key = doc['key'] as Record<string, unknown> | undefined;\n if (!key) return false;\n const entries = Object.entries(key);\n return entries.length === 1 && entries[0]?.[0] === '_id' && entries[0]?.[1] === 1;\n}\n\nfunction parseIndex(doc: Document): MongoSchemaIndex {\n const keySpec = doc['key'] as Record<string, unknown>;\n return new MongoSchemaIndex({\n keys: parseIndexKeys(keySpec),\n unique: doc['unique'] as boolean | undefined,\n sparse: doc['sparse'] as boolean | undefined,\n expireAfterSeconds: doc['expireAfterSeconds'] as number | undefined,\n partialFilterExpression: doc['partialFilterExpression'] as Record<string, unknown> | undefined,\n wildcardProjection: doc['wildcardProjection'] as Record<string, 0 | 1> | undefined,\n collation: doc['collation'] as Record<string, unknown> | undefined,\n weights: doc['weights'] as Record<string, number> | undefined,\n default_language: doc['default_language'] as string | undefined,\n language_override: doc['language_override'] as string | undefined,\n });\n}\n\nfunction parseValidator(options: Document): MongoSchemaValidator | undefined {\n const validator = options['validator'] as Record<string, unknown> | undefined;\n if (!validator) return undefined;\n\n const jsonSchema = validator['$jsonSchema'] as Record<string, unknown> | undefined;\n if (!jsonSchema) return undefined;\n\n return new MongoSchemaValidator({\n jsonSchema,\n validationLevel: (options['validationLevel'] as 'strict' | 'moderate') ?? 'strict',\n validationAction: (options['validationAction'] as 'error' | 'warn') ?? 'error',\n });\n}\n\nfunction parseCollectionOptions(info: Document): MongoSchemaCollectionOptions | undefined {\n const options = info['options'] as Record<string, unknown> | undefined;\n if (!options) return undefined;\n\n const capped = options['capped'] as boolean | undefined;\n const size = options['size'] as number | undefined;\n const max = options['max'] as number | undefined;\n const timeseries = options['timeseries'] as\n | { timeField: string; metaField?: string; granularity?: 'seconds' | 'minutes' | 'hours' }\n | undefined;\n const collation = options['collation'] as Record<string, unknown> | undefined;\n const changeStreamPreAndPostImages = options['changeStreamPreAndPostImages'] as\n | { enabled: boolean }\n | undefined;\n const clusteredIndex = options['clusteredIndex'] as { name?: string } | undefined;\n\n const hasMeaningfulOptions =\n capped || timeseries || collation || changeStreamPreAndPostImages || clusteredIndex;\n if (!hasMeaningfulOptions) return undefined;\n\n return new MongoSchemaCollectionOptions({\n ...(capped ? { capped: { size: size ?? 0, ...(max != null ? { max } : {}) } } : {}),\n ...(timeseries ? { timeseries } : {}),\n ...(collation ? { collation } : {}),\n ...(changeStreamPreAndPostImages ? { changeStreamPreAndPostImages } : {}),\n ...(clusteredIndex ? { clusteredIndex } : {}),\n });\n}\n\nexport async function introspectSchema(db: Db): Promise<MongoSchemaIR> {\n const collectionInfos = await db.listCollections().toArray();\n\n const collections: MongoSchemaCollection[] = [];\n\n for (const info of collectionInfos) {\n const name = info['name'] as string;\n const type = info['type'] as string | undefined;\n\n if (name === PRISMA_MIGRATIONS_COLLECTION) continue;\n if (name.startsWith('system.')) continue;\n if (type === 'view') continue;\n\n const indexDocs = await db.collection(name).listIndexes().toArray();\n const indexes = indexDocs.filter((doc) => !isDefaultIdIndex(doc)).map(parseIndex);\n\n const infoOptions = 'options' in info ? (info['options'] as Record<string, unknown>) : {};\n const validator = parseValidator(infoOptions);\n const options = parseCollectionOptions(info);\n\n collections.push(\n new MongoSchemaCollection({\n name,\n indexes,\n ...(validator ? { validator } : {}),\n ...(options ? { options } : {}),\n }),\n );\n }\n\n return new MongoSchemaIR(collections);\n}\n","import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport { parseMarkerRowSafely } from '@prisma-next/errors/execution';\nimport type {\n RawAggregateCommand,\n RawFindOneAndUpdateCommand,\n RawInsertOneCommand,\n} from '@prisma-next/mongo-query-ast/execution';\nimport { type } from 'arktype';\nimport type { Db, Document, UpdateFilter } from 'mongodb';\n\nexport const COLLECTION = '_prisma_migrations';\nexport const MONGO_MARKER_COLLECTION = `_prisma_migrations marker documents in ${COLLECTION}`;\nexport const MONGO_LEDGER_COLLECTION = `_prisma_migrations ledger documents in ${COLLECTION}`;\n\nconst MongoMarkerDocSchema = type({\n space: 'string',\n storageHash: 'string',\n profileHash: 'string',\n 'contractJson?': 'unknown | null',\n 'canonicalVersion?': 'number | null',\n 'updatedAt?': 'Date',\n 'appTag?': 'string | null',\n 'meta?': type({ '[string]': 'unknown' }).or('null'),\n 'invariants?': type('string').array(),\n '+': 'delete',\n});\n\nexport function parseMongoMarkerDoc(doc: unknown): ContractMarkerRecord {\n const result = MongoMarkerDocSchema(doc);\n if (result instanceof type.errors) {\n throw new Error(`Invalid marker doc on ${COLLECTION}: ${result.summary}`);\n }\n return {\n storageHash: result.storageHash,\n profileHash: result.profileHash,\n contractJson: result.contractJson ?? null,\n canonicalVersion: result.canonicalVersion ?? null,\n updatedAt: result.updatedAt ?? new Date(),\n appTag: result.appTag ?? null,\n meta: (result.meta as Record<string, unknown> | null) ?? {},\n invariants: result.invariants ?? [],\n };\n}\n\nexport function parseMongoMarkerDocSafely(doc: unknown, space: string): ContractMarkerRecord {\n return parseMarkerRowSafely(doc, parseMongoMarkerDoc, {\n space,\n markerLocation: MONGO_MARKER_COLLECTION,\n });\n}\n\nexport async function executeAggregate(db: Db, cmd: RawAggregateCommand): Promise<Document[]> {\n return db\n .collection(cmd.collection)\n .aggregate(cmd.pipeline as Record<string, unknown>[])\n .toArray();\n}\n\nexport async function executeInsertOne(db: Db, cmd: RawInsertOneCommand): Promise<void> {\n await db.collection(cmd.collection).insertOne(cmd.document);\n}\n\nexport async function executeFindOneAndUpdate(\n db: Db,\n cmd: RawFindOneAndUpdateCommand,\n): Promise<Document | null> {\n return db\n .collection(cmd.collection)\n .findOneAndUpdate(cmd.filter, cmd.update as UpdateFilter<Document> | Document[], {\n upsert: cmd.upsert,\n });\n}\n","import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type { MongoControlAdapter } from '@prisma-next/family-mongo/control-adapter';\nimport type {\n ControlDriverInstance,\n ControlFamilyInstance,\n} from '@prisma-next/framework-components/control';\nimport type { MongoAdapter, MongoDriver } from '@prisma-next/mongo-lowering';\nimport type {\n MongoDdlCommandVisitor,\n MongoInspectionCommandVisitor,\n} from '@prisma-next/mongo-query-ast/control';\nimport type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport type { Db } from 'mongodb';\nimport { createMongoAdapter } from '../mongo-adapter';\nimport { MongoCommandExecutor, MongoInspectionExecutor } from './command-executor';\nimport { MongoControlAdapterImpl } from './mongo-control-adapter';\n\nexport function extractDb(driver: ControlDriverInstance<'mongo', 'mongo'>): Db {\n const mongoDriver = driver as ControlDriverInstance<'mongo', 'mongo'> & { db?: Db };\n if (!mongoDriver.db) {\n throw new Error(\n 'Mongo control driver does not expose a db property. ' +\n 'Use mongoControlDriver.create() from `@prisma-next/driver-mongo/control`.',\n );\n }\n return mongoDriver.db;\n}\n\n/**\n * Marker / ledger operations the Mongo runner depends on. Every method\n * takes a `space` parameter so each loaded contract space addresses its\n * own marker row independently — see ADR 212 for the per-space\n * mechanism.\n */\nexport interface MarkerOperations {\n readMarker(space: string): Promise<ContractMarkerRecord | null>;\n initMarker(\n space: string,\n destination: {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly invariants?: readonly string[];\n },\n ): Promise<void>;\n updateMarker(\n space: string,\n expectedFrom: string,\n destination: {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly invariants?: readonly string[];\n },\n ): Promise<boolean>;\n writeLedgerEntry(\n space: string,\n entry: {\n readonly edgeId: string;\n readonly from: string;\n readonly to: string;\n readonly migrationName: string;\n readonly migrationHash: string;\n readonly operations: readonly unknown[];\n },\n ): Promise<void>;\n}\n\nexport interface MongoRunnerDependencies {\n readonly commandExecutor: MongoDdlCommandVisitor<Promise<void>>;\n readonly inspectionExecutor: MongoInspectionCommandVisitor<Promise<Record<string, unknown>[]>>;\n readonly adapter: MongoAdapter;\n readonly driver: MongoDriver;\n readonly markerOps: MarkerOperations;\n readonly introspectSchema: () => Promise<MongoSchemaIR>;\n}\n\n/**\n * Build the runner-dependencies envelope. `controlAdapter` is the\n * dispatch surface for wire-level Mongo CAS operations (marker reads,\n * marker advances, ledger appends, introspection); the envelope's\n * `markerOps` shim simply forwards each call through it. When the\n * caller already has a `MongoControlAdapter` on the control stack it\n * can pass it in; otherwise a default `MongoControlAdapterImpl` is\n * constructed locally.\n */\nexport function createMongoRunnerDeps(\n controlDriver: ControlDriverInstance<'mongo', 'mongo'>,\n driver: MongoDriver,\n // Vestigial after the family→adapter SPI refactor: the runner dependencies\n // now route every wire-level call through `controlAdapter`, so the `family`\n // instance is no longer consulted. Kept on the signature to avoid rippling\n // through ~14 call sites; a follow-up that already touches this factory\n // should drop the parameter outright.\n _family: ControlFamilyInstance<'mongo', MongoSchemaIR>,\n controlAdapter: MongoControlAdapter<'mongo'> = new MongoControlAdapterImpl(),\n): MongoRunnerDependencies {\n return {\n commandExecutor: new MongoCommandExecutor(extractDb(controlDriver)),\n inspectionExecutor: new MongoInspectionExecutor(extractDb(controlDriver)),\n adapter: createMongoAdapter(),\n driver,\n markerOps: {\n readMarker: (space) => controlAdapter.readMarker(controlDriver, space),\n initMarker: (space, dest) => controlAdapter.initMarker(controlDriver, space, dest),\n updateMarker: (space, expectedFrom, dest) =>\n controlAdapter.updateMarker(controlDriver, space, expectedFrom, dest),\n writeLedgerEntry: (space, entry) =>\n controlAdapter.writeLedgerEntry(controlDriver, space, entry),\n },\n introspectSchema: () => controlAdapter.introspectSchema(controlDriver),\n };\n}\n","import type { ContractMarkerRecord, LedgerEntryRecord } from '@prisma-next/contract/types';\nimport { withMarkerReadErrorHandling } from '@prisma-next/errors/execution';\nimport type { MongoControlAdapter } from '@prisma-next/family-mongo/control-adapter';\nimport type { ControlDriverInstance } from '@prisma-next/framework-components/control';\nimport { ledgerOriginFromStored } from '@prisma-next/migration-tools/ledger-origin';\nimport {\n RawAggregateCommand,\n RawFindOneAndUpdateCommand,\n RawInsertOneCommand,\n} from '@prisma-next/mongo-query-ast/execution';\nimport type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport type { Document } from 'mongodb';\nimport { introspectSchema } from './introspect-schema';\nimport {\n COLLECTION,\n executeAggregate,\n executeFindOneAndUpdate,\n executeInsertOne,\n MONGO_LEDGER_COLLECTION,\n MONGO_MARKER_COLLECTION,\n parseMongoMarkerDocSafely,\n} from './marker-ledger';\nimport { extractDb } from './runner-deps';\n\n/**\n * Mongo control adapter for control-plane operations like introspection\n * and marker-ledger CAS. Implements the family-level `MongoControlAdapter`\n * SPI by extracting the underlying `Db` from the framework-shaped driver\n * per call.\n */\nexport class MongoControlAdapterImpl implements MongoControlAdapter<'mongo'> {\n readonly familyId = 'mongo' as const;\n readonly targetId = 'mongo' as const;\n\n async readMarker(\n driver: ControlDriverInstance<'mongo', 'mongo'>,\n space: string,\n ): Promise<ContractMarkerRecord | null> {\n const db = extractDb(driver);\n const markerContext = { space, markerLocation: MONGO_MARKER_COLLECTION };\n const docs = await withMarkerReadErrorHandling(\n () =>\n executeAggregate(\n db,\n new RawAggregateCommand(COLLECTION, [{ $match: { _id: space, space } }, { $limit: 1 }]),\n ),\n markerContext,\n );\n const doc = docs[0];\n if (!doc) return null;\n return parseMongoMarkerDocSafely(doc, space);\n }\n\n async readAllMarkers(\n driver: ControlDriverInstance<'mongo', 'mongo'>,\n ): Promise<ReadonlyMap<string, ContractMarkerRecord>> {\n const db = extractDb(driver);\n const markerContext = { space: 'app', markerLocation: MONGO_MARKER_COLLECTION };\n const docs = await withMarkerReadErrorHandling(\n () =>\n executeAggregate(\n db,\n new RawAggregateCommand(COLLECTION, [\n {\n $match: {\n _id: { $type: 'string' },\n space: { $type: 'string' },\n $expr: { $eq: ['$_id', '$space'] },\n },\n },\n ]),\n ),\n markerContext,\n );\n const out = new Map<string, ContractMarkerRecord>();\n for (const doc of docs) {\n const space = doc['space'];\n /* v8 ignore next -- @preserve type-narrowing guard: the $match stage above filters on `space: { $type: 'string' }`, so this branch is unreachable at runtime. The check exists so the `out.set(space, ...)` call below can accept `string`. */\n if (typeof space !== 'string') continue;\n out.set(space, parseMongoMarkerDocSafely(doc, space));\n }\n return out;\n }\n\n async initMarker(\n driver: ControlDriverInstance<'mongo', 'mongo'>,\n space: string,\n destination: {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly invariants?: readonly string[];\n },\n ): Promise<void> {\n const db = extractDb(driver);\n const cmd = new RawInsertOneCommand(COLLECTION, {\n _id: space,\n space,\n storageHash: destination.storageHash,\n profileHash: destination.profileHash,\n contractJson: null,\n canonicalVersion: null,\n updatedAt: new Date(),\n appTag: null,\n meta: {},\n invariants: destination.invariants ?? [],\n });\n await executeInsertOne(db, cmd);\n }\n\n async updateMarker(\n driver: ControlDriverInstance<'mongo', 'mongo'>,\n space: string,\n expectedFrom: string,\n destination: {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly invariants?: readonly string[];\n },\n ): Promise<boolean> {\n const db = extractDb(driver);\n const setBase: Record<string, unknown> = {\n storageHash: destination.storageHash,\n profileHash: destination.profileHash,\n updatedAt: new Date(),\n };\n const update: Document | Document[] =\n destination.invariants === undefined\n ? { $set: setBase }\n : [\n {\n $set: {\n ...setBase,\n invariants: {\n $sortArray: {\n input: {\n $setUnion: [{ $ifNull: ['$invariants', []] }, destination.invariants],\n },\n sortBy: 1,\n },\n },\n },\n },\n ];\n const cmd = new RawFindOneAndUpdateCommand(\n COLLECTION,\n { _id: space, space, storageHash: expectedFrom },\n update,\n false,\n );\n const result = await executeFindOneAndUpdate(db, cmd);\n return result !== null;\n }\n\n async writeLedgerEntry(\n driver: ControlDriverInstance<'mongo', 'mongo'>,\n space: string,\n entry: {\n readonly edgeId: string;\n readonly from: string;\n readonly to: string;\n readonly migrationName: string;\n readonly migrationHash: string;\n readonly operations: readonly unknown[];\n },\n ): Promise<void> {\n const db = extractDb(driver);\n const cmd = new RawInsertOneCommand(COLLECTION, {\n type: 'ledger',\n space,\n edgeId: entry.edgeId,\n from: entry.from,\n to: entry.to,\n migrationName: entry.migrationName,\n migrationHash: entry.migrationHash,\n operations: entry.operations,\n appliedAt: new Date(),\n });\n await executeInsertOne(db, cmd);\n }\n\n async readLedger(\n driver: ControlDriverInstance<'mongo', 'mongo'>,\n space?: string,\n ): Promise<readonly LedgerEntryRecord[]> {\n const db = extractDb(driver);\n const ledgerContext = { space: space ?? '*', markerLocation: MONGO_LEDGER_COLLECTION };\n const matchStage: Record<string, unknown> = { type: 'ledger' };\n if (space !== undefined) {\n matchStage['space'] = space;\n }\n const docs = await withMarkerReadErrorHandling(\n () =>\n executeAggregate(\n db,\n new RawAggregateCommand(COLLECTION, [{ $match: matchStage }, { $sort: { _id: 1 } }]),\n ),\n ledgerContext,\n );\n\n const entries: LedgerEntryRecord[] = [];\n for (const doc of docs) {\n const migrationName = doc['migrationName'];\n const migrationHash = doc['migrationHash'];\n const from = doc['from'];\n const to = doc['to'];\n const docSpace = doc['space'];\n if (typeof migrationName !== 'string' || typeof migrationHash !== 'string') {\n continue;\n }\n if (typeof from !== 'string' || typeof to !== 'string') {\n continue;\n }\n if (typeof docSpace !== 'string') {\n continue;\n }\n const appliedAt = doc['appliedAt'];\n const appliedAtDate =\n appliedAt instanceof Date\n ? appliedAt\n : appliedAt !== undefined\n ? new Date(String(appliedAt))\n : new Date();\n const operations = doc['operations'];\n const opList = Array.isArray(operations) ? operations : [];\n entries.push({\n space: docSpace,\n migrationName,\n migrationHash,\n from: ledgerOriginFromStored(from),\n to,\n appliedAt: appliedAtDate,\n operationCount: opList.length,\n });\n }\n return entries;\n }\n\n async introspectSchema(driver: ControlDriverInstance<'mongo', 'mongo'>): Promise<MongoSchemaIR> {\n return introspectSchema(extractDb(driver));\n }\n}\n","import type { ControlDriverInstance } from '@prisma-next/framework-components/control';\nimport type { Db, MongoClient } from 'mongodb';\n\nexport interface MongoControlDriverInstance extends ControlDriverInstance<'mongo', 'mongo'> {\n readonly db: Db;\n}\n\nclass MongoControlDriverImpl implements MongoControlDriverInstance {\n readonly familyId = 'mongo' as const;\n readonly targetId = 'mongo' as const;\n readonly db: Db;\n readonly #client: MongoClient;\n\n constructor(db: Db, client: MongoClient) {\n this.db = db;\n this.#client = client;\n }\n\n query(): Promise<never> {\n throw new Error('MongoDB control driver does not support SQL queries');\n }\n\n async close(): Promise<void> {\n await this.#client.close();\n }\n}\n\nexport function createMongoControlDriver(db: Db, client: MongoClient): MongoControlDriverInstance {\n return new MongoControlDriverImpl(db, client);\n}\n","import type { ContractMarkerRecord, LedgerEntryRecord } from '@prisma-next/contract/types';\nimport type { MongoControlAdapterDescriptor } from '@prisma-next/family-mongo/control-adapter';\nimport type { ControlDriverInstance } from '@prisma-next/framework-components/control';\nimport type { Db } from 'mongodb';\n\nexport { MongoCommandExecutor, MongoInspectionExecutor } from '../core/command-executor';\nexport { introspectSchema } from '../core/introspect-schema';\nexport { MongoControlAdapterImpl } from '../core/mongo-control-adapter';\nexport {\n createMongoControlDriver,\n type MongoControlDriverInstance,\n} from '../core/mongo-control-driver';\nexport {\n createMongoRunnerDeps,\n extractDb,\n type MarkerOperations,\n type MongoRunnerDependencies,\n} from '../core/runner-deps';\nexport { createMongoAdapter } from '../mongo-adapter';\n\nimport { mongoCodecDescriptors } from '../core/codecs';\nimport { MongoControlAdapterImpl } from '../core/mongo-control-adapter';\n\nconst defaultControlAdapter = new MongoControlAdapterImpl();\n\nfunction controlDriverFromDb(db: Db): ControlDriverInstance<'mongo', 'mongo'> & { db: Db } {\n return {\n familyId: 'mongo',\n targetId: 'mongo',\n db,\n query: () => Promise.reject(new Error('MongoDB control driver does not support SQL queries')),\n close: async () => {},\n };\n}\n\nexport async function readMarker(db: Db, space: string): Promise<ContractMarkerRecord | null> {\n return defaultControlAdapter.readMarker(controlDriverFromDb(db), space);\n}\n\nexport async function readAllMarkers(db: Db): Promise<ReadonlyMap<string, ContractMarkerRecord>> {\n return defaultControlAdapter.readAllMarkers(controlDriverFromDb(db));\n}\n\nexport async function initMarker(\n db: Db,\n space: string,\n destination: {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly invariants?: readonly string[];\n },\n): Promise<void> {\n await defaultControlAdapter.initMarker(controlDriverFromDb(db), space, destination);\n}\n\nexport async function updateMarker(\n db: Db,\n space: string,\n expectedFrom: string,\n destination: {\n readonly storageHash: string;\n readonly profileHash: string;\n readonly invariants?: readonly string[];\n },\n): Promise<boolean> {\n return defaultControlAdapter.updateMarker(\n controlDriverFromDb(db),\n space,\n expectedFrom,\n destination,\n );\n}\n\nexport async function readLedger(db: Db, space?: string): Promise<readonly LedgerEntryRecord[]> {\n return defaultControlAdapter.readLedger(controlDriverFromDb(db), space);\n}\n\nexport async function writeLedgerEntry(\n db: Db,\n space: string,\n entry: {\n readonly edgeId: string;\n readonly from: string;\n readonly to: string;\n readonly migrationName: string;\n readonly migrationHash: string;\n readonly operations: readonly unknown[];\n },\n): Promise<void> {\n await defaultControlAdapter.writeLedgerEntry(controlDriverFromDb(db), space, entry);\n}\n\nexport const mongoAdapterDescriptor: MongoControlAdapterDescriptor<'mongo'> = {\n kind: 'adapter',\n id: 'mongo',\n familyId: 'mongo',\n targetId: 'mongo',\n version: '0.0.1',\n scalarTypeDescriptors: new Map([\n ['String', 'mongo/string@1'],\n ['Int', 'mongo/int32@1'],\n ['Boolean', 'mongo/bool@1'],\n ['DateTime', 'mongo/date@1'],\n ['ObjectId', 'mongo/objectId@1'],\n ['Float', 'mongo/double@1'],\n ]),\n types: {\n codecTypes: {\n codecDescriptors: mongoCodecDescriptors,\n import: {\n package: '@prisma-next/adapter-mongo/codec-types',\n named: 'CodecTypes',\n alias: 'MongoCodecTypes',\n },\n typeImports: [\n {\n package: '@prisma-next/adapter-mongo/codec-types',\n named: 'Vector',\n alias: 'Vector',\n },\n ],\n },\n },\n create(_stack) {\n return new MongoControlAdapterImpl();\n },\n};\n\nexport default mongoAdapterDescriptor;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,36 +1,37 @@
1
1
  {
2
2
  "name": "@prisma-next/adapter-mongo",
3
- "version": "0.12.0-dev.4",
3
+ "version": "0.12.0-dev.40",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "MongoDB adapter for Prisma Next (lowers commands to wire format)",
8
8
  "dependencies": {
9
- "@prisma-next/config": "0.12.0-dev.4",
10
- "@prisma-next/contract": "0.12.0-dev.4",
11
- "@prisma-next/errors": "0.12.0-dev.4",
12
- "@prisma-next/family-mongo": "0.12.0-dev.4",
13
- "@prisma-next/framework-components": "0.12.0-dev.4",
14
- "@prisma-next/mongo-codec": "0.12.0-dev.4",
15
- "@prisma-next/mongo-contract": "0.12.0-dev.4",
16
- "@prisma-next/mongo-lowering": "0.12.0-dev.4",
17
- "@prisma-next/mongo-query-ast": "0.12.0-dev.4",
18
- "@prisma-next/mongo-schema-ir": "0.12.0-dev.4",
19
- "@prisma-next/mongo-value": "0.12.0-dev.4",
20
- "@prisma-next/mongo-wire": "0.12.0-dev.4",
21
- "@prisma-next/operations": "0.12.0-dev.4",
22
- "@prisma-next/utils": "0.12.0-dev.4",
9
+ "@prisma-next/config": "0.12.0-dev.40",
10
+ "@prisma-next/contract": "0.12.0-dev.40",
11
+ "@prisma-next/errors": "0.12.0-dev.40",
12
+ "@prisma-next/family-mongo": "0.12.0-dev.40",
13
+ "@prisma-next/framework-components": "0.12.0-dev.40",
14
+ "@prisma-next/migration-tools": "0.12.0-dev.40",
15
+ "@prisma-next/mongo-codec": "0.12.0-dev.40",
16
+ "@prisma-next/mongo-contract": "0.12.0-dev.40",
17
+ "@prisma-next/mongo-lowering": "0.12.0-dev.40",
18
+ "@prisma-next/mongo-query-ast": "0.12.0-dev.40",
19
+ "@prisma-next/mongo-schema-ir": "0.12.0-dev.40",
20
+ "@prisma-next/mongo-value": "0.12.0-dev.40",
21
+ "@prisma-next/mongo-wire": "0.12.0-dev.40",
22
+ "@prisma-next/operations": "0.12.0-dev.40",
23
+ "@prisma-next/utils": "0.12.0-dev.40",
23
24
  "arktype": "^2.2.0"
24
25
  },
25
26
  "devDependencies": {
26
27
  "mongodb": "^7.2.0",
27
- "@prisma-next/driver-mongo": "0.12.0-dev.4",
28
- "@prisma-next/errors": "0.12.0-dev.4",
29
- "@prisma-next/mongo-contract-psl": "0.12.0-dev.4",
30
- "@prisma-next/psl-parser": "0.12.0-dev.4",
31
- "@prisma-next/test-utils": "0.12.0-dev.4",
32
- "@prisma-next/tsconfig": "0.12.0-dev.4",
33
- "@prisma-next/tsdown": "0.12.0-dev.4",
28
+ "@prisma-next/driver-mongo": "0.12.0-dev.40",
29
+ "@prisma-next/errors": "0.12.0-dev.40",
30
+ "@prisma-next/mongo-contract-psl": "0.12.0-dev.40",
31
+ "@prisma-next/psl-parser": "0.12.0-dev.40",
32
+ "@prisma-next/test-utils": "0.12.0-dev.40",
33
+ "@prisma-next/tsconfig": "0.12.0-dev.40",
34
+ "@prisma-next/tsdown": "0.12.0-dev.40",
34
35
  "mongodb-memory-server": "11.1.0",
35
36
  "tsdown": "0.22.0",
36
37
  "typescript": "5.9.3",
@@ -1,6 +1,6 @@
1
1
  import type { ContractMarkerRecord } from '@prisma-next/contract/types';
2
- import { parseMarkerRowSafely, withMarkerReadErrorHandling } from '@prisma-next/errors/execution';
3
- import {
2
+ import { parseMarkerRowSafely } from '@prisma-next/errors/execution';
3
+ import type {
4
4
  RawAggregateCommand,
5
5
  RawFindOneAndUpdateCommand,
6
6
  RawInsertOneCommand,
@@ -8,20 +8,10 @@ import {
8
8
  import { type } from 'arktype';
9
9
  import type { Db, Document, UpdateFilter } from 'mongodb';
10
10
 
11
- const COLLECTION = '_prisma_migrations';
12
- const MONGO_MARKER_COLLECTION = `_prisma_migrations marker documents in ${COLLECTION}`;
11
+ export const COLLECTION = '_prisma_migrations';
12
+ export const MONGO_MARKER_COLLECTION = `_prisma_migrations marker documents in ${COLLECTION}`;
13
+ export const MONGO_LEDGER_COLLECTION = `_prisma_migrations ledger documents in ${COLLECTION}`;
13
14
 
14
- /**
15
- * Marker doc shape.
16
- *
17
- * Same fields as the SQL marker row but camelCase + Mongo-native types:
18
- * `Date` is BSON-hydrated, `meta` is a native object (not JSON-stringified),
19
- * `_id` and any extension fields are tolerated. `invariants?` is optional —
20
- * absent reads as `[]` (schemaless default); present-but-malformed throws.
21
- *
22
- * `space` is required: every marker doc is keyed by its space id (`_id`)
23
- * and stamped with a matching `space` field for partitioned reads.
24
- */
25
15
  const MongoMarkerDocSchema = type({
26
16
  space: 'string',
27
17
  storageHash: 'string',
@@ -35,7 +25,7 @@ const MongoMarkerDocSchema = type({
35
25
  '+': 'delete',
36
26
  });
37
27
 
38
- function parseMongoMarkerDoc(doc: unknown): ContractMarkerRecord {
28
+ export function parseMongoMarkerDoc(doc: unknown): ContractMarkerRecord {
39
29
  const result = MongoMarkerDocSchema(doc);
40
30
  if (result instanceof type.errors) {
41
31
  throw new Error(`Invalid marker doc on ${COLLECTION}: ${result.summary}`);
@@ -52,201 +42,31 @@ function parseMongoMarkerDoc(doc: unknown): ContractMarkerRecord {
52
42
  };
53
43
  }
54
44
 
55
- function parseMongoMarkerDocSafely(doc: unknown, space: string): ContractMarkerRecord {
45
+ export function parseMongoMarkerDocSafely(doc: unknown, space: string): ContractMarkerRecord {
56
46
  return parseMarkerRowSafely(doc, parseMongoMarkerDoc, {
57
47
  space,
58
48
  markerLocation: MONGO_MARKER_COLLECTION,
59
49
  });
60
50
  }
61
51
 
62
- async function executeAggregate(db: Db, cmd: RawAggregateCommand): Promise<Document[]> {
52
+ export async function executeAggregate(db: Db, cmd: RawAggregateCommand): Promise<Document[]> {
63
53
  return db
64
54
  .collection(cmd.collection)
65
55
  .aggregate(cmd.pipeline as Record<string, unknown>[])
66
56
  .toArray();
67
57
  }
68
58
 
69
- async function executeInsertOne(db: Db, cmd: RawInsertOneCommand): Promise<void> {
59
+ export async function executeInsertOne(db: Db, cmd: RawInsertOneCommand): Promise<void> {
70
60
  await db.collection(cmd.collection).insertOne(cmd.document);
71
61
  }
72
62
 
73
- async function executeFindOneAndUpdate(
63
+ export async function executeFindOneAndUpdate(
74
64
  db: Db,
75
65
  cmd: RawFindOneAndUpdateCommand,
76
66
  ): Promise<Document | null> {
77
- // `cmd.update` is `Document | ReadonlyArray<Document>` per the AST. The
78
- // MongoDB driver's `findOneAndUpdate` accepts the same shape under the
79
- // type `UpdateFilter<T> | Document[]`. The driver's runtime path handles
80
- // both forms identically — pipelines (array) and update docs (object).
81
- // One cast to that union keeps the call single-arm.
82
67
  return db
83
68
  .collection(cmd.collection)
84
69
  .findOneAndUpdate(cmd.filter, cmd.update as UpdateFilter<Document> | Document[], {
85
70
  upsert: cmd.upsert,
86
71
  });
87
72
  }
88
-
89
- /**
90
- * Reads the marker document for the given contract space, or returns
91
- * `null` if no marker has been written for that space yet. Each space
92
- * owns one row keyed by `_id: <space>` — see ADR 212 for the per-space
93
- * mechanism this enables.
94
- */
95
- export async function readMarker(db: Db, space: string): Promise<ContractMarkerRecord | null> {
96
- const markerContext = { space, markerLocation: MONGO_MARKER_COLLECTION };
97
- const docs = await withMarkerReadErrorHandling(
98
- () =>
99
- executeAggregate(
100
- db,
101
- new RawAggregateCommand(COLLECTION, [{ $match: { _id: space, space } }, { $limit: 1 }]),
102
- ),
103
- markerContext,
104
- );
105
- const doc = docs[0];
106
- if (!doc) return null;
107
- return parseMongoMarkerDocSafely(doc, space);
108
- }
109
-
110
- /**
111
- * Reads every marker doc in the collection (one per contract space)
112
- * and returns them keyed by `space`. Used by the per-space verifier
113
- * to detect marker-vs-on-disk drift and orphan marker rows. Returns
114
- * an empty map when no marker docs have been written yet.
115
- *
116
- * Marker docs are keyed by `_id: <space>` (string); ledger entries
117
- * live in the same collection but use a driver-generated `ObjectId`
118
- * `_id` plus `type: 'ledger'`. The filter selects string-keyed docs
119
- * with a `space` field, which excludes ledger entries by construction.
120
- */
121
- export async function readAllMarkers(db: Db): Promise<ReadonlyMap<string, ContractMarkerRecord>> {
122
- const markerContext = { space: 'app', markerLocation: MONGO_MARKER_COLLECTION };
123
- const docs = await withMarkerReadErrorHandling(
124
- () =>
125
- executeAggregate(
126
- db,
127
- new RawAggregateCommand(COLLECTION, [
128
- {
129
- $match: {
130
- _id: { $type: 'string' },
131
- space: { $type: 'string' },
132
- $expr: { $eq: ['$_id', '$space'] },
133
- },
134
- },
135
- ]),
136
- ),
137
- markerContext,
138
- );
139
- const out = new Map<string, ContractMarkerRecord>();
140
- for (const doc of docs) {
141
- const space = doc['space'];
142
- /* v8 ignore next -- @preserve type-narrowing guard: the $match stage above filters on `space: { $type: 'string' }`, so this branch is unreachable at runtime. The check exists so the `out.set(space, ...)` call below can accept `string`. */
143
- if (typeof space !== 'string') continue;
144
- out.set(space, parseMongoMarkerDocSafely(doc, space));
145
- }
146
- return out;
147
- }
148
-
149
- export async function initMarker(
150
- db: Db,
151
- space: string,
152
- destination: {
153
- readonly storageHash: string;
154
- readonly profileHash: string;
155
- readonly invariants?: readonly string[];
156
- },
157
- ): Promise<void> {
158
- const cmd = new RawInsertOneCommand(COLLECTION, {
159
- _id: space,
160
- space,
161
- storageHash: destination.storageHash,
162
- profileHash: destination.profileHash,
163
- contractJson: null,
164
- canonicalVersion: null,
165
- updatedAt: new Date(),
166
- appTag: null,
167
- meta: {},
168
- invariants: destination.invariants ?? [],
169
- });
170
- await executeInsertOne(db, cmd);
171
- }
172
-
173
- /**
174
- * Updates the marker doc for the given space atomically (CAS on
175
- * `expectedFrom`).
176
- *
177
- * `destination.invariants`:
178
- * - `undefined` → existing field left untouched.
179
- * - explicit value → merged into the existing field server-side via an
180
- * aggregation pipeline (`$setUnion + $sortArray`), atomic at the
181
- * document level. `[]` is a no-op merge.
182
- */
183
- export async function updateMarker(
184
- db: Db,
185
- space: string,
186
- expectedFrom: string,
187
- destination: {
188
- readonly storageHash: string;
189
- readonly profileHash: string;
190
- readonly invariants?: readonly string[];
191
- },
192
- ): Promise<boolean> {
193
- const setBase: Record<string, unknown> = {
194
- storageHash: destination.storageHash,
195
- profileHash: destination.profileHash,
196
- updatedAt: new Date(),
197
- };
198
- // When invariants is supplied, use an aggregation pipeline so the
199
- // merge runs server-side against the doc's current value (atomic, no
200
- // read-then-write window). When omitted, a regular update doc keeps
201
- // the field untouched.
202
- const update: Document | Document[] =
203
- destination.invariants === undefined
204
- ? { $set: setBase }
205
- : [
206
- {
207
- $set: {
208
- ...setBase,
209
- invariants: {
210
- $sortArray: {
211
- input: { $setUnion: [{ $ifNull: ['$invariants', []] }, destination.invariants] },
212
- sortBy: 1,
213
- },
214
- },
215
- },
216
- },
217
- ];
218
- const cmd = new RawFindOneAndUpdateCommand(
219
- COLLECTION,
220
- { _id: space, space, storageHash: expectedFrom },
221
- update,
222
- false,
223
- );
224
- const result = await executeFindOneAndUpdate(db, cmd);
225
- return result !== null;
226
- }
227
-
228
- /**
229
- * Appends a ledger entry for the given space. Ledger entries co-exist
230
- * with marker docs in the same collection; marker docs use `_id: <space>`
231
- * (string), ledger entries use `type: 'ledger'` plus a driver-generated
232
- * ObjectId. Reads partition the two by filter shape.
233
- *
234
- * The same `edgeId` may legitimately recur across different spaces (e.g.
235
- * a synthetic ∅→head edge on first apply), so the ledger key is
236
- * `(space, edgeId)` — the doc carries `space` for partitioned reads.
237
- */
238
- export async function writeLedgerEntry(
239
- db: Db,
240
- space: string,
241
- entry: { readonly edgeId: string; readonly from: string; readonly to: string },
242
- ): Promise<void> {
243
- const cmd = new RawInsertOneCommand(COLLECTION, {
244
- type: 'ledger',
245
- space,
246
- edgeId: entry.edgeId,
247
- from: entry.from,
248
- to: entry.to,
249
- appliedAt: new Date(),
250
- });
251
- await executeInsertOne(db, cmd);
252
- }
@@ -1,14 +1,24 @@
1
- import type { ContractMarkerRecord } from '@prisma-next/contract/types';
1
+ import type { ContractMarkerRecord, LedgerEntryRecord } from '@prisma-next/contract/types';
2
+ import { withMarkerReadErrorHandling } from '@prisma-next/errors/execution';
2
3
  import type { MongoControlAdapter } from '@prisma-next/family-mongo/control-adapter';
3
4
  import type { ControlDriverInstance } from '@prisma-next/framework-components/control';
5
+ import { ledgerOriginFromStored } from '@prisma-next/migration-tools/ledger-origin';
6
+ import {
7
+ RawAggregateCommand,
8
+ RawFindOneAndUpdateCommand,
9
+ RawInsertOneCommand,
10
+ } from '@prisma-next/mongo-query-ast/execution';
4
11
  import type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';
12
+ import type { Document } from 'mongodb';
5
13
  import { introspectSchema } from './introspect-schema';
6
14
  import {
7
- initMarker,
8
- readAllMarkers,
9
- readMarker,
10
- updateMarker,
11
- writeLedgerEntry,
15
+ COLLECTION,
16
+ executeAggregate,
17
+ executeFindOneAndUpdate,
18
+ executeInsertOne,
19
+ MONGO_LEDGER_COLLECTION,
20
+ MONGO_MARKER_COLLECTION,
21
+ parseMongoMarkerDocSafely,
12
22
  } from './marker-ledger';
13
23
  import { extractDb } from './runner-deps';
14
24
 
@@ -16,7 +26,7 @@ import { extractDb } from './runner-deps';
16
26
  * Mongo control adapter for control-plane operations like introspection
17
27
  * and marker-ledger CAS. Implements the family-level `MongoControlAdapter`
18
28
  * SPI by extracting the underlying `Db` from the framework-shaped driver
19
- * and forwarding to the wire-level helpers in this package.
29
+ * per call.
20
30
  */
21
31
  export class MongoControlAdapterImpl implements MongoControlAdapter<'mongo'> {
22
32
  readonly familyId = 'mongo' as const;
@@ -26,13 +36,50 @@ export class MongoControlAdapterImpl implements MongoControlAdapter<'mongo'> {
26
36
  driver: ControlDriverInstance<'mongo', 'mongo'>,
27
37
  space: string,
28
38
  ): Promise<ContractMarkerRecord | null> {
29
- return readMarker(extractDb(driver), space);
39
+ const db = extractDb(driver);
40
+ const markerContext = { space, markerLocation: MONGO_MARKER_COLLECTION };
41
+ const docs = await withMarkerReadErrorHandling(
42
+ () =>
43
+ executeAggregate(
44
+ db,
45
+ new RawAggregateCommand(COLLECTION, [{ $match: { _id: space, space } }, { $limit: 1 }]),
46
+ ),
47
+ markerContext,
48
+ );
49
+ const doc = docs[0];
50
+ if (!doc) return null;
51
+ return parseMongoMarkerDocSafely(doc, space);
30
52
  }
31
53
 
32
54
  async readAllMarkers(
33
55
  driver: ControlDriverInstance<'mongo', 'mongo'>,
34
56
  ): Promise<ReadonlyMap<string, ContractMarkerRecord>> {
35
- return readAllMarkers(extractDb(driver));
57
+ const db = extractDb(driver);
58
+ const markerContext = { space: 'app', markerLocation: MONGO_MARKER_COLLECTION };
59
+ const docs = await withMarkerReadErrorHandling(
60
+ () =>
61
+ executeAggregate(
62
+ db,
63
+ new RawAggregateCommand(COLLECTION, [
64
+ {
65
+ $match: {
66
+ _id: { $type: 'string' },
67
+ space: { $type: 'string' },
68
+ $expr: { $eq: ['$_id', '$space'] },
69
+ },
70
+ },
71
+ ]),
72
+ ),
73
+ markerContext,
74
+ );
75
+ const out = new Map<string, ContractMarkerRecord>();
76
+ for (const doc of docs) {
77
+ const space = doc['space'];
78
+ /* v8 ignore next -- @preserve type-narrowing guard: the $match stage above filters on `space: { $type: 'string' }`, so this branch is unreachable at runtime. The check exists so the `out.set(space, ...)` call below can accept `string`. */
79
+ if (typeof space !== 'string') continue;
80
+ out.set(space, parseMongoMarkerDocSafely(doc, space));
81
+ }
82
+ return out;
36
83
  }
37
84
 
38
85
  async initMarker(
@@ -44,7 +91,20 @@ export class MongoControlAdapterImpl implements MongoControlAdapter<'mongo'> {
44
91
  readonly invariants?: readonly string[];
45
92
  },
46
93
  ): Promise<void> {
47
- await initMarker(extractDb(driver), space, destination);
94
+ const db = extractDb(driver);
95
+ const cmd = new RawInsertOneCommand(COLLECTION, {
96
+ _id: space,
97
+ space,
98
+ storageHash: destination.storageHash,
99
+ profileHash: destination.profileHash,
100
+ contractJson: null,
101
+ canonicalVersion: null,
102
+ updatedAt: new Date(),
103
+ appTag: null,
104
+ meta: {},
105
+ invariants: destination.invariants ?? [],
106
+ });
107
+ await executeInsertOne(db, cmd);
48
108
  }
49
109
 
50
110
  async updateMarker(
@@ -57,15 +117,122 @@ export class MongoControlAdapterImpl implements MongoControlAdapter<'mongo'> {
57
117
  readonly invariants?: readonly string[];
58
118
  },
59
119
  ): Promise<boolean> {
60
- return updateMarker(extractDb(driver), space, expectedFrom, destination);
120
+ const db = extractDb(driver);
121
+ const setBase: Record<string, unknown> = {
122
+ storageHash: destination.storageHash,
123
+ profileHash: destination.profileHash,
124
+ updatedAt: new Date(),
125
+ };
126
+ const update: Document | Document[] =
127
+ destination.invariants === undefined
128
+ ? { $set: setBase }
129
+ : [
130
+ {
131
+ $set: {
132
+ ...setBase,
133
+ invariants: {
134
+ $sortArray: {
135
+ input: {
136
+ $setUnion: [{ $ifNull: ['$invariants', []] }, destination.invariants],
137
+ },
138
+ sortBy: 1,
139
+ },
140
+ },
141
+ },
142
+ },
143
+ ];
144
+ const cmd = new RawFindOneAndUpdateCommand(
145
+ COLLECTION,
146
+ { _id: space, space, storageHash: expectedFrom },
147
+ update,
148
+ false,
149
+ );
150
+ const result = await executeFindOneAndUpdate(db, cmd);
151
+ return result !== null;
61
152
  }
62
153
 
63
154
  async writeLedgerEntry(
64
155
  driver: ControlDriverInstance<'mongo', 'mongo'>,
65
156
  space: string,
66
- entry: { readonly edgeId: string; readonly from: string; readonly to: string },
157
+ entry: {
158
+ readonly edgeId: string;
159
+ readonly from: string;
160
+ readonly to: string;
161
+ readonly migrationName: string;
162
+ readonly migrationHash: string;
163
+ readonly operations: readonly unknown[];
164
+ },
67
165
  ): Promise<void> {
68
- await writeLedgerEntry(extractDb(driver), space, entry);
166
+ const db = extractDb(driver);
167
+ const cmd = new RawInsertOneCommand(COLLECTION, {
168
+ type: 'ledger',
169
+ space,
170
+ edgeId: entry.edgeId,
171
+ from: entry.from,
172
+ to: entry.to,
173
+ migrationName: entry.migrationName,
174
+ migrationHash: entry.migrationHash,
175
+ operations: entry.operations,
176
+ appliedAt: new Date(),
177
+ });
178
+ await executeInsertOne(db, cmd);
179
+ }
180
+
181
+ async readLedger(
182
+ driver: ControlDriverInstance<'mongo', 'mongo'>,
183
+ space?: string,
184
+ ): Promise<readonly LedgerEntryRecord[]> {
185
+ const db = extractDb(driver);
186
+ const ledgerContext = { space: space ?? '*', markerLocation: MONGO_LEDGER_COLLECTION };
187
+ const matchStage: Record<string, unknown> = { type: 'ledger' };
188
+ if (space !== undefined) {
189
+ matchStage['space'] = space;
190
+ }
191
+ const docs = await withMarkerReadErrorHandling(
192
+ () =>
193
+ executeAggregate(
194
+ db,
195
+ new RawAggregateCommand(COLLECTION, [{ $match: matchStage }, { $sort: { _id: 1 } }]),
196
+ ),
197
+ ledgerContext,
198
+ );
199
+
200
+ const entries: LedgerEntryRecord[] = [];
201
+ for (const doc of docs) {
202
+ const migrationName = doc['migrationName'];
203
+ const migrationHash = doc['migrationHash'];
204
+ const from = doc['from'];
205
+ const to = doc['to'];
206
+ const docSpace = doc['space'];
207
+ if (typeof migrationName !== 'string' || typeof migrationHash !== 'string') {
208
+ continue;
209
+ }
210
+ if (typeof from !== 'string' || typeof to !== 'string') {
211
+ continue;
212
+ }
213
+ if (typeof docSpace !== 'string') {
214
+ continue;
215
+ }
216
+ const appliedAt = doc['appliedAt'];
217
+ const appliedAtDate =
218
+ appliedAt instanceof Date
219
+ ? appliedAt
220
+ : appliedAt !== undefined
221
+ ? new Date(String(appliedAt))
222
+ : new Date();
223
+ const operations = doc['operations'];
224
+ const opList = Array.isArray(operations) ? operations : [];
225
+ entries.push({
226
+ space: docSpace,
227
+ migrationName,
228
+ migrationHash,
229
+ from: ledgerOriginFromStored(from),
230
+ to,
231
+ appliedAt: appliedAtDate,
232
+ operationCount: opList.length,
233
+ });
234
+ }
235
+ return entries;
69
236
  }
70
237
 
71
238
  async introspectSchema(driver: ControlDriverInstance<'mongo', 'mongo'>): Promise<MongoSchemaIR> {
@@ -57,6 +57,9 @@ export interface MarkerOperations {
57
57
  readonly edgeId: string;
58
58
  readonly from: string;
59
59
  readonly to: string;
60
+ readonly migrationName: string;
61
+ readonly migrationHash: string;
62
+ readonly operations: readonly unknown[];
60
63
  },
61
64
  ): Promise<void>;
62
65
  }
@@ -82,11 +85,11 @@ export interface MongoRunnerDependencies {
82
85
  export function createMongoRunnerDeps(
83
86
  controlDriver: ControlDriverInstance<'mongo', 'mongo'>,
84
87
  driver: MongoDriver,
85
- // Vestigial after the M2.5 family→adapter SPI dispatch refactor: the runner
86
- // dependencies now route every wire-level call through `controlAdapter`, so
87
- // the `family` instance is no longer consulted. Kept on the signature to
88
- // avoid rippling through ~14 call sites mid-orchestration; a follow-up that
89
- // already touches this factory should drop the parameter outright.
88
+ // Vestigial after the family→adapter SPI refactor: the runner dependencies
89
+ // now route every wire-level call through `controlAdapter`, so the `family`
90
+ // instance is no longer consulted. Kept on the signature to avoid rippling
91
+ // through ~14 call sites; a follow-up that already touches this factory
92
+ // should drop the parameter outright.
90
93
  _family: ControlFamilyInstance<'mongo', MongoSchemaIR>,
91
94
  controlAdapter: MongoControlAdapter<'mongo'> = new MongoControlAdapterImpl(),
92
95
  ): MongoRunnerDependencies {
@@ -1,14 +1,10 @@
1
+ import type { ContractMarkerRecord, LedgerEntryRecord } from '@prisma-next/contract/types';
1
2
  import type { MongoControlAdapterDescriptor } from '@prisma-next/family-mongo/control-adapter';
3
+ import type { ControlDriverInstance } from '@prisma-next/framework-components/control';
4
+ import type { Db } from 'mongodb';
2
5
 
3
6
  export { MongoCommandExecutor, MongoInspectionExecutor } from '../core/command-executor';
4
7
  export { introspectSchema } from '../core/introspect-schema';
5
- export {
6
- initMarker,
7
- readAllMarkers,
8
- readMarker,
9
- updateMarker,
10
- writeLedgerEntry,
11
- } from '../core/marker-ledger';
12
8
  export { MongoControlAdapterImpl } from '../core/mongo-control-adapter';
13
9
  export {
14
10
  createMongoControlDriver,
@@ -25,6 +21,75 @@ export { createMongoAdapter } from '../mongo-adapter';
25
21
  import { mongoCodecDescriptors } from '../core/codecs';
26
22
  import { MongoControlAdapterImpl } from '../core/mongo-control-adapter';
27
23
 
24
+ const defaultControlAdapter = new MongoControlAdapterImpl();
25
+
26
+ function controlDriverFromDb(db: Db): ControlDriverInstance<'mongo', 'mongo'> & { db: Db } {
27
+ return {
28
+ familyId: 'mongo',
29
+ targetId: 'mongo',
30
+ db,
31
+ query: () => Promise.reject(new Error('MongoDB control driver does not support SQL queries')),
32
+ close: async () => {},
33
+ };
34
+ }
35
+
36
+ export async function readMarker(db: Db, space: string): Promise<ContractMarkerRecord | null> {
37
+ return defaultControlAdapter.readMarker(controlDriverFromDb(db), space);
38
+ }
39
+
40
+ export async function readAllMarkers(db: Db): Promise<ReadonlyMap<string, ContractMarkerRecord>> {
41
+ return defaultControlAdapter.readAllMarkers(controlDriverFromDb(db));
42
+ }
43
+
44
+ export async function initMarker(
45
+ db: Db,
46
+ space: string,
47
+ destination: {
48
+ readonly storageHash: string;
49
+ readonly profileHash: string;
50
+ readonly invariants?: readonly string[];
51
+ },
52
+ ): Promise<void> {
53
+ await defaultControlAdapter.initMarker(controlDriverFromDb(db), space, destination);
54
+ }
55
+
56
+ export async function updateMarker(
57
+ db: Db,
58
+ space: string,
59
+ expectedFrom: string,
60
+ destination: {
61
+ readonly storageHash: string;
62
+ readonly profileHash: string;
63
+ readonly invariants?: readonly string[];
64
+ },
65
+ ): Promise<boolean> {
66
+ return defaultControlAdapter.updateMarker(
67
+ controlDriverFromDb(db),
68
+ space,
69
+ expectedFrom,
70
+ destination,
71
+ );
72
+ }
73
+
74
+ export async function readLedger(db: Db, space?: string): Promise<readonly LedgerEntryRecord[]> {
75
+ return defaultControlAdapter.readLedger(controlDriverFromDb(db), space);
76
+ }
77
+
78
+ export async function writeLedgerEntry(
79
+ db: Db,
80
+ space: string,
81
+ entry: {
82
+ readonly edgeId: string;
83
+ readonly from: string;
84
+ readonly to: string;
85
+ readonly migrationName: string;
86
+ readonly migrationHash: string;
87
+ readonly operations: readonly unknown[];
88
+ },
89
+ ): Promise<void> {
90
+ await defaultControlAdapter.writeLedgerEntry(controlDriverFromDb(db), space, entry);
91
+ }
92
+
28
93
  export const mongoAdapterDescriptor: MongoControlAdapterDescriptor<'mongo'> = {
29
94
  kind: 'adapter',
30
95
  id: 'mongo',