@prisma-next/cli 0.12.0 → 0.13.0-dev.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/README.md +2 -2
- package/dist/cli.mjs +180 -163
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-KgJorIvG.mjs → client-CJzuo5wX.mjs} +222 -107
- package/dist/client-CJzuo5wX.mjs.map +1 -0
- package/dist/{command-helpers-Bbw1GbwL.mjs → command-helpers-DGMvGBeX.mjs} +318 -25
- package/dist/command-helpers-DGMvGBeX.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.d.mts.map +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +4 -5
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.d.mts.map +1 -1
- package/dist/commands/db-schema.mjs +3 -3
- package/dist/commands/db-schema.mjs.map +1 -1
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +6 -6
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +10 -7
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.d.mts.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +37 -3
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +298 -12
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.d.mts +55 -13
- package/dist/commands/migration-check.d.mts.map +1 -1
- package/dist/commands/migration-check.mjs +3 -2
- package/dist/commands/migration-graph.d.mts +17 -8
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +185 -2
- package/dist/commands/migration-graph.mjs.map +1 -0
- package/dist/commands/migration-list.d.mts +26 -27
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +2 -190
- package/dist/commands/migration-log.d.mts +9 -19
- package/dist/commands/migration-log.d.mts.map +1 -1
- package/dist/commands/migration-log.mjs +1 -137
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +6 -5
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +1 -1
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.d.mts +17 -21
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +24 -36
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +42 -144
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +3 -759
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.d.mts.map +1 -1
- package/dist/commands/ref.mjs +4 -4
- package/dist/commands/ref.mjs.map +1 -1
- package/dist/commands/telemetry/index.d.mts +7 -0
- package/dist/commands/telemetry/index.d.mts.map +1 -0
- package/dist/commands/telemetry/index.mjs +2 -0
- package/dist/{config-loader-B6sJjXTv.mjs → config-loader-p9JMrekQ.mjs} +1 -1
- package/dist/{config-loader-B6sJjXTv.mjs.map → config-loader-p9JMrekQ.mjs.map} +1 -1
- package/dist/config-loader.mjs +1 -1
- package/dist/{contract-at-errors-BxP-TOMl.mjs → contract-at-errors-CFXsstzm.mjs} +2 -2
- package/dist/{contract-at-errors-BxP-TOMl.mjs.map → contract-at-errors-CFXsstzm.mjs.map} +1 -1
- package/dist/{contract-emit-DxcGl4Uq.mjs → contract-emit-B_qriF8B.mjs} +5 -5
- package/dist/{contract-emit-DxcGl4Uq.mjs.map → contract-emit-B_qriF8B.mjs.map} +1 -1
- package/dist/{contract-emit-D-4jrNve.mjs → contract-emit-C8HmtboH.mjs} +12 -7
- package/dist/contract-emit-C8HmtboH.mjs.map +1 -0
- package/dist/{contract-enrichment-a0V5Y_mL.mjs → contract-enrichment-gn9sWbPw.mjs} +1 -1
- package/dist/{contract-enrichment-a0V5Y_mL.mjs.map → contract-enrichment-gn9sWbPw.mjs.map} +1 -1
- package/dist/{contract-infer-D8uEbJuu.mjs → contract-infer-BYT_ra_U.mjs} +5 -5
- package/dist/contract-infer-BYT_ra_U.mjs.map +1 -0
- package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs → contract-space-aggregate-loader-ClI1KN6d.mjs} +5 -5
- package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs.map → contract-space-aggregate-loader-ClI1KN6d.mjs.map} +1 -1
- package/dist/{db-verify-v_vUKXTU.mjs → db-verify-C24FKhb7.mjs} +6 -6
- package/dist/{db-verify-v_vUKXTU.mjs.map → db-verify-C24FKhb7.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +5 -3
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +3 -3
- package/dist/exports/index.mjs +1 -1
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.d.mts +1 -3
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{extension-pack-inputs-IDvjRCi3.mjs → extension-pack-inputs-1ySHqxKG.mjs} +1 -1
- package/dist/{extension-pack-inputs-IDvjRCi3.mjs.map → extension-pack-inputs-1ySHqxKG.mjs.map} +1 -1
- package/dist/{framework-components-fYXjz_in.mjs → framework-components-YVQHhPH7.mjs} +2 -2
- package/dist/{framework-components-fYXjz_in.mjs.map → framework-components-YVQHhPH7.mjs.map} +1 -1
- package/dist/{global-flags-DEHjV8_s.d.mts → global-flags-BpoOYtNZ.d.mts} +1 -1
- package/dist/{global-flags-DEHjV8_s.d.mts.map → global-flags-BpoOYtNZ.d.mts.map} +1 -1
- package/dist/{init-Cv9UzWL5.mjs → init-0HwB-Vh8.mjs} +5 -58
- package/dist/init-0HwB-Vh8.mjs.map +1 -0
- package/dist/{inspect-live-schema-C6ohV_oQ.mjs → inspect-live-schema-DF6IwcDl.mjs} +7 -5
- package/dist/inspect-live-schema-DF6IwcDl.mjs.map +1 -0
- package/dist/migration-check-soB5uZEQ.mjs +573 -0
- package/dist/migration-check-soB5uZEQ.mjs.map +1 -0
- package/dist/migration-cli.mjs +1 -1
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-CjvwO6at.mjs → migration-command-scaffold-DA-Lhx6o.mjs} +5 -5
- package/dist/{migration-command-scaffold-CjvwO6at.mjs.map → migration-command-scaffold-DA-Lhx6o.mjs.map} +1 -1
- package/dist/migration-graph-command-render-CEez7YUK.mjs +1960 -0
- package/dist/migration-graph-command-render-CEez7YUK.mjs.map +1 -0
- package/dist/migration-list-DlJJ_38Z.mjs +230 -0
- package/dist/migration-list-DlJJ_38Z.mjs.map +1 -0
- package/dist/migration-log-CG0qQAFm.mjs +222 -0
- package/dist/migration-log-CG0qQAFm.mjs.map +1 -0
- package/dist/migration-path-target-Ce6OZImp.mjs +38 -0
- package/dist/migration-path-target-Ce6OZImp.mjs.map +1 -0
- package/dist/{migration-plan-9DJ7q7_z.mjs → migration-plan-z5Ing-TD.mjs} +9 -8
- package/dist/migration-plan-z5Ing-TD.mjs.map +1 -0
- package/dist/migration-status-CgWSoI_g.mjs +446 -0
- package/dist/migration-status-CgWSoI_g.mjs.map +1 -0
- package/dist/{output-B60Gw5fu.mjs → output-mEQ74_nd.mjs} +1 -1
- package/dist/{output-B60Gw5fu.mjs.map → output-mEQ74_nd.mjs.map} +1 -1
- package/dist/{progress-adapter-C644QK8l.mjs → progress-adapter-CjAeTxY_.mjs} +1 -1
- package/dist/{progress-adapter-C644QK8l.mjs.map → progress-adapter-CjAeTxY_.mjs.map} +1 -1
- package/dist/{ref-advancement-DUZqsue6.mjs → ref-advancement-BkXlikCA.mjs} +1 -1
- package/dist/{ref-advancement-DUZqsue6.mjs.map → ref-advancement-BkXlikCA.mjs.map} +1 -1
- package/dist/schemas-CeGMYFYX.d.mts +191 -0
- package/dist/schemas-CeGMYFYX.d.mts.map +1 -0
- package/dist/schemas-KhXMzNA_.mjs +112 -0
- package/dist/schemas-KhXMzNA_.mjs.map +1 -0
- package/dist/telemetry-BIM4beEO.mjs +122 -0
- package/dist/telemetry-BIM4beEO.mjs.map +1 -0
- package/dist/{terminal-ui-5Y6mrg93.d.mts → terminal-ui-DGRNFWna.d.mts} +1 -1
- package/dist/terminal-ui-DGRNFWna.d.mts.map +1 -0
- package/dist/{types-Dt_SfqFm.d.mts → types-C_tYiJYx.d.mts} +53 -31
- package/dist/types-C_tYiJYx.d.mts.map +1 -0
- package/dist/{verify-DCA9Sldu.mjs → verify-DcOYZ1tH.mjs} +2 -2
- package/dist/{verify-DCA9Sldu.mjs.map → verify-DcOYZ1tH.mjs.map} +1 -1
- package/package.json +26 -22
- package/src/cli.ts +5 -0
- package/src/commands/contract-infer.ts +2 -2
- package/src/commands/db-update.ts +7 -1
- package/src/commands/init/index.ts +6 -35
- package/src/commands/init/init.ts +1 -14
- package/src/commands/init/inputs.ts +0 -75
- package/src/commands/inspect-live-schema.ts +10 -0
- package/src/commands/json/schemas.ts +195 -0
- package/src/commands/migrate.ts +527 -8
- package/src/commands/migration-check.ts +469 -134
- package/src/commands/migration-graph.ts +164 -91
- package/src/commands/migration-list.ts +72 -39
- package/src/commands/migration-log.ts +52 -102
- package/src/commands/migration-new.ts +2 -1
- package/src/commands/migration-plan.ts +2 -1
- package/src/commands/migration-show.ts +31 -66
- package/src/commands/migration-status-overlay.ts +61 -0
- package/src/commands/migration-status.ts +458 -1066
- package/src/commands/telemetry/index.ts +107 -0
- package/src/commands/telemetry/status.ts +67 -0
- package/src/control-api/client.ts +70 -9
- package/src/control-api/operations/contract-emit.ts +22 -2
- package/src/control-api/operations/db-init.ts +6 -3
- package/src/control-api/operations/{db-apply.ts → db-run.ts} +55 -14
- package/src/control-api/operations/db-update.ts +7 -4
- package/src/control-api/operations/db-verify.ts +15 -5
- package/src/control-api/operations/{migration-apply.ts → migrate.ts} +181 -80
- package/src/control-api/operations/{apply.ts → run-migration.ts} +33 -27
- package/src/control-api/types.ts +56 -29
- package/src/utils/cli-errors.ts +70 -2
- package/src/utils/formatters/errors.ts +11 -0
- package/src/utils/formatters/migration-graph-command-render.ts +239 -0
- package/src/utils/formatters/migration-graph-grid-layout.ts +1134 -0
- package/src/utils/formatters/migration-graph-labels.ts +408 -0
- package/src/utils/formatters/migration-graph-model.ts +103 -0
- package/src/utils/formatters/migration-graph-occlusion-render.ts +258 -0
- package/src/utils/formatters/migration-graph-rows.ts +128 -15
- package/src/utils/formatters/migration-graph-space-render.ts +188 -0
- package/src/utils/formatters/migration-list-data-column.ts +4 -91
- package/src/utils/formatters/migration-list-graph-topology.ts +72 -94
- package/src/utils/formatters/migration-list-render.ts +135 -71
- package/src/utils/formatters/migration-list-styler.ts +46 -5
- package/src/utils/formatters/migration-list-types.ts +5 -21
- package/src/utils/formatters/migration-log-table.ts +205 -0
- package/src/utils/formatters/migrations.ts +33 -11
- package/src/utils/global-flags.ts +35 -0
- package/src/utils/integrity-violation-to-check-failure.ts +28 -19
- package/src/utils/legend.ts +38 -0
- package/src/utils/migration-path-target.ts +60 -0
- package/src/utils/telemetry.ts +68 -32
- package/dist/client-KgJorIvG.mjs.map +0 -1
- package/dist/command-helpers-Bbw1GbwL.mjs.map +0 -1
- package/dist/commands/migration-list.mjs.map +0 -1
- package/dist/commands/migration-log.mjs.map +0 -1
- package/dist/commands/migration-status.mjs.map +0 -1
- package/dist/contract-emit-D-4jrNve.mjs.map +0 -1
- package/dist/contract-infer-D8uEbJuu.mjs.map +0 -1
- package/dist/graph-render-rFAqZujX.mjs +0 -1081
- package/dist/graph-render-rFAqZujX.mjs.map +0 -1
- package/dist/init-Cv9UzWL5.mjs.map +0 -1
- package/dist/inspect-live-schema-C6ohV_oQ.mjs.map +0 -1
- package/dist/migration-check-BiBJoYYW.mjs +0 -341
- package/dist/migration-check-BiBJoYYW.mjs.map +0 -1
- package/dist/migration-graph-D7DVUElV.mjs +0 -1232
- package/dist/migration-graph-D7DVUElV.mjs.map +0 -1
- package/dist/migration-list-styler-BRwF4-gy.mjs +0 -399
- package/dist/migration-list-styler-BRwF4-gy.mjs.map +0 -1
- package/dist/migration-plan-9DJ7q7_z.mjs.map +0 -1
- package/dist/migration-types-D2FW63pr.d.mts +0 -15
- package/dist/migration-types-D2FW63pr.d.mts.map +0 -1
- package/dist/migrations-Cv2jxNNK.mjs +0 -228
- package/dist/migrations-Cv2jxNNK.mjs.map +0 -1
- package/dist/terminal-ui-5Y6mrg93.d.mts.map +0 -1
- package/dist/types-Dt_SfqFm.d.mts.map +0 -1
- package/src/utils/formatters/graph-migration-mapper.ts +0 -235
- package/src/utils/formatters/graph-render.ts +0 -1323
- package/src/utils/formatters/graph-types.ts +0 -120
- package/src/utils/formatters/migration-graph-layout.ts +0 -1119
- package/src/utils/formatters/migration-graph-tree-render.ts +0 -459
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Contract } from '@prisma-next/contract/types';
|
|
2
2
|
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
3
3
|
import type {
|
|
4
|
+
ControlAdapterInstance,
|
|
4
5
|
ControlDriverInstance,
|
|
5
6
|
ControlExtensionDescriptor,
|
|
6
7
|
ControlFamilyInstance,
|
|
@@ -9,7 +10,7 @@ import type {
|
|
|
9
10
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
10
11
|
import { notOk } from '@prisma-next/utils/result';
|
|
11
12
|
import type { DbUpdateResult, OnControlProgress } from '../types';
|
|
12
|
-
import {
|
|
13
|
+
import { executeRun } from './db-run';
|
|
13
14
|
|
|
14
15
|
const DB_UPDATE_POLICY = {
|
|
15
16
|
allowedOperationClasses: ['additive', 'widening', 'destructive'] as const,
|
|
@@ -26,6 +27,7 @@ const DB_UPDATE_POLICY = {
|
|
|
26
27
|
*/
|
|
27
28
|
export interface ExecuteDbUpdateOptions<TFamilyId extends string, TTargetId extends string> {
|
|
28
29
|
readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;
|
|
30
|
+
readonly adapter: ControlAdapterInstance<TFamilyId, TTargetId>;
|
|
29
31
|
readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
|
|
30
32
|
readonly contract: Contract;
|
|
31
33
|
readonly mode: 'plan' | 'apply';
|
|
@@ -56,6 +58,7 @@ export async function executeDbUpdate<TFamilyId extends string, TTargetId extend
|
|
|
56
58
|
): Promise<DbUpdateResult> {
|
|
57
59
|
const sharedInputs = {
|
|
58
60
|
driver: options.driver,
|
|
61
|
+
adapter: options.adapter,
|
|
59
62
|
familyInstance: options.familyInstance,
|
|
60
63
|
contract: options.contract,
|
|
61
64
|
migrations: options.migrations,
|
|
@@ -71,7 +74,7 @@ export async function executeDbUpdate<TFamilyId extends string, TTargetId extend
|
|
|
71
74
|
const gate = await guardDestructiveChanges<TFamilyId, TTargetId>(sharedInputs);
|
|
72
75
|
if (gate !== null) return gate;
|
|
73
76
|
}
|
|
74
|
-
return (await
|
|
77
|
+
return (await executeRun<TFamilyId, TTargetId>({
|
|
75
78
|
...sharedInputs,
|
|
76
79
|
mode: options.mode,
|
|
77
80
|
})) as DbUpdateResult;
|
|
@@ -85,9 +88,9 @@ export async function executeDbUpdate<TFamilyId extends string, TTargetId extend
|
|
|
85
88
|
* run.
|
|
86
89
|
*/
|
|
87
90
|
async function guardDestructiveChanges<TFamilyId extends string, TTargetId extends string>(
|
|
88
|
-
sharedInputs: Omit<Parameters<typeof
|
|
91
|
+
sharedInputs: Omit<Parameters<typeof executeRun<TFamilyId, TTargetId>>[0], 'mode'>,
|
|
89
92
|
): Promise<DbUpdateResult | null> {
|
|
90
|
-
const planResult = (await
|
|
93
|
+
const planResult = (await executeRun<TFamilyId, TTargetId>({
|
|
91
94
|
...sharedInputs,
|
|
92
95
|
mode: 'plan',
|
|
93
96
|
})) as DbUpdateResult;
|
|
@@ -8,11 +8,12 @@ import type {
|
|
|
8
8
|
} from '@prisma-next/framework-components/control';
|
|
9
9
|
import {
|
|
10
10
|
type ContractSpaceMember,
|
|
11
|
+
collectAggregateNamespaces,
|
|
11
12
|
requireHeadRef,
|
|
12
13
|
type VerifierOutput,
|
|
13
14
|
verifyMigration,
|
|
14
15
|
} from '@prisma-next/migration-tools/aggregate';
|
|
15
|
-
import { castAs } from '@prisma-next/utils/casts';
|
|
16
|
+
import { blindCast, castAs } from '@prisma-next/utils/casts';
|
|
16
17
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
17
18
|
import { CliStructuredError } from '../../utils/cli-errors';
|
|
18
19
|
import {
|
|
@@ -101,7 +102,12 @@ export async function executeDbVerify<TFamilyId extends string, TTargetId extend
|
|
|
101
102
|
const markersBySpaceId = await familyInstance.readAllMarkers({ driver });
|
|
102
103
|
const schemaIntrospection = skipSchema
|
|
103
104
|
? null
|
|
104
|
-
: await runIntrospection({
|
|
105
|
+
: await runIntrospection({
|
|
106
|
+
driver,
|
|
107
|
+
familyInstance,
|
|
108
|
+
onProgress,
|
|
109
|
+
contract: collectAggregateNamespaces(aggregate),
|
|
110
|
+
});
|
|
105
111
|
|
|
106
112
|
emitVerifySpan(onProgress, 'spanStart');
|
|
107
113
|
const verifyResult = verifyMigration({
|
|
@@ -130,8 +136,9 @@ async function runIntrospection<TFamilyId extends string, TTargetId extends stri
|
|
|
130
136
|
driver: ControlDriverInstance<TFamilyId, TTargetId>;
|
|
131
137
|
familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
|
|
132
138
|
onProgress: OnControlProgress | undefined;
|
|
139
|
+
contract: unknown;
|
|
133
140
|
}): Promise<unknown> {
|
|
134
|
-
const { driver, familyInstance, onProgress } = args;
|
|
141
|
+
const { driver, familyInstance, onProgress, contract } = args;
|
|
135
142
|
onProgress?.({
|
|
136
143
|
action: 'dbVerify',
|
|
137
144
|
kind: 'spanStart',
|
|
@@ -139,7 +146,7 @@ async function runIntrospection<TFamilyId extends string, TTargetId extends stri
|
|
|
139
146
|
label: 'Introspecting database schema',
|
|
140
147
|
});
|
|
141
148
|
try {
|
|
142
|
-
const result = await familyInstance.introspect({ driver });
|
|
149
|
+
const result = await familyInstance.introspect({ driver, contract });
|
|
143
150
|
onProgress?.({
|
|
144
151
|
action: 'dbVerify',
|
|
145
152
|
kind: 'spanEnd',
|
|
@@ -179,7 +186,10 @@ export function createPerMemberVerifier<TFamilyId extends string, TTargetId exte
|
|
|
179
186
|
// The family's `TSchemaIR` is opaque to migration-tools; the
|
|
180
187
|
// aggregate verifier passes through whatever we hand it. The
|
|
181
188
|
// family expects its own IR shape on the way back.
|
|
182
|
-
schema:
|
|
189
|
+
schema: blindCast<
|
|
190
|
+
never,
|
|
191
|
+
'family TSchemaIR is opaque to migration-tools; projectedSchema is passed straight through'
|
|
192
|
+
>(projectedSchema),
|
|
183
193
|
strict: verifyMode === 'strict',
|
|
184
194
|
frameworkComponents,
|
|
185
195
|
});
|
|
@@ -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 {
|
|
@@ -7,6 +11,7 @@ import type {
|
|
|
7
11
|
TargetMigrationsCapability,
|
|
8
12
|
} from '@prisma-next/framework-components/control';
|
|
9
13
|
import {
|
|
14
|
+
buildSynthMigrationEdge,
|
|
10
15
|
type ContractMarkerRecordLike,
|
|
11
16
|
type ContractSpaceAggregate,
|
|
12
17
|
type ContractSpaceMember,
|
|
@@ -24,14 +29,14 @@ import {
|
|
|
24
29
|
buildContractSpaceAggregate,
|
|
25
30
|
} from '../../utils/contract-space-aggregate-loader';
|
|
26
31
|
import type {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
MigrateFailure,
|
|
33
|
+
MigratePathDecision,
|
|
34
|
+
MigrateResult,
|
|
35
|
+
MigrateSuccess,
|
|
31
36
|
OnControlProgress,
|
|
32
37
|
PerSpaceExecutionEntry,
|
|
33
38
|
} from '../types';
|
|
34
|
-
import {
|
|
39
|
+
import { buildPerSpaceBreakdown, runMigration } from './run-migration';
|
|
35
40
|
|
|
36
41
|
/**
|
|
37
42
|
* Inputs for the aggregate-walking `migrate` control-api
|
|
@@ -42,7 +47,7 @@ import { applyMigration, buildPerSpaceBreakdown } from './apply';
|
|
|
42
47
|
* is the single descriptor-free seam between the CLI and the
|
|
43
48
|
* aggregate runtime.
|
|
44
49
|
*/
|
|
45
|
-
export interface
|
|
50
|
+
export interface ExecuteMigrateOptions<TFamilyId extends string, TTargetId extends string> {
|
|
46
51
|
readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;
|
|
47
52
|
readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
|
|
48
53
|
/** Already-validated app contract (the canonical "where we are heading" hash). */
|
|
@@ -95,16 +100,16 @@ export interface ExecuteMigrationApplyOptions<TFamilyId extends string, TTargetI
|
|
|
95
100
|
* marker to `member.headRef.hash` (or `refHash` for the app
|
|
96
101
|
* member when provided). Empty-graph members fail loudly — a
|
|
97
102
|
* "never planned" space is a user-error condition for replay.
|
|
98
|
-
* 4. Hand off to {@link
|
|
103
|
+
* 4. Hand off to {@link runMigration} (the runner-driving tail
|
|
99
104
|
* shared with `db init` / `db update`). Marker advancement is
|
|
100
105
|
* inside the per-space transaction.
|
|
101
106
|
*
|
|
102
107
|
* Encodes the replay-only contract: every contract space must have an
|
|
103
108
|
* authored migration graph on disk before this operation can advance it.
|
|
104
109
|
*/
|
|
105
|
-
export async function
|
|
106
|
-
options:
|
|
107
|
-
): Promise<
|
|
110
|
+
export async function executeMigrate<TFamilyId extends string, TTargetId extends string>(
|
|
111
|
+
options: ExecuteMigrateOptions<TFamilyId, TTargetId>,
|
|
112
|
+
): Promise<MigrateResult> {
|
|
108
113
|
const {
|
|
109
114
|
driver,
|
|
110
115
|
familyInstance,
|
|
@@ -151,58 +156,36 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
|
|
|
151
156
|
// The aggregate passed the integrity gate, so every member's head ref
|
|
152
157
|
// is resolved (the app's is synthesised from the live contract).
|
|
153
158
|
const headRef = requireHeadRef(member);
|
|
154
|
-
const
|
|
159
|
+
const memberTargetHash = isAppMember && refHash !== undefined ? refHash : headRef.hash;
|
|
160
|
+
const memberRefInvariants = isAppMember && refHash !== undefined ? refInvariants : undefined;
|
|
155
161
|
const liveMarker = markerRows.get(member.spaceId) ?? null;
|
|
156
162
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// still surfaces the member in `perSpace[]` as already-at-head;
|
|
165
|
-
// the runner is not invoked for these members because they have
|
|
166
|
-
// no authored ops and (for greenfield extensions) no marker to
|
|
167
|
-
// advance.
|
|
168
|
-
const liveHash = liveMarker?.storageHash;
|
|
169
|
-
if (
|
|
170
|
-
targetHash === liveHash ||
|
|
171
|
-
(liveHash === undefined && targetHash === EMPTY_CONTRACT_HASH)
|
|
172
|
-
) {
|
|
173
|
-
atHeadResolutions.set(
|
|
174
|
-
member.spaceId,
|
|
175
|
-
buildAtHeadResolution({
|
|
176
|
-
aggregateTargetId: aggregate.targetId,
|
|
177
|
-
member,
|
|
178
|
-
targetHash,
|
|
179
|
-
liveMarker,
|
|
180
|
-
}),
|
|
181
|
-
);
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
return notOk(buildNeverPlannedFailure(member.spaceId, targetHash));
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const targetInvariants =
|
|
188
|
-
isAppMember && refHash !== undefined && refInvariants !== undefined
|
|
189
|
-
? refInvariants
|
|
190
|
-
: headRef.invariants;
|
|
191
|
-
const targetMember: ContractSpaceMember =
|
|
192
|
-
targetHash === headRef.hash && targetInvariants === headRef.invariants
|
|
193
|
-
? member
|
|
194
|
-
: { ...member, headRef: { hash: targetHash, invariants: targetInvariants } };
|
|
195
|
-
|
|
196
|
-
const walked = graphWalkStrategy({
|
|
197
|
-
aggregateTargetId: aggregate.targetId,
|
|
198
|
-
member: targetMember,
|
|
199
|
-
currentMarker: liveMarker,
|
|
200
|
-
...(isAppMember && refName !== undefined ? { refName } : {}),
|
|
163
|
+
const outcome = planMemberPath({
|
|
164
|
+
member,
|
|
165
|
+
aggregate,
|
|
166
|
+
targetHash: memberTargetHash,
|
|
167
|
+
refInvariants: memberRefInvariants,
|
|
168
|
+
liveMarker,
|
|
169
|
+
...(isAppMember ? { refName } : {}),
|
|
201
170
|
});
|
|
202
|
-
|
|
203
|
-
|
|
171
|
+
|
|
172
|
+
if (outcome.kind === 'at-head') {
|
|
173
|
+
// Empty-graph member whose live marker already matches the target.
|
|
174
|
+
// Kept out of the runner schedule so we don't write spurious markers
|
|
175
|
+
// for greenfield extensions, but merged back into the success envelope
|
|
176
|
+
// so every loaded member is represented.
|
|
177
|
+
atHeadResolutions.set(member.spaceId, outcome.plan);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (outcome.kind === 'never-planned') {
|
|
181
|
+
return notOk(buildNeverPlannedFailure(outcome.spaceId, outcome.targetHash));
|
|
182
|
+
}
|
|
183
|
+
if (outcome.kind === 'unreachable') {
|
|
184
|
+
return notOk(
|
|
185
|
+
buildPathNotFoundFailure(outcome.spaceId, outcome.liveMarker, outcome.targetHash),
|
|
186
|
+
);
|
|
204
187
|
}
|
|
205
|
-
if (
|
|
188
|
+
if (outcome.kind === 'unsatisfiable') {
|
|
206
189
|
// Surface the canonical MIGRATION.NO_INVARIANT_PATH envelope
|
|
207
190
|
// (the error rendering pipeline maps it to meta.code +
|
|
208
191
|
// meta.required + meta.missing + meta.structuralPath that the
|
|
@@ -213,10 +196,12 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
|
|
|
213
196
|
// string here would leave the structural lookup with a hash that
|
|
214
197
|
// is never a graph node, producing an empty `structuralPath` and
|
|
215
198
|
// a less actionable diagnostic.
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
199
|
+
const structural = findPathWithDecision(
|
|
200
|
+
outcome.targetMember.graph(),
|
|
201
|
+
outcome.liveHash,
|
|
202
|
+
memberTargetHash,
|
|
203
|
+
{ required: new Set<string>() },
|
|
204
|
+
);
|
|
220
205
|
const structuralPath =
|
|
221
206
|
structural.kind === 'ok'
|
|
222
207
|
? structural.decision.selectedPath.map((edge) => ({
|
|
@@ -228,14 +213,14 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
|
|
|
228
213
|
}))
|
|
229
214
|
: [];
|
|
230
215
|
throw errorNoInvariantPath({
|
|
231
|
-
...(
|
|
232
|
-
required: targetInvariants,
|
|
233
|
-
missing:
|
|
216
|
+
...(outcome.refName !== undefined ? { refName: outcome.refName } : {}),
|
|
217
|
+
required: outcome.targetInvariants,
|
|
218
|
+
missing: outcome.missing,
|
|
234
219
|
structuralPath,
|
|
235
220
|
});
|
|
236
221
|
}
|
|
237
222
|
|
|
238
|
-
perSpacePlans.set(member.spaceId,
|
|
223
|
+
perSpacePlans.set(member.spaceId, outcome.plan);
|
|
239
224
|
}
|
|
240
225
|
|
|
241
226
|
const canonicalOrder = [...aggregate.extensions.map((m) => m.spaceId), aggregate.app.spaceId];
|
|
@@ -276,7 +261,7 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
|
|
|
276
261
|
);
|
|
277
262
|
}
|
|
278
263
|
|
|
279
|
-
const applied = await
|
|
264
|
+
const applied = await runMigration({
|
|
280
265
|
aggregate,
|
|
281
266
|
perSpacePlans,
|
|
282
267
|
applyOrder,
|
|
@@ -285,12 +270,12 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
|
|
|
285
270
|
migrations,
|
|
286
271
|
frameworkComponents,
|
|
287
272
|
policy: { allowedOperationClasses: ['additive', 'widening', 'destructive', 'data'] },
|
|
288
|
-
action: '
|
|
273
|
+
action: 'migrate',
|
|
289
274
|
...ifDefined('onProgress', onProgress),
|
|
290
275
|
});
|
|
291
276
|
|
|
292
277
|
if (!applied.ok) {
|
|
293
|
-
const failure:
|
|
278
|
+
const failure: MigrateFailure = {
|
|
294
279
|
code: 'RUNNER_FAILED',
|
|
295
280
|
summary: applied.failure.summary,
|
|
296
281
|
why: applied.failure.why,
|
|
@@ -319,7 +304,7 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
|
|
|
319
304
|
includeMarkers: true,
|
|
320
305
|
});
|
|
321
306
|
const totalMigrationsApplied = applied.value.orderedResolutions.reduce(
|
|
322
|
-
(sum, r) => sum +
|
|
307
|
+
(sum, r) => sum + r.entry.migrationEdges.length,
|
|
323
308
|
0,
|
|
324
309
|
);
|
|
325
310
|
const summary = `Applied ${totalMigrationsApplied} migration(s) (${applied.value.totalOpsExecuted} operation(s)) across ${orderedAll.length} contract space(s)`;
|
|
@@ -335,6 +320,119 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
|
|
|
335
320
|
);
|
|
336
321
|
}
|
|
337
322
|
|
|
323
|
+
/**
|
|
324
|
+
* Outcome variants for one member's path computation.
|
|
325
|
+
*
|
|
326
|
+
* Callers switch on `kind` and map to their own error representation:
|
|
327
|
+
* `executeMigrate` throws / returns `notOk`; `executeMigrateShowCommand`
|
|
328
|
+
* returns a CLI structured error. The shared discriminant guarantees both
|
|
329
|
+
* paths feed `graphWalkStrategy` the same inputs.
|
|
330
|
+
*
|
|
331
|
+
* @internal Exported for `executeMigrateShowCommand` to call.
|
|
332
|
+
*/
|
|
333
|
+
export type MemberPathOutcome =
|
|
334
|
+
| { readonly kind: 'ok'; readonly plan: PerSpacePlan }
|
|
335
|
+
| { readonly kind: 'at-head'; readonly plan: PerSpacePlan }
|
|
336
|
+
| { readonly kind: 'never-planned'; readonly spaceId: string; readonly targetHash: string }
|
|
337
|
+
| {
|
|
338
|
+
readonly kind: 'unreachable';
|
|
339
|
+
readonly spaceId: string;
|
|
340
|
+
readonly liveMarker: ContractMarkerRecordLike | null;
|
|
341
|
+
readonly targetHash: string;
|
|
342
|
+
}
|
|
343
|
+
| {
|
|
344
|
+
readonly kind: 'unsatisfiable';
|
|
345
|
+
readonly spaceId: string;
|
|
346
|
+
readonly isAppMember: boolean;
|
|
347
|
+
readonly missing: readonly string[];
|
|
348
|
+
readonly targetInvariants: readonly string[];
|
|
349
|
+
readonly targetMember: ContractSpaceMember;
|
|
350
|
+
readonly liveHash: string;
|
|
351
|
+
readonly refName: string | undefined;
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Compute the graph-walk path for one contract-space member.
|
|
356
|
+
*
|
|
357
|
+
* Encapsulates the invariant-correct input assembly that both
|
|
358
|
+
* `executeMigrate` and `executeMigrateShowCommand` must use:
|
|
359
|
+
* - `currentMarker` carries the full live marker including `invariants`
|
|
360
|
+
* (not a stripped `{ storageHash, invariants: [] }` shell).
|
|
361
|
+
* - `targetInvariants` uses the caller-supplied `refInvariants` when a
|
|
362
|
+
* `--to` ref was resolved (not always the file head ref's invariants).
|
|
363
|
+
*
|
|
364
|
+
* Both callers map the returned `MemberPathOutcome` to their own error
|
|
365
|
+
* representation; the path-compute logic is shared and identical.
|
|
366
|
+
*
|
|
367
|
+
* @internal Exported for `executeMigrateShowCommand`.
|
|
368
|
+
*/
|
|
369
|
+
export function planMemberPath({
|
|
370
|
+
member,
|
|
371
|
+
aggregate,
|
|
372
|
+
targetHash,
|
|
373
|
+
refInvariants,
|
|
374
|
+
liveMarker,
|
|
375
|
+
refName,
|
|
376
|
+
}: {
|
|
377
|
+
readonly member: ContractSpaceMember;
|
|
378
|
+
readonly aggregate: Pick<ContractSpaceAggregate, 'targetId' | 'app'>;
|
|
379
|
+
readonly targetHash: string;
|
|
380
|
+
readonly refInvariants: readonly string[] | undefined;
|
|
381
|
+
readonly liveMarker: ContractMarkerRecordLike | null;
|
|
382
|
+
readonly refName?: string;
|
|
383
|
+
}): MemberPathOutcome {
|
|
384
|
+
const isAppMember = member.spaceId === aggregate.app.spaceId;
|
|
385
|
+
const headRef = requireHeadRef(member);
|
|
386
|
+
|
|
387
|
+
if (member.graph().nodes.size === 0) {
|
|
388
|
+
const liveHash = liveMarker?.storageHash;
|
|
389
|
+
if (targetHash === liveHash || (liveHash === undefined && targetHash === EMPTY_CONTRACT_HASH)) {
|
|
390
|
+
return {
|
|
391
|
+
kind: 'at-head',
|
|
392
|
+
plan: buildAtHeadResolution({
|
|
393
|
+
aggregateTargetId: aggregate.targetId,
|
|
394
|
+
member,
|
|
395
|
+
targetHash,
|
|
396
|
+
liveMarker,
|
|
397
|
+
}),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
return { kind: 'never-planned', spaceId: member.spaceId, targetHash };
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const targetInvariants =
|
|
404
|
+
isAppMember && refInvariants !== undefined ? refInvariants : headRef.invariants;
|
|
405
|
+
const targetMember: ContractSpaceMember =
|
|
406
|
+
targetHash === headRef.hash && targetInvariants === headRef.invariants
|
|
407
|
+
? member
|
|
408
|
+
: { ...member, headRef: { hash: targetHash, invariants: targetInvariants } };
|
|
409
|
+
|
|
410
|
+
const walked = graphWalkStrategy({
|
|
411
|
+
aggregateTargetId: aggregate.targetId,
|
|
412
|
+
member: targetMember,
|
|
413
|
+
currentMarker: liveMarker,
|
|
414
|
+
...(isAppMember && refName !== undefined ? { refName } : {}),
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
if (walked.kind === 'unreachable') {
|
|
418
|
+
return { kind: 'unreachable', spaceId: member.spaceId, liveMarker, targetHash };
|
|
419
|
+
}
|
|
420
|
+
if (walked.kind === 'unsatisfiable') {
|
|
421
|
+
const liveHash = liveMarker?.storageHash ?? EMPTY_CONTRACT_HASH;
|
|
422
|
+
return {
|
|
423
|
+
kind: 'unsatisfiable',
|
|
424
|
+
spaceId: member.spaceId,
|
|
425
|
+
isAppMember,
|
|
426
|
+
missing: walked.missing,
|
|
427
|
+
targetInvariants,
|
|
428
|
+
targetMember,
|
|
429
|
+
liveHash,
|
|
430
|
+
refName,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
return { kind: 'ok', plan: walked.result };
|
|
434
|
+
}
|
|
435
|
+
|
|
338
436
|
/**
|
|
339
437
|
* Build a zero-op {@link PerSpacePlan} for an empty-graph
|
|
340
438
|
* member whose live marker already matches the target. Lets the apply
|
|
@@ -361,7 +459,13 @@ function buildAtHeadResolution(args: {
|
|
|
361
459
|
displayOps: [],
|
|
362
460
|
destinationContract: member.contract(),
|
|
363
461
|
strategy: 'graph-walk',
|
|
364
|
-
migrationEdges: [
|
|
462
|
+
migrationEdges: [
|
|
463
|
+
buildSynthMigrationEdge({
|
|
464
|
+
currentMarkerStorageHash: liveMarker?.storageHash,
|
|
465
|
+
destinationStorageHash: targetHash,
|
|
466
|
+
operationCount: 0,
|
|
467
|
+
}),
|
|
468
|
+
],
|
|
365
469
|
};
|
|
366
470
|
}
|
|
367
471
|
|
|
@@ -389,9 +493,9 @@ interface BuildSuccessArgs {
|
|
|
389
493
|
readonly summary: string;
|
|
390
494
|
}
|
|
391
495
|
|
|
392
|
-
function buildSuccess(args: BuildSuccessArgs):
|
|
496
|
+
function buildSuccess(args: BuildSuccessArgs): MigrateSuccess {
|
|
393
497
|
// The marker hash surfaced at the top level is the **app member's**
|
|
394
|
-
// post-
|
|
498
|
+
// post-migrate marker (the top-level `markerHash` field).
|
|
395
499
|
// Per-space markers live on `perSpace[].marker.storageHash`.
|
|
396
500
|
const appResolution = args.orderedResolutions.find(
|
|
397
501
|
(r) => r.spaceId === args.aggregate.app.spaceId,
|
|
@@ -404,7 +508,7 @@ function buildSuccess(args: BuildSuccessArgs): MigrationApplySuccess {
|
|
|
404
508
|
// JSON-shape consumers (e.g. `parsed.applied.length` in integration
|
|
405
509
|
// tests). The aggregate per-space breakdown lives on `perSpace[]`.
|
|
406
510
|
const applied = args.orderedResolutions.flatMap((r) => {
|
|
407
|
-
const edges = r.entry.migrationEdges
|
|
511
|
+
const edges = r.entry.migrationEdges;
|
|
408
512
|
return edges.map((edge) => ({
|
|
409
513
|
spaceId: r.spaceId,
|
|
410
514
|
dirName: edge.dirName,
|
|
@@ -416,7 +520,7 @@ function buildSuccess(args: BuildSuccessArgs): MigrationApplySuccess {
|
|
|
416
520
|
});
|
|
417
521
|
|
|
418
522
|
const appPlan = appResolution?.entry;
|
|
419
|
-
const pathDecision:
|
|
523
|
+
const pathDecision: MigratePathDecision | undefined = appPlan?.pathDecision
|
|
420
524
|
? {
|
|
421
525
|
fromHash: appPlan.pathDecision.fromHash,
|
|
422
526
|
toHash: appPlan.pathDecision.toHash,
|
|
@@ -455,10 +559,7 @@ function buildSuccess(args: BuildSuccessArgs): MigrationApplySuccess {
|
|
|
455
559
|
*
|
|
456
560
|
* @internal Exported for testing only.
|
|
457
561
|
*/
|
|
458
|
-
export function buildNeverPlannedFailure(
|
|
459
|
-
spaceId: string,
|
|
460
|
-
targetHash: string,
|
|
461
|
-
): MigrationApplyFailure {
|
|
562
|
+
export function buildNeverPlannedFailure(spaceId: string, targetHash: string): MigrateFailure {
|
|
462
563
|
return {
|
|
463
564
|
code: 'MIGRATION_PATH_NOT_FOUND',
|
|
464
565
|
summary: `No on-disk migrations for contract space "${spaceId}"`,
|
|
@@ -481,7 +582,7 @@ export function buildPathNotFoundFailure(
|
|
|
481
582
|
spaceId: string,
|
|
482
583
|
marker: ContractMarkerRecordLike | null,
|
|
483
584
|
targetHash: string,
|
|
484
|
-
):
|
|
585
|
+
): MigrateFailure {
|
|
485
586
|
const fromHash = marker?.storageHash ?? '<empty>';
|
|
486
587
|
// The app-case phrasing names the user-visible condition (a
|
|
487
588
|
// 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
|
|
|
@@ -141,6 +146,7 @@ export async function applyMigration<TFamilyId extends string, TTargetId extends
|
|
|
141
146
|
destinationContract: r.entry.destinationContract,
|
|
142
147
|
policy,
|
|
143
148
|
frameworkComponents,
|
|
149
|
+
migrationEdges: r.entry.migrationEdges,
|
|
144
150
|
// Per-space post-apply schema verification is non-strict: each
|
|
145
151
|
// space's `destinationContract` describes only its own slice; a
|
|
146
152
|
// strict verifier would treat every other space's tables as
|
|
@@ -151,7 +157,7 @@ export async function applyMigration<TFamilyId extends string, TTargetId extends
|
|
|
151
157
|
const runnerResult = await runner.execute({ driver, perSpaceOptions });
|
|
152
158
|
|
|
153
159
|
if (!runnerResult.ok) {
|
|
154
|
-
onProgress?.({ action, kind: 'spanEnd', spanId:
|
|
160
|
+
onProgress?.({ action, kind: 'spanEnd', spanId: RUN_SPAN_ID, outcome: 'error' });
|
|
155
161
|
return notOk({
|
|
156
162
|
summary: runnerResult.failure.summary,
|
|
157
163
|
...ifDefined('why', runnerResult.failure.why),
|
|
@@ -162,7 +168,7 @@ export async function applyMigration<TFamilyId extends string, TTargetId extends
|
|
|
162
168
|
},
|
|
163
169
|
});
|
|
164
170
|
}
|
|
165
|
-
onProgress?.({ action, kind: 'spanEnd', spanId:
|
|
171
|
+
onProgress?.({ action, kind: 'spanEnd', spanId: RUN_SPAN_ID, outcome: 'ok' });
|
|
166
172
|
|
|
167
173
|
const totalOpsPlanned = runnerResult.value.perSpaceResults.reduce(
|
|
168
174
|
(sum, r) => sum + r.value.operationsPlanned,
|
|
@@ -194,7 +200,7 @@ export async function applyMigration<TFamilyId extends string, TTargetId extends
|
|
|
194
200
|
* advances as the last step of each space's transaction) and `false`
|
|
195
201
|
* for plan-mode (no marker has been written yet).
|
|
196
202
|
*
|
|
197
|
-
* Exported alongside {@link
|
|
203
|
+
* Exported alongside {@link runMigration} so plan-mode callers can
|
|
198
204
|
* assemble the same per-space block without going through the runner.
|
|
199
205
|
*/
|
|
200
206
|
export function buildPerSpaceBreakdown(
|
|
@@ -243,18 +249,18 @@ export function collectOrdered(
|
|
|
243
249
|
}
|
|
244
250
|
|
|
245
251
|
/**
|
|
246
|
-
* Action-appropriate label for the `spanStart` event the
|
|
247
|
-
* primitive emits. `
|
|
252
|
+
* Action-appropriate label for the `spanStart` event the run
|
|
253
|
+
* primitive emits. `runMigration` is shared by `db init`, `db update`,
|
|
248
254
|
* and `migrate`; the span label tracks the user-visible action
|
|
249
255
|
* so structured-progress output reads naturally for each surface.
|
|
250
256
|
*/
|
|
251
|
-
export function progressLabelForAction(action:
|
|
257
|
+
export function progressLabelForAction(action: RunAction): string {
|
|
252
258
|
switch (action) {
|
|
253
259
|
case 'dbInit':
|
|
254
260
|
return 'Initialising database across spaces';
|
|
255
261
|
case 'dbUpdate':
|
|
256
262
|
return 'Updating database across spaces';
|
|
257
|
-
case '
|
|
258
|
-
return '
|
|
263
|
+
case 'migrate':
|
|
264
|
+
return 'Running migration plan across spaces';
|
|
259
265
|
}
|
|
260
266
|
}
|