@rehearsal-db/core 0.1.0-beta.14 → 0.1.0-beta.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -1
- package/README.md +1 -0
- package/dist/package.json +101 -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_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 +128 -0
- package/dist/src/cli/basic_command_handlers.d.mts +4 -0
- package/dist/src/cli/basic_command_handlers.mjs +56 -0
- package/dist/src/cli/cli_commands.d.mts +14 -0
- package/dist/src/cli/cli_commands.mjs +32 -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 +719 -0
- package/dist/src/cli/identity_command_handlers.d.mts +8 -0
- package/dist/src/cli/identity_command_handlers.mjs +43 -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 +159 -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 +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 +269 -0
- package/dist/src/cli/runtime_commands.mjs +601 -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 +17 -0
- package/dist/src/identity/claim_identity_lookup.mjs +125 -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/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 +224 -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 +637 -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 +627 -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 +133 -10
- package/docs/configuration.md +9 -3
- package/docs/index.md +1 -0
- package/docs/releasing.md +8 -3
- package/docs/typescript.md +134 -0
- package/package.json +36 -29
- package/types/configuration.d.mts +5 -0
- package/scripts/install/create_root_config.mjs +0 -19
- 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 -1399
- package/src/baseline/privacy_structured_json.mjs +0 -554
- 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 -972
- package/src/cli/renderers.mjs +0 -638
- package/src/cli/runtime_commands.mjs +0 -707
- package/src/cli/source_commands.mjs +0 -355
- package/src/cli/terminal.mjs +0 -275
- package/src/identity/claim.mjs +0 -1165
- package/src/identity/provider_configuration.mjs +0 -177
- package/src/identity/storage.mjs +0 -165
- package/src/project/configuration.d.mts +0 -164
- package/src/project/configuration.mjs +0 -1337
- package/src/project/install_scaffold.mjs +0 -325
- package/src/project/setup.mjs +0 -474
- 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 -798
- package/src/source/asset_transfer.mjs +0 -259
- package/src/source/baseline.mjs +0 -482
- package/src/source/external_reader.mjs +0 -368
- package/src/source/postgresql_access.mjs +0 -638
- 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
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** Load a discovered config and resolve every owned path from the project root. */
|
|
2
|
+
import { basename, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { findRehearsalConfigPath, } from "./configuration_discovery.mjs";
|
|
5
|
+
import { normalizeRehearsalConfig } from "./configuration_normalization.mjs";
|
|
6
|
+
import { assertIdentifier } from "./configuration_validation.mjs";
|
|
7
|
+
export const loadRehearsalConfig = async ({ projectRoot = process.cwd(), configPath, } = {}) => {
|
|
8
|
+
const root = resolve(projectRoot);
|
|
9
|
+
const path = await findRehearsalConfigPath({
|
|
10
|
+
projectRoot: root,
|
|
11
|
+
...(configPath === undefined ? {} : { configPath }),
|
|
12
|
+
});
|
|
13
|
+
const module = await import(`${pathToFileURL(path).href}?loaded=${Date.now()}`);
|
|
14
|
+
const config = normalizeRehearsalConfig(module.default);
|
|
15
|
+
const resolveOwnedPath = (value) => {
|
|
16
|
+
const resolved = resolve(root, value);
|
|
17
|
+
if (relative(root, resolved).startsWith("..")) {
|
|
18
|
+
throw new Error(`A Rehearsal path escaped the project root: ${value}.`);
|
|
19
|
+
}
|
|
20
|
+
return resolved;
|
|
21
|
+
};
|
|
22
|
+
const paths = Object.freeze({
|
|
23
|
+
artifactDirectory: resolveOwnedPath(config.baseline.artifactDirectory),
|
|
24
|
+
applicationEnvironment: resolveOwnedPath(config.application.environmentFile),
|
|
25
|
+
migrationDirectory: resolveOwnedPath((config.supabase ?? config.postgresql).migrationDirectory),
|
|
26
|
+
rehearsalConfig: config.supabase
|
|
27
|
+
? resolveOwnedPath(config.supabase.rehearsalConfig)
|
|
28
|
+
: null,
|
|
29
|
+
runtimeWorkdir: resolveOwnedPath((config.supabase ?? config.postgresql).runtimeWorkdir),
|
|
30
|
+
serviceEnvironment: config.supabase?.serviceEnvironmentFile
|
|
31
|
+
? resolveOwnedPath(config.supabase.serviceEnvironmentFile)
|
|
32
|
+
: null,
|
|
33
|
+
sanitizationPolicy: resolveOwnedPath(config.baseline.sanitizationPolicy),
|
|
34
|
+
sourcePolicy: config.preparation
|
|
35
|
+
? resolveOwnedPath(config.preparation.sourcePolicy)
|
|
36
|
+
: null,
|
|
37
|
+
privacyKey: config.preparation
|
|
38
|
+
? resolveOwnedPath(config.preparation.privacyKey)
|
|
39
|
+
: null,
|
|
40
|
+
runtimePolicy: config.runtimePolicy
|
|
41
|
+
? resolveOwnedPath(config.runtimePolicy)
|
|
42
|
+
: null,
|
|
43
|
+
identityPolicy: config.identityPolicy
|
|
44
|
+
? resolveOwnedPath(config.identityPolicy)
|
|
45
|
+
: null,
|
|
46
|
+
supabaseWorkdir: config.supabase
|
|
47
|
+
? resolveOwnedPath(config.supabase.workdir)
|
|
48
|
+
: null,
|
|
49
|
+
runtimeAdapter: config.application.runtimeAdapter
|
|
50
|
+
? resolveOwnedPath(config.application.runtimeAdapter)
|
|
51
|
+
: null,
|
|
52
|
+
});
|
|
53
|
+
if (basename(paths.artifactDirectory) !== ".rehearsal") {
|
|
54
|
+
throw new Error('config.baseline.artifactDirectory must resolve to a directory named ".rehearsal" so destructive reset and cleanup operations have an explicit safety boundary.');
|
|
55
|
+
}
|
|
56
|
+
const requiredRuntimeWorkdir = join(paths.artifactDirectory, "runtime");
|
|
57
|
+
if (paths.runtimeWorkdir !== requiredRuntimeWorkdir) {
|
|
58
|
+
throw new Error(`config.${config.runtime.target}.runtimeWorkdir must resolve to the runtime directory inside config.baseline.artifactDirectory.`);
|
|
59
|
+
}
|
|
60
|
+
const environmentRelativePath = relative(paths.artifactDirectory, paths.applicationEnvironment);
|
|
61
|
+
if (!environmentRelativePath ||
|
|
62
|
+
environmentRelativePath.startsWith("..") ||
|
|
63
|
+
isAbsolute(environmentRelativePath)) {
|
|
64
|
+
throw new Error("config.application.environmentFile must resolve to a file inside config.baseline.artifactDirectory.");
|
|
65
|
+
}
|
|
66
|
+
if (paths.privacyKey) {
|
|
67
|
+
const privacyKeyRelativePath = relative(paths.artifactDirectory, paths.privacyKey);
|
|
68
|
+
if (!privacyKeyRelativePath ||
|
|
69
|
+
privacyKeyRelativePath.startsWith("..") ||
|
|
70
|
+
isAbsolute(privacyKeyRelativePath)) {
|
|
71
|
+
throw new Error("config.preparation.privacyKey must resolve to a secret file inside config.baseline.artifactDirectory.");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
assertIdentifier(config.runtime.projectId, "config.runtime.projectId");
|
|
75
|
+
return Object.freeze({
|
|
76
|
+
config,
|
|
77
|
+
configPath: path,
|
|
78
|
+
projectRoot: root,
|
|
79
|
+
paths,
|
|
80
|
+
});
|
|
81
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Validate the raw object shape and known keys before applying defaults. */
|
|
2
|
+
import { type PlainObject } from "./configuration_validation.mjs";
|
|
3
|
+
export interface ValidatedConfigurationSchema {
|
|
4
|
+
root: PlainObject;
|
|
5
|
+
project: PlainObject;
|
|
6
|
+
runtime: PlainObject;
|
|
7
|
+
runtimeTarget: "supabase" | "postgresql";
|
|
8
|
+
supabase: PlainObject | null;
|
|
9
|
+
postgresql: PlainObject | null;
|
|
10
|
+
baseline: PlainObject;
|
|
11
|
+
containerRuntime: PlainObject;
|
|
12
|
+
cleanup: PlainObject;
|
|
13
|
+
preparation: PlainObject | null;
|
|
14
|
+
dependentTargets: PlainObject[];
|
|
15
|
+
application: PlainObject;
|
|
16
|
+
readiness: PlainObject | null;
|
|
17
|
+
httpProofs: PlainObject[];
|
|
18
|
+
safety: PlainObject;
|
|
19
|
+
verification: PlainObject;
|
|
20
|
+
authentication: PlainObject | null;
|
|
21
|
+
}
|
|
22
|
+
export declare const validateConfigurationSchema: (input: unknown) => ValidatedConfigurationSchema;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/** Validate the raw object shape and known keys before applying defaults. */
|
|
2
|
+
import { resolveRuntimeTarget } from "../targets/target.mjs";
|
|
3
|
+
import { REHEARSAL_CONFIG_VERSION } from "./configuration_contract.mjs";
|
|
4
|
+
import { assertKnownKeys, assertPlainObject, } from "./configuration_validation.mjs";
|
|
5
|
+
const assertObjectArray = (value, path) => {
|
|
6
|
+
if (!Array.isArray(value))
|
|
7
|
+
throw new Error(`${path} must be an array.`);
|
|
8
|
+
return value.map((entry, index) => assertPlainObject(entry, `${path}[${index}]`));
|
|
9
|
+
};
|
|
10
|
+
export const validateConfigurationSchema = (input) => {
|
|
11
|
+
const root = assertPlainObject(input, "config");
|
|
12
|
+
assertKnownKeys(root, [
|
|
13
|
+
"schemaVersion",
|
|
14
|
+
"project",
|
|
15
|
+
"supabase",
|
|
16
|
+
"postgresql",
|
|
17
|
+
"baseline",
|
|
18
|
+
"containerRuntime",
|
|
19
|
+
"cleanup",
|
|
20
|
+
"preparation",
|
|
21
|
+
"runtimePolicy",
|
|
22
|
+
"identityPolicy",
|
|
23
|
+
"dependentTargets",
|
|
24
|
+
"application",
|
|
25
|
+
"runtime",
|
|
26
|
+
"safety",
|
|
27
|
+
"verification",
|
|
28
|
+
], "config");
|
|
29
|
+
if (root.schemaVersion !== REHEARSAL_CONFIG_VERSION) {
|
|
30
|
+
throw new Error(`Unsupported Rehearsal configuration version: ${String(root.schemaVersion)}. Expected ${REHEARSAL_CONFIG_VERSION}.`);
|
|
31
|
+
}
|
|
32
|
+
const project = assertPlainObject(root.project, "config.project");
|
|
33
|
+
assertKnownKeys(project, ["name"], "config.project");
|
|
34
|
+
const runtime = assertPlainObject(root.runtime ?? {}, "config.runtime");
|
|
35
|
+
assertKnownKeys(runtime, [
|
|
36
|
+
"target",
|
|
37
|
+
"applicationUrl",
|
|
38
|
+
"projectId",
|
|
39
|
+
"apiPort",
|
|
40
|
+
"databasePort",
|
|
41
|
+
"studioPort",
|
|
42
|
+
], "config.runtime");
|
|
43
|
+
const runtimeTarget = resolveRuntimeTarget(runtime.target).id;
|
|
44
|
+
const supabase = runtimeTarget === "supabase"
|
|
45
|
+
? assertPlainObject(root.supabase, "config.supabase")
|
|
46
|
+
: null;
|
|
47
|
+
const postgresql = runtimeTarget === "postgresql"
|
|
48
|
+
? assertPlainObject(root.postgresql, "config.postgresql")
|
|
49
|
+
: null;
|
|
50
|
+
if (runtimeTarget === "supabase" && root.postgresql !== undefined) {
|
|
51
|
+
throw new Error("config.postgresql is not allowed when config.runtime.target is supabase.");
|
|
52
|
+
}
|
|
53
|
+
if (runtimeTarget === "postgresql" && root.supabase !== undefined) {
|
|
54
|
+
throw new Error("config.supabase is not allowed when config.runtime.target is postgresql.");
|
|
55
|
+
}
|
|
56
|
+
if (supabase) {
|
|
57
|
+
assertKnownKeys(supabase, [
|
|
58
|
+
"workdir",
|
|
59
|
+
"migrationDirectory",
|
|
60
|
+
"rehearsalConfig",
|
|
61
|
+
"runtimeWorkdir",
|
|
62
|
+
"authentication",
|
|
63
|
+
"serviceEnvironmentFile",
|
|
64
|
+
"serviceEnvironmentVariables",
|
|
65
|
+
], "config.supabase");
|
|
66
|
+
}
|
|
67
|
+
if (postgresql) {
|
|
68
|
+
assertKnownKeys(postgresql, ["migrationDirectory", "runtimeWorkdir", "image", "database", "user"], "config.postgresql");
|
|
69
|
+
}
|
|
70
|
+
const baseline = assertPlainObject(root.baseline, "config.baseline");
|
|
71
|
+
assertKnownKeys(baseline, ["artifactDirectory", "sanitizationPolicy"], "config.baseline");
|
|
72
|
+
const containerRuntime = assertPlainObject(root.containerRuntime ?? {}, "config.containerRuntime");
|
|
73
|
+
assertKnownKeys(containerRuntime, ["autoStartColima"], "config.containerRuntime");
|
|
74
|
+
const cleanup = assertPlainObject(root.cleanup ?? {}, "config.cleanup");
|
|
75
|
+
assertKnownKeys(cleanup, ["retainBaselineGenerations"], "config.cleanup");
|
|
76
|
+
const preparation = root.preparation === undefined
|
|
77
|
+
? null
|
|
78
|
+
: assertPlainObject(root.preparation, "config.preparation");
|
|
79
|
+
if (preparation) {
|
|
80
|
+
assertKnownKeys(preparation, [
|
|
81
|
+
"sourcePolicy",
|
|
82
|
+
"privacyKey",
|
|
83
|
+
"batchRows",
|
|
84
|
+
"maximumRows",
|
|
85
|
+
"maximumBytes",
|
|
86
|
+
"diskHeadroomBytes",
|
|
87
|
+
], "config.preparation");
|
|
88
|
+
}
|
|
89
|
+
const dependentTargets = assertObjectArray(root.dependentTargets ?? [], "config.dependentTargets");
|
|
90
|
+
dependentTargets.forEach((target, index) => assertKnownKeys(target, ["name", "configPath", "prepareCommand"], `config.dependentTargets[${index}]`));
|
|
91
|
+
const application = assertPlainObject(root.application, "config.application");
|
|
92
|
+
assertKnownKeys(application, [
|
|
93
|
+
"startCommand",
|
|
94
|
+
"proofCommand",
|
|
95
|
+
"environmentFile",
|
|
96
|
+
"environmentVariables",
|
|
97
|
+
"readiness",
|
|
98
|
+
"httpProofs",
|
|
99
|
+
"runtimeAdapter",
|
|
100
|
+
], "config.application");
|
|
101
|
+
const readiness = application.readiness === undefined
|
|
102
|
+
? null
|
|
103
|
+
: assertPlainObject(application.readiness, "config.application.readiness");
|
|
104
|
+
if (readiness) {
|
|
105
|
+
assertKnownKeys(readiness, ["url", "expectedStatus", "timeoutSeconds"], "config.application.readiness");
|
|
106
|
+
}
|
|
107
|
+
const httpProofs = assertObjectArray(application.httpProofs ?? [], "config.application.httpProofs");
|
|
108
|
+
httpProofs.forEach((proof, index) => {
|
|
109
|
+
const path = `config.application.httpProofs[${index}]`;
|
|
110
|
+
assertKnownKeys(proof, ["name", "kind", "url", "method", "expectedStatus", "json"], path);
|
|
111
|
+
if (proof.json !== undefined) {
|
|
112
|
+
assertKnownKeys(assertPlainObject(proof.json, `${path}.json`), ["path", "equals", "notEquals", "minimumItems"], `${path}.json`);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
const safety = assertPlainObject(root.safety ?? {}, "config.safety");
|
|
116
|
+
assertKnownKeys(safety, [
|
|
117
|
+
"allowedHosts",
|
|
118
|
+
"blockedEnvironmentVariables",
|
|
119
|
+
"authenticationProviders",
|
|
120
|
+
"hostedAccess",
|
|
121
|
+
"outboundNetwork",
|
|
122
|
+
], "config.safety");
|
|
123
|
+
const verification = assertPlainObject(root.verification ?? {}, "config.verification");
|
|
124
|
+
assertKnownKeys(verification, ["commands"], "config.verification");
|
|
125
|
+
const authentication = supabase?.authentication === undefined
|
|
126
|
+
? null
|
|
127
|
+
: assertPlainObject(supabase.authentication, "config.supabase.authentication");
|
|
128
|
+
if (authentication) {
|
|
129
|
+
assertKnownKeys(authentication, ["enableLocalSignup", "environmentFile", "providers"], "config.supabase.authentication");
|
|
130
|
+
if (Array.isArray(authentication.providers)) {
|
|
131
|
+
const providers = assertObjectArray(authentication.providers, "config.supabase.authentication.providers");
|
|
132
|
+
providers.forEach((provider, index) => assertKnownKeys(provider, [
|
|
133
|
+
"name",
|
|
134
|
+
"clientIdEnvironmentVariable",
|
|
135
|
+
"clientSecretEnvironmentVariable",
|
|
136
|
+
"skipNonceCheck",
|
|
137
|
+
"emailOptional",
|
|
138
|
+
], `config.supabase.authentication.providers[${index}]`));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
root,
|
|
143
|
+
project,
|
|
144
|
+
runtime,
|
|
145
|
+
runtimeTarget,
|
|
146
|
+
supabase,
|
|
147
|
+
postgresql,
|
|
148
|
+
baseline,
|
|
149
|
+
containerRuntime,
|
|
150
|
+
cleanup,
|
|
151
|
+
preparation,
|
|
152
|
+
dependentTargets,
|
|
153
|
+
application,
|
|
154
|
+
readiness,
|
|
155
|
+
httpProofs,
|
|
156
|
+
safety,
|
|
157
|
+
verification,
|
|
158
|
+
authentication,
|
|
159
|
+
};
|
|
160
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Provide the reusable, side-effect-free validators used by the
|
|
3
|
+
* Rehearsal configuration schema and normalizer.
|
|
4
|
+
*/
|
|
5
|
+
export type PlainObject = Record<string, unknown>;
|
|
6
|
+
export declare const isPlainObject: (value: unknown) => value is PlainObject;
|
|
7
|
+
export declare const assertPlainObject: (value: unknown, path: string) => PlainObject;
|
|
8
|
+
export declare const assertKnownKeys: (value: PlainObject, keys: readonly string[], path: string) => void;
|
|
9
|
+
export declare const assertNonEmptyString: (value: unknown, path: string) => string;
|
|
10
|
+
export declare const assertRelativePath: (value: unknown, path: string) => string;
|
|
11
|
+
export declare const assertCommand: (value: unknown, path: string) => string;
|
|
12
|
+
export declare const assertIdentifier: (value: unknown, path: string) => string;
|
|
13
|
+
export declare const assertDatabaseIdentifier: (value: unknown, path: string) => string;
|
|
14
|
+
export declare const assertPostgresImage: (value: unknown, path: string) => string;
|
|
15
|
+
export declare const assertEnvironmentKey: (value: unknown, path: string) => string;
|
|
16
|
+
export declare const assertEnvironmentReference: (value: unknown, path: string) => string;
|
|
17
|
+
export declare const assertPort: (value: unknown, path: string) => number;
|
|
18
|
+
export declare const assertBoolean: (value: unknown, path: string) => boolean;
|
|
19
|
+
export declare const assertPositiveInteger: (value: unknown, path: string) => number;
|
|
20
|
+
export declare const assertHttpStatus: (value: unknown, path: string) => number;
|
|
21
|
+
export declare const assertStringArray: (value: unknown, path: string) => string[];
|
|
22
|
+
export declare const assertLoopbackUrl: (value: unknown, path: string, allowedHosts: readonly string[], supportedHosts: ReadonlySet<string>) => string;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Provide the reusable, side-effect-free validators used by the
|
|
3
|
+
* Rehearsal configuration schema and normalizer.
|
|
4
|
+
*/
|
|
5
|
+
import { isAbsolute } from "node:path";
|
|
6
|
+
const SAFE_COMMAND_PATTERN = /^[^\n\r\0]+$/u;
|
|
7
|
+
const IDENTIFIER_PATTERN = /^[a-z0-9][a-z0-9-]{1,62}$/u;
|
|
8
|
+
const DATABASE_IDENTIFIER_PATTERN = /^[a-z][a-z0-9_]{0,62}$/u;
|
|
9
|
+
const POSTGRES_IMAGE_PATTERN = /^postgres:\d+(?:\.\d+)?-alpine$/u;
|
|
10
|
+
const ENVIRONMENT_KEY_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
11
|
+
const ENVIRONMENT_REFERENCE_PATTERN = /^[a-z0-9][a-z0-9-]*:[A-Z][A-Z0-9_]*$/u;
|
|
12
|
+
export const isPlainObject = (value) => value !== null &&
|
|
13
|
+
typeof value === "object" &&
|
|
14
|
+
!Array.isArray(value) &&
|
|
15
|
+
Object.getPrototypeOf(value) === Object.prototype;
|
|
16
|
+
export const assertPlainObject = (value, path) => {
|
|
17
|
+
if (!isPlainObject(value))
|
|
18
|
+
throw new Error(`${path} must be an object.`);
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
export const assertKnownKeys = (value, keys, path) => {
|
|
22
|
+
for (const key of Object.keys(value)) {
|
|
23
|
+
if (!keys.includes(key)) {
|
|
24
|
+
throw new Error(`Unknown Rehearsal configuration property: ${path}.${key}.`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
export const assertNonEmptyString = (value, path) => {
|
|
29
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
30
|
+
throw new Error(`${path} must be a non-empty string.`);
|
|
31
|
+
}
|
|
32
|
+
return value.trim();
|
|
33
|
+
};
|
|
34
|
+
export const assertRelativePath = (value, path) => {
|
|
35
|
+
const normalized = assertNonEmptyString(value, path);
|
|
36
|
+
if (isAbsolute(normalized) || normalized.split(/[\\/]/u).includes("..")) {
|
|
37
|
+
throw new Error(`${path} must stay inside the project root.`);
|
|
38
|
+
}
|
|
39
|
+
return normalized;
|
|
40
|
+
};
|
|
41
|
+
export const assertCommand = (value, path) => {
|
|
42
|
+
const command = assertNonEmptyString(value, path);
|
|
43
|
+
if (!SAFE_COMMAND_PATTERN.test(command)) {
|
|
44
|
+
throw new Error(`${path} contains an unsafe control character.`);
|
|
45
|
+
}
|
|
46
|
+
return command;
|
|
47
|
+
};
|
|
48
|
+
export const assertIdentifier = (value, path) => {
|
|
49
|
+
const identifier = assertNonEmptyString(value, path);
|
|
50
|
+
if (!IDENTIFIER_PATTERN.test(identifier)) {
|
|
51
|
+
throw new Error(`${path} must contain only lowercase letters, digits, and hyphens.`);
|
|
52
|
+
}
|
|
53
|
+
return identifier;
|
|
54
|
+
};
|
|
55
|
+
export const assertDatabaseIdentifier = (value, path) => {
|
|
56
|
+
const identifier = assertNonEmptyString(value, path);
|
|
57
|
+
if (!DATABASE_IDENTIFIER_PATTERN.test(identifier)) {
|
|
58
|
+
throw new Error(`${path} must be a safe lowercase PostgreSQL identifier.`);
|
|
59
|
+
}
|
|
60
|
+
return identifier;
|
|
61
|
+
};
|
|
62
|
+
export const assertPostgresImage = (value, path) => {
|
|
63
|
+
const image = assertNonEmptyString(value, path);
|
|
64
|
+
if (!POSTGRES_IMAGE_PATTERN.test(image)) {
|
|
65
|
+
throw new Error(`${path} must name an official versioned Alpine PostgreSQL image, such as postgres:17-alpine.`);
|
|
66
|
+
}
|
|
67
|
+
return image;
|
|
68
|
+
};
|
|
69
|
+
export const assertEnvironmentKey = (value, path) => {
|
|
70
|
+
const key = assertNonEmptyString(value, path);
|
|
71
|
+
if (!ENVIRONMENT_KEY_PATTERN.test(key)) {
|
|
72
|
+
throw new Error(`${path} must be a safe environment variable name.`);
|
|
73
|
+
}
|
|
74
|
+
return key;
|
|
75
|
+
};
|
|
76
|
+
export const assertEnvironmentReference = (value, path) => {
|
|
77
|
+
if (typeof value !== "string" || !ENVIRONMENT_REFERENCE_PATTERN.test(value)) {
|
|
78
|
+
throw new Error(`${path} must reference target:VARIABLE, such as primary:DATABASE_URL.`);
|
|
79
|
+
}
|
|
80
|
+
return value;
|
|
81
|
+
};
|
|
82
|
+
export const assertPort = (value, path) => {
|
|
83
|
+
if (typeof value !== "number" ||
|
|
84
|
+
!Number.isSafeInteger(value) ||
|
|
85
|
+
value < 1024 ||
|
|
86
|
+
value > 65_535) {
|
|
87
|
+
throw new Error(`${path} must be a non-privileged TCP port.`);
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
};
|
|
91
|
+
export const assertBoolean = (value, path) => {
|
|
92
|
+
if (typeof value !== "boolean") {
|
|
93
|
+
throw new Error(`${path} must be true or false.`);
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
};
|
|
97
|
+
export const assertPositiveInteger = (value, path) => {
|
|
98
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1) {
|
|
99
|
+
throw new Error(`${path} must be a positive integer.`);
|
|
100
|
+
}
|
|
101
|
+
return value;
|
|
102
|
+
};
|
|
103
|
+
export const assertHttpStatus = (value, path) => {
|
|
104
|
+
if (typeof value !== "number" ||
|
|
105
|
+
!Number.isSafeInteger(value) ||
|
|
106
|
+
value < 100 ||
|
|
107
|
+
value > 599) {
|
|
108
|
+
throw new Error(`${path} must be an HTTP status.`);
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
};
|
|
112
|
+
export const assertStringArray = (value, path) => {
|
|
113
|
+
if (!Array.isArray(value) ||
|
|
114
|
+
value.length === 0 ||
|
|
115
|
+
value.some((entry) => typeof entry !== "string" || !entry.trim())) {
|
|
116
|
+
throw new Error(`${path} must be a non-empty string array.`);
|
|
117
|
+
}
|
|
118
|
+
return [...new Set(value.map((entry) => entry.trim()))];
|
|
119
|
+
};
|
|
120
|
+
export const assertLoopbackUrl = (value, path, allowedHosts, supportedHosts) => {
|
|
121
|
+
let parsed;
|
|
122
|
+
try {
|
|
123
|
+
parsed = new URL(assertNonEmptyString(value, path));
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
throw new Error(`${path} must be a valid loopback URL.`);
|
|
127
|
+
}
|
|
128
|
+
if (!allowedHosts.includes(parsed.hostname) ||
|
|
129
|
+
!supportedHosts.has(parsed.hostname)) {
|
|
130
|
+
throw new Error(`${path} must use an explicitly allowed loopback host.`);
|
|
131
|
+
}
|
|
132
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
133
|
+
throw new Error(`${path} must use HTTP or HTTPS.`);
|
|
134
|
+
}
|
|
135
|
+
return parsed.toString().replace(/\/$/u, "");
|
|
136
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Resolve an npm consumer root conservatively and create the same
|
|
3
|
+
* project-owned scaffolding used by the guided setup command.
|
|
4
|
+
*/
|
|
5
|
+
export type ConsumerRootResolution = {
|
|
6
|
+
status: "skipped";
|
|
7
|
+
reason: string;
|
|
8
|
+
} | {
|
|
9
|
+
status: "resolved";
|
|
10
|
+
projectRoot: string;
|
|
11
|
+
};
|
|
12
|
+
export interface ResolveConsumerRootOptions {
|
|
13
|
+
packageRoot?: string;
|
|
14
|
+
initCwd?: string;
|
|
15
|
+
localPrefix?: string;
|
|
16
|
+
globalInstall?: boolean | string;
|
|
17
|
+
workspace?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface InstalledScaffoldOptions {
|
|
20
|
+
packageRoot?: string;
|
|
21
|
+
environment?: NodeJS.ProcessEnv;
|
|
22
|
+
isPortAvailable?: (port: number) => boolean | Promise<boolean>;
|
|
23
|
+
}
|
|
24
|
+
export interface InstalledScaffoldFile {
|
|
25
|
+
path: string;
|
|
26
|
+
action: string;
|
|
27
|
+
}
|
|
28
|
+
export type InstalledScaffoldResult = {
|
|
29
|
+
status: "skipped";
|
|
30
|
+
reason: string;
|
|
31
|
+
} | {
|
|
32
|
+
status: "ready";
|
|
33
|
+
projectRoot: string;
|
|
34
|
+
target: string;
|
|
35
|
+
files: InstalledScaffoldFile[];
|
|
36
|
+
configurationPath: string;
|
|
37
|
+
configurationAction: string;
|
|
38
|
+
rootConfigGenerationSkipped: boolean;
|
|
39
|
+
};
|
|
40
|
+
export declare const renderInstalledRehearsalScaffold: (result: InstalledScaffoldResult) => string;
|
|
41
|
+
export declare const resolveRehearsalConsumerRoot: ({ packageRoot, initCwd, localPrefix, globalInstall, workspace, }?: ResolveConsumerRootOptions) => Promise<ConsumerRootResolution>;
|
|
42
|
+
export declare const createInstalledRehearsalScaffold: ({ packageRoot, environment, isPortAvailable, }?: InstalledScaffoldOptions) => Promise<InstalledScaffoldResult>;
|