@prisma-next/cli 0.12.0-dev.33 → 0.12.0-dev.35
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/README.md +2 -2
- package/dist/cli.mjs +5 -5
- package/dist/{client-xeWpMlq1.mjs → client-CcqChq9N.mjs} +42 -42
- package/dist/client-CcqChq9N.mjs.map +1 -0
- package/dist/command-helpers-DK_5ItoJ.mjs.map +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.mjs +2 -2
- package/dist/commands/db-schema.mjs +1 -1
- package/dist/commands/db-sign.mjs +1 -1
- package/dist/commands/db-update.mjs +2 -2
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +2 -2
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +2 -2
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-graph.d.mts +1 -1
- package/dist/commands/migration-list.d.mts +1 -1
- package/dist/commands/migration-log.d.mts +1 -1
- package/dist/commands/migration-log.mjs +1 -1
- package/dist/commands/migration-show.d.mts +1 -1
- package/dist/commands/migration-show.mjs +1 -1
- package/dist/commands/migration-status.mjs +1 -1
- package/dist/commands/ref.d.mts +1 -1
- package/dist/{contract-infer-2wtPflGH.mjs → contract-infer-DaHH7CGX.mjs} +2 -2
- package/dist/{contract-infer-2wtPflGH.mjs.map → contract-infer-DaHH7CGX.mjs.map} +1 -1
- package/dist/{db-verify-CxHiSiTG.mjs → db-verify-DppLAhYT.mjs} +2 -2
- package/dist/{db-verify-CxHiSiTG.mjs.map → db-verify-DppLAhYT.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +2 -2
- package/dist/exports/control-api.mjs +1 -1
- package/dist/{inspect-live-schema-RekOwfi5.mjs → inspect-live-schema-CfOSPCmR.mjs} +2 -2
- package/dist/{inspect-live-schema-RekOwfi5.mjs.map → inspect-live-schema-CfOSPCmR.mjs.map} +1 -1
- package/dist/{migration-command-scaffold-ApB3NxWY.mjs → migration-command-scaffold-CIxCqBXa.mjs} +2 -2
- package/dist/{migration-command-scaffold-ApB3NxWY.mjs.map → migration-command-scaffold-CIxCqBXa.mjs.map} +1 -1
- package/dist/{migration-log-DD_vCbYW.mjs → migration-log-CwP41Cke.mjs} +2 -2
- package/dist/{migration-log-DD_vCbYW.mjs.map → migration-log-CwP41Cke.mjs.map} +1 -1
- package/dist/{migration-status-qV8ctwPy.mjs → migration-status-GMRiiLFP.mjs} +2 -2
- package/dist/{migration-status-qV8ctwPy.mjs.map → migration-status-GMRiiLFP.mjs.map} +1 -1
- package/dist/{types-Mh7mdPHM.d.mts → types-Cculk5KV.d.mts} +32 -30
- package/dist/{types-Mh7mdPHM.d.mts.map → types-Cculk5KV.d.mts.map} +1 -1
- package/package.json +18 -18
- package/src/commands/migrate.ts +6 -6
- package/src/control-api/client.ts +6 -6
- package/src/control-api/operations/db-init.ts +3 -3
- package/src/control-api/operations/{db-apply.ts → db-run.ts} +16 -12
- package/src/control-api/operations/db-update.ts +4 -4
- package/src/control-api/operations/{migration-apply.ts → migrate.ts} +22 -21
- package/src/control-api/operations/{apply.ts → run-migration.ts} +32 -27
- package/src/control-api/types.ts +30 -28
- package/src/utils/cli-errors.ts +2 -2
- package/dist/client-xeWpMlq1.mjs.map +0 -1
package/src/commands/migrate.ts
CHANGED
|
@@ -11,8 +11,8 @@ import { Command } from 'commander';
|
|
|
11
11
|
import { loadConfig } from '../config-loader';
|
|
12
12
|
import { createControlClient } from '../control-api/client';
|
|
13
13
|
import type {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
MigrateFailure,
|
|
15
|
+
MigratePathDecision,
|
|
16
16
|
PerSpaceExecutionEntry,
|
|
17
17
|
} from '../control-api/types';
|
|
18
18
|
import {
|
|
@@ -76,14 +76,14 @@ export interface MigrateResult {
|
|
|
76
76
|
}[];
|
|
77
77
|
readonly summary: string;
|
|
78
78
|
readonly perSpace: readonly PerSpaceExecutionEntry[];
|
|
79
|
-
readonly pathDecision?:
|
|
79
|
+
readonly pathDecision?: MigratePathDecision;
|
|
80
80
|
readonly timings: {
|
|
81
81
|
readonly total: number;
|
|
82
82
|
};
|
|
83
83
|
readonly advancedRef?: { readonly name: string; readonly hash: string } | null;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
function mapApplyFailure(failure:
|
|
86
|
+
function mapApplyFailure(failure: MigrateFailure): CliStructuredErrorType {
|
|
87
87
|
if (failure.code === 'MIGRATION_PATH_NOT_FOUND') {
|
|
88
88
|
return errorPathUnreachable(failure);
|
|
89
89
|
}
|
|
@@ -136,7 +136,7 @@ async function executeMigrateCommand(
|
|
|
136
136
|
|
|
137
137
|
// Construct the family instance up-front so the on-disk contract read
|
|
138
138
|
// crosses the serializer seam (`familyInstance.deserializeContract`) at
|
|
139
|
-
// the read site. The downstream `client.
|
|
139
|
+
// the read site. The downstream `client.migrate({ contract })`
|
|
140
140
|
// re-validates internally (no harm — validation is idempotent), but
|
|
141
141
|
// closing the gap at the read site is what makes the cast-pattern
|
|
142
142
|
// lint enforceable and matches the other CLI commands. See TML-2536.
|
|
@@ -312,7 +312,7 @@ async function executeMigrateCommand(
|
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
-
const applyResult = await client.
|
|
315
|
+
const applyResult = await client.migrate({
|
|
316
316
|
contract: applyContract,
|
|
317
317
|
migrationsDir,
|
|
318
318
|
...ifDefined('refHash', refEntry?.hash),
|
|
@@ -33,7 +33,7 @@ import { ContractValidationError } from './errors';
|
|
|
33
33
|
import { executeDbInit } from './operations/db-init';
|
|
34
34
|
import { executeDbUpdate } from './operations/db-update';
|
|
35
35
|
import { type ExecuteDbVerifyResult, executeDbVerify } from './operations/db-verify';
|
|
36
|
-
import {
|
|
36
|
+
import { executeMigrate } from './operations/migrate';
|
|
37
37
|
|
|
38
38
|
import type {
|
|
39
39
|
ControlActionName,
|
|
@@ -47,8 +47,8 @@ import type {
|
|
|
47
47
|
EmitOptions,
|
|
48
48
|
EmitResult,
|
|
49
49
|
IntrospectOptions,
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
MigrateOptions,
|
|
51
|
+
MigrateResult,
|
|
52
52
|
OnControlProgress,
|
|
53
53
|
SchemaVerifyOptions,
|
|
54
54
|
SignOptions,
|
|
@@ -457,9 +457,9 @@ class ControlClientImpl implements ControlClient {
|
|
|
457
457
|
return familyInstance.readLedger({ driver, ...ifDefined('space', space) });
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
-
async
|
|
460
|
+
async migrate(options: MigrateOptions): Promise<MigrateResult> {
|
|
461
461
|
const { onProgress } = options;
|
|
462
|
-
await this.connectWithProgress(options.connection, '
|
|
462
|
+
await this.connectWithProgress(options.connection, 'migrate', onProgress);
|
|
463
463
|
const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
|
|
464
464
|
|
|
465
465
|
if (!hasMigrations(this.options.target)) {
|
|
@@ -474,7 +474,7 @@ class ControlClientImpl implements ControlClient {
|
|
|
474
474
|
throw new ContractValidationError(message, error);
|
|
475
475
|
}
|
|
476
476
|
|
|
477
|
-
return
|
|
477
|
+
return executeMigrate({
|
|
478
478
|
driver,
|
|
479
479
|
familyInstance,
|
|
480
480
|
contract,
|
|
@@ -8,14 +8,14 @@ import type {
|
|
|
8
8
|
} from '@prisma-next/framework-components/control';
|
|
9
9
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
10
10
|
import type { DbInitResult, OnControlProgress } from '../types';
|
|
11
|
-
import {
|
|
11
|
+
import { executeRun } from './db-run';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Options for executing the `db init` operation.
|
|
15
15
|
*
|
|
16
16
|
* `db init` runs the loader → planner → runner pipeline:
|
|
17
17
|
*
|
|
18
|
-
* 1. {@link
|
|
18
|
+
* 1. {@link executeRun} loads a `ContractSpaceAggregate` via
|
|
19
19
|
* {@link import('@prisma-next/migration-tools/aggregate').loadContractSpaceAggregate}
|
|
20
20
|
* from the supplied descriptor set + on-disk on-disk artefacts.
|
|
21
21
|
* 2. The aggregate planner runs with `callerPolicy.ignoreGraphFor`
|
|
@@ -68,7 +68,7 @@ export interface ExecuteDbInitOptions<TFamilyId extends string, TTargetId extend
|
|
|
68
68
|
export async function executeDbInit<TFamilyId extends string, TTargetId extends string>(
|
|
69
69
|
options: ExecuteDbInitOptions<TFamilyId, TTargetId>,
|
|
70
70
|
): Promise<DbInitResult> {
|
|
71
|
-
const result = await
|
|
71
|
+
const result = await executeRun<TFamilyId, TTargetId>({
|
|
72
72
|
driver: options.driver,
|
|
73
73
|
familyInstance: options.familyInstance,
|
|
74
74
|
contract: options.contract,
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backs `db init` / `db update`. Strategy: introspect → planMigration; synth-for-app + graph-walk-extensions; plan-mode + orphan-marker preflight.
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
import type { Contract } from '@prisma-next/contract/types';
|
|
2
6
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
3
7
|
import type {
|
|
@@ -33,19 +37,19 @@ import type {
|
|
|
33
37
|
OnControlProgress,
|
|
34
38
|
PerSpaceExecutionEntry,
|
|
35
39
|
} from '../types';
|
|
40
|
+
import { stripOperations } from './migration-helpers';
|
|
36
41
|
import {
|
|
37
|
-
applyMigration,
|
|
38
42
|
buildPerSpaceBreakdown,
|
|
39
43
|
collectOrdered,
|
|
40
44
|
type OrderedResolution,
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
runMigration,
|
|
46
|
+
} from './run-migration';
|
|
43
47
|
|
|
44
48
|
/**
|
|
45
|
-
* Span IDs emitted via `onProgress` during the
|
|
49
|
+
* Span IDs emitted via `onProgress` during the run flow.
|
|
46
50
|
* Stable identifiers consumed by the structured-output renderer and by
|
|
47
51
|
* tests asserting on span ids. The `apply` span itself is owned by
|
|
48
|
-
* the {@link
|
|
52
|
+
* the {@link runMigration} primitive — only the introspect / plan
|
|
49
53
|
* spans are emitted directly here.
|
|
50
54
|
*/
|
|
51
55
|
const SPAN_IDS = {
|
|
@@ -54,13 +58,13 @@ const SPAN_IDS = {
|
|
|
54
58
|
} as const;
|
|
55
59
|
|
|
56
60
|
/**
|
|
57
|
-
* Inputs shared by `db init` and `db update`
|
|
61
|
+
* Inputs shared by `db init` and `db update` run flows.
|
|
58
62
|
*
|
|
59
63
|
* Accepts the already-validated app contract + descriptor list — the
|
|
60
64
|
* loader gathers the rest from disk + descriptors. The CLI is the
|
|
61
65
|
* descriptor-import boundary; everything downstream is descriptor-free.
|
|
62
66
|
*/
|
|
63
|
-
export interface
|
|
67
|
+
export interface ExecuteRunOptions<TFamilyId extends string, TTargetId extends string> {
|
|
64
68
|
readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;
|
|
65
69
|
readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
|
|
66
70
|
readonly contract: Contract;
|
|
@@ -98,8 +102,8 @@ export interface ExecuteApplyOptions<TFamilyId extends string, TTargetId extends
|
|
|
98
102
|
* transaction across every space; failure on any space rolls back
|
|
99
103
|
* every space's writes.
|
|
100
104
|
*/
|
|
101
|
-
export async function
|
|
102
|
-
options:
|
|
105
|
+
export async function executeRun<TFamilyId extends string, TTargetId extends string>(
|
|
106
|
+
options: ExecuteRunOptions<TFamilyId, TTargetId>,
|
|
103
107
|
): Promise<DbInitResult | DbUpdateResult> {
|
|
104
108
|
const {
|
|
105
109
|
driver,
|
|
@@ -213,13 +217,13 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
|
|
|
213
217
|
});
|
|
214
218
|
}
|
|
215
219
|
|
|
216
|
-
// 5.
|
|
220
|
+
// 5. Run mode: hand off to the shared `runMigration` primitive.
|
|
217
221
|
// The runner-driving tail is identical for `db init` / `db update` /
|
|
218
222
|
// `migrate` — only how each caller produces `perSpacePlans`
|
|
219
223
|
// differs (synth + graph-walk via planMigration here; graph-walk
|
|
220
224
|
// only for migrate). Each caller produces perSpacePlans differently;
|
|
221
|
-
// this helper handles the shared
|
|
222
|
-
const applied = await
|
|
225
|
+
// this helper handles the shared run tail.
|
|
226
|
+
const applied = await runMigration({
|
|
223
227
|
aggregate,
|
|
224
228
|
perSpacePlans: planResult.value.perSpace,
|
|
225
229
|
applyOrder: planResult.value.applyOrder,
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
10
10
|
import { notOk } from '@prisma-next/utils/result';
|
|
11
11
|
import type { DbUpdateResult, OnControlProgress } from '../types';
|
|
12
|
-
import {
|
|
12
|
+
import { executeRun } from './db-run';
|
|
13
13
|
|
|
14
14
|
const DB_UPDATE_POLICY = {
|
|
15
15
|
allowedOperationClasses: ['additive', 'widening', 'destructive'] as const,
|
|
@@ -71,7 +71,7 @@ export async function executeDbUpdate<TFamilyId extends string, TTargetId extend
|
|
|
71
71
|
const gate = await guardDestructiveChanges<TFamilyId, TTargetId>(sharedInputs);
|
|
72
72
|
if (gate !== null) return gate;
|
|
73
73
|
}
|
|
74
|
-
return (await
|
|
74
|
+
return (await executeRun<TFamilyId, TTargetId>({
|
|
75
75
|
...sharedInputs,
|
|
76
76
|
mode: options.mode,
|
|
77
77
|
})) as DbUpdateResult;
|
|
@@ -85,9 +85,9 @@ export async function executeDbUpdate<TFamilyId extends string, TTargetId extend
|
|
|
85
85
|
* run.
|
|
86
86
|
*/
|
|
87
87
|
async function guardDestructiveChanges<TFamilyId extends string, TTargetId extends string>(
|
|
88
|
-
sharedInputs: Omit<Parameters<typeof
|
|
88
|
+
sharedInputs: Omit<Parameters<typeof executeRun<TFamilyId, TTargetId>>[0], 'mode'>,
|
|
89
89
|
): Promise<DbUpdateResult | null> {
|
|
90
|
-
const planResult = (await
|
|
90
|
+
const planResult = (await executeRun<TFamilyId, TTargetId>({
|
|
91
91
|
...sharedInputs,
|
|
92
92
|
mode: 'plan',
|
|
93
93
|
})) as DbUpdateResult;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backs the `migrate` command. Strategy: graph-walk-all-members, replay-only (no introspect/synth/planner).
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
import type { Contract } from '@prisma-next/contract/types';
|
|
2
6
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
3
7
|
import type {
|
|
@@ -25,14 +29,14 @@ import {
|
|
|
25
29
|
buildContractSpaceAggregate,
|
|
26
30
|
} from '../../utils/contract-space-aggregate-loader';
|
|
27
31
|
import type {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
MigrateFailure,
|
|
33
|
+
MigratePathDecision,
|
|
34
|
+
MigrateResult,
|
|
35
|
+
MigrateSuccess,
|
|
32
36
|
OnControlProgress,
|
|
33
37
|
PerSpaceExecutionEntry,
|
|
34
38
|
} from '../types';
|
|
35
|
-
import {
|
|
39
|
+
import { buildPerSpaceBreakdown, runMigration } from './run-migration';
|
|
36
40
|
|
|
37
41
|
/**
|
|
38
42
|
* Inputs for the aggregate-walking `migrate` control-api
|
|
@@ -43,7 +47,7 @@ import { applyMigration, buildPerSpaceBreakdown } from './apply';
|
|
|
43
47
|
* is the single descriptor-free seam between the CLI and the
|
|
44
48
|
* aggregate runtime.
|
|
45
49
|
*/
|
|
46
|
-
export interface
|
|
50
|
+
export interface ExecuteMigrateOptions<TFamilyId extends string, TTargetId extends string> {
|
|
47
51
|
readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;
|
|
48
52
|
readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
|
|
49
53
|
/** Already-validated app contract (the canonical "where we are heading" hash). */
|
|
@@ -96,16 +100,16 @@ export interface ExecuteMigrationApplyOptions<TFamilyId extends string, TTargetI
|
|
|
96
100
|
* marker to `member.headRef.hash` (or `refHash` for the app
|
|
97
101
|
* member when provided). Empty-graph members fail loudly — a
|
|
98
102
|
* "never planned" space is a user-error condition for replay.
|
|
99
|
-
* 4. Hand off to {@link
|
|
103
|
+
* 4. Hand off to {@link runMigration} (the runner-driving tail
|
|
100
104
|
* shared with `db init` / `db update`). Marker advancement is
|
|
101
105
|
* inside the per-space transaction.
|
|
102
106
|
*
|
|
103
107
|
* Encodes the replay-only contract: every contract space must have an
|
|
104
108
|
* authored migration graph on disk before this operation can advance it.
|
|
105
109
|
*/
|
|
106
|
-
export async function
|
|
107
|
-
options:
|
|
108
|
-
): Promise<
|
|
110
|
+
export async function executeMigrate<TFamilyId extends string, TTargetId extends string>(
|
|
111
|
+
options: ExecuteMigrateOptions<TFamilyId, TTargetId>,
|
|
112
|
+
): Promise<MigrateResult> {
|
|
109
113
|
const {
|
|
110
114
|
driver,
|
|
111
115
|
familyInstance,
|
|
@@ -277,7 +281,7 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
|
|
|
277
281
|
);
|
|
278
282
|
}
|
|
279
283
|
|
|
280
|
-
const applied = await
|
|
284
|
+
const applied = await runMigration({
|
|
281
285
|
aggregate,
|
|
282
286
|
perSpacePlans,
|
|
283
287
|
applyOrder,
|
|
@@ -286,12 +290,12 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
|
|
|
286
290
|
migrations,
|
|
287
291
|
frameworkComponents,
|
|
288
292
|
policy: { allowedOperationClasses: ['additive', 'widening', 'destructive', 'data'] },
|
|
289
|
-
action: '
|
|
293
|
+
action: 'migrate',
|
|
290
294
|
...ifDefined('onProgress', onProgress),
|
|
291
295
|
});
|
|
292
296
|
|
|
293
297
|
if (!applied.ok) {
|
|
294
|
-
const failure:
|
|
298
|
+
const failure: MigrateFailure = {
|
|
295
299
|
code: 'RUNNER_FAILED',
|
|
296
300
|
summary: applied.failure.summary,
|
|
297
301
|
why: applied.failure.why,
|
|
@@ -396,9 +400,9 @@ interface BuildSuccessArgs {
|
|
|
396
400
|
readonly summary: string;
|
|
397
401
|
}
|
|
398
402
|
|
|
399
|
-
function buildSuccess(args: BuildSuccessArgs):
|
|
403
|
+
function buildSuccess(args: BuildSuccessArgs): MigrateSuccess {
|
|
400
404
|
// The marker hash surfaced at the top level is the **app member's**
|
|
401
|
-
// post-
|
|
405
|
+
// post-migrate marker (the top-level `markerHash` field).
|
|
402
406
|
// Per-space markers live on `perSpace[].marker.storageHash`.
|
|
403
407
|
const appResolution = args.orderedResolutions.find(
|
|
404
408
|
(r) => r.spaceId === args.aggregate.app.spaceId,
|
|
@@ -423,7 +427,7 @@ function buildSuccess(args: BuildSuccessArgs): MigrationApplySuccess {
|
|
|
423
427
|
});
|
|
424
428
|
|
|
425
429
|
const appPlan = appResolution?.entry;
|
|
426
|
-
const pathDecision:
|
|
430
|
+
const pathDecision: MigratePathDecision | undefined = appPlan?.pathDecision
|
|
427
431
|
? {
|
|
428
432
|
fromHash: appPlan.pathDecision.fromHash,
|
|
429
433
|
toHash: appPlan.pathDecision.toHash,
|
|
@@ -462,10 +466,7 @@ function buildSuccess(args: BuildSuccessArgs): MigrationApplySuccess {
|
|
|
462
466
|
*
|
|
463
467
|
* @internal Exported for testing only.
|
|
464
468
|
*/
|
|
465
|
-
export function buildNeverPlannedFailure(
|
|
466
|
-
spaceId: string,
|
|
467
|
-
targetHash: string,
|
|
468
|
-
): MigrationApplyFailure {
|
|
469
|
+
export function buildNeverPlannedFailure(spaceId: string, targetHash: string): MigrateFailure {
|
|
469
470
|
return {
|
|
470
471
|
code: 'MIGRATION_PATH_NOT_FOUND',
|
|
471
472
|
summary: `No on-disk migrations for contract space "${spaceId}"`,
|
|
@@ -488,7 +489,7 @@ export function buildPathNotFoundFailure(
|
|
|
488
489
|
spaceId: string,
|
|
489
490
|
marker: ContractMarkerRecordLike | null,
|
|
490
491
|
targetHash: string,
|
|
491
|
-
):
|
|
492
|
+
): MigrateFailure {
|
|
492
493
|
const fromHash = marker?.storageHash ?? '<empty>';
|
|
493
494
|
// The app-case phrasing names the user-visible condition (a
|
|
494
495
|
// contract has been emitted that no on-disk migration reaches) so
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared runner tail (`runMigration` + `buildPerSpaceBreakdown`/`collectOrdered`).
|
|
3
|
+
* Backs no command directly; consumed by db-run and migrate.
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
2
7
|
import type {
|
|
3
8
|
ControlDriverInstance,
|
|
@@ -11,32 +16,32 @@ import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
|
11
16
|
import type { OnControlProgress, PerSpaceExecutionEntry } from '../types';
|
|
12
17
|
|
|
13
18
|
/**
|
|
14
|
-
* Span id emitted via `onProgress` for the
|
|
19
|
+
* Span id emitted via `onProgress` for the run phase. Stable
|
|
15
20
|
* identifier consumed by the structured-output renderer and by tests.
|
|
16
21
|
*/
|
|
17
|
-
const
|
|
22
|
+
const RUN_SPAN_ID = 'apply' as const;
|
|
18
23
|
|
|
19
24
|
/**
|
|
20
|
-
* Action that originated this
|
|
25
|
+
* Action that originated this run call. Threaded into `OnControlProgress`
|
|
21
26
|
* events so the parent CLI command can attribute the span correctly,
|
|
22
27
|
* and used to compose action-specific summary phrasing.
|
|
23
28
|
*/
|
|
24
|
-
export type
|
|
29
|
+
export type RunAction = 'dbInit' | 'dbUpdate' | 'migrate';
|
|
25
30
|
|
|
26
31
|
/**
|
|
27
|
-
* Failure variant emitted by {@link
|
|
28
|
-
* itself rejects the
|
|
32
|
+
* Failure variant emitted by {@link runMigration} when the runner
|
|
33
|
+
* itself rejects the run. Mirrors the failure shape callers
|
|
29
34
|
* already wrap into their own action-specific failure envelopes
|
|
30
|
-
* (`DbInitFailure`, `DbUpdateFailure`, `
|
|
35
|
+
* (`DbInitFailure`, `DbUpdateFailure`, `MigrateFailure`) so each
|
|
31
36
|
* caller keeps owning its own discriminated failure code.
|
|
32
37
|
*/
|
|
33
|
-
export interface
|
|
38
|
+
export interface RunnerFailure {
|
|
34
39
|
readonly summary: string;
|
|
35
40
|
readonly why?: string;
|
|
36
41
|
readonly meta: Record<string, unknown>;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
|
-
export interface
|
|
44
|
+
export interface RunMigrationInputs<TFamilyId extends string, TTargetId extends string> {
|
|
40
45
|
readonly aggregate: ContractSpaceAggregate;
|
|
41
46
|
/**
|
|
42
47
|
* Per-space plans, keyed by `spaceId`. Produced by either the full
|
|
@@ -62,22 +67,22 @@ export interface ApplyMigrationInputs<TFamilyId extends string, TTargetId extend
|
|
|
62
67
|
>;
|
|
63
68
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;
|
|
64
69
|
readonly policy: MigrationOperationPolicy;
|
|
65
|
-
readonly action:
|
|
70
|
+
readonly action: RunAction;
|
|
66
71
|
readonly onProgress?: OnControlProgress;
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
/**
|
|
70
75
|
* Resolved per-space plan in canonical schedule order. Surfaced from
|
|
71
|
-
* {@link
|
|
76
|
+
* {@link runMigration} to callers so each one can build its own
|
|
72
77
|
* action-specific success envelope (e.g. `DbInitSuccess` vs
|
|
73
|
-
* `
|
|
78
|
+
* `MigrateSuccess`) without re-deriving the ordering.
|
|
74
79
|
*/
|
|
75
80
|
export interface OrderedResolution {
|
|
76
81
|
readonly spaceId: string;
|
|
77
82
|
readonly entry: PerSpacePlan;
|
|
78
83
|
}
|
|
79
84
|
|
|
80
|
-
export interface
|
|
85
|
+
export interface RunMigrationValue {
|
|
81
86
|
readonly orderedResolutions: readonly OrderedResolution[];
|
|
82
87
|
readonly totalOpsPlanned: number;
|
|
83
88
|
readonly totalOpsExecuted: number;
|
|
@@ -89,10 +94,10 @@ export interface ApplyMigrationValue {
|
|
|
89
94
|
readonly perSpace: readonly PerSpaceExecutionEntry[];
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
export type
|
|
97
|
+
export type RunMigrationResult = Result<RunMigrationValue, RunnerFailure>;
|
|
93
98
|
|
|
94
99
|
/**
|
|
95
|
-
* Runner-driving tail shared by every
|
|
100
|
+
* Runner-driving tail shared by every run caller — `db init`,
|
|
96
101
|
* `db update`, and `migrate`. Consumes already-resolved per-space
|
|
97
102
|
* plans (the planner-vs-replay distinction is owned by the caller) and
|
|
98
103
|
* dispatches them to the runner in canonical order.
|
|
@@ -107,9 +112,9 @@ export type ApplyMigrationResult = Result<ApplyMigrationValue, ApplyRunnerFailur
|
|
|
107
112
|
* so callers don't have to duplicate it; the `action` field on each
|
|
108
113
|
* progress event is taken from the caller's `action` argument.
|
|
109
114
|
*/
|
|
110
|
-
export async function
|
|
111
|
-
inputs:
|
|
112
|
-
): Promise<
|
|
115
|
+
export async function runMigration<TFamilyId extends string, TTargetId extends string>(
|
|
116
|
+
inputs: RunMigrationInputs<TFamilyId, TTargetId>,
|
|
117
|
+
): Promise<RunMigrationResult> {
|
|
113
118
|
const {
|
|
114
119
|
aggregate,
|
|
115
120
|
perSpacePlans,
|
|
@@ -130,7 +135,7 @@ export async function applyMigration<TFamilyId extends string, TTargetId extends
|
|
|
130
135
|
onProgress?.({
|
|
131
136
|
action,
|
|
132
137
|
kind: 'spanStart',
|
|
133
|
-
spanId:
|
|
138
|
+
spanId: RUN_SPAN_ID,
|
|
134
139
|
label: progressLabelForAction(action),
|
|
135
140
|
});
|
|
136
141
|
|
|
@@ -152,7 +157,7 @@ export async function applyMigration<TFamilyId extends string, TTargetId extends
|
|
|
152
157
|
const runnerResult = await runner.execute({ driver, perSpaceOptions });
|
|
153
158
|
|
|
154
159
|
if (!runnerResult.ok) {
|
|
155
|
-
onProgress?.({ action, kind: 'spanEnd', spanId:
|
|
160
|
+
onProgress?.({ action, kind: 'spanEnd', spanId: RUN_SPAN_ID, outcome: 'error' });
|
|
156
161
|
return notOk({
|
|
157
162
|
summary: runnerResult.failure.summary,
|
|
158
163
|
...ifDefined('why', runnerResult.failure.why),
|
|
@@ -163,7 +168,7 @@ export async function applyMigration<TFamilyId extends string, TTargetId extends
|
|
|
163
168
|
},
|
|
164
169
|
});
|
|
165
170
|
}
|
|
166
|
-
onProgress?.({ action, kind: 'spanEnd', spanId:
|
|
171
|
+
onProgress?.({ action, kind: 'spanEnd', spanId: RUN_SPAN_ID, outcome: 'ok' });
|
|
167
172
|
|
|
168
173
|
const totalOpsPlanned = runnerResult.value.perSpaceResults.reduce(
|
|
169
174
|
(sum, r) => sum + r.value.operationsPlanned,
|
|
@@ -195,7 +200,7 @@ export async function applyMigration<TFamilyId extends string, TTargetId extends
|
|
|
195
200
|
* advances as the last step of each space's transaction) and `false`
|
|
196
201
|
* for plan-mode (no marker has been written yet).
|
|
197
202
|
*
|
|
198
|
-
* Exported alongside {@link
|
|
203
|
+
* Exported alongside {@link runMigration} so plan-mode callers can
|
|
199
204
|
* assemble the same per-space block without going through the runner.
|
|
200
205
|
*/
|
|
201
206
|
export function buildPerSpaceBreakdown(
|
|
@@ -244,18 +249,18 @@ export function collectOrdered(
|
|
|
244
249
|
}
|
|
245
250
|
|
|
246
251
|
/**
|
|
247
|
-
* Action-appropriate label for the `spanStart` event the
|
|
248
|
-
* primitive emits. `
|
|
252
|
+
* Action-appropriate label for the `spanStart` event the run
|
|
253
|
+
* primitive emits. `runMigration` is shared by `db init`, `db update`,
|
|
249
254
|
* and `migrate`; the span label tracks the user-visible action
|
|
250
255
|
* so structured-progress output reads naturally for each surface.
|
|
251
256
|
*/
|
|
252
|
-
export function progressLabelForAction(action:
|
|
257
|
+
export function progressLabelForAction(action: RunAction): string {
|
|
253
258
|
switch (action) {
|
|
254
259
|
case 'dbInit':
|
|
255
260
|
return 'Initialising database across spaces';
|
|
256
261
|
case 'dbUpdate':
|
|
257
262
|
return 'Updating database across spaces';
|
|
258
|
-
case '
|
|
259
|
-
return '
|
|
263
|
+
case 'migrate':
|
|
264
|
+
return 'Running migration plan across spaces';
|
|
260
265
|
}
|
|
261
266
|
}
|
package/src/control-api/types.ts
CHANGED
|
@@ -71,7 +71,7 @@ export type ControlActionName =
|
|
|
71
71
|
| 'dbInit'
|
|
72
72
|
| 'dbUpdate'
|
|
73
73
|
| 'dbVerify'
|
|
74
|
-
| '
|
|
74
|
+
| 'migrate'
|
|
75
75
|
| 'verify'
|
|
76
76
|
| 'schemaVerify'
|
|
77
77
|
| 'sign'
|
|
@@ -540,17 +540,17 @@ export type EmitResult = Result<EmitSuccess, EmitFailure>;
|
|
|
540
540
|
// ============================================================================
|
|
541
541
|
|
|
542
542
|
/**
|
|
543
|
-
* Options for the aggregate-walking `
|
|
543
|
+
* Options for the aggregate-walking `migrate` operation.
|
|
544
544
|
*
|
|
545
545
|
* The control-api operation is responsible for: loading the
|
|
546
546
|
* contract-space aggregate, reading per-space marker rows from the
|
|
547
547
|
* live database, plotting per-space paths via `graphWalkStrategy`
|
|
548
548
|
* (replay-only — no synth, no introspection), and dispatching
|
|
549
|
-
* through the shared `
|
|
549
|
+
* through the shared `runMigration` primitive. The CLI command
|
|
550
550
|
* just resolves the descriptor surface (config, refs, contract
|
|
551
551
|
* envelope, app-space migration packages) and hands the inputs in.
|
|
552
552
|
*/
|
|
553
|
-
export interface
|
|
553
|
+
export interface MigrateOptions {
|
|
554
554
|
/** Already-validated app contract (the canonical "where we are heading" hash). */
|
|
555
555
|
readonly contract: unknown;
|
|
556
556
|
/** Migrations root directory (`migrations/` under the project). */
|
|
@@ -575,7 +575,7 @@ export interface MigrationApplyOptions {
|
|
|
575
575
|
*/
|
|
576
576
|
readonly refName?: string;
|
|
577
577
|
/**
|
|
578
|
-
* Database connection. If provided,
|
|
578
|
+
* Database connection. If provided, migrate will connect before executing.
|
|
579
579
|
* If omitted, the client must already be connected.
|
|
580
580
|
*/
|
|
581
581
|
readonly connection?: unknown;
|
|
@@ -619,7 +619,7 @@ export interface MigrationApplyStep {
|
|
|
619
619
|
* Per-space aggregate detail (markers, ops grouped by space) lives
|
|
620
620
|
* on `perSpace[]`; this list is the per-edge view.
|
|
621
621
|
*/
|
|
622
|
-
export interface
|
|
622
|
+
export interface MigrateRanEntry {
|
|
623
623
|
readonly spaceId: string;
|
|
624
624
|
readonly dirName: string;
|
|
625
625
|
readonly migrationHash: string;
|
|
@@ -629,13 +629,13 @@ export interface MigrationApplyAppliedEntry {
|
|
|
629
629
|
}
|
|
630
630
|
|
|
631
631
|
/**
|
|
632
|
-
* Successful
|
|
633
|
-
* (`markerHash` is the **app member's** post-
|
|
632
|
+
* Successful migrate result. Carries both the top-level fields
|
|
633
|
+
* (`markerHash` is the **app member's** post-migrate marker) and the
|
|
634
634
|
* per-space breakdown (`perSpace` — markers / operations in canonical
|
|
635
635
|
* schedule order).
|
|
636
636
|
*/
|
|
637
637
|
/**
|
|
638
|
-
* Path-decision summary for the **app member** post-
|
|
638
|
+
* Path-decision summary for the **app member** post-migrate. Surfaced
|
|
639
639
|
* at the top level (and consumed by the cli-journeys suite, which
|
|
640
640
|
* inspects `requiredInvariants`/`satisfiedInvariants`/
|
|
641
641
|
* `selectedPath` to validate invariant routing).
|
|
@@ -643,7 +643,7 @@ export interface MigrationApplyAppliedEntry {
|
|
|
643
643
|
* Per-space path decisions for extension members are not surfaced —
|
|
644
644
|
* extensions own their own ref/invariant control.
|
|
645
645
|
*/
|
|
646
|
-
export interface
|
|
646
|
+
export interface MigratePathDecision {
|
|
647
647
|
readonly fromHash: string;
|
|
648
648
|
readonly toHash: string;
|
|
649
649
|
readonly alternativeCount: number;
|
|
@@ -660,10 +660,10 @@ export interface MigrationApplyPathDecision {
|
|
|
660
660
|
}[];
|
|
661
661
|
}
|
|
662
662
|
|
|
663
|
-
export interface
|
|
663
|
+
export interface MigrateSuccess {
|
|
664
664
|
readonly migrationsApplied: number;
|
|
665
665
|
readonly markerHash: string;
|
|
666
|
-
readonly applied: readonly
|
|
666
|
+
readonly applied: readonly MigrateRanEntry[];
|
|
667
667
|
readonly summary: string;
|
|
668
668
|
/**
|
|
669
669
|
* Per-space breakdown in canonical schedule order (extensions
|
|
@@ -674,31 +674,31 @@ export interface MigrationApplySuccess {
|
|
|
674
674
|
/**
|
|
675
675
|
* Path-decision data for the app member. Present whenever the
|
|
676
676
|
* graph-walk strategy ran for the app (i.e. always for the
|
|
677
|
-
* aggregate-walking
|
|
677
|
+
* aggregate-walking migrate path). Absent only for the no-op
|
|
678
678
|
* "Already up to date" early return when the app has no plan.
|
|
679
679
|
*/
|
|
680
|
-
readonly pathDecision?:
|
|
680
|
+
readonly pathDecision?: MigratePathDecision;
|
|
681
681
|
}
|
|
682
682
|
|
|
683
683
|
/**
|
|
684
|
-
* Failure codes for
|
|
684
|
+
* Failure codes for migrate operation.
|
|
685
685
|
*/
|
|
686
|
-
export type
|
|
686
|
+
export type MigrateFailureCode = 'RUNNER_FAILED' | 'MIGRATION_PATH_NOT_FOUND';
|
|
687
687
|
|
|
688
688
|
/**
|
|
689
|
-
* Failure details for
|
|
689
|
+
* Failure details for migrate operation.
|
|
690
690
|
*/
|
|
691
|
-
export interface
|
|
692
|
-
readonly code:
|
|
691
|
+
export interface MigrateFailure {
|
|
692
|
+
readonly code: MigrateFailureCode;
|
|
693
693
|
readonly summary: string;
|
|
694
694
|
readonly why: string | undefined;
|
|
695
695
|
readonly meta: Record<string, unknown> | undefined;
|
|
696
696
|
}
|
|
697
697
|
|
|
698
698
|
/**
|
|
699
|
-
* Result type for
|
|
699
|
+
* Result type for migrate operation.
|
|
700
700
|
*/
|
|
701
|
-
export type
|
|
701
|
+
export type MigrateResult = Result<MigrateSuccess, MigrateFailure>;
|
|
702
702
|
|
|
703
703
|
// ============================================================================
|
|
704
704
|
// Standalone Contract Emit Types
|
|
@@ -892,17 +892,19 @@ export interface ControlClient {
|
|
|
892
892
|
readLedger(space?: string): Promise<readonly LedgerEntryRecord[]>;
|
|
893
893
|
|
|
894
894
|
/**
|
|
895
|
-
*
|
|
895
|
+
* Advances the database along the migration graph to the target contract.
|
|
896
896
|
* Each migration runs in its own transaction with full execution checks.
|
|
897
|
-
* Resume-safe: re-running after failure picks up from the last
|
|
897
|
+
* Resume-safe: re-running after failure picks up from the last run migration.
|
|
898
898
|
*
|
|
899
|
-
* @param options.
|
|
900
|
-
* @param options.
|
|
901
|
-
* @param options.
|
|
902
|
-
* @
|
|
899
|
+
* @param options.contract - The target contract to migrate to
|
|
900
|
+
* @param options.migrationsDir - Root migrations directory (`migrations/` under the project)
|
|
901
|
+
* @param options.refHash - Optional app-space ref override hash
|
|
902
|
+
* @param options.refInvariants - Required invariants on the user-supplied ref
|
|
903
|
+
* @param options.refName - Resolved name of the user-supplied app-space ref
|
|
904
|
+
* @returns Result pattern: Ok with migration details, NotOk with failure details
|
|
903
905
|
* @throws If not connected, target doesn't support migrations, or infrastructure failure
|
|
904
906
|
*/
|
|
905
|
-
|
|
907
|
+
migrate(options: MigrateOptions): Promise<MigrateResult>;
|
|
906
908
|
|
|
907
909
|
/**
|
|
908
910
|
* Introspects the database schema.
|