@prisma-next/cli 0.12.0-dev.6 → 0.12.0-dev.61

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 (202) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.mjs +180 -163
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/{client-KgJorIvG.mjs → client-ROablRVC.mjs} +198 -105
  5. package/dist/client-ROablRVC.mjs.map +1 -0
  6. package/dist/{command-helpers-Bbw1GbwL.mjs → command-helpers-DGMvGBeX.mjs} +318 -25
  7. package/dist/command-helpers-DGMvGBeX.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 +4 -5
  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 -3
  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 +6 -6
  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 +10 -7
  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 +37 -3
  27. package/dist/commands/migrate.d.mts.map +1 -1
  28. package/dist/commands/migrate.mjs +292 -12
  29. package/dist/commands/migrate.mjs.map +1 -1
  30. package/dist/commands/migration-check.d.mts +55 -13
  31. package/dist/commands/migration-check.d.mts.map +1 -1
  32. package/dist/commands/migration-check.mjs +3 -2
  33. package/dist/commands/migration-graph.d.mts +17 -8
  34. package/dist/commands/migration-graph.d.mts.map +1 -1
  35. package/dist/commands/migration-graph.mjs +185 -2
  36. package/dist/commands/migration-graph.mjs.map +1 -0
  37. package/dist/commands/migration-list.d.mts +26 -27
  38. package/dist/commands/migration-list.d.mts.map +1 -1
  39. package/dist/commands/migration-list.mjs +2 -190
  40. package/dist/commands/migration-log.d.mts +9 -19
  41. package/dist/commands/migration-log.d.mts.map +1 -1
  42. package/dist/commands/migration-log.mjs +1 -137
  43. package/dist/commands/migration-new.d.mts.map +1 -1
  44. package/dist/commands/migration-new.mjs +4 -4
  45. package/dist/commands/migration-new.mjs.map +1 -1
  46. package/dist/commands/migration-plan.d.mts +1 -1
  47. package/dist/commands/migration-plan.d.mts.map +1 -1
  48. package/dist/commands/migration-plan.mjs +1 -1
  49. package/dist/commands/migration-show.d.mts +17 -21
  50. package/dist/commands/migration-show.d.mts.map +1 -1
  51. package/dist/commands/migration-show.mjs +24 -36
  52. package/dist/commands/migration-show.mjs.map +1 -1
  53. package/dist/commands/migration-status.d.mts +42 -144
  54. package/dist/commands/migration-status.d.mts.map +1 -1
  55. package/dist/commands/migration-status.mjs +3 -759
  56. package/dist/commands/ref.d.mts +1 -1
  57. package/dist/commands/ref.d.mts.map +1 -1
  58. package/dist/commands/ref.mjs +4 -4
  59. package/dist/commands/ref.mjs.map +1 -1
  60. package/dist/commands/telemetry/index.d.mts +7 -0
  61. package/dist/commands/telemetry/index.d.mts.map +1 -0
  62. package/dist/commands/telemetry/index.mjs +2 -0
  63. package/dist/{config-loader-B6sJjXTv.mjs → config-loader-p9JMrekQ.mjs} +1 -1
  64. package/dist/{config-loader-B6sJjXTv.mjs.map → config-loader-p9JMrekQ.mjs.map} +1 -1
  65. package/dist/config-loader.mjs +1 -1
  66. package/dist/{contract-at-errors-BxP-TOMl.mjs → contract-at-errors-CFXsstzm.mjs} +2 -2
  67. package/dist/{contract-at-errors-BxP-TOMl.mjs.map → contract-at-errors-CFXsstzm.mjs.map} +1 -1
  68. package/dist/{contract-emit-DxcGl4Uq.mjs → contract-emit-B_qriF8B.mjs} +5 -5
  69. package/dist/{contract-emit-DxcGl4Uq.mjs.map → contract-emit-B_qriF8B.mjs.map} +1 -1
  70. package/dist/{contract-emit-D-4jrNve.mjs → contract-emit-C8HmtboH.mjs} +12 -7
  71. package/dist/contract-emit-C8HmtboH.mjs.map +1 -0
  72. package/dist/{contract-enrichment-a0V5Y_mL.mjs → contract-enrichment-gn9sWbPw.mjs} +1 -1
  73. package/dist/{contract-enrichment-a0V5Y_mL.mjs.map → contract-enrichment-gn9sWbPw.mjs.map} +1 -1
  74. package/dist/{contract-infer-D8uEbJuu.mjs → contract-infer-Bsp46T8u.mjs} +3 -3
  75. package/dist/{contract-infer-D8uEbJuu.mjs.map → contract-infer-Bsp46T8u.mjs.map} +1 -1
  76. package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs → contract-space-aggregate-loader-ClI1KN6d.mjs} +5 -5
  77. package/dist/{contract-space-aggregate-loader-DvZwdkrr.mjs.map → contract-space-aggregate-loader-ClI1KN6d.mjs.map} +1 -1
  78. package/dist/{db-verify-v_vUKXTU.mjs → db-verify-CMKyBJZH.mjs} +6 -6
  79. package/dist/{db-verify-v_vUKXTU.mjs.map → db-verify-CMKyBJZH.mjs.map} +1 -1
  80. package/dist/exports/control-api.d.mts +2 -2
  81. package/dist/exports/control-api.d.mts.map +1 -1
  82. package/dist/exports/control-api.mjs +3 -3
  83. package/dist/exports/index.mjs +1 -1
  84. package/dist/exports/index.mjs.map +1 -1
  85. package/dist/exports/init-output.d.mts +1 -3
  86. package/dist/exports/init-output.d.mts.map +1 -1
  87. package/dist/exports/init-output.mjs +1 -1
  88. package/dist/{extension-pack-inputs-IDvjRCi3.mjs → extension-pack-inputs-1ySHqxKG.mjs} +1 -1
  89. package/dist/{extension-pack-inputs-IDvjRCi3.mjs.map → extension-pack-inputs-1ySHqxKG.mjs.map} +1 -1
  90. package/dist/{framework-components-fYXjz_in.mjs → framework-components-YVQHhPH7.mjs} +2 -2
  91. package/dist/{framework-components-fYXjz_in.mjs.map → framework-components-YVQHhPH7.mjs.map} +1 -1
  92. package/dist/{global-flags-DEHjV8_s.d.mts → global-flags-BpoOYtNZ.d.mts} +1 -1
  93. package/dist/{global-flags-DEHjV8_s.d.mts.map → global-flags-BpoOYtNZ.d.mts.map} +1 -1
  94. package/dist/{init-Cv9UzWL5.mjs → init-0HwB-Vh8.mjs} +5 -58
  95. package/dist/init-0HwB-Vh8.mjs.map +1 -0
  96. package/dist/{inspect-live-schema-C6ohV_oQ.mjs → inspect-live-schema-CDXkYGh0.mjs} +5 -5
  97. package/dist/{inspect-live-schema-C6ohV_oQ.mjs.map → inspect-live-schema-CDXkYGh0.mjs.map} +1 -1
  98. package/dist/migration-check-VwM8xCZV.mjs +574 -0
  99. package/dist/migration-check-VwM8xCZV.mjs.map +1 -0
  100. package/dist/migration-cli.mjs +1 -1
  101. package/dist/migration-cli.mjs.map +1 -1
  102. package/dist/{migration-command-scaffold-CjvwO6at.mjs → migration-command-scaffold-BC3X6KBg.mjs} +5 -5
  103. package/dist/{migration-command-scaffold-CjvwO6at.mjs.map → migration-command-scaffold-BC3X6KBg.mjs.map} +1 -1
  104. package/dist/migration-graph-space-render-Cpg0ql8v.mjs +2370 -0
  105. package/dist/migration-graph-space-render-Cpg0ql8v.mjs.map +1 -0
  106. package/dist/migration-list-CyLslAtv.mjs +230 -0
  107. package/dist/migration-list-CyLslAtv.mjs.map +1 -0
  108. package/dist/migration-log-DvC-Iq_k.mjs +222 -0
  109. package/dist/migration-log-DvC-Iq_k.mjs.map +1 -0
  110. package/dist/migration-path-target-Ce6OZImp.mjs +38 -0
  111. package/dist/migration-path-target-Ce6OZImp.mjs.map +1 -0
  112. package/dist/{migration-plan-9DJ7q7_z.mjs → migration-plan-DUBRTJEl.mjs} +7 -7
  113. package/dist/{migration-plan-9DJ7q7_z.mjs.map → migration-plan-DUBRTJEl.mjs.map} +1 -1
  114. package/dist/migration-status-DnEW9YQn.mjs +447 -0
  115. package/dist/migration-status-DnEW9YQn.mjs.map +1 -0
  116. package/dist/{output-B60Gw5fu.mjs → output-mEQ74_nd.mjs} +1 -1
  117. package/dist/{output-B60Gw5fu.mjs.map → output-mEQ74_nd.mjs.map} +1 -1
  118. package/dist/{progress-adapter-C644QK8l.mjs → progress-adapter-CjAeTxY_.mjs} +1 -1
  119. package/dist/{progress-adapter-C644QK8l.mjs.map → progress-adapter-CjAeTxY_.mjs.map} +1 -1
  120. package/dist/{ref-advancement-DUZqsue6.mjs → ref-advancement-BkXlikCA.mjs} +1 -1
  121. package/dist/{ref-advancement-DUZqsue6.mjs.map → ref-advancement-BkXlikCA.mjs.map} +1 -1
  122. package/dist/schemas-CeGMYFYX.d.mts +191 -0
  123. package/dist/schemas-CeGMYFYX.d.mts.map +1 -0
  124. package/dist/schemas-KhXMzNA_.mjs +112 -0
  125. package/dist/schemas-KhXMzNA_.mjs.map +1 -0
  126. package/dist/telemetry-BIM4beEO.mjs +122 -0
  127. package/dist/telemetry-BIM4beEO.mjs.map +1 -0
  128. package/dist/{terminal-ui-5Y6mrg93.d.mts → terminal-ui-DGRNFWna.d.mts} +1 -1
  129. package/dist/terminal-ui-DGRNFWna.d.mts.map +1 -0
  130. package/dist/{types-Dt_SfqFm.d.mts → types-BepB6ydp.d.mts} +44 -31
  131. package/dist/types-BepB6ydp.d.mts.map +1 -0
  132. package/dist/{verify-DCA9Sldu.mjs → verify-DcOYZ1tH.mjs} +2 -2
  133. package/dist/{verify-DCA9Sldu.mjs.map → verify-DcOYZ1tH.mjs.map} +1 -1
  134. package/package.json +25 -22
  135. package/src/cli.ts +5 -0
  136. package/src/commands/db-update.ts +7 -1
  137. package/src/commands/init/index.ts +6 -35
  138. package/src/commands/init/init.ts +1 -14
  139. package/src/commands/init/inputs.ts +0 -75
  140. package/src/commands/json/schemas.ts +195 -0
  141. package/src/commands/migrate.ts +518 -8
  142. package/src/commands/migration-check.ts +469 -134
  143. package/src/commands/migration-graph.ts +164 -91
  144. package/src/commands/migration-list.ts +72 -39
  145. package/src/commands/migration-log.ts +52 -102
  146. package/src/commands/migration-show.ts +31 -66
  147. package/src/commands/migration-status-overlay.ts +61 -0
  148. package/src/commands/migration-status.ts +457 -1067
  149. package/src/commands/telemetry/index.ts +107 -0
  150. package/src/commands/telemetry/status.ts +67 -0
  151. package/src/control-api/client.ts +40 -9
  152. package/src/control-api/operations/contract-emit.ts +22 -2
  153. package/src/control-api/operations/db-init.ts +3 -3
  154. package/src/control-api/operations/{db-apply.ts → db-run.ts} +51 -13
  155. package/src/control-api/operations/db-update.ts +4 -4
  156. package/src/control-api/operations/db-verify.ts +15 -5
  157. package/src/control-api/operations/{migration-apply.ts → migrate.ts} +181 -80
  158. package/src/control-api/operations/{apply.ts → run-migration.ts} +33 -27
  159. package/src/control-api/types.ts +46 -29
  160. package/src/utils/cli-errors.ts +70 -2
  161. package/src/utils/formatters/errors.ts +11 -0
  162. package/src/utils/formatters/migration-graph-lane-colors.ts +194 -0
  163. package/src/utils/formatters/migration-graph-layout.ts +227 -38
  164. package/src/utils/formatters/migration-graph-rows.ts +128 -15
  165. package/src/utils/formatters/migration-graph-space-render.ts +148 -0
  166. package/src/utils/formatters/migration-graph-tree-render.ts +959 -81
  167. package/src/utils/formatters/migration-list-data-column.ts +4 -91
  168. package/src/utils/formatters/migration-list-graph-topology.ts +72 -94
  169. package/src/utils/formatters/migration-list-render.ts +135 -71
  170. package/src/utils/formatters/migration-list-styler.ts +46 -5
  171. package/src/utils/formatters/migration-list-types.ts +5 -21
  172. package/src/utils/formatters/migration-log-table.ts +205 -0
  173. package/src/utils/formatters/migrations.ts +33 -11
  174. package/src/utils/global-flags.ts +35 -0
  175. package/src/utils/integrity-violation-to-check-failure.ts +28 -19
  176. package/src/utils/legend.ts +38 -0
  177. package/src/utils/migration-path-target.ts +60 -0
  178. package/src/utils/telemetry.ts +68 -32
  179. package/dist/client-KgJorIvG.mjs.map +0 -1
  180. package/dist/command-helpers-Bbw1GbwL.mjs.map +0 -1
  181. package/dist/commands/migration-list.mjs.map +0 -1
  182. package/dist/commands/migration-log.mjs.map +0 -1
  183. package/dist/commands/migration-status.mjs.map +0 -1
  184. package/dist/contract-emit-D-4jrNve.mjs.map +0 -1
  185. package/dist/graph-render-rFAqZujX.mjs +0 -1081
  186. package/dist/graph-render-rFAqZujX.mjs.map +0 -1
  187. package/dist/init-Cv9UzWL5.mjs.map +0 -1
  188. package/dist/migration-check-BiBJoYYW.mjs +0 -341
  189. package/dist/migration-check-BiBJoYYW.mjs.map +0 -1
  190. package/dist/migration-graph-D7DVUElV.mjs +0 -1232
  191. package/dist/migration-graph-D7DVUElV.mjs.map +0 -1
  192. package/dist/migration-list-styler-BRwF4-gy.mjs +0 -399
  193. package/dist/migration-list-styler-BRwF4-gy.mjs.map +0 -1
  194. package/dist/migration-types-D2FW63pr.d.mts +0 -15
  195. package/dist/migration-types-D2FW63pr.d.mts.map +0 -1
  196. package/dist/migrations-Cv2jxNNK.mjs +0 -228
  197. package/dist/migrations-Cv2jxNNK.mjs.map +0 -1
  198. package/dist/terminal-ui-5Y6mrg93.d.mts.map +0 -1
  199. package/dist/types-Dt_SfqFm.d.mts.map +0 -1
  200. package/src/utils/formatters/graph-migration-mapper.ts +0 -235
  201. package/src/utils/formatters/graph-render.ts +0 -1323
  202. package/src/utils/formatters/graph-types.ts +0 -120
@@ -0,0 +1,107 @@
1
+ import { userConfigPath, writeUserConfig } from '@prisma-next/cli-telemetry';
2
+ import { Command } from 'commander';
3
+ import {
4
+ addGlobalOptions,
5
+ setCommandDescriptions,
6
+ setCommandExamples,
7
+ } from '../../utils/command-helpers';
8
+ import { formatCommandHelp } from '../../utils/formatters/help';
9
+ import {
10
+ type CommonCommandOptions,
11
+ parseGlobalFlags,
12
+ parseGlobalFlagsOrExit,
13
+ } from '../../utils/global-flags';
14
+ import { isCI } from '../../utils/is-ci';
15
+ import { createTerminalUI } from '../../utils/terminal-ui';
16
+ import { formatTelemetryStatusLines, resolveTelemetryStatus } from './status';
17
+
18
+ function createTelemetryStatusCommand(): Command {
19
+ const command = new Command('status');
20
+ setCommandDescriptions(
21
+ command,
22
+ 'Show whether anonymous CLI telemetry is enabled and why',
23
+ 'Reports whether telemetry is currently enabled or disabled and the reason\n' +
24
+ '(default-on, stored opt-out, environment opt-out, or CI), the path to your\n' +
25
+ 'user-level config file, and whether an installation ID has been stored.\n' +
26
+ 'Read-only: never sends an event, never mints an ID, never writes anything.',
27
+ );
28
+ return addGlobalOptions(command).action((options: CommonCommandOptions) => {
29
+ const flags = parseGlobalFlagsOrExit(options);
30
+ const ui = createTerminalUI(flags);
31
+ const status = resolveTelemetryStatus({ env: process.env, inCI: isCI() });
32
+ if (flags.json) {
33
+ ui.output(JSON.stringify(status));
34
+ } else {
35
+ for (const line of formatTelemetryStatusLines(status)) {
36
+ ui.output(line);
37
+ }
38
+ }
39
+ process.exit(0);
40
+ });
41
+ }
42
+
43
+ function createTelemetryEnableCommand(): Command {
44
+ const command = new Command('enable');
45
+ setCommandDescriptions(
46
+ command,
47
+ 'Enable anonymous CLI telemetry',
48
+ 'Stores "enableTelemetry": true in your user-level config and mints an\n' +
49
+ 'installation ID if one is not already stored.',
50
+ );
51
+ return addGlobalOptions(command).action((options: CommonCommandOptions) => {
52
+ const flags = parseGlobalFlagsOrExit(options);
53
+ writeUserConfig({ enableTelemetry: true });
54
+ const ui = createTerminalUI(flags);
55
+ if (flags.json) {
56
+ ui.output(JSON.stringify({ enableTelemetry: true, configPath: userConfigPath() }));
57
+ } else {
58
+ ui.output(`Telemetry enabled. Preference stored in ${userConfigPath()}.`);
59
+ }
60
+ process.exit(0);
61
+ });
62
+ }
63
+
64
+ function createTelemetryDisableCommand(): Command {
65
+ const command = new Command('disable');
66
+ setCommandDescriptions(
67
+ command,
68
+ 'Disable anonymous CLI telemetry',
69
+ 'Stores "enableTelemetry": false in your user-level config. No installation\n' +
70
+ 'ID is minted and no event is sent.',
71
+ );
72
+ return addGlobalOptions(command).action((options: CommonCommandOptions) => {
73
+ const flags = parseGlobalFlagsOrExit(options);
74
+ writeUserConfig({ enableTelemetry: false });
75
+ const ui = createTerminalUI(flags);
76
+ if (flags.json) {
77
+ ui.output(JSON.stringify({ enableTelemetry: false, configPath: userConfigPath() }));
78
+ } else {
79
+ ui.output(`Telemetry disabled. Preference stored in ${userConfigPath()}.`);
80
+ }
81
+ process.exit(0);
82
+ });
83
+ }
84
+
85
+ export function createTelemetryCommand(): Command {
86
+ const command = new Command('telemetry');
87
+ setCommandDescriptions(
88
+ command,
89
+ 'Inspect and change anonymous CLI telemetry',
90
+ 'Show telemetry status, or enable / disable anonymous CLI usage data.\n' +
91
+ 'Telemetry is on by default (opt-out); see https://prisma-next.dev/docs/cli/telemetry\n' +
92
+ 'for what is collected and why.',
93
+ );
94
+ setCommandExamples(command, [
95
+ 'prisma-next telemetry status',
96
+ 'prisma-next telemetry disable',
97
+ 'prisma-next telemetry enable',
98
+ ]);
99
+ command.configureHelp({
100
+ formatHelp: (cmd) => formatCommandHelp({ command: cmd, flags: parseGlobalFlags({}) }),
101
+ subcommandDescription: () => '',
102
+ });
103
+ command.addCommand(createTelemetryStatusCommand());
104
+ command.addCommand(createTelemetryEnableCommand());
105
+ command.addCommand(createTelemetryDisableCommand());
106
+ return command;
107
+ }
@@ -0,0 +1,67 @@
1
+ import { readUserConfig, resolveGating, userConfigPath } from '@prisma-next/cli-telemetry';
2
+
3
+ /**
4
+ * Why telemetry resolves the way it does, in the order the CLI's
5
+ * `resolveTelemetryGate` evaluates: CI hard-disables first, then the env
6
+ * opt-outs, then the stored `enableTelemetry`, then the opt-out default.
7
+ */
8
+ export type TelemetryStatusReason =
9
+ | 'ci'
10
+ | 'env-opt-out'
11
+ | 'stored-opt-out'
12
+ | 'stored-opt-in'
13
+ | 'default-on';
14
+
15
+ export interface TelemetryStatus {
16
+ readonly enabled: boolean;
17
+ readonly reason: TelemetryStatusReason;
18
+ readonly configPath: string;
19
+ readonly installationIdStored: boolean;
20
+ }
21
+
22
+ /**
23
+ * Resolves the same gate the runtime uses (CI check + `resolveGating`) and
24
+ * projects it into a user-facing status. Pure read: never mints, never
25
+ * writes. The `installationId` value itself is never surfaced — only its
26
+ * presence — so `status` discloses nothing identifying.
27
+ */
28
+ export function resolveTelemetryStatus(inputs: {
29
+ readonly env: Readonly<Record<string, string | undefined>>;
30
+ readonly inCI: boolean;
31
+ }): TelemetryStatus {
32
+ const config = readUserConfig();
33
+ const configPath = userConfigPath();
34
+ const installationIdStored =
35
+ typeof config.installationId === 'string' && config.installationId.length > 0;
36
+
37
+ if (inputs.inCI) {
38
+ return { enabled: false, reason: 'ci', configPath, installationIdStored };
39
+ }
40
+
41
+ const gating = resolveGating({ env: inputs.env, config });
42
+ if (!gating.enabled) {
43
+ const reason: TelemetryStatusReason =
44
+ gating.reason === 'env-override' ? 'env-opt-out' : 'stored-opt-out';
45
+ return { enabled: false, reason, configPath, installationIdStored };
46
+ }
47
+
48
+ const reason: TelemetryStatusReason =
49
+ config.enableTelemetry === true ? 'stored-opt-in' : 'default-on';
50
+ return { enabled: true, reason, configPath, installationIdStored };
51
+ }
52
+
53
+ const REASON_EXPLANATION: Record<TelemetryStatusReason, string> = {
54
+ ci: 'CI environment detected — telemetry is hard-disabled.',
55
+ 'env-opt-out': 'an environment opt-out is set (DO_NOT_TRACK / PRISMA_NEXT_DISABLE_TELEMETRY).',
56
+ 'stored-opt-out': '"enableTelemetry": false is stored in your config.',
57
+ 'stored-opt-in': '"enableTelemetry": true is stored in your config.',
58
+ 'default-on': 'no explicit choice is stored, so the opt-out default applies.',
59
+ };
60
+
61
+ export function formatTelemetryStatusLines(status: TelemetryStatus): string[] {
62
+ return [
63
+ `Telemetry is ${status.enabled ? 'enabled' : 'disabled'}: ${REASON_EXPLANATION[status.reason]}`,
64
+ `Config file: ${status.configPath}`,
65
+ `Installation ID: ${status.installationIdStored ? 'stored' : 'not stored'}`,
66
+ ];
67
+ }
@@ -1,4 +1,8 @@
1
- import type { Contract, ContractMarkerRecord } from '@prisma-next/contract/types';
1
+ import type {
2
+ Contract,
3
+ ContractMarkerRecord,
4
+ LedgerEntryRecord,
5
+ } from '@prisma-next/contract/types';
2
6
  import { emit as emitContractArtifacts } from '@prisma-next/emitter';
3
7
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
4
8
  import type {
@@ -21,15 +25,17 @@ import {
21
25
  hasSchemaView,
22
26
  } from '@prisma-next/framework-components/control';
23
27
  import type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';
28
+ import { blindCast } from '@prisma-next/utils/casts';
24
29
  import { ifDefined } from '@prisma-next/utils/defined';
25
30
  import { notOk, ok } from '@prisma-next/utils/result';
31
+ import { toExtensionInputs } from '../utils/extension-pack-inputs';
26
32
  import { assertFrameworkComponentsCompatible } from '../utils/framework-components';
27
33
  import { enrichContract } from './contract-enrichment';
28
34
  import { ContractValidationError } from './errors';
29
35
  import { executeDbInit } from './operations/db-init';
30
36
  import { executeDbUpdate } from './operations/db-update';
31
37
  import { type ExecuteDbVerifyResult, executeDbVerify } from './operations/db-verify';
32
- import { executeMigrationApply } from './operations/migration-apply';
38
+ import { executeMigrate } from './operations/migrate';
33
39
 
34
40
  import type {
35
41
  ControlActionName,
@@ -43,8 +49,8 @@ import type {
43
49
  EmitOptions,
44
50
  EmitResult,
45
51
  IntrospectOptions,
46
- MigrationApplyOptions,
47
- MigrationApplyResult,
52
+ MigrateOptions,
53
+ MigrateResult,
48
54
  OnControlProgress,
49
55
  SchemaVerifyOptions,
50
56
  SignOptions,
@@ -447,9 +453,15 @@ class ControlClientImpl implements ControlClient {
447
453
  return familyInstance.readAllMarkers({ driver });
448
454
  }
449
455
 
450
- async migrationApply(options: MigrationApplyOptions): Promise<MigrationApplyResult> {
456
+ /** Reads the per-migration journal; omit `space` to return every space. */
457
+ async readLedger(space?: string): Promise<readonly LedgerEntryRecord[]> {
458
+ const { driver, familyInstance } = await this.ensureConnected();
459
+ return familyInstance.readLedger({ driver, ...ifDefined('space', space) });
460
+ }
461
+
462
+ async migrate(options: MigrateOptions): Promise<MigrateResult> {
451
463
  const { onProgress } = options;
452
- await this.connectWithProgress(options.connection, 'migrationApply', onProgress);
464
+ await this.connectWithProgress(options.connection, 'migrate', onProgress);
453
465
  const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
454
466
 
455
467
  if (!hasMigrations(this.options.target)) {
@@ -464,7 +476,7 @@ class ControlClientImpl implements ControlClient {
464
476
  throw new ContractValidationError(message, error);
465
477
  }
466
478
 
467
- return executeMigrationApply({
479
+ return executeMigrate({
468
480
  driver,
469
481
  familyInstance,
470
482
  contract,
@@ -564,8 +576,26 @@ class ControlClientImpl implements ControlClient {
564
576
 
565
577
  try {
566
578
  const stack = this.stack!;
579
+ const extensionInputs = toExtensionInputs(
580
+ blindCast<
581
+ readonly unknown[],
582
+ 'toExtensionInputs accepts readonly unknown[] per its documented structural cast boundary'
583
+ >(stack.extensionPacks),
584
+ );
585
+ const composedExtensionContracts = new Map<string, Contract>(
586
+ extensionInputs
587
+ .filter((p) => p.contractSpace !== undefined)
588
+ .map((p) => [
589
+ p.id,
590
+ blindCast<
591
+ Contract,
592
+ 'contractSpace.contractJson is the typed contract for this extension space'
593
+ >(p.contractSpace!.contractJson),
594
+ ]),
595
+ );
567
596
  const sourceContext = {
568
597
  composedExtensionPacks: stack.extensionPacks.map((p) => p.id),
598
+ composedExtensionContracts,
569
599
  scalarTypeDescriptors: stack.scalarTypeDescriptors,
570
600
  authoringContributions: stack.authoringContributions,
571
601
  codecLookup: stack.codecLookup,
@@ -644,8 +674,9 @@ class ControlClientImpl implements ControlClient {
644
674
  this.frameworkComponents ?? [],
645
675
  );
646
676
 
677
+ let deserializedContract: Contract;
647
678
  try {
648
- this.familyInstance.deserializeContract(enrichedIR);
679
+ deserializedContract = this.familyInstance.deserializeContract(enrichedIR);
649
680
  } catch (error) {
650
681
  onProgress?.({
651
682
  action: 'emit',
@@ -663,7 +694,7 @@ class ControlClientImpl implements ControlClient {
663
694
  }
664
695
 
665
696
  const result = await emitContractArtifacts(
666
- enrichedIR,
697
+ deserializedContract,
667
698
  this.stack!,
668
699
  this.options.family.emission,
669
700
  {
@@ -3,12 +3,14 @@ import type { Contract } from '@prisma-next/contract/types';
3
3
  import { emit, getEmittedArtifactPaths } from '@prisma-next/emitter';
4
4
  import { createControlStack } from '@prisma-next/framework-components/control';
5
5
  import { abortable } from '@prisma-next/utils/abortable';
6
+ import { blindCast } from '@prisma-next/utils/casts';
6
7
  import { ifDefined } from '@prisma-next/utils/defined';
7
8
  import type { JsonObject } from '@prisma-next/utils/json';
8
9
  import { dirname, join } from 'pathe';
9
10
  import { loadConfig } from '../../config-loader';
10
11
  import { errorContractConfigMissing, errorRuntime } from '../../utils/cli-errors';
11
12
  import { queueEmitByOutput } from '../../utils/emit-queue';
13
+ import { toExtensionInputs } from '../../utils/extension-pack-inputs';
12
14
  import { assertFrameworkComponentsCompatible } from '../../utils/framework-components';
13
15
  import { publishContractArtifactPair } from '../../utils/publish-contract-artifact-pair';
14
16
  import { validateContractDeps } from '../../utils/validate-contract-deps';
@@ -194,8 +196,26 @@ export async function executeContractEmit(
194
196
  return queueEmitByOutput(outputJsonPath, async () => {
195
197
  const stack = createControlStack(config);
196
198
 
199
+ const extensionInputs = toExtensionInputs(
200
+ blindCast<
201
+ readonly unknown[],
202
+ 'toExtensionInputs accepts readonly unknown[] per its documented structural cast boundary'
203
+ >(stack.extensionPacks),
204
+ );
205
+ const composedExtensionContracts = new Map<string, Contract>(
206
+ extensionInputs
207
+ .filter((p) => p.contractSpace !== undefined)
208
+ .map((p) => [
209
+ p.id,
210
+ blindCast<
211
+ Contract,
212
+ 'contractSpace.contractJson is the typed contract for this extension space'
213
+ >(p.contractSpace!.contractJson),
214
+ ]),
215
+ );
197
216
  const sourceContext = {
198
217
  composedExtensionPacks: stack.extensionPacks.map((p) => p.id),
218
+ composedExtensionContracts,
199
219
  scalarTypeDescriptors: stack.scalarTypeDescriptors,
200
220
  authoringContributions: stack.authoringContributions,
201
221
  codecLookup: stack.codecLookup,
@@ -247,7 +267,7 @@ export async function executeContractEmit(
247
267
  validatedContract.value as unknown as Contract,
248
268
  frameworkComponents,
249
269
  );
250
- familyInstance.deserializeContract(enrichedIR);
270
+ const deserializedContract = familyInstance.deserializeContract(enrichedIR);
251
271
  // Each target's descriptor ships a `contractSerializer` SPI; the
252
272
  // framework canonicalizer threads its `serializeContract` so the
253
273
  // on-disk JSON envelope is constructed by target-owned code
@@ -260,7 +280,7 @@ export async function executeContractEmit(
260
280
  const serializeContract = (c: Contract): JsonObject =>
261
281
  contractSerializer.serializeContract(c);
262
282
  emitResult = await unlessAborted(
263
- emit(enrichedIR, stack, config.family.emission, {
283
+ emit(deserializedContract, stack, config.family.emission, {
264
284
  outputJsonPath,
265
285
  serializeContract,
266
286
  ...ifDefined('shouldPreserveEmpty', contractSerializer.shouldPreserveEmpty),
@@ -8,14 +8,14 @@ import type {
8
8
  } from '@prisma-next/framework-components/control';
9
9
  import { ifDefined } from '@prisma-next/utils/defined';
10
10
  import type { DbInitResult, OnControlProgress } from '../types';
11
- import { executeApply } from './db-apply';
11
+ import { executeRun } from './db-run';
12
12
 
13
13
  /**
14
14
  * Options for executing the `db init` operation.
15
15
  *
16
16
  * `db init` runs the loader → planner → runner pipeline:
17
17
  *
18
- * 1. {@link executeApply} loads a `ContractSpaceAggregate` via
18
+ * 1. {@link executeRun} loads a `ContractSpaceAggregate` via
19
19
  * {@link import('@prisma-next/migration-tools/aggregate').loadContractSpaceAggregate}
20
20
  * from the supplied descriptor set + on-disk on-disk artefacts.
21
21
  * 2. The aggregate planner runs with `callerPolicy.ignoreGraphFor`
@@ -68,7 +68,7 @@ export interface ExecuteDbInitOptions<TFamilyId extends string, TTargetId extend
68
68
  export async function executeDbInit<TFamilyId extends string, TTargetId extends string>(
69
69
  options: ExecuteDbInitOptions<TFamilyId, TTargetId>,
70
70
  ): Promise<DbInitResult> {
71
- const result = await executeApply<TFamilyId, TTargetId>({
71
+ const result = await executeRun<TFamilyId, TTargetId>({
72
72
  driver: options.driver,
73
73
  familyInstance: options.familyInstance,
74
74
  contract: options.contract,
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Backs `db init` / `db update`. Strategy: introspect → planMigration; synth-for-app + graph-walk-extensions; plan-mode + orphan-marker preflight.
3
+ */
4
+
1
5
  import type { Contract } from '@prisma-next/contract/types';
2
6
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
3
7
  import type {
@@ -5,6 +9,7 @@ import type {
5
9
  ControlExtensionDescriptor,
6
10
  ControlFamilyInstance,
7
11
  MigrationOperationPolicy,
12
+ MigrationPlannerConflict,
8
13
  MigrationPlanOperation,
9
14
  OperationPreview,
10
15
  TargetMigrationsCapability,
@@ -12,9 +17,11 @@ import type {
12
17
  import { hasOperationPreview } from '@prisma-next/framework-components/control';
13
18
  import {
14
19
  type ContractSpaceAggregate,
20
+ collectAggregateNamespaces,
15
21
  type PlannerError,
16
22
  planMigration,
17
23
  } from '@prisma-next/migration-tools/aggregate';
24
+ import { blindCast } from '@prisma-next/utils/casts';
18
25
  import { ifDefined } from '@prisma-next/utils/defined';
19
26
  import { notOk, ok } from '@prisma-next/utils/result';
20
27
  import { CliStructuredError } from '../../utils/cli-errors';
@@ -32,14 +39,19 @@ import type {
32
39
  OnControlProgress,
33
40
  PerSpaceExecutionEntry,
34
41
  } from '../types';
35
- import { applyMigration, buildPerSpaceBreakdown, collectOrdered } from './apply';
36
42
  import { stripOperations } from './migration-helpers';
43
+ import {
44
+ buildPerSpaceBreakdown,
45
+ collectOrdered,
46
+ type OrderedResolution,
47
+ runMigration,
48
+ } from './run-migration';
37
49
 
38
50
  /**
39
- * Span IDs emitted via `onProgress` during the apply flow.
51
+ * Span IDs emitted via `onProgress` during the run flow.
40
52
  * Stable identifiers consumed by the structured-output renderer and by
41
53
  * tests asserting on span ids. The `apply` span itself is owned by
42
- * the {@link applyMigration} primitive — only the introspect / plan
54
+ * the {@link runMigration} primitive — only the introspect / plan
43
55
  * spans are emitted directly here.
44
56
  */
45
57
  const SPAN_IDS = {
@@ -48,13 +60,13 @@ const SPAN_IDS = {
48
60
  } as const;
49
61
 
50
62
  /**
51
- * Inputs shared by `db init` and `db update` apply flows.
63
+ * Inputs shared by `db init` and `db update` run flows.
52
64
  *
53
65
  * Accepts the already-validated app contract + descriptor list — the
54
66
  * loader gathers the rest from disk + descriptors. The CLI is the
55
67
  * descriptor-import boundary; everything downstream is descriptor-free.
56
68
  */
57
- export interface ExecuteApplyOptions<TFamilyId extends string, TTargetId extends string> {
69
+ export interface ExecuteRunOptions<TFamilyId extends string, TTargetId extends string> {
58
70
  readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;
59
71
  readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
60
72
  readonly contract: Contract;
@@ -92,8 +104,8 @@ export interface ExecuteApplyOptions<TFamilyId extends string, TTargetId extends
92
104
  * transaction across every space; failure on any space rolls back
93
105
  * every space's writes.
94
106
  */
95
- export async function executeApply<TFamilyId extends string, TTargetId extends string>(
96
- options: ExecuteApplyOptions<TFamilyId, TTargetId>,
107
+ export async function executeRun<TFamilyId extends string, TTargetId extends string>(
108
+ options: ExecuteRunOptions<TFamilyId, TTargetId>,
97
109
  ): Promise<DbInitResult | DbUpdateResult> {
98
110
  const {
99
111
  driver,
@@ -147,7 +159,10 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
147
159
  spanId: SPAN_IDS.introspect,
148
160
  label: 'Introspecting database schema',
149
161
  });
150
- const schemaIR = await familyInstance.introspect({ driver });
162
+ const schemaIR = await familyInstance.introspect({
163
+ driver,
164
+ contract: collectAggregateNamespaces(aggregate),
165
+ });
151
166
  onProgress?.({ action, kind: 'spanEnd', spanId: SPAN_IDS.introspect, outcome: 'ok' });
152
167
 
153
168
  // 3. Plan via aggregate planner. App is forced through synth (today's
@@ -175,6 +190,7 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
175
190
  onProgress?.({ action, kind: 'spanEnd', spanId: SPAN_IDS.plan, outcome: 'ok' });
176
191
 
177
192
  const orderedResolutions = collectOrdered(planResult.value.applyOrder, planResult.value.perSpace);
193
+ const plannerWarnings = aggregatePlannerWarnings(orderedResolutions);
178
194
 
179
195
  // The destination's structural shape comes from the app's plan — its
180
196
  // `destination` is the storage hash users see in CLI output.
@@ -202,16 +218,17 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
202
218
  preview,
203
219
  perSpace,
204
220
  summary,
221
+ ...ifDefined('warnings', plannerWarnings),
205
222
  });
206
223
  }
207
224
 
208
- // 5. Apply mode: hand off to the shared `applyMigration` primitive.
225
+ // 5. Run mode: hand off to the shared `runMigration` primitive.
209
226
  // The runner-driving tail is identical for `db init` / `db update` /
210
227
  // `migrate` — only how each caller produces `perSpacePlans`
211
228
  // differs (synth + graph-walk via planMigration here; graph-walk
212
229
  // only for migrate). Each caller produces perSpacePlans differently;
213
- // this helper handles the shared apply tail.
214
- const applied = await applyMigration({
230
+ // this helper handles the shared run tail.
231
+ const applied = await runMigration({
215
232
  aggregate,
216
233
  perSpacePlans: planResult.value.perSpace,
217
234
  applyOrder: planResult.value.applyOrder,
@@ -228,6 +245,7 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
228
245
  summary: applied.failure.summary,
229
246
  ...ifDefined('why', applied.failure.why),
230
247
  meta: applied.failure.meta,
248
+ ...ifDefined('warnings', plannerWarnings),
231
249
  });
232
250
  }
233
251
 
@@ -246,9 +264,17 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
246
264
  operationsExecuted: applied.value.totalOpsExecuted,
247
265
  perSpace: applied.value.perSpace,
248
266
  summary,
267
+ ...ifDefined('warnings', plannerWarnings),
249
268
  });
250
269
  }
251
270
 
271
+ function aggregatePlannerWarnings(
272
+ orderedResolutions: readonly OrderedResolution[],
273
+ ): readonly MigrationPlannerConflict[] | undefined {
274
+ const warnings = orderedResolutions.flatMap((r) => r.entry.warnings ?? []);
275
+ return warnings.length > 0 ? warnings : undefined;
276
+ }
277
+
252
278
  /**
253
279
  * Compare the live `_prisma_marker` rows against the aggregate's
254
280
  * declared members. Any marker row whose `space` is not a member of
@@ -307,7 +333,10 @@ function mapPlannerError(error: PlannerError): DbInitResult | DbUpdateResult {
307
333
  why: undefined,
308
334
  meta: undefined,
309
335
  };
310
- return notOk(failure) as DbInitResult | DbUpdateResult;
336
+ return blindCast<
337
+ DbInitResult | DbUpdateResult,
338
+ 'notOk(failure) is shape-compatible with both DbInitResult and DbUpdateResult; the union is the return type of the surrounding function'
339
+ >(notOk(failure));
311
340
  }
312
341
  if (error.kind === 'extensionPathUnreachable') {
313
342
  return buildRunnerFailure({
@@ -339,6 +368,7 @@ function wrapPlanResult(args: {
339
368
  readonly preview: OperationPreview | undefined;
340
369
  readonly perSpace: readonly PerSpaceExecutionEntry[];
341
370
  readonly summary: string;
371
+ readonly warnings?: readonly MigrationPlannerConflict[];
342
372
  }): DbInitResult | DbUpdateResult {
343
373
  const success: DbInitSuccess | DbUpdateSuccess = {
344
374
  mode: 'plan',
@@ -352,6 +382,7 @@ function wrapPlanResult(args: {
352
382
  },
353
383
  perSpace: args.perSpace,
354
384
  summary: args.summary,
385
+ ...ifDefined('warnings', args.warnings),
355
386
  };
356
387
  return ok(success);
357
388
  }
@@ -363,6 +394,7 @@ function wrapApplyResult(args: {
363
394
  readonly operationsExecuted: number;
364
395
  readonly perSpace: readonly PerSpaceExecutionEntry[];
365
396
  readonly summary: string;
397
+ readonly warnings?: readonly MigrationPlannerConflict[];
366
398
  }): DbInitResult | DbUpdateResult {
367
399
  const success: DbInitSuccess | DbUpdateSuccess = {
368
400
  mode: 'apply',
@@ -380,6 +412,7 @@ function wrapApplyResult(args: {
380
412
  : { storageHash: args.destination.storageHash },
381
413
  perSpace: args.perSpace,
382
414
  summary: args.summary,
415
+ ...ifDefined('warnings', args.warnings),
383
416
  };
384
417
  return ok(success);
385
418
  }
@@ -388,6 +421,7 @@ function buildRunnerFailure(args: {
388
421
  readonly summary: string;
389
422
  readonly why?: string;
390
423
  readonly meta: Record<string, unknown>;
424
+ readonly warnings?: readonly MigrationPlannerConflict[];
391
425
  }): DbInitResult | DbUpdateResult {
392
426
  const failure: DbInitFailure | DbUpdateFailure = {
393
427
  code: 'RUNNER_FAILED',
@@ -395,6 +429,10 @@ function buildRunnerFailure(args: {
395
429
  why: args.why,
396
430
  meta: args.meta,
397
431
  conflicts: undefined,
432
+ ...ifDefined('warnings', args.warnings),
398
433
  };
399
- return notOk(failure) as DbInitResult | DbUpdateResult;
434
+ return blindCast<
435
+ DbInitResult | DbUpdateResult,
436
+ 'notOk(failure) is shape-compatible with both DbInitResult and DbUpdateResult; the union is the return type of the surrounding function'
437
+ >(notOk(failure));
400
438
  }
@@ -9,7 +9,7 @@ import type {
9
9
  import { ifDefined } from '@prisma-next/utils/defined';
10
10
  import { notOk } from '@prisma-next/utils/result';
11
11
  import type { DbUpdateResult, OnControlProgress } from '../types';
12
- import { executeApply } from './db-apply';
12
+ import { executeRun } from './db-run';
13
13
 
14
14
  const DB_UPDATE_POLICY = {
15
15
  allowedOperationClasses: ['additive', 'widening', 'destructive'] as const,
@@ -71,7 +71,7 @@ export async function executeDbUpdate<TFamilyId extends string, TTargetId extend
71
71
  const gate = await guardDestructiveChanges<TFamilyId, TTargetId>(sharedInputs);
72
72
  if (gate !== null) return gate;
73
73
  }
74
- return (await executeApply<TFamilyId, TTargetId>({
74
+ return (await executeRun<TFamilyId, TTargetId>({
75
75
  ...sharedInputs,
76
76
  mode: options.mode,
77
77
  })) as DbUpdateResult;
@@ -85,9 +85,9 @@ export async function executeDbUpdate<TFamilyId extends string, TTargetId extend
85
85
  * run.
86
86
  */
87
87
  async function guardDestructiveChanges<TFamilyId extends string, TTargetId extends string>(
88
- sharedInputs: Omit<Parameters<typeof executeApply<TFamilyId, TTargetId>>[0], 'mode'>,
88
+ sharedInputs: Omit<Parameters<typeof executeRun<TFamilyId, TTargetId>>[0], 'mode'>,
89
89
  ): Promise<DbUpdateResult | null> {
90
- const planResult = (await executeApply<TFamilyId, TTargetId>({
90
+ const planResult = (await executeRun<TFamilyId, TTargetId>({
91
91
  ...sharedInputs,
92
92
  mode: 'plan',
93
93
  })) as DbUpdateResult;
@@ -8,11 +8,12 @@ import type {
8
8
  } from '@prisma-next/framework-components/control';
9
9
  import {
10
10
  type ContractSpaceMember,
11
+ collectAggregateNamespaces,
11
12
  requireHeadRef,
12
13
  type VerifierOutput,
13
14
  verifyMigration,
14
15
  } from '@prisma-next/migration-tools/aggregate';
15
- import { castAs } from '@prisma-next/utils/casts';
16
+ import { blindCast, castAs } from '@prisma-next/utils/casts';
16
17
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
17
18
  import { CliStructuredError } from '../../utils/cli-errors';
18
19
  import {
@@ -101,7 +102,12 @@ export async function executeDbVerify<TFamilyId extends string, TTargetId extend
101
102
  const markersBySpaceId = await familyInstance.readAllMarkers({ driver });
102
103
  const schemaIntrospection = skipSchema
103
104
  ? null
104
- : await runIntrospection({ driver, familyInstance, onProgress });
105
+ : await runIntrospection({
106
+ driver,
107
+ familyInstance,
108
+ onProgress,
109
+ contract: collectAggregateNamespaces(aggregate),
110
+ });
105
111
 
106
112
  emitVerifySpan(onProgress, 'spanStart');
107
113
  const verifyResult = verifyMigration({
@@ -130,8 +136,9 @@ async function runIntrospection<TFamilyId extends string, TTargetId extends stri
130
136
  driver: ControlDriverInstance<TFamilyId, TTargetId>;
131
137
  familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
132
138
  onProgress: OnControlProgress | undefined;
139
+ contract: unknown;
133
140
  }): Promise<unknown> {
134
- const { driver, familyInstance, onProgress } = args;
141
+ const { driver, familyInstance, onProgress, contract } = args;
135
142
  onProgress?.({
136
143
  action: 'dbVerify',
137
144
  kind: 'spanStart',
@@ -139,7 +146,7 @@ async function runIntrospection<TFamilyId extends string, TTargetId extends stri
139
146
  label: 'Introspecting database schema',
140
147
  });
141
148
  try {
142
- const result = await familyInstance.introspect({ driver });
149
+ const result = await familyInstance.introspect({ driver, contract });
143
150
  onProgress?.({
144
151
  action: 'dbVerify',
145
152
  kind: 'spanEnd',
@@ -179,7 +186,10 @@ export function createPerMemberVerifier<TFamilyId extends string, TTargetId exte
179
186
  // The family's `TSchemaIR` is opaque to migration-tools; the
180
187
  // aggregate verifier passes through whatever we hand it. The
181
188
  // family expects its own IR shape on the way back.
182
- schema: projectedSchema as never,
189
+ schema: blindCast<
190
+ never,
191
+ 'family TSchemaIR is opaque to migration-tools; projectedSchema is passed straight through'
192
+ >(projectedSchema),
183
193
  strict: verifyMode === 'strict',
184
194
  frameworkComponents,
185
195
  });