@prisma-next/cli 0.5.0-dev.8 → 0.5.0-dev.81

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 (186) hide show
  1. package/README.md +56 -21
  2. package/dist/cli-errors-B9OBbled.d.mts +3 -0
  3. package/dist/cli-errors-D3_sMh2K.mjs +33 -0
  4. package/dist/cli-errors-D3_sMh2K.mjs.map +1 -0
  5. package/dist/cli.mjs +16 -78
  6. package/dist/cli.mjs.map +1 -1
  7. package/dist/client-qVH-rEgd.mjs +1595 -0
  8. package/dist/client-qVH-rEgd.mjs.map +1 -0
  9. package/dist/{result-handler-Ba3zWQsI.mjs → command-helpers-BeZHkxV8.mjs} +70 -47
  10. package/dist/command-helpers-BeZHkxV8.mjs.map +1 -0
  11. package/dist/commands/contract-emit.d.mts.map +1 -1
  12. package/dist/commands/contract-emit.mjs +2 -4
  13. package/dist/commands/contract-infer.d.mts.map +1 -1
  14. package/dist/commands/contract-infer.mjs +2 -4
  15. package/dist/commands/db-init.d.mts.map +1 -1
  16. package/dist/commands/db-init.mjs +16 -13
  17. package/dist/commands/db-init.mjs.map +1 -1
  18. package/dist/commands/db-schema.d.mts.map +1 -1
  19. package/dist/commands/db-schema.mjs +6 -7
  20. package/dist/commands/db-schema.mjs.map +1 -1
  21. package/dist/commands/db-sign.d.mts.map +1 -1
  22. package/dist/commands/db-sign.mjs +9 -9
  23. package/dist/commands/db-sign.mjs.map +1 -1
  24. package/dist/commands/db-update.d.mts.map +1 -1
  25. package/dist/commands/db-update.mjs +15 -13
  26. package/dist/commands/db-update.mjs.map +1 -1
  27. package/dist/commands/db-verify.d.mts.map +1 -1
  28. package/dist/commands/db-verify.mjs +1 -321
  29. package/dist/commands/migration-apply.d.mts +28 -13
  30. package/dist/commands/migration-apply.d.mts.map +1 -1
  31. package/dist/commands/migration-apply.mjs +55 -151
  32. package/dist/commands/migration-apply.mjs.map +1 -1
  33. package/dist/commands/migration-new.d.mts +0 -1
  34. package/dist/commands/migration-new.d.mts.map +1 -1
  35. package/dist/commands/migration-new.mjs +34 -40
  36. package/dist/commands/migration-new.mjs.map +1 -1
  37. package/dist/commands/migration-plan.d.mts +33 -6
  38. package/dist/commands/migration-plan.d.mts.map +1 -1
  39. package/dist/commands/migration-plan.mjs +2 -348
  40. package/dist/commands/migration-ref.d.mts +1 -1
  41. package/dist/commands/migration-ref.d.mts.map +1 -1
  42. package/dist/commands/migration-ref.mjs +8 -12
  43. package/dist/commands/migration-ref.mjs.map +1 -1
  44. package/dist/commands/migration-show.d.mts +13 -7
  45. package/dist/commands/migration-show.d.mts.map +1 -1
  46. package/dist/commands/migration-show.mjs +35 -36
  47. package/dist/commands/migration-show.mjs.map +1 -1
  48. package/dist/commands/migration-status.d.mts +126 -5
  49. package/dist/commands/migration-status.d.mts.map +1 -1
  50. package/dist/commands/migration-status.mjs +2 -4
  51. package/dist/{config-loader-C25b63rJ.mjs → config-loader-B6sJjXTv.mjs} +3 -5
  52. package/dist/config-loader-B6sJjXTv.mjs.map +1 -0
  53. package/dist/config-loader.d.mts +0 -1
  54. package/dist/config-loader.d.mts.map +1 -1
  55. package/dist/config-loader.mjs +2 -3
  56. package/dist/contract-emit-9DBda5Ou.mjs +150 -0
  57. package/dist/contract-emit-9DBda5Ou.mjs.map +1 -0
  58. package/dist/contract-emit-B77TsJqf.mjs +327 -0
  59. package/dist/contract-emit-B77TsJqf.mjs.map +1 -0
  60. package/dist/{contract-enrichment-CAOELa-H.mjs → contract-enrichment-Dani0mMW.mjs} +4 -6
  61. package/dist/contract-enrichment-Dani0mMW.mjs.map +1 -0
  62. package/dist/{contract-infer-D9cC3rJm.mjs → contract-infer-BK9YFGEG.mjs} +13 -22
  63. package/dist/contract-infer-BK9YFGEG.mjs.map +1 -0
  64. package/dist/db-verify-C0y1PCO2.mjs +404 -0
  65. package/dist/db-verify-C0y1PCO2.mjs.map +1 -0
  66. package/dist/exports/config-types.mjs +1 -2
  67. package/dist/exports/control-api.d.mts +101 -586
  68. package/dist/exports/control-api.d.mts.map +1 -1
  69. package/dist/exports/control-api.mjs +4 -6
  70. package/dist/exports/index.d.mts.map +1 -1
  71. package/dist/exports/index.mjs +28 -30
  72. package/dist/exports/index.mjs.map +1 -1
  73. package/dist/exports/init-output.d.mts +2 -4
  74. package/dist/exports/init-output.d.mts.map +1 -1
  75. package/dist/exports/init-output.mjs +2 -3
  76. package/dist/extension-pack-inputs-C7xgE-vv.mjs +74 -0
  77. package/dist/extension-pack-inputs-C7xgE-vv.mjs.map +1 -0
  78. package/dist/{framework-components-Cr--XBKy.mjs → framework-components-ChqVUxR-.mjs} +3 -4
  79. package/dist/{framework-components-Cr--XBKy.mjs.map → framework-components-ChqVUxR-.mjs.map} +1 -1
  80. package/dist/global-flags-Icqpxk23.d.mts +12 -0
  81. package/dist/global-flags-Icqpxk23.d.mts.map +1 -0
  82. package/dist/helpers-eqdN8tH6.mjs +25 -0
  83. package/dist/helpers-eqdN8tH6.mjs.map +1 -0
  84. package/dist/{init-C5220SY9.mjs → init-CoDVPvQ4.mjs} +26 -35
  85. package/dist/init-CoDVPvQ4.mjs.map +1 -0
  86. package/dist/{inspect-live-schema-yrHAvG71.mjs → inspect-live-schema-CWYxGKlb.mjs} +10 -11
  87. package/dist/inspect-live-schema-CWYxGKlb.mjs.map +1 -0
  88. package/dist/migration-cli.d.mts +41 -12
  89. package/dist/migration-cli.d.mts.map +1 -1
  90. package/dist/migration-cli.mjs +309 -86
  91. package/dist/migration-cli.mjs.map +1 -1
  92. package/dist/{migration-command-scaffold-B3B09et6.mjs → migration-command-scaffold-B5dORFEv.mjs} +8 -9
  93. package/dist/migration-command-scaffold-B5dORFEv.mjs.map +1 -0
  94. package/dist/migration-plan-C6lVaHsO.mjs +554 -0
  95. package/dist/migration-plan-C6lVaHsO.mjs.map +1 -0
  96. package/dist/{migration-status-DUMiH8_G.mjs → migration-status-CZ-D5k7k.mjs} +272 -65
  97. package/dist/migration-status-CZ-D5k7k.mjs.map +1 -0
  98. package/dist/migrations-D_UJnpuW.mjs +216 -0
  99. package/dist/migrations-D_UJnpuW.mjs.map +1 -0
  100. package/dist/{output-BpcQrnnq.mjs → output-B16Kefzx.mjs} +9 -3
  101. package/dist/output-B16Kefzx.mjs.map +1 -0
  102. package/dist/{progress-adapter-DvQWB1nK.mjs → progress-adapter-DFfvZcYL.mjs} +2 -2
  103. package/dist/{progress-adapter-DvQWB1nK.mjs.map → progress-adapter-DFfvZcYL.mjs.map} +1 -1
  104. package/dist/result-handler-rmPVKIP2.mjs +25 -0
  105. package/dist/result-handler-rmPVKIP2.mjs.map +1 -0
  106. package/dist/rolldown-runtime-twds-ZHy.mjs +14 -0
  107. package/dist/{terminal-ui-C3ZLwQxK.mjs → terminal-ui-C_hFNbAn.mjs} +4 -28
  108. package/dist/terminal-ui-C_hFNbAn.mjs.map +1 -0
  109. package/dist/types-D7x-IFLO.d.mts +858 -0
  110. package/dist/types-D7x-IFLO.d.mts.map +1 -0
  111. package/dist/{verify-Bkycc-Tf.mjs → verify-CiwNWM9N.mjs} +3 -4
  112. package/dist/verify-CiwNWM9N.mjs.map +1 -0
  113. package/package.json +26 -24
  114. package/src/cli.ts +32 -6
  115. package/src/commands/contract-emit.ts +67 -163
  116. package/src/commands/contract-infer.ts +7 -20
  117. package/src/commands/db-init.ts +15 -3
  118. package/src/commands/db-update.ts +9 -4
  119. package/src/commands/db-verify.ts +47 -15
  120. package/src/commands/init/index.ts +1 -1
  121. package/src/commands/init/init.ts +2 -2
  122. package/src/commands/init/templates/code-templates.ts +12 -4
  123. package/src/commands/inspect-live-schema.ts +10 -5
  124. package/src/commands/migration-apply.ts +114 -212
  125. package/src/commands/migration-new.ts +42 -45
  126. package/src/commands/migration-plan.ts +212 -72
  127. package/src/commands/migration-ref.ts +8 -7
  128. package/src/commands/migration-show.ts +60 -41
  129. package/src/commands/migration-status.ts +483 -64
  130. package/src/config-path-validation.ts +0 -1
  131. package/src/control-api/client.ts +85 -5
  132. package/src/control-api/contract-enrichment.ts +6 -4
  133. package/src/control-api/operations/apply-aggregate.ts +290 -0
  134. package/src/control-api/operations/contract-emit.ts +198 -115
  135. package/src/control-api/operations/db-apply-aggregate.ts +397 -0
  136. package/src/control-api/operations/db-init.ts +51 -253
  137. package/src/control-api/operations/db-update.ts +66 -183
  138. package/src/control-api/operations/db-verify.ts +342 -0
  139. package/src/control-api/operations/migration-apply.ts +424 -131
  140. package/src/control-api/types.ts +280 -29
  141. package/src/exports/control-api.ts +15 -3
  142. package/src/load-ts-contract.ts +28 -26
  143. package/src/migration-cli.ts +445 -122
  144. package/src/utils/cli-errors.ts +49 -2
  145. package/src/utils/combine-schema-results.ts +84 -0
  146. package/src/utils/command-helpers.ts +69 -25
  147. package/src/utils/contract-space-aggregate-loader.ts +204 -0
  148. package/src/utils/contract-space-extension-migrations-pass.ts +120 -0
  149. package/src/utils/contract-space-migrate-pass.ts +156 -0
  150. package/src/utils/emit-queue.ts +26 -0
  151. package/src/utils/extension-pack-inputs.ts +170 -0
  152. package/src/utils/formatters/graph-migration-mapper.ts +7 -3
  153. package/src/utils/formatters/migrations.ts +197 -61
  154. package/src/utils/publish-contract-artifact-pair.ts +134 -0
  155. package/dist/cli-errors-BFYgBH3L.d.mts +0 -4
  156. package/dist/cli-errors-Cd79vmTH.mjs +0 -5
  157. package/dist/client-CrsnY58k.mjs +0 -997
  158. package/dist/client-CrsnY58k.mjs.map +0 -1
  159. package/dist/commands/db-verify.mjs.map +0 -1
  160. package/dist/commands/migration-plan.mjs.map +0 -1
  161. package/dist/config-loader-C25b63rJ.mjs.map +0 -1
  162. package/dist/contract-emit--feXyNd7.mjs +0 -4
  163. package/dist/contract-emit-NJ01hiiv.mjs +0 -195
  164. package/dist/contract-emit-NJ01hiiv.mjs.map +0 -1
  165. package/dist/contract-emit-V5SSitUT.mjs +0 -122
  166. package/dist/contract-emit-V5SSitUT.mjs.map +0 -1
  167. package/dist/contract-enrichment-CAOELa-H.mjs.map +0 -1
  168. package/dist/contract-infer-D9cC3rJm.mjs.map +0 -1
  169. package/dist/extract-operation-statements-DsFfxXVZ.mjs +0 -13
  170. package/dist/extract-operation-statements-DsFfxXVZ.mjs.map +0 -1
  171. package/dist/extract-sql-ddl-D9UbZDyz.mjs +0 -26
  172. package/dist/extract-sql-ddl-D9UbZDyz.mjs.map +0 -1
  173. package/dist/init-C5220SY9.mjs.map +0 -1
  174. package/dist/inspect-live-schema-yrHAvG71.mjs.map +0 -1
  175. package/dist/migration-command-scaffold-B3B09et6.mjs.map +0 -1
  176. package/dist/migration-status-DUMiH8_G.mjs.map +0 -1
  177. package/dist/migrations-Bo5WtTla.mjs +0 -153
  178. package/dist/migrations-Bo5WtTla.mjs.map +0 -1
  179. package/dist/output-BpcQrnnq.mjs.map +0 -1
  180. package/dist/result-handler-Ba3zWQsI.mjs.map +0 -1
  181. package/dist/terminal-ui-C3ZLwQxK.mjs.map +0 -1
  182. package/dist/validate-contract-deps-B_Cs29TL.mjs +0 -37
  183. package/dist/validate-contract-deps-B_Cs29TL.mjs.map +0 -1
  184. package/dist/verify-Bkycc-Tf.mjs.map +0 -1
  185. package/src/control-api/operations/extract-operation-statements.ts +0 -14
  186. package/src/control-api/operations/extract-sql-ddl.ts +0 -47
@@ -0,0 +1,342 @@
1
+ import type { Contract } from '@prisma-next/contract/types';
2
+ import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
3
+ import type {
4
+ ControlDriverInstance,
5
+ ControlExtensionDescriptor,
6
+ ControlFamilyInstance,
7
+ VerifyDatabaseSchemaResult,
8
+ } from '@prisma-next/framework-components/control';
9
+ import {
10
+ type AggregateVerifierOutput,
11
+ type ContractSpaceMember,
12
+ verifyAggregate,
13
+ } from '@prisma-next/migration-tools/aggregate';
14
+ import { notOk, ok, type Result } from '@prisma-next/utils/result';
15
+ import { CliStructuredError } from '../../utils/cli-errors';
16
+ import {
17
+ type BuildAggregateInputs,
18
+ buildContractSpaceAggregate,
19
+ } from '../../utils/contract-space-aggregate-loader';
20
+ import type { OnControlProgress } from '../types';
21
+
22
+ /**
23
+ * Span IDs emitted via `onProgress` during the aggregate verify flow.
24
+ * Mirrors the span identifiers used by the legacy precheck / marker-check
25
+ * helpers so structured-output renderers and progress tests keep working.
26
+ */
27
+ const SPAN_IDS = {
28
+ introspect: 'introspect',
29
+ verify: 'verify',
30
+ } as const;
31
+
32
+ /**
33
+ * Inputs for the aggregate `db verify` operation.
34
+ *
35
+ * Loader → verifier pipeline. The loader (sole descriptor-import
36
+ * boundary) builds a {@link import('@prisma-next/migration-tools/aggregate').ContractSpaceAggregate};
37
+ * the aggregate verifier bundles `markerCheck` + per-space pre-projected
38
+ * `schemaCheck`. `mode: 'strict' | 'lenient'` maps directly to the user
39
+ * facing `--strict` flag.
40
+ */
41
+ export interface ExecuteDbVerifyOptions<TFamilyId extends string, TTargetId extends string> {
42
+ readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;
43
+ readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
44
+ readonly contract: Contract;
45
+ readonly migrationsDir: string;
46
+ readonly targetId: TTargetId;
47
+ readonly extensionPacks: ReadonlyArray<ControlExtensionDescriptor<TFamilyId, TTargetId>>;
48
+ readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;
49
+ readonly mode: 'strict' | 'lenient';
50
+ readonly skipSchema: boolean;
51
+ readonly skipMarker: boolean;
52
+ readonly onProgress?: OnControlProgress;
53
+ }
54
+
55
+ /**
56
+ * Result of the aggregate verify operation.
57
+ *
58
+ * Marker-check failures are surfaced as a {@link CliStructuredError}
59
+ * (same envelope code `5002` the legacy `runContractSpaceVerifierMarkerCheck`
60
+ * emitted, so downstream tooling and integration tests assert on the
61
+ * same shape).
62
+ *
63
+ * On success, the per-space schema results are returned for the CLI to
64
+ * render. When `skipSchema` is true (`--marker-only`), the schema map
65
+ * is empty.
66
+ */
67
+ export interface ExecuteDbVerifySuccess {
68
+ readonly schemaResults: ReadonlyMap<string, VerifyDatabaseSchemaResult>;
69
+ readonly memberOrder: readonly string[];
70
+ readonly appSpaceId: string;
71
+ }
72
+
73
+ export type ExecuteDbVerifyResult = Result<ExecuteDbVerifySuccess, CliStructuredError>;
74
+
75
+ /**
76
+ * Loader → verifier pipeline shared by `db verify` modes (`full`,
77
+ * `marker-only`, `schema-only`).
78
+ *
79
+ * 1. **Load**: build a {@link import('@prisma-next/migration-tools/aggregate').ContractSpaceAggregate}
80
+ * from descriptors + on-disk on-disk artefacts. Layout / drift /
81
+ * integrity / disjointness violations short-circuit with a
82
+ * structured CLI error.
83
+ * 2. **Read DB state**: marker rows + (when `skipSchema` is `false`)
84
+ * schema introspection.
85
+ * 3. **Verify**: {@link verifyAggregate} returns per-space
86
+ * `markerCheck` + per-space pre-projected `schemaCheck` (closes F23).
87
+ * Marker mismatches map to `CliStructuredError` (code `5002`) so
88
+ * callers (CLI command) can render and exit. Schema results are
89
+ * returned to the caller verbatim.
90
+ */
91
+ export async function executeDbVerify<TFamilyId extends string, TTargetId extends string>(
92
+ options: ExecuteDbVerifyOptions<TFamilyId, TTargetId>,
93
+ ): Promise<ExecuteDbVerifyResult> {
94
+ const { driver, familyInstance, onProgress, skipSchema, skipMarker } = options;
95
+ const loaded = await buildContractSpaceAggregate(buildLoadInputs(options));
96
+ if (!loaded.ok) return notOk(loaded.failure);
97
+ const aggregate = loaded.value;
98
+
99
+ const markersBySpaceId = await familyInstance.readAllMarkers({ driver });
100
+ const schemaIntrospection = skipSchema
101
+ ? null
102
+ : await runIntrospection({ driver, familyInstance, onProgress });
103
+
104
+ emitVerifySpan(onProgress, 'spanStart');
105
+ const verifyResult = verifyAggregate({
106
+ aggregate,
107
+ markersBySpaceId,
108
+ schemaIntrospection,
109
+ mode: options.mode,
110
+ verifySchemaForMember: createPerMemberVerifier(options),
111
+ });
112
+ return finaliseVerifyResult({ verifyResult, aggregate, skipMarker, onProgress });
113
+ }
114
+
115
+ function buildLoadInputs<TFamilyId extends string, TTargetId extends string>(
116
+ options: ExecuteDbVerifyOptions<TFamilyId, TTargetId>,
117
+ ): BuildAggregateInputs<TFamilyId, TTargetId> {
118
+ return {
119
+ targetId: options.targetId,
120
+ migrationsDir: options.migrationsDir,
121
+ appContract: options.contract,
122
+ extensionPacks: options.extensionPacks,
123
+ validateContract: (json) => options.familyInstance.validateContract(json),
124
+ };
125
+ }
126
+
127
+ async function runIntrospection<TFamilyId extends string, TTargetId extends string>(args: {
128
+ driver: ControlDriverInstance<TFamilyId, TTargetId>;
129
+ familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
130
+ onProgress: OnControlProgress | undefined;
131
+ }): Promise<unknown> {
132
+ const { driver, familyInstance, onProgress } = args;
133
+ onProgress?.({
134
+ action: 'dbVerify',
135
+ kind: 'spanStart',
136
+ spanId: SPAN_IDS.introspect,
137
+ label: 'Introspecting database schema',
138
+ });
139
+ try {
140
+ const result = await familyInstance.introspect({ driver });
141
+ onProgress?.({
142
+ action: 'dbVerify',
143
+ kind: 'spanEnd',
144
+ spanId: SPAN_IDS.introspect,
145
+ outcome: 'ok',
146
+ });
147
+ return result;
148
+ } catch (error) {
149
+ onProgress?.({
150
+ action: 'dbVerify',
151
+ kind: 'spanEnd',
152
+ spanId: SPAN_IDS.introspect,
153
+ outcome: 'error',
154
+ });
155
+ throw error;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Build the per-member schema callback handed to the aggregate verifier.
161
+ * When `skipSchema` is true the callback short-circuits with a synthetic
162
+ * `ok` result so the verifier still runs the (cheap) schemaCheck loop
163
+ * without invoking the family's verification path.
164
+ */
165
+ function createPerMemberVerifier<TFamilyId extends string, TTargetId extends string>(
166
+ options: ExecuteDbVerifyOptions<TFamilyId, TTargetId>,
167
+ ): (
168
+ projectedSchema: unknown,
169
+ member: ContractSpaceMember,
170
+ verifyMode: 'strict' | 'lenient',
171
+ ) => VerifyDatabaseSchemaResult {
172
+ const { skipSchema, familyInstance, frameworkComponents } = options;
173
+ return (projectedSchema, member, verifyMode) => {
174
+ if (skipSchema) return buildSkippedSchemaResult(member);
175
+ return familyInstance.schemaVerifyAgainstSchema({
176
+ contract: member.contract,
177
+ // The family's `TSchemaIR` is opaque to migration-tools; the
178
+ // aggregate verifier passes through whatever we hand it. The
179
+ // family expects its own IR shape on the way back.
180
+ schema: projectedSchema as never,
181
+ strict: verifyMode === 'strict',
182
+ frameworkComponents,
183
+ });
184
+ };
185
+ }
186
+
187
+ function emitVerifySpan(
188
+ onProgress: OnControlProgress | undefined,
189
+ kind: 'spanStart' | 'spanEndOk' | 'spanEndError',
190
+ ): void {
191
+ if (kind === 'spanStart') {
192
+ onProgress?.({
193
+ action: 'dbVerify',
194
+ kind: 'spanStart',
195
+ spanId: SPAN_IDS.verify,
196
+ label: 'Verifying contract spaces',
197
+ });
198
+ return;
199
+ }
200
+ onProgress?.({
201
+ action: 'dbVerify',
202
+ kind: 'spanEnd',
203
+ spanId: SPAN_IDS.verify,
204
+ outcome: kind === 'spanEndOk' ? 'ok' : 'error',
205
+ });
206
+ }
207
+
208
+ /**
209
+ * Map an {@link AggregateVerifierOutput} to the operation's
210
+ * {@link ExecuteDbVerifyResult}, applying the `skipMarker` policy used
211
+ * by the CLI's `--schema-only` mode.
212
+ */
213
+ function finaliseVerifyResult(args: {
214
+ verifyResult: AggregateVerifierOutput<VerifyDatabaseSchemaResult>;
215
+ aggregate: {
216
+ readonly app: { readonly spaceId: string };
217
+ readonly extensions: ReadonlyArray<{ readonly spaceId: string }>;
218
+ };
219
+ skipMarker: boolean;
220
+ onProgress: OnControlProgress | undefined;
221
+ }): ExecuteDbVerifyResult {
222
+ const { verifyResult, aggregate, skipMarker, onProgress } = args;
223
+ if (!verifyResult.ok) {
224
+ emitVerifySpan(onProgress, 'spanEndError');
225
+ return notOk(
226
+ new CliStructuredError('5002', 'Aggregate verifier introspection failed', {
227
+ domain: 'MIG',
228
+ why: verifyResult.failure.detail,
229
+ fix: 'Check database connectivity and the introspection tooling.',
230
+ docsUrl: 'https://pris.ly/contract-spaces',
231
+ }),
232
+ );
233
+ }
234
+ const markerError = skipMarker
235
+ ? null
236
+ : mapMarkerCheckFailures(aggregate.app.spaceId, verifyResult.value.markerCheck);
237
+ if (markerError !== null) {
238
+ emitVerifySpan(onProgress, 'spanEndError');
239
+ return notOk(markerError);
240
+ }
241
+ emitVerifySpan(onProgress, 'spanEndOk');
242
+ return ok({
243
+ schemaResults: verifyResult.value.schemaCheck.perSpace,
244
+ memberOrder: [aggregate.app.spaceId, ...aggregate.extensions.map((e) => e.spaceId)],
245
+ appSpaceId: aggregate.app.spaceId,
246
+ });
247
+ }
248
+
249
+ function buildSkippedSchemaResult(member: ContractSpaceMember): VerifyDatabaseSchemaResult {
250
+ const profileHash = (member.contract as { profileHash?: string }).profileHash;
251
+ return {
252
+ ok: true,
253
+ summary: 'Schema verification skipped',
254
+ contract: {
255
+ storageHash: member.headRef.hash,
256
+ ...(profileHash ? { profileHash } : {}),
257
+ },
258
+ target: { expected: member.contract.target },
259
+ schema: {
260
+ issues: [],
261
+ root: {
262
+ status: 'pass',
263
+ kind: 'skipped',
264
+ name: member.spaceId,
265
+ contractPath: '',
266
+ code: 'SKIPPED',
267
+ message: 'Schema verification skipped',
268
+ expected: undefined,
269
+ actual: undefined,
270
+ children: [],
271
+ },
272
+ counts: { pass: 0, warn: 0, fail: 0, totalNodes: 0 },
273
+ },
274
+ timings: { total: 0 },
275
+ };
276
+ }
277
+
278
+ /**
279
+ * Translate per-space marker check failures and orphan markers into a
280
+ * single CLI structured error envelope. Preserves the legacy code
281
+ * `5002` (was emitted by `runContractSpaceVerifierMarkerCheck`).
282
+ */
283
+ function mapMarkerCheckFailures(
284
+ appSpaceId: string,
285
+ section: {
286
+ readonly perSpace: ReadonlyMap<
287
+ string,
288
+ | { readonly kind: 'ok' }
289
+ | { readonly kind: 'absent' }
290
+ | { readonly kind: 'hashMismatch'; readonly markerHash: string; readonly expected: string }
291
+ | { readonly kind: 'missingInvariants'; readonly missing: readonly string[] }
292
+ >;
293
+ readonly orphanMarkers: readonly { readonly spaceId: string; readonly row: unknown }[];
294
+ },
295
+ ): CliStructuredError | null {
296
+ const violations: Array<{
297
+ kind: string;
298
+ spaceId: string;
299
+ remediation: string;
300
+ }> = [];
301
+ for (const [spaceId, result] of section.perSpace) {
302
+ if (result.kind === 'ok' || result.kind === 'absent') continue;
303
+ if (result.kind === 'hashMismatch') {
304
+ violations.push({
305
+ kind: 'hashMismatch',
306
+ spaceId,
307
+ remediation:
308
+ spaceId === appSpaceId
309
+ ? 'Run `prisma-next db update` to advance the marker, or roll the database back to the recorded hash.'
310
+ : `Apply on-disk migrations under \`migrations/${spaceId}/\` to advance the marker, or remove the conflicting marker row.`,
311
+ });
312
+ continue;
313
+ }
314
+ if (result.kind === 'missingInvariants') {
315
+ violations.push({
316
+ kind: 'invariantsMismatch',
317
+ spaceId,
318
+ remediation: `Re-apply the migrations under \`migrations/${spaceId}/\` so the marker carries invariants: ${result.missing.join(', ')}.`,
319
+ });
320
+ }
321
+ }
322
+ for (const orphan of section.orphanMarkers) {
323
+ violations.push({
324
+ kind: 'orphanMarker',
325
+ spaceId: orphan.spaceId,
326
+ remediation: `Add the corresponding extension to \`extensionPacks\` in \`prisma-next.config.ts\`, or delete the orphan marker row for "${orphan.spaceId}".`,
327
+ });
328
+ }
329
+ if (violations.length === 0) return null;
330
+ const lines = violations.map((v) => `- [${v.kind}] ${v.spaceId}: ${v.remediation}`);
331
+ const summary =
332
+ violations.length === 1
333
+ ? 'Contract-space verifier found a violation'
334
+ : `Contract-space verifier found violations (${violations.length})`;
335
+ return new CliStructuredError('5002', summary, {
336
+ domain: 'MIG',
337
+ why: `The on-disk \`migrations/\` directory, the \`extensionPacks\` declaration, and the live database marker rows are not in agreement.\n${lines.join('\n')}`,
338
+ fix: violations[0]?.remediation ?? 'Review and reconcile the violations listed above.',
339
+ docsUrl: 'https://pris.ly/contract-spaces',
340
+ meta: { violations },
341
+ });
342
+ }