prisma-next 0.5.0-dev.9 → 0.5.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 (142) hide show
  1. package/dist/cli-errors-B9OBbled.d.mts +3 -0
  2. package/dist/cli-errors-D3_sMh2K.mjs +33 -0
  3. package/dist/cli-errors-D3_sMh2K.mjs.map +1 -0
  4. package/dist/cli.mjs +16 -78
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/client-BCnP7cHo.mjs +1485 -0
  7. package/dist/client-BCnP7cHo.mjs.map +1 -0
  8. package/dist/{result-handler-Ba3zWQsI.mjs → command-helpers-BeZHkxV8.mjs} +70 -47
  9. package/dist/command-helpers-BeZHkxV8.mjs.map +1 -0
  10. package/dist/commands/contract-emit.d.mts.map +1 -1
  11. package/dist/commands/contract-emit.mjs +2 -4
  12. package/dist/commands/contract-infer.d.mts.map +1 -1
  13. package/dist/commands/contract-infer.mjs +2 -4
  14. package/dist/commands/db-init.d.mts.map +1 -1
  15. package/dist/commands/db-init.mjs +16 -13
  16. package/dist/commands/db-init.mjs.map +1 -1
  17. package/dist/commands/db-schema.d.mts.map +1 -1
  18. package/dist/commands/db-schema.mjs +6 -7
  19. package/dist/commands/db-schema.mjs.map +1 -1
  20. package/dist/commands/db-sign.d.mts.map +1 -1
  21. package/dist/commands/db-sign.mjs +9 -9
  22. package/dist/commands/db-sign.mjs.map +1 -1
  23. package/dist/commands/db-update.d.mts.map +1 -1
  24. package/dist/commands/db-update.mjs +15 -13
  25. package/dist/commands/db-update.mjs.map +1 -1
  26. package/dist/commands/db-verify.d.mts.map +1 -1
  27. package/dist/commands/db-verify.mjs +1 -321
  28. package/dist/commands/migration-apply.d.mts +28 -13
  29. package/dist/commands/migration-apply.d.mts.map +1 -1
  30. package/dist/commands/migration-apply.mjs +55 -151
  31. package/dist/commands/migration-apply.mjs.map +1 -1
  32. package/dist/commands/migration-new.d.mts +0 -1
  33. package/dist/commands/migration-new.d.mts.map +1 -1
  34. package/dist/commands/migration-new.mjs +34 -40
  35. package/dist/commands/migration-new.mjs.map +1 -1
  36. package/dist/commands/migration-plan.d.mts +33 -6
  37. package/dist/commands/migration-plan.d.mts.map +1 -1
  38. package/dist/commands/migration-plan.mjs +2 -348
  39. package/dist/commands/migration-ref.d.mts +1 -1
  40. package/dist/commands/migration-ref.d.mts.map +1 -1
  41. package/dist/commands/migration-ref.mjs +8 -12
  42. package/dist/commands/migration-ref.mjs.map +1 -1
  43. package/dist/commands/migration-show.d.mts +64 -10
  44. package/dist/commands/migration-show.d.mts.map +1 -1
  45. package/dist/commands/migration-show.mjs +166 -60
  46. package/dist/commands/migration-show.mjs.map +1 -1
  47. package/dist/commands/migration-status.d.mts +126 -5
  48. package/dist/commands/migration-status.d.mts.map +1 -1
  49. package/dist/commands/migration-status.mjs +2 -4
  50. package/dist/{config-loader-C25b63rJ.mjs → config-loader-B6sJjXTv.mjs} +3 -5
  51. package/dist/config-loader-B6sJjXTv.mjs.map +1 -0
  52. package/dist/config-loader.d.mts +0 -1
  53. package/dist/config-loader.d.mts.map +1 -1
  54. package/dist/config-loader.mjs +2 -3
  55. package/dist/contract-emit-9DBda5Ou.mjs +150 -0
  56. package/dist/contract-emit-9DBda5Ou.mjs.map +1 -0
  57. package/dist/contract-emit-B77TsJqf.mjs +327 -0
  58. package/dist/contract-emit-B77TsJqf.mjs.map +1 -0
  59. package/dist/{contract-enrichment-CAOELa-H.mjs → contract-enrichment-Dani0mMW.mjs} +4 -6
  60. package/dist/contract-enrichment-Dani0mMW.mjs.map +1 -0
  61. package/dist/{contract-infer-D9cC3rJm.mjs → contract-infer-ByxhPjpW.mjs} +13 -22
  62. package/dist/contract-infer-ByxhPjpW.mjs.map +1 -0
  63. package/dist/contract-space-aggregate-loader-BrwKK6Q6.mjs +160 -0
  64. package/dist/contract-space-aggregate-loader-BrwKK6Q6.mjs.map +1 -0
  65. package/dist/db-verify-Czm5T-J4.mjs +404 -0
  66. package/dist/db-verify-Czm5T-J4.mjs.map +1 -0
  67. package/dist/exports/config-types.mjs +1 -2
  68. package/dist/exports/control-api.d.mts +101 -586
  69. package/dist/exports/control-api.d.mts.map +1 -1
  70. package/dist/exports/control-api.mjs +4 -6
  71. package/dist/exports/index.d.mts.map +1 -1
  72. package/dist/exports/index.mjs +28 -30
  73. package/dist/exports/index.mjs.map +1 -1
  74. package/dist/exports/init-output.d.mts +2 -4
  75. package/dist/exports/init-output.d.mts.map +1 -1
  76. package/dist/exports/init-output.mjs +2 -3
  77. package/dist/{framework-components-Cr--XBKy.mjs → framework-components-ChqVUxR-.mjs} +3 -4
  78. package/dist/{framework-components-Cr--XBKy.mjs.map → framework-components-ChqVUxR-.mjs.map} +1 -1
  79. package/dist/global-flags-Icqpxk23.d.mts +12 -0
  80. package/dist/global-flags-Icqpxk23.d.mts.map +1 -0
  81. package/dist/helpers-eqdN8tH6.mjs +25 -0
  82. package/dist/helpers-eqdN8tH6.mjs.map +1 -0
  83. package/dist/{init-C5220SY9.mjs → init-DETSgw3h.mjs} +40 -49
  84. package/dist/init-DETSgw3h.mjs.map +1 -0
  85. package/dist/{inspect-live-schema-yrHAvG71.mjs → inspect-live-schema-DxdBd4Er.mjs} +10 -11
  86. package/dist/inspect-live-schema-DxdBd4Er.mjs.map +1 -0
  87. package/dist/migration-cli.d.mts +41 -12
  88. package/dist/migration-cli.d.mts.map +1 -1
  89. package/dist/migration-cli.mjs +309 -86
  90. package/dist/migration-cli.mjs.map +1 -1
  91. package/dist/{migration-command-scaffold-B3B09et6.mjs → migration-command-scaffold-BdV8JYXV.mjs} +8 -9
  92. package/dist/migration-command-scaffold-BdV8JYXV.mjs.map +1 -0
  93. package/dist/migration-plan-mRu5K81L.mjs +494 -0
  94. package/dist/migration-plan-mRu5K81L.mjs.map +1 -0
  95. package/dist/{migration-status-DUMiH8_G.mjs → migration-status-By9G5p2H.mjs} +270 -65
  96. package/dist/migration-status-By9G5p2H.mjs.map +1 -0
  97. package/dist/migrations-CTsyBXCA.mjs +229 -0
  98. package/dist/migrations-CTsyBXCA.mjs.map +1 -0
  99. package/dist/{output-BpcQrnnq.mjs → output-B16Kefzx.mjs} +9 -3
  100. package/dist/output-B16Kefzx.mjs.map +1 -0
  101. package/dist/{progress-adapter-DvQWB1nK.mjs → progress-adapter-DFfvZcYL.mjs} +2 -2
  102. package/dist/{progress-adapter-DvQWB1nK.mjs.map → progress-adapter-DFfvZcYL.mjs.map} +1 -1
  103. package/dist/result-handler-rmPVKIP2.mjs +25 -0
  104. package/dist/result-handler-rmPVKIP2.mjs.map +1 -0
  105. package/dist/rolldown-runtime-twds-ZHy.mjs +14 -0
  106. package/dist/{terminal-ui-C3ZLwQxK.mjs → terminal-ui-C_hFNbAn.mjs} +4 -28
  107. package/dist/terminal-ui-C_hFNbAn.mjs.map +1 -0
  108. package/dist/types-LItU7E4l.d.mts +856 -0
  109. package/dist/types-LItU7E4l.d.mts.map +1 -0
  110. package/dist/{verify-Bkycc-Tf.mjs → verify-CiwNWM9N.mjs} +3 -4
  111. package/dist/verify-CiwNWM9N.mjs.map +1 -0
  112. package/package.json +19 -17
  113. package/dist/cli-errors-BFYgBH3L.d.mts +0 -4
  114. package/dist/cli-errors-Cd79vmTH.mjs +0 -5
  115. package/dist/client-CrsnY58k.mjs +0 -997
  116. package/dist/client-CrsnY58k.mjs.map +0 -1
  117. package/dist/commands/db-verify.mjs.map +0 -1
  118. package/dist/commands/migration-plan.mjs.map +0 -1
  119. package/dist/config-loader-C25b63rJ.mjs.map +0 -1
  120. package/dist/contract-emit--feXyNd7.mjs +0 -4
  121. package/dist/contract-emit-NJ01hiiv.mjs +0 -195
  122. package/dist/contract-emit-NJ01hiiv.mjs.map +0 -1
  123. package/dist/contract-emit-V5SSitUT.mjs +0 -122
  124. package/dist/contract-emit-V5SSitUT.mjs.map +0 -1
  125. package/dist/contract-enrichment-CAOELa-H.mjs.map +0 -1
  126. package/dist/contract-infer-D9cC3rJm.mjs.map +0 -1
  127. package/dist/extract-operation-statements-DsFfxXVZ.mjs +0 -13
  128. package/dist/extract-operation-statements-DsFfxXVZ.mjs.map +0 -1
  129. package/dist/extract-sql-ddl-D9UbZDyz.mjs +0 -26
  130. package/dist/extract-sql-ddl-D9UbZDyz.mjs.map +0 -1
  131. package/dist/init-C5220SY9.mjs.map +0 -1
  132. package/dist/inspect-live-schema-yrHAvG71.mjs.map +0 -1
  133. package/dist/migration-command-scaffold-B3B09et6.mjs.map +0 -1
  134. package/dist/migration-status-DUMiH8_G.mjs.map +0 -1
  135. package/dist/migrations-Bo5WtTla.mjs +0 -153
  136. package/dist/migrations-Bo5WtTla.mjs.map +0 -1
  137. package/dist/output-BpcQrnnq.mjs.map +0 -1
  138. package/dist/result-handler-Ba3zWQsI.mjs.map +0 -1
  139. package/dist/terminal-ui-C3ZLwQxK.mjs.map +0 -1
  140. package/dist/validate-contract-deps-B_Cs29TL.mjs +0 -37
  141. package/dist/validate-contract-deps-B_Cs29TL.mjs.map +0 -1
  142. package/dist/verify-Bkycc-Tf.mjs.map +0 -1
@@ -0,0 +1,1485 @@
1
+ import { p as errorRunnerFailed, t as CliStructuredError } from "./cli-errors-D3_sMh2K.mjs";
2
+ import { t as assertFrameworkComponentsCompatible } from "./framework-components-ChqVUxR-.mjs";
3
+ import { t as enrichContract } from "./contract-enrichment-Dani0mMW.mjs";
4
+ import { t as buildContractSpaceAggregate } from "./contract-space-aggregate-loader-BrwKK6Q6.mjs";
5
+ import { emit } from "@prisma-next/emitter";
6
+ import { ifDefined } from "@prisma-next/utils/defined";
7
+ import { notOk, ok } from "@prisma-next/utils/result";
8
+ import { APP_SPACE_ID, createControlStack, hasMigrations, hasMultiSpaceRunner, hasOperationPreview, hasPslContractInfer, hasSchemaView } from "@prisma-next/framework-components/control";
9
+ import { findPathWithDecision } from "@prisma-next/migration-tools/migration-graph";
10
+ import { graphWalkStrategy, planAggregate, verifyAggregate } from "@prisma-next/migration-tools/aggregate";
11
+ import { EMPTY_CONTRACT_HASH } from "@prisma-next/migration-tools/constants";
12
+ import { errorNoInvariantPath } from "@prisma-next/migration-tools/errors";
13
+ //#region src/control-api/errors.ts
14
+ var ContractValidationError = class extends Error {
15
+ cause;
16
+ constructor(message, cause) {
17
+ super(message);
18
+ this.name = "ContractValidationError";
19
+ this.cause = cause;
20
+ }
21
+ };
22
+ //#endregion
23
+ //#region src/control-api/operations/apply-aggregate.ts
24
+ /**
25
+ * Span id emitted via `onProgress` for the apply phase. Stable
26
+ * identifier consumed by the structured-output renderer and by tests.
27
+ */
28
+ const APPLY_SPAN_ID = "apply";
29
+ /**
30
+ * Runner-driving tail shared by every aggregate apply caller — `db init`,
31
+ * `db update`, and `migration apply`. Consumes already-resolved per-space
32
+ * plans (the planner-vs-replay distinction is owned by the caller) and
33
+ * dispatches them to the multi-space runner in canonical order.
34
+ *
35
+ * Marker advancement is part of the runner's per-space transaction
36
+ * (the SQL family runner writes the marker as the last step of each
37
+ * space's transaction), so this primitive does not advance markers
38
+ * separately — by the time `executeAcrossSpaces` returns ok, every
39
+ * space's marker has been advanced to its plan's destination.
40
+ *
41
+ * Span emission (`spanStart 'apply'` / `spanEnd 'apply'`) is owned here
42
+ * so callers don't have to duplicate it; the `action` field on each
43
+ * progress event is taken from the caller's `action` argument.
44
+ */
45
+ async function applyAggregate(inputs) {
46
+ const { aggregate, perSpacePlans, applyOrder, driver, familyInstance, migrations, frameworkComponents, policy, action, onProgress } = inputs;
47
+ const orderedResolutions = collectOrdered(applyOrder, perSpacePlans);
48
+ const runner = migrations.createRunner(familyInstance);
49
+ if (!hasMultiSpaceRunner(runner)) throw errorRunnerFailed(`Runner for target "${aggregate.targetId}" does not implement \`executeAcrossSpaces\``, { why: `${labelForAction(action)} requires multi-space-capable runners (today: every SQL family runner).` });
50
+ onProgress?.({
51
+ action,
52
+ kind: "spanStart",
53
+ spanId: APPLY_SPAN_ID,
54
+ label: progressLabelForAction(action)
55
+ });
56
+ const perSpaceOptions = orderedResolutions.map((r) => ({
57
+ space: r.spaceId,
58
+ plan: r.entry.plan,
59
+ driver,
60
+ destinationContract: r.entry.destinationContract,
61
+ policy,
62
+ frameworkComponents,
63
+ strictVerification: false
64
+ }));
65
+ const runnerResult = await runner.executeAcrossSpaces({
66
+ driver,
67
+ perSpaceOptions
68
+ });
69
+ if (!runnerResult.ok) {
70
+ onProgress?.({
71
+ action,
72
+ kind: "spanEnd",
73
+ spanId: APPLY_SPAN_ID,
74
+ outcome: "error"
75
+ });
76
+ return notOk({
77
+ summary: runnerResult.failure.summary,
78
+ ...ifDefined("why", runnerResult.failure.why),
79
+ meta: {
80
+ ...runnerResult.failure.meta ?? {},
81
+ failingSpace: runnerResult.failure.failingSpace
82
+ }
83
+ });
84
+ }
85
+ onProgress?.({
86
+ action,
87
+ kind: "spanEnd",
88
+ spanId: APPLY_SPAN_ID,
89
+ outcome: "ok"
90
+ });
91
+ return ok({
92
+ orderedResolutions,
93
+ totalOpsPlanned: runnerResult.value.perSpaceResults.reduce((sum, r) => sum + r.value.operationsPlanned, 0),
94
+ totalOpsExecuted: runnerResult.value.perSpaceResults.reduce((sum, r) => sum + r.value.operationsExecuted, 0),
95
+ perSpace: buildPerSpaceBreakdown(orderedResolutions, aggregate.app.spaceId, { includeMarkers: true })
96
+ });
97
+ }
98
+ /**
99
+ * Project the planner's per-space resolutions into the
100
+ * `AggregatePerSpaceExecutionEntry[]` shape the CLI surfaces.
101
+ *
102
+ * `includeMarkers` is `true` for apply-mode (each space's marker is
103
+ * the `destination.storageHash` of its plan, which the runner
104
+ * advances as the last step of each space's transaction) and `false`
105
+ * for plan-mode (no marker has been written yet).
106
+ *
107
+ * Exported alongside {@link applyAggregate} so plan-mode callers can
108
+ * assemble the same per-space block without going through the runner.
109
+ */
110
+ function buildPerSpaceBreakdown(orderedResolutions, appSpaceId, options) {
111
+ return orderedResolutions.map((r) => {
112
+ const operations = r.entry.displayOps.map((op) => ({
113
+ id: op.id,
114
+ label: op.label,
115
+ operationClass: op.operationClass
116
+ }));
117
+ const base = {
118
+ spaceId: r.spaceId,
119
+ kind: r.spaceId === appSpaceId ? "app" : "extension",
120
+ operations
121
+ };
122
+ if (!options.includeMarkers) return base;
123
+ return {
124
+ ...base,
125
+ marker: { storageHash: r.entry.plan.destination.storageHash }
126
+ };
127
+ });
128
+ }
129
+ /**
130
+ * Materialise the `applyOrder` ordering into resolved per-space
131
+ * entries. Throws if the planner output is missing a member listed
132
+ * in `applyOrder` — a wiring bug that should never reach runtime.
133
+ *
134
+ * Exported so callers building their own success envelopes after a
135
+ * plan-mode dispatch can replay the same ordering.
136
+ */
137
+ function collectOrdered(applyOrder, perSpace) {
138
+ return applyOrder.map((spaceId) => {
139
+ const entry = perSpace.get(spaceId);
140
+ if (!entry) throw new Error(`Aggregate planner output missing per-space plan for "${spaceId}"`);
141
+ return {
142
+ spaceId,
143
+ entry
144
+ };
145
+ });
146
+ }
147
+ /**
148
+ * Action-appropriate label for the `spanStart` event the apply
149
+ * primitive emits. `applyAggregate` is shared by `db init`, `db update`,
150
+ * and `migration apply`; the span label tracks the user-visible action
151
+ * so structured-progress output reads naturally for each surface.
152
+ */
153
+ function progressLabelForAction(action) {
154
+ switch (action) {
155
+ case "dbInit": return "Initialising database across spaces";
156
+ case "dbUpdate": return "Updating database across spaces";
157
+ case "migrationApply": return "Applying migration plan across spaces";
158
+ }
159
+ }
160
+ function labelForAction(action) {
161
+ switch (action) {
162
+ case "dbInit": return "db init";
163
+ case "dbUpdate": return "db update";
164
+ case "migrationApply": return "migration apply";
165
+ }
166
+ }
167
+ //#endregion
168
+ //#region src/control-api/operations/migration-helpers.ts
169
+ /**
170
+ * Strips operation objects to their public shape (id, label, operationClass).
171
+ * Used at the API boundary to avoid leaking internal fields (precheck, execute, postcheck, etc.).
172
+ */
173
+ function stripOperations(operations) {
174
+ return operations.map((op) => ({
175
+ id: op.id,
176
+ label: op.label,
177
+ operationClass: op.operationClass
178
+ }));
179
+ }
180
+ //#endregion
181
+ //#region src/control-api/operations/db-apply-aggregate.ts
182
+ /**
183
+ * Span IDs emitted via `onProgress` during the aggregate apply flow.
184
+ * Stable identifiers consumed by the structured-output renderer and by
185
+ * tests asserting on span ids. The `apply` span itself is owned by
186
+ * the {@link applyAggregate} primitive — only the introspect / plan
187
+ * spans are emitted directly here.
188
+ */
189
+ const SPAN_IDS$1 = {
190
+ introspect: "introspect",
191
+ plan: "plan"
192
+ };
193
+ /**
194
+ * Loader → planner → runner pipeline shared by `db init` and `db update`.
195
+ *
196
+ * The pipeline:
197
+ *
198
+ * 1. **Load**: build a {@link ContractSpaceAggregate} from the descriptor
199
+ * set + on-disk on-disk artefacts. Any layout / drift / disjointness /
200
+ * integrity violation short-circuits with a structured error.
201
+ * 2. **Read DB state**: marker rows (`familyInstance.readAllMarkers`)
202
+ * + introspected schema (`familyInstance.introspect`).
203
+ * 3. **Plan**: {@link planAggregate} chooses graph-walk vs synth per
204
+ * member according to `callerPolicy.ignoreGraphFor`. The app member
205
+ * is forced through synth (today's daily-driver behaviour); every
206
+ * extension member walks its on-disk graph.
207
+ * 4. **Apply** (when `mode === 'apply'`): every per-space `MigrationPlan`
208
+ * feeds into the runner's `executeAcrossSpaces` — one outer
209
+ * transaction across every space; failure on any space rolls back
210
+ * every space's writes.
211
+ */
212
+ async function executeAggregateApply(options) {
213
+ const { driver, familyInstance, contract, mode, migrations, frameworkComponents, migrationsDir, extensionPacks, targetId, policy, action, onProgress } = options;
214
+ const loaded = await buildContractSpaceAggregate({
215
+ targetId,
216
+ migrationsDir,
217
+ appContract: contract,
218
+ extensionPacks,
219
+ validateContract: (json) => familyInstance.validateContract(json)
220
+ });
221
+ if (!loaded.ok) throw loaded.failure;
222
+ const aggregate = loaded.value;
223
+ const markerRows = await familyInstance.readAllMarkers({ driver });
224
+ if (mode === "apply") {
225
+ const orphanMarkerError = detectOrphanMarkers(aggregate, markerRows);
226
+ if (orphanMarkerError !== null) throw orphanMarkerError;
227
+ }
228
+ onProgress?.({
229
+ action,
230
+ kind: "spanStart",
231
+ spanId: SPAN_IDS$1.introspect,
232
+ label: "Introspecting database schema"
233
+ });
234
+ const schemaIR = await familyInstance.introspect({ driver });
235
+ onProgress?.({
236
+ action,
237
+ kind: "spanEnd",
238
+ spanId: SPAN_IDS$1.introspect,
239
+ outcome: "ok"
240
+ });
241
+ onProgress?.({
242
+ action,
243
+ kind: "spanStart",
244
+ spanId: SPAN_IDS$1.plan,
245
+ label: "Planning migration"
246
+ });
247
+ const planResult = await planAggregate({
248
+ aggregate,
249
+ currentDBState: {
250
+ markersBySpaceId: markerRows,
251
+ schemaIntrospection: schemaIR
252
+ },
253
+ familyInstance,
254
+ migrations,
255
+ frameworkComponents,
256
+ callerPolicy: { ignoreGraphFor: new Set([aggregate.app.spaceId]) },
257
+ operationPolicy: policy
258
+ });
259
+ if (!planResult.ok) {
260
+ onProgress?.({
261
+ action,
262
+ kind: "spanEnd",
263
+ spanId: SPAN_IDS$1.plan,
264
+ outcome: "error"
265
+ });
266
+ return mapPlannerError(planResult.failure);
267
+ }
268
+ onProgress?.({
269
+ action,
270
+ kind: "spanEnd",
271
+ spanId: SPAN_IDS$1.plan,
272
+ outcome: "ok"
273
+ });
274
+ const orderedResolutions = collectOrdered(planResult.value.applyOrder, planResult.value.perSpace);
275
+ const appResolution = orderedResolutions.find((r) => r.spaceId === aggregate.app.spaceId);
276
+ if (!appResolution) throw new Error("Aggregate planner returned no plan for the app member — the planner is supposed to always emit one.");
277
+ const appPlan = appResolution.entry.plan;
278
+ if (mode === "plan") {
279
+ const aggregateOps = orderedResolutions.flatMap((r) => r.entry.displayOps);
280
+ const preview = hasOperationPreview(familyInstance) ? familyInstance.toOperationPreview(aggregateOps) : void 0;
281
+ const perSpace = buildPerSpaceBreakdown(orderedResolutions, aggregate.app.spaceId, { includeMarkers: false });
282
+ const summary = `Planned ${aggregateOps.length} operation(s) across ${orderedResolutions.length} space(s)`;
283
+ return wrapPlanResult({
284
+ operations: aggregateOps,
285
+ destination: appPlan.destination,
286
+ preview,
287
+ perSpace,
288
+ summary
289
+ });
290
+ }
291
+ const applied = await applyAggregate({
292
+ aggregate,
293
+ perSpacePlans: planResult.value.perSpace,
294
+ applyOrder: planResult.value.applyOrder,
295
+ driver,
296
+ familyInstance,
297
+ migrations,
298
+ frameworkComponents,
299
+ policy,
300
+ action,
301
+ ...ifDefined("onProgress", onProgress)
302
+ });
303
+ if (!applied.ok) return buildRunnerFailure({
304
+ summary: applied.failure.summary,
305
+ ...ifDefined("why", applied.failure.why),
306
+ meta: applied.failure.meta
307
+ });
308
+ const aggregateOps = applied.value.orderedResolutions.flatMap((r) => r.entry.displayOps);
309
+ const summary = action === "dbInit" ? `Applied ${applied.value.totalOpsExecuted} operation(s) across ${applied.value.orderedResolutions.length} space(s), database signed` : applied.value.totalOpsExecuted === 0 ? `Database already matches contract across ${applied.value.orderedResolutions.length} space(s), signature updated` : `Applied ${applied.value.totalOpsExecuted} operation(s) across ${applied.value.orderedResolutions.length} space(s), signature updated`;
310
+ return wrapApplyResult({
311
+ operations: aggregateOps,
312
+ destination: appPlan.destination,
313
+ operationsPlanned: applied.value.totalOpsPlanned,
314
+ operationsExecuted: applied.value.totalOpsExecuted,
315
+ perSpace: applied.value.perSpace,
316
+ summary
317
+ });
318
+ }
319
+ /**
320
+ * Compare the live `_prisma_marker` rows against the aggregate's
321
+ * declared members. Any marker row whose `space` is not a member of
322
+ * the aggregate is an "orphan" — typically a marker left behind by
323
+ * an extension that was removed from `extensionPacks` without first
324
+ * cleaning up its on-disk migrations / database tables.
325
+ *
326
+ * Returns a {@link CliStructuredError} envelope (code `5002`,
327
+ * `kind: 'orphanMarker'`) for the first orphan it finds, or `null`
328
+ * when every marker row maps to a declared member. Mirrors the M2
329
+ * `runContractSpaceVerifierMarkerCheck` envelope so downstream
330
+ * tooling (integration tests, JSON consumers) keeps asserting on the
331
+ * same shape.
332
+ */
333
+ function detectOrphanMarkers(aggregate, markerRows) {
334
+ const memberSpaceIds = new Set([aggregate.app.spaceId, ...aggregate.extensions.map((m) => m.spaceId)]);
335
+ const orphans = [];
336
+ for (const [spaceId, row] of markerRows) if (row !== null && row !== void 0 && !memberSpaceIds.has(spaceId)) orphans.push(spaceId);
337
+ if (orphans.length === 0) return null;
338
+ orphans.sort((a, b) => a.localeCompare(b));
339
+ return new CliStructuredError("5002", orphans.length === 1 ? `Orphan contract-space marker detected for "${orphans[0]}"` : `Orphan contract-space markers detected for ${orphans.length} spaces`, {
340
+ domain: "MIG",
341
+ why: `The database has \`_prisma_marker\` rows for spaces (${orphans.map((s) => `"${s}"`).join(", ")}) that are not declared in the project's \`extensionPacks\`. The aggregate pipeline refuses to advance markers it cannot account for.`,
342
+ fix: "Either re-declare the missing extension(s) in `extensionPacks` (so the aggregate owns them again), or remove the orphan marker row(s) from `_prisma_marker` once you have confirmed the corresponding tables can be safely retired.",
343
+ docsUrl: "https://pris.ly/contract-spaces",
344
+ meta: { violations: orphans.map((spaceId) => ({
345
+ kind: "orphanMarker",
346
+ spaceId
347
+ })) }
348
+ });
349
+ }
350
+ function mapPlannerError(error) {
351
+ if (error.kind === "appSynthFailure") return notOk({
352
+ code: "PLANNING_FAILED",
353
+ summary: "Migration planning failed due to conflicts",
354
+ conflicts: error.conflicts,
355
+ why: void 0,
356
+ meta: void 0
357
+ });
358
+ if (error.kind === "extensionPathUnreachable") return buildRunnerFailure({
359
+ summary: `Cannot resolve apply path for extension space "${error.spaceId}"`,
360
+ why: `No path in the on-disk migration graph for extension space "${error.spaceId}" reaches the on-disk head ref hash "${error.target}".`,
361
+ meta: {
362
+ spaceId: error.spaceId,
363
+ target: error.target
364
+ }
365
+ });
366
+ if (error.kind === "extensionPathUnsatisfiable") return buildRunnerFailure({
367
+ summary: `Cannot resolve apply path for extension space "${error.spaceId}"`,
368
+ why: `On-disk migration graph for extension space "${error.spaceId}" reaches the on-disk head ref but does not cover required invariants: ${error.missingInvariants.join(", ")}.`,
369
+ meta: {
370
+ spaceId: error.spaceId,
371
+ missingInvariants: error.missingInvariants
372
+ }
373
+ });
374
+ return buildRunnerFailure({
375
+ summary: `Aggregate planner policy conflict for space "${error.spaceId}"`,
376
+ why: error.detail,
377
+ meta: { spaceId: error.spaceId }
378
+ });
379
+ }
380
+ function wrapPlanResult(args) {
381
+ return ok({
382
+ mode: "plan",
383
+ plan: {
384
+ operations: stripOperations(args.operations),
385
+ ...ifDefined("preview", args.preview)
386
+ },
387
+ destination: {
388
+ storageHash: args.destination.storageHash,
389
+ ...ifDefined("profileHash", args.destination.profileHash)
390
+ },
391
+ perSpace: args.perSpace,
392
+ summary: args.summary
393
+ });
394
+ }
395
+ function wrapApplyResult(args) {
396
+ return ok({
397
+ mode: "apply",
398
+ plan: { operations: stripOperations(args.operations) },
399
+ destination: {
400
+ storageHash: args.destination.storageHash,
401
+ ...ifDefined("profileHash", args.destination.profileHash)
402
+ },
403
+ execution: {
404
+ operationsPlanned: args.operationsPlanned,
405
+ operationsExecuted: args.operationsExecuted
406
+ },
407
+ marker: args.destination.profileHash ? {
408
+ storageHash: args.destination.storageHash,
409
+ profileHash: args.destination.profileHash
410
+ } : { storageHash: args.destination.storageHash },
411
+ perSpace: args.perSpace,
412
+ summary: args.summary
413
+ });
414
+ }
415
+ function buildRunnerFailure(args) {
416
+ return notOk({
417
+ code: "RUNNER_FAILED",
418
+ summary: args.summary,
419
+ why: args.why,
420
+ meta: args.meta,
421
+ conflicts: void 0
422
+ });
423
+ }
424
+ //#endregion
425
+ //#region src/control-api/operations/db-init.ts
426
+ /**
427
+ * Execute `db init` against the configured contract.
428
+ *
429
+ * Routes through the loader → planner → runner pipeline (sub-spec
430
+ * "Commit-by-commit § Commit 4"). Always additive-only; destructive
431
+ * changes belong to `db update`.
432
+ */
433
+ async function executeDbInit(options) {
434
+ return await executeAggregateApply({
435
+ driver: options.driver,
436
+ familyInstance: options.familyInstance,
437
+ contract: options.contract,
438
+ mode: options.mode,
439
+ migrations: options.migrations,
440
+ frameworkComponents: options.frameworkComponents,
441
+ migrationsDir: options.migrationsDir,
442
+ targetId: options.targetId,
443
+ extensionPacks: options.extensionPacks ?? [],
444
+ policy: { allowedOperationClasses: ["additive"] },
445
+ action: "dbInit",
446
+ ...ifDefined("onProgress", options.onProgress)
447
+ });
448
+ }
449
+ //#endregion
450
+ //#region src/control-api/operations/db-update.ts
451
+ const DB_UPDATE_POLICY = { allowedOperationClasses: [
452
+ "additive",
453
+ "widening",
454
+ "destructive"
455
+ ] };
456
+ /**
457
+ * Execute `db update` against the configured contract.
458
+ *
459
+ * Routes through the loader → planner → runner pipeline. Destructive
460
+ * operations require either `acceptDataLoss: true` or a prior
461
+ * `mode: 'plan'` invocation that surfaces the destructive ops; the
462
+ * confirmation gate is implemented here so the lower-level applier
463
+ * remains policy-agnostic.
464
+ */
465
+ async function executeDbUpdate(options) {
466
+ const sharedInputs = {
467
+ driver: options.driver,
468
+ familyInstance: options.familyInstance,
469
+ contract: options.contract,
470
+ migrations: options.migrations,
471
+ frameworkComponents: options.frameworkComponents,
472
+ migrationsDir: options.migrationsDir,
473
+ targetId: options.targetId,
474
+ extensionPacks: options.extensionPacks ?? [],
475
+ policy: DB_UPDATE_POLICY,
476
+ action: "dbUpdate",
477
+ ...ifDefined("onProgress", options.onProgress)
478
+ };
479
+ if (options.mode === "apply" && !options.acceptDataLoss) {
480
+ const gate = await guardDestructiveChanges(sharedInputs);
481
+ if (gate !== null) return gate;
482
+ }
483
+ return await executeAggregateApply({
484
+ ...sharedInputs,
485
+ mode: options.mode
486
+ });
487
+ }
488
+ /**
489
+ * Pre-plan once when running `db update apply` without `acceptDataLoss`.
490
+ * Surfaces destructive operations across every space; if any are
491
+ * planned, returns a `DESTRUCTIVE_CHANGES` failure that the CLI shows
492
+ * as a confirmation prompt. Returns `null` when the apply is safe to
493
+ * run.
494
+ */
495
+ async function guardDestructiveChanges(sharedInputs) {
496
+ const planResult = await executeAggregateApply({
497
+ ...sharedInputs,
498
+ mode: "plan"
499
+ });
500
+ if (!planResult.ok) return planResult;
501
+ const destructiveOps = planResult.value.plan.operations.filter((op) => op.operationClass === "destructive").map((op) => ({
502
+ id: op.id,
503
+ label: op.label
504
+ }));
505
+ if (destructiveOps.length === 0) return null;
506
+ return notOk({
507
+ code: "DESTRUCTIVE_CHANGES",
508
+ summary: `Planned ${destructiveOps.length} destructive operation(s) that require confirmation`,
509
+ why: "Destructive operations require confirmation — re-run with -y to accept",
510
+ conflicts: void 0,
511
+ meta: { destructiveOperations: destructiveOps }
512
+ });
513
+ }
514
+ //#endregion
515
+ //#region src/control-api/operations/db-verify.ts
516
+ /**
517
+ * Span IDs emitted via `onProgress` during the aggregate verify flow.
518
+ * Mirrors the span identifiers used by the legacy precheck / marker-check
519
+ * helpers so structured-output renderers and progress tests keep working.
520
+ */
521
+ const SPAN_IDS = {
522
+ introspect: "introspect",
523
+ verify: "verify"
524
+ };
525
+ /**
526
+ * Loader → verifier pipeline shared by `db verify` modes (`full`,
527
+ * `marker-only`, `schema-only`).
528
+ *
529
+ * 1. **Load**: build a {@link import('@prisma-next/migration-tools/aggregate').ContractSpaceAggregate}
530
+ * from descriptors + on-disk on-disk artefacts. Layout / drift /
531
+ * integrity / disjointness violations short-circuit with a
532
+ * structured CLI error.
533
+ * 2. **Read DB state**: marker rows + (when `skipSchema` is `false`)
534
+ * schema introspection.
535
+ * 3. **Verify**: {@link verifyAggregate} returns per-space
536
+ * `markerCheck` + per-space pre-projected `schemaCheck` (closes F23).
537
+ * Marker mismatches map to `CliStructuredError` (code `5002`) so
538
+ * callers (CLI command) can render and exit. Schema results are
539
+ * returned to the caller verbatim.
540
+ */
541
+ async function executeDbVerify(options) {
542
+ const { driver, familyInstance, onProgress, skipSchema, skipMarker } = options;
543
+ const loaded = await buildContractSpaceAggregate(buildLoadInputs(options));
544
+ if (!loaded.ok) return notOk(loaded.failure);
545
+ const aggregate = loaded.value;
546
+ const markersBySpaceId = await familyInstance.readAllMarkers({ driver });
547
+ const schemaIntrospection = skipSchema ? null : await runIntrospection({
548
+ driver,
549
+ familyInstance,
550
+ onProgress
551
+ });
552
+ emitVerifySpan(onProgress, "spanStart");
553
+ return finaliseVerifyResult({
554
+ verifyResult: verifyAggregate({
555
+ aggregate,
556
+ markersBySpaceId,
557
+ schemaIntrospection,
558
+ mode: options.mode,
559
+ verifySchemaForMember: createPerMemberVerifier(options)
560
+ }),
561
+ aggregate,
562
+ skipMarker,
563
+ onProgress
564
+ });
565
+ }
566
+ function buildLoadInputs(options) {
567
+ return {
568
+ targetId: options.targetId,
569
+ migrationsDir: options.migrationsDir,
570
+ appContract: options.contract,
571
+ extensionPacks: options.extensionPacks,
572
+ validateContract: (json) => options.familyInstance.validateContract(json)
573
+ };
574
+ }
575
+ async function runIntrospection(args) {
576
+ const { driver, familyInstance, onProgress } = args;
577
+ onProgress?.({
578
+ action: "dbVerify",
579
+ kind: "spanStart",
580
+ spanId: SPAN_IDS.introspect,
581
+ label: "Introspecting database schema"
582
+ });
583
+ try {
584
+ const result = await familyInstance.introspect({ driver });
585
+ onProgress?.({
586
+ action: "dbVerify",
587
+ kind: "spanEnd",
588
+ spanId: SPAN_IDS.introspect,
589
+ outcome: "ok"
590
+ });
591
+ return result;
592
+ } catch (error) {
593
+ onProgress?.({
594
+ action: "dbVerify",
595
+ kind: "spanEnd",
596
+ spanId: SPAN_IDS.introspect,
597
+ outcome: "error"
598
+ });
599
+ throw error;
600
+ }
601
+ }
602
+ /**
603
+ * Build the per-member schema callback handed to the aggregate verifier.
604
+ * When `skipSchema` is true the callback short-circuits with a synthetic
605
+ * `ok` result so the verifier still runs the (cheap) schemaCheck loop
606
+ * without invoking the family's verification path.
607
+ */
608
+ function createPerMemberVerifier(options) {
609
+ const { skipSchema, familyInstance, frameworkComponents } = options;
610
+ return (projectedSchema, member, verifyMode) => {
611
+ if (skipSchema) return buildSkippedSchemaResult(member);
612
+ return familyInstance.schemaVerifyAgainstSchema({
613
+ contract: member.contract,
614
+ schema: projectedSchema,
615
+ strict: verifyMode === "strict",
616
+ frameworkComponents
617
+ });
618
+ };
619
+ }
620
+ function emitVerifySpan(onProgress, kind) {
621
+ if (kind === "spanStart") {
622
+ onProgress?.({
623
+ action: "dbVerify",
624
+ kind: "spanStart",
625
+ spanId: SPAN_IDS.verify,
626
+ label: "Verifying contract spaces"
627
+ });
628
+ return;
629
+ }
630
+ onProgress?.({
631
+ action: "dbVerify",
632
+ kind: "spanEnd",
633
+ spanId: SPAN_IDS.verify,
634
+ outcome: kind === "spanEndOk" ? "ok" : "error"
635
+ });
636
+ }
637
+ /**
638
+ * Map an {@link AggregateVerifierOutput} to the operation's
639
+ * {@link ExecuteDbVerifyResult}, applying the `skipMarker` policy used
640
+ * by the CLI's `--schema-only` mode.
641
+ */
642
+ function finaliseVerifyResult(args) {
643
+ const { verifyResult, aggregate, skipMarker, onProgress } = args;
644
+ if (!verifyResult.ok) {
645
+ emitVerifySpan(onProgress, "spanEndError");
646
+ return notOk(new CliStructuredError("5002", "Aggregate verifier introspection failed", {
647
+ domain: "MIG",
648
+ why: verifyResult.failure.detail,
649
+ fix: "Check database connectivity and the introspection tooling.",
650
+ docsUrl: "https://pris.ly/contract-spaces"
651
+ }));
652
+ }
653
+ const markerError = skipMarker ? null : mapMarkerCheckFailures(aggregate.app.spaceId, verifyResult.value.markerCheck);
654
+ if (markerError !== null) {
655
+ emitVerifySpan(onProgress, "spanEndError");
656
+ return notOk(markerError);
657
+ }
658
+ emitVerifySpan(onProgress, "spanEndOk");
659
+ return ok({
660
+ schemaResults: verifyResult.value.schemaCheck.perSpace,
661
+ memberOrder: [aggregate.app.spaceId, ...aggregate.extensions.map((e) => e.spaceId)],
662
+ appSpaceId: aggregate.app.spaceId
663
+ });
664
+ }
665
+ function buildSkippedSchemaResult(member) {
666
+ const profileHash = member.contract.profileHash;
667
+ return {
668
+ ok: true,
669
+ summary: "Schema verification skipped",
670
+ contract: {
671
+ storageHash: member.headRef.hash,
672
+ ...profileHash ? { profileHash } : {}
673
+ },
674
+ target: { expected: member.contract.target },
675
+ schema: {
676
+ issues: [],
677
+ root: {
678
+ status: "pass",
679
+ kind: "skipped",
680
+ name: member.spaceId,
681
+ contractPath: "",
682
+ code: "SKIPPED",
683
+ message: "Schema verification skipped",
684
+ expected: void 0,
685
+ actual: void 0,
686
+ children: []
687
+ },
688
+ counts: {
689
+ pass: 0,
690
+ warn: 0,
691
+ fail: 0,
692
+ totalNodes: 0
693
+ }
694
+ },
695
+ timings: { total: 0 }
696
+ };
697
+ }
698
+ /**
699
+ * Translate per-space marker check failures and orphan markers into a
700
+ * single CLI structured error envelope. Preserves the legacy code
701
+ * `5002` (was emitted by `runContractSpaceVerifierMarkerCheck`).
702
+ */
703
+ function mapMarkerCheckFailures(appSpaceId, section) {
704
+ const violations = [];
705
+ for (const [spaceId, result] of section.perSpace) {
706
+ if (result.kind === "ok" || result.kind === "absent") continue;
707
+ if (result.kind === "hashMismatch") {
708
+ violations.push({
709
+ kind: "hashMismatch",
710
+ spaceId,
711
+ remediation: spaceId === appSpaceId ? "Run `prisma-next db update` to advance the marker, or roll the database back to the recorded hash." : `Apply on-disk migrations under \`migrations/${spaceId}/\` to advance the marker, or remove the conflicting marker row.`
712
+ });
713
+ continue;
714
+ }
715
+ if (result.kind === "missingInvariants") violations.push({
716
+ kind: "invariantsMismatch",
717
+ spaceId,
718
+ remediation: `Re-apply the migrations under \`migrations/${spaceId}/\` so the marker carries invariants: ${result.missing.join(", ")}.`
719
+ });
720
+ }
721
+ for (const orphan of section.orphanMarkers) violations.push({
722
+ kind: "orphanMarker",
723
+ spaceId: orphan.spaceId,
724
+ remediation: `Add the corresponding extension to \`extensionPacks\` in \`prisma-next.config.ts\`, or delete the orphan marker row for "${orphan.spaceId}".`
725
+ });
726
+ if (violations.length === 0) return null;
727
+ const lines = violations.map((v) => `- [${v.kind}] ${v.spaceId}: ${v.remediation}`);
728
+ return new CliStructuredError("5002", violations.length === 1 ? "Contract-space verifier found a violation" : `Contract-space verifier found violations (${violations.length})`, {
729
+ domain: "MIG",
730
+ why: `The on-disk \`migrations/\` directory, the \`extensionPacks\` declaration, and the live database marker rows are not in agreement.\n${lines.join("\n")}`,
731
+ fix: violations[0]?.remediation ?? "Review and reconcile the violations listed above.",
732
+ docsUrl: "https://pris.ly/contract-spaces",
733
+ meta: { violations }
734
+ });
735
+ }
736
+ //#endregion
737
+ //#region src/control-api/operations/migration-apply.ts
738
+ /**
739
+ * Apply pending migrations across every contract space (app +
740
+ * extensions). Replay-only: graph-walk against the on-disk graph for
741
+ * every member; no synth, no introspection.
742
+ *
743
+ * Pipeline:
744
+ *
745
+ * 1. Load aggregate from disk (loader hydrates extension graphs;
746
+ * caller provides app-space packages).
747
+ * 2. Read live marker rows per space (`familyInstance.readAllMarkers`).
748
+ * 3. Per member: `graphWalkStrategy` plots the path from the live
749
+ * marker to `member.headRef.hash` (or `refHash` for the app
750
+ * member when provided). Empty-graph members fail loudly — a
751
+ * "never planned" space is a user-error condition for replay.
752
+ * 4. Hand off to {@link applyAggregate} (the runner-driving tail
753
+ * shared with `db init` / `db update`). Marker advancement is
754
+ * inside the per-space transaction.
755
+ *
756
+ * Sub-spec § `migration apply` semantics + § Required changes 1.
757
+ */
758
+ async function executeMigrationApply(options) {
759
+ const { driver, familyInstance, contract, migrations, frameworkComponents, migrationsDir, extensionPacks, targetId, appMigrationPackages, refHash, refInvariants, refName, onProgress } = options;
760
+ const loaded = await buildContractSpaceAggregate({
761
+ targetId,
762
+ migrationsDir,
763
+ appContract: contract,
764
+ extensionPacks,
765
+ validateContract: (json) => familyInstance.validateContract(json),
766
+ appMigrationPackages
767
+ });
768
+ if (!loaded.ok) throw loaded.failure;
769
+ const aggregate = loaded.value;
770
+ const markerRows = await familyInstance.readAllMarkers({ driver });
771
+ const allMembers = [aggregate.app, ...aggregate.extensions];
772
+ const perSpacePlans = /* @__PURE__ */ new Map();
773
+ const atHeadResolutions = /* @__PURE__ */ new Map();
774
+ for (const member of allMembers) {
775
+ const isAppMember = member.spaceId === aggregate.app.spaceId;
776
+ const targetHash = isAppMember && refHash !== void 0 ? refHash : member.headRef.hash;
777
+ const liveMarker = markerRows.get(member.spaceId) ?? null;
778
+ if (member.migrations.graph.nodes.size === 0) {
779
+ const liveHash = liveMarker?.storageHash;
780
+ if (targetHash === liveHash || liveHash === void 0 && targetHash === EMPTY_CONTRACT_HASH) {
781
+ atHeadResolutions.set(member.spaceId, buildAtHeadResolution({
782
+ aggregateTargetId: aggregate.targetId,
783
+ member,
784
+ targetHash,
785
+ liveMarker
786
+ }));
787
+ continue;
788
+ }
789
+ return notOk(buildNeverPlannedFailure(member.spaceId, targetHash));
790
+ }
791
+ const targetInvariants = isAppMember && refHash !== void 0 && refInvariants !== void 0 ? refInvariants : member.headRef.invariants;
792
+ const targetMember = targetHash === member.headRef.hash && targetInvariants === member.headRef.invariants ? member : {
793
+ ...member,
794
+ headRef: {
795
+ hash: targetHash,
796
+ invariants: targetInvariants
797
+ }
798
+ };
799
+ const walked = graphWalkStrategy({
800
+ aggregateTargetId: aggregate.targetId,
801
+ member: targetMember,
802
+ currentMarker: liveMarker,
803
+ ...isAppMember && refName !== void 0 ? { refName } : {}
804
+ });
805
+ if (walked.kind === "unreachable") return notOk(buildPathNotFoundFailure(member.spaceId, liveMarker, targetHash));
806
+ if (walked.kind === "unsatisfiable") {
807
+ const fromHash = liveMarker?.storageHash ?? EMPTY_CONTRACT_HASH;
808
+ const structural = findPathWithDecision(targetMember.migrations.graph, fromHash, targetHash, { required: /* @__PURE__ */ new Set() });
809
+ const structuralPath = structural.kind === "ok" ? structural.decision.selectedPath.map((edge) => ({
810
+ dirName: edge.dirName,
811
+ migrationHash: edge.migrationHash,
812
+ from: edge.from,
813
+ to: edge.to,
814
+ invariants: edge.invariants
815
+ })) : [];
816
+ throw errorNoInvariantPath({
817
+ ...isAppMember && refName !== void 0 ? { refName } : {},
818
+ required: targetInvariants,
819
+ missing: walked.missing,
820
+ structuralPath
821
+ });
822
+ }
823
+ perSpacePlans.set(member.spaceId, walked.result);
824
+ }
825
+ const canonicalOrder = [...aggregate.extensions.map((m) => m.spaceId), aggregate.app.spaceId];
826
+ const applyOrder = canonicalOrder.filter((spaceId) => perSpacePlans.has(spaceId));
827
+ if (sumPlannedOps(applyOrder, perSpacePlans) === 0) {
828
+ const ordered = canonicalOrder.filter((spaceId) => perSpacePlans.has(spaceId) || atHeadResolutions.has(spaceId)).map((spaceId) => {
829
+ const entry = perSpacePlans.get(spaceId) ?? atHeadResolutions.get(spaceId);
830
+ if (entry === void 0) throw new Error(`Unreachable: missing per-space plan for "${spaceId}"`);
831
+ return {
832
+ spaceId,
833
+ entry
834
+ };
835
+ });
836
+ const perSpace = buildPerSpaceBreakdown(ordered, aggregate.app.spaceId, { includeMarkers: true });
837
+ const totalSpaces = ordered.length;
838
+ return ok(buildSuccess({
839
+ aggregate,
840
+ orderedResolutions: ordered,
841
+ perSpace,
842
+ totalOpsExecuted: 0,
843
+ summary: totalSpaces === 0 ? "Already up to date — no contract spaces are loaded" : totalSpaces === 1 ? "Already up to date" : `Already up to date across ${totalSpaces} space(s)`
844
+ }));
845
+ }
846
+ const applied = await applyAggregate({
847
+ aggregate,
848
+ perSpacePlans,
849
+ applyOrder,
850
+ driver,
851
+ familyInstance,
852
+ migrations,
853
+ frameworkComponents,
854
+ policy: { allowedOperationClasses: [
855
+ "additive",
856
+ "widening",
857
+ "destructive",
858
+ "data"
859
+ ] },
860
+ action: "migrationApply",
861
+ ...ifDefined("onProgress", onProgress)
862
+ });
863
+ if (!applied.ok) return notOk({
864
+ code: "RUNNER_FAILED",
865
+ summary: applied.failure.summary,
866
+ why: applied.failure.why,
867
+ meta: applied.failure.meta
868
+ });
869
+ const orderedAll = canonicalOrder.filter((spaceId) => perSpacePlans.has(spaceId) || atHeadResolutions.has(spaceId)).map((spaceId) => {
870
+ if (perSpacePlans.has(spaceId)) {
871
+ const fromRunner = applied.value.orderedResolutions.find((r) => r.spaceId === spaceId);
872
+ if (fromRunner !== void 0) return fromRunner;
873
+ }
874
+ const entry = atHeadResolutions.get(spaceId);
875
+ if (entry === void 0) throw new Error(`Unreachable: missing per-space plan for "${spaceId}"`);
876
+ return {
877
+ spaceId,
878
+ entry
879
+ };
880
+ });
881
+ const perSpaceAll = buildPerSpaceBreakdown(orderedAll, aggregate.app.spaceId, { includeMarkers: true });
882
+ const summary = `Applied ${applied.value.orderedResolutions.reduce((sum, r) => sum + (r.entry.migrationEdges?.length ?? 0), 0)} migration(s) (${applied.value.totalOpsExecuted} operation(s)) across ${orderedAll.length} contract space(s)`;
883
+ return ok(buildSuccess({
884
+ aggregate,
885
+ orderedResolutions: orderedAll,
886
+ perSpace: perSpaceAll,
887
+ totalOpsExecuted: applied.value.totalOpsExecuted,
888
+ summary
889
+ }));
890
+ }
891
+ /**
892
+ * Build a zero-op {@link AggregatePerSpacePlan} for an empty-graph
893
+ * member whose live marker already matches the target. Lets the apply
894
+ * pipeline thread the member through `perSpacePlans` -> `applyOrder`
895
+ * -> the success envelope's `perSpace[]` block so the result reflects
896
+ * every loaded space, even when there is nothing to execute.
897
+ */
898
+ function buildAtHeadResolution(args) {
899
+ const { aggregateTargetId, member, targetHash, liveMarker } = args;
900
+ return {
901
+ plan: {
902
+ targetId: aggregateTargetId,
903
+ spaceId: member.spaceId,
904
+ origin: liveMarker === null ? null : { storageHash: liveMarker.storageHash },
905
+ destination: { storageHash: targetHash },
906
+ operations: [],
907
+ providedInvariants: []
908
+ },
909
+ displayOps: [],
910
+ destinationContract: member.contract,
911
+ strategy: "graph-walk",
912
+ migrationEdges: []
913
+ };
914
+ }
915
+ function sumPlannedOps(applyOrder, perSpacePlans) {
916
+ let total = 0;
917
+ for (const spaceId of applyOrder) {
918
+ const entry = perSpacePlans.get(spaceId);
919
+ if (!entry) continue;
920
+ total += entry.plan.operations.length;
921
+ }
922
+ return total;
923
+ }
924
+ function buildSuccess(args) {
925
+ const appResolution = args.orderedResolutions.find((r) => r.spaceId === args.aggregate.app.spaceId);
926
+ const appMarkerHash = appResolution?.entry.plan.destination.storageHash ?? args.aggregate.app.headRef.hash;
927
+ const applied = args.orderedResolutions.flatMap((r) => {
928
+ return (r.entry.migrationEdges ?? []).map((edge) => ({
929
+ spaceId: r.spaceId,
930
+ dirName: edge.dirName,
931
+ migrationHash: edge.migrationHash,
932
+ from: edge.from,
933
+ to: edge.to,
934
+ operationsExecuted: edge.operationCount
935
+ }));
936
+ });
937
+ const appPlan = appResolution?.entry;
938
+ const pathDecision = appPlan?.pathDecision ? {
939
+ fromHash: appPlan.pathDecision.fromHash,
940
+ toHash: appPlan.pathDecision.toHash,
941
+ alternativeCount: appPlan.pathDecision.alternativeCount,
942
+ tieBreakReasons: appPlan.pathDecision.tieBreakReasons,
943
+ ...appPlan.pathDecision.refName !== void 0 ? { refName: appPlan.pathDecision.refName } : {},
944
+ requiredInvariants: appPlan.pathDecision.requiredInvariants ?? [],
945
+ satisfiedInvariants: appPlan.pathDecision.satisfiedInvariants ?? [],
946
+ selectedPath: appPlan.pathDecision.selectedPath.map((entry) => ({
947
+ dirName: entry.dirName,
948
+ migrationHash: entry.migrationHash,
949
+ from: entry.from,
950
+ to: entry.to,
951
+ invariants: entry.invariants
952
+ }))
953
+ } : void 0;
954
+ return {
955
+ migrationsApplied: applied.length,
956
+ markerHash: appMarkerHash,
957
+ applied,
958
+ summary: args.summary,
959
+ perSpace: args.perSpace,
960
+ ...pathDecision !== void 0 ? { pathDecision } : {}
961
+ };
962
+ }
963
+ function buildNeverPlannedFailure(spaceId, targetHash) {
964
+ return {
965
+ code: "MIGRATION_PATH_NOT_FOUND",
966
+ summary: `No on-disk migrations for contract space "${spaceId}"`,
967
+ why: `migration apply 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.`,
968
+ meta: {
969
+ spaceId,
970
+ target: targetHash,
971
+ kind: "neverPlanned"
972
+ }
973
+ };
974
+ }
975
+ function buildPathNotFoundFailure(spaceId, marker, targetHash) {
976
+ const fromHash = marker?.storageHash ?? "<empty>";
977
+ return {
978
+ code: "MIGRATION_PATH_NOT_FOUND",
979
+ summary: spaceId === "app" ? "Current contract has no planned migration path" : `Current contract has no planned migration path for contract space "${spaceId}"`,
980
+ 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.`,
981
+ meta: {
982
+ spaceId,
983
+ fromHash,
984
+ targetHash,
985
+ kind: "pathUnreachable"
986
+ }
987
+ };
988
+ }
989
+ //#endregion
990
+ //#region src/control-api/client.ts
991
+ /**
992
+ * Creates a programmatic control client for Prisma Next operations.
993
+ *
994
+ * The client accepts framework component descriptors at creation time,
995
+ * manages driver lifecycle via connect()/close(), and exposes domain
996
+ * operations that delegate to the existing family instance methods.
997
+ *
998
+ * @see {@link ControlClient} for the client interface
999
+ * @see README.md "Programmatic Control API" section for usage examples
1000
+ */
1001
+ function createControlClient(options) {
1002
+ return new ControlClientImpl(options);
1003
+ }
1004
+ /**
1005
+ * Implementation of ControlClient.
1006
+ * Manages initialization and connection state, delegates operations to family instance.
1007
+ */
1008
+ var ControlClientImpl = class {
1009
+ options;
1010
+ stack = null;
1011
+ driver = null;
1012
+ familyInstance = null;
1013
+ frameworkComponents = null;
1014
+ initialized = false;
1015
+ defaultConnection;
1016
+ constructor(options) {
1017
+ this.options = options;
1018
+ this.defaultConnection = options.connection;
1019
+ }
1020
+ init() {
1021
+ if (this.initialized) return;
1022
+ this.stack = createControlStack({
1023
+ family: this.options.family,
1024
+ target: this.options.target,
1025
+ adapter: this.options.adapter,
1026
+ driver: this.options.driver,
1027
+ extensionPacks: this.options.extensionPacks
1028
+ });
1029
+ this.familyInstance = this.options.family.create(this.stack);
1030
+ const rawComponents = [
1031
+ this.options.target,
1032
+ this.options.adapter,
1033
+ ...this.options.extensionPacks ?? []
1034
+ ];
1035
+ this.frameworkComponents = assertFrameworkComponentsCompatible(this.options.family.familyId, this.options.target.targetId, rawComponents);
1036
+ this.initialized = true;
1037
+ }
1038
+ async connect(connection) {
1039
+ this.init();
1040
+ if (this.driver) throw new Error("Already connected. Call close() before reconnecting.");
1041
+ const resolvedConnection = connection ?? this.defaultConnection;
1042
+ if (resolvedConnection === void 0) throw new Error("No connection provided. Pass a connection to connect() or provide a default connection when creating the client.");
1043
+ if (!this.stack?.driver) throw new Error("Driver is not configured. Pass a driver descriptor when creating the control client to enable database operations.");
1044
+ this.driver = await this.stack.driver.create(resolvedConnection);
1045
+ }
1046
+ async close() {
1047
+ if (this.driver) {
1048
+ await this.driver.close();
1049
+ this.driver = null;
1050
+ }
1051
+ }
1052
+ async ensureConnected() {
1053
+ this.init();
1054
+ if (!this.driver && this.defaultConnection !== void 0) await this.connect(this.defaultConnection);
1055
+ if (!this.driver || !this.familyInstance || !this.frameworkComponents) throw new Error("Not connected. Call connect(connection) first.");
1056
+ return {
1057
+ driver: this.driver,
1058
+ familyInstance: this.familyInstance,
1059
+ frameworkComponents: this.frameworkComponents
1060
+ };
1061
+ }
1062
+ async connectWithProgress(connection, action, onProgress) {
1063
+ if (connection === void 0) return;
1064
+ onProgress?.({
1065
+ action,
1066
+ kind: "spanStart",
1067
+ spanId: "connect",
1068
+ label: "Connecting to database..."
1069
+ });
1070
+ try {
1071
+ await this.connect(connection);
1072
+ onProgress?.({
1073
+ action,
1074
+ kind: "spanEnd",
1075
+ spanId: "connect",
1076
+ outcome: "ok"
1077
+ });
1078
+ } catch (error) {
1079
+ onProgress?.({
1080
+ action,
1081
+ kind: "spanEnd",
1082
+ spanId: "connect",
1083
+ outcome: "error"
1084
+ });
1085
+ throw error;
1086
+ }
1087
+ }
1088
+ async verify(options) {
1089
+ const { onProgress } = options;
1090
+ await this.connectWithProgress(options.connection, "verify", onProgress);
1091
+ const { driver, familyInstance } = await this.ensureConnected();
1092
+ let contract;
1093
+ try {
1094
+ contract = familyInstance.validateContract(options.contract);
1095
+ } catch (error) {
1096
+ throw new ContractValidationError(error instanceof Error ? error.message : String(error), error);
1097
+ }
1098
+ onProgress?.({
1099
+ action: "verify",
1100
+ kind: "spanStart",
1101
+ spanId: "verify",
1102
+ label: "Verifying database marker..."
1103
+ });
1104
+ try {
1105
+ const result = await familyInstance.verify({
1106
+ driver,
1107
+ contract,
1108
+ expectedTargetId: this.options.target.targetId,
1109
+ contractPath: ""
1110
+ });
1111
+ onProgress?.({
1112
+ action: "verify",
1113
+ kind: "spanEnd",
1114
+ spanId: "verify",
1115
+ outcome: result.ok ? "ok" : "error"
1116
+ });
1117
+ return result;
1118
+ } catch (error) {
1119
+ onProgress?.({
1120
+ action: "verify",
1121
+ kind: "spanEnd",
1122
+ spanId: "verify",
1123
+ outcome: "error"
1124
+ });
1125
+ throw error;
1126
+ }
1127
+ }
1128
+ async schemaVerify(options) {
1129
+ const { onProgress } = options;
1130
+ await this.connectWithProgress(options.connection, "schemaVerify", onProgress);
1131
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
1132
+ let contract;
1133
+ try {
1134
+ contract = familyInstance.validateContract(options.contract);
1135
+ } catch (error) {
1136
+ throw new ContractValidationError(error instanceof Error ? error.message : String(error), error);
1137
+ }
1138
+ onProgress?.({
1139
+ action: "schemaVerify",
1140
+ kind: "spanStart",
1141
+ spanId: "schemaVerify",
1142
+ label: "Verifying database schema..."
1143
+ });
1144
+ try {
1145
+ const result = await familyInstance.schemaVerify({
1146
+ driver,
1147
+ contract,
1148
+ strict: options.strict ?? false,
1149
+ contractPath: "",
1150
+ frameworkComponents
1151
+ });
1152
+ onProgress?.({
1153
+ action: "schemaVerify",
1154
+ kind: "spanEnd",
1155
+ spanId: "schemaVerify",
1156
+ outcome: result.ok ? "ok" : "error"
1157
+ });
1158
+ return result;
1159
+ } catch (error) {
1160
+ onProgress?.({
1161
+ action: "schemaVerify",
1162
+ kind: "spanEnd",
1163
+ spanId: "schemaVerify",
1164
+ outcome: "error"
1165
+ });
1166
+ throw error;
1167
+ }
1168
+ }
1169
+ async sign(options) {
1170
+ const { onProgress } = options;
1171
+ await this.connectWithProgress(options.connection, "sign", onProgress);
1172
+ const { driver, familyInstance } = await this.ensureConnected();
1173
+ let contract;
1174
+ try {
1175
+ contract = familyInstance.validateContract(options.contract);
1176
+ } catch (error) {
1177
+ throw new ContractValidationError(error instanceof Error ? error.message : String(error), error);
1178
+ }
1179
+ onProgress?.({
1180
+ action: "sign",
1181
+ kind: "spanStart",
1182
+ spanId: "sign",
1183
+ label: "Signing database..."
1184
+ });
1185
+ try {
1186
+ const result = await familyInstance.sign({
1187
+ driver,
1188
+ contract,
1189
+ contractPath: options.contractPath ?? "",
1190
+ ...ifDefined("configPath", options.configPath)
1191
+ });
1192
+ onProgress?.({
1193
+ action: "sign",
1194
+ kind: "spanEnd",
1195
+ spanId: "sign",
1196
+ outcome: "ok"
1197
+ });
1198
+ return result;
1199
+ } catch (error) {
1200
+ onProgress?.({
1201
+ action: "sign",
1202
+ kind: "spanEnd",
1203
+ spanId: "sign",
1204
+ outcome: "error"
1205
+ });
1206
+ throw error;
1207
+ }
1208
+ }
1209
+ async dbInit(options) {
1210
+ const { onProgress } = options;
1211
+ await this.connectWithProgress(options.connection, "dbInit", onProgress);
1212
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
1213
+ if (!hasMigrations(this.options.target)) throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
1214
+ let contract;
1215
+ try {
1216
+ contract = familyInstance.validateContract(options.contract);
1217
+ } catch (error) {
1218
+ throw new ContractValidationError(error instanceof Error ? error.message : String(error), error);
1219
+ }
1220
+ return executeDbInit({
1221
+ driver,
1222
+ familyInstance,
1223
+ contract,
1224
+ mode: options.mode,
1225
+ migrations: this.options.target.migrations,
1226
+ frameworkComponents,
1227
+ migrationsDir: options.migrationsDir,
1228
+ targetId: this.options.target.targetId,
1229
+ extensionPacks: this.options.extensionPacks ?? [],
1230
+ ...ifDefined("onProgress", onProgress)
1231
+ });
1232
+ }
1233
+ async dbUpdate(options) {
1234
+ const { onProgress } = options;
1235
+ await this.connectWithProgress(options.connection, "dbUpdate", onProgress);
1236
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
1237
+ if (!hasMigrations(this.options.target)) throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
1238
+ let contract;
1239
+ try {
1240
+ contract = familyInstance.validateContract(options.contract);
1241
+ } catch (error) {
1242
+ throw new ContractValidationError(error instanceof Error ? error.message : String(error), error);
1243
+ }
1244
+ return executeDbUpdate({
1245
+ driver,
1246
+ familyInstance,
1247
+ contract,
1248
+ mode: options.mode,
1249
+ migrations: this.options.target.migrations,
1250
+ frameworkComponents,
1251
+ migrationsDir: options.migrationsDir,
1252
+ targetId: this.options.target.targetId,
1253
+ extensionPacks: this.options.extensionPacks ?? [],
1254
+ ...ifDefined("acceptDataLoss", options.acceptDataLoss),
1255
+ ...ifDefined("onProgress", onProgress)
1256
+ });
1257
+ }
1258
+ async dbVerify(options) {
1259
+ const { onProgress } = options;
1260
+ await this.connectWithProgress(options.connection, "dbVerify", onProgress);
1261
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
1262
+ let contract;
1263
+ try {
1264
+ contract = familyInstance.validateContract(options.contract);
1265
+ } catch (error) {
1266
+ throw new ContractValidationError(error instanceof Error ? error.message : String(error), error);
1267
+ }
1268
+ return executeDbVerify({
1269
+ driver,
1270
+ familyInstance,
1271
+ contract,
1272
+ migrationsDir: options.migrationsDir,
1273
+ targetId: this.options.target.targetId,
1274
+ extensionPacks: this.options.extensionPacks ?? [],
1275
+ frameworkComponents,
1276
+ mode: options.strict ? "strict" : "lenient",
1277
+ skipSchema: options.skipSchema,
1278
+ skipMarker: options.skipMarker,
1279
+ ...ifDefined("onProgress", onProgress)
1280
+ });
1281
+ }
1282
+ async readMarker() {
1283
+ const { driver, familyInstance } = await this.ensureConnected();
1284
+ return familyInstance.readMarker({
1285
+ driver,
1286
+ space: APP_SPACE_ID
1287
+ });
1288
+ }
1289
+ async readAllMarkers() {
1290
+ const { driver, familyInstance } = await this.ensureConnected();
1291
+ return familyInstance.readAllMarkers({ driver });
1292
+ }
1293
+ async migrationApply(options) {
1294
+ const { onProgress } = options;
1295
+ await this.connectWithProgress(options.connection, "migrationApply", onProgress);
1296
+ const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
1297
+ if (!hasMigrations(this.options.target)) throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
1298
+ let contract;
1299
+ try {
1300
+ contract = familyInstance.validateContract(options.contract);
1301
+ } catch (error) {
1302
+ throw new ContractValidationError(error instanceof Error ? error.message : String(error), error);
1303
+ }
1304
+ return executeMigrationApply({
1305
+ driver,
1306
+ familyInstance,
1307
+ contract,
1308
+ migrations: this.options.target.migrations,
1309
+ frameworkComponents,
1310
+ migrationsDir: options.migrationsDir,
1311
+ extensionPacks: this.options.extensionPacks ?? [],
1312
+ targetId: this.options.target.targetId,
1313
+ appMigrationPackages: options.appMigrationPackages,
1314
+ ...ifDefined("refHash", options.refHash),
1315
+ ...ifDefined("refInvariants", options.refInvariants),
1316
+ ...ifDefined("refName", options.refName),
1317
+ ...ifDefined("onProgress", onProgress)
1318
+ });
1319
+ }
1320
+ async introspect(options) {
1321
+ const onProgress = options?.onProgress;
1322
+ await this.connectWithProgress(options?.connection, "introspect", onProgress);
1323
+ const { driver, familyInstance } = await this.ensureConnected();
1324
+ options?.schema;
1325
+ onProgress?.({
1326
+ action: "introspect",
1327
+ kind: "spanStart",
1328
+ spanId: "introspect",
1329
+ label: "Introspecting database schema..."
1330
+ });
1331
+ try {
1332
+ const result = await familyInstance.introspect({ driver });
1333
+ onProgress?.({
1334
+ action: "introspect",
1335
+ kind: "spanEnd",
1336
+ spanId: "introspect",
1337
+ outcome: "ok"
1338
+ });
1339
+ return result;
1340
+ } catch (error) {
1341
+ onProgress?.({
1342
+ action: "introspect",
1343
+ kind: "spanEnd",
1344
+ spanId: "introspect",
1345
+ outcome: "error"
1346
+ });
1347
+ throw error;
1348
+ }
1349
+ }
1350
+ toSchemaView(schemaIR) {
1351
+ this.init();
1352
+ if (this.familyInstance && hasSchemaView(this.familyInstance)) return this.familyInstance.toSchemaView(schemaIR);
1353
+ }
1354
+ inferPslContract(schemaIR) {
1355
+ this.init();
1356
+ if (this.familyInstance && hasPslContractInfer(this.familyInstance)) return this.familyInstance.inferPslContract(schemaIR);
1357
+ }
1358
+ toOperationPreview(operations) {
1359
+ this.init();
1360
+ if (this.familyInstance && hasOperationPreview(this.familyInstance)) return this.familyInstance.toOperationPreview(operations);
1361
+ }
1362
+ async emit(options) {
1363
+ const { onProgress, contractConfig } = options;
1364
+ this.init();
1365
+ if (!this.familyInstance) throw new Error("Family instance was not initialized. This is a bug.");
1366
+ let contractRaw;
1367
+ onProgress?.({
1368
+ action: "emit",
1369
+ kind: "spanStart",
1370
+ spanId: "resolveSource",
1371
+ label: "Resolving contract source..."
1372
+ });
1373
+ try {
1374
+ const stack = this.stack;
1375
+ const sourceContext = {
1376
+ composedExtensionPacks: stack.extensionPacks.map((p) => p.id),
1377
+ scalarTypeDescriptors: stack.scalarTypeDescriptors,
1378
+ authoringContributions: stack.authoringContributions,
1379
+ codecLookup: stack.codecLookup,
1380
+ controlMutationDefaults: stack.controlMutationDefaults,
1381
+ resolvedInputs: contractConfig.source.inputs ?? []
1382
+ };
1383
+ const providerResult = await contractConfig.source.load(sourceContext);
1384
+ if (!providerResult.ok) {
1385
+ onProgress?.({
1386
+ action: "emit",
1387
+ kind: "spanEnd",
1388
+ spanId: "resolveSource",
1389
+ outcome: "error"
1390
+ });
1391
+ return notOk({
1392
+ code: "CONTRACT_SOURCE_INVALID",
1393
+ summary: providerResult.failure.summary,
1394
+ why: providerResult.failure.summary,
1395
+ meta: providerResult.failure.meta,
1396
+ diagnostics: providerResult.failure
1397
+ });
1398
+ }
1399
+ contractRaw = providerResult.value;
1400
+ onProgress?.({
1401
+ action: "emit",
1402
+ kind: "spanEnd",
1403
+ spanId: "resolveSource",
1404
+ outcome: "ok"
1405
+ });
1406
+ } catch (error) {
1407
+ onProgress?.({
1408
+ action: "emit",
1409
+ kind: "spanEnd",
1410
+ spanId: "resolveSource",
1411
+ outcome: "error"
1412
+ });
1413
+ const message = error instanceof Error ? error.message : String(error);
1414
+ return notOk({
1415
+ code: "CONTRACT_SOURCE_INVALID",
1416
+ summary: "Failed to resolve contract source",
1417
+ why: message,
1418
+ diagnostics: {
1419
+ summary: "Contract source provider threw an exception",
1420
+ diagnostics: [{
1421
+ code: "PROVIDER_THROW",
1422
+ message
1423
+ }]
1424
+ },
1425
+ meta: void 0
1426
+ });
1427
+ }
1428
+ onProgress?.({
1429
+ action: "emit",
1430
+ kind: "spanStart",
1431
+ spanId: "emit",
1432
+ label: "Emitting contract..."
1433
+ });
1434
+ try {
1435
+ const enrichedIR = enrichContract(contractRaw, this.frameworkComponents ?? []);
1436
+ try {
1437
+ this.familyInstance.validateContract(enrichedIR);
1438
+ } catch (error) {
1439
+ onProgress?.({
1440
+ action: "emit",
1441
+ kind: "spanEnd",
1442
+ spanId: "emit",
1443
+ outcome: "error"
1444
+ });
1445
+ return notOk({
1446
+ code: "CONTRACT_VALIDATION_FAILED",
1447
+ summary: "Contract validation failed",
1448
+ why: error instanceof Error ? error.message : String(error),
1449
+ meta: void 0
1450
+ });
1451
+ }
1452
+ const result = await emit(enrichedIR, this.stack, this.options.family.emission);
1453
+ onProgress?.({
1454
+ action: "emit",
1455
+ kind: "spanEnd",
1456
+ spanId: "emit",
1457
+ outcome: "ok"
1458
+ });
1459
+ return ok({
1460
+ storageHash: result.storageHash,
1461
+ ...ifDefined("executionHash", result.executionHash),
1462
+ profileHash: result.profileHash,
1463
+ contractJson: result.contractJson,
1464
+ contractDts: result.contractDts
1465
+ });
1466
+ } catch (error) {
1467
+ onProgress?.({
1468
+ action: "emit",
1469
+ kind: "spanEnd",
1470
+ spanId: "emit",
1471
+ outcome: "error"
1472
+ });
1473
+ return notOk({
1474
+ code: "EMIT_FAILED",
1475
+ summary: "Failed to emit contract",
1476
+ why: error instanceof Error ? error.message : String(error),
1477
+ meta: void 0
1478
+ });
1479
+ }
1480
+ }
1481
+ };
1482
+ //#endregion
1483
+ export { ContractValidationError as a, executeDbInit as i, executeDbVerify as n, executeDbUpdate as r, createControlClient as t };
1484
+
1485
+ //# sourceMappingURL=client-BCnP7cHo.mjs.map