@rehearsal-db/core 0.1.0-beta.9 → 0.1.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +319 -1
- package/COMPATIBILITY.md +15 -0
- package/README.md +73 -16
- package/dist/package.json +100 -0
- package/dist/scripts/install/create_root_config.d.mts +3 -0
- package/dist/scripts/install/create_root_config.mjs +13 -0
- package/dist/scripts/runtime/manage_database.d.mts +6 -0
- package/{scripts → dist/scripts}/runtime/manage_database.mjs +6 -8
- package/dist/src/application/interactive_task.d.mts +30 -0
- package/dist/src/application/interactive_task.mjs +83 -0
- package/dist/src/application/session.d.mts +97 -0
- package/dist/src/application/session.mjs +380 -0
- package/dist/src/baseline/artifact.d.mts +119 -0
- package/dist/src/baseline/artifact.mjs +522 -0
- package/dist/src/baseline/builder.d.mts +19 -0
- package/dist/src/baseline/builder.mjs +142 -0
- package/dist/src/baseline/input_discovery.d.mts +13 -0
- package/dist/src/baseline/input_discovery.mjs +149 -0
- package/dist/src/baseline/policy_review.d.mts +56 -0
- package/dist/src/baseline/policy_review.mjs +73 -0
- package/dist/src/baseline/preparation.d.mts +63 -0
- package/dist/src/baseline/preparation.mjs +295 -0
- package/dist/src/baseline/privacy_contract.d.mts +147 -0
- package/dist/src/baseline/privacy_contract.mjs +2 -0
- package/dist/src/baseline/privacy_dependency_graph.d.mts +7 -0
- package/dist/src/baseline/privacy_dependency_graph.mjs +110 -0
- package/dist/src/baseline/privacy_engine.d.mts +5 -0
- package/dist/src/baseline/privacy_engine.mjs +4 -0
- package/dist/src/baseline/privacy_key.d.mts +7 -0
- package/dist/src/baseline/privacy_key.mjs +42 -0
- package/dist/src/baseline/privacy_pipeline.d.mts +7 -0
- package/dist/src/baseline/privacy_pipeline.mjs +127 -0
- package/dist/src/baseline/privacy_policy.d.mts +6 -0
- package/dist/src/baseline/privacy_policy.mjs +237 -0
- package/dist/src/baseline/privacy_recipe_validation.d.mts +5 -0
- package/dist/src/baseline/privacy_recipe_validation.mjs +289 -0
- package/dist/src/baseline/privacy_rule_registry.d.mts +18 -0
- package/dist/src/baseline/privacy_rule_registry.mjs +143 -0
- package/dist/src/baseline/privacy_structured_json.d.mts +66 -0
- package/dist/src/baseline/privacy_structured_json.mjs +464 -0
- package/dist/src/baseline/privacy_transforms.d.mts +18 -0
- package/dist/src/baseline/privacy_transforms.mjs +142 -0
- package/dist/src/baseline/privacy_validation_shared.d.mts +31 -0
- package/dist/src/baseline/privacy_validation_shared.mjs +127 -0
- package/dist/src/baseline/sanitization_policy.d.mts +69 -0
- package/dist/src/baseline/sanitization_policy.mjs +227 -0
- package/dist/src/baseline/schema_snapshot.d.mts +19 -0
- package/dist/src/baseline/schema_snapshot.mjs +79 -0
- package/dist/src/cli/application_command_handlers.d.mts +8 -0
- package/dist/src/cli/application_command_handlers.mjs +52 -0
- package/dist/src/cli/arguments.d.mts +38 -0
- package/dist/src/cli/arguments.mjs +130 -0
- package/dist/src/cli/basic_command_handlers.d.mts +4 -0
- package/dist/src/cli/basic_command_handlers.mjs +57 -0
- package/dist/src/cli/cli_commands.d.mts +14 -0
- package/dist/src/cli/cli_commands.mjs +34 -0
- package/dist/src/cli/command_contract.d.mts +57 -0
- package/dist/src/cli/command_contract.mjs +39 -0
- package/dist/src/cli/command_output.d.mts +12 -0
- package/dist/src/cli/command_output.mjs +20 -0
- package/dist/src/cli/command_registry.d.mts +7 -0
- package/dist/src/cli/command_registry.mjs +14 -0
- package/dist/src/cli/guided.d.mts +53 -0
- package/dist/src/cli/guided.mjs +727 -0
- package/dist/src/cli/identity_command_handlers.d.mts +12 -0
- package/dist/src/cli/identity_command_handlers.mjs +239 -0
- package/dist/src/cli/identity_renderer.d.mts +8 -0
- package/dist/src/cli/identity_renderer.mjs +33 -0
- package/dist/src/cli/init_command.d.mts +21 -0
- package/dist/src/cli/init_command.mjs +90 -0
- package/dist/src/cli/inspection_command_handlers.d.mts +3 -0
- package/dist/src/cli/inspection_command_handlers.mjs +79 -0
- package/dist/src/cli/project_command_handlers.d.mts +11 -0
- package/dist/src/cli/project_command_handlers.mjs +92 -0
- package/dist/src/cli/project_commands.d.mts +283 -0
- package/dist/src/cli/project_commands.mjs +192 -0
- package/dist/src/cli/rehearsal.d.mts +7 -0
- package/dist/src/cli/rehearsal.mjs +190 -0
- package/dist/src/cli/renderers.d.mts +299 -0
- package/dist/src/cli/renderers.mjs +554 -0
- package/dist/src/cli/runtime_action_handlers.d.mts +12 -0
- package/dist/src/cli/runtime_action_handlers.mjs +201 -0
- package/dist/src/cli/runtime_commands.d.mts +294 -0
- package/dist/src/cli/runtime_commands.mjs +619 -0
- package/dist/src/cli/source_command_handlers.d.mts +8 -0
- package/dist/src/cli/source_command_handlers.mjs +62 -0
- package/dist/src/cli/source_commands.d.mts +436 -0
- package/dist/src/cli/source_commands.mjs +301 -0
- package/dist/src/cli/terminal.d.mts +59 -0
- package/dist/src/cli/terminal.mjs +230 -0
- package/dist/src/identity/claim.d.mts +5 -0
- package/dist/src/identity/claim.mjs +4 -0
- package/dist/src/identity/claim_contract.d.mts +135 -0
- package/dist/src/identity/claim_contract.mjs +6 -0
- package/dist/src/identity/claim_executor.d.mts +10 -0
- package/dist/src/identity/claim_executor.mjs +358 -0
- package/dist/src/identity/claim_identity_lookup.d.mts +23 -0
- package/dist/src/identity/claim_identity_lookup.mjs +134 -0
- package/dist/src/identity/claim_plan.d.mts +8 -0
- package/dist/src/identity/claim_plan.mjs +61 -0
- package/dist/src/identity/claim_policy.d.mts +3 -0
- package/dist/src/identity/claim_policy.mjs +377 -0
- package/dist/src/identity/claim_sql.d.mts +10 -0
- package/dist/src/identity/claim_sql.mjs +46 -0
- package/dist/src/identity/claim_verification.d.mts +13 -0
- package/dist/src/identity/claim_verification.mjs +35 -0
- package/dist/src/identity/connect.d.mts +30 -0
- package/dist/src/identity/connect.mjs +111 -0
- package/dist/src/identity/provider_configuration.d.mts +37 -0
- package/dist/src/identity/provider_configuration.mjs +132 -0
- package/dist/src/identity/storage.d.mts +21 -0
- package/dist/src/identity/storage.mjs +122 -0
- package/dist/src/index.d.mts +4 -0
- package/dist/src/index.mjs +3 -0
- package/dist/src/project/configuration.d.mts +13 -0
- package/dist/src/project/configuration.mjs +9 -0
- package/dist/src/project/configuration_contract.d.mts +125 -0
- package/dist/src/project/configuration_contract.mjs +27 -0
- package/dist/src/project/configuration_discovery.d.mts +7 -0
- package/dist/src/project/configuration_discovery.mjs +37 -0
- package/dist/src/project/configuration_normalization.d.mts +107 -0
- package/dist/src/project/configuration_normalization.mjs +302 -0
- package/dist/src/project/configuration_rendering.d.mts +14 -0
- package/dist/src/project/configuration_rendering.mjs +228 -0
- package/dist/src/project/configuration_resolution.d.mts +125 -0
- package/dist/src/project/configuration_resolution.mjs +81 -0
- package/dist/src/project/configuration_schema.d.mts +22 -0
- package/dist/src/project/configuration_schema.mjs +160 -0
- package/dist/src/project/configuration_validation.d.mts +22 -0
- package/dist/src/project/configuration_validation.mjs +136 -0
- package/dist/src/project/install_scaffold.d.mts +42 -0
- package/dist/src/project/install_scaffold.mjs +263 -0
- package/dist/src/project/project_detection.d.mts +14 -0
- package/dist/src/project/project_detection.mjs +47 -0
- package/dist/src/project/setup.d.mts +87 -0
- package/dist/src/project/setup.mjs +414 -0
- package/dist/src/project/support_report.d.mts +88 -0
- package/dist/src/project/support_report.mjs +83 -0
- package/dist/src/runtime/cleanup.d.mts +85 -0
- package/dist/src/runtime/cleanup.mjs +311 -0
- package/dist/src/runtime/lifecycle_engine.d.mts +63 -0
- package/dist/src/runtime/lifecycle_engine.mjs +114 -0
- package/dist/src/runtime/migration_history.d.mts +51 -0
- package/dist/src/runtime/migration_history.mjs +170 -0
- package/dist/src/runtime/plan.d.mts +134 -0
- package/dist/src/runtime/plan.mjs +640 -0
- package/dist/src/runtime/policy.d.mts +66 -0
- package/dist/src/runtime/policy.mjs +369 -0
- package/dist/src/runtime/restore.d.mts +22 -0
- package/dist/src/runtime/restore.mjs +276 -0
- package/dist/src/runtime/service_environment.d.mts +11 -0
- package/dist/src/runtime/service_environment.mjs +58 -0
- package/dist/src/runtime/topology.d.mts +51 -0
- package/dist/src/runtime/topology.mjs +128 -0
- package/dist/src/shared/diagnostics.d.mts +97 -0
- package/dist/src/shared/diagnostics.mjs +170 -0
- package/dist/src/shared/human_output.d.mts +2 -0
- package/{src → dist/src}/shared/human_output.mjs +1 -3
- package/dist/src/shared/node_runtime.d.mts +14 -0
- package/dist/src/shared/node_runtime.mjs +25 -0
- package/dist/src/shared/operation_guard.d.mts +22 -0
- package/dist/src/shared/operation_guard.mjs +150 -0
- package/dist/src/shared/process_environment.d.mts +15 -0
- package/dist/src/shared/process_environment.mjs +50 -0
- package/dist/src/source/access.d.mts +165 -0
- package/dist/src/source/access.mjs +602 -0
- package/dist/src/source/asset_transfer.d.mts +27 -0
- package/dist/src/source/asset_transfer.mjs +202 -0
- package/dist/src/source/baseline.d.mts +66 -0
- package/dist/src/source/baseline.mjs +372 -0
- package/dist/src/source/external_reader.d.mts +42 -0
- package/dist/src/source/external_reader.mjs +278 -0
- package/dist/src/source/postgresql_access.d.mts +86 -0
- package/dist/src/source/postgresql_access.mjs +454 -0
- package/dist/src/source/privacy_paths.d.mts +10 -0
- package/dist/src/source/privacy_paths.mjs +27 -0
- package/dist/src/targets/driver.d.mts +2 -0
- package/dist/src/targets/driver.mjs +13 -0
- package/dist/src/targets/postgresql.d.mts +5 -0
- package/dist/src/targets/postgresql.mjs +620 -0
- package/dist/src/targets/supabase.d.mts +8 -0
- package/dist/src/targets/supabase.mjs +629 -0
- package/dist/src/targets/supabase_environment.d.mts +54 -0
- package/dist/src/targets/supabase_environment.mjs +184 -0
- package/dist/src/targets/target.d.mts +33 -0
- package/dist/src/targets/target.mjs +39 -0
- package/docs/README.md +35 -16
- package/docs/architecture.md +135 -10
- package/docs/commands.md +53 -15
- package/docs/configuration.md +186 -31
- package/docs/getting-started.md +66 -9
- package/docs/index.md +67 -0
- package/docs/production-source.md +175 -17
- package/docs/releasing.md +24 -12
- package/docs/roadmap.md +3 -2
- package/docs/runtime-lifecycle.md +99 -0
- package/docs/runtime-policies.md +80 -7
- package/docs/sanitization.md +572 -4
- package/docs/security-model.md +8 -0
- package/docs/stability.md +70 -0
- package/docs/standalone-workflow.md +36 -13
- package/docs/troubleshooting.md +82 -10
- package/docs/tutorial.md +7 -1
- package/docs/typescript.md +134 -0
- package/package.json +44 -29
- package/types/configuration.d.mts +5 -0
- package/src/README.md +0 -17
- package/src/application/session.mjs +0 -313
- package/src/baseline/artifact.mjs +0 -661
- package/src/baseline/builder.mjs +0 -164
- package/src/baseline/input_discovery.mjs +0 -155
- package/src/baseline/policy_review.mjs +0 -92
- package/src/baseline/preparation.mjs +0 -323
- package/src/baseline/privacy_engine.mjs +0 -413
- package/src/baseline/sanitization_policy.mjs +0 -299
- package/src/baseline/schema_snapshot.mjs +0 -113
- package/src/cli/arguments.mjs +0 -120
- package/src/cli/guided.mjs +0 -807
- package/src/cli/rehearsal.mjs +0 -933
- package/src/cli/renderers.mjs +0 -584
- package/src/cli/runtime_commands.mjs +0 -553
- package/src/cli/source_commands.mjs +0 -326
- package/src/cli/terminal.mjs +0 -275
- package/src/identity/claim.mjs +0 -975
- package/src/identity/storage.mjs +0 -165
- package/src/project/configuration.d.mts +0 -153
- package/src/project/configuration.mjs +0 -1197
- package/src/project/setup.mjs +0 -379
- package/src/project/support_report.mjs +0 -105
- package/src/runtime/cleanup.mjs +0 -381
- package/src/runtime/migration_history.mjs +0 -220
- package/src/runtime/plan.mjs +0 -685
- package/src/runtime/policy.mjs +0 -438
- package/src/runtime/restore.mjs +0 -329
- package/src/runtime/service_environment.mjs +0 -82
- package/src/runtime/topology.mjs +0 -177
- package/src/shared/diagnostics.mjs +0 -199
- package/src/shared/operation_guard.mjs +0 -162
- package/src/shared/process_environment.mjs +0 -68
- package/src/source/access.mjs +0 -578
- package/src/source/asset_transfer.mjs +0 -177
- package/src/source/baseline.mjs +0 -446
- package/src/source/postgresql_access.mjs +0 -480
- package/src/targets/postgresql.mjs +0 -808
- package/src/targets/supabase.mjs +0 -830
- package/src/targets/supabase_environment.mjs +0 -229
- package/src/targets/target.mjs +0 -66
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
/** Launch ordinary application commands against verified local runtime files. */
|
|
2
|
-
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
import { readFile, stat } from "node:fs/promises";
|
|
5
|
-
import { parseEnv } from "node:util";
|
|
6
|
-
import {
|
|
7
|
-
createCleanProcessEnvironment,
|
|
8
|
-
isLoopbackUrl,
|
|
9
|
-
} from "../shared/process_environment.mjs";
|
|
10
|
-
|
|
11
|
-
const ENV_KEY = /^[A-Z][A-Z0-9_]*$/u;
|
|
12
|
-
const MAX_DIAGNOSTIC_BYTES = 64 * 1024;
|
|
13
|
-
|
|
14
|
-
const parseCommand = (source) => {
|
|
15
|
-
if (typeof source !== "string" || !source.trim())
|
|
16
|
-
throw new Error("Application command is empty.");
|
|
17
|
-
const values = [];
|
|
18
|
-
let value = "";
|
|
19
|
-
let quote = null;
|
|
20
|
-
for (const character of source) {
|
|
21
|
-
if (quote) {
|
|
22
|
-
if (character === quote) quote = null;
|
|
23
|
-
else value += character;
|
|
24
|
-
} else if (character === '"' || character === "'") quote = character;
|
|
25
|
-
else if (/\s/u.test(character)) {
|
|
26
|
-
if (value) values.push(value);
|
|
27
|
-
value = "";
|
|
28
|
-
} else value += character;
|
|
29
|
-
}
|
|
30
|
-
if (quote)
|
|
31
|
-
throw new Error("Application command contains an unmatched quote.");
|
|
32
|
-
if (value) values.push(value);
|
|
33
|
-
return values;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const readOwnedEnvironment = async (path) => {
|
|
37
|
-
const details = await stat(path);
|
|
38
|
-
if (!details.isFile() || (details.mode & 0o077) !== 0) {
|
|
39
|
-
throw new Error(
|
|
40
|
-
"Runtime environment file must be an owner-only regular file.",
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
return parseEnv(await readFile(path, "utf8"));
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const assertLocalValue = (key, value) => {
|
|
47
|
-
if (/(_URL|_URI|_ENDPOINT)$/u.test(key)) {
|
|
48
|
-
let parsed;
|
|
49
|
-
try {
|
|
50
|
-
parsed = new URL(value);
|
|
51
|
-
} catch {
|
|
52
|
-
throw new Error(`Mapped runtime variable ${key} is not a URL.`);
|
|
53
|
-
}
|
|
54
|
-
if (
|
|
55
|
-
!isLoopbackUrl(value) &&
|
|
56
|
-
!["postgres:", "postgresql:"].includes(parsed.protocol)
|
|
57
|
-
) {
|
|
58
|
-
throw new Error(`Mapped runtime variable ${key} is not local.`);
|
|
59
|
-
}
|
|
60
|
-
if (
|
|
61
|
-
["postgres:", "postgresql:"].includes(parsed.protocol) &&
|
|
62
|
-
!["127.0.0.1", "::1", "localhost"].includes(parsed.hostname)
|
|
63
|
-
) {
|
|
64
|
-
throw new Error(`Mapped runtime variable ${key} is not local.`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export const buildApplicationEnvironment = async ({ files, mappings }) => {
|
|
70
|
-
if (!files?.primary)
|
|
71
|
-
throw new Error("Primary runtime environment file is required.");
|
|
72
|
-
const environments = {};
|
|
73
|
-
for (const [name, path] of Object.entries(files)) {
|
|
74
|
-
environments[name] = await readOwnedEnvironment(path);
|
|
75
|
-
}
|
|
76
|
-
if (!mappings || typeof mappings !== "object" || Array.isArray(mappings)) {
|
|
77
|
-
throw new Error("Application environment mappings must be an object.");
|
|
78
|
-
}
|
|
79
|
-
const result = {};
|
|
80
|
-
for (const [destination, reference] of Object.entries(mappings)) {
|
|
81
|
-
if (!ENV_KEY.test(destination) || typeof reference !== "string") {
|
|
82
|
-
throw new Error("Application environment mapping is invalid.");
|
|
83
|
-
}
|
|
84
|
-
const separator = reference.indexOf(":");
|
|
85
|
-
if (separator < 1)
|
|
86
|
-
throw new Error(
|
|
87
|
-
`Application mapping ${destination} has an invalid reference.`,
|
|
88
|
-
);
|
|
89
|
-
const target = reference.slice(0, separator);
|
|
90
|
-
const source = reference.slice(separator + 1);
|
|
91
|
-
if (!ENV_KEY.test(source) || !environments[target]?.[source]) {
|
|
92
|
-
throw new Error(
|
|
93
|
-
`Application mapping ${destination} references a missing runtime value.`,
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
assertLocalValue(source, environments[target][source]);
|
|
97
|
-
result[destination] = environments[target][source];
|
|
98
|
-
}
|
|
99
|
-
return result;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const boundedAppend = (current, chunk) =>
|
|
103
|
-
`${current}${chunk}`.slice(-MAX_DIAGNOSTIC_BYTES);
|
|
104
|
-
|
|
105
|
-
export const summarizeProjectCommandFailure = ({
|
|
106
|
-
error,
|
|
107
|
-
status,
|
|
108
|
-
stdout = "",
|
|
109
|
-
stderr = "",
|
|
110
|
-
}) => {
|
|
111
|
-
const stdoutBytes = Buffer.byteLength(String(stdout));
|
|
112
|
-
const stderrBytes = Buffer.byteLength(String(stderr));
|
|
113
|
-
if (error) {
|
|
114
|
-
const code = /^[A-Z0-9_]+$/u.test(error.code ?? "")
|
|
115
|
-
? ` (${error.code})`
|
|
116
|
-
: "";
|
|
117
|
-
return `command could not start${code}; output withheld`;
|
|
118
|
-
}
|
|
119
|
-
return `exit ${Number.isInteger(status) ? status : "unknown"}; stdout ${stdoutBytes} bytes; stderr ${stderrBytes} bytes; output withheld`;
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const waitForReadiness = async ({ readiness, fetchImplementation, child }) => {
|
|
123
|
-
if (!readiness || !isLoopbackUrl(readiness.url)) {
|
|
124
|
-
throw new Error("Application readiness must use a loopback URL.");
|
|
125
|
-
}
|
|
126
|
-
const expected = readiness.expectedStatus ?? 200;
|
|
127
|
-
const deadline = Date.now() + (readiness.timeoutSeconds ?? 30) * 1_000;
|
|
128
|
-
let lastStatus = null;
|
|
129
|
-
while (Date.now() < deadline) {
|
|
130
|
-
if (child.exitCode !== null || child.signalCode !== null)
|
|
131
|
-
throw new Error("Application exited before becoming ready.");
|
|
132
|
-
try {
|
|
133
|
-
const response = await fetchImplementation(readiness.url, {
|
|
134
|
-
redirect: "manual",
|
|
135
|
-
signal: AbortSignal.timeout(2_000),
|
|
136
|
-
});
|
|
137
|
-
lastStatus = response.status;
|
|
138
|
-
if (response.status === expected)
|
|
139
|
-
return { url: readiness.url, status: response.status };
|
|
140
|
-
} catch {
|
|
141
|
-
// A bounded retry is expected while the local process starts.
|
|
142
|
-
}
|
|
143
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
144
|
-
}
|
|
145
|
-
throw new Error(
|
|
146
|
-
`Application readiness timed out${lastStatus === null ? "" : ` after HTTP ${lastStatus}`}.`,
|
|
147
|
-
);
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const stopOwnedChild = async (child) => {
|
|
151
|
-
const exited = () => child.exitCode !== null || child.signalCode !== null;
|
|
152
|
-
if (exited()) return;
|
|
153
|
-
const kill = (signal) => {
|
|
154
|
-
try {
|
|
155
|
-
if (process.platform === "win32") child.kill(signal);
|
|
156
|
-
else process.kill(-child.pid, signal);
|
|
157
|
-
} catch (error) {
|
|
158
|
-
if (error?.code !== "ESRCH") throw error;
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
kill("SIGTERM");
|
|
162
|
-
await Promise.race([
|
|
163
|
-
new Promise((resolve) => child.once("exit", resolve)),
|
|
164
|
-
new Promise((resolve) => setTimeout(resolve, 5_000)),
|
|
165
|
-
]);
|
|
166
|
-
if (!exited()) {
|
|
167
|
-
kill("SIGKILL");
|
|
168
|
-
await new Promise((resolve) => child.once("exit", resolve));
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
export const startApplicationSession = async ({
|
|
173
|
-
command,
|
|
174
|
-
cwd,
|
|
175
|
-
files,
|
|
176
|
-
mappings,
|
|
177
|
-
readiness,
|
|
178
|
-
inheritedEnvironment = process.env,
|
|
179
|
-
spawnImplementation = spawn,
|
|
180
|
-
fetchImplementation = fetch,
|
|
181
|
-
}) => {
|
|
182
|
-
const mapped = await buildApplicationEnvironment({ files, mappings });
|
|
183
|
-
const [program, ...args] = parseCommand(command);
|
|
184
|
-
const child = spawnImplementation(program, args, {
|
|
185
|
-
cwd,
|
|
186
|
-
env: createCleanProcessEnvironment({
|
|
187
|
-
inheritedEnvironment,
|
|
188
|
-
overrides: mapped,
|
|
189
|
-
}),
|
|
190
|
-
detached: process.platform !== "win32",
|
|
191
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
192
|
-
});
|
|
193
|
-
let stdout = "";
|
|
194
|
-
let stderr = "";
|
|
195
|
-
child.stdout?.on("data", (chunk) => {
|
|
196
|
-
stdout = boundedAppend(stdout, chunk.toString("utf8"));
|
|
197
|
-
});
|
|
198
|
-
child.stderr?.on("data", (chunk) => {
|
|
199
|
-
stderr = boundedAppend(stderr, chunk.toString("utf8"));
|
|
200
|
-
});
|
|
201
|
-
await new Promise((resolve, reject) => {
|
|
202
|
-
child.once("spawn", resolve);
|
|
203
|
-
child.once("error", reject);
|
|
204
|
-
});
|
|
205
|
-
try {
|
|
206
|
-
const ready = await waitForReadiness({
|
|
207
|
-
readiness,
|
|
208
|
-
fetchImplementation,
|
|
209
|
-
child,
|
|
210
|
-
});
|
|
211
|
-
return Object.freeze({
|
|
212
|
-
pid: child.pid,
|
|
213
|
-
ready,
|
|
214
|
-
stop: () => stopOwnedChild(child),
|
|
215
|
-
diagnostics: () => ({
|
|
216
|
-
stdoutBytes: Buffer.byteLength(stdout),
|
|
217
|
-
stderrBytes: Buffer.byteLength(stderr),
|
|
218
|
-
}),
|
|
219
|
-
});
|
|
220
|
-
} catch (error) {
|
|
221
|
-
await stopOwnedChild(child);
|
|
222
|
-
throw error;
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const valueAtPath = (value, path) => {
|
|
227
|
-
let current = value;
|
|
228
|
-
for (const segment of path) {
|
|
229
|
-
if (
|
|
230
|
-
current === null ||
|
|
231
|
-
typeof current !== "object" ||
|
|
232
|
-
!Object.hasOwn(current, segment)
|
|
233
|
-
) {
|
|
234
|
-
return undefined;
|
|
235
|
-
}
|
|
236
|
-
current = current[segment];
|
|
237
|
-
}
|
|
238
|
-
return current;
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
export const runHttpProofs = async ({
|
|
242
|
-
checks,
|
|
243
|
-
fetchImplementation = fetch,
|
|
244
|
-
}) => {
|
|
245
|
-
if (!Array.isArray(checks) || checks.length < 2) {
|
|
246
|
-
throw new Error(
|
|
247
|
-
"HTTP proof requires explicit positive and negative checks.",
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
if (
|
|
251
|
-
!checks.some((check) => check.kind === "positive") ||
|
|
252
|
-
!checks.some((check) => check.kind === "negative")
|
|
253
|
-
) {
|
|
254
|
-
throw new Error(
|
|
255
|
-
"HTTP proof requires at least one positive and one negative check.",
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
const results = [];
|
|
259
|
-
for (const check of checks) {
|
|
260
|
-
if (!isLoopbackUrl(check.url))
|
|
261
|
-
throw new Error(`HTTP proof ${check.name} is not local.`);
|
|
262
|
-
const response = await fetchImplementation(check.url, {
|
|
263
|
-
method: check.method ?? "GET",
|
|
264
|
-
redirect: "manual",
|
|
265
|
-
signal: AbortSignal.timeout(check.timeoutMs ?? 5_000),
|
|
266
|
-
});
|
|
267
|
-
if (response.status !== check.expectedStatus) {
|
|
268
|
-
throw new Error(
|
|
269
|
-
`HTTP proof ${check.name} expected status ${check.expectedStatus} but received ${response.status}.`,
|
|
270
|
-
);
|
|
271
|
-
}
|
|
272
|
-
const bytes = Buffer.from(await response.arrayBuffer());
|
|
273
|
-
if (bytes.length > 1024 * 1024)
|
|
274
|
-
throw new Error(`HTTP proof ${check.name} returned too much data.`);
|
|
275
|
-
if (check.json) {
|
|
276
|
-
let parsed;
|
|
277
|
-
try {
|
|
278
|
-
parsed = JSON.parse(bytes.toString("utf8"));
|
|
279
|
-
} catch {
|
|
280
|
-
throw new Error(`HTTP proof ${check.name} did not return JSON.`);
|
|
281
|
-
}
|
|
282
|
-
const actual = valueAtPath(parsed, check.json.path);
|
|
283
|
-
if (
|
|
284
|
-
Object.hasOwn(check.json, "equals") &&
|
|
285
|
-
!Object.is(actual, check.json.equals)
|
|
286
|
-
) {
|
|
287
|
-
throw new Error(
|
|
288
|
-
`HTTP proof ${check.name} did not return its required value.`,
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
if (
|
|
292
|
-
Object.hasOwn(check.json, "notEquals") &&
|
|
293
|
-
Object.is(actual, check.json.notEquals)
|
|
294
|
-
) {
|
|
295
|
-
throw new Error(`HTTP proof ${check.name} returned a forbidden value.`);
|
|
296
|
-
}
|
|
297
|
-
if (
|
|
298
|
-
check.json.minimumItems !== undefined &&
|
|
299
|
-
(!Array.isArray(actual) || actual.length < check.json.minimumItems)
|
|
300
|
-
) {
|
|
301
|
-
throw new Error(
|
|
302
|
-
`HTTP proof ${check.name} returned too few matching items.`,
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
results.push({
|
|
307
|
-
name: check.name,
|
|
308
|
-
kind: check.kind,
|
|
309
|
-
status: response.status,
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
return Object.freeze(results);
|
|
313
|
-
};
|