@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec-types.d.mts","names":[],"sources":["../src/exports/codec-types.ts"],"
|
|
1
|
+
{"version":3,"file":"codec-types.d.mts","names":[],"sources":["../src/exports/codec-types.ts"],"mappings":";KAAY,UAAA;EAAA,SACD,kBAAA;IAAA,SAA+B,KAAA;IAAA,SAAwB,MAAA;EAAA;EAAA,SACvD,gBAAA;IAAA,SAA6B,KAAA;IAAA,SAAwB,MAAA;EAAA;EAAA,SACrD,gBAAA;IAAA,SAA6B,KAAA;IAAA,SAAwB,MAAA;EAAA;EAAA,SACrD,eAAA;IAAA,SAA4B,KAAA;IAAA,SAAwB,MAAA;EAAA;EAAA,SACpD,cAAA;IAAA,SAA2B,KAAA;IAAA,SAAyB,MAAA;EAAA;EAAA,SACpD,cAAA;IAAA,SAA2B,KAAA,EAAO,IAAA;IAAA,SAAe,MAAA,EAAQ,IAAA;EAAA;EAAA,SACzD,gBAAA;IAAA,SACE,KAAA;IAAA,SACA,MAAA;EAAA;AAAA"}
|
package/dist/codec-types.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {};
|
package/dist/control.d.mts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { a as DropCollectionCall, c as schemaCollectionToCreateCollectionOptions, i as CreateIndexCall, l as schemaIndexToCreateIndexOptions, n as CollModMeta, o as DropIndexCall, r as CreateCollectionCall, s as OpFactoryCall, t as CollModCall } from "./op-factory-call-
|
|
1
|
+
import { a as DropCollectionCall, c as schemaCollectionToCreateCollectionOptions, i as CreateIndexCall, l as schemaIndexToCreateIndexOptions, n as CollModMeta, o as DropIndexCall, r as CreateCollectionCall, s as OpFactoryCall, t as CollModCall } from "./op-factory-call-9z5D19cP.mjs";
|
|
2
2
|
import { MongoSchemaIR } from "@prisma-next/mongo-schema-ir";
|
|
3
3
|
import { MongoQueryPlan } from "@prisma-next/mongo-query-ast/execution";
|
|
4
4
|
import { AnyMongoMigrationOperation, MongoAndExpr, MongoDdlCommandVisitor, MongoExistsExpr, MongoExprFilter, MongoFieldFilter, MongoFilterExpr, MongoFilterVisitor, MongoInspectionCommandVisitor, MongoMigrationPlanOperation, MongoNotExpr, MongoOrExpr } from "@prisma-next/mongo-query-ast/control";
|
|
5
5
|
import { Migration, MigrationMeta } from "@prisma-next/migration-tools/migration";
|
|
6
6
|
import { MigrationOperationPolicy, MigrationPlan, MigrationPlanOperation, MigrationPlanWithAuthoringSurface, MigrationPlanner, MigrationPlannerConflict, MigrationPlannerResult, MigrationRunnerExecutionChecks, MigrationRunnerResult, MigrationScaffoldContext, OperationContext } from "@prisma-next/framework-components/control";
|
|
7
7
|
import { MongoContract } from "@prisma-next/mongo-contract";
|
|
8
|
-
import { ContractMarkerRecord } from "@prisma-next/contract/types";
|
|
8
|
+
import { Contract, ContractMarkerRecord } from "@prisma-next/contract/types";
|
|
9
9
|
import { Db } from "mongodb";
|
|
10
10
|
import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
|
|
11
11
|
import { MongoAdapter, MongoDriver } from "@prisma-next/mongo-lowering";
|
|
@@ -33,10 +33,21 @@ declare function readMarker(db: Db): Promise<ContractMarkerRecord | null>;
|
|
|
33
33
|
declare function initMarker(db: Db, destination: {
|
|
34
34
|
readonly storageHash: string;
|
|
35
35
|
readonly profileHash: string;
|
|
36
|
+
readonly invariants?: readonly string[];
|
|
36
37
|
}): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Updates the marker doc atomically (CAS on `expectedFrom`).
|
|
40
|
+
*
|
|
41
|
+
* `destination.invariants`:
|
|
42
|
+
* - `undefined` → existing field left untouched.
|
|
43
|
+
* - explicit value → merged into the existing field server-side via an
|
|
44
|
+
* aggregation pipeline (`$setUnion + $sortArray`), atomic at the
|
|
45
|
+
* document level. `[]` is a no-op merge.
|
|
46
|
+
*/
|
|
37
47
|
declare function updateMarker(db: Db, expectedFrom: string, destination: {
|
|
38
48
|
readonly storageHash: string;
|
|
39
49
|
readonly profileHash: string;
|
|
50
|
+
readonly invariants?: readonly string[];
|
|
40
51
|
}): Promise<boolean>;
|
|
41
52
|
declare function writeLedgerEntry(db: Db, entry: {
|
|
42
53
|
readonly edgeId: string;
|
|
@@ -68,7 +79,12 @@ declare class MongoMigrationPlanner implements MigrationPlanner<'mongo', 'mongo'
|
|
|
68
79
|
readonly contract: unknown;
|
|
69
80
|
readonly schema: unknown;
|
|
70
81
|
readonly policy: MigrationOperationPolicy;
|
|
71
|
-
|
|
82
|
+
/**
|
|
83
|
+
* The "from" contract (state the planner assumes the database starts at),
|
|
84
|
+
* or `null` for reconciliation flows. Used to populate `describe().from`
|
|
85
|
+
* on the produced plan as `fromContract?.storage.storageHash ?? null`.
|
|
86
|
+
*/
|
|
87
|
+
readonly fromContract: Contract | null;
|
|
72
88
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', 'mongo'>>;
|
|
73
89
|
}): MigrationPlannerResult;
|
|
74
90
|
/**
|
|
@@ -89,10 +105,12 @@ interface MarkerOperations {
|
|
|
89
105
|
initMarker(destination: {
|
|
90
106
|
readonly storageHash: string;
|
|
91
107
|
readonly profileHash: string;
|
|
108
|
+
readonly invariants?: readonly string[];
|
|
92
109
|
}): Promise<void>;
|
|
93
110
|
updateMarker(expectedFrom: string, destination: {
|
|
94
111
|
readonly storageHash: string;
|
|
95
112
|
readonly profileHash: string;
|
|
113
|
+
readonly invariants?: readonly string[];
|
|
96
114
|
}): Promise<boolean>;
|
|
97
115
|
writeLedgerEntry(entry: {
|
|
98
116
|
readonly edgeId: string;
|
|
@@ -165,9 +183,8 @@ declare function renderOps(calls: ReadonlyArray<OpFactoryCall>): MongoMigrationP
|
|
|
165
183
|
//#endregion
|
|
166
184
|
//#region src/core/render-typescript.d.ts
|
|
167
185
|
interface RenderMigrationMeta {
|
|
168
|
-
readonly from: string;
|
|
186
|
+
readonly from: string | null;
|
|
169
187
|
readonly to: string;
|
|
170
|
-
readonly kind?: string;
|
|
171
188
|
readonly labels?: readonly string[];
|
|
172
189
|
}
|
|
173
190
|
/**
|
|
@@ -176,8 +193,8 @@ interface RenderMigrationMeta {
|
|
|
176
193
|
* `Migration` (i.e. `MongoMigration`) from `@prisma-next/family-mongo`, and
|
|
177
194
|
* implements the abstract `operations` and `describe` members. `meta` is
|
|
178
195
|
* always rendered — `describe()` is part of the `Migration` contract, so
|
|
179
|
-
* even an empty stub must satisfy it; callers pass
|
|
180
|
-
* migration-new scaffold.
|
|
196
|
+
* even an empty stub must satisfy it; callers pass `from: null` for a
|
|
197
|
+
* baseline `migration-new` scaffold (and a real `to` hash either way).
|
|
181
198
|
*
|
|
182
199
|
* The walk is polymorphic: each call node contributes its own
|
|
183
200
|
* `renderTypeScript()` expression and declares its own
|
package/dist/control.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/contract-to-schema.ts","../src/core/ddl-formatter.ts","../src/core/filter-evaluator.ts","../src/core/marker-ledger.ts","../src/core/mongo-ops-serializer.ts","../src/core/mongo-planner.ts","../src/core/mongo-runner.ts","../src/core/planner-produced-migration.ts","../src/core/render-ops.ts","../src/core/render-typescript.ts"],"
|
|
1
|
+
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/contract-to-schema.ts","../src/core/ddl-formatter.ts","../src/core/filter-evaluator.ts","../src/core/marker-ledger.ts","../src/core/mongo-ops-serializer.ts","../src/core/mongo-planner.ts","../src/core/mongo-runner.ts","../src/core/planner-produced-migration.ts","../src/core/render-ops.ts","../src/core/render-typescript.ts"],"mappings":"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"}
|
package/dist/control.mjs
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import { n as contractToMongoSchemaIR, t as verifyMongoSchema } from "./verify-mongo-schema-
|
|
2
|
-
import { a as dropCollection, n as createCollection, o as dropIndex, r as createIndex, t as collMod } from "./migration-factories-
|
|
1
|
+
import { n as contractToMongoSchemaIR, t as verifyMongoSchema } from "./verify-mongo-schema-DlPXaotB.mjs";
|
|
2
|
+
import { a as dropCollection, n as createCollection, o as dropIndex, r as createIndex, t as collMod } from "./migration-factories-ZBsWqXt-.mjs";
|
|
3
3
|
import { canonicalize, deepEqual } from "@prisma-next/mongo-schema-ir";
|
|
4
4
|
import { AggregateCommand, MongoAddFieldsStage, MongoLimitStage, MongoLookupStage, MongoMatchStage, MongoMergeStage, MongoProjectStage, MongoSortStage, RawAggregateCommand, RawDeleteManyCommand, RawDeleteOneCommand, RawFindOneAndDeleteCommand, RawFindOneAndUpdateCommand, RawInsertManyCommand, RawInsertOneCommand, RawUpdateManyCommand, RawUpdateOneCommand } from "@prisma-next/mongo-query-ast/execution";
|
|
5
|
+
import { type } from "arktype";
|
|
5
6
|
import { CollModCommand, CreateCollectionCommand, CreateIndexCommand, DropCollectionCommand, DropIndexCommand, ListCollectionsCommand, ListIndexesCommand, MongoAndExpr, MongoExistsExpr, MongoFieldFilter, MongoNotExpr, MongoOrExpr } from "@prisma-next/mongo-query-ast/control";
|
|
6
7
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
7
|
-
import { type } from "arktype";
|
|
8
8
|
import { TsExpression, jsonToTsSource, renderImports } from "@prisma-next/ts-render";
|
|
9
9
|
import { Migration } from "@prisma-next/migration-tools/migration";
|
|
10
10
|
import { detectScaffoldRuntime, shebangLineFor } from "@prisma-next/migration-tools/migration-ts";
|
|
11
11
|
import { errorRunnerFailed } from "@prisma-next/errors/execution";
|
|
12
12
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
13
|
-
|
|
14
13
|
//#region src/core/ddl-formatter.ts
|
|
15
14
|
function formatKeySpec(keys) {
|
|
16
15
|
return `{ ${keys.map((k) => `${JSON.stringify(k.field)}: ${JSON.stringify(k.direction)}`).join(", ")} }`;
|
|
@@ -80,7 +79,6 @@ function formatMongoOperations(operations) {
|
|
|
80
79
|
}
|
|
81
80
|
return statements;
|
|
82
81
|
}
|
|
83
|
-
|
|
84
82
|
//#endregion
|
|
85
83
|
//#region src/core/filter-evaluator.ts
|
|
86
84
|
function getNestedField(doc, path) {
|
|
@@ -133,11 +131,35 @@ var FilterEvaluator = class {
|
|
|
133
131
|
throw new Error("Aggregation expression filters are not supported in migration checks");
|
|
134
132
|
}
|
|
135
133
|
};
|
|
136
|
-
|
|
137
134
|
//#endregion
|
|
138
135
|
//#region src/core/marker-ledger.ts
|
|
139
136
|
const COLLECTION = "_prisma_migrations";
|
|
140
137
|
const MARKER_ID = "marker";
|
|
138
|
+
const MongoMarkerDocSchema = type({
|
|
139
|
+
storageHash: "string",
|
|
140
|
+
profileHash: "string",
|
|
141
|
+
"contractJson?": "unknown | null",
|
|
142
|
+
"canonicalVersion?": "number | null",
|
|
143
|
+
"updatedAt?": "Date",
|
|
144
|
+
"appTag?": "string | null",
|
|
145
|
+
"meta?": type({ "[string]": "unknown" }).or("null"),
|
|
146
|
+
"invariants?": type("string").array(),
|
|
147
|
+
"+": "delete"
|
|
148
|
+
});
|
|
149
|
+
function parseMongoMarkerDoc(doc) {
|
|
150
|
+
const result = MongoMarkerDocSchema(doc);
|
|
151
|
+
if (result instanceof type.errors) throw new Error(`Invalid marker doc on ${COLLECTION}: ${result.summary}`);
|
|
152
|
+
return {
|
|
153
|
+
storageHash: result.storageHash,
|
|
154
|
+
profileHash: result.profileHash,
|
|
155
|
+
contractJson: result.contractJson ?? null,
|
|
156
|
+
canonicalVersion: result.canonicalVersion ?? null,
|
|
157
|
+
updatedAt: result.updatedAt ?? /* @__PURE__ */ new Date(),
|
|
158
|
+
appTag: result.appTag ?? null,
|
|
159
|
+
meta: result.meta ?? {},
|
|
160
|
+
invariants: result.invariants ?? []
|
|
161
|
+
};
|
|
162
|
+
}
|
|
141
163
|
async function executeAggregate(db, cmd) {
|
|
142
164
|
return db.collection(cmd.collection).aggregate(cmd.pipeline).toArray();
|
|
143
165
|
}
|
|
@@ -150,15 +172,7 @@ async function executeFindOneAndUpdate(db, cmd) {
|
|
|
150
172
|
async function readMarker(db) {
|
|
151
173
|
const doc = (await executeAggregate(db, new RawAggregateCommand(COLLECTION, [{ $match: { _id: MARKER_ID } }, { $limit: 1 }])))[0];
|
|
152
174
|
if (!doc) return null;
|
|
153
|
-
return
|
|
154
|
-
storageHash: doc["storageHash"],
|
|
155
|
-
profileHash: doc["profileHash"],
|
|
156
|
-
contractJson: doc["contractJson"] ?? null,
|
|
157
|
-
canonicalVersion: doc["canonicalVersion"] ?? null,
|
|
158
|
-
updatedAt: doc["updatedAt"],
|
|
159
|
-
appTag: doc["appTag"] ?? null,
|
|
160
|
-
meta: doc["meta"] ?? {}
|
|
161
|
-
};
|
|
175
|
+
return parseMongoMarkerDoc(doc);
|
|
162
176
|
}
|
|
163
177
|
async function initMarker(db, destination) {
|
|
164
178
|
await executeInsertOne(db, new RawInsertOneCommand(COLLECTION, {
|
|
@@ -169,18 +183,36 @@ async function initMarker(db, destination) {
|
|
|
169
183
|
canonicalVersion: null,
|
|
170
184
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
171
185
|
appTag: null,
|
|
172
|
-
meta: {}
|
|
186
|
+
meta: {},
|
|
187
|
+
invariants: destination.invariants ?? []
|
|
173
188
|
}));
|
|
174
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Updates the marker doc atomically (CAS on `expectedFrom`).
|
|
192
|
+
*
|
|
193
|
+
* `destination.invariants`:
|
|
194
|
+
* - `undefined` → existing field left untouched.
|
|
195
|
+
* - explicit value → merged into the existing field server-side via an
|
|
196
|
+
* aggregation pipeline (`$setUnion + $sortArray`), atomic at the
|
|
197
|
+
* document level. `[]` is a no-op merge.
|
|
198
|
+
*/
|
|
175
199
|
async function updateMarker(db, expectedFrom, destination) {
|
|
176
|
-
|
|
177
|
-
_id: MARKER_ID,
|
|
178
|
-
storageHash: expectedFrom
|
|
179
|
-
}, { $set: {
|
|
200
|
+
const setBase = {
|
|
180
201
|
storageHash: destination.storageHash,
|
|
181
202
|
profileHash: destination.profileHash,
|
|
182
203
|
updatedAt: /* @__PURE__ */ new Date()
|
|
183
|
-
}
|
|
204
|
+
};
|
|
205
|
+
const update = destination.invariants === void 0 ? { $set: setBase } : [{ $set: {
|
|
206
|
+
...setBase,
|
|
207
|
+
invariants: { $sortArray: {
|
|
208
|
+
input: { $setUnion: [{ $ifNull: ["$invariants", []] }, destination.invariants] },
|
|
209
|
+
sortBy: 1
|
|
210
|
+
} }
|
|
211
|
+
} }];
|
|
212
|
+
return await executeFindOneAndUpdate(db, new RawFindOneAndUpdateCommand(COLLECTION, {
|
|
213
|
+
_id: MARKER_ID,
|
|
214
|
+
storageHash: expectedFrom
|
|
215
|
+
}, update, false)) !== null;
|
|
184
216
|
}
|
|
185
217
|
async function writeLedgerEntry(db, entry) {
|
|
186
218
|
await executeInsertOne(db, new RawInsertOneCommand(COLLECTION, {
|
|
@@ -191,7 +223,6 @@ async function writeLedgerEntry(db, entry) {
|
|
|
191
223
|
appliedAt: /* @__PURE__ */ new Date()
|
|
192
224
|
}));
|
|
193
225
|
}
|
|
194
|
-
|
|
195
226
|
//#endregion
|
|
196
227
|
//#region src/core/mongo-ops-serializer.ts
|
|
197
228
|
const CreateIndexJson = type({
|
|
@@ -320,13 +351,9 @@ const QueryPlanJson = type({
|
|
|
320
351
|
target: "string",
|
|
321
352
|
storageHash: "string",
|
|
322
353
|
lane: "string",
|
|
323
|
-
paramDescriptors: "unknown[]",
|
|
324
354
|
"targetFamily?": "string",
|
|
325
355
|
"profileHash?": "string",
|
|
326
|
-
"annotations?": "Record<string, unknown>"
|
|
327
|
-
"refs?": "Record<string, unknown>",
|
|
328
|
-
"projection?": "Record<string, string> | string[]",
|
|
329
|
-
"projectionTypes?": "Record<string, string>"
|
|
356
|
+
"annotations?": "Record<string, unknown>"
|
|
330
357
|
})
|
|
331
358
|
});
|
|
332
359
|
const CheckJson = type({
|
|
@@ -364,7 +391,7 @@ const DataTransformOperationJson = type({
|
|
|
364
391
|
});
|
|
365
392
|
function validate(schema, data, context) {
|
|
366
393
|
try {
|
|
367
|
-
return schema.assert(data);
|
|
394
|
+
return schema.assert(stripUndefinedDeep(data));
|
|
368
395
|
} catch (error) {
|
|
369
396
|
/* v8 ignore start -- assertion libraries always throw Error instances */
|
|
370
397
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -372,6 +399,51 @@ function validate(schema, data, context) {
|
|
|
372
399
|
throw new Error(`Invalid ${context}: ${message}`);
|
|
373
400
|
}
|
|
374
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* Strip `undefined`-valued properties before they reach arktype's optional-key
|
|
404
|
+
* assertions.
|
|
405
|
+
*
|
|
406
|
+
* Op IRs (e.g. `CreateCollectionCommand`) assign every optional field on
|
|
407
|
+
* every instance — fields the caller did not provide land as
|
|
408
|
+
* `undefined`-valued properties. arktype treats `{ foo?: 'boolean' }` as
|
|
409
|
+
* "key may be absent, but if present must be boolean", so the bare instance
|
|
410
|
+
* fails validation when it crosses the deserialize boundary in-process
|
|
411
|
+
* (no JSON round-trip happens between planner → runner). This helper
|
|
412
|
+
* recovers the JSON-round-tripped shape (undefined keys absent) without
|
|
413
|
+
* forcing every caller to round-trip.
|
|
414
|
+
*
|
|
415
|
+
* Returns the original value reference whenever no change is needed.
|
|
416
|
+
* That preserves prototype-bound payload values such as BSON wrappers
|
|
417
|
+
* (`ObjectId`, `Decimal128`, `Binary`, …) which embed no `undefined`
|
|
418
|
+
* own-enumerable properties and therefore never trigger a rebuild.
|
|
419
|
+
* Top-level op IRs (class instances with `undefined` optional fields)
|
|
420
|
+
* still get flattened to plain records as required by arktype.
|
|
421
|
+
*/
|
|
422
|
+
function stripUndefinedDeep(value) {
|
|
423
|
+
if (Array.isArray(value)) {
|
|
424
|
+
let changed = false;
|
|
425
|
+
const next = value.map((item) => {
|
|
426
|
+
const stripped = stripUndefinedDeep(item);
|
|
427
|
+
if (stripped !== item) changed = true;
|
|
428
|
+
return stripped;
|
|
429
|
+
});
|
|
430
|
+
return changed ? next : value;
|
|
431
|
+
}
|
|
432
|
+
if (value === null || typeof value !== "object") return value;
|
|
433
|
+
const entries = Object.entries(value);
|
|
434
|
+
const out = {};
|
|
435
|
+
let changed = false;
|
|
436
|
+
for (const [key, val] of entries) {
|
|
437
|
+
if (val === void 0) {
|
|
438
|
+
changed = true;
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
const stripped = stripUndefinedDeep(val);
|
|
442
|
+
if (stripped !== val) changed = true;
|
|
443
|
+
out[key] = stripped;
|
|
444
|
+
}
|
|
445
|
+
return changed ? out : value;
|
|
446
|
+
}
|
|
375
447
|
function deserializeFilterExpr(json) {
|
|
376
448
|
const record = json;
|
|
377
449
|
const kind = record["kind"];
|
|
@@ -490,13 +562,9 @@ function deserializeMongoQueryPlan(json) {
|
|
|
490
562
|
target: m.target,
|
|
491
563
|
storageHash: m.storageHash,
|
|
492
564
|
lane: m.lane,
|
|
493
|
-
paramDescriptors: m.paramDescriptors,
|
|
494
565
|
...ifDefined("targetFamily", m.targetFamily),
|
|
495
566
|
...ifDefined("profileHash", m.profileHash),
|
|
496
|
-
...ifDefined("annotations", m.annotations)
|
|
497
|
-
...ifDefined("refs", m.refs),
|
|
498
|
-
...ifDefined("projection", m.projection),
|
|
499
|
-
...ifDefined("projectionTypes", m.projectionTypes)
|
|
567
|
+
...ifDefined("annotations", m.annotations)
|
|
500
568
|
};
|
|
501
569
|
return {
|
|
502
570
|
collection: data.collection,
|
|
@@ -625,7 +693,6 @@ function deserializeMongoOps(json) {
|
|
|
625
693
|
function serializeMongoOps(ops) {
|
|
626
694
|
return JSON.stringify(ops, null, 2);
|
|
627
695
|
}
|
|
628
|
-
|
|
629
696
|
//#endregion
|
|
630
697
|
//#region src/core/op-factory-call.ts
|
|
631
698
|
const TARGET_MIGRATION_MODULE = "@prisma-next/target-mongo/migration";
|
|
@@ -780,13 +847,11 @@ function schemaCollectionToCreateCollectionOptions(coll) {
|
|
|
780
847
|
changeStreamPreAndPostImages: opts?.changeStreamPreAndPostImages
|
|
781
848
|
};
|
|
782
849
|
}
|
|
783
|
-
|
|
784
850
|
//#endregion
|
|
785
851
|
//#region src/core/render-ops.ts
|
|
786
852
|
function renderOps(calls) {
|
|
787
853
|
return calls.map((call) => call.toOp());
|
|
788
854
|
}
|
|
789
|
-
|
|
790
855
|
//#endregion
|
|
791
856
|
//#region src/core/render-typescript.ts
|
|
792
857
|
/**
|
|
@@ -818,8 +883,8 @@ const BASE_IMPORTS = [{
|
|
|
818
883
|
* `Migration` (i.e. `MongoMigration`) from `@prisma-next/family-mongo`, and
|
|
819
884
|
* implements the abstract `operations` and `describe` members. `meta` is
|
|
820
885
|
* always rendered — `describe()` is part of the `Migration` contract, so
|
|
821
|
-
* even an empty stub must satisfy it; callers pass
|
|
822
|
-
* migration-new scaffold.
|
|
886
|
+
* even an empty stub must satisfy it; callers pass `from: null` for a
|
|
887
|
+
* baseline `migration-new` scaffold (and a real `to` hash either way).
|
|
823
888
|
*
|
|
824
889
|
* The walk is polymorphic: each call node contributes its own
|
|
825
890
|
* `renderTypeScript()` expression and declares its own
|
|
@@ -861,7 +926,6 @@ function buildDescribeMethod(meta) {
|
|
|
861
926
|
lines.push(" return {");
|
|
862
927
|
lines.push(` from: ${JSON.stringify(meta.from)},`);
|
|
863
928
|
lines.push(` to: ${JSON.stringify(meta.to)},`);
|
|
864
|
-
if (meta.kind) lines.push(` kind: ${JSON.stringify(meta.kind)},`);
|
|
865
929
|
if (meta.labels && meta.labels.length > 0) lines.push(` labels: ${jsonToTsSource(meta.labels)},`);
|
|
866
930
|
lines.push(" };");
|
|
867
931
|
lines.push(" }");
|
|
@@ -872,7 +936,6 @@ function indent(text, spaces) {
|
|
|
872
936
|
const pad = " ".repeat(spaces);
|
|
873
937
|
return text.split("\n").map((line) => line.trim() ? `${pad}${line}` : line).join("\n");
|
|
874
938
|
}
|
|
875
|
-
|
|
876
939
|
//#endregion
|
|
877
940
|
//#region src/core/planner-produced-migration.ts
|
|
878
941
|
/**
|
|
@@ -892,6 +955,8 @@ function indent(text, spaces) {
|
|
|
892
955
|
* cycle.
|
|
893
956
|
*/
|
|
894
957
|
var PlannerProducedMongoMigration = class extends Migration {
|
|
958
|
+
calls;
|
|
959
|
+
meta;
|
|
895
960
|
targetId = "mongo";
|
|
896
961
|
constructor(calls, meta) {
|
|
897
962
|
super();
|
|
@@ -908,12 +973,10 @@ var PlannerProducedMongoMigration = class extends Migration {
|
|
|
908
973
|
return renderCallsToTypeScript(this.calls, {
|
|
909
974
|
from: this.meta.from,
|
|
910
975
|
to: this.meta.to,
|
|
911
|
-
...ifDefined("kind", this.meta.kind),
|
|
912
976
|
...ifDefined("labels", this.meta.labels)
|
|
913
977
|
});
|
|
914
978
|
}
|
|
915
979
|
};
|
|
916
|
-
|
|
917
980
|
//#endregion
|
|
918
981
|
//#region src/core/mongo-planner.ts
|
|
919
982
|
function buildIndexLookupKey(index) {
|
|
@@ -973,8 +1036,8 @@ var MongoMigrationPlanner = class {
|
|
|
973
1036
|
const originColl = originIR.collection(collName);
|
|
974
1037
|
const destColl = destinationIR.collection(collName);
|
|
975
1038
|
if (!originColl) {
|
|
976
|
-
if (destColl && collectionHasOptions(destColl)) {
|
|
977
|
-
const opts = schemaCollectionToCreateCollectionOptions(destColl);
|
|
1039
|
+
if (destColl && (collectionHasOptions(destColl) || destColl.indexes.length === 0)) {
|
|
1040
|
+
const opts = collectionHasOptions(destColl) ? schemaCollectionToCreateCollectionOptions(destColl) : void 0;
|
|
978
1041
|
collCreates.push(new CreateCollectionCall(collName, opts));
|
|
979
1042
|
}
|
|
980
1043
|
} else if (!destColl) collDrops.push(new DropCollectionCall(collName));
|
|
@@ -1030,7 +1093,7 @@ var MongoMigrationPlanner = class {
|
|
|
1030
1093
|
return {
|
|
1031
1094
|
kind: "success",
|
|
1032
1095
|
plan: new PlannerProducedMongoMigration(result.calls, {
|
|
1033
|
-
from: options.
|
|
1096
|
+
from: options.fromContract?.storage.storageHash ?? null,
|
|
1034
1097
|
to: contract.storage.storageHash
|
|
1035
1098
|
})
|
|
1036
1099
|
};
|
|
@@ -1086,7 +1149,6 @@ function planMutableOptionsDiffCall(collName, origin, dest) {
|
|
|
1086
1149
|
operationClass: desiredCSPPI.enabled ? "widening" : "destructive"
|
|
1087
1150
|
});
|
|
1088
1151
|
}
|
|
1089
|
-
|
|
1090
1152
|
//#endregion
|
|
1091
1153
|
//#region src/core/mongo-runner.ts
|
|
1092
1154
|
const READ_ONLY_CHECK_COMMAND_KINDS = new Set(["aggregate", "rawAggregate"]);
|
|
@@ -1098,6 +1160,7 @@ function runnerFailure(code, summary, opts) {
|
|
|
1098
1160
|
});
|
|
1099
1161
|
}
|
|
1100
1162
|
var MongoMigrationRunner = class {
|
|
1163
|
+
deps;
|
|
1101
1164
|
constructor(deps) {
|
|
1102
1165
|
this.deps = deps;
|
|
1103
1166
|
}
|
|
@@ -1142,40 +1205,44 @@ var MongoMigrationRunner = class {
|
|
|
1142
1205
|
}
|
|
1143
1206
|
const destination = options.plan.destination;
|
|
1144
1207
|
const profileHash = options.destinationContract.profileHash ?? destination.storageHash;
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
if (
|
|
1208
|
+
const incomingInvariants = options.plan.providedInvariants ?? [];
|
|
1209
|
+
const existingInvariantSet = new Set(existingMarker?.invariants ?? []);
|
|
1210
|
+
const incomingIsSubsetOfExisting = incomingInvariants.every((id) => existingInvariantSet.has(id));
|
|
1211
|
+
const markerAlreadyAtDestination = existingMarker !== null && existingMarker.storageHash === destination.storageHash && existingMarker.profileHash === profileHash;
|
|
1212
|
+
if (!(operationsExecuted === 0 && markerAlreadyAtDestination && incomingIsSubsetOfExisting)) {
|
|
1213
|
+
const liveSchema = await this.deps.introspectSchema();
|
|
1214
|
+
const verifyResult = verifyMongoSchema({
|
|
1215
|
+
contract: options.destinationContract,
|
|
1216
|
+
schema: liveSchema,
|
|
1217
|
+
strict: options.strictVerification ?? true,
|
|
1218
|
+
frameworkComponents: options.frameworkComponents,
|
|
1219
|
+
...options.context ? { context: options.context } : {}
|
|
1220
|
+
});
|
|
1221
|
+
if (!verifyResult.ok) return runnerFailure("SCHEMA_VERIFY_FAILED", verifyResult.summary, {
|
|
1222
|
+
why: "The resulting database schema does not satisfy the destination contract.",
|
|
1223
|
+
meta: { issues: verifyResult.schema.issues }
|
|
1224
|
+
});
|
|
1225
|
+
if (existingMarker) {
|
|
1226
|
+
if (!await markerOps.updateMarker(existingMarker.storageHash, {
|
|
1227
|
+
storageHash: destination.storageHash,
|
|
1228
|
+
profileHash,
|
|
1229
|
+
invariants: incomingInvariants
|
|
1230
|
+
})) return runnerFailure("MARKER_CAS_FAILURE", "Marker was modified by another process during migration execution.", { meta: {
|
|
1231
|
+
expectedStorageHash: existingMarker.storageHash,
|
|
1232
|
+
destinationStorageHash: destination.storageHash
|
|
1233
|
+
} });
|
|
1234
|
+
} else await markerOps.initMarker({
|
|
1163
1235
|
storageHash: destination.storageHash,
|
|
1164
|
-
profileHash
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
await markerOps.writeLedgerEntry({
|
|
1175
|
-
edgeId: `${originHash}->${destination.storageHash}`,
|
|
1176
|
-
from: originHash,
|
|
1177
|
-
to: destination.storageHash
|
|
1178
|
-
});
|
|
1236
|
+
profileHash,
|
|
1237
|
+
invariants: incomingInvariants
|
|
1238
|
+
});
|
|
1239
|
+
const originHash = existingMarker?.storageHash ?? "";
|
|
1240
|
+
await markerOps.writeLedgerEntry({
|
|
1241
|
+
edgeId: `${originHash}->${destination.storageHash}`,
|
|
1242
|
+
from: originHash,
|
|
1243
|
+
to: destination.storageHash
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1179
1246
|
return ok({
|
|
1180
1247
|
operationsPlanned: operations.length,
|
|
1181
1248
|
operationsExecuted
|
|
@@ -1195,7 +1262,7 @@ var MongoMigrationRunner = class {
|
|
|
1195
1262
|
};
|
|
1196
1263
|
}
|
|
1197
1264
|
for (const plan of op.run) {
|
|
1198
|
-
const wireCommand = await adapter.lower(plan);
|
|
1265
|
+
const wireCommand = await adapter.lower(plan, {});
|
|
1199
1266
|
for await (const _ of driver.execute(wireCommand));
|
|
1200
1267
|
}
|
|
1201
1268
|
if (runPostchecks && op.postcheck.length > 0) {
|
|
@@ -1221,7 +1288,7 @@ var MongoMigrationRunner = class {
|
|
|
1221
1288
|
collection: check.source.collection
|
|
1222
1289
|
}
|
|
1223
1290
|
});
|
|
1224
|
-
const wireCommand = await adapter.lower(check.source);
|
|
1291
|
+
const wireCommand = await adapter.lower(check.source, {});
|
|
1225
1292
|
let matchFound = false;
|
|
1226
1293
|
for await (const row of driver.execute(wireCommand)) if (filterEvaluator.evaluate(check.filter, row)) {
|
|
1227
1294
|
matchFound = true;
|
|
@@ -1254,10 +1321,7 @@ var MongoMigrationRunner = class {
|
|
|
1254
1321
|
}
|
|
1255
1322
|
ensureMarkerCompatibility(marker, plan) {
|
|
1256
1323
|
const origin = plan.origin ?? null;
|
|
1257
|
-
if (!origin)
|
|
1258
|
-
if (marker) return runnerFailure("MARKER_ORIGIN_MISMATCH", "Database already has a contract marker but the plan has no origin. This would silently overwrite the existing marker.", { meta: { markerStorageHash: marker.storageHash } });
|
|
1259
|
-
return;
|
|
1260
|
-
}
|
|
1324
|
+
if (!origin) return;
|
|
1261
1325
|
if (!marker) return runnerFailure("MARKER_ORIGIN_MISMATCH", `Missing contract marker: expected origin storage hash ${origin.storageHash}.`, { meta: { expectedOriginStorageHash: origin.storageHash } });
|
|
1262
1326
|
if (marker.storageHash !== origin.storageHash) return runnerFailure("MARKER_ORIGIN_MISMATCH", `Existing contract marker (${marker.storageHash}) does not match plan origin (${origin.storageHash}).`, { meta: {
|
|
1263
1327
|
markerStorageHash: marker.storageHash,
|
|
@@ -1265,7 +1329,7 @@ var MongoMigrationRunner = class {
|
|
|
1265
1329
|
} });
|
|
1266
1330
|
}
|
|
1267
1331
|
};
|
|
1268
|
-
|
|
1269
1332
|
//#endregion
|
|
1270
1333
|
export { CollModCall, CreateCollectionCall, CreateIndexCall, DropCollectionCall, DropIndexCall, FilterEvaluator, MongoMigrationPlanner, MongoMigrationRunner, PlannerProducedMongoMigration, contractToMongoSchemaIR, deserializeMongoOp, deserializeMongoOps, formatMongoOperations, initMarker, readMarker, renderCallsToTypeScript, renderOps, schemaCollectionToCreateCollectionOptions, schemaIndexToCreateIndexOptions, serializeMongoOps, updateMarker, writeLedgerEntry };
|
|
1334
|
+
|
|
1271
1335
|
//# sourceMappingURL=control.mjs.map
|