@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.
- package/CHANGELOG.md +319 -1
- package/COMPATIBILITY.md +15 -0
- package/README.md +73 -16
- package/dist/package.json +100 -0
- package/dist/scripts/install/create_root_config.d.mts +3 -0
- package/dist/scripts/install/create_root_config.mjs +13 -0
- package/dist/scripts/runtime/manage_database.d.mts +6 -0
- package/{scripts → dist/scripts}/runtime/manage_database.mjs +6 -8
- package/dist/src/application/interactive_task.d.mts +30 -0
- package/dist/src/application/interactive_task.mjs +83 -0
- package/dist/src/application/session.d.mts +97 -0
- package/dist/src/application/session.mjs +380 -0
- package/dist/src/baseline/artifact.d.mts +119 -0
- package/dist/src/baseline/artifact.mjs +522 -0
- package/dist/src/baseline/builder.d.mts +19 -0
- package/dist/src/baseline/builder.mjs +142 -0
- package/dist/src/baseline/input_discovery.d.mts +13 -0
- package/dist/src/baseline/input_discovery.mjs +149 -0
- package/dist/src/baseline/policy_review.d.mts +56 -0
- package/dist/src/baseline/policy_review.mjs +73 -0
- package/dist/src/baseline/preparation.d.mts +63 -0
- package/dist/src/baseline/preparation.mjs +295 -0
- package/dist/src/baseline/privacy_contract.d.mts +147 -0
- package/dist/src/baseline/privacy_contract.mjs +2 -0
- package/dist/src/baseline/privacy_dependency_graph.d.mts +7 -0
- package/dist/src/baseline/privacy_dependency_graph.mjs +110 -0
- package/dist/src/baseline/privacy_engine.d.mts +5 -0
- package/dist/src/baseline/privacy_engine.mjs +4 -0
- package/dist/src/baseline/privacy_key.d.mts +7 -0
- package/dist/src/baseline/privacy_key.mjs +42 -0
- package/dist/src/baseline/privacy_pipeline.d.mts +7 -0
- package/dist/src/baseline/privacy_pipeline.mjs +127 -0
- package/dist/src/baseline/privacy_policy.d.mts +6 -0
- package/dist/src/baseline/privacy_policy.mjs +237 -0
- package/dist/src/baseline/privacy_recipe_validation.d.mts +5 -0
- package/dist/src/baseline/privacy_recipe_validation.mjs +289 -0
- package/dist/src/baseline/privacy_rule_registry.d.mts +18 -0
- package/dist/src/baseline/privacy_rule_registry.mjs +143 -0
- package/dist/src/baseline/privacy_structured_json.d.mts +66 -0
- package/dist/src/baseline/privacy_structured_json.mjs +464 -0
- package/dist/src/baseline/privacy_transforms.d.mts +18 -0
- package/dist/src/baseline/privacy_transforms.mjs +142 -0
- package/dist/src/baseline/privacy_validation_shared.d.mts +31 -0
- package/dist/src/baseline/privacy_validation_shared.mjs +127 -0
- package/dist/src/baseline/sanitization_policy.d.mts +69 -0
- package/dist/src/baseline/sanitization_policy.mjs +227 -0
- package/dist/src/baseline/schema_snapshot.d.mts +19 -0
- package/dist/src/baseline/schema_snapshot.mjs +79 -0
- package/dist/src/cli/application_command_handlers.d.mts +8 -0
- package/dist/src/cli/application_command_handlers.mjs +52 -0
- package/dist/src/cli/arguments.d.mts +38 -0
- package/dist/src/cli/arguments.mjs +130 -0
- package/dist/src/cli/basic_command_handlers.d.mts +4 -0
- package/dist/src/cli/basic_command_handlers.mjs +57 -0
- package/dist/src/cli/cli_commands.d.mts +14 -0
- package/dist/src/cli/cli_commands.mjs +34 -0
- package/dist/src/cli/command_contract.d.mts +57 -0
- package/dist/src/cli/command_contract.mjs +39 -0
- package/dist/src/cli/command_output.d.mts +12 -0
- package/dist/src/cli/command_output.mjs +20 -0
- package/dist/src/cli/command_registry.d.mts +7 -0
- package/dist/src/cli/command_registry.mjs +14 -0
- package/dist/src/cli/guided.d.mts +53 -0
- package/dist/src/cli/guided.mjs +727 -0
- package/dist/src/cli/identity_command_handlers.d.mts +12 -0
- package/dist/src/cli/identity_command_handlers.mjs +239 -0
- package/dist/src/cli/identity_renderer.d.mts +8 -0
- package/dist/src/cli/identity_renderer.mjs +33 -0
- package/dist/src/cli/init_command.d.mts +21 -0
- package/dist/src/cli/init_command.mjs +90 -0
- package/dist/src/cli/inspection_command_handlers.d.mts +3 -0
- package/dist/src/cli/inspection_command_handlers.mjs +79 -0
- package/dist/src/cli/project_command_handlers.d.mts +11 -0
- package/dist/src/cli/project_command_handlers.mjs +92 -0
- package/dist/src/cli/project_commands.d.mts +283 -0
- package/dist/src/cli/project_commands.mjs +192 -0
- package/dist/src/cli/rehearsal.d.mts +7 -0
- package/dist/src/cli/rehearsal.mjs +190 -0
- package/dist/src/cli/renderers.d.mts +299 -0
- package/dist/src/cli/renderers.mjs +554 -0
- package/dist/src/cli/runtime_action_handlers.d.mts +12 -0
- package/dist/src/cli/runtime_action_handlers.mjs +201 -0
- package/dist/src/cli/runtime_commands.d.mts +294 -0
- package/dist/src/cli/runtime_commands.mjs +619 -0
- package/dist/src/cli/source_command_handlers.d.mts +8 -0
- package/dist/src/cli/source_command_handlers.mjs +62 -0
- package/dist/src/cli/source_commands.d.mts +436 -0
- package/dist/src/cli/source_commands.mjs +301 -0
- package/dist/src/cli/terminal.d.mts +59 -0
- package/dist/src/cli/terminal.mjs +230 -0
- package/dist/src/identity/claim.d.mts +5 -0
- package/dist/src/identity/claim.mjs +4 -0
- package/dist/src/identity/claim_contract.d.mts +135 -0
- package/dist/src/identity/claim_contract.mjs +6 -0
- package/dist/src/identity/claim_executor.d.mts +10 -0
- package/dist/src/identity/claim_executor.mjs +358 -0
- package/dist/src/identity/claim_identity_lookup.d.mts +23 -0
- package/dist/src/identity/claim_identity_lookup.mjs +134 -0
- package/dist/src/identity/claim_plan.d.mts +8 -0
- package/dist/src/identity/claim_plan.mjs +61 -0
- package/dist/src/identity/claim_policy.d.mts +3 -0
- package/dist/src/identity/claim_policy.mjs +377 -0
- package/dist/src/identity/claim_sql.d.mts +10 -0
- package/dist/src/identity/claim_sql.mjs +46 -0
- package/dist/src/identity/claim_verification.d.mts +13 -0
- package/dist/src/identity/claim_verification.mjs +35 -0
- package/dist/src/identity/connect.d.mts +30 -0
- package/dist/src/identity/connect.mjs +111 -0
- package/dist/src/identity/provider_configuration.d.mts +37 -0
- package/dist/src/identity/provider_configuration.mjs +132 -0
- package/dist/src/identity/storage.d.mts +21 -0
- package/dist/src/identity/storage.mjs +122 -0
- package/dist/src/index.d.mts +4 -0
- package/dist/src/index.mjs +3 -0
- package/dist/src/project/configuration.d.mts +13 -0
- package/dist/src/project/configuration.mjs +9 -0
- package/dist/src/project/configuration_contract.d.mts +125 -0
- package/dist/src/project/configuration_contract.mjs +27 -0
- package/dist/src/project/configuration_discovery.d.mts +7 -0
- package/dist/src/project/configuration_discovery.mjs +37 -0
- package/dist/src/project/configuration_normalization.d.mts +107 -0
- package/dist/src/project/configuration_normalization.mjs +302 -0
- package/dist/src/project/configuration_rendering.d.mts +14 -0
- package/dist/src/project/configuration_rendering.mjs +228 -0
- package/dist/src/project/configuration_resolution.d.mts +125 -0
- package/dist/src/project/configuration_resolution.mjs +81 -0
- package/dist/src/project/configuration_schema.d.mts +22 -0
- package/dist/src/project/configuration_schema.mjs +160 -0
- package/dist/src/project/configuration_validation.d.mts +22 -0
- package/dist/src/project/configuration_validation.mjs +136 -0
- package/dist/src/project/install_scaffold.d.mts +42 -0
- package/dist/src/project/install_scaffold.mjs +263 -0
- package/dist/src/project/project_detection.d.mts +14 -0
- package/dist/src/project/project_detection.mjs +47 -0
- package/dist/src/project/setup.d.mts +87 -0
- package/dist/src/project/setup.mjs +414 -0
- package/dist/src/project/support_report.d.mts +88 -0
- package/dist/src/project/support_report.mjs +83 -0
- package/dist/src/runtime/cleanup.d.mts +85 -0
- package/dist/src/runtime/cleanup.mjs +311 -0
- package/dist/src/runtime/lifecycle_engine.d.mts +63 -0
- package/dist/src/runtime/lifecycle_engine.mjs +114 -0
- package/dist/src/runtime/migration_history.d.mts +51 -0
- package/dist/src/runtime/migration_history.mjs +170 -0
- package/dist/src/runtime/plan.d.mts +134 -0
- package/dist/src/runtime/plan.mjs +640 -0
- package/dist/src/runtime/policy.d.mts +66 -0
- package/dist/src/runtime/policy.mjs +369 -0
- package/dist/src/runtime/restore.d.mts +22 -0
- package/dist/src/runtime/restore.mjs +276 -0
- package/dist/src/runtime/service_environment.d.mts +11 -0
- package/dist/src/runtime/service_environment.mjs +58 -0
- package/dist/src/runtime/topology.d.mts +51 -0
- package/dist/src/runtime/topology.mjs +128 -0
- package/dist/src/shared/diagnostics.d.mts +97 -0
- package/dist/src/shared/diagnostics.mjs +170 -0
- package/dist/src/shared/human_output.d.mts +2 -0
- package/{src → dist/src}/shared/human_output.mjs +1 -3
- package/dist/src/shared/node_runtime.d.mts +14 -0
- package/dist/src/shared/node_runtime.mjs +25 -0
- package/dist/src/shared/operation_guard.d.mts +22 -0
- package/dist/src/shared/operation_guard.mjs +150 -0
- package/dist/src/shared/process_environment.d.mts +15 -0
- package/dist/src/shared/process_environment.mjs +50 -0
- package/dist/src/source/access.d.mts +165 -0
- package/dist/src/source/access.mjs +602 -0
- package/dist/src/source/asset_transfer.d.mts +27 -0
- package/dist/src/source/asset_transfer.mjs +202 -0
- package/dist/src/source/baseline.d.mts +66 -0
- package/dist/src/source/baseline.mjs +372 -0
- package/dist/src/source/external_reader.d.mts +42 -0
- package/dist/src/source/external_reader.mjs +278 -0
- package/dist/src/source/postgresql_access.d.mts +86 -0
- package/dist/src/source/postgresql_access.mjs +454 -0
- package/dist/src/source/privacy_paths.d.mts +10 -0
- package/dist/src/source/privacy_paths.mjs +27 -0
- package/dist/src/targets/driver.d.mts +2 -0
- package/dist/src/targets/driver.mjs +13 -0
- package/dist/src/targets/postgresql.d.mts +5 -0
- package/dist/src/targets/postgresql.mjs +620 -0
- package/dist/src/targets/supabase.d.mts +8 -0
- package/dist/src/targets/supabase.mjs +629 -0
- package/dist/src/targets/supabase_environment.d.mts +54 -0
- package/dist/src/targets/supabase_environment.mjs +184 -0
- package/dist/src/targets/target.d.mts +33 -0
- package/dist/src/targets/target.mjs +39 -0
- package/docs/README.md +35 -16
- package/docs/architecture.md +135 -10
- package/docs/commands.md +53 -15
- package/docs/configuration.md +186 -31
- package/docs/getting-started.md +66 -9
- package/docs/index.md +67 -0
- package/docs/production-source.md +175 -17
- package/docs/releasing.md +24 -12
- package/docs/roadmap.md +3 -2
- package/docs/runtime-lifecycle.md +99 -0
- package/docs/runtime-policies.md +80 -7
- package/docs/sanitization.md +572 -4
- package/docs/security-model.md +8 -0
- package/docs/stability.md +70 -0
- package/docs/standalone-workflow.md +36 -13
- package/docs/troubleshooting.md +82 -10
- package/docs/tutorial.md +7 -1
- package/docs/typescript.md +134 -0
- package/package.json +44 -29
- package/types/configuration.d.mts +5 -0
- package/src/README.md +0 -17
- package/src/application/session.mjs +0 -313
- package/src/baseline/artifact.mjs +0 -661
- package/src/baseline/builder.mjs +0 -164
- package/src/baseline/input_discovery.mjs +0 -155
- package/src/baseline/policy_review.mjs +0 -92
- package/src/baseline/preparation.mjs +0 -323
- package/src/baseline/privacy_engine.mjs +0 -413
- package/src/baseline/sanitization_policy.mjs +0 -299
- package/src/baseline/schema_snapshot.mjs +0 -113
- package/src/cli/arguments.mjs +0 -120
- package/src/cli/guided.mjs +0 -807
- package/src/cli/rehearsal.mjs +0 -933
- package/src/cli/renderers.mjs +0 -584
- package/src/cli/runtime_commands.mjs +0 -553
- package/src/cli/source_commands.mjs +0 -326
- package/src/cli/terminal.mjs +0 -275
- package/src/identity/claim.mjs +0 -975
- package/src/identity/storage.mjs +0 -165
- package/src/project/configuration.d.mts +0 -153
- package/src/project/configuration.mjs +0 -1197
- package/src/project/setup.mjs +0 -379
- package/src/project/support_report.mjs +0 -105
- package/src/runtime/cleanup.mjs +0 -381
- package/src/runtime/migration_history.mjs +0 -220
- package/src/runtime/plan.mjs +0 -685
- package/src/runtime/policy.mjs +0 -438
- package/src/runtime/restore.mjs +0 -329
- package/src/runtime/service_environment.mjs +0 -82
- package/src/runtime/topology.mjs +0 -177
- package/src/shared/diagnostics.mjs +0 -199
- package/src/shared/operation_guard.mjs +0 -162
- package/src/shared/process_environment.mjs +0 -68
- package/src/source/access.mjs +0 -578
- package/src/source/asset_transfer.mjs +0 -177
- package/src/source/baseline.mjs +0 -446
- package/src/source/postgresql_access.mjs +0 -480
- package/src/targets/postgresql.mjs +0 -808
- package/src/targets/supabase.mjs +0 -830
- package/src/targets/supabase_environment.mjs +0 -229
- package/src/targets/target.mjs +0 -66
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/** Project-scoped CLI orchestration that is shared by direct and guided commands. */
|
|
2
|
+
import { summarizeBaselinePreparation } from "../baseline/preparation.mjs";
|
|
3
|
+
import type { RehearsalConfigPathOptions } from "../project/configuration.mjs";
|
|
4
|
+
import type { RehearsalCleanupPlan } from "../runtime/cleanup.mjs";
|
|
5
|
+
import type { RehearsalCliFlags } from "./arguments.mjs";
|
|
6
|
+
import type { CliSessionState } from "./command_contract.mjs";
|
|
7
|
+
type CleanupStackPlan = Readonly<{
|
|
8
|
+
digest: string;
|
|
9
|
+
targets: readonly Readonly<{
|
|
10
|
+
name: string;
|
|
11
|
+
configPath: string;
|
|
12
|
+
plan: RehearsalCleanupPlan;
|
|
13
|
+
}>[];
|
|
14
|
+
}>;
|
|
15
|
+
type RunManager = (input: {
|
|
16
|
+
action: "discard";
|
|
17
|
+
flags: RehearsalCliFlags;
|
|
18
|
+
configPath?: string;
|
|
19
|
+
targetName?: string;
|
|
20
|
+
}) => unknown;
|
|
21
|
+
interface ProjectCommandInput {
|
|
22
|
+
readonly flags: RehearsalCliFlags;
|
|
23
|
+
readonly planOptions: RehearsalConfigPathOptions;
|
|
24
|
+
readonly session?: CliSessionState;
|
|
25
|
+
}
|
|
26
|
+
export declare const createProjectCommands: ({ projectRoot, runManager, }: {
|
|
27
|
+
projectRoot: string;
|
|
28
|
+
runManager: RunManager;
|
|
29
|
+
}) => Readonly<{
|
|
30
|
+
loadIdentityClaimPlan: ({ flags, planOptions, session, }: ProjectCommandInput) => Promise<{
|
|
31
|
+
loaded: Readonly<{
|
|
32
|
+
config: Readonly<{
|
|
33
|
+
schemaVersion: 1;
|
|
34
|
+
project: Readonly<{
|
|
35
|
+
name: string;
|
|
36
|
+
}>;
|
|
37
|
+
supabase: Readonly<{
|
|
38
|
+
workdir: string;
|
|
39
|
+
migrationDirectory: string;
|
|
40
|
+
rehearsalConfig: string;
|
|
41
|
+
runtimeWorkdir: string;
|
|
42
|
+
authentication: Readonly<{
|
|
43
|
+
enableLocalSignup: true;
|
|
44
|
+
environmentFile: string;
|
|
45
|
+
providers: readonly Readonly<{
|
|
46
|
+
name: "github" | "google";
|
|
47
|
+
clientIdEnvironmentVariable: string;
|
|
48
|
+
clientSecretEnvironmentVariable: string;
|
|
49
|
+
skipNonceCheck: boolean;
|
|
50
|
+
emailOptional: boolean;
|
|
51
|
+
}>[];
|
|
52
|
+
}> | null;
|
|
53
|
+
serviceEnvironmentFile: string | null;
|
|
54
|
+
serviceEnvironmentVariables: readonly string[];
|
|
55
|
+
}> | null;
|
|
56
|
+
postgresql: Readonly<{
|
|
57
|
+
migrationDirectory: string;
|
|
58
|
+
runtimeWorkdir: string;
|
|
59
|
+
image: string;
|
|
60
|
+
database: string;
|
|
61
|
+
user: string;
|
|
62
|
+
}> | null;
|
|
63
|
+
baseline: Readonly<{
|
|
64
|
+
artifactDirectory: string;
|
|
65
|
+
sanitizationPolicy: string;
|
|
66
|
+
}>;
|
|
67
|
+
containerRuntime: Readonly<{
|
|
68
|
+
autoStartColima: boolean;
|
|
69
|
+
}>;
|
|
70
|
+
cleanup: Readonly<{
|
|
71
|
+
retainBaselineGenerations: number;
|
|
72
|
+
}>;
|
|
73
|
+
preparation: Readonly<{
|
|
74
|
+
sourcePolicy: string;
|
|
75
|
+
privacyKey: string;
|
|
76
|
+
batchRows: number;
|
|
77
|
+
maximumRows: number;
|
|
78
|
+
maximumBytes: number;
|
|
79
|
+
diskHeadroomBytes: number;
|
|
80
|
+
}> | null;
|
|
81
|
+
runtimePolicy: string | null;
|
|
82
|
+
identityPolicy: string | null;
|
|
83
|
+
dependentTargets: readonly Readonly<{
|
|
84
|
+
name: string;
|
|
85
|
+
configPath: string;
|
|
86
|
+
prepareCommand: string | null;
|
|
87
|
+
}>[];
|
|
88
|
+
application: Readonly<{
|
|
89
|
+
startCommand: string;
|
|
90
|
+
proofCommand: string;
|
|
91
|
+
environmentFile: string;
|
|
92
|
+
environmentVariables: Readonly<Record<string, string>>;
|
|
93
|
+
readiness: Readonly<{
|
|
94
|
+
url: string;
|
|
95
|
+
expectedStatus: number;
|
|
96
|
+
timeoutSeconds: number;
|
|
97
|
+
}> | null;
|
|
98
|
+
httpProofs: readonly Readonly<{
|
|
99
|
+
name: string;
|
|
100
|
+
kind: "positive" | "negative";
|
|
101
|
+
url: string;
|
|
102
|
+
method: string;
|
|
103
|
+
expectedStatus: number;
|
|
104
|
+
json: Readonly<{
|
|
105
|
+
path: readonly string[];
|
|
106
|
+
equals?: unknown;
|
|
107
|
+
notEquals?: unknown;
|
|
108
|
+
minimumItems?: number;
|
|
109
|
+
}> | null;
|
|
110
|
+
}>[];
|
|
111
|
+
runtimeAdapter: string | null;
|
|
112
|
+
}>;
|
|
113
|
+
runtime: Readonly<{
|
|
114
|
+
target: "postgresql" | "supabase";
|
|
115
|
+
applicationUrl: string;
|
|
116
|
+
projectId: string;
|
|
117
|
+
ports: Readonly<{
|
|
118
|
+
api?: number;
|
|
119
|
+
database: number;
|
|
120
|
+
studio?: number;
|
|
121
|
+
}>;
|
|
122
|
+
}>;
|
|
123
|
+
safety: Readonly<{
|
|
124
|
+
allowedHosts: readonly string[];
|
|
125
|
+
blockedEnvironmentVariables: readonly string[];
|
|
126
|
+
authenticationProviders: readonly string[];
|
|
127
|
+
hostedAccess: "disabled";
|
|
128
|
+
outboundNetwork: "deny";
|
|
129
|
+
}>;
|
|
130
|
+
verification: Readonly<{
|
|
131
|
+
commands: readonly string[];
|
|
132
|
+
}>;
|
|
133
|
+
}>;
|
|
134
|
+
configPath: string;
|
|
135
|
+
projectRoot: string;
|
|
136
|
+
paths: Readonly<{
|
|
137
|
+
artifactDirectory: string;
|
|
138
|
+
applicationEnvironment: string;
|
|
139
|
+
migrationDirectory: string;
|
|
140
|
+
rehearsalConfig: string | null;
|
|
141
|
+
runtimeWorkdir: string;
|
|
142
|
+
serviceEnvironment: string | null;
|
|
143
|
+
sanitizationPolicy: string;
|
|
144
|
+
sourcePolicy: string | null;
|
|
145
|
+
privacyKey: string | null;
|
|
146
|
+
runtimePolicy: string | null;
|
|
147
|
+
identityPolicy: string | null;
|
|
148
|
+
supabaseWorkdir: string | null;
|
|
149
|
+
runtimeAdapter: string | null;
|
|
150
|
+
}>;
|
|
151
|
+
}>;
|
|
152
|
+
plan: import("../identity/claim_contract.mjs").IdentityClaimPlan;
|
|
153
|
+
}>;
|
|
154
|
+
loadIdentityPolicy: ({ planOptions }: ProjectCommandInput) => Promise<{
|
|
155
|
+
loaded: Readonly<{
|
|
156
|
+
config: Readonly<{
|
|
157
|
+
schemaVersion: 1;
|
|
158
|
+
project: Readonly<{
|
|
159
|
+
name: string;
|
|
160
|
+
}>;
|
|
161
|
+
supabase: Readonly<{
|
|
162
|
+
workdir: string;
|
|
163
|
+
migrationDirectory: string;
|
|
164
|
+
rehearsalConfig: string;
|
|
165
|
+
runtimeWorkdir: string;
|
|
166
|
+
authentication: Readonly<{
|
|
167
|
+
enableLocalSignup: true;
|
|
168
|
+
environmentFile: string;
|
|
169
|
+
providers: readonly Readonly<{
|
|
170
|
+
name: "github" | "google";
|
|
171
|
+
clientIdEnvironmentVariable: string;
|
|
172
|
+
clientSecretEnvironmentVariable: string;
|
|
173
|
+
skipNonceCheck: boolean;
|
|
174
|
+
emailOptional: boolean;
|
|
175
|
+
}>[];
|
|
176
|
+
}> | null;
|
|
177
|
+
serviceEnvironmentFile: string | null;
|
|
178
|
+
serviceEnvironmentVariables: readonly string[];
|
|
179
|
+
}> | null;
|
|
180
|
+
postgresql: Readonly<{
|
|
181
|
+
migrationDirectory: string;
|
|
182
|
+
runtimeWorkdir: string;
|
|
183
|
+
image: string;
|
|
184
|
+
database: string;
|
|
185
|
+
user: string;
|
|
186
|
+
}> | null;
|
|
187
|
+
baseline: Readonly<{
|
|
188
|
+
artifactDirectory: string;
|
|
189
|
+
sanitizationPolicy: string;
|
|
190
|
+
}>;
|
|
191
|
+
containerRuntime: Readonly<{
|
|
192
|
+
autoStartColima: boolean;
|
|
193
|
+
}>;
|
|
194
|
+
cleanup: Readonly<{
|
|
195
|
+
retainBaselineGenerations: number;
|
|
196
|
+
}>;
|
|
197
|
+
preparation: Readonly<{
|
|
198
|
+
sourcePolicy: string;
|
|
199
|
+
privacyKey: string;
|
|
200
|
+
batchRows: number;
|
|
201
|
+
maximumRows: number;
|
|
202
|
+
maximumBytes: number;
|
|
203
|
+
diskHeadroomBytes: number;
|
|
204
|
+
}> | null;
|
|
205
|
+
runtimePolicy: string | null;
|
|
206
|
+
identityPolicy: string | null;
|
|
207
|
+
dependentTargets: readonly Readonly<{
|
|
208
|
+
name: string;
|
|
209
|
+
configPath: string;
|
|
210
|
+
prepareCommand: string | null;
|
|
211
|
+
}>[];
|
|
212
|
+
application: Readonly<{
|
|
213
|
+
startCommand: string;
|
|
214
|
+
proofCommand: string;
|
|
215
|
+
environmentFile: string;
|
|
216
|
+
environmentVariables: Readonly<Record<string, string>>;
|
|
217
|
+
readiness: Readonly<{
|
|
218
|
+
url: string;
|
|
219
|
+
expectedStatus: number;
|
|
220
|
+
timeoutSeconds: number;
|
|
221
|
+
}> | null;
|
|
222
|
+
httpProofs: readonly Readonly<{
|
|
223
|
+
name: string;
|
|
224
|
+
kind: "positive" | "negative";
|
|
225
|
+
url: string;
|
|
226
|
+
method: string;
|
|
227
|
+
expectedStatus: number;
|
|
228
|
+
json: Readonly<{
|
|
229
|
+
path: readonly string[];
|
|
230
|
+
equals?: unknown;
|
|
231
|
+
notEquals?: unknown;
|
|
232
|
+
minimumItems?: number;
|
|
233
|
+
}> | null;
|
|
234
|
+
}>[];
|
|
235
|
+
runtimeAdapter: string | null;
|
|
236
|
+
}>;
|
|
237
|
+
runtime: Readonly<{
|
|
238
|
+
target: "postgresql" | "supabase";
|
|
239
|
+
applicationUrl: string;
|
|
240
|
+
projectId: string;
|
|
241
|
+
ports: Readonly<{
|
|
242
|
+
api?: number;
|
|
243
|
+
database: number;
|
|
244
|
+
studio?: number;
|
|
245
|
+
}>;
|
|
246
|
+
}>;
|
|
247
|
+
safety: Readonly<{
|
|
248
|
+
allowedHosts: readonly string[];
|
|
249
|
+
blockedEnvironmentVariables: readonly string[];
|
|
250
|
+
authenticationProviders: readonly string[];
|
|
251
|
+
hostedAccess: "disabled";
|
|
252
|
+
outboundNetwork: "deny";
|
|
253
|
+
}>;
|
|
254
|
+
verification: Readonly<{
|
|
255
|
+
commands: readonly string[];
|
|
256
|
+
}>;
|
|
257
|
+
}>;
|
|
258
|
+
configPath: string;
|
|
259
|
+
projectRoot: string;
|
|
260
|
+
paths: Readonly<{
|
|
261
|
+
artifactDirectory: string;
|
|
262
|
+
applicationEnvironment: string;
|
|
263
|
+
migrationDirectory: string;
|
|
264
|
+
rehearsalConfig: string | null;
|
|
265
|
+
runtimeWorkdir: string;
|
|
266
|
+
serviceEnvironment: string | null;
|
|
267
|
+
sanitizationPolicy: string;
|
|
268
|
+
sourcePolicy: string | null;
|
|
269
|
+
privacyKey: string | null;
|
|
270
|
+
runtimePolicy: string | null;
|
|
271
|
+
identityPolicy: string | null;
|
|
272
|
+
supabaseWorkdir: string | null;
|
|
273
|
+
runtimeAdapter: string | null;
|
|
274
|
+
}>;
|
|
275
|
+
}>;
|
|
276
|
+
policy: import("../identity/claim_contract.mjs").IdentityPolicy;
|
|
277
|
+
}>;
|
|
278
|
+
planRuntimeStackCleanup: ({ flags, planOptions, }: ProjectCommandInput) => Promise<RehearsalCleanupPlan | CleanupStackPlan>;
|
|
279
|
+
runBaselinePreparation: ({ flags, planOptions, session, }: ProjectCommandInput) => Promise<ReturnType<typeof summarizeBaselinePreparation>>;
|
|
280
|
+
runCleanup: ({ flags, planOptions, session, }: ProjectCommandInput) => Promise<unknown>;
|
|
281
|
+
runSetup: ({ flags, planOptions, session, }: ProjectCommandInput) => Promise<unknown>;
|
|
282
|
+
}>;
|
|
283
|
+
export {};
|
|
@@ -0,0 +1,192 @@
|
|
|
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 loadIdentityPolicy = async ({ planOptions }) => {
|
|
160
|
+
const loaded = await loadRehearsalConfig(planOptions);
|
|
161
|
+
if (!loaded.paths.identityPolicy) {
|
|
162
|
+
throw new Error("No config.identityPolicy is configured.");
|
|
163
|
+
}
|
|
164
|
+
const policy = validateIdentityPolicy(JSON.parse(await readFile(loaded.paths.identityPolicy, "utf8")));
|
|
165
|
+
return { loaded, policy };
|
|
166
|
+
};
|
|
167
|
+
const loadIdentityClaimPlan = async ({ flags, planOptions, session, }) => {
|
|
168
|
+
const { loaded, policy } = await loadIdentityPolicy({
|
|
169
|
+
flags,
|
|
170
|
+
planOptions,
|
|
171
|
+
...(session === undefined ? {} : { session }),
|
|
172
|
+
});
|
|
173
|
+
if (!flags.identityName) {
|
|
174
|
+
throw new Error("Choose a declared identity with --identity=<name>.");
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
loaded,
|
|
178
|
+
plan: createIdentityClaimPlan({
|
|
179
|
+
policy,
|
|
180
|
+
name: flags.identityName,
|
|
181
|
+
}),
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
return Object.freeze({
|
|
185
|
+
loadIdentityClaimPlan,
|
|
186
|
+
loadIdentityPolicy,
|
|
187
|
+
planRuntimeStackCleanup,
|
|
188
|
+
runBaselinePreparation,
|
|
189
|
+
runCleanup,
|
|
190
|
+
runSetup,
|
|
191
|
+
});
|
|
192
|
+
};
|
|
@@ -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
|
+
}
|