@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,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Load and strictly validate the versioned, project-owned Rehearsal
|
|
3
|
+
* configuration contract. Do not run directly; this module is reusable script
|
|
4
|
+
* infrastructure.
|
|
5
|
+
*/
|
|
6
|
+
export declare const normalizeRehearsalConfig: (input: unknown) => Readonly<{
|
|
7
|
+
schemaVersion: 1;
|
|
8
|
+
project: Readonly<{
|
|
9
|
+
name: string;
|
|
10
|
+
}>;
|
|
11
|
+
supabase: Readonly<{
|
|
12
|
+
workdir: string;
|
|
13
|
+
migrationDirectory: string;
|
|
14
|
+
rehearsalConfig: string;
|
|
15
|
+
runtimeWorkdir: string;
|
|
16
|
+
authentication: Readonly<{
|
|
17
|
+
enableLocalSignup: true;
|
|
18
|
+
environmentFile: string;
|
|
19
|
+
providers: readonly Readonly<{
|
|
20
|
+
name: "github" | "google";
|
|
21
|
+
clientIdEnvironmentVariable: string;
|
|
22
|
+
clientSecretEnvironmentVariable: string;
|
|
23
|
+
skipNonceCheck: boolean;
|
|
24
|
+
emailOptional: boolean;
|
|
25
|
+
}>[];
|
|
26
|
+
}> | null;
|
|
27
|
+
serviceEnvironmentFile: string | null;
|
|
28
|
+
serviceEnvironmentVariables: readonly string[];
|
|
29
|
+
}> | null;
|
|
30
|
+
postgresql: Readonly<{
|
|
31
|
+
migrationDirectory: string;
|
|
32
|
+
runtimeWorkdir: string;
|
|
33
|
+
image: string;
|
|
34
|
+
database: string;
|
|
35
|
+
user: string;
|
|
36
|
+
}> | null;
|
|
37
|
+
baseline: Readonly<{
|
|
38
|
+
artifactDirectory: string;
|
|
39
|
+
sanitizationPolicy: string;
|
|
40
|
+
}>;
|
|
41
|
+
containerRuntime: Readonly<{
|
|
42
|
+
autoStartColima: boolean;
|
|
43
|
+
}>;
|
|
44
|
+
cleanup: Readonly<{
|
|
45
|
+
retainBaselineGenerations: number;
|
|
46
|
+
}>;
|
|
47
|
+
preparation: Readonly<{
|
|
48
|
+
sourcePolicy: string;
|
|
49
|
+
privacyKey: string;
|
|
50
|
+
batchRows: number;
|
|
51
|
+
maximumRows: number;
|
|
52
|
+
maximumBytes: number;
|
|
53
|
+
diskHeadroomBytes: number;
|
|
54
|
+
}> | null;
|
|
55
|
+
runtimePolicy: string | null;
|
|
56
|
+
identityPolicy: string | null;
|
|
57
|
+
dependentTargets: readonly Readonly<{
|
|
58
|
+
name: string;
|
|
59
|
+
configPath: string;
|
|
60
|
+
prepareCommand: string | null;
|
|
61
|
+
}>[];
|
|
62
|
+
application: Readonly<{
|
|
63
|
+
startCommand: string;
|
|
64
|
+
proofCommand: string;
|
|
65
|
+
environmentFile: string;
|
|
66
|
+
environmentVariables: Readonly<Record<string, string>>;
|
|
67
|
+
readiness: Readonly<{
|
|
68
|
+
url: string;
|
|
69
|
+
expectedStatus: number;
|
|
70
|
+
timeoutSeconds: number;
|
|
71
|
+
}> | null;
|
|
72
|
+
httpProofs: readonly Readonly<{
|
|
73
|
+
name: string;
|
|
74
|
+
kind: "positive" | "negative";
|
|
75
|
+
url: string;
|
|
76
|
+
method: string;
|
|
77
|
+
expectedStatus: number;
|
|
78
|
+
json: Readonly<{
|
|
79
|
+
path: readonly string[];
|
|
80
|
+
equals?: unknown;
|
|
81
|
+
notEquals?: unknown;
|
|
82
|
+
minimumItems?: number;
|
|
83
|
+
}> | null;
|
|
84
|
+
}>[];
|
|
85
|
+
runtimeAdapter: string | null;
|
|
86
|
+
}>;
|
|
87
|
+
runtime: Readonly<{
|
|
88
|
+
target: "postgresql" | "supabase";
|
|
89
|
+
applicationUrl: string;
|
|
90
|
+
projectId: string;
|
|
91
|
+
ports: Readonly<{
|
|
92
|
+
api?: number;
|
|
93
|
+
database: number;
|
|
94
|
+
studio?: number;
|
|
95
|
+
}>;
|
|
96
|
+
}>;
|
|
97
|
+
safety: Readonly<{
|
|
98
|
+
allowedHosts: readonly string[];
|
|
99
|
+
blockedEnvironmentVariables: readonly string[];
|
|
100
|
+
authenticationProviders: readonly string[];
|
|
101
|
+
hostedAccess: "disabled";
|
|
102
|
+
outboundNetwork: "deny";
|
|
103
|
+
}>;
|
|
104
|
+
verification: Readonly<{
|
|
105
|
+
commands: readonly string[];
|
|
106
|
+
}>;
|
|
107
|
+
}>;
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Load and strictly validate the versioned, project-owned Rehearsal
|
|
3
|
+
* configuration contract. Do not run directly; this module is reusable script
|
|
4
|
+
* infrastructure.
|
|
5
|
+
*/
|
|
6
|
+
import { REHEARSAL_CONFIG_VERSION, REHEARSAL_DEFAULTS, } from "./configuration_contract.mjs";
|
|
7
|
+
import { validateConfigurationSchema } from "./configuration_schema.mjs";
|
|
8
|
+
import { assertBoolean, assertCommand, assertDatabaseIdentifier, assertEnvironmentKey, assertEnvironmentReference, assertHttpStatus, assertIdentifier, assertKnownKeys, assertLoopbackUrl, assertNonEmptyString, assertPlainObject, assertPort, assertPostgresImage, assertPositiveInteger, assertRelativePath, assertStringArray, } from "./configuration_validation.mjs";
|
|
9
|
+
const LOOPBACK_HOSTS = new Set(REHEARSAL_DEFAULTS.safety.allowedHosts);
|
|
10
|
+
const SUPPORTED_SUPABASE_OAUTH_PROVIDERS = new Set(["github", "google"]);
|
|
11
|
+
const normalizeApplicationEnvironmentMappings = (value = {}) => {
|
|
12
|
+
const mappings = assertPlainObject(value, "config.application.environmentVariables");
|
|
13
|
+
return Object.freeze(Object.fromEntries(Object.entries(mappings).map(([key, reference]) => {
|
|
14
|
+
const path = `config.application.environmentVariables.${key}`;
|
|
15
|
+
assertEnvironmentKey(key, path);
|
|
16
|
+
return [key, assertEnvironmentReference(reference, path)];
|
|
17
|
+
})));
|
|
18
|
+
};
|
|
19
|
+
export const normalizeRehearsalConfig = (input) => {
|
|
20
|
+
const { root, project, runtime, runtimeTarget, supabase, postgresql, baseline, containerRuntime, cleanup, preparation, dependentTargets, application, readiness, httpProofs, safety, verification, authentication, } = validateConfigurationSchema(input);
|
|
21
|
+
const projectName = assertIdentifier(project.name, "config.project.name");
|
|
22
|
+
const normalizedDependentTargets = dependentTargets.map((target, index) => {
|
|
23
|
+
const path = `config.dependentTargets[${index}]`;
|
|
24
|
+
const name = assertIdentifier(target.name, `${path}.name`);
|
|
25
|
+
return Object.freeze({
|
|
26
|
+
name,
|
|
27
|
+
configPath: assertRelativePath(target.configPath, `${path}.configPath`),
|
|
28
|
+
prepareCommand: target.prepareCommand === undefined
|
|
29
|
+
? null
|
|
30
|
+
: assertCommand(target.prepareCommand, `${path}.prepareCommand`),
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
if (new Set(normalizedDependentTargets.map((target) => target.name)).size !==
|
|
34
|
+
normalizedDependentTargets.length) {
|
|
35
|
+
throw new Error("config.dependentTargets names must be unique.");
|
|
36
|
+
}
|
|
37
|
+
if (httpProofs.length > 0 &&
|
|
38
|
+
(!httpProofs.some((proof) => proof.kind === "positive") ||
|
|
39
|
+
!httpProofs.some((proof) => proof.kind === "negative"))) {
|
|
40
|
+
throw new Error("config.application.httpProofs must include at least one positive and one negative check.");
|
|
41
|
+
}
|
|
42
|
+
const allowedHosts = assertStringArray(safety.allowedHosts ?? REHEARSAL_DEFAULTS.safety.allowedHosts, "config.safety.allowedHosts");
|
|
43
|
+
if (allowedHosts.some((host) => !LOOPBACK_HOSTS.has(host))) {
|
|
44
|
+
throw new Error("config.safety.allowedHosts may contain loopback hosts only in configuration version 1.");
|
|
45
|
+
}
|
|
46
|
+
if ((safety.hostedAccess ?? "disabled") !== "disabled") {
|
|
47
|
+
throw new Error("config.safety.hostedAccess must remain disabled in configuration version 1.");
|
|
48
|
+
}
|
|
49
|
+
if ((safety.outboundNetwork ?? "deny") !== "deny") {
|
|
50
|
+
throw new Error("config.safety.outboundNetwork must remain deny in configuration version 1.");
|
|
51
|
+
}
|
|
52
|
+
const ports = {
|
|
53
|
+
...(runtimeTarget === "supabase"
|
|
54
|
+
? { api: assertPort(runtime.apiPort, "config.runtime.apiPort") }
|
|
55
|
+
: {}),
|
|
56
|
+
database: assertPort(runtime.databasePort, "config.runtime.databasePort"),
|
|
57
|
+
...(runtimeTarget === "supabase"
|
|
58
|
+
? { studio: assertPort(runtime.studioPort, "config.runtime.studioPort") }
|
|
59
|
+
: {}),
|
|
60
|
+
};
|
|
61
|
+
if (new Set(Object.values(ports)).size !== Object.values(ports).length) {
|
|
62
|
+
throw new Error("Rehearsal runtime ports must be unique.");
|
|
63
|
+
}
|
|
64
|
+
const commands = verification.commands ?? [];
|
|
65
|
+
if (!Array.isArray(commands)) {
|
|
66
|
+
throw new Error("config.verification.commands must be an array.");
|
|
67
|
+
}
|
|
68
|
+
let normalizedAuthentication = null;
|
|
69
|
+
if (authentication) {
|
|
70
|
+
if (!supabase) {
|
|
71
|
+
throw new Error("config.supabase.authentication requires the Supabase runtime target.");
|
|
72
|
+
}
|
|
73
|
+
if (authentication.enableLocalSignup !== true) {
|
|
74
|
+
throw new Error("config.supabase.authentication.enableLocalSignup must be true to create the disposable local identity used by an OAuth sign-in.");
|
|
75
|
+
}
|
|
76
|
+
if (supabase.serviceEnvironmentFile !== undefined ||
|
|
77
|
+
supabase.serviceEnvironmentVariables !== undefined) {
|
|
78
|
+
throw new Error("config.supabase.authentication cannot be combined with the legacy serviceEnvironmentFile or serviceEnvironmentVariables fields.");
|
|
79
|
+
}
|
|
80
|
+
if (safety.authenticationProviders !== undefined) {
|
|
81
|
+
throw new Error("config.safety.authenticationProviders is derived from config.supabase.authentication and must be omitted.");
|
|
82
|
+
}
|
|
83
|
+
if (!Array.isArray(authentication.providers) ||
|
|
84
|
+
authentication.providers.length === 0) {
|
|
85
|
+
throw new Error("config.supabase.authentication.providers must be a non-empty array.");
|
|
86
|
+
}
|
|
87
|
+
const providers = authentication.providers.map((entry, index) => {
|
|
88
|
+
const path = `config.supabase.authentication.providers[${index}]`;
|
|
89
|
+
const value = entry;
|
|
90
|
+
const reviewedName = assertNonEmptyString(value.name, `${path}.name`);
|
|
91
|
+
if (!SUPPORTED_SUPABASE_OAUTH_PROVIDERS.has(reviewedName)) {
|
|
92
|
+
throw new Error(`${path}.name must be one of: ${[...SUPPORTED_SUPABASE_OAUTH_PROVIDERS].join(", ")}.`);
|
|
93
|
+
}
|
|
94
|
+
const name = reviewedName;
|
|
95
|
+
const environmentVariable = (key) => {
|
|
96
|
+
return assertEnvironmentKey(value[key], `${path}.${key}`);
|
|
97
|
+
};
|
|
98
|
+
const optionalBoolean = (key) => {
|
|
99
|
+
if (value[key] === undefined)
|
|
100
|
+
return false;
|
|
101
|
+
if (typeof value[key] !== "boolean") {
|
|
102
|
+
throw new Error(`${path}.${key} must be a boolean.`);
|
|
103
|
+
}
|
|
104
|
+
return value[key];
|
|
105
|
+
};
|
|
106
|
+
return Object.freeze({
|
|
107
|
+
name,
|
|
108
|
+
clientIdEnvironmentVariable: environmentVariable("clientIdEnvironmentVariable"),
|
|
109
|
+
clientSecretEnvironmentVariable: environmentVariable("clientSecretEnvironmentVariable"),
|
|
110
|
+
skipNonceCheck: optionalBoolean("skipNonceCheck"),
|
|
111
|
+
emailOptional: optionalBoolean("emailOptional"),
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
if (new Set(providers.map(({ name }) => name)).size !==
|
|
115
|
+
providers.length) {
|
|
116
|
+
throw new Error("config.supabase.authentication.providers must not contain duplicate providers.");
|
|
117
|
+
}
|
|
118
|
+
const credentialVariables = providers.flatMap((provider) => [
|
|
119
|
+
provider.clientIdEnvironmentVariable,
|
|
120
|
+
provider.clientSecretEnvironmentVariable,
|
|
121
|
+
]);
|
|
122
|
+
if (new Set(credentialVariables).size !== credentialVariables.length) {
|
|
123
|
+
throw new Error("config.supabase.authentication provider environment variables must be unique.");
|
|
124
|
+
}
|
|
125
|
+
normalizedAuthentication = Object.freeze({
|
|
126
|
+
enableLocalSignup: true,
|
|
127
|
+
environmentFile: assertRelativePath(authentication.environmentFile, "config.supabase.authentication.environmentFile"),
|
|
128
|
+
providers: Object.freeze(providers),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const serviceEnvironmentVariables = normalizedAuthentication
|
|
132
|
+
? normalizedAuthentication.providers.flatMap((provider) => [
|
|
133
|
+
provider.clientIdEnvironmentVariable,
|
|
134
|
+
provider.clientSecretEnvironmentVariable,
|
|
135
|
+
])
|
|
136
|
+
: (supabase?.serviceEnvironmentVariables ?? []);
|
|
137
|
+
if (!Array.isArray(serviceEnvironmentVariables)) {
|
|
138
|
+
throw new Error("config.supabase.serviceEnvironmentVariables must be an array.");
|
|
139
|
+
}
|
|
140
|
+
const normalizedServiceEnvironmentVariables = serviceEnvironmentVariables.map((value, index) => assertNonEmptyString(value, `config.supabase.serviceEnvironmentVariables[${index}]`));
|
|
141
|
+
if (new Set(normalizedServiceEnvironmentVariables).size !==
|
|
142
|
+
normalizedServiceEnvironmentVariables.length) {
|
|
143
|
+
throw new Error("config.supabase.serviceEnvironmentVariables must not contain duplicates.");
|
|
144
|
+
}
|
|
145
|
+
if (Boolean(normalizedAuthentication?.environmentFile ??
|
|
146
|
+
supabase?.serviceEnvironmentFile) !== Boolean(normalizedServiceEnvironmentVariables.length)) {
|
|
147
|
+
throw new Error("config.supabase.serviceEnvironmentFile and serviceEnvironmentVariables must be configured together.");
|
|
148
|
+
}
|
|
149
|
+
const authenticationProviders = normalizedAuthentication
|
|
150
|
+
? normalizedAuthentication.providers.map(({ name }) => name)
|
|
151
|
+
: safety.authenticationProviders
|
|
152
|
+
? assertStringArray(safety.authenticationProviders, "config.safety.authenticationProviders")
|
|
153
|
+
: [];
|
|
154
|
+
if (runtimeTarget === "postgresql" && authenticationProviders.length > 0) {
|
|
155
|
+
throw new Error("config.safety.authenticationProviders is available only for the Supabase target.");
|
|
156
|
+
}
|
|
157
|
+
return Object.freeze({
|
|
158
|
+
schemaVersion: REHEARSAL_CONFIG_VERSION,
|
|
159
|
+
project: Object.freeze({ name: projectName }),
|
|
160
|
+
supabase: supabase
|
|
161
|
+
? Object.freeze({
|
|
162
|
+
workdir: assertRelativePath(supabase.workdir, "config.supabase.workdir"),
|
|
163
|
+
migrationDirectory: assertRelativePath(supabase.migrationDirectory, "config.supabase.migrationDirectory"),
|
|
164
|
+
rehearsalConfig: assertRelativePath(supabase.rehearsalConfig, "config.supabase.rehearsalConfig"),
|
|
165
|
+
runtimeWorkdir: assertRelativePath(supabase.runtimeWorkdir, "config.supabase.runtimeWorkdir"),
|
|
166
|
+
authentication: normalizedAuthentication,
|
|
167
|
+
serviceEnvironmentFile: normalizedAuthentication
|
|
168
|
+
? normalizedAuthentication.environmentFile
|
|
169
|
+
: supabase.serviceEnvironmentFile
|
|
170
|
+
? assertRelativePath(supabase.serviceEnvironmentFile, "config.supabase.serviceEnvironmentFile")
|
|
171
|
+
: null,
|
|
172
|
+
serviceEnvironmentVariables: Object.freeze(normalizedServiceEnvironmentVariables),
|
|
173
|
+
})
|
|
174
|
+
: null,
|
|
175
|
+
postgresql: postgresql
|
|
176
|
+
? Object.freeze({
|
|
177
|
+
migrationDirectory: assertRelativePath(postgresql.migrationDirectory, "config.postgresql.migrationDirectory"),
|
|
178
|
+
runtimeWorkdir: assertRelativePath(postgresql.runtimeWorkdir ?? ".rehearsal/runtime", "config.postgresql.runtimeWorkdir"),
|
|
179
|
+
image: assertPostgresImage(postgresql.image ?? "postgres:17-alpine", "config.postgresql.image"),
|
|
180
|
+
database: assertDatabaseIdentifier(postgresql.database ?? "postgres", "config.postgresql.database"),
|
|
181
|
+
user: assertDatabaseIdentifier(postgresql.user ?? "postgres", "config.postgresql.user"),
|
|
182
|
+
})
|
|
183
|
+
: null,
|
|
184
|
+
baseline: Object.freeze({
|
|
185
|
+
artifactDirectory: assertRelativePath(baseline.artifactDirectory ??
|
|
186
|
+
REHEARSAL_DEFAULTS.baseline.artifactDirectory, "config.baseline.artifactDirectory"),
|
|
187
|
+
sanitizationPolicy: assertRelativePath(baseline.sanitizationPolicy, "config.baseline.sanitizationPolicy"),
|
|
188
|
+
}),
|
|
189
|
+
containerRuntime: Object.freeze({
|
|
190
|
+
autoStartColima: assertBoolean(containerRuntime.autoStartColima ??
|
|
191
|
+
REHEARSAL_DEFAULTS.containerRuntime.autoStartColima, "config.containerRuntime.autoStartColima"),
|
|
192
|
+
}),
|
|
193
|
+
cleanup: Object.freeze({
|
|
194
|
+
retainBaselineGenerations: assertPositiveInteger(cleanup.retainBaselineGenerations ??
|
|
195
|
+
REHEARSAL_DEFAULTS.cleanup.retainBaselineGenerations, "config.cleanup.retainBaselineGenerations"),
|
|
196
|
+
}),
|
|
197
|
+
preparation: preparation
|
|
198
|
+
? Object.freeze({
|
|
199
|
+
sourcePolicy: assertRelativePath(preparation.sourcePolicy, "config.preparation.sourcePolicy"),
|
|
200
|
+
privacyKey: assertRelativePath(preparation.privacyKey, "config.preparation.privacyKey"),
|
|
201
|
+
batchRows: assertPositiveInteger(preparation.batchRows ?? 500, "config.preparation.batchRows"),
|
|
202
|
+
maximumRows: assertPositiveInteger(preparation.maximumRows ?? 1_000_000, "config.preparation.maximumRows"),
|
|
203
|
+
maximumBytes: assertPositiveInteger(preparation.maximumBytes ?? 2 * 1024 * 1024 * 1024, "config.preparation.maximumBytes"),
|
|
204
|
+
diskHeadroomBytes: assertPositiveInteger(preparation.diskHeadroomBytes ?? 64 * 1024 * 1024, "config.preparation.diskHeadroomBytes"),
|
|
205
|
+
})
|
|
206
|
+
: null,
|
|
207
|
+
runtimePolicy: root.runtimePolicy === undefined
|
|
208
|
+
? null
|
|
209
|
+
: assertRelativePath(root.runtimePolicy, "config.runtimePolicy"),
|
|
210
|
+
identityPolicy: root.identityPolicy === undefined
|
|
211
|
+
? null
|
|
212
|
+
: assertRelativePath(root.identityPolicy, "config.identityPolicy"),
|
|
213
|
+
dependentTargets: Object.freeze(normalizedDependentTargets),
|
|
214
|
+
application: Object.freeze({
|
|
215
|
+
startCommand: assertCommand(application.startCommand, "config.application.startCommand"),
|
|
216
|
+
proofCommand: assertCommand(application.proofCommand, "config.application.proofCommand"),
|
|
217
|
+
environmentFile: assertRelativePath(application.environmentFile ?? ".rehearsal/runtime.env", "config.application.environmentFile"),
|
|
218
|
+
environmentVariables: normalizeApplicationEnvironmentMappings(application.environmentVariables),
|
|
219
|
+
readiness: readiness
|
|
220
|
+
? Object.freeze({
|
|
221
|
+
url: assertLoopbackUrl(readiness.url, "config.application.readiness.url", allowedHosts, LOOPBACK_HOSTS),
|
|
222
|
+
expectedStatus: assertHttpStatus(readiness.expectedStatus, "config.application.readiness.expectedStatus"),
|
|
223
|
+
timeoutSeconds: assertPositiveInteger(readiness.timeoutSeconds ?? 30, "config.application.readiness.timeoutSeconds"),
|
|
224
|
+
})
|
|
225
|
+
: null,
|
|
226
|
+
httpProofs: Object.freeze(httpProofs.map((proof, index) => {
|
|
227
|
+
const path = `config.application.httpProofs[${index}]`;
|
|
228
|
+
const value = assertPlainObject(proof, path);
|
|
229
|
+
assertKnownKeys(value, ["name", "kind", "url", "method", "expectedStatus", "json"], path);
|
|
230
|
+
if (typeof value.kind !== "string" ||
|
|
231
|
+
!["positive", "negative"].includes(value.kind)) {
|
|
232
|
+
throw new Error(`${path}.kind must be positive or negative.`);
|
|
233
|
+
}
|
|
234
|
+
const expectedStatus = assertHttpStatus(value.expectedStatus, `${path}.expectedStatus`);
|
|
235
|
+
const method = assertNonEmptyString(value.method ?? "GET", `${path}.method`).toUpperCase();
|
|
236
|
+
if (!["GET", "HEAD"].includes(method)) {
|
|
237
|
+
throw new Error(`${path}.method must be GET or HEAD.`);
|
|
238
|
+
}
|
|
239
|
+
let json = null;
|
|
240
|
+
if (value.json !== undefined) {
|
|
241
|
+
const assertion = assertPlainObject(value.json, `${path}.json`);
|
|
242
|
+
assertKnownKeys(assertion, ["path", "equals", "notEquals", "minimumItems"], `${path}.json`);
|
|
243
|
+
if (!Array.isArray(assertion.path) ||
|
|
244
|
+
assertion.path.length === 0 ||
|
|
245
|
+
assertion.path.some((segment) => typeof segment !== "string" || !segment)) {
|
|
246
|
+
throw new Error(`${path}.json.path must be a non-empty string array.`);
|
|
247
|
+
}
|
|
248
|
+
const assertionCount = [
|
|
249
|
+
Object.hasOwn(assertion, "equals"),
|
|
250
|
+
Object.hasOwn(assertion, "notEquals"),
|
|
251
|
+
assertion.minimumItems !== undefined,
|
|
252
|
+
].filter(Boolean).length;
|
|
253
|
+
if (assertionCount !== 1) {
|
|
254
|
+
throw new Error(`${path}.json must declare exactly one assertion.`);
|
|
255
|
+
}
|
|
256
|
+
json = Object.freeze({
|
|
257
|
+
path: Object.freeze([...assertion.path]),
|
|
258
|
+
...(Object.hasOwn(assertion, "equals")
|
|
259
|
+
? { equals: assertion.equals }
|
|
260
|
+
: {}),
|
|
261
|
+
...(Object.hasOwn(assertion, "notEquals")
|
|
262
|
+
? { notEquals: assertion.notEquals }
|
|
263
|
+
: {}),
|
|
264
|
+
...(assertion.minimumItems !== undefined
|
|
265
|
+
? {
|
|
266
|
+
minimumItems: assertPositiveInteger(assertion.minimumItems, `${path}.json.minimumItems`),
|
|
267
|
+
}
|
|
268
|
+
: {}),
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
return Object.freeze({
|
|
272
|
+
name: assertNonEmptyString(value.name, `${path}.name`),
|
|
273
|
+
kind: value.kind,
|
|
274
|
+
url: assertLoopbackUrl(value.url, `${path}.url`, allowedHosts, LOOPBACK_HOSTS),
|
|
275
|
+
method,
|
|
276
|
+
expectedStatus,
|
|
277
|
+
json,
|
|
278
|
+
});
|
|
279
|
+
})),
|
|
280
|
+
runtimeAdapter: application.runtimeAdapter === undefined
|
|
281
|
+
? null
|
|
282
|
+
: assertRelativePath(application.runtimeAdapter, "config.application.runtimeAdapter"),
|
|
283
|
+
}),
|
|
284
|
+
runtime: Object.freeze({
|
|
285
|
+
target: runtimeTarget,
|
|
286
|
+
applicationUrl: assertLoopbackUrl(runtime.applicationUrl ?? REHEARSAL_DEFAULTS.runtime.applicationUrl, "config.runtime.applicationUrl", allowedHosts, LOOPBACK_HOSTS),
|
|
287
|
+
projectId: assertNonEmptyString(runtime.projectId ?? REHEARSAL_DEFAULTS.runtime.projectId, "config.runtime.projectId"),
|
|
288
|
+
ports: Object.freeze(ports),
|
|
289
|
+
}),
|
|
290
|
+
safety: Object.freeze({
|
|
291
|
+
allowedHosts: Object.freeze(allowedHosts),
|
|
292
|
+
blockedEnvironmentVariables: Object.freeze(assertStringArray(safety.blockedEnvironmentVariables ??
|
|
293
|
+
REHEARSAL_DEFAULTS.safety.blockedEnvironmentVariables, "config.safety.blockedEnvironmentVariables")),
|
|
294
|
+
authenticationProviders: Object.freeze(authenticationProviders),
|
|
295
|
+
hostedAccess: "disabled",
|
|
296
|
+
outboundNetwork: "deny",
|
|
297
|
+
}),
|
|
298
|
+
verification: Object.freeze({
|
|
299
|
+
commands: Object.freeze(commands.map((command, index) => assertCommand(command, `config.verification.commands[${index}]`))),
|
|
300
|
+
}),
|
|
301
|
+
});
|
|
302
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Render reviewed starter configurations without reading or writing a project. */
|
|
2
|
+
import type { DetectedProject } from "./project_detection.mjs";
|
|
3
|
+
export declare const renderDetectedConfig: (detected: DetectedProject, { applicationUrl, ports, }?: {
|
|
4
|
+
applicationUrl?: string;
|
|
5
|
+
ports?: {
|
|
6
|
+
api: number;
|
|
7
|
+
database: number;
|
|
8
|
+
studio: number;
|
|
9
|
+
};
|
|
10
|
+
}) => string;
|
|
11
|
+
export declare const renderDetectedPostgresqlConfig: (detected: DetectedProject, { applicationUrl, databasePort }?: {
|
|
12
|
+
applicationUrl?: string | undefined;
|
|
13
|
+
databasePort?: number | undefined;
|
|
14
|
+
}) => string;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/** Render reviewed starter configurations without reading or writing a project. */
|
|
2
|
+
export const renderDetectedConfig = (detected, { applicationUrl = "http://localhost:5175", ports = { api: 58321, database: 58322, studio: 58323 }, } = {}) => `// @ts-check
|
|
3
|
+
/**
|
|
4
|
+
* Rehearsal configuration for ${detected.projectName}.
|
|
5
|
+
* Generated from this project by \`npx rehearsal\` and safe to commit.
|
|
6
|
+
* Review the CHECK comments. Keep passwords, tokens, and production URLs out of this file.
|
|
7
|
+
* Docs: https://github.com/Ddupasquier/rehearsal-db/blob/main/docs/configuration.md
|
|
8
|
+
*/
|
|
9
|
+
import { defineRehearsalConfig } from "@rehearsal-db/core";
|
|
10
|
+
|
|
11
|
+
export default defineRehearsalConfig({
|
|
12
|
+
// Configuration format. Rehearsal will explain if an upgrade is ever needed.
|
|
13
|
+
schemaVersion: 1,
|
|
14
|
+
// Stable local name used in Rehearsal labels and reports.
|
|
15
|
+
project: { name: ${JSON.stringify(detected.projectName)} },
|
|
16
|
+
|
|
17
|
+
// Project files Rehearsal reads. Every path stays inside this repository.
|
|
18
|
+
supabase: {
|
|
19
|
+
workdir: ".",
|
|
20
|
+
migrationDirectory: "supabase/migrations",
|
|
21
|
+
rehearsalConfig: "infrastructure/rehearsal/supabase/config.toml",
|
|
22
|
+
runtimeWorkdir: ".rehearsal/runtime",
|
|
23
|
+
|
|
24
|
+
// Optional local OAuth connection. Secrets stay in the ignored owner-only file.
|
|
25
|
+
// Register http://127.0.0.1:${ports.api}/auth/v1/callback with the provider.
|
|
26
|
+
// authentication: {
|
|
27
|
+
// enableLocalSignup: true,
|
|
28
|
+
// environmentFile: ".env.rehearsal-service.local",
|
|
29
|
+
// providers: [
|
|
30
|
+
// {
|
|
31
|
+
// name: "google",
|
|
32
|
+
// clientIdEnvironmentVariable: "REHEARSAL_GOOGLE_CLIENT_ID",
|
|
33
|
+
// clientSecretEnvironmentVariable: "REHEARSAL_GOOGLE_CLIENT_SECRET",
|
|
34
|
+
// skipNonceCheck: false,
|
|
35
|
+
// emailOptional: false,
|
|
36
|
+
// },
|
|
37
|
+
// ],
|
|
38
|
+
// },
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// Immutable local baseline files. Rehearsal never reads production on your behalf.
|
|
42
|
+
baseline: {
|
|
43
|
+
artifactDirectory: ".rehearsal",
|
|
44
|
+
sanitizationPolicy: "infrastructure/rehearsal/sanitization-policy.json",
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// Optional source refresh. Enable only after reviewing docs/standalone-workflow.md.
|
|
48
|
+
// Credentials are read from the environment named by source-access-policy.json.
|
|
49
|
+
// preparation: {
|
|
50
|
+
// sourcePolicy: "infrastructure/rehearsal/source-access-policy.json",
|
|
51
|
+
// privacyKey: ".rehearsal/secrets/privacy.key",
|
|
52
|
+
// batchRows: 500,
|
|
53
|
+
// maximumRows: 1000000,
|
|
54
|
+
// maximumBytes: 2147483648,
|
|
55
|
+
// diskHeadroomBytes: 67108864,
|
|
56
|
+
// },
|
|
57
|
+
// Optional reviewed schemas/extensions/triggers/local-only rows.
|
|
58
|
+
// runtimePolicy: "infrastructure/rehearsal/runtime-policy.json",
|
|
59
|
+
// Optional copied-account association. The JSON stores only reviewed matcher hashes;
|
|
60
|
+
// raw emails/subjects and provider credentials stay in ignored environment files.
|
|
61
|
+
// After setup, run: npx rehearsal identity connect
|
|
62
|
+
// identityPolicy: "infrastructure/rehearsal/identity-policy.json",
|
|
63
|
+
|
|
64
|
+
// Reuse any running Docker-compatible engine. Keep startup explicit so a
|
|
65
|
+
// Rehearsal command cannot wake unrelated containers after Docker was stopped.
|
|
66
|
+
containerRuntime: {
|
|
67
|
+
autoStartColima: false,
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
// Keep this many baseline generations, always including the active one.
|
|
71
|
+
cleanup: {
|
|
72
|
+
retainBaselineGenerations: 2,
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
// Optional: manage another isolated database as part of the same rehearsal.
|
|
76
|
+
// Give that database its own complete config, ports, project ID, and .rehearsal folder.
|
|
77
|
+
// dependentTargets: [
|
|
78
|
+
// {
|
|
79
|
+
// name: "publication-api",
|
|
80
|
+
// configPath: "rehearsal.publication.config.mjs",
|
|
81
|
+
// prepareCommand: "npm run rehearsal:prepare-publication",
|
|
82
|
+
// },
|
|
83
|
+
// ],
|
|
84
|
+
|
|
85
|
+
application: {
|
|
86
|
+
// CHECK: commands detected from package.json. Change them if they are not correct.
|
|
87
|
+
startCommand: ${JSON.stringify(detected.applicationCommand)},
|
|
88
|
+
proofCommand: ${JSON.stringify(detected.verificationCommand)},
|
|
89
|
+
environmentFile: ".rehearsal/runtime.env",
|
|
90
|
+
// Map generated local runtime values directly into the normal app command.
|
|
91
|
+
// environmentVariables: { DATABASE_URL: "primary:DATABASE_URL" },
|
|
92
|
+
// Required by \`rehearsal open\`; use a route that responds only when the app is ready.
|
|
93
|
+
readiness: { url: ${JSON.stringify(applicationUrl)}, expectedStatus: 200, timeoutSeconds: 30 },
|
|
94
|
+
// Add at least one real positive and one negative before enabling HTTP proofs.
|
|
95
|
+
// httpProofs: [],
|
|
96
|
+
// Optional project-specific restore hook:
|
|
97
|
+
// runtimeAdapter: "infrastructure/rehearsal/runtime-adapter.mjs",
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// Disposable local runtime identity, URL, and dedicated ports.
|
|
101
|
+
runtime: {
|
|
102
|
+
target: "supabase",
|
|
103
|
+
applicationUrl: ${JSON.stringify(applicationUrl)},
|
|
104
|
+
projectId: "${detected.projectName}-rehearsal",
|
|
105
|
+
apiPort: ${ports.api},
|
|
106
|
+
databasePort: ${ports.database},
|
|
107
|
+
studioPort: ${ports.studio},
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
// Only local runtime URLs are accepted; common hosted credentials are quarantined.
|
|
111
|
+
safety: {
|
|
112
|
+
allowedHosts: ["127.0.0.1", "::1", "localhost"],
|
|
113
|
+
blockedEnvironmentVariables: [
|
|
114
|
+
"SUPABASE_ACCESS_TOKEN",
|
|
115
|
+
"SUPABASE_DB_PASSWORD",
|
|
116
|
+
"SUPABASE_PROJECT_ID",
|
|
117
|
+
],
|
|
118
|
+
hostedAccess: "disabled",
|
|
119
|
+
outboundNetwork: "deny",
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
`.replaceAll("\t", " ");
|
|
123
|
+
export const renderDetectedPostgresqlConfig = (detected, { applicationUrl = "http://localhost:5175", databasePort = 58322 } = {}) => `// @ts-check
|
|
124
|
+
/**
|
|
125
|
+
* Rehearsal configuration for ${detected.projectName}.
|
|
126
|
+
* Generated from this project by \`npx rehearsal\` and safe to commit.
|
|
127
|
+
* Review the CHECK comments. Keep passwords, tokens, and production URLs out of this file.
|
|
128
|
+
* Docs: https://github.com/Ddupasquier/rehearsal-db/blob/main/docs/configuration.md
|
|
129
|
+
*/
|
|
130
|
+
import { defineRehearsalConfig } from "@rehearsal-db/core";
|
|
131
|
+
|
|
132
|
+
export default defineRehearsalConfig({
|
|
133
|
+
// Configuration format. Rehearsal will explain if an upgrade is ever needed.
|
|
134
|
+
schemaVersion: 1,
|
|
135
|
+
// Stable local name used in Rehearsal labels and reports.
|
|
136
|
+
project: { name: ${JSON.stringify(detected.projectName)} },
|
|
137
|
+
|
|
138
|
+
// Disposable PostgreSQL settings. The image must already exist locally.
|
|
139
|
+
postgresql: {
|
|
140
|
+
migrationDirectory: ${JSON.stringify(detected.postgresqlMigrationDirectory)},
|
|
141
|
+
runtimeWorkdir: ".rehearsal/runtime",
|
|
142
|
+
image: "postgres:17-alpine",
|
|
143
|
+
database: "postgres",
|
|
144
|
+
user: "postgres",
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
// Immutable local baseline files. Rehearsal never reads production on your behalf.
|
|
148
|
+
baseline: {
|
|
149
|
+
artifactDirectory: ".rehearsal",
|
|
150
|
+
sanitizationPolicy: "infrastructure/rehearsal/sanitization-policy.json",
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
// Optional source refresh. Enable only after reviewing docs/standalone-workflow.md.
|
|
154
|
+
// Credentials are read from the environment named by source-access-policy.json.
|
|
155
|
+
// preparation: {
|
|
156
|
+
// sourcePolicy: "infrastructure/rehearsal/source-access-policy.json",
|
|
157
|
+
// privacyKey: ".rehearsal/secrets/privacy.key",
|
|
158
|
+
// batchRows: 500,
|
|
159
|
+
// maximumRows: 1000000,
|
|
160
|
+
// maximumBytes: 2147483648,
|
|
161
|
+
// diskHeadroomBytes: 67108864,
|
|
162
|
+
// },
|
|
163
|
+
// Optional reviewed schemas/extensions/triggers/local-only rows.
|
|
164
|
+
// runtimePolicy: "infrastructure/rehearsal/runtime-policy.json",
|
|
165
|
+
// Optional copied-account association. The JSON stores only reviewed matcher hashes;
|
|
166
|
+
// raw emails/subjects and provider credentials stay in ignored environment files.
|
|
167
|
+
// After setup, run: npx rehearsal identity connect
|
|
168
|
+
// identityPolicy: "infrastructure/rehearsal/identity-policy.json",
|
|
169
|
+
|
|
170
|
+
// Reuse any running Docker-compatible engine. Keep startup explicit so a
|
|
171
|
+
// Rehearsal command cannot wake unrelated containers after Docker was stopped.
|
|
172
|
+
containerRuntime: {
|
|
173
|
+
autoStartColima: false,
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
// Keep this many baseline generations, always including the active one.
|
|
177
|
+
cleanup: {
|
|
178
|
+
retainBaselineGenerations: 2,
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
// Optional: manage another isolated database as part of the same rehearsal.
|
|
182
|
+
// Give that database its own complete config, ports, project ID, and .rehearsal folder.
|
|
183
|
+
// dependentTargets: [
|
|
184
|
+
// {
|
|
185
|
+
// name: "publication-api",
|
|
186
|
+
// configPath: "rehearsal.publication.config.mjs",
|
|
187
|
+
// prepareCommand: "npm run rehearsal:prepare-publication",
|
|
188
|
+
// },
|
|
189
|
+
// ],
|
|
190
|
+
|
|
191
|
+
application: {
|
|
192
|
+
// CHECK: commands detected from package.json. Change them if they are not correct.
|
|
193
|
+
startCommand: ${JSON.stringify(detected.applicationCommand)},
|
|
194
|
+
proofCommand: ${JSON.stringify(detected.verificationCommand)},
|
|
195
|
+
environmentFile: ".rehearsal/runtime.env",
|
|
196
|
+
// Map generated local runtime values directly into the normal app command.
|
|
197
|
+
// environmentVariables: { DATABASE_URL: "primary:DATABASE_URL" },
|
|
198
|
+
// Required by \`rehearsal open\`; use a route that responds only when the app is ready.
|
|
199
|
+
readiness: { url: ${JSON.stringify(applicationUrl)}, expectedStatus: 200, timeoutSeconds: 30 },
|
|
200
|
+
// Add at least one real positive and one negative before enabling HTTP proofs.
|
|
201
|
+
// httpProofs: [],
|
|
202
|
+
// Optional project-specific restore hook:
|
|
203
|
+
// runtimeAdapter: "infrastructure/rehearsal/runtime-adapter.mjs",
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
// Disposable local runtime identity, URL, and dedicated database port.
|
|
207
|
+
runtime: {
|
|
208
|
+
target: "postgresql",
|
|
209
|
+
applicationUrl: ${JSON.stringify(applicationUrl)},
|
|
210
|
+
projectId: "${detected.projectName}-rehearsal",
|
|
211
|
+
databasePort: ${databasePort},
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
// Only local runtime URLs are accepted; common hosted credentials are quarantined.
|
|
215
|
+
safety: {
|
|
216
|
+
allowedHosts: ["127.0.0.1", "::1", "localhost"],
|
|
217
|
+
blockedEnvironmentVariables: [
|
|
218
|
+
"DATABASE_URL",
|
|
219
|
+
"PGHOST",
|
|
220
|
+
"PGPASSWORD",
|
|
221
|
+
"PGPORT",
|
|
222
|
+
"PGUSER",
|
|
223
|
+
],
|
|
224
|
+
hostedAccess: "disabled",
|
|
225
|
+
outboundNetwork: "deny",
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
`.replaceAll("\t", " ");
|