@prisma-next/cli 0.12.0 → 0.13.0-dev.2

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 (213) 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-CJzuo5wX.mjs} +222 -107
  5. package/dist/client-CJzuo5wX.mjs.map +1 -0
  6. package/dist/{command-helpers-Bbw1GbwL.mjs → command-helpers-DGMvGBeX.mjs} +318 -25
  7. package/dist/command-helpers-DGMvGBeX.mjs.map +1 -0
  8. package/dist/commands/contract-emit.d.mts.map +1 -1
  9. package/dist/commands/contract-emit.mjs +1 -1
  10. package/dist/commands/contract-infer.d.mts.map +1 -1
  11. package/dist/commands/contract-infer.mjs +1 -1
  12. package/dist/commands/db-init.d.mts.map +1 -1
  13. package/dist/commands/db-init.mjs +4 -5
  14. package/dist/commands/db-init.mjs.map +1 -1
  15. package/dist/commands/db-schema.d.mts.map +1 -1
  16. package/dist/commands/db-schema.mjs +3 -3
  17. package/dist/commands/db-schema.mjs.map +1 -1
  18. package/dist/commands/db-sign.d.mts.map +1 -1
  19. package/dist/commands/db-sign.mjs +6 -6
  20. package/dist/commands/db-sign.mjs.map +1 -1
  21. package/dist/commands/db-update.d.mts.map +1 -1
  22. package/dist/commands/db-update.mjs +10 -7
  23. package/dist/commands/db-update.mjs.map +1 -1
  24. package/dist/commands/db-verify.d.mts.map +1 -1
  25. package/dist/commands/db-verify.mjs +1 -1
  26. package/dist/commands/migrate.d.mts +37 -3
  27. package/dist/commands/migrate.d.mts.map +1 -1
  28. package/dist/commands/migrate.mjs +298 -12
  29. package/dist/commands/migrate.mjs.map +1 -1
  30. package/dist/commands/migration-check.d.mts +55 -13
  31. package/dist/commands/migration-check.d.mts.map +1 -1
  32. package/dist/commands/migration-check.mjs +3 -2
  33. package/dist/commands/migration-graph.d.mts +17 -8
  34. package/dist/commands/migration-graph.d.mts.map +1 -1
  35. package/dist/commands/migration-graph.mjs +185 -2
  36. package/dist/commands/migration-graph.mjs.map +1 -0
  37. package/dist/commands/migration-list.d.mts +26 -27
  38. package/dist/commands/migration-list.d.mts.map +1 -1
  39. package/dist/commands/migration-list.mjs +2 -190
  40. package/dist/commands/migration-log.d.mts +9 -19
  41. package/dist/commands/migration-log.d.mts.map +1 -1
  42. package/dist/commands/migration-log.mjs +1 -137
  43. package/dist/commands/migration-new.d.mts.map +1 -1
  44. package/dist/commands/migration-new.mjs +6 -5
  45. package/dist/commands/migration-new.mjs.map +1 -1
  46. package/dist/commands/migration-plan.d.mts +1 -1
  47. package/dist/commands/migration-plan.d.mts.map +1 -1
  48. package/dist/commands/migration-plan.mjs +1 -1
  49. package/dist/commands/migration-show.d.mts +17 -21
  50. package/dist/commands/migration-show.d.mts.map +1 -1
  51. package/dist/commands/migration-show.mjs +24 -36
  52. package/dist/commands/migration-show.mjs.map +1 -1
  53. package/dist/commands/migration-status.d.mts +42 -144
  54. package/dist/commands/migration-status.d.mts.map +1 -1
  55. package/dist/commands/migration-status.mjs +3 -759
  56. package/dist/commands/ref.d.mts +1 -1
  57. package/dist/commands/ref.d.mts.map +1 -1
  58. package/dist/commands/ref.mjs +4 -4
  59. package/dist/commands/ref.mjs.map +1 -1
  60. package/dist/commands/telemetry/index.d.mts +7 -0
  61. package/dist/commands/telemetry/index.d.mts.map +1 -0
  62. package/dist/commands/telemetry/index.mjs +2 -0
  63. package/dist/{config-loader-B6sJjXTv.mjs → config-loader-p9JMrekQ.mjs} +1 -1
  64. package/dist/{config-loader-B6sJjXTv.mjs.map → config-loader-p9JMrekQ.mjs.map} +1 -1
  65. package/dist/config-loader.mjs +1 -1
  66. package/dist/{contract-at-errors-BxP-TOMl.mjs → contract-at-errors-CFXsstzm.mjs} +2 -2
  67. package/dist/{contract-at-errors-BxP-TOMl.mjs.map → contract-at-errors-CFXsstzm.mjs.map} +1 -1
  68. package/dist/{contract-emit-DxcGl4Uq.mjs → contract-emit-B_qriF8B.mjs} +5 -5
  69. package/dist/{contract-emit-DxcGl4Uq.mjs.map → contract-emit-B_qriF8B.mjs.map} +1 -1
  70. package/dist/{contract-emit-D-4jrNve.mjs → contract-emit-C8HmtboH.mjs} +12 -7
  71. package/dist/contract-emit-C8HmtboH.mjs.map +1 -0
  72. package/dist/{contract-enrichment-a0V5Y_mL.mjs → contract-enrichment-gn9sWbPw.mjs} +1 -1
  73. package/dist/{contract-enrichment-a0V5Y_mL.mjs.map → contract-enrichment-gn9sWbPw.mjs.map} +1 -1
  74. package/dist/{contract-infer-D8uEbJuu.mjs → contract-infer-BYT_ra_U.mjs} +5 -5
  75. package/dist/contract-infer-BYT_ra_U.mjs.map +1 -0
  76. package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs → contract-space-aggregate-loader-ClI1KN6d.mjs} +5 -5
  77. package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs.map → contract-space-aggregate-loader-ClI1KN6d.mjs.map} +1 -1
  78. package/dist/{db-verify-v_vUKXTU.mjs → db-verify-C24FKhb7.mjs} +6 -6
  79. package/dist/{db-verify-v_vUKXTU.mjs.map → db-verify-C24FKhb7.mjs.map} +1 -1
  80. package/dist/exports/control-api.d.mts +5 -3
  81. package/dist/exports/control-api.d.mts.map +1 -1
  82. package/dist/exports/control-api.mjs +3 -3
  83. package/dist/exports/index.mjs +1 -1
  84. package/dist/exports/index.mjs.map +1 -1
  85. package/dist/exports/init-output.d.mts +1 -3
  86. package/dist/exports/init-output.d.mts.map +1 -1
  87. package/dist/exports/init-output.mjs +1 -1
  88. package/dist/{extension-pack-inputs-IDvjRCi3.mjs → extension-pack-inputs-1ySHqxKG.mjs} +1 -1
  89. package/dist/{extension-pack-inputs-IDvjRCi3.mjs.map → extension-pack-inputs-1ySHqxKG.mjs.map} +1 -1
  90. package/dist/{framework-components-fYXjz_in.mjs → framework-components-YVQHhPH7.mjs} +2 -2
  91. package/dist/{framework-components-fYXjz_in.mjs.map → framework-components-YVQHhPH7.mjs.map} +1 -1
  92. package/dist/{global-flags-DEHjV8_s.d.mts → global-flags-BpoOYtNZ.d.mts} +1 -1
  93. package/dist/{global-flags-DEHjV8_s.d.mts.map → global-flags-BpoOYtNZ.d.mts.map} +1 -1
  94. package/dist/{init-Cv9UzWL5.mjs → init-0HwB-Vh8.mjs} +5 -58
  95. package/dist/init-0HwB-Vh8.mjs.map +1 -0
  96. package/dist/{inspect-live-schema-C6ohV_oQ.mjs → inspect-live-schema-DF6IwcDl.mjs} +7 -5
  97. package/dist/inspect-live-schema-DF6IwcDl.mjs.map +1 -0
  98. package/dist/migration-check-VwM8xCZV.mjs +574 -0
  99. package/dist/migration-check-VwM8xCZV.mjs.map +1 -0
  100. package/dist/migration-cli.mjs +1 -1
  101. package/dist/migration-cli.mjs.map +1 -1
  102. package/dist/{migration-command-scaffold-CjvwO6at.mjs → migration-command-scaffold-DA-Lhx6o.mjs} +5 -5
  103. package/dist/{migration-command-scaffold-CjvwO6at.mjs.map → migration-command-scaffold-DA-Lhx6o.mjs.map} +1 -1
  104. package/dist/migration-graph-command-render-CEez7YUK.mjs +1960 -0
  105. package/dist/migration-graph-command-render-CEez7YUK.mjs.map +1 -0
  106. package/dist/migration-list-DlJJ_38Z.mjs +230 -0
  107. package/dist/migration-list-DlJJ_38Z.mjs.map +1 -0
  108. package/dist/migration-log-CG0qQAFm.mjs +222 -0
  109. package/dist/migration-log-CG0qQAFm.mjs.map +1 -0
  110. package/dist/migration-path-target-Ce6OZImp.mjs +38 -0
  111. package/dist/migration-path-target-Ce6OZImp.mjs.map +1 -0
  112. package/dist/{migration-plan-9DJ7q7_z.mjs → migration-plan-z5Ing-TD.mjs} +9 -8
  113. package/dist/migration-plan-z5Ing-TD.mjs.map +1 -0
  114. package/dist/migration-status-CD-LC2Ip.mjs +447 -0
  115. package/dist/migration-status-CD-LC2Ip.mjs.map +1 -0
  116. package/dist/{output-B60Gw5fu.mjs → output-mEQ74_nd.mjs} +1 -1
  117. package/dist/{output-B60Gw5fu.mjs.map → output-mEQ74_nd.mjs.map} +1 -1
  118. package/dist/{progress-adapter-C644QK8l.mjs → progress-adapter-CjAeTxY_.mjs} +1 -1
  119. package/dist/{progress-adapter-C644QK8l.mjs.map → progress-adapter-CjAeTxY_.mjs.map} +1 -1
  120. package/dist/{ref-advancement-DUZqsue6.mjs → ref-advancement-BkXlikCA.mjs} +1 -1
  121. package/dist/{ref-advancement-DUZqsue6.mjs.map → ref-advancement-BkXlikCA.mjs.map} +1 -1
  122. package/dist/schemas-CeGMYFYX.d.mts +191 -0
  123. package/dist/schemas-CeGMYFYX.d.mts.map +1 -0
  124. package/dist/schemas-KhXMzNA_.mjs +112 -0
  125. package/dist/schemas-KhXMzNA_.mjs.map +1 -0
  126. package/dist/telemetry-BIM4beEO.mjs +122 -0
  127. package/dist/telemetry-BIM4beEO.mjs.map +1 -0
  128. package/dist/{terminal-ui-5Y6mrg93.d.mts → terminal-ui-DGRNFWna.d.mts} +1 -1
  129. package/dist/terminal-ui-DGRNFWna.d.mts.map +1 -0
  130. package/dist/{types-Dt_SfqFm.d.mts → types-C_tYiJYx.d.mts} +53 -31
  131. package/dist/types-C_tYiJYx.d.mts.map +1 -0
  132. package/dist/{verify-DCA9Sldu.mjs → verify-DcOYZ1tH.mjs} +2 -2
  133. package/dist/{verify-DCA9Sldu.mjs.map → verify-DcOYZ1tH.mjs.map} +1 -1
  134. package/package.json +26 -22
  135. package/src/cli.ts +5 -0
  136. package/src/commands/contract-infer.ts +2 -2
  137. package/src/commands/db-update.ts +7 -1
  138. package/src/commands/init/index.ts +6 -35
  139. package/src/commands/init/init.ts +1 -14
  140. package/src/commands/init/inputs.ts +0 -75
  141. package/src/commands/inspect-live-schema.ts +10 -0
  142. package/src/commands/json/schemas.ts +195 -0
  143. package/src/commands/migrate.ts +527 -8
  144. package/src/commands/migration-check.ts +469 -134
  145. package/src/commands/migration-graph.ts +164 -91
  146. package/src/commands/migration-list.ts +72 -39
  147. package/src/commands/migration-log.ts +52 -102
  148. package/src/commands/migration-new.ts +2 -1
  149. package/src/commands/migration-plan.ts +2 -1
  150. package/src/commands/migration-show.ts +31 -66
  151. package/src/commands/migration-status-overlay.ts +61 -0
  152. package/src/commands/migration-status.ts +458 -1066
  153. package/src/commands/telemetry/index.ts +107 -0
  154. package/src/commands/telemetry/status.ts +67 -0
  155. package/src/control-api/client.ts +70 -9
  156. package/src/control-api/operations/contract-emit.ts +22 -2
  157. package/src/control-api/operations/db-init.ts +6 -3
  158. package/src/control-api/operations/{db-apply.ts → db-run.ts} +55 -14
  159. package/src/control-api/operations/db-update.ts +7 -4
  160. package/src/control-api/operations/db-verify.ts +15 -5
  161. package/src/control-api/operations/{migration-apply.ts → migrate.ts} +181 -80
  162. package/src/control-api/operations/{apply.ts → run-migration.ts} +33 -27
  163. package/src/control-api/types.ts +56 -29
  164. package/src/utils/cli-errors.ts +70 -2
  165. package/src/utils/formatters/errors.ts +11 -0
  166. package/src/utils/formatters/migration-graph-command-render.ts +239 -0
  167. package/src/utils/formatters/migration-graph-grid-layout.ts +1134 -0
  168. package/src/utils/formatters/migration-graph-labels.ts +408 -0
  169. package/src/utils/formatters/migration-graph-model.ts +103 -0
  170. package/src/utils/formatters/migration-graph-occlusion-render.ts +258 -0
  171. package/src/utils/formatters/migration-graph-rows.ts +128 -15
  172. package/src/utils/formatters/migration-graph-space-render.ts +188 -0
  173. package/src/utils/formatters/migration-list-data-column.ts +4 -91
  174. package/src/utils/formatters/migration-list-graph-topology.ts +72 -94
  175. package/src/utils/formatters/migration-list-render.ts +135 -71
  176. package/src/utils/formatters/migration-list-styler.ts +46 -5
  177. package/src/utils/formatters/migration-list-types.ts +5 -21
  178. package/src/utils/formatters/migration-log-table.ts +205 -0
  179. package/src/utils/formatters/migrations.ts +33 -11
  180. package/src/utils/global-flags.ts +35 -0
  181. package/src/utils/integrity-violation-to-check-failure.ts +28 -19
  182. package/src/utils/legend.ts +38 -0
  183. package/src/utils/migration-path-target.ts +60 -0
  184. package/src/utils/telemetry.ts +68 -32
  185. package/dist/client-KgJorIvG.mjs.map +0 -1
  186. package/dist/command-helpers-Bbw1GbwL.mjs.map +0 -1
  187. package/dist/commands/migration-list.mjs.map +0 -1
  188. package/dist/commands/migration-log.mjs.map +0 -1
  189. package/dist/commands/migration-status.mjs.map +0 -1
  190. package/dist/contract-emit-D-4jrNve.mjs.map +0 -1
  191. package/dist/contract-infer-D8uEbJuu.mjs.map +0 -1
  192. package/dist/graph-render-rFAqZujX.mjs +0 -1081
  193. package/dist/graph-render-rFAqZujX.mjs.map +0 -1
  194. package/dist/init-Cv9UzWL5.mjs.map +0 -1
  195. package/dist/inspect-live-schema-C6ohV_oQ.mjs.map +0 -1
  196. package/dist/migration-check-BiBJoYYW.mjs +0 -341
  197. package/dist/migration-check-BiBJoYYW.mjs.map +0 -1
  198. package/dist/migration-graph-D7DVUElV.mjs +0 -1232
  199. package/dist/migration-graph-D7DVUElV.mjs.map +0 -1
  200. package/dist/migration-list-styler-BRwF4-gy.mjs +0 -399
  201. package/dist/migration-list-styler-BRwF4-gy.mjs.map +0 -1
  202. package/dist/migration-plan-9DJ7q7_z.mjs.map +0 -1
  203. package/dist/migration-types-D2FW63pr.d.mts +0 -15
  204. package/dist/migration-types-D2FW63pr.d.mts.map +0 -1
  205. package/dist/migrations-Cv2jxNNK.mjs +0 -228
  206. package/dist/migrations-Cv2jxNNK.mjs.map +0 -1
  207. package/dist/terminal-ui-5Y6mrg93.d.mts.map +0 -1
  208. package/dist/types-Dt_SfqFm.d.mts.map +0 -1
  209. package/src/utils/formatters/graph-migration-mapper.ts +0 -235
  210. package/src/utils/formatters/graph-render.ts +0 -1323
  211. package/src/utils/formatters/graph-types.ts +0 -120
  212. package/src/utils/formatters/migration-graph-layout.ts +0 -1119
  213. package/src/utils/formatters/migration-graph-tree-render.ts +0 -459
@@ -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,56 +20,45 @@ 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
+ serializeLedgerEntriesForJson,
28
+ } from '../utils/formatters/migration-log-table';
27
29
  import { formatStyledHeader } from '../utils/formatters/styled';
28
30
  import type { CommonCommandOptions } from '../utils/global-flags';
29
31
  import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
30
32
  import { handleResult } from '../utils/result-handler';
31
33
  import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
34
+ import type { MigrationLogResult } from './json/schemas';
35
+
36
+ export type { MigrationLogResult };
32
37
 
33
38
  interface MigrationLogOptions extends CommonCommandOptions {
34
39
  readonly db?: string;
35
40
  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;
45
- }
46
-
47
- export interface MigrationLogResult {
48
- readonly ok: true;
49
- readonly markerHash: string | null;
50
- readonly applied: readonly MigrationLogEntry[];
51
- readonly summary: string;
41
+ readonly utc?: boolean;
42
+ readonly ascii?: boolean;
52
43
  }
53
44
 
54
45
  export async function executeMigrationLogCommand(
55
46
  options: MigrationLogOptions,
56
47
  flags: GlobalFlags,
57
48
  ui: TerminalUI,
58
- ): Promise<Result<MigrationLogResult, CliStructuredError>> {
49
+ ): Promise<Result<readonly LedgerEntryRecord[], CliStructuredError>> {
59
50
  const config = await loadConfig(options.config);
60
- const { configPath, appMigrationsRelative, migrationsDir } = resolveMigrationPaths(
61
- options.config,
62
- config,
63
- );
51
+ const { configPath } = resolveMigrationPaths(options.config, config);
64
52
 
65
53
  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' }));
54
+ const missingDb = requireLiveDatabase({
55
+ dbConnection,
56
+ hasDriver: !!config.driver,
57
+ why: `migration log needs a database connection and driver to read the ledger (set db.connection in ${configPath}, or pass --db <url>)`,
58
+ commandName: 'migration log',
59
+ });
60
+ if (missingDb) {
61
+ return notOk(missingDb);
76
62
  }
77
63
  if (!targetSupportsMigrations(config.target)) {
78
64
  return notOk(errorUnexpected('Target does not support migrations'));
@@ -81,10 +67,9 @@ export async function executeMigrationLogCommand(
81
67
  if (!flags.json && !flags.quiet) {
82
68
  const header = formatStyledHeader({
83
69
  command: 'migration log',
84
- description: 'Show executed migration history',
70
+ description: 'Show executed migration history from the database ledger',
85
71
  details: [
86
72
  { label: 'config', value: configPath },
87
- { label: 'migrations', value: appMigrationsRelative },
88
73
  ...(typeof dbConnection === 'string'
89
74
  ? [{ label: 'database', value: maskConnectionUrl(dbConnection) }]
90
75
  : []),
@@ -94,64 +79,18 @@ export async function executeMigrationLogCommand(
94
79
  ui.stderr(header);
95
80
  }
96
81
 
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
82
  const client = createControlClient({
105
83
  family: config.family,
106
84
  target: config.target,
107
85
  adapter: config.adapter,
108
- driver: config.driver,
86
+ ...ifDefined('driver', config.driver),
109
87
  extensionPacks: config.extensionPacks ?? [],
110
88
  });
111
89
 
112
90
  try {
113
91
  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
- });
92
+ const ledger = await client.readLedger();
93
+ return ok(ledger);
155
94
  } catch (error) {
156
95
  if (CliStructuredError.is(error)) return notOk(error);
157
96
  if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
@@ -170,11 +109,13 @@ export function createMigrationLogCommand(): Command {
170
109
  setCommandDescriptions(
171
110
  command,
172
111
  '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.',
112
+ 'Reads the database ledger and displays every applied migration edge\n' +
113
+ 'in chronological order, including rollbacks and re-applies, merged\n' +
114
+ 'across all contract spaces. Requires a database connection.',
175
115
  );
176
116
  setCommandExamples(command, [
177
117
  'prisma-next migration log --db $DATABASE_URL',
118
+ 'prisma-next migration log --utc --db $DATABASE_URL',
178
119
  'prisma-next migration log --json --db $DATABASE_URL',
179
120
  ]);
180
121
  setCommandSeeAlso(command, [
@@ -186,24 +127,33 @@ export function createMigrationLogCommand(): Command {
186
127
  addGlobalOptions(command)
187
128
  .option('--db <url>', 'Database connection string')
188
129
  .option('--config <path>', 'Path to prisma-next.config.ts')
130
+ .option('--utc', 'Render human timestamps in UTC instead of local time')
131
+ .option('--ascii', 'Use ASCII glyphs (pipe-friendly)')
189
132
  .action(async (options: MigrationLogOptions) => {
190
133
  const flags = parseGlobalFlagsOrExit(options);
191
134
  const ui = createTerminalUI(flags);
192
135
  const result = await executeMigrationLogCommand(options, flags, ui);
193
- const exitCode = handleResult(result, flags, ui, (logResult) => {
136
+ const exitCode = handleResult(result, flags, ui, (entries) => {
194
137
  if (flags.json) {
195
- ui.output(JSON.stringify(logResult, null, 2));
138
+ const records = serializeLedgerEntriesForJson(entries);
139
+ const result: MigrationLogResult = {
140
+ ok: true,
141
+ records,
142
+ summary: `${records.length} migration(s) applied`,
143
+ };
144
+ ui.output(JSON.stringify(result, null, 2));
196
145
  } 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);
146
+ if (entries.length === 0) {
147
+ ui.output(MIGRATION_LOG_EMPTY_MESSAGE);
200
148
  } 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}`);
149
+ const styler = createAnsiMigrationListStyler({ useColor: ui.useColor });
150
+ ui.output(
151
+ renderMigrationLogTable(entries, {
152
+ utc: options.utc === true,
153
+ styler,
154
+ glyphMode: ui.resolveGlyphMode(options.ascii === true),
155
+ }),
156
+ );
207
157
  }
208
158
  }
209
159
  });
@@ -77,6 +77,7 @@ async function executeMigrationNewCommand(
77
77
  // at the read site, not somewhere downstream. See TML-2536.
78
78
  const stack = createControlStack(config);
79
79
  const familyInstance = config.family.create(stack);
80
+ const controlAdapter = config.adapter.create(stack);
80
81
 
81
82
  const contractPathAbsolute = resolveContractPath(config);
82
83
 
@@ -234,7 +235,7 @@ async function executeMigrationNewCommand(
234
235
  }
235
236
  }
236
237
 
237
- const planner = migrations.createPlanner(familyInstance);
238
+ const planner = migrations.createPlanner(controlAdapter);
238
239
  const emptyPlan = planner.emptyMigration(
239
240
  {
240
241
  packageDir,
@@ -276,6 +276,7 @@ async function executeMigrationPlanCommand(
276
276
  // started dispatching on raw shapes. See TML-2536.
277
277
  const stack = createControlStack(config);
278
278
  const familyInstance = config.family.create(stack);
279
+ const controlAdapter = config.adapter.create(stack);
279
280
 
280
281
  let toContract: Contract;
281
282
  try {
@@ -475,7 +476,7 @@ async function executeMigrationPlanCommand(
475
476
  }
476
477
 
477
478
  try {
478
- const planner = migrations.createPlanner(familyInstance);
479
+ const planner = migrations.createPlanner(controlAdapter);
479
480
 
480
481
  if (
481
482
  isAutoBaseline &&
@@ -4,7 +4,6 @@ import {
4
4
  APP_SPACE_ID,
5
5
  createControlStack,
6
6
  type MigrationPlanOperation,
7
- type OperationPreview,
8
7
  } from '@prisma-next/framework-components/control';
9
8
  import { loadContractSpaceAggregate } from '@prisma-next/migration-tools/aggregate';
10
9
  import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
@@ -13,7 +12,7 @@ import { castAs } from '@prisma-next/utils/casts';
13
12
  import { ifDefined } from '@prisma-next/utils/defined';
14
13
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
15
14
  import { Command } from 'commander';
16
- import { isAbsolute, relative, resolve } from 'pathe';
15
+ import { relative } from 'pathe';
17
16
  import { loadConfig } from '../config-loader';
18
17
  import { createControlClient } from '../control-api/client';
19
18
  import {
@@ -36,61 +35,33 @@ import { formatMigrationShowOutput } from '../utils/formatters/migrations';
36
35
  import { formatStyledHeader } from '../utils/formatters/styled';
37
36
  import type { CommonCommandOptions } from '../utils/global-flags';
38
37
  import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
38
+ import {
39
+ findPackageByDirPath,
40
+ looksLikePath,
41
+ resolveAppTargetPath,
42
+ } from '../utils/migration-path-target';
39
43
  import { handleResult } from '../utils/result-handler';
40
44
  import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
45
+ import type { MigrationShowResult } from './json/schemas';
41
46
 
42
47
  interface MigrationShowOptions extends CommonCommandOptions {
43
48
  readonly config?: string;
44
49
  }
45
50
 
46
51
  export interface MigrationShowPresent {
47
- readonly spaceId: string;
48
- readonly dirName: string;
49
- readonly dirPath: string;
50
- readonly from: string | null;
51
- readonly to: string;
52
- readonly migrationHash: string;
52
+ readonly space: string;
53
+ readonly name: string;
54
+ readonly fromContract: string | null;
55
+ readonly toContract: string;
56
+ readonly hash: string;
53
57
  readonly createdAt: string;
54
- readonly operations: readonly {
55
- readonly id: string;
56
- readonly label: string;
57
- readonly operationClass: string;
58
- }[];
59
- readonly preview: OperationPreview;
60
- readonly summary: string;
61
- }
62
-
63
- export interface MigrationShowResult {
64
- readonly ok: true;
65
- readonly migration: MigrationShowPresent;
66
- }
67
-
68
- function looksLikePath(target: string): boolean {
69
- return target.includes('/') || target.includes('\\');
58
+ readonly operations: { id: string; label: string; operationClass: string }[];
59
+ readonly preview: {
60
+ statements: { text: string; language: string }[];
61
+ };
70
62
  }
71
63
 
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
- }
64
+ export type { MigrationShowResult };
94
65
 
95
66
  function pkgToPresent(
96
67
  spaceId: string,
@@ -98,33 +69,23 @@ function pkgToPresent(
98
69
  client: ReturnType<typeof createControlClient>,
99
70
  ): MigrationShowPresent {
100
71
  const ops = castAs<readonly MigrationPlanOperation[]>(pkg.ops);
101
- const preview: OperationPreview = client.toOperationPreview(ops) ?? { statements: [] };
72
+ const rawPreview = client.toOperationPreview(ops) ?? { statements: [] };
102
73
  return {
103
- spaceId,
104
- dirName: pkg.dirName,
105
- dirPath: relative(process.cwd(), pkg.dirPath),
106
- from: pkg.metadata.from,
107
- to: pkg.metadata.to,
108
- migrationHash: pkg.metadata.migrationHash,
74
+ space: spaceId,
75
+ name: pkg.dirName,
76
+ fromContract: pkg.metadata.from,
77
+ toContract: pkg.metadata.to,
78
+ hash: pkg.metadata.migrationHash,
109
79
  createdAt: pkg.metadata.createdAt,
110
80
  operations: ops.map((op) => ({
111
81
  id: op.id,
112
82
  label: op.label,
113
83
  operationClass: op.operationClass,
114
84
  })),
115
- preview,
116
- summary: `${ops.length} operation(s)`,
85
+ preview: { statements: [...rawPreview.statements] },
117
86
  };
118
87
  }
119
88
 
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
89
  async function executeMigrationShowCommand(
129
90
  target: string,
130
91
  options: MigrationShowOptions,
@@ -248,9 +209,11 @@ async function executeMigrationShowCommand(
248
209
  appPkg = matchedPkg;
249
210
  }
250
211
 
212
+ const migration = pkgToPresent(APP_SPACE_ID, appPkg, client);
251
213
  return ok({
252
214
  ok: true,
253
- migration: pkgToPresent(APP_SPACE_ID, appPkg, client),
215
+ summary: `Migration ${migration.name} in ${migration.space}: ${migration.operations.length} operation(s)`,
216
+ migration,
254
217
  });
255
218
  }
256
219
 
@@ -260,11 +223,13 @@ export function createMigrationShowCommand(): Command {
260
223
  command,
261
224
  'Display migration package contents',
262
225
  'Shows the operations, statement preview, and metadata for one app-space migration.\n' +
263
- 'Accepts a directory path, directory name, or hash prefix.',
226
+ 'Accepts a directory path, directory name, or hash prefix.\n' +
227
+ 'Offline — does not consult the database.',
264
228
  );
265
229
  setCommandExamples(command, [
266
230
  'prisma-next migration show 20260101_100000_add_user',
267
231
  'prisma-next migration show sha256:a1b2c3',
232
+ 'prisma-next migration show 20260101_100000_add_user --json',
268
233
  ]);
269
234
  setCommandSeeAlso(command, [
270
235
  { verb: 'migration status', oneLiner: 'Show migration path and pending status' },
@@ -273,7 +238,7 @@ export function createMigrationShowCommand(): Command {
273
238
  { verb: 'migration graph', oneLiner: 'Show the migration graph topology' },
274
239
  ]);
275
240
  addGlobalOptions(command)
276
- .argument('<target>', 'Migration reference: directory name, hash/prefix, or path')
241
+ .argument('<target>', 'Migration reference: directory name, hash/prefix, ref, or path')
277
242
  .option('--config <path>', 'Path to prisma-next.config.ts')
278
243
  .action(async (target: string, options: MigrationShowOptions) => {
279
244
  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-labels';
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
+ }