@prisma-next/cli 0.12.0-dev.9 → 0.13.0-dev.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 (214) 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-CJzuo5wX.mjs} +222 -107
  5. package/dist/client-CJzuo5wX.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 +298 -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 +16 -25
  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 +6 -5
  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-BYT_ra_U.mjs} +5 -5
  75. package/dist/contract-infer-BYT_ra_U.mjs.map +1 -0
  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-C24FKhb7.mjs} +6 -6
  79. package/dist/{db-verify-v_vUKXTU.mjs.map → db-verify-C24FKhb7.mjs.map} +1 -1
  80. package/dist/exports/control-api.d.mts +5 -3
  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-DF6IwcDl.mjs} +7 -5
  97. package/dist/inspect-live-schema-DF6IwcDl.mjs.map +1 -0
  98. package/dist/migration-check-soB5uZEQ.mjs +573 -0
  99. package/dist/migration-check-soB5uZEQ.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-DA-Lhx6o.mjs} +5 -5
  103. package/dist/{migration-command-scaffold-CjvwO6at.mjs.map → migration-command-scaffold-DA-Lhx6o.mjs.map} +1 -1
  104. package/dist/migration-graph-command-render-CEez7YUK.mjs +1960 -0
  105. package/dist/migration-graph-command-render-CEez7YUK.mjs.map +1 -0
  106. package/dist/migration-list-DlJJ_38Z.mjs +230 -0
  107. package/dist/migration-list-DlJJ_38Z.mjs.map +1 -0
  108. package/dist/migration-log-CG0qQAFm.mjs +222 -0
  109. package/dist/migration-log-CG0qQAFm.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-z5Ing-TD.mjs} +9 -8
  113. package/dist/migration-plan-z5Ing-TD.mjs.map +1 -0
  114. package/dist/migration-status-CgWSoI_g.mjs +446 -0
  115. package/dist/migration-status-CgWSoI_g.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-C_tYiJYx.d.mts} +53 -31
  131. package/dist/types-C_tYiJYx.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 +26 -22
  135. package/src/cli.ts +5 -0
  136. package/src/commands/contract-infer.ts +2 -2
  137. package/src/commands/db-update.ts +7 -1
  138. package/src/commands/init/index.ts +6 -35
  139. package/src/commands/init/init.ts +1 -14
  140. package/src/commands/init/inputs.ts +0 -75
  141. package/src/commands/inspect-live-schema.ts +10 -0
  142. package/src/commands/json/schemas.ts +195 -0
  143. package/src/commands/migrate.ts +527 -8
  144. package/src/commands/migration-check.ts +469 -134
  145. package/src/commands/migration-graph.ts +151 -119
  146. package/src/commands/migration-list.ts +72 -39
  147. package/src/commands/migration-log.ts +52 -102
  148. package/src/commands/migration-new.ts +2 -1
  149. package/src/commands/migration-plan.ts +2 -1
  150. package/src/commands/migration-show.ts +31 -66
  151. package/src/commands/migration-status-overlay.ts +61 -0
  152. package/src/commands/migration-status.ts +458 -1066
  153. package/src/commands/telemetry/index.ts +107 -0
  154. package/src/commands/telemetry/status.ts +67 -0
  155. package/src/control-api/client.ts +70 -9
  156. package/src/control-api/operations/contract-emit.ts +22 -2
  157. package/src/control-api/operations/db-init.ts +6 -3
  158. package/src/control-api/operations/{db-apply.ts → db-run.ts} +55 -14
  159. package/src/control-api/operations/db-update.ts +7 -4
  160. package/src/control-api/operations/db-verify.ts +15 -5
  161. package/src/control-api/operations/{migration-apply.ts → migrate.ts} +181 -80
  162. package/src/control-api/operations/{apply.ts → run-migration.ts} +33 -27
  163. package/src/control-api/types.ts +56 -29
  164. package/src/utils/cli-errors.ts +70 -2
  165. package/src/utils/formatters/errors.ts +11 -0
  166. package/src/utils/formatters/migration-graph-command-render.ts +239 -0
  167. package/src/utils/formatters/migration-graph-grid-layout.ts +1134 -0
  168. package/src/utils/formatters/migration-graph-labels.ts +408 -0
  169. package/src/utils/formatters/migration-graph-model.ts +103 -0
  170. package/src/utils/formatters/migration-graph-occlusion-render.ts +258 -0
  171. package/src/utils/formatters/migration-graph-rows.ts +128 -15
  172. package/src/utils/formatters/migration-graph-space-render.ts +188 -0
  173. package/src/utils/formatters/migration-list-data-column.ts +4 -91
  174. package/src/utils/formatters/migration-list-graph-topology.ts +72 -94
  175. package/src/utils/formatters/migration-list-render.ts +135 -71
  176. package/src/utils/formatters/migration-list-styler.ts +46 -5
  177. package/src/utils/formatters/migration-list-types.ts +5 -21
  178. package/src/utils/formatters/migration-log-table.ts +205 -0
  179. package/src/utils/formatters/migrations.ts +33 -11
  180. package/src/utils/global-flags.ts +35 -0
  181. package/src/utils/integrity-violation-to-check-failure.ts +28 -19
  182. package/src/utils/legend.ts +38 -0
  183. package/src/utils/migration-path-target.ts +60 -0
  184. package/src/utils/telemetry.ts +68 -32
  185. package/dist/client-KgJorIvG.mjs.map +0 -1
  186. package/dist/command-helpers-Bbw1GbwL.mjs.map +0 -1
  187. package/dist/commands/migration-list.mjs.map +0 -1
  188. package/dist/commands/migration-log.mjs.map +0 -1
  189. package/dist/commands/migration-status.mjs.map +0 -1
  190. package/dist/contract-emit-D-4jrNve.mjs.map +0 -1
  191. package/dist/contract-infer-D8uEbJuu.mjs.map +0 -1
  192. package/dist/graph-render-rFAqZujX.mjs +0 -1081
  193. package/dist/graph-render-rFAqZujX.mjs.map +0 -1
  194. package/dist/init-Cv9UzWL5.mjs.map +0 -1
  195. package/dist/inspect-live-schema-C6ohV_oQ.mjs.map +0 -1
  196. package/dist/migration-check-BiBJoYYW.mjs +0 -341
  197. package/dist/migration-check-BiBJoYYW.mjs.map +0 -1
  198. package/dist/migration-graph-C9WC-7eO.mjs +0 -1478
  199. package/dist/migration-graph-C9WC-7eO.mjs.map +0 -1
  200. package/dist/migration-list-styler-BRwF4-gy.mjs +0 -399
  201. package/dist/migration-list-styler-BRwF4-gy.mjs.map +0 -1
  202. package/dist/migration-plan-9DJ7q7_z.mjs.map +0 -1
  203. package/dist/migration-types-D2FW63pr.d.mts +0 -15
  204. package/dist/migration-types-D2FW63pr.d.mts.map +0 -1
  205. package/dist/migrations-Cv2jxNNK.mjs +0 -228
  206. package/dist/migrations-Cv2jxNNK.mjs.map +0 -1
  207. package/dist/terminal-ui-5Y6mrg93.d.mts.map +0 -1
  208. package/dist/types-Dt_SfqFm.d.mts.map +0 -1
  209. package/src/utils/formatters/graph-migration-mapper.ts +0 -235
  210. package/src/utils/formatters/graph-render.ts +0 -1323
  211. package/src/utils/formatters/graph-types.ts +0 -120
  212. package/src/utils/formatters/migration-graph-lane-colors.ts +0 -31
  213. package/src/utils/formatters/migration-graph-layout.ts +0 -1141
  214. package/src/utils/formatters/migration-graph-tree-render.ts +0 -768
@@ -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,7 +1,13 @@
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';
7
+ import type { AuthoringPslBlockDescriptorNamespace } from '@prisma-next/framework-components/authoring';
3
8
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
4
9
  import type {
10
+ ControlAdapterInstance,
5
11
  ControlDriverInstance,
6
12
  ControlFamilyInstance,
7
13
  ControlStack,
@@ -21,15 +27,17 @@ import {
21
27
  hasSchemaView,
22
28
  } from '@prisma-next/framework-components/control';
23
29
  import type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';
30
+ import { blindCast } from '@prisma-next/utils/casts';
24
31
  import { ifDefined } from '@prisma-next/utils/defined';
25
32
  import { notOk, ok } from '@prisma-next/utils/result';
33
+ import { toExtensionInputs } from '../utils/extension-pack-inputs';
26
34
  import { assertFrameworkComponentsCompatible } from '../utils/framework-components';
27
35
  import { enrichContract } from './contract-enrichment';
28
36
  import { ContractValidationError } from './errors';
29
37
  import { executeDbInit } from './operations/db-init';
30
38
  import { executeDbUpdate } from './operations/db-update';
31
39
  import { type ExecuteDbVerifyResult, executeDbVerify } from './operations/db-verify';
32
- import { executeMigrationApply } from './operations/migration-apply';
40
+ import { executeMigrate } from './operations/migrate';
33
41
 
34
42
  import type {
35
43
  ControlActionName,
@@ -43,8 +51,8 @@ import type {
43
51
  EmitOptions,
44
52
  EmitResult,
45
53
  IntrospectOptions,
46
- MigrationApplyOptions,
47
- MigrationApplyResult,
54
+ MigrateOptions,
55
+ MigrateResult,
48
56
  OnControlProgress,
49
57
  SchemaVerifyOptions,
50
58
  SignOptions,
@@ -149,6 +157,23 @@ class ControlClientImpl implements ControlClient {
149
157
  }
150
158
  }
151
159
 
160
+ /**
161
+ * Construct the control adapter once for a migration operation and return
162
+ * it, mirroring how the runtime plane builds the execution adapter once in
163
+ * `createExecutionStack`. Only `dbInit` / `dbUpdate` need it (it lowers the
164
+ * planner's DDL); read-only operations never build it. The descriptor is
165
+ * optional on the stack — targets without migrations omit it.
166
+ */
167
+ private buildControlAdapter(): ControlAdapterInstance<string, string> {
168
+ this.init();
169
+ if (!this.stack?.adapter) {
170
+ throw new Error(
171
+ `Target "${this.options.target.targetId}" requires an adapter for migrations`,
172
+ );
173
+ }
174
+ return this.stack.adapter.create(this.stack);
175
+ }
176
+
152
177
  private async ensureConnected(): Promise<{
153
178
  driver: ControlDriverInstance<string, string>;
154
179
  familyInstance: ControlFamilyInstance<string, unknown>;
@@ -359,6 +384,8 @@ class ControlClientImpl implements ControlClient {
359
384
  throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
360
385
  }
361
386
 
387
+ const adapter = this.buildControlAdapter();
388
+
362
389
  let contract: Contract;
363
390
  try {
364
391
  contract = familyInstance.deserializeContract(options.contract);
@@ -369,6 +396,7 @@ class ControlClientImpl implements ControlClient {
369
396
 
370
397
  return executeDbInit({
371
398
  driver,
399
+ adapter,
372
400
  familyInstance,
373
401
  contract,
374
402
  mode: options.mode,
@@ -390,6 +418,8 @@ class ControlClientImpl implements ControlClient {
390
418
  throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
391
419
  }
392
420
 
421
+ const adapter = this.buildControlAdapter();
422
+
393
423
  let contract: Contract;
394
424
  try {
395
425
  contract = familyInstance.deserializeContract(options.contract);
@@ -400,6 +430,7 @@ class ControlClientImpl implements ControlClient {
400
430
 
401
431
  return executeDbUpdate({
402
432
  driver,
433
+ adapter,
403
434
  familyInstance,
404
435
  contract,
405
436
  mode: options.mode,
@@ -447,9 +478,15 @@ class ControlClientImpl implements ControlClient {
447
478
  return familyInstance.readAllMarkers({ driver });
448
479
  }
449
480
 
450
- async migrationApply(options: MigrationApplyOptions): Promise<MigrationApplyResult> {
481
+ /** Reads the per-migration journal; omit `space` to return every space. */
482
+ async readLedger(space?: string): Promise<readonly LedgerEntryRecord[]> {
483
+ const { driver, familyInstance } = await this.ensureConnected();
484
+ return familyInstance.readLedger({ driver, ...ifDefined('space', space) });
485
+ }
486
+
487
+ async migrate(options: MigrateOptions): Promise<MigrateResult> {
451
488
  const { onProgress } = options;
452
- await this.connectWithProgress(options.connection, 'migrationApply', onProgress);
489
+ await this.connectWithProgress(options.connection, 'migrate', onProgress);
453
490
  const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
454
491
 
455
492
  if (!hasMigrations(this.options.target)) {
@@ -464,7 +501,7 @@ class ControlClientImpl implements ControlClient {
464
501
  throw new ContractValidationError(message, error);
465
502
  }
466
503
 
467
- return executeMigrationApply({
504
+ return executeMigrate({
468
505
  driver,
469
506
  familyInstance,
470
507
  contract,
@@ -535,6 +572,11 @@ class ControlClientImpl implements ControlClient {
535
572
  return undefined;
536
573
  }
537
574
 
575
+ getPslBlockDescriptors(): AuthoringPslBlockDescriptorNamespace {
576
+ this.init();
577
+ return this.stack!.authoringContributions.pslBlockDescriptors;
578
+ }
579
+
538
580
  toOperationPreview(operations: readonly MigrationPlanOperation[]): OperationPreview | undefined {
539
581
  this.init();
540
582
  if (this.familyInstance && hasOperationPreview(this.familyInstance)) {
@@ -564,8 +606,26 @@ class ControlClientImpl implements ControlClient {
564
606
 
565
607
  try {
566
608
  const stack = this.stack!;
609
+ const extensionInputs = toExtensionInputs(
610
+ blindCast<
611
+ readonly unknown[],
612
+ 'toExtensionInputs accepts readonly unknown[] per its documented structural cast boundary'
613
+ >(stack.extensionPacks),
614
+ );
615
+ const composedExtensionContracts = new Map<string, Contract>(
616
+ extensionInputs
617
+ .filter((p) => p.contractSpace !== undefined)
618
+ .map((p) => [
619
+ p.id,
620
+ blindCast<
621
+ Contract,
622
+ 'contractSpace.contractJson is the typed contract for this extension space'
623
+ >(p.contractSpace!.contractJson),
624
+ ]),
625
+ );
567
626
  const sourceContext = {
568
627
  composedExtensionPacks: stack.extensionPacks.map((p) => p.id),
628
+ composedExtensionContracts,
569
629
  scalarTypeDescriptors: stack.scalarTypeDescriptors,
570
630
  authoringContributions: stack.authoringContributions,
571
631
  codecLookup: stack.codecLookup,
@@ -644,8 +704,9 @@ class ControlClientImpl implements ControlClient {
644
704
  this.frameworkComponents ?? [],
645
705
  );
646
706
 
707
+ let deserializedContract: Contract;
647
708
  try {
648
- this.familyInstance.deserializeContract(enrichedIR);
709
+ deserializedContract = this.familyInstance.deserializeContract(enrichedIR);
649
710
  } catch (error) {
650
711
  onProgress?.({
651
712
  action: 'emit',
@@ -663,7 +724,7 @@ class ControlClientImpl implements ControlClient {
663
724
  }
664
725
 
665
726
  const result = await emitContractArtifacts(
666
- enrichedIR,
727
+ deserializedContract,
667
728
  this.stack!,
668
729
  this.options.family.emission,
669
730
  {
@@ -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),
@@ -1,6 +1,7 @@
1
1
  import type { Contract } from '@prisma-next/contract/types';
2
2
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
3
3
  import type {
4
+ ControlAdapterInstance,
4
5
  ControlDriverInstance,
5
6
  ControlExtensionDescriptor,
6
7
  ControlFamilyInstance,
@@ -8,14 +9,14 @@ import type {
8
9
  } from '@prisma-next/framework-components/control';
9
10
  import { ifDefined } from '@prisma-next/utils/defined';
10
11
  import type { DbInitResult, OnControlProgress } from '../types';
11
- import { executeApply } from './db-apply';
12
+ import { executeRun } from './db-run';
12
13
 
13
14
  /**
14
15
  * Options for executing the `db init` operation.
15
16
  *
16
17
  * `db init` runs the loader → planner → runner pipeline:
17
18
  *
18
- * 1. {@link executeApply} loads a `ContractSpaceAggregate` via
19
+ * 1. {@link executeRun} loads a `ContractSpaceAggregate` via
19
20
  * {@link import('@prisma-next/migration-tools/aggregate').loadContractSpaceAggregate}
20
21
  * from the supplied descriptor set + on-disk on-disk artefacts.
21
22
  * 2. The aggregate planner runs with `callerPolicy.ignoreGraphFor`
@@ -29,6 +30,7 @@ import { executeApply } from './db-apply';
29
30
  */
30
31
  export interface ExecuteDbInitOptions<TFamilyId extends string, TTargetId extends string> {
31
32
  readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;
33
+ readonly adapter: ControlAdapterInstance<TFamilyId, TTargetId>;
32
34
  readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
33
35
  readonly contract: Contract;
34
36
  readonly mode: 'plan' | 'apply';
@@ -68,8 +70,9 @@ export interface ExecuteDbInitOptions<TFamilyId extends string, TTargetId extend
68
70
  export async function executeDbInit<TFamilyId extends string, TTargetId extends string>(
69
71
  options: ExecuteDbInitOptions<TFamilyId, TTargetId>,
70
72
  ): Promise<DbInitResult> {
71
- const result = await executeApply<TFamilyId, TTargetId>({
73
+ const result = await executeRun<TFamilyId, TTargetId>({
72
74
  driver: options.driver,
75
+ adapter: options.adapter,
73
76
  familyInstance: options.familyInstance,
74
77
  contract: options.contract,
75
78
  mode: options.mode,
@@ -1,10 +1,16 @@
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 {
8
+ ControlAdapterInstance,
4
9
  ControlDriverInstance,
5
10
  ControlExtensionDescriptor,
6
11
  ControlFamilyInstance,
7
12
  MigrationOperationPolicy,
13
+ MigrationPlannerConflict,
8
14
  MigrationPlanOperation,
9
15
  OperationPreview,
10
16
  TargetMigrationsCapability,
@@ -12,9 +18,11 @@ import type {
12
18
  import { hasOperationPreview } from '@prisma-next/framework-components/control';
13
19
  import {
14
20
  type ContractSpaceAggregate,
21
+ collectAggregateNamespaces,
15
22
  type PlannerError,
16
23
  planMigration,
17
24
  } from '@prisma-next/migration-tools/aggregate';
25
+ import { blindCast } from '@prisma-next/utils/casts';
18
26
  import { ifDefined } from '@prisma-next/utils/defined';
19
27
  import { notOk, ok } from '@prisma-next/utils/result';
20
28
  import { CliStructuredError } from '../../utils/cli-errors';
@@ -32,14 +40,19 @@ import type {
32
40
  OnControlProgress,
33
41
  PerSpaceExecutionEntry,
34
42
  } from '../types';
35
- import { applyMigration, buildPerSpaceBreakdown, collectOrdered } from './apply';
36
43
  import { stripOperations } from './migration-helpers';
44
+ import {
45
+ buildPerSpaceBreakdown,
46
+ collectOrdered,
47
+ type OrderedResolution,
48
+ runMigration,
49
+ } from './run-migration';
37
50
 
38
51
  /**
39
- * Span IDs emitted via `onProgress` during the apply flow.
52
+ * Span IDs emitted via `onProgress` during the run flow.
40
53
  * Stable identifiers consumed by the structured-output renderer and by
41
54
  * tests asserting on span ids. The `apply` span itself is owned by
42
- * the {@link applyMigration} primitive — only the introspect / plan
55
+ * the {@link runMigration} primitive — only the introspect / plan
43
56
  * spans are emitted directly here.
44
57
  */
45
58
  const SPAN_IDS = {
@@ -48,14 +61,15 @@ const SPAN_IDS = {
48
61
  } as const;
49
62
 
50
63
  /**
51
- * Inputs shared by `db init` and `db update` apply flows.
64
+ * Inputs shared by `db init` and `db update` run flows.
52
65
  *
53
66
  * Accepts the already-validated app contract + descriptor list — the
54
67
  * loader gathers the rest from disk + descriptors. The CLI is the
55
68
  * descriptor-import boundary; everything downstream is descriptor-free.
56
69
  */
57
- export interface ExecuteApplyOptions<TFamilyId extends string, TTargetId extends string> {
70
+ export interface ExecuteRunOptions<TFamilyId extends string, TTargetId extends string> {
58
71
  readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;
72
+ readonly adapter: ControlAdapterInstance<TFamilyId, TTargetId>;
59
73
  readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
60
74
  readonly contract: Contract;
61
75
  readonly mode: 'plan' | 'apply';
@@ -92,11 +106,12 @@ export interface ExecuteApplyOptions<TFamilyId extends string, TTargetId extends
92
106
  * transaction across every space; failure on any space rolls back
93
107
  * every space's writes.
94
108
  */
95
- export async function executeApply<TFamilyId extends string, TTargetId extends string>(
96
- options: ExecuteApplyOptions<TFamilyId, TTargetId>,
109
+ export async function executeRun<TFamilyId extends string, TTargetId extends string>(
110
+ options: ExecuteRunOptions<TFamilyId, TTargetId>,
97
111
  ): Promise<DbInitResult | DbUpdateResult> {
98
112
  const {
99
113
  driver,
114
+ adapter,
100
115
  familyInstance,
101
116
  contract,
102
117
  mode,
@@ -147,7 +162,10 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
147
162
  spanId: SPAN_IDS.introspect,
148
163
  label: 'Introspecting database schema',
149
164
  });
150
- const schemaIR = await familyInstance.introspect({ driver });
165
+ const schemaIR = await familyInstance.introspect({
166
+ driver,
167
+ contract: collectAggregateNamespaces(aggregate),
168
+ });
151
169
  onProgress?.({ action, kind: 'spanEnd', spanId: SPAN_IDS.introspect, outcome: 'ok' });
152
170
 
153
171
  // 3. Plan via aggregate planner. App is forced through synth (today's
@@ -162,7 +180,7 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
162
180
  const planResult = await planMigration<TFamilyId, TTargetId>({
163
181
  aggregate,
164
182
  currentDBState: { markersBySpaceId: markerRows, schemaIntrospection: schemaIR },
165
- familyInstance,
183
+ adapter,
166
184
  migrations,
167
185
  frameworkComponents,
168
186
  callerPolicy: { ignoreGraphFor: new Set([aggregate.app.spaceId]) },
@@ -175,6 +193,7 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
175
193
  onProgress?.({ action, kind: 'spanEnd', spanId: SPAN_IDS.plan, outcome: 'ok' });
176
194
 
177
195
  const orderedResolutions = collectOrdered(planResult.value.applyOrder, planResult.value.perSpace);
196
+ const plannerWarnings = aggregatePlannerWarnings(orderedResolutions);
178
197
 
179
198
  // The destination's structural shape comes from the app's plan — its
180
199
  // `destination` is the storage hash users see in CLI output.
@@ -202,16 +221,17 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
202
221
  preview,
203
222
  perSpace,
204
223
  summary,
224
+ ...ifDefined('warnings', plannerWarnings),
205
225
  });
206
226
  }
207
227
 
208
- // 5. Apply mode: hand off to the shared `applyMigration` primitive.
228
+ // 5. Run mode: hand off to the shared `runMigration` primitive.
209
229
  // The runner-driving tail is identical for `db init` / `db update` /
210
230
  // `migrate` — only how each caller produces `perSpacePlans`
211
231
  // differs (synth + graph-walk via planMigration here; graph-walk
212
232
  // only for migrate). Each caller produces perSpacePlans differently;
213
- // this helper handles the shared apply tail.
214
- const applied = await applyMigration({
233
+ // this helper handles the shared run tail.
234
+ const applied = await runMigration({
215
235
  aggregate,
216
236
  perSpacePlans: planResult.value.perSpace,
217
237
  applyOrder: planResult.value.applyOrder,
@@ -228,6 +248,7 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
228
248
  summary: applied.failure.summary,
229
249
  ...ifDefined('why', applied.failure.why),
230
250
  meta: applied.failure.meta,
251
+ ...ifDefined('warnings', plannerWarnings),
231
252
  });
232
253
  }
233
254
 
@@ -246,9 +267,17 @@ export async function executeApply<TFamilyId extends string, TTargetId extends s
246
267
  operationsExecuted: applied.value.totalOpsExecuted,
247
268
  perSpace: applied.value.perSpace,
248
269
  summary,
270
+ ...ifDefined('warnings', plannerWarnings),
249
271
  });
250
272
  }
251
273
 
274
+ function aggregatePlannerWarnings(
275
+ orderedResolutions: readonly OrderedResolution[],
276
+ ): readonly MigrationPlannerConflict[] | undefined {
277
+ const warnings = orderedResolutions.flatMap((r) => r.entry.warnings ?? []);
278
+ return warnings.length > 0 ? warnings : undefined;
279
+ }
280
+
252
281
  /**
253
282
  * Compare the live `_prisma_marker` rows against the aggregate's
254
283
  * declared members. Any marker row whose `space` is not a member of
@@ -307,7 +336,10 @@ function mapPlannerError(error: PlannerError): DbInitResult | DbUpdateResult {
307
336
  why: undefined,
308
337
  meta: undefined,
309
338
  };
310
- return notOk(failure) as DbInitResult | DbUpdateResult;
339
+ return blindCast<
340
+ DbInitResult | DbUpdateResult,
341
+ 'notOk(failure) is shape-compatible with both DbInitResult and DbUpdateResult; the union is the return type of the surrounding function'
342
+ >(notOk(failure));
311
343
  }
312
344
  if (error.kind === 'extensionPathUnreachable') {
313
345
  return buildRunnerFailure({
@@ -339,6 +371,7 @@ function wrapPlanResult(args: {
339
371
  readonly preview: OperationPreview | undefined;
340
372
  readonly perSpace: readonly PerSpaceExecutionEntry[];
341
373
  readonly summary: string;
374
+ readonly warnings?: readonly MigrationPlannerConflict[];
342
375
  }): DbInitResult | DbUpdateResult {
343
376
  const success: DbInitSuccess | DbUpdateSuccess = {
344
377
  mode: 'plan',
@@ -352,6 +385,7 @@ function wrapPlanResult(args: {
352
385
  },
353
386
  perSpace: args.perSpace,
354
387
  summary: args.summary,
388
+ ...ifDefined('warnings', args.warnings),
355
389
  };
356
390
  return ok(success);
357
391
  }
@@ -363,6 +397,7 @@ function wrapApplyResult(args: {
363
397
  readonly operationsExecuted: number;
364
398
  readonly perSpace: readonly PerSpaceExecutionEntry[];
365
399
  readonly summary: string;
400
+ readonly warnings?: readonly MigrationPlannerConflict[];
366
401
  }): DbInitResult | DbUpdateResult {
367
402
  const success: DbInitSuccess | DbUpdateSuccess = {
368
403
  mode: 'apply',
@@ -380,6 +415,7 @@ function wrapApplyResult(args: {
380
415
  : { storageHash: args.destination.storageHash },
381
416
  perSpace: args.perSpace,
382
417
  summary: args.summary,
418
+ ...ifDefined('warnings', args.warnings),
383
419
  };
384
420
  return ok(success);
385
421
  }
@@ -388,6 +424,7 @@ function buildRunnerFailure(args: {
388
424
  readonly summary: string;
389
425
  readonly why?: string;
390
426
  readonly meta: Record<string, unknown>;
427
+ readonly warnings?: readonly MigrationPlannerConflict[];
391
428
  }): DbInitResult | DbUpdateResult {
392
429
  const failure: DbInitFailure | DbUpdateFailure = {
393
430
  code: 'RUNNER_FAILED',
@@ -395,6 +432,10 @@ function buildRunnerFailure(args: {
395
432
  why: args.why,
396
433
  meta: args.meta,
397
434
  conflicts: undefined,
435
+ ...ifDefined('warnings', args.warnings),
398
436
  };
399
- return notOk(failure) as DbInitResult | DbUpdateResult;
437
+ return blindCast<
438
+ DbInitResult | DbUpdateResult,
439
+ 'notOk(failure) is shape-compatible with both DbInitResult and DbUpdateResult; the union is the return type of the surrounding function'
440
+ >(notOk(failure));
400
441
  }