prisma-next 0.11.0 → 0.12.0-dev.1

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 (147) hide show
  1. package/dist/cli.mjs +259 -12
  2. package/dist/cli.mjs.map +1 -1
  3. package/dist/{client-oXO2WCPD.mjs → client-KgJorIvG.mjs} +72 -60
  4. package/dist/client-KgJorIvG.mjs.map +1 -0
  5. package/dist/{command-helpers-BSb0tRC8.mjs → command-helpers-Bbw1GbwL.mjs} +646 -46
  6. package/dist/command-helpers-Bbw1GbwL.mjs.map +1 -0
  7. package/dist/commands/contract-emit.d.mts.map +1 -1
  8. package/dist/commands/contract-emit.mjs +1 -1
  9. package/dist/commands/contract-infer.d.mts.map +1 -1
  10. package/dist/commands/contract-infer.mjs +1 -1
  11. package/dist/commands/db-init.d.mts.map +1 -1
  12. package/dist/commands/db-init.mjs +32 -7
  13. package/dist/commands/db-init.mjs.map +1 -1
  14. package/dist/commands/db-schema.d.mts.map +1 -1
  15. package/dist/commands/db-schema.mjs +3 -4
  16. package/dist/commands/db-schema.mjs.map +1 -1
  17. package/dist/commands/db-sign.d.mts.map +1 -1
  18. package/dist/commands/db-sign.mjs +12 -10
  19. package/dist/commands/db-sign.mjs.map +1 -1
  20. package/dist/commands/db-update.d.mts.map +1 -1
  21. package/dist/commands/db-update.mjs +41 -11
  22. package/dist/commands/db-update.mjs.map +1 -1
  23. package/dist/commands/db-verify.d.mts.map +1 -1
  24. package/dist/commands/db-verify.mjs +1 -1
  25. package/dist/commands/migrate.d.mts +6 -2
  26. package/dist/commands/migrate.d.mts.map +1 -1
  27. package/dist/commands/migrate.mjs +75 -40
  28. package/dist/commands/migrate.mjs.map +1 -1
  29. package/dist/commands/migration-check.d.mts +4 -3
  30. package/dist/commands/migration-check.d.mts.map +1 -1
  31. package/dist/commands/migration-check.mjs +1 -280
  32. package/dist/commands/migration-graph.d.mts +13 -2
  33. package/dist/commands/migration-graph.d.mts.map +1 -1
  34. package/dist/commands/migration-graph.mjs +2 -137
  35. package/dist/commands/migration-list.d.mts +64 -4
  36. package/dist/commands/migration-list.d.mts.map +1 -1
  37. package/dist/commands/migration-list.mjs +143 -56
  38. package/dist/commands/migration-list.mjs.map +1 -1
  39. package/dist/commands/migration-log.d.mts +10 -1
  40. package/dist/commands/migration-log.d.mts.map +1 -1
  41. package/dist/commands/migration-log.mjs +10 -15
  42. package/dist/commands/migration-log.mjs.map +1 -1
  43. package/dist/commands/migration-new.d.mts.map +1 -1
  44. package/dist/commands/migration-new.mjs +32 -38
  45. package/dist/commands/migration-new.mjs.map +1 -1
  46. package/dist/commands/migration-plan.d.mts +3 -2
  47. package/dist/commands/migration-plan.d.mts.map +1 -1
  48. package/dist/commands/migration-plan.mjs +1 -1
  49. package/dist/commands/migration-show.d.mts +4 -55
  50. package/dist/commands/migration-show.d.mts.map +1 -1
  51. package/dist/commands/migration-show.mjs +61 -153
  52. package/dist/commands/migration-show.mjs.map +1 -1
  53. package/dist/commands/migration-status.d.mts +12 -49
  54. package/dist/commands/migration-status.d.mts.map +1 -1
  55. package/dist/commands/migration-status.mjs +85 -81
  56. package/dist/commands/migration-status.mjs.map +1 -1
  57. package/dist/commands/ref.d.mts +1 -1
  58. package/dist/commands/ref.d.mts.map +1 -1
  59. package/dist/commands/ref.mjs +38 -10
  60. package/dist/commands/ref.mjs.map +1 -1
  61. package/dist/config-loader-B6sJjXTv.mjs.map +1 -1
  62. package/dist/config-loader.d.mts.map +1 -1
  63. package/dist/contract-at-errors-BxP-TOMl.mjs +42 -0
  64. package/dist/contract-at-errors-BxP-TOMl.mjs.map +1 -0
  65. package/dist/{contract-emit-bcrpT-wD.mjs → contract-emit-D-4jrNve.mjs} +25 -10
  66. package/dist/contract-emit-D-4jrNve.mjs.map +1 -0
  67. package/dist/{contract-emit-r4y8Zhf1.mjs → contract-emit-DxcGl4Uq.mjs} +19 -14
  68. package/dist/contract-emit-DxcGl4Uq.mjs.map +1 -0
  69. package/dist/{contract-enrichment-Dani0mMW.mjs → contract-enrichment-a0V5Y_mL.mjs} +4 -25
  70. package/dist/contract-enrichment-a0V5Y_mL.mjs.map +1 -0
  71. package/dist/{contract-infer-BmySmqVT.mjs → contract-infer-D8uEbJuu.mjs} +4 -5
  72. package/dist/{contract-infer-BmySmqVT.mjs.map → contract-infer-D8uEbJuu.mjs.map} +1 -1
  73. package/dist/contract-space-aggregate-loader-DvZwdkrr.mjs +247 -0
  74. package/dist/contract-space-aggregate-loader-DvZwdkrr.mjs.map +1 -0
  75. package/dist/{db-verify-BClPs3ph.mjs → db-verify-v_vUKXTU.mjs} +5 -7
  76. package/dist/{db-verify-BClPs3ph.mjs.map → db-verify-v_vUKXTU.mjs.map} +1 -1
  77. package/dist/exports/control-api.d.mts +3 -3
  78. package/dist/exports/control-api.d.mts.map +1 -1
  79. package/dist/exports/control-api.mjs +3 -3
  80. package/dist/exports/index.d.mts.map +1 -1
  81. package/dist/exports/index.mjs +1 -1
  82. package/dist/exports/index.mjs.map +1 -1
  83. package/dist/exports/init-output.d.mts.map +1 -1
  84. package/dist/exports/init-output.mjs +1 -1
  85. package/dist/extension-pack-inputs-IDvjRCi3.mjs +62 -0
  86. package/dist/extension-pack-inputs-IDvjRCi3.mjs.map +1 -0
  87. package/dist/{framework-components-65gOHkHB.mjs → framework-components-fYXjz_in.mjs} +2 -2
  88. package/dist/{framework-components-65gOHkHB.mjs.map → framework-components-fYXjz_in.mjs.map} +1 -1
  89. package/dist/global-flags-DEHjV8_s.d.mts +34 -0
  90. package/dist/global-flags-DEHjV8_s.d.mts.map +1 -0
  91. package/dist/{graph-render-DJVv0_uf.mjs → graph-render-rFAqZujX.mjs} +2 -2
  92. package/dist/{graph-render-DJVv0_uf.mjs.map → graph-render-rFAqZujX.mjs.map} +1 -1
  93. package/dist/{init-BCJZPWE1.mjs → init-Cv9UzWL5.mjs} +20 -269
  94. package/dist/init-Cv9UzWL5.mjs.map +1 -0
  95. package/dist/{inspect-live-schema-DSRbFoOL.mjs → inspect-live-schema-C6ohV_oQ.mjs} +4 -5
  96. package/dist/{inspect-live-schema-DSRbFoOL.mjs.map → inspect-live-schema-C6ohV_oQ.mjs.map} +1 -1
  97. package/dist/migration-check-BiBJoYYW.mjs +341 -0
  98. package/dist/migration-check-BiBJoYYW.mjs.map +1 -0
  99. package/dist/migration-cli.d.mts.map +1 -1
  100. package/dist/migration-cli.mjs +4 -4
  101. package/dist/migration-cli.mjs.map +1 -1
  102. package/dist/{migration-command-scaffold-Bzd9La5c.mjs → migration-command-scaffold-CjvwO6at.mjs} +4 -5
  103. package/dist/{migration-command-scaffold-Bzd9La5c.mjs.map → migration-command-scaffold-CjvwO6at.mjs.map} +1 -1
  104. package/dist/migration-graph-D7DVUElV.mjs +1232 -0
  105. package/dist/migration-graph-D7DVUElV.mjs.map +1 -0
  106. package/dist/migration-list-styler-BRwF4-gy.mjs +399 -0
  107. package/dist/migration-list-styler-BRwF4-gy.mjs.map +1 -0
  108. package/dist/{migration-plan-CFwqw3Gk.mjs → migration-plan-9DJ7q7_z.mjs} +372 -133
  109. package/dist/migration-plan-9DJ7q7_z.mjs.map +1 -0
  110. package/dist/{migration-types-BXWvz12q.d.mts → migration-types-D2FW63pr.d.mts} +1 -1
  111. package/dist/{migration-types-BXWvz12q.d.mts.map → migration-types-D2FW63pr.d.mts.map} +1 -1
  112. package/dist/{migrations-CwZMa1Ck.mjs → migrations-Cv2jxNNK.mjs} +12 -13
  113. package/dist/migrations-Cv2jxNNK.mjs.map +1 -0
  114. package/dist/{output-BlsrGMEF.mjs → output-B60Gw5fu.mjs} +1 -1
  115. package/dist/{output-BlsrGMEF.mjs.map → output-B60Gw5fu.mjs.map} +1 -1
  116. package/dist/{progress-adapter-DFfvZcYL.mjs → progress-adapter-C644QK8l.mjs} +1 -1
  117. package/dist/{progress-adapter-DFfvZcYL.mjs.map → progress-adapter-C644QK8l.mjs.map} +1 -1
  118. package/dist/ref-advancement-DUZqsue6.mjs +50 -0
  119. package/dist/ref-advancement-DUZqsue6.mjs.map +1 -0
  120. package/dist/terminal-ui-5Y6mrg93.d.mts +133 -0
  121. package/dist/terminal-ui-5Y6mrg93.d.mts.map +1 -0
  122. package/dist/{types--CqjMdk0.d.mts → types-Dt_SfqFm.d.mts} +28 -28
  123. package/dist/types-Dt_SfqFm.d.mts.map +1 -0
  124. package/dist/{verify-Bom75OYI.mjs → verify-DCA9Sldu.mjs} +2 -2
  125. package/dist/{verify-Bom75OYI.mjs.map → verify-DCA9Sldu.mjs.map} +1 -1
  126. package/package.json +28 -17
  127. package/dist/cli-errors-Czmx92Zy.d.mts +0 -3
  128. package/dist/cli-errors-Djtz98Vm.mjs +0 -71
  129. package/dist/cli-errors-Djtz98Vm.mjs.map +0 -1
  130. package/dist/client-oXO2WCPD.mjs.map +0 -1
  131. package/dist/command-helpers-BSb0tRC8.mjs.map +0 -1
  132. package/dist/commands/migration-check.mjs.map +0 -1
  133. package/dist/commands/migration-graph.mjs.map +0 -1
  134. package/dist/contract-emit-bcrpT-wD.mjs.map +0 -1
  135. package/dist/contract-emit-r4y8Zhf1.mjs.map +0 -1
  136. package/dist/contract-enrichment-Dani0mMW.mjs.map +0 -1
  137. package/dist/contract-space-aggregate-loader-BmNQwlws.mjs +0 -160
  138. package/dist/contract-space-aggregate-loader-BmNQwlws.mjs.map +0 -1
  139. package/dist/global-flags-CdE7M0d9.d.mts +0 -15
  140. package/dist/global-flags-CdE7M0d9.d.mts.map +0 -1
  141. package/dist/init-BCJZPWE1.mjs.map +0 -1
  142. package/dist/migration-plan-CFwqw3Gk.mjs.map +0 -1
  143. package/dist/migrations-CwZMa1Ck.mjs.map +0 -1
  144. package/dist/rolldown-runtime-twds-ZHy.mjs +0 -14
  145. package/dist/terminal-ui-BiB_8KNo.mjs +0 -379
  146. package/dist/terminal-ui-BiB_8KNo.mjs.map +0 -1
  147. package/dist/types--CqjMdk0.d.mts.map +0 -1
@@ -1,23 +1,23 @@
1
1
  import { t as loadConfig } from "./config-loader-B6sJjXTv.mjs";
2
- import { _ as errorTargetMigrationNotSupported, a as errorContractValidationFailed, b as mapMigrationToolsError, l as errorFileNotFound, p as errorMigrationPlanningFailed, t as CliStructuredError, x as mapRefResolutionError, y as errorUnexpected } from "./cli-errors-Djtz98Vm.mjs";
3
- import { t as assertFrameworkComponentsCompatible } from "./framework-components-65gOHkHB.mjs";
4
- import { t as createTerminalUI } from "./terminal-ui-BiB_8KNo.mjs";
5
- import { S as formatStyledHeader, a as loadMigrationPackages, c as resolveContractPath, d as setCommandDescriptions, f as setCommandExamples, i as getTargetMigrations, l as resolveMigrationPaths, t as addGlobalOptions, v as parseGlobalFlagsOrExit, y as handleResult } from "./command-helpers-BSb0tRC8.mjs";
6
- import { n as toExtensionInputs, t as buildContractSpaceAggregate } from "./contract-space-aggregate-loader-BmNQwlws.mjs";
2
+ import { $ as errorTargetMigrationNotSupported, A as CliStructuredError, G as errorPlanForgotTheFlag, P as errorContractValidationFailed, R as errorFileNotFound, T as formatStyledHeader, U as errorMigrationPlanningFailed, Z as errorSnapshotMissing, _ as createTerminalUI, g as parseGlobalFlagsOrExit, l as setCommandDescriptions, nt as mapMigrationToolsError, o as resolveContractPath, r as getTargetMigrations, rt as mapRefResolutionError, s as resolveMigrationPaths, t as addGlobalOptions, tt as errorUnexpected, u as setCommandExamples, y as handleResult } from "./command-helpers-Bbw1GbwL.mjs";
3
+ import { t as assertFrameworkComponentsCompatible } from "./framework-components-fYXjz_in.mjs";
4
+ import { n as toExtensionInputs } from "./extension-pack-inputs-IDvjRCi3.mjs";
5
+ import { a as loadContractSpaceAggregateForCli, n as buildContractSpaceAggregate } from "./contract-space-aggregate-loader-DvZwdkrr.mjs";
6
+ import { t as mapContractAtError } from "./contract-at-errors-BxP-TOMl.mjs";
7
7
  import { Command } from "commander";
8
8
  import { getEmittedArtifactPaths } from "@prisma-next/emitter";
9
9
  import { notOk, ok } from "@prisma-next/utils/result";
10
10
  import { join, relative } from "pathe";
11
- import { readFile } from "node:fs/promises";
11
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
12
12
  import { createControlStack, hasOperationPreview } from "@prisma-next/framework-components/control";
13
- import { copyFilesWithRename, formatMigrationDirName, materialiseExtensionMigrationPackageIfMissing, writeMigrationPackage } from "@prisma-next/migration-tools/io";
14
- import { findLatestMigration } from "@prisma-next/migration-tools/migration-graph";
15
13
  import { emitContractSpaceArtefacts, planAllSpaces, readContractSpaceHeadRef, spaceMigrationDirectory } from "@prisma-next/migration-tools/spaces";
16
14
  import { MigrationToolsError } from "@prisma-next/migration-tools/errors";
15
+ import { assertHashIsGraphNode, findLatestMigration, isGraphNode } from "@prisma-next/migration-tools/migration-graph";
17
16
  import { parseContractRef } from "@prisma-next/migration-tools/ref-resolution";
18
- import { readRefs } from "@prisma-next/migration-tools/refs";
19
17
  import { computeMigrationHash } from "@prisma-next/migration-tools/hash";
18
+ import { copyFilesWithRename, formatMigrationDirName, materialiseExtensionMigrationPackageIfMissing, writeMigrationPackage } from "@prisma-next/migration-tools/io";
20
19
  import { writeMigrationTs } from "@prisma-next/migration-tools/migration-ts";
20
+ import { canonicalizeJson } from "@prisma-next/framework-components/utils";
21
21
  import { deriveProvidedInvariants } from "@prisma-next/migration-tools/invariants";
22
22
  //#region src/utils/contract-space-seed-phase.ts
23
23
  /**
@@ -109,44 +109,203 @@ function buildPlaceholderContractDts(spaceId) {
109
109
  ].join("\n");
110
110
  }
111
111
  //#endregion
112
- //#region src/commands/migration-plan.ts
113
- /**
114
- * Load a predecessor migration's destination contract from its sibling
115
- * `end-contract.json` on disk and route it through the family's
116
- * `ContractSerializer` (via `deserializeContract`) so the in-memory shape
117
- * is the hydrated `Contract` every other caller sees. Bypassing this
118
- * seam was the root cause of TML-2536: a raw `JSON.parse(...) as Contract`
119
- * here let polymorphic `storage.types` entries reach the planner without
120
- * the `kind` discriminator the planner dispatches on.
121
- *
122
- * Throws `CliStructuredError` with:
123
- * - `errorFileNotFound` when the sibling file is missing — the user
124
- * has likely deleted or never authored the snapshot, and the
125
- * message names the file and points them at re-emitting from the
126
- * source.
127
- * - `errorContractValidationFailed` when the JSON parses but the
128
- * family deserializer rejects it (legacy untagged shape, structural
129
- * mismatch, etc.) — the message names the predecessor's path so
130
- * the operator can locate the bad snapshot.
131
- */
132
- async function readPredecessorEndContract(migrationDir, familyInstance) {
133
- const path = join(migrationDir, "end-contract.json");
134
- let raw;
112
+ //#region src/utils/plan-resolution.ts
113
+ const FULL_HASH_PATTERN = /^sha256:([0-9a-f]{64}|empty)$/;
114
+ function looksLikeFullHash(input) {
115
+ return FULL_HASH_PATTERN.test(input);
116
+ }
117
+ function graphIsEmpty(member) {
118
+ return member.packages.length === 0;
119
+ }
120
+ function getReachableRefs(refs, graph) {
121
+ return Object.entries(refs).flatMap(([name, entry]) => entry && isGraphNode(entry.hash, graph) ? [{
122
+ name,
123
+ hash: entry.hash
124
+ }] : []).sort((a, b) => a.name.localeCompare(b.name));
125
+ }
126
+ function assertFromIsGraphNode(fromHash, graph, refs, graphTipHash) {
135
127
  try {
136
- raw = await readFile(path, "utf-8");
128
+ assertHashIsGraphNode(fromHash, graph);
137
129
  } catch (error) {
138
- if (error instanceof Error && error.code === "ENOENT") throw errorFileNotFound(path, {
139
- why: `Predecessor migration is missing its destination contract snapshot at ${path}`,
140
- fix: "Re-emit the predecessor migration (`prisma-next migration plan` from its source) so its sibling `end-contract.json` is restored, then re-run this command."
141
- });
130
+ if (MigrationToolsError.is(error) && error.code === "MIGRATION.HASH_NOT_IN_GRAPH") throw errorPlanForgotTheFlag(fromHash, getReachableRefs(refs, graph), graphTipHash);
142
131
  throw error;
143
132
  }
133
+ }
134
+ async function resolveContractRef(parsed, member, options) {
135
+ const { hash, provenance } = parsed;
136
+ const refName = provenance.kind === "ref" ? provenance.refName : void 0;
137
+ try {
138
+ const at = await member.contractAt(hash, refName !== void 0 ? { refName } : void 0);
139
+ if (at.provenance === "snapshot") return ok({
140
+ kind: "snapshot",
141
+ hash: at.hash,
142
+ contract: at.contract,
143
+ contractJson: at.contractJson,
144
+ contractDts: at.contractDts
145
+ });
146
+ return ok({
147
+ kind: "graph-node",
148
+ hash: at.hash,
149
+ contract: at.contract,
150
+ contractJson: at.contractJson,
151
+ contractDts: at.contractDts,
152
+ sourceDir: at.sourceDir
153
+ });
154
+ } catch (error) {
155
+ return mapContractAtError(error, options?.artifactRole !== void 0 ? { artifactRole: options.artifactRole } : void 0);
156
+ }
157
+ }
158
+ async function resolveFromPolicy(parsed, input, refs, explicitFromLabel) {
159
+ const resolution = await resolveContractRef(parsed, input.member, {
160
+ ...explicitFromLabel !== void 0 ? { explicitLabel: explicitFromLabel } : {},
161
+ artifactRole: "from"
162
+ });
163
+ if (!resolution.ok) return resolution;
164
+ if (resolution.value.kind === "graph-node") return ok({
165
+ kind: "graph-node",
166
+ fromHash: resolution.value.hash,
167
+ fromContract: resolution.value.contract,
168
+ sourceDir: resolution.value.sourceDir
169
+ });
170
+ const { hash, contract, contractJson, contractDts } = resolution.value;
171
+ if (graphIsEmpty(input.member)) return ok({
172
+ kind: "auto-baseline",
173
+ fromHash: hash,
174
+ fromContract: contract,
175
+ contractDts,
176
+ contractJson
177
+ });
178
+ const graph = input.member.graph();
179
+ const graphTip = findLatestMigration(graph)?.to ?? null;
144
180
  try {
145
- return familyInstance.deserializeContract(JSON.parse(raw));
181
+ assertFromIsGraphNode(hash, graph, refs, graphTip);
146
182
  } catch (error) {
147
- if (CliStructuredError.is(error)) throw error;
148
- throw errorContractValidationFailed(`Predecessor contract at ${path} failed to deserialize: ${error instanceof Error ? error.message : String(error)}`, { where: { path } });
183
+ if (CliStructuredError.is(error)) return notOk(error);
184
+ throw error;
185
+ }
186
+ return ok({
187
+ kind: "snapshot",
188
+ fromHash: hash,
189
+ fromContract: contract,
190
+ contractDts,
191
+ contractJson
192
+ });
193
+ }
194
+ async function resolveFromForPlan(input) {
195
+ const { optionsFrom, member } = input;
196
+ const graph = member.graph();
197
+ const refs = member.refs;
198
+ if (optionsFrom === void 0) {
199
+ const dbRef = refs["db"];
200
+ if (!dbRef) return ok({
201
+ kind: "greenfield",
202
+ fromHash: null,
203
+ fromContract: null
204
+ });
205
+ return resolveFromPolicy({
206
+ hash: dbRef.hash,
207
+ provenance: {
208
+ kind: "ref",
209
+ refName: "db"
210
+ }
211
+ }, input, refs);
212
+ }
213
+ const refResult = parseContractRef(optionsFrom, {
214
+ graph,
215
+ refs
216
+ });
217
+ if (!refResult.ok) {
218
+ if (looksLikeFullHash(optionsFrom)) {
219
+ const empty = graphIsEmpty(member);
220
+ const graphTip = findLatestMigration(graph)?.to ?? null;
221
+ if (empty) return notOk(errorSnapshotMissing(optionsFrom, { viaRef: false }));
222
+ return notOk(errorPlanForgotTheFlag(optionsFrom, getReachableRefs(refs, graph), graphTip));
223
+ }
224
+ return notOk(mapRefResolutionError(refResult.failure));
225
+ }
226
+ return resolveFromPolicy(refResult.value, input, refs, optionsFrom);
227
+ }
228
+ async function resolveToForPlan(optionsTo, input) {
229
+ const { member } = input;
230
+ const graph = member.graph();
231
+ const refs = member.refs;
232
+ const refResult = parseContractRef(optionsTo, {
233
+ graph,
234
+ refs
235
+ });
236
+ if (!refResult.ok) return notOk(mapRefResolutionError(refResult.failure));
237
+ const resolution = await resolveContractRef(refResult.value, member, {
238
+ explicitLabel: optionsTo,
239
+ artifactRole: "to"
240
+ });
241
+ if (!resolution.ok) return resolution;
242
+ const { hash, contract, contractJson, contractDts } = resolution.value;
243
+ return ok({
244
+ hash,
245
+ contract,
246
+ contractJson,
247
+ contractDts
248
+ });
249
+ }
250
+ //#endregion
251
+ //#region src/commands/migration-plan.ts
252
+ async function writeSnapshotContractArtifacts(packageDir, contractJson, contractDts, artifactBasename) {
253
+ await mkdir(packageDir, { recursive: true });
254
+ const jsonContent = `${canonicalizeJson(contractJson)}\n`;
255
+ const dtsContent = contractDts.endsWith("\n") ? contractDts : `${contractDts}\n`;
256
+ await writeFile(join(packageDir, `${artifactBasename}.json`), jsonContent);
257
+ await writeFile(join(packageDir, `${artifactBasename}.d.ts`), dtsContent);
258
+ }
259
+ async function writeSnapshotStartContract(packageDir, contractJson, contractDts) {
260
+ await writeSnapshotContractArtifacts(packageDir, contractJson, contractDts, "start-contract");
261
+ }
262
+ async function runPlannerLeg(planner, migrations, frameworkComponents, contract, fromContract, spaceId) {
263
+ const fromSchema = migrations.contractToSchema(fromContract, frameworkComponents);
264
+ const plannerResult = planner.plan({
265
+ contract,
266
+ schema: fromSchema,
267
+ policy: { allowedOperationClasses: [
268
+ "additive",
269
+ "widening",
270
+ "destructive",
271
+ "data"
272
+ ] },
273
+ fromContract,
274
+ frameworkComponents,
275
+ spaceId
276
+ });
277
+ if (plannerResult.kind === "failure") return notOk(errorMigrationPlanningFailed({ conflicts: plannerResult.conflicts }));
278
+ let plannedOps = [];
279
+ let hasPlaceholders = false;
280
+ try {
281
+ plannedOps = plannerResult.plan.operations;
282
+ if (plannedOps.length === 0) return notOk(errorMigrationPlanningFailed({ conflicts: [{
283
+ kind: "unsupportedChange",
284
+ summary: "Contract changed but planner produced no operations. This indicates unsupported or ignored changes."
285
+ }] }));
286
+ } catch (e) {
287
+ if (CliStructuredError.is(e) && e.domain === "MIG" && e.code === "2001") hasPlaceholders = true;
288
+ else throw e;
149
289
  }
290
+ return ok({
291
+ plannedOps,
292
+ migrationTsContent: plannerResult.plan.renderTypeScript(),
293
+ hasPlaceholders
294
+ });
295
+ }
296
+ async function writePlannedMigrationPackage(packageDir, fromHash, toHash, createdAt, leg) {
297
+ const opsForWrite = leg.hasPlaceholders ? [] : leg.plannedOps;
298
+ const metadataWithInvariants = {
299
+ from: fromHash,
300
+ to: toHash,
301
+ providedInvariants: deriveProvidedInvariants(opsForWrite),
302
+ createdAt: createdAt.toISOString()
303
+ };
304
+ await writeMigrationPackage(packageDir, {
305
+ ...metadataWithInvariants,
306
+ migrationHash: computeMigrationHash(metadataWithInvariants, opsForWrite)
307
+ }, opsForWrite);
308
+ await writeMigrationTs(packageDir, leg.migrationTsContent);
150
309
  }
151
310
  async function executeMigrationPlanCommand(options, flags, ui, startTime) {
152
311
  const config = await loadConfig(options.config);
@@ -172,6 +331,10 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
172
331
  label: "from",
173
332
  value: options.from
174
333
  });
334
+ if (options.to) details.push({
335
+ label: "to",
336
+ value: options.to
337
+ });
175
338
  if (options.name) details.push({
176
339
  label: "name",
177
340
  value: options.name
@@ -205,43 +368,61 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
205
368
  }
206
369
  const rawStorageHash = toContract.storage?.storageHash;
207
370
  if (typeof rawStorageHash !== "string") return notOk(errorContractValidationFailed("Contract is missing storageHash", { where: { path: contractPathAbsolute } }));
208
- const toStorageHash = rawStorageHash;
371
+ let toStorageHash = rawStorageHash;
372
+ let toArtifacts = null;
209
373
  let fromContract = null;
210
374
  let fromHash = null;
211
375
  let fromContractSourceDir = null;
212
- try {
213
- const { bundles, graph } = await loadMigrationPackages(appMigrationsDir);
214
- if (options.from) {
215
- const refs = await readRefs(resolveMigrationPaths(options.config, config).refsDir);
216
- const refResult = parseContractRef(options.from, {
217
- graph,
218
- refs
219
- });
220
- if (!refResult.ok) return notOk(mapRefResolutionError(refResult.failure));
221
- fromHash = refResult.value.hash;
222
- const matchingBundle = bundles.find((p) => p.metadata.to === fromHash);
223
- if (!matchingBundle) return notOk(errorUnexpected(`No migration bundle found for --from "${options.from}" (resolved hash: ${fromHash})`, {
224
- why: `The ref resolved successfully but no on-disk migration package has an end-contract hash matching ${fromHash}.`,
225
- fix: "Provide a ref or hash that corresponds to an existing migration package, or run `migration list` to see available migrations."
226
- }));
227
- fromContractSourceDir = matchingBundle.dirPath;
228
- fromContract = await readPredecessorEndContract(fromContractSourceDir, familyInstance);
229
- } else {
230
- const latestMigration = findLatestMigration(graph);
231
- if (latestMigration) {
232
- fromHash = latestMigration.to;
233
- const leafPkg = bundles.find((p) => p.metadata.migrationHash === latestMigration.migrationHash);
234
- if (leafPkg) {
235
- fromContractSourceDir = leafPkg.dirPath;
236
- fromContract = await readPredecessorEndContract(fromContractSourceDir, familyInstance);
237
- }
238
- }
239
- }
240
- } catch (error) {
241
- if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
242
- if (CliStructuredError.is(error)) return notOk(error);
243
- const message = error instanceof Error ? error.message : String(error);
244
- return notOk(errorUnexpected(message, { why: `Unexpected error while loading migrations: ${message}` }));
376
+ let snapshotStartContract = null;
377
+ let isAutoBaseline = false;
378
+ const tolerantAggregateResult = await loadContractSpaceAggregateForCli({
379
+ targetId: config.target.targetId,
380
+ migrationsDir,
381
+ appContract: toContract,
382
+ extensionPacks: config.extensionPacks ?? [],
383
+ deserializeContract: (json) => familyInstance.deserializeContract(json)
384
+ });
385
+ if (!tolerantAggregateResult.ok) return notOk(tolerantAggregateResult.failure);
386
+ const resolutionMember = tolerantAggregateResult.value.app;
387
+ const resolutionResult = await resolveFromForPlan({
388
+ optionsFrom: options.from,
389
+ member: resolutionMember
390
+ });
391
+ if (!resolutionResult.ok) return notOk(resolutionResult.failure);
392
+ switch (resolutionResult.value.kind) {
393
+ case "greenfield": break;
394
+ case "graph-node":
395
+ fromHash = resolutionResult.value.fromHash;
396
+ fromContract = resolutionResult.value.fromContract;
397
+ fromContractSourceDir = resolutionResult.value.sourceDir;
398
+ break;
399
+ case "snapshot":
400
+ fromHash = resolutionResult.value.fromHash;
401
+ fromContract = resolutionResult.value.fromContract;
402
+ snapshotStartContract = {
403
+ contractJson: resolutionResult.value.contractJson,
404
+ contractDts: resolutionResult.value.contractDts
405
+ };
406
+ break;
407
+ case "auto-baseline":
408
+ fromHash = resolutionResult.value.fromHash;
409
+ fromContract = resolutionResult.value.fromContract;
410
+ snapshotStartContract = {
411
+ contractJson: resolutionResult.value.contractJson,
412
+ contractDts: resolutionResult.value.contractDts
413
+ };
414
+ isAutoBaseline = true;
415
+ break;
416
+ }
417
+ if (options.to !== void 0) {
418
+ const toResolution = await resolveToForPlan(options.to, { member: resolutionMember });
419
+ if (!toResolution.ok) return notOk(toResolution.failure);
420
+ toContract = toResolution.value.contract;
421
+ toStorageHash = toResolution.value.hash;
422
+ toArtifacts = {
423
+ contractJson: toResolution.value.contractJson,
424
+ contractDts: toResolution.value.contractDts
425
+ };
245
426
  }
246
427
  const seedResult = await runContractSpaceSeedPhase({
247
428
  migrationsDir,
@@ -257,7 +438,7 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
257
438
  spaceId: r.spaceId,
258
439
  dirName
259
440
  })));
260
- if (fromHash === toStorageHash) return ok({
441
+ if (fromHash === toStorageHash && !isAutoBaseline) return ok({
261
442
  ok: true,
262
443
  noOp: true,
263
444
  from: fromHash,
@@ -283,58 +464,11 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
283
464
  config.adapter,
284
465
  ...config.extensionPacks ?? []
285
466
  ]);
286
- const timestamp = /* @__PURE__ */ new Date();
287
- const packageDir = join(appMigrationsDir, formatMigrationDirName(timestamp, options.name ?? "migration"));
288
- const baseMetadata = {
289
- from: fromHash,
290
- to: toStorageHash,
291
- hints: {
292
- used: [],
293
- applied: [],
294
- plannerVersion: "2.0.0"
295
- },
296
- labels: [],
297
- createdAt: timestamp.toISOString()
298
- };
299
- try {
300
- const planner = migrations.createPlanner(familyInstance);
301
- const fromSchema = migrations.contractToSchema(fromContract, frameworkComponents);
302
- const plannerResult = planner.plan({
303
- contract: aggregate.app.contract,
304
- schema: fromSchema,
305
- policy: { allowedOperationClasses: [
306
- "additive",
307
- "widening",
308
- "destructive",
309
- "data"
310
- ] },
311
- fromContract,
312
- frameworkComponents,
313
- spaceId: aggregate.app.spaceId
314
- });
315
- if (plannerResult.kind === "failure") return notOk(errorMigrationPlanningFailed({ conflicts: plannerResult.conflicts }));
316
- let plannedOps = [];
317
- let hasPlaceholders = false;
318
- try {
319
- plannedOps = plannerResult.plan.operations;
320
- if (plannedOps.length === 0) return notOk(errorMigrationPlanningFailed({ conflicts: [{
321
- kind: "unsupportedChange",
322
- summary: "Contract changed but planner produced no operations. This indicates unsupported or ignored changes."
323
- }] }));
324
- } catch (e) {
325
- if (CliStructuredError.is(e) && e.domain === "MIG" && e.code === "2001") hasPlaceholders = true;
326
- else throw e;
467
+ async function writeDestinationEndContract(packageDir) {
468
+ if (toArtifacts !== null) {
469
+ await writeSnapshotContractArtifacts(packageDir, toArtifacts.contractJson, toArtifacts.contractDts, "end-contract");
470
+ return;
327
471
  }
328
- const migrationTsContent = plannerResult.plan.renderTypeScript();
329
- const opsForWrite = hasPlaceholders ? [] : plannedOps;
330
- const metadataWithInvariants = {
331
- ...baseMetadata,
332
- providedInvariants: deriveProvidedInvariants(opsForWrite)
333
- };
334
- await writeMigrationPackage(packageDir, {
335
- ...metadataWithInvariants,
336
- migrationHash: computeMigrationHash(metadataWithInvariants, opsForWrite)
337
- }, opsForWrite);
338
472
  const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
339
473
  await copyFilesWithRename(packageDir, [{
340
474
  sourcePath: destinationArtifacts.jsonPath,
@@ -343,6 +477,100 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
343
477
  sourcePath: destinationArtifacts.dtsPath,
344
478
  destName: "end-contract.d.ts"
345
479
  }]);
480
+ }
481
+ try {
482
+ const planner = migrations.createPlanner(familyInstance);
483
+ if (isAutoBaseline && fromHash !== null && fromContract !== null && snapshotStartContract !== null) {
484
+ const baselineTimestamp = /* @__PURE__ */ new Date();
485
+ const deltaTimestamp = new Date(baselineTimestamp.getTime() + 6e4);
486
+ const baselineDirName = formatMigrationDirName(baselineTimestamp, "baseline");
487
+ const deltaDirName = formatMigrationDirName(deltaTimestamp, options.name ?? "migration");
488
+ const baselinePackageDir = join(appMigrationsDir, baselineDirName);
489
+ const deltaPackageDir = join(appMigrationsDir, deltaDirName);
490
+ const baselineLeg = await runPlannerLeg(planner, migrations, frameworkComponents, fromContract, null, aggregate.app.spaceId);
491
+ if (!baselineLeg.ok) return notOk(baselineLeg.failure);
492
+ await writePlannedMigrationPackage(baselinePackageDir, null, fromHash, baselineTimestamp, baselineLeg.value);
493
+ await writeSnapshotContractArtifacts(baselinePackageDir, snapshotStartContract.contractJson, snapshotStartContract.contractDts, "end-contract");
494
+ if (fromHash === toStorageHash) {
495
+ const baselineOps = baselineLeg.value.hasPlaceholders ? [] : baselineLeg.value.plannedOps;
496
+ if (baselineLeg.value.hasPlaceholders) {
497
+ const baselineDir = relative(process.cwd(), baselinePackageDir);
498
+ return ok({
499
+ ok: true,
500
+ noOp: false,
501
+ from: fromHash,
502
+ to: toStorageHash,
503
+ dir: baselineDir,
504
+ baselineDir,
505
+ operations: [],
506
+ emittedExtensionDirs,
507
+ pendingPlaceholders: true,
508
+ summary: "Planned baseline with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit",
509
+ timings: { total: Date.now() - startTime }
510
+ });
511
+ }
512
+ const preview = hasOperationPreview(familyInstance) ? familyInstance.toOperationPreview(baselineOps) : void 0;
513
+ return ok({
514
+ ok: true,
515
+ noOp: false,
516
+ from: fromHash,
517
+ to: toStorageHash,
518
+ baselineDir: relative(process.cwd(), baselinePackageDir),
519
+ operations: baselineOps.map((op) => ({
520
+ id: op.id,
521
+ label: op.label,
522
+ operationClass: op.operationClass
523
+ })),
524
+ emittedExtensionDirs,
525
+ ...preview !== void 0 ? { preview } : {},
526
+ summary: buildAutoBaselinePlanSummary(0, emittedExtensionDirs.length),
527
+ timings: { total: Date.now() - startTime }
528
+ });
529
+ }
530
+ const deltaLeg = await runPlannerLeg(planner, migrations, frameworkComponents, aggregate.app.contract(), fromContract, aggregate.app.spaceId);
531
+ if (!deltaLeg.ok) return notOk(deltaLeg.failure);
532
+ await writePlannedMigrationPackage(deltaPackageDir, fromHash, toStorageHash, deltaTimestamp, deltaLeg.value);
533
+ await writeDestinationEndContract(deltaPackageDir);
534
+ await writeSnapshotStartContract(deltaPackageDir, snapshotStartContract.contractJson, snapshotStartContract.contractDts);
535
+ const deltaOps = deltaLeg.value.hasPlaceholders ? [] : deltaLeg.value.plannedOps;
536
+ if (deltaLeg.value.hasPlaceholders) return ok({
537
+ ok: true,
538
+ noOp: false,
539
+ from: fromHash,
540
+ to: toStorageHash,
541
+ dir: relative(process.cwd(), deltaPackageDir),
542
+ baselineDir: relative(process.cwd(), baselinePackageDir),
543
+ operations: [],
544
+ emittedExtensionDirs,
545
+ pendingPlaceholders: true,
546
+ summary: "Planned baseline + migration with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit",
547
+ timings: { total: Date.now() - startTime }
548
+ });
549
+ const preview = hasOperationPreview(familyInstance) ? familyInstance.toOperationPreview(deltaOps) : void 0;
550
+ return ok({
551
+ ok: true,
552
+ noOp: false,
553
+ from: fromHash,
554
+ to: toStorageHash,
555
+ dir: relative(process.cwd(), deltaPackageDir),
556
+ baselineDir: relative(process.cwd(), baselinePackageDir),
557
+ operations: deltaOps.map((op) => ({
558
+ id: op.id,
559
+ label: op.label,
560
+ operationClass: op.operationClass
561
+ })),
562
+ emittedExtensionDirs,
563
+ ...preview !== void 0 ? { preview } : {},
564
+ summary: buildAutoBaselinePlanSummary(deltaOps.length, emittedExtensionDirs.length),
565
+ timings: { total: Date.now() - startTime }
566
+ });
567
+ }
568
+ const timestamp = /* @__PURE__ */ new Date();
569
+ const packageDir = join(appMigrationsDir, formatMigrationDirName(timestamp, options.name ?? "migration"));
570
+ const deltaLeg = await runPlannerLeg(planner, migrations, frameworkComponents, aggregate.app.contract(), fromContract, aggregate.app.spaceId);
571
+ if (!deltaLeg.ok) return notOk(deltaLeg.failure);
572
+ await writePlannedMigrationPackage(packageDir, fromHash, toStorageHash, timestamp, deltaLeg.value);
573
+ await writeDestinationEndContract(packageDir);
346
574
  if (fromContractSourceDir !== null) {
347
575
  const sourceArtifacts = getEmittedArtifactPaths(join(fromContractSourceDir, "end-contract.json"));
348
576
  await copyFilesWithRename(packageDir, [{
@@ -352,9 +580,8 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
352
580
  sourcePath: sourceArtifacts.dtsPath,
353
581
  destName: "start-contract.d.ts"
354
582
  }]);
355
- }
356
- await writeMigrationTs(packageDir, migrationTsContent);
357
- if (hasPlaceholders) return ok({
583
+ } else if (snapshotStartContract !== null) await writeSnapshotStartContract(packageDir, snapshotStartContract.contractJson, snapshotStartContract.contractDts);
584
+ if (deltaLeg.value.hasPlaceholders) return ok({
358
585
  ok: true,
359
586
  noOp: false,
360
587
  from: fromHash,
@@ -366,6 +593,7 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
366
593
  summary: "Planned migration with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit",
367
594
  timings: { total: Date.now() - startTime }
368
595
  });
596
+ const plannedOps = deltaLeg.value.plannedOps;
369
597
  const preview = hasOperationPreview(familyInstance) ? familyInstance.toOperationPreview(plannedOps) : void 0;
370
598
  return ok({
371
599
  ok: true,
@@ -393,8 +621,12 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
393
621
  function createMigrationPlanCommand() {
394
622
  const command = new Command("plan");
395
623
  setCommandDescriptions(command, "Plan a migration from contract changes", "Compares the emitted contract against the latest on-disk migration state and\nproduces a new migration package with the required operations. No database\nconnection is needed — this is a fully offline operation.");
396
- setCommandExamples(command, ["prisma-next migration plan", "prisma-next migration plan --name add-users-table"]);
397
- addGlobalOptions(command).option("--config <path>", "Path to prisma-next.config.ts").option("--name <slug>", "Name slug for the migration directory", "migration").option("--from <contract>", "Starting contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)").action(async (options) => {
624
+ setCommandExamples(command, [
625
+ "prisma-next migration plan",
626
+ "prisma-next migration plan --name add-users-table",
627
+ "prisma-next migration plan --to <migration-dir>^ --name rollback"
628
+ ]);
629
+ addGlobalOptions(command).option("--config <path>", "Path to prisma-next.config.ts").option("--name <slug>", "Name slug for the migration directory", "migration").option("--from <contract>", "Starting contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)").option("--to <contract>", "Destination contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path); defaults to the emitted contract").action(async (options) => {
398
630
  const flags = parseGlobalFlagsOrExit(options);
399
631
  const startTime = Date.now();
400
632
  const ui = createTerminalUI(flags);
@@ -425,6 +657,11 @@ function buildPlanSummary(plannedOpsCount, emittedExtensionDirsCount) {
425
657
  if (emittedExtensionDirsCount === 0) return base;
426
658
  return `${base}; materialised ${emittedExtensionDirsCount} ${emittedExtensionDirsCount === 1 ? "extension-space migration" : "extension-space migrations"}`;
427
659
  }
660
+ function buildAutoBaselinePlanSummary(deltaOpsCount, emittedExtensionDirsCount) {
661
+ const base = `Planned baseline + ${deltaOpsCount} operation(s)`;
662
+ if (emittedExtensionDirsCount === 0) return base;
663
+ return `${base}; materialised ${emittedExtensionDirsCount} ${emittedExtensionDirsCount === 1 ? "extension-space migration" : "extension-space migrations"}`;
664
+ }
428
665
  function formatMigrationPlanOutput(result, flags) {
429
666
  const lines = [];
430
667
  const useColor = flags.color !== false;
@@ -476,10 +713,12 @@ function formatMigrationPlanOutput(result, flags) {
476
713
  }
477
714
  lines.push(dim_(`from: ${result.from}`));
478
715
  lines.push(dim_(`to: ${result.to}`));
716
+ if (result.baselineDir) lines.push(dim_(`Baseline → ${result.baselineDir}`));
479
717
  if (result.dir) lines.push(dim_(`App space → ${result.dir}`));
480
718
  for (const entry of result.emittedExtensionDirs) lines.push(dim_(`Extension space ${entry.spaceId} → migrations/${entry.spaceId}/${entry.dirName}`));
481
719
  lines.push("");
482
- lines.push(`Next: review ${green_(result.dir ?? "<dir>")} if needed, then run ${green_("prisma-next migrate")}.`);
720
+ const reviewTarget = result.baselineDir !== void 0 && result.dir !== void 0 ? `${result.baselineDir} and ${result.dir}` : result.baselineDir ?? result.dir ?? "<dir>";
721
+ lines.push(`Next: review ${green_(reviewTarget)} if needed, then run ${green_("prisma-next migrate")}.`);
483
722
  if (result.preview && result.preview.statements.length > 0) {
484
723
  const allSql = result.preview.statements.every((s) => s.language === "sql");
485
724
  lines.push("");
@@ -526,4 +765,4 @@ function resolveBundleByPrefix(bundles, needle) {
526
765
  //#endregion
527
766
  export { formatMigrationPlanOutput as n, resolveBundleByPrefix as r, createMigrationPlanCommand as t };
528
767
 
529
- //# sourceMappingURL=migration-plan-CFwqw3Gk.mjs.map
768
+ //# sourceMappingURL=migration-plan-9DJ7q7_z.mjs.map