@prisma-next/cli 0.4.0-dev.4 → 0.4.0-dev.6

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 (81) hide show
  1. package/README.md +8 -7
  2. package/dist/cli-errors-BUuJr6py.mjs +5 -0
  3. package/dist/{cli-errors-DStABy9d.d.mts → cli-errors-Dic2eADK.d.mts} +1 -0
  4. package/dist/cli.mjs +9 -9
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/{client-tdnbk0OR.mjs → client-DUs1DH-1.mjs} +2 -2
  7. package/dist/{client-tdnbk0OR.mjs.map → client-DUs1DH-1.mjs.map} +1 -1
  8. package/dist/commands/contract-emit.mjs +1 -1
  9. package/dist/commands/contract-infer.mjs +1 -1
  10. package/dist/commands/db-init.mjs +5 -5
  11. package/dist/commands/db-schema.mjs +3 -3
  12. package/dist/commands/db-sign.mjs +4 -4
  13. package/dist/commands/db-update.mjs +5 -5
  14. package/dist/commands/db-verify.mjs +4 -4
  15. package/dist/commands/migration-apply.mjs +6 -6
  16. package/dist/commands/migration-apply.mjs.map +1 -1
  17. package/dist/commands/migration-emit.d.mts +38 -0
  18. package/dist/commands/migration-emit.d.mts.map +1 -0
  19. package/dist/commands/migration-emit.mjs +81 -0
  20. package/dist/commands/migration-emit.mjs.map +1 -0
  21. package/dist/commands/migration-new.mjs +5 -5
  22. package/dist/commands/migration-new.mjs.map +1 -1
  23. package/dist/commands/migration-plan.d.mts.map +1 -1
  24. package/dist/commands/migration-plan.mjs +13 -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.mjs +2 -2
  28. package/dist/commands/migration-show.d.mts +1 -1
  29. package/dist/commands/migration-show.mjs +4 -4
  30. package/dist/commands/migration-show.mjs.map +1 -1
  31. package/dist/commands/migration-status.mjs +1 -1
  32. package/dist/{contract-emit-Ctn6mH9H.mjs → contract-emit-7jqH6dq9.mjs} +5 -5
  33. package/dist/{contract-emit-Ctn6mH9H.mjs.map → contract-emit-7jqH6dq9.mjs.map} +1 -1
  34. package/dist/contract-emit-C2_J2U7A.mjs +4 -0
  35. package/dist/{contract-emit-CRoS1nx5.mjs → contract-emit-CKig_Lra.mjs} +4 -4
  36. package/dist/{contract-emit-CRoS1nx5.mjs.map → contract-emit-CKig_Lra.mjs.map} +1 -1
  37. package/dist/{contract-infer-Ba1SE57Q.mjs → contract-infer-BFlIbyl9.mjs} +3 -3
  38. package/dist/{contract-infer-Ba1SE57Q.mjs.map → contract-infer-BFlIbyl9.mjs.map} +1 -1
  39. package/dist/exports/control-api.mjs +2 -2
  40. package/dist/exports/index.mjs +1 -1
  41. package/dist/{framework-components-BAsliT4V.mjs → framework-components-Bsr1GaIj.mjs} +2 -2
  42. package/dist/{framework-components-BAsliT4V.mjs.map → framework-components-Bsr1GaIj.mjs.map} +1 -1
  43. package/dist/{init-Dx5uJ-6Q.mjs → init-DlFLMBaU.mjs} +2 -2
  44. package/dist/{init-Dx5uJ-6Q.mjs.map → init-DlFLMBaU.mjs.map} +1 -1
  45. package/dist/{inspect-live-schema-gYQiWfpl.mjs → inspect-live-schema-gjmUZ8xm.mjs} +4 -4
  46. package/dist/{inspect-live-schema-gYQiWfpl.mjs.map → inspect-live-schema-gjmUZ8xm.mjs.map} +1 -1
  47. package/dist/{migration-command-scaffold-x4n_ZhAh.mjs → migration-command-scaffold-CfllKppa.mjs} +4 -4
  48. package/dist/{migration-command-scaffold-x4n_ZhAh.mjs.map → migration-command-scaffold-CfllKppa.mjs.map} +1 -1
  49. package/dist/migration-emit-dRXV6QSz.mjs +72 -0
  50. package/dist/migration-emit-dRXV6QSz.mjs.map +1 -0
  51. package/dist/{migration-status-DyVDf5NI.mjs → migration-status-BwKCQB_a.mjs} +5 -5
  52. package/dist/{migration-status-DyVDf5NI.mjs.map → migration-status-BwKCQB_a.mjs.map} +1 -1
  53. package/dist/{migrations-DTZBYXm1.mjs → migrations-BIsjFjSV.mjs} +6 -15
  54. package/dist/migrations-BIsjFjSV.mjs.map +1 -0
  55. package/dist/{result-handler-oK_vA-Fn.mjs → result-handler-AFK4hxyX.mjs} +2 -2
  56. package/dist/result-handler-AFK4hxyX.mjs.map +1 -0
  57. package/dist/{validate-contract-deps-esa-VQ0h.mjs → validate-contract-deps-DBH6iTAD.mjs} +1 -1
  58. package/dist/{validate-contract-deps-esa-VQ0h.mjs.map → validate-contract-deps-DBH6iTAD.mjs.map} +1 -1
  59. package/dist/{verify-DlFQ2FOw.mjs → verify-C56CuQc7.mjs} +2 -2
  60. package/dist/{verify-DlFQ2FOw.mjs.map → verify-C56CuQc7.mjs.map} +1 -1
  61. package/package.json +19 -19
  62. package/src/cli.ts +4 -4
  63. package/src/commands/migration-apply.ts +2 -2
  64. package/src/commands/migration-emit.ts +132 -0
  65. package/src/commands/migration-new.ts +3 -3
  66. package/src/commands/migration-plan.ts +15 -57
  67. package/src/commands/migration-show.ts +1 -1
  68. package/src/commands/migration-status.ts +1 -1
  69. package/src/lib/migration-emit.ts +113 -0
  70. package/src/utils/cli-errors.ts +5 -0
  71. package/src/utils/formatters/help.ts +1 -1
  72. package/src/utils/formatters/migrations.ts +6 -20
  73. package/dist/cli-errors-BDCYR5ap.mjs +0 -4
  74. package/dist/commands/migration-verify.d.mts +0 -16
  75. package/dist/commands/migration-verify.d.mts.map +0 -1
  76. package/dist/commands/migration-verify.mjs +0 -110
  77. package/dist/commands/migration-verify.mjs.map +0 -1
  78. package/dist/contract-emit-CGpb33vs.mjs +0 -4
  79. package/dist/migrations-DTZBYXm1.mjs.map +0 -1
  80. package/dist/result-handler-oK_vA-Fn.mjs.map +0 -1
  81. package/src/commands/migration-verify.ts +0 -180
@@ -0,0 +1,132 @@
1
+ import { MigrationToolsError } from '@prisma-next/migration-tools/types';
2
+ import { notOk, ok, type Result } from '@prisma-next/utils/result';
3
+ import { Command } from 'commander';
4
+ import { loadConfig } from '../config-loader';
5
+ import { emitMigration } from '../lib/migration-emit';
6
+ import {
7
+ CliStructuredError,
8
+ errorRuntime,
9
+ errorTargetMigrationNotSupported,
10
+ errorUnexpected,
11
+ } from '../utils/cli-errors';
12
+ import {
13
+ addGlobalOptions,
14
+ getTargetMigrations,
15
+ setCommandDescriptions,
16
+ setCommandExamples,
17
+ } from '../utils/command-helpers';
18
+ import { formatMigrationEmitCommandOutput } from '../utils/formatters/migrations';
19
+ import { formatStyledHeader } from '../utils/formatters/styled';
20
+ import { assertFrameworkComponentsCompatible } from '../utils/framework-components';
21
+ import type { CommonCommandOptions } from '../utils/global-flags';
22
+ import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
23
+ import { handleResult } from '../utils/result-handler';
24
+ import { TerminalUI } from '../utils/terminal-ui';
25
+
26
+ export interface MigrationEmitOptions extends CommonCommandOptions {
27
+ readonly dir: string;
28
+ readonly config?: string;
29
+ }
30
+
31
+ export interface MigrationEmitResult {
32
+ readonly ok: boolean;
33
+ readonly dir: string;
34
+ readonly migrationId: string;
35
+ readonly summary: string;
36
+ }
37
+
38
+ async function executeMigrationEmitCommand(
39
+ options: MigrationEmitOptions,
40
+ flags: GlobalFlags,
41
+ ui: TerminalUI,
42
+ ): Promise<Result<MigrationEmitResult, CliStructuredError>> {
43
+ const dir = options.dir;
44
+
45
+ if (!flags.json && !flags.quiet) {
46
+ const header = formatStyledHeader({
47
+ command: 'migration emit',
48
+ description: 'Emit ops.json from migration.ts and compute migrationId',
49
+ details: [{ label: 'dir', value: dir }],
50
+ flags,
51
+ });
52
+ ui.stderr(header);
53
+ }
54
+
55
+ try {
56
+ const config = await loadConfig(options.config);
57
+ const migrations = getTargetMigrations(config.target);
58
+ if (!migrations) {
59
+ throw errorTargetMigrationNotSupported({
60
+ why: `Target "${config.target.id}" does not support migrations`,
61
+ });
62
+ }
63
+ const frameworkComponents = assertFrameworkComponentsCompatible(
64
+ config.family.familyId,
65
+ config.target.targetId,
66
+ [config.target, config.adapter, ...(config.extensionPacks ?? [])],
67
+ );
68
+
69
+ const { migrationId } = await emitMigration(dir, {
70
+ targetId: config.target.targetId,
71
+ migrations,
72
+ frameworkComponents,
73
+ });
74
+
75
+ return ok({
76
+ ok: true,
77
+ dir,
78
+ migrationId,
79
+ summary: `Emitted ops.json and attested migrationId: ${migrationId}`,
80
+ });
81
+ } catch (error) {
82
+ if (CliStructuredError.is(error)) {
83
+ return notOk(error);
84
+ }
85
+ if (MigrationToolsError.is(error)) {
86
+ return notOk(
87
+ errorRuntime(error.message, {
88
+ why: error.why,
89
+ fix: error.fix,
90
+ meta: { code: error.code, ...(error.details ?? {}) },
91
+ }),
92
+ );
93
+ }
94
+ return notOk(
95
+ errorUnexpected(error instanceof Error ? error.message : String(error), {
96
+ why: `Failed to emit migration: ${error instanceof Error ? error.message : String(error)}`,
97
+ }),
98
+ );
99
+ }
100
+ }
101
+
102
+ export function createMigrationEmitCommand(): Command {
103
+ const command = new Command('emit');
104
+ setCommandDescriptions(
105
+ command,
106
+ 'Emit ops.json from migration.ts and compute migrationId',
107
+ 'Evaluates migration.ts in the package directory, resolves it to ops.json,\n' +
108
+ 'then computes and persists the content-addressed migrationId in manifest.json.',
109
+ );
110
+ setCommandExamples(command, ['prisma-next migration emit --dir migrations/20250101-add-users']);
111
+ addGlobalOptions(command)
112
+ .requiredOption('--dir <path>', 'Path to the migration package directory')
113
+ .option('--config <path>', 'Path to prisma-next.config.ts')
114
+ .action(async (options: MigrationEmitOptions) => {
115
+ const flags = parseGlobalFlags(options);
116
+ const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
117
+
118
+ const result = await executeMigrationEmitCommand(options, flags, ui);
119
+
120
+ const exitCode = handleResult(result, flags, ui, (emitResult) => {
121
+ if (flags.json) {
122
+ ui.output(JSON.stringify(emitResult, null, 2));
123
+ } else if (!flags.quiet) {
124
+ ui.log(formatMigrationEmitCommandOutput(emitResult, flags));
125
+ }
126
+ });
127
+
128
+ process.exit(exitCode);
129
+ });
130
+
131
+ return command;
132
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `migration new` — scaffolds a migration package with a migration.ts file
3
3
  * for manual authoring. The user writes operation descriptors and data
4
- * transforms; `migration verify` resolves them to ops.json.
4
+ * transforms; `migration emit` resolves them to ops.json.
5
5
  */
6
6
 
7
7
  import { readFileSync } from 'node:fs';
@@ -214,7 +214,7 @@ export function createMigrationNewCommand(): Command {
214
214
  'Scaffold a new migration for manual authoring',
215
215
  'Creates a migration package with a migration.ts file for manual authoring.\n' +
216
216
  'Write operation descriptors and data transforms in migration.ts, then run\n' +
217
- '`migration verify` to resolve and attest the package.',
217
+ '`migration emit` to resolve and attest the package.',
218
218
  );
219
219
  setCommandExamples(command, [
220
220
  'prisma-next migration new --name split-name',
@@ -248,7 +248,7 @@ export function createMigrationNewCommand(): Command {
248
248
  ui.output(` from: ${value.from}`);
249
249
  ui.output(` to: ${value.to}`);
250
250
  ui.output(
251
- `\nEdit migration.ts, then run \`prisma-next migration verify --dir ${value.dir}\` to attest.`,
251
+ `\nEdit migration.ts, then run \`prisma-next migration emit --dir "${value.dir}"\` to attest.`,
252
252
  );
253
253
  }
254
254
  });
@@ -1,24 +1,16 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import type { Contract } from '@prisma-next/contract/types';
3
- import type { OperationDescriptor } from '@prisma-next/framework-components/control';
4
- import { attestMigration } from '@prisma-next/migration-tools/attestation';
5
3
  import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
6
4
  import { findLatestMigration } from '@prisma-next/migration-tools/dag';
7
- import {
8
- formatMigrationDirName,
9
- writeMigrationOps,
10
- writeMigrationPackage,
11
- } from '@prisma-next/migration-tools/io';
12
- import {
13
- evaluateMigrationTs,
14
- scaffoldMigrationTs,
15
- } from '@prisma-next/migration-tools/migration-ts';
5
+ import { formatMigrationDirName, writeMigrationPackage } from '@prisma-next/migration-tools/io';
6
+ import { scaffoldMigrationTs } from '@prisma-next/migration-tools/migration-ts';
16
7
  import { type MigrationManifest, MigrationToolsError } from '@prisma-next/migration-tools/types';
17
8
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
18
9
  import { Command } from 'commander';
19
10
  import { join, relative } from 'pathe';
20
11
  import { loadConfig } from '../config-loader';
21
12
  import { extractSqlDdl } from '../control-api/operations/extract-sql-ddl';
13
+ import { emitMigration } from '../lib/migration-emit';
22
14
  import {
23
15
  type CliErrorConflict,
24
16
  CliStructuredError,
@@ -178,7 +170,7 @@ async function executeMigrationPlanCommand(
178
170
  return notOk(
179
171
  errorRuntime('A draft migration to this contract already exists', {
180
172
  why: `Draft migration at "${existingDraft.dirName}" already targets ${toStorageHash}`,
181
- fix: `Run 'prisma-next migration verify --dir ${migrationsRelative}/${existingDraft.dirName}' to attest it, or delete it and re-plan.`,
173
+ fix: `Run 'prisma-next migration emit --dir ${migrationsRelative}/${existingDraft.dirName}' to attest it, or delete it and re-plan.`,
182
174
  }),
183
175
  );
184
176
  }
@@ -302,56 +294,22 @@ async function executeMigrationPlanCommand(
302
294
  };
303
295
 
304
296
  try {
305
- // Always write migration.ts with the descriptors
306
- // Write package with empty ops first (draft)
307
297
  await writeMigrationPackage(packageDir, manifest, []);
308
298
  await scaffoldMigrationTs(packageDir, {
309
299
  descriptors: descriptorResult.descriptors,
310
300
  contractJsonPath: contractPathAbsolute,
311
301
  });
312
302
 
313
- if (descriptorResult.needsDataMigration) {
314
- // Draft user must fill in dataTransform and run verify
315
- const result: MigrationPlanResult = {
316
- ok: true,
317
- noOp: false,
318
- from: fromHash,
319
- to: toStorageHash,
320
- dir: relative(process.cwd(), packageDir),
321
- operations: descriptorResult.descriptors.map((d) => ({
322
- id: (d as { kind: string }).kind,
323
- label: (d as { kind: string }).kind,
324
- operationClass: 'data' as const,
325
- })),
326
- sql: [],
327
- summary: `Planned ${descriptorResult.descriptors.length} operation(s) — data migration required. Edit migration.ts and run \`migration verify --dir ${relative(process.cwd(), packageDir)}\` to attest.`,
328
- timings: { total: Date.now() - startTime },
329
- };
330
- return ok(result);
331
- }
332
-
333
- // No data migration — evaluate, resolve, write ops, attest
334
- const evaluatedDescriptors = await evaluateMigrationTs(packageDir);
335
-
336
- if (!migrations.resolveDescriptors) {
337
- throw errorTargetMigrationNotSupported({
338
- why: `Target "${config.target.targetId}" does not implement resolveDescriptors; cannot finalize migration plan with migration.ts`,
339
- });
340
- }
341
-
342
- const resolvedOps = migrations.resolveDescriptors(
343
- evaluatedDescriptors as OperationDescriptor[],
344
- {
345
- fromContract,
346
- toContract: toContractJson,
347
- frameworkComponents,
348
- },
349
- );
350
-
351
- await writeMigrationOps(packageDir, resolvedOps);
352
- const migrationId = await attestMigration(packageDir);
303
+ // Always run emit inline; structured errors thrown during evaluation
304
+ // (e.g. invalid migration.ts shape, missing file) propagate through
305
+ // emitMigration to the CLI envelope.
306
+ const { operations, migrationId } = await emitMigration(packageDir, {
307
+ targetId: config.target.targetId,
308
+ migrations,
309
+ frameworkComponents,
310
+ });
353
311
 
354
- const sql = extractSqlDdl(resolvedOps);
312
+ const sql = extractSqlDdl(operations);
355
313
  const result: MigrationPlanResult = {
356
314
  ok: true,
357
315
  noOp: false,
@@ -359,13 +317,13 @@ async function executeMigrationPlanCommand(
359
317
  to: toStorageHash,
360
318
  migrationId,
361
319
  dir: relative(process.cwd(), packageDir),
362
- operations: resolvedOps.map((op) => ({
320
+ operations: operations.map((op) => ({
363
321
  id: op.id,
364
322
  label: op.label,
365
323
  operationClass: op.operationClass,
366
324
  })),
367
325
  sql,
368
- summary: `Planned ${resolvedOps.length} operation(s)`,
326
+ summary: `Planned ${operations.length} operation(s)`,
369
327
  timings: { total: Date.now() - startTime },
370
328
  };
371
329
  return ok(result);
@@ -137,7 +137,7 @@ async function executeMigrationShowCommand(
137
137
  return notOk(
138
138
  errorRuntime('No attested migrations found', {
139
139
  why: `All migrations in ${migrationsRelative} are drafts (migrationId: null)`,
140
- fix: 'Run `prisma-next migration verify --dir <path>` to attest a draft migration.',
140
+ fix: 'Run `prisma-next migration emit --dir <path>` to attest a draft migration.',
141
141
  }),
142
142
  );
143
143
  }
@@ -460,7 +460,7 @@ async function executeMigrationStatusCommand(
460
460
  severity: 'warn',
461
461
  message: `${drafts.length} draft migration(s) found: ${drafts.map((d) => d.dirName).join(', ')}`,
462
462
  hints: [
463
- "Run 'prisma-next migration verify --dir <path>' to attest draft migrations before applying",
463
+ "Run 'prisma-next migration emit --dir <path>' to attest draft migrations before applying",
464
464
  ],
465
465
  });
466
466
  }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Shared helper for emitting `ops.json` from a migration package's
3
+ * `migration.ts`.
4
+ *
5
+ * Two flows are dispatched here:
6
+ * - Descriptor flow (Postgres): the framework evaluates `migration.ts` (which
7
+ * re-exports the planner's descriptor list), calls the target's
8
+ * `resolveDescriptors` to produce display-oriented operations, and writes
9
+ * `ops.json` + `manifest.json`.
10
+ * - Class flow (Mongo): the target owns the source-loading and ops
11
+ * serialization step via its `emit` capability. The capability
12
+ * dynamic-imports `migration.ts`, invokes its class to produce
13
+ * operations, and calls `writeMigrationOps`. The framework helper
14
+ * then attests the package (single source of truth for
15
+ * `migrationId`).
16
+ *
17
+ * Used by `migration emit` (always) and `migration plan` (always, after
18
+ * scaffolding `migration.ts`). Both flows run in-process so that structured
19
+ * errors thrown during evaluation propagate as real exceptions and the
20
+ * CLI's error envelope renders them with full structured metadata.
21
+ */
22
+
23
+ import assert from 'node:assert/strict';
24
+ import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
25
+ import type {
26
+ MigrationPlanOperation,
27
+ OperationDescriptor,
28
+ TargetMigrationsCapability,
29
+ } from '@prisma-next/framework-components/control';
30
+ import { attestMigration } from '@prisma-next/migration-tools/attestation';
31
+ import { readMigrationPackage, writeMigrationOps } from '@prisma-next/migration-tools/io';
32
+ import { evaluateMigrationTs, hasMigrationTs } from '@prisma-next/migration-tools/migration-ts';
33
+ import { errorMigrationFileMissing, errorTargetMigrationNotSupported } from '../utils/cli-errors';
34
+
35
+ /**
36
+ * Context passed to `emitMigration`. Captures everything the helper needs to
37
+ * dispatch to the right flow without re-loading the config.
38
+ */
39
+ export interface EmitMigrationContext {
40
+ readonly targetId: string;
41
+ readonly migrations: TargetMigrationsCapability;
42
+ readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<string, string>>;
43
+ }
44
+
45
+ /**
46
+ * Result of a successful emit: the operations that were written to `ops.json`
47
+ * (display-oriented shape) and the content-addressed migrationId persisted to
48
+ * `manifest.json`.
49
+ */
50
+ export interface EmitMigrationResult {
51
+ readonly operations: readonly MigrationPlanOperation[];
52
+ readonly migrationId: string;
53
+ }
54
+
55
+ /**
56
+ * Emit `ops.json` and attest `migrationId` for the migration package at `dir`.
57
+ *
58
+ * Dispatches to descriptor flow when the target implements `resolveDescriptors`,
59
+ * otherwise to the target's `emit` capability. Throws a structured error if
60
+ * `migration.ts` is missing or the target supports neither flow. Other
61
+ * structured errors thrown during evaluation propagate unchanged.
62
+ */
63
+ export async function emitMigration(
64
+ dir: string,
65
+ ctx: EmitMigrationContext,
66
+ ): Promise<EmitMigrationResult> {
67
+ if (!(await hasMigrationTs(dir))) {
68
+ throw errorMigrationFileMissing(dir);
69
+ }
70
+
71
+ if (ctx.migrations.resolveDescriptors) {
72
+ return emitDescriptorFlow(dir, ctx.migrations, ctx);
73
+ }
74
+
75
+ if (ctx.migrations.emit) {
76
+ const operations = await ctx.migrations.emit({
77
+ dir,
78
+ frameworkComponents: ctx.frameworkComponents,
79
+ });
80
+ const migrationId = await attestMigration(dir);
81
+ return { operations, migrationId };
82
+ }
83
+
84
+ throw errorTargetMigrationNotSupported({
85
+ why: `Target "${ctx.targetId}" does not implement resolveDescriptors or emit; cannot emit a migration package`,
86
+ });
87
+ }
88
+
89
+ /**
90
+ * Descriptor flow: evaluate `migration.ts` to obtain a list of operation
91
+ * descriptors, hand them to the target's `resolveDescriptors` along with the
92
+ * manifest's contract bookends, then persist `ops.json` and attest the package.
93
+ */
94
+ async function emitDescriptorFlow(
95
+ dir: string,
96
+ migrations: TargetMigrationsCapability,
97
+ ctx: EmitMigrationContext,
98
+ ): Promise<EmitMigrationResult> {
99
+ assert(
100
+ migrations.resolveDescriptors,
101
+ 'emitDescriptorFlow requires resolveDescriptors; gated by caller',
102
+ );
103
+ const pkg = await readMigrationPackage(dir);
104
+ const descriptors = await evaluateMigrationTs(dir);
105
+ const operations = migrations.resolveDescriptors(descriptors as OperationDescriptor[], {
106
+ fromContract: pkg.manifest.fromContract,
107
+ toContract: pkg.manifest.toContract,
108
+ frameworkComponents: ctx.frameworkComponents,
109
+ });
110
+ await writeMigrationOps(dir, operations);
111
+ const migrationId = await attestMigration(dir);
112
+ return { operations, migrationId };
113
+ }
@@ -30,3 +30,8 @@ export {
30
30
  errorSchemaVerificationFailed,
31
31
  errorTargetMismatch,
32
32
  } from '@prisma-next/errors/execution';
33
+ export {
34
+ errorMigrationFileMissing,
35
+ errorMigrationInvalidDefaultExport,
36
+ errorMigrationPlanNotArray,
37
+ } from '@prisma-next/errors/migration';
@@ -137,7 +137,7 @@ function getCommandDocsUrl(commandPath: string): string | undefined {
137
137
  'migration apply': 'https://pris.ly/migration-apply',
138
138
  'migration show': 'https://pris.ly/migration-show',
139
139
  'migration status': 'https://pris.ly/migration-status',
140
- 'migration verify': 'https://pris.ly/migration-verify',
140
+ 'migration emit': 'https://pris.ly/migration-emit',
141
141
  };
142
142
  return docsMap[commandPath];
143
143
  }
@@ -136,9 +136,8 @@ export interface MigrationApplyCommandOutputResult {
136
136
  };
137
137
  }
138
138
 
139
- export interface MigrationVerifyCommandOutputResult {
140
- readonly status: 'verified' | 'attested';
141
- readonly migrationId?: string;
139
+ export interface MigrationEmitCommandOutputResult {
140
+ readonly migrationId: string;
142
141
  }
143
142
 
144
143
  export function formatMigrationApplyCommandOutput(
@@ -183,8 +182,8 @@ export function formatMigrationApplyCommandOutput(
183
182
  return lines.join('\n');
184
183
  }
185
184
 
186
- export function formatMigrationVerifyCommandOutput(
187
- result: MigrationVerifyCommandOutputResult,
185
+ export function formatMigrationEmitCommandOutput(
186
+ result: MigrationEmitCommandOutputResult,
188
187
  flags: GlobalFlags,
189
188
  ): string {
190
189
  if (flags.quiet) {
@@ -194,23 +193,10 @@ export function formatMigrationVerifyCommandOutput(
194
193
  const lines: string[] = [];
195
194
  const useColor = flags.color !== false;
196
195
  const formatGreen = createColorFormatter(useColor, green);
197
- const formatYellow = createColorFormatter(useColor, yellow);
198
196
  const formatDimText = (text: string) => formatDim(useColor, text);
199
197
 
200
- switch (result.status) {
201
- case 'verified':
202
- lines.push(`${formatGreen('✔')} Migration verified`);
203
- if (result.migrationId) {
204
- lines.push(formatDimText(` migrationId: ${result.migrationId}`));
205
- }
206
- break;
207
- case 'attested':
208
- lines.push(`${formatYellow('◉')} Draft migration attested`);
209
- if (result.migrationId) {
210
- lines.push(formatDimText(` migrationId: ${result.migrationId}`));
211
- }
212
- break;
213
- }
198
+ lines.push(`${formatGreen('✔')} Emitted ops.json and attested migration`);
199
+ lines.push(formatDimText(` migrationId: ${result.migrationId}`));
214
200
 
215
201
  return lines.join('\n');
216
202
  }
@@ -1,4 +0,0 @@
1
- import { CliStructuredError, errorConfigValidation as errorConfigValidation$1, errorContractConfigMissing as errorContractConfigMissing$1, errorContractValidationFailed, errorDatabaseConnectionRequired, errorDriverRequired, errorFileNotFound, errorMigrationPlanningFailed, errorTargetMigrationNotSupported, errorUnexpected as errorUnexpected$1 } from "@prisma-next/errors/control";
2
- import { ERROR_CODE_DESTRUCTIVE_CHANGES, errorDestructiveChanges, errorHashMismatch, errorMarkerMissing, errorRunnerFailed, errorRuntime as errorRuntime$1, errorTargetMismatch } from "@prisma-next/errors/execution";
3
-
4
- export { errorUnexpected$1 as _, errorContractValidationFailed as a, errorDriverRequired as c, errorMarkerMissing as d, errorMigrationPlanningFailed as f, errorTargetMismatch as g, errorTargetMigrationNotSupported as h, errorContractConfigMissing$1 as i, errorFileNotFound as l, errorRuntime$1 as m, ERROR_CODE_DESTRUCTIVE_CHANGES as n, errorDatabaseConnectionRequired as o, errorRunnerFailed as p, errorConfigValidation$1 as r, errorDestructiveChanges as s, CliStructuredError as t, errorHashMismatch as u };
@@ -1,16 +0,0 @@
1
- import { Command } from "commander";
2
-
3
- //#region src/commands/migration-verify.d.ts
4
- interface MigrationVerifyResult {
5
- readonly ok: boolean;
6
- readonly status: 'verified' | 'attested';
7
- readonly dir: string;
8
- readonly migrationId?: string;
9
- readonly storedMigrationId?: string;
10
- readonly computedMigrationId?: string;
11
- readonly summary: string;
12
- }
13
- declare function createMigrationVerifyCommand(): Command;
14
- //#endregion
15
- export { MigrationVerifyResult, createMigrationVerifyCommand };
16
- //# sourceMappingURL=migration-verify.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migration-verify.d.mts","names":[],"sources":["../../src/commands/migration-verify.ts"],"sourcesContent":[],"mappings":";;;UAkCiB,qBAAA;;EAAA,SAAA,MAAA,EAAA,UAAqB,GAAA,UAAA;EAkHtB,SAAA,GAAA,EAAA,MAAA;;;;;;iBAAA,4BAAA,CAAA,GAAgC"}
@@ -1,110 +0,0 @@
1
- import { t as loadConfig } from "../config-loader-C4VXKl8f.mjs";
2
- import { _ as errorUnexpected, h as errorTargetMigrationNotSupported, m as errorRuntime, t as CliStructuredError } from "../cli-errors-BDCYR5ap.mjs";
3
- import { t as assertFrameworkComponentsCompatible } from "../framework-components-BAsliT4V.mjs";
4
- import { _ as formatStyledHeader, d as setCommandExamples, m as parseGlobalFlags, n as addGlobalOptions, r as getTargetMigrations, t as handleResult, u as setCommandDescriptions } from "../result-handler-oK_vA-Fn.mjs";
5
- import { t as TerminalUI } from "../terminal-ui-C5k88MmW.mjs";
6
- import { o as formatMigrationVerifyCommandOutput } from "../migrations-DTZBYXm1.mjs";
7
- import { Command } from "commander";
8
- import { notOk, ok } from "@prisma-next/utils/result";
9
- import { ifDefined } from "@prisma-next/utils/defined";
10
- import { readMigrationPackage, writeMigrationOps } from "@prisma-next/migration-tools/io";
11
- import { MigrationToolsError } from "@prisma-next/migration-tools/types";
12
- import { evaluateMigrationTs, hasMigrationTs } from "@prisma-next/migration-tools/migration-ts";
13
- import { attestMigration, verifyMigration } from "@prisma-next/migration-tools/attestation";
14
-
15
- //#region src/commands/migration-verify.ts
16
- async function executeMigrationVerifyCommand(options, flags, ui) {
17
- const dir = options.dir;
18
- if (!flags.json && !flags.quiet) {
19
- const header = formatStyledHeader({
20
- command: "migration verify",
21
- description: "Verify migration package integrity",
22
- details: [{
23
- label: "dir",
24
- value: dir
25
- }],
26
- flags
27
- });
28
- ui.stderr(header);
29
- }
30
- try {
31
- if (await hasMigrationTs(dir)) {
32
- const pkg = await readMigrationPackage(dir);
33
- const descriptors = await evaluateMigrationTs(dir);
34
- const config = await loadConfig(options.config);
35
- const migrations = getTargetMigrations(config.target);
36
- if (!migrations?.resolveDescriptors) throw errorTargetMigrationNotSupported({ why: `Target "${config.target.targetId}" does not implement resolveDescriptors; cannot verify a migration package that uses migration.ts` });
37
- const frameworkComponents = assertFrameworkComponentsCompatible(config.family.familyId, config.target.targetId, [
38
- config.target,
39
- config.adapter,
40
- ...config.extensionPacks ?? []
41
- ]);
42
- await writeMigrationOps(dir, migrations.resolveDescriptors(descriptors, {
43
- fromContract: pkg.manifest.fromContract,
44
- toContract: pkg.manifest.toContract,
45
- frameworkComponents
46
- }));
47
- }
48
- const result = await verifyMigration(dir);
49
- if (result.ok) return ok({
50
- ok: true,
51
- status: "verified",
52
- dir,
53
- ...ifDefined("migrationId", result.storedMigrationId),
54
- ...ifDefined("storedMigrationId", result.storedMigrationId),
55
- ...ifDefined("computedMigrationId", result.computedMigrationId),
56
- summary: "Migration package verified — migrationId matches"
57
- });
58
- if (result.reason === "draft") {
59
- const migrationId$1 = await attestMigration(dir);
60
- return ok({
61
- ok: true,
62
- status: "attested",
63
- dir,
64
- migrationId: migrationId$1,
65
- summary: `Draft migration attested with migrationId: ${migrationId$1}`
66
- });
67
- }
68
- const migrationId = await attestMigration(dir);
69
- return ok({
70
- ok: true,
71
- status: "attested",
72
- dir,
73
- migrationId,
74
- summary: `Migration re-attested with migrationId: ${migrationId}`
75
- });
76
- } catch (error) {
77
- if (CliStructuredError.is(error)) return notOk(error);
78
- if (MigrationToolsError.is(error)) return notOk(errorRuntime(error.message, {
79
- why: error.why,
80
- fix: error.fix,
81
- meta: {
82
- code: error.code,
83
- ...error.details ?? {}
84
- }
85
- }));
86
- return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to verify migration: ${error instanceof Error ? error.message : String(error)}` }));
87
- }
88
- }
89
- function createMigrationVerifyCommand() {
90
- const command = new Command("verify");
91
- setCommandDescriptions(command, "Verify a migration package migrationId", "Recomputes the content-addressed migrationId for a migration package and compares\nit against the stored value. Draft migrations (migrationId: null) are automatically\nattested. If migration.ts exists, it is always re-evaluated and ops.json is refreshed.");
92
- setCommandExamples(command, ["prisma-next migration verify --dir migrations/20250101-add-users"]);
93
- addGlobalOptions(command).requiredOption("--dir <path>", "Path to the migration package directory").option("--config <path>", "Path to prisma-next.config.ts (required when migration.ts exists)").action(async (options) => {
94
- const flags = parseGlobalFlags(options);
95
- const ui = new TerminalUI({
96
- color: flags.color,
97
- interactive: flags.interactive
98
- });
99
- const exitCode = handleResult(await executeMigrationVerifyCommand(options, flags, ui), flags, ui, (verifyResult) => {
100
- if (flags.json) ui.output(JSON.stringify(verifyResult, null, 2));
101
- else if (!flags.quiet) ui.log(formatMigrationVerifyCommandOutput(verifyResult, flags));
102
- });
103
- process.exit(exitCode);
104
- });
105
- return command;
106
- }
107
-
108
- //#endregion
109
- export { createMigrationVerifyCommand };
110
- //# sourceMappingURL=migration-verify.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migration-verify.mjs","names":["migrationId"],"sources":["../../src/commands/migration-verify.ts"],"sourcesContent":["import type { OperationDescriptor } from '@prisma-next/framework-components/control';\nimport { attestMigration, verifyMigration } from '@prisma-next/migration-tools/attestation';\nimport { readMigrationPackage, writeMigrationOps } from '@prisma-next/migration-tools/io';\nimport { evaluateMigrationTs, hasMigrationTs } from '@prisma-next/migration-tools/migration-ts';\nimport { MigrationToolsError } from '@prisma-next/migration-tools/types';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { loadConfig } from '../config-loader';\nimport {\n CliStructuredError,\n errorRuntime,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n getTargetMigrations,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport { formatMigrationVerifyCommandOutput } from '../utils/formatters/migrations';\nimport { formatStyledHeader } from '../utils/formatters/styled';\nimport { assertFrameworkComponentsCompatible } from '../utils/framework-components';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface MigrationVerifyOptions extends CommonCommandOptions {\n readonly dir: string;\n readonly config?: string;\n}\n\nexport interface MigrationVerifyResult {\n readonly ok: boolean;\n readonly status: 'verified' | 'attested';\n readonly dir: string;\n readonly migrationId?: string;\n readonly storedMigrationId?: string;\n readonly computedMigrationId?: string;\n readonly summary: string;\n}\n\nasync function executeMigrationVerifyCommand(\n options: MigrationVerifyOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n): Promise<Result<MigrationVerifyResult, CliStructuredError>> {\n const dir = options.dir;\n\n if (!flags.json && !flags.quiet) {\n const header = formatStyledHeader({\n command: 'migration verify',\n description: 'Verify migration package integrity',\n details: [{ label: 'dir', value: dir }],\n flags,\n });\n ui.stderr(header);\n }\n\n try {\n // If migration.ts exists, always evaluate and resolve to ops.json.\n // This ensures ops.json is always fresh relative to migration.ts.\n if (await hasMigrationTs(dir)) {\n const pkg = await readMigrationPackage(dir);\n const descriptors = await evaluateMigrationTs(dir);\n\n const config = await loadConfig(options.config);\n const migrations = getTargetMigrations(config.target);\n if (!migrations?.resolveDescriptors) {\n throw errorTargetMigrationNotSupported({\n why: `Target \"${config.target.targetId}\" does not implement resolveDescriptors; cannot verify a migration package that uses migration.ts`,\n });\n }\n\n const frameworkComponents = assertFrameworkComponentsCompatible(\n config.family.familyId,\n config.target.targetId,\n [config.target, config.adapter, ...(config.extensionPacks ?? [])],\n );\n\n const resolvedOps = migrations.resolveDescriptors(descriptors as OperationDescriptor[], {\n fromContract: pkg.manifest.fromContract,\n toContract: pkg.manifest.toContract,\n frameworkComponents,\n });\n\n await writeMigrationOps(dir, resolvedOps);\n }\n\n // Now verify/attest with the (potentially updated) ops.json\n const result = await verifyMigration(dir);\n\n if (result.ok) {\n return ok({\n ok: true,\n status: 'verified',\n dir,\n ...ifDefined('migrationId', result.storedMigrationId),\n ...ifDefined('storedMigrationId', result.storedMigrationId),\n ...ifDefined('computedMigrationId', result.computedMigrationId),\n summary: 'Migration package verified — migrationId matches',\n });\n }\n\n if (result.reason === 'draft') {\n const migrationId = await attestMigration(dir);\n return ok({\n ok: true,\n status: 'attested',\n dir,\n migrationId,\n summary: `Draft migration attested with migrationId: ${migrationId}`,\n });\n }\n\n // Mismatch — ops.json was just rewritten by migration.ts evaluation above,\n // so this means the stored migrationId is stale. Re-attest.\n const migrationId = await attestMigration(dir);\n return ok({\n ok: true,\n status: 'attested',\n dir,\n migrationId,\n summary: `Migration re-attested with migrationId: ${migrationId}`,\n });\n } catch (error) {\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n if (MigrationToolsError.is(error)) {\n return notOk(\n errorRuntime(error.message, {\n why: error.why,\n fix: error.fix,\n meta: { code: error.code, ...(error.details ?? {}) },\n }),\n );\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to verify migration: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n}\n\nexport function createMigrationVerifyCommand(): Command {\n const command = new Command('verify');\n setCommandDescriptions(\n command,\n 'Verify a migration package migrationId',\n 'Recomputes the content-addressed migrationId for a migration package and compares\\n' +\n 'it against the stored value. Draft migrations (migrationId: null) are automatically\\n' +\n 'attested. If migration.ts exists, it is always re-evaluated and ops.json is refreshed.',\n );\n setCommandExamples(command, ['prisma-next migration verify --dir migrations/20250101-add-users']);\n addGlobalOptions(command)\n .requiredOption('--dir <path>', 'Path to the migration package directory')\n .option('--config <path>', 'Path to prisma-next.config.ts (required when migration.ts exists)')\n .action(async (options: MigrationVerifyOptions) => {\n const flags = parseGlobalFlags(options);\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n\n const result = await executeMigrationVerifyCommand(options, flags, ui);\n\n const exitCode = handleResult(result, flags, ui, (verifyResult) => {\n if (flags.json) {\n ui.output(JSON.stringify(verifyResult, null, 2));\n } else if (!flags.quiet) {\n ui.log(formatMigrationVerifyCommandOutput(verifyResult, flags));\n }\n });\n\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}
@@ -1,4 +0,0 @@
1
- import "./config-loader-C4VXKl8f.mjs";
2
- import { t as executeContractEmit } from "./contract-emit-CRoS1nx5.mjs";
3
-
4
- export { executeContractEmit };