prisma-next 0.10.0 → 0.11.0-dev.10

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 (120) hide show
  1. package/dist/cli-errors-Bw2GlweY.mjs +175 -0
  2. package/dist/cli-errors-Bw2GlweY.mjs.map +1 -0
  3. package/dist/cli.mjs +400 -13
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/{client-Brv4qlfB.mjs → client-UnIveZxZ.mjs} +6 -5
  6. package/dist/client-UnIveZxZ.mjs.map +1 -0
  7. package/dist/{command-helpers-D3vL5yi8.mjs → command-helpers-CRfjbZRz.mjs} +109 -12
  8. package/dist/command-helpers-CRfjbZRz.mjs.map +1 -0
  9. package/dist/commands/contract-emit.d.mts.map +1 -1
  10. package/dist/commands/contract-emit.mjs +1 -1
  11. package/dist/commands/contract-infer.mjs +1 -1
  12. package/dist/commands/db-init.d.mts.map +1 -1
  13. package/dist/commands/db-init.mjs +47 -21
  14. package/dist/commands/db-init.mjs.map +1 -1
  15. package/dist/commands/db-schema.mjs +6 -10
  16. package/dist/commands/db-schema.mjs.map +1 -1
  17. package/dist/commands/db-sign.mjs +8 -12
  18. package/dist/commands/db-sign.mjs.map +1 -1
  19. package/dist/commands/db-update.d.mts.map +1 -1
  20. package/dist/commands/db-update.mjs +47 -19
  21. package/dist/commands/db-update.mjs.map +1 -1
  22. package/dist/commands/db-verify.mjs +1 -1
  23. package/dist/commands/migrate.d.mts +5 -1
  24. package/dist/commands/migrate.d.mts.map +1 -1
  25. package/dist/commands/migrate.mjs +47 -15
  26. package/dist/commands/migrate.mjs.map +1 -1
  27. package/dist/commands/migration-check.mjs +5 -8
  28. package/dist/commands/migration-check.mjs.map +1 -1
  29. package/dist/commands/migration-graph.d.mts +1 -1
  30. package/dist/commands/migration-graph.mjs +6 -10
  31. package/dist/commands/migration-graph.mjs.map +1 -1
  32. package/dist/commands/migration-list.mjs +5 -9
  33. package/dist/commands/migration-list.mjs.map +1 -1
  34. package/dist/commands/migration-log.d.mts.map +1 -1
  35. package/dist/commands/migration-log.mjs +7 -10
  36. package/dist/commands/migration-log.mjs.map +1 -1
  37. package/dist/commands/migration-new.mjs +6 -10
  38. package/dist/commands/migration-new.mjs.map +1 -1
  39. package/dist/commands/migration-plan.d.mts +2 -1
  40. package/dist/commands/migration-plan.d.mts.map +1 -1
  41. package/dist/commands/migration-plan.mjs +1 -1
  42. package/dist/commands/migration-show.d.mts +1 -1
  43. package/dist/commands/migration-show.mjs +9 -13
  44. package/dist/commands/migration-show.mjs.map +1 -1
  45. package/dist/commands/migration-status.d.mts +1 -1
  46. package/dist/commands/migration-status.d.mts.map +1 -1
  47. package/dist/commands/migration-status.mjs +37 -15
  48. package/dist/commands/migration-status.mjs.map +1 -1
  49. package/dist/commands/ref.d.mts +1 -1
  50. package/dist/commands/ref.d.mts.map +1 -1
  51. package/dist/commands/ref.mjs +41 -25
  52. package/dist/commands/ref.mjs.map +1 -1
  53. package/dist/{contract-emit-iynA3BCA.mjs → contract-emit-C6rlsljO.mjs} +7 -6
  54. package/dist/contract-emit-C6rlsljO.mjs.map +1 -0
  55. package/dist/{contract-emit-C3STUIBg.mjs → contract-emit-mqXmapxB.mjs} +22 -20
  56. package/dist/contract-emit-mqXmapxB.mjs.map +1 -0
  57. package/dist/{contract-infer-Cnj8G1E2.mjs → contract-infer-C4jxc1aZ.mjs} +9 -14
  58. package/dist/contract-infer-C4jxc1aZ.mjs.map +1 -0
  59. package/dist/{contract-space-aggregate-loader-pAc8CDfY.mjs → contract-space-aggregate-loader-CGakRlKM.mjs} +2 -2
  60. package/dist/{contract-space-aggregate-loader-pAc8CDfY.mjs.map → contract-space-aggregate-loader-CGakRlKM.mjs.map} +1 -1
  61. package/dist/{db-verify-D7cyH_zz.mjs → db-verify-1d8tDoFN.mjs} +9 -13
  62. package/dist/db-verify-1d8tDoFN.mjs.map +1 -0
  63. package/dist/exports/control-api.d.mts +1 -1
  64. package/dist/exports/control-api.mjs +2 -2
  65. package/dist/exports/index.mjs +2 -2
  66. package/dist/exports/init-output.mjs +1 -1
  67. package/dist/{framework-components-xFLFpZUO.mjs → framework-components-Bexd0f4E.mjs} +2 -2
  68. package/dist/{framework-components-xFLFpZUO.mjs.map → framework-components-Bexd0f4E.mjs.map} +1 -1
  69. package/dist/{global-flags-DGmw6Kqg.d.mts → global-flags-CdE7M0d9.d.mts} +4 -1
  70. package/dist/global-flags-CdE7M0d9.d.mts.map +1 -0
  71. package/dist/{graph-render-eJDcLWny.mjs → graph-render-BE8vmJ_7.mjs} +1 -1
  72. package/dist/{graph-render-eJDcLWny.mjs.map → graph-render-BE8vmJ_7.mjs.map} +1 -1
  73. package/dist/{init-eh2z5Tl6.mjs → init-ByoeQphC.mjs} +528 -627
  74. package/dist/init-ByoeQphC.mjs.map +1 -0
  75. package/dist/{inspect-live-schema-CWLK_lgs.mjs → inspect-live-schema-B1Q49RF0.mjs} +4 -4
  76. package/dist/{inspect-live-schema-CWLK_lgs.mjs.map → inspect-live-schema-B1Q49RF0.mjs.map} +1 -1
  77. package/dist/{migration-command-scaffold-CmXXC1UZ.mjs → migration-command-scaffold-oY4P1Qto.mjs} +4 -4
  78. package/dist/{migration-command-scaffold-CmXXC1UZ.mjs.map → migration-command-scaffold-oY4P1Qto.mjs.map} +1 -1
  79. package/dist/{migration-plan-CHyUlBV0.mjs → migration-plan-jdAHg_gK.mjs} +349 -94
  80. package/dist/migration-plan-jdAHg_gK.mjs.map +1 -0
  81. package/dist/{migration-types-D2FW63pr.d.mts → migration-types-BXWvz12q.d.mts} +1 -1
  82. package/dist/{migration-types-D2FW63pr.d.mts.map → migration-types-BXWvz12q.d.mts.map} +1 -1
  83. package/dist/{migrations-DyUf5lTt.mjs → migrations-B7n518mT.mjs} +11 -2
  84. package/dist/migrations-B7n518mT.mjs.map +1 -0
  85. package/dist/{output-B60Gw5fu.mjs → output-CUIdfYo5.mjs} +1 -1
  86. package/dist/{output-B60Gw5fu.mjs.map → output-CUIdfYo5.mjs.map} +1 -1
  87. package/dist/quick-reference-mongo.md +1 -1
  88. package/dist/quick-reference-postgres.md +1 -1
  89. package/dist/readme-mongo.md +35 -0
  90. package/dist/readme-postgres.md +34 -0
  91. package/dist/ref-advancement-DRh5Nquq.mjs +50 -0
  92. package/dist/ref-advancement-DRh5Nquq.mjs.map +1 -0
  93. package/dist/{terminal-ui-XtOQsqe9.mjs → terminal-ui-BiB_8KNo.mjs} +131 -24
  94. package/dist/terminal-ui-BiB_8KNo.mjs.map +1 -0
  95. package/dist/{types-0aS865QN.d.mts → types-UWB2-rrw.d.mts} +12 -4
  96. package/dist/types-UWB2-rrw.d.mts.map +1 -0
  97. package/dist/{verify-D7ypCCe6.mjs → verify-C5UvbrF1.mjs} +2 -2
  98. package/dist/{verify-D7ypCCe6.mjs.map → verify-C5UvbrF1.mjs.map} +1 -1
  99. package/package.json +13 -11
  100. package/dist/cli-errors-CF60g2cG.mjs +0 -71
  101. package/dist/cli-errors-CF60g2cG.mjs.map +0 -1
  102. package/dist/cli-errors-DdcjVLJV.d.mts +0 -3
  103. package/dist/client-Brv4qlfB.mjs.map +0 -1
  104. package/dist/command-helpers-D3vL5yi8.mjs.map +0 -1
  105. package/dist/contract-emit-C3STUIBg.mjs.map +0 -1
  106. package/dist/contract-emit-iynA3BCA.mjs.map +0 -1
  107. package/dist/contract-infer-Cnj8G1E2.mjs.map +0 -1
  108. package/dist/db-verify-D7cyH_zz.mjs.map +0 -1
  109. package/dist/errors-Cw6kyTyV.mjs +0 -56
  110. package/dist/errors-Cw6kyTyV.mjs.map +0 -1
  111. package/dist/global-flags-DGmw6Kqg.d.mts.map +0 -1
  112. package/dist/helpers-eqdN8tH6.mjs +0 -25
  113. package/dist/helpers-eqdN8tH6.mjs.map +0 -1
  114. package/dist/init-eh2z5Tl6.mjs.map +0 -1
  115. package/dist/migration-plan-CHyUlBV0.mjs.map +0 -1
  116. package/dist/migrations-DyUf5lTt.mjs.map +0 -1
  117. package/dist/result-handler-Bm_6dDYg.mjs +0 -25
  118. package/dist/result-handler-Bm_6dDYg.mjs.map +0 -1
  119. package/dist/terminal-ui-XtOQsqe9.mjs.map +0 -1
  120. package/dist/types-0aS865QN.d.mts.map +0 -1
@@ -1,24 +1,24 @@
1
1
  import { t as loadConfig } from "./config-loader-B6sJjXTv.mjs";
2
- import { _ as errorUnexpected, a as errorContractValidationFailed, f as errorMigrationPlanningFailed, h as errorTargetMigrationNotSupported, l as errorFileNotFound, t as CliStructuredError, v as mapMigrationToolsError, y as mapRefResolutionError } from "./cli-errors-CF60g2cG.mjs";
3
- import { t as assertFrameworkComponentsCompatible } from "./framework-components-xFLFpZUO.mjs";
4
- import { _ as parseGlobalFlags, a as loadMigrationPackages, b as formatStyledHeader, c as resolveContractPath, d as setCommandDescriptions, f as setCommandExamples, i as getTargetMigrations, l as resolveMigrationPaths, t as addGlobalOptions } from "./command-helpers-D3vL5yi8.mjs";
5
- import { t as handleResult } from "./result-handler-Bm_6dDYg.mjs";
6
- import { t as TerminalUI } from "./terminal-ui-XtOQsqe9.mjs";
7
- import { n as toExtensionInputs, t as buildContractSpaceAggregate } from "./contract-space-aggregate-loader-pAc8CDfY.mjs";
2
+ import { C as errorSnapshotMissing, D as mapMigrationToolsError, E as errorUnexpected, O as mapRefResolutionError, _ as errorPlanForgotTheFlag, a as errorContractValidationFailed, l as errorFileNotFound, m as errorMigrationPlanningFailed, t as CliStructuredError, w as errorTargetMigrationNotSupported } from "./cli-errors-Bw2GlweY.mjs";
3
+ import { t as assertFrameworkComponentsCompatible } from "./framework-components-Bexd0f4E.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-CRfjbZRz.mjs";
6
+ import { n as toExtensionInputs, t as buildContractSpaceAggregate } from "./contract-space-aggregate-loader-CGakRlKM.mjs";
8
7
  import { Command } from "commander";
9
8
  import { getEmittedArtifactPaths } from "@prisma-next/emitter";
10
9
  import { notOk, ok } from "@prisma-next/utils/result";
11
10
  import { join, relative } from "pathe";
12
- import { readFile } from "node:fs/promises";
11
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
13
12
  import { createControlStack, hasOperationPreview } from "@prisma-next/framework-components/control";
14
13
  import { copyFilesWithRename, formatMigrationDirName, materialiseExtensionMigrationPackageIfMissing, writeMigrationPackage } from "@prisma-next/migration-tools/io";
15
- import { findLatestMigration } from "@prisma-next/migration-tools/migration-graph";
14
+ import { assertHashIsGraphNode, findLatestMigration, isGraphNode } from "@prisma-next/migration-tools/migration-graph";
16
15
  import { emitContractSpaceArtefacts, planAllSpaces, readContractSpaceHeadRef, spaceMigrationDirectory } from "@prisma-next/migration-tools/spaces";
17
16
  import { MigrationToolsError } from "@prisma-next/migration-tools/errors";
17
+ import { readRefSnapshot, readRefs } from "@prisma-next/migration-tools/refs";
18
18
  import { parseContractRef } from "@prisma-next/migration-tools/ref-resolution";
19
- import { readRefs } from "@prisma-next/migration-tools/refs";
20
19
  import { computeMigrationHash } from "@prisma-next/migration-tools/hash";
21
20
  import { writeMigrationTs } from "@prisma-next/migration-tools/migration-ts";
21
+ import { canonicalizeJson } from "@prisma-next/framework-components/utils";
22
22
  import { deriveProvidedInvariants } from "@prisma-next/migration-tools/invariants";
23
23
  //#region src/utils/contract-space-seed-phase.ts
24
24
  /**
@@ -110,6 +110,135 @@ function buildPlaceholderContractDts(spaceId) {
110
110
  ].join("\n");
111
111
  }
112
112
  //#endregion
113
+ //#region src/utils/plan-resolution.ts
114
+ const FULL_HASH_PATTERN = /^sha256:([0-9a-f]{64}|empty)$/;
115
+ function looksLikeFullHash(input) {
116
+ return FULL_HASH_PATTERN.test(input);
117
+ }
118
+ function graphIsEmpty(bundles) {
119
+ return bundles.length === 0;
120
+ }
121
+ function getReachableRefs(refs, graph) {
122
+ return Object.entries(refs).flatMap(([name, entry]) => entry && isGraphNode(entry.hash, graph) ? [{
123
+ name,
124
+ hash: entry.hash
125
+ }] : []).sort((a, b) => a.name.localeCompare(b.name));
126
+ }
127
+ function assertFromIsGraphNode(fromHash, graph, refs, graphTipHash) {
128
+ try {
129
+ assertHashIsGraphNode(fromHash, graph);
130
+ } catch (error) {
131
+ if (MigrationToolsError.is(error) && error.code === "MIGRATION.HASH_NOT_IN_GRAPH") throw errorPlanForgotTheFlag(fromHash, getReachableRefs(refs, graph), graphTipHash);
132
+ throw error;
133
+ }
134
+ }
135
+ async function deserializeSnapshotContract(familyInstance, contract) {
136
+ try {
137
+ return ok(familyInstance.deserializeContract(contract));
138
+ } catch (error) {
139
+ if (CliStructuredError.is(error)) return notOk(error);
140
+ return notOk(errorContractValidationFailed(`Ref snapshot contract failed to deserialize: ${error instanceof Error ? error.message : String(error)}`, { where: { path: "ref-snapshot" } }));
141
+ }
142
+ }
143
+ async function resolveGraphNodeFromBundle(fromHash, bundles, readBundleEndContract, explicitFromLabel) {
144
+ const matchingBundle = bundles.find((pkg) => pkg.metadata.to === fromHash);
145
+ if (!matchingBundle) return notOk(errorUnexpected(explicitFromLabel ? `No migration bundle found for --from "${explicitFromLabel}" (resolved hash: ${fromHash})` : `No migration bundle found for graph node ${fromHash}`, {
146
+ why: `The hash ${fromHash} is a graph node but no on-disk migration package has an end-contract hash matching it.`,
147
+ fix: "Provide a ref or hash that corresponds to an existing migration package, or run `migration list` to see available migrations."
148
+ }));
149
+ try {
150
+ return ok({
151
+ kind: "graph-node",
152
+ fromHash,
153
+ fromContract: await readBundleEndContract(matchingBundle.dirPath),
154
+ sourceDir: matchingBundle.dirPath
155
+ });
156
+ } catch (error) {
157
+ if (CliStructuredError.is(error)) return notOk(error);
158
+ throw error;
159
+ }
160
+ }
161
+ async function resolveFromRefName(refName, fromHash, input, refs) {
162
+ const { refsDir, bundles, graph, familyInstance, readBundleEndContract } = input;
163
+ const empty = graphIsEmpty(bundles);
164
+ const graphTip = findLatestMigration(graph)?.to ?? null;
165
+ let snapshot;
166
+ try {
167
+ snapshot = await readRefSnapshot(refsDir, refName);
168
+ } catch (error) {
169
+ if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
170
+ throw error;
171
+ }
172
+ if (snapshot) {
173
+ const contractResult = await deserializeSnapshotContract(familyInstance, snapshot.contract);
174
+ if (!contractResult.ok) return contractResult;
175
+ const fromContract = contractResult.value;
176
+ const { contractDts, contract: contractJson } = snapshot;
177
+ if (empty) return ok({
178
+ kind: "auto-baseline",
179
+ fromHash,
180
+ fromContract,
181
+ contractDts,
182
+ contractJson
183
+ });
184
+ try {
185
+ assertFromIsGraphNode(fromHash, graph, refs, graphTip);
186
+ } catch (error) {
187
+ if (CliStructuredError.is(error)) return notOk(error);
188
+ throw error;
189
+ }
190
+ return ok({
191
+ kind: "snapshot",
192
+ fromHash,
193
+ fromContract,
194
+ contractDts,
195
+ contractJson
196
+ });
197
+ }
198
+ if (isGraphNode(fromHash, graph)) return resolveGraphNodeFromBundle(fromHash, bundles, readBundleEndContract);
199
+ return notOk(errorSnapshotMissing(refName));
200
+ }
201
+ async function resolveFromHashProvenance(fromHash, input, _refs, explicitFromLabel) {
202
+ const { bundles, graph } = input;
203
+ if (isGraphNode(fromHash, graph)) return resolveGraphNodeFromBundle(fromHash, bundles, input.readBundleEndContract, explicitFromLabel);
204
+ throw new Error(`resolveFromHashProvenance: non-graph-node hash ${fromHash} should be refused via looksLikeFullHash before this helper is called`);
205
+ }
206
+ async function resolveFromForPlan(input) {
207
+ const { optionsFrom, refsDir, graph } = input;
208
+ let refs;
209
+ try {
210
+ refs = await readRefs(refsDir);
211
+ } catch (error) {
212
+ if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
213
+ throw error;
214
+ }
215
+ if (optionsFrom === void 0) {
216
+ const dbRef = refs["db"];
217
+ if (!dbRef) return ok({
218
+ kind: "greenfield",
219
+ fromHash: null,
220
+ fromContract: null
221
+ });
222
+ return resolveFromRefName("db", dbRef.hash, input, refs);
223
+ }
224
+ const refResult = parseContractRef(optionsFrom, {
225
+ graph,
226
+ refs
227
+ });
228
+ if (!refResult.ok) {
229
+ if (looksLikeFullHash(optionsFrom)) {
230
+ const empty = graphIsEmpty(input.bundles);
231
+ const graphTip = findLatestMigration(graph)?.to ?? null;
232
+ if (empty) return notOk(errorSnapshotMissing(optionsFrom, { viaRef: false }));
233
+ return notOk(errorPlanForgotTheFlag(optionsFrom, getReachableRefs(refs, graph), graphTip));
234
+ }
235
+ return notOk(mapRefResolutionError(refResult.failure));
236
+ }
237
+ const { hash: fromHash, provenance } = refResult.value;
238
+ if (provenance.kind === "ref") return resolveFromRefName(provenance.refName, fromHash, input, refs);
239
+ return resolveFromHashProvenance(fromHash, input, refs, optionsFrom);
240
+ }
241
+ //#endregion
113
242
  //#region src/commands/migration-plan.ts
114
243
  /**
115
244
  * Load a predecessor migration's destination contract from its sibling
@@ -149,6 +278,70 @@ async function readPredecessorEndContract(migrationDir, familyInstance) {
149
278
  throw errorContractValidationFailed(`Predecessor contract at ${path} failed to deserialize: ${error instanceof Error ? error.message : String(error)}`, { where: { path } });
150
279
  }
151
280
  }
281
+ async function writeSnapshotContractArtifacts(packageDir, contractJson, contractDts, artifactBasename) {
282
+ await mkdir(packageDir, { recursive: true });
283
+ const jsonContent = `${canonicalizeJson(contractJson)}\n`;
284
+ const dtsContent = contractDts.endsWith("\n") ? contractDts : `${contractDts}\n`;
285
+ await writeFile(join(packageDir, `${artifactBasename}.json`), jsonContent);
286
+ await writeFile(join(packageDir, `${artifactBasename}.d.ts`), dtsContent);
287
+ }
288
+ async function writeSnapshotStartContract(packageDir, contractJson, contractDts) {
289
+ await writeSnapshotContractArtifacts(packageDir, contractJson, contractDts, "start-contract");
290
+ }
291
+ async function runPlannerLeg(planner, migrations, frameworkComponents, contract, fromContract, spaceId) {
292
+ const fromSchema = migrations.contractToSchema(fromContract, frameworkComponents);
293
+ const plannerResult = planner.plan({
294
+ contract,
295
+ schema: fromSchema,
296
+ policy: { allowedOperationClasses: [
297
+ "additive",
298
+ "widening",
299
+ "destructive",
300
+ "data"
301
+ ] },
302
+ fromContract,
303
+ frameworkComponents,
304
+ spaceId
305
+ });
306
+ if (plannerResult.kind === "failure") return notOk(errorMigrationPlanningFailed({ conflicts: plannerResult.conflicts }));
307
+ let plannedOps = [];
308
+ let hasPlaceholders = false;
309
+ try {
310
+ plannedOps = plannerResult.plan.operations;
311
+ if (plannedOps.length === 0) return notOk(errorMigrationPlanningFailed({ conflicts: [{
312
+ kind: "unsupportedChange",
313
+ summary: "Contract changed but planner produced no operations. This indicates unsupported or ignored changes."
314
+ }] }));
315
+ } catch (e) {
316
+ if (CliStructuredError.is(e) && e.domain === "MIG" && e.code === "2001") hasPlaceholders = true;
317
+ else throw e;
318
+ }
319
+ return ok({
320
+ plannedOps,
321
+ migrationTsContent: plannerResult.plan.renderTypeScript(),
322
+ hasPlaceholders
323
+ });
324
+ }
325
+ async function writePlannedMigrationPackage(packageDir, fromHash, toHash, createdAt, leg) {
326
+ const opsForWrite = leg.hasPlaceholders ? [] : leg.plannedOps;
327
+ const metadataWithInvariants = {
328
+ from: fromHash,
329
+ to: toHash,
330
+ hints: {
331
+ used: [],
332
+ applied: [],
333
+ plannerVersion: "2.0.0"
334
+ },
335
+ labels: [],
336
+ providedInvariants: deriveProvidedInvariants(opsForWrite),
337
+ createdAt: createdAt.toISOString()
338
+ };
339
+ await writeMigrationPackage(packageDir, {
340
+ ...metadataWithInvariants,
341
+ migrationHash: computeMigrationHash(metadataWithInvariants, opsForWrite)
342
+ }, opsForWrite);
343
+ await writeMigrationTs(packageDir, leg.migrationTsContent);
344
+ }
152
345
  async function executeMigrationPlanCommand(options, flags, ui, startTime) {
153
346
  const config = await loadConfig(options.config);
154
347
  const { configPath, migrationsDir, appMigrationsDir, appMigrationsRelative } = resolveMigrationPaths(options.config, config);
@@ -207,36 +400,47 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
207
400
  const rawStorageHash = toContract.storage?.storageHash;
208
401
  if (typeof rawStorageHash !== "string") return notOk(errorContractValidationFailed("Contract is missing storageHash", { where: { path: contractPathAbsolute } }));
209
402
  const toStorageHash = rawStorageHash;
403
+ const { refsDir } = resolveMigrationPaths(options.config, config);
210
404
  let fromContract = null;
211
405
  let fromHash = null;
212
406
  let fromContractSourceDir = null;
407
+ let snapshotStartContract = null;
408
+ let isAutoBaseline = false;
213
409
  try {
214
410
  const { bundles, graph } = await loadMigrationPackages(appMigrationsDir);
215
- if (options.from) {
216
- const refs = await readRefs(resolveMigrationPaths(options.config, config).refsDir);
217
- const refResult = parseContractRef(options.from, {
218
- graph,
219
- refs
220
- });
221
- if (!refResult.ok) return notOk(mapRefResolutionError(refResult.failure));
222
- fromHash = refResult.value.hash;
223
- const matchingBundle = bundles.find((p) => p.metadata.to === fromHash);
224
- if (!matchingBundle) return notOk(errorUnexpected(`No migration bundle found for --from "${options.from}" (resolved hash: ${fromHash})`, {
225
- why: `The ref resolved successfully but no on-disk migration package has an end-contract hash matching ${fromHash}.`,
226
- fix: "Provide a ref or hash that corresponds to an existing migration package, or run `migration list` to see available migrations."
227
- }));
228
- fromContractSourceDir = matchingBundle.dirPath;
229
- fromContract = await readPredecessorEndContract(fromContractSourceDir, familyInstance);
230
- } else {
231
- const latestMigration = findLatestMigration(graph);
232
- if (latestMigration) {
233
- fromHash = latestMigration.to;
234
- const leafPkg = bundles.find((p) => p.metadata.migrationHash === latestMigration.migrationHash);
235
- if (leafPkg) {
236
- fromContractSourceDir = leafPkg.dirPath;
237
- fromContract = await readPredecessorEndContract(fromContractSourceDir, familyInstance);
238
- }
239
- }
411
+ const resolutionResult = await resolveFromForPlan({
412
+ optionsFrom: options.from,
413
+ refsDir,
414
+ bundles,
415
+ graph,
416
+ familyInstance,
417
+ readBundleEndContract: (migrationDir) => readPredecessorEndContract(migrationDir, familyInstance)
418
+ });
419
+ if (!resolutionResult.ok) return notOk(resolutionResult.failure);
420
+ switch (resolutionResult.value.kind) {
421
+ case "greenfield": break;
422
+ case "graph-node":
423
+ fromHash = resolutionResult.value.fromHash;
424
+ fromContract = resolutionResult.value.fromContract;
425
+ fromContractSourceDir = resolutionResult.value.sourceDir;
426
+ break;
427
+ case "snapshot":
428
+ fromHash = resolutionResult.value.fromHash;
429
+ fromContract = resolutionResult.value.fromContract;
430
+ snapshotStartContract = {
431
+ contractJson: resolutionResult.value.contractJson,
432
+ contractDts: resolutionResult.value.contractDts
433
+ };
434
+ break;
435
+ case "auto-baseline":
436
+ fromHash = resolutionResult.value.fromHash;
437
+ fromContract = resolutionResult.value.fromContract;
438
+ snapshotStartContract = {
439
+ contractJson: resolutionResult.value.contractJson,
440
+ contractDts: resolutionResult.value.contractDts
441
+ };
442
+ isAutoBaseline = true;
443
+ break;
240
444
  }
241
445
  } catch (error) {
242
446
  if (MigrationToolsError.is(error)) return notOk(mapMigrationToolsError(error));
@@ -258,7 +462,7 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
258
462
  spaceId: r.spaceId,
259
463
  dirName
260
464
  })));
261
- if (fromHash === toStorageHash) return ok({
465
+ if (fromHash === toStorageHash && !isAutoBaseline) return ok({
262
466
  ok: true,
263
467
  noOp: true,
264
468
  from: fromHash,
@@ -284,58 +488,105 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
284
488
  config.adapter,
285
489
  ...config.extensionPacks ?? []
286
490
  ]);
287
- const timestamp = /* @__PURE__ */ new Date();
288
- const packageDir = join(appMigrationsDir, formatMigrationDirName(timestamp, options.name ?? "migration"));
289
- const baseMetadata = {
290
- from: fromHash,
291
- to: toStorageHash,
292
- hints: {
293
- used: [],
294
- applied: [],
295
- plannerVersion: "2.0.0"
296
- },
297
- labels: [],
298
- createdAt: timestamp.toISOString()
299
- };
300
491
  try {
301
492
  const planner = migrations.createPlanner(familyInstance);
302
- const fromSchema = migrations.contractToSchema(fromContract, frameworkComponents);
303
- const plannerResult = planner.plan({
304
- contract: aggregate.app.contract,
305
- schema: fromSchema,
306
- policy: { allowedOperationClasses: [
307
- "additive",
308
- "widening",
309
- "destructive",
310
- "data"
311
- ] },
312
- fromContract,
313
- frameworkComponents,
314
- spaceId: aggregate.app.spaceId
315
- });
316
- if (plannerResult.kind === "failure") return notOk(errorMigrationPlanningFailed({ conflicts: plannerResult.conflicts }));
317
- let plannedOps = [];
318
- let hasPlaceholders = false;
319
- try {
320
- plannedOps = plannerResult.plan.operations;
321
- if (plannedOps.length === 0) return notOk(errorMigrationPlanningFailed({ conflicts: [{
322
- kind: "unsupportedChange",
323
- summary: "Contract changed but planner produced no operations. This indicates unsupported or ignored changes."
324
- }] }));
325
- } catch (e) {
326
- if (CliStructuredError.is(e) && e.domain === "MIG" && e.code === "2001") hasPlaceholders = true;
327
- else throw e;
493
+ if (isAutoBaseline && fromHash !== null && fromContract !== null && snapshotStartContract !== null) {
494
+ const baselineTimestamp = /* @__PURE__ */ new Date();
495
+ const deltaTimestamp = new Date(baselineTimestamp.getTime() + 6e4);
496
+ const baselineDirName = formatMigrationDirName(baselineTimestamp, "baseline");
497
+ const deltaDirName = formatMigrationDirName(deltaTimestamp, options.name ?? "migration");
498
+ const baselinePackageDir = join(appMigrationsDir, baselineDirName);
499
+ const deltaPackageDir = join(appMigrationsDir, deltaDirName);
500
+ const baselineLeg = await runPlannerLeg(planner, migrations, frameworkComponents, fromContract, null, aggregate.app.spaceId);
501
+ if (!baselineLeg.ok) return notOk(baselineLeg.failure);
502
+ await writePlannedMigrationPackage(baselinePackageDir, null, fromHash, baselineTimestamp, baselineLeg.value);
503
+ await writeSnapshotContractArtifacts(baselinePackageDir, snapshotStartContract.contractJson, snapshotStartContract.contractDts, "end-contract");
504
+ if (fromHash === toStorageHash) {
505
+ const baselineOps = baselineLeg.value.hasPlaceholders ? [] : baselineLeg.value.plannedOps;
506
+ if (baselineLeg.value.hasPlaceholders) {
507
+ const baselineDir = relative(process.cwd(), baselinePackageDir);
508
+ return ok({
509
+ ok: true,
510
+ noOp: false,
511
+ from: fromHash,
512
+ to: toStorageHash,
513
+ dir: baselineDir,
514
+ baselineDir,
515
+ operations: [],
516
+ emittedExtensionDirs,
517
+ pendingPlaceholders: true,
518
+ summary: "Planned baseline with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit",
519
+ timings: { total: Date.now() - startTime }
520
+ });
521
+ }
522
+ const preview = hasOperationPreview(familyInstance) ? familyInstance.toOperationPreview(baselineOps) : void 0;
523
+ return ok({
524
+ ok: true,
525
+ noOp: false,
526
+ from: fromHash,
527
+ to: toStorageHash,
528
+ baselineDir: relative(process.cwd(), baselinePackageDir),
529
+ operations: baselineOps.map((op) => ({
530
+ id: op.id,
531
+ label: op.label,
532
+ operationClass: op.operationClass
533
+ })),
534
+ emittedExtensionDirs,
535
+ ...preview !== void 0 ? { preview } : {},
536
+ summary: buildAutoBaselinePlanSummary(0, emittedExtensionDirs.length),
537
+ timings: { total: Date.now() - startTime }
538
+ });
539
+ }
540
+ const deltaLeg = await runPlannerLeg(planner, migrations, frameworkComponents, aggregate.app.contract, fromContract, aggregate.app.spaceId);
541
+ if (!deltaLeg.ok) return notOk(deltaLeg.failure);
542
+ await writePlannedMigrationPackage(deltaPackageDir, fromHash, toStorageHash, deltaTimestamp, deltaLeg.value);
543
+ const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
544
+ await copyFilesWithRename(deltaPackageDir, [{
545
+ sourcePath: destinationArtifacts.jsonPath,
546
+ destName: "end-contract.json"
547
+ }, {
548
+ sourcePath: destinationArtifacts.dtsPath,
549
+ destName: "end-contract.d.ts"
550
+ }]);
551
+ await writeSnapshotStartContract(deltaPackageDir, snapshotStartContract.contractJson, snapshotStartContract.contractDts);
552
+ const deltaOps = deltaLeg.value.hasPlaceholders ? [] : deltaLeg.value.plannedOps;
553
+ if (deltaLeg.value.hasPlaceholders) return ok({
554
+ ok: true,
555
+ noOp: false,
556
+ from: fromHash,
557
+ to: toStorageHash,
558
+ dir: relative(process.cwd(), deltaPackageDir),
559
+ baselineDir: relative(process.cwd(), baselinePackageDir),
560
+ operations: [],
561
+ emittedExtensionDirs,
562
+ pendingPlaceholders: true,
563
+ summary: "Planned baseline + migration with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit",
564
+ timings: { total: Date.now() - startTime }
565
+ });
566
+ const preview = hasOperationPreview(familyInstance) ? familyInstance.toOperationPreview(deltaOps) : void 0;
567
+ return ok({
568
+ ok: true,
569
+ noOp: false,
570
+ from: fromHash,
571
+ to: toStorageHash,
572
+ dir: relative(process.cwd(), deltaPackageDir),
573
+ baselineDir: relative(process.cwd(), baselinePackageDir),
574
+ operations: deltaOps.map((op) => ({
575
+ id: op.id,
576
+ label: op.label,
577
+ operationClass: op.operationClass
578
+ })),
579
+ emittedExtensionDirs,
580
+ ...preview !== void 0 ? { preview } : {},
581
+ summary: buildAutoBaselinePlanSummary(deltaOps.length, emittedExtensionDirs.length),
582
+ timings: { total: Date.now() - startTime }
583
+ });
328
584
  }
329
- const migrationTsContent = plannerResult.plan.renderTypeScript();
330
- const opsForWrite = hasPlaceholders ? [] : plannedOps;
331
- const metadataWithInvariants = {
332
- ...baseMetadata,
333
- providedInvariants: deriveProvidedInvariants(opsForWrite)
334
- };
335
- await writeMigrationPackage(packageDir, {
336
- ...metadataWithInvariants,
337
- migrationHash: computeMigrationHash(metadataWithInvariants, opsForWrite)
338
- }, opsForWrite);
585
+ const timestamp = /* @__PURE__ */ new Date();
586
+ const packageDir = join(appMigrationsDir, formatMigrationDirName(timestamp, options.name ?? "migration"));
587
+ const deltaLeg = await runPlannerLeg(planner, migrations, frameworkComponents, aggregate.app.contract, fromContract, aggregate.app.spaceId);
588
+ if (!deltaLeg.ok) return notOk(deltaLeg.failure);
589
+ await writePlannedMigrationPackage(packageDir, fromHash, toStorageHash, timestamp, deltaLeg.value);
339
590
  const destinationArtifacts = getEmittedArtifactPaths(contractPathAbsolute);
340
591
  await copyFilesWithRename(packageDir, [{
341
592
  sourcePath: destinationArtifacts.jsonPath,
@@ -353,9 +604,8 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
353
604
  sourcePath: sourceArtifacts.dtsPath,
354
605
  destName: "start-contract.d.ts"
355
606
  }]);
356
- }
357
- await writeMigrationTs(packageDir, migrationTsContent);
358
- if (hasPlaceholders) return ok({
607
+ } else if (snapshotStartContract !== null) await writeSnapshotStartContract(packageDir, snapshotStartContract.contractJson, snapshotStartContract.contractDts);
608
+ if (deltaLeg.value.hasPlaceholders) return ok({
359
609
  ok: true,
360
610
  noOp: false,
361
611
  from: fromHash,
@@ -367,6 +617,7 @@ async function executeMigrationPlanCommand(options, flags, ui, startTime) {
367
617
  summary: "Planned migration with placeholder(s) — edit migration.ts then run `node migration.ts` to self-emit",
368
618
  timings: { total: Date.now() - startTime }
369
619
  });
620
+ const plannedOps = deltaLeg.value.plannedOps;
370
621
  const preview = hasOperationPreview(familyInstance) ? familyInstance.toOperationPreview(plannedOps) : void 0;
371
622
  return ok({
372
623
  ok: true,
@@ -396,12 +647,9 @@ function createMigrationPlanCommand() {
396
647
  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.");
397
648
  setCommandExamples(command, ["prisma-next migration plan", "prisma-next migration plan --name add-users-table"]);
398
649
  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) => {
399
- const flags = parseGlobalFlags(options);
650
+ const flags = parseGlobalFlagsOrExit(options);
400
651
  const startTime = Date.now();
401
- const ui = new TerminalUI({
402
- color: flags.color,
403
- interactive: flags.interactive
404
- });
652
+ const ui = createTerminalUI(flags);
405
653
  const exitCode = handleResult(await executeMigrationPlanCommand(options, flags, ui, startTime), flags, ui, (planResult) => {
406
654
  if (flags.json) ui.output(JSON.stringify(planResult, null, 2));
407
655
  else if (!flags.quiet) ui.log(formatMigrationPlanOutput(planResult, flags));
@@ -429,6 +677,11 @@ function buildPlanSummary(plannedOpsCount, emittedExtensionDirsCount) {
429
677
  if (emittedExtensionDirsCount === 0) return base;
430
678
  return `${base}; materialised ${emittedExtensionDirsCount} ${emittedExtensionDirsCount === 1 ? "extension-space migration" : "extension-space migrations"}`;
431
679
  }
680
+ function buildAutoBaselinePlanSummary(deltaOpsCount, emittedExtensionDirsCount) {
681
+ const base = `Planned baseline + ${deltaOpsCount} operation(s)`;
682
+ if (emittedExtensionDirsCount === 0) return base;
683
+ return `${base}; materialised ${emittedExtensionDirsCount} ${emittedExtensionDirsCount === 1 ? "extension-space migration" : "extension-space migrations"}`;
684
+ }
432
685
  function formatMigrationPlanOutput(result, flags) {
433
686
  const lines = [];
434
687
  const useColor = flags.color !== false;
@@ -480,10 +733,12 @@ function formatMigrationPlanOutput(result, flags) {
480
733
  }
481
734
  lines.push(dim_(`from: ${result.from}`));
482
735
  lines.push(dim_(`to: ${result.to}`));
736
+ if (result.baselineDir) lines.push(dim_(`Baseline → ${result.baselineDir}`));
483
737
  if (result.dir) lines.push(dim_(`App space → ${result.dir}`));
484
738
  for (const entry of result.emittedExtensionDirs) lines.push(dim_(`Extension space ${entry.spaceId} → migrations/${entry.spaceId}/${entry.dirName}`));
485
739
  lines.push("");
486
- lines.push(`Next: review ${green_(result.dir ?? "<dir>")} if needed, then run ${green_("prisma-next migrate")}.`);
740
+ const reviewTarget = result.baselineDir !== void 0 && result.dir !== void 0 ? `${result.baselineDir} and ${result.dir}` : result.baselineDir ?? result.dir ?? "<dir>";
741
+ lines.push(`Next: review ${green_(reviewTarget)} if needed, then run ${green_("prisma-next migrate")}.`);
487
742
  if (result.preview && result.preview.statements.length > 0) {
488
743
  const allSql = result.preview.statements.every((s) => s.language === "sql");
489
744
  lines.push("");
@@ -530,4 +785,4 @@ function resolveBundleByPrefix(bundles, needle) {
530
785
  //#endregion
531
786
  export { formatMigrationPlanOutput as n, resolveBundleByPrefix as r, createMigrationPlanCommand as t };
532
787
 
533
- //# sourceMappingURL=migration-plan-CHyUlBV0.mjs.map
788
+ //# sourceMappingURL=migration-plan-jdAHg_gK.mjs.map