@prisma-next/target-mongo 0.5.0-dev.9 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codec-types.d.mts.map +1 -1
- package/dist/codec-types.mjs +1 -1
- package/dist/control.d.mts +24 -7
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +150 -86
- package/dist/control.mjs.map +1 -1
- package/dist/descriptor-meta-DdXFJeK1.mjs +12 -0
- package/dist/descriptor-meta-DdXFJeK1.mjs.map +1 -0
- package/dist/{migration-factories-Dbk5afMU.mjs → migration-factories-ZBsWqXt-.mjs} +4 -3
- package/dist/migration-factories-ZBsWqXt-.mjs.map +1 -0
- package/dist/migration.d.mts +7 -1
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +2 -3
- package/dist/{op-factory-call-CVgzmLJh.d.mts → op-factory-call-9z5D19cP.d.mts} +1 -2
- package/dist/op-factory-call-9z5D19cP.d.mts.map +1 -0
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +3 -15
- package/dist/pack.mjs.map +1 -1
- package/dist/runtime.d.mts +15 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +21 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/schema-verify.d.mts.map +1 -1
- package/dist/schema-verify.mjs +2 -3
- package/dist/{verify-mongo-schema-P0TRBJNs.mjs → verify-mongo-schema-DlPXaotB.mjs} +2 -6
- package/dist/verify-mongo-schema-DlPXaotB.mjs.map +1 -0
- package/package.json +20 -17
- package/src/core/marker-ledger.ts +90 -20
- package/src/core/migration-factories.ts +8 -0
- package/src/core/mongo-ops-serializer.ts +49 -9
- package/src/core/mongo-planner.ts +25 -4
- package/src/core/mongo-runner.ts +80 -57
- package/src/core/planner-produced-migration.ts +0 -1
- package/src/core/render-typescript.ts +3 -7
- package/src/exports/runtime.ts +32 -0
- package/dist/migration-factories-Dbk5afMU.mjs.map +0 -1
- package/dist/op-factory-call-CVgzmLJh.d.mts.map +0 -1
- package/dist/verify-mongo-schema-P0TRBJNs.mjs.map +0 -1
|
@@ -197,13 +197,9 @@ const PlanMetaJson = type({
|
|
|
197
197
|
target: 'string',
|
|
198
198
|
storageHash: 'string',
|
|
199
199
|
lane: 'string',
|
|
200
|
-
paramDescriptors: 'unknown[]',
|
|
201
200
|
'targetFamily?': 'string',
|
|
202
201
|
'profileHash?': 'string',
|
|
203
202
|
'annotations?': 'Record<string, unknown>',
|
|
204
|
-
'refs?': 'Record<string, unknown>',
|
|
205
|
-
'projection?': 'Record<string, string> | string[]',
|
|
206
|
-
'projectionTypes?': 'Record<string, string>',
|
|
207
203
|
});
|
|
208
204
|
|
|
209
205
|
const QueryPlanJson = type({
|
|
@@ -256,7 +252,7 @@ const DataTransformOperationJson = type({
|
|
|
256
252
|
|
|
257
253
|
function validate<T>(schema: { assert: (data: unknown) => T }, data: unknown, context: string): T {
|
|
258
254
|
try {
|
|
259
|
-
return schema.assert(data);
|
|
255
|
+
return schema.assert(stripUndefinedDeep(data));
|
|
260
256
|
} catch (error) {
|
|
261
257
|
/* v8 ignore start -- assertion libraries always throw Error instances */
|
|
262
258
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -265,6 +261,54 @@ function validate<T>(schema: { assert: (data: unknown) => T }, data: unknown, co
|
|
|
265
261
|
}
|
|
266
262
|
}
|
|
267
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Strip `undefined`-valued properties before they reach arktype's optional-key
|
|
266
|
+
* assertions.
|
|
267
|
+
*
|
|
268
|
+
* Op IRs (e.g. `CreateCollectionCommand`) assign every optional field on
|
|
269
|
+
* every instance — fields the caller did not provide land as
|
|
270
|
+
* `undefined`-valued properties. arktype treats `{ foo?: 'boolean' }` as
|
|
271
|
+
* "key may be absent, but if present must be boolean", so the bare instance
|
|
272
|
+
* fails validation when it crosses the deserialize boundary in-process
|
|
273
|
+
* (no JSON round-trip happens between planner → runner). This helper
|
|
274
|
+
* recovers the JSON-round-tripped shape (undefined keys absent) without
|
|
275
|
+
* forcing every caller to round-trip.
|
|
276
|
+
*
|
|
277
|
+
* Returns the original value reference whenever no change is needed.
|
|
278
|
+
* That preserves prototype-bound payload values such as BSON wrappers
|
|
279
|
+
* (`ObjectId`, `Decimal128`, `Binary`, …) which embed no `undefined`
|
|
280
|
+
* own-enumerable properties and therefore never trigger a rebuild.
|
|
281
|
+
* Top-level op IRs (class instances with `undefined` optional fields)
|
|
282
|
+
* still get flattened to plain records as required by arktype.
|
|
283
|
+
*/
|
|
284
|
+
function stripUndefinedDeep(value: unknown): unknown {
|
|
285
|
+
if (Array.isArray(value)) {
|
|
286
|
+
let changed = false;
|
|
287
|
+
const next = value.map((item) => {
|
|
288
|
+
const stripped = stripUndefinedDeep(item);
|
|
289
|
+
if (stripped !== item) changed = true;
|
|
290
|
+
return stripped;
|
|
291
|
+
});
|
|
292
|
+
return changed ? next : value;
|
|
293
|
+
}
|
|
294
|
+
if (value === null || typeof value !== 'object') {
|
|
295
|
+
return value;
|
|
296
|
+
}
|
|
297
|
+
const entries = Object.entries(value as Record<string, unknown>);
|
|
298
|
+
const out: Record<string, unknown> = {};
|
|
299
|
+
let changed = false;
|
|
300
|
+
for (const [key, val] of entries) {
|
|
301
|
+
if (val === undefined) {
|
|
302
|
+
changed = true;
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const stripped = stripUndefinedDeep(val);
|
|
306
|
+
if (stripped !== val) changed = true;
|
|
307
|
+
out[key] = stripped;
|
|
308
|
+
}
|
|
309
|
+
return changed ? out : value;
|
|
310
|
+
}
|
|
311
|
+
|
|
268
312
|
function deserializeFilterExpr(json: unknown): MongoFilterExpr {
|
|
269
313
|
const record = json as Record<string, unknown>;
|
|
270
314
|
const kind = record['kind'] as string;
|
|
@@ -427,13 +471,9 @@ export function deserializeMongoQueryPlan(json: unknown): MongoQueryPlan {
|
|
|
427
471
|
target: m.target,
|
|
428
472
|
storageHash: m.storageHash,
|
|
429
473
|
lane: m.lane,
|
|
430
|
-
paramDescriptors: m.paramDescriptors as PlanMeta['paramDescriptors'],
|
|
431
474
|
...ifDefined('targetFamily', m.targetFamily),
|
|
432
475
|
...ifDefined('profileHash', m.profileHash),
|
|
433
476
|
...ifDefined('annotations', m.annotations),
|
|
434
|
-
...ifDefined('refs', m.refs),
|
|
435
|
-
...ifDefined('projection', m.projection),
|
|
436
|
-
...ifDefined('projectionTypes', m.projectionTypes),
|
|
437
477
|
};
|
|
438
478
|
return { collection: data.collection, command, meta };
|
|
439
479
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
1
2
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
2
3
|
import type {
|
|
3
4
|
MigrationOperationClass,
|
|
@@ -131,8 +132,23 @@ export class MongoMigrationPlanner implements MigrationPlanner<'mongo', 'mongo'>
|
|
|
131
132
|
const destColl = destinationIR.collection(collName);
|
|
132
133
|
|
|
133
134
|
if (!originColl) {
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
// Provision contract-declared collections that are absent from
|
|
136
|
+
// the live database. MongoDB lazily materialises a collection
|
|
137
|
+
// on first write, so subsequent `createIndex` calls in the same
|
|
138
|
+
// plan would create the collection for us implicitly — but the
|
|
139
|
+
// schema verifier treats an unmaterialised contract collection
|
|
140
|
+
// as a `missing_table` issue, so a plan that lacks both options
|
|
141
|
+
// and indexes (e.g. a plain `users` collection from the init
|
|
142
|
+
// scaffold) ends up provisioning nothing and failing verify.
|
|
143
|
+
// The planner therefore emits an explicit createCollection for
|
|
144
|
+
// any contract collection that has options/validator OR no
|
|
145
|
+
// indexes to ride along on. Collections that have indexes
|
|
146
|
+
// continue to rely on createIndex for materialisation, keeping
|
|
147
|
+
// existing plans byte-stable.
|
|
148
|
+
if (destColl && (collectionHasOptions(destColl) || destColl.indexes.length === 0)) {
|
|
149
|
+
const opts = collectionHasOptions(destColl)
|
|
150
|
+
? schemaCollectionToCreateCollectionOptions(destColl)
|
|
151
|
+
: undefined;
|
|
136
152
|
collCreates.push(new CreateCollectionCall(collName, opts));
|
|
137
153
|
}
|
|
138
154
|
} else if (!destColl) {
|
|
@@ -225,7 +241,12 @@ export class MongoMigrationPlanner implements MigrationPlanner<'mongo', 'mongo'>
|
|
|
225
241
|
readonly contract: unknown;
|
|
226
242
|
readonly schema: unknown;
|
|
227
243
|
readonly policy: MigrationOperationPolicy;
|
|
228
|
-
|
|
244
|
+
/**
|
|
245
|
+
* The "from" contract (state the planner assumes the database starts at),
|
|
246
|
+
* or `null` for reconciliation flows. Used to populate `describe().from`
|
|
247
|
+
* on the produced plan as `fromContract?.storage.storageHash ?? null`.
|
|
248
|
+
*/
|
|
249
|
+
readonly fromContract: Contract | null;
|
|
229
250
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', 'mongo'>>;
|
|
230
251
|
}): MigrationPlannerResult {
|
|
231
252
|
const contract = options.contract as MongoContract;
|
|
@@ -234,7 +255,7 @@ export class MongoMigrationPlanner implements MigrationPlanner<'mongo', 'mongo'>
|
|
|
234
255
|
return {
|
|
235
256
|
kind: 'success',
|
|
236
257
|
plan: new PlannerProducedMongoMigration(result.calls, {
|
|
237
|
-
from: options.
|
|
258
|
+
from: options.fromContract?.storage.storageHash ?? null,
|
|
238
259
|
to: contract.storage.storageHash,
|
|
239
260
|
}),
|
|
240
261
|
};
|
package/src/core/mongo-runner.ts
CHANGED
|
@@ -34,10 +34,15 @@ export interface MarkerOperations {
|
|
|
34
34
|
initMarker(destination: {
|
|
35
35
|
readonly storageHash: string;
|
|
36
36
|
readonly profileHash: string;
|
|
37
|
+
readonly invariants?: readonly string[];
|
|
37
38
|
}): Promise<void>;
|
|
38
39
|
updateMarker(
|
|
39
40
|
expectedFrom: string,
|
|
40
|
-
destination: {
|
|
41
|
+
destination: {
|
|
42
|
+
readonly storageHash: string;
|
|
43
|
+
readonly profileHash: string;
|
|
44
|
+
readonly invariants?: readonly string[];
|
|
45
|
+
},
|
|
41
46
|
): Promise<boolean>;
|
|
42
47
|
writeLedgerEntry(entry: {
|
|
43
48
|
readonly edgeId: string;
|
|
@@ -177,60 +182,82 @@ export class MongoMigrationRunner {
|
|
|
177
182
|
const destination = options.plan.destination;
|
|
178
183
|
const profileHash = options.destinationContract.profileHash ?? destination.storageHash;
|
|
179
184
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
185
|
+
const incomingInvariants = options.plan.providedInvariants ?? [];
|
|
186
|
+
const existingInvariantSet = new Set(existingMarker?.invariants ?? []);
|
|
187
|
+
const incomingIsSubsetOfExisting = incomingInvariants.every((id) =>
|
|
188
|
+
existingInvariantSet.has(id),
|
|
189
|
+
);
|
|
190
|
+
const markerAlreadyAtDestination =
|
|
191
|
+
existingMarker !== null &&
|
|
192
|
+
existingMarker.storageHash === destination.storageHash &&
|
|
193
|
+
existingMarker.profileHash === profileHash;
|
|
194
|
+
|
|
195
|
+
// Skip marker/ledger writes (and schema verification) only when the apply
|
|
196
|
+
// is a true no-op: no operations executed, marker already at destination,
|
|
197
|
+
// and every incoming invariant is already in the stored set.
|
|
198
|
+
//
|
|
199
|
+
// Divergence from the SQL runners (postgres/sqlite): those runners gate
|
|
200
|
+
// the no-op skip on `isSelfEdge` (origin === destination) only, so a
|
|
201
|
+
// non-self-edge `db update` that introspects-as-no-op still writes a
|
|
202
|
+
// ledger entry. Mongo skips even those because the runner has no
|
|
203
|
+
// structural distinction between self-edge and re-apply — invariant-
|
|
204
|
+
// aware routing here does not yet differentiate between the two
|
|
205
|
+
// ledger semantics. If the SQL audit-trail behavior should hold for
|
|
206
|
+
// Mongo too, gate this `isNoOp` on a self-edge check (or, conversely,
|
|
207
|
+
// align the SQL runners to skip non-self-edge no-ops uniformly).
|
|
208
|
+
const isNoOp =
|
|
209
|
+
operationsExecuted === 0 && markerAlreadyAtDestination && incomingIsSubsetOfExisting;
|
|
210
|
+
|
|
211
|
+
if (!isNoOp) {
|
|
212
|
+
const liveSchema = await this.deps.introspectSchema();
|
|
213
|
+
const verifyResult = verifyMongoSchema({
|
|
214
|
+
contract: options.destinationContract,
|
|
215
|
+
schema: liveSchema,
|
|
216
|
+
strict: options.strictVerification ?? true,
|
|
217
|
+
frameworkComponents: options.frameworkComponents,
|
|
218
|
+
...(options.context ? { context: options.context } : {}),
|
|
200
219
|
});
|
|
201
|
-
|
|
220
|
+
if (!verifyResult.ok) {
|
|
221
|
+
return runnerFailure('SCHEMA_VERIFY_FAILED', verifyResult.summary, {
|
|
222
|
+
why: 'The resulting database schema does not satisfy the destination contract.',
|
|
223
|
+
meta: { issues: verifyResult.schema.issues },
|
|
224
|
+
});
|
|
225
|
+
}
|
|
202
226
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
227
|
+
if (existingMarker) {
|
|
228
|
+
const updated = await markerOps.updateMarker(existingMarker.storageHash, {
|
|
229
|
+
storageHash: destination.storageHash,
|
|
230
|
+
profileHash,
|
|
231
|
+
invariants: incomingInvariants,
|
|
232
|
+
});
|
|
233
|
+
if (!updated) {
|
|
234
|
+
return runnerFailure(
|
|
235
|
+
'MARKER_CAS_FAILURE',
|
|
236
|
+
'Marker was modified by another process during migration execution.',
|
|
237
|
+
{
|
|
238
|
+
meta: {
|
|
239
|
+
expectedStorageHash: existingMarker.storageHash,
|
|
240
|
+
destinationStorageHash: destination.storageHash,
|
|
241
|
+
},
|
|
216
242
|
},
|
|
217
|
-
|
|
218
|
-
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
await markerOps.initMarker({
|
|
247
|
+
storageHash: destination.storageHash,
|
|
248
|
+
profileHash,
|
|
249
|
+
invariants: incomingInvariants,
|
|
250
|
+
});
|
|
219
251
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
252
|
+
|
|
253
|
+
const originHash = existingMarker?.storageHash ?? '';
|
|
254
|
+
await markerOps.writeLedgerEntry({
|
|
255
|
+
edgeId: `${originHash}->${destination.storageHash}`,
|
|
256
|
+
from: originHash,
|
|
257
|
+
to: destination.storageHash,
|
|
224
258
|
});
|
|
225
259
|
}
|
|
226
260
|
|
|
227
|
-
const originHash = existingMarker?.storageHash ?? '';
|
|
228
|
-
await markerOps.writeLedgerEntry({
|
|
229
|
-
edgeId: `${originHash}->${destination.storageHash}`,
|
|
230
|
-
from: originHash,
|
|
231
|
-
to: destination.storageHash,
|
|
232
|
-
});
|
|
233
|
-
|
|
234
261
|
return ok({ operationsPlanned: operations.length, operationsExecuted });
|
|
235
262
|
}
|
|
236
263
|
|
|
@@ -271,7 +298,7 @@ export class MongoMigrationRunner {
|
|
|
271
298
|
}
|
|
272
299
|
|
|
273
300
|
for (const plan of op.run) {
|
|
274
|
-
const wireCommand = await adapter.lower(plan);
|
|
301
|
+
const wireCommand = await adapter.lower(plan, {});
|
|
275
302
|
for await (const _ of driver.execute(wireCommand)) {
|
|
276
303
|
/* consume */
|
|
277
304
|
}
|
|
@@ -319,7 +346,7 @@ export class MongoMigrationRunner {
|
|
|
319
346
|
},
|
|
320
347
|
);
|
|
321
348
|
}
|
|
322
|
-
const wireCommand = await adapter.lower(check.source);
|
|
349
|
+
const wireCommand = await adapter.lower(check.source, {});
|
|
323
350
|
let matchFound = false;
|
|
324
351
|
for await (const row of driver.execute<Record<string, unknown>>(wireCommand)) {
|
|
325
352
|
if (filterEvaluator.evaluate(check.filter, row)) {
|
|
@@ -387,13 +414,9 @@ export class MongoMigrationRunner {
|
|
|
387
414
|
): MigrationRunnerResult | undefined {
|
|
388
415
|
const origin = plan.origin ?? null;
|
|
389
416
|
if (!origin) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
'Database already has a contract marker but the plan has no origin. This would silently overwrite the existing marker.',
|
|
394
|
-
{ meta: { markerStorageHash: marker.storageHash } },
|
|
395
|
-
);
|
|
396
|
-
}
|
|
417
|
+
// No origin assertion on the plan — the caller has done its own
|
|
418
|
+
// correctness check (typically `db update` via live-schema
|
|
419
|
+
// introspection) and does not rely on marker continuity.
|
|
397
420
|
return undefined;
|
|
398
421
|
}
|
|
399
422
|
|
|
@@ -3,9 +3,8 @@ import { type ImportRequirement, jsonToTsSource, renderImports } from '@prisma-n
|
|
|
3
3
|
import type { OpFactoryCall } from './op-factory-call';
|
|
4
4
|
|
|
5
5
|
export interface RenderMigrationMeta {
|
|
6
|
-
readonly from: string;
|
|
6
|
+
readonly from: string | null;
|
|
7
7
|
readonly to: string;
|
|
8
|
-
readonly kind?: string;
|
|
9
8
|
readonly labels?: readonly string[];
|
|
10
9
|
}
|
|
11
10
|
|
|
@@ -36,8 +35,8 @@ const BASE_IMPORTS: readonly ImportRequirement[] = [
|
|
|
36
35
|
* `Migration` (i.e. `MongoMigration`) from `@prisma-next/family-mongo`, and
|
|
37
36
|
* implements the abstract `operations` and `describe` members. `meta` is
|
|
38
37
|
* always rendered — `describe()` is part of the `Migration` contract, so
|
|
39
|
-
* even an empty stub must satisfy it; callers pass
|
|
40
|
-
* migration-new scaffold.
|
|
38
|
+
* even an empty stub must satisfy it; callers pass `from: null` for a
|
|
39
|
+
* baseline `migration-new` scaffold (and a real `to` hash either way).
|
|
41
40
|
*
|
|
42
41
|
* The walk is polymorphic: each call node contributes its own
|
|
43
42
|
* `renderTypeScript()` expression and declares its own
|
|
@@ -89,9 +88,6 @@ function buildDescribeMethod(meta: RenderMigrationMeta): string {
|
|
|
89
88
|
lines.push(' return {');
|
|
90
89
|
lines.push(` from: ${JSON.stringify(meta.from)},`);
|
|
91
90
|
lines.push(` to: ${JSON.stringify(meta.to)},`);
|
|
92
|
-
if (meta.kind) {
|
|
93
|
-
lines.push(` kind: ${JSON.stringify(meta.kind)},`);
|
|
94
|
-
}
|
|
95
91
|
if (meta.labels && meta.labels.length > 0) {
|
|
96
92
|
lines.push(` labels: ${jsonToTsSource(meta.labels)},`);
|
|
97
93
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RuntimeTargetDescriptor,
|
|
3
|
+
RuntimeTargetInstance,
|
|
4
|
+
} from '@prisma-next/framework-components/execution';
|
|
5
|
+
import { type MongoCodecRegistry, newMongoCodecRegistry } from '@prisma-next/mongo-codec';
|
|
6
|
+
import { mongoTargetDescriptorMeta } from '../core/descriptor-meta';
|
|
7
|
+
|
|
8
|
+
export interface MongoRuntimeTargetInstance extends RuntimeTargetInstance<'mongo', 'mongo'> {}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Target-mongo deliberately does NOT import `MongoRuntimeTargetDescriptor` from `@prisma-next/mongo-runtime`. The target package is a control-plane residence and must not pull the Mongo execution-plane package into its dependency closure. The runtime descriptor here is shaped to satisfy the framework's `RuntimeTargetDescriptor` plus the structural `MongoStaticContributions` (`codecs`) that `@prisma-next/mongo-runtime`
|
|
12
|
+
* consumers narrow to at composition time.
|
|
13
|
+
*/
|
|
14
|
+
const mongoRuntimeTargetDescriptor: RuntimeTargetDescriptor<
|
|
15
|
+
'mongo',
|
|
16
|
+
'mongo',
|
|
17
|
+
MongoRuntimeTargetInstance
|
|
18
|
+
> & {
|
|
19
|
+
readonly codecs: () => MongoCodecRegistry;
|
|
20
|
+
} = {
|
|
21
|
+
...mongoTargetDescriptorMeta,
|
|
22
|
+
// The target descriptor itself contributes no codecs — the standard set lives on the adapter descriptor (see `@prisma-next/adapter-mongo/runtime`).
|
|
23
|
+
codecs: () => newMongoCodecRegistry(),
|
|
24
|
+
create(): MongoRuntimeTargetInstance {
|
|
25
|
+
return {
|
|
26
|
+
familyId: 'mongo',
|
|
27
|
+
targetId: 'mongo',
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default mongoRuntimeTargetDescriptor;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"migration-factories-Dbk5afMU.mjs","names":["MATCH_ALL_FILTER: MongoFilterExpr","precheck: readonly MongoDataTransformCheck[]","postcheck: readonly MongoDataTransformCheck[]","postcheckExpect: 'exists' | 'notExists'","run: MongoQueryPlan[]"],"sources":["../src/core/migration-factories.ts"],"sourcesContent":["import type {\n MongoDataTransformCheck,\n MongoDataTransformOperation,\n MongoFilterExpr,\n MongoIndexKey,\n} from '@prisma-next/mongo-query-ast/control';\nimport {\n buildIndexOpId,\n CollModCommand,\n type CollModOptions,\n CreateCollectionCommand,\n type CreateCollectionOptions,\n CreateIndexCommand,\n type CreateIndexOptions,\n DropCollectionCommand,\n DropIndexCommand,\n defaultMongoIndexName,\n keysToKeySpec,\n ListCollectionsCommand,\n ListIndexesCommand,\n MongoAndExpr,\n MongoExistsExpr,\n MongoFieldFilter,\n type MongoMigrationPlanOperation,\n} from '@prisma-next/mongo-query-ast/control';\nimport type { MongoQueryPlan } from '@prisma-next/mongo-query-ast/execution';\nimport type { CollModMeta } from './op-factory-call';\n\ninterface Buildable {\n build(): MongoQueryPlan;\n}\n\nfunction isBuildable(value: unknown): value is Buildable {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'build' in value &&\n typeof (value as { build: unknown }).build === 'function'\n );\n}\n\nfunction resolveQuery(value: MongoQueryPlan | Buildable): MongoQueryPlan {\n return isBuildable(value) ? value.build() : value;\n}\n\n// Every MongoDB document carries `_id`, so `exists('_id')` is equivalent to\n// \"match all\". The filter AST has no identity/always-true expression.\nconst MATCH_ALL_FILTER: MongoFilterExpr = MongoExistsExpr.exists('_id');\n\nexport function dataTransform(\n name: string,\n options: {\n check?: {\n source: () => MongoQueryPlan | Buildable;\n filter?: MongoFilterExpr;\n expect?: 'exists' | 'notExists';\n description?: string;\n };\n run: () => MongoQueryPlan | Buildable;\n },\n): MongoDataTransformOperation {\n let precheck: readonly MongoDataTransformCheck[] = [];\n let postcheck: readonly MongoDataTransformCheck[] = [];\n\n if (options.check) {\n const source = resolveQuery(options.check.source());\n const filter = options.check.filter ?? MATCH_ALL_FILTER;\n const description = options.check.description ?? `Check for data transform: ${name}`;\n const precheckExpect = options.check.expect ?? 'exists';\n const postcheckExpect: 'exists' | 'notExists' =\n precheckExpect === 'exists' ? 'notExists' : 'exists';\n\n precheck = [{ description, source, filter, expect: precheckExpect }];\n postcheck = [{ description, source, filter, expect: postcheckExpect }];\n }\n\n const run: MongoQueryPlan[] = [resolveQuery(options.run())];\n\n return {\n id: `data_transform.${name}`,\n label: `Data transform: ${name}`,\n operationClass: 'data',\n name,\n precheck,\n run,\n postcheck,\n };\n}\n\nfunction formatKeys(keys: ReadonlyArray<MongoIndexKey>): string {\n return keys.map((k) => `${k.field}:${k.direction}`).join(', ');\n}\n\nfunction isTextIndex(keys: ReadonlyArray<MongoIndexKey>): boolean {\n return keys.some((k) => k.direction === 'text');\n}\n\nfunction keyFilter(keys: ReadonlyArray<MongoIndexKey>) {\n return isTextIndex(keys)\n ? MongoFieldFilter.eq('key._fts', 'text')\n : MongoFieldFilter.eq('key', keysToKeySpec(keys));\n}\n\nexport function createIndex(\n collection: string,\n keys: ReadonlyArray<MongoIndexKey>,\n options?: CreateIndexOptions,\n): MongoMigrationPlanOperation {\n const name = defaultMongoIndexName(keys);\n const filter = keyFilter(keys);\n const fullFilter = options?.unique\n ? MongoAndExpr.of([filter, MongoFieldFilter.eq('unique', true)])\n : filter;\n\n return {\n id: buildIndexOpId('create', collection, keys),\n label: `Create index on ${collection} (${formatKeys(keys)})`,\n operationClass: 'additive',\n precheck: [\n {\n description: `index does not already exist on ${collection}`,\n source: new ListIndexesCommand(collection),\n filter,\n expect: 'notExists',\n },\n ],\n execute: [\n {\n description: `create index on ${collection}`,\n command: new CreateIndexCommand(collection, keys, {\n ...options,\n unique: options?.unique ?? undefined,\n name,\n }),\n },\n ],\n postcheck: [\n {\n description: `index exists on ${collection}`,\n source: new ListIndexesCommand(collection),\n filter: fullFilter,\n expect: 'exists',\n },\n ],\n };\n}\n\nexport function dropIndex(\n collection: string,\n keys: ReadonlyArray<MongoIndexKey>,\n): MongoMigrationPlanOperation {\n const indexName = defaultMongoIndexName(keys);\n const filter = keyFilter(keys);\n\n return {\n id: buildIndexOpId('drop', collection, keys),\n label: `Drop index on ${collection} (${formatKeys(keys)})`,\n operationClass: 'destructive',\n precheck: [\n {\n description: `index exists on ${collection}`,\n source: new ListIndexesCommand(collection),\n filter,\n expect: 'exists',\n },\n ],\n execute: [\n {\n description: `drop index on ${collection}`,\n command: new DropIndexCommand(collection, indexName),\n },\n ],\n postcheck: [\n {\n description: `index no longer exists on ${collection}`,\n source: new ListIndexesCommand(collection),\n filter,\n expect: 'notExists',\n },\n ],\n };\n}\n\nexport function createCollection(\n collection: string,\n options?: CreateCollectionOptions,\n): MongoMigrationPlanOperation {\n return {\n id: `collection.${collection}.create`,\n label: `Create collection ${collection}`,\n operationClass: 'additive',\n precheck: [\n {\n description: `collection ${collection} does not exist`,\n source: new ListCollectionsCommand(),\n filter: MongoFieldFilter.eq('name', collection),\n expect: 'notExists',\n },\n ],\n execute: [\n {\n description: `create collection ${collection}`,\n command: new CreateCollectionCommand(collection, options),\n },\n ],\n postcheck: [],\n };\n}\n\nexport function dropCollection(collection: string): MongoMigrationPlanOperation {\n return {\n id: `collection.${collection}.drop`,\n label: `Drop collection ${collection}`,\n operationClass: 'destructive',\n precheck: [],\n execute: [\n {\n description: `drop collection ${collection}`,\n command: new DropCollectionCommand(collection),\n },\n ],\n postcheck: [],\n };\n}\n\nexport function setValidation(\n collection: string,\n schema: Record<string, unknown>,\n options?: { validationLevel?: 'strict' | 'moderate'; validationAction?: 'error' | 'warn' },\n): MongoMigrationPlanOperation {\n return {\n id: `collection.${collection}.setValidation`,\n label: `Set validation on ${collection}`,\n operationClass: 'destructive',\n precheck: [],\n execute: [\n {\n description: `set validation on ${collection}`,\n command: new CollModCommand(collection, {\n validator: { $jsonSchema: schema },\n validationLevel: options?.validationLevel,\n validationAction: options?.validationAction,\n }),\n },\n ],\n postcheck: [],\n };\n}\n\nexport function collMod(\n collection: string,\n options: CollModOptions,\n meta?: CollModMeta,\n): MongoMigrationPlanOperation {\n const hasValidator = options.validator != null && Object.keys(options.validator).length > 0;\n\n return {\n id: meta?.id ?? `collection.${collection}.collMod`,\n label: meta?.label ?? `Modify collection ${collection}`,\n operationClass: meta?.operationClass ?? 'destructive',\n precheck:\n options.validator != null\n ? [\n {\n description: `collection ${collection} exists`,\n source: new ListCollectionsCommand(),\n filter: MongoFieldFilter.eq('name', collection),\n expect: 'exists' as const,\n },\n ]\n : [],\n execute: [\n {\n description: `modify ${collection}`,\n command: new CollModCommand(collection, options),\n },\n ],\n postcheck: hasValidator\n ? [\n {\n description: `validator applied on ${collection}`,\n source: new ListCollectionsCommand(),\n filter: MongoAndExpr.of([\n MongoFieldFilter.eq('name', collection),\n ...(options.validationLevel\n ? [MongoFieldFilter.eq('options.validationLevel', options.validationLevel)]\n : []),\n ...(options.validationAction\n ? [MongoFieldFilter.eq('options.validationAction', options.validationAction)]\n : []),\n ]),\n expect: 'exists' as const,\n },\n ]\n : [],\n };\n}\n\nexport function validatedCollection(\n name: string,\n schema: Record<string, unknown>,\n indexes: ReadonlyArray<{ keys: MongoIndexKey[]; unique?: boolean }>,\n): MongoMigrationPlanOperation[] {\n return [\n createCollection(name, {\n validator: { $jsonSchema: schema },\n validationLevel: 'strict',\n validationAction: 'error',\n }),\n ...indexes.map((idx) => createIndex(name, idx.keys, { unique: idx.unique })),\n ];\n}\n"],"mappings":";;;AAgCA,SAAS,YAAY,OAAoC;AACvD,QACE,OAAO,UAAU,YACjB,UAAU,QACV,WAAW,SACX,OAAQ,MAA6B,UAAU;;AAInD,SAAS,aAAa,OAAmD;AACvE,QAAO,YAAY,MAAM,GAAG,MAAM,OAAO,GAAG;;AAK9C,MAAMA,mBAAoC,gBAAgB,OAAO,MAAM;AAEvE,SAAgB,cACd,MACA,SAS6B;CAC7B,IAAIC,WAA+C,EAAE;CACrD,IAAIC,YAAgD,EAAE;AAEtD,KAAI,QAAQ,OAAO;EACjB,MAAM,SAAS,aAAa,QAAQ,MAAM,QAAQ,CAAC;EACnD,MAAM,SAAS,QAAQ,MAAM,UAAU;EACvC,MAAM,cAAc,QAAQ,MAAM,eAAe,6BAA6B;EAC9E,MAAM,iBAAiB,QAAQ,MAAM,UAAU;EAC/C,MAAMC,kBACJ,mBAAmB,WAAW,cAAc;AAE9C,aAAW,CAAC;GAAE;GAAa;GAAQ;GAAQ,QAAQ;GAAgB,CAAC;AACpE,cAAY,CAAC;GAAE;GAAa;GAAQ;GAAQ,QAAQ;GAAiB,CAAC;;CAGxE,MAAMC,MAAwB,CAAC,aAAa,QAAQ,KAAK,CAAC,CAAC;AAE3D,QAAO;EACL,IAAI,kBAAkB;EACtB,OAAO,mBAAmB;EAC1B,gBAAgB;EAChB;EACA;EACA;EACA;EACD;;AAGH,SAAS,WAAW,MAA4C;AAC9D,QAAO,KAAK,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,YAAY,CAAC,KAAK,KAAK;;AAGhE,SAAS,YAAY,MAA6C;AAChE,QAAO,KAAK,MAAM,MAAM,EAAE,cAAc,OAAO;;AAGjD,SAAS,UAAU,MAAoC;AACrD,QAAO,YAAY,KAAK,GACpB,iBAAiB,GAAG,YAAY,OAAO,GACvC,iBAAiB,GAAG,OAAO,cAAc,KAAK,CAAC;;AAGrD,SAAgB,YACd,YACA,MACA,SAC6B;CAC7B,MAAM,OAAO,sBAAsB,KAAK;CACxC,MAAM,SAAS,UAAU,KAAK;CAC9B,MAAM,aAAa,SAAS,SACxB,aAAa,GAAG,CAAC,QAAQ,iBAAiB,GAAG,UAAU,KAAK,CAAC,CAAC,GAC9D;AAEJ,QAAO;EACL,IAAI,eAAe,UAAU,YAAY,KAAK;EAC9C,OAAO,mBAAmB,WAAW,IAAI,WAAW,KAAK,CAAC;EAC1D,gBAAgB;EAChB,UAAU,CACR;GACE,aAAa,mCAAmC;GAChD,QAAQ,IAAI,mBAAmB,WAAW;GAC1C;GACA,QAAQ;GACT,CACF;EACD,SAAS,CACP;GACE,aAAa,mBAAmB;GAChC,SAAS,IAAI,mBAAmB,YAAY,MAAM;IAChD,GAAG;IACH,QAAQ,SAAS,UAAU;IAC3B;IACD,CAAC;GACH,CACF;EACD,WAAW,CACT;GACE,aAAa,mBAAmB;GAChC,QAAQ,IAAI,mBAAmB,WAAW;GAC1C,QAAQ;GACR,QAAQ;GACT,CACF;EACF;;AAGH,SAAgB,UACd,YACA,MAC6B;CAC7B,MAAM,YAAY,sBAAsB,KAAK;CAC7C,MAAM,SAAS,UAAU,KAAK;AAE9B,QAAO;EACL,IAAI,eAAe,QAAQ,YAAY,KAAK;EAC5C,OAAO,iBAAiB,WAAW,IAAI,WAAW,KAAK,CAAC;EACxD,gBAAgB;EAChB,UAAU,CACR;GACE,aAAa,mBAAmB;GAChC,QAAQ,IAAI,mBAAmB,WAAW;GAC1C;GACA,QAAQ;GACT,CACF;EACD,SAAS,CACP;GACE,aAAa,iBAAiB;GAC9B,SAAS,IAAI,iBAAiB,YAAY,UAAU;GACrD,CACF;EACD,WAAW,CACT;GACE,aAAa,6BAA6B;GAC1C,QAAQ,IAAI,mBAAmB,WAAW;GAC1C;GACA,QAAQ;GACT,CACF;EACF;;AAGH,SAAgB,iBACd,YACA,SAC6B;AAC7B,QAAO;EACL,IAAI,cAAc,WAAW;EAC7B,OAAO,qBAAqB;EAC5B,gBAAgB;EAChB,UAAU,CACR;GACE,aAAa,cAAc,WAAW;GACtC,QAAQ,IAAI,wBAAwB;GACpC,QAAQ,iBAAiB,GAAG,QAAQ,WAAW;GAC/C,QAAQ;GACT,CACF;EACD,SAAS,CACP;GACE,aAAa,qBAAqB;GAClC,SAAS,IAAI,wBAAwB,YAAY,QAAQ;GAC1D,CACF;EACD,WAAW,EAAE;EACd;;AAGH,SAAgB,eAAe,YAAiD;AAC9E,QAAO;EACL,IAAI,cAAc,WAAW;EAC7B,OAAO,mBAAmB;EAC1B,gBAAgB;EAChB,UAAU,EAAE;EACZ,SAAS,CACP;GACE,aAAa,mBAAmB;GAChC,SAAS,IAAI,sBAAsB,WAAW;GAC/C,CACF;EACD,WAAW,EAAE;EACd;;AAGH,SAAgB,cACd,YACA,QACA,SAC6B;AAC7B,QAAO;EACL,IAAI,cAAc,WAAW;EAC7B,OAAO,qBAAqB;EAC5B,gBAAgB;EAChB,UAAU,EAAE;EACZ,SAAS,CACP;GACE,aAAa,qBAAqB;GAClC,SAAS,IAAI,eAAe,YAAY;IACtC,WAAW,EAAE,aAAa,QAAQ;IAClC,iBAAiB,SAAS;IAC1B,kBAAkB,SAAS;IAC5B,CAAC;GACH,CACF;EACD,WAAW,EAAE;EACd;;AAGH,SAAgB,QACd,YACA,SACA,MAC6B;CAC7B,MAAM,eAAe,QAAQ,aAAa,QAAQ,OAAO,KAAK,QAAQ,UAAU,CAAC,SAAS;AAE1F,QAAO;EACL,IAAI,MAAM,MAAM,cAAc,WAAW;EACzC,OAAO,MAAM,SAAS,qBAAqB;EAC3C,gBAAgB,MAAM,kBAAkB;EACxC,UACE,QAAQ,aAAa,OACjB,CACE;GACE,aAAa,cAAc,WAAW;GACtC,QAAQ,IAAI,wBAAwB;GACpC,QAAQ,iBAAiB,GAAG,QAAQ,WAAW;GAC/C,QAAQ;GACT,CACF,GACD,EAAE;EACR,SAAS,CACP;GACE,aAAa,UAAU;GACvB,SAAS,IAAI,eAAe,YAAY,QAAQ;GACjD,CACF;EACD,WAAW,eACP,CACE;GACE,aAAa,wBAAwB;GACrC,QAAQ,IAAI,wBAAwB;GACpC,QAAQ,aAAa,GAAG;IACtB,iBAAiB,GAAG,QAAQ,WAAW;IACvC,GAAI,QAAQ,kBACR,CAAC,iBAAiB,GAAG,2BAA2B,QAAQ,gBAAgB,CAAC,GACzE,EAAE;IACN,GAAI,QAAQ,mBACR,CAAC,iBAAiB,GAAG,4BAA4B,QAAQ,iBAAiB,CAAC,GAC3E,EAAE;IACP,CAAC;GACF,QAAQ;GACT,CACF,GACD,EAAE;EACP;;AAGH,SAAgB,oBACd,MACA,QACA,SAC+B;AAC/B,QAAO,CACL,iBAAiB,MAAM;EACrB,WAAW,EAAE,aAAa,QAAQ;EAClC,iBAAiB;EACjB,kBAAkB;EACnB,CAAC,EACF,GAAG,QAAQ,KAAK,QAAQ,YAAY,MAAM,IAAI,MAAM,EAAE,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAC7E"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"op-factory-call-CVgzmLJh.d.mts","names":[],"sources":["../src/core/op-factory-call.ts"],"sourcesContent":[],"mappings":";;;;;;;AAwEqC,UA3BpB,WAAA,CA2BoB;EAAiB,SAAA,EAAA,CAAA,EAAA,MAAA;EAgCzC,SAAA,KAAA,CAAA,EAAc,MAAA;EAII,SAAA,cAAA,CAAA,EA5DH,uBA4DG;;uBAvDhB,iBAAA,SAA0B,YAAA,YAAwB,aA0DX,CAAA;EAAd,kBAAA,WAAA,EAAA,MAAA;EAQ9B,kBAAA,cAAA,EAhE0B,uBAgE1B;EAfyB,kBAAA,KAAA,EAAA,MAAA;EAAiB,SAAA,IAAA,CAAA,CAAA,EA/CjC,2BA+CiC;EAwBvC,kBAAA,CAAA,CAAA,EAAA,SArEoB,iBAqEC,EAAA;EAId,UAAA,MAAA,CAAA,CAAA,EAAA,IAAA;;AAWV,cAvEG,eAAA,SAAwB,iBAAA,CAuE3B;EAfgC,SAAA,WAAA,EAAA,aAAA;EAAiB,SAAA,cAAA,EAAA,UAAA;EA0B9C,SAAA,UAAA,EAAmB,MAAA;EAsBnB,SAAA,IAAA,EApGI,aAoGQ,CApGM,aAoGN,CAAA;EAGL,SAAA,OAAA,EAtGA,kBAsGA,GAAA,SAAA;EACH,SAAA,KAAA,EAAA,MAAA;EACU,WAAA,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAnGjB,aAmGiB,CAnGH,aAmGG,CAAA,EAAA,OAAA,CAAA,EAlGb,kBAkGa;EAGgB,IAAA,CAAA,CAAA,EA3FjC,2BA2FiC;EAAuB,gBAAA,CAAA,CAAA,EAAA,MAAA;;AARjC,cAxEpB,aAAA,SAAsB,iBAAA,CAwEF;EAAiB,SAAA,WAAA,EAAA,WAAA;EA6BtC,SAAA,cAAa,EAAA,aAAA;EACrB,SAAA,UAAA,EAAA,MAAA;EACA,SAAA,IAAA,EAnGa,aAmGb,CAnG2B,aAmG3B,CAAA;EACA,SAAA,KAAA,EAAA,MAAA;EACA,WAAA,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAlGoC,aAkGpC,CAlGkD,aAkGlD,CAAA;EACA,IAAA,CAAA,CAAA,EA3FM,2BA2FN;EAAW,gBAAA,CAAA,CAAA,EAAA,MAAA;AAEf;AAcgB,cAlGH,oBAAA,SAA6B,iBAAA,CAmGlC;;;;oBA/FY;;4CAGwB;UAQlC;;;cAWG,kBAAA,SAA2B,iBAAA;;;;;;UAa9B;;;cASG,WAAA,SAAoB,iBAAA;;;oBAGb;iBACH;2BACU;;2CAGgB,uBAAuB;UAUxD;;;KAWE,eAAA,GACR,kBACA,gBACA,uBACA,qBACA;iBAEY,+BAAA,QAAuC,mBAAmB;iBAc1D,yCAAA,OACR,wBACL"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"verify-mongo-schema-P0TRBJNs.mjs","names":["issues: SchemaIssue[]","collectionChildren: SchemaVerificationNode[]","nodes: SchemaVerificationNode[]","MongoSchemaIRCtor","MongoSchemaCollectionCtor","MongoSchemaIndexCtor","projectedKeys: IndexKey[]","MongoSchemaCollectionOptionsCtor","out: Record<string, unknown>"],"sources":["../src/core/contract-to-schema.ts","../src/core/schema-diff.ts","../src/core/schema-verify/canonicalize-introspection.ts","../src/core/schema-verify/verify-mongo-schema.ts"],"sourcesContent":["import type {\n MongoContract,\n MongoStorageCollection,\n MongoStorageCollectionOptions,\n MongoStorageIndex,\n MongoStorageValidator,\n} from '@prisma-next/mongo-contract';\nimport {\n MongoSchemaCollection,\n MongoSchemaCollectionOptions,\n MongoSchemaIndex,\n MongoSchemaIR,\n MongoSchemaValidator,\n} from '@prisma-next/mongo-schema-ir';\n\nfunction convertIndex(index: MongoStorageIndex): MongoSchemaIndex {\n return new MongoSchemaIndex({\n keys: index.keys,\n unique: index.unique,\n sparse: index.sparse,\n expireAfterSeconds: index.expireAfterSeconds,\n partialFilterExpression: index.partialFilterExpression,\n wildcardProjection: index.wildcardProjection,\n collation: index.collation,\n weights: index.weights,\n default_language: index.default_language,\n language_override: index.language_override,\n });\n}\n\nfunction convertValidator(v: MongoStorageValidator): MongoSchemaValidator {\n return new MongoSchemaValidator({\n jsonSchema: v.jsonSchema,\n validationLevel: v.validationLevel,\n validationAction: v.validationAction,\n });\n}\n\nfunction convertOptions(o: MongoStorageCollectionOptions): MongoSchemaCollectionOptions {\n return new MongoSchemaCollectionOptions(o);\n}\n\nfunction convertCollection(name: string, def: MongoStorageCollection): MongoSchemaCollection {\n const indexes = (def.indexes ?? []).map(convertIndex);\n return new MongoSchemaCollection({\n name,\n indexes,\n ...(def.validator != null && { validator: convertValidator(def.validator) }),\n ...(def.options != null && { options: convertOptions(def.options) }),\n });\n}\n\nexport function contractToMongoSchemaIR(contract: MongoContract | null): MongoSchemaIR {\n if (!contract) {\n return new MongoSchemaIR([]);\n }\n\n const collections = Object.entries(contract.storage.collections).map(([name, def]) =>\n convertCollection(name, def),\n );\n\n return new MongoSchemaIR(collections);\n}\n","import type {\n SchemaIssue,\n SchemaVerificationNode,\n} from '@prisma-next/framework-components/control';\nimport type {\n MongoSchemaCollection,\n MongoSchemaIndex,\n MongoSchemaIR,\n} from '@prisma-next/mongo-schema-ir';\nimport { canonicalize, deepEqual } from '@prisma-next/mongo-schema-ir';\n\nexport function diffMongoSchemas(\n live: MongoSchemaIR,\n expected: MongoSchemaIR,\n strict: boolean,\n): {\n root: SchemaVerificationNode;\n issues: SchemaIssue[];\n counts: { pass: number; warn: number; fail: number; totalNodes: number };\n} {\n const issues: SchemaIssue[] = [];\n const collectionChildren: SchemaVerificationNode[] = [];\n let pass = 0;\n let warn = 0;\n let fail = 0;\n\n const allNames = new Set([...live.collectionNames, ...expected.collectionNames]);\n\n for (const name of [...allNames].sort()) {\n const liveColl = live.collection(name);\n const expectedColl = expected.collection(name);\n\n if (!liveColl && expectedColl) {\n issues.push({\n kind: 'missing_table',\n table: name,\n message: `Collection \"${name}\" is missing from the database`,\n });\n collectionChildren.push({\n status: 'fail',\n kind: 'collection',\n name,\n contractPath: `storage.collections.${name}`,\n code: 'MISSING_COLLECTION',\n message: `Collection \"${name}\" is missing`,\n expected: name,\n actual: null,\n children: [],\n });\n fail++;\n continue;\n }\n\n if (liveColl && !expectedColl) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'extra_table',\n table: name,\n message: `Extra collection \"${name}\" exists in the database but not in the contract`,\n });\n collectionChildren.push({\n status,\n kind: 'collection',\n name,\n contractPath: `storage.collections.${name}`,\n code: 'EXTRA_COLLECTION',\n message: `Extra collection \"${name}\" found`,\n expected: null,\n actual: name,\n children: [],\n });\n if (status === 'fail') fail++;\n else warn++;\n continue;\n }\n\n const lc = liveColl as MongoSchemaCollection;\n const ec = expectedColl as MongoSchemaCollection;\n const indexChildren = diffIndexes(name, lc, ec, strict, issues);\n const validatorChildren = diffValidator(name, lc, ec, strict, issues);\n const optionsChildren = diffOptions(name, lc, ec, strict, issues);\n const children = [...indexChildren, ...validatorChildren, ...optionsChildren];\n\n const worstStatus = children.reduce<'pass' | 'warn' | 'fail'>(\n (s, c) => (c.status === 'fail' ? 'fail' : c.status === 'warn' && s !== 'fail' ? 'warn' : s),\n 'pass',\n );\n\n for (const c of children) {\n if (c.status === 'pass') pass++;\n else if (c.status === 'warn') warn++;\n else fail++;\n }\n\n if (children.length === 0) {\n pass++;\n }\n\n collectionChildren.push({\n status: worstStatus,\n kind: 'collection',\n name,\n contractPath: `storage.collections.${name}`,\n code: worstStatus === 'pass' ? 'MATCH' : 'DRIFT',\n message:\n worstStatus === 'pass' ? `Collection \"${name}\" matches` : `Collection \"${name}\" has drift`,\n expected: name,\n actual: name,\n children,\n });\n }\n\n const rootStatus = fail > 0 ? 'fail' : warn > 0 ? 'warn' : 'pass';\n const totalNodes = pass + warn + fail + collectionChildren.length;\n\n const root: SchemaVerificationNode = {\n status: rootStatus,\n kind: 'root',\n name: 'mongo-schema',\n contractPath: 'storage',\n code: rootStatus === 'pass' ? 'MATCH' : 'DRIFT',\n message: rootStatus === 'pass' ? 'Schema matches' : 'Schema has drift',\n expected: null,\n actual: null,\n children: collectionChildren,\n };\n\n return { root, issues, counts: { pass, warn, fail, totalNodes } };\n}\n\nfunction buildIndexLookupKey(index: MongoSchemaIndex): string {\n const keys = index.keys.map((k) => `${k.field}:${k.direction}`).join(',');\n const opts = [\n index.unique ? 'unique' : '',\n index.sparse ? 'sparse' : '',\n index.expireAfterSeconds != null ? `ttl:${index.expireAfterSeconds}` : '',\n index.partialFilterExpression ? `pfe:${canonicalize(index.partialFilterExpression)}` : '',\n index.wildcardProjection ? `wp:${canonicalize(index.wildcardProjection)}` : '',\n index.collation ? `col:${canonicalize(index.collation)}` : '',\n index.weights ? `wt:${canonicalize(index.weights)}` : '',\n index.default_language ? `dl:${index.default_language}` : '',\n index.language_override ? `lo:${index.language_override}` : '',\n ]\n .filter(Boolean)\n .join(';');\n return opts ? `${keys}|${opts}` : keys;\n}\n\nfunction formatIndexName(index: MongoSchemaIndex): string {\n return index.keys.map((k) => `${k.field}:${k.direction}`).join(', ');\n}\n\nfunction diffIndexes(\n collName: string,\n live: MongoSchemaCollection,\n expected: MongoSchemaCollection,\n strict: boolean,\n issues: SchemaIssue[],\n): SchemaVerificationNode[] {\n const nodes: SchemaVerificationNode[] = [];\n const liveLookup = new Map<string, MongoSchemaIndex>();\n for (const idx of live.indexes) liveLookup.set(buildIndexLookupKey(idx), idx);\n\n const expectedLookup = new Map<string, MongoSchemaIndex>();\n for (const idx of expected.indexes) expectedLookup.set(buildIndexLookupKey(idx), idx);\n\n for (const [key, idx] of expectedLookup) {\n if (liveLookup.has(key)) {\n nodes.push({\n status: 'pass',\n kind: 'index',\n name: formatIndexName(idx),\n contractPath: `storage.collections.${collName}.indexes`,\n code: 'MATCH',\n message: `Index ${formatIndexName(idx)} matches`,\n expected: key,\n actual: key,\n children: [],\n });\n } else {\n issues.push({\n kind: 'index_mismatch',\n table: collName,\n indexOrConstraint: formatIndexName(idx),\n message: `Index ${formatIndexName(idx)} missing on collection \"${collName}\"`,\n });\n nodes.push({\n status: 'fail',\n kind: 'index',\n name: formatIndexName(idx),\n contractPath: `storage.collections.${collName}.indexes`,\n code: 'MISSING_INDEX',\n message: `Index ${formatIndexName(idx)} missing`,\n expected: key,\n actual: null,\n children: [],\n });\n }\n }\n\n for (const [key, idx] of liveLookup) {\n if (!expectedLookup.has(key)) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'extra_index',\n table: collName,\n indexOrConstraint: formatIndexName(idx),\n message: `Extra index ${formatIndexName(idx)} on collection \"${collName}\"`,\n });\n nodes.push({\n status,\n kind: 'index',\n name: formatIndexName(idx),\n contractPath: `storage.collections.${collName}.indexes`,\n code: 'EXTRA_INDEX',\n message: `Extra index ${formatIndexName(idx)}`,\n expected: null,\n actual: key,\n children: [],\n });\n }\n }\n\n return nodes;\n}\n\nfunction diffValidator(\n collName: string,\n live: MongoSchemaCollection,\n expected: MongoSchemaCollection,\n strict: boolean,\n issues: SchemaIssue[],\n): SchemaVerificationNode[] {\n if (!live.validator && !expected.validator) return [];\n\n if (expected.validator && !live.validator) {\n issues.push({\n kind: 'type_missing',\n table: collName,\n message: `Validator missing on collection \"${collName}\"`,\n });\n return [\n {\n status: 'fail',\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.collections.${collName}.validator`,\n code: 'MISSING_VALIDATOR',\n message: 'Validator missing',\n expected: canonicalize(expected.validator.jsonSchema),\n actual: null,\n children: [],\n },\n ];\n }\n\n if (!expected.validator && live.validator) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'extra_validator',\n table: collName,\n message: `Extra validator on collection \"${collName}\"`,\n });\n return [\n {\n status,\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.collections.${collName}.validator`,\n code: 'EXTRA_VALIDATOR',\n message: 'Extra validator found',\n expected: null,\n actual: canonicalize(live.validator.jsonSchema),\n children: [],\n },\n ];\n }\n\n const liveVal = live.validator as NonNullable<typeof live.validator>;\n const expectedVal = expected.validator as NonNullable<typeof expected.validator>;\n const liveSchema = canonicalize(liveVal.jsonSchema);\n const expectedSchema = canonicalize(expectedVal.jsonSchema);\n\n if (\n liveSchema !== expectedSchema ||\n liveVal.validationLevel !== expectedVal.validationLevel ||\n liveVal.validationAction !== expectedVal.validationAction\n ) {\n issues.push({\n kind: 'type_mismatch',\n table: collName,\n expected: expectedSchema,\n actual: liveSchema,\n message: `Validator mismatch on collection \"${collName}\"`,\n });\n return [\n {\n status: 'fail',\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.collections.${collName}.validator`,\n code: 'VALIDATOR_MISMATCH',\n message: 'Validator mismatch',\n expected: {\n jsonSchema: expectedVal.jsonSchema,\n validationLevel: expectedVal.validationLevel,\n validationAction: expectedVal.validationAction,\n },\n actual: {\n jsonSchema: liveVal.jsonSchema,\n validationLevel: liveVal.validationLevel,\n validationAction: liveVal.validationAction,\n },\n children: [],\n },\n ];\n }\n\n return [\n {\n status: 'pass',\n kind: 'validator',\n name: 'validator',\n contractPath: `storage.collections.${collName}.validator`,\n code: 'MATCH',\n message: 'Validator matches',\n expected: expectedSchema,\n actual: liveSchema,\n children: [],\n },\n ];\n}\n\nfunction diffOptions(\n collName: string,\n live: MongoSchemaCollection,\n expected: MongoSchemaCollection,\n strict: boolean,\n issues: SchemaIssue[],\n): SchemaVerificationNode[] {\n if (!live.options && !expected.options) return [];\n\n if (!expected.options && live.options) {\n const status = strict ? 'fail' : 'warn';\n issues.push({\n kind: 'type_mismatch',\n table: collName,\n actual: canonicalize(live.options),\n message: `Extra collection options on \"${collName}\"`,\n });\n return [\n {\n status,\n kind: 'options',\n name: 'options',\n contractPath: `storage.collections.${collName}.options`,\n code: 'EXTRA_OPTIONS',\n message: 'Extra collection options found',\n expected: null,\n actual: live.options,\n children: [],\n },\n ];\n }\n\n if (deepEqual(live.options, expected.options)) {\n return [\n {\n status: 'pass',\n kind: 'options',\n name: 'options',\n contractPath: `storage.collections.${collName}.options`,\n code: 'MATCH',\n message: 'Collection options match',\n expected: canonicalize(expected.options),\n actual: canonicalize(live.options),\n children: [],\n },\n ];\n }\n\n issues.push({\n kind: 'type_mismatch',\n table: collName,\n expected: canonicalize(expected.options),\n actual: canonicalize(live.options),\n message: `Collection options mismatch on \"${collName}\"`,\n });\n return [\n {\n status: 'fail',\n kind: 'options',\n name: 'options',\n contractPath: `storage.collections.${collName}.options`,\n code: 'OPTIONS_MISMATCH',\n message: 'Collection options mismatch',\n expected: expected.options,\n actual: live.options,\n children: [],\n },\n ];\n}\n","/**\n * Canonicalizes a live (introspected) `MongoSchemaIR` against the expected\n * (contract-built) IR before diffing. MongoDB applies server-side defaults\n * to several option/index families that are absent from authored contracts,\n * which would otherwise cause `verifyMongoSchema` to report false-positive\n * drift on a fresh `migration apply`.\n *\n * The normalization is contract-aware where it has to be: server defaults\n * are stripped from the live IR for fields the contract did not specify, so\n * a contract that *does* specify a value still gets compared faithfully.\n *\n * Symmetric defaults — like `changeStreamPreAndPostImages: { enabled: false }`,\n * which is equivalent to \"absent\" on both sides — are stripped from both IRs\n * so either authoring style verifies.\n */\n\nimport type {\n MongoSchemaCollection,\n MongoSchemaCollectionOptions,\n MongoSchemaIndex,\n MongoSchemaIR,\n} from '@prisma-next/mongo-schema-ir';\nimport {\n MongoSchemaCollection as MongoSchemaCollectionCtor,\n MongoSchemaCollectionOptions as MongoSchemaCollectionOptionsCtor,\n MongoSchemaIndex as MongoSchemaIndexCtor,\n MongoSchemaIR as MongoSchemaIRCtor,\n} from '@prisma-next/mongo-schema-ir';\nimport { ifDefined } from '@prisma-next/utils/defined';\n\nexport interface CanonicalizedSchemas {\n readonly live: MongoSchemaIR;\n readonly expected: MongoSchemaIR;\n}\n\nexport function canonicalizeSchemasForVerification(\n live: MongoSchemaIR,\n expected: MongoSchemaIR,\n): CanonicalizedSchemas {\n const expectedByName = new Map<string, MongoSchemaCollection>();\n for (const c of expected.collections) expectedByName.set(c.name, c);\n\n const liveByName = new Map<string, MongoSchemaCollection>();\n for (const c of live.collections) liveByName.set(c.name, c);\n\n const canonicalLive = live.collections.map((c) =>\n canonicalizeLiveCollection(c, expectedByName.get(c.name)),\n );\n const canonicalExpected = expected.collections.map((c) =>\n canonicalizeExpectedCollection(c, liveByName.get(c.name)),\n );\n\n return {\n live: new MongoSchemaIRCtor(canonicalLive),\n expected: new MongoSchemaIRCtor(canonicalExpected),\n };\n}\n\nfunction canonicalizeLiveCollection(\n liveColl: MongoSchemaCollection,\n expectedColl: MongoSchemaCollection | undefined,\n): MongoSchemaCollection {\n const expectedIndexes = expectedColl?.indexes ?? [];\n const indexes = liveColl.indexes.map((idx) =>\n canonicalizeLiveIndex(idx, findExpectedIndexCounterpart(idx, expectedIndexes)),\n );\n\n const options = liveColl.options\n ? canonicalizeLiveOptions(liveColl.options, expectedColl?.options)\n : undefined;\n\n return new MongoSchemaCollectionCtor({\n name: liveColl.name,\n indexes,\n ...ifDefined('validator', liveColl.validator),\n ...ifDefined('options', options),\n });\n}\n\nfunction canonicalizeExpectedCollection(\n expectedColl: MongoSchemaCollection,\n liveColl: MongoSchemaCollection | undefined,\n): MongoSchemaCollection {\n // Symmetric text-index key ordering: a contract-shaped text index preserves\n // the user-authored field order, but the introspected counterpart comes\n // back from MongoDB with `weights` keys in alphabetical order, so we\n // canonicalize both sides to alphabetical text-key order. The order of\n // text fields within the text block is semantically irrelevant — relevance\n // is governed by `weights`, not key order.\n const indexes = expectedColl.indexes.map(canonicalizeTextIndexKeyOrder);\n\n const options = expectedColl.options\n ? canonicalizeExpectedOptions(expectedColl.options, liveColl?.options)\n : undefined;\n\n return new MongoSchemaCollectionCtor({\n name: expectedColl.name,\n indexes,\n ...ifDefined('validator', expectedColl.validator),\n ...ifDefined('options', options),\n });\n}\n\nfunction canonicalizeTextIndexKeyOrder(index: MongoSchemaIndex): MongoSchemaIndex {\n const hasTextKey = index.keys.some((k) => k.direction === 'text');\n if (!hasTextKey) return index;\n return new MongoSchemaIndexCtor({\n keys: sortTextKeys(index.keys),\n unique: index.unique,\n ...ifDefined('sparse', index.sparse),\n ...ifDefined('expireAfterSeconds', index.expireAfterSeconds),\n ...ifDefined('partialFilterExpression', index.partialFilterExpression),\n ...ifDefined('wildcardProjection', index.wildcardProjection),\n ...ifDefined('collation', index.collation),\n ...ifDefined('weights', index.weights),\n ...ifDefined('default_language', index.default_language),\n ...ifDefined('language_override', index.language_override),\n });\n}\n\n/**\n * Returns a copy of `keys` with text-direction entries sorted alphabetically\n * while preserving the relative position of non-text entries. Compound text\n * indexes (`{a: 1, _fts: 'text', _ftsx: 1, b: 1}`) keep their scalar\n * prefix/suffix layout; only the contiguous text block is reordered.\n */\nfunction sortTextKeys(\n keys: ReadonlyArray<{\n readonly field: string;\n readonly direction: 'text' | 1 | -1 | '2dsphere' | '2d' | 'hashed';\n }>,\n): ReadonlyArray<{\n readonly field: string;\n readonly direction: 'text' | 1 | -1 | '2dsphere' | '2d' | 'hashed';\n}> {\n const textEntries = keys.filter((k) => k.direction === 'text');\n if (textEntries.length <= 1) return keys;\n const sortedText = [...textEntries].sort((a, b) => a.field.localeCompare(b.field));\n let textIdx = 0;\n return keys.map((k) => {\n if (k.direction !== 'text') return k;\n const next = sortedText[textIdx++];\n /* v8 ignore next 3 -- @preserve invariant guard: textIdx is always < sortedText.length here because we only consume sortedText for text-direction entries and sortedText is built from the same filter. */\n if (next === undefined) {\n throw new Error('sortTextKeys: text-key counts mismatched');\n }\n return next;\n });\n}\n\nfunction canonicalizeLiveIndex(\n liveIndex: MongoSchemaIndex,\n expectedIndex: MongoSchemaIndex | undefined,\n): MongoSchemaIndex {\n const projectedKeys = sortTextKeys(projectTextIndexKeys(liveIndex));\n const collation = liveIndex.collation\n ? stripUnspecifiedFields(liveIndex.collation, expectedIndex?.collation)\n : liveIndex.collation;\n\n // Text-index server defaults: when the contract did not set\n // `weights`/`default_language`/`language_override`, MongoDB applies\n // `weights = {<field>: 1, ...}` (uniform), `'english'`, and `'language'`\n // respectively. Strip them from live *only* when the live value matches\n // those defaults — preserving non-default live values lets the verifier\n // surface drift when the live index is tampered (e.g. weights tuned\n // out-of-band, custom `default_language`/`language_override`) even though\n // the contract authored neither.\n const weights =\n expectedIndex?.weights === undefined && hasDefaultTextWeights(projectedKeys, liveIndex.weights)\n ? undefined\n : liveIndex.weights;\n const default_language =\n expectedIndex?.default_language === undefined && liveIndex.default_language === 'english'\n ? undefined\n : liveIndex.default_language;\n const language_override =\n expectedIndex?.language_override === undefined && liveIndex.language_override === 'language'\n ? undefined\n : liveIndex.language_override;\n\n return new MongoSchemaIndexCtor({\n keys: projectedKeys,\n unique: liveIndex.unique,\n ...ifDefined('sparse', liveIndex.sparse),\n ...ifDefined('expireAfterSeconds', liveIndex.expireAfterSeconds),\n ...ifDefined('partialFilterExpression', liveIndex.partialFilterExpression),\n ...ifDefined('wildcardProjection', liveIndex.wildcardProjection),\n ...ifDefined('collation', collation),\n ...ifDefined('weights', weights),\n ...ifDefined('default_language', default_language),\n ...ifDefined('language_override', language_override),\n });\n}\n\n/**\n * Locate the contract-side index that corresponds to a live index for the\n * purpose of contract-aware normalization. We deliberately match by the\n * *projected* (contract-shaped) key list — so a live `_fts/_ftsx` index\n * resolves to a contract `{title: 'text', body: 'text'}` index — and pick\n * the first match. Contracts very rarely contain duplicate-key indexes; if\n * we have no counterpart we fall back to no normalization for that index.\n */\nfunction findExpectedIndexCounterpart(\n liveIndex: MongoSchemaIndex,\n expectedIndexes: ReadonlyArray<MongoSchemaIndex>,\n): MongoSchemaIndex | undefined {\n const projectedLiveKeys = sortTextKeys(projectTextIndexKeys(liveIndex));\n const liveKeySig = projectedLiveKeys.map((k) => `${k.field}:${k.direction}`).join(',');\n for (const expected of expectedIndexes) {\n const expectedKeySig = sortTextKeys(expected.keys)\n .map((k) => `${k.field}:${k.direction}`)\n .join(',');\n if (expectedKeySig === liveKeySig) return expected;\n }\n return undefined;\n}\n\n/**\n * MongoDB expands a contract-shaped text index like\n * `[{title: 'text'}, {body: 'text'}]` into its internal weighted vector\n * representation `[{_fts: 'text'}, {_ftsx: 1}]`. We project back to\n * contract-shaped keys via `weights`, iterating in whatever order MongoDB\n * returns them (alphabetical, in practice). `sortTextKeys` is applied\n * downstream to canonicalize the order on both sides, so this projection\n * does not depend on a specific iteration order.\n */\nfunction projectTextIndexKeys(liveIndex: MongoSchemaIndex): ReadonlyArray<{\n readonly field: string;\n readonly direction: 'text' | 1 | -1 | '2dsphere' | '2d' | 'hashed';\n}> {\n const isTextIndex =\n liveIndex.keys.length >= 1 &&\n liveIndex.keys.some((k) => k.field === '_fts' && k.direction === 'text');\n\n if (!isTextIndex || !liveIndex.weights) return liveIndex.keys;\n\n const textKeys = Object.keys(liveIndex.weights).map((field) => ({\n field,\n direction: 'text' as const,\n }));\n\n // Splice the projected text fields into the original `_fts/_ftsx` slot so\n // compound text indexes that mix scalar prefixes *and* suffixes — e.g.\n // `[prefix, _fts, _ftsx, suffix]` — keep their original layout. Flattening\n // scalars first would yield `[prefix, suffix, ...text]`, which `sortTextKeys`\n // (downstream) cannot recover because it only reorders text-direction\n // entries within their existing positions. MongoDB always emits exactly one\n // `_fts`/`_ftsx` pair per index, so we don't need to guard against\n // duplicates.\n type IndexKey = (typeof liveIndex.keys)[number];\n const projectedKeys: IndexKey[] = [];\n for (const key of liveIndex.keys) {\n if (key.field === '_ftsx') continue;\n if (key.field === '_fts') {\n projectedKeys.push(...textKeys);\n continue;\n }\n projectedKeys.push(key);\n }\n return projectedKeys;\n}\n\n/**\n * MongoDB's server-default `weights` for an authored-without-weights text\n * index assigns `1` to every text-direction field. Returns `true` only when\n * `liveWeights` is exactly that uniform shape (every projected text-direction\n * key weighted at `1`) so the canonicalizer leaves non-default weights —\n * including out-of-band relevance tweaks — visible to the verifier.\n *\n * `projectTextIndexKeys` derives text-direction keys from the live weights\n * map, so the count is guaranteed to match; we only have to check the value\n * shape.\n */\nfunction hasDefaultTextWeights(\n projectedKeys: ReadonlyArray<{\n readonly field: string;\n readonly direction: 'text' | 1 | -1 | '2dsphere' | '2d' | 'hashed';\n }>,\n liveWeights: MongoSchemaIndex['weights'],\n): boolean {\n if (liveWeights === undefined) return false;\n const textFields = projectedKeys.filter((k) => k.direction === 'text').map((k) => k.field);\n return textFields.every((field) => liveWeights[field] === 1);\n}\n\nfunction canonicalizeLiveOptions(\n liveOptions: MongoSchemaCollectionOptions,\n expectedOptions: MongoSchemaCollectionOptions | undefined,\n): MongoSchemaCollectionOptions | undefined {\n const collation = liveOptions.collation\n ? stripUnspecifiedFields(liveOptions.collation, expectedOptions?.collation)\n : undefined;\n\n // Timeseries: drop `bucketMaxSpanSeconds` (and any other server-applied\n // extras) when the contract did not specify them.\n const timeseries = liveOptions.timeseries\n ? (stripUnspecifiedFields(\n liveOptions.timeseries as Record<string, unknown>,\n expectedOptions?.timeseries as Record<string, unknown> | undefined,\n ) as MongoSchemaCollectionOptions['timeseries'])\n : undefined;\n\n // ClusteredIndex: drop `key`, `unique`, `v` and any other server-applied\n // extras when the contract did not specify them.\n const clusteredIndex = liveOptions.clusteredIndex\n ? (stripUnspecifiedFields(\n liveOptions.clusteredIndex as Record<string, unknown>,\n expectedOptions?.clusteredIndex as Record<string, unknown> | undefined,\n ) as MongoSchemaCollectionOptions['clusteredIndex'])\n : undefined;\n\n // changeStreamPreAndPostImages: `{enabled: false}` is equivalent to\n // \"absent\". Strip it from live so it round-trips with a contract that\n // omits the field, and is symmetric with the expected-side stripping.\n const changeStreamPreAndPostImages = isDisabledChangeStream(\n liveOptions.changeStreamPreAndPostImages,\n )\n ? undefined\n : liveOptions.changeStreamPreAndPostImages;\n\n const hasMeaningful =\n liveOptions.capped || timeseries || collation || changeStreamPreAndPostImages || clusteredIndex;\n if (!hasMeaningful) return undefined;\n\n return new MongoSchemaCollectionOptionsCtor({\n ...ifDefined('capped', liveOptions.capped),\n ...ifDefined('timeseries', timeseries),\n ...ifDefined('collation', collation),\n ...ifDefined('changeStreamPreAndPostImages', changeStreamPreAndPostImages),\n ...ifDefined('clusteredIndex', clusteredIndex),\n });\n}\n\nfunction canonicalizeExpectedOptions(\n expectedOptions: MongoSchemaCollectionOptions,\n _liveOptions: MongoSchemaCollectionOptions | undefined,\n): MongoSchemaCollectionOptions | undefined {\n // Symmetric: a contract `{enabled: false}` is equivalent to absent.\n const changeStreamPreAndPostImages = isDisabledChangeStream(\n expectedOptions.changeStreamPreAndPostImages,\n )\n ? undefined\n : expectedOptions.changeStreamPreAndPostImages;\n\n const hasMeaningful =\n expectedOptions.capped ||\n expectedOptions.timeseries ||\n expectedOptions.collation ||\n changeStreamPreAndPostImages ||\n expectedOptions.clusteredIndex;\n if (!hasMeaningful) return undefined;\n\n return new MongoSchemaCollectionOptionsCtor({\n ...ifDefined('capped', expectedOptions.capped),\n ...ifDefined('timeseries', expectedOptions.timeseries),\n ...ifDefined('collation', expectedOptions.collation),\n ...ifDefined('changeStreamPreAndPostImages', changeStreamPreAndPostImages),\n ...ifDefined('clusteredIndex', expectedOptions.clusteredIndex),\n });\n}\n\nfunction isDisabledChangeStream(value: { enabled: boolean } | undefined): boolean {\n return value !== undefined && value.enabled === false;\n}\n\n/**\n * Returns a copy of `live` containing only the keys that `expected` defines.\n * Used for option families whose individual sub-fields are server-extended\n * with platform defaults (collation, timeseries, clusteredIndex), so the\n * verifier should compare only what the contract actually authored.\n *\n * When `expected` is `undefined` — i.e. the contract authored nothing for\n * this whole option family but the live IR has it — we return `live`\n * unchanged so the verifier still sees the entire live block and can\n * surface it as drift. (Returning `undefined` here would silently strip a\n * server-attached collation/timeseries/clusteredIndex that the contract\n * never asked for, hiding real drift.)\n */\nfunction stripUnspecifiedFields(\n live: Record<string, unknown>,\n expected: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n if (expected === undefined) return live;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(expected)) {\n if (Object.hasOwn(live, key)) out[key] = live[key];\n }\n return out;\n}\n","import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n OperationContext,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport { VERIFY_CODE_SCHEMA_FAILURE } from '@prisma-next/framework-components/control';\nimport type { MongoContract } from '@prisma-next/mongo-contract';\nimport type { MongoSchemaIR } from '@prisma-next/mongo-schema-ir';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { contractToMongoSchemaIR } from '../contract-to-schema';\nimport { diffMongoSchemas } from '../schema-diff';\nimport { canonicalizeSchemasForVerification } from './canonicalize-introspection';\n\nexport interface VerifyMongoSchemaOptions {\n readonly contract: MongoContract;\n readonly schema: MongoSchemaIR;\n readonly strict: boolean;\n readonly context?: OperationContext;\n /**\n * Active framework components participating in this composition. Mongo\n * verification does not currently consult them, but the parameter exists\n * for parity with `verifySqlSchema` so callers can pass the same envelope.\n */\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', string>>;\n}\n\nexport function verifyMongoSchema(options: VerifyMongoSchemaOptions): VerifyDatabaseSchemaResult {\n const { contract, schema, strict, context } = options;\n const startTime = Date.now();\n\n const expectedIR = contractToMongoSchemaIR(contract);\n // Strip server-applied defaults (and authored equivalents) before diffing so\n // the verifier compares like-with-like — see `canonicalize-introspection.ts`.\n const { live: canonicalLive, expected: canonicalExpected } = canonicalizeSchemasForVerification(\n schema,\n expectedIR,\n );\n const { root, issues, counts } = diffMongoSchemas(canonicalLive, canonicalExpected, strict);\n\n const ok = counts.fail === 0;\n const profileHash = typeof contract.profileHash === 'string' ? contract.profileHash : '';\n\n return {\n ok,\n ...ifDefined('code', ok ? undefined : VERIFY_CODE_SCHEMA_FAILURE),\n summary: ok ? 'Schema matches contract' : `Schema verification found ${counts.fail} issue(s)`,\n contract: {\n storageHash: contract.storage.storageHash,\n ...(profileHash ? { profileHash } : {}),\n },\n target: { expected: contract.target },\n schema: { issues, root, counts },\n meta: {\n strict,\n ...ifDefined('contractPath', context?.contractPath),\n ...ifDefined('configPath', context?.configPath),\n },\n timings: { total: Date.now() - startTime },\n };\n}\n"],"mappings":"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"}
|