@rehearsal-db/core 0.1.0-beta.9 → 0.1.0-rc.2

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 (247) hide show
  1. package/CHANGELOG.md +319 -1
  2. package/COMPATIBILITY.md +15 -0
  3. package/README.md +73 -16
  4. package/dist/package.json +100 -0
  5. package/dist/scripts/install/create_root_config.d.mts +3 -0
  6. package/dist/scripts/install/create_root_config.mjs +13 -0
  7. package/dist/scripts/runtime/manage_database.d.mts +6 -0
  8. package/{scripts → dist/scripts}/runtime/manage_database.mjs +6 -8
  9. package/dist/src/application/interactive_task.d.mts +30 -0
  10. package/dist/src/application/interactive_task.mjs +83 -0
  11. package/dist/src/application/session.d.mts +97 -0
  12. package/dist/src/application/session.mjs +380 -0
  13. package/dist/src/baseline/artifact.d.mts +119 -0
  14. package/dist/src/baseline/artifact.mjs +522 -0
  15. package/dist/src/baseline/builder.d.mts +19 -0
  16. package/dist/src/baseline/builder.mjs +142 -0
  17. package/dist/src/baseline/input_discovery.d.mts +13 -0
  18. package/dist/src/baseline/input_discovery.mjs +149 -0
  19. package/dist/src/baseline/policy_review.d.mts +56 -0
  20. package/dist/src/baseline/policy_review.mjs +73 -0
  21. package/dist/src/baseline/preparation.d.mts +63 -0
  22. package/dist/src/baseline/preparation.mjs +295 -0
  23. package/dist/src/baseline/privacy_contract.d.mts +147 -0
  24. package/dist/src/baseline/privacy_contract.mjs +2 -0
  25. package/dist/src/baseline/privacy_dependency_graph.d.mts +7 -0
  26. package/dist/src/baseline/privacy_dependency_graph.mjs +110 -0
  27. package/dist/src/baseline/privacy_engine.d.mts +5 -0
  28. package/dist/src/baseline/privacy_engine.mjs +4 -0
  29. package/dist/src/baseline/privacy_key.d.mts +7 -0
  30. package/dist/src/baseline/privacy_key.mjs +42 -0
  31. package/dist/src/baseline/privacy_pipeline.d.mts +7 -0
  32. package/dist/src/baseline/privacy_pipeline.mjs +127 -0
  33. package/dist/src/baseline/privacy_policy.d.mts +6 -0
  34. package/dist/src/baseline/privacy_policy.mjs +237 -0
  35. package/dist/src/baseline/privacy_recipe_validation.d.mts +5 -0
  36. package/dist/src/baseline/privacy_recipe_validation.mjs +289 -0
  37. package/dist/src/baseline/privacy_rule_registry.d.mts +18 -0
  38. package/dist/src/baseline/privacy_rule_registry.mjs +143 -0
  39. package/dist/src/baseline/privacy_structured_json.d.mts +66 -0
  40. package/dist/src/baseline/privacy_structured_json.mjs +464 -0
  41. package/dist/src/baseline/privacy_transforms.d.mts +18 -0
  42. package/dist/src/baseline/privacy_transforms.mjs +142 -0
  43. package/dist/src/baseline/privacy_validation_shared.d.mts +31 -0
  44. package/dist/src/baseline/privacy_validation_shared.mjs +127 -0
  45. package/dist/src/baseline/sanitization_policy.d.mts +69 -0
  46. package/dist/src/baseline/sanitization_policy.mjs +227 -0
  47. package/dist/src/baseline/schema_snapshot.d.mts +19 -0
  48. package/dist/src/baseline/schema_snapshot.mjs +79 -0
  49. package/dist/src/cli/application_command_handlers.d.mts +8 -0
  50. package/dist/src/cli/application_command_handlers.mjs +52 -0
  51. package/dist/src/cli/arguments.d.mts +38 -0
  52. package/dist/src/cli/arguments.mjs +130 -0
  53. package/dist/src/cli/basic_command_handlers.d.mts +4 -0
  54. package/dist/src/cli/basic_command_handlers.mjs +57 -0
  55. package/dist/src/cli/cli_commands.d.mts +14 -0
  56. package/dist/src/cli/cli_commands.mjs +34 -0
  57. package/dist/src/cli/command_contract.d.mts +57 -0
  58. package/dist/src/cli/command_contract.mjs +39 -0
  59. package/dist/src/cli/command_output.d.mts +12 -0
  60. package/dist/src/cli/command_output.mjs +20 -0
  61. package/dist/src/cli/command_registry.d.mts +7 -0
  62. package/dist/src/cli/command_registry.mjs +14 -0
  63. package/dist/src/cli/guided.d.mts +53 -0
  64. package/dist/src/cli/guided.mjs +727 -0
  65. package/dist/src/cli/identity_command_handlers.d.mts +12 -0
  66. package/dist/src/cli/identity_command_handlers.mjs +239 -0
  67. package/dist/src/cli/identity_renderer.d.mts +8 -0
  68. package/dist/src/cli/identity_renderer.mjs +33 -0
  69. package/dist/src/cli/init_command.d.mts +21 -0
  70. package/dist/src/cli/init_command.mjs +90 -0
  71. package/dist/src/cli/inspection_command_handlers.d.mts +3 -0
  72. package/dist/src/cli/inspection_command_handlers.mjs +79 -0
  73. package/dist/src/cli/project_command_handlers.d.mts +11 -0
  74. package/dist/src/cli/project_command_handlers.mjs +92 -0
  75. package/dist/src/cli/project_commands.d.mts +283 -0
  76. package/dist/src/cli/project_commands.mjs +192 -0
  77. package/dist/src/cli/rehearsal.d.mts +7 -0
  78. package/dist/src/cli/rehearsal.mjs +190 -0
  79. package/dist/src/cli/renderers.d.mts +299 -0
  80. package/dist/src/cli/renderers.mjs +554 -0
  81. package/dist/src/cli/runtime_action_handlers.d.mts +12 -0
  82. package/dist/src/cli/runtime_action_handlers.mjs +201 -0
  83. package/dist/src/cli/runtime_commands.d.mts +294 -0
  84. package/dist/src/cli/runtime_commands.mjs +619 -0
  85. package/dist/src/cli/source_command_handlers.d.mts +8 -0
  86. package/dist/src/cli/source_command_handlers.mjs +62 -0
  87. package/dist/src/cli/source_commands.d.mts +436 -0
  88. package/dist/src/cli/source_commands.mjs +301 -0
  89. package/dist/src/cli/terminal.d.mts +59 -0
  90. package/dist/src/cli/terminal.mjs +230 -0
  91. package/dist/src/identity/claim.d.mts +5 -0
  92. package/dist/src/identity/claim.mjs +4 -0
  93. package/dist/src/identity/claim_contract.d.mts +135 -0
  94. package/dist/src/identity/claim_contract.mjs +6 -0
  95. package/dist/src/identity/claim_executor.d.mts +10 -0
  96. package/dist/src/identity/claim_executor.mjs +358 -0
  97. package/dist/src/identity/claim_identity_lookup.d.mts +23 -0
  98. package/dist/src/identity/claim_identity_lookup.mjs +134 -0
  99. package/dist/src/identity/claim_plan.d.mts +8 -0
  100. package/dist/src/identity/claim_plan.mjs +61 -0
  101. package/dist/src/identity/claim_policy.d.mts +3 -0
  102. package/dist/src/identity/claim_policy.mjs +377 -0
  103. package/dist/src/identity/claim_sql.d.mts +10 -0
  104. package/dist/src/identity/claim_sql.mjs +46 -0
  105. package/dist/src/identity/claim_verification.d.mts +13 -0
  106. package/dist/src/identity/claim_verification.mjs +35 -0
  107. package/dist/src/identity/connect.d.mts +30 -0
  108. package/dist/src/identity/connect.mjs +111 -0
  109. package/dist/src/identity/provider_configuration.d.mts +37 -0
  110. package/dist/src/identity/provider_configuration.mjs +132 -0
  111. package/dist/src/identity/storage.d.mts +21 -0
  112. package/dist/src/identity/storage.mjs +122 -0
  113. package/dist/src/index.d.mts +4 -0
  114. package/dist/src/index.mjs +3 -0
  115. package/dist/src/project/configuration.d.mts +13 -0
  116. package/dist/src/project/configuration.mjs +9 -0
  117. package/dist/src/project/configuration_contract.d.mts +125 -0
  118. package/dist/src/project/configuration_contract.mjs +27 -0
  119. package/dist/src/project/configuration_discovery.d.mts +7 -0
  120. package/dist/src/project/configuration_discovery.mjs +37 -0
  121. package/dist/src/project/configuration_normalization.d.mts +107 -0
  122. package/dist/src/project/configuration_normalization.mjs +302 -0
  123. package/dist/src/project/configuration_rendering.d.mts +14 -0
  124. package/dist/src/project/configuration_rendering.mjs +228 -0
  125. package/dist/src/project/configuration_resolution.d.mts +125 -0
  126. package/dist/src/project/configuration_resolution.mjs +81 -0
  127. package/dist/src/project/configuration_schema.d.mts +22 -0
  128. package/dist/src/project/configuration_schema.mjs +160 -0
  129. package/dist/src/project/configuration_validation.d.mts +22 -0
  130. package/dist/src/project/configuration_validation.mjs +136 -0
  131. package/dist/src/project/install_scaffold.d.mts +42 -0
  132. package/dist/src/project/install_scaffold.mjs +263 -0
  133. package/dist/src/project/project_detection.d.mts +14 -0
  134. package/dist/src/project/project_detection.mjs +47 -0
  135. package/dist/src/project/setup.d.mts +87 -0
  136. package/dist/src/project/setup.mjs +414 -0
  137. package/dist/src/project/support_report.d.mts +88 -0
  138. package/dist/src/project/support_report.mjs +83 -0
  139. package/dist/src/runtime/cleanup.d.mts +85 -0
  140. package/dist/src/runtime/cleanup.mjs +311 -0
  141. package/dist/src/runtime/lifecycle_engine.d.mts +63 -0
  142. package/dist/src/runtime/lifecycle_engine.mjs +114 -0
  143. package/dist/src/runtime/migration_history.d.mts +51 -0
  144. package/dist/src/runtime/migration_history.mjs +170 -0
  145. package/dist/src/runtime/plan.d.mts +134 -0
  146. package/dist/src/runtime/plan.mjs +640 -0
  147. package/dist/src/runtime/policy.d.mts +66 -0
  148. package/dist/src/runtime/policy.mjs +369 -0
  149. package/dist/src/runtime/restore.d.mts +22 -0
  150. package/dist/src/runtime/restore.mjs +276 -0
  151. package/dist/src/runtime/service_environment.d.mts +11 -0
  152. package/dist/src/runtime/service_environment.mjs +58 -0
  153. package/dist/src/runtime/topology.d.mts +51 -0
  154. package/dist/src/runtime/topology.mjs +128 -0
  155. package/dist/src/shared/diagnostics.d.mts +97 -0
  156. package/dist/src/shared/diagnostics.mjs +170 -0
  157. package/dist/src/shared/human_output.d.mts +2 -0
  158. package/{src → dist/src}/shared/human_output.mjs +1 -3
  159. package/dist/src/shared/node_runtime.d.mts +14 -0
  160. package/dist/src/shared/node_runtime.mjs +25 -0
  161. package/dist/src/shared/operation_guard.d.mts +22 -0
  162. package/dist/src/shared/operation_guard.mjs +150 -0
  163. package/dist/src/shared/process_environment.d.mts +15 -0
  164. package/dist/src/shared/process_environment.mjs +50 -0
  165. package/dist/src/source/access.d.mts +165 -0
  166. package/dist/src/source/access.mjs +602 -0
  167. package/dist/src/source/asset_transfer.d.mts +27 -0
  168. package/dist/src/source/asset_transfer.mjs +202 -0
  169. package/dist/src/source/baseline.d.mts +66 -0
  170. package/dist/src/source/baseline.mjs +372 -0
  171. package/dist/src/source/external_reader.d.mts +42 -0
  172. package/dist/src/source/external_reader.mjs +278 -0
  173. package/dist/src/source/postgresql_access.d.mts +86 -0
  174. package/dist/src/source/postgresql_access.mjs +454 -0
  175. package/dist/src/source/privacy_paths.d.mts +10 -0
  176. package/dist/src/source/privacy_paths.mjs +27 -0
  177. package/dist/src/targets/driver.d.mts +2 -0
  178. package/dist/src/targets/driver.mjs +13 -0
  179. package/dist/src/targets/postgresql.d.mts +5 -0
  180. package/dist/src/targets/postgresql.mjs +620 -0
  181. package/dist/src/targets/supabase.d.mts +8 -0
  182. package/dist/src/targets/supabase.mjs +629 -0
  183. package/dist/src/targets/supabase_environment.d.mts +54 -0
  184. package/dist/src/targets/supabase_environment.mjs +184 -0
  185. package/dist/src/targets/target.d.mts +33 -0
  186. package/dist/src/targets/target.mjs +39 -0
  187. package/docs/README.md +35 -16
  188. package/docs/architecture.md +135 -10
  189. package/docs/commands.md +53 -15
  190. package/docs/configuration.md +186 -31
  191. package/docs/getting-started.md +66 -9
  192. package/docs/index.md +67 -0
  193. package/docs/production-source.md +175 -17
  194. package/docs/releasing.md +24 -12
  195. package/docs/roadmap.md +3 -2
  196. package/docs/runtime-lifecycle.md +99 -0
  197. package/docs/runtime-policies.md +80 -7
  198. package/docs/sanitization.md +572 -4
  199. package/docs/security-model.md +8 -0
  200. package/docs/stability.md +70 -0
  201. package/docs/standalone-workflow.md +36 -13
  202. package/docs/troubleshooting.md +82 -10
  203. package/docs/tutorial.md +7 -1
  204. package/docs/typescript.md +134 -0
  205. package/package.json +44 -29
  206. package/types/configuration.d.mts +5 -0
  207. package/src/README.md +0 -17
  208. package/src/application/session.mjs +0 -313
  209. package/src/baseline/artifact.mjs +0 -661
  210. package/src/baseline/builder.mjs +0 -164
  211. package/src/baseline/input_discovery.mjs +0 -155
  212. package/src/baseline/policy_review.mjs +0 -92
  213. package/src/baseline/preparation.mjs +0 -323
  214. package/src/baseline/privacy_engine.mjs +0 -413
  215. package/src/baseline/sanitization_policy.mjs +0 -299
  216. package/src/baseline/schema_snapshot.mjs +0 -113
  217. package/src/cli/arguments.mjs +0 -120
  218. package/src/cli/guided.mjs +0 -807
  219. package/src/cli/rehearsal.mjs +0 -933
  220. package/src/cli/renderers.mjs +0 -584
  221. package/src/cli/runtime_commands.mjs +0 -553
  222. package/src/cli/source_commands.mjs +0 -326
  223. package/src/cli/terminal.mjs +0 -275
  224. package/src/identity/claim.mjs +0 -975
  225. package/src/identity/storage.mjs +0 -165
  226. package/src/project/configuration.d.mts +0 -153
  227. package/src/project/configuration.mjs +0 -1197
  228. package/src/project/setup.mjs +0 -379
  229. package/src/project/support_report.mjs +0 -105
  230. package/src/runtime/cleanup.mjs +0 -381
  231. package/src/runtime/migration_history.mjs +0 -220
  232. package/src/runtime/plan.mjs +0 -685
  233. package/src/runtime/policy.mjs +0 -438
  234. package/src/runtime/restore.mjs +0 -329
  235. package/src/runtime/service_environment.mjs +0 -82
  236. package/src/runtime/topology.mjs +0 -177
  237. package/src/shared/diagnostics.mjs +0 -199
  238. package/src/shared/operation_guard.mjs +0 -162
  239. package/src/shared/process_environment.mjs +0 -68
  240. package/src/source/access.mjs +0 -578
  241. package/src/source/asset_transfer.mjs +0 -177
  242. package/src/source/baseline.mjs +0 -446
  243. package/src/source/postgresql_access.mjs +0 -480
  244. package/src/targets/postgresql.mjs +0 -808
  245. package/src/targets/supabase.mjs +0 -830
  246. package/src/targets/supabase_environment.mjs +0 -229
  247. package/src/targets/target.mjs +0 -66
@@ -0,0 +1,554 @@
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 { formatCount } from "../shared/human_output.mjs";
6
+ import { terminalStyle } from "./terminal.mjs";
7
+ const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
8
+ export const renderPlan = (plan, verbosity) => {
9
+ if ("targets" in plan) {
10
+ return [
11
+ "REHEARSAL STACK PLAN",
12
+ "",
13
+ ...plan.targets.flatMap((target) => [
14
+ `${target.name} — ${target.plan.config.project}`,
15
+ ` ✓ ${target.plan.environment.kind}`,
16
+ ...(target.plan.environment.authenticationProviders.length
17
+ ? [
18
+ ` ✓ External identity providers ${target.plan.environment.authenticationProviders.join(", ")}`,
19
+ ...(target.plan.environment.authenticationCallback
20
+ ? [
21
+ ` → Register callback ${target.plan.environment.authenticationCallback}`,
22
+ ]
23
+ : []),
24
+ ]
25
+ : []),
26
+ ` ✓ baseline ${target.plan.baseline.generationId}`,
27
+ ` ✓ ${formatCount(target.plan.baseline.tableCount, "table")}; ${formatCount(target.plan.baseline.rowCount, "row")}`,
28
+ ...(target.plan.migrations.candidates.length
29
+ ? target.plan.migrations.candidates.map((migration) => ` → ${migration.filename}`)
30
+ : [" ✓ No candidate migrations"]),
31
+ "",
32
+ ]),
33
+ `Combined candidate digest: ${plan.migrations.candidateSha256}`,
34
+ "",
35
+ "Execution plan",
36
+ ...plan.execution.map((step, index) => ` ${index + 1}. ${step}`),
37
+ ...(verbosity === "normal"
38
+ ? []
39
+ : [
40
+ "",
41
+ ...plan.targets.flatMap((target) => target.plan.environment.barriers.map((barrier) => ` ✓ ${target.name}: ${barrier}`)),
42
+ ]),
43
+ "",
44
+ plan.guarantee,
45
+ "",
46
+ plan.migrations.candidateCount
47
+ ? "Next: run rehearsal run to review and confirm this exact cross-target migration set."
48
+ : "Next: run rehearsal run to verify the runtime stack and project proofs.",
49
+ ].join("\n");
50
+ }
51
+ const candidates = plan.migrations.candidates.length
52
+ ? plan.migrations.candidates
53
+ .map((entry) => ` → ${entry.filename} (${entry.sha256.slice(0, 12)})`)
54
+ .join("\n")
55
+ : " ✓ No candidate migrations";
56
+ const verbose = verbosity === "normal"
57
+ ? []
58
+ : [
59
+ "",
60
+ "Safety barriers",
61
+ ...plan.environment.barriers.map((barrier) => ` ✓ ${barrier}`),
62
+ ` ✓ Candidate digest: ${plan.migrations.candidateSha256}`,
63
+ ];
64
+ return [
65
+ "REHEARSAL PLAN",
66
+ "",
67
+ "Environment",
68
+ ` ✓ ${plan.environment.kind}`,
69
+ ` ✓ Hosted access ${plan.environment.hostedAccess}`,
70
+ ` ✓ Hosted target configuration ${plan.environment.outboundNetwork}`,
71
+ ...(plan.environment.authenticationProviders.length
72
+ ? [
73
+ ` ✓ External identity providers ${plan.environment.authenticationProviders.join(", ")}`,
74
+ ...(plan.environment.authenticationCallback
75
+ ? [
76
+ ` → Register callback ${plan.environment.authenticationCallback}`,
77
+ ]
78
+ : []),
79
+ ]
80
+ : []),
81
+ "",
82
+ "Baseline",
83
+ ` ✓ ${plan.baseline.generationId}`,
84
+ ` ✓ ${formatCount(plan.baseline.tableCount, "table")}; ${formatCount(plan.baseline.rowCount, "row")}`,
85
+ ` ✓ ${plan.baseline.verification}`,
86
+ "",
87
+ "Migrations",
88
+ ` ✓ ${plan.migrations.representedCount} represented by baseline`,
89
+ candidates,
90
+ "",
91
+ "Execution plan",
92
+ ...plan.execution.map((step, index) => ` ${index + 1}. ${step}`),
93
+ ...verbose,
94
+ "",
95
+ plan.guarantee,
96
+ "",
97
+ plan.migrations.candidateCount
98
+ ? "Next: run rehearsal run to review and confirm this exact candidate set."
99
+ : "Next: run rehearsal run to verify the baseline and application proof.",
100
+ ].join("\n");
101
+ };
102
+ export const renderDoctor = (doctor, verbosity) => {
103
+ const lines = doctor.checks.map((check) => {
104
+ const marker = check.status === "pass" ? "✓" : "✗";
105
+ const detail = verbosity === "normal" && check.status === "pass"
106
+ ? ""
107
+ : ` — ${check.detail}`;
108
+ return `${marker} ${check.label}${detail}`;
109
+ });
110
+ for (const check of doctor.checks.filter((entry) => entry.status === "fail")) {
111
+ if (check.remediation)
112
+ lines.push(` Try: ${check.remediation}`);
113
+ }
114
+ if (doctor.ambientHostedVariables?.presentButQuarantined.length) {
115
+ lines.push(`! ${doctor.ambientHostedVariables.presentButQuarantined.length} ambient hosted credential variables detected and quarantined.`);
116
+ }
117
+ return [
118
+ ...lines,
119
+ "",
120
+ doctor.state,
121
+ "",
122
+ doctor.state === "READY"
123
+ ? "Next: run rehearsal explain to review the migration plan."
124
+ : "Next: fix the items above, then run rehearsal doctor again.",
125
+ ].join("\n");
126
+ };
127
+ export const renderBaseline = (baseline) => [
128
+ "REHEARSAL BASELINE",
129
+ `Rehearsal: ${baseline.rehearsalVersion}`,
130
+ `Generation: ${baseline.generationId}`,
131
+ `Created: ${baseline.createdAt ?? "unknown"}`,
132
+ `Format: ${baseline.formatVersion}`,
133
+ `Tables: ${baseline.tableCount}`,
134
+ `Rows: ${baseline.rowCount}`,
135
+ `Migrations: ${baseline.migrationCount} through ${baseline.migrationCutoff}`,
136
+ `Data SHA-256: ${baseline.dataSha256}`,
137
+ `Sanitization policy SHA-256: ${baseline.sanitizationPolicySha256}`,
138
+ `Verification: ${baseline.verification}`,
139
+ "Source rows and sensitive values are never printed by inspect.",
140
+ ].join("\n");
141
+ export const renderMigrations = (inspection, verbosity) => {
142
+ const entries = inspection.migrations.map((migration) => {
143
+ const digest = verbosity === "normal" ? "" : ` (${migration.sha256})`;
144
+ return `${migration.status.padEnd(28)} ${migration.filename}${digest}`;
145
+ });
146
+ return [
147
+ "REHEARSAL MIGRATIONS",
148
+ `Baseline: ${inspection.baselineGenerationId} through ${inspection.baselineCutoff}`,
149
+ `Candidate digest: ${inspection.candidateSha256}`,
150
+ "",
151
+ ...entries,
152
+ ].join("\n");
153
+ };
154
+ export const renderCandidates = (summary, verbosity) => {
155
+ const multipleTargets = (summary.targets?.length ?? 0) > 1;
156
+ return [
157
+ "REHEARSAL CANDIDATES",
158
+ ...(multipleTargets
159
+ ? (summary.targets ?? []).flatMap((target) => [
160
+ "",
161
+ `${target.name} — baseline ${target.baselineGenerationId}`,
162
+ ...(target.candidates.length
163
+ ? target.candidates.map((migration) => verbosity === "normal"
164
+ ? ` → ${migration.filename}`
165
+ : ` → ${migration.filename} (${migration.sha256})`)
166
+ : [" ✓ No candidate migrations"]),
167
+ ])
168
+ : [
169
+ `Baseline: ${summary.baselineGenerationId}`,
170
+ ...(summary.candidates.length
171
+ ? summary.candidates.map((migration) => verbosity === "normal"
172
+ ? ` → ${migration.filename}`
173
+ : ` → ${migration.filename} (${migration.sha256})`)
174
+ : [" ✓ No candidate migrations"]),
175
+ ]),
176
+ "",
177
+ `${multipleTargets ? "Combined candidate digest" : "Candidate digest"}: ${summary.candidateSha256}`,
178
+ `${multipleTargets ? "Pending across all targets" : "Pending"}: ${summary.candidateCount}`,
179
+ "",
180
+ summary.candidateCount
181
+ ? "Next: run rehearsal run for a guided confirmation, or pass the full digest in automation."
182
+ : "Next: run rehearsal run to verify the baseline and application proof.",
183
+ ].join("\n");
184
+ };
185
+ export const renderInit = (result) => [
186
+ `REHEARSAL INIT — ${result.mode.toUpperCase()}`,
187
+ `Destination: ${result.destination}`,
188
+ `Detected package manager: ${result.detected.packageManager}`,
189
+ `Detected Supabase config: ${result.detected.hasSupabaseConfig ? "yes" : "no"}`,
190
+ `Detected migrations: ${result.detected.hasMigrations ? "yes" : "no"}`,
191
+ ...(result.availableOptions?.length
192
+ ? [
193
+ "",
194
+ "AVAILABLE BUT NOT ENABLED",
195
+ ...result.availableOptions.map((option) => ` → ${option.path}: ${option.summary}`),
196
+ ]
197
+ : []),
198
+ ...(result.source ? ["", result.source] : []),
199
+ result.nextAction,
200
+ ].join("\n");
201
+ const summarizeSetupReadinessChecks = (readiness) => {
202
+ const failures = readiness.checks.filter((check) => check.status === "fail");
203
+ const policyMissing = failures.some((check) => check.id === "sanitization-policy" &&
204
+ /ENOENT|no such file/iu.test(check.detail));
205
+ const baselineMissing = failures.some((check) => check.id === "baseline" && /ENOENT|no such file/iu.test(check.detail));
206
+ const hidden = new Set([
207
+ ...(policyMissing ? ["sanitization-policy"] : []),
208
+ ...(baselineMissing
209
+ ? ["baseline", "baseline-permissions", "migration-history"]
210
+ : []),
211
+ ]);
212
+ if (policyMissing &&
213
+ failures.some((check) => check.id === "paths" && /sanitization-policy/iu.test(check.detail))) {
214
+ hidden.add("paths");
215
+ }
216
+ const passed = readiness.checks.filter((check) => check.status === "pass").length;
217
+ return [
218
+ ...(passed ? [` ✓ ${passed} environment and safety checks passed`] : []),
219
+ ...failures
220
+ .filter((check) => !hidden.has(check.id))
221
+ .map((check) => ` ○ ${check.label}: ${check.detail}`),
222
+ ...(policyMissing
223
+ ? [" ○ Baseline policy: not created yet (next guided step)"]
224
+ : []),
225
+ ...(baselineMissing
226
+ ? [" ○ Verified baseline: not created yet (after policy review)"]
227
+ : []),
228
+ ];
229
+ };
230
+ export const renderSetup = (result) => {
231
+ const actionMarker = {
232
+ create: "+",
233
+ update: "~",
234
+ unchanged: "=",
235
+ };
236
+ return [
237
+ `REHEARSAL SETUP — ${result.mode.toUpperCase()}`,
238
+ `Project: ${result.project}`,
239
+ `Configuration: ${result.configurationPath} (${result.configurationAction === "create"
240
+ ? result.mode === "written"
241
+ ? "created"
242
+ : "will create"
243
+ : "existing"})`,
244
+ ...(result.rootConfigGenerationSkipped
245
+ ? [
246
+ "Root config: not created because the existing supported configuration remains active.",
247
+ ]
248
+ : []),
249
+ `Database: ${result.target === "postgresql" ? "PostgreSQL" : "Supabase"}`,
250
+ `Runtime ID: ${result.projectId}`,
251
+ `Application: ${result.applicationUrl}`,
252
+ result.target === "postgresql"
253
+ ? `Port: database ${result.ports.database}`
254
+ : `Ports: API ${result.ports.api}, database ${result.ports.database}, Studio ${result.ports.studio}`,
255
+ "",
256
+ "Files",
257
+ ...result.files.map((file) => ` ${actionMarker[file.action]} ${file.action} ${file.path}`),
258
+ "",
259
+ "Safety boundaries",
260
+ ...result.safety.map((barrier) => ` ✓ ${barrier}`),
261
+ ...(result.readiness
262
+ ? [
263
+ "",
264
+ `Readiness — ${result.readiness.state}`,
265
+ ...summarizeSetupReadinessChecks(result.readiness),
266
+ ]
267
+ : []),
268
+ "",
269
+ result.nextAction,
270
+ ].join("\n");
271
+ };
272
+ export const renderBaselinePreparation = (result, flags = {}) => [
273
+ `BASELINE POLICY — ${result.mode.toUpperCase()}`,
274
+ `Destination: ${result.destination}`,
275
+ `Records: ${formatCount(result.rowCount, "row")} from ${result.recordsPath}`,
276
+ `Migrations: ${formatCount(result.migrationCount, "migration")} through ${result.migrationCutoff}`,
277
+ "",
278
+ "Detected shape (values are never printed)",
279
+ ...result.tables.map((table) => ` ${terminalStyle(flags, "36", "→")} ${table.name}: ${formatCount(table.rowCount, "row")}; ${table.columns.join(", ")}`),
280
+ "",
281
+ "Every column is marked REVIEW REQUIRED for action, generated status, identity, and foreign key metadata.",
282
+ "The draft cannot be activated until those decisions are completed.",
283
+ "",
284
+ result.nextAction,
285
+ ].join("\n");
286
+ export const renderBaselineInputInspection = (result, flags = {}) => [
287
+ "BASELINE INPUTS — READY",
288
+ `Records: ${formatCount(result.rowCount, "row")} across ${formatCount(result.tables.length, "table")} from ${result.recordsPath}`,
289
+ `Migrations: ${formatCount(result.migrationCount, "migration")} through ${result.migrationCutoff} from ${result.ledgerPath}`,
290
+ result.assetsPath
291
+ ? `Storage: ${formatCount(result.assetCount, "asset")} from ${result.assetsPath}`
292
+ : "Storage: no asset manifest selected",
293
+ "",
294
+ "Detected tables (values are never printed)",
295
+ ...result.tables.map((table) => ` ${terminalStyle(flags, "36", "→")} ${table.name}: ${formatCount(table.rowCount, "row")}`),
296
+ "",
297
+ "All inputs are project-local regular files and passed structural validation.",
298
+ ].join("\n");
299
+ export const renderSupportReport = (report) => {
300
+ const toolLine = (label, tool) => `${label}: ${tool.status === "available" ? tool.version : tool.status.replaceAll("_", " ")}`;
301
+ return [
302
+ "REHEARSAL SUPPORT REPORT",
303
+ "",
304
+ `Rehearsal: ${report.rehearsalVersion}`,
305
+ `System: ${report.system.platform} ${report.system.architecture} (${report.system.release})`,
306
+ `Node.js: ${report.system.node}`,
307
+ toolLine("npm", report.tools.npm),
308
+ toolLine("Supabase CLI", report.tools.supabase),
309
+ toolLine("Docker client", report.tools.dockerClient),
310
+ toolLine("Docker server", report.tools.dockerServer),
311
+ "",
312
+ `Readiness: ${report.readiness.state}`,
313
+ ...report.readiness.checks.map((check) => check.status === "pass"
314
+ ? ` ✓ ${check.label}`
315
+ : ` ✗ ${check.label}${check.remediation ? ` — ${check.remediation}` : ""}`),
316
+ ...(report.readiness.quarantinedHostedVariableCount
317
+ ? [
318
+ ` ! ${formatCount(report.readiness.quarantinedHostedVariableCount, "ambient hosted variable")} detected and quarantined`,
319
+ ]
320
+ : []),
321
+ "",
322
+ "Privacy: no row values, credentials, project paths, migration SQL, or baseline identifiers are included.",
323
+ "Review this report before sharing it.",
324
+ `Report a reproducible issue: ${report.supportUrl}`,
325
+ ].join("\n");
326
+ };
327
+ export const formatBytes = (bytes) => {
328
+ if (!Number.isFinite(bytes) || bytes <= 0)
329
+ return "0 B";
330
+ const units = ["B", "KiB", "MiB", "GiB", "TiB"];
331
+ const unit = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
332
+ const value = bytes / 1024 ** unit;
333
+ return `${value >= 10 || unit === 0 ? value.toFixed(0) : value.toFixed(1)} ${units[unit] ?? "B"}`;
334
+ };
335
+ export const renderCleanup = (result) => {
336
+ const plan = result.plan;
337
+ if ("targets" in plan) {
338
+ const selectedCount = plan.targets.reduce((total, target) => total +
339
+ target.plan.baselines.removed.length +
340
+ (target.plan.runtime.included && target.plan.runtime.detected ? 1 : 0) +
341
+ target.plan.images.candidates.length, 0);
342
+ return [
343
+ `REHEARSAL STACK CLEANUP — ${result.mode.toUpperCase()}`,
344
+ ...plan.targets.flatMap((target) => [
345
+ "",
346
+ `${target.name} — ${target.plan.project}`,
347
+ target.plan.baselines.available
348
+ ? ` ${target.plan.baselines.removed.length ? "→" : "✓"} ${formatCount(target.plan.baselines.removed.length, "old baseline generation")} selected; keeping ${target.plan.baselines.retained.length}`
349
+ : " ○ No active baseline found",
350
+ !target.plan.runtime.detected
351
+ ? " ✓ No runtime found"
352
+ : target.plan.runtime.included
353
+ ? ` → Runtime ${target.plan.projectId} selected`
354
+ : ` ○ Runtime ${target.plan.projectId} preserved`,
355
+ ...(target.plan.images.requested
356
+ ? target.plan.images.inspected
357
+ ? target.plan.images.candidates.length
358
+ ? [
359
+ ` → ${formatCount(target.plan.images.candidates.length, "older unused Supabase image")} selected`,
360
+ ...target.plan.images.candidates.flatMap((image) => image.tags.map((tag) => ` ${tag}`)),
361
+ ]
362
+ : [" ✓ No older unused Supabase images found"]
363
+ : [" ! Docker is unavailable; shared images were not inspected"]
364
+ : []),
365
+ ]),
366
+ "",
367
+ ...(result.mode === "preview"
368
+ ? selectedCount
369
+ ? [
370
+ "Nothing has been removed.",
371
+ `Combined cleanup set: ${plan.digest.slice(0, 12)}`,
372
+ `Exact confirmation: --confirm-cleanup=${plan.digest}`,
373
+ "Review every target above, then rerun with --write and this exact confirmation.",
374
+ ]
375
+ : ["Nothing has been removed, and nothing is currently selected."]
376
+ : result.applied && "targets" in result.applied
377
+ ? result.applied.targets.map((target) => `${target.name}: removed ${formatCount(target.applied.baselines.removed.length, "old baseline generation")}; runtime ${target.applied.runtimeRemoved ? "removed" : "preserved"}; removed ${formatCount(target.applied.imagesRemoved.length, "unused image tag")}.`)
378
+ : []),
379
+ ].join("\n");
380
+ }
381
+ const imageBytes = plan.images.candidates.reduce((total, image) => total + image.bytes, 0);
382
+ const dockerImages = plan.docker.resources.find((resource) => isObject(resource) &&
383
+ resource.Type === "Images" &&
384
+ typeof resource.Size === "string" &&
385
+ typeof resource.Reclaimable === "string");
386
+ const selectedCount = plan.baselines.removed.length +
387
+ (plan.runtime.included && plan.runtime.detected ? 1 : 0) +
388
+ plan.images.candidates.length;
389
+ return [
390
+ `REHEARSAL CLEANUP — ${result.mode.toUpperCase()}`,
391
+ `Project: ${plan.project}`,
392
+ ...(dockerImages
393
+ ? [
394
+ `Docker images: ${dockerImages.Size}; ${dockerImages.Reclaimable} reclaimable across Docker`,
395
+ ]
396
+ : ["Docker usage: unavailable"]),
397
+ ...(plan.docker.disk
398
+ ? [
399
+ `Container disk: ${formatBytes(plan.docker.disk.usedBytes)} used of ${formatBytes(plan.docker.disk.totalBytes)}; ${formatBytes(plan.docker.disk.availableBytes)} free (${plan.docker.disk.usedPercent}% used)`,
400
+ ]
401
+ : []),
402
+ "",
403
+ "Project-owned files",
404
+ plan.baselines.available
405
+ ? ` ${plan.baselines.removed.length ? "→" : "✓"} ${formatCount(plan.baselines.removed.length, "old baseline generation")} selected; keeping ${plan.baselines.retained.length}`
406
+ : " ○ No active baseline found",
407
+ "",
408
+ "Disposable runtime",
409
+ !plan.runtime.detected
410
+ ? " ✓ No runtime found"
411
+ : plan.runtime.included
412
+ ? " → Current project runtime selected"
413
+ : " ○ Current project runtime preserved (add --include-runtime to select it)",
414
+ "",
415
+ "Shared Docker images",
416
+ !plan.images.requested
417
+ ? " ○ Not inspected (add --include-images to inspect older unused Supabase images)"
418
+ : !plan.images.inspected
419
+ ? " ! Docker is unavailable; images could not be inspected"
420
+ : plan.images.candidates.length
421
+ ? ` → ${formatCount(plan.images.candidates.length, "older unused Supabase image")} selected; approximately ${formatBytes(imageBytes)}`
422
+ : " ✓ No older unused Supabase images found",
423
+ ...(plan.images.candidates.length
424
+ ? plan.images.candidates.flatMap((image) => image.tags.map((tag) => ` ${tag}`))
425
+ : []),
426
+ "",
427
+ ...(result.mode === "preview"
428
+ ? selectedCount
429
+ ? [
430
+ "Nothing has been removed.",
431
+ `Cleanup set: ${plan.digest.slice(0, 12)}`,
432
+ `Exact confirmation: --confirm-cleanup=${plan.digest}`,
433
+ "Review this list, then rerun the same command with --write and the exact confirmation above.",
434
+ ]
435
+ : [
436
+ "Nothing has been removed, and nothing is currently selected.",
437
+ "Use --include-runtime or --include-images only when you want those shared resources considered.",
438
+ ]
439
+ : [
440
+ `Removed ${formatCount(!result.applied || "targets" in result.applied ? 0 : result.applied.baselines.removed.length, "old baseline generation")}.`,
441
+ result.applied &&
442
+ !("targets" in result.applied) &&
443
+ result.applied.runtimeRemoved
444
+ ? "Removed this project's disposable runtime and volumes."
445
+ : "Preserved this project's disposable runtime.",
446
+ `Removed ${formatCount(!result.applied || "targets" in result.applied ? 0 : result.applied.imagesRemoved.length, "unused image tag")}.`,
447
+ ]),
448
+ ].join("\n");
449
+ };
450
+ export const renderSourceAccessPlan = (plan) => [
451
+ "SOURCE ACCESS — PREVIEW",
452
+ "",
453
+ `Target fingerprint: ${plan.review.targetFingerprint}`,
454
+ plan.review.reader.mode === "managed"
455
+ ? `Managed temporary reader: ${plan.review.reader.role} (${plan.review.reader.validForMinutes} minutes)`
456
+ : `Externally managed reader: ${plan.review.reader.role} (maximum ${plan.review.reader.maximumValidForMinutes} minutes remaining)`,
457
+ `Source changes by Rehearsal: ${plan.review.reader.sourceChanges ? "export views and temporary roles" : "none"}`,
458
+ `Export schema: ${plan.review.exportSchema}`,
459
+ ...plan.review.relations.map((relation) => ` → ${relation.source.schema}.${relation.source.table} → ${relation.view}: ${relation.columns.join(", ")} (${relation.rowScope.kind})`),
460
+ ...(plan.review.assets.length
461
+ ? plan.review.assets.map((asset) => ` → Storage ${asset.bucket}/${"prefix" in asset
462
+ ? asset.prefix
463
+ : `[private prefix from ${asset.prefixEnvironmentVariable}]`} (${asset.rights})`)
464
+ : [" ○ No source assets declared"]),
465
+ "",
466
+ "Deny checks",
467
+ ...plan.review.denyChecks.map((check) => ` ✓ ${check}`),
468
+ "",
469
+ "No source changes have been made.",
470
+ `Exact confirmation: --confirm-source-access=${plan.digest}`,
471
+ ].join("\n");
472
+ export const renderRefreshWorkflow = ({ mode, plan, refreshed, runtime, cleanup, }) => {
473
+ const cleanupLine = cleanup
474
+ ? "removed" in cleanup
475
+ ? `Cleanup complete: ${formatCount(cleanup.removed.length, "old generation")} removed.`
476
+ : `Cleanup incomplete: ${formatCount(cleanup.remaining, "old generation")} still eligible. Run rehearsal cleanup.`
477
+ : "Cleanup status unavailable.";
478
+ return [
479
+ `DATABASE COPY REFRESH — ${mode.toUpperCase()}`,
480
+ "",
481
+ `Current baseline: ${plan.review.currentBaseline}`,
482
+ `Source access expires: ${plan.review.source.readerExpiresAt}`,
483
+ `Runtime targets reset: ${plan.review.runtimeTargets.join(", ")}`,
484
+ `Baseline retention: ${formatCount(plan.review.retention.generations, "generation")}`,
485
+ ...(plan.review.retention.removeAfterReplacement.length
486
+ ? [
487
+ "Old generations removed after success:",
488
+ ...plan.review.retention.removeAfterReplacement.map((generation) => ` → ${generation}`),
489
+ ]
490
+ : ["Old generations removed after success: none"]),
491
+ "",
492
+ ...plan.review.steps.map((step, index) => ` ${index + 1}. ${step}`),
493
+ "",
494
+ ...(mode === "preview"
495
+ ? [
496
+ "Nothing has changed. The current baseline and runtime remain intact.",
497
+ `Exact confirmation: --confirm-refresh=${plan.digest}`,
498
+ ]
499
+ : [
500
+ `Activated baseline: ${refreshed?.generationId ?? "unknown"}`,
501
+ `Runtime reset: ${runtime?.targets?.length ?? 1} target(s)`,
502
+ cleanupLine,
503
+ "",
504
+ "Next: retire source access when no other refresh is needed, then run a rehearsal.",
505
+ ]),
506
+ ].join("\n");
507
+ };
508
+ export const renderSourceRetirement = ({ mode, plan, }) => [
509
+ `SOURCE ACCESS RETIREMENT — ${mode.toUpperCase()}`,
510
+ `Target fingerprint: ${plan.review.targetFingerprint}`,
511
+ `Reader: ${plan.review.readerRole}`,
512
+ `Export schema: ${plan.review.exportSchema}`,
513
+ ...(plan.review.accessMode === "managed"
514
+ ? plan.review.views.map((view) => ` → remove view ${view}`)
515
+ : [
516
+ " → remove only Rehearsal's local credential and receipt",
517
+ " ✓ preserve the provider-owned reader and export views",
518
+ ]),
519
+ "",
520
+ ...(mode === "preview"
521
+ ? [
522
+ "Nothing has been removed.",
523
+ `Exact confirmation: --confirm-source-retirement=${plan.digest}`,
524
+ ]
525
+ : [
526
+ plan.review.accessMode === "managed"
527
+ ? "The exact reader, export views, and local credential files were retired."
528
+ : "Rehearsal's local credential and receipt were removed. Provider-owned database resources were preserved.",
529
+ ]),
530
+ ].join("\n");
531
+ export const renderIdentityPlan = ({ mode, plan, }) => [
532
+ `LOCAL IDENTITY — ${mode.toUpperCase()}`,
533
+ `Identity: ${plan.review.name}`,
534
+ `Matcher: ${plan.review.matcher.type}`,
535
+ plan.review.matcher.type === "verified-email"
536
+ ? `Providers: ${plan.review.matcher.providers.join(", ")}`
537
+ : `Provider: ${plan.review.matcher.provider}`,
538
+ plan.review.matcher.type === "verified-email"
539
+ ? `Approved email receipt: ${plan.review.matcher.approvedEmailSha256}`
540
+ : `Approved subject receipt: ${plan.review.matcher.approvedSubjectSha256}`,
541
+ `Relational references: ${plan.review.references.length}`,
542
+ `Nested JSON references: ${plan.review.jsonReferences.length}`,
543
+ `Signup defaults: ${plan.review.signupDefaults.length}`,
544
+ `Storage path references: ${plan.review.pathReferences.length}`,
545
+ `Asset scopes: ${plan.review.assets.length}`,
546
+ `Token hook assertion: ${plan.review.tokenHook ? "configured" : "not configured"}`,
547
+ "",
548
+ ...(mode === "preview"
549
+ ? [
550
+ "No local account was changed.",
551
+ `Exact confirmation: --confirm-identity=${plan.digest}`,
552
+ ]
553
+ : ["The verified local identity now owns the declared copied graph."]),
554
+ ].join("\n");
@@ -0,0 +1,12 @@
1
+ /** Database runtime action handlers shared by guided and scripted CLI flows. */
2
+ import type { CommandDefinition } from "./command_contract.mjs";
3
+ import type { createRuntimeCommands } from "./runtime_commands.mjs";
4
+ export type RuntimeAction = "run" | "start" | "migrate" | "reset" | "status" | "stop" | "discard" | "verify";
5
+ type RuntimeCommands = ReturnType<typeof createRuntimeCommands>;
6
+ export declare const createRuntimeActionHandlers: ({ prepareCandidateConfirmation, prepareDependentTargets, proveRuntimeStack, runRuntimeStack, }: {
7
+ prepareCandidateConfirmation: RuntimeCommands["prepareCandidateConfirmation"];
8
+ prepareDependentTargets: RuntimeCommands["prepareDependentTargets"];
9
+ proveRuntimeStack: RuntimeCommands["proveRuntimeStack"];
10
+ runRuntimeStack: RuntimeCommands["runRuntimeStack"];
11
+ }) => readonly CommandDefinition[];
12
+ export {};