@prisma-next/cli 0.11.0-dev.3 → 0.11.0-dev.30
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/cli-errors-DFF1LlfU.mjs +215 -0
- package/dist/cli-errors-DFF1LlfU.mjs.map +1 -0
- package/dist/cli.mjs +8 -9
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-oXO2WCPD.mjs → client-a5NJce0-.mjs} +5 -5
- package/dist/{client-oXO2WCPD.mjs.map → client-a5NJce0-.mjs.map} +1 -1
- package/dist/{command-helpers-DtavI0wJ.mjs → command-helpers-BnqwTptC.mjs} +380 -6
- package/dist/command-helpers-BnqwTptC.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 +33 -7
- 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 -4
- 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 -7
- 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 +36 -8
- 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 +5 -1
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +44 -9
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.d.mts.map +1 -1
- package/dist/commands/migration-check.mjs +2 -3
- package/dist/commands/migration-check.mjs.map +1 -1
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +2 -3
- package/dist/commands/migration-graph.mjs.map +1 -1
- package/dist/commands/migration-list.d.mts +57 -13
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +2 -103
- package/dist/commands/migration-log.d.mts.map +1 -1
- package/dist/commands/migration-log.mjs +3 -4
- package/dist/commands/migration-log.mjs.map +1 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +3 -4
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +1 -0
- 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 +1 -1
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +6 -7
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +6 -7
- package/dist/commands/migration-status.mjs.map +1 -1
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.d.mts.map +1 -1
- package/dist/commands/ref.mjs +34 -9
- package/dist/commands/ref.mjs.map +1 -1
- package/dist/config-loader-B6sJjXTv.mjs.map +1 -1
- package/dist/config-loader.d.mts.map +1 -1
- package/dist/{contract-emit-bcrpT-wD.mjs → contract-emit-DYBHfZqL.mjs} +8 -7
- package/dist/contract-emit-DYBHfZqL.mjs.map +1 -0
- package/dist/{contract-emit-uwT-Mj8-.mjs → contract-emit-aFcOi3aw.mjs} +20 -14
- package/dist/contract-emit-aFcOi3aw.mjs.map +1 -0
- package/dist/{contract-enrichment-Dani0mMW.mjs → contract-enrichment-XmUPhmsS.mjs} +4 -25
- package/dist/contract-enrichment-XmUPhmsS.mjs.map +1 -0
- package/dist/{contract-infer-pKkiCt7C.mjs → contract-infer-BpJeg-Eu.mjs} +3 -4
- package/dist/{contract-infer-pKkiCt7C.mjs.map → contract-infer-BpJeg-Eu.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-BmNQwlws.mjs → contract-space-aggregate-loader-EVU3n9YE.mjs} +2 -2
- package/dist/{contract-space-aggregate-loader-BmNQwlws.mjs.map → contract-space-aggregate-loader-EVU3n9YE.mjs.map} +1 -1
- package/dist/{db-verify-AoIUriL4.mjs → db-verify-CxtdGiL3.mjs} +6 -7
- package/dist/{db-verify-AoIUriL4.mjs.map → db-verify-CxtdGiL3.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +1 -1
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +3 -3
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +1 -1
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{framework-components-65gOHkHB.mjs → framework-components-DTcjouhS.mjs} +2 -2
- package/dist/{framework-components-65gOHkHB.mjs.map → framework-components-DTcjouhS.mjs.map} +1 -1
- package/dist/global-flags-CdE7M0d9.d.mts.map +1 -1
- package/dist/graph-render-DJVv0_uf.mjs.map +1 -1
- package/dist/{init-YX6lCJpG.mjs → init-eGkSo7hi.mjs} +5 -5
- package/dist/{init-YX6lCJpG.mjs.map → init-eGkSo7hi.mjs.map} +1 -1
- package/dist/{inspect-live-schema-LeWvkZVz.mjs → inspect-live-schema-B1GCyjAJ.mjs} +5 -5
- package/dist/{inspect-live-schema-LeWvkZVz.mjs.map → inspect-live-schema-B1GCyjAJ.mjs.map} +1 -1
- package/dist/migration-cli.d.mts.map +1 -1
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-BtkunvFQ.mjs → migration-command-scaffold-CNdZl60X.mjs} +5 -5
- package/dist/{migration-command-scaffold-BtkunvFQ.mjs.map → migration-command-scaffold-CNdZl60X.mjs.map} +1 -1
- package/dist/migration-list-CnYiHrNV.mjs +288 -0
- package/dist/migration-list-CnYiHrNV.mjs.map +1 -0
- package/dist/{migration-plan-C2jeH1J5.mjs → migration-plan-BnmP_yNz.mjs} +346 -88
- package/dist/migration-plan-BnmP_yNz.mjs.map +1 -0
- package/dist/{migrations-CwZMa1Ck.mjs → migrations-C7YTBnLy.mjs} +11 -2
- package/dist/migrations-C7YTBnLy.mjs.map +1 -0
- package/dist/{output-BlsrGMEF.mjs → output-CUIdfYo5.mjs} +1 -1
- package/dist/{output-BlsrGMEF.mjs.map → output-CUIdfYo5.mjs.map} +1 -1
- package/dist/{progress-adapter-DFfvZcYL.mjs → progress-adapter-xASh41wr.mjs} +1 -1
- package/dist/{progress-adapter-DFfvZcYL.mjs.map → progress-adapter-xASh41wr.mjs.map} +1 -1
- package/dist/ref-advancement-CHJ_8HxQ.mjs +50 -0
- package/dist/ref-advancement-CHJ_8HxQ.mjs.map +1 -0
- package/dist/{types--CqjMdk0.d.mts → types-UWB2-rrw.d.mts} +12 -4
- package/dist/types-UWB2-rrw.d.mts.map +1 -0
- package/dist/{verify-Bom75OYI.mjs → verify-DX4RQwq4.mjs} +2 -2
- package/dist/{verify-Bom75OYI.mjs.map → verify-DX4RQwq4.mjs.map} +1 -1
- package/package.json +20 -20
- package/src/commands/contract-emit.ts +19 -7
- package/src/commands/db-init.ts +48 -2
- package/src/commands/db-update.ts +45 -0
- package/src/commands/migrate.ts +73 -3
- package/src/commands/migration-list.ts +145 -74
- package/src/commands/migration-plan.ts +365 -128
- package/src/commands/ref.ts +46 -6
- package/src/control-api/contract-enrichment.ts +6 -42
- package/src/control-api/operations/contract-emit.ts +7 -4
- package/src/control-api/types.ts +7 -0
- package/src/utils/cli-errors.ts +224 -0
- package/src/utils/formatters/migration-list-render.ts +171 -0
- package/src/utils/formatters/migration-list-styler.ts +56 -0
- package/src/utils/formatters/migrations.ts +25 -0
- package/src/utils/plan-resolution.ts +257 -0
- package/src/utils/ref-advancement.ts +68 -0
- package/dist/cli-errors-Czmx92Zy.d.mts +0 -3
- package/dist/cli-errors-Djtz98Vm.mjs +0 -71
- package/dist/cli-errors-Djtz98Vm.mjs.map +0 -1
- package/dist/command-helpers-DtavI0wJ.mjs.map +0 -1
- package/dist/commands/migration-list.mjs.map +0 -1
- package/dist/contract-emit-bcrpT-wD.mjs.map +0 -1
- package/dist/contract-emit-uwT-Mj8-.mjs.map +0 -1
- package/dist/contract-enrichment-Dani0mMW.mjs.map +0 -1
- package/dist/migration-plan-C2jeH1J5.mjs.map +0 -1
- package/dist/migrations-CwZMa1Ck.mjs.map +0 -1
- package/dist/terminal-ui-BiB_8KNo.mjs +0 -379
- package/dist/terminal-ui-BiB_8KNo.mjs.map +0 -1
- package/dist/types--CqjMdk0.d.mts.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import type { Contract } from '@prisma-next/contract/types';
|
|
3
3
|
import { getEmittedArtifactPaths } from '@prisma-next/emitter';
|
|
4
4
|
import {
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type MigrationPlanOperation,
|
|
9
9
|
type OperationPreview,
|
|
10
10
|
} from '@prisma-next/framework-components/control';
|
|
11
|
+
import { canonicalizeJson } from '@prisma-next/framework-components/utils';
|
|
11
12
|
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
12
13
|
import { computeMigrationHash } from '@prisma-next/migration-tools/hash';
|
|
13
14
|
import { deriveProvidedInvariants } from '@prisma-next/migration-tools/invariants';
|
|
@@ -17,10 +18,7 @@ import {
|
|
|
17
18
|
writeMigrationPackage,
|
|
18
19
|
} from '@prisma-next/migration-tools/io';
|
|
19
20
|
import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
|
|
20
|
-
import { findLatestMigration } from '@prisma-next/migration-tools/migration-graph';
|
|
21
21
|
import { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';
|
|
22
|
-
import { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
23
|
-
import { readRefs } from '@prisma-next/migration-tools/refs';
|
|
24
22
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
25
23
|
import { Command } from 'commander';
|
|
26
24
|
import { join, relative } from 'pathe';
|
|
@@ -34,7 +32,6 @@ import {
|
|
|
34
32
|
errorTargetMigrationNotSupported,
|
|
35
33
|
errorUnexpected,
|
|
36
34
|
mapMigrationToolsError,
|
|
37
|
-
mapRefResolutionError,
|
|
38
35
|
} from '../utils/cli-errors';
|
|
39
36
|
import {
|
|
40
37
|
addGlobalOptions,
|
|
@@ -52,6 +49,7 @@ import { formatStyledHeader } from '../utils/formatters/styled';
|
|
|
52
49
|
import { assertFrameworkComponentsCompatible } from '../utils/framework-components';
|
|
53
50
|
import type { CommonCommandOptions } from '../utils/global-flags';
|
|
54
51
|
import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
|
|
52
|
+
import { resolveFromForPlan } from '../utils/plan-resolution';
|
|
55
53
|
import { handleResult } from '../utils/result-handler';
|
|
56
54
|
import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
|
|
57
55
|
|
|
@@ -110,12 +108,128 @@ async function readPredecessorEndContract(
|
|
|
110
108
|
}
|
|
111
109
|
}
|
|
112
110
|
|
|
111
|
+
async function writeSnapshotContractArtifacts(
|
|
112
|
+
packageDir: string,
|
|
113
|
+
contractJson: unknown,
|
|
114
|
+
contractDts: string,
|
|
115
|
+
artifactBasename: 'start-contract' | 'end-contract',
|
|
116
|
+
): Promise<void> {
|
|
117
|
+
await mkdir(packageDir, { recursive: true });
|
|
118
|
+
const jsonContent = `${canonicalizeJson(contractJson)}\n`;
|
|
119
|
+
const dtsContent = contractDts.endsWith('\n') ? contractDts : `${contractDts}\n`;
|
|
120
|
+
await writeFile(join(packageDir, `${artifactBasename}.json`), jsonContent);
|
|
121
|
+
await writeFile(join(packageDir, `${artifactBasename}.d.ts`), dtsContent);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function writeSnapshotStartContract(
|
|
125
|
+
packageDir: string,
|
|
126
|
+
contractJson: unknown,
|
|
127
|
+
contractDts: string,
|
|
128
|
+
): Promise<void> {
|
|
129
|
+
await writeSnapshotContractArtifacts(packageDir, contractJson, contractDts, 'start-contract');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
type PlannerSuccess = {
|
|
133
|
+
readonly plannedOps: readonly MigrationPlanOperation[];
|
|
134
|
+
readonly migrationTsContent: string;
|
|
135
|
+
readonly hasPlaceholders: boolean;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
type TargetMigrationsApi = NonNullable<ReturnType<typeof getTargetMigrations>>;
|
|
139
|
+
|
|
140
|
+
async function runPlannerLeg(
|
|
141
|
+
planner: ReturnType<TargetMigrationsApi['createPlanner']>,
|
|
142
|
+
migrations: TargetMigrationsApi,
|
|
143
|
+
frameworkComponents: ReturnType<typeof assertFrameworkComponentsCompatible>,
|
|
144
|
+
contract: Contract,
|
|
145
|
+
fromContract: Contract | null,
|
|
146
|
+
spaceId: string,
|
|
147
|
+
): Promise<Result<PlannerSuccess, CliStructuredError>> {
|
|
148
|
+
const fromSchema = migrations.contractToSchema(fromContract, frameworkComponents);
|
|
149
|
+
const plannerResult = planner.plan({
|
|
150
|
+
contract,
|
|
151
|
+
schema: fromSchema,
|
|
152
|
+
policy: { allowedOperationClasses: ['additive', 'widening', 'destructive', 'data'] },
|
|
153
|
+
fromContract,
|
|
154
|
+
frameworkComponents,
|
|
155
|
+
spaceId,
|
|
156
|
+
});
|
|
157
|
+
if (plannerResult.kind === 'failure') {
|
|
158
|
+
return notOk(
|
|
159
|
+
errorMigrationPlanningFailed({
|
|
160
|
+
conflicts: plannerResult.conflicts as readonly CliErrorConflict[],
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let plannedOps: readonly MigrationPlanOperation[] = [];
|
|
166
|
+
let hasPlaceholders = false;
|
|
167
|
+
try {
|
|
168
|
+
plannedOps = plannerResult.plan.operations;
|
|
169
|
+
if (plannedOps.length === 0) {
|
|
170
|
+
return notOk(
|
|
171
|
+
errorMigrationPlanningFailed({
|
|
172
|
+
conflicts: [
|
|
173
|
+
{
|
|
174
|
+
kind: 'unsupportedChange',
|
|
175
|
+
summary:
|
|
176
|
+
'Contract changed but planner produced no operations. ' +
|
|
177
|
+
'This indicates unsupported or ignored changes.',
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
}),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
} catch (e) {
|
|
184
|
+
if (CliStructuredError.is(e) && e.domain === 'MIG' && e.code === '2001') {
|
|
185
|
+
hasPlaceholders = true;
|
|
186
|
+
} else {
|
|
187
|
+
throw e;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return ok({
|
|
192
|
+
plannedOps,
|
|
193
|
+
migrationTsContent: plannerResult.plan.renderTypeScript(),
|
|
194
|
+
hasPlaceholders,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function writePlannedMigrationPackage(
|
|
199
|
+
packageDir: string,
|
|
200
|
+
fromHash: string | null,
|
|
201
|
+
toHash: string,
|
|
202
|
+
createdAt: Date,
|
|
203
|
+
leg: PlannerSuccess,
|
|
204
|
+
): Promise<void> {
|
|
205
|
+
const opsForWrite = leg.hasPlaceholders ? [] : leg.plannedOps;
|
|
206
|
+
const metadataWithInvariants: Omit<MigrationMetadata, 'migrationHash'> = {
|
|
207
|
+
from: fromHash,
|
|
208
|
+
to: toHash,
|
|
209
|
+
hints: {
|
|
210
|
+
used: [],
|
|
211
|
+
applied: [],
|
|
212
|
+
plannerVersion: '2.0.0',
|
|
213
|
+
},
|
|
214
|
+
labels: [],
|
|
215
|
+
providedInvariants: deriveProvidedInvariants(opsForWrite),
|
|
216
|
+
createdAt: createdAt.toISOString(),
|
|
217
|
+
};
|
|
218
|
+
const metadata: MigrationMetadata = {
|
|
219
|
+
...metadataWithInvariants,
|
|
220
|
+
migrationHash: computeMigrationHash(metadataWithInvariants, opsForWrite),
|
|
221
|
+
};
|
|
222
|
+
await writeMigrationPackage(packageDir, metadata, opsForWrite);
|
|
223
|
+
await writeMigrationTs(packageDir, leg.migrationTsContent);
|
|
224
|
+
}
|
|
225
|
+
|
|
113
226
|
export interface MigrationPlanResult {
|
|
114
227
|
readonly ok: boolean;
|
|
115
228
|
readonly noOp: boolean;
|
|
116
229
|
readonly from: string | null;
|
|
117
230
|
readonly to: string;
|
|
118
231
|
readonly dir?: string;
|
|
232
|
+
readonly baselineDir?: string;
|
|
119
233
|
/**
|
|
120
234
|
* Extension-space migration packages materialised onto disk during this
|
|
121
235
|
* `plan` run. Each entry names a `migrations/<spaceId>/<dirName>/`
|
|
@@ -236,62 +350,64 @@ async function executeMigrationPlanCommand(
|
|
|
236
350
|
}
|
|
237
351
|
const toStorageHash = rawStorageHash;
|
|
238
352
|
|
|
239
|
-
|
|
353
|
+
const { refsDir } = resolveMigrationPaths(options.config, config);
|
|
354
|
+
|
|
240
355
|
let fromContract: Contract | null = null;
|
|
241
356
|
let fromHash: string | null = null;
|
|
242
357
|
let fromContractSourceDir: string | null = null;
|
|
358
|
+
let snapshotStartContract: { contractJson: unknown; contractDts: string } | null = null;
|
|
359
|
+
let isAutoBaseline = false;
|
|
243
360
|
|
|
244
361
|
try {
|
|
245
362
|
const { bundles, graph } = await loadMigrationPackages(appMigrationsDir);
|
|
246
363
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
364
|
+
const resolutionResult = await resolveFromForPlan({
|
|
365
|
+
optionsFrom: options.from,
|
|
366
|
+
refsDir,
|
|
367
|
+
bundles,
|
|
368
|
+
graph,
|
|
369
|
+
familyInstance,
|
|
370
|
+
readBundleEndContract: (migrationDir) =>
|
|
371
|
+
readPredecessorEndContract(migrationDir, familyInstance),
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
if (!resolutionResult.ok) {
|
|
375
|
+
return notOk(resolutionResult.failure);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
switch (resolutionResult.value.kind) {
|
|
379
|
+
case 'greenfield':
|
|
380
|
+
break;
|
|
381
|
+
case 'graph-node':
|
|
382
|
+
fromHash = resolutionResult.value.fromHash;
|
|
383
|
+
fromContract = resolutionResult.value.fromContract;
|
|
384
|
+
fromContractSourceDir = resolutionResult.value.sourceDir;
|
|
385
|
+
break;
|
|
386
|
+
case 'snapshot':
|
|
387
|
+
fromHash = resolutionResult.value.fromHash;
|
|
388
|
+
fromContract = resolutionResult.value.fromContract;
|
|
389
|
+
snapshotStartContract = {
|
|
390
|
+
contractJson: resolutionResult.value.contractJson,
|
|
391
|
+
contractDts: resolutionResult.value.contractDts,
|
|
392
|
+
};
|
|
393
|
+
break;
|
|
394
|
+
case 'auto-baseline':
|
|
395
|
+
fromHash = resolutionResult.value.fromHash;
|
|
396
|
+
fromContract = resolutionResult.value.fromContract;
|
|
397
|
+
snapshotStartContract = {
|
|
398
|
+
contractJson: resolutionResult.value.contractJson,
|
|
399
|
+
contractDts: resolutionResult.value.contractDts,
|
|
400
|
+
};
|
|
401
|
+
isAutoBaseline = true;
|
|
402
|
+
break;
|
|
280
403
|
}
|
|
281
404
|
} catch (error) {
|
|
282
405
|
if (MigrationToolsError.is(error)) {
|
|
283
406
|
return notOk(mapMigrationToolsError(error));
|
|
284
407
|
}
|
|
285
|
-
// `readPredecessorEndContract` raises a `CliStructuredError` directly
|
|
286
|
-
// for the missing-snapshot case so the operator gets a precise
|
|
287
|
-
// why/fix; pass it through unchanged rather than re-wrapping.
|
|
288
408
|
if (CliStructuredError.is(error)) {
|
|
289
409
|
return notOk(error);
|
|
290
410
|
}
|
|
291
|
-
// Wrap unexpected (non-MigrationToolsError) failures from the migration
|
|
292
|
-
// load phase in a structured CLI envelope. Letting them throw would
|
|
293
|
-
// bypass `handleResult()` and crash the command — see CLI structured-
|
|
294
|
-
// errors guideline (CliStructuredError + Result pattern).
|
|
295
411
|
const message = error instanceof Error ? error.message : String(error);
|
|
296
412
|
return notOk(
|
|
297
413
|
errorUnexpected(message, {
|
|
@@ -325,8 +441,10 @@ async function executeMigrationPlanCommand(
|
|
|
325
441
|
r.newMigrationDirs.map((dirName) => ({ spaceId: r.spaceId, dirName })),
|
|
326
442
|
);
|
|
327
443
|
|
|
328
|
-
// Check for no-op (same hash means no changes)
|
|
329
|
-
|
|
444
|
+
// Check for no-op (same hash means no changes). Auto-baseline is exempt:
|
|
445
|
+
// an empty graph with db ref at the current contract still needs a
|
|
446
|
+
// null → fromHash baseline bundle so migrate can anchor the marker.
|
|
447
|
+
if (fromHash === toStorageHash && !isAutoBaseline) {
|
|
330
448
|
const result: MigrationPlanResult = {
|
|
331
449
|
ok: true,
|
|
332
450
|
noOp: true,
|
|
@@ -375,92 +493,187 @@ async function executeMigrationPlanCommand(
|
|
|
375
493
|
[config.target, config.adapter, ...(config.extensionPacks ?? [])],
|
|
376
494
|
);
|
|
377
495
|
|
|
378
|
-
// Build manifest and write migration package
|
|
379
|
-
const timestamp = new Date();
|
|
380
|
-
const slug = options.name ?? 'migration';
|
|
381
|
-
const dirName = formatMigrationDirName(timestamp, slug);
|
|
382
|
-
const packageDir = join(appMigrationsDir, dirName);
|
|
383
|
-
|
|
384
|
-
const baseMetadata: Omit<MigrationMetadata, 'migrationHash' | 'providedInvariants'> = {
|
|
385
|
-
from: fromHash,
|
|
386
|
-
to: toStorageHash,
|
|
387
|
-
hints: {
|
|
388
|
-
used: [],
|
|
389
|
-
applied: [],
|
|
390
|
-
plannerVersion: '2.0.0',
|
|
391
|
-
},
|
|
392
|
-
labels: [],
|
|
393
|
-
createdAt: timestamp.toISOString(),
|
|
394
|
-
};
|
|
395
|
-
|
|
396
496
|
try {
|
|
397
497
|
const planner = migrations.createPlanner(familyInstance);
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
498
|
+
|
|
499
|
+
if (
|
|
500
|
+
isAutoBaseline &&
|
|
501
|
+
fromHash !== null &&
|
|
502
|
+
fromContract !== null &&
|
|
503
|
+
snapshotStartContract !== null
|
|
504
|
+
) {
|
|
505
|
+
const baselineTimestamp = new Date();
|
|
506
|
+
const deltaTimestamp = new Date(baselineTimestamp.getTime() + 60_000);
|
|
507
|
+
const baselineDirName = formatMigrationDirName(baselineTimestamp, 'baseline');
|
|
508
|
+
const deltaDirName = formatMigrationDirName(deltaTimestamp, options.name ?? 'migration');
|
|
509
|
+
const baselinePackageDir = join(appMigrationsDir, baselineDirName);
|
|
510
|
+
const deltaPackageDir = join(appMigrationsDir, deltaDirName);
|
|
511
|
+
|
|
512
|
+
const baselineLeg = await runPlannerLeg(
|
|
513
|
+
planner,
|
|
514
|
+
migrations,
|
|
515
|
+
frameworkComponents,
|
|
516
|
+
fromContract,
|
|
517
|
+
null,
|
|
518
|
+
aggregate.app.spaceId,
|
|
412
519
|
);
|
|
413
|
-
|
|
520
|
+
if (!baselineLeg.ok) {
|
|
521
|
+
return notOk(baselineLeg.failure);
|
|
522
|
+
}
|
|
414
523
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
524
|
+
await writePlannedMigrationPackage(
|
|
525
|
+
baselinePackageDir,
|
|
526
|
+
null,
|
|
527
|
+
fromHash,
|
|
528
|
+
baselineTimestamp,
|
|
529
|
+
baselineLeg.value,
|
|
530
|
+
);
|
|
531
|
+
await writeSnapshotContractArtifacts(
|
|
532
|
+
baselinePackageDir,
|
|
533
|
+
snapshotStartContract.contractJson,
|
|
534
|
+
snapshotStartContract.contractDts,
|
|
535
|
+
'end-contract',
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
if (fromHash === toStorageHash) {
|
|
539
|
+
const baselineOps = baselineLeg.value.hasPlaceholders ? [] : baselineLeg.value.plannedOps;
|
|
540
|
+
if (baselineLeg.value.hasPlaceholders) {
|
|
541
|
+
const baselineDir = relative(process.cwd(), baselinePackageDir);
|
|
542
|
+
const result: MigrationPlanResult = {
|
|
543
|
+
ok: true,
|
|
544
|
+
noOp: false,
|
|
545
|
+
from: fromHash,
|
|
546
|
+
to: toStorageHash,
|
|
547
|
+
dir: baselineDir,
|
|
548
|
+
baselineDir,
|
|
549
|
+
operations: [],
|
|
550
|
+
emittedExtensionDirs,
|
|
551
|
+
pendingPlaceholders: true,
|
|
552
|
+
summary:
|
|
553
|
+
'Planned baseline with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit',
|
|
554
|
+
timings: { total: Date.now() - startTime },
|
|
555
|
+
};
|
|
556
|
+
return ok(result);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const preview = hasOperationPreview(familyInstance)
|
|
560
|
+
? familyInstance.toOperationPreview(baselineOps)
|
|
561
|
+
: undefined;
|
|
562
|
+
const result: MigrationPlanResult = {
|
|
563
|
+
ok: true,
|
|
564
|
+
noOp: false,
|
|
565
|
+
from: fromHash,
|
|
566
|
+
to: toStorageHash,
|
|
567
|
+
baselineDir: relative(process.cwd(), baselinePackageDir),
|
|
568
|
+
operations: baselineOps.map((op) => ({
|
|
569
|
+
id: op.id,
|
|
570
|
+
label: op.label,
|
|
571
|
+
operationClass: op.operationClass,
|
|
572
|
+
})),
|
|
573
|
+
emittedExtensionDirs,
|
|
574
|
+
...(preview !== undefined ? { preview } : {}),
|
|
575
|
+
summary: buildAutoBaselinePlanSummary(0, emittedExtensionDirs.length),
|
|
576
|
+
timings: { total: Date.now() - startTime },
|
|
577
|
+
};
|
|
578
|
+
return ok(result);
|
|
437
579
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
580
|
+
|
|
581
|
+
const deltaLeg = await runPlannerLeg(
|
|
582
|
+
planner,
|
|
583
|
+
migrations,
|
|
584
|
+
frameworkComponents,
|
|
585
|
+
aggregate.app.contract,
|
|
586
|
+
fromContract,
|
|
587
|
+
aggregate.app.spaceId,
|
|
588
|
+
);
|
|
589
|
+
if (!deltaLeg.ok) {
|
|
590
|
+
return notOk(deltaLeg.failure);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
await writePlannedMigrationPackage(
|
|
594
|
+
deltaPackageDir,
|
|
595
|
+
fromHash,
|
|
596
|
+
toStorageHash,
|
|
597
|
+
deltaTimestamp,
|
|
598
|
+
deltaLeg.value,
|
|
599
|
+
);
|
|
600
|
+
const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
|
|
601
|
+
await copyFilesWithRename(deltaPackageDir, [
|
|
602
|
+
{ sourcePath: destinationArtifacts.jsonPath, destName: 'end-contract.json' },
|
|
603
|
+
{ sourcePath: destinationArtifacts.dtsPath, destName: 'end-contract.d.ts' },
|
|
604
|
+
]);
|
|
605
|
+
await writeSnapshotStartContract(
|
|
606
|
+
deltaPackageDir,
|
|
607
|
+
snapshotStartContract.contractJson,
|
|
608
|
+
snapshotStartContract.contractDts,
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
const deltaOps = deltaLeg.value.hasPlaceholders ? [] : deltaLeg.value.plannedOps;
|
|
612
|
+
if (deltaLeg.value.hasPlaceholders) {
|
|
613
|
+
const result: MigrationPlanResult = {
|
|
614
|
+
ok: true,
|
|
615
|
+
noOp: false,
|
|
616
|
+
from: fromHash,
|
|
617
|
+
to: toStorageHash,
|
|
618
|
+
dir: relative(process.cwd(), deltaPackageDir),
|
|
619
|
+
baselineDir: relative(process.cwd(), baselinePackageDir),
|
|
620
|
+
operations: [],
|
|
621
|
+
emittedExtensionDirs,
|
|
622
|
+
pendingPlaceholders: true,
|
|
623
|
+
summary:
|
|
624
|
+
'Planned baseline + migration with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit',
|
|
625
|
+
timings: { total: Date.now() - startTime },
|
|
626
|
+
};
|
|
627
|
+
return ok(result);
|
|
443
628
|
}
|
|
629
|
+
|
|
630
|
+
const preview = hasOperationPreview(familyInstance)
|
|
631
|
+
? familyInstance.toOperationPreview(deltaOps)
|
|
632
|
+
: undefined;
|
|
633
|
+
const result: MigrationPlanResult = {
|
|
634
|
+
ok: true,
|
|
635
|
+
noOp: false,
|
|
636
|
+
from: fromHash,
|
|
637
|
+
to: toStorageHash,
|
|
638
|
+
dir: relative(process.cwd(), deltaPackageDir),
|
|
639
|
+
baselineDir: relative(process.cwd(), baselinePackageDir),
|
|
640
|
+
operations: deltaOps.map((op) => ({
|
|
641
|
+
id: op.id,
|
|
642
|
+
label: op.label,
|
|
643
|
+
operationClass: op.operationClass,
|
|
644
|
+
})),
|
|
645
|
+
emittedExtensionDirs,
|
|
646
|
+
...(preview !== undefined ? { preview } : {}),
|
|
647
|
+
summary: buildAutoBaselinePlanSummary(deltaOps.length, emittedExtensionDirs.length),
|
|
648
|
+
timings: { total: Date.now() - startTime },
|
|
649
|
+
};
|
|
650
|
+
return ok(result);
|
|
444
651
|
}
|
|
445
652
|
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
653
|
+
const timestamp = new Date();
|
|
654
|
+
const slug = options.name ?? 'migration';
|
|
655
|
+
const dirName = formatMigrationDirName(timestamp, slug);
|
|
656
|
+
const packageDir = join(appMigrationsDir, dirName);
|
|
657
|
+
|
|
658
|
+
const deltaLeg = await runPlannerLeg(
|
|
659
|
+
planner,
|
|
660
|
+
migrations,
|
|
661
|
+
frameworkComponents,
|
|
662
|
+
aggregate.app.contract,
|
|
663
|
+
fromContract,
|
|
664
|
+
aggregate.app.spaceId,
|
|
665
|
+
);
|
|
666
|
+
if (!deltaLeg.ok) {
|
|
667
|
+
return notOk(deltaLeg.failure);
|
|
668
|
+
}
|
|
462
669
|
|
|
463
|
-
await
|
|
670
|
+
await writePlannedMigrationPackage(
|
|
671
|
+
packageDir,
|
|
672
|
+
fromHash,
|
|
673
|
+
toStorageHash,
|
|
674
|
+
timestamp,
|
|
675
|
+
deltaLeg.value,
|
|
676
|
+
);
|
|
464
677
|
const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
|
|
465
678
|
await copyFilesWithRename(packageDir, [
|
|
466
679
|
{ sourcePath: destinationArtifacts.jsonPath, destName: 'end-contract.json' },
|
|
@@ -474,10 +687,15 @@ async function executeMigrationPlanCommand(
|
|
|
474
687
|
{ sourcePath: sourceArtifacts.jsonPath, destName: 'start-contract.json' },
|
|
475
688
|
{ sourcePath: sourceArtifacts.dtsPath, destName: 'start-contract.d.ts' },
|
|
476
689
|
]);
|
|
690
|
+
} else if (snapshotStartContract !== null) {
|
|
691
|
+
await writeSnapshotStartContract(
|
|
692
|
+
packageDir,
|
|
693
|
+
snapshotStartContract.contractJson,
|
|
694
|
+
snapshotStartContract.contractDts,
|
|
695
|
+
);
|
|
477
696
|
}
|
|
478
|
-
await writeMigrationTs(packageDir, migrationTsContent);
|
|
479
697
|
|
|
480
|
-
if (hasPlaceholders) {
|
|
698
|
+
if (deltaLeg.value.hasPlaceholders) {
|
|
481
699
|
const result: MigrationPlanResult = {
|
|
482
700
|
ok: true,
|
|
483
701
|
noOp: false,
|
|
@@ -494,6 +712,7 @@ async function executeMigrationPlanCommand(
|
|
|
494
712
|
return ok(result);
|
|
495
713
|
}
|
|
496
714
|
|
|
715
|
+
const plannedOps = deltaLeg.value.plannedOps;
|
|
497
716
|
const preview = hasOperationPreview(familyInstance)
|
|
498
717
|
? familyInstance.toOperationPreview(plannedOps)
|
|
499
718
|
: undefined;
|
|
@@ -593,6 +812,17 @@ function buildPlanSummary(plannedOpsCount: number, emittedExtensionDirsCount: nu
|
|
|
593
812
|
return `${base}; materialised ${emittedExtensionDirsCount} ${noun}`;
|
|
594
813
|
}
|
|
595
814
|
|
|
815
|
+
function buildAutoBaselinePlanSummary(
|
|
816
|
+
deltaOpsCount: number,
|
|
817
|
+
emittedExtensionDirsCount: number,
|
|
818
|
+
): string {
|
|
819
|
+
const base = `Planned baseline + ${deltaOpsCount} operation(s)`;
|
|
820
|
+
if (emittedExtensionDirsCount === 0) return base;
|
|
821
|
+
const noun =
|
|
822
|
+
emittedExtensionDirsCount === 1 ? 'extension-space migration' : 'extension-space migrations';
|
|
823
|
+
return `${base}; materialised ${emittedExtensionDirsCount} ${noun}`;
|
|
824
|
+
}
|
|
825
|
+
|
|
596
826
|
export function formatMigrationPlanOutput(result: MigrationPlanResult, flags: GlobalFlags): string {
|
|
597
827
|
const lines: string[] = [];
|
|
598
828
|
const useColor = flags.color !== false;
|
|
@@ -672,6 +902,9 @@ export function formatMigrationPlanOutput(result: MigrationPlanResult, flags: Gl
|
|
|
672
902
|
|
|
673
903
|
lines.push(dim_(`from: ${result.from}`));
|
|
674
904
|
lines.push(dim_(`to: ${result.to}`));
|
|
905
|
+
if (result.baselineDir) {
|
|
906
|
+
lines.push(dim_(`Baseline → ${result.baselineDir}`));
|
|
907
|
+
}
|
|
675
908
|
if (result.dir) {
|
|
676
909
|
lines.push(dim_(`App space → ${result.dir}`));
|
|
677
910
|
}
|
|
@@ -689,8 +922,12 @@ export function formatMigrationPlanOutput(result: MigrationPlanResult, flags: Gl
|
|
|
689
922
|
// (`prisma-next migrate`) regardless of how many spaces were
|
|
690
923
|
// materialised — `db update` is a dev-time convenience, not the
|
|
691
924
|
// canonical replay step.
|
|
925
|
+
const reviewTarget =
|
|
926
|
+
result.baselineDir !== undefined && result.dir !== undefined
|
|
927
|
+
? `${result.baselineDir} and ${result.dir}`
|
|
928
|
+
: (result.baselineDir ?? result.dir ?? '<dir>');
|
|
692
929
|
lines.push(
|
|
693
|
-
`Next: review ${green_(
|
|
930
|
+
`Next: review ${green_(reviewTarget)} if needed, then run ${green_('prisma-next migrate')}.`,
|
|
694
931
|
);
|
|
695
932
|
|
|
696
933
|
if (result.preview && result.preview.statements.length > 0) {
|