@rehearsal-db/core 0.1.0-beta.14 → 0.1.0-beta.16

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 (236) hide show
  1. package/CHANGELOG.md +51 -1
  2. package/README.md +1 -0
  3. package/dist/package.json +101 -0
  4. package/dist/scripts/install/create_root_config.d.mts +3 -0
  5. package/dist/scripts/install/create_root_config.mjs +13 -0
  6. package/dist/scripts/runtime/manage_database.d.mts +6 -0
  7. package/{scripts → dist/scripts}/runtime/manage_database.mjs +6 -8
  8. package/dist/src/application/session.d.mts +96 -0
  9. package/dist/src/application/session.mjs +380 -0
  10. package/dist/src/baseline/artifact.d.mts +119 -0
  11. package/dist/src/baseline/artifact.mjs +522 -0
  12. package/dist/src/baseline/builder.d.mts +19 -0
  13. package/dist/src/baseline/builder.mjs +142 -0
  14. package/dist/src/baseline/input_discovery.d.mts +13 -0
  15. package/dist/src/baseline/input_discovery.mjs +149 -0
  16. package/dist/src/baseline/policy_review.d.mts +56 -0
  17. package/dist/src/baseline/policy_review.mjs +73 -0
  18. package/dist/src/baseline/preparation.d.mts +63 -0
  19. package/dist/src/baseline/preparation.mjs +295 -0
  20. package/dist/src/baseline/privacy_contract.d.mts +147 -0
  21. package/dist/src/baseline/privacy_contract.mjs +2 -0
  22. package/dist/src/baseline/privacy_dependency_graph.d.mts +7 -0
  23. package/dist/src/baseline/privacy_dependency_graph.mjs +110 -0
  24. package/dist/src/baseline/privacy_engine.d.mts +5 -0
  25. package/dist/src/baseline/privacy_engine.mjs +4 -0
  26. package/dist/src/baseline/privacy_key.d.mts +7 -0
  27. package/dist/src/baseline/privacy_key.mjs +42 -0
  28. package/dist/src/baseline/privacy_pipeline.d.mts +7 -0
  29. package/dist/src/baseline/privacy_pipeline.mjs +127 -0
  30. package/dist/src/baseline/privacy_policy.d.mts +6 -0
  31. package/dist/src/baseline/privacy_policy.mjs +237 -0
  32. package/dist/src/baseline/privacy_recipe_validation.d.mts +5 -0
  33. package/dist/src/baseline/privacy_recipe_validation.mjs +289 -0
  34. package/dist/src/baseline/privacy_rule_registry.d.mts +18 -0
  35. package/dist/src/baseline/privacy_rule_registry.mjs +143 -0
  36. package/dist/src/baseline/privacy_structured_json.d.mts +66 -0
  37. package/dist/src/baseline/privacy_structured_json.mjs +464 -0
  38. package/dist/src/baseline/privacy_transforms.d.mts +18 -0
  39. package/dist/src/baseline/privacy_transforms.mjs +142 -0
  40. package/dist/src/baseline/privacy_validation_shared.d.mts +31 -0
  41. package/dist/src/baseline/privacy_validation_shared.mjs +127 -0
  42. package/dist/src/baseline/sanitization_policy.d.mts +69 -0
  43. package/dist/src/baseline/sanitization_policy.mjs +227 -0
  44. package/dist/src/baseline/schema_snapshot.d.mts +19 -0
  45. package/dist/src/baseline/schema_snapshot.mjs +79 -0
  46. package/dist/src/cli/application_command_handlers.d.mts +8 -0
  47. package/dist/src/cli/application_command_handlers.mjs +52 -0
  48. package/dist/src/cli/arguments.d.mts +38 -0
  49. package/dist/src/cli/arguments.mjs +128 -0
  50. package/dist/src/cli/basic_command_handlers.d.mts +4 -0
  51. package/dist/src/cli/basic_command_handlers.mjs +56 -0
  52. package/dist/src/cli/cli_commands.d.mts +14 -0
  53. package/dist/src/cli/cli_commands.mjs +32 -0
  54. package/dist/src/cli/command_contract.d.mts +57 -0
  55. package/dist/src/cli/command_contract.mjs +39 -0
  56. package/dist/src/cli/command_output.d.mts +12 -0
  57. package/dist/src/cli/command_output.mjs +20 -0
  58. package/dist/src/cli/command_registry.d.mts +7 -0
  59. package/dist/src/cli/command_registry.mjs +14 -0
  60. package/dist/src/cli/guided.d.mts +53 -0
  61. package/dist/src/cli/guided.mjs +719 -0
  62. package/dist/src/cli/identity_command_handlers.d.mts +8 -0
  63. package/dist/src/cli/identity_command_handlers.mjs +43 -0
  64. package/dist/src/cli/init_command.d.mts +21 -0
  65. package/dist/src/cli/init_command.mjs +90 -0
  66. package/dist/src/cli/inspection_command_handlers.d.mts +3 -0
  67. package/dist/src/cli/inspection_command_handlers.mjs +79 -0
  68. package/dist/src/cli/project_command_handlers.d.mts +11 -0
  69. package/dist/src/cli/project_command_handlers.mjs +92 -0
  70. package/dist/src/cli/project_commands.d.mts +159 -0
  71. package/dist/src/cli/project_commands.mjs +183 -0
  72. package/dist/src/cli/rehearsal.d.mts +7 -0
  73. package/dist/src/cli/rehearsal.mjs +190 -0
  74. package/dist/src/cli/renderers.d.mts +299 -0
  75. package/dist/src/cli/renderers.mjs +554 -0
  76. package/dist/src/cli/runtime_action_handlers.d.mts +12 -0
  77. package/dist/src/cli/runtime_action_handlers.mjs +201 -0
  78. package/dist/src/cli/runtime_commands.d.mts +269 -0
  79. package/dist/src/cli/runtime_commands.mjs +601 -0
  80. package/dist/src/cli/source_command_handlers.d.mts +8 -0
  81. package/dist/src/cli/source_command_handlers.mjs +62 -0
  82. package/dist/src/cli/source_commands.d.mts +436 -0
  83. package/dist/src/cli/source_commands.mjs +301 -0
  84. package/dist/src/cli/terminal.d.mts +59 -0
  85. package/dist/src/cli/terminal.mjs +230 -0
  86. package/dist/src/identity/claim.d.mts +5 -0
  87. package/dist/src/identity/claim.mjs +4 -0
  88. package/dist/src/identity/claim_contract.d.mts +135 -0
  89. package/dist/src/identity/claim_contract.mjs +6 -0
  90. package/dist/src/identity/claim_executor.d.mts +10 -0
  91. package/dist/src/identity/claim_executor.mjs +358 -0
  92. package/dist/src/identity/claim_identity_lookup.d.mts +17 -0
  93. package/dist/src/identity/claim_identity_lookup.mjs +125 -0
  94. package/dist/src/identity/claim_plan.d.mts +8 -0
  95. package/dist/src/identity/claim_plan.mjs +61 -0
  96. package/dist/src/identity/claim_policy.d.mts +3 -0
  97. package/dist/src/identity/claim_policy.mjs +377 -0
  98. package/dist/src/identity/claim_sql.d.mts +10 -0
  99. package/dist/src/identity/claim_sql.mjs +46 -0
  100. package/dist/src/identity/claim_verification.d.mts +13 -0
  101. package/dist/src/identity/claim_verification.mjs +35 -0
  102. package/dist/src/identity/provider_configuration.d.mts +37 -0
  103. package/dist/src/identity/provider_configuration.mjs +132 -0
  104. package/dist/src/identity/storage.d.mts +21 -0
  105. package/dist/src/identity/storage.mjs +122 -0
  106. package/dist/src/index.d.mts +4 -0
  107. package/dist/src/index.mjs +3 -0
  108. package/dist/src/project/configuration.d.mts +13 -0
  109. package/dist/src/project/configuration.mjs +9 -0
  110. package/dist/src/project/configuration_contract.d.mts +125 -0
  111. package/dist/src/project/configuration_contract.mjs +27 -0
  112. package/dist/src/project/configuration_discovery.d.mts +7 -0
  113. package/dist/src/project/configuration_discovery.mjs +37 -0
  114. package/dist/src/project/configuration_normalization.d.mts +107 -0
  115. package/dist/src/project/configuration_normalization.mjs +302 -0
  116. package/dist/src/project/configuration_rendering.d.mts +14 -0
  117. package/dist/src/project/configuration_rendering.mjs +224 -0
  118. package/dist/src/project/configuration_resolution.d.mts +125 -0
  119. package/dist/src/project/configuration_resolution.mjs +81 -0
  120. package/dist/src/project/configuration_schema.d.mts +22 -0
  121. package/dist/src/project/configuration_schema.mjs +160 -0
  122. package/dist/src/project/configuration_validation.d.mts +22 -0
  123. package/dist/src/project/configuration_validation.mjs +136 -0
  124. package/dist/src/project/install_scaffold.d.mts +42 -0
  125. package/dist/src/project/install_scaffold.mjs +263 -0
  126. package/dist/src/project/project_detection.d.mts +14 -0
  127. package/dist/src/project/project_detection.mjs +47 -0
  128. package/dist/src/project/setup.d.mts +87 -0
  129. package/dist/src/project/setup.mjs +414 -0
  130. package/dist/src/project/support_report.d.mts +88 -0
  131. package/dist/src/project/support_report.mjs +83 -0
  132. package/dist/src/runtime/cleanup.d.mts +85 -0
  133. package/dist/src/runtime/cleanup.mjs +311 -0
  134. package/dist/src/runtime/lifecycle_engine.d.mts +63 -0
  135. package/dist/src/runtime/lifecycle_engine.mjs +114 -0
  136. package/dist/src/runtime/migration_history.d.mts +51 -0
  137. package/dist/src/runtime/migration_history.mjs +170 -0
  138. package/dist/src/runtime/plan.d.mts +134 -0
  139. package/dist/src/runtime/plan.mjs +637 -0
  140. package/dist/src/runtime/policy.d.mts +66 -0
  141. package/dist/src/runtime/policy.mjs +369 -0
  142. package/dist/src/runtime/restore.d.mts +22 -0
  143. package/dist/src/runtime/restore.mjs +276 -0
  144. package/dist/src/runtime/service_environment.d.mts +11 -0
  145. package/dist/src/runtime/service_environment.mjs +58 -0
  146. package/dist/src/runtime/topology.d.mts +51 -0
  147. package/dist/src/runtime/topology.mjs +128 -0
  148. package/dist/src/shared/diagnostics.d.mts +97 -0
  149. package/dist/src/shared/diagnostics.mjs +170 -0
  150. package/dist/src/shared/human_output.d.mts +2 -0
  151. package/{src → dist/src}/shared/human_output.mjs +1 -3
  152. package/dist/src/shared/node_runtime.d.mts +14 -0
  153. package/dist/src/shared/node_runtime.mjs +25 -0
  154. package/dist/src/shared/operation_guard.d.mts +22 -0
  155. package/dist/src/shared/operation_guard.mjs +150 -0
  156. package/dist/src/shared/process_environment.d.mts +15 -0
  157. package/dist/src/shared/process_environment.mjs +50 -0
  158. package/dist/src/source/access.d.mts +165 -0
  159. package/dist/src/source/access.mjs +602 -0
  160. package/dist/src/source/asset_transfer.d.mts +27 -0
  161. package/dist/src/source/asset_transfer.mjs +202 -0
  162. package/dist/src/source/baseline.d.mts +66 -0
  163. package/dist/src/source/baseline.mjs +372 -0
  164. package/dist/src/source/external_reader.d.mts +42 -0
  165. package/dist/src/source/external_reader.mjs +278 -0
  166. package/dist/src/source/postgresql_access.d.mts +86 -0
  167. package/dist/src/source/postgresql_access.mjs +454 -0
  168. package/dist/src/source/privacy_paths.d.mts +10 -0
  169. package/dist/src/source/privacy_paths.mjs +27 -0
  170. package/dist/src/targets/driver.d.mts +2 -0
  171. package/dist/src/targets/driver.mjs +13 -0
  172. package/dist/src/targets/postgresql.d.mts +5 -0
  173. package/dist/src/targets/postgresql.mjs +620 -0
  174. package/dist/src/targets/supabase.d.mts +8 -0
  175. package/dist/src/targets/supabase.mjs +627 -0
  176. package/dist/src/targets/supabase_environment.d.mts +49 -0
  177. package/dist/src/targets/supabase_environment.mjs +162 -0
  178. package/dist/src/targets/target.d.mts +33 -0
  179. package/dist/src/targets/target.mjs +39 -0
  180. package/docs/README.md +1 -0
  181. package/docs/architecture.md +133 -10
  182. package/docs/configuration.md +9 -3
  183. package/docs/index.md +1 -0
  184. package/docs/releasing.md +8 -3
  185. package/docs/typescript.md +134 -0
  186. package/package.json +36 -29
  187. package/types/configuration.d.mts +5 -0
  188. package/scripts/install/create_root_config.mjs +0 -19
  189. package/src/README.md +0 -17
  190. package/src/application/session.mjs +0 -435
  191. package/src/baseline/artifact.mjs +0 -684
  192. package/src/baseline/builder.mjs +0 -164
  193. package/src/baseline/input_discovery.mjs +0 -155
  194. package/src/baseline/policy_review.mjs +0 -92
  195. package/src/baseline/preparation.mjs +0 -323
  196. package/src/baseline/privacy_engine.mjs +0 -1399
  197. package/src/baseline/privacy_structured_json.mjs +0 -554
  198. package/src/baseline/sanitization_policy.mjs +0 -299
  199. package/src/baseline/schema_snapshot.mjs +0 -113
  200. package/src/cli/arguments.mjs +0 -126
  201. package/src/cli/guided.mjs +0 -814
  202. package/src/cli/init_command.mjs +0 -94
  203. package/src/cli/rehearsal.mjs +0 -972
  204. package/src/cli/renderers.mjs +0 -638
  205. package/src/cli/runtime_commands.mjs +0 -707
  206. package/src/cli/source_commands.mjs +0 -355
  207. package/src/cli/terminal.mjs +0 -275
  208. package/src/identity/claim.mjs +0 -1165
  209. package/src/identity/provider_configuration.mjs +0 -177
  210. package/src/identity/storage.mjs +0 -165
  211. package/src/project/configuration.d.mts +0 -164
  212. package/src/project/configuration.mjs +0 -1337
  213. package/src/project/install_scaffold.mjs +0 -325
  214. package/src/project/setup.mjs +0 -474
  215. package/src/project/support_report.mjs +0 -105
  216. package/src/runtime/cleanup.mjs +0 -381
  217. package/src/runtime/migration_history.mjs +0 -220
  218. package/src/runtime/plan.mjs +0 -722
  219. package/src/runtime/policy.mjs +0 -438
  220. package/src/runtime/restore.mjs +0 -337
  221. package/src/runtime/service_environment.mjs +0 -82
  222. package/src/runtime/topology.mjs +0 -177
  223. package/src/shared/diagnostics.mjs +0 -207
  224. package/src/shared/node_runtime.mjs +0 -31
  225. package/src/shared/operation_guard.mjs +0 -162
  226. package/src/shared/process_environment.mjs +0 -68
  227. package/src/source/access.mjs +0 -798
  228. package/src/source/asset_transfer.mjs +0 -259
  229. package/src/source/baseline.mjs +0 -482
  230. package/src/source/external_reader.mjs +0 -368
  231. package/src/source/postgresql_access.mjs +0 -638
  232. package/src/source/privacy_paths.mjs +0 -38
  233. package/src/targets/postgresql.mjs +0 -808
  234. package/src/targets/supabase.mjs +0 -840
  235. package/src/targets/supabase_environment.mjs +0 -229
  236. package/src/targets/target.mjs +0 -66
@@ -0,0 +1,183 @@
1
+ /** Project-scoped CLI orchestration that is shared by direct and guided commands. */
2
+ import { createHash } from "node:crypto";
3
+ import { readFile } from "node:fs/promises";
4
+ import { applyBaselinePreparation, planBaselinePreparation, summarizeBaselinePreparation, } from "../baseline/preparation.mjs";
5
+ import { createIdentityClaimPlan, validateIdentityPolicy, } from "../identity/claim.mjs";
6
+ import { loadRehearsalConfig } from "../project/configuration.mjs";
7
+ import { applyRehearsalSetup, planRehearsalSetup, summarizeRehearsalSetup, } from "../project/setup.mjs";
8
+ import { applyRehearsalCleanup, planRehearsalCleanup, } from "../runtime/cleanup.mjs";
9
+ import { runRehearsalDoctor } from "../runtime/plan.mjs";
10
+ import { loadRuntimeTopology } from "../runtime/topology.mjs";
11
+ const isCleanupStackPlan = (plan) => "targets" in plan;
12
+ export const createProjectCommands = ({ projectRoot, runManager, }) => {
13
+ const runSetup = async ({ flags, planOptions, session, }) => {
14
+ const setupTarget = flags.target === "supabase" || flags.target === "postgresql"
15
+ ? flags.target
16
+ : undefined;
17
+ if (flags.target !== undefined && setupTarget === undefined) {
18
+ throw new Error("--target must be supabase or postgresql.");
19
+ }
20
+ const plan = session?.setupPlan ??
21
+ (await planRehearsalSetup({
22
+ projectRoot,
23
+ ...(planOptions.configPath === undefined
24
+ ? {}
25
+ : { configPath: planOptions.configPath }),
26
+ ...(setupTarget === undefined ? {} : { target: setupTarget }),
27
+ }));
28
+ if (flags.write)
29
+ await applyRehearsalSetup(plan);
30
+ const result = summarizeRehearsalSetup(plan, {
31
+ mode: flags.write ? "written" : "preview",
32
+ });
33
+ if (!flags.write)
34
+ return result;
35
+ const readiness = await runRehearsalDoctor(planOptions);
36
+ const needsBaselinePolicy = readiness.checks.some((check) => check.id === "sanitization-policy" && check.status === "fail");
37
+ return {
38
+ ...result,
39
+ readiness,
40
+ nextAction: [
41
+ `Review ${result.configurationPath}.`,
42
+ readiness.state === "READY"
43
+ ? "Next: run rehearsal explain to review the migration plan."
44
+ : needsBaselinePolicy
45
+ ? flags.guided
46
+ ? "Next: choose Prepare the script to create a reviewable policy draft."
47
+ : "Next: run rehearsal again and choose Prepare a reviewable baseline policy draft."
48
+ : "Next: complete the remaining readiness items shown above.",
49
+ ].join("\n"),
50
+ };
51
+ };
52
+ const runBaselinePreparation = async ({ flags, planOptions, session, }) => {
53
+ if (!flags.recordsPath || !flags.ledgerPath) {
54
+ throw new Error("baseline prepare requires --records and --ledger input paths.");
55
+ }
56
+ const plan = session?.preparationPlan ??
57
+ (await planBaselinePreparation({
58
+ ...planOptions,
59
+ recordsPath: flags.recordsPath,
60
+ ledgerPath: flags.ledgerPath,
61
+ }));
62
+ if (flags.write)
63
+ await applyBaselinePreparation(plan);
64
+ const result = summarizeBaselinePreparation(plan, {
65
+ mode: flags.write ? "written" : "preview",
66
+ });
67
+ return flags.guided && flags.write
68
+ ? {
69
+ ...result,
70
+ nextAction: "Next: choose Review the script to classify every column interactively.",
71
+ }
72
+ : result;
73
+ };
74
+ const planRuntimeStackCleanup = async ({ flags, planOptions, }) => {
75
+ const topology = await loadRuntimeTopology(planOptions);
76
+ const targets = [];
77
+ for (const target of topology.targets) {
78
+ targets.push({
79
+ name: target.name,
80
+ configPath: target.configPath,
81
+ plan: await planRehearsalCleanup({
82
+ projectRoot: topology.projectRoot,
83
+ configPath: target.configPath,
84
+ includeRuntime: flags.includeRuntime,
85
+ includeImages: flags.includeImages && target.primary,
86
+ }),
87
+ });
88
+ }
89
+ if (targets.length === 1)
90
+ return targets[0].plan;
91
+ return {
92
+ digest: createHash("sha256")
93
+ .update(JSON.stringify(targets.map((target) => ({
94
+ name: target.name,
95
+ digest: target.plan.digest,
96
+ }))))
97
+ .digest("hex"),
98
+ targets,
99
+ };
100
+ };
101
+ const runCleanup = async ({ flags, planOptions, session, }) => {
102
+ const plan = session?.cleanupPlan ??
103
+ (await planRuntimeStackCleanup({ flags, planOptions }));
104
+ if (!flags.write)
105
+ return { mode: "preview", plan };
106
+ if (isCleanupStackPlan(plan)) {
107
+ if (flags.cleanupConfirmation !== plan.digest) {
108
+ throw new Error("Cleanup requires the exact --confirm-cleanup digest from a fresh preview. Nothing was removed.");
109
+ }
110
+ for (const target of plan.targets) {
111
+ const refreshed = await planRehearsalCleanup({
112
+ projectRoot,
113
+ configPath: target.configPath,
114
+ includeRuntime: target.plan.runtime.included,
115
+ includeImages: target.plan.images.requested,
116
+ });
117
+ if (refreshed.digest !== target.plan.digest) {
118
+ throw new Error(`Cleanup targets changed for ${target.name} after the preview. Nothing was removed; preview the cleanup again.`);
119
+ }
120
+ }
121
+ const appliedTargets = [];
122
+ for (const target of plan.targets) {
123
+ appliedTargets.push({
124
+ name: target.name,
125
+ applied: await applyRehearsalCleanup({
126
+ plan: target.plan,
127
+ confirmation: target.plan.digest,
128
+ projectRoot,
129
+ configPath: target.configPath,
130
+ removeRuntime: async () => {
131
+ runManager({
132
+ action: "discard",
133
+ flags,
134
+ configPath: target.configPath,
135
+ targetName: target.name,
136
+ });
137
+ },
138
+ }),
139
+ });
140
+ }
141
+ return {
142
+ mode: "written",
143
+ plan,
144
+ applied: { targets: appliedTargets },
145
+ };
146
+ }
147
+ const applied = await applyRehearsalCleanup({
148
+ plan,
149
+ ...(flags.cleanupConfirmation === undefined
150
+ ? {}
151
+ : { confirmation: flags.cleanupConfirmation }),
152
+ ...planOptions,
153
+ removeRuntime: async () => {
154
+ runManager({ action: "discard", flags });
155
+ },
156
+ });
157
+ return { mode: "written", plan, applied };
158
+ };
159
+ const loadIdentityClaimPlan = async ({ flags, planOptions, }) => {
160
+ const loaded = await loadRehearsalConfig(planOptions);
161
+ if (!loaded.paths.identityPolicy) {
162
+ throw new Error("No config.identityPolicy is configured.");
163
+ }
164
+ if (!flags.identityName) {
165
+ throw new Error("Choose a declared identity with --identity=<name>.");
166
+ }
167
+ const policy = validateIdentityPolicy(JSON.parse(await readFile(loaded.paths.identityPolicy, "utf8")));
168
+ return {
169
+ loaded,
170
+ plan: createIdentityClaimPlan({
171
+ policy,
172
+ name: flags.identityName,
173
+ }),
174
+ };
175
+ };
176
+ return Object.freeze({
177
+ loadIdentityClaimPlan,
178
+ planRuntimeStackCleanup,
179
+ runBaselinePreparation,
180
+ runCleanup,
181
+ runSetup,
182
+ });
183
+ };
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Purpose: Parse one CLI request, protect project mutations, and delegate to
4
+ * the typed command registry shared by scripted and guided flows.
5
+ * Run: `npm run rehearsal -- doctor` or `npm run rehearsal -- explain`.
6
+ */
7
+ export {};
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Purpose: Parse one CLI request, protect project mutations, and delegate to
4
+ * the typed command registry shared by scripted and guided flows.
5
+ * Run: `npm run rehearsal -- doctor` or `npm run rehearsal -- explain`.
6
+ */
7
+ import { join } from "node:path";
8
+ import { performance } from "node:perf_hooks";
9
+ import { fileURLToPath } from "node:url";
10
+ import * as prompts from "@clack/prompts";
11
+ import { normalizeRehearsalError, renderHumanError, serializeRehearsalError, } from "../shared/diagnostics.mjs";
12
+ import { acquireProjectOperation, assertInstalledPackageFingerprint, createInstalledPackageFingerprint, } from "../shared/operation_guard.mjs";
13
+ import { commandMutatesProject, parseArguments, resetGuidedFlags, } from "./arguments.mjs";
14
+ import { usage } from "./basic_command_handlers.mjs";
15
+ import { createCliCommandRegistry } from "./cli_commands.mjs";
16
+ import { createCliSessionState, resetCommandSessionState, } from "./command_contract.mjs";
17
+ import { createCommandEmitter } from "./command_output.mjs";
18
+ import { createGuidedHome } from "./guided.mjs";
19
+ import { createProjectCommands } from "./project_commands.mjs";
20
+ import { buildRuntimeStackPlan, createRuntimeCommands, runRuntimeStackDoctor, topologyCandidateSummary, topologyCommandEnvironment, } from "./runtime_commands.mjs";
21
+ import { buildRefreshWorkflowPlan, createRefreshWorkflow, } from "./source_commands.mjs";
22
+ import { installGuidedExitShortcut, useStyledPrompts } from "./terminal.mjs";
23
+ const packageRoot = fileURLToPath(new URL("../../..", import.meta.url));
24
+ const projectRoot = process.cwd();
25
+ const managerPath = join(packageRoot, "dist/scripts/runtime/manage_database.mjs");
26
+ const state = createCliSessionState();
27
+ let activePackageFingerprint = null;
28
+ const runtimeCommands = createRuntimeCommands({
29
+ packageRoot,
30
+ projectRoot,
31
+ managerPath,
32
+ getActivePackageFingerprint: () => activePackageFingerprint ?? undefined,
33
+ });
34
+ const projectCommands = createProjectCommands({
35
+ projectRoot,
36
+ runManager: runtimeCommands.runManager,
37
+ });
38
+ const runRefreshWorkflow = createRefreshWorkflow({
39
+ runRuntimeStack: runtimeCommands.runRuntimeStack,
40
+ prepareDependentTargets: runtimeCommands.prepareDependentTargets,
41
+ topologyCommandEnvironment,
42
+ });
43
+ const commandRegistry = createCliCommandRegistry({
44
+ projectRoot,
45
+ projectCommands,
46
+ runtimeCommands,
47
+ runRefreshWorkflow,
48
+ });
49
+ const emitCommand = createCommandEmitter({
50
+ startedAt: new Date(),
51
+ startedMs: performance.now(),
52
+ });
53
+ const runGuidedHome = createGuidedHome({
54
+ projectRoot,
55
+ topologyCandidateSummary,
56
+ buildRefreshWorkflowPlan,
57
+ planRuntimeStackCleanup: projectCommands.planRuntimeStackCleanup,
58
+ getLastGuidedDetails: () => state.lastGuidedDetails,
59
+ session: state,
60
+ });
61
+ const errorMessage = (error) => error instanceof Error ? error.message : String(error);
62
+ const executeCommand = async ({ command, flags, planOptions, guided, }) => {
63
+ const result = await commandRegistry.execute({
64
+ command,
65
+ flags,
66
+ planOptions,
67
+ guided,
68
+ state,
69
+ });
70
+ if (!result) {
71
+ throw new Error(`Unknown Rehearsal command: ${command}.\n\n${usage()}`);
72
+ }
73
+ const exitCode = emitCommand({ command, flags, result });
74
+ if (exitCode !== undefined)
75
+ process.exitCode = exitCode;
76
+ };
77
+ const main = async () => {
78
+ const { flags, positionals } = parseArguments(process.argv.slice(2));
79
+ const command = positionals.join(" ") || "help";
80
+ const planOptions = {
81
+ projectRoot,
82
+ ...(flags.configPath === undefined ? {} : { configPath: flags.configPath }),
83
+ };
84
+ const wantsAutomaticGuide = positionals.length === 0 &&
85
+ !flags.help &&
86
+ !flags.json &&
87
+ process.stdin.isTTY &&
88
+ process.stdout.isTTY;
89
+ const guided = command === "guide" || wantsAutomaticGuide;
90
+ const mutating = commandMutatesProject({ command, flags, guided });
91
+ const operation = mutating
92
+ ? await acquireProjectOperation({ projectRoot, command })
93
+ : null;
94
+ if (operation) {
95
+ activePackageFingerprint = createInstalledPackageFingerprint({
96
+ packageRoot,
97
+ });
98
+ }
99
+ try {
100
+ if (!guided) {
101
+ await executeCommand({ command, flags, planOptions, guided: false });
102
+ return;
103
+ }
104
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
105
+ throw new Error("The guided home screen requires an interactive terminal. Run rehearsal --help to list scriptable commands.");
106
+ }
107
+ flags.guided = true;
108
+ const installExitShortcut = () => installGuidedExitShortcut(flags, {
109
+ beforeExit: () => operation?.releaseSync(),
110
+ });
111
+ let removeGuidedExitShortcut = installExitShortcut();
112
+ if (useStyledPrompts(flags)) {
113
+ prompts.intro("REHEARSAL · Safe local migration testing");
114
+ }
115
+ try {
116
+ while (true) {
117
+ resetGuidedFlags(flags);
118
+ resetCommandSessionState(state);
119
+ const selected = await runGuidedHome({ flags, planOptions });
120
+ if (!selected)
121
+ break;
122
+ if (selected === "home")
123
+ continue;
124
+ const opensApplication = selected === "open";
125
+ if (opensApplication) {
126
+ removeGuidedExitShortcut?.();
127
+ removeGuidedExitShortcut = null;
128
+ }
129
+ try {
130
+ await executeCommand({
131
+ command: selected,
132
+ flags,
133
+ planOptions,
134
+ guided: true,
135
+ });
136
+ }
137
+ finally {
138
+ if (opensApplication) {
139
+ removeGuidedExitShortcut = installExitShortcut();
140
+ }
141
+ }
142
+ if (flags.exitGuidedSession)
143
+ break;
144
+ }
145
+ }
146
+ finally {
147
+ removeGuidedExitShortcut?.();
148
+ }
149
+ if (useStyledPrompts(flags)) {
150
+ prompts.outro("See you at the next rehearsal.");
151
+ }
152
+ else {
153
+ console.log("See you at the next rehearsal.");
154
+ }
155
+ }
156
+ finally {
157
+ if (operation) {
158
+ try {
159
+ assertInstalledPackageFingerprint({
160
+ packageRoot,
161
+ expected: activePackageFingerprint,
162
+ });
163
+ }
164
+ finally {
165
+ activePackageFingerprint = null;
166
+ await operation.release();
167
+ }
168
+ }
169
+ }
170
+ };
171
+ try {
172
+ await main();
173
+ }
174
+ catch (error) {
175
+ const failure = normalizeRehearsalError(error, {
176
+ expected: "a safe, versioned, local-only Rehearsal operation",
177
+ actual: errorMessage(error),
178
+ refused: "No further Rehearsal action was performed.",
179
+ suggestions: ["Run rehearsal doctor for actionable readiness checks."],
180
+ });
181
+ const wantsJson = process.argv.includes("--json");
182
+ const debug = process.argv.includes("--debug");
183
+ if (wantsJson) {
184
+ console.log(JSON.stringify(serializeRehearsalError(failure, { debug }), null, 2));
185
+ }
186
+ else {
187
+ console.error(renderHumanError(failure, { debug }));
188
+ }
189
+ process.exitCode = failure.exitCode;
190
+ }
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Human-readable CLI output. These functions are intentionally pure so the
3
+ * command and guided workflow modules can focus on orchestration.
4
+ */
5
+ import type { SourceAccessPlan } from "../source/access.mjs";
6
+ import type { IdentityClaimPlan } from "../identity/claim.mjs";
7
+ type Verbosity = "normal" | "verbose";
8
+ type RenderFlags = Readonly<{
9
+ plain?: boolean;
10
+ json?: boolean;
11
+ }>;
12
+ type RenderMode = "preview" | "written";
13
+ interface RenderMigration {
14
+ readonly filename: string;
15
+ readonly sha256: string;
16
+ readonly status?: string;
17
+ }
18
+ interface RenderEnvironment {
19
+ readonly kind: string;
20
+ readonly hostedAccess: string;
21
+ readonly outboundNetwork: string;
22
+ readonly authenticationProviders: readonly string[];
23
+ readonly authenticationCallback: string | null;
24
+ readonly barriers: readonly string[];
25
+ }
26
+ interface RenderBaselineSummary {
27
+ readonly generationId: string;
28
+ readonly tableCount: number;
29
+ readonly rowCount: number;
30
+ readonly verification: string;
31
+ }
32
+ interface RenderMigrationSummary {
33
+ readonly candidates: readonly RenderMigration[];
34
+ readonly candidateSha256: string;
35
+ readonly candidateCount: number;
36
+ readonly representedCount: number;
37
+ }
38
+ interface RenderSinglePlan {
39
+ readonly environment: RenderEnvironment;
40
+ readonly baseline: RenderBaselineSummary;
41
+ readonly migrations: RenderMigrationSummary;
42
+ readonly execution: readonly string[];
43
+ readonly guarantee: string;
44
+ }
45
+ interface RenderStackTarget {
46
+ readonly name: string;
47
+ readonly plan: {
48
+ readonly config: {
49
+ readonly project: string;
50
+ };
51
+ readonly environment: RenderEnvironment;
52
+ readonly baseline: RenderBaselineSummary;
53
+ readonly migrations: Pick<RenderMigrationSummary, "candidates">;
54
+ };
55
+ }
56
+ interface RenderStackPlan {
57
+ readonly targets: readonly RenderStackTarget[];
58
+ readonly migrations: Pick<RenderMigrationSummary, "candidateSha256" | "candidateCount">;
59
+ readonly execution: readonly string[];
60
+ readonly guarantee: string;
61
+ }
62
+ interface ReadinessCheck {
63
+ readonly id: string;
64
+ readonly label: string;
65
+ readonly status: "pass" | "fail";
66
+ readonly detail: string;
67
+ readonly remediation?: string;
68
+ }
69
+ interface ReadinessReport {
70
+ readonly state: string;
71
+ readonly checks: readonly ReadinessCheck[];
72
+ readonly ambientHostedVariables?: {
73
+ readonly presentButQuarantined: readonly string[];
74
+ };
75
+ }
76
+ interface RenderTable {
77
+ readonly name: string;
78
+ readonly rowCount: number;
79
+ readonly columns: readonly string[];
80
+ }
81
+ interface CandidateSummary {
82
+ readonly baselineGenerationId: string;
83
+ readonly candidates: readonly RenderMigration[];
84
+ readonly candidateSha256: string;
85
+ readonly candidateCount: number;
86
+ readonly targets?: readonly {
87
+ readonly name: string;
88
+ readonly baselineGenerationId: string;
89
+ readonly candidates: readonly RenderMigration[];
90
+ }[];
91
+ }
92
+ interface CleanupImage {
93
+ readonly bytes: number;
94
+ readonly tags: readonly string[];
95
+ }
96
+ interface CleanupTargetPlan {
97
+ readonly project: string;
98
+ readonly projectId: string;
99
+ readonly baselines: {
100
+ readonly available: boolean;
101
+ readonly removed: readonly string[];
102
+ readonly retained: readonly string[];
103
+ };
104
+ readonly runtime: {
105
+ readonly included: boolean;
106
+ readonly detected: boolean;
107
+ };
108
+ readonly images: {
109
+ readonly requested: boolean;
110
+ readonly inspected: boolean;
111
+ readonly candidates: readonly CleanupImage[];
112
+ };
113
+ }
114
+ interface AppliedCleanup {
115
+ readonly baselines: {
116
+ readonly removed: readonly string[];
117
+ };
118
+ readonly runtimeRemoved: boolean;
119
+ readonly imagesRemoved: readonly string[];
120
+ }
121
+ interface CleanupResult {
122
+ readonly mode: RenderMode;
123
+ readonly plan: (CleanupTargetPlan & {
124
+ readonly digest: string;
125
+ readonly docker: {
126
+ readonly resources: readonly unknown[];
127
+ readonly disk: {
128
+ readonly usedBytes: number;
129
+ readonly totalBytes: number;
130
+ readonly availableBytes: number;
131
+ readonly usedPercent: number;
132
+ } | null;
133
+ };
134
+ }) | {
135
+ readonly targets: readonly {
136
+ readonly name: string;
137
+ readonly plan: CleanupTargetPlan;
138
+ }[];
139
+ readonly digest: string;
140
+ };
141
+ readonly applied?: AppliedCleanup | {
142
+ readonly targets: readonly {
143
+ readonly name: string;
144
+ readonly applied: AppliedCleanup;
145
+ }[];
146
+ };
147
+ }
148
+ interface RefreshPlan {
149
+ readonly digest: string;
150
+ readonly review: {
151
+ readonly currentBaseline: string;
152
+ readonly source: {
153
+ readonly readerExpiresAt: string;
154
+ };
155
+ readonly runtimeTargets: readonly string[];
156
+ readonly retention: {
157
+ readonly generations: number;
158
+ readonly removeAfterReplacement: readonly string[];
159
+ };
160
+ readonly steps: readonly string[];
161
+ };
162
+ }
163
+ interface SourceRetirementPlan {
164
+ readonly digest: string;
165
+ readonly review: {
166
+ readonly targetFingerprint: string;
167
+ readonly readerRole: string;
168
+ readonly exportSchema: string;
169
+ readonly accessMode: "managed" | "external";
170
+ readonly views: readonly string[];
171
+ };
172
+ }
173
+ export declare const renderPlan: (plan: RenderSinglePlan | RenderStackPlan, verbosity: Verbosity) => string;
174
+ export declare const renderDoctor: (doctor: ReadinessReport, verbosity: Verbosity) => string;
175
+ export declare const renderBaseline: (baseline: {
176
+ rehearsalVersion: string;
177
+ generationId: string;
178
+ createdAt?: string | null;
179
+ formatVersion: number;
180
+ tableCount: number;
181
+ rowCount: number;
182
+ migrationCount: number;
183
+ migrationCutoff: string;
184
+ dataSha256: string;
185
+ sanitizationPolicySha256: string;
186
+ verification: string;
187
+ }) => string;
188
+ export declare const renderMigrations: (inspection: {
189
+ baselineGenerationId: string;
190
+ baselineCutoff: string;
191
+ candidateSha256: string;
192
+ migrations: readonly (RenderMigration & {
193
+ status: string;
194
+ })[];
195
+ }, verbosity: Verbosity) => string;
196
+ export declare const renderCandidates: (summary: CandidateSummary, verbosity: Verbosity) => string;
197
+ export declare const renderInit: (result: {
198
+ mode: RenderMode;
199
+ destination: string;
200
+ detected: {
201
+ packageManager: string;
202
+ hasSupabaseConfig: boolean;
203
+ hasMigrations: boolean;
204
+ };
205
+ availableOptions?: readonly {
206
+ path: string;
207
+ summary: string;
208
+ }[];
209
+ source?: string | null;
210
+ nextAction: string;
211
+ }) => string;
212
+ export declare const renderSetup: (result: {
213
+ mode: RenderMode;
214
+ project: string;
215
+ configurationPath: string;
216
+ configurationAction: "create" | "update" | "unchanged";
217
+ rootConfigGenerationSkipped?: boolean;
218
+ target: "postgresql" | "supabase";
219
+ projectId: string;
220
+ applicationUrl: string;
221
+ ports: Readonly<Record<string, number>> & {
222
+ readonly database: number;
223
+ };
224
+ files: readonly {
225
+ action: "create" | "update" | "unchanged";
226
+ path: string;
227
+ }[];
228
+ safety: readonly string[];
229
+ readiness?: ReadinessReport;
230
+ nextAction: string;
231
+ }) => string;
232
+ export declare const renderBaselinePreparation: (result: {
233
+ mode: RenderMode;
234
+ destination: string;
235
+ rowCount: number;
236
+ recordsPath: string;
237
+ migrationCount: number;
238
+ migrationCutoff: string;
239
+ tables: readonly RenderTable[];
240
+ nextAction: string;
241
+ }, flags?: RenderFlags) => string;
242
+ export declare const renderBaselineInputInspection: (result: {
243
+ rowCount: number;
244
+ recordsPath: string;
245
+ migrationCount: number;
246
+ migrationCutoff: string;
247
+ ledgerPath: string;
248
+ assetsPath?: string;
249
+ assetCount: number;
250
+ tables: readonly RenderTable[];
251
+ }, flags?: RenderFlags) => string;
252
+ export declare const renderSupportReport: (report: {
253
+ rehearsalVersion: string;
254
+ system: {
255
+ platform: string;
256
+ architecture: string;
257
+ release: string;
258
+ node: string;
259
+ };
260
+ tools: Record<"npm" | "supabase" | "dockerClient" | "dockerServer", {
261
+ status: string;
262
+ version?: string;
263
+ }>;
264
+ readiness: {
265
+ state: string;
266
+ checks: readonly Omit<ReadinessCheck, "detail">[];
267
+ quarantinedHostedVariableCount: number;
268
+ };
269
+ supportUrl: string;
270
+ }) => string;
271
+ export declare const formatBytes: (bytes: number) => string;
272
+ export declare const renderCleanup: (result: CleanupResult) => string;
273
+ export declare const renderSourceAccessPlan: (plan: SourceAccessPlan) => string;
274
+ export declare const renderRefreshWorkflow: ({ mode, plan, refreshed, runtime, cleanup, }: {
275
+ mode: RenderMode;
276
+ plan: RefreshPlan;
277
+ refreshed?: {
278
+ generationId: string;
279
+ };
280
+ runtime?: {
281
+ targets?: readonly unknown[];
282
+ };
283
+ cleanup?: {
284
+ state: "complete";
285
+ removed: readonly string[];
286
+ } | {
287
+ state: string;
288
+ remaining: number;
289
+ };
290
+ }) => string;
291
+ export declare const renderSourceRetirement: ({ mode, plan, }: {
292
+ mode: RenderMode;
293
+ plan: SourceRetirementPlan;
294
+ }) => string;
295
+ export declare const renderIdentityPlan: ({ mode, plan, }: {
296
+ mode: RenderMode;
297
+ plan: IdentityClaimPlan;
298
+ }) => string;
299
+ export {};