@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,37 @@
1
+ /** Render reviewed local OAuth declarations into the disposable Supabase runtime. */
2
+ export interface SupabaseAuthenticationProvider {
3
+ name: "google" | "github";
4
+ clientIdEnvironmentVariable: string;
5
+ clientSecretEnvironmentVariable: string;
6
+ skipNonceCheck?: boolean;
7
+ emailOptional?: boolean;
8
+ }
9
+ export interface SupabaseAuthentication {
10
+ providers: readonly SupabaseAuthenticationProvider[];
11
+ }
12
+ interface TomlSection {
13
+ name: string;
14
+ start: number;
15
+ end: number;
16
+ }
17
+ interface ProviderReplacementSection extends TomlSection {
18
+ provider: string;
19
+ }
20
+ interface ProviderTransition {
21
+ readonly replacedProviders: readonly string[];
22
+ readonly replacementSections: readonly ProviderReplacementSection[];
23
+ }
24
+ export declare const inspectSupabaseAuthenticationProviderTransition: ({ source, authentication, }: {
25
+ source: string;
26
+ authentication: SupabaseAuthentication | null;
27
+ }) => Readonly<ProviderTransition>;
28
+ export declare const renderSupabaseAuthenticationProviders: ({ authentication, apiPort, }: {
29
+ authentication: SupabaseAuthentication | null;
30
+ apiPort: number;
31
+ }) => string;
32
+ export declare const applySupabaseAuthenticationProviders: ({ source, authentication, apiPort, }: {
33
+ source: string;
34
+ authentication: SupabaseAuthentication | null;
35
+ apiPort: number;
36
+ }) => string;
37
+ export {};
@@ -0,0 +1,132 @@
1
+ /** Render reviewed local OAuth declarations into the disposable Supabase runtime. */
2
+ const escapePattern = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/gu, "\\$&");
3
+ const LEGACY_PROVIDER_KEYS = new Set([
4
+ "client_id",
5
+ "email_optional",
6
+ "enabled",
7
+ "redirect_uri",
8
+ "secret",
9
+ "skip_nonce_check",
10
+ ]);
11
+ const SAFE_BOOLEAN_PROVIDER_KEYS = new Set([
12
+ "email_optional",
13
+ "skip_nonce_check",
14
+ ]);
15
+ const readTomlSections = (source) => {
16
+ const lines = source.split("\n");
17
+ const sections = [];
18
+ for (let index = 0; index < lines.length; index += 1) {
19
+ const match = /^\s*\[([^\]]+)\]\s*(?:#.*)?$/u.exec(lines[index]);
20
+ if (!match)
21
+ continue;
22
+ if (sections.length)
23
+ sections.at(-1).end = index;
24
+ sections.push({ name: match[1], start: index, end: lines.length });
25
+ }
26
+ return { lines, sections };
27
+ };
28
+ const validateLegacyProviderSection = ({ lines, section, provider, }) => {
29
+ const seen = new Set();
30
+ for (const line of lines.slice(section.start + 1, section.end)) {
31
+ if (!line.trim() || /^\s*#/u.test(line))
32
+ continue;
33
+ const assignment = /^\s*([A-Za-z0-9_]+)\s*=/u.exec(line);
34
+ if (!assignment || !LEGACY_PROVIDER_KEYS.has(assignment[1])) {
35
+ throw new Error(`The local Supabase template auth.external.${provider.name} section contains an unsupported setting. Rehearsal left the tracked template unchanged.`);
36
+ }
37
+ const key = assignment[1];
38
+ if (seen.has(key)) {
39
+ throw new Error(`The local Supabase template auth.external.${provider.name} section contains duplicate ${key} settings.`);
40
+ }
41
+ seen.add(key);
42
+ if (SAFE_BOOLEAN_PROVIDER_KEYS.has(key) &&
43
+ !new RegExp(`^\\s*${escapePattern(key)}\\s*=\\s*${key === "skip_nonce_check"
44
+ ? (provider.skipNonceCheck ?? false)
45
+ : (provider.emailOptional ?? false)}\\s*(?:#.*)?$`, "u").test(line)) {
46
+ throw new Error(`The local Supabase template auth.external.${provider.name}.${key} setting must match its explicit declarative authentication option before Rehearsal can replace the legacy provider in its disposable runtime copy.`);
47
+ }
48
+ }
49
+ };
50
+ export const inspectSupabaseAuthenticationProviderTransition = ({ source, authentication, }) => {
51
+ if (!authentication)
52
+ return Object.freeze({ replacedProviders: [], replacementSections: [] });
53
+ const { lines, sections } = readTomlSections(source);
54
+ const replacementSections = [];
55
+ for (const provider of authentication.providers) {
56
+ const sectionName = `auth.external.${provider.name}`;
57
+ const nested = sections.filter((section) => section.name.startsWith(`${sectionName}.`));
58
+ if (nested.length) {
59
+ throw new Error(`The local Supabase template contains unsupported nested ${sectionName} configuration. Rehearsal left the tracked template unchanged.`);
60
+ }
61
+ const matching = sections.filter((section) => section.name === sectionName);
62
+ if (matching.length > 1) {
63
+ throw new Error(`The local Supabase template declares ${sectionName} more than once.`);
64
+ }
65
+ if (matching.length === 1) {
66
+ validateLegacyProviderSection({
67
+ lines,
68
+ section: matching[0],
69
+ provider,
70
+ });
71
+ replacementSections.push({ ...matching[0], provider: provider.name });
72
+ }
73
+ }
74
+ return Object.freeze({
75
+ replacedProviders: Object.freeze(replacementSections.map(({ provider }) => provider)),
76
+ replacementSections: Object.freeze(replacementSections),
77
+ });
78
+ };
79
+ export const renderSupabaseAuthenticationProviders = ({ authentication, apiPort, }) => {
80
+ if (!authentication)
81
+ return "";
82
+ return authentication.providers
83
+ .map((provider) => `# Generated by Rehearsal from config.supabase.authentication.
84
+ [auth.external.${provider.name}]
85
+ enabled = true
86
+ client_id = "env(${provider.clientIdEnvironmentVariable})"
87
+ secret = "env(${provider.clientSecretEnvironmentVariable})"
88
+ redirect_uri = "http://127.0.0.1:${apiPort}/auth/v1/callback"
89
+ ${provider.skipNonceCheck ? "skip_nonce_check = true\n" : ""}${provider.emailOptional ? "email_optional = true\n" : ""}
90
+ `)
91
+ .join("\n");
92
+ };
93
+ const enableLocalSignup = (source) => {
94
+ const lines = source.split("\n");
95
+ let inAuth = false;
96
+ let authSections = 0;
97
+ let signupSettings = 0;
98
+ const updated = lines.map((line) => {
99
+ const section = /^\s*\[([^\]]+)\]\s*(?:#.*)?$/u.exec(line);
100
+ if (section) {
101
+ inAuth = section[1] === "auth";
102
+ if (inAuth)
103
+ authSections += 1;
104
+ return line;
105
+ }
106
+ if (!inAuth || !/^\s*enable_signup\s*=/u.test(line))
107
+ return line;
108
+ signupSettings += 1;
109
+ const comment = line.match(/\s+#.*$/u)?.[0] ?? "";
110
+ return `enable_signup = true${comment}`;
111
+ });
112
+ if (authSections !== 1 || signupSettings !== 1) {
113
+ throw new Error("The local Supabase template must declare exactly one [auth] section with one enable_signup setting before Rehearsal can configure OAuth.");
114
+ }
115
+ return updated.join("\n");
116
+ };
117
+ export const applySupabaseAuthenticationProviders = ({ source, authentication, apiPort, }) => {
118
+ if (!authentication)
119
+ return source;
120
+ const transition = inspectSupabaseAuthenticationProviderTransition({
121
+ source,
122
+ authentication,
123
+ });
124
+ const { lines } = readTomlSections(source);
125
+ const replacedIndexes = new Set(transition.replacementSections.flatMap((section) => Array.from({ length: section.end - section.start }, (_, offset) => section.start + offset)));
126
+ const transitionedSource = lines
127
+ .filter((_, index) => !replacedIndexes.has(index))
128
+ .join("\n");
129
+ const runtimeSource = enableLocalSignup(transitionedSource);
130
+ const separator = runtimeSource.endsWith("\n") ? "\n" : "\n\n";
131
+ return `${runtimeSource}${separator}${renderSupabaseAuthenticationProviders({ authentication, apiPort })}`;
132
+ };
@@ -0,0 +1,21 @@
1
+ /** Copy and verify local Supabase Storage objects during an identity claim. */
2
+ export interface StorageObjectReceipt {
3
+ readonly bytes: number;
4
+ readonly sha256: string;
5
+ }
6
+ export interface SupabaseStorageTransfer {
7
+ copyAndVerify(input: {
8
+ bucket: string;
9
+ source: string;
10
+ destination: string;
11
+ }): Promise<StorageObjectReceipt>;
12
+ remove(input: {
13
+ bucket: string;
14
+ paths: readonly string[];
15
+ }): Promise<void>;
16
+ }
17
+ export declare const createSupabaseStorageTransfer: ({ url, serviceRoleKey, fetchImpl, }: {
18
+ url: string;
19
+ serviceRoleKey: string;
20
+ fetchImpl?: typeof fetch;
21
+ }) => SupabaseStorageTransfer;
@@ -0,0 +1,122 @@
1
+ /** Copy and verify local Supabase Storage objects during an identity claim. */
2
+ import { createHash } from "node:crypto";
3
+ import { assertLoopbackUrl } from "../shared/process_environment.mjs";
4
+ const BUCKET = /^[a-z0-9][a-z0-9.-]{0,99}$/u;
5
+ const MAXIMUM_VERIFICATION_BYTES = 100 * 1024 * 1024;
6
+ const REQUEST_TIMEOUT_MS = 30_000;
7
+ const objectPath = (value) => {
8
+ if (typeof value !== "string" ||
9
+ !value ||
10
+ value.startsWith("/") ||
11
+ value.split("/").some((part) => !part || part === "." || part === "..")) {
12
+ throw new Error("A Storage identity-claim path is unsafe.");
13
+ }
14
+ return value.split("/").map(encodeURIComponent).join("/");
15
+ };
16
+ const readVerifiedObject = async ({ fetchImpl, baseUrl, headers, bucket, path, }) => {
17
+ const response = await fetchImpl(`${baseUrl}/storage/v1/object/${encodeURIComponent(bucket)}/${objectPath(path)}?rehearsal=${Date.now()}`, {
18
+ headers,
19
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
20
+ });
21
+ if (!response.ok) {
22
+ throw new Error(`Supabase Storage could not read an identity-claim object (HTTP ${response.status}).`);
23
+ }
24
+ const declaredBytes = Number(response.headers.get("content-length"));
25
+ if (Number.isFinite(declaredBytes) &&
26
+ declaredBytes > MAXIMUM_VERIFICATION_BYTES) {
27
+ throw new Error("A Storage identity-claim object exceeds the 100 MiB verification limit.");
28
+ }
29
+ const bytes = Buffer.from(await response.arrayBuffer());
30
+ if (bytes.length > MAXIMUM_VERIFICATION_BYTES) {
31
+ throw new Error("A Storage identity-claim object exceeds the 100 MiB verification limit.");
32
+ }
33
+ return Object.freeze({
34
+ bytes: bytes.length,
35
+ sha256: createHash("sha256").update(bytes).digest("hex"),
36
+ });
37
+ };
38
+ export const createSupabaseStorageTransfer = ({ url, serviceRoleKey, fetchImpl = globalThis.fetch, }) => {
39
+ assertLoopbackUrl("Rehearsal Supabase Storage", url);
40
+ if (typeof serviceRoleKey !== "string" || !serviceRoleKey) {
41
+ throw new Error("The local runtime environment has no Storage service key.");
42
+ }
43
+ if (typeof fetchImpl !== "function") {
44
+ throw new Error("A Fetch implementation is required for Storage transfer.");
45
+ }
46
+ const baseUrl = url.replace(/\/$/u, "");
47
+ const headers = Object.freeze({
48
+ apikey: serviceRoleKey,
49
+ authorization: `Bearer ${serviceRoleKey}`,
50
+ });
51
+ const removePaths = async ({ bucket, paths, }) => {
52
+ if (!BUCKET.test(bucket ?? "") || !Array.isArray(paths) || !paths.length) {
53
+ throw new Error("A Storage identity-claim removal is invalid.");
54
+ }
55
+ paths.forEach(objectPath);
56
+ const response = await fetchImpl(`${baseUrl}/storage/v1/object/${encodeURIComponent(bucket)}`, {
57
+ method: "DELETE",
58
+ headers: { ...headers, "content-type": "application/json" },
59
+ body: JSON.stringify({ prefixes: paths }),
60
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
61
+ });
62
+ if (!response.ok) {
63
+ throw new Error(`Supabase Storage could not remove identity-claim objects (HTTP ${response.status}).`);
64
+ }
65
+ };
66
+ return Object.freeze({
67
+ async copyAndVerify({ bucket, source, destination, }) {
68
+ if (!BUCKET.test(bucket ?? "")) {
69
+ throw new Error("A Storage identity-claim bucket is invalid.");
70
+ }
71
+ objectPath(source);
72
+ objectPath(destination);
73
+ if (source === destination) {
74
+ throw new Error("A Storage identity-claim copy must change the path.");
75
+ }
76
+ const sourceReceipt = await readVerifiedObject({
77
+ fetchImpl,
78
+ baseUrl,
79
+ headers,
80
+ bucket,
81
+ path: source,
82
+ });
83
+ try {
84
+ const response = await fetchImpl(`${baseUrl}/storage/v1/object/copy`, {
85
+ method: "POST",
86
+ headers: { ...headers, "content-type": "application/json" },
87
+ body: JSON.stringify({
88
+ bucketId: bucket,
89
+ sourceKey: source,
90
+ destinationKey: destination,
91
+ }),
92
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
93
+ });
94
+ if (!response.ok) {
95
+ throw new Error(`Supabase Storage could not copy an identity-claim object (HTTP ${response.status}).`);
96
+ }
97
+ const destinationReceipt = await readVerifiedObject({
98
+ fetchImpl,
99
+ baseUrl,
100
+ headers,
101
+ bucket,
102
+ path: destination,
103
+ });
104
+ if (sourceReceipt.bytes !== destinationReceipt.bytes ||
105
+ sourceReceipt.sha256 !== destinationReceipt.sha256) {
106
+ throw new Error("Supabase Storage copied an identity object with a different byte digest.");
107
+ }
108
+ return destinationReceipt;
109
+ }
110
+ catch (error) {
111
+ try {
112
+ await removePaths({ bucket, paths: [destination] });
113
+ }
114
+ catch (cleanupError) {
115
+ throw new AggregateError([error, cleanupError], "Storage identity-copy verification failed and destination cleanup needs attention.");
116
+ }
117
+ throw error;
118
+ }
119
+ },
120
+ remove: removePaths,
121
+ });
122
+ };
@@ -0,0 +1,4 @@
1
+ /** Public Rehearsal package entry point. */
2
+ export { REHEARSAL_CONFIG_VERSION, defineRehearsalConfig, findRehearsalConfigPath, inspectDetectedProject, loadRehearsalConfig, renderDetectedConfig, renderDetectedPostgresqlConfig, } from "./project/configuration.mjs";
3
+ export type { DetectedProject, RehearsalConfig, RehearsalConfigPathOptions, RehearsalConfigVersion, } from "./project/configuration.mjs";
4
+ export { EXCLUDED_VALUE, SANITIZATION_ACTIONS, applySanitizationAction, normalizeSanitizationAction, readBoundRuntimeSanitizationPolicy, validateSanitizationCoverage, validateRuntimeSanitizationPolicy, } from "./baseline/sanitization_policy.mjs";
@@ -0,0 +1,3 @@
1
+ /** Public Rehearsal package entry point. */
2
+ export { REHEARSAL_CONFIG_VERSION, defineRehearsalConfig, findRehearsalConfigPath, inspectDetectedProject, loadRehearsalConfig, renderDetectedConfig, renderDetectedPostgresqlConfig, } from "./project/configuration.mjs";
3
+ export { EXCLUDED_VALUE, SANITIZATION_ACTIONS, applySanitizationAction, normalizeSanitizationAction, readBoundRuntimeSanitizationPolicy, validateSanitizationCoverage, validateRuntimeSanitizationPolicy, } from "./baseline/sanitization_policy.mjs";
@@ -0,0 +1,181 @@
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 type { RehearsalConfig, RehearsalConfigVersion } from "../../types/configuration.mjs";
7
+ export type { RehearsalConfig, RehearsalConfigVersion };
8
+ export declare const REHEARSAL_CONFIG_VERSION = 1;
9
+ export declare const REHEARSAL_DEFAULTS: Readonly<{
10
+ baseline: Readonly<{
11
+ artifactDirectory: ".rehearsal";
12
+ }>;
13
+ containerRuntime: Readonly<{
14
+ autoStartColima: true;
15
+ }>;
16
+ cleanup: Readonly<{
17
+ retainBaselineGenerations: 2;
18
+ }>;
19
+ runtime: Readonly<{
20
+ applicationUrl: "http://localhost:5175";
21
+ projectId: "rehearsal-local";
22
+ }>;
23
+ safety: Readonly<{
24
+ allowedHosts: readonly string[];
25
+ blockedEnvironmentVariables: readonly string[];
26
+ hostedAccess: "disabled";
27
+ outboundNetwork: "deny";
28
+ }>;
29
+ }>;
30
+ export interface DetectedProject {
31
+ projectName: string;
32
+ packageManager: "npm" | "pnpm" | "yarn";
33
+ hasSupabaseConfig: boolean;
34
+ hasMigrations: boolean;
35
+ hasPostgresqlMigrations: boolean;
36
+ postgresqlMigrationDirectory: string;
37
+ applicationCommand: string;
38
+ verificationCommand: string;
39
+ }
40
+ export interface RehearsalConfigPathOptions {
41
+ projectRoot?: string;
42
+ configPath?: string;
43
+ }
44
+ export declare const defineRehearsalConfig: <const Config extends RehearsalConfig>(config: Config) => Config;
45
+ export declare const findRehearsalConfigPath: ({ projectRoot, configPath, }?: RehearsalConfigPathOptions) => Promise<string>;
46
+ export declare const loadRehearsalConfig: ({ projectRoot, configPath, }?: RehearsalConfigPathOptions) => Promise<Readonly<{
47
+ config: Readonly<{
48
+ schemaVersion: 1;
49
+ project: Readonly<{
50
+ name: string;
51
+ }>;
52
+ supabase: Readonly<{
53
+ workdir: string;
54
+ migrationDirectory: string;
55
+ rehearsalConfig: string;
56
+ runtimeWorkdir: string;
57
+ authentication: Readonly<{
58
+ enableLocalSignup: true;
59
+ environmentFile: string;
60
+ providers: readonly Readonly<{
61
+ name: "github" | "google";
62
+ clientIdEnvironmentVariable: string;
63
+ clientSecretEnvironmentVariable: string;
64
+ skipNonceCheck: boolean;
65
+ emailOptional: boolean;
66
+ }>[];
67
+ }> | null;
68
+ serviceEnvironmentFile: string | null;
69
+ serviceEnvironmentVariables: readonly string[];
70
+ }> | null;
71
+ postgresql: Readonly<{
72
+ migrationDirectory: string;
73
+ runtimeWorkdir: string;
74
+ image: string;
75
+ database: string;
76
+ user: string;
77
+ }> | null;
78
+ baseline: Readonly<{
79
+ artifactDirectory: string;
80
+ sanitizationPolicy: string;
81
+ }>;
82
+ containerRuntime: Readonly<{
83
+ autoStartColima: boolean;
84
+ }>;
85
+ cleanup: Readonly<{
86
+ retainBaselineGenerations: number;
87
+ }>;
88
+ preparation: Readonly<{
89
+ sourcePolicy: string;
90
+ privacyKey: string;
91
+ batchRows: number;
92
+ maximumRows: number;
93
+ maximumBytes: number;
94
+ diskHeadroomBytes: number;
95
+ }> | null;
96
+ runtimePolicy: string | null;
97
+ identityPolicy: string | null;
98
+ dependentTargets: readonly Readonly<{
99
+ name: string;
100
+ configPath: string;
101
+ prepareCommand: string | null;
102
+ }>[];
103
+ application: Readonly<{
104
+ startCommand: string;
105
+ proofCommand: string;
106
+ environmentFile: string;
107
+ environmentVariables: Readonly<Record<string, string>>;
108
+ readiness: Readonly<{
109
+ url: string;
110
+ expectedStatus: number;
111
+ timeoutSeconds: number;
112
+ }> | null;
113
+ httpProofs: readonly Readonly<{
114
+ name: string;
115
+ kind: "positive" | "negative";
116
+ url: string;
117
+ method: string;
118
+ expectedStatus: number;
119
+ json: Readonly<{
120
+ path: readonly string[];
121
+ equals?: unknown;
122
+ notEquals?: unknown;
123
+ minimumItems?: number;
124
+ }> | null;
125
+ }>[];
126
+ runtimeAdapter: string | null;
127
+ }>;
128
+ runtime: Readonly<{
129
+ target: "postgresql" | "supabase";
130
+ applicationUrl: string;
131
+ projectId: string;
132
+ ports: Readonly<{
133
+ api?: number;
134
+ database: number;
135
+ studio?: number;
136
+ }>;
137
+ }>;
138
+ safety: Readonly<{
139
+ allowedHosts: readonly string[];
140
+ blockedEnvironmentVariables: readonly string[];
141
+ authenticationProviders: readonly string[];
142
+ hostedAccess: "disabled";
143
+ outboundNetwork: "deny";
144
+ }>;
145
+ verification: Readonly<{
146
+ commands: readonly string[];
147
+ }>;
148
+ }>;
149
+ configPath: string;
150
+ projectRoot: string;
151
+ paths: Readonly<{
152
+ artifactDirectory: string;
153
+ applicationEnvironment: string;
154
+ migrationDirectory: string;
155
+ rehearsalConfig: string | null;
156
+ runtimeWorkdir: string;
157
+ serviceEnvironment: string | null;
158
+ sanitizationPolicy: string;
159
+ sourcePolicy: string | null;
160
+ privacyKey: string | null;
161
+ runtimePolicy: string | null;
162
+ identityPolicy: string | null;
163
+ supabaseWorkdir: string | null;
164
+ runtimeAdapter: string | null;
165
+ }>;
166
+ }>>;
167
+ export declare const inspectDetectedProject: ({ projectRoot, }?: {
168
+ projectRoot?: string;
169
+ }) => Promise<DetectedProject>;
170
+ export declare const renderDetectedConfig: (detected: DetectedProject, { applicationUrl, ports, }?: {
171
+ applicationUrl?: string;
172
+ ports?: {
173
+ api: number;
174
+ database: number;
175
+ studio: number;
176
+ };
177
+ }) => string;
178
+ export declare const renderDetectedPostgresqlConfig: (detected: DetectedProject, { applicationUrl, databasePort }?: {
179
+ applicationUrl?: string | undefined;
180
+ databasePort?: number | undefined;
181
+ }) => string;