@prisma-next/cli 0.12.0-dev.4 → 0.12.0-dev.41

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 (166) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.mjs +180 -163
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/{client-KgJorIvG.mjs → client-Dk-zRFuT.mjs} +83 -58
  5. package/dist/client-Dk-zRFuT.mjs.map +1 -0
  6. package/dist/{command-helpers-Bbw1GbwL.mjs → command-helpers-xvg9oq4T.mjs} +301 -23
  7. package/dist/command-helpers-xvg9oq4T.mjs.map +1 -0
  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 +4 -5
  11. package/dist/commands/db-init.mjs.map +1 -1
  12. package/dist/commands/db-schema.mjs +3 -3
  13. package/dist/commands/db-sign.mjs +4 -4
  14. package/dist/commands/db-update.d.mts.map +1 -1
  15. package/dist/commands/db-update.mjs +10 -7
  16. package/dist/commands/db-update.mjs.map +1 -1
  17. package/dist/commands/db-verify.mjs +1 -1
  18. package/dist/commands/migrate.d.mts +2 -2
  19. package/dist/commands/migrate.d.mts.map +1 -1
  20. package/dist/commands/migrate.mjs +6 -8
  21. package/dist/commands/migrate.mjs.map +1 -1
  22. package/dist/commands/migration-check.d.mts +55 -1
  23. package/dist/commands/migration-check.d.mts.map +1 -1
  24. package/dist/commands/migration-check.mjs +2 -2
  25. package/dist/commands/migration-graph.d.mts +25 -7
  26. package/dist/commands/migration-graph.d.mts.map +1 -1
  27. package/dist/commands/migration-graph.mjs +170 -2
  28. package/dist/commands/migration-graph.mjs.map +1 -0
  29. package/dist/commands/migration-list.d.mts +24 -26
  30. package/dist/commands/migration-list.d.mts.map +1 -1
  31. package/dist/commands/migration-list.mjs +2 -190
  32. package/dist/commands/migration-log.d.mts +20 -15
  33. package/dist/commands/migration-log.d.mts.map +1 -1
  34. package/dist/commands/migration-log.mjs +1 -137
  35. package/dist/commands/migration-new.mjs +3 -3
  36. package/dist/commands/migration-plan.d.mts +1 -1
  37. package/dist/commands/migration-plan.mjs +1 -1
  38. package/dist/commands/migration-show.d.mts +1 -4
  39. package/dist/commands/migration-show.d.mts.map +1 -1
  40. package/dist/commands/migration-show.mjs +13 -25
  41. package/dist/commands/migration-show.mjs.map +1 -1
  42. package/dist/commands/migration-status.d.mts +41 -141
  43. package/dist/commands/migration-status.d.mts.map +1 -1
  44. package/dist/commands/migration-status.mjs +2 -759
  45. package/dist/commands/ref.d.mts +1 -1
  46. package/dist/commands/ref.mjs +3 -3
  47. package/dist/commands/telemetry/index.d.mts +7 -0
  48. package/dist/commands/telemetry/index.d.mts.map +1 -0
  49. package/dist/commands/telemetry/index.mjs +2 -0
  50. package/dist/{contract-at-errors-BxP-TOMl.mjs → contract-at-errors-Wj3u4Xco.mjs} +2 -2
  51. package/dist/{contract-at-errors-BxP-TOMl.mjs.map → contract-at-errors-Wj3u4Xco.mjs.map} +1 -1
  52. package/dist/{contract-emit-DxcGl4Uq.mjs → contract-emit-CZU0UO6M.mjs} +3 -3
  53. package/dist/{contract-emit-DxcGl4Uq.mjs.map → contract-emit-CZU0UO6M.mjs.map} +1 -1
  54. package/dist/{contract-emit-D-4jrNve.mjs → contract-emit-FetLZ3jn.mjs} +5 -5
  55. package/dist/{contract-emit-D-4jrNve.mjs.map → contract-emit-FetLZ3jn.mjs.map} +1 -1
  56. package/dist/{contract-infer-D8uEbJuu.mjs → contract-infer-3wtOsS_H.mjs} +3 -3
  57. package/dist/{contract-infer-D8uEbJuu.mjs.map → contract-infer-3wtOsS_H.mjs.map} +1 -1
  58. package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs → contract-space-aggregate-loader-BdRPfM3Q.mjs} +63 -5
  59. package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs.map → contract-space-aggregate-loader-BdRPfM3Q.mjs.map} +1 -1
  60. package/dist/{db-verify-v_vUKXTU.mjs → db-verify-BisylXFZ.mjs} +4 -4
  61. package/dist/{db-verify-v_vUKXTU.mjs.map → db-verify-BisylXFZ.mjs.map} +1 -1
  62. package/dist/exports/control-api.d.mts +2 -2
  63. package/dist/exports/control-api.d.mts.map +1 -1
  64. package/dist/exports/control-api.mjs +2 -2
  65. package/dist/exports/index.mjs +1 -1
  66. package/dist/exports/init-output.mjs +1 -1
  67. package/dist/{framework-components-fYXjz_in.mjs → framework-components-Be4inY3I.mjs} +2 -2
  68. package/dist/{framework-components-fYXjz_in.mjs.map → framework-components-Be4inY3I.mjs.map} +1 -1
  69. package/dist/{global-flags-DEHjV8_s.d.mts → global-flags-DG4uY5tV.d.mts} +1 -1
  70. package/dist/{global-flags-DEHjV8_s.d.mts.map → global-flags-DG4uY5tV.d.mts.map} +1 -1
  71. package/dist/{init-Cv9UzWL5.mjs → init-BTE2U7lG.mjs} +5 -58
  72. package/dist/init-BTE2U7lG.mjs.map +1 -0
  73. package/dist/{inspect-live-schema-C6ohV_oQ.mjs → inspect-live-schema-Cy9Y4wsL.mjs} +3 -3
  74. package/dist/{inspect-live-schema-C6ohV_oQ.mjs.map → inspect-live-schema-Cy9Y4wsL.mjs.map} +1 -1
  75. package/dist/{migration-check-BiBJoYYW.mjs → migration-check-CUavU7U9.mjs} +236 -88
  76. package/dist/migration-check-CUavU7U9.mjs.map +1 -0
  77. package/dist/{migration-command-scaffold-CjvwO6at.mjs → migration-command-scaffold-BxOxtyJ6.mjs} +3 -3
  78. package/dist/{migration-command-scaffold-CjvwO6at.mjs.map → migration-command-scaffold-BxOxtyJ6.mjs.map} +1 -1
  79. package/dist/migration-graph-space-render-ByJ83gxp.mjs +1966 -0
  80. package/dist/migration-graph-space-render-ByJ83gxp.mjs.map +1 -0
  81. package/dist/migration-list-jK6QeczE.mjs +228 -0
  82. package/dist/migration-list-jK6QeczE.mjs.map +1 -0
  83. package/dist/migration-list-types-DS63IdFd.d.mts +23 -0
  84. package/dist/migration-list-types-DS63IdFd.d.mts.map +1 -0
  85. package/dist/migration-log-DWI6dZyi.mjs +215 -0
  86. package/dist/migration-log-DWI6dZyi.mjs.map +1 -0
  87. package/dist/migration-path-target-DqcrbOis.mjs +24 -0
  88. package/dist/migration-path-target-DqcrbOis.mjs.map +1 -0
  89. package/dist/{migration-plan-9DJ7q7_z.mjs → migration-plan-NHdlUwPG.mjs} +5 -6
  90. package/dist/{migration-plan-9DJ7q7_z.mjs.map → migration-plan-NHdlUwPG.mjs.map} +1 -1
  91. package/dist/migration-status-DI6Ldjbo.mjs +439 -0
  92. package/dist/migration-status-DI6Ldjbo.mjs.map +1 -0
  93. package/dist/{output-B60Gw5fu.mjs → output-CF_hqzI-.mjs} +1 -1
  94. package/dist/{output-B60Gw5fu.mjs.map → output-CF_hqzI-.mjs.map} +1 -1
  95. package/dist/{ref-advancement-DUZqsue6.mjs → ref-advancement-CJY9zOv7.mjs} +1 -1
  96. package/dist/{ref-advancement-DUZqsue6.mjs.map → ref-advancement-CJY9zOv7.mjs.map} +1 -1
  97. package/dist/telemetry-DQP0BvKv.mjs +122 -0
  98. package/dist/telemetry-DQP0BvKv.mjs.map +1 -0
  99. package/dist/{types-Dt_SfqFm.d.mts → types-Cculk5KV.d.mts} +44 -31
  100. package/dist/types-Cculk5KV.d.mts.map +1 -0
  101. package/dist/{verify-DCA9Sldu.mjs → verify-tvHRBBVP.mjs} +2 -2
  102. package/dist/{verify-DCA9Sldu.mjs.map → verify-tvHRBBVP.mjs.map} +1 -1
  103. package/package.json +22 -19
  104. package/src/cli.ts +5 -0
  105. package/src/commands/db-update.ts +7 -1
  106. package/src/commands/init/index.ts +6 -35
  107. package/src/commands/init/init.ts +1 -14
  108. package/src/commands/init/inputs.ts +0 -75
  109. package/src/commands/migrate.ts +6 -6
  110. package/src/commands/migration-check.ts +340 -117
  111. package/src/commands/migration-graph.ts +163 -90
  112. package/src/commands/migration-list.ts +55 -25
  113. package/src/commands/migration-log.ts +49 -98
  114. package/src/commands/migration-show.ts +10 -38
  115. package/src/commands/migration-status-overlay.ts +61 -0
  116. package/src/commands/migration-status.ts +440 -1056
  117. package/src/commands/telemetry/index.ts +107 -0
  118. package/src/commands/telemetry/status.ts +67 -0
  119. package/src/control-api/client.ts +20 -9
  120. package/src/control-api/operations/contract-emit.ts +2 -2
  121. package/src/control-api/operations/db-init.ts +3 -3
  122. package/src/control-api/operations/{db-apply.ts → db-run.ts} +37 -10
  123. package/src/control-api/operations/db-update.ts +4 -4
  124. package/src/control-api/operations/{migration-apply.ts → migrate.ts} +32 -24
  125. package/src/control-api/operations/{apply.ts → run-migration.ts} +33 -27
  126. package/src/control-api/types.ts +46 -29
  127. package/src/utils/cli-errors.ts +47 -2
  128. package/src/utils/formatters/errors.ts +11 -0
  129. package/src/utils/formatters/migration-graph-lane-colors.ts +194 -0
  130. package/src/utils/formatters/migration-graph-layout.ts +51 -7
  131. package/src/utils/formatters/migration-graph-rows.ts +128 -15
  132. package/src/utils/formatters/migration-graph-space-render.ts +138 -0
  133. package/src/utils/formatters/migration-graph-tree-render.ts +405 -77
  134. package/src/utils/formatters/migration-list-data-column.ts +4 -91
  135. package/src/utils/formatters/migration-list-graph-topology.ts +68 -90
  136. package/src/utils/formatters/migration-list-render.ts +122 -70
  137. package/src/utils/formatters/migration-list-styler.ts +48 -5
  138. package/src/utils/formatters/migration-log-table.ts +200 -0
  139. package/src/utils/formatters/migrations.ts +25 -1
  140. package/src/utils/global-flags.ts +35 -0
  141. package/src/utils/legend.ts +38 -0
  142. package/src/utils/migration-path-target.ts +39 -0
  143. package/src/utils/telemetry.ts +68 -32
  144. package/dist/client-KgJorIvG.mjs.map +0 -1
  145. package/dist/command-helpers-Bbw1GbwL.mjs.map +0 -1
  146. package/dist/commands/migration-list.mjs.map +0 -1
  147. package/dist/commands/migration-log.mjs.map +0 -1
  148. package/dist/commands/migration-status.mjs.map +0 -1
  149. package/dist/extension-pack-inputs-IDvjRCi3.mjs +0 -62
  150. package/dist/extension-pack-inputs-IDvjRCi3.mjs.map +0 -1
  151. package/dist/graph-render-rFAqZujX.mjs +0 -1081
  152. package/dist/graph-render-rFAqZujX.mjs.map +0 -1
  153. package/dist/init-Cv9UzWL5.mjs.map +0 -1
  154. package/dist/migration-check-BiBJoYYW.mjs.map +0 -1
  155. package/dist/migration-graph-D7DVUElV.mjs +0 -1232
  156. package/dist/migration-graph-D7DVUElV.mjs.map +0 -1
  157. package/dist/migration-list-styler-BRwF4-gy.mjs +0 -399
  158. package/dist/migration-list-styler-BRwF4-gy.mjs.map +0 -1
  159. package/dist/migration-types-D2FW63pr.d.mts +0 -15
  160. package/dist/migration-types-D2FW63pr.d.mts.map +0 -1
  161. package/dist/migrations-Cv2jxNNK.mjs +0 -228
  162. package/dist/migrations-Cv2jxNNK.mjs.map +0 -1
  163. package/dist/types-Dt_SfqFm.d.mts.map +0 -1
  164. package/src/utils/formatters/graph-migration-mapper.ts +0 -235
  165. package/src/utils/formatters/graph-render.ts +0 -1323
  166. package/src/utils/formatters/graph-types.ts +0 -120
@@ -1,18 +1,15 @@
1
- import type { MigrationPlanOperation } from '@prisma-next/framework-components/control';
2
- import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
1
+ import type { LedgerEntryRecord } from '@prisma-next/contract/types';
3
2
  import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
4
- import { findPath } from '@prisma-next/migration-tools/migration-graph';
3
+ import { ifDefined } from '@prisma-next/utils/defined';
5
4
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
6
- import { cyan, dim } from 'colorette';
7
5
  import { Command } from 'commander';
8
6
  import { loadConfig } from '../config-loader';
9
7
  import { createControlClient } from '../control-api/client';
10
8
  import {
11
9
  CliStructuredError,
12
- errorDatabaseConnectionRequired,
13
- errorDriverRequired,
14
10
  errorUnexpected,
15
11
  mapMigrationToolsError,
12
+ requireLiveDatabase,
16
13
  } from '../utils/cli-errors';
17
14
  import {
18
15
  addGlobalOptions,
@@ -23,7 +20,13 @@ import {
23
20
  setCommandSeeAlso,
24
21
  targetSupportsMigrations,
25
22
  } from '../utils/command-helpers';
26
- import { buildReadAggregate } from '../utils/contract-space-aggregate-loader';
23
+ import { createAnsiMigrationListStyler } from '../utils/formatters/migration-list-styler';
24
+ import {
25
+ MIGRATION_LOG_EMPTY_MESSAGE,
26
+ renderMigrationLogTable,
27
+ type SerializedLedgerEntryRecord,
28
+ serializeLedgerEntriesForJson,
29
+ } from '../utils/formatters/migration-log-table';
27
30
  import { formatStyledHeader } from '../utils/formatters/styled';
28
31
  import type { CommonCommandOptions } from '../utils/global-flags';
29
32
  import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
@@ -33,46 +36,32 @@ import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
33
36
  interface MigrationLogOptions extends CommonCommandOptions {
34
37
  readonly db?: string;
35
38
  readonly config?: string;
36
- }
37
-
38
- export interface MigrationLogEntry {
39
- readonly dirName: string;
40
- readonly from: string;
41
- readonly to: string;
42
- readonly migrationHash: string;
43
- readonly operationCount: number;
44
- readonly createdAt: string;
39
+ readonly utc?: boolean;
40
+ readonly ascii?: boolean;
45
41
  }
46
42
 
47
43
  export interface MigrationLogResult {
48
44
  readonly ok: true;
49
- readonly markerHash: string | null;
50
- readonly applied: readonly MigrationLogEntry[];
51
- readonly summary: string;
45
+ readonly entries: readonly SerializedLedgerEntryRecord[];
52
46
  }
53
47
 
54
48
  export async function executeMigrationLogCommand(
55
49
  options: MigrationLogOptions,
56
50
  flags: GlobalFlags,
57
51
  ui: TerminalUI,
58
- ): Promise<Result<MigrationLogResult, CliStructuredError>> {
52
+ ): Promise<Result<readonly LedgerEntryRecord[], CliStructuredError>> {
59
53
  const config = await loadConfig(options.config);
60
- const { configPath, appMigrationsRelative, migrationsDir } = resolveMigrationPaths(
61
- options.config,
62
- config,
63
- );
54
+ const { configPath } = resolveMigrationPaths(options.config, config);
64
55
 
65
56
  const dbConnection = options.db ?? config.db?.connection;
66
- if (!dbConnection) {
67
- return notOk(
68
- errorDatabaseConnectionRequired({
69
- why: `Database connection is required for migration log (set db.connection in ${configPath}, or pass --db <url>)`,
70
- commandName: 'migration log',
71
- }),
72
- );
73
- }
74
- if (!config.driver) {
75
- return notOk(errorDriverRequired({ why: 'Config.driver is required for migration log' }));
57
+ const missingDb = requireLiveDatabase({
58
+ dbConnection,
59
+ hasDriver: !!config.driver,
60
+ why: `migration log needs a database connection and driver to read the ledger (set db.connection in ${configPath}, or pass --db <url>)`,
61
+ commandName: 'migration log',
62
+ });
63
+ if (missingDb) {
64
+ return notOk(missingDb);
76
65
  }
77
66
  if (!targetSupportsMigrations(config.target)) {
78
67
  return notOk(errorUnexpected('Target does not support migrations'));
@@ -81,10 +70,9 @@ export async function executeMigrationLogCommand(
81
70
  if (!flags.json && !flags.quiet) {
82
71
  const header = formatStyledHeader({
83
72
  command: 'migration log',
84
- description: 'Show executed migration history',
73
+ description: 'Show executed migration history from the database ledger',
85
74
  details: [
86
75
  { label: 'config', value: configPath },
87
- { label: 'migrations', value: appMigrationsRelative },
88
76
  ...(typeof dbConnection === 'string'
89
77
  ? [{ label: 'database', value: maskConnectionUrl(dbConnection) }]
90
78
  : []),
@@ -94,64 +82,18 @@ export async function executeMigrationLogCommand(
94
82
  ui.stderr(header);
95
83
  }
96
84
 
97
- const loaded = await buildReadAggregate(config, { migrationsDir });
98
- if (!loaded.ok) {
99
- return loaded;
100
- }
101
- const graph = loaded.value.aggregate.app.graph();
102
- const bundles = loaded.value.aggregate.app.packages;
103
-
104
85
  const client = createControlClient({
105
86
  family: config.family,
106
87
  target: config.target,
107
88
  adapter: config.adapter,
108
- driver: config.driver,
89
+ ...ifDefined('driver', config.driver),
109
90
  extensionPacks: config.extensionPacks ?? [],
110
91
  });
111
92
 
112
93
  try {
113
94
  await client.connect(dbConnection);
114
- const marker = await client.readMarker();
115
- const markerHash = marker?.storageHash ?? null;
116
-
117
- if (!markerHash) {
118
- return ok({
119
- ok: true,
120
- markerHash: null,
121
- applied: [],
122
- summary: 'No migrations applied (database has no marker)',
123
- });
124
- }
125
-
126
- const appliedPath = findPath(graph, EMPTY_CONTRACT_HASH, markerHash);
127
- if (appliedPath === null) {
128
- return notOk(
129
- errorUnexpected('Database marker is not reachable from migration history', {
130
- why: `Marker hash ${markerHash} is not reachable from the root of the on-disk migration graph.`,
131
- fix: 'The database may have been migrated outside this project. Use `migration status` to inspect the current state.',
132
- }),
133
- );
134
- }
135
- const pkgByDirName = new Map(bundles.map((p) => [p.dirName, p]));
136
- const entries: MigrationLogEntry[] = appliedPath.map((edge) => {
137
- const pkg = pkgByDirName.get(edge.dirName);
138
- const ops = (pkg?.ops ?? []) as readonly MigrationPlanOperation[];
139
- return {
140
- dirName: edge.dirName,
141
- from: edge.from,
142
- to: edge.to,
143
- migrationHash: edge.migrationHash,
144
- operationCount: ops.length,
145
- createdAt: edge.createdAt,
146
- };
147
- });
148
-
149
- return ok({
150
- ok: true,
151
- markerHash,
152
- applied: entries,
153
- summary: `${entries.length} migration(s) applied`,
154
- });
95
+ const ledger = await client.readLedger();
96
+ return ok(ledger);
155
97
  } catch (error) {
156
98
  if (CliStructuredError.is(error)) return notOk(error);
157
99
  if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
@@ -170,11 +112,13 @@ export function createMigrationLogCommand(): Command {
170
112
  setCommandDescriptions(
171
113
  command,
172
114
  'Show executed migration history',
173
- 'Reads the database marker and displays the applied migration chain\n' +
174
- 'from the initial state to the current marker position.',
115
+ 'Reads the database ledger and displays every applied migration edge\n' +
116
+ 'in chronological order, including rollbacks and re-applies, merged\n' +
117
+ 'across all contract spaces. Requires a database connection.',
175
118
  );
176
119
  setCommandExamples(command, [
177
120
  'prisma-next migration log --db $DATABASE_URL',
121
+ 'prisma-next migration log --utc --db $DATABASE_URL',
178
122
  'prisma-next migration log --json --db $DATABASE_URL',
179
123
  ]);
180
124
  setCommandSeeAlso(command, [
@@ -186,24 +130,31 @@ export function createMigrationLogCommand(): Command {
186
130
  addGlobalOptions(command)
187
131
  .option('--db <url>', 'Database connection string')
188
132
  .option('--config <path>', 'Path to prisma-next.config.ts')
133
+ .option('--utc', 'Render human timestamps in UTC instead of local time')
134
+ .option('--ascii', 'Use ASCII glyphs (pipe-friendly)')
189
135
  .action(async (options: MigrationLogOptions) => {
190
136
  const flags = parseGlobalFlagsOrExit(options);
191
137
  const ui = createTerminalUI(flags);
192
138
  const result = await executeMigrationLogCommand(options, flags, ui);
193
- const exitCode = handleResult(result, flags, ui, (logResult) => {
139
+ const exitCode = handleResult(result, flags, ui, (entries) => {
194
140
  if (flags.json) {
195
- ui.output(JSON.stringify(logResult, null, 2));
141
+ const result: MigrationLogResult = {
142
+ ok: true,
143
+ entries: serializeLedgerEntriesForJson(entries),
144
+ };
145
+ ui.output(JSON.stringify(result, null, 2));
196
146
  } else if (!flags.quiet) {
197
- const c = (fn: (s: string) => string, s: string) => (flags.color !== false ? fn(s) : s);
198
- if (logResult.applied.length === 0) {
199
- ui.log(logResult.summary);
147
+ if (entries.length === 0) {
148
+ ui.output(MIGRATION_LOG_EMPTY_MESSAGE);
200
149
  } else {
201
- for (const entry of logResult.applied) {
202
- ui.log(
203
- `${c(cyan, '✓')} ${entry.dirName} ${c(dim, entry.migrationHash.slice(0, 16) + '…')} ${entry.operationCount} op(s)`,
204
- );
205
- }
206
- ui.log(`\n${logResult.summary}`);
150
+ const styler = createAnsiMigrationListStyler({ useColor: ui.useColor });
151
+ ui.output(
152
+ renderMigrationLogTable(entries, {
153
+ utc: options.utc === true,
154
+ styler,
155
+ glyphMode: ui.resolveGlyphMode(options.ascii === true),
156
+ }),
157
+ );
207
158
  }
208
159
  }
209
160
  });
@@ -13,7 +13,7 @@ import { castAs } from '@prisma-next/utils/casts';
13
13
  import { ifDefined } from '@prisma-next/utils/defined';
14
14
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
15
15
  import { Command } from 'commander';
16
- import { isAbsolute, relative, resolve } from 'pathe';
16
+ import { relative } from 'pathe';
17
17
  import { loadConfig } from '../config-loader';
18
18
  import { createControlClient } from '../control-api/client';
19
19
  import {
@@ -36,6 +36,11 @@ import { formatMigrationShowOutput } from '../utils/formatters/migrations';
36
36
  import { formatStyledHeader } from '../utils/formatters/styled';
37
37
  import type { CommonCommandOptions } from '../utils/global-flags';
38
38
  import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
39
+ import {
40
+ findPackageByDirPath,
41
+ looksLikePath,
42
+ resolveAppTargetPath,
43
+ } from '../utils/migration-path-target';
39
44
  import { handleResult } from '../utils/result-handler';
40
45
  import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
41
46
 
@@ -65,33 +70,6 @@ export interface MigrationShowResult {
65
70
  readonly migration: MigrationShowPresent;
66
71
  }
67
72
 
68
- function looksLikePath(target: string): boolean {
69
- return target.includes('/') || target.includes('\\');
70
- }
71
-
72
- export function resolveAppTargetPath(
73
- target: string,
74
- appMigrationsDir: string,
75
- appMigrationsRelative: string,
76
- ): Result<string, CliStructuredError> {
77
- const targetPath = resolve(target);
78
- const relativeToApp = relative(appMigrationsDir, targetPath);
79
- const isOutsideAppDir =
80
- relativeToApp === '' ||
81
- relativeToApp === '.' ||
82
- relativeToApp.startsWith('..') ||
83
- isAbsolute(relativeToApp);
84
- if (isOutsideAppDir) {
85
- return notOk(
86
- errorRuntime('Target must point to an app-space migration', {
87
- why: `Expected a path under ${appMigrationsRelative}, got ${target}`,
88
- fix: 'Pass an app-space migration directory or use a hash prefix.',
89
- }),
90
- );
91
- }
92
- return ok(targetPath);
93
- }
94
-
95
73
  function pkgToPresent(
96
74
  spaceId: string,
97
75
  pkg: OnDiskMigrationPackage,
@@ -117,14 +95,6 @@ function pkgToPresent(
117
95
  };
118
96
  }
119
97
 
120
- function findPackageByDirPath(
121
- packages: readonly OnDiskMigrationPackage[],
122
- resolvedDirPath: string,
123
- ): OnDiskMigrationPackage | undefined {
124
- const normalized = resolve(resolvedDirPath);
125
- return packages.find((p) => resolve(p.dirPath) === normalized);
126
- }
127
-
128
98
  async function executeMigrationShowCommand(
129
99
  target: string,
130
100
  options: MigrationShowOptions,
@@ -260,11 +230,13 @@ export function createMigrationShowCommand(): Command {
260
230
  command,
261
231
  'Display migration package contents',
262
232
  'Shows the operations, statement preview, and metadata for one app-space migration.\n' +
263
- 'Accepts a directory path, directory name, or hash prefix.',
233
+ 'Accepts a directory path, directory name, or hash prefix.\n' +
234
+ 'Offline — does not consult the database.',
264
235
  );
265
236
  setCommandExamples(command, [
266
237
  'prisma-next migration show 20260101_100000_add_user',
267
238
  'prisma-next migration show sha256:a1b2c3',
239
+ 'prisma-next migration show 20260101_100000_add_user --json',
268
240
  ]);
269
241
  setCommandSeeAlso(command, [
270
242
  { verb: 'migration status', oneLiner: 'Show migration path and pending status' },
@@ -273,7 +245,7 @@ export function createMigrationShowCommand(): Command {
273
245
  { verb: 'migration graph', oneLiner: 'Show the migration graph topology' },
274
246
  ]);
275
247
  addGlobalOptions(command)
276
- .argument('<target>', 'Migration reference: directory name, hash/prefix, or path')
248
+ .argument('<target>', 'Migration reference: directory name, hash/prefix, ref, or path')
277
249
  .option('--config <path>', 'Path to prisma-next.config.ts')
278
250
  .action(async (target: string, options: MigrationShowOptions) => {
279
251
  const flags = parseGlobalFlagsOrExit(options);
@@ -0,0 +1,61 @@
1
+ import type { MigrationGraph } from '@prisma-next/migration-tools/graph';
2
+ import { findPath } from '@prisma-next/migration-tools/migration-graph';
3
+ import type { MigrationEdgeAnnotation } from '../utils/formatters/migration-graph-tree-render';
4
+
5
+ export interface DeriveStatusEdgeAnnotationsInput {
6
+ readonly graph: MigrationGraph;
7
+ readonly targetHash: string;
8
+ readonly originHash: string;
9
+ readonly appliedMigrationHashes: ReadonlySet<string>;
10
+ readonly showAppliedOverlay: boolean;
11
+ }
12
+
13
+ export function deriveStatusEdgeAnnotations(
14
+ input: DeriveStatusEdgeAnnotationsInput,
15
+ ): ReadonlyMap<string, MigrationEdgeAnnotation> {
16
+ const annotations = new Map<string, MigrationEdgeAnnotation>();
17
+
18
+ if (input.showAppliedOverlay) {
19
+ for (const edge of input.graph.migrationByHash.values()) {
20
+ if (input.appliedMigrationHashes.has(edge.migrationHash)) {
21
+ annotations.set(edge.migrationHash, { status: 'applied' });
22
+ }
23
+ }
24
+ }
25
+
26
+ if (!input.graph.nodes.has(input.originHash)) {
27
+ return annotations;
28
+ }
29
+
30
+ const pendingPath = findPath(input.graph, input.originHash, input.targetHash);
31
+ if (!pendingPath) {
32
+ return annotations;
33
+ }
34
+
35
+ for (const edge of pendingPath) {
36
+ if (input.appliedMigrationHashes.has(edge.migrationHash)) {
37
+ continue;
38
+ }
39
+ const existing = annotations.get(edge.migrationHash);
40
+ if (existing?.status === 'applied') {
41
+ continue;
42
+ }
43
+ annotations.set(edge.migrationHash, { status: 'pending' });
44
+ }
45
+
46
+ return annotations;
47
+ }
48
+
49
+ export function appliedHashesFromLedger(
50
+ ledgerEntries: ReadonlyArray<{ readonly migrationHash: string }>,
51
+ ): ReadonlySet<string> {
52
+ return new Set(ledgerEntries.map((entry) => entry.migrationHash));
53
+ }
54
+
55
+ export function statusForMigrationHash(
56
+ migrationHash: string,
57
+ annotations: ReadonlyMap<string, MigrationEdgeAnnotation>,
58
+ ): 'applied' | 'pending' | null {
59
+ const status = annotations.get(migrationHash)?.status;
60
+ return status ?? null;
61
+ }