@prisma-next/cli 0.4.0-dev.9 → 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 (157) hide show
  1. package/README.md +26 -18
  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-Cd79vmTH.mjs +5 -0
  6. package/dist/cli.mjs +127 -25
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/{client-CJxHfhze.mjs → client-CrsnY58k.mjs} +9 -8
  9. package/dist/{client-CJxHfhze.mjs.map → client-CrsnY58k.mjs.map} +1 -1
  10. package/dist/commands/contract-emit.d.mts.map +1 -1
  11. package/dist/commands/contract-emit.mjs +7 -7
  12. package/dist/commands/contract-infer.mjs +8 -8
  13. package/dist/commands/db-init.mjs +8 -8
  14. package/dist/commands/db-schema.mjs +8 -8
  15. package/dist/commands/db-sign.mjs +8 -8
  16. package/dist/commands/db-update.mjs +8 -8
  17. package/dist/commands/db-verify.mjs +9 -9
  18. package/dist/commands/migration-apply.d.mts +1 -1
  19. package/dist/commands/migration-apply.d.mts.map +1 -1
  20. package/dist/commands/migration-apply.mjs +37 -28
  21. package/dist/commands/migration-apply.mjs.map +1 -1
  22. package/dist/commands/migration-new.d.mts.map +1 -1
  23. package/dist/commands/migration-new.mjs +48 -23
  24. package/dist/commands/migration-new.mjs.map +1 -1
  25. package/dist/commands/migration-plan.d.mts +6 -1
  26. package/dist/commands/migration-plan.d.mts.map +1 -1
  27. package/dist/commands/migration-plan.mjs +94 -71
  28. package/dist/commands/migration-plan.mjs.map +1 -1
  29. package/dist/commands/migration-ref.d.mts +6 -4
  30. package/dist/commands/migration-ref.d.mts.map +1 -1
  31. package/dist/commands/migration-ref.mjs +29 -34
  32. package/dist/commands/migration-ref.mjs.map +1 -1
  33. package/dist/commands/migration-show.d.mts +2 -2
  34. package/dist/commands/migration-show.d.mts.map +1 -1
  35. package/dist/commands/migration-show.mjs +11 -16
  36. package/dist/commands/migration-show.mjs.map +1 -1
  37. package/dist/commands/migration-status.d.mts +4 -5
  38. package/dist/commands/migration-status.d.mts.map +1 -1
  39. package/dist/commands/migration-status.mjs +7 -7
  40. package/dist/config-loader-C25b63rJ.mjs +90 -0
  41. package/dist/config-loader-C25b63rJ.mjs.map +1 -0
  42. package/dist/config-loader.d.mts.map +1 -1
  43. package/dist/config-loader.mjs +1 -1
  44. package/dist/contract-emit-DxgyXrqV.mjs +6 -0
  45. package/dist/{contract-emit-gpJNLGs7.mjs → contract-emit-NJ01hiiv.mjs} +20 -16
  46. package/dist/contract-emit-NJ01hiiv.mjs.map +1 -0
  47. package/dist/{contract-emit-CKig_Lra.mjs → contract-emit-V5SSitUT.mjs} +25 -21
  48. package/dist/contract-emit-V5SSitUT.mjs.map +1 -0
  49. package/dist/{contract-enrichment-CGW6mm-E.mjs → contract-enrichment-CAOELa-H.mjs} +1 -1
  50. package/dist/{contract-enrichment-CGW6mm-E.mjs.map → contract-enrichment-CAOELa-H.mjs.map} +1 -1
  51. package/dist/{contract-infer-BDJgg7Xb.mjs → contract-infer-D9cC3rJm.mjs} +4 -4
  52. package/dist/{contract-infer-BDJgg7Xb.mjs.map → contract-infer-D9cC3rJm.mjs.map} +1 -1
  53. package/dist/exports/control-api.d.mts +2 -2
  54. package/dist/exports/control-api.d.mts.map +1 -1
  55. package/dist/exports/control-api.mjs +6 -6
  56. package/dist/exports/index.mjs +7 -7
  57. package/dist/exports/init-output.d.mts +39 -0
  58. package/dist/exports/init-output.d.mts.map +1 -0
  59. package/dist/exports/init-output.mjs +3 -0
  60. package/dist/{extract-operation-statements-DZUJNmL3.mjs → extract-operation-statements-DsFfxXVZ.mjs} +2 -2
  61. package/dist/{extract-operation-statements-DZUJNmL3.mjs.map → extract-operation-statements-DsFfxXVZ.mjs.map} +1 -1
  62. package/dist/{extract-sql-ddl-DDMX-9mz.mjs → extract-sql-ddl-D9UbZDyz.mjs} +1 -1
  63. package/dist/{extract-sql-ddl-DDMX-9mz.mjs.map → extract-sql-ddl-D9UbZDyz.mjs.map} +1 -1
  64. package/dist/{framework-components-Bsr1GaIj.mjs → framework-components-Cr--XBKy.mjs} +2 -2
  65. package/dist/{framework-components-Bsr1GaIj.mjs.map → framework-components-Cr--XBKy.mjs.map} +1 -1
  66. package/dist/init-m8x0UoPY.mjs +2062 -0
  67. package/dist/init-m8x0UoPY.mjs.map +1 -0
  68. package/dist/{inspect-live-schema-ChqrALmw.mjs → inspect-live-schema-yrHAvG71.mjs} +6 -6
  69. package/dist/{inspect-live-schema-ChqrALmw.mjs.map → inspect-live-schema-yrHAvG71.mjs.map} +1 -1
  70. package/dist/migration-cli.d.mts +50 -0
  71. package/dist/migration-cli.d.mts.map +1 -0
  72. package/dist/migration-cli.mjs +184 -0
  73. package/dist/migration-cli.mjs.map +1 -0
  74. package/dist/{migration-command-scaffold-B0oH_hyB.mjs → migration-command-scaffold-B3B09et6.mjs} +7 -7
  75. package/dist/{migration-command-scaffold-B0oH_hyB.mjs.map → migration-command-scaffold-B3B09et6.mjs.map} +1 -1
  76. package/dist/{migration-status-CPamfEPj.mjs → migration-status-DUMiH8_G.mjs} +25 -43
  77. package/dist/migration-status-DUMiH8_G.mjs.map +1 -0
  78. package/dist/{migrations-BIsjFjSV.mjs → migrations-Bo5WtTla.mjs} +4 -15
  79. package/dist/migrations-Bo5WtTla.mjs.map +1 -0
  80. package/dist/output-BpcQrnnq.mjs +103 -0
  81. package/dist/output-BpcQrnnq.mjs.map +1 -0
  82. package/dist/{progress-adapter-B-YvmcDu.mjs → progress-adapter-DvQWB1nK.mjs} +1 -1
  83. package/dist/{progress-adapter-B-YvmcDu.mjs.map → progress-adapter-DvQWB1nK.mjs.map} +1 -1
  84. package/dist/quick-reference-mongo.md +34 -13
  85. package/dist/quick-reference-postgres.md +11 -9
  86. package/dist/{result-handler-AFK4hxyX.mjs → result-handler-Ba3zWQsI.mjs} +26 -88
  87. package/dist/result-handler-Ba3zWQsI.mjs.map +1 -0
  88. package/dist/{terminal-ui-C5k88MmW.mjs → terminal-ui-C3ZLwQxK.mjs} +76 -2
  89. package/dist/terminal-ui-C3ZLwQxK.mjs.map +1 -0
  90. package/dist/{validate-contract-deps-DBH6iTAD.mjs → validate-contract-deps-B_Cs29TL.mjs} +1 -1
  91. package/dist/{validate-contract-deps-DBH6iTAD.mjs.map → validate-contract-deps-B_Cs29TL.mjs.map} +1 -1
  92. package/dist/{verify-C56CuQc7.mjs → verify-Bkycc-Tf.mjs} +2 -2
  93. package/dist/{verify-C56CuQc7.mjs.map → verify-Bkycc-Tf.mjs.map} +1 -1
  94. package/package.json +24 -19
  95. package/src/cli.ts +1 -5
  96. package/src/commands/contract-emit.ts +9 -10
  97. package/src/commands/init/detect-pnpm-catalog.ts +141 -0
  98. package/src/commands/init/errors.ts +254 -0
  99. package/src/commands/init/exit-codes.ts +62 -0
  100. package/src/commands/init/hygiene-gitattributes.ts +97 -0
  101. package/src/commands/init/hygiene-gitignore.ts +48 -0
  102. package/src/commands/init/hygiene-package-scripts.ts +91 -0
  103. package/src/commands/init/index.ts +112 -7
  104. package/src/commands/init/init.ts +766 -144
  105. package/src/commands/init/inputs.ts +421 -0
  106. package/src/commands/init/output.ts +147 -0
  107. package/src/commands/init/probe-db.ts +308 -0
  108. package/src/commands/init/reinit-cleanup.ts +83 -0
  109. package/src/commands/init/templates/agent-skill-mongo.md +63 -31
  110. package/src/commands/init/templates/agent-skill-postgres.md +1 -1
  111. package/src/commands/init/templates/agent-skill.ts +25 -3
  112. package/src/commands/init/templates/code-templates.ts +125 -32
  113. package/src/commands/init/templates/env.ts +80 -0
  114. package/src/commands/init/templates/quick-reference-mongo.md +34 -13
  115. package/src/commands/init/templates/quick-reference-postgres.md +11 -9
  116. package/src/commands/init/templates/quick-reference.ts +42 -3
  117. package/src/commands/init/templates/tsconfig.ts +167 -5
  118. package/src/commands/migration-apply.ts +37 -26
  119. package/src/commands/migration-new.ts +39 -17
  120. package/src/commands/migration-plan.ts +119 -104
  121. package/src/commands/migration-ref.ts +32 -47
  122. package/src/commands/migration-show.ts +6 -16
  123. package/src/commands/migration-status.ts +30 -55
  124. package/src/config-loader.ts +35 -29
  125. package/src/config-path-validation.ts +75 -0
  126. package/src/control-api/client.ts +2 -1
  127. package/src/control-api/operations/contract-emit.ts +24 -23
  128. package/src/control-api/types.ts +1 -1
  129. package/src/exports/init-output.ts +10 -0
  130. package/src/migration-cli.ts +254 -0
  131. package/src/utils/cli-errors.ts +1 -0
  132. package/src/utils/command-helpers.ts +18 -22
  133. package/src/utils/formatters/graph-migration-mapper.ts +5 -14
  134. package/src/utils/formatters/help.ts +0 -1
  135. package/src/utils/formatters/migrations.ts +2 -29
  136. package/dist/cli-errors-BUuJr6py.mjs +0 -5
  137. package/dist/cli-errors-Dic2eADK.d.mts +0 -4
  138. package/dist/commands/migration-emit.d.mts +0 -38
  139. package/dist/commands/migration-emit.d.mts.map +0 -1
  140. package/dist/commands/migration-emit.mjs +0 -81
  141. package/dist/commands/migration-emit.mjs.map +0 -1
  142. package/dist/config-loader-C4VXKl8f.mjs +0 -43
  143. package/dist/config-loader-C4VXKl8f.mjs.map +0 -1
  144. package/dist/contract-emit-CKig_Lra.mjs.map +0 -1
  145. package/dist/contract-emit-CU-SYNe4.mjs +0 -6
  146. package/dist/contract-emit-gpJNLGs7.mjs.map +0 -1
  147. package/dist/init-DZWvhEP0.mjs +0 -430
  148. package/dist/init-DZWvhEP0.mjs.map +0 -1
  149. package/dist/migration-emit-Du4DBMqz.mjs +0 -125
  150. package/dist/migration-emit-Du4DBMqz.mjs.map +0 -1
  151. package/dist/migration-status-CPamfEPj.mjs.map +0 -1
  152. package/dist/migrations-BIsjFjSV.mjs.map +0 -1
  153. package/dist/result-handler-AFK4hxyX.mjs.map +0 -1
  154. package/dist/terminal-ui-C5k88MmW.mjs.map +0 -1
  155. package/src/commands/migration-emit.ts +0 -134
  156. package/src/lib/migration-emit.ts +0 -125
  157. package/src/lib/migration-strategy.ts +0 -49
@@ -0,0 +1,184 @@
1
+ import { t as loadConfig } from "./config-loader-C25b63rJ.mjs";
2
+ import { readFileSync, writeFileSync } from "node:fs";
3
+ import { CliStructuredError, errorMigrationCliInvalidConfigArg } from "@prisma-next/errors/control";
4
+ import { dirname, join } from "pathe";
5
+ import { createControlStack } from "@prisma-next/framework-components/control";
6
+ import { errorMigrationTargetMismatch } from "@prisma-next/errors/migration";
7
+ import { fileURLToPath } from "node:url";
8
+ import { buildMigrationArtifacts, isDirectEntrypoint, printMigrationHelp } from "@prisma-next/migration-tools/migration";
9
+
10
+ //#region src/migration-cli.ts
11
+ /**
12
+ * The migration-file CLI interface: the actor invoked when the author runs
13
+ * `node migration.ts` directly.
14
+ *
15
+ * Naming: this is *not* a "migration runner" in the apply-time sense. The
16
+ * apply-time runner is the thing `prisma-next migration apply` uses to
17
+ * execute migration JSON ops against a database. `MigrationCLI` is the
18
+ * tiny CLI surface owned by an authored `migration.ts` file: parse the
19
+ * file's argv, load the project's `prisma-next.config.ts`, assemble a
20
+ * `ControlStack`, instantiate the migration class, and serialize.
21
+ *
22
+ * The user authors a migration class, then calls
23
+ * `MigrationCLI.run(import.meta.url, MigrationClass)` at module scope
24
+ * after the class definition. When the file is invoked as a node
25
+ * entrypoint (`node migration.ts`), the CLI:
26
+ *
27
+ * 1. Detects whether the file is the direct entrypoint (no-op when imported).
28
+ * 2. Parses CLI args (`--help`, `--dry-run`, `--config <path>`).
29
+ * 3. Loads the project's `prisma-next.config.ts` via the same `loadConfig`
30
+ * the CLI commands use, walking up from the migration file's directory.
31
+ * 4. Probe-instantiates the migration class without a stack so it can read
32
+ * `targetId` and verify it matches `config.target.targetId`
33
+ * (`PN-MIG-2006` on mismatch) before any stack-driven adapter
34
+ * construction runs.
35
+ * 5. Assembles a `ControlStack` from the loaded config descriptors and
36
+ * constructs the migration with that stack.
37
+ * 6. Reads any previously-scaffolded `migration.json`, then calls
38
+ * `buildMigrationArtifacts` from `@prisma-next/migration-tools` to
39
+ * produce in-memory `ops.json` + `migration.json` content. Persists
40
+ * the result to disk (or prints in dry-run mode).
41
+ *
42
+ * File I/O lives here, in `@prisma-next/cli`: this is the only place
43
+ * that legitimately combines config loading, stack assembly, and
44
+ * on-disk persistence. `@prisma-next/migration-tools` owns the pure
45
+ * conversion from a `Migration` instance to artifact strings; `Migration`
46
+ * stays a pure abstract class.
47
+ */
48
+ /**
49
+ * Parse the subset of `process.argv` that `MigrationCLI.run` cares about.
50
+ * Recognised flags: `--help`, `--dry-run`, `--config <path>` /
51
+ * `--config=<path>`. Unknown flags are ignored to keep the surface
52
+ * forgiving for ad-hoc tooling that wraps a migration file.
53
+ *
54
+ * Throws `errorMigrationCliInvalidConfigArg` (`PN-CLI-4012`) when
55
+ * `--config` is missing its path argument or is followed by another flag
56
+ * (e.g. `--config --dry-run`); silently consuming the next flag would
57
+ * either drop dry-run handling or serialize against the wrong project.
58
+ *
59
+ * NOTE: this hand-rolled parser is a known wart, tracked separately by
60
+ * TML-2318 ("Migration CLI: replace handrolled arg parser with shared
61
+ * CLI library"). Until that lands the surface is intentionally tiny.
62
+ */
63
+ function parseArgs(argv) {
64
+ let help = false;
65
+ let dryRun = false;
66
+ let configPath;
67
+ for (let i = 0; i < argv.length; i++) {
68
+ const arg = argv[i];
69
+ if (arg === "--help" || arg === "-h") help = true;
70
+ else if (arg === "--dry-run") dryRun = true;
71
+ else if (arg === "--config") {
72
+ const next = argv[i + 1];
73
+ if (next === void 0) throw errorMigrationCliInvalidConfigArg();
74
+ if (next.startsWith("-")) throw errorMigrationCliInvalidConfigArg({ nextToken: next });
75
+ configPath = next;
76
+ i++;
77
+ } else if (arg.startsWith("--config=")) configPath = arg.slice(9);
78
+ }
79
+ return {
80
+ help,
81
+ dryRun,
82
+ configPath
83
+ };
84
+ }
85
+ /**
86
+ * The CLI surface invoked by an authored `migration.ts` file. Exposed as
87
+ * a class with a static `run` method (rather than a free function) to
88
+ * give the concept a stable identity in the ubiquitous language: this is
89
+ * the "migration-file CLI", distinct from the apply-time runner that
90
+ * executes migration JSON ops.
91
+ *
92
+ * Currently a single static method. Future surface (e.g. a programmatic
93
+ * `MigrationCLI.serializeOnly(...)` for tests, or extra subcommands) can
94
+ * land here without changing the import shape used by every authored
95
+ * migration.
96
+ */
97
+ var MigrationCLI = class {
98
+ /**
99
+ * Orchestrates a class-flow `migration.ts` script run. Awaitable:
100
+ * callers may `await MigrationCLI.run(...)` to surface async failures
101
+ * from config loading, but the typical usage pattern (top-level call
102
+ * after the class definition) does not require awaiting because
103
+ * node's module evaluation keeps the promise alive until completion.
104
+ *
105
+ * Any throwable inside this function must surface through the internal
106
+ * try/catch — script callers do not await, so an unhandled rejection
107
+ * would silently exit 0. Treat the try/catch as load-bearing for the
108
+ * no-await usage pattern.
109
+ */
110
+ static async run(importMetaUrl, MigrationClass) {
111
+ if (!importMetaUrl) return;
112
+ if (!isDirectEntrypoint(importMetaUrl)) return;
113
+ try {
114
+ const args = parseArgs(process.argv.slice(2));
115
+ if (args.help) {
116
+ printMigrationHelp();
117
+ return;
118
+ }
119
+ const migrationDir = dirname(fileURLToPath(importMetaUrl));
120
+ const config = await loadConfig(args.configPath);
121
+ const probe = new MigrationClass();
122
+ if (probe.targetId !== config.target.targetId) throw errorMigrationTargetMismatch({
123
+ migrationTargetId: probe.targetId,
124
+ configTargetId: config.target.targetId
125
+ });
126
+ serializeMigrationToDisk(new MigrationClass(createControlStack(config)), migrationDir, args.dryRun);
127
+ } catch (err) {
128
+ if (CliStructuredError.is(err)) process.stderr.write(`${err.message}: ${err.why}\n`);
129
+ else process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
130
+ process.exitCode = 1;
131
+ }
132
+ }
133
+ };
134
+ /**
135
+ * Read a previously-scaffolded `migration.json` from disk, returning
136
+ * `null` when the file is missing or unparseable. The CLI feeds this into
137
+ * `buildMigrationArtifacts` so the pure builder can preserve fields owned
138
+ * by `migration plan` (contract bookends, hints, labels, `createdAt`)
139
+ * across re-emits.
140
+ */
141
+ function readExistingManifest(manifestPath) {
142
+ let raw;
143
+ try {
144
+ raw = readFileSync(manifestPath, "utf-8");
145
+ } catch {
146
+ return null;
147
+ }
148
+ try {
149
+ return JSON.parse(raw);
150
+ } catch {
151
+ return null;
152
+ }
153
+ }
154
+ /**
155
+ * Persist a migration instance's artifacts to `migrationDir`. In
156
+ * `dryRun` mode the artifacts are printed to stdout (with the same
157
+ * `--- migration.json --- / --- ops.json ---` framing the legacy
158
+ * `serializeMigration` helper used) and no files are written. Otherwise
159
+ * `ops.json` and `migration.json` are written next to `migration.ts` and
160
+ * a confirmation line is printed.
161
+ *
162
+ * File I/O lives in the CLI rather than `@prisma-next/migration-tools`
163
+ * so the migration-tools package stays focused on the pure
164
+ * `Migration` → in-memory artifact conversion. The CLI is the only
165
+ * legitimate site for combining config loading, stack assembly, and
166
+ * filesystem persistence.
167
+ */
168
+ function serializeMigrationToDisk(instance, migrationDir, dryRun) {
169
+ const manifestPath = join(migrationDir, "migration.json");
170
+ const { opsJson, manifestJson } = buildMigrationArtifacts(instance, readExistingManifest(manifestPath));
171
+ if (dryRun) {
172
+ process.stdout.write(`--- migration.json ---\n${manifestJson}\n`);
173
+ process.stdout.write("--- ops.json ---\n");
174
+ process.stdout.write(`${opsJson}\n`);
175
+ return;
176
+ }
177
+ writeFileSync(join(migrationDir, "ops.json"), opsJson);
178
+ writeFileSync(manifestPath, manifestJson);
179
+ process.stdout.write(`Wrote ops.json + migration.json to ${migrationDir}\n`);
180
+ }
181
+
182
+ //#endregion
183
+ export { MigrationCLI };
184
+ //# sourceMappingURL=migration-cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-cli.mjs","names":["configPath: string | undefined","raw: string"],"sources":["../src/migration-cli.ts"],"sourcesContent":["/**\n * The migration-file CLI interface: the actor invoked when the author runs\n * `node migration.ts` directly.\n *\n * Naming: this is *not* a \"migration runner\" in the apply-time sense. The\n * apply-time runner is the thing `prisma-next migration apply` uses to\n * execute migration JSON ops against a database. `MigrationCLI` is the\n * tiny CLI surface owned by an authored `migration.ts` file: parse the\n * file's argv, load the project's `prisma-next.config.ts`, assemble a\n * `ControlStack`, instantiate the migration class, and serialize.\n *\n * The user authors a migration class, then calls\n * `MigrationCLI.run(import.meta.url, MigrationClass)` at module scope\n * after the class definition. When the file is invoked as a node\n * entrypoint (`node migration.ts`), the CLI:\n *\n * 1. Detects whether the file is the direct entrypoint (no-op when imported).\n * 2. Parses CLI args (`--help`, `--dry-run`, `--config <path>`).\n * 3. Loads the project's `prisma-next.config.ts` via the same `loadConfig`\n * the CLI commands use, walking up from the migration file's directory.\n * 4. Probe-instantiates the migration class without a stack so it can read\n * `targetId` and verify it matches `config.target.targetId`\n * (`PN-MIG-2006` on mismatch) before any stack-driven adapter\n * construction runs.\n * 5. Assembles a `ControlStack` from the loaded config descriptors and\n * constructs the migration with that stack.\n * 6. Reads any previously-scaffolded `migration.json`, then calls\n * `buildMigrationArtifacts` from `@prisma-next/migration-tools` to\n * produce in-memory `ops.json` + `migration.json` content. Persists\n * the result to disk (or prints in dry-run mode).\n *\n * File I/O lives here, in `@prisma-next/cli`: this is the only place\n * that legitimately combines config loading, stack assembly, and\n * on-disk persistence. `@prisma-next/migration-tools` owns the pure\n * conversion from a `Migration` instance to artifact strings; `Migration`\n * stays a pure abstract class.\n */\n\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { CliStructuredError, errorMigrationCliInvalidConfigArg } from '@prisma-next/errors/control';\nimport { errorMigrationTargetMismatch } from '@prisma-next/errors/migration';\nimport { createControlStack } from '@prisma-next/framework-components/control';\nimport {\n buildMigrationArtifacts,\n isDirectEntrypoint,\n type Migration,\n printMigrationHelp,\n} from '@prisma-next/migration-tools/migration';\nimport type { MigrationManifest } from '@prisma-next/migration-tools/types';\nimport { dirname, join } from 'pathe';\nimport { loadConfig } from './config-loader';\n\n/**\n * Constructor shape accepted by `MigrationCLI.run`. `Migration` subclasses\n * accept an optional `ControlStack` in their constructor (each subclass\n * narrows the stack to its own family/target generics); the CLI always\n * passes one assembled from the loaded config. We use a rest-args `any[]`\n * constructor signature so that subclass constructors with narrower\n * parameter types remain assignable - constructor type compatibility in\n * TS is contravariant in the parameter, and a wider `unknown` parameter\n * on the alias side would reject any narrower subclass signature.\n *\n * The CLI only ever passes one argument (`new MigrationClass(stack)`);\n * the rest-arity is purely a type-compatibility concession for subclass\n * constructors that declare narrower parameter types, not an extension\n * point for additional construction arguments.\n */\n// biome-ignore lint/suspicious/noExplicitAny: see JSDoc - rest args with any are the idiomatic TS pattern for accepting arbitrary subclass constructor signatures\nexport type MigrationConstructor = new (...args: any[]) => Migration;\n\ninterface ParsedArgs {\n readonly help: boolean;\n readonly dryRun: boolean;\n readonly configPath: string | undefined;\n}\n\n/**\n * Parse the subset of `process.argv` that `MigrationCLI.run` cares about.\n * Recognised flags: `--help`, `--dry-run`, `--config <path>` /\n * `--config=<path>`. Unknown flags are ignored to keep the surface\n * forgiving for ad-hoc tooling that wraps a migration file.\n *\n * Throws `errorMigrationCliInvalidConfigArg` (`PN-CLI-4012`) when\n * `--config` is missing its path argument or is followed by another flag\n * (e.g. `--config --dry-run`); silently consuming the next flag would\n * either drop dry-run handling or serialize against the wrong project.\n *\n * NOTE: this hand-rolled parser is a known wart, tracked separately by\n * TML-2318 (\"Migration CLI: replace handrolled arg parser with shared\n * CLI library\"). Until that lands the surface is intentionally tiny.\n */\nfunction parseArgs(argv: readonly string[]): ParsedArgs {\n let help = false;\n let dryRun = false;\n let configPath: string | undefined;\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!;\n if (arg === '--help' || arg === '-h') {\n help = true;\n } else if (arg === '--dry-run') {\n dryRun = true;\n } else if (arg === '--config') {\n const next = argv[i + 1];\n if (next === undefined) {\n throw errorMigrationCliInvalidConfigArg();\n }\n if (next.startsWith('-')) {\n throw errorMigrationCliInvalidConfigArg({ nextToken: next });\n }\n configPath = next;\n i++;\n } else if (arg.startsWith('--config=')) {\n configPath = arg.slice('--config='.length);\n }\n }\n\n return { help, dryRun, configPath };\n}\n\n/**\n * The CLI surface invoked by an authored `migration.ts` file. Exposed as\n * a class with a static `run` method (rather than a free function) to\n * give the concept a stable identity in the ubiquitous language: this is\n * the \"migration-file CLI\", distinct from the apply-time runner that\n * executes migration JSON ops.\n *\n * Currently a single static method. Future surface (e.g. a programmatic\n * `MigrationCLI.serializeOnly(...)` for tests, or extra subcommands) can\n * land here without changing the import shape used by every authored\n * migration.\n */\n// 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.\nexport class MigrationCLI {\n /**\n * Orchestrates a class-flow `migration.ts` script run. Awaitable:\n * callers may `await MigrationCLI.run(...)` to surface async failures\n * from config loading, but the typical usage pattern (top-level call\n * after the class definition) does not require awaiting because\n * node's module evaluation keeps the promise alive until completion.\n *\n * Any throwable inside this function must surface through the internal\n * try/catch — script callers do not await, so an unhandled rejection\n * would silently exit 0. Treat the try/catch as load-bearing for the\n * no-await usage pattern.\n */\n static async run(importMetaUrl: string, MigrationClass: MigrationConstructor): Promise<void> {\n if (!importMetaUrl) return;\n if (!isDirectEntrypoint(importMetaUrl)) return;\n\n try {\n const args = parseArgs(process.argv.slice(2));\n\n if (args.help) {\n printMigrationHelp();\n return;\n }\n\n const migrationFile = fileURLToPath(importMetaUrl);\n const migrationDir = dirname(migrationFile);\n\n const config = await loadConfig(args.configPath);\n\n // Probe-instantiate without a stack so we can read `targetId` before\n // any target-specific constructor side effects (e.g.\n // `PostgresMigration`'s `stack.adapter.create(stack)`) run. Concrete\n // subclasses are required to accept the no-arg form; the abstract\n // `Migration` constructor declares `stack?` and target subclasses\n // (Postgres, Mongo) propagate that optionality. This makes the\n // target-mismatch guard fail fast with `PN-MIG-2006` before any\n // stack-driven adapter construction begins, even if the wrong-target\n // adapter's `create` would otherwise succeed and silently misshapen\n // the stored adapter cast.\n const probe = new MigrationClass();\n\n if (probe.targetId !== config.target.targetId) {\n throw errorMigrationTargetMismatch({\n migrationTargetId: probe.targetId,\n configTargetId: config.target.targetId,\n });\n }\n\n const stack = createControlStack(config);\n const instance = new MigrationClass(stack);\n\n serializeMigrationToDisk(instance, migrationDir, args.dryRun);\n } catch (err) {\n if (CliStructuredError.is(err)) {\n process.stderr.write(`${err.message}: ${err.why}\\n`);\n } else {\n process.stderr.write(`${err instanceof Error ? err.message : String(err)}\\n`);\n }\n process.exitCode = 1;\n }\n }\n}\n\n/**\n * Read a previously-scaffolded `migration.json` from disk, returning\n * `null` when the file is missing or unparseable. The CLI feeds this into\n * `buildMigrationArtifacts` so the pure builder can preserve fields owned\n * by `migration plan` (contract bookends, hints, labels, `createdAt`)\n * across re-emits.\n */\nfunction readExistingManifest(manifestPath: string): Partial<MigrationManifest> | null {\n let raw: string;\n try {\n raw = readFileSync(manifestPath, 'utf-8');\n } catch {\n return null;\n }\n try {\n return JSON.parse(raw) as Partial<MigrationManifest>;\n } catch {\n return null;\n }\n}\n\n/**\n * Persist a migration instance's artifacts to `migrationDir`. In\n * `dryRun` mode the artifacts are printed to stdout (with the same\n * `--- migration.json --- / --- ops.json ---` framing the legacy\n * `serializeMigration` helper used) and no files are written. Otherwise\n * `ops.json` and `migration.json` are written next to `migration.ts` and\n * a confirmation line is printed.\n *\n * File I/O lives in the CLI rather than `@prisma-next/migration-tools`\n * so the migration-tools package stays focused on the pure\n * `Migration` → in-memory artifact conversion. The CLI is the only\n * legitimate site for combining config loading, stack assembly, and\n * filesystem persistence.\n */\nfunction serializeMigrationToDisk(\n instance: Migration,\n migrationDir: string,\n dryRun: boolean,\n): void {\n const manifestPath = join(migrationDir, 'migration.json');\n const existing = readExistingManifest(manifestPath);\n const { opsJson, manifestJson } = buildMigrationArtifacts(instance, existing);\n\n if (dryRun) {\n process.stdout.write(`--- migration.json ---\\n${manifestJson}\\n`);\n process.stdout.write('--- ops.json ---\\n');\n process.stdout.write(`${opsJson}\\n`);\n return;\n }\n\n writeFileSync(join(migrationDir, 'ops.json'), opsJson);\n writeFileSync(manifestPath, manifestJson);\n\n process.stdout.write(`Wrote ops.json + migration.json to ${migrationDir}\\n`);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4FA,SAAS,UAAU,MAAqC;CACtD,IAAI,OAAO;CACX,IAAI,SAAS;CACb,IAAIA;AAEJ,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,MAAM,KAAK;AACjB,MAAI,QAAQ,YAAY,QAAQ,KAC9B,QAAO;WACE,QAAQ,YACjB,UAAS;WACA,QAAQ,YAAY;GAC7B,MAAM,OAAO,KAAK,IAAI;AACtB,OAAI,SAAS,OACX,OAAM,mCAAmC;AAE3C,OAAI,KAAK,WAAW,IAAI,CACtB,OAAM,kCAAkC,EAAE,WAAW,MAAM,CAAC;AAE9D,gBAAa;AACb;aACS,IAAI,WAAW,YAAY,CACpC,cAAa,IAAI,MAAM,EAAmB;;AAI9C,QAAO;EAAE;EAAM;EAAQ;EAAY;;;;;;;;;;;;;;AAgBrC,IAAa,eAAb,MAA0B;;;;;;;;;;;;;CAaxB,aAAa,IAAI,eAAuB,gBAAqD;AAC3F,MAAI,CAAC,cAAe;AACpB,MAAI,CAAC,mBAAmB,cAAc,CAAE;AAExC,MAAI;GACF,MAAM,OAAO,UAAU,QAAQ,KAAK,MAAM,EAAE,CAAC;AAE7C,OAAI,KAAK,MAAM;AACb,wBAAoB;AACpB;;GAIF,MAAM,eAAe,QADC,cAAc,cAAc,CACP;GAE3C,MAAM,SAAS,MAAM,WAAW,KAAK,WAAW;GAYhD,MAAM,QAAQ,IAAI,gBAAgB;AAElC,OAAI,MAAM,aAAa,OAAO,OAAO,SACnC,OAAM,6BAA6B;IACjC,mBAAmB,MAAM;IACzB,gBAAgB,OAAO,OAAO;IAC/B,CAAC;AAMJ,4BAFiB,IAAI,eADP,mBAAmB,OAAO,CACE,EAEP,cAAc,KAAK,OAAO;WACtD,KAAK;AACZ,OAAI,mBAAmB,GAAG,IAAI,CAC5B,SAAQ,OAAO,MAAM,GAAG,IAAI,QAAQ,IAAI,IAAI,IAAI,IAAI;OAEpD,SAAQ,OAAO,MAAM,GAAG,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC,IAAI;AAE/E,WAAQ,WAAW;;;;;;;;;;;AAYzB,SAAS,qBAAqB,cAAyD;CACrF,IAAIC;AACJ,KAAI;AACF,QAAM,aAAa,cAAc,QAAQ;SACnC;AACN,SAAO;;AAET,KAAI;AACF,SAAO,KAAK,MAAM,IAAI;SAChB;AACN,SAAO;;;;;;;;;;;;;;;;;AAkBX,SAAS,yBACP,UACA,cACA,QACM;CACN,MAAM,eAAe,KAAK,cAAc,iBAAiB;CAEzD,MAAM,EAAE,SAAS,iBAAiB,wBAAwB,UADzC,qBAAqB,aAAa,CAC0B;AAE7E,KAAI,QAAQ;AACV,UAAQ,OAAO,MAAM,2BAA2B,aAAa,IAAI;AACjE,UAAQ,OAAO,MAAM,qBAAqB;AAC1C,UAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AACpC;;AAGF,eAAc,KAAK,cAAc,WAAW,EAAE,QAAQ;AACtD,eAAc,cAAc,aAAa;AAEzC,SAAQ,OAAO,MAAM,sCAAsC,aAAa,IAAI"}
@@ -1,12 +1,12 @@
1
- import { t as loadConfig } from "./config-loader-C4VXKl8f.mjs";
2
- import { a as errorContractValidationFailed, c as errorDriverRequired, g as errorTargetMigrationNotSupported, l as errorFileNotFound, o as errorDatabaseConnectionRequired, v as errorUnexpected } from "./cli-errors-BUuJr6py.mjs";
3
- import { t as createControlClient } from "./client-CJxHfhze.mjs";
4
- import { _ as formatStyledHeader, a as maskConnectionUrl, n as addGlobalOptions, s as resolveContractPath } from "./result-handler-AFK4hxyX.mjs";
5
- import { t as createProgressAdapter } from "./progress-adapter-B-YvmcDu.mjs";
1
+ import { t as loadConfig } from "./config-loader-C25b63rJ.mjs";
2
+ import { _ as errorUnexpected, a as errorContractValidationFailed, c as errorDriverRequired, h as errorTargetMigrationNotSupported, l as errorFileNotFound, o as errorDatabaseConnectionRequired } from "./cli-errors-Cd79vmTH.mjs";
3
+ import { t as createControlClient } from "./client-CrsnY58k.mjs";
4
+ import { _ as formatStyledHeader, a as maskConnectionUrl, n as addGlobalOptions, s as resolveContractPath } from "./result-handler-Ba3zWQsI.mjs";
5
+ import { t as createProgressAdapter } from "./progress-adapter-DvQWB1nK.mjs";
6
6
  import { notOk, ok } from "@prisma-next/utils/result";
7
- import { relative, resolve } from "node:path";
8
7
  import { hasMigrations } from "@prisma-next/framework-components/control";
9
8
  import { readFile } from "node:fs/promises";
9
+ import { relative, resolve } from "node:path";
10
10
 
11
11
  //#region src/utils/migration-command-scaffold.ts
12
12
  /**
@@ -102,4 +102,4 @@ function addMigrationCommandOptions(command) {
102
102
 
103
103
  //#endregion
104
104
  export { prepareMigrationContext as n, addMigrationCommandOptions as t };
105
- //# sourceMappingURL=migration-command-scaffold-B0oH_hyB.mjs.map
105
+ //# sourceMappingURL=migration-command-scaffold-B3B09et6.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"migration-command-scaffold-B0oH_hyB.mjs","names":["details: Array<{ label: string; value: string }>","contractJsonContent: string","contractJson: Record<string, unknown>"],"sources":["../src/utils/migration-command-scaffold.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { relative, resolve } from 'node:path';\nimport { hasMigrations } from '@prisma-next/framework-components/control';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport type { Command } from 'commander';\nimport { loadConfig } from '../config-loader';\nimport { createControlClient } from '../control-api/client';\nimport type { ControlClient } from '../control-api/types';\nimport {\n type CliStructuredError,\n errorContractValidationFailed,\n errorDatabaseConnectionRequired,\n errorDriverRequired,\n errorFileNotFound,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n} from './cli-errors';\nimport { addGlobalOptions, maskConnectionUrl, resolveContractPath } from './command-helpers';\nimport { formatStyledHeader } from './formatters/styled';\nimport type { GlobalFlags } from './global-flags';\nimport { createProgressAdapter } from './progress-adapter';\nimport type { TerminalUI } from './terminal-ui';\n\n/**\n * Resolved context for a migration command.\n * Contains everything needed to invoke a control-api operation.\n */\nexport interface MigrationContext {\n readonly client: ControlClient;\n readonly contractJson: Record<string, unknown>;\n readonly dbConnection: unknown;\n readonly onProgress: ReturnType<typeof createProgressAdapter>;\n readonly configPath: string;\n readonly contractPath: string;\n readonly contractPathAbsolute: string;\n readonly config: Awaited<ReturnType<typeof loadConfig>>;\n}\n\n/**\n * Command-specific configuration for the shared scaffold.\n */\nexport interface MigrationCommandDescriptor {\n readonly commandName: string;\n readonly description: string;\n readonly url: string;\n}\n\n/**\n * Prepares the shared context for migration commands (db init, db update).\n *\n * Handles: config loading, contract file reading, JSON parsing, connection resolution,\n * driver/migration-support validation, client creation, and header output.\n *\n * Returns a Result with either the resolved context or a structured error.\n */\nexport async function prepareMigrationContext(\n options: { readonly db?: string; readonly config?: string; readonly dryRun?: boolean },\n flags: GlobalFlags,\n ui: TerminalUI,\n descriptor: MigrationCommandDescriptor,\n): Promise<Result<MigrationContext, CliStructuredError>> {\n // Load config\n const config = await loadConfig(options.config);\n const configPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n const contractPathAbsolute = resolveContractPath(config);\n const contractPath = relative(process.cwd(), contractPathAbsolute);\n\n // Output header to stderr (decoration)\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n ];\n if (options.db) {\n details.push({ label: 'database', value: maskConnectionUrl(options.db) });\n }\n if (options.dryRun) {\n details.push({ label: 'mode', value: 'dry run' });\n }\n const header = formatStyledHeader({\n command: descriptor.commandName,\n description: descriptor.description,\n url: descriptor.url,\n details,\n flags,\n });\n ui.stderr(header);\n }\n\n // Load contract file\n let contractJsonContent: string;\n try {\n contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n return notOk(\n errorFileNotFound(contractPathAbsolute, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n fix: `Run \\`prisma-next contract emit\\` to generate ${contractPath}, or update \\`config.contract.output\\` in ${configPath}`,\n }),\n );\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n\n // Parse contract JSON\n let contractJson: Record<string, unknown>;\n try {\n contractJson = JSON.parse(contractJsonContent) as Record<string, unknown>;\n } catch (error) {\n return notOk(\n errorContractValidationFailed(\n `Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,\n { where: { path: contractPathAbsolute } },\n ),\n );\n }\n\n // Resolve database connection (--db flag or config.db.connection)\n const dbConnection = options.db ?? config.db?.connection;\n if (!dbConnection) {\n return notOk(\n errorDatabaseConnectionRequired({\n why: `Database connection is required for ${descriptor.commandName} (set db.connection in ${configPath}, or pass --db <url>)`,\n commandName: descriptor.commandName,\n }),\n );\n }\n\n // Check for driver\n if (!config.driver) {\n return notOk(\n errorDriverRequired({ why: `Config.driver is required for ${descriptor.commandName}` }),\n );\n }\n\n if (!hasMigrations(config.target)) {\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support migrations`,\n }),\n );\n }\n\n // Create control client\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n driver: config.driver,\n extensionPacks: config.extensionPacks ?? [],\n });\n\n // Create progress adapter\n const onProgress = createProgressAdapter({ ui, flags });\n\n return ok({\n client,\n contractJson,\n dbConnection,\n onProgress,\n configPath,\n contractPath,\n contractPathAbsolute,\n config,\n });\n}\n\n/**\n * Registers the shared CLI options for migration commands (db init, db update).\n */\nexport function addMigrationCommandOptions(command: Command): Command {\n addGlobalOptions(command);\n return command\n .option('--db <url>', 'Database connection string')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--dry-run', 'Preview planned operations without applying', false);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAuDA,eAAsB,wBACpB,SACA,OACA,IACA,YACuD;CAEvD,MAAM,SAAS,MAAM,WAAW,QAAQ,OAAO;CAC/C,MAAM,aAAa,QAAQ,SACvB,SAAS,QAAQ,KAAK,EAAE,QAAQ,QAAQ,OAAO,CAAC,GAChD;CACJ,MAAM,uBAAuB,oBAAoB,OAAO;CACxD,MAAM,eAAe,SAAS,QAAQ,KAAK,EAAE,qBAAqB;AAGlE,KAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,OAAO;EAC/B,MAAMA,UAAmD,CACvD;GAAE,OAAO;GAAU,OAAO;GAAY,EACtC;GAAE,OAAO;GAAY,OAAO;GAAc,CAC3C;AACD,MAAI,QAAQ,GACV,SAAQ,KAAK;GAAE,OAAO;GAAY,OAAO,kBAAkB,QAAQ,GAAG;GAAE,CAAC;AAE3E,MAAI,QAAQ,OACV,SAAQ,KAAK;GAAE,OAAO;GAAQ,OAAO;GAAW,CAAC;EAEnD,MAAM,SAAS,mBAAmB;GAChC,SAAS,WAAW;GACpB,aAAa,WAAW;GACxB,KAAK,WAAW;GAChB;GACA;GACD,CAAC;AACF,KAAG,OAAO,OAAO;;CAInB,IAAIC;AACJ,KAAI;AACF,wBAAsB,MAAM,SAAS,sBAAsB,QAAQ;UAC5D,OAAO;AACd,MAAI,iBAAiB,SAAU,MAA4B,SAAS,SAClE,QAAO,MACL,kBAAkB,sBAAsB;GACtC,KAAK,8BAA8B;GACnC,KAAK,iDAAiD,aAAa,4CAA4C;GAChH,CAAC,CACH;AAEH,SAAO,MACL,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAAE,EACtE,KAAK,iCAAiC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IAC7F,CAAC,CACH;;CAIH,IAAIC;AACJ,KAAI;AACF,iBAAe,KAAK,MAAM,oBAAoB;UACvC,OAAO;AACd,SAAO,MACL,8BACE,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACnF,EAAE,OAAO,EAAE,MAAM,sBAAsB,EAAE,CAC1C,CACF;;CAIH,MAAM,eAAe,QAAQ,MAAM,OAAO,IAAI;AAC9C,KAAI,CAAC,aACH,QAAO,MACL,gCAAgC;EAC9B,KAAK,uCAAuC,WAAW,YAAY,yBAAyB,WAAW;EACvG,aAAa,WAAW;EACzB,CAAC,CACH;AAIH,KAAI,CAAC,OAAO,OACV,QAAO,MACL,oBAAoB,EAAE,KAAK,iCAAiC,WAAW,eAAe,CAAC,CACxF;AAGH,KAAI,CAAC,cAAc,OAAO,OAAO,CAC/B,QAAO,MACL,iCAAiC,EAC/B,KAAK,WAAW,OAAO,OAAO,GAAG,gCAClC,CAAC,CACH;CAIH,MAAM,SAAS,oBAAoB;EACjC,QAAQ,OAAO;EACf,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf,gBAAgB,OAAO,kBAAkB,EAAE;EAC5C,CAAC;CAGF,MAAM,aAAa,sBAAsB;EAAE;EAAI;EAAO,CAAC;AAEvD,QAAO,GAAG;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;;;;;AAMJ,SAAgB,2BAA2B,SAA2B;AACpE,kBAAiB,QAAQ;AACzB,QAAO,QACJ,OAAO,cAAc,6BAA6B,CAClD,OAAO,mBAAmB,gCAAgC,CAC1D,OAAO,aAAa,+CAA+C,MAAM"}
1
+ {"version":3,"file":"migration-command-scaffold-B3B09et6.mjs","names":["details: Array<{ label: string; value: string }>","contractJsonContent: string","contractJson: Record<string, unknown>"],"sources":["../src/utils/migration-command-scaffold.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { relative, resolve } from 'node:path';\nimport { hasMigrations } from '@prisma-next/framework-components/control';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport type { Command } from 'commander';\nimport { loadConfig } from '../config-loader';\nimport { createControlClient } from '../control-api/client';\nimport type { ControlClient } from '../control-api/types';\nimport {\n type CliStructuredError,\n errorContractValidationFailed,\n errorDatabaseConnectionRequired,\n errorDriverRequired,\n errorFileNotFound,\n errorTargetMigrationNotSupported,\n errorUnexpected,\n} from './cli-errors';\nimport { addGlobalOptions, maskConnectionUrl, resolveContractPath } from './command-helpers';\nimport { formatStyledHeader } from './formatters/styled';\nimport type { GlobalFlags } from './global-flags';\nimport { createProgressAdapter } from './progress-adapter';\nimport type { TerminalUI } from './terminal-ui';\n\n/**\n * Resolved context for a migration command.\n * Contains everything needed to invoke a control-api operation.\n */\nexport interface MigrationContext {\n readonly client: ControlClient;\n readonly contractJson: Record<string, unknown>;\n readonly dbConnection: unknown;\n readonly onProgress: ReturnType<typeof createProgressAdapter>;\n readonly configPath: string;\n readonly contractPath: string;\n readonly contractPathAbsolute: string;\n readonly config: Awaited<ReturnType<typeof loadConfig>>;\n}\n\n/**\n * Command-specific configuration for the shared scaffold.\n */\nexport interface MigrationCommandDescriptor {\n readonly commandName: string;\n readonly description: string;\n readonly url: string;\n}\n\n/**\n * Prepares the shared context for migration commands (db init, db update).\n *\n * Handles: config loading, contract file reading, JSON parsing, connection resolution,\n * driver/migration-support validation, client creation, and header output.\n *\n * Returns a Result with either the resolved context or a structured error.\n */\nexport async function prepareMigrationContext(\n options: { readonly db?: string; readonly config?: string; readonly dryRun?: boolean },\n flags: GlobalFlags,\n ui: TerminalUI,\n descriptor: MigrationCommandDescriptor,\n): Promise<Result<MigrationContext, CliStructuredError>> {\n // Load config\n const config = await loadConfig(options.config);\n const configPath = options.config\n ? relative(process.cwd(), resolve(options.config))\n : 'prisma-next.config.ts';\n const contractPathAbsolute = resolveContractPath(config);\n const contractPath = relative(process.cwd(), contractPathAbsolute);\n\n // Output header to stderr (decoration)\n if (!flags.json && !flags.quiet) {\n const details: Array<{ label: string; value: string }> = [\n { label: 'config', value: configPath },\n { label: 'contract', value: contractPath },\n ];\n if (options.db) {\n details.push({ label: 'database', value: maskConnectionUrl(options.db) });\n }\n if (options.dryRun) {\n details.push({ label: 'mode', value: 'dry run' });\n }\n const header = formatStyledHeader({\n command: descriptor.commandName,\n description: descriptor.description,\n url: descriptor.url,\n details,\n flags,\n });\n ui.stderr(header);\n }\n\n // Load contract file\n let contractJsonContent: string;\n try {\n contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');\n } catch (error) {\n if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {\n return notOk(\n errorFileNotFound(contractPathAbsolute, {\n why: `Contract file not found at ${contractPathAbsolute}`,\n fix: `Run \\`prisma-next contract emit\\` to generate ${contractPath}, or update \\`config.contract.output\\` in ${configPath}`,\n }),\n );\n }\n return notOk(\n errorUnexpected(error instanceof Error ? error.message : String(error), {\n why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,\n }),\n );\n }\n\n // Parse contract JSON\n let contractJson: Record<string, unknown>;\n try {\n contractJson = JSON.parse(contractJsonContent) as Record<string, unknown>;\n } catch (error) {\n return notOk(\n errorContractValidationFailed(\n `Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,\n { where: { path: contractPathAbsolute } },\n ),\n );\n }\n\n // Resolve database connection (--db flag or config.db.connection)\n const dbConnection = options.db ?? config.db?.connection;\n if (!dbConnection) {\n return notOk(\n errorDatabaseConnectionRequired({\n why: `Database connection is required for ${descriptor.commandName} (set db.connection in ${configPath}, or pass --db <url>)`,\n commandName: descriptor.commandName,\n }),\n );\n }\n\n // Check for driver\n if (!config.driver) {\n return notOk(\n errorDriverRequired({ why: `Config.driver is required for ${descriptor.commandName}` }),\n );\n }\n\n if (!hasMigrations(config.target)) {\n return notOk(\n errorTargetMigrationNotSupported({\n why: `Target \"${config.target.id}\" does not support migrations`,\n }),\n );\n }\n\n // Create control client\n const client = createControlClient({\n family: config.family,\n target: config.target,\n adapter: config.adapter,\n driver: config.driver,\n extensionPacks: config.extensionPacks ?? [],\n });\n\n // Create progress adapter\n const onProgress = createProgressAdapter({ ui, flags });\n\n return ok({\n client,\n contractJson,\n dbConnection,\n onProgress,\n configPath,\n contractPath,\n contractPathAbsolute,\n config,\n });\n}\n\n/**\n * Registers the shared CLI options for migration commands (db init, db update).\n */\nexport function addMigrationCommandOptions(command: Command): Command {\n addGlobalOptions(command);\n return command\n .option('--db <url>', 'Database connection string')\n .option('--config <path>', 'Path to prisma-next.config.ts')\n .option('--dry-run', 'Preview planned operations without applying', false);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAuDA,eAAsB,wBACpB,SACA,OACA,IACA,YACuD;CAEvD,MAAM,SAAS,MAAM,WAAW,QAAQ,OAAO;CAC/C,MAAM,aAAa,QAAQ,SACvB,SAAS,QAAQ,KAAK,EAAE,QAAQ,QAAQ,OAAO,CAAC,GAChD;CACJ,MAAM,uBAAuB,oBAAoB,OAAO;CACxD,MAAM,eAAe,SAAS,QAAQ,KAAK,EAAE,qBAAqB;AAGlE,KAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,OAAO;EAC/B,MAAMA,UAAmD,CACvD;GAAE,OAAO;GAAU,OAAO;GAAY,EACtC;GAAE,OAAO;GAAY,OAAO;GAAc,CAC3C;AACD,MAAI,QAAQ,GACV,SAAQ,KAAK;GAAE,OAAO;GAAY,OAAO,kBAAkB,QAAQ,GAAG;GAAE,CAAC;AAE3E,MAAI,QAAQ,OACV,SAAQ,KAAK;GAAE,OAAO;GAAQ,OAAO;GAAW,CAAC;EAEnD,MAAM,SAAS,mBAAmB;GAChC,SAAS,WAAW;GACpB,aAAa,WAAW;GACxB,KAAK,WAAW;GAChB;GACA;GACD,CAAC;AACF,KAAG,OAAO,OAAO;;CAInB,IAAIC;AACJ,KAAI;AACF,wBAAsB,MAAM,SAAS,sBAAsB,QAAQ;UAC5D,OAAO;AACd,MAAI,iBAAiB,SAAU,MAA4B,SAAS,SAClE,QAAO,MACL,kBAAkB,sBAAsB;GACtC,KAAK,8BAA8B;GACnC,KAAK,iDAAiD,aAAa,4CAA4C;GAChH,CAAC,CACH;AAEH,SAAO,MACL,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAAE,EACtE,KAAK,iCAAiC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IAC7F,CAAC,CACH;;CAIH,IAAIC;AACJ,KAAI;AACF,iBAAe,KAAK,MAAM,oBAAoB;UACvC,OAAO;AACd,SAAO,MACL,8BACE,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACnF,EAAE,OAAO,EAAE,MAAM,sBAAsB,EAAE,CAC1C,CACF;;CAIH,MAAM,eAAe,QAAQ,MAAM,OAAO,IAAI;AAC9C,KAAI,CAAC,aACH,QAAO,MACL,gCAAgC;EAC9B,KAAK,uCAAuC,WAAW,YAAY,yBAAyB,WAAW;EACvG,aAAa,WAAW;EACzB,CAAC,CACH;AAIH,KAAI,CAAC,OAAO,OACV,QAAO,MACL,oBAAoB,EAAE,KAAK,iCAAiC,WAAW,eAAe,CAAC,CACxF;AAGH,KAAI,CAAC,cAAc,OAAO,OAAO,CAC/B,QAAO,MACL,iCAAiC,EAC/B,KAAK,WAAW,OAAO,OAAO,GAAG,gCAClC,CAAC,CACH;CAIH,MAAM,SAAS,oBAAoB;EACjC,QAAQ,OAAO;EACf,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB,QAAQ,OAAO;EACf,gBAAgB,OAAO,kBAAkB,EAAE;EAC5C,CAAC;CAGF,MAAM,aAAa,sBAAsB;EAAE;EAAI;EAAO,CAAC;AAEvD,QAAO,GAAG;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;;;;;AAMJ,SAAgB,2BAA2B,SAA2B;AACpE,kBAAiB,QAAQ;AACzB,QAAO,QACJ,OAAO,cAAc,6BAA6B,CAClD,OAAO,mBAAmB,gCAAgC,CAC1D,OAAO,aAAa,+CAA+C,MAAM"}
@@ -1,16 +1,16 @@
1
- import { t as loadConfig } from "./config-loader-C4VXKl8f.mjs";
2
- import { h as errorRuntime, v as errorUnexpected } from "./cli-errors-BUuJr6py.mjs";
3
- import { t as createControlClient } from "./client-CJxHfhze.mjs";
4
- import { _ as formatStyledHeader, a as maskConnectionUrl, c as resolveMigrationPaths, d as setCommandExamples, i as loadAllBundles, m as parseGlobalFlags, n as addGlobalOptions, o as readContractEnvelope, p as toPathDecisionResult, t as handleResult, u as setCommandDescriptions } from "./result-handler-AFK4hxyX.mjs";
5
- import { t as TerminalUI } from "./terminal-ui-C5k88MmW.mjs";
1
+ import { t as loadConfig } from "./config-loader-C25b63rJ.mjs";
2
+ import { _ as errorUnexpected, m as errorRuntime } from "./cli-errors-Cd79vmTH.mjs";
3
+ import { t as createControlClient } from "./client-CrsnY58k.mjs";
4
+ import { t as TerminalUI } from "./terminal-ui-C3ZLwQxK.mjs";
5
+ import { _ as formatStyledHeader, a as maskConnectionUrl, c as resolveMigrationPaths, d as setCommandExamples, i as loadAllBundles, m as parseGlobalFlags, n as addGlobalOptions, o as readContractEnvelope, p as toPathDecisionResult, t as handleResult, u as setCommandDescriptions } from "./result-handler-Ba3zWQsI.mjs";
6
6
  import { Command } from "commander";
7
7
  import { notOk, ok } from "@prisma-next/utils/result";
8
8
  import { ifDefined } from "@prisma-next/utils/defined";
9
9
  import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
10
10
  import { findPath, findPathWithDecision, findReachableLeaves } from "@prisma-next/migration-tools/dag";
11
- import { MigrationToolsError } from "@prisma-next/migration-tools/types";
12
11
  import { bold, cyan, dim, magenta, yellow } from "colorette";
13
12
  import { readRefs, resolveRef } from "@prisma-next/migration-tools/refs";
13
+ import { MigrationToolsError } from "@prisma-next/migration-tools/types";
14
14
  import dagre from "@dagrejs/dagre";
15
15
 
16
16
  //#region src/utils/formatters/graph-types.ts
@@ -155,12 +155,9 @@ function migrationGraphToRenderInput(input) {
155
155
  id: shortHash(contractHash),
156
156
  markers: contractMarkers
157
157
  });
158
- const matchingDraft = input.draftEdges?.find((d) => d.to === contractHash);
159
- const fromHash = matchingDraft?.from ?? spineTargetHash;
160
- if (graph.nodes.has(fromHash) || fromHash === spineTargetHash) edgeList.push({
161
- from: toShortId(fromHash),
158
+ if (graph.nodes.has(spineTargetHash) || spineTargetHash === EMPTY_CONTRACT_HASH) edgeList.push({
159
+ from: toShortId(spineTargetHash),
162
160
  to: shortHash(contractHash),
163
- ...ifDefined("label", matchingDraft ? `${matchingDraft.dirName} [draft]` : void 0),
164
161
  style: "dashed"
165
162
  });
166
163
  }
@@ -1244,14 +1241,14 @@ function determineLimit(opts) {
1244
1241
  }
1245
1242
  async function executeMigrationStatusCommand(options, flags, ui) {
1246
1243
  const config = await loadConfig(options.config);
1247
- const { configPath, migrationsDir, migrationsRelative, refsPath } = resolveMigrationPaths(options.config, config);
1244
+ const { configPath, migrationsDir, migrationsRelative, refsDir } = resolveMigrationPaths(options.config, config);
1248
1245
  const dbConnection = options.db ?? config.db?.connection;
1249
1246
  const hasDriver = !!config.driver;
1250
1247
  let activeRefName;
1251
1248
  let activeRefHash;
1252
1249
  let allRefs = {};
1253
1250
  try {
1254
- allRefs = await readRefs(refsPath);
1251
+ allRefs = await readRefs(refsDir);
1255
1252
  } catch (error) {
1256
1253
  if (MigrationToolsError.is(error)) return notOk(errorRuntime(error.message, {
1257
1254
  why: error.why,
@@ -1262,10 +1259,8 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1262
1259
  }
1263
1260
  if (options.ref) {
1264
1261
  activeRefName = options.ref;
1265
- const refHash = allRefs[activeRefName];
1266
- if (refHash) activeRefHash = refHash;
1267
- else try {
1268
- activeRefHash = resolveRef(allRefs, activeRefName);
1262
+ try {
1263
+ activeRefHash = resolveRef(allRefs, activeRefName).hash;
1269
1264
  } catch (error) {
1270
1265
  if (MigrationToolsError.is(error)) return notOk(errorRuntime(error.message, {
1271
1266
  why: error.why,
@@ -1275,9 +1270,9 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1275
1270
  throw error;
1276
1271
  }
1277
1272
  }
1278
- const statusRefs = Object.entries(allRefs).map(([name, hash]) => ({
1273
+ const statusRefs = Object.entries(allRefs).map(([name, entry]) => ({
1279
1274
  name,
1280
- hash,
1275
+ hash: entry.hash,
1281
1276
  active: name === activeRefName
1282
1277
  }));
1283
1278
  if (!flags.json && !flags.quiet) {
@@ -1316,11 +1311,10 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1316
1311
  hints: ["Run 'prisma-next contract emit' to generate a valid contract"]
1317
1312
  });
1318
1313
  }
1319
- let attested;
1320
- let drafts;
1314
+ let bundles;
1321
1315
  let graph;
1322
1316
  try {
1323
- ({attested, drafts, graph} = await loadAllBundles(migrationsDir));
1317
+ ({bundles, graph} = await loadAllBundles(migrationsDir));
1324
1318
  } catch (error) {
1325
1319
  if (MigrationToolsError.is(error)) return notOk(errorRuntime(error.message, {
1326
1320
  why: error.why,
@@ -1329,13 +1323,7 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1329
1323
  }));
1330
1324
  return notOk(errorUnexpected(error instanceof Error ? error.message : String(error), { why: `Failed to read migrations directory: ${error instanceof Error ? error.message : String(error)}` }));
1331
1325
  }
1332
- if (drafts.length > 0) diagnostics.push({
1333
- code: "MIGRATION.DRAFTS",
1334
- severity: "warn",
1335
- message: `${drafts.length} draft migration(s) found: ${drafts.map((d) => d.dirName).join(", ")}`,
1336
- hints: ["Run 'prisma-next migration emit --dir <path>' to attest draft migrations before applying"]
1337
- });
1338
- if (attested.length === 0) {
1326
+ if (bundles.length === 0) {
1339
1327
  if (contractHash !== EMPTY_CONTRACT_HASH) diagnostics.push({
1340
1328
  code: "CONTRACT.AHEAD",
1341
1329
  severity: "warn",
@@ -1401,7 +1389,7 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1401
1389
  migrations: [],
1402
1390
  targetHash: EMPTY_CONTRACT_HASH,
1403
1391
  contractHash,
1404
- summary: `${attested.length} migration(s) on disk`,
1392
+ summary: `${bundles.length} migration(s) on disk`,
1405
1393
  diagnostics,
1406
1394
  markerHash,
1407
1395
  ...statusRefs.length > 0 ? { refs: statusRefs } : {}
@@ -1425,12 +1413,12 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1425
1413
  migrations: [],
1426
1414
  targetHash: EMPTY_CONTRACT_HASH,
1427
1415
  contractHash,
1428
- summary: `${attested.length} migration(s) on disk`,
1416
+ summary: `${bundles.length} migration(s) on disk`,
1429
1417
  diagnostics,
1430
1418
  ...ifDefined("markerHash", markerHash),
1431
1419
  ...statusRefs.length > 0 ? { refs: statusRefs } : {},
1432
1420
  graph,
1433
- bundles: attested,
1421
+ bundles,
1434
1422
  diverged: true
1435
1423
  });
1436
1424
  const chain = resolveDisplayChain(graph, targetHash, markerHash);
@@ -1439,11 +1427,11 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1439
1427
  fix: "The migration history may have gaps. Check the migrations directory for missing or corrupted packages."
1440
1428
  }));
1441
1429
  const edgeStatuses = deriveEdgeStatuses(graph, targetHash, contractHash, markerHash, mode);
1442
- const entries = buildMigrationEntries(chain, attested, mode, markerHash, edgeStatuses);
1430
+ const entries = buildMigrationEntries(chain, bundles, mode, markerHash, edgeStatuses);
1443
1431
  const pendingCount = edgeStatuses.filter((e) => e.status === "pending").length;
1444
1432
  const appliedCount = edgeStatuses.filter((e) => e.status === "applied").length;
1445
1433
  let summary;
1446
- if (mode === "online") if (markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash === contractHash) summary = `${attested.length} migration(s) on disk`;
1434
+ if (mode === "online") if (markerHash !== void 0 && !graph.nodes.has(markerHash) && markerHash === contractHash) summary = `${bundles.length} migration(s) on disk`;
1447
1435
  else if (activeRefHash && markerHash !== void 0) summary = summarizeRefDistance(graph, markerHash, activeRefHash, activeRefName);
1448
1436
  else if (pendingCount === 0) summary = `Database is up to date (${appliedCount} migration${appliedCount !== 1 ? "s" : ""} applied)`;
1449
1437
  else if (markerHash === void 0) summary = `${pendingCount} pending migration(s) — database has no marker`;
@@ -1484,8 +1472,7 @@ async function executeMigrationStatusCommand(options, flags, ui) {
1484
1472
  ...statusRefs.length > 0 ? { refs: statusRefs } : {},
1485
1473
  ...ifDefined("pathDecision", pathDecision),
1486
1474
  graph,
1487
- bundles: attested,
1488
- ...drafts.length > 0 ? { drafts } : {},
1475
+ bundles,
1489
1476
  edgeStatuses,
1490
1477
  ...ifDefined("activeRefHash", activeRefHash),
1491
1478
  ...ifDefined("activeRefName", activeRefName)
@@ -1517,12 +1504,7 @@ function createMigrationStatusCommand() {
1517
1504
  refs: statusResult.refs,
1518
1505
  activeRefHash: statusResult.activeRefHash,
1519
1506
  activeRefName: statusResult.activeRefName,
1520
- edgeStatuses: statusResult.edgeStatuses,
1521
- draftEdges: statusResult.drafts?.map((d) => ({
1522
- from: d.manifest.from,
1523
- to: d.manifest.to,
1524
- dirName: d.dirName
1525
- }))
1507
+ edgeStatuses: statusResult.edgeStatuses
1526
1508
  });
1527
1509
  const graphToRender = options.graph || statusResult.diverged ? renderInput.graph : extractRelevantSubgraph(renderInput.graph, renderInput.relevantPaths);
1528
1510
  const dagreOptions = !options.graph && isLinearGraph(graphToRender) ? { ranksep: 1 } : void 0;
@@ -1580,4 +1562,4 @@ function summarizeRefDistance(graph, markerHash, refHash, refName) {
1580
1562
 
1581
1563
  //#endregion
1582
1564
  export { deriveEdgeStatuses as n, createMigrationStatusCommand as t };
1583
- //# sourceMappingURL=migration-status-CPamfEPj.mjs.map
1565
+ //# sourceMappingURL=migration-status-DUMiH8_G.mjs.map