@rehearsal-db/core 0.1.0-beta.13 → 0.1.0-beta.15
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 +93 -0
- package/README.md +4 -1
- 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/session.d.mts +96 -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_engine.d.mts +162 -0
- package/dist/src/baseline/privacy_engine.mjs +1162 -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/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/arguments.d.mts +43 -0
- package/dist/src/cli/arguments.mjs +135 -0
- package/dist/src/cli/guided.d.mts +51 -0
- package/dist/src/cli/guided.mjs +713 -0
- package/dist/src/cli/init_command.d.mts +21 -0
- package/dist/src/cli/init_command.mjs +90 -0
- package/dist/src/cli/project_commands.d.mts +157 -0
- package/dist/src/cli/project_commands.mjs +183 -0
- package/dist/src/cli/rehearsal.d.mts +7 -0
- package/dist/src/cli/rehearsal.mjs +706 -0
- package/dist/src/cli/renderers.d.mts +299 -0
- package/dist/src/cli/renderers.mjs +554 -0
- package/dist/src/cli/runtime_commands.d.mts +265 -0
- package/dist/src/cli/runtime_commands.mjs +599 -0
- package/dist/src/cli/source_commands.d.mts +432 -0
- package/dist/src/cli/source_commands.mjs +299 -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 +144 -0
- package/dist/src/identity/claim.mjs +947 -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 +181 -0
- package/dist/src/project/configuration.mjs +958 -0
- package/dist/src/project/install_scaffold.d.mts +42 -0
- package/dist/src/project/install_scaffold.mjs +263 -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/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 +636 -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 +641 -0
- package/dist/src/targets/supabase.d.mts +8 -0
- package/dist/src/targets/supabase.mjs +659 -0
- package/dist/src/targets/supabase_environment.d.mts +49 -0
- package/dist/src/targets/supabase_environment.mjs +162 -0
- package/dist/src/targets/target.d.mts +33 -0
- package/dist/src/targets/target.mjs +39 -0
- package/docs/README.md +1 -0
- package/docs/architecture.md +17 -9
- package/docs/configuration.md +49 -8
- package/docs/getting-started.md +28 -0
- package/docs/index.md +1 -0
- package/docs/production-source.md +43 -7
- package/docs/releasing.md +5 -3
- package/docs/roadmap.md +3 -2
- package/docs/sanitization.md +415 -14
- package/docs/standalone-workflow.md +12 -6
- package/docs/troubleshooting.md +5 -1
- package/docs/tutorial.md +5 -0
- package/docs/typescript.md +87 -0
- package/package.json +35 -29
- package/scripts/install/create_root_config.mjs +0 -36
- package/src/README.md +0 -17
- package/src/application/session.mjs +0 -435
- package/src/baseline/artifact.mjs +0 -684
- 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 -952
- package/src/baseline/sanitization_policy.mjs +0 -299
- package/src/baseline/schema_snapshot.mjs +0 -113
- package/src/cli/arguments.mjs +0 -126
- package/src/cli/guided.mjs +0 -814
- package/src/cli/init_command.mjs +0 -94
- package/src/cli/rehearsal.mjs +0 -970
- package/src/cli/renderers.mjs +0 -626
- package/src/cli/runtime_commands.mjs +0 -707
- package/src/cli/source_commands.mjs +0 -347
- package/src/cli/terminal.mjs +0 -275
- package/src/identity/claim.mjs +0 -1163
- package/src/identity/provider_configuration.mjs +0 -177
- package/src/identity/storage.mjs +0 -165
- package/src/project/configuration.mjs +0 -1337
- package/src/project/install_scaffold.mjs +0 -291
- package/src/project/setup.mjs +0 -466
- 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 -722
- package/src/runtime/policy.mjs +0 -438
- package/src/runtime/restore.mjs +0 -337
- package/src/runtime/service_environment.mjs +0 -82
- package/src/runtime/topology.mjs +0 -177
- package/src/shared/diagnostics.mjs +0 -207
- package/src/shared/node_runtime.mjs +0 -31
- package/src/shared/operation_guard.mjs +0 -162
- package/src/shared/process_environment.mjs +0 -68
- package/src/source/access.mjs +0 -753
- package/src/source/asset_transfer.mjs +0 -207
- package/src/source/baseline.mjs +0 -482
- package/src/source/external_reader.mjs +0 -255
- package/src/source/postgresql_access.mjs +0 -581
- package/src/source/privacy_paths.mjs +0 -38
- package/src/targets/postgresql.mjs +0 -808
- package/src/targets/supabase.mjs +0 -840
- package/src/targets/supabase_environment.mjs +0 -229
- package/src/targets/target.mjs +0 -66
- /package/{src/project → types}/configuration.d.mts +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Preview or create the project-root Rehearsal configuration. */
|
|
2
|
+
interface InitFlags {
|
|
3
|
+
readonly configPath: string | undefined;
|
|
4
|
+
readonly write: boolean;
|
|
5
|
+
}
|
|
6
|
+
interface InitOptions {
|
|
7
|
+
readonly projectRoot: string;
|
|
8
|
+
readonly flags: InitFlags;
|
|
9
|
+
}
|
|
10
|
+
export declare const runRehearsalInit: ({ projectRoot, flags }: InitOptions) => Promise<{
|
|
11
|
+
mode: string;
|
|
12
|
+
destination: string;
|
|
13
|
+
detected: import("../project/configuration.mjs").DetectedProject;
|
|
14
|
+
source: string;
|
|
15
|
+
availableOptions: {
|
|
16
|
+
path: string;
|
|
17
|
+
summary: string;
|
|
18
|
+
}[];
|
|
19
|
+
nextAction: string;
|
|
20
|
+
}>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/** Preview or create the project-root Rehearsal configuration. */
|
|
2
|
+
import { writeFile } from "node:fs/promises";
|
|
3
|
+
import { join, relative } from "node:path";
|
|
4
|
+
import { findRehearsalConfigPath, inspectDetectedProject, loadRehearsalConfig, renderDetectedConfig, renderDetectedPostgresqlConfig, } from "../project/configuration.mjs";
|
|
5
|
+
const findExistingConfig = async ({ projectRoot, configPath, }) => {
|
|
6
|
+
try {
|
|
7
|
+
return await findRehearsalConfigPath({
|
|
8
|
+
projectRoot,
|
|
9
|
+
...(configPath === undefined ? {} : { configPath }),
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (String(error instanceof Error ? error.message : error).startsWith("No Rehearsal configuration")) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export const runRehearsalInit = async ({ projectRoot, flags }) => {
|
|
20
|
+
const detected = await inspectDetectedProject({ projectRoot });
|
|
21
|
+
const destination = join(projectRoot, "rehearsal.config.mjs");
|
|
22
|
+
const existingPath = await findExistingConfig({
|
|
23
|
+
projectRoot,
|
|
24
|
+
configPath: flags.configPath,
|
|
25
|
+
});
|
|
26
|
+
if (existingPath) {
|
|
27
|
+
if (flags.write) {
|
|
28
|
+
throw new Error(`A Rehearsal configuration already exists at ${relative(projectRoot, existingPath)}; Rehearsal will not overwrite it.`);
|
|
29
|
+
}
|
|
30
|
+
const loaded = await loadRehearsalConfig({
|
|
31
|
+
projectRoot,
|
|
32
|
+
...(flags.configPath === undefined
|
|
33
|
+
? {}
|
|
34
|
+
: { configPath: flags.configPath }),
|
|
35
|
+
});
|
|
36
|
+
let source;
|
|
37
|
+
if (loaded.config.supabase) {
|
|
38
|
+
const { api, database, studio } = loaded.config.runtime.ports;
|
|
39
|
+
if (api === undefined || studio === undefined) {
|
|
40
|
+
throw new Error("The normalized Supabase configuration is missing its API or Studio port.");
|
|
41
|
+
}
|
|
42
|
+
source = renderDetectedConfig(detected, {
|
|
43
|
+
applicationUrl: loaded.config.runtime.applicationUrl,
|
|
44
|
+
ports: { api, database, studio },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
source = renderDetectedPostgresqlConfig(detected, {
|
|
49
|
+
applicationUrl: loaded.config.runtime.applicationUrl,
|
|
50
|
+
databasePort: loaded.config.runtime.ports.database,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const usesLegacyAuthentication = Boolean(loaded.config.supabase?.serviceEnvironmentFile ||
|
|
54
|
+
loaded.config.safety.authenticationProviders.length);
|
|
55
|
+
const availableOptions = loaded.config.supabase && !loaded.config.supabase.authentication
|
|
56
|
+
? [
|
|
57
|
+
{
|
|
58
|
+
path: "supabase.authentication",
|
|
59
|
+
summary: usesLegacyAuthentication
|
|
60
|
+
? "A declarative Google/GitHub replacement is available; replace the legacy provider fields together after review."
|
|
61
|
+
: "Configure local Google or GitHub sign-in with credentials kept outside tracked files.",
|
|
62
|
+
},
|
|
63
|
+
]
|
|
64
|
+
: [];
|
|
65
|
+
return {
|
|
66
|
+
mode: "current-template",
|
|
67
|
+
destination: relative(projectRoot, existingPath),
|
|
68
|
+
detected,
|
|
69
|
+
source,
|
|
70
|
+
availableOptions,
|
|
71
|
+
nextAction: "Your existing configuration was not changed. Compare it with this installed-release template to discover new optional keys.",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const source = detected.hasSupabaseConfig
|
|
75
|
+
? renderDetectedConfig(detected)
|
|
76
|
+
: renderDetectedPostgresqlConfig(detected);
|
|
77
|
+
if (flags.write) {
|
|
78
|
+
await writeFile(destination, source, { flag: "wx", mode: 0o600 });
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
mode: flags.write ? "written" : "preview",
|
|
82
|
+
destination: relative(projectRoot, destination),
|
|
83
|
+
detected,
|
|
84
|
+
source,
|
|
85
|
+
availableOptions: [],
|
|
86
|
+
nextAction: flags.write
|
|
87
|
+
? "Review the generated safety settings and run rehearsal doctor."
|
|
88
|
+
: "Review this preview, then rerun rehearsal init --write to create it.",
|
|
89
|
+
};
|
|
90
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
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
|
+
type CleanupStackPlan = Readonly<{
|
|
7
|
+
digest: string;
|
|
8
|
+
targets: readonly Readonly<{
|
|
9
|
+
name: string;
|
|
10
|
+
configPath: string;
|
|
11
|
+
plan: RehearsalCleanupPlan;
|
|
12
|
+
}>[];
|
|
13
|
+
}>;
|
|
14
|
+
type RunManager = (input: {
|
|
15
|
+
action: "discard";
|
|
16
|
+
flags: RehearsalCliFlags;
|
|
17
|
+
configPath?: string;
|
|
18
|
+
targetName?: string;
|
|
19
|
+
}) => unknown;
|
|
20
|
+
interface ProjectCommandInput {
|
|
21
|
+
readonly flags: RehearsalCliFlags;
|
|
22
|
+
readonly planOptions: RehearsalConfigPathOptions;
|
|
23
|
+
}
|
|
24
|
+
export declare const createProjectCommands: ({ projectRoot, runManager, }: {
|
|
25
|
+
projectRoot: string;
|
|
26
|
+
runManager: RunManager;
|
|
27
|
+
}) => Readonly<{
|
|
28
|
+
loadIdentityClaimPlan: ({ flags, planOptions, }: ProjectCommandInput) => Promise<{
|
|
29
|
+
loaded: Readonly<{
|
|
30
|
+
config: Readonly<{
|
|
31
|
+
schemaVersion: 1;
|
|
32
|
+
project: Readonly<{
|
|
33
|
+
name: string;
|
|
34
|
+
}>;
|
|
35
|
+
supabase: Readonly<{
|
|
36
|
+
workdir: string;
|
|
37
|
+
migrationDirectory: string;
|
|
38
|
+
rehearsalConfig: string;
|
|
39
|
+
runtimeWorkdir: string;
|
|
40
|
+
authentication: Readonly<{
|
|
41
|
+
enableLocalSignup: true;
|
|
42
|
+
environmentFile: string;
|
|
43
|
+
providers: readonly Readonly<{
|
|
44
|
+
name: "github" | "google";
|
|
45
|
+
clientIdEnvironmentVariable: string;
|
|
46
|
+
clientSecretEnvironmentVariable: string;
|
|
47
|
+
skipNonceCheck: boolean;
|
|
48
|
+
emailOptional: boolean;
|
|
49
|
+
}>[];
|
|
50
|
+
}> | null;
|
|
51
|
+
serviceEnvironmentFile: string | null;
|
|
52
|
+
serviceEnvironmentVariables: readonly string[];
|
|
53
|
+
}> | null;
|
|
54
|
+
postgresql: Readonly<{
|
|
55
|
+
migrationDirectory: string;
|
|
56
|
+
runtimeWorkdir: string;
|
|
57
|
+
image: string;
|
|
58
|
+
database: string;
|
|
59
|
+
user: string;
|
|
60
|
+
}> | null;
|
|
61
|
+
baseline: Readonly<{
|
|
62
|
+
artifactDirectory: string;
|
|
63
|
+
sanitizationPolicy: string;
|
|
64
|
+
}>;
|
|
65
|
+
containerRuntime: Readonly<{
|
|
66
|
+
autoStartColima: boolean;
|
|
67
|
+
}>;
|
|
68
|
+
cleanup: Readonly<{
|
|
69
|
+
retainBaselineGenerations: number;
|
|
70
|
+
}>;
|
|
71
|
+
preparation: Readonly<{
|
|
72
|
+
sourcePolicy: string;
|
|
73
|
+
privacyKey: string;
|
|
74
|
+
batchRows: number;
|
|
75
|
+
maximumRows: number;
|
|
76
|
+
maximumBytes: number;
|
|
77
|
+
diskHeadroomBytes: number;
|
|
78
|
+
}> | null;
|
|
79
|
+
runtimePolicy: string | null;
|
|
80
|
+
identityPolicy: string | null;
|
|
81
|
+
dependentTargets: readonly Readonly<{
|
|
82
|
+
name: string;
|
|
83
|
+
configPath: string;
|
|
84
|
+
prepareCommand: string | null;
|
|
85
|
+
}>[];
|
|
86
|
+
application: Readonly<{
|
|
87
|
+
startCommand: string;
|
|
88
|
+
proofCommand: string;
|
|
89
|
+
environmentFile: string;
|
|
90
|
+
environmentVariables: Readonly<Record<string, string>>;
|
|
91
|
+
readiness: Readonly<{
|
|
92
|
+
url: string;
|
|
93
|
+
expectedStatus: number;
|
|
94
|
+
timeoutSeconds: number;
|
|
95
|
+
}> | null;
|
|
96
|
+
httpProofs: readonly Readonly<{
|
|
97
|
+
name: string;
|
|
98
|
+
kind: "positive" | "negative";
|
|
99
|
+
url: string;
|
|
100
|
+
method: string;
|
|
101
|
+
expectedStatus: number;
|
|
102
|
+
json: Readonly<{
|
|
103
|
+
path: readonly string[];
|
|
104
|
+
equals?: unknown;
|
|
105
|
+
notEquals?: unknown;
|
|
106
|
+
minimumItems?: number;
|
|
107
|
+
}> | null;
|
|
108
|
+
}>[];
|
|
109
|
+
runtimeAdapter: string | null;
|
|
110
|
+
}>;
|
|
111
|
+
runtime: Readonly<{
|
|
112
|
+
target: "postgresql" | "supabase";
|
|
113
|
+
applicationUrl: string;
|
|
114
|
+
projectId: string;
|
|
115
|
+
ports: Readonly<{
|
|
116
|
+
api?: number;
|
|
117
|
+
database: number;
|
|
118
|
+
studio?: number;
|
|
119
|
+
}>;
|
|
120
|
+
}>;
|
|
121
|
+
safety: Readonly<{
|
|
122
|
+
allowedHosts: readonly string[];
|
|
123
|
+
blockedEnvironmentVariables: readonly string[];
|
|
124
|
+
authenticationProviders: readonly string[];
|
|
125
|
+
hostedAccess: "disabled";
|
|
126
|
+
outboundNetwork: "deny";
|
|
127
|
+
}>;
|
|
128
|
+
verification: Readonly<{
|
|
129
|
+
commands: readonly string[];
|
|
130
|
+
}>;
|
|
131
|
+
}>;
|
|
132
|
+
configPath: string;
|
|
133
|
+
projectRoot: string;
|
|
134
|
+
paths: Readonly<{
|
|
135
|
+
artifactDirectory: string;
|
|
136
|
+
applicationEnvironment: string;
|
|
137
|
+
migrationDirectory: string;
|
|
138
|
+
rehearsalConfig: string | null;
|
|
139
|
+
runtimeWorkdir: string;
|
|
140
|
+
serviceEnvironment: string | null;
|
|
141
|
+
sanitizationPolicy: string;
|
|
142
|
+
sourcePolicy: string | null;
|
|
143
|
+
privacyKey: string | null;
|
|
144
|
+
runtimePolicy: string | null;
|
|
145
|
+
identityPolicy: string | null;
|
|
146
|
+
supabaseWorkdir: string | null;
|
|
147
|
+
runtimeAdapter: string | null;
|
|
148
|
+
}>;
|
|
149
|
+
}>;
|
|
150
|
+
plan: import("../identity/claim.mjs").IdentityClaimPlan;
|
|
151
|
+
}>;
|
|
152
|
+
planRuntimeStackCleanup: ({ flags, planOptions, }: ProjectCommandInput) => Promise<RehearsalCleanupPlan | CleanupStackPlan>;
|
|
153
|
+
runBaselinePreparation: ({ flags, planOptions, }: ProjectCommandInput) => Promise<ReturnType<typeof summarizeBaselinePreparation>>;
|
|
154
|
+
runCleanup: ({ flags, planOptions, }: ProjectCommandInput) => Promise<unknown>;
|
|
155
|
+
runSetup: ({ flags, planOptions, }: ProjectCommandInput) => Promise<unknown>;
|
|
156
|
+
}>;
|
|
157
|
+
export {};
|
|
@@ -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, }) => {
|
|
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 = flags.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, }) => {
|
|
53
|
+
if (!flags.recordsPath || !flags.ledgerPath) {
|
|
54
|
+
throw new Error("baseline prepare requires --records and --ledger input paths.");
|
|
55
|
+
}
|
|
56
|
+
const plan = flags.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, }) => {
|
|
102
|
+
const plan = flags.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: Run the versioned, project-configured Rehearsal developer CLI for
|
|
4
|
+
* initialization, readiness, planning, inspection, and verified local execution.
|
|
5
|
+
* Run: `npm run rehearsal -- doctor` or `npm run rehearsal -- explain`.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|