@prisma-next/cli 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/README.md +13 -9
  2. package/dist/cli.mjs +259 -12
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/{client-oXO2WCPD.mjs → client-KgJorIvG.mjs} +72 -60
  5. package/dist/client-KgJorIvG.mjs.map +1 -0
  6. package/dist/{command-helpers-BSb0tRC8.mjs → command-helpers-Bbw1GbwL.mjs} +646 -46
  7. package/dist/command-helpers-Bbw1GbwL.mjs.map +1 -0
  8. package/dist/commands/contract-emit.d.mts.map +1 -1
  9. package/dist/commands/contract-emit.mjs +1 -1
  10. package/dist/commands/contract-infer.d.mts.map +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 +32 -7
  14. package/dist/commands/db-init.mjs.map +1 -1
  15. package/dist/commands/db-schema.d.mts.map +1 -1
  16. package/dist/commands/db-schema.mjs +3 -4
  17. package/dist/commands/db-schema.mjs.map +1 -1
  18. package/dist/commands/db-sign.d.mts.map +1 -1
  19. package/dist/commands/db-sign.mjs +12 -10
  20. package/dist/commands/db-sign.mjs.map +1 -1
  21. package/dist/commands/db-update.d.mts.map +1 -1
  22. package/dist/commands/db-update.mjs +41 -11
  23. package/dist/commands/db-update.mjs.map +1 -1
  24. package/dist/commands/db-verify.d.mts.map +1 -1
  25. package/dist/commands/db-verify.mjs +1 -1
  26. package/dist/commands/migrate.d.mts +6 -2
  27. package/dist/commands/migrate.d.mts.map +1 -1
  28. package/dist/commands/migrate.mjs +75 -40
  29. package/dist/commands/migrate.mjs.map +1 -1
  30. package/dist/commands/migration-check.d.mts +4 -3
  31. package/dist/commands/migration-check.d.mts.map +1 -1
  32. package/dist/commands/migration-check.mjs +1 -280
  33. package/dist/commands/migration-graph.d.mts +13 -2
  34. package/dist/commands/migration-graph.d.mts.map +1 -1
  35. package/dist/commands/migration-graph.mjs +2 -137
  36. package/dist/commands/migration-list.d.mts +64 -4
  37. package/dist/commands/migration-list.d.mts.map +1 -1
  38. package/dist/commands/migration-list.mjs +143 -56
  39. package/dist/commands/migration-list.mjs.map +1 -1
  40. package/dist/commands/migration-log.d.mts +10 -1
  41. package/dist/commands/migration-log.d.mts.map +1 -1
  42. package/dist/commands/migration-log.mjs +10 -15
  43. package/dist/commands/migration-log.mjs.map +1 -1
  44. package/dist/commands/migration-new.d.mts.map +1 -1
  45. package/dist/commands/migration-new.mjs +32 -38
  46. package/dist/commands/migration-new.mjs.map +1 -1
  47. package/dist/commands/migration-plan.d.mts +3 -2
  48. package/dist/commands/migration-plan.d.mts.map +1 -1
  49. package/dist/commands/migration-plan.mjs +1 -1
  50. package/dist/commands/migration-show.d.mts +4 -55
  51. package/dist/commands/migration-show.d.mts.map +1 -1
  52. package/dist/commands/migration-show.mjs +61 -153
  53. package/dist/commands/migration-show.mjs.map +1 -1
  54. package/dist/commands/migration-status.d.mts +12 -49
  55. package/dist/commands/migration-status.d.mts.map +1 -1
  56. package/dist/commands/migration-status.mjs +85 -81
  57. package/dist/commands/migration-status.mjs.map +1 -1
  58. package/dist/commands/ref.d.mts +1 -1
  59. package/dist/commands/ref.d.mts.map +1 -1
  60. package/dist/commands/ref.mjs +38 -10
  61. package/dist/commands/ref.mjs.map +1 -1
  62. package/dist/config-loader-B6sJjXTv.mjs.map +1 -1
  63. package/dist/config-loader.d.mts.map +1 -1
  64. package/dist/contract-at-errors-BxP-TOMl.mjs +42 -0
  65. package/dist/contract-at-errors-BxP-TOMl.mjs.map +1 -0
  66. package/dist/{contract-emit-bcrpT-wD.mjs → contract-emit-D-4jrNve.mjs} +25 -10
  67. package/dist/contract-emit-D-4jrNve.mjs.map +1 -0
  68. package/dist/{contract-emit-r4y8Zhf1.mjs → contract-emit-DxcGl4Uq.mjs} +19 -14
  69. package/dist/contract-emit-DxcGl4Uq.mjs.map +1 -0
  70. package/dist/{contract-enrichment-Dani0mMW.mjs → contract-enrichment-a0V5Y_mL.mjs} +4 -25
  71. package/dist/contract-enrichment-a0V5Y_mL.mjs.map +1 -0
  72. package/dist/{contract-infer-BmySmqVT.mjs → contract-infer-D8uEbJuu.mjs} +4 -5
  73. package/dist/{contract-infer-BmySmqVT.mjs.map → contract-infer-D8uEbJuu.mjs.map} +1 -1
  74. package/dist/contract-space-aggregate-loader-DvZwdkrr.mjs +247 -0
  75. package/dist/contract-space-aggregate-loader-DvZwdkrr.mjs.map +1 -0
  76. package/dist/{db-verify-BClPs3ph.mjs → db-verify-v_vUKXTU.mjs} +5 -7
  77. package/dist/{db-verify-BClPs3ph.mjs.map → db-verify-v_vUKXTU.mjs.map} +1 -1
  78. package/dist/exports/control-api.d.mts +3 -3
  79. package/dist/exports/control-api.d.mts.map +1 -1
  80. package/dist/exports/control-api.mjs +3 -3
  81. package/dist/exports/index.d.mts.map +1 -1
  82. package/dist/exports/index.mjs +1 -1
  83. package/dist/exports/index.mjs.map +1 -1
  84. package/dist/exports/init-output.d.mts.map +1 -1
  85. package/dist/exports/init-output.mjs +1 -1
  86. package/dist/extension-pack-inputs-IDvjRCi3.mjs +62 -0
  87. package/dist/extension-pack-inputs-IDvjRCi3.mjs.map +1 -0
  88. package/dist/{framework-components-65gOHkHB.mjs → framework-components-fYXjz_in.mjs} +2 -2
  89. package/dist/{framework-components-65gOHkHB.mjs.map → framework-components-fYXjz_in.mjs.map} +1 -1
  90. package/dist/global-flags-DEHjV8_s.d.mts +34 -0
  91. package/dist/global-flags-DEHjV8_s.d.mts.map +1 -0
  92. package/dist/{graph-render-DJVv0_uf.mjs → graph-render-rFAqZujX.mjs} +2 -2
  93. package/dist/{graph-render-DJVv0_uf.mjs.map → graph-render-rFAqZujX.mjs.map} +1 -1
  94. package/dist/{init-BCJZPWE1.mjs → init-Cv9UzWL5.mjs} +20 -269
  95. package/dist/init-Cv9UzWL5.mjs.map +1 -0
  96. package/dist/{inspect-live-schema-DSRbFoOL.mjs → inspect-live-schema-C6ohV_oQ.mjs} +4 -5
  97. package/dist/{inspect-live-schema-DSRbFoOL.mjs.map → inspect-live-schema-C6ohV_oQ.mjs.map} +1 -1
  98. package/dist/migration-check-BiBJoYYW.mjs +341 -0
  99. package/dist/migration-check-BiBJoYYW.mjs.map +1 -0
  100. package/dist/migration-cli.d.mts.map +1 -1
  101. package/dist/migration-cli.mjs +4 -4
  102. package/dist/migration-cli.mjs.map +1 -1
  103. package/dist/{migration-command-scaffold-Bzd9La5c.mjs → migration-command-scaffold-CjvwO6at.mjs} +4 -5
  104. package/dist/{migration-command-scaffold-Bzd9La5c.mjs.map → migration-command-scaffold-CjvwO6at.mjs.map} +1 -1
  105. package/dist/migration-graph-D7DVUElV.mjs +1232 -0
  106. package/dist/migration-graph-D7DVUElV.mjs.map +1 -0
  107. package/dist/migration-list-styler-BRwF4-gy.mjs +399 -0
  108. package/dist/migration-list-styler-BRwF4-gy.mjs.map +1 -0
  109. package/dist/{migration-plan-CFwqw3Gk.mjs → migration-plan-9DJ7q7_z.mjs} +372 -133
  110. package/dist/migration-plan-9DJ7q7_z.mjs.map +1 -0
  111. package/dist/{migration-types-BXWvz12q.d.mts → migration-types-D2FW63pr.d.mts} +1 -1
  112. package/dist/{migration-types-BXWvz12q.d.mts.map → migration-types-D2FW63pr.d.mts.map} +1 -1
  113. package/dist/{migrations-CwZMa1Ck.mjs → migrations-Cv2jxNNK.mjs} +12 -13
  114. package/dist/migrations-Cv2jxNNK.mjs.map +1 -0
  115. package/dist/{output-BlsrGMEF.mjs → output-B60Gw5fu.mjs} +1 -1
  116. package/dist/{output-BlsrGMEF.mjs.map → output-B60Gw5fu.mjs.map} +1 -1
  117. package/dist/{progress-adapter-DFfvZcYL.mjs → progress-adapter-C644QK8l.mjs} +1 -1
  118. package/dist/{progress-adapter-DFfvZcYL.mjs.map → progress-adapter-C644QK8l.mjs.map} +1 -1
  119. package/dist/ref-advancement-DUZqsue6.mjs +50 -0
  120. package/dist/ref-advancement-DUZqsue6.mjs.map +1 -0
  121. package/dist/terminal-ui-5Y6mrg93.d.mts +133 -0
  122. package/dist/terminal-ui-5Y6mrg93.d.mts.map +1 -0
  123. package/dist/{types--CqjMdk0.d.mts → types-Dt_SfqFm.d.mts} +28 -28
  124. package/dist/types-Dt_SfqFm.d.mts.map +1 -0
  125. package/dist/{verify-Bom75OYI.mjs → verify-DCA9Sldu.mjs} +2 -2
  126. package/dist/{verify-Bom75OYI.mjs.map → verify-DCA9Sldu.mjs.map} +1 -1
  127. package/package.json +35 -24
  128. package/src/commands/contract-emit.ts +19 -7
  129. package/src/commands/contract-infer.ts +1 -1
  130. package/src/commands/db-init.ts +48 -2
  131. package/src/commands/db-sign.ts +9 -5
  132. package/src/commands/db-update.ts +54 -8
  133. package/src/commands/init/hygiene-gitattributes.ts +2 -2
  134. package/src/commands/init/index.ts +2 -1
  135. package/src/commands/init/templates/code-templates.ts +4 -2
  136. package/src/commands/init/templates/env.ts +13 -14
  137. package/src/commands/migrate.ts +125 -44
  138. package/src/commands/migration-check.ts +43 -83
  139. package/src/commands/migration-graph.ts +75 -60
  140. package/src/commands/migration-list.ts +220 -74
  141. package/src/commands/migration-log.ts +8 -14
  142. package/src/commands/migration-new.ts +44 -48
  143. package/src/commands/migration-plan.ts +412 -197
  144. package/src/commands/migration-show.ts +65 -284
  145. package/src/commands/migration-status.ts +127 -124
  146. package/src/commands/ref.ts +53 -8
  147. package/src/control-api/client.ts +0 -1
  148. package/src/control-api/contract-enrichment.ts +6 -42
  149. package/src/control-api/operations/{apply-aggregate.ts → apply.ts} +44 -75
  150. package/src/control-api/operations/contract-emit.ts +14 -6
  151. package/src/control-api/operations/{db-apply-aggregate.ts → db-apply.ts} +19 -19
  152. package/src/control-api/operations/db-init.ts +4 -4
  153. package/src/control-api/operations/db-update.ts +4 -4
  154. package/src/control-api/operations/db-verify.ts +15 -11
  155. package/src/control-api/operations/migration-apply.ts +56 -47
  156. package/src/control-api/types.ts +26 -27
  157. package/src/migration-cli.ts +4 -4
  158. package/src/utils/cli-errors.ts +234 -0
  159. package/src/utils/command-helpers.ts +9 -24
  160. package/src/utils/contract-at-errors.ts +96 -0
  161. package/src/utils/contract-space-aggregate-loader.ts +336 -117
  162. package/src/utils/formatters/migration-graph-layout.ts +1119 -0
  163. package/src/utils/formatters/migration-graph-rows.ts +336 -0
  164. package/src/utils/formatters/migration-graph-tree-render.ts +459 -0
  165. package/src/utils/formatters/migration-list-data-column.ts +115 -0
  166. package/src/utils/formatters/migration-list-graph-topology.ts +368 -0
  167. package/src/utils/formatters/migration-list-render.ts +191 -0
  168. package/src/utils/formatters/migration-list-styler.ts +63 -0
  169. package/src/utils/formatters/migration-list-types.ts +21 -0
  170. package/src/utils/formatters/migrations.ts +37 -46
  171. package/src/utils/glyph-mode.ts +22 -0
  172. package/src/utils/integrity-violation-to-check-failure.ts +130 -0
  173. package/src/utils/plan-resolution.ts +258 -0
  174. package/src/utils/ref-advancement.ts +68 -0
  175. package/src/utils/terminal-ui.ts +42 -1
  176. package/dist/cli-errors-Czmx92Zy.d.mts +0 -3
  177. package/dist/cli-errors-Djtz98Vm.mjs +0 -71
  178. package/dist/cli-errors-Djtz98Vm.mjs.map +0 -1
  179. package/dist/client-oXO2WCPD.mjs.map +0 -1
  180. package/dist/command-helpers-BSb0tRC8.mjs.map +0 -1
  181. package/dist/commands/migration-check.mjs.map +0 -1
  182. package/dist/commands/migration-graph.mjs.map +0 -1
  183. package/dist/contract-emit-bcrpT-wD.mjs.map +0 -1
  184. package/dist/contract-emit-r4y8Zhf1.mjs.map +0 -1
  185. package/dist/contract-enrichment-Dani0mMW.mjs.map +0 -1
  186. package/dist/contract-space-aggregate-loader-BmNQwlws.mjs +0 -160
  187. package/dist/contract-space-aggregate-loader-BmNQwlws.mjs.map +0 -1
  188. package/dist/global-flags-CdE7M0d9.d.mts +0 -15
  189. package/dist/global-flags-CdE7M0d9.d.mts.map +0 -1
  190. package/dist/init-BCJZPWE1.mjs.map +0 -1
  191. package/dist/migration-plan-CFwqw3Gk.mjs.map +0 -1
  192. package/dist/migrations-CwZMa1Ck.mjs.map +0 -1
  193. package/dist/rolldown-runtime-twds-ZHy.mjs +0 -14
  194. package/dist/terminal-ui-BiB_8KNo.mjs +0 -379
  195. package/dist/terminal-ui-BiB_8KNo.mjs.map +0 -1
  196. package/dist/types--CqjMdk0.d.mts.map +0 -1
@@ -7,16 +7,16 @@ import type {
7
7
  TargetMigrationsCapability,
8
8
  } from '@prisma-next/framework-components/control';
9
9
  import {
10
- type AggregatePerSpacePlan,
11
10
  type ContractMarkerRecordLike,
12
11
  type ContractSpaceAggregate,
13
12
  type ContractSpaceMember,
14
13
  graphWalkStrategy,
14
+ type PerSpacePlan,
15
+ requireHeadRef,
15
16
  } from '@prisma-next/migration-tools/aggregate';
16
17
  import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
17
18
  import { errorNoInvariantPath } from '@prisma-next/migration-tools/errors';
18
19
  import { findPathWithDecision } from '@prisma-next/migration-tools/migration-graph';
19
- import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
20
20
  import { ifDefined } from '@prisma-next/utils/defined';
21
21
  import { notOk, ok } from '@prisma-next/utils/result';
22
22
  import {
@@ -24,14 +24,14 @@ import {
24
24
  buildContractSpaceAggregate,
25
25
  } from '../../utils/contract-space-aggregate-loader';
26
26
  import type {
27
- AggregatePerSpaceExecutionEntry,
28
27
  MigrationApplyFailure,
29
28
  MigrationApplyPathDecision,
30
29
  MigrationApplyResult,
31
30
  MigrationApplySuccess,
32
31
  OnControlProgress,
32
+ PerSpaceExecutionEntry,
33
33
  } from '../types';
34
- import { applyAggregate, buildPerSpaceBreakdown } from './apply-aggregate';
34
+ import { applyMigration, buildPerSpaceBreakdown } from './apply';
35
35
 
36
36
  /**
37
37
  * Inputs for the aggregate-walking `migrate` control-api
@@ -56,21 +56,10 @@ export interface ExecuteMigrationApplyOptions<TFamilyId extends string, TTargetI
56
56
  readonly migrationsDir: string;
57
57
  readonly extensionPacks: ReadonlyArray<ControlExtensionDescriptor<TFamilyId, TTargetId>>;
58
58
  readonly targetId: TTargetId;
59
- /**
60
- * Already-loaded app-space migration packages. The CLI command
61
- * loads these via `loadMigrationPackages(appMigrationsDir)`; the
62
- * operation hydrates the app member's graph with them. Required
63
- * because the framework-neutral aggregate loader doesn't know how
64
- * to read the user's `migrations/` directory layout (it's family-
65
- * aware: ops.json shape, manifest keys, etc.).
66
- */
67
- readonly appMigrationPackages: ReadonlyArray<OnDiskMigrationPackage>;
68
59
  /**
69
60
  * Optional app-space ref override. When provided, the app member's
70
61
  * graph-walk targets this hash instead of `member.headRef.hash`.
71
62
  * Extensions are unaffected — they always walk to their own head.
72
- *
73
- * Sub-spec § `--ref <hash>` semantics under multi-space.
74
63
  */
75
64
  readonly refHash?: string;
76
65
  /**
@@ -106,7 +95,7 @@ export interface ExecuteMigrationApplyOptions<TFamilyId extends string, TTargetI
106
95
  * marker to `member.headRef.hash` (or `refHash` for the app
107
96
  * member when provided). Empty-graph members fail loudly — a
108
97
  * "never planned" space is a user-error condition for replay.
109
- * 4. Hand off to {@link applyAggregate} (the runner-driving tail
98
+ * 4. Hand off to {@link applyMigration} (the runner-driving tail
110
99
  * shared with `db init` / `db update`). Marker advancement is
111
100
  * inside the per-space transaction.
112
101
  *
@@ -125,7 +114,6 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
125
114
  migrationsDir,
126
115
  extensionPacks,
127
116
  targetId,
128
- appMigrationPackages,
129
117
  refHash,
130
118
  refInvariants,
131
119
  refName,
@@ -138,7 +126,6 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
138
126
  appContract: contract,
139
127
  extensionPacks,
140
128
  deserializeContract: (json) => familyInstance.deserializeContract(json),
141
- appMigrationPackages,
142
129
  };
143
130
  const loaded = await buildContractSpaceAggregate(loadInputs);
144
131
  if (!loaded.ok) {
@@ -152,21 +139,24 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
152
139
  // when provided, otherwise its own head; extensions always walk
153
140
  // to their own head ref.
154
141
  const allMembers: ReadonlyArray<ContractSpaceMember> = [aggregate.app, ...aggregate.extensions];
155
- const perSpacePlans = new Map<string, AggregatePerSpacePlan>();
142
+ const perSpacePlans = new Map<string, PerSpacePlan>();
156
143
  // Already-at-head empty-graph members (typically extensions whose
157
144
  // head ref is the empty sentinel, or whose live marker already
158
145
  // matches the target). Kept out of the runner schedule so we don't
159
146
  // write spurious markers for greenfield extensions, but merged back
160
147
  // into the success envelope so every loaded member is represented.
161
- const atHeadResolutions = new Map<string, AggregatePerSpacePlan>();
148
+ const atHeadResolutions = new Map<string, PerSpacePlan>();
162
149
  for (const member of allMembers) {
163
150
  const isAppMember = member.spaceId === aggregate.app.spaceId;
164
- const targetHash = isAppMember && refHash !== undefined ? refHash : member.headRef.hash;
151
+ // The aggregate passed the integrity gate, so every member's head ref
152
+ // is resolved (the app's is synthesised from the live contract).
153
+ const headRef = requireHeadRef(member);
154
+ const targetHash = isAppMember && refHash !== undefined ? refHash : headRef.hash;
165
155
  const liveMarker = markerRows.get(member.spaceId) ?? null;
166
156
 
167
157
  // Empty-graph members fail loudly: replay needs an on-disk path
168
158
  // and an empty graph means the user has never planned this space.
169
- if (member.migrations.graph.nodes.size === 0) {
159
+ if (member.graph().nodes.size === 0) {
170
160
  // Edge case: target == EMPTY (greenfield, nothing to do) or
171
161
  // the live marker already matches the target. Loader integrity
172
162
  // allows this for extensions whose head ref is the empty
@@ -197,9 +187,9 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
197
187
  const targetInvariants =
198
188
  isAppMember && refHash !== undefined && refInvariants !== undefined
199
189
  ? refInvariants
200
- : member.headRef.invariants;
190
+ : headRef.invariants;
201
191
  const targetMember: ContractSpaceMember =
202
- targetHash === member.headRef.hash && targetInvariants === member.headRef.invariants
192
+ targetHash === headRef.hash && targetInvariants === headRef.invariants
203
193
  ? member
204
194
  : { ...member, headRef: { hash: targetHash, invariants: targetInvariants } };
205
195
 
@@ -224,7 +214,7 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
224
214
  // is never a graph node, producing an empty `structuralPath` and
225
215
  // a less actionable diagnostic.
226
216
  const fromHash = liveMarker?.storageHash ?? EMPTY_CONTRACT_HASH;
227
- const structural = findPathWithDecision(targetMember.migrations.graph, fromHash, targetHash, {
217
+ const structural = findPathWithDecision(targetMember.graph(), fromHash, targetHash, {
228
218
  required: new Set<string>(),
229
219
  });
230
220
  const structuralPath =
@@ -286,7 +276,7 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
286
276
  );
287
277
  }
288
278
 
289
- const applied = await applyAggregate({
279
+ const applied = await applyMigration({
290
280
  aggregate,
291
281
  perSpacePlans,
292
282
  applyOrder,
@@ -346,7 +336,7 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
346
336
  }
347
337
 
348
338
  /**
349
- * Build a zero-op {@link AggregatePerSpacePlan} for an empty-graph
339
+ * Build a zero-op {@link PerSpacePlan} for an empty-graph
350
340
  * member whose live marker already matches the target. Lets the apply
351
341
  * pipeline thread the member through `perSpacePlans` -> `applyOrder`
352
342
  * -> the success envelope's `perSpace[]` block so the result reflects
@@ -357,7 +347,7 @@ function buildAtHeadResolution(args: {
357
347
  readonly member: ContractSpaceMember;
358
348
  readonly targetHash: string;
359
349
  readonly liveMarker: ContractMarkerRecordLike | null;
360
- }): AggregatePerSpacePlan {
350
+ }): PerSpacePlan {
361
351
  const { aggregateTargetId, member, targetHash, liveMarker } = args;
362
352
  return {
363
353
  plan: {
@@ -369,7 +359,7 @@ function buildAtHeadResolution(args: {
369
359
  providedInvariants: [],
370
360
  },
371
361
  displayOps: [],
372
- destinationContract: member.contract,
362
+ destinationContract: member.contract(),
373
363
  strategy: 'graph-walk',
374
364
  migrationEdges: [],
375
365
  };
@@ -377,7 +367,7 @@ function buildAtHeadResolution(args: {
377
367
 
378
368
  function sumPlannedOps(
379
369
  applyOrder: readonly string[],
380
- perSpacePlans: ReadonlyMap<string, AggregatePerSpacePlan>,
370
+ perSpacePlans: ReadonlyMap<string, PerSpacePlan>,
381
371
  ): number {
382
372
  let total = 0;
383
373
  for (const spaceId of applyOrder) {
@@ -392,28 +382,27 @@ interface BuildSuccessArgs {
392
382
  readonly aggregate: ContractSpaceAggregate;
393
383
  readonly orderedResolutions: ReadonlyArray<{
394
384
  readonly spaceId: string;
395
- readonly entry: AggregatePerSpacePlan;
385
+ readonly entry: PerSpacePlan;
396
386
  }>;
397
- readonly perSpace: ReadonlyArray<AggregatePerSpaceExecutionEntry>;
387
+ readonly perSpace: ReadonlyArray<PerSpaceExecutionEntry>;
398
388
  readonly totalOpsExecuted: number;
399
389
  readonly summary: string;
400
390
  }
401
391
 
402
392
  function buildSuccess(args: BuildSuccessArgs): MigrationApplySuccess {
403
393
  // The marker hash surfaced at the top level is the **app member's**
404
- // post-apply marker (today's single-space `markerHash` field).
394
+ // post-apply marker (the top-level `markerHash` field).
405
395
  // Per-space markers live on `perSpace[].marker.storageHash`.
406
396
  const appResolution = args.orderedResolutions.find(
407
397
  (r) => r.spaceId === args.aggregate.app.spaceId,
408
398
  );
409
399
  const appMarkerHash =
410
- appResolution?.entry.plan.destination.storageHash ?? args.aggregate.app.headRef.hash;
400
+ appResolution?.entry.plan.destination.storageHash ?? requireHeadRef(args.aggregate.app).hash;
411
401
 
412
402
  // Per-migration entries (one per authored edge) preserve the
413
- // single-space `migrationsApplied` count semantics for back-compat
414
- // with existing JSON-shape consumers (e.g. `parsed.applied.length`
415
- // in integration tests). The aggregate per-space breakdown lives on
416
- // `perSpace[]`.
403
+ // `migrationsApplied` count semantics for back-compat with existing
404
+ // JSON-shape consumers (e.g. `parsed.applied.length` in integration
405
+ // tests). The aggregate per-space breakdown lives on `perSpace[]`.
417
406
  const applied = args.orderedResolutions.flatMap((r) => {
418
407
  const edges = r.entry.migrationEdges ?? [];
419
408
  return edges.map((edge) => ({
@@ -458,26 +447,46 @@ function buildSuccess(args: BuildSuccessArgs): MigrationApplySuccess {
458
447
  };
459
448
  }
460
449
 
461
- function buildNeverPlannedFailure(spaceId: string, targetHash: string): MigrationApplyFailure {
450
+ /**
451
+ * Build the `neverPlanned` failure raised when a contract space has no on-disk
452
+ * migration graph but migrate was asked to reach a target hash. The `why`
453
+ * states only the condition; the recovery sequence is composed by
454
+ * `errorPathUnreachable`'s `fix`.
455
+ *
456
+ * @internal Exported for testing only.
457
+ */
458
+ export function buildNeverPlannedFailure(
459
+ spaceId: string,
460
+ targetHash: string,
461
+ ): MigrationApplyFailure {
462
462
  return {
463
463
  code: 'MIGRATION_PATH_NOT_FOUND',
464
464
  summary: `No on-disk migrations for contract space "${spaceId}"`,
465
- why: `migrate is replay-only: every contract space must have an authored migration graph on disk. Space "${spaceId}" has no migrations under \`migrations/${spaceId}/\` but its head ref targets "${targetHash}". Run \`prisma-next migration plan\` first to materialise the path.`,
465
+ why: `migrate is replay-only: every contract space must have an authored migration graph on disk. Space "${spaceId}" has no migrations under \`migrations/${spaceId}/\` but its head ref targets "${targetHash}".`,
466
466
  meta: { spaceId, target: targetHash, kind: 'neverPlanned' },
467
467
  };
468
468
  }
469
469
 
470
- function buildPathNotFoundFailure(
470
+ /**
471
+ * Build the `pathUnreachable` failure raised when an emitted contract has no
472
+ * on-disk migration edge from the current marker to the target. The `why`
473
+ * states only the condition (no edge between the two named states, and migrate
474
+ * replays edges rather than inventing them); the recovery sequence — plan the
475
+ * edge, then re-apply — is composed by `errorPathUnreachable`'s `fix`, so the
476
+ * two read as one non-redundant plan-then-apply story.
477
+ *
478
+ * @internal Exported for testing only.
479
+ */
480
+ export function buildPathNotFoundFailure(
471
481
  spaceId: string,
472
482
  marker: ContractMarkerRecordLike | null,
473
483
  targetHash: string,
474
484
  ): MigrationApplyFailure {
475
485
  const fromHash = marker?.storageHash ?? '<empty>';
476
- // The single-space-degenerate phrasing names the user-visible
477
- // condition (a contract has been emitted that no on-disk
478
- // migration reaches) so the error reads naturally for the
479
- // single-space app case. Multi-space callers see the same
480
- // condition expressed against the offending space.
486
+ // The app-case phrasing names the user-visible condition (a
487
+ // contract has been emitted that no on-disk migration reaches) so
488
+ // the error reads naturally for the app member. Extension spaces
489
+ // see the same condition expressed against the offending space.
481
490
  const summary =
482
491
  spaceId === 'app'
483
492
  ? 'Current contract has no planned migration path'
@@ -485,7 +494,7 @@ function buildPathNotFoundFailure(
485
494
  return {
486
495
  code: 'MIGRATION_PATH_NOT_FOUND',
487
496
  summary,
488
- why: `Cannot reach target "${targetHash}" from current marker "${fromHash}" in space "${spaceId}". The on-disk migration graph for this space does not connect the two states. Run \`prisma-next migration plan\` to materialise the path.`,
497
+ why: `No migration edge connects the current state "${fromHash}" to the target "${targetHash}" in contract space "${spaceId}". The on-disk migration graph does not join the two, and migrate replays existing edges it never invents one.`,
489
498
  meta: { spaceId, fromHash, targetHash, kind: 'pathUnreachable' },
490
499
  };
491
500
  }
@@ -18,7 +18,6 @@ import type {
18
18
  VerifyDatabaseSchemaResult,
19
19
  } from '@prisma-next/framework-components/control';
20
20
  import type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';
21
- import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
22
21
  import type { Result } from '@prisma-next/utils/result';
23
22
  import type { ExecuteDbVerifyResult } from './operations/db-verify';
24
23
 
@@ -246,7 +245,7 @@ export interface DbUpdateOptions {
246
245
  * Options for the dbVerify operation.
247
246
  *
248
247
  * Drives the loader → aggregate-verifier pipeline. `strict` maps to
249
- * `verifyAggregate({ mode: 'strict' | 'lenient' })`; `skipSchema`
248
+ * `verifyMigration({ mode: 'strict' | 'lenient' })`; `skipSchema`
250
249
  * mirrors the `--marker-only` CLI flag and short-circuits the schema
251
250
  * portion of the verifier.
252
251
  */
@@ -327,7 +326,7 @@ export interface EmitOptions {
327
326
  * to the user instead of being collapsed into a single ambiguous
328
327
  * top-level hash.
329
328
  */
330
- export interface AggregatePerSpaceExecutionEntry {
329
+ export interface PerSpaceExecutionEntry {
331
330
  readonly spaceId: string;
332
331
  /** `'app'` for the application's contract space; `'extension'` for any extension space. */
333
332
  readonly kind: 'app' | 'extension';
@@ -388,9 +387,9 @@ export interface DbInitSuccess {
388
387
  * alphabetically, then app). Present whenever the aggregate flow
389
388
  * produced one — both `mode: 'plan'` and `mode: 'apply'`.
390
389
  *
391
- * See {@link AggregatePerSpaceExecutionEntry}.
390
+ * See {@link PerSpaceExecutionEntry}.
392
391
  */
393
- readonly perSpace?: ReadonlyArray<AggregatePerSpaceExecutionEntry>;
392
+ readonly perSpace?: ReadonlyArray<PerSpaceExecutionEntry>;
394
393
  readonly summary: string;
395
394
  }
396
395
 
@@ -458,9 +457,9 @@ export interface DbUpdateSuccess {
458
457
  };
459
458
  /**
460
459
  * Per-space breakdown in canonical schedule order (extensions
461
- * alphabetically, then app). See {@link AggregatePerSpaceExecutionEntry}.
460
+ * alphabetically, then app). See {@link PerSpaceExecutionEntry}.
462
461
  */
463
- readonly perSpace?: ReadonlyArray<AggregatePerSpaceExecutionEntry>;
462
+ readonly perSpace?: ReadonlyArray<PerSpaceExecutionEntry>;
464
463
  readonly summary: string;
465
464
  }
466
465
 
@@ -539,7 +538,7 @@ export type EmitResult = Result<EmitSuccess, EmitFailure>;
539
538
  * contract-space aggregate, reading per-space marker rows from the
540
539
  * live database, plotting per-space paths via `graphWalkStrategy`
541
540
  * (replay-only — no synth, no introspection), and dispatching
542
- * through the shared `applyAggregate` primitive. The CLI command
541
+ * through the shared `applyMigration` primitive. The CLI command
543
542
  * just resolves the descriptor surface (config, refs, contract
544
543
  * envelope, app-space migration packages) and hands the inputs in.
545
544
  */
@@ -548,12 +547,6 @@ export interface MigrationApplyOptions {
548
547
  readonly contract: unknown;
549
548
  /** Migrations root directory (`migrations/` under the project). */
550
549
  readonly migrationsDir: string;
551
- /**
552
- * Already-loaded app-space migration packages. The CLI loads these
553
- * via `loadMigrationPackages(appMigrationsDir)` before invoking
554
- * `migrationApply`.
555
- */
556
- readonly appMigrationPackages: ReadonlyArray<OnDiskMigrationPackage>;
557
550
  /**
558
551
  * Optional app-space ref override. When provided, the app member's
559
552
  * graph-walk targets this hash instead of `contract.storage.storageHash`.
@@ -584,11 +577,11 @@ export interface MigrationApplyOptions {
584
577
 
585
578
  /**
586
579
  * A single on-disk migration package surfaced to the operation. The
587
- * SQL family already produces this shape via `loadMigrationPackages`;
588
- * the operation hands it through to the framework-neutral aggregate
589
- * loader's `appMigrationPackages` slot.
580
+ * SQL family surfaces this shape from the tolerant contract-space
581
+ * aggregate's app packages; the operation hands it through to the
582
+ * framework-neutral aggregate loader's `appMigrationPackages` slot.
590
583
  *
591
- * (Originally named `MigrationApplyStep` for the legacy single-space
584
+ * (Originally named `MigrationApplyStep` for the earlier app-only
592
585
  * apply path; the name is kept for compatibility with existing CLI
593
586
  * callers and tests.)
594
587
  */
@@ -612,8 +605,8 @@ export interface MigrationApplyStep {
612
605
  */
613
606
  /**
614
607
  * One entry per authored migration package applied. Preserves the
615
- * single-space `migrationsApplied` count semantics (each entry is
616
- * one migration directory) so `applied.length === migrationsApplied`.
608
+ * `migrationsApplied` count semantics (each entry is one migration
609
+ * directory) so `applied.length === migrationsApplied`.
617
610
  *
618
611
  * Per-space aggregate detail (markers, ops grouped by space) lives
619
612
  * on `perSpace[]`; this list is the per-edge view.
@@ -628,16 +621,15 @@ export interface MigrationApplyAppliedEntry {
628
621
  }
629
622
 
630
623
  /**
631
- * Successful migrationApply result. Carries both the legacy
632
- * single-space fields (`markerHash` is the **app member's** post-apply
633
- * marker, surfaced for back-compat with single-space callers) and the
624
+ * Successful migrationApply result. Carries both the top-level fields
625
+ * (`markerHash` is the **app member's** post-apply marker) and the
634
626
  * per-space breakdown (`perSpace` — markers / operations in canonical
635
627
  * schedule order).
636
628
  */
637
629
  /**
638
630
  * Path-decision summary for the **app member** post-apply. Surfaced
639
- * for back-compat with single-space callers (and the cli-journeys
640
- * suite, which inspects `requiredInvariants`/`satisfiedInvariants`/
631
+ * at the top level (and consumed by the cli-journeys suite, which
632
+ * inspects `requiredInvariants`/`satisfiedInvariants`/
641
633
  * `selectedPath` to validate invariant routing).
642
634
  *
643
635
  * Per-space path decisions for extension members are not surfaced —
@@ -667,10 +659,10 @@ export interface MigrationApplySuccess {
667
659
  readonly summary: string;
668
660
  /**
669
661
  * Per-space breakdown in canonical schedule order (extensions
670
- * alphabetically, then app). See {@link AggregatePerSpaceExecutionEntry}.
662
+ * alphabetically, then app). See {@link PerSpaceExecutionEntry}.
671
663
  * Always present for the aggregate-walking operation.
672
664
  */
673
- readonly perSpace: ReadonlyArray<AggregatePerSpaceExecutionEntry>;
665
+ readonly perSpace: ReadonlyArray<PerSpaceExecutionEntry>;
674
666
  /**
675
667
  * Path-decision data for the app member. Present whenever the
676
668
  * graph-walk strategy ran for the app (i.e. always for the
@@ -719,6 +711,13 @@ export type MigrationApplyResult = Result<MigrationApplySuccess, MigrationApplyF
719
711
  export interface ContractEmitOptions {
720
712
  /** Path to the prisma-next.config.ts file */
721
713
  readonly configPath: string;
714
+ /**
715
+ * Directory to write contract artifacts into. When set, `contract.json` and
716
+ * `contract.d.ts` are written inside this directory, taking precedence over
717
+ * any output path from the loaded config. The value must be an absolute path;
718
+ * resolution against cwd is the caller's responsibility.
719
+ */
720
+ readonly outputPath?: string;
722
721
  /** Optional AbortSignal for cancelling the in-flight emit */
723
722
  readonly signal?: AbortSignal;
724
723
  /** Optional progress callback for observing source-resolution and emit spans */
@@ -461,8 +461,8 @@ function writeStructuredError(stream: MigrationCliWritable, err: CliStructuredEr
461
461
  * `null` when the file is missing and throwing `MIGRATION.INVALID_JSON`
462
462
  * when the file is present but cannot be parsed as JSON. The CLI feeds
463
463
  * this into `buildMigrationArtifacts` so the pure builder can preserve
464
- * fields owned by `migration plan` (contract bookends, hints, labels,
465
- * `createdAt`) across re-emits.
464
+ * fields owned by `migration plan` (contract bookends, `createdAt`) across
465
+ * re-emits.
466
466
  *
467
467
  * Author-time path: this loader still does not verify the manifest hash
468
468
  * or schema — that is the apply-time loader's job. Hash mismatch is the
@@ -472,8 +472,8 @@ function writeStructuredError(stream: MigrationCliWritable, err: CliStructuredEr
472
472
  * however, is now surfaced rather than swallowed: a malformed
473
473
  * `migration.json` indicates either a hand-edit gone wrong or partial
474
474
  * write, and silently rebuilding from `describe()` would discard the
475
- * user's on-disk content (preserved bookends, hints, labels,
476
- * `createdAt`) without any indication something was wrong on disk.
475
+ * user's on-disk content (preserved bookends, `createdAt`) without any
476
+ * indication something was wrong on disk.
477
477
  * Apply-time consumers always route through the verifying
478
478
  * `readMigrationPackage` in `@prisma-next/migration-tools/io` instead.
479
479
  */