@prisma-next/cli 0.3.0-pr.99.5 → 0.3.0

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 (257) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +381 -128
  3. package/dist/agent-skill-mongo.md +106 -0
  4. package/dist/agent-skill-postgres.md +106 -0
  5. package/dist/cli-errors-BDCYR5ap.mjs +4 -0
  6. package/dist/cli-errors-DStABy9d.d.mts +3 -0
  7. package/dist/cli.d.mts +1 -0
  8. package/dist/cli.js +1 -2910
  9. package/dist/cli.mjs +261 -0
  10. package/dist/cli.mjs.map +1 -0
  11. package/dist/client-DiUkJAeN.mjs +987 -0
  12. package/dist/client-DiUkJAeN.mjs.map +1 -0
  13. package/dist/commands/contract-emit.d.mts +7 -0
  14. package/dist/commands/contract-emit.d.mts.map +1 -0
  15. package/dist/commands/contract-emit.mjs +9 -0
  16. package/dist/commands/contract-infer.d.mts +7 -0
  17. package/dist/commands/contract-infer.d.mts.map +1 -0
  18. package/dist/commands/contract-infer.mjs +10 -0
  19. package/dist/commands/db-init.d.mts +7 -0
  20. package/dist/commands/db-init.d.mts.map +1 -0
  21. package/dist/commands/db-init.mjs +126 -0
  22. package/dist/commands/db-init.mjs.map +1 -0
  23. package/dist/commands/db-schema.d.mts +7 -0
  24. package/dist/commands/db-schema.d.mts.map +1 -0
  25. package/dist/commands/db-schema.mjs +56 -0
  26. package/dist/commands/db-schema.mjs.map +1 -0
  27. package/dist/commands/db-sign.d.mts +7 -0
  28. package/dist/commands/db-sign.d.mts.map +1 -0
  29. package/dist/commands/db-sign.mjs +137 -0
  30. package/dist/commands/db-sign.mjs.map +1 -0
  31. package/dist/commands/db-update.d.mts +7 -0
  32. package/dist/commands/db-update.d.mts.map +1 -0
  33. package/dist/commands/db-update.mjs +123 -0
  34. package/dist/commands/db-update.mjs.map +1 -0
  35. package/dist/commands/db-verify.d.mts +7 -0
  36. package/dist/commands/db-verify.d.mts.map +1 -0
  37. package/dist/commands/db-verify.mjs +323 -0
  38. package/dist/commands/db-verify.mjs.map +1 -0
  39. package/dist/commands/migration-apply.d.mts +36 -0
  40. package/dist/commands/migration-apply.d.mts.map +1 -0
  41. package/dist/commands/migration-apply.mjs +245 -0
  42. package/dist/commands/migration-apply.mjs.map +1 -0
  43. package/dist/commands/migration-new.d.mts +8 -0
  44. package/dist/commands/migration-new.d.mts.map +1 -0
  45. package/dist/commands/migration-new.mjs +152 -0
  46. package/dist/commands/migration-new.mjs.map +1 -0
  47. package/dist/commands/migration-plan.d.mts +47 -0
  48. package/dist/commands/migration-plan.d.mts.map +1 -0
  49. package/dist/commands/migration-plan.mjs +313 -0
  50. package/dist/commands/migration-plan.mjs.map +1 -0
  51. package/dist/commands/migration-ref.d.mts +43 -0
  52. package/dist/commands/migration-ref.d.mts.map +1 -0
  53. package/dist/commands/migration-ref.mjs +195 -0
  54. package/dist/commands/migration-ref.mjs.map +1 -0
  55. package/dist/commands/migration-show.d.mts +28 -0
  56. package/dist/commands/migration-show.d.mts.map +1 -0
  57. package/dist/commands/migration-show.mjs +140 -0
  58. package/dist/commands/migration-show.mjs.map +1 -0
  59. package/dist/commands/migration-status.d.mts +86 -0
  60. package/dist/commands/migration-status.d.mts.map +1 -0
  61. package/dist/commands/migration-status.mjs +9 -0
  62. package/dist/commands/migration-verify.d.mts +16 -0
  63. package/dist/commands/migration-verify.d.mts.map +1 -0
  64. package/dist/commands/migration-verify.mjs +110 -0
  65. package/dist/commands/migration-verify.mjs.map +1 -0
  66. package/dist/config-loader-C4VXKl8f.mjs +43 -0
  67. package/dist/config-loader-C4VXKl8f.mjs.map +1 -0
  68. package/dist/{config-loader.d.ts → config-loader.d.mts} +8 -3
  69. package/dist/config-loader.d.mts.map +1 -0
  70. package/dist/config-loader.mjs +3 -0
  71. package/dist/contract-emit-D2wDXfyo.mjs +191 -0
  72. package/dist/contract-emit-D2wDXfyo.mjs.map +1 -0
  73. package/dist/contract-emit-Zm_sd1wQ.mjs +112 -0
  74. package/dist/contract-emit-Zm_sd1wQ.mjs.map +1 -0
  75. package/dist/contract-emit-kN-IkKTE.mjs +6 -0
  76. package/dist/contract-enrichment-CGW6mm-E.mjs +79 -0
  77. package/dist/contract-enrichment-CGW6mm-E.mjs.map +1 -0
  78. package/dist/contract-infer-DozZT511.mjs +90 -0
  79. package/dist/contract-infer-DozZT511.mjs.map +1 -0
  80. package/dist/exports/config-types.d.mts +2 -0
  81. package/dist/exports/config-types.mjs +3 -0
  82. package/dist/exports/control-api.d.mts +624 -0
  83. package/dist/exports/control-api.d.mts.map +1 -0
  84. package/dist/exports/control-api.mjs +8 -0
  85. package/dist/{load-ts-contract.d.ts → exports/index.d.mts} +12 -7
  86. package/dist/exports/index.d.mts.map +1 -0
  87. package/dist/exports/index.mjs +142 -0
  88. package/dist/exports/index.mjs.map +1 -0
  89. package/dist/extract-operation-statements-DZUJNmL3.mjs +13 -0
  90. package/dist/extract-operation-statements-DZUJNmL3.mjs.map +1 -0
  91. package/dist/extract-sql-ddl-DDMX-9mz.mjs +26 -0
  92. package/dist/extract-sql-ddl-DDMX-9mz.mjs.map +1 -0
  93. package/dist/framework-components-BAsliT4V.mjs +59 -0
  94. package/dist/framework-components-BAsliT4V.mjs.map +1 -0
  95. package/dist/init-6Pvm_esG.mjs +430 -0
  96. package/dist/init-6Pvm_esG.mjs.map +1 -0
  97. package/dist/inspect-live-schema-BYnhztxZ.mjs +91 -0
  98. package/dist/inspect-live-schema-BYnhztxZ.mjs.map +1 -0
  99. package/dist/migration-command-scaffold-CntCcntR.mjs +105 -0
  100. package/dist/migration-command-scaffold-CntCcntR.mjs.map +1 -0
  101. package/dist/migration-status-CJANY4yr.mjs +1583 -0
  102. package/dist/migration-status-CJANY4yr.mjs.map +1 -0
  103. package/dist/migrations-DTZBYXm1.mjs +173 -0
  104. package/dist/migrations-DTZBYXm1.mjs.map +1 -0
  105. package/dist/progress-adapter-B-YvmcDu.mjs +43 -0
  106. package/dist/progress-adapter-B-YvmcDu.mjs.map +1 -0
  107. package/dist/quick-reference-mongo.md +93 -0
  108. package/dist/quick-reference-postgres.md +91 -0
  109. package/dist/result-handler-oK_vA-Fn.mjs +697 -0
  110. package/dist/result-handler-oK_vA-Fn.mjs.map +1 -0
  111. package/dist/terminal-ui-C5k88MmW.mjs +274 -0
  112. package/dist/terminal-ui-C5k88MmW.mjs.map +1 -0
  113. package/dist/validate-contract-deps-esa-VQ0h.mjs +37 -0
  114. package/dist/validate-contract-deps-esa-VQ0h.mjs.map +1 -0
  115. package/dist/verify-DlFQ2FOw.mjs +385 -0
  116. package/dist/verify-DlFQ2FOw.mjs.map +1 -0
  117. package/package.json +87 -40
  118. package/src/cli.ts +118 -58
  119. package/src/commands/contract-emit.ts +101 -78
  120. package/src/commands/contract-infer-paths.ts +32 -0
  121. package/src/commands/contract-infer.ts +143 -0
  122. package/src/commands/db-init.ts +97 -219
  123. package/src/commands/db-schema.ts +77 -0
  124. package/src/commands/db-sign.ts +46 -73
  125. package/src/commands/db-update.ts +236 -0
  126. package/src/commands/db-verify.ts +409 -119
  127. package/src/commands/init/detect-package-manager.ts +47 -0
  128. package/src/commands/init/index.ts +21 -0
  129. package/src/commands/init/init.ts +203 -0
  130. package/src/commands/init/templates/agent-skill-mongo.md +106 -0
  131. package/src/commands/init/templates/agent-skill-postgres.md +106 -0
  132. package/src/commands/init/templates/agent-skill.ts +19 -0
  133. package/src/commands/init/templates/code-templates.ts +168 -0
  134. package/src/commands/init/templates/quick-reference-mongo.md +93 -0
  135. package/src/commands/init/templates/quick-reference-postgres.md +91 -0
  136. package/src/commands/init/templates/quick-reference.ts +19 -0
  137. package/src/commands/init/templates/render.ts +20 -0
  138. package/src/commands/init/templates/tsconfig.ts +35 -0
  139. package/src/commands/inspect-live-schema.ts +170 -0
  140. package/src/commands/migration-apply.ts +427 -0
  141. package/src/commands/migration-new.ts +260 -0
  142. package/src/commands/migration-plan.ts +519 -0
  143. package/src/commands/migration-ref.ts +305 -0
  144. package/src/commands/migration-show.ts +246 -0
  145. package/src/commands/migration-status.ts +864 -0
  146. package/src/commands/migration-verify.ts +180 -0
  147. package/src/config-loader.ts +13 -3
  148. package/src/control-api/client.ts +205 -183
  149. package/src/control-api/contract-enrichment.ts +119 -0
  150. package/src/control-api/errors.ts +9 -0
  151. package/src/control-api/operations/contract-emit.ts +181 -0
  152. package/src/control-api/operations/db-init.ts +53 -49
  153. package/src/control-api/operations/db-update.ts +220 -0
  154. package/src/control-api/operations/extract-operation-statements.ts +14 -0
  155. package/src/control-api/operations/extract-sql-ddl.ts +47 -0
  156. package/src/control-api/operations/migration-apply.ts +191 -0
  157. package/src/control-api/operations/migration-helpers.ts +49 -0
  158. package/src/control-api/types.ts +274 -52
  159. package/src/exports/config-types.ts +4 -3
  160. package/src/exports/control-api.ts +15 -5
  161. package/src/load-ts-contract.ts +30 -19
  162. package/src/utils/cli-errors.ts +14 -8
  163. package/src/utils/command-helpers.ts +302 -3
  164. package/src/utils/formatters/emit.ts +67 -0
  165. package/src/utils/formatters/errors.ts +82 -0
  166. package/src/utils/formatters/graph-migration-mapper.ts +240 -0
  167. package/src/utils/formatters/graph-render.ts +1323 -0
  168. package/src/utils/formatters/graph-types.ts +120 -0
  169. package/src/utils/formatters/help.ts +380 -0
  170. package/src/utils/formatters/helpers.ts +28 -0
  171. package/src/utils/formatters/migrations.ts +346 -0
  172. package/src/utils/formatters/styled.ts +212 -0
  173. package/src/utils/formatters/verify.ts +621 -0
  174. package/src/utils/framework-components.ts +13 -10
  175. package/src/utils/global-flags.ts +41 -23
  176. package/src/utils/migration-command-scaffold.ts +184 -0
  177. package/src/utils/migration-types.ts +12 -0
  178. package/src/utils/progress-adapter.ts +18 -29
  179. package/src/utils/result-handler.ts +12 -13
  180. package/src/utils/shutdown.ts +92 -0
  181. package/src/utils/suggest-command.ts +31 -0
  182. package/src/utils/terminal-ui.ts +276 -0
  183. package/src/utils/validate-contract-deps.ts +49 -0
  184. package/dist/chunk-AGOTG4L3.js +0 -965
  185. package/dist/chunk-AGOTG4L3.js.map +0 -1
  186. package/dist/chunk-HLLI4YL7.js +0 -180
  187. package/dist/chunk-HLLI4YL7.js.map +0 -1
  188. package/dist/chunk-HWYQOCAJ.js +0 -47
  189. package/dist/chunk-HWYQOCAJ.js.map +0 -1
  190. package/dist/chunk-VG2R7DGF.js +0 -735
  191. package/dist/chunk-VG2R7DGF.js.map +0 -1
  192. package/dist/cli.d.ts +0 -2
  193. package/dist/cli.d.ts.map +0 -1
  194. package/dist/cli.js.map +0 -1
  195. package/dist/commands/contract-emit.d.ts +0 -3
  196. package/dist/commands/contract-emit.d.ts.map +0 -1
  197. package/dist/commands/contract-emit.js +0 -10
  198. package/dist/commands/contract-emit.js.map +0 -1
  199. package/dist/commands/db-init.d.ts +0 -3
  200. package/dist/commands/db-init.d.ts.map +0 -1
  201. package/dist/commands/db-init.js +0 -257
  202. package/dist/commands/db-init.js.map +0 -1
  203. package/dist/commands/db-introspect.d.ts +0 -3
  204. package/dist/commands/db-introspect.d.ts.map +0 -1
  205. package/dist/commands/db-introspect.js +0 -155
  206. package/dist/commands/db-introspect.js.map +0 -1
  207. package/dist/commands/db-schema-verify.d.ts +0 -3
  208. package/dist/commands/db-schema-verify.d.ts.map +0 -1
  209. package/dist/commands/db-schema-verify.js +0 -171
  210. package/dist/commands/db-schema-verify.js.map +0 -1
  211. package/dist/commands/db-sign.d.ts +0 -3
  212. package/dist/commands/db-sign.d.ts.map +0 -1
  213. package/dist/commands/db-sign.js +0 -195
  214. package/dist/commands/db-sign.js.map +0 -1
  215. package/dist/commands/db-verify.d.ts +0 -3
  216. package/dist/commands/db-verify.d.ts.map +0 -1
  217. package/dist/commands/db-verify.js +0 -193
  218. package/dist/commands/db-verify.js.map +0 -1
  219. package/dist/config-loader.d.ts.map +0 -1
  220. package/dist/config-loader.js +0 -7
  221. package/dist/config-loader.js.map +0 -1
  222. package/dist/control-api/client.d.ts +0 -13
  223. package/dist/control-api/client.d.ts.map +0 -1
  224. package/dist/control-api/operations/db-init.d.ts +0 -29
  225. package/dist/control-api/operations/db-init.d.ts.map +0 -1
  226. package/dist/control-api/types.d.ts +0 -387
  227. package/dist/control-api/types.d.ts.map +0 -1
  228. package/dist/exports/config-types.d.ts +0 -3
  229. package/dist/exports/config-types.d.ts.map +0 -1
  230. package/dist/exports/config-types.js +0 -6
  231. package/dist/exports/config-types.js.map +0 -1
  232. package/dist/exports/control-api.d.ts +0 -13
  233. package/dist/exports/control-api.d.ts.map +0 -1
  234. package/dist/exports/control-api.js +0 -7
  235. package/dist/exports/control-api.js.map +0 -1
  236. package/dist/exports/index.d.ts +0 -4
  237. package/dist/exports/index.d.ts.map +0 -1
  238. package/dist/exports/index.js +0 -176
  239. package/dist/exports/index.js.map +0 -1
  240. package/dist/load-ts-contract.d.ts.map +0 -1
  241. package/dist/utils/cli-errors.d.ts +0 -7
  242. package/dist/utils/cli-errors.d.ts.map +0 -1
  243. package/dist/utils/command-helpers.d.ts +0 -12
  244. package/dist/utils/command-helpers.d.ts.map +0 -1
  245. package/dist/utils/framework-components.d.ts +0 -70
  246. package/dist/utils/framework-components.d.ts.map +0 -1
  247. package/dist/utils/global-flags.d.ts +0 -25
  248. package/dist/utils/global-flags.d.ts.map +0 -1
  249. package/dist/utils/output.d.ts +0 -142
  250. package/dist/utils/output.d.ts.map +0 -1
  251. package/dist/utils/progress-adapter.d.ts +0 -26
  252. package/dist/utils/progress-adapter.d.ts.map +0 -1
  253. package/dist/utils/result-handler.d.ts +0 -15
  254. package/dist/utils/result-handler.d.ts.map +0 -1
  255. package/src/commands/db-introspect.ts +0 -227
  256. package/src/commands/db-schema-verify.ts +0 -238
  257. package/src/utils/output.ts +0 -1471
@@ -0,0 +1,305 @@
1
+ import {
2
+ readRefs,
3
+ resolveRef,
4
+ validateRefName,
5
+ validateRefValue,
6
+ writeRefs,
7
+ } from '@prisma-next/migration-tools/refs';
8
+ import { MigrationToolsError } from '@prisma-next/migration-tools/types';
9
+ import { notOk, ok, type Result } from '@prisma-next/utils/result';
10
+ import { Command } from 'commander';
11
+ import { resolve } from 'pathe';
12
+ import { loadConfig } from '../config-loader';
13
+ import { CliStructuredError, errorRuntime, errorUnexpected } from '../utils/cli-errors';
14
+ import { addGlobalOptions, setCommandDescriptions } from '../utils/command-helpers';
15
+ import { formatCommandHelp } from '../utils/formatters/help';
16
+ import { parseGlobalFlags } from '../utils/global-flags';
17
+ import { handleResult } from '../utils/result-handler';
18
+ import { TerminalUI } from '../utils/terminal-ui';
19
+
20
+ interface RefSetResult {
21
+ readonly ok: true;
22
+ readonly ref: string;
23
+ readonly hash: string;
24
+ }
25
+
26
+ interface RefGetResult {
27
+ readonly ok: true;
28
+ readonly ref: string;
29
+ readonly hash: string;
30
+ }
31
+
32
+ interface RefDeleteResult {
33
+ readonly ok: true;
34
+ readonly ref: string;
35
+ readonly deleted: true;
36
+ }
37
+
38
+ interface RefListResult {
39
+ readonly ok: true;
40
+ readonly refs: Record<string, string>;
41
+ }
42
+
43
+ function resolveRefsPath(configPath?: string, config?: { migrations?: { dir?: string } }): string {
44
+ const base = configPath ? resolve(configPath, '..') : process.cwd();
45
+ return resolve(base, config?.migrations?.dir ?? 'migrations', 'refs.json');
46
+ }
47
+
48
+ function mapError(error: unknown): CliStructuredError {
49
+ if (MigrationToolsError.is(error)) {
50
+ return errorRuntime(error.message, {
51
+ why: error.why,
52
+ fix: error.fix,
53
+ meta: { code: error.code },
54
+ });
55
+ }
56
+ return errorUnexpected(error instanceof Error ? error.message : String(error));
57
+ }
58
+
59
+ function cliErrorInvalidRefName(name: string): CliStructuredError {
60
+ return errorRuntime(`Invalid ref name "${name}"`, {
61
+ why: `Ref name "${name}" does not match the required format`,
62
+ fix: 'Ref names must be lowercase alphanumeric with hyphens or forward slashes, no `.` or `..` segments',
63
+ });
64
+ }
65
+
66
+ function cliErrorInvalidRefValue(hash: string): CliStructuredError {
67
+ return errorRuntime(`Invalid contract hash "${hash}"`, {
68
+ why: `"${hash}" is not a valid contract hash`,
69
+ fix: 'Contract hashes must match the format "sha256:<64 hex chars>". Copy the hash from `prisma-next contract emit` or `migration status --json`.',
70
+ });
71
+ }
72
+
73
+ function errorRefNotFound(name: string): CliStructuredError {
74
+ return errorRuntime(`Ref "${name}" does not exist`, {
75
+ why: `No ref named "${name}" found in refs.json`,
76
+ fix: `Run \`prisma-next migration ref list\` to see available refs, or \`prisma-next migration ref set ${name} <hash>\` to create it`,
77
+ });
78
+ }
79
+
80
+ async function executeRefSetCommand(
81
+ name: string,
82
+ hash: string,
83
+ options: { config?: string },
84
+ ): Promise<Result<RefSetResult, CliStructuredError>> {
85
+ if (!validateRefName(name)) {
86
+ return notOk(cliErrorInvalidRefName(name));
87
+ }
88
+ if (!validateRefValue(hash)) {
89
+ return notOk(cliErrorInvalidRefValue(hash));
90
+ }
91
+
92
+ try {
93
+ const config = await loadConfig(options.config);
94
+ const refsPath = resolveRefsPath(options.config, config);
95
+ const refs = await readRefs(refsPath);
96
+ const updated = { ...refs, [name]: hash };
97
+ await writeRefs(refsPath, updated);
98
+ return ok({ ok: true as const, ref: name, hash });
99
+ } catch (error) {
100
+ if (error instanceof CliStructuredError) return notOk(error);
101
+ return notOk(mapError(error));
102
+ }
103
+ }
104
+
105
+ async function executeRefGetCommand(
106
+ name: string,
107
+ options: { config?: string },
108
+ ): Promise<Result<RefGetResult, CliStructuredError>> {
109
+ try {
110
+ const config = await loadConfig(options.config);
111
+ const refsPath = resolveRefsPath(options.config, config);
112
+ const refs = await readRefs(refsPath);
113
+ const hash = resolveRef(refs, name);
114
+ return ok({ ok: true as const, ref: name, hash });
115
+ } catch (error) {
116
+ if (error instanceof CliStructuredError) return notOk(error);
117
+ return notOk(mapError(error));
118
+ }
119
+ }
120
+
121
+ async function executeRefDeleteCommand(
122
+ name: string,
123
+ options: { config?: string },
124
+ ): Promise<Result<RefDeleteResult, CliStructuredError>> {
125
+ try {
126
+ const config = await loadConfig(options.config);
127
+ const refsPath = resolveRefsPath(options.config, config);
128
+ const refs = await readRefs(refsPath);
129
+ if (!Object.hasOwn(refs, name)) {
130
+ return notOk(errorRefNotFound(name));
131
+ }
132
+ const { [name]: _, ...remaining } = refs;
133
+ await writeRefs(refsPath, remaining);
134
+ return ok({ ok: true as const, ref: name, deleted: true as const });
135
+ } catch (error) {
136
+ if (error instanceof CliStructuredError) return notOk(error);
137
+ return notOk(mapError(error));
138
+ }
139
+ }
140
+
141
+ async function executeRefListCommand(options: {
142
+ config?: string;
143
+ }): Promise<Result<RefListResult, CliStructuredError>> {
144
+ try {
145
+ const config = await loadConfig(options.config);
146
+ const refsPath = resolveRefsPath(options.config, config);
147
+ const refs = await readRefs(refsPath);
148
+ return ok({ ok: true as const, refs });
149
+ } catch (error) {
150
+ if (error instanceof CliStructuredError) return notOk(error);
151
+ return notOk(mapError(error));
152
+ }
153
+ }
154
+
155
+ function createRefSetCommand(): Command {
156
+ const command = new Command('set');
157
+ setCommandDescriptions(
158
+ command,
159
+ 'Set a ref to a contract hash',
160
+ 'Sets a named ref to point to a contract hash in migrations/refs.json.',
161
+ );
162
+ addGlobalOptions(command)
163
+ .argument('<name>', 'Ref name (e.g., staging, production)')
164
+ .argument('<hash>', 'Contract hash to point to')
165
+ .option('--config <path>', 'Path to prisma-next.config.ts')
166
+ .action(
167
+ async (
168
+ name: string,
169
+ hash: string,
170
+ options: { config?: string; json?: string | boolean; quiet?: boolean },
171
+ ) => {
172
+ const flags = parseGlobalFlags(options);
173
+ const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
174
+ const result = await executeRefSetCommand(name, hash, options);
175
+ const exitCode = handleResult(result, flags, ui, (value) => {
176
+ if (flags.json) {
177
+ ui.output(JSON.stringify(value));
178
+ } else if (!flags.quiet) {
179
+ ui.output(`Set ref "${value.ref}" → ${value.hash}`);
180
+ }
181
+ });
182
+ process.exit(exitCode);
183
+ },
184
+ );
185
+ return command;
186
+ }
187
+
188
+ function createRefGetCommand(): Command {
189
+ const command = new Command('get');
190
+ setCommandDescriptions(
191
+ command,
192
+ 'Get the hash for a ref',
193
+ 'Reads a named ref from migrations/refs.json and prints its contract hash.',
194
+ );
195
+ addGlobalOptions(command)
196
+ .argument('<name>', 'Ref name to look up')
197
+ .option('--config <path>', 'Path to prisma-next.config.ts')
198
+ .action(
199
+ async (
200
+ name: string,
201
+ options: { config?: string; json?: string | boolean; quiet?: boolean },
202
+ ) => {
203
+ const flags = parseGlobalFlags(options);
204
+ const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
205
+ const result = await executeRefGetCommand(name, options);
206
+ const exitCode = handleResult(result, flags, ui, (value) => {
207
+ if (flags.json) {
208
+ ui.output(JSON.stringify(value));
209
+ } else {
210
+ ui.output(value.hash);
211
+ }
212
+ });
213
+ process.exit(exitCode);
214
+ },
215
+ );
216
+ return command;
217
+ }
218
+
219
+ function createRefDeleteCommand(): Command {
220
+ const command = new Command('delete');
221
+ setCommandDescriptions(command, 'Delete a ref', 'Removes a named ref from migrations/refs.json.');
222
+ addGlobalOptions(command)
223
+ .argument('<name>', 'Ref name to delete')
224
+ .option('--config <path>', 'Path to prisma-next.config.ts')
225
+ .action(
226
+ async (
227
+ name: string,
228
+ options: { config?: string; json?: string | boolean; quiet?: boolean },
229
+ ) => {
230
+ const flags = parseGlobalFlags(options);
231
+ const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
232
+ const result = await executeRefDeleteCommand(name, options);
233
+ const exitCode = handleResult(result, flags, ui, (value) => {
234
+ if (flags.json) {
235
+ ui.output(JSON.stringify(value));
236
+ } else if (!flags.quiet) {
237
+ ui.output(`Deleted ref "${value.ref}"`);
238
+ }
239
+ });
240
+ process.exit(exitCode);
241
+ },
242
+ );
243
+ return command;
244
+ }
245
+
246
+ function createRefListCommand(): Command {
247
+ const command = new Command('list');
248
+ setCommandDescriptions(
249
+ command,
250
+ 'List all refs',
251
+ 'Lists all named refs from migrations/refs.json.',
252
+ );
253
+ addGlobalOptions(command)
254
+ .option('--config <path>', 'Path to prisma-next.config.ts')
255
+ .action(async (options: { config?: string; json?: string | boolean; quiet?: boolean }) => {
256
+ const flags = parseGlobalFlags(options);
257
+ const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
258
+ const result = await executeRefListCommand(options);
259
+ const exitCode = handleResult(result, flags, ui, (value) => {
260
+ if (flags.json) {
261
+ ui.output(JSON.stringify(value));
262
+ } else if (!flags.quiet) {
263
+ const entries = Object.entries(value.refs);
264
+ if (entries.length === 0) {
265
+ ui.output('No refs defined');
266
+ } else {
267
+ for (const [refName, hash] of entries) {
268
+ ui.output(`${refName} → ${hash}`);
269
+ }
270
+ }
271
+ }
272
+ });
273
+ process.exit(exitCode);
274
+ });
275
+ return command;
276
+ }
277
+
278
+ export {
279
+ executeRefSetCommand,
280
+ executeRefGetCommand,
281
+ executeRefDeleteCommand,
282
+ executeRefListCommand,
283
+ cliErrorInvalidRefName,
284
+ cliErrorInvalidRefValue,
285
+ errorRefNotFound,
286
+ };
287
+
288
+ export function createMigrationRefCommand(): Command {
289
+ const command = new Command('ref');
290
+ setCommandDescriptions(
291
+ command,
292
+ 'Manage migration refs',
293
+ 'Manage named refs in migrations/refs.json. Refs map logical environment\n' +
294
+ 'names (e.g., staging, production) to contract hashes.',
295
+ );
296
+ addGlobalOptions(command).configureHelp({
297
+ formatHelp: (cmd) => formatCommandHelp({ command: cmd, flags: parseGlobalFlags({}) }),
298
+ subcommandDescription: () => '',
299
+ });
300
+ command.addCommand(createRefSetCommand());
301
+ command.addCommand(createRefGetCommand());
302
+ command.addCommand(createRefDeleteCommand());
303
+ command.addCommand(createRefListCommand());
304
+ return command;
305
+ }
@@ -0,0 +1,246 @@
1
+ import type { MigrationPlanOperation } from '@prisma-next/framework-components/control';
2
+ import { findLatestMigration, reconstructGraph } from '@prisma-next/migration-tools/dag';
3
+ import { readMigrationPackage, readMigrationsDir } from '@prisma-next/migration-tools/io';
4
+ import type { MigrationBundle } from '@prisma-next/migration-tools/types';
5
+ import { isAttested, MigrationToolsError } from '@prisma-next/migration-tools/types';
6
+ import { notOk, ok, type Result } from '@prisma-next/utils/result';
7
+ import { Command } from 'commander';
8
+ import { relative, resolve } from 'pathe';
9
+ import { loadConfig } from '../config-loader';
10
+ import { extractOperationStatements } from '../control-api/operations/extract-operation-statements';
11
+ import { type CliStructuredError, errorRuntime, errorUnexpected } from '../utils/cli-errors';
12
+ import {
13
+ addGlobalOptions,
14
+ setCommandDescriptions,
15
+ setCommandExamples,
16
+ } from '../utils/command-helpers';
17
+ import { formatMigrationShowOutput } from '../utils/formatters/migrations';
18
+ import { formatStyledHeader } from '../utils/formatters/styled';
19
+ import type { CommonCommandOptions } from '../utils/global-flags';
20
+ import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
21
+ import { handleResult } from '../utils/result-handler';
22
+ import { TerminalUI } from '../utils/terminal-ui';
23
+
24
+ interface MigrationShowOptions extends CommonCommandOptions {
25
+ readonly config?: string;
26
+ }
27
+
28
+ export interface MigrationShowResult {
29
+ readonly ok: true;
30
+ readonly dirName: string;
31
+ readonly dirPath: string;
32
+ readonly from: string;
33
+ readonly to: string;
34
+ readonly migrationId: string | null;
35
+ readonly kind: string;
36
+ readonly createdAt: string;
37
+ readonly operations: readonly {
38
+ readonly id: string;
39
+ readonly label: string;
40
+ readonly operationClass: string;
41
+ }[];
42
+ readonly sql: readonly string[];
43
+ readonly summary: string;
44
+ }
45
+
46
+ function looksLikePath(target: string): boolean {
47
+ return target.includes('/') || target.includes('\\');
48
+ }
49
+
50
+ export function resolveByHashPrefix(
51
+ packages: readonly MigrationBundle[],
52
+ prefix: string,
53
+ ): Result<MigrationBundle, CliStructuredError> {
54
+ const normalizedPrefix = prefix.startsWith('sha256:') ? prefix : `sha256:${prefix}`;
55
+ const attested = packages.filter((p) => typeof p.manifest.migrationId === 'string');
56
+ const matches = attested.filter((p) => p.manifest.migrationId!.startsWith(normalizedPrefix));
57
+
58
+ if (matches.length === 1) {
59
+ return ok(matches[0]!);
60
+ }
61
+
62
+ if (matches.length === 0) {
63
+ return notOk(
64
+ errorRuntime('No migration found matching prefix', {
65
+ why: `No attested migration has a migrationId starting with "${normalizedPrefix}"`,
66
+ fix: 'Run `prisma-next migration show` (no argument) to see the latest migration, or check the migrations directory for available packages.',
67
+ }),
68
+ );
69
+ }
70
+
71
+ const candidates = matches.map((p) => ` ${p.dirName} ${p.manifest.migrationId}`).join('\n');
72
+ return notOk(
73
+ errorRuntime('Ambiguous hash prefix', {
74
+ why: `Multiple migrations match prefix "${normalizedPrefix}":\n${candidates}`,
75
+ fix: 'Provide a longer prefix to uniquely identify the migration.',
76
+ }),
77
+ );
78
+ }
79
+
80
+ async function executeMigrationShowCommand(
81
+ target: string | undefined,
82
+ options: MigrationShowOptions,
83
+ flags: GlobalFlags,
84
+ ui: TerminalUI,
85
+ ): Promise<Result<MigrationShowResult, CliStructuredError>> {
86
+ const config = await loadConfig(options.config);
87
+ const configPath = options.config
88
+ ? relative(process.cwd(), resolve(options.config))
89
+ : 'prisma-next.config.ts';
90
+
91
+ const migrationsDir = resolve(
92
+ options.config ? resolve(options.config, '..') : process.cwd(),
93
+ config.migrations?.dir ?? 'migrations',
94
+ );
95
+ const migrationsRelative = relative(process.cwd(), migrationsDir);
96
+
97
+ if (!flags.json && !flags.quiet) {
98
+ const details: Array<{ label: string; value: string }> = [
99
+ { label: 'config', value: configPath },
100
+ { label: 'migrations', value: migrationsRelative },
101
+ ];
102
+ if (target) {
103
+ details.push({ label: 'target', value: target });
104
+ }
105
+ const header = formatStyledHeader({
106
+ command: 'migration show',
107
+ description: 'Display migration package contents',
108
+ details,
109
+ flags,
110
+ });
111
+ ui.stderr(header);
112
+ }
113
+
114
+ let pkg: MigrationBundle;
115
+
116
+ try {
117
+ if (target && looksLikePath(target)) {
118
+ pkg = await readMigrationPackage(resolve(target));
119
+ } else {
120
+ const allPackages = await readMigrationsDir(migrationsDir);
121
+ if (allPackages.length === 0) {
122
+ return notOk(
123
+ errorRuntime('No migrations found', {
124
+ why: `No migration packages found in ${migrationsRelative}`,
125
+ fix: 'Run `prisma-next migration plan` to create a migration first.',
126
+ }),
127
+ );
128
+ }
129
+
130
+ if (target) {
131
+ const resolved = resolveByHashPrefix(allPackages, target);
132
+ if (!resolved.ok) return resolved;
133
+ pkg = resolved.value;
134
+ } else {
135
+ const attested = allPackages.filter(isAttested);
136
+ if (attested.length === 0) {
137
+ return notOk(
138
+ errorRuntime('No attested migrations found', {
139
+ why: `All migrations in ${migrationsRelative} are drafts (migrationId: null)`,
140
+ fix: 'Run `prisma-next migration verify --dir <path>` to attest a draft migration.',
141
+ }),
142
+ );
143
+ }
144
+ const graph = reconstructGraph(attested);
145
+ const latestMigration = findLatestMigration(graph);
146
+ if (!latestMigration) {
147
+ return notOk(
148
+ errorRuntime('Could not resolve latest migration', {
149
+ why: 'No latest migration found in the migration history',
150
+ fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',
151
+ }),
152
+ );
153
+ }
154
+ const leafPkg = attested.find(
155
+ (p) => p.manifest.migrationId === latestMigration.migrationId,
156
+ );
157
+ if (!leafPkg) {
158
+ return notOk(
159
+ errorRuntime('Could not resolve latest migration', {
160
+ why: `Latest migration ${latestMigration.dirName} does not match any package`,
161
+ fix: 'The migrations directory may be corrupted. Inspect the migration.json files.',
162
+ }),
163
+ );
164
+ }
165
+ pkg = leafPkg;
166
+ }
167
+ }
168
+ } catch (error) {
169
+ if (MigrationToolsError.is(error)) {
170
+ return notOk(
171
+ errorRuntime(error.message, {
172
+ why: error.why,
173
+ fix: error.fix,
174
+ meta: { code: error.code, ...(error.details ?? {}) },
175
+ }),
176
+ );
177
+ }
178
+ return notOk(
179
+ errorUnexpected(error instanceof Error ? error.message : String(error), {
180
+ why: `Failed to read migration: ${error instanceof Error ? error.message : String(error)}`,
181
+ }),
182
+ );
183
+ }
184
+
185
+ const ops = pkg.ops as readonly MigrationPlanOperation[];
186
+ const sql = extractOperationStatements(config.family.familyId, ops) ?? [];
187
+
188
+ const result: MigrationShowResult = {
189
+ ok: true,
190
+ dirName: pkg.dirName,
191
+ dirPath: relative(process.cwd(), pkg.dirPath),
192
+ from: pkg.manifest.from,
193
+ to: pkg.manifest.to,
194
+ migrationId: pkg.manifest.migrationId,
195
+ kind: pkg.manifest.kind,
196
+ createdAt: pkg.manifest.createdAt,
197
+ operations: ops.map((op) => ({
198
+ id: op.id,
199
+ label: op.label,
200
+ operationClass: op.operationClass,
201
+ })),
202
+ sql,
203
+ summary: `${ops.length} operation(s)`,
204
+ };
205
+ return ok(result);
206
+ }
207
+
208
+ export function createMigrationShowCommand(): Command {
209
+ const command = new Command('show');
210
+ setCommandDescriptions(
211
+ command,
212
+ 'Display migration package contents',
213
+ 'Shows the operations, DDL preview, and metadata for a migration package.\n' +
214
+ 'Accepts a directory path, a hash prefix (git-style), or defaults to the\n' +
215
+ 'latest migration.',
216
+ );
217
+ setCommandExamples(command, [
218
+ 'prisma-next migration show',
219
+ 'prisma-next migration show sha256:a1b2c3',
220
+ ]);
221
+ addGlobalOptions(command)
222
+ .argument(
223
+ '[target]',
224
+ 'Migration directory path or migrationId hash prefix (defaults to latest)',
225
+ )
226
+ .option('--config <path>', 'Path to prisma-next.config.ts')
227
+ .action(async (target: string | undefined, options: MigrationShowOptions) => {
228
+ const flags = parseGlobalFlags(options);
229
+
230
+ const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
231
+
232
+ const result = await executeMigrationShowCommand(target, options, flags, ui);
233
+
234
+ const exitCode = handleResult(result, flags, ui, (showResult) => {
235
+ if (flags.json) {
236
+ ui.output(JSON.stringify(showResult, null, 2));
237
+ } else if (!flags.quiet) {
238
+ ui.log(formatMigrationShowOutput(showResult, flags));
239
+ }
240
+ });
241
+
242
+ process.exit(exitCode);
243
+ });
244
+
245
+ return command;
246
+ }