@prisma-next/target-mongo 0.4.0-dev.8 → 0.4.0-dev.9
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/control.d.mts +9 -3
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +289 -12
- package/dist/control.mjs.map +1 -1
- package/dist/{migration-factories-Brzz-QGG.mjs → migration-factories-gwi81C8u.mjs} +43 -3
- package/dist/migration-factories-gwi81C8u.mjs.map +1 -0
- package/dist/migration.d.mts +15 -2
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +2 -2
- package/package.json +13 -12
- package/src/core/migration-factories.ts +69 -1
- package/src/core/mongo-ops-serializer.ts +324 -5
- package/src/core/mongo-runner.ts +144 -8
- package/src/exports/migration.ts +1 -0
- package/dist/migration-factories-Brzz-QGG.mjs.map +0 -1
package/src/core/mongo-runner.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContractMarkerRecord } from '@prisma-next/contract/types';
|
|
2
|
+
import { errorRunnerFailed } from '@prisma-next/errors/execution';
|
|
2
3
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
3
4
|
import type {
|
|
4
5
|
MigrationOperationPolicy,
|
|
@@ -8,13 +9,29 @@ import type {
|
|
|
8
9
|
MigrationRunnerFailure,
|
|
9
10
|
MigrationRunnerResult,
|
|
10
11
|
} from '@prisma-next/framework-components/control';
|
|
12
|
+
import type { MongoAdapter, MongoDriver } from '@prisma-next/mongo-lowering';
|
|
11
13
|
import type {
|
|
14
|
+
AnyMongoMigrationOperation,
|
|
15
|
+
MongoDataTransformCheck,
|
|
16
|
+
MongoDataTransformOperation,
|
|
12
17
|
MongoDdlCommandVisitor,
|
|
13
18
|
MongoInspectionCommandVisitor,
|
|
14
19
|
MongoMigrationCheck,
|
|
15
20
|
MongoMigrationPlanOperation,
|
|
16
21
|
} from '@prisma-next/mongo-query-ast/control';
|
|
17
22
|
import { notOk, ok } from '@prisma-next/utils/result';
|
|
23
|
+
|
|
24
|
+
const READ_ONLY_CHECK_COMMAND_KINDS: ReadonlySet<string> = new Set(['aggregate', 'rawAggregate']);
|
|
25
|
+
|
|
26
|
+
function hasProfileHash(value: unknown): value is { readonly profileHash: string } {
|
|
27
|
+
return (
|
|
28
|
+
typeof value === 'object' &&
|
|
29
|
+
value !== null &&
|
|
30
|
+
Object.hasOwn(value, 'profileHash') &&
|
|
31
|
+
typeof (value as { profileHash: unknown }).profileHash === 'string'
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
18
35
|
import { FilterEvaluator } from './filter-evaluator';
|
|
19
36
|
import { deserializeMongoOps } from './mongo-ops-serializer';
|
|
20
37
|
|
|
@@ -38,6 +55,8 @@ export interface MarkerOperations {
|
|
|
38
55
|
export interface MongoRunnerDependencies {
|
|
39
56
|
readonly commandExecutor: MongoDdlCommandVisitor<Promise<void>>;
|
|
40
57
|
readonly inspectionExecutor: MongoInspectionCommandVisitor<Promise<Record<string, unknown>[]>>;
|
|
58
|
+
readonly adapter: MongoAdapter;
|
|
59
|
+
readonly driver: MongoDriver;
|
|
41
60
|
readonly markerOps: MarkerOperations;
|
|
42
61
|
}
|
|
43
62
|
|
|
@@ -67,7 +86,7 @@ export class MongoMigrationRunner {
|
|
|
67
86
|
readonly executionChecks?: MigrationRunnerExecutionChecks;
|
|
68
87
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'mongo', 'mongo'>>;
|
|
69
88
|
}): Promise<MigrationRunnerResult> {
|
|
70
|
-
const { commandExecutor, inspectionExecutor, markerOps } = this.deps;
|
|
89
|
+
const { commandExecutor, inspectionExecutor, adapter, driver, markerOps } = this.deps;
|
|
71
90
|
const operations = deserializeMongoOps(options.plan.operations as readonly unknown[]);
|
|
72
91
|
|
|
73
92
|
const policyCheck = this.enforcePolicyCompatibility(options.policy, operations);
|
|
@@ -90,9 +109,26 @@ export class MongoMigrationRunner {
|
|
|
90
109
|
for (const operation of operations) {
|
|
91
110
|
options.callbacks?.onOperationStart?.(operation);
|
|
92
111
|
try {
|
|
112
|
+
if (operation.operationClass === 'data') {
|
|
113
|
+
const result = await this.executeDataTransform(
|
|
114
|
+
operation as MongoDataTransformOperation,
|
|
115
|
+
adapter,
|
|
116
|
+
driver,
|
|
117
|
+
filterEvaluator,
|
|
118
|
+
runIdempotency,
|
|
119
|
+
runPrechecks,
|
|
120
|
+
runPostchecks,
|
|
121
|
+
);
|
|
122
|
+
if (result.failure) return result.failure;
|
|
123
|
+
if (result.executed) operationsExecuted += 1;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const ddlOp = operation as MongoMigrationPlanOperation;
|
|
128
|
+
|
|
93
129
|
if (runPostchecks && runIdempotency) {
|
|
94
130
|
const allSatisfied = await this.allChecksSatisfied(
|
|
95
|
-
|
|
131
|
+
ddlOp.postcheck,
|
|
96
132
|
inspectionExecutor,
|
|
97
133
|
filterEvaluator,
|
|
98
134
|
);
|
|
@@ -101,7 +137,7 @@ export class MongoMigrationRunner {
|
|
|
101
137
|
|
|
102
138
|
if (runPrechecks) {
|
|
103
139
|
const precheckResult = await this.evaluateChecks(
|
|
104
|
-
|
|
140
|
+
ddlOp.precheck,
|
|
105
141
|
inspectionExecutor,
|
|
106
142
|
filterEvaluator,
|
|
107
143
|
);
|
|
@@ -114,13 +150,13 @@ export class MongoMigrationRunner {
|
|
|
114
150
|
}
|
|
115
151
|
}
|
|
116
152
|
|
|
117
|
-
for (const step of
|
|
153
|
+
for (const step of ddlOp.execute) {
|
|
118
154
|
await step.command.accept(commandExecutor);
|
|
119
155
|
}
|
|
120
156
|
|
|
121
157
|
if (runPostchecks) {
|
|
122
158
|
const postcheckResult = await this.evaluateChecks(
|
|
123
|
-
|
|
159
|
+
ddlOp.postcheck,
|
|
124
160
|
inspectionExecutor,
|
|
125
161
|
filterEvaluator,
|
|
126
162
|
);
|
|
@@ -140,8 +176,9 @@ export class MongoMigrationRunner {
|
|
|
140
176
|
}
|
|
141
177
|
|
|
142
178
|
const destination = options.plan.destination;
|
|
143
|
-
const
|
|
144
|
-
|
|
179
|
+
const profileHash = hasProfileHash(options.destinationContract)
|
|
180
|
+
? options.destinationContract.profileHash
|
|
181
|
+
: destination.storageHash;
|
|
145
182
|
|
|
146
183
|
if (
|
|
147
184
|
operationsExecuted === 0 &&
|
|
@@ -185,6 +222,105 @@ export class MongoMigrationRunner {
|
|
|
185
222
|
return ok({ operationsPlanned: operations.length, operationsExecuted });
|
|
186
223
|
}
|
|
187
224
|
|
|
225
|
+
private async executeDataTransform(
|
|
226
|
+
op: MongoDataTransformOperation,
|
|
227
|
+
adapter: MongoAdapter,
|
|
228
|
+
driver: MongoDriver,
|
|
229
|
+
filterEvaluator: FilterEvaluator,
|
|
230
|
+
runIdempotency: boolean,
|
|
231
|
+
runPrechecks: boolean,
|
|
232
|
+
runPostchecks: boolean,
|
|
233
|
+
): Promise<{ executed: boolean; failure?: MigrationRunnerResult }> {
|
|
234
|
+
if (runPostchecks && runIdempotency && op.postcheck.length > 0) {
|
|
235
|
+
const allSatisfied = await this.evaluateDataTransformChecks(
|
|
236
|
+
op.postcheck,
|
|
237
|
+
adapter,
|
|
238
|
+
driver,
|
|
239
|
+
filterEvaluator,
|
|
240
|
+
);
|
|
241
|
+
if (allSatisfied) return { executed: false };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (runPrechecks && op.precheck.length > 0) {
|
|
245
|
+
const passed = await this.evaluateDataTransformChecks(
|
|
246
|
+
op.precheck,
|
|
247
|
+
adapter,
|
|
248
|
+
driver,
|
|
249
|
+
filterEvaluator,
|
|
250
|
+
);
|
|
251
|
+
if (!passed) {
|
|
252
|
+
return {
|
|
253
|
+
executed: false,
|
|
254
|
+
failure: runnerFailure('PRECHECK_FAILED', `Operation ${op.id} failed during precheck`, {
|
|
255
|
+
meta: { operationId: op.id, name: op.name },
|
|
256
|
+
}),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
for (const plan of op.run) {
|
|
262
|
+
const wireCommand = adapter.lower(plan);
|
|
263
|
+
for await (const _ of driver.execute(wireCommand)) {
|
|
264
|
+
/* consume */
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (runPostchecks && op.postcheck.length > 0) {
|
|
269
|
+
const passed = await this.evaluateDataTransformChecks(
|
|
270
|
+
op.postcheck,
|
|
271
|
+
adapter,
|
|
272
|
+
driver,
|
|
273
|
+
filterEvaluator,
|
|
274
|
+
);
|
|
275
|
+
if (!passed) {
|
|
276
|
+
return {
|
|
277
|
+
executed: false,
|
|
278
|
+
failure: runnerFailure('POSTCHECK_FAILED', `Operation ${op.id} failed during postcheck`, {
|
|
279
|
+
meta: { operationId: op.id, name: op.name },
|
|
280
|
+
}),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return { executed: true };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private async evaluateDataTransformChecks(
|
|
289
|
+
checks: readonly MongoDataTransformCheck[],
|
|
290
|
+
adapter: MongoAdapter,
|
|
291
|
+
driver: MongoDriver,
|
|
292
|
+
filterEvaluator: FilterEvaluator,
|
|
293
|
+
): Promise<boolean> {
|
|
294
|
+
for (const check of checks) {
|
|
295
|
+
const commandKind = check.source.command.kind;
|
|
296
|
+
if (!READ_ONLY_CHECK_COMMAND_KINDS.has(commandKind)) {
|
|
297
|
+
throw errorRunnerFailed(
|
|
298
|
+
`Data-transform check rejected: command kind "${commandKind}" is not read-only`,
|
|
299
|
+
{
|
|
300
|
+
why: 'Data-transform checks must use aggregate or rawAggregate commands so the pre/postcheck path cannot mutate the database.',
|
|
301
|
+
fix: 'Author the check.source as an aggregate pipeline (or rawAggregate) rather than a DML write command.',
|
|
302
|
+
meta: {
|
|
303
|
+
checkDescription: check.description,
|
|
304
|
+
commandKind,
|
|
305
|
+
collection: check.source.collection,
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
const wireCommand = adapter.lower(check.source);
|
|
311
|
+
let matchFound = false;
|
|
312
|
+
for await (const row of driver.execute<Record<string, unknown>>(wireCommand)) {
|
|
313
|
+
if (filterEvaluator.evaluate(check.filter, row)) {
|
|
314
|
+
matchFound = true;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const passed = check.expect === 'exists' ? matchFound : !matchFound;
|
|
319
|
+
if (!passed) return false;
|
|
320
|
+
}
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
|
|
188
324
|
private async evaluateChecks(
|
|
189
325
|
checks: readonly MongoMigrationCheck[],
|
|
190
326
|
inspectionExecutor: MongoInspectionCommandVisitor<Promise<Record<string, unknown>[]>>,
|
|
@@ -212,7 +348,7 @@ export class MongoMigrationRunner {
|
|
|
212
348
|
|
|
213
349
|
private enforcePolicyCompatibility(
|
|
214
350
|
policy: MigrationOperationPolicy,
|
|
215
|
-
operations: readonly
|
|
351
|
+
operations: readonly AnyMongoMigrationOperation[],
|
|
216
352
|
): MigrationRunnerResult | undefined {
|
|
217
353
|
const allowedClasses = new Set(policy.allowedOperationClasses);
|
|
218
354
|
for (const operation of operations) {
|
package/src/exports/migration.ts
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"migration-factories-Brzz-QGG.mjs","names":[],"sources":["../src/core/migration-factories.ts"],"sourcesContent":["import type { MongoIndexKey } 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 MongoFieldFilter,\n type MongoMigrationPlanOperation,\n} from '@prisma-next/mongo-query-ast/control';\nimport type { CollModMeta } from './op-factory-call';\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":"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"}
|