@prisma-next/cli 0.4.2 → 0.5.0-dev.10

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 (79) hide show
  1. package/README.md +17 -18
  2. package/dist/cli-errors-By1iVE3z.mjs +34 -0
  3. package/dist/cli-errors-By1iVE3z.mjs.map +1 -0
  4. package/dist/{cli-errors-BFYgBH3L.d.mts → cli-errors-D2NPMaxW.d.mts} +1 -0
  5. package/dist/cli.mjs +11 -11
  6. package/dist/{client-CrsnY58k.mjs → client-faKQqcix.mjs} +17 -2
  7. package/dist/client-faKQqcix.mjs.map +1 -0
  8. package/dist/commands/contract-emit.mjs +5 -5
  9. package/dist/commands/contract-infer.mjs +6 -6
  10. package/dist/commands/db-init.mjs +6 -6
  11. package/dist/commands/db-schema.mjs +5 -5
  12. package/dist/commands/db-sign.mjs +4 -4
  13. package/dist/commands/db-update.mjs +6 -6
  14. package/dist/commands/db-verify.mjs +4 -4
  15. package/dist/commands/migration-apply.d.mts +1 -1
  16. package/dist/commands/migration-apply.d.mts.map +1 -1
  17. package/dist/commands/migration-apply.mjs +10 -34
  18. package/dist/commands/migration-apply.mjs.map +1 -1
  19. package/dist/commands/migration-new.d.mts.map +1 -1
  20. package/dist/commands/migration-new.mjs +18 -23
  21. package/dist/commands/migration-new.mjs.map +1 -1
  22. package/dist/commands/migration-plan.d.mts +6 -3
  23. package/dist/commands/migration-plan.d.mts.map +1 -1
  24. package/dist/commands/migration-plan.mjs +27 -32
  25. package/dist/commands/migration-plan.mjs.map +1 -1
  26. package/dist/commands/migration-ref.d.mts +1 -1
  27. package/dist/commands/migration-ref.d.mts.map +1 -1
  28. package/dist/commands/migration-ref.mjs +4 -8
  29. package/dist/commands/migration-ref.mjs.map +1 -1
  30. package/dist/commands/migration-show.d.mts +4 -4
  31. package/dist/commands/migration-show.d.mts.map +1 -1
  32. package/dist/commands/migration-show.mjs +15 -22
  33. package/dist/commands/migration-show.mjs.map +1 -1
  34. package/dist/commands/migration-status.d.mts +5 -4
  35. package/dist/commands/migration-status.d.mts.map +1 -1
  36. package/dist/commands/migration-status.mjs +5 -5
  37. package/dist/{contract-emit-NJ01hiiv.mjs → contract-emit-B5wnhTuF.mjs} +4 -4
  38. package/dist/{contract-emit-NJ01hiiv.mjs.map → contract-emit-B5wnhTuF.mjs.map} +1 -1
  39. package/dist/contract-emit-B9wkchud.mjs +6 -0
  40. package/dist/{contract-emit-V5SSitUT.mjs → contract-emit-PeB96eHy.mjs} +3 -3
  41. package/dist/{contract-emit-V5SSitUT.mjs.map → contract-emit-PeB96eHy.mjs.map} +1 -1
  42. package/dist/{contract-infer-D9cC3rJm.mjs → contract-infer-DnY9fUw0.mjs} +3 -3
  43. package/dist/{contract-infer-D9cC3rJm.mjs.map → contract-infer-DnY9fUw0.mjs.map} +1 -1
  44. package/dist/exports/control-api.mjs +4 -4
  45. package/dist/exports/index.mjs +5 -5
  46. package/dist/{framework-components-Cr--XBKy.mjs → framework-components-C6el-5x_.mjs} +2 -2
  47. package/dist/{framework-components-Cr--XBKy.mjs.map → framework-components-C6el-5x_.mjs.map} +1 -1
  48. package/dist/{init-m8x0UoPY.mjs → init-jf33mNQ6.mjs} +3 -3
  49. package/dist/{init-m8x0UoPY.mjs.map → init-jf33mNQ6.mjs.map} +1 -1
  50. package/dist/{inspect-live-schema-yrHAvG71.mjs → inspect-live-schema-CqoZhKC1.mjs} +4 -4
  51. package/dist/{inspect-live-schema-yrHAvG71.mjs.map → inspect-live-schema-CqoZhKC1.mjs.map} +1 -1
  52. package/dist/migration-cli.mjs +13 -6
  53. package/dist/migration-cli.mjs.map +1 -1
  54. package/dist/{migration-command-scaffold-B3B09et6.mjs → migration-command-scaffold-B40VaF-m.mjs} +4 -4
  55. package/dist/{migration-command-scaffold-B3B09et6.mjs.map → migration-command-scaffold-B40VaF-m.mjs.map} +1 -1
  56. package/dist/{migration-status-DUMiH8_G.mjs → migration-status-CDgFxhAo.mjs} +10 -22
  57. package/dist/migration-status-CDgFxhAo.mjs.map +1 -0
  58. package/dist/{migrations-Bo5WtTla.mjs → migrations-CKRMAKka.mjs} +2 -2
  59. package/dist/migrations-CKRMAKka.mjs.map +1 -0
  60. package/dist/{result-handler-Ba3zWQsI.mjs → result-handler-DcV0QoTr.mjs} +6 -14
  61. package/dist/{result-handler-Ba3zWQsI.mjs.map → result-handler-DcV0QoTr.mjs.map} +1 -1
  62. package/package.json +14 -14
  63. package/src/commands/migration-apply.ts +11 -46
  64. package/src/commands/migration-new.ts +19 -27
  65. package/src/commands/migration-plan.ts +52 -41
  66. package/src/commands/migration-ref.ts +8 -7
  67. package/src/commands/migration-show.ts +23 -27
  68. package/src/commands/migration-status.ts +20 -32
  69. package/src/control-api/operations/migration-apply.ts +15 -0
  70. package/src/migration-cli.ts +16 -9
  71. package/src/utils/cli-errors.ts +45 -1
  72. package/src/utils/command-helpers.ts +8 -21
  73. package/src/utils/formatters/graph-migration-mapper.ts +1 -1
  74. package/src/utils/formatters/migrations.ts +2 -2
  75. package/dist/cli-errors-Cd79vmTH.mjs +0 -5
  76. package/dist/client-CrsnY58k.mjs.map +0 -1
  77. package/dist/contract-emit-DxgyXrqV.mjs +0 -6
  78. package/dist/migration-status-DUMiH8_G.mjs.map +0 -1
  79. package/dist/migrations-Bo5WtTla.mjs.map +0 -1
@@ -5,16 +5,17 @@ import {
5
5
  createControlStack,
6
6
  type MigrationPlanOperation,
7
7
  } from '@prisma-next/framework-components/control';
8
- import { computeMigrationId } from '@prisma-next/migration-tools/attestation';
9
8
  import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
10
9
  import { findLatestMigration } from '@prisma-next/migration-tools/dag';
10
+ import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
11
+ import { computeMigrationHash } from '@prisma-next/migration-tools/hash';
11
12
  import {
12
13
  copyFilesWithRename,
13
14
  formatMigrationDirName,
14
15
  writeMigrationPackage,
15
16
  } from '@prisma-next/migration-tools/io';
17
+ import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
16
18
  import { writeMigrationTs } from '@prisma-next/migration-tools/migration-ts';
17
- import { type MigrationManifest, MigrationToolsError } from '@prisma-next/migration-tools/types';
18
19
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
19
20
  import { Command } from 'commander';
20
21
  import { join, relative } from 'pathe';
@@ -29,11 +30,12 @@ import {
29
30
  errorRuntime,
30
31
  errorTargetMigrationNotSupported,
31
32
  errorUnexpected,
33
+ mapMigrationToolsError,
32
34
  } from '../utils/cli-errors';
33
35
  import {
34
36
  addGlobalOptions,
35
37
  getTargetMigrations,
36
- loadAllBundles,
38
+ loadMigrationPackages,
37
39
  resolveContractPath,
38
40
  resolveMigrationPaths,
39
41
  setCommandDescriptions,
@@ -76,22 +78,6 @@ export interface MigrationPlanResult {
76
78
  };
77
79
  }
78
80
 
79
- function mapMigrationToolsError(error: unknown): CliStructuredError {
80
- if (CliStructuredError.is(error)) {
81
- return error;
82
- }
83
- if (MigrationToolsError.is(error)) {
84
- return errorRuntime(error.message, {
85
- why: error.why,
86
- fix: error.fix,
87
- meta: { code: error.code, ...(error.details ?? {}) },
88
- });
89
- }
90
- return errorUnexpected(error instanceof Error ? error.message : String(error), {
91
- why: `Unexpected error during migration plan: ${error instanceof Error ? error.message : String(error)}`,
92
- });
93
- }
94
-
95
81
  async function executeMigrationPlanCommand(
96
82
  options: MigrationPlanOptions,
97
83
  flags: GlobalFlags,
@@ -177,7 +163,7 @@ async function executeMigrationPlanCommand(
177
163
  let fromContractSourceDir: string | null = null;
178
164
 
179
165
  try {
180
- const { bundles, graph } = await loadAllBundles(migrationsDir);
166
+ const { bundles, graph } = await loadMigrationPackages(migrationsDir);
181
167
 
182
168
  if (options.from) {
183
169
  const resolved = resolveBundleByPrefix(bundles, options.from);
@@ -195,16 +181,18 @@ async function executeMigrationPlanCommand(
195
181
  }),
196
182
  );
197
183
  }
198
- fromHash = resolved.value.manifest.to;
199
- fromContract = resolved.value.manifest.toContract;
184
+ fromHash = resolved.value.metadata.to;
185
+ fromContract = resolved.value.metadata.toContract;
200
186
  fromContractSourceDir = resolved.value.dirPath;
201
187
  } else {
202
188
  const latestMigration = findLatestMigration(graph);
203
189
  if (latestMigration) {
204
190
  fromHash = latestMigration.to;
205
- const leafPkg = bundles.find((p) => p.manifest.migrationId === latestMigration.migrationId);
191
+ const leafPkg = bundles.find(
192
+ (p) => p.metadata.migrationHash === latestMigration.migrationHash,
193
+ );
206
194
  if (leafPkg) {
207
- fromContract = leafPkg.manifest.toContract;
195
+ fromContract = leafPkg.metadata.toContract;
208
196
  fromContractSourceDir = leafPkg.dirPath;
209
197
  }
210
198
  }
@@ -213,7 +201,16 @@ async function executeMigrationPlanCommand(
213
201
  if (MigrationToolsError.is(error)) {
214
202
  return notOk(mapMigrationToolsError(error));
215
203
  }
216
- throw error;
204
+ // Wrap unexpected (non-MigrationToolsError) failures from the migration
205
+ // load phase in a structured CLI envelope. Letting them throw would
206
+ // bypass `handleResult()` and crash the command — see CLI structured-
207
+ // errors guideline (CliStructuredError + Result pattern).
208
+ const message = error instanceof Error ? error.message : String(error);
209
+ return notOk(
210
+ errorUnexpected(message, {
211
+ why: `Unexpected error while loading migrations: ${message}`,
212
+ }),
213
+ );
217
214
  }
218
215
 
219
216
  // Check for no-op (same hash means no changes)
@@ -251,7 +248,7 @@ async function executeMigrationPlanCommand(
251
248
  const dirName = formatMigrationDirName(timestamp, slug);
252
249
  const packageDir = join(migrationsDir, dirName);
253
250
 
254
- const baseManifest: Omit<MigrationManifest, 'migrationId'> = {
251
+ const baseMetadata: Omit<MigrationMetadata, 'migrationHash'> = {
255
252
  from: fromHash,
256
253
  to: toStorageHash,
257
254
  kind: 'regular',
@@ -320,18 +317,18 @@ async function executeMigrationPlanCommand(
320
317
 
321
318
  const migrationTsContent = plannerResult.plan.renderTypeScript();
322
319
 
323
- // Always-attest: compute migrationId over (manifest, ops). When
324
- // placeholders blocked lowering, ops is `[]` and the id hashes over
325
- // the empty list — re-emitting after the user fills the placeholder
326
- // produces a different id (over the real ops). This is intentional;
320
+ // Always-attest: compute migrationHash over (metadata, ops). When
321
+ // placeholders blocked lowering, ops is `[]` and the hash is computed
322
+ // over the empty list — re-emitting after the user fills the placeholder
323
+ // produces a different hash (over the real ops). This is intentional;
327
324
  // there is no on-disk "draft" state.
328
325
  const opsForWrite = hasPlaceholders ? [] : plannedOps;
329
- const manifest: MigrationManifest = {
330
- ...baseManifest,
331
- migrationId: computeMigrationId(baseManifest, opsForWrite),
326
+ const metadata: MigrationMetadata = {
327
+ ...baseMetadata,
328
+ migrationHash: computeMigrationHash(baseMetadata, opsForWrite),
332
329
  };
333
330
 
334
- await writeMigrationPackage(packageDir, manifest, opsForWrite);
331
+ await writeMigrationPackage(packageDir, metadata, opsForWrite);
335
332
  const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
336
333
  await copyFilesWithRename(packageDir, [
337
334
  { sourcePath: destinationArtifacts.jsonPath, destName: 'end-contract.json' },
@@ -382,7 +379,18 @@ async function executeMigrationPlanCommand(
382
379
  };
383
380
  return ok(result);
384
381
  } catch (error) {
385
- return notOk(mapMigrationToolsError(error));
382
+ if (CliStructuredError.is(error)) {
383
+ return notOk(error);
384
+ }
385
+ if (MigrationToolsError.is(error)) {
386
+ return notOk(mapMigrationToolsError(error));
387
+ }
388
+ const message = error instanceof Error ? error.message : String(error);
389
+ return notOk(
390
+ errorUnexpected(message, {
391
+ why: `Unexpected error during migration plan: ${message}`,
392
+ }),
393
+ );
386
394
  }
387
395
  }
388
396
 
@@ -489,7 +497,7 @@ function formatMigrationPlanOutput(result: MigrationPlanResult, flags: GlobalFla
489
497
 
490
498
  lines.push('');
491
499
  lines.push(
492
- `Next: ${green_(`node ${result.dir ?? '<dir>'}/migration.ts`)} to emit ops.json and attest migrationId before running ${green_('prisma-next migration apply')}.`,
500
+ `Next: review ${green_(result.dir ?? '<dir>')} if needed, then run ${green_('prisma-next migration apply')}.`,
493
501
  );
494
502
 
495
503
  if (result.sql && result.sql.length > 0) {
@@ -517,24 +525,27 @@ export type PrefixResolutionFailure =
517
525
  | { reason: 'not-found' };
518
526
 
519
527
  /**
520
- * Resolve a migration bundle by exact hash or prefix match.
528
+ * Resolve a migration package by **target contract hash** (`metadata.to`)
529
+ * using exact match or prefix match.
521
530
  *
531
+ * Note: matches `metadata.to` (the contract hash this migration produces),
532
+ * not `metadata.migrationHash` (the package's content-addressed identity).
522
533
  * Tries exact match first, then prefix match (auto-prepending `sha256:` when
523
- * the needle omits the scheme). Returns the matched bundle on success, or a
534
+ * the needle omits the scheme). Returns the matched package on success, or a
524
535
  * discriminated failure indicating whether the prefix was ambiguous or simply
525
536
  * not found.
526
537
  *
527
538
  * @internal Exported for testing only.
528
539
  */
529
- export function resolveBundleByPrefix<T extends { manifest: { to: string } }>(
540
+ export function resolveBundleByPrefix<T extends { metadata: { to: string } }>(
530
541
  bundles: readonly T[],
531
542
  needle: string,
532
543
  ): Result<T, PrefixResolutionFailure> {
533
- const exact = bundles.find((p) => p.manifest.to === needle);
544
+ const exact = bundles.find((p) => p.metadata.to === needle);
534
545
  if (exact) return ok(exact);
535
546
 
536
547
  const prefixWithScheme = needle.startsWith('sha256:') ? needle : `sha256:${needle}`;
537
- const candidates = bundles.filter((p) => p.manifest.to.startsWith(prefixWithScheme));
548
+ const candidates = bundles.filter((p) => p.metadata.to.startsWith(prefixWithScheme));
538
549
 
539
550
  if (candidates.length === 1) return ok(candidates[0]!);
540
551
  if (candidates.length > 1) return notOk({ reason: 'ambiguous', count: candidates.length });
@@ -1,3 +1,4 @@
1
+ import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
1
2
  import type { RefEntry } from '@prisma-next/migration-tools/refs';
2
3
  import {
3
4
  deleteRef,
@@ -7,11 +8,15 @@ import {
7
8
  validateRefValue,
8
9
  writeRef,
9
10
  } from '@prisma-next/migration-tools/refs';
10
- import { MigrationToolsError } from '@prisma-next/migration-tools/types';
11
11
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
12
12
  import { Command } from 'commander';
13
13
  import { loadConfig } from '../config-loader';
14
- import { CliStructuredError, errorRuntime, errorUnexpected } from '../utils/cli-errors';
14
+ import {
15
+ CliStructuredError,
16
+ errorRuntime,
17
+ errorUnexpected,
18
+ mapMigrationToolsError,
19
+ } from '../utils/cli-errors';
15
20
  import {
16
21
  addGlobalOptions,
17
22
  resolveMigrationPaths,
@@ -49,11 +54,7 @@ interface RefListResult {
49
54
 
50
55
  function mapError(error: unknown): CliStructuredError {
51
56
  if (MigrationToolsError.is(error)) {
52
- return errorRuntime(error.message, {
53
- why: error.why,
54
- fix: error.fix,
55
- meta: { code: error.code },
56
- });
57
+ return mapMigrationToolsError(error);
57
58
  }
58
59
  return errorUnexpected(error instanceof Error ? error.message : String(error));
59
60
  }
@@ -1,14 +1,19 @@
1
1
  import type { MigrationPlanOperation } from '@prisma-next/framework-components/control';
2
2
  import { findLatestMigration, reconstructGraph } from '@prisma-next/migration-tools/dag';
3
+ import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
3
4
  import { readMigrationPackage, readMigrationsDir } from '@prisma-next/migration-tools/io';
4
- import type { MigrationBundle } from '@prisma-next/migration-tools/types';
5
- import { MigrationToolsError } from '@prisma-next/migration-tools/types';
5
+ import type { MigrationPackage } from '@prisma-next/migration-tools/package';
6
6
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
7
7
  import { Command } from 'commander';
8
8
  import { relative, resolve } from 'pathe';
9
9
  import { loadConfig } from '../config-loader';
10
10
  import { extractOperationStatements } from '../control-api/operations/extract-operation-statements';
11
- import { type CliStructuredError, errorRuntime, errorUnexpected } from '../utils/cli-errors';
11
+ import {
12
+ type CliStructuredError,
13
+ errorRuntime,
14
+ errorUnexpected,
15
+ mapMigrationToolsError,
16
+ } from '../utils/cli-errors';
12
17
  import {
13
18
  addGlobalOptions,
14
19
  setCommandDescriptions,
@@ -31,7 +36,7 @@ export interface MigrationShowResult {
31
36
  readonly dirPath: string;
32
37
  readonly from: string;
33
38
  readonly to: string;
34
- readonly migrationId: string;
39
+ readonly migrationHash: string;
35
40
  readonly kind: string;
36
41
  readonly createdAt: string;
37
42
  readonly operations: readonly {
@@ -48,11 +53,11 @@ function looksLikePath(target: string): boolean {
48
53
  }
49
54
 
50
55
  export function resolveByHashPrefix(
51
- packages: readonly MigrationBundle[],
56
+ packages: readonly MigrationPackage[],
52
57
  prefix: string,
53
- ): Result<MigrationBundle, CliStructuredError> {
58
+ ): Result<MigrationPackage, CliStructuredError> {
54
59
  const normalizedPrefix = prefix.startsWith('sha256:') ? prefix : `sha256:${prefix}`;
55
- const matches = packages.filter((p) => p.manifest.migrationId.startsWith(normalizedPrefix));
60
+ const matches = packages.filter((p) => p.metadata.migrationHash.startsWith(normalizedPrefix));
56
61
 
57
62
  if (matches.length === 1) {
58
63
  return ok(matches[0]!);
@@ -61,13 +66,13 @@ export function resolveByHashPrefix(
61
66
  if (matches.length === 0) {
62
67
  return notOk(
63
68
  errorRuntime('No migration found matching prefix', {
64
- why: `No migration has a migrationId starting with "${normalizedPrefix}"`,
69
+ why: `No migration has a migrationHash starting with "${normalizedPrefix}"`,
65
70
  fix: 'Run `prisma-next migration show` (no argument) to see the latest migration, or check the migrations directory for available packages.',
66
71
  }),
67
72
  );
68
73
  }
69
74
 
70
- const candidates = matches.map((p) => ` ${p.dirName} ${p.manifest.migrationId}`).join('\n');
75
+ const candidates = matches.map((p) => ` ${p.dirName} ${p.metadata.migrationHash}`).join('\n');
71
76
  return notOk(
72
77
  errorRuntime('Ambiguous hash prefix', {
73
78
  why: `Multiple migrations match prefix "${normalizedPrefix}":\n${candidates}`,
@@ -110,7 +115,7 @@ async function executeMigrationShowCommand(
110
115
  ui.stderr(header);
111
116
  }
112
117
 
113
- let pkg: MigrationBundle;
118
+ let pkg: MigrationPackage;
114
119
 
115
120
  try {
116
121
  if (target && looksLikePath(target)) {
@@ -142,7 +147,7 @@ async function executeMigrationShowCommand(
142
147
  );
143
148
  }
144
149
  const leafPkg = allPackages.find(
145
- (p) => p.manifest.migrationId === latestMigration.migrationId,
150
+ (p) => p.metadata.migrationHash === latestMigration.migrationHash,
146
151
  );
147
152
  if (!leafPkg) {
148
153
  return notOk(
@@ -157,13 +162,7 @@ async function executeMigrationShowCommand(
157
162
  }
158
163
  } catch (error) {
159
164
  if (MigrationToolsError.is(error)) {
160
- return notOk(
161
- errorRuntime(error.message, {
162
- why: error.why,
163
- fix: error.fix,
164
- meta: { code: error.code, ...(error.details ?? {}) },
165
- }),
166
- );
165
+ return notOk(mapMigrationToolsError(error));
167
166
  }
168
167
  return notOk(
169
168
  errorUnexpected(error instanceof Error ? error.message : String(error), {
@@ -179,11 +178,11 @@ async function executeMigrationShowCommand(
179
178
  ok: true,
180
179
  dirName: pkg.dirName,
181
180
  dirPath: relative(process.cwd(), pkg.dirPath),
182
- from: pkg.manifest.from,
183
- to: pkg.manifest.to,
184
- migrationId: pkg.manifest.migrationId,
185
- kind: pkg.manifest.kind,
186
- createdAt: pkg.manifest.createdAt,
181
+ from: pkg.metadata.from,
182
+ to: pkg.metadata.to,
183
+ migrationHash: pkg.metadata.migrationHash,
184
+ kind: pkg.metadata.kind,
185
+ createdAt: pkg.metadata.createdAt,
187
186
  operations: ops.map((op) => ({
188
187
  id: op.id,
189
188
  label: op.label,
@@ -209,10 +208,7 @@ export function createMigrationShowCommand(): Command {
209
208
  'prisma-next migration show sha256:a1b2c3',
210
209
  ]);
211
210
  addGlobalOptions(command)
212
- .argument(
213
- '[target]',
214
- 'Migration directory path or migrationId hash prefix (defaults to latest)',
215
- )
211
+ .argument('[target]', 'Migration directory path or migrationHash prefix (defaults to latest)')
216
212
  .option('--config <path>', 'Path to prisma-next.config.ts')
217
213
  .action(async (target: string | undefined, options: MigrationShowOptions) => {
218
214
  const flags = parseGlobalFlags(options);
@@ -5,14 +5,11 @@ import {
5
5
  findPathWithDecision,
6
6
  findReachableLeaves,
7
7
  } from '@prisma-next/migration-tools/dag';
8
+ import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
9
+ import type { MigrationChainEntry, MigrationGraph } from '@prisma-next/migration-tools/graph';
10
+ import type { MigrationPackage } from '@prisma-next/migration-tools/package';
8
11
  import type { Refs } from '@prisma-next/migration-tools/refs';
9
12
  import { readRefs, resolveRef } from '@prisma-next/migration-tools/refs';
10
- import type {
11
- MigrationBundle,
12
- MigrationChainEntry,
13
- MigrationGraph,
14
- } from '@prisma-next/migration-tools/types';
15
- import { MigrationToolsError } from '@prisma-next/migration-tools/types';
16
13
  import { ifDefined } from '@prisma-next/utils/defined';
17
14
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
18
15
  import { cyan, dim, magenta, yellow } from 'colorette';
@@ -20,10 +17,15 @@ import { Command } from 'commander';
20
17
 
21
18
  import { loadConfig } from '../config-loader';
22
19
  import { createControlClient } from '../control-api/client';
23
- import { type CliStructuredError, errorRuntime, errorUnexpected } from '../utils/cli-errors';
20
+ import {
21
+ type CliStructuredError,
22
+ errorRuntime,
23
+ errorUnexpected,
24
+ mapMigrationToolsError,
25
+ } from '../utils/cli-errors';
24
26
  import {
25
27
  addGlobalOptions,
26
- loadAllBundles,
28
+ loadMigrationPackages,
27
29
  maskConnectionUrl,
28
30
  readContractEnvelope,
29
31
  resolveMigrationPaths,
@@ -61,7 +63,7 @@ export interface MigrationStatusEntry {
61
63
  readonly dirName: string;
62
64
  readonly from: string;
63
65
  readonly to: string;
64
- readonly migrationId: string;
66
+ readonly migrationHash: string;
65
67
  readonly operationCount: number;
66
68
  readonly operationSummary: string;
67
69
  readonly hasDestructive: boolean;
@@ -86,7 +88,7 @@ export interface MigrationStatusResult {
86
88
  readonly refName?: string;
87
89
  readonly selectedPath: readonly {
88
90
  readonly dirName: string;
89
- readonly migrationId: string;
91
+ readonly migrationHash: string;
90
92
  readonly from: string;
91
93
  readonly to: string;
92
94
  }[];
@@ -94,7 +96,7 @@ export interface MigrationStatusResult {
94
96
  readonly summary: string;
95
97
  readonly diagnostics: readonly StatusDiagnostic[];
96
98
  readonly graph?: MigrationGraph;
97
- readonly bundles?: readonly MigrationBundle[];
99
+ readonly bundles?: readonly MigrationPackage[];
98
100
  readonly edgeStatuses?: readonly EdgeStatus[];
99
101
  readonly activeRefHash?: string;
100
102
  readonly activeRefName?: string;
@@ -225,7 +227,7 @@ export function deriveEdgeStatuses(
225
227
  */
226
228
  function buildMigrationEntries(
227
229
  chain: readonly MigrationChainEntry[],
228
- packages: readonly MigrationBundle[],
230
+ packages: readonly MigrationPackage[],
229
231
  mode: 'online' | 'offline',
230
232
  markerHash: string | undefined,
231
233
  edgeStatuses?: readonly EdgeStatus[],
@@ -261,7 +263,7 @@ function buildMigrationEntries(
261
263
  dirName: migration.dirName,
262
264
  from: migration.from,
263
265
  to: migration.to,
264
- migrationId: migration.migrationId,
266
+ migrationHash: migration.migrationHash,
265
267
  operationCount: ops.length,
266
268
  operationSummary: summary,
267
269
  hasDestructive,
@@ -360,13 +362,7 @@ async function executeMigrationStatusCommand(
360
362
  allRefs = await readRefs(refsDir);
361
363
  } catch (error) {
362
364
  if (MigrationToolsError.is(error)) {
363
- return notOk(
364
- errorRuntime(error.message, {
365
- why: error.why,
366
- fix: error.fix,
367
- meta: { code: error.code },
368
- }),
369
- );
365
+ return notOk(mapMigrationToolsError(error));
370
366
  }
371
367
  throw error;
372
368
  }
@@ -377,13 +373,7 @@ async function executeMigrationStatusCommand(
377
373
  activeRefHash = resolveRef(allRefs, activeRefName).hash;
378
374
  } catch (error) {
379
375
  if (MigrationToolsError.is(error)) {
380
- return notOk(
381
- errorRuntime(error.message, {
382
- why: error.why,
383
- fix: error.fix,
384
- meta: { code: error.code },
385
- }),
386
- );
376
+ return notOk(mapMigrationToolsError(error));
387
377
  }
388
378
  throw error;
389
379
  }
@@ -429,15 +419,13 @@ async function executeMigrationStatusCommand(
429
419
  });
430
420
  }
431
421
 
432
- let bundles: readonly MigrationBundle[];
422
+ let bundles: readonly MigrationPackage[];
433
423
  let graph: MigrationGraph;
434
424
  try {
435
- ({ bundles, graph } = await loadAllBundles(migrationsDir));
425
+ ({ bundles, graph } = await loadMigrationPackages(migrationsDir));
436
426
  } catch (error) {
437
427
  if (MigrationToolsError.is(error)) {
438
- return notOk(
439
- errorRuntime(error.message, { why: error.why, fix: error.fix, meta: { code: error.code } }),
440
- );
428
+ return notOk(mapMigrationToolsError(error));
441
429
  }
442
430
  return notOk(
443
431
  errorUnexpected(error instanceof Error ? error.message : String(error), {
@@ -30,6 +30,21 @@ export interface ExecuteMigrationApplyOptions<TFamilyId extends string, TTargetI
30
30
  readonly onProgress?: OnControlProgress;
31
31
  }
32
32
 
33
+ /**
34
+ * Apply a sequence of migration packages against the configured driver.
35
+ *
36
+ * Validates the path's continuity (origin → ... → destination, no gaps),
37
+ * then drives the family/target's migration runner over each package's
38
+ * operations in order, surfacing per-migration progress through `onProgress`.
39
+ *
40
+ * The `pendingMigrations` parameter is trusted input. Callers are responsible
41
+ * for upstream verification of the originating migration packages — typically
42
+ * by loading them via `readMigrationPackage` from
43
+ * `@prisma-next/migration-tools/io`, which performs hash-integrity checks at
44
+ * the load boundary. This operation does not re-verify the packages and
45
+ * assumes the `(metadata, ops)` pairs on disk have not been tampered with
46
+ * since emit.
47
+ */
33
48
  export async function executeMigrationApply<TFamilyId extends string, TTargetId extends string>(
34
49
  options: ExecuteMigrationApplyOptions<TFamilyId, TTargetId>,
35
50
  ): Promise<MigrationApplyResult> {
@@ -41,13 +41,13 @@ import { fileURLToPath } from 'node:url';
41
41
  import { CliStructuredError, errorMigrationCliInvalidConfigArg } from '@prisma-next/errors/control';
42
42
  import { errorMigrationTargetMismatch } from '@prisma-next/errors/migration';
43
43
  import { createControlStack } from '@prisma-next/framework-components/control';
44
+ import type { MigrationMetadata } from '@prisma-next/migration-tools/metadata';
44
45
  import {
45
46
  buildMigrationArtifacts,
46
47
  isDirectEntrypoint,
47
48
  type Migration,
48
49
  printMigrationHelp,
49
50
  } from '@prisma-next/migration-tools/migration';
50
- import type { MigrationManifest } from '@prisma-next/migration-tools/types';
51
51
  import { dirname, join } from 'pathe';
52
52
  import { loadConfig } from './config-loader';
53
53
 
@@ -202,16 +202,23 @@ export class MigrationCLI {
202
202
  * `buildMigrationArtifacts` so the pure builder can preserve fields owned
203
203
  * by `migration plan` (contract bookends, hints, labels, `createdAt`)
204
204
  * across re-emits.
205
+ *
206
+ * Author-time path: this loader is non-verifying by design. Hash mismatch
207
+ * is the *expected* outcome of a re-author (the developer's source
208
+ * changes invalidate the prior hash by construction), and verification
209
+ * here would block legitimate regenerations. Apply-time consumers always
210
+ * route through the verifying `readMigrationPackage` in
211
+ * `@prisma-next/migration-tools/io` instead.
205
212
  */
206
- function readExistingManifest(manifestPath: string): Partial<MigrationManifest> | null {
213
+ function readExistingMetadata(metadataPath: string): Partial<MigrationMetadata> | null {
207
214
  let raw: string;
208
215
  try {
209
- raw = readFileSync(manifestPath, 'utf-8');
216
+ raw = readFileSync(metadataPath, 'utf-8');
210
217
  } catch {
211
218
  return null;
212
219
  }
213
220
  try {
214
- return JSON.parse(raw) as Partial<MigrationManifest>;
221
+ return JSON.parse(raw) as Partial<MigrationMetadata>;
215
222
  } catch {
216
223
  return null;
217
224
  }
@@ -236,19 +243,19 @@ function serializeMigrationToDisk(
236
243
  migrationDir: string,
237
244
  dryRun: boolean,
238
245
  ): void {
239
- const manifestPath = join(migrationDir, 'migration.json');
240
- const existing = readExistingManifest(manifestPath);
241
- const { opsJson, manifestJson } = buildMigrationArtifacts(instance, existing);
246
+ const metadataPath = join(migrationDir, 'migration.json');
247
+ const existing = readExistingMetadata(metadataPath);
248
+ const { opsJson, metadataJson } = buildMigrationArtifacts(instance, existing);
242
249
 
243
250
  if (dryRun) {
244
- process.stdout.write(`--- migration.json ---\n${manifestJson}\n`);
251
+ process.stdout.write(`--- migration.json ---\n${metadataJson}\n`);
245
252
  process.stdout.write('--- ops.json ---\n');
246
253
  process.stdout.write(`${opsJson}\n`);
247
254
  return;
248
255
  }
249
256
 
250
257
  writeFileSync(join(migrationDir, 'ops.json'), opsJson);
251
- writeFileSync(manifestPath, manifestJson);
258
+ writeFileSync(metadataPath, metadataJson);
252
259
 
253
260
  process.stdout.write(`Wrote ops.json + migration.json to ${migrationDir}\n`);
254
261
  }
@@ -3,7 +3,8 @@
3
3
  * CLI-specific errors (e.g., Commander.js argument validation) can be added here if needed.
4
4
  */
5
5
  export type { CliErrorConflict, CliErrorEnvelope } from '@prisma-next/errors/control';
6
- export {
6
+
7
+ import {
7
8
  CliStructuredError,
8
9
  errorConfigFileNotFound,
9
10
  errorConfigValidation,
@@ -20,6 +21,26 @@ export {
20
21
  errorTargetMigrationNotSupported,
21
22
  errorUnexpected,
22
23
  } from '@prisma-next/errors/control';
24
+ import { errorRuntime } from '@prisma-next/errors/execution';
25
+ import type { MigrationToolsError } from '@prisma-next/migration-tools/errors';
26
+
27
+ export {
28
+ CliStructuredError,
29
+ errorConfigFileNotFound,
30
+ errorConfigValidation,
31
+ errorContractConfigMissing,
32
+ errorContractMissingExtensionPacks,
33
+ errorContractValidationFailed,
34
+ errorDatabaseConnectionRequired,
35
+ errorDriverRequired,
36
+ errorFamilyReadMarkerSqlRequired,
37
+ errorFileNotFound,
38
+ errorMigrationCliInvalidConfigArg,
39
+ errorMigrationPlanningFailed,
40
+ errorQueryRunnerFactoryRequired,
41
+ errorTargetMigrationNotSupported,
42
+ errorUnexpected,
43
+ };
23
44
  export {
24
45
  ERROR_CODE_DESTRUCTIVE_CHANGES,
25
46
  errorDestructiveChanges,
@@ -38,3 +59,26 @@ export {
38
59
  errorUnfilledPlaceholder,
39
60
  placeholder,
40
61
  } from '@prisma-next/errors/migration';
62
+
63
+ /**
64
+ * Maps a `MigrationToolsError` raised by the migration-tools loader/graph
65
+ * surface (`readMigrationPackage`, `readMigrationsDir`, `readRefs`,
66
+ * `resolveRef`, `reconstructGraph`, ...) into a CLI `errorRuntime` envelope.
67
+ *
68
+ * The full `error.details` payload is forwarded into `meta` so machine
69
+ * consumers (`--json`) see structural fields like `dir`, `storedHash`,
70
+ * `computedHash` (for `MIGRATION.HASH_MISMATCH`) alongside the stable
71
+ * `code`. The user-visible `summary`/`why`/`fix` text is unchanged.
72
+ *
73
+ * Callers are expected to gate on `MigrationToolsError.is(error)` first
74
+ * (mirroring the original inline pattern); non-`MigrationToolsError`
75
+ * values are caller-classified (rethrow, wrap with command-specific
76
+ * `errorUnexpected`, etc.).
77
+ */
78
+ export function mapMigrationToolsError(error: MigrationToolsError): CliStructuredError {
79
+ return errorRuntime(error.message, {
80
+ why: error.why,
81
+ fix: error.fix,
82
+ meta: { code: error.code, ...(error.details ?? {}) },
83
+ });
84
+ }