@prisma-next/cli 0.4.1 → 0.4.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 (121) hide show
  1. package/README.md +1 -6
  2. package/dist/agent-skill-mongo.md +63 -31
  3. package/dist/agent-skill-postgres.md +1 -1
  4. package/dist/cli-errors-BFYgBH3L.d.mts +4 -0
  5. package/dist/{cli-errors-CznZA5-d.mjs → cli-errors-Cd79vmTH.mjs} +2 -2
  6. package/dist/cli.mjs +126 -13
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/{client-DGKrciLM.mjs → client-CrsnY58k.mjs} +4 -4
  9. package/dist/{client-DGKrciLM.mjs.map → client-CrsnY58k.mjs.map} +1 -1
  10. package/dist/commands/contract-emit.mjs +7 -2
  11. package/dist/commands/contract-infer.mjs +8 -2
  12. package/dist/commands/db-init.mjs +8 -7
  13. package/dist/commands/db-init.mjs.map +1 -1
  14. package/dist/commands/db-schema.mjs +8 -5
  15. package/dist/commands/db-schema.mjs.map +1 -1
  16. package/dist/commands/db-sign.mjs +8 -7
  17. package/dist/commands/db-sign.mjs.map +1 -1
  18. package/dist/commands/db-update.mjs +8 -7
  19. package/dist/commands/db-update.mjs.map +1 -1
  20. package/dist/commands/db-verify.mjs +8 -7
  21. package/dist/commands/db-verify.mjs.map +1 -1
  22. package/dist/commands/migration-apply.mjs +9 -8
  23. package/dist/commands/migration-apply.mjs.map +1 -1
  24. package/dist/commands/migration-new.mjs +5 -5
  25. package/dist/commands/migration-plan.mjs +6 -6
  26. package/dist/commands/migration-ref.d.mts +6 -4
  27. package/dist/commands/migration-ref.d.mts.map +1 -1
  28. package/dist/commands/migration-ref.mjs +29 -34
  29. package/dist/commands/migration-ref.mjs.map +1 -1
  30. package/dist/commands/migration-show.d.mts +1 -1
  31. package/dist/commands/migration-show.mjs +6 -6
  32. package/dist/commands/migration-status.d.mts.map +1 -1
  33. package/dist/commands/migration-status.mjs +7 -2
  34. package/dist/{config-loader-_xQZsw0i.mjs → config-loader-C25b63rJ.mjs} +1 -1
  35. package/dist/{config-loader-_xQZsw0i.mjs.map → config-loader-C25b63rJ.mjs.map} +1 -1
  36. package/dist/config-loader.mjs +1 -1
  37. package/dist/contract-emit-DxgyXrqV.mjs +6 -0
  38. package/dist/{contract-emit-mU1_B_m9.mjs → contract-emit-NJ01hiiv.mjs} +10 -10
  39. package/dist/contract-emit-NJ01hiiv.mjs.map +1 -0
  40. package/dist/{contract-emit-DgeWdonT.mjs → contract-emit-V5SSitUT.mjs} +6 -6
  41. package/dist/{contract-emit-DgeWdonT.mjs.map → contract-emit-V5SSitUT.mjs.map} +1 -1
  42. package/dist/{contract-enrichment-BV4KpbNW.mjs → contract-enrichment-CAOELa-H.mjs} +1 -1
  43. package/dist/{contract-enrichment-BV4KpbNW.mjs.map → contract-enrichment-CAOELa-H.mjs.map} +1 -1
  44. package/dist/{contract-infer-CUbiWGX0.mjs → contract-infer-D9cC3rJm.mjs} +4 -4
  45. package/dist/{contract-infer-CUbiWGX0.mjs.map → contract-infer-D9cC3rJm.mjs.map} +1 -1
  46. package/dist/exports/control-api.mjs +6 -4
  47. package/dist/exports/index.mjs +7 -2
  48. package/dist/exports/index.mjs.map +1 -1
  49. package/dist/exports/init-output.d.mts +39 -0
  50. package/dist/exports/init-output.d.mts.map +1 -0
  51. package/dist/exports/init-output.mjs +3 -0
  52. package/dist/{extract-operation-statements-DWWFz1PK.mjs → extract-operation-statements-DsFfxXVZ.mjs} +2 -2
  53. package/dist/{extract-operation-statements-DWWFz1PK.mjs.map → extract-operation-statements-DsFfxXVZ.mjs.map} +1 -1
  54. package/dist/{extract-sql-ddl-7zn_AFS8.mjs → extract-sql-ddl-D9UbZDyz.mjs} +1 -1
  55. package/dist/{extract-sql-ddl-7zn_AFS8.mjs.map → extract-sql-ddl-D9UbZDyz.mjs.map} +1 -1
  56. package/dist/{framework-components-B__p--vT.mjs → framework-components-Cr--XBKy.mjs} +2 -2
  57. package/dist/{framework-components-B__p--vT.mjs.map → framework-components-Cr--XBKy.mjs.map} +1 -1
  58. package/dist/init-m8x0UoPY.mjs +2062 -0
  59. package/dist/init-m8x0UoPY.mjs.map +1 -0
  60. package/dist/{inspect-live-schema-wIYBTdL3.mjs → inspect-live-schema-yrHAvG71.mjs} +6 -6
  61. package/dist/{inspect-live-schema-wIYBTdL3.mjs.map → inspect-live-schema-yrHAvG71.mjs.map} +1 -1
  62. package/dist/migration-cli.d.mts +50 -0
  63. package/dist/migration-cli.d.mts.map +1 -0
  64. package/dist/migration-cli.mjs +184 -0
  65. package/dist/migration-cli.mjs.map +1 -0
  66. package/dist/{migration-command-scaffold-BC73xQSo.mjs → migration-command-scaffold-B3B09et6.mjs} +6 -6
  67. package/dist/{migration-command-scaffold-BC73xQSo.mjs.map → migration-command-scaffold-B3B09et6.mjs.map} +1 -1
  68. package/dist/{migration-status-CXBbScH5.mjs → migration-status-DUMiH8_G.mjs} +12 -14
  69. package/dist/{migration-status-CXBbScH5.mjs.map → migration-status-DUMiH8_G.mjs.map} +1 -1
  70. package/dist/{migrations-DYRAjiVh.mjs → migrations-Bo5WtTla.mjs} +2 -2
  71. package/dist/{migrations-DYRAjiVh.mjs.map → migrations-Bo5WtTla.mjs.map} +1 -1
  72. package/dist/output-BpcQrnnq.mjs +103 -0
  73. package/dist/output-BpcQrnnq.mjs.map +1 -0
  74. package/dist/{progress-adapter-Bwouy73-.mjs → progress-adapter-DvQWB1nK.mjs} +1 -1
  75. package/dist/{progress-adapter-Bwouy73-.mjs.map → progress-adapter-DvQWB1nK.mjs.map} +1 -1
  76. package/dist/quick-reference-mongo.md +34 -13
  77. package/dist/quick-reference-postgres.md +11 -9
  78. package/dist/{result-handler-CGohaH1o.mjs → result-handler-Ba3zWQsI.mjs} +5 -78
  79. package/dist/result-handler-Ba3zWQsI.mjs.map +1 -0
  80. package/dist/{terminal-ui-BuPXVRFY.mjs → terminal-ui-C3ZLwQxK.mjs} +76 -2
  81. package/dist/terminal-ui-C3ZLwQxK.mjs.map +1 -0
  82. package/dist/{validate-contract-deps-DZqv9m7H.mjs → validate-contract-deps-B_Cs29TL.mjs} +1 -1
  83. package/dist/{validate-contract-deps-DZqv9m7H.mjs.map → validate-contract-deps-B_Cs29TL.mjs.map} +1 -1
  84. package/dist/{verify-Cm2UFuZA.mjs → verify-Bkycc-Tf.mjs} +2 -2
  85. package/dist/{verify-Cm2UFuZA.mjs.map → verify-Bkycc-Tf.mjs.map} +1 -1
  86. package/package.json +25 -16
  87. package/src/commands/init/detect-pnpm-catalog.ts +141 -0
  88. package/src/commands/init/errors.ts +254 -0
  89. package/src/commands/init/exit-codes.ts +62 -0
  90. package/src/commands/init/hygiene-gitattributes.ts +97 -0
  91. package/src/commands/init/hygiene-gitignore.ts +48 -0
  92. package/src/commands/init/hygiene-package-scripts.ts +91 -0
  93. package/src/commands/init/index.ts +112 -7
  94. package/src/commands/init/init.ts +766 -144
  95. package/src/commands/init/inputs.ts +421 -0
  96. package/src/commands/init/output.ts +147 -0
  97. package/src/commands/init/probe-db.ts +308 -0
  98. package/src/commands/init/reinit-cleanup.ts +83 -0
  99. package/src/commands/init/templates/agent-skill-mongo.md +63 -31
  100. package/src/commands/init/templates/agent-skill-postgres.md +1 -1
  101. package/src/commands/init/templates/agent-skill.ts +25 -3
  102. package/src/commands/init/templates/code-templates.ts +125 -32
  103. package/src/commands/init/templates/env.ts +80 -0
  104. package/src/commands/init/templates/quick-reference-mongo.md +34 -13
  105. package/src/commands/init/templates/quick-reference-postgres.md +11 -9
  106. package/src/commands/init/templates/quick-reference.ts +42 -3
  107. package/src/commands/init/templates/tsconfig.ts +167 -5
  108. package/src/commands/migration-apply.ts +3 -3
  109. package/src/commands/migration-ref.ts +32 -47
  110. package/src/commands/migration-status.ts +16 -21
  111. package/src/exports/init-output.ts +10 -0
  112. package/src/migration-cli.ts +254 -0
  113. package/src/utils/cli-errors.ts +1 -0
  114. package/src/utils/command-helpers.ts +3 -3
  115. package/dist/cli-errors-z37sV3eR.d.mts +0 -4
  116. package/dist/contract-emit-304WZtZJ.mjs +0 -4
  117. package/dist/contract-emit-mU1_B_m9.mjs.map +0 -1
  118. package/dist/init-DRquYpPa.mjs +0 -430
  119. package/dist/init-DRquYpPa.mjs.map +0 -1
  120. package/dist/result-handler-CGohaH1o.mjs.map +0 -1
  121. package/dist/terminal-ui-BuPXVRFY.mjs.map +0 -1
@@ -1,17 +1,22 @@
1
+ import type { RefEntry } from '@prisma-next/migration-tools/refs';
1
2
  import {
3
+ deleteRef,
4
+ readRef,
2
5
  readRefs,
3
- resolveRef,
4
6
  validateRefName,
5
7
  validateRefValue,
6
- writeRefs,
8
+ writeRef,
7
9
  } from '@prisma-next/migration-tools/refs';
8
10
  import { MigrationToolsError } from '@prisma-next/migration-tools/types';
9
11
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
10
12
  import { Command } from 'commander';
11
- import { resolve } from 'pathe';
12
13
  import { loadConfig } from '../config-loader';
13
14
  import { CliStructuredError, errorRuntime, errorUnexpected } from '../utils/cli-errors';
14
- import { addGlobalOptions, setCommandDescriptions } from '../utils/command-helpers';
15
+ import {
16
+ addGlobalOptions,
17
+ resolveMigrationPaths,
18
+ setCommandDescriptions,
19
+ } from '../utils/command-helpers';
15
20
  import { formatCommandHelp } from '../utils/formatters/help';
16
21
  import { parseGlobalFlags } from '../utils/global-flags';
17
22
  import { handleResult } from '../utils/result-handler';
@@ -21,12 +26,14 @@ interface RefSetResult {
21
26
  readonly ok: true;
22
27
  readonly ref: string;
23
28
  readonly hash: string;
29
+ readonly invariants: readonly string[];
24
30
  }
25
31
 
26
32
  interface RefGetResult {
27
33
  readonly ok: true;
28
34
  readonly ref: string;
29
35
  readonly hash: string;
36
+ readonly invariants: readonly string[];
30
37
  }
31
38
 
32
39
  interface RefDeleteResult {
@@ -37,12 +44,7 @@ interface RefDeleteResult {
37
44
 
38
45
  interface RefListResult {
39
46
  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');
47
+ readonly refs: Record<string, RefEntry>;
46
48
  }
47
49
 
48
50
  function mapError(error: unknown): CliStructuredError {
@@ -70,13 +72,6 @@ function cliErrorInvalidRefValue(hash: string): CliStructuredError {
70
72
  });
71
73
  }
72
74
 
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
75
  async function executeRefSetCommand(
81
76
  name: string,
82
77
  hash: string,
@@ -91,11 +86,10 @@ async function executeRefSetCommand(
91
86
 
92
87
  try {
93
88
  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 });
89
+ const { refsDir } = resolveMigrationPaths(options.config, config);
90
+ const entry: RefEntry = { hash, invariants: [] };
91
+ await writeRef(refsDir, name, entry);
92
+ return ok({ ok: true as const, ref: name, hash, invariants: [] });
99
93
  } catch (error) {
100
94
  if (error instanceof CliStructuredError) return notOk(error);
101
95
  return notOk(mapError(error));
@@ -108,10 +102,9 @@ async function executeRefGetCommand(
108
102
  ): Promise<Result<RefGetResult, CliStructuredError>> {
109
103
  try {
110
104
  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 });
105
+ const { refsDir } = resolveMigrationPaths(options.config, config);
106
+ const entry = await readRef(refsDir, name);
107
+ return ok({ ok: true as const, ref: name, hash: entry.hash, invariants: entry.invariants });
115
108
  } catch (error) {
116
109
  if (error instanceof CliStructuredError) return notOk(error);
117
110
  return notOk(mapError(error));
@@ -124,13 +117,8 @@ async function executeRefDeleteCommand(
124
117
  ): Promise<Result<RefDeleteResult, CliStructuredError>> {
125
118
  try {
126
119
  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);
120
+ const { refsDir } = resolveMigrationPaths(options.config, config);
121
+ await deleteRef(refsDir, name);
134
122
  return ok({ ok: true as const, ref: name, deleted: true as const });
135
123
  } catch (error) {
136
124
  if (error instanceof CliStructuredError) return notOk(error);
@@ -143,8 +131,8 @@ async function executeRefListCommand(options: {
143
131
  }): Promise<Result<RefListResult, CliStructuredError>> {
144
132
  try {
145
133
  const config = await loadConfig(options.config);
146
- const refsPath = resolveRefsPath(options.config, config);
147
- const refs = await readRefs(refsPath);
134
+ const { refsDir } = resolveMigrationPaths(options.config, config);
135
+ const refs = await readRefs(refsDir);
148
136
  return ok({ ok: true as const, refs });
149
137
  } catch (error) {
150
138
  if (error instanceof CliStructuredError) return notOk(error);
@@ -157,7 +145,7 @@ function createRefSetCommand(): Command {
157
145
  setCommandDescriptions(
158
146
  command,
159
147
  'Set a ref to a contract hash',
160
- 'Sets a named ref to point to a contract hash in migrations/refs.json.',
148
+ 'Sets a named ref to point to a contract hash in migrations/refs/.',
161
149
  );
162
150
  addGlobalOptions(command)
163
151
  .argument('<name>', 'Ref name (e.g., staging, production)')
@@ -190,7 +178,7 @@ function createRefGetCommand(): Command {
190
178
  setCommandDescriptions(
191
179
  command,
192
180
  'Get the hash for a ref',
193
- 'Reads a named ref from migrations/refs.json and prints its contract hash.',
181
+ 'Reads a named ref from migrations/refs/ and prints its contract hash.',
194
182
  );
195
183
  addGlobalOptions(command)
196
184
  .argument('<name>', 'Ref name to look up')
@@ -218,7 +206,7 @@ function createRefGetCommand(): Command {
218
206
 
219
207
  function createRefDeleteCommand(): Command {
220
208
  const command = new Command('delete');
221
- setCommandDescriptions(command, 'Delete a ref', 'Removes a named ref from migrations/refs.json.');
209
+ setCommandDescriptions(command, 'Delete a ref', 'Removes a named ref from migrations/refs/.');
222
210
  addGlobalOptions(command)
223
211
  .argument('<name>', 'Ref name to delete')
224
212
  .option('--config <path>', 'Path to prisma-next.config.ts')
@@ -245,11 +233,7 @@ function createRefDeleteCommand(): Command {
245
233
 
246
234
  function createRefListCommand(): Command {
247
235
  const command = new Command('list');
248
- setCommandDescriptions(
249
- command,
250
- 'List all refs',
251
- 'Lists all named refs from migrations/refs.json.',
252
- );
236
+ setCommandDescriptions(command, 'List all refs', 'Lists all named refs from migrations/refs/.');
253
237
  addGlobalOptions(command)
254
238
  .option('--config <path>', 'Path to prisma-next.config.ts')
255
239
  .action(async (options: { config?: string; json?: string | boolean; quiet?: boolean }) => {
@@ -264,8 +248,10 @@ function createRefListCommand(): Command {
264
248
  if (entries.length === 0) {
265
249
  ui.output('No refs defined');
266
250
  } else {
267
- for (const [refName, hash] of entries) {
268
- ui.output(`${refName} ${hash}`);
251
+ for (const [refName, entry] of entries) {
252
+ const invariantsSuffix =
253
+ entry.invariants.length > 0 ? ` [invariants: ${entry.invariants.join(', ')}]` : '';
254
+ ui.output(`${refName} → ${entry.hash}${invariantsSuffix}`);
269
255
  }
270
256
  }
271
257
  }
@@ -282,7 +268,6 @@ export {
282
268
  executeRefListCommand,
283
269
  cliErrorInvalidRefName,
284
270
  cliErrorInvalidRefValue,
285
- errorRefNotFound,
286
271
  };
287
272
 
288
273
  export function createMigrationRefCommand(): Command {
@@ -290,7 +275,7 @@ export function createMigrationRefCommand(): Command {
290
275
  setCommandDescriptions(
291
276
  command,
292
277
  'Manage migration refs',
293
- 'Manage named refs in migrations/refs.json. Refs map logical environment\n' +
278
+ 'Manage named refs in migrations/refs/. Refs map logical environment\n' +
294
279
  'names (e.g., staging, production) to contract hashes.',
295
280
  );
296
281
  addGlobalOptions(command).configureHelp({
@@ -345,7 +345,7 @@ async function executeMigrationStatusCommand(
345
345
  ui: TerminalUI,
346
346
  ): Promise<Result<MigrationStatusResult, CliStructuredError>> {
347
347
  const config = await loadConfig(options.config);
348
- const { configPath, migrationsDir, migrationsRelative, refsPath } = resolveMigrationPaths(
348
+ const { configPath, migrationsDir, migrationsRelative, refsDir } = resolveMigrationPaths(
349
349
  options.config,
350
350
  config,
351
351
  );
@@ -357,7 +357,7 @@ async function executeMigrationStatusCommand(
357
357
  let activeRefHash: string | undefined;
358
358
  let allRefs: Refs = {};
359
359
  try {
360
- allRefs = await readRefs(refsPath);
360
+ allRefs = await readRefs(refsDir);
361
361
  } catch (error) {
362
362
  if (MigrationToolsError.is(error)) {
363
363
  return notOk(
@@ -373,30 +373,25 @@ async function executeMigrationStatusCommand(
373
373
 
374
374
  if (options.ref) {
375
375
  activeRefName = options.ref;
376
- const refHash = allRefs[activeRefName];
377
- if (refHash) {
378
- activeRefHash = refHash;
379
- } else {
380
- try {
381
- activeRefHash = resolveRef(allRefs, activeRefName);
382
- } catch (error) {
383
- if (MigrationToolsError.is(error)) {
384
- return notOk(
385
- errorRuntime(error.message, {
386
- why: error.why,
387
- fix: error.fix,
388
- meta: { code: error.code },
389
- }),
390
- );
391
- }
392
- throw error;
376
+ try {
377
+ activeRefHash = resolveRef(allRefs, activeRefName).hash;
378
+ } catch (error) {
379
+ if (MigrationToolsError.is(error)) {
380
+ return notOk(
381
+ errorRuntime(error.message, {
382
+ why: error.why,
383
+ fix: error.fix,
384
+ meta: { code: error.code },
385
+ }),
386
+ );
393
387
  }
388
+ throw error;
394
389
  }
395
390
  }
396
391
 
397
- const statusRefs: StatusRef[] = Object.entries(allRefs).map(([name, hash]) => ({
392
+ const statusRefs: StatusRef[] = Object.entries(allRefs).map(([name, entry]) => ({
398
393
  name,
399
- hash,
394
+ hash: entry.hash,
400
395
  active: name === activeRefName,
401
396
  }));
402
397
 
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Public re-export of the `init --json` success-document schema (FR1.5).
3
+ *
4
+ * Imported as `@prisma-next/cli/init-output`. The shared error envelope is
5
+ * exported separately from `@prisma-next/errors`; consumers should branch
6
+ * on the `ok` discriminator (success documents carry `ok: true`, error
7
+ * envelopes carry `ok: false`) per the
8
+ * [Style Guide § JSON Semantics](../../../../../../../docs/CLI%20Style%20Guide.md#json-semantics).
9
+ */
10
+ export { type InitOutput, InitOutputSchema } from '../commands/init/output';
@@ -0,0 +1,254 @@
1
+ /**
2
+ * The migration-file CLI interface: the actor invoked when the author runs
3
+ * `node migration.ts` directly.
4
+ *
5
+ * Naming: this is *not* a "migration runner" in the apply-time sense. The
6
+ * apply-time runner is the thing `prisma-next migration apply` uses to
7
+ * execute migration JSON ops against a database. `MigrationCLI` is the
8
+ * tiny CLI surface owned by an authored `migration.ts` file: parse the
9
+ * file's argv, load the project's `prisma-next.config.ts`, assemble a
10
+ * `ControlStack`, instantiate the migration class, and serialize.
11
+ *
12
+ * The user authors a migration class, then calls
13
+ * `MigrationCLI.run(import.meta.url, MigrationClass)` at module scope
14
+ * after the class definition. When the file is invoked as a node
15
+ * entrypoint (`node migration.ts`), the CLI:
16
+ *
17
+ * 1. Detects whether the file is the direct entrypoint (no-op when imported).
18
+ * 2. Parses CLI args (`--help`, `--dry-run`, `--config <path>`).
19
+ * 3. Loads the project's `prisma-next.config.ts` via the same `loadConfig`
20
+ * the CLI commands use, walking up from the migration file's directory.
21
+ * 4. Probe-instantiates the migration class without a stack so it can read
22
+ * `targetId` and verify it matches `config.target.targetId`
23
+ * (`PN-MIG-2006` on mismatch) before any stack-driven adapter
24
+ * construction runs.
25
+ * 5. Assembles a `ControlStack` from the loaded config descriptors and
26
+ * constructs the migration with that stack.
27
+ * 6. Reads any previously-scaffolded `migration.json`, then calls
28
+ * `buildMigrationArtifacts` from `@prisma-next/migration-tools` to
29
+ * produce in-memory `ops.json` + `migration.json` content. Persists
30
+ * the result to disk (or prints in dry-run mode).
31
+ *
32
+ * File I/O lives here, in `@prisma-next/cli`: this is the only place
33
+ * that legitimately combines config loading, stack assembly, and
34
+ * on-disk persistence. `@prisma-next/migration-tools` owns the pure
35
+ * conversion from a `Migration` instance to artifact strings; `Migration`
36
+ * stays a pure abstract class.
37
+ */
38
+
39
+ import { readFileSync, writeFileSync } from 'node:fs';
40
+ import { fileURLToPath } from 'node:url';
41
+ import { CliStructuredError, errorMigrationCliInvalidConfigArg } from '@prisma-next/errors/control';
42
+ import { errorMigrationTargetMismatch } from '@prisma-next/errors/migration';
43
+ import { createControlStack } from '@prisma-next/framework-components/control';
44
+ import {
45
+ buildMigrationArtifacts,
46
+ isDirectEntrypoint,
47
+ type Migration,
48
+ printMigrationHelp,
49
+ } from '@prisma-next/migration-tools/migration';
50
+ import type { MigrationManifest } from '@prisma-next/migration-tools/types';
51
+ import { dirname, join } from 'pathe';
52
+ import { loadConfig } from './config-loader';
53
+
54
+ /**
55
+ * Constructor shape accepted by `MigrationCLI.run`. `Migration` subclasses
56
+ * accept an optional `ControlStack` in their constructor (each subclass
57
+ * narrows the stack to its own family/target generics); the CLI always
58
+ * passes one assembled from the loaded config. We use a rest-args `any[]`
59
+ * constructor signature so that subclass constructors with narrower
60
+ * parameter types remain assignable - constructor type compatibility in
61
+ * TS is contravariant in the parameter, and a wider `unknown` parameter
62
+ * on the alias side would reject any narrower subclass signature.
63
+ *
64
+ * The CLI only ever passes one argument (`new MigrationClass(stack)`);
65
+ * the rest-arity is purely a type-compatibility concession for subclass
66
+ * constructors that declare narrower parameter types, not an extension
67
+ * point for additional construction arguments.
68
+ */
69
+ // biome-ignore lint/suspicious/noExplicitAny: see JSDoc - rest args with any are the idiomatic TS pattern for accepting arbitrary subclass constructor signatures
70
+ export type MigrationConstructor = new (...args: any[]) => Migration;
71
+
72
+ interface ParsedArgs {
73
+ readonly help: boolean;
74
+ readonly dryRun: boolean;
75
+ readonly configPath: string | undefined;
76
+ }
77
+
78
+ /**
79
+ * Parse the subset of `process.argv` that `MigrationCLI.run` cares about.
80
+ * Recognised flags: `--help`, `--dry-run`, `--config <path>` /
81
+ * `--config=<path>`. Unknown flags are ignored to keep the surface
82
+ * forgiving for ad-hoc tooling that wraps a migration file.
83
+ *
84
+ * Throws `errorMigrationCliInvalidConfigArg` (`PN-CLI-4012`) when
85
+ * `--config` is missing its path argument or is followed by another flag
86
+ * (e.g. `--config --dry-run`); silently consuming the next flag would
87
+ * either drop dry-run handling or serialize against the wrong project.
88
+ *
89
+ * NOTE: this hand-rolled parser is a known wart, tracked separately by
90
+ * TML-2318 ("Migration CLI: replace handrolled arg parser with shared
91
+ * CLI library"). Until that lands the surface is intentionally tiny.
92
+ */
93
+ function parseArgs(argv: readonly string[]): ParsedArgs {
94
+ let help = false;
95
+ let dryRun = false;
96
+ let configPath: string | undefined;
97
+
98
+ for (let i = 0; i < argv.length; i++) {
99
+ const arg = argv[i]!;
100
+ if (arg === '--help' || arg === '-h') {
101
+ help = true;
102
+ } else if (arg === '--dry-run') {
103
+ dryRun = true;
104
+ } else if (arg === '--config') {
105
+ const next = argv[i + 1];
106
+ if (next === undefined) {
107
+ throw errorMigrationCliInvalidConfigArg();
108
+ }
109
+ if (next.startsWith('-')) {
110
+ throw errorMigrationCliInvalidConfigArg({ nextToken: next });
111
+ }
112
+ configPath = next;
113
+ i++;
114
+ } else if (arg.startsWith('--config=')) {
115
+ configPath = arg.slice('--config='.length);
116
+ }
117
+ }
118
+
119
+ return { help, dryRun, configPath };
120
+ }
121
+
122
+ /**
123
+ * The CLI surface invoked by an authored `migration.ts` file. Exposed as
124
+ * a class with a static `run` method (rather than a free function) to
125
+ * give the concept a stable identity in the ubiquitous language: this is
126
+ * the "migration-file CLI", distinct from the apply-time runner that
127
+ * executes migration JSON ops.
128
+ *
129
+ * Currently a single static method. Future surface (e.g. a programmatic
130
+ * `MigrationCLI.serializeOnly(...)` for tests, or extra subcommands) can
131
+ * land here without changing the import shape used by every authored
132
+ * migration.
133
+ */
134
+ // biome-ignore lint/complexity/noStaticOnlyClass: see JSDoc - intentional class facade for the migration-file CLI surface; future methods will share state derived from argv/config.
135
+ export class MigrationCLI {
136
+ /**
137
+ * Orchestrates a class-flow `migration.ts` script run. Awaitable:
138
+ * callers may `await MigrationCLI.run(...)` to surface async failures
139
+ * from config loading, but the typical usage pattern (top-level call
140
+ * after the class definition) does not require awaiting because
141
+ * node's module evaluation keeps the promise alive until completion.
142
+ *
143
+ * Any throwable inside this function must surface through the internal
144
+ * try/catch — script callers do not await, so an unhandled rejection
145
+ * would silently exit 0. Treat the try/catch as load-bearing for the
146
+ * no-await usage pattern.
147
+ */
148
+ static async run(importMetaUrl: string, MigrationClass: MigrationConstructor): Promise<void> {
149
+ if (!importMetaUrl) return;
150
+ if (!isDirectEntrypoint(importMetaUrl)) return;
151
+
152
+ try {
153
+ const args = parseArgs(process.argv.slice(2));
154
+
155
+ if (args.help) {
156
+ printMigrationHelp();
157
+ return;
158
+ }
159
+
160
+ const migrationFile = fileURLToPath(importMetaUrl);
161
+ const migrationDir = dirname(migrationFile);
162
+
163
+ const config = await loadConfig(args.configPath);
164
+
165
+ // Probe-instantiate without a stack so we can read `targetId` before
166
+ // any target-specific constructor side effects (e.g.
167
+ // `PostgresMigration`'s `stack.adapter.create(stack)`) run. Concrete
168
+ // subclasses are required to accept the no-arg form; the abstract
169
+ // `Migration` constructor declares `stack?` and target subclasses
170
+ // (Postgres, Mongo) propagate that optionality. This makes the
171
+ // target-mismatch guard fail fast with `PN-MIG-2006` before any
172
+ // stack-driven adapter construction begins, even if the wrong-target
173
+ // adapter's `create` would otherwise succeed and silently misshapen
174
+ // the stored adapter cast.
175
+ const probe = new MigrationClass();
176
+
177
+ if (probe.targetId !== config.target.targetId) {
178
+ throw errorMigrationTargetMismatch({
179
+ migrationTargetId: probe.targetId,
180
+ configTargetId: config.target.targetId,
181
+ });
182
+ }
183
+
184
+ const stack = createControlStack(config);
185
+ const instance = new MigrationClass(stack);
186
+
187
+ serializeMigrationToDisk(instance, migrationDir, args.dryRun);
188
+ } catch (err) {
189
+ if (CliStructuredError.is(err)) {
190
+ process.stderr.write(`${err.message}: ${err.why}\n`);
191
+ } else {
192
+ process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
193
+ }
194
+ process.exitCode = 1;
195
+ }
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Read a previously-scaffolded `migration.json` from disk, returning
201
+ * `null` when the file is missing or unparseable. The CLI feeds this into
202
+ * `buildMigrationArtifacts` so the pure builder can preserve fields owned
203
+ * by `migration plan` (contract bookends, hints, labels, `createdAt`)
204
+ * across re-emits.
205
+ */
206
+ function readExistingManifest(manifestPath: string): Partial<MigrationManifest> | null {
207
+ let raw: string;
208
+ try {
209
+ raw = readFileSync(manifestPath, 'utf-8');
210
+ } catch {
211
+ return null;
212
+ }
213
+ try {
214
+ return JSON.parse(raw) as Partial<MigrationManifest>;
215
+ } catch {
216
+ return null;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Persist a migration instance's artifacts to `migrationDir`. In
222
+ * `dryRun` mode the artifacts are printed to stdout (with the same
223
+ * `--- migration.json --- / --- ops.json ---` framing the legacy
224
+ * `serializeMigration` helper used) and no files are written. Otherwise
225
+ * `ops.json` and `migration.json` are written next to `migration.ts` and
226
+ * a confirmation line is printed.
227
+ *
228
+ * File I/O lives in the CLI rather than `@prisma-next/migration-tools`
229
+ * so the migration-tools package stays focused on the pure
230
+ * `Migration` → in-memory artifact conversion. The CLI is the only
231
+ * legitimate site for combining config loading, stack assembly, and
232
+ * filesystem persistence.
233
+ */
234
+ function serializeMigrationToDisk(
235
+ instance: Migration,
236
+ migrationDir: string,
237
+ dryRun: boolean,
238
+ ): void {
239
+ const manifestPath = join(migrationDir, 'migration.json');
240
+ const existing = readExistingManifest(manifestPath);
241
+ const { opsJson, manifestJson } = buildMigrationArtifacts(instance, existing);
242
+
243
+ if (dryRun) {
244
+ process.stdout.write(`--- migration.json ---\n${manifestJson}\n`);
245
+ process.stdout.write('--- ops.json ---\n');
246
+ process.stdout.write(`${opsJson}\n`);
247
+ return;
248
+ }
249
+
250
+ writeFileSync(join(migrationDir, 'ops.json'), opsJson);
251
+ writeFileSync(manifestPath, manifestJson);
252
+
253
+ process.stdout.write(`Wrote ops.json + migration.json to ${migrationDir}\n`);
254
+ }
@@ -14,6 +14,7 @@ export {
14
14
  errorDriverRequired,
15
15
  errorFamilyReadMarkerSqlRequired,
16
16
  errorFileNotFound,
17
+ errorMigrationCliInvalidConfigArg,
17
18
  errorMigrationPlanningFailed,
18
19
  errorQueryRunnerFactoryRequired,
19
20
  errorTargetMigrationNotSupported,
@@ -85,7 +85,7 @@ export function resolveMigrationPaths(
85
85
  configPath: string;
86
86
  migrationsDir: string;
87
87
  migrationsRelative: string;
88
- refsPath: string;
88
+ refsDir: string;
89
89
  } {
90
90
  const configPath = configOption
91
91
  ? relative(process.cwd(), resolve(configOption))
@@ -95,8 +95,8 @@ export function resolveMigrationPaths(
95
95
  config.migrations?.dir ?? 'migrations',
96
96
  );
97
97
  const migrationsRelative = relative(process.cwd(), migrationsDir);
98
- const refsPath = resolve(migrationsDir, 'refs.json');
99
- return { configPath, migrationsDir, migrationsRelative, refsPath };
98
+ const refsDir = resolve(migrationsDir, 'refs');
99
+ return { configPath, migrationsDir, migrationsRelative, refsDir };
100
100
  }
101
101
 
102
102
  /**
@@ -1,4 +0,0 @@
1
- import { CliStructuredError } from "@prisma-next/errors/control";
2
- import "@prisma-next/errors/execution";
3
- import "@prisma-next/errors/migration";
4
- export { CliStructuredError as t };
@@ -1,4 +0,0 @@
1
- import "./config-loader-_xQZsw0i.mjs";
2
- import { t as executeContractEmit } from "./contract-emit-DgeWdonT.mjs";
3
-
4
- export { executeContractEmit };
@@ -1 +0,0 @@
1
- {"version":3,"file":"contract-emit-mU1_B_m9.mjs","names":["lines: string[]","exhaustive: never","config: Awaited<ReturnType<typeof loadConfig>>","errorUnexpected","outputPaths: ReturnType<typeof getEmittedArtifactPaths>"],"sources":["../src/utils/formatters/emit.ts","../src/commands/contract-emit.ts"],"sourcesContent":["import { ifDefined } from '@prisma-next/utils/defined';\nimport { relative } from 'pathe';\n\nimport type { GlobalFlags } from '../global-flags';\nimport { isVerbose } from './helpers';\n\n// EmitContractResult type for CLI output formatting (includes file paths)\nexport interface EmitContractResult {\n readonly storageHash: string;\n readonly executionHash?: string;\n readonly profileHash: string;\n readonly outDir: string;\n readonly files: {\n readonly json: string;\n readonly dts: string;\n };\n readonly timings: {\n readonly total: number;\n };\n}\n\n/**\n * Formats human-readable output for contract emit.\n */\nexport function formatEmitOutput(result: EmitContractResult, flags: GlobalFlags): string {\n if (flags.quiet) {\n return '';\n }\n\n const lines: string[] = [];\n\n // Convert absolute paths to relative paths from cwd\n const jsonPath = relative(process.cwd(), result.files.json);\n const dtsPath = relative(process.cwd(), result.files.dts);\n\n lines.push(`✔ Emitted contract.json → ${jsonPath}`);\n lines.push(`✔ Emitted contract.d.ts → ${dtsPath}`);\n lines.push(` storageHash: ${result.storageHash}`);\n if (result.executionHash) {\n lines.push(` executionHash: ${result.executionHash}`);\n }\n if (result.profileHash) {\n lines.push(` profileHash: ${result.profileHash}`);\n }\n if (isVerbose(flags, 1)) {\n lines.push(` Total time: ${result.timings.total}ms`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Formats JSON output for contract emit.\n */\nexport function formatEmitJson(result: EmitContractResult): string {\n const output = {\n ok: true,\n storageHash: result.storageHash,\n ...ifDefined('executionHash', result.executionHash),\n ...(result.profileHash ? { profileHash: result.profileHash } : {}),\n outDir: result.outDir,\n files: result.files,\n timings: result.timings,\n };\n\n return JSON.stringify(output, null, 2);\n}\n","import { mkdirSync, writeFileSync } from 'node:fs';\nimport { getEmittedArtifactPaths } from '@prisma-next/emitter';\nimport { errorContractConfigMissing } from '@prisma-next/errors/control';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { Command } from 'commander';\nimport { dirname, relative, resolve } from 'pathe';\nimport { loadConfig } from '../config-loader';\nimport { createControlClient } from '../control-api/client';\nimport type { EmitFailure } from '../control-api/types';\nimport {\n CliStructuredError,\n errorContractValidationFailed,\n errorRuntime,\n errorUnexpected,\n} from '../utils/cli-errors';\nimport {\n addGlobalOptions,\n setCommandDescriptions,\n setCommandExamples,\n} from '../utils/command-helpers';\nimport {\n type EmitContractResult,\n formatEmitJson,\n formatEmitOutput,\n} from '../utils/formatters/emit';\nimport { formatStyledHeader, formatSuccessMessage } from '../utils/formatters/styled';\nimport type { CommonCommandOptions } from '../utils/global-flags';\nimport { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';\nimport { createProgressAdapter } from '../utils/progress-adapter';\nimport { handleResult } from '../utils/result-handler';\nimport { TerminalUI } from '../utils/terminal-ui';\n\ninterface ContractEmitOptions extends CommonCommandOptions {\n readonly config?: string;\n}\n\nfunction mapDiagnosticsToIssues(\n failure: EmitFailure,\n): ReadonlyArray<{ kind: string; message: string }> {\n const diagnostics = failure.diagnostics?.diagnostics ?? [];\n return diagnostics.map((diagnostic) => {\n const location =\n diagnostic.sourceId && diagnostic.span\n ? ` (${diagnostic.sourceId}:${diagnostic.span.start.line}:${diagnostic.span.start.column})`\n : diagnostic.sourceId\n ? ` (${diagnostic.sourceId})`\n : '';\n return {\n kind: diagnostic.code,\n message: `${diagnostic.message}${location}`,\n };\n });\n}\n\n/**\n * Maps an EmitFailure to a CliStructuredError for consistent error handling.\n */\nfunction mapEmitFailure(\n failure: EmitFailure,\n context?: { readonly configPath?: string },\n): CliStructuredError {\n if (failure.code === 'CONTRACT_SOURCE_INVALID') {\n const issues = mapDiagnosticsToIssues(failure);\n return errorRuntime(failure.summary, {\n why: failure.why ?? 'Contract source provider failed',\n fix: 'Check your contract source provider in prisma-next.config.ts and ensure it returns Result<Contract, Diagnostics>',\n ...(issues.length > 0 ? { meta: { issues } } : {}),\n });\n }\n\n if (failure.code === 'CONTRACT_VALIDATION_FAILED') {\n return errorContractValidationFailed(\n failure.why ?? 'Contract validation failed while emitting',\n context?.configPath ? { where: { path: context.configPath } } : undefined,\n );\n }\n\n if (failure.code === 'EMIT_FAILED') {\n return errorRuntime(failure.summary, {\n why: failure.why ?? 'Failed to emit contract',\n fix: 'Check your contract configuration and ensure the source is valid',\n });\n }\n\n // Exhaustive check - TypeScript will error if a new code is added but not handled\n const exhaustive: never = failure.code;\n throw new Error(`Unhandled EmitFailure code: ${exhaustive}`);\n}\n\n/**\n * Executes the contract emit command and returns a structured Result.\n */\nasync function executeContractEmitCommand(\n options: ContractEmitOptions,\n flags: GlobalFlags,\n ui: TerminalUI,\n startTime: number,\n): Promise<Result<EmitContractResult, CliStructuredError>> {\n // Load config\n let config: Awaited<ReturnType<typeof loadConfig>>;\n try {\n config = await loadConfig(options.config);\n } catch (error) {\n // Convert thrown CliStructuredError to Result\n if (error instanceof CliStructuredError) {\n return notOk(error);\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: 'Failed to load config',\n }),\n );\n }\n\n const configPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n\n // Resolve contract from config\n if (!config.contract) {\n return notOk(\n errorContractConfigMissing({\n why: 'Config.contract is required for emit. Define it in your config: contract: { source: ..., output: ... }',\n }),\n );\n }\n\n // Contract config is already normalized by defineConfig() with defaults applied\n const contractConfig = config.contract;\n\n // Resolve artifact paths from config (already normalized by defineConfig() with defaults)\n if (!contractConfig.output) {\n return notOk(\n errorContractConfigMissing({\n why: 'Contract config must have output path. This should not happen if defineConfig() was used.',\n }),\n );\n }\n let outputPaths: ReturnType<typeof getEmittedArtifactPaths>;\n try {\n outputPaths = getEmittedArtifactPaths(contractConfig.output);\n } catch (error) {\n return notOk(\n errorContractConfigMissing({\n why: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n const { jsonPath: outputJsonPath, dtsPath: outputDtsPath } = outputPaths;\n\n // Output header to stderr (decoration)\n if (!flags.json && !flags.quiet) {\n const contractPath = relative(process.cwd(), outputJsonPath);\n const typesPath = relative(process.cwd(), outputDtsPath);\n const header = formatStyledHeader({\n command: 'contract emit',\n description: 'Emit your contract artifacts',\n url: 'https://pris.ly/contract-emit',\n details: [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n { label: 'types', value: typesPath },\n ],\n flags,\n });\n ui.stderr(header);\n }\n\n // Create control client (no driver needed for emit)\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n extensionPacks: config.extensionPacks ?? [],\n });\n\n // Create progress adapter\n const onProgress = createProgressAdapter({ ui, flags });\n\n try {\n // Call emit with progress callback\n const result = await client.emit({\n contractConfig: {\n source: contractConfig.source,\n output: outputJsonPath,\n },\n onProgress,\n });\n\n // Handle failures by mapping to CLI structured error\n if (!result.ok) {\n return notOk(mapEmitFailure(result.failure, { configPath }));\n }\n\n // Create directories if needed\n mkdirSync(dirname(outputJsonPath), { recursive: true });\n mkdirSync(dirname(outputDtsPath), { recursive: true });\n\n // Write the results to files\n writeFileSync(outputJsonPath, result.value.contractJson, 'utf-8');\n writeFileSync(outputDtsPath, result.value.contractDts, 'utf-8');\n\n // Validate that contract.d.ts type imports are resolvable\n const { validateContractDeps } = await import('../utils/validate-contract-deps');\n const depsValidation = validateContractDeps(result.value.contractDts, dirname(outputDtsPath));\n if (depsValidation.warning) {\n ui.warn(depsValidation.warning);\n }\n\n // Convert success result to CLI output format\n const emitResult: EmitContractResult = {\n storageHash: result.value.storageHash,\n ...(result.value.executionHash ? { executionHash: result.value.executionHash } : {}),\n profileHash: result.value.profileHash,\n outDir: dirname(outputJsonPath),\n files: {\n json: outputJsonPath,\n dts: outputDtsPath,\n },\n timings: { total: Date.now() - startTime },\n };\n\n return ok(emitResult);\n } catch (error) {\n // Use static type guard to work across module boundaries\n if (CliStructuredError.is(error)) {\n return notOk(error);\n }\n\n // Wrap unexpected errors\n return notOk(\n errorUnexpected('Unexpected error during contract emit', {\n why: error instanceof Error ? error.message : String(error),\n }),\n );\n } finally {\n await client.close();\n }\n}\n\nexport function createContractEmitCommand(): Command {\n const command = new Command('emit');\n setCommandDescriptions(\n command,\n 'Emit your contract artifacts',\n 'Reads your contract source (TypeScript or Prisma schema) and emits contract.json and\\n' +\n 'contract.d.ts. The contract.json contains the canonical contract structure, and\\n' +\n 'contract.d.ts provides TypeScript types for type-safe query building.',\n );\n setCommandExamples(command, [\n 'prisma-next contract emit',\n 'prisma-next contract emit --config ./custom-config.ts',\n ]);\n addGlobalOptions(command)\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .action(async (options: ContractEmitOptions) => {\n const flags = parseGlobalFlags(options);\n const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });\n const startTime = Date.now();\n\n const result = await executeContractEmitCommand(options, flags, ui, startTime);\n\n // Handle result - formats output and returns exit code\n const exitCode = handleResult(result, flags, ui, (emitResult) => {\n if (flags.json) {\n ui.output(formatEmitJson(emitResult));\n } else {\n const output = formatEmitOutput(emitResult, flags);\n if (output) {\n ui.log(output);\n }\n if (!flags.quiet) {\n ui.success(formatSuccessMessage(flags));\n }\n }\n });\n process.exit(exitCode);\n });\n\n return command;\n}\n"],"mappings":"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"}