@descryy/runtime-controller 0.2.0 → 0.3.0
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/LICENSE +6 -0
- package/dist/capability-registry.d.ts +0 -15
- package/dist/capability-registry.d.ts.map +1 -1
- package/dist/capability-registry.js +9 -19
- package/dist/capability-registry.js.map +1 -1
- package/dist/collector-version.d.ts +1 -8
- package/dist/collector-version.d.ts.map +1 -1
- package/dist/collector-version.js +1 -8
- package/dist/collector-version.js.map +1 -1
- package/dist/container-sandbox.d.ts +66 -150
- package/dist/container-sandbox.d.ts.map +1 -1
- package/dist/container-sandbox.js +62 -143
- package/dist/container-sandbox.js.map +1 -1
- package/dist/controller.d.ts +54 -87
- package/dist/controller.d.ts.map +1 -1
- package/dist/controller.js +77 -104
- package/dist/controller.js.map +1 -1
- package/dist/dependency-version-check.d.ts +19 -73
- package/dist/dependency-version-check.d.ts.map +1 -1
- package/dist/dependency-version-check.js +18 -67
- package/dist/dependency-version-check.js.map +1 -1
- package/dist/env.d.ts +4 -10
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +4 -10
- package/dist/env.js.map +1 -1
- package/dist/environment-metadata.d.ts +9 -17
- package/dist/environment-metadata.d.ts.map +1 -1
- package/dist/environment-metadata.js +12 -29
- package/dist/environment-metadata.js.map +1 -1
- package/dist/environment-version-check.d.ts +22 -65
- package/dist/environment-version-check.d.ts.map +1 -1
- package/dist/environment-version-check.js +24 -66
- package/dist/environment-version-check.js.map +1 -1
- package/dist/execution-safety.d.ts +54 -112
- package/dist/execution-safety.d.ts.map +1 -1
- package/dist/execution-safety.js +48 -102
- package/dist/execution-safety.js.map +1 -1
- package/dist/index.d.ts +0 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestration.d.ts +24 -39
- package/dist/orchestration.d.ts.map +1 -1
- package/dist/orchestration.js +39 -75
- package/dist/orchestration.js.map +1 -1
- package/dist/process-collector.d.ts +8 -22
- package/dist/process-collector.d.ts.map +1 -1
- package/dist/process-collector.js +23 -55
- package/dist/process-collector.js.map +1 -1
- package/dist/process-manager.d.ts +45 -80
- package/dist/process-manager.d.ts.map +1 -1
- package/dist/process-manager.js +51 -101
- package/dist/process-manager.js.map +1 -1
- package/dist/readiness.d.ts +28 -70
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +73 -95
- package/dist/readiness.js.map +1 -1
- package/dist/sandbox.d.ts +60 -105
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/sandbox.js +78 -121
- package/dist/sandbox.js.map +1 -1
- package/package.json +7 -2
package/dist/process-manager.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Process manager (
|
|
3
|
-
*
|
|
4
|
-
* exit-code capture, cleanup with a shutdown grace period, and orphan-
|
|
5
|
-
* process prevention.
|
|
2
|
+
* Process manager (§5): command execution, cwd, env, process-group tracking,
|
|
3
|
+
* stdout/stderr capture, exit-code capture, graceful shutdown, orphan prevention.
|
|
6
4
|
*
|
|
7
|
-
* Orphan prevention and group-kill use POSIX process groups
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* written — out of scope for the V1 fixture-app slice (plan §34).
|
|
5
|
+
* Orphan prevention and group-kill use POSIX process groups (`detached: true` +
|
|
6
|
+
* `process.kill(-pid, signal)`) -- not portable to Windows, out of scope for the V1
|
|
7
|
+
* fixture-app slice (§34).
|
|
11
8
|
*/
|
|
12
9
|
import { execFile, spawn } from "node:child_process";
|
|
13
10
|
import { readFileSync } from "node:fs";
|
|
@@ -17,25 +14,17 @@ import { applyResourceLimits } from "./execution-safety.js";
|
|
|
17
14
|
import { applySandbox } from "./sandbox.js";
|
|
18
15
|
import { applyContainerSandbox } from "./container-sandbox.js";
|
|
19
16
|
/**
|
|
20
|
-
* Binds port 0
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* socket, which `spawnProcess`'s child-process model doesn't support, and
|
|
25
|
-
* which no ordinary application would cooperate with anyway.
|
|
17
|
+
* Binds port 0, reads it, releases immediately. A real gap remains between "free
|
|
18
|
+
* when checked" and "still free when the child binds it" (RT-024's gap, one layer
|
|
19
|
+
* down) -- unavoidable without binding the real service directly to the probe
|
|
20
|
+
* socket, which the child-process model doesn't support.
|
|
26
21
|
*
|
|
27
|
-
* **Examined
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* What was done instead is to make the one symptom legible: `awaitReadiness`
|
|
34
|
-
* now names the endpoint it probed, so a collision reads as "nothing
|
|
35
|
-
* answered on this port" rather than as an unexplained application failure.
|
|
36
|
-
* In practice the window is a handful of milliseconds and readiness always
|
|
37
|
-
* runs afterwards, so a genuine collision fails loudly -- it is just no
|
|
38
|
-
* longer misattributed.
|
|
22
|
+
* **Examined and deliberately not "fixed".** Holding the probe socket longer widens
|
|
23
|
+
* the window rather than narrowing it (the child can't bind while we hold it).
|
|
24
|
+
* Instead, `awaitReadiness` names the endpoint it probed, so a collision reads as
|
|
25
|
+
* "nothing answered on this port" rather than an unexplained failure. Window is a
|
|
26
|
+
* handful of ms in practice; a genuine collision still fails loudly, just correctly
|
|
27
|
+
* attributed.
|
|
39
28
|
*/
|
|
40
29
|
export function allocateEphemeralPort() {
|
|
41
30
|
return new Promise((resolve, reject) => {
|
|
@@ -63,16 +52,13 @@ export function spawnProcess(options) {
|
|
|
63
52
|
let killStarted = false;
|
|
64
53
|
let child;
|
|
65
54
|
let settled = false;
|
|
66
|
-
// Set only for the container backend (
|
|
67
|
-
//
|
|
68
|
-
// needs a real, separate stop mechanism from the bwrap path's group-kill.
|
|
55
|
+
// Set only for the container backend -- see kill() for why it needs a separate
|
|
56
|
+
// stop mechanism from bwrap's group-kill.
|
|
69
57
|
let containerName = null;
|
|
70
58
|
let resolveExit;
|
|
71
59
|
let exitPromise;
|
|
72
|
-
// Everything a (re)spawn needs to reset, in one place --
|
|
73
|
-
//
|
|
74
|
-
// sites (initial construction, restart) must not drift into resetting
|
|
75
|
-
// different subsets of this state.
|
|
60
|
+
// Everything a (re)spawn needs to reset, in one place -- construction and
|
|
61
|
+
// restart() must not drift into resetting different subsets of this state.
|
|
76
62
|
function spawnChild() {
|
|
77
63
|
startedAt = new Date().toISOString();
|
|
78
64
|
exitedAt = null;
|
|
@@ -85,9 +71,7 @@ export function spawnProcess(options) {
|
|
|
85
71
|
resolveExit = resolve;
|
|
86
72
|
});
|
|
87
73
|
// "container" skips applyResourceLimits entirely (see SpawnProcessOptions.
|
|
88
|
-
// sandboxBackend
|
|
89
|
-
// for this backend yet) -- every other case (the default, "bwrap")
|
|
90
|
-
// behaves exactly as before this field existed.
|
|
74
|
+
// sandboxBackend) -- "bwrap" (default) is unchanged.
|
|
91
75
|
const wrapped = options.sandboxBackend === "container"
|
|
92
76
|
? applyContainerSandbox({
|
|
93
77
|
interpreterCommand: options.command,
|
|
@@ -118,15 +102,10 @@ export function spawnProcess(options) {
|
|
|
118
102
|
child.stderr?.on("data", (chunk) => {
|
|
119
103
|
outputBuffer += chunk.toString("utf8");
|
|
120
104
|
});
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
// process, and with no `'exit'` guaranteed either, `exitPromise` would
|
|
126
|
-
// otherwise hang forever -- both `'exit'` and `'error'` settle the same
|
|
127
|
-
// promise exactly once, so a failed-to-spawn process reads as an
|
|
128
|
-
// ordinary never-became-ready process (no pid, no output, no readiness
|
|
129
|
-
// success) rather than crashing the caller or hanging its cleanup.
|
|
105
|
+
// A bad command (ENOENT) surfaces as an `'error'` event, not a thrown exception,
|
|
106
|
+
// and per Node's docs `'exit'` may never fire after `'error'`. Both events settle
|
|
107
|
+
// the same promise exactly once, so a failed spawn reads as an ordinary
|
|
108
|
+
// never-became-ready process rather than crashing the caller or hanging cleanup.
|
|
130
109
|
function settle(info) {
|
|
131
110
|
if (settled)
|
|
132
111
|
return;
|
|
@@ -164,22 +143,16 @@ export function spawnProcess(options) {
|
|
|
164
143
|
return;
|
|
165
144
|
}
|
|
166
145
|
if (containerName !== null) {
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
// container running in testing. `docker stop` targets the actual
|
|
173
|
-
// container by name and is the real stop mechanism for this backend;
|
|
174
|
-
// `--rm` (set by applyContainerSandbox) then removes it once stopped.
|
|
175
|
-
// Still falls through to the group-kill below afterward, as a
|
|
176
|
-
// backstop for the local CLI process itself.
|
|
146
|
+
// Found via escape testing: the local `docker` CLI process is not the
|
|
147
|
+
// container -- it runs under `dockerd`, a separate process tree.
|
|
148
|
+
// Signaling the CLI's process group left a real orphaned container running.
|
|
149
|
+
// `docker stop` (by name) is the real stop mechanism here; `--rm` then
|
|
150
|
+
// removes it. Still falls through to group-kill below as a CLI backstop.
|
|
177
151
|
try {
|
|
178
152
|
await execFileAsync("docker", ["stop", "--time", String(Math.max(1, Math.round(gracePeriodMs / 1000))), containerName]);
|
|
179
153
|
}
|
|
180
154
|
catch {
|
|
181
|
-
// Already stopped/removed
|
|
182
|
-
// first) -- not an error worth surfacing to the caller.
|
|
155
|
+
// Already stopped/removed -- not worth surfacing.
|
|
183
156
|
}
|
|
184
157
|
}
|
|
185
158
|
signalGroup(pid, "SIGTERM");
|
|
@@ -190,11 +163,9 @@ export function spawnProcess(options) {
|
|
|
190
163
|
}
|
|
191
164
|
}
|
|
192
165
|
async function restart(gracePeriodMs = DEFAULT_GRACE_PERIOD_MS) {
|
|
193
|
-
// Safe
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
// `spawnChild()` reassigns `child`/`exitPromise`/etc., so no event from
|
|
197
|
-
// the outgoing process can land on the incoming one's state.
|
|
166
|
+
// Safe regardless of the old process's state -- kill() is idempotent, and this
|
|
167
|
+
// waits for it to be fully gone before spawnChild() reassigns state, so no
|
|
168
|
+
// outgoing event can land on the incoming process.
|
|
198
169
|
await kill(gracePeriodMs);
|
|
199
170
|
spawnChild();
|
|
200
171
|
}
|
|
@@ -207,17 +178,12 @@ export function spawnProcess(options) {
|
|
|
207
178
|
};
|
|
208
179
|
}
|
|
209
180
|
/**
|
|
210
|
-
* `process.kill(pid, 0)` sends no signal
|
|
211
|
-
*
|
|
212
|
-
* pid we don't own but that unambiguously exists, still alive for this
|
|
213
|
-
* purpose. Any other error is treated as "exists" rather than guessed away.
|
|
181
|
+
* `process.kill(pid, 0)` sends no signal, only asks the kernel if delivery is
|
|
182
|
+
* possible. `ESRCH` = no such pid; `EPERM` = a pid we don't own but that exists.
|
|
214
183
|
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* would be circular. Tiny and pure, the same "duplicated because both
|
|
219
|
-
* copies are easy to notice drift" precedent `attach-to-running-jvm-process.ts`'s
|
|
220
|
-
* own `reconstructJvmSourcePath` already sets for exactly this reason.
|
|
184
|
+
* Deliberate second copy of `orchestrator/src/attach-to-running-process.ts`'s
|
|
185
|
+
* same-named function, not an import -- `orchestrator` depends on `controller`, so
|
|
186
|
+
* the reverse import would be circular. Tiny and pure enough to duplicate safely.
|
|
221
187
|
*/
|
|
222
188
|
export function isProcessAlive(pid) {
|
|
223
189
|
try {
|
|
@@ -230,25 +196,15 @@ export function isProcessAlive(pid) {
|
|
|
230
196
|
}
|
|
231
197
|
const DEFAULT_ATTACH_POLL_INTERVAL_MS = 50;
|
|
232
198
|
/**
|
|
233
|
-
* `ManagedProcess` for a service
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
* growing-whole-buffer contract a spawned process's in-memory pipe buffer
|
|
237
|
-
* already has (`spawnProcess`'s own `outputBuffer`), so `createPollingProcessOutputSource`
|
|
238
|
-
* (`@descryy/runtime-orchestrator`) needs no attach-specific branch to
|
|
239
|
-
* consume either one.
|
|
199
|
+
* `ManagedProcess` for a service attached to rather than spawned. Reads
|
|
200
|
+
* `attach.logFilePath` fresh on every `readOutput()` -- same growing-whole-buffer
|
|
201
|
+
* contract as a spawned process's in-memory pipe buffer.
|
|
240
202
|
*
|
|
241
|
-
* **`kill()` is a deliberate no-op, not a stub.** Descry did not spawn this
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
* terminating a process this runtime does not own, silently, the exact
|
|
247
|
-
* thing the disclosure promises never happens. Ending observation (no
|
|
248
|
-
* further polling) is the honest equivalent of "stop" here; the target
|
|
249
|
-
* keeps running, as it was before Descry ever attached. `restart()` throws
|
|
250
|
-
* for the same reason: there is no command this runtime could relaunch it
|
|
251
|
-
* with.
|
|
203
|
+
* **`kill()` is a deliberate no-op, not a stub.** Descry did not spawn this process,
|
|
204
|
+
* and `ATTACH_MODE_SCOPE_DISCLOSURE` is unconditional: attach mode never executes
|
|
205
|
+
* code in or applies changes to what it observes. Ending observation is the honest
|
|
206
|
+
* equivalent of "stop" -- the target keeps running. `restart()` throws for the same
|
|
207
|
+
* reason: no command exists to relaunch it with.
|
|
252
208
|
*/
|
|
253
209
|
export function attachManagedProcess(options) {
|
|
254
210
|
const { pid, logFilePath } = options.attach;
|
|
@@ -265,16 +221,12 @@ export function attachManagedProcess(options) {
|
|
|
265
221
|
}
|
|
266
222
|
return {
|
|
267
223
|
processId: options.processId,
|
|
268
|
-
// No command to report
|
|
269
|
-
// so there is nothing here that isn't a guess. Disclosed rather than
|
|
270
|
-
// fabricated.
|
|
224
|
+
// No real command to report -- disclosed rather than guessed.
|
|
271
225
|
command: `<attached: pid ${String(pid)}, ${logFilePath}>`,
|
|
272
226
|
pid,
|
|
273
227
|
startedAt,
|
|
274
228
|
exitedAt,
|
|
275
|
-
//
|
|
276
|
-
// reap — see this function's own module doc on why `kill()` never
|
|
277
|
-
// signals it, which is exactly what would let us learn either.
|
|
229
|
+
// Not observable for a process we don't spawn or reap.
|
|
278
230
|
exitCode: null,
|
|
279
231
|
signal: null,
|
|
280
232
|
serviceName: options.serviceName ?? null,
|
|
@@ -306,7 +258,7 @@ export function attachManagedProcess(options) {
|
|
|
306
258
|
return exitPromise;
|
|
307
259
|
}
|
|
308
260
|
function kill() {
|
|
309
|
-
// Deliberate no-op
|
|
261
|
+
// Deliberate no-op -- see module doc above.
|
|
310
262
|
return Promise.resolve();
|
|
311
263
|
}
|
|
312
264
|
function restart() {
|
|
@@ -318,13 +270,11 @@ export function attachManagedProcess(options) {
|
|
|
318
270
|
const execFileAsync = promisify(execFile);
|
|
319
271
|
function signalGroup(pid, signal) {
|
|
320
272
|
try {
|
|
321
|
-
// Negative pid targets the whole process group (
|
|
322
|
-
// leader, per `detached: true`), not just the immediate process --
|
|
323
|
-
// this is the orphan-prevention mechanism.
|
|
273
|
+
// Negative pid targets the whole process group (orphan-prevention mechanism).
|
|
324
274
|
process.kill(-pid, signal);
|
|
325
275
|
}
|
|
326
276
|
catch {
|
|
327
|
-
// Group already gone --
|
|
277
|
+
// Group already gone -- not an error.
|
|
328
278
|
}
|
|
329
279
|
}
|
|
330
280
|
function sleep(ms) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-manager.js","sourceRoot":"","sources":["../src/process-manager.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"process-manager.js","sourceRoot":"","sources":["../src/process-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/readiness.d.ts
CHANGED
|
@@ -1,65 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Readiness (
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* — two names for the same shape, a caller-provided async predicate,
|
|
7
|
-
* consolidated into one mechanism rather than two near-identical ones).
|
|
2
|
+
* Readiness (§6): "readiness must not equal 'process exists.'" Mechanisms: HTTP
|
|
3
|
+
* check, TCP/port check, log pattern, subprocess command, or a caller hook
|
|
4
|
+
* (consolidates the plan's "framework readiness hook" and "user-defined readiness"
|
|
5
|
+
* into one predicate shape).
|
|
8
6
|
*
|
|
9
|
-
* **`command` is not `custom-hook` (RT-193).**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* while `command` runs an actual subprocess and reads its actual exit
|
|
14
|
-
* code — the same "declared, not inferred" shape `ServiceConfiguration`'s
|
|
15
|
-
* own `command` field already uses, one layer over. A caller with a real
|
|
16
|
-
* CLI health check (`pg_isready`, a framework's own readiness script) had
|
|
17
|
-
* no way to express that without either wrapping it in a hand-written
|
|
18
|
-
* `custom-hook` predicate that shells out itself, or reimplementing the
|
|
19
|
-
* subprocess plumbing per caller — `command` is that plumbing, built once.
|
|
7
|
+
* **`command` is not `custom-hook` (RT-193).** `custom-hook` runs a caller's
|
|
8
|
+
* in-process predicate; `command` runs a real subprocess and reads its exit code --
|
|
9
|
+
* built once so a caller with a real CLI health check (`pg_isready`) doesn't have to
|
|
10
|
+
* shell out from inside a hand-written hook.
|
|
20
11
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
12
|
+
* **What `ready: true` does not claim (RT-024).** All mechanisms answer "is
|
|
13
|
+
* something listening/responding," none confirm it's the process *this* spawn
|
|
14
|
+
* produced -- a stale listener from an already-dead run satisfies `http`/`tcp-port`
|
|
15
|
+
* identically. `log-pattern` only carries the stronger this-run guarantee if `read`
|
|
16
|
+
* is wired to this execution's own `readOutput()` (caller discipline, not enforced
|
|
17
|
+
* by the type). An ephemeral port closes this gap by construction; a fixed,
|
|
18
|
+
* guessable port doesn't. Found via a real 30s collector timeout that looked like a
|
|
19
|
+
* Playwright bug and was actually a wait on an orphaned process from a dead session
|
|
20
|
+
* (RT-024).
|
|
23
21
|
*
|
|
24
|
-
* **
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* `
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* this-run-specific guarantee when its `read` is wired to *this* execution's
|
|
31
|
-
* own `ManagedProcess.readOutput()` — the type does not enforce that wiring,
|
|
32
|
-
* it is a caller discipline. `custom-hook` inherits whatever the caller's
|
|
33
|
-
* predicate actually checks.
|
|
34
|
-
*
|
|
35
|
-
* Callers that bind a fixed, guessable host:port are exposed to this gap;
|
|
36
|
-
* an ephemeral port closes it by construction (nothing else can already be
|
|
37
|
-
* bound there) rather than by detection, and is the default recommendation
|
|
38
|
-
* over a startup-nonce scheme, which only earns its extra machinery when a
|
|
39
|
-
* fixed port is genuinely unavoidable (a contract with an external tool).
|
|
40
|
-
* See `descry-runtime`'s `DECISIONS.md` RT-024 for how this was found —
|
|
41
|
-
* a real 30-second collector timeout that looked like a Playwright/DOM bug
|
|
42
|
-
* and was actually a correct wait against an orphaned process from a
|
|
43
|
-
* different, already-dead session.
|
|
44
|
-
*
|
|
45
|
-
* **A single probe attempt cannot outlive `timeoutMs` (RT-030).** `http`
|
|
46
|
-
* and `tcp-port` each bind their I/O to the *remaining* time until
|
|
47
|
-
* `deadline`, not a fresh per-attempt budget — a target that accepts a
|
|
48
|
-
* connection and never responds no longer lets one probe attempt run past
|
|
49
|
-
* the caller's own deadline. Measured, not theoretical: an unbound `fetch`
|
|
50
|
-
* here let `awaitReadiness` run past its own `timeoutMs` (>8s observed
|
|
51
|
-
* against a 3s budget). `command` honors the same discipline, the same
|
|
52
|
-
* way: `execFile`'s own `timeout` option is set to the remaining time until
|
|
53
|
-
* `deadline`, so a command that hangs instead of exiting is killed rather
|
|
54
|
-
* than left to run past the caller's own budget — it owns a real OS
|
|
55
|
-
* resource (a child process) exactly like the http/tcp-port sockets do, so
|
|
56
|
-
* it gets the same treatment. `custom-hook` is not wrapped the same way — a
|
|
57
|
-
* caller-supplied predicate owns its own timeout behavior, same as every
|
|
58
|
-
* other `Collector`-shaped callback contract in this repo (`collector.ts`'s
|
|
59
|
-
* own docs put "must resolve, not hang" on the implementer); imposing a
|
|
60
|
-
* surprise external cancellation on arbitrary caller code would be a
|
|
61
|
-
* different, larger decision than fixing the mechanisms that own a real OS
|
|
62
|
-
* resource this module can actually clean up.
|
|
22
|
+
* **A single probe attempt cannot outlive `timeoutMs` (RT-030).** `http`/`tcp-port`
|
|
23
|
+
* bind I/O to the remaining time until `deadline`, not a fresh per-attempt budget --
|
|
24
|
+
* measured: an unbound `fetch` here let `awaitReadiness` run 8s+ past a 3s budget.
|
|
25
|
+
* `command` gets the same treatment via `execFile`'s `timeout`. `custom-hook` is not
|
|
26
|
+
* wrapped this way -- a caller predicate owns its own timeout, same as every other
|
|
27
|
+
* `Collector` callback contract here.
|
|
63
28
|
*/
|
|
64
29
|
export declare const READINESS_MECHANISMS: readonly ["http", "tcp-port", "log-pattern", "command", "custom-hook"];
|
|
65
30
|
export type ReadinessMechanism = (typeof READINESS_MECHANISMS)[number];
|
|
@@ -76,18 +41,11 @@ export interface TcpPortReadinessCheck {
|
|
|
76
41
|
export interface LogPatternReadinessCheck {
|
|
77
42
|
readonly kind: "log-pattern";
|
|
78
43
|
/**
|
|
79
|
-
* Tested against `read()`'s full return value on every poll
|
|
80
|
-
* `
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* absent (the exact shape RT-069 found and fixed in
|
|
85
|
-
* `evidence-store/redaction.ts`'s `EMAIL_PATTERN`, and RT-070 also found
|
|
86
|
-
* and fixed in this package's own `PORT_PLACEHOLDER`) would run that
|
|
87
|
-
* catastrophic-backtracking cost against a growing buffer on every single
|
|
88
|
-
* poll. Nothing in this repo currently supplies such a pattern here, so
|
|
89
|
-
* this is a disclosed risk at the call site rather than a fixed one — the
|
|
90
|
-
* regex is the caller's, not this module's, to bound.
|
|
44
|
+
* Tested against `read()`'s full return value on every poll. `read` wires to
|
|
45
|
+
* `readOutput()`, an unbounded, growing stdout+stderr buffer -- an unbounded
|
|
46
|
+
* quantifier before an absent-able literal (the ReDoS shape RT-069/RT-070 already
|
|
47
|
+
* fixed elsewhere) would backtrack catastrophically here on every poll. Disclosed
|
|
48
|
+
* risk, not fixed: the regex is the caller's to bound, not this module's.
|
|
91
49
|
*/
|
|
92
50
|
readonly pattern: RegExp;
|
|
93
51
|
/** Pulls currently-buffered output — wire this to a ManagedProcess's `readOutput`. */
|
package/dist/readiness.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAKH,eAAO,MAAM,oBAAoB,wEAAyE,CAAC;AAC3G,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,oFAAoF;IACpF,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,qBAAqB,GACrB,wBAAwB,GACxB,qBAAqB,GACrB,wBAAwB,CAAC;AAE7B,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAID,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,cAAc,EAAE,EACjC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAwC1B"}
|