@prisma-next/cli 0.11.0-dev.9 → 0.12.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.
Files changed (186) hide show
  1. package/README.md +13 -9
  2. package/dist/cli.mjs +9 -10
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/{client-UnIveZxZ.mjs → client-KgJorIvG.mjs} +72 -60
  5. package/dist/client-KgJorIvG.mjs.map +1 -0
  6. package/dist/{command-helpers-CRfjbZRz.mjs → command-helpers-Bbw1GbwL.mjs} +642 -45
  7. package/dist/command-helpers-Bbw1GbwL.mjs.map +1 -0
  8. package/dist/commands/contract-emit.d.mts.map +1 -1
  9. package/dist/commands/contract-emit.mjs +1 -1
  10. package/dist/commands/contract-infer.d.mts.map +1 -1
  11. package/dist/commands/contract-infer.mjs +1 -1
  12. package/dist/commands/db-init.d.mts.map +1 -1
  13. package/dist/commands/db-init.mjs +5 -7
  14. package/dist/commands/db-init.mjs.map +1 -1
  15. package/dist/commands/db-schema.d.mts.map +1 -1
  16. package/dist/commands/db-schema.mjs +3 -4
  17. package/dist/commands/db-schema.mjs.map +1 -1
  18. package/dist/commands/db-sign.d.mts.map +1 -1
  19. package/dist/commands/db-sign.mjs +12 -10
  20. package/dist/commands/db-sign.mjs.map +1 -1
  21. package/dist/commands/db-update.d.mts.map +1 -1
  22. package/dist/commands/db-update.mjs +12 -11
  23. package/dist/commands/db-update.mjs.map +1 -1
  24. package/dist/commands/db-verify.d.mts.map +1 -1
  25. package/dist/commands/db-verify.mjs +1 -1
  26. package/dist/commands/migrate.d.mts +2 -2
  27. package/dist/commands/migrate.d.mts.map +1 -1
  28. package/dist/commands/migrate.mjs +68 -69
  29. package/dist/commands/migrate.mjs.map +1 -1
  30. package/dist/commands/migration-check.d.mts +4 -3
  31. package/dist/commands/migration-check.d.mts.map +1 -1
  32. package/dist/commands/migration-check.mjs +1 -280
  33. package/dist/commands/migration-graph.d.mts +13 -2
  34. package/dist/commands/migration-graph.d.mts.map +1 -1
  35. package/dist/commands/migration-graph.mjs +2 -137
  36. package/dist/commands/migration-list.d.mts +64 -4
  37. package/dist/commands/migration-list.d.mts.map +1 -1
  38. package/dist/commands/migration-list.mjs +143 -56
  39. package/dist/commands/migration-list.mjs.map +1 -1
  40. package/dist/commands/migration-log.d.mts +10 -1
  41. package/dist/commands/migration-log.d.mts.map +1 -1
  42. package/dist/commands/migration-log.mjs +10 -15
  43. package/dist/commands/migration-log.mjs.map +1 -1
  44. package/dist/commands/migration-new.d.mts.map +1 -1
  45. package/dist/commands/migration-new.mjs +32 -38
  46. package/dist/commands/migration-new.mjs.map +1 -1
  47. package/dist/commands/migration-plan.d.mts +2 -2
  48. package/dist/commands/migration-plan.d.mts.map +1 -1
  49. package/dist/commands/migration-plan.mjs +1 -1
  50. package/dist/commands/migration-show.d.mts +4 -55
  51. package/dist/commands/migration-show.d.mts.map +1 -1
  52. package/dist/commands/migration-show.mjs +61 -153
  53. package/dist/commands/migration-show.mjs.map +1 -1
  54. package/dist/commands/migration-status.d.mts +12 -49
  55. package/dist/commands/migration-status.d.mts.map +1 -1
  56. package/dist/commands/migration-status.mjs +84 -80
  57. package/dist/commands/migration-status.mjs.map +1 -1
  58. package/dist/commands/ref.d.mts +1 -1
  59. package/dist/commands/ref.d.mts.map +1 -1
  60. package/dist/commands/ref.mjs +10 -8
  61. package/dist/commands/ref.mjs.map +1 -1
  62. package/dist/config-loader-B6sJjXTv.mjs.map +1 -1
  63. package/dist/config-loader.d.mts.map +1 -1
  64. package/dist/contract-at-errors-BxP-TOMl.mjs +42 -0
  65. package/dist/contract-at-errors-BxP-TOMl.mjs.map +1 -0
  66. package/dist/{contract-emit-C6rlsljO.mjs → contract-emit-D-4jrNve.mjs} +21 -7
  67. package/dist/{contract-emit-C6rlsljO.mjs.map → contract-emit-D-4jrNve.mjs.map} +1 -1
  68. package/dist/{contract-emit-mqXmapxB.mjs → contract-emit-DxcGl4Uq.mjs} +4 -6
  69. package/dist/{contract-emit-mqXmapxB.mjs.map → contract-emit-DxcGl4Uq.mjs.map} +1 -1
  70. package/dist/{contract-enrichment-Dani0mMW.mjs → contract-enrichment-a0V5Y_mL.mjs} +4 -25
  71. package/dist/contract-enrichment-a0V5Y_mL.mjs.map +1 -0
  72. package/dist/{contract-infer-C4jxc1aZ.mjs → contract-infer-D8uEbJuu.mjs} +3 -4
  73. package/dist/{contract-infer-C4jxc1aZ.mjs.map → contract-infer-D8uEbJuu.mjs.map} +1 -1
  74. package/dist/contract-space-aggregate-loader-DvZwdkrr.mjs +247 -0
  75. package/dist/contract-space-aggregate-loader-DvZwdkrr.mjs.map +1 -0
  76. package/dist/{db-verify-1d8tDoFN.mjs → db-verify-v_vUKXTU.mjs} +5 -7
  77. package/dist/{db-verify-1d8tDoFN.mjs.map → db-verify-v_vUKXTU.mjs.map} +1 -1
  78. package/dist/exports/control-api.d.mts +3 -3
  79. package/dist/exports/control-api.d.mts.map +1 -1
  80. package/dist/exports/control-api.mjs +3 -3
  81. package/dist/exports/index.d.mts.map +1 -1
  82. package/dist/exports/index.mjs +1 -1
  83. package/dist/exports/index.mjs.map +1 -1
  84. package/dist/exports/init-output.d.mts.map +1 -1
  85. package/dist/exports/init-output.mjs +1 -1
  86. package/dist/extension-pack-inputs-IDvjRCi3.mjs +62 -0
  87. package/dist/extension-pack-inputs-IDvjRCi3.mjs.map +1 -0
  88. package/dist/{framework-components-Bexd0f4E.mjs → framework-components-fYXjz_in.mjs} +2 -2
  89. package/dist/{framework-components-Bexd0f4E.mjs.map → framework-components-fYXjz_in.mjs.map} +1 -1
  90. package/dist/global-flags-DEHjV8_s.d.mts +34 -0
  91. package/dist/global-flags-DEHjV8_s.d.mts.map +1 -0
  92. package/dist/{graph-render-BE8vmJ_7.mjs → graph-render-rFAqZujX.mjs} +2 -2
  93. package/dist/{graph-render-BE8vmJ_7.mjs.map → graph-render-rFAqZujX.mjs.map} +1 -1
  94. package/dist/{init-ByoeQphC.mjs → init-Cv9UzWL5.mjs} +17 -19
  95. package/dist/init-Cv9UzWL5.mjs.map +1 -0
  96. package/dist/{inspect-live-schema-B1Q49RF0.mjs → inspect-live-schema-C6ohV_oQ.mjs} +4 -5
  97. package/dist/{inspect-live-schema-B1Q49RF0.mjs.map → inspect-live-schema-C6ohV_oQ.mjs.map} +1 -1
  98. package/dist/migration-check-BiBJoYYW.mjs +341 -0
  99. package/dist/migration-check-BiBJoYYW.mjs.map +1 -0
  100. package/dist/migration-cli.d.mts.map +1 -1
  101. package/dist/migration-cli.mjs +4 -4
  102. package/dist/migration-cli.mjs.map +1 -1
  103. package/dist/{migration-command-scaffold-oY4P1Qto.mjs → migration-command-scaffold-CjvwO6at.mjs} +4 -5
  104. package/dist/{migration-command-scaffold-oY4P1Qto.mjs.map → migration-command-scaffold-CjvwO6at.mjs.map} +1 -1
  105. package/dist/migration-graph-D7DVUElV.mjs +1232 -0
  106. package/dist/migration-graph-D7DVUElV.mjs.map +1 -0
  107. package/dist/migration-list-styler-BRwF4-gy.mjs +399 -0
  108. package/dist/migration-list-styler-BRwF4-gy.mjs.map +1 -0
  109. package/dist/{migration-plan-jdAHg_gK.mjs → migration-plan-9DJ7q7_z.mjs} +169 -189
  110. package/dist/migration-plan-9DJ7q7_z.mjs.map +1 -0
  111. package/dist/{migration-types-BXWvz12q.d.mts → migration-types-D2FW63pr.d.mts} +1 -1
  112. package/dist/{migration-types-BXWvz12q.d.mts.map → migration-types-D2FW63pr.d.mts.map} +1 -1
  113. package/dist/{migrations-B7n518mT.mjs → migrations-Cv2jxNNK.mjs} +3 -13
  114. package/dist/migrations-Cv2jxNNK.mjs.map +1 -0
  115. package/dist/{output-CUIdfYo5.mjs → output-B60Gw5fu.mjs} +1 -1
  116. package/dist/{output-CUIdfYo5.mjs.map → output-B60Gw5fu.mjs.map} +1 -1
  117. package/dist/{progress-adapter-DFfvZcYL.mjs → progress-adapter-C644QK8l.mjs} +1 -1
  118. package/dist/{progress-adapter-DFfvZcYL.mjs.map → progress-adapter-C644QK8l.mjs.map} +1 -1
  119. package/dist/{ref-advancement-DRh5Nquq.mjs → ref-advancement-DUZqsue6.mjs} +1 -1
  120. package/dist/{ref-advancement-DRh5Nquq.mjs.map → ref-advancement-DUZqsue6.mjs.map} +1 -1
  121. package/dist/terminal-ui-5Y6mrg93.d.mts +133 -0
  122. package/dist/terminal-ui-5Y6mrg93.d.mts.map +1 -0
  123. package/dist/{types-UWB2-rrw.d.mts → types-Dt_SfqFm.d.mts} +18 -26
  124. package/dist/types-Dt_SfqFm.d.mts.map +1 -0
  125. package/dist/{verify-C5UvbrF1.mjs → verify-DCA9Sldu.mjs} +2 -2
  126. package/dist/{verify-C5UvbrF1.mjs.map → verify-DCA9Sldu.mjs.map} +1 -1
  127. package/package.json +34 -23
  128. package/src/commands/db-sign.ts +9 -5
  129. package/src/commands/db-update.ts +9 -8
  130. package/src/commands/init/templates/env.ts +13 -14
  131. package/src/commands/migrate.ts +87 -76
  132. package/src/commands/migration-check.ts +43 -83
  133. package/src/commands/migration-graph.ts +75 -60
  134. package/src/commands/migration-list.ts +220 -74
  135. package/src/commands/migration-log.ts +8 -14
  136. package/src/commands/migration-new.ts +44 -48
  137. package/src/commands/migration-plan.ts +107 -129
  138. package/src/commands/migration-show.ts +65 -284
  139. package/src/commands/migration-status.ts +127 -124
  140. package/src/commands/ref.ts +9 -4
  141. package/src/control-api/client.ts +0 -1
  142. package/src/control-api/contract-enrichment.ts +6 -42
  143. package/src/control-api/operations/{apply-aggregate.ts → apply.ts} +44 -75
  144. package/src/control-api/operations/contract-emit.ts +7 -2
  145. package/src/control-api/operations/{db-apply-aggregate.ts → db-apply.ts} +19 -19
  146. package/src/control-api/operations/db-init.ts +4 -4
  147. package/src/control-api/operations/db-update.ts +4 -4
  148. package/src/control-api/operations/db-verify.ts +15 -11
  149. package/src/control-api/operations/migration-apply.ts +56 -47
  150. package/src/control-api/types.ts +19 -27
  151. package/src/migration-cli.ts +4 -4
  152. package/src/utils/cli-errors.ts +73 -12
  153. package/src/utils/command-helpers.ts +1 -20
  154. package/src/utils/contract-at-errors.ts +96 -0
  155. package/src/utils/contract-space-aggregate-loader.ts +336 -117
  156. package/src/utils/formatters/migration-graph-layout.ts +1119 -0
  157. package/src/utils/formatters/migration-graph-rows.ts +336 -0
  158. package/src/utils/formatters/migration-graph-tree-render.ts +459 -0
  159. package/src/utils/formatters/migration-list-data-column.ts +115 -0
  160. package/src/utils/formatters/migration-list-graph-topology.ts +368 -0
  161. package/src/utils/formatters/migration-list-render.ts +191 -0
  162. package/src/utils/formatters/migration-list-styler.ts +63 -0
  163. package/src/utils/formatters/migration-list-types.ts +21 -0
  164. package/src/utils/formatters/migrations.ts +12 -46
  165. package/src/utils/glyph-mode.ts +22 -0
  166. package/src/utils/integrity-violation-to-check-failure.ts +130 -0
  167. package/src/utils/plan-resolution.ts +134 -133
  168. package/src/utils/terminal-ui.ts +42 -1
  169. package/dist/cli-errors-Bw2GlweY.mjs +0 -175
  170. package/dist/cli-errors-Bw2GlweY.mjs.map +0 -1
  171. package/dist/client-UnIveZxZ.mjs.map +0 -1
  172. package/dist/command-helpers-CRfjbZRz.mjs.map +0 -1
  173. package/dist/commands/migration-check.mjs.map +0 -1
  174. package/dist/commands/migration-graph.mjs.map +0 -1
  175. package/dist/contract-enrichment-Dani0mMW.mjs.map +0 -1
  176. package/dist/contract-space-aggregate-loader-CGakRlKM.mjs +0 -160
  177. package/dist/contract-space-aggregate-loader-CGakRlKM.mjs.map +0 -1
  178. package/dist/global-flags-CdE7M0d9.d.mts +0 -15
  179. package/dist/global-flags-CdE7M0d9.d.mts.map +0 -1
  180. package/dist/init-ByoeQphC.mjs.map +0 -1
  181. package/dist/migration-plan-jdAHg_gK.mjs.map +0 -1
  182. package/dist/migrations-B7n518mT.mjs.map +0 -1
  183. package/dist/rolldown-runtime-twds-ZHy.mjs +0 -14
  184. package/dist/terminal-ui-BiB_8KNo.mjs +0 -379
  185. package/dist/terminal-ui-BiB_8KNo.mjs.map +0 -1
  186. package/dist/types-UWB2-rrw.d.mts.map +0 -1
@@ -2,7 +2,6 @@ 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 {
5
- type ControlFamilyInstance,
6
5
  createControlStack,
7
6
  hasOperationPreview,
8
7
  type MigrationPlanOperation,
@@ -36,20 +35,22 @@ import {
36
35
  import {
37
36
  addGlobalOptions,
38
37
  getTargetMigrations,
39
- loadMigrationPackages,
40
38
  resolveContractPath,
41
39
  resolveMigrationPaths,
42
40
  setCommandDescriptions,
43
41
  setCommandExamples,
44
42
  } from '../utils/command-helpers';
45
- import { buildContractSpaceAggregate } from '../utils/contract-space-aggregate-loader';
43
+ import {
44
+ buildContractSpaceAggregate,
45
+ loadContractSpaceAggregateForCli,
46
+ } from '../utils/contract-space-aggregate-loader';
46
47
  import { runContractSpaceSeedPhase } from '../utils/contract-space-seed-phase';
47
48
  import { toExtensionInputs } from '../utils/extension-pack-inputs';
48
49
  import { formatStyledHeader } from '../utils/formatters/styled';
49
50
  import { assertFrameworkComponentsCompatible } from '../utils/framework-components';
50
51
  import type { CommonCommandOptions } from '../utils/global-flags';
51
52
  import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
52
- import { resolveFromForPlan } from '../utils/plan-resolution';
53
+ import { resolveFromForPlan, resolveToForPlan } from '../utils/plan-resolution';
53
54
  import { handleResult } from '../utils/result-handler';
54
55
  import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
55
56
 
@@ -57,55 +58,7 @@ interface MigrationPlanOptions extends CommonCommandOptions {
57
58
  readonly config?: string;
58
59
  readonly name?: string;
59
60
  readonly from?: string;
60
- }
61
-
62
- /**
63
- * Load a predecessor migration's destination contract from its sibling
64
- * `end-contract.json` on disk and route it through the family's
65
- * `ContractSerializer` (via `deserializeContract`) so the in-memory shape
66
- * is the hydrated `Contract` every other caller sees. Bypassing this
67
- * seam was the root cause of TML-2536: a raw `JSON.parse(...) as Contract`
68
- * here let polymorphic `storage.types` entries reach the planner without
69
- * the `kind` discriminator the planner dispatches on.
70
- *
71
- * Throws `CliStructuredError` with:
72
- * - `errorFileNotFound` when the sibling file is missing — the user
73
- * has likely deleted or never authored the snapshot, and the
74
- * message names the file and points them at re-emitting from the
75
- * source.
76
- * - `errorContractValidationFailed` when the JSON parses but the
77
- * family deserializer rejects it (legacy untagged shape, structural
78
- * mismatch, etc.) — the message names the predecessor's path so
79
- * the operator can locate the bad snapshot.
80
- */
81
- async function readPredecessorEndContract(
82
- migrationDir: string,
83
- familyInstance: ControlFamilyInstance<string, unknown>,
84
- ): Promise<Contract> {
85
- const path = join(migrationDir, 'end-contract.json');
86
- let raw: string;
87
- try {
88
- raw = await readFile(path, 'utf-8');
89
- } catch (error) {
90
- if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {
91
- throw errorFileNotFound(path, {
92
- why: `Predecessor migration is missing its destination contract snapshot at ${path}`,
93
- fix: 'Re-emit the predecessor migration (`prisma-next migration plan` from its source) so its sibling `end-contract.json` is restored, then re-run this command.',
94
- });
95
- }
96
- throw error;
97
- }
98
- try {
99
- return familyInstance.deserializeContract(JSON.parse(raw) as unknown);
100
- } catch (error) {
101
- if (CliStructuredError.is(error)) {
102
- throw error;
103
- }
104
- throw errorContractValidationFailed(
105
- `Predecessor contract at ${path} failed to deserialize: ${error instanceof Error ? error.message : String(error)}`,
106
- { where: { path } },
107
- );
108
- }
61
+ readonly to?: string;
109
62
  }
110
63
 
111
64
  async function writeSnapshotContractArtifacts(
@@ -206,12 +159,6 @@ async function writePlannedMigrationPackage(
206
159
  const metadataWithInvariants: Omit<MigrationMetadata, 'migrationHash'> = {
207
160
  from: fromHash,
208
161
  to: toHash,
209
- hints: {
210
- used: [],
211
- applied: [],
212
- plannerVersion: '2.0.0',
213
- },
214
- labels: [],
215
162
  providedInvariants: deriveProvidedInvariants(opsForWrite),
216
163
  createdAt: createdAt.toISOString(),
217
164
  };
@@ -239,7 +186,7 @@ export interface MigrationPlanResult {
239
186
  *
240
187
  * Surfacing these in the result (rather than only via `ui.step` log
241
188
  * lines) makes the cross-space side effect explicit to JSON consumers
242
- * and the success-summary renderer — the same multi-space side effect
189
+ * and the success-summary renderer — the same cross-space side effect
243
190
  * that `migrate` will replay.
244
191
  */
245
192
  readonly emittedExtensionDirs: readonly { readonly spaceId: string; readonly dirName: string }[];
@@ -288,6 +235,9 @@ async function executeMigrationPlanCommand(
288
235
  if (options.from) {
289
236
  details.push({ label: 'from', value: options.from });
290
237
  }
238
+ if (options.to) {
239
+ details.push({ label: 'to', value: options.to });
240
+ }
291
241
  if (options.name) {
292
242
  details.push({ label: 'name', value: options.name });
293
243
  }
@@ -321,8 +271,7 @@ async function executeMigrationPlanCommand(
321
271
  );
322
272
  }
323
273
 
324
- // Construct the family instance up-front so on-disk reads (the app
325
- // contract here + every `readPredecessorEndContract` below) cross the
274
+ // Construct the family instance up-front so on-disk contract reads cross the
326
275
  // serializer seam at the read site, not after the planner has already
327
276
  // started dispatching on raw shapes. See TML-2536.
328
277
  const stack = createControlStack(config);
@@ -348,9 +297,12 @@ async function executeMigrationPlanCommand(
348
297
  }),
349
298
  );
350
299
  }
351
- const toStorageHash = rawStorageHash;
300
+ let toStorageHash: string = rawStorageHash;
352
301
 
353
- const { refsDir } = resolveMigrationPaths(options.config, config);
302
+ // When `--to <ref>` resolves a non-default destination, these carry its raw
303
+ // artifacts so the planned package's `end-contract.*` is written from the
304
+ // resolved target rather than copied from the emitted `contract.json`.
305
+ let toArtifacts: { contractJson: unknown; contractDts: string } | null = null;
354
306
 
355
307
  let fromContract: Contract | null = null;
356
308
  let fromHash: string | null = null;
@@ -358,62 +310,71 @@ async function executeMigrationPlanCommand(
358
310
  let snapshotStartContract: { contractJson: unknown; contractDts: string } | null = null;
359
311
  let isAutoBaseline = false;
360
312
 
361
- try {
362
- const { bundles, graph } = await loadMigrationPackages(appMigrationsDir);
363
-
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
- });
313
+ const tolerantAggregateResult = await loadContractSpaceAggregateForCli({
314
+ targetId: config.target.targetId,
315
+ migrationsDir,
316
+ appContract: toContract,
317
+ extensionPacks: config.extensionPacks ?? [],
318
+ deserializeContract: (json: unknown) => familyInstance.deserializeContract(json),
319
+ });
320
+ if (!tolerantAggregateResult.ok) {
321
+ return notOk(tolerantAggregateResult.failure);
322
+ }
323
+ const resolutionMember = tolerantAggregateResult.value.app;
373
324
 
374
- if (!resolutionResult.ok) {
375
- return notOk(resolutionResult.failure);
376
- }
325
+ const resolutionResult = await resolveFromForPlan({
326
+ optionsFrom: options.from,
327
+ member: resolutionMember,
328
+ });
377
329
 
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;
403
- }
404
- } catch (error) {
405
- if (MigrationToolsError.is(error)) {
406
- return notOk(mapMigrationToolsError(error));
407
- }
408
- if (CliStructuredError.is(error)) {
409
- return notOk(error);
330
+ if (!resolutionResult.ok) {
331
+ return notOk(resolutionResult.failure);
332
+ }
333
+
334
+ switch (resolutionResult.value.kind) {
335
+ case 'greenfield':
336
+ break;
337
+ case 'graph-node':
338
+ fromHash = resolutionResult.value.fromHash;
339
+ fromContract = resolutionResult.value.fromContract;
340
+ fromContractSourceDir = resolutionResult.value.sourceDir;
341
+ break;
342
+ case 'snapshot':
343
+ fromHash = resolutionResult.value.fromHash;
344
+ fromContract = resolutionResult.value.fromContract;
345
+ snapshotStartContract = {
346
+ contractJson: resolutionResult.value.contractJson,
347
+ contractDts: resolutionResult.value.contractDts,
348
+ };
349
+ break;
350
+ case 'auto-baseline':
351
+ fromHash = resolutionResult.value.fromHash;
352
+ fromContract = resolutionResult.value.fromContract;
353
+ snapshotStartContract = {
354
+ contractJson: resolutionResult.value.contractJson,
355
+ contractDts: resolutionResult.value.contractDts,
356
+ };
357
+ isAutoBaseline = true;
358
+ break;
359
+ }
360
+
361
+ // `--to <ref>` swaps the planner destination to an arbitrary resolved
362
+ // contract (e.g. an ancestor / rollback target). The from-side resolution
363
+ // above is untouched; only the destination + its emitted `end-contract.*`
364
+ // change.
365
+ if (options.to !== undefined) {
366
+ const toResolution = await resolveToForPlan(options.to, {
367
+ member: resolutionMember,
368
+ });
369
+ if (!toResolution.ok) {
370
+ return notOk(toResolution.failure);
410
371
  }
411
- const message = error instanceof Error ? error.message : String(error);
412
- return notOk(
413
- errorUnexpected(message, {
414
- why: `Unexpected error while loading migrations: ${message}`,
415
- }),
416
- );
372
+ toContract = toResolution.value.contract;
373
+ toStorageHash = toResolution.value.hash;
374
+ toArtifacts = {
375
+ contractJson: toResolution.value.contractJson,
376
+ contractDts: toResolution.value.contractDts,
377
+ };
417
378
  }
418
379
 
419
380
  // Phase 1 — seed: unconditionally re-emit per-space pinned artefacts
@@ -493,6 +454,26 @@ async function executeMigrationPlanCommand(
493
454
  [config.target, config.adapter, ...(config.extensionPacks ?? [])],
494
455
  );
495
456
 
457
+ // Write the planned package's destination `end-contract.*`. With `--to`, the
458
+ // resolved target's raw artifacts are written; otherwise the emitted
459
+ // `contract.json` / `contract.d.ts` are copied verbatim (today's behaviour).
460
+ async function writeDestinationEndContract(packageDir: string): Promise<void> {
461
+ if (toArtifacts !== null) {
462
+ await writeSnapshotContractArtifacts(
463
+ packageDir,
464
+ toArtifacts.contractJson,
465
+ toArtifacts.contractDts,
466
+ 'end-contract',
467
+ );
468
+ return;
469
+ }
470
+ const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
471
+ await copyFilesWithRename(packageDir, [
472
+ { sourcePath: destinationArtifacts.jsonPath, destName: 'end-contract.json' },
473
+ { sourcePath: destinationArtifacts.dtsPath, destName: 'end-contract.d.ts' },
474
+ ]);
475
+ }
476
+
496
477
  try {
497
478
  const planner = migrations.createPlanner(familyInstance);
498
479
 
@@ -582,7 +563,7 @@ async function executeMigrationPlanCommand(
582
563
  planner,
583
564
  migrations,
584
565
  frameworkComponents,
585
- aggregate.app.contract,
566
+ aggregate.app.contract(),
586
567
  fromContract,
587
568
  aggregate.app.spaceId,
588
569
  );
@@ -597,11 +578,7 @@ async function executeMigrationPlanCommand(
597
578
  deltaTimestamp,
598
579
  deltaLeg.value,
599
580
  );
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
- ]);
581
+ await writeDestinationEndContract(deltaPackageDir);
605
582
  await writeSnapshotStartContract(
606
583
  deltaPackageDir,
607
584
  snapshotStartContract.contractJson,
@@ -659,7 +636,7 @@ async function executeMigrationPlanCommand(
659
636
  planner,
660
637
  migrations,
661
638
  frameworkComponents,
662
- aggregate.app.contract,
639
+ aggregate.app.contract(),
663
640
  fromContract,
664
641
  aggregate.app.spaceId,
665
642
  );
@@ -674,11 +651,7 @@ async function executeMigrationPlanCommand(
674
651
  timestamp,
675
652
  deltaLeg.value,
676
653
  );
677
- const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
678
- await copyFilesWithRename(packageDir, [
679
- { sourcePath: destinationArtifacts.jsonPath, destName: 'end-contract.json' },
680
- { sourcePath: destinationArtifacts.dtsPath, destName: 'end-contract.d.ts' },
681
- ]);
654
+ await writeDestinationEndContract(packageDir);
682
655
  if (fromContractSourceDir !== null) {
683
656
  const sourceArtifacts = getEmittedArtifactPaths(
684
657
  join(fromContractSourceDir, 'end-contract.json'),
@@ -761,6 +734,7 @@ export function createMigrationPlanCommand(): Command {
761
734
  setCommandExamples(command, [
762
735
  'prisma-next migration plan',
763
736
  'prisma-next migration plan --name add-users-table',
737
+ 'prisma-next migration plan --to <migration-dir>^ --name rollback',
764
738
  ]);
765
739
  addGlobalOptions(command)
766
740
  .option('--config <path>', 'Path to prisma-next.config.ts')
@@ -769,6 +743,10 @@ export function createMigrationPlanCommand(): Command {
769
743
  '--from <contract>',
770
744
  'Starting contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)',
771
745
  )
746
+ .option(
747
+ '--to <contract>',
748
+ 'Destination contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path); defaults to the emitted contract',
749
+ )
772
750
  .action(async (options: MigrationPlanOptions) => {
773
751
  const flags = parseGlobalFlagsOrExit(options);
774
752
  const startTime = Date.now();