@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.
Files changed (169) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +4 -1
  3. package/dist/package.json +100 -0
  4. package/dist/scripts/install/create_root_config.d.mts +3 -0
  5. package/dist/scripts/install/create_root_config.mjs +13 -0
  6. package/dist/scripts/runtime/manage_database.d.mts +6 -0
  7. package/{scripts → dist/scripts}/runtime/manage_database.mjs +6 -8
  8. package/dist/src/application/session.d.mts +96 -0
  9. package/dist/src/application/session.mjs +380 -0
  10. package/dist/src/baseline/artifact.d.mts +119 -0
  11. package/dist/src/baseline/artifact.mjs +522 -0
  12. package/dist/src/baseline/builder.d.mts +19 -0
  13. package/dist/src/baseline/builder.mjs +142 -0
  14. package/dist/src/baseline/input_discovery.d.mts +13 -0
  15. package/dist/src/baseline/input_discovery.mjs +149 -0
  16. package/dist/src/baseline/policy_review.d.mts +56 -0
  17. package/dist/src/baseline/policy_review.mjs +73 -0
  18. package/dist/src/baseline/preparation.d.mts +63 -0
  19. package/dist/src/baseline/preparation.mjs +295 -0
  20. package/dist/src/baseline/privacy_engine.d.mts +162 -0
  21. package/dist/src/baseline/privacy_engine.mjs +1162 -0
  22. package/dist/src/baseline/privacy_structured_json.d.mts +66 -0
  23. package/dist/src/baseline/privacy_structured_json.mjs +464 -0
  24. package/dist/src/baseline/sanitization_policy.d.mts +69 -0
  25. package/dist/src/baseline/sanitization_policy.mjs +227 -0
  26. package/dist/src/baseline/schema_snapshot.d.mts +19 -0
  27. package/dist/src/baseline/schema_snapshot.mjs +79 -0
  28. package/dist/src/cli/arguments.d.mts +43 -0
  29. package/dist/src/cli/arguments.mjs +135 -0
  30. package/dist/src/cli/guided.d.mts +51 -0
  31. package/dist/src/cli/guided.mjs +713 -0
  32. package/dist/src/cli/init_command.d.mts +21 -0
  33. package/dist/src/cli/init_command.mjs +90 -0
  34. package/dist/src/cli/project_commands.d.mts +157 -0
  35. package/dist/src/cli/project_commands.mjs +183 -0
  36. package/dist/src/cli/rehearsal.d.mts +7 -0
  37. package/dist/src/cli/rehearsal.mjs +706 -0
  38. package/dist/src/cli/renderers.d.mts +299 -0
  39. package/dist/src/cli/renderers.mjs +554 -0
  40. package/dist/src/cli/runtime_commands.d.mts +265 -0
  41. package/dist/src/cli/runtime_commands.mjs +599 -0
  42. package/dist/src/cli/source_commands.d.mts +432 -0
  43. package/dist/src/cli/source_commands.mjs +299 -0
  44. package/dist/src/cli/terminal.d.mts +59 -0
  45. package/dist/src/cli/terminal.mjs +230 -0
  46. package/dist/src/identity/claim.d.mts +144 -0
  47. package/dist/src/identity/claim.mjs +947 -0
  48. package/dist/src/identity/provider_configuration.d.mts +37 -0
  49. package/dist/src/identity/provider_configuration.mjs +132 -0
  50. package/dist/src/identity/storage.d.mts +21 -0
  51. package/dist/src/identity/storage.mjs +122 -0
  52. package/dist/src/index.d.mts +4 -0
  53. package/dist/src/index.mjs +3 -0
  54. package/dist/src/project/configuration.d.mts +181 -0
  55. package/dist/src/project/configuration.mjs +958 -0
  56. package/dist/src/project/install_scaffold.d.mts +42 -0
  57. package/dist/src/project/install_scaffold.mjs +263 -0
  58. package/dist/src/project/setup.d.mts +87 -0
  59. package/dist/src/project/setup.mjs +414 -0
  60. package/dist/src/project/support_report.d.mts +88 -0
  61. package/dist/src/project/support_report.mjs +83 -0
  62. package/dist/src/runtime/cleanup.d.mts +85 -0
  63. package/dist/src/runtime/cleanup.mjs +311 -0
  64. package/dist/src/runtime/migration_history.d.mts +51 -0
  65. package/dist/src/runtime/migration_history.mjs +170 -0
  66. package/dist/src/runtime/plan.d.mts +134 -0
  67. package/dist/src/runtime/plan.mjs +636 -0
  68. package/dist/src/runtime/policy.d.mts +66 -0
  69. package/dist/src/runtime/policy.mjs +369 -0
  70. package/dist/src/runtime/restore.d.mts +22 -0
  71. package/dist/src/runtime/restore.mjs +276 -0
  72. package/dist/src/runtime/service_environment.d.mts +11 -0
  73. package/dist/src/runtime/service_environment.mjs +58 -0
  74. package/dist/src/runtime/topology.d.mts +51 -0
  75. package/dist/src/runtime/topology.mjs +128 -0
  76. package/dist/src/shared/diagnostics.d.mts +97 -0
  77. package/dist/src/shared/diagnostics.mjs +170 -0
  78. package/dist/src/shared/human_output.d.mts +2 -0
  79. package/{src → dist/src}/shared/human_output.mjs +1 -3
  80. package/dist/src/shared/node_runtime.d.mts +14 -0
  81. package/dist/src/shared/node_runtime.mjs +25 -0
  82. package/dist/src/shared/operation_guard.d.mts +22 -0
  83. package/dist/src/shared/operation_guard.mjs +150 -0
  84. package/dist/src/shared/process_environment.d.mts +15 -0
  85. package/dist/src/shared/process_environment.mjs +50 -0
  86. package/dist/src/source/access.d.mts +165 -0
  87. package/dist/src/source/access.mjs +602 -0
  88. package/dist/src/source/asset_transfer.d.mts +27 -0
  89. package/dist/src/source/asset_transfer.mjs +202 -0
  90. package/dist/src/source/baseline.d.mts +66 -0
  91. package/dist/src/source/baseline.mjs +372 -0
  92. package/dist/src/source/external_reader.d.mts +42 -0
  93. package/dist/src/source/external_reader.mjs +278 -0
  94. package/dist/src/source/postgresql_access.d.mts +86 -0
  95. package/dist/src/source/postgresql_access.mjs +454 -0
  96. package/dist/src/source/privacy_paths.d.mts +10 -0
  97. package/dist/src/source/privacy_paths.mjs +27 -0
  98. package/dist/src/targets/driver.d.mts +2 -0
  99. package/dist/src/targets/driver.mjs +13 -0
  100. package/dist/src/targets/postgresql.d.mts +5 -0
  101. package/dist/src/targets/postgresql.mjs +641 -0
  102. package/dist/src/targets/supabase.d.mts +8 -0
  103. package/dist/src/targets/supabase.mjs +659 -0
  104. package/dist/src/targets/supabase_environment.d.mts +49 -0
  105. package/dist/src/targets/supabase_environment.mjs +162 -0
  106. package/dist/src/targets/target.d.mts +33 -0
  107. package/dist/src/targets/target.mjs +39 -0
  108. package/docs/README.md +1 -0
  109. package/docs/architecture.md +17 -9
  110. package/docs/configuration.md +49 -8
  111. package/docs/getting-started.md +28 -0
  112. package/docs/index.md +1 -0
  113. package/docs/production-source.md +43 -7
  114. package/docs/releasing.md +5 -3
  115. package/docs/roadmap.md +3 -2
  116. package/docs/sanitization.md +415 -14
  117. package/docs/standalone-workflow.md +12 -6
  118. package/docs/troubleshooting.md +5 -1
  119. package/docs/tutorial.md +5 -0
  120. package/docs/typescript.md +87 -0
  121. package/package.json +35 -29
  122. package/scripts/install/create_root_config.mjs +0 -36
  123. package/src/README.md +0 -17
  124. package/src/application/session.mjs +0 -435
  125. package/src/baseline/artifact.mjs +0 -684
  126. package/src/baseline/builder.mjs +0 -164
  127. package/src/baseline/input_discovery.mjs +0 -155
  128. package/src/baseline/policy_review.mjs +0 -92
  129. package/src/baseline/preparation.mjs +0 -323
  130. package/src/baseline/privacy_engine.mjs +0 -952
  131. package/src/baseline/sanitization_policy.mjs +0 -299
  132. package/src/baseline/schema_snapshot.mjs +0 -113
  133. package/src/cli/arguments.mjs +0 -126
  134. package/src/cli/guided.mjs +0 -814
  135. package/src/cli/init_command.mjs +0 -94
  136. package/src/cli/rehearsal.mjs +0 -970
  137. package/src/cli/renderers.mjs +0 -626
  138. package/src/cli/runtime_commands.mjs +0 -707
  139. package/src/cli/source_commands.mjs +0 -347
  140. package/src/cli/terminal.mjs +0 -275
  141. package/src/identity/claim.mjs +0 -1163
  142. package/src/identity/provider_configuration.mjs +0 -177
  143. package/src/identity/storage.mjs +0 -165
  144. package/src/project/configuration.mjs +0 -1337
  145. package/src/project/install_scaffold.mjs +0 -291
  146. package/src/project/setup.mjs +0 -466
  147. package/src/project/support_report.mjs +0 -105
  148. package/src/runtime/cleanup.mjs +0 -381
  149. package/src/runtime/migration_history.mjs +0 -220
  150. package/src/runtime/plan.mjs +0 -722
  151. package/src/runtime/policy.mjs +0 -438
  152. package/src/runtime/restore.mjs +0 -337
  153. package/src/runtime/service_environment.mjs +0 -82
  154. package/src/runtime/topology.mjs +0 -177
  155. package/src/shared/diagnostics.mjs +0 -207
  156. package/src/shared/node_runtime.mjs +0 -31
  157. package/src/shared/operation_guard.mjs +0 -162
  158. package/src/shared/process_environment.mjs +0 -68
  159. package/src/source/access.mjs +0 -753
  160. package/src/source/asset_transfer.mjs +0 -207
  161. package/src/source/baseline.mjs +0 -482
  162. package/src/source/external_reader.mjs +0 -255
  163. package/src/source/postgresql_access.mjs +0 -581
  164. package/src/source/privacy_paths.mjs +0 -38
  165. package/src/targets/postgresql.mjs +0 -808
  166. package/src/targets/supabase.mjs +0 -840
  167. package/src/targets/supabase_environment.mjs +0 -229
  168. package/src/targets/target.mjs +0 -66
  169. /package/{src/project → types}/configuration.d.mts +0 -0
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Purpose: Start and inspect local-only Supabase workdirs without inheriting hosted
3
+ * credentials. Do not run directly; this module is reusable script infrastructure.
4
+ */
5
+ import { type ProcessEnvironment } from "../shared/process_environment.mjs";
6
+ export interface LocalSupabaseEnvironment {
7
+ readonly apiUrl: string;
8
+ readonly databaseUrl: string;
9
+ readonly publishableKey: string;
10
+ readonly serviceRoleKey: string;
11
+ readonly studioUrl: string | undefined;
12
+ readonly databaseHost: string;
13
+ readonly databasePort: string;
14
+ readonly databaseName: string;
15
+ readonly databaseUser: string;
16
+ readonly databasePassword: string;
17
+ }
18
+ interface LocalCommandOptions {
19
+ readonly capture?: boolean;
20
+ readonly cwd?: string | undefined;
21
+ readonly input?: string | Uint8Array;
22
+ readonly environment?: ProcessEnvironment;
23
+ readonly maxBuffer?: number;
24
+ }
25
+ interface SupabaseCommandOptions {
26
+ readonly cwd?: string | undefined;
27
+ readonly workdir?: string | undefined;
28
+ readonly environment?: ProcessEnvironment;
29
+ }
30
+ export declare const parseSupabaseStatusEnvironment: (output: string) => Record<string, string>;
31
+ export declare const parseLocalSupabaseEnvironment: (output: string) => LocalSupabaseEnvironment;
32
+ export declare const runLocalCommand: (command: string, args: readonly string[], { capture, cwd, input, environment, maxBuffer, }?: LocalCommandOptions) => string;
33
+ export declare const localCommandSucceeds: (command: string, args: readonly string[], { cwd, environment, }?: Pick<LocalCommandOptions, "cwd" | "environment">) => boolean;
34
+ export declare const ensureLocalContainerRuntime: ({ cwd, autoStartColima, }?: {
35
+ cwd?: string | undefined;
36
+ autoStartColima?: boolean;
37
+ }) => void;
38
+ export declare const readLocalSupabaseEnvironment: ({ cwd, workdir, environment, }?: SupabaseCommandOptions) => LocalSupabaseEnvironment;
39
+ export declare const startLocalSupabase: ({ cwd, workdir, exclude, environment, applyMigrations, autoStartColima, }?: SupabaseCommandOptions & {
40
+ exclude?: readonly string[];
41
+ applyMigrations?: boolean;
42
+ autoStartColima?: boolean;
43
+ }) => LocalSupabaseEnvironment;
44
+ export declare const stopLocalSupabase: ({ cwd, workdir, environment, }?: SupabaseCommandOptions) => void;
45
+ export declare const removeLocalSupabaseProjectResources: ({ cwd, projectId, }?: {
46
+ cwd?: string;
47
+ projectId?: string;
48
+ }) => void;
49
+ export {};
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Purpose: Start and inspect local-only Supabase workdirs without inheriting hosted
3
+ * credentials. Do not run directly; this module is reusable script infrastructure.
4
+ */
5
+ import { spawnSync } from "node:child_process";
6
+ import { createCleanProcessEnvironment, } from "../shared/process_environment.mjs";
7
+ export const parseSupabaseStatusEnvironment = (output) => {
8
+ const values = {};
9
+ for (const line of output.split("\n")) {
10
+ const match = line.match(/^([A-Z][A-Z0-9_]*)=(.*)$/u);
11
+ if (!match)
12
+ continue;
13
+ const key = match[1];
14
+ const rawValue = match[2];
15
+ if (key === undefined || rawValue === undefined)
16
+ continue;
17
+ let value = rawValue.trim();
18
+ if (value.startsWith('"') && value.endsWith('"')) {
19
+ const parsed = JSON.parse(value);
20
+ if (typeof parsed !== "string")
21
+ continue;
22
+ value = parsed;
23
+ }
24
+ values[key] = value;
25
+ }
26
+ return values;
27
+ };
28
+ export const parseLocalSupabaseEnvironment = (output) => {
29
+ const values = parseSupabaseStatusEnvironment(output);
30
+ const required = (label, value) => {
31
+ if (!value)
32
+ throw new Error(`Local Supabase status omitted its ${label}.`);
33
+ return value;
34
+ };
35
+ const apiUrl = required("API URL", values.API_URL);
36
+ const databaseUrl = required("database URL", values.DB_URL);
37
+ const publishableKey = required("publishable key", values.PUBLISHABLE_KEY ?? values.ANON_KEY);
38
+ const serviceRoleKey = required("service-role key", values.SERVICE_ROLE_KEY ?? values.SECRET_KEY);
39
+ const api = new URL(apiUrl);
40
+ const database = new URL(databaseUrl);
41
+ const loopbackHosts = ["127.0.0.1", "::1", "localhost"];
42
+ if (!loopbackHosts.includes(api.hostname) ||
43
+ !loopbackHosts.includes(database.hostname) ||
44
+ !["postgres:", "postgresql:"].includes(database.protocol)) {
45
+ throw new Error("Refusing to use a non-local Supabase status target.");
46
+ }
47
+ const databaseName = decodeURIComponent(database.pathname.replace(/^\//u, ""));
48
+ if (!database.port ||
49
+ !databaseName ||
50
+ !database.username ||
51
+ !database.password) {
52
+ throw new Error("Local Supabase status returned an incomplete database URL.");
53
+ }
54
+ return {
55
+ apiUrl,
56
+ databaseUrl,
57
+ publishableKey,
58
+ serviceRoleKey,
59
+ studioUrl: values.STUDIO_URL,
60
+ databaseHost: database.hostname,
61
+ databasePort: database.port,
62
+ databaseName,
63
+ databaseUser: decodeURIComponent(database.username),
64
+ databasePassword: decodeURIComponent(database.password),
65
+ };
66
+ };
67
+ export const runLocalCommand = (command, args, { capture = false, cwd = process.cwd(), input, environment = {}, maxBuffer = 16 * 1024 * 1024, } = {}) => {
68
+ const shouldPipe = capture || input !== undefined;
69
+ const result = spawnSync(command, args, {
70
+ cwd,
71
+ encoding: "utf8",
72
+ env: createCleanProcessEnvironment({ overrides: environment }),
73
+ input,
74
+ maxBuffer,
75
+ stdio: shouldPipe ? ["pipe", "pipe", "pipe"] : "inherit",
76
+ });
77
+ if (result.error)
78
+ throw result.error;
79
+ if (result.status !== 0) {
80
+ const detail = [result.stdout, result.stderr]
81
+ .filter(Boolean)
82
+ .join("\n")
83
+ .trim();
84
+ throw new Error(`${command} ${args.join(" ")} failed${detail ? `:\n${detail}` : "."}`);
85
+ }
86
+ return result.stdout ?? "";
87
+ };
88
+ export const localCommandSucceeds = (command, args, { cwd = process.cwd(), environment = {}, } = {}) => spawnSync(command, args, {
89
+ cwd,
90
+ env: createCleanProcessEnvironment({ overrides: environment }),
91
+ stdio: "ignore",
92
+ }).status === 0;
93
+ export const ensureLocalContainerRuntime = ({ cwd = process.cwd(), autoStartColima = true, } = {}) => {
94
+ if (localCommandSucceeds("docker", ["info"], { cwd }))
95
+ return;
96
+ if (autoStartColima && localCommandSucceeds("colima", ["version"], { cwd })) {
97
+ runLocalCommand("colima", ["start"], { cwd });
98
+ }
99
+ if (!localCommandSucceeds("docker", ["info"], { cwd })) {
100
+ throw new Error("A local Docker-compatible runtime is required. Install Docker or Colima, then rerun the command.");
101
+ }
102
+ };
103
+ const withWorkdir = (args, workdir) => (workdir ? [...args, "--workdir", workdir] : [...args]);
104
+ export const readLocalSupabaseEnvironment = ({ cwd, workdir, environment = {}, } = {}) => {
105
+ const output = runLocalCommand("supabase", withWorkdir(["status", "-o", "env"], workdir), { capture: true, cwd, environment });
106
+ return parseLocalSupabaseEnvironment(output);
107
+ };
108
+ export const startLocalSupabase = ({ cwd, workdir, exclude = [], environment = {}, applyMigrations = true, autoStartColima = true, } = {}) => {
109
+ ensureLocalContainerRuntime({ cwd, autoStartColima });
110
+ const startArguments = withWorkdir(["start", ...(exclude.length ? ["--exclude", exclude.join(",")] : [])], workdir);
111
+ runLocalCommand("supabase", startArguments, {
112
+ capture: true,
113
+ cwd,
114
+ environment,
115
+ });
116
+ if (applyMigrations) {
117
+ runLocalCommand("supabase", withWorkdir(["migration", "up", "--local"], workdir), { capture: true, cwd, environment });
118
+ }
119
+ return readLocalSupabaseEnvironment({ cwd, workdir, environment });
120
+ };
121
+ export const stopLocalSupabase = ({ cwd, workdir, environment = {}, } = {}) => {
122
+ if (!localCommandSucceeds("docker", ["info"], { cwd }))
123
+ return;
124
+ runLocalCommand("supabase", withWorkdir(["stop"], workdir), {
125
+ cwd,
126
+ environment,
127
+ });
128
+ };
129
+ export const removeLocalSupabaseProjectResources = ({ cwd = process.cwd(), projectId, } = {}) => {
130
+ if (!/^[a-z0-9][a-z0-9-]{1,62}$/u.test(projectId ?? "")) {
131
+ throw new Error("Refusing to remove local Supabase resources without an exact safe project id.");
132
+ }
133
+ const label = `com.supabase.cli.project=${projectId}`;
134
+ const list = (resource, format) => runLocalCommand("docker", [resource, "ls", "--filter", `label=${label}`, "--format", format], { capture: true, cwd })
135
+ .split("\n")
136
+ .map((value) => value.trim())
137
+ .filter(Boolean);
138
+ const containers = runLocalCommand("docker", ["ps", "--all", "--filter", `label=${label}`, "--format", "{{.ID}}"], { capture: true, cwd })
139
+ .split("\n")
140
+ .map((value) => value.trim())
141
+ .filter(Boolean);
142
+ if (containers.length) {
143
+ runLocalCommand("docker", ["rm", "--force", ...containers], {
144
+ capture: true,
145
+ cwd,
146
+ });
147
+ }
148
+ const volumes = list("volume", "{{.Name}}");
149
+ if (volumes.length) {
150
+ runLocalCommand("docker", ["volume", "rm", "--force", ...volumes], {
151
+ capture: true,
152
+ cwd,
153
+ });
154
+ }
155
+ const networks = list("network", "{{.ID}}");
156
+ if (networks.length) {
157
+ runLocalCommand("docker", ["network", "rm", ...networks], {
158
+ capture: true,
159
+ cwd,
160
+ });
161
+ }
162
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Purpose: Define the small, closed set of database runtimes Rehearsal can select.
3
+ * Runtime implementations stay behind this boundary so the shared CLI does not
4
+ * need target-specific branches.
5
+ */
6
+ declare const TARGETS: Readonly<{
7
+ supabase: Readonly<{
8
+ id: "supabase";
9
+ label: "Supabase";
10
+ }>;
11
+ postgresql: Readonly<{
12
+ id: "postgresql";
13
+ label: "PostgreSQL";
14
+ }>;
15
+ }>;
16
+ export type RehearsalRuntimeTargetId = keyof typeof TARGETS;
17
+ export type RehearsalRuntimeTarget = (typeof TARGETS)[RehearsalRuntimeTargetId];
18
+ export interface RuntimeInvocation {
19
+ readonly action: string;
20
+ readonly configPath: string | undefined;
21
+ readonly confirmation: string | undefined;
22
+ }
23
+ export interface RuntimeManagerArguments {
24
+ managerPath: string;
25
+ action: string;
26
+ configPath?: string;
27
+ confirmation?: string;
28
+ }
29
+ export declare const REHEARSAL_RUNTIME_TARGETS: readonly ("postgresql" | "supabase")[];
30
+ export declare const parseRuntimeInvocation: (arguments_?: readonly string[]) => RuntimeInvocation;
31
+ export declare const buildRuntimeManagerArguments: ({ managerPath, action, configPath, confirmation, }: RuntimeManagerArguments) => string[];
32
+ export declare const resolveRuntimeTarget: (value?: unknown) => RehearsalRuntimeTarget;
33
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Purpose: Define the small, closed set of database runtimes Rehearsal can select.
3
+ * Runtime implementations stay behind this boundary so the shared CLI does not
4
+ * need target-specific branches.
5
+ */
6
+ const TARGETS = Object.freeze({
7
+ supabase: Object.freeze({
8
+ id: "supabase",
9
+ label: "Supabase",
10
+ }),
11
+ postgresql: Object.freeze({
12
+ id: "postgresql",
13
+ label: "PostgreSQL",
14
+ }),
15
+ });
16
+ export const REHEARSAL_RUNTIME_TARGETS = Object.freeze(Object.keys(TARGETS));
17
+ export const parseRuntimeInvocation = (arguments_ = process.argv.slice(2)) => {
18
+ const action = arguments_.find((argument) => !argument.startsWith("--"));
19
+ const valueAfter = (prefix) => arguments_
20
+ .find((argument) => argument.startsWith(prefix))
21
+ ?.slice(prefix.length);
22
+ return Object.freeze({
23
+ action: action ?? "status",
24
+ configPath: valueAfter("--config="),
25
+ confirmation: valueAfter("--confirm-candidates="),
26
+ });
27
+ };
28
+ export const buildRuntimeManagerArguments = ({ managerPath, action, configPath, confirmation, }) => [
29
+ managerPath,
30
+ action,
31
+ ...(configPath ? [`--config=${configPath}`] : []),
32
+ ...(confirmation ? [`--confirm-candidates=${confirmation}`] : []),
33
+ ];
34
+ export const resolveRuntimeTarget = (value = "supabase") => {
35
+ if (typeof value !== "string" || !Object.hasOwn(TARGETS, value)) {
36
+ throw new Error(`Unsupported Rehearsal runtime target: ${String(value)}. Supported targets: ${REHEARSAL_RUNTIME_TARGETS.join(", ")}.`);
37
+ }
38
+ return TARGETS[value];
39
+ };
package/docs/README.md CHANGED
@@ -24,6 +24,7 @@ want to accomplish and follow that path.
24
24
 
25
25
  - [CLI commands](commands.md)
26
26
  - [Configuration](configuration.md)
27
+ - [TypeScript](typescript.md)
27
28
  - [Runtime and identity policies](runtime-policies.md)
28
29
  - [Database adapters](adapters.md)
29
30
  - [Glossary](glossary.md)
@@ -5,7 +5,7 @@ documentation so each file has one obvious home.
5
5
 
6
6
  ```text
7
7
  rehearsal-db/
8
- ├── src/ shipped package code
8
+ ├── src/ TypeScript product source
9
9
  │ ├── application/ application lifecycle and HTTP proofs
10
10
  │ ├── baseline/ sanitized baseline creation and validation
11
11
  │ ├── cli/ the `rehearsal` executable
@@ -16,8 +16,12 @@ rehearsal-db/
16
16
  │ ├── source/ approved source access and refresh
17
17
  │ └── targets/ PostgreSQL and Supabase adapters
18
18
  ├── scripts/
19
- │ ├── runtime/ shipped internal runtime launcher
19
+ │ ├── build/ clean TypeScript compilation helpers
20
+ │ ├── install/ TypeScript install-hook source
21
+ │ ├── runtime/ TypeScript runtime-launcher source
20
22
  │ └── verification/ repository-only package and fixture proofs
23
+ ├── dist/ generated ESM and declaration output; never edit directly
24
+ ├── types/ reviewed public root declaration contract
21
25
  ├── tests/
22
26
  │ ├── unit/ mirrors the `src/` domains
23
27
  │ ├── integration/ multi-module and terminal behavior
@@ -29,6 +33,7 @@ rehearsal-db/
29
33
  ## Boundaries
30
34
 
31
35
  - `src/` must never import from tests or repository-only verification scripts.
36
+ - `dist/` is rebuilt from TypeScript before tests and packaging; never hand-edit it.
32
37
  - Database-specific lifecycle behavior belongs in `src/targets/`.
33
38
  - `scripts/verification/` is never included in the npm package.
34
39
  - Public consumers use only the paths declared in `package.json#exports`.
@@ -40,13 +45,13 @@ rehearsal-db/
40
45
  The executable is split by responsibility so no command file becomes a second
41
46
  application layer:
42
47
 
43
- - `rehearsal.mjs` starts the process and dispatches commands.
44
- - `arguments.mjs` owns the command-line contract and mutation classification.
45
- - `guided.mjs` owns the interactive home screen and first-run questions.
46
- - `runtime_commands.mjs` coordinates local runtimes and project proofs.
47
- - `source_commands.mjs` coordinates approved source access and refresh.
48
- - `renderers.mjs` formats human-readable results without changing state.
49
- - `terminal.mjs` owns prompts, colors, and terminal exit behavior.
48
+ - `rehearsal.mts` starts the process and dispatches commands.
49
+ - `arguments.mts` owns the command-line contract and mutation classification.
50
+ - `guided.mts` owns the interactive home screen and first-run questions.
51
+ - `runtime_commands.mts` coordinates local runtimes and project proofs.
52
+ - `source_commands.mts` coordinates approved source access and refresh.
53
+ - `renderers.mts` formats human-readable results without changing state.
54
+ - `terminal.mts` owns prompts, colors, and terminal exit behavior.
50
55
 
51
56
  ## Adding something new
52
57
 
@@ -56,3 +61,6 @@ application layer:
56
61
  4. Add an integration or contract test only when behavior crosses domains or becomes a
57
62
  public promise.
58
63
  5. Update the documentation map when adding a new guide.
64
+
65
+ Run `npm run build` after changing TypeScript source. Tests import the generated package
66
+ code so the same JavaScript boundary consumers install is exercised locally.
@@ -25,6 +25,11 @@ Use this page when changing the generated file. The schema is strict: misspelled
25
25
  unknown fields, and unsupported versions are errors. The `.mjs` extension works in both
26
26
  CommonJS and ESM projects.
27
27
 
28
+ The generated file also receives editor and compiler checking through the package's
29
+ public TypeScript declarations. See [TypeScript](typescript.md) for the supported type
30
+ surface and checked examples. Keep the runnable configuration as `.mjs`; a project does
31
+ not need to rename it to `.ts`.
32
+
28
33
  ```ts
29
34
  // @ts-check
30
35
  import { defineRehearsalConfig } from "@rehearsal-db/core";
@@ -130,15 +135,20 @@ All paths resolve inside the consuming project. The artifact directory must be n
130
135
  `.rehearsal`; this is an intentional deletion guard. `runtimeWorkdir` must be its
131
136
  `runtime` child, and the generated application environment file must remain inside it.
132
137
 
133
- Rehearsal never overwrites this config. To start over, move the existing file somewhere
134
- safe, run setup again, and compare the two files before deleting either one.
135
-
138
+ Rehearsal never overwrites this config. Do not move it merely to inspect a newer template.
136
139
  After upgrading, run `npx rehearsal init` to print the current release's generated
137
140
  template beside the path of your existing config. This comparison is read-only; adding a
138
141
  new optional key remains your decision. `npx rehearsal init --write` still refuses to
139
142
  replace an existing config. Rehearsal lists important optional settings that are
140
143
  available but not enabled before printing the template, so they are easier to find.
141
144
 
145
+ If setup was interrupted after creating only some files, rerun
146
+ `npx rehearsal setup --write`. It recreates missing generated files while preserving every
147
+ existing config and local runtime file. When a config itself is invalid, keep it as a
148
+ backup, generate a fresh file, and manually carry over only reviewed settings. See
149
+ [Getting started](getting-started.md#if-setup-or-an-upgrade-is-interrupted) for the short
150
+ recovery table.
151
+
142
152
  ### Workspaces and custom paths
143
153
 
144
154
  For an npm workspace, automatic creation proceeds only when exactly one application
@@ -151,8 +161,38 @@ checkout never receive project files.
151
161
 
152
162
  Supported existing config paths—including
153
163
  `infrastructure/rehearsal/rehearsal.config.mjs`—are preserved, and a second root config
154
- is not created. Use `--config=<project-relative-path>` for other explicit paths when
155
- running commands.
164
+ is not created. The install hook reports the exact active path when npm displays lifecycle
165
+ output, and setup always prints it. `npx rehearsal init --json` is the package-manager-
166
+ independent way to check the `destination`. Rehearsal checks the supported root filenames
167
+ first and then the supported nested filenames; finding any one of them prevents automatic
168
+ creation of another. Use
169
+ `--config=<project-relative-path>` for other explicit paths when running commands.
170
+
171
+ ### Move an existing config to the project root
172
+
173
+ Relocation is deliberately manual because the config may contain reviewed settings and
174
+ JavaScript imports. Rehearsal will never move it or create a competing root file.
175
+
176
+ From the directory containing `package.json`:
177
+
178
+ 1. Stop active Rehearsal commands and confirm the current path with
179
+ `npx rehearsal init --json`.
180
+ 2. Make a backup outside every supported discovery path, such as
181
+ `.rehearsal/config-backups/rehearsal.config.mjs`.
182
+ 3. Move—do not copy—the active file to `./rehearsal.config.mjs`. Use `git mv` when it is
183
+ tracked so review history remains clear.
184
+ 4. Update relative JavaScript `import` specifiers inside the moved file. Imports resolve
185
+ from the config file's own folder, so a nested `../../something.mjs` import usually
186
+ changes after moving to the root.
187
+ 5. Leave config values such as `migrationDirectory`, `sanitizationPolicy`,
188
+ `runtimePolicy`, `identityPolicy`, and `dependentTargets[].configPath` unchanged unless
189
+ the referenced project file also moved. Rehearsal resolves those values from the
190
+ project root, not from the config file's folder.
191
+ 6. Run `npx rehearsal init --json`, confirm its `destination` is
192
+ `rehearsal.config.mjs`, then run `npx rehearsal doctor` and review the Git diff.
193
+
194
+ If validation fails, restore the backup to its original path. Do not run setup while the
195
+ move is half-finished; setup may correctly conclude that no supported config exists.
156
196
 
157
197
  `preparation.privacyKey` must stay inside `.rehearsal`; it is ignored and owner-readable
158
198
  only. Source, runtime, and identity policy files are declarations that may be reviewed in
@@ -431,9 +471,10 @@ may use these explicit subpaths:
431
471
  - `@rehearsal-db/core/service-environment`
432
472
  - `@rehearsal-db/core/source-access`
433
473
 
434
- These advanced entry points are ESM JavaScript APIs in the first beta. The root
435
- configuration and sanitization API has TypeScript declarations; the advanced subpaths do
436
- not yet promise a typed surface.
474
+ These advanced entry points ship as ordinary ESM JavaScript with generated TypeScript
475
+ declarations. The root configuration and sanitization contract remains the explicitly
476
+ reviewed public type surface. The exact current boundary is documented in
477
+ [TypeScript](typescript.md).
437
478
 
438
479
  Undocumented files under `scripts/` are package internals and are not compatibility
439
480
  contracts.
@@ -48,6 +48,11 @@ especially:
48
48
  - the application start and proof commands;
49
49
  - the local ports.
50
50
 
51
+ If a supported configuration already exists elsewhere—such as
52
+ `infrastructure/rehearsal/rehearsal.config.mjs`—the installer reports that exact active
53
+ path and does not generate a competing root file. Keep using it, or follow the explicit
54
+ [root relocation guide](configuration.md#move-an-existing-config-to-the-project-root).
55
+
51
56
  If lifecycle scripts were disabled, a global/cache install was used, or a workspace has
52
57
  more than one possible application, no project files are created automatically. Run
53
58
  `npx rehearsal` inside the intended application, choose **Set the stage**, select
@@ -180,6 +185,29 @@ config during an upgrade. After upgrading, run `npx rehearsal init` to see impor
180
185
  optional settings you have not enabled and compare your file with the installed
181
186
  release's current template.
182
187
 
188
+ ### If setup or an upgrade is interrupted
189
+
190
+ Do not delete `.rehearsal/` or your existing config. Start with:
191
+
192
+ ```bash
193
+ npx rehearsal doctor
194
+ npx rehearsal init
195
+ ```
196
+
197
+ Use the reported problem to choose the smallest recovery:
198
+
199
+ | Problem | Safe recovery |
200
+ | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
201
+ | A generated runtime file is missing | Run `npx rehearsal setup --write`. Existing files stay unchanged; only missing files are created. |
202
+ | A port is already in use | Stop the conflicting local process or choose new dedicated ports, then rerun setup. |
203
+ | The config is malformed | Move it to a backup name, run `npx rehearsal setup --write`, then copy reviewed choices into the new file. |
204
+ | OAuth credentials are missing | Create the ignored file named by `supabase.authentication.environmentFile`, add only the declared keys, and run `chmod 600` on it. Ambient shell credentials are not used. |
205
+ | An npm upgrade fails | Keep the existing config, baseline, and lockfile. Restore dependencies with your package manager, then run `npx rehearsal init` before retrying. |
206
+ | Docker storage is low | Run `npx rehearsal cleanup` for an exact preview. Rehearsal never performs a global Docker prune or silently removes volumes. |
207
+
208
+ `setup --write` and package installation never replace a config. If an existing file is
209
+ invalid, Rehearsal fails closed and leaves it available for comparison.
210
+
183
211
  ## Files Rehearsal creates
184
212
 
185
213
  - `rehearsal.config.mjs` — reviewed project configuration
package/docs/index.md CHANGED
@@ -11,6 +11,7 @@ you want, then follow the links when a term or safety check is new.
11
11
  | Try Rehearsal without touching my app | [Hands-on tutorial](tutorial.md) |
12
12
  | Understand what makes a copy safe | [Baselines](baselines.md) and [Sanitization](sanitization.md) |
13
13
  | Look up a command or setting | [CLI commands](commands.md) and [Configuration](configuration.md) |
14
+ | Add editor and compiler type checks | [TypeScript](typescript.md) |
14
15
  | Fix something that did not work | [Troubleshooting](troubleshooting.md) |
15
16
 
16
17
  ## The short version
@@ -23,6 +23,15 @@ The privacy key stays ignored under `.rehearsal`. The policy is safe to review i
23
23
  control because it contains hashes and environment-variable names, not credentials,
24
24
  URLs, or raw owner identifiers.
25
25
 
26
+ `credentialFile` must be inside the selected target's
27
+ `baseline.artifactDirectory`, under its `secrets` folder. With the default artifact
28
+ directory, use `.rehearsal/secrets/source-reader.env`. For a target whose artifact
29
+ directory is `targets/primary/.rehearsal`, use
30
+ `targets/primary/.rehearsal/secrets/source-reader.env` and place `privacyKey` there as
31
+ well. Rehearsal stores that target's access receipt beside its baseline at
32
+ `targets/primary/.rehearsal/source-access-receipt.json`; separate targets never share
33
+ the receipt or credential.
34
+
26
35
  ## 2. Declare the source boundary
27
36
 
28
37
  `source-access-policy.json` uses this shape:
@@ -112,6 +121,7 @@ an administrator credential or change the source:
112
121
  "reader": {
113
122
  "mode": "external",
114
123
  "role": "provided_rehearsal_reader",
124
+ "allowedMemberships": ["provided_rehearsal_readers"],
115
125
  "connectionEnvironmentVariable": "REHEARSAL_SOURCE_READER_URL",
116
126
  "credentialFile": ".rehearsal/secrets/source-reader.env",
117
127
  "maximumValidForMinutes": 30
@@ -153,9 +163,25 @@ view hash, columns, scopes, or reader declaration requires a new verification.
153
163
 
154
164
  External mode also requires all non-system readable columns to match only the declared
155
165
  views and migration-ledger columns. It refuses database, schema, relation, or column
156
- writes; inherited roles; privileged role attributes; executable non-system
157
- security-definer functions; a reader-owned view; or a database-enforced expiration
158
- beyond `maximumValidForMinutes`.
166
+ writes; privileged role attributes; executable non-system security-definer functions;
167
+ a reader-owned view; or a database-enforced expiration beyond
168
+ `maximumValidForMinutes`.
169
+
170
+ This audit is intentionally conservative: an object-level table, column, sequence, or
171
+ function grant remains disallowed even when its schema currently lacks `USAGE`. Do not
172
+ use schema visibility to hide stale grants. Revoke the object grant or move the approved
173
+ surface behind the reviewed export views; this also avoids relying on behavior of an
174
+ already-open or prepared database session.
175
+
176
+ An external login may inherit its narrow permissions from provider-owned group roles.
177
+ List every direct group in `allowedMemberships`; omit the field or use `[]` when the
178
+ login has direct grants only. Rehearsal requires the observed membership set to match
179
+ that list exactly. Every allowed group must be non-login and non-privileged, grant
180
+ inheritance to the login, disable both `SET ROLE` and membership delegation, inherit no
181
+ other role, and own none of the export views. PostgreSQL versions that cannot prove the
182
+ per-membership role-switch setting are refused. Rehearsal still evaluates the login's
183
+ complete effective read, write, sequence, and executable-function surface after those
184
+ checks. Adding, removing, or changing a group changes the reviewed plan digest.
159
185
 
160
186
  `targetSchema` is optional and defaults to `public`. Set it when the restored relation
161
187
  belongs elsewhere, and use the same schema in privacy policy version 2. This is relation
@@ -167,6 +193,9 @@ non-identity prefix may use `prefix`. A private owner prefix uses
167
193
  the privacy policy below. Its raw value stays out of tracked files and the review plan
168
194
  records only its SHA-256. Objects outside the exact bucket/prefix are not listed or
169
195
  downloaded. Size, object-count, path, ETag, and exact-byte checks apply.
196
+ Supabase folder entries are traversed recursively beneath that exact prefix. Folder
197
+ entries count toward `maximumObjects`, so the same reviewed limit bounds both the
198
+ directory walk and transferred files. Empty files remain files and are copied normally.
170
199
 
171
200
  ## 3. Use executable privacy policy version 2
172
201
 
@@ -227,10 +256,12 @@ Every selected column has one action and, where required, a bounded recipe:
227
256
  }
228
257
  ```
229
258
 
230
- Supported recipes are intentionally small: keyed UUID/email/text/integer pseudonyms,
231
- explicit constants, bounded or grouped date shifts, identity-aware path mappings, and
232
- fully classified JSON objects. Unknown tables, columns, JSON keys, formats, and recipes
233
- stop the refresh. There is no JavaScript or SQL escape hatch. See
259
+ Supported recipes are intentionally small: keyed UUID/email/text/integer/hex/GTIN/URL
260
+ pseudonyms, hexadecimal digests of explicitly named sanitized columns, explicit
261
+ constants, bounded or grouped date shifts, identity-aware path mappings, bounded
262
+ reviewed-identity substitution inside retained text, and fully classified JSON
263
+ structures. Unknown tables, columns, digest inputs, JSON keys, formats, and recipes stop
264
+ the refresh. There is no JavaScript or SQL escape hatch. See
234
265
  [Sanitization policy](sanitization.md) for complete path and date examples.
235
266
 
236
267
  ## 4. Preview, apply, refresh, and retire
@@ -290,6 +321,11 @@ limits access, and the provider may revoke it sooner. Rehearsal does not claim t
290
321
  cleanup revoked a provider-owned credential. Unrelated roles, grants, objects, baselines,
291
322
  runtimes, and Docker resources are preserved.
292
323
 
324
+ Apply, refresh, and retire always resolve those local files from the active config's
325
+ `baseline.artifactDirectory`. If you switch configs or target roots, Rehearsal refuses
326
+ to retire a receipt from the other target. It also refuses an existing receipt or
327
+ credential rather than overwriting or adopting it.
328
+
293
329
  ## Limits
294
330
 
295
331
  Rehearsal does not decide consent, licensing, retention rights, privileged-role meaning,
package/docs/releasing.md CHANGED
@@ -32,9 +32,11 @@ publication must change and prove the workflow before narrowing that permission.
32
32
  1. Start from protected `main` on a dedicated ticketed release branch.
33
33
  2. Verify the exact version, changelog, compatibility notes, and packed file list.
34
34
  3. Run unit and contract tests, formatting, package-content and secret audits,
35
- dependency audit, clean tarball installation, and the installed Docker fixture.
36
- 4. Have a developer unfamiliar with the implementing project follow the clean-project
37
- onboarding. Correct and retest the first confusing, missing, or wrong instruction.
35
+ dependency audit, packed onboarding, clean tarball installation, and the installed
36
+ Docker fixtures.
37
+ 4. When an unfamiliar developer is available, have them follow clean-project onboarding.
38
+ Correct and retest the first confusing, missing, or wrong instruction. Record when
39
+ this human observation was unavailable; automated checks must not impersonate it.
38
40
  5. Change `private` to `false` only in the reviewed release change.
39
41
  6. Record the exact tarball filename, SHA-1, SHA-256, allowlisted files, unpacked size,
40
42
  executable, declared runtime dependency inventory, and dependency audit result.
package/docs/roadmap.md CHANGED
@@ -29,8 +29,9 @@ Track the full breakdown in
29
29
  invalid migrations, identities, images, persistence, reset, and cleanup.
30
30
  - Run the released package in the real reporting consumer and record positives and
31
31
  negatives.
32
- - Directly observe any claimed Google account-picker/callback and unfamiliar-developer
33
- onboarding behavior; automation cannot invent that evidence.
32
+ - Directly observe any claimed Google account-picker/callback. Record
33
+ unfamiliar-developer onboarding behavior when a tester is available; automation cannot
34
+ invent either kind of human evidence.
34
35
  - Remove consumer integration code only after its replacement passes and that deletion
35
36
  is separately approved.
36
37