@nodetool-ai/reliability-harness 0.8.1
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/README.md +34 -0
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/cli.d.ts +73 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +201 -0
- package/dist/cli.js.map +1 -0
- package/dist/compare.d.ts +98 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +267 -0
- package/dist/compare.js.map +1 -0
- package/dist/core/diff.d.ts +39 -0
- package/dist/core/diff.d.ts.map +1 -0
- package/dist/core/diff.js +114 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/golden.d.ts +50 -0
- package/dist/core/golden.d.ts.map +1 -0
- package/dist/core/golden.js +150 -0
- package/dist/core/golden.js.map +1 -0
- package/dist/core/invariants/index.d.ts +32 -0
- package/dist/core/invariants/index.d.ts.map +1 -0
- package/dist/core/invariants/index.js +44 -0
- package/dist/core/invariants/index.js.map +1 -0
- package/dist/core/invariants/leaks.d.ts +22 -0
- package/dist/core/invariants/leaks.d.ts.map +1 -0
- package/dist/core/invariants/leaks.js +55 -0
- package/dist/core/invariants/leaks.js.map +1 -0
- package/dist/core/invariants/lifecycle.d.ts +19 -0
- package/dist/core/invariants/lifecycle.d.ts.map +1 -0
- package/dist/core/invariants/lifecycle.js +95 -0
- package/dist/core/invariants/lifecycle.js.map +1 -0
- package/dist/core/invariants/protocol-validity.d.ts +4 -0
- package/dist/core/invariants/protocol-validity.d.ts.map +1 -0
- package/dist/core/invariants/protocol-validity.js +131 -0
- package/dist/core/invariants/protocol-validity.js.map +1 -0
- package/dist/core/invariants/state-machine.d.ts +29 -0
- package/dist/core/invariants/state-machine.d.ts.map +1 -0
- package/dist/core/invariants/state-machine.js +79 -0
- package/dist/core/invariants/state-machine.js.map +1 -0
- package/dist/core/invariants/status-sets.d.ts +27 -0
- package/dist/core/invariants/status-sets.d.ts.map +1 -0
- package/dist/core/invariants/status-sets.js +51 -0
- package/dist/core/invariants/status-sets.js.map +1 -0
- package/dist/core/invariants/terminal-uniqueness.d.ts +23 -0
- package/dist/core/invariants/terminal-uniqueness.d.ts.map +1 -0
- package/dist/core/invariants/terminal-uniqueness.js +99 -0
- package/dist/core/invariants/terminal-uniqueness.js.map +1 -0
- package/dist/core/invariants/types.d.ts +25 -0
- package/dist/core/invariants/types.d.ts.map +1 -0
- package/dist/core/invariants/types.js +2 -0
- package/dist/core/invariants/types.js.map +1 -0
- package/dist/core/journey.d.ts +121 -0
- package/dist/core/journey.d.ts.map +1 -0
- package/dist/core/journey.js +166 -0
- package/dist/core/journey.js.map +1 -0
- package/dist/core/normalize.d.ts +86 -0
- package/dist/core/normalize.d.ts.map +1 -0
- package/dist/core/normalize.js +182 -0
- package/dist/core/normalize.js.map +1 -0
- package/dist/core/record.d.ts +160 -0
- package/dist/core/record.d.ts.map +1 -0
- package/dist/core/record.js +74 -0
- package/dist/core/record.js.map +1 -0
- package/dist/core/stable-json.d.ts +13 -0
- package/dist/core/stable-json.d.ts.map +1 -0
- package/dist/core/stable-json.js +28 -0
- package/dist/core/stable-json.js.map +1 -0
- package/dist/drivers/anchors.d.ts +29 -0
- package/dist/drivers/anchors.d.ts.map +1 -0
- package/dist/drivers/anchors.js +48 -0
- package/dist/drivers/anchors.js.map +1 -0
- package/dist/drivers/app-headless.d.ts +39 -0
- package/dist/drivers/app-headless.d.ts.map +1 -0
- package/dist/drivers/app-headless.js +123 -0
- package/dist/drivers/app-headless.js.map +1 -0
- package/dist/drivers/browser.d.ts +34 -0
- package/dist/drivers/browser.d.ts.map +1 -0
- package/dist/drivers/browser.js +176 -0
- package/dist/drivers/browser.js.map +1 -0
- package/dist/drivers/cli.d.ts +13 -0
- package/dist/drivers/cli.d.ts.map +1 -0
- package/dist/drivers/cli.js +82 -0
- package/dist/drivers/cli.js.map +1 -0
- package/dist/drivers/fixture-nodes.d.ts +124 -0
- package/dist/drivers/fixture-nodes.d.ts.map +1 -0
- package/dist/drivers/fixture-nodes.js +189 -0
- package/dist/drivers/fixture-nodes.js.map +1 -0
- package/dist/drivers/index.d.ts +18 -0
- package/dist/drivers/index.d.ts.map +1 -0
- package/dist/drivers/index.js +12 -0
- package/dist/drivers/index.js.map +1 -0
- package/dist/drivers/kernel.d.ts +8 -0
- package/dist/drivers/kernel.d.ts.map +1 -0
- package/dist/drivers/kernel.js +247 -0
- package/dist/drivers/kernel.js.map +1 -0
- package/dist/drivers/packaged.d.ts +28 -0
- package/dist/drivers/packaged.d.ts.map +1 -0
- package/dist/drivers/packaged.js +303 -0
- package/dist/drivers/packaged.js.map +1 -0
- package/dist/drivers/python-bridge.d.ts +22 -0
- package/dist/drivers/python-bridge.d.ts.map +1 -0
- package/dist/drivers/python-bridge.js +48 -0
- package/dist/drivers/python-bridge.js.map +1 -0
- package/dist/drivers/registry.d.ts +11 -0
- package/dist/drivers/registry.d.ts.map +1 -0
- package/dist/drivers/registry.js +18 -0
- package/dist/drivers/registry.js.map +1 -0
- package/dist/drivers/relay-fields.d.ts +70 -0
- package/dist/drivers/relay-fields.d.ts.map +1 -0
- package/dist/drivers/relay-fields.js +104 -0
- package/dist/drivers/relay-fields.js.map +1 -0
- package/dist/drivers/repo-root.d.ts +9 -0
- package/dist/drivers/repo-root.d.ts.map +1 -0
- package/dist/drivers/repo-root.js +39 -0
- package/dist/drivers/repo-root.js.map +1 -0
- package/dist/drivers/spawn-cli.d.ts +8 -0
- package/dist/drivers/spawn-cli.d.ts.map +1 -0
- package/dist/drivers/spawn-cli.js +41 -0
- package/dist/drivers/spawn-cli.js.map +1 -0
- package/dist/drivers/types.d.ts +18 -0
- package/dist/drivers/types.d.ts.map +1 -0
- package/dist/drivers/types.js +2 -0
- package/dist/drivers/types.js.map +1 -0
- package/dist/drivers/ws-server.d.ts +8 -0
- package/dist/drivers/ws-server.d.ts.map +1 -0
- package/dist/drivers/ws-server.js +382 -0
- package/dist/drivers/ws-server.js.map +1 -0
- package/dist/faults/bridge-faults.d.ts +7 -0
- package/dist/faults/bridge-faults.d.ts.map +1 -0
- package/dist/faults/bridge-faults.js +90 -0
- package/dist/faults/bridge-faults.js.map +1 -0
- package/dist/faults/host-faults.d.ts +40 -0
- package/dist/faults/host-faults.d.ts.map +1 -0
- package/dist/faults/host-faults.js +70 -0
- package/dist/faults/host-faults.js.map +1 -0
- package/dist/faults/index.d.ts +21 -0
- package/dist/faults/index.d.ts.map +1 -0
- package/dist/faults/index.js +29 -0
- package/dist/faults/index.js.map +1 -0
- package/dist/faults/net-proxy.d.ts +75 -0
- package/dist/faults/net-proxy.d.ts.map +1 -0
- package/dist/faults/net-proxy.js +305 -0
- package/dist/faults/net-proxy.js.map +1 -0
- package/dist/faults/provider-faults.d.ts +12 -0
- package/dist/faults/provider-faults.d.ts.map +1 -0
- package/dist/faults/provider-faults.js +110 -0
- package/dist/faults/provider-faults.js.map +1 -0
- package/dist/faults/registry.d.ts +17 -0
- package/dist/faults/registry.d.ts.map +1 -0
- package/dist/faults/registry.js +15 -0
- package/dist/faults/registry.js.map +1 -0
- package/dist/faults/types.d.ts +42 -0
- package/dist/faults/types.d.ts.map +1 -0
- package/dist/faults/types.js +2 -0
- package/dist/faults/types.js.map +1 -0
- package/dist/faults/ws-faults.d.ts +6 -0
- package/dist/faults/ws-faults.d.ts.map +1 -0
- package/dist/faults/ws-faults.js +48 -0
- package/dist/faults/ws-faults.js.map +1 -0
- package/dist/faults/ws-proxy.d.ts +36 -0
- package/dist/faults/ws-proxy.d.ts.map +1 -0
- package/dist/faults/ws-proxy.js +40 -0
- package/dist/faults/ws-proxy.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless-app driver (§12) — wraps `nodetool app debug` (packages/cli's
|
|
3
|
+
* app-debug runtime, `packages/cli/src/app-debug/`) for journeys whose
|
|
4
|
+
* workflow carries an app document. Spawns the real CLI (`spawn-cli.ts`)
|
|
5
|
+
* rather than importing the app-debug runtime directly: `@nodetool-ai/cli`'s
|
|
6
|
+
* own `reliability` command (item 6) depends on this package, so a static
|
|
7
|
+
* import the other way would be a circular workspace dependency — and
|
|
8
|
+
* spawning the entry point is what every non-oracle driver does anyway
|
|
9
|
+
* (§12: "drivers speak entry points, never internals").
|
|
10
|
+
*
|
|
11
|
+
* A journey "carries an app" when its workflow JSON has a top-level
|
|
12
|
+
* `app_doc` field — the same shape `nodetool debug`/`workflows run` read
|
|
13
|
+
* (`packages/cli/src/debug/target.ts`). None of the seed journeys (§5) do
|
|
14
|
+
* yet, so `supports()` reports `false` for all of them today and the driver
|
|
15
|
+
* is skipped as not-applicable — the "headless-app or n/a-clean" leg of C4's
|
|
16
|
+
* done-when (item 8). The conversion half (`runRecordFromAppDebugReport`) is
|
|
17
|
+
* still fully implemented and unit-tested against a fabricated report, so a
|
|
18
|
+
* future app-carrying journey exercises real, proven code — not a stub.
|
|
19
|
+
*/
|
|
20
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
21
|
+
import { tmpdir } from "node:os";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { makeFrame } from "../core/record.js";
|
|
24
|
+
import { spawnNodetoolCli } from "./spawn-cli.js";
|
|
25
|
+
/** True when a journey's workflow carries an `app_doc` the app-headless
|
|
26
|
+
* driver can run — pure, no IO, so `compare.ts` and tests can check
|
|
27
|
+
* applicability without spawning a process. */
|
|
28
|
+
export function journeyHasAppDoc(journey) {
|
|
29
|
+
return journey.workflow["app_doc"] != null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Folds a decoded `AppDebugReport` (`report.json`) plus each triggered run's
|
|
33
|
+
* own `server/run-N.messages.jsonl` lines (already read, in `runs[]` order)
|
|
34
|
+
* into one `RunRecord`. Pure — mirrors `record.ts`'s
|
|
35
|
+
* `runRecordFromDebugBundle` split between the pure conversion and the IO
|
|
36
|
+
* that reads the bundle off disk, so this half is unit-testable without a
|
|
37
|
+
* real app document or a spawned process.
|
|
38
|
+
*/
|
|
39
|
+
export function runRecordFromAppDebugReport(report, runMessages, options = {}) {
|
|
40
|
+
const startedAt = options.startedAt ?? 0;
|
|
41
|
+
const frames = [];
|
|
42
|
+
let seq = 0;
|
|
43
|
+
for (const messages of runMessages) {
|
|
44
|
+
for (const message of messages) {
|
|
45
|
+
frames.push(makeFrame(seq, "app-headless", "server_to_client", message, startedAt + seq));
|
|
46
|
+
seq++;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const runs = report.runs ?? [];
|
|
50
|
+
const firstFailure = runs.find((r) => r.status && r.status !== "completed");
|
|
51
|
+
const status = runs.length === 0 ? "unknown" : firstFailure?.status ?? "completed";
|
|
52
|
+
const lastRun = runs[runs.length - 1];
|
|
53
|
+
return {
|
|
54
|
+
journeyId: options.journeyId,
|
|
55
|
+
surface: "app-headless",
|
|
56
|
+
jobId: null,
|
|
57
|
+
workflowId: report.target?.workflowId ?? null,
|
|
58
|
+
startedAt: frames.length > 0 ? startedAt : null,
|
|
59
|
+
finishedAt: frames.length > 0 ? startedAt + Math.max(frames.length - 1, 0) : null,
|
|
60
|
+
durationMs: lastRun?.durationMs ?? null,
|
|
61
|
+
status,
|
|
62
|
+
error: firstFailure?.error ?? runs.find((r) => r.error)?.error ?? null,
|
|
63
|
+
params: {},
|
|
64
|
+
frames
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export class AppHeadlessDriver {
|
|
68
|
+
name = "app-headless";
|
|
69
|
+
supports(journey) {
|
|
70
|
+
return journeyHasAppDoc(journey);
|
|
71
|
+
}
|
|
72
|
+
async run(journey) {
|
|
73
|
+
if (!this.supports(journey)) {
|
|
74
|
+
throw new Error(`journey "${journey.manifest.name}": app-headless driver only runs ` +
|
|
75
|
+
`journeys whose workflow carries an "app_doc" — this journey has none (n/a)`);
|
|
76
|
+
}
|
|
77
|
+
const bundleDir = await mkdtemp(join(tmpdir(), "nodetool-reliability-app-"));
|
|
78
|
+
try {
|
|
79
|
+
const workflowPath = join(bundleDir, "workflow.json");
|
|
80
|
+
await writeFile(workflowPath, JSON.stringify(journey.workflow), "utf8");
|
|
81
|
+
const timeoutMs = journey.manifest.timeoutMs + 30_000;
|
|
82
|
+
const { code, stdout, stderr } = await spawnNodetoolCli([
|
|
83
|
+
"app",
|
|
84
|
+
"debug",
|
|
85
|
+
workflowPath,
|
|
86
|
+
"--params",
|
|
87
|
+
JSON.stringify(journey.manifest.params ?? {}),
|
|
88
|
+
"--out",
|
|
89
|
+
bundleDir,
|
|
90
|
+
"--json"
|
|
91
|
+
], timeoutMs);
|
|
92
|
+
let report;
|
|
93
|
+
try {
|
|
94
|
+
report = JSON.parse(await readFile(join(bundleDir, "report.json"), "utf8"));
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
throw new Error(`journey "${journey.manifest.name}": app-headless driver produced no ` +
|
|
98
|
+
`readable report (exit ${code})\n--- stdout ---\n${stdout}\n` +
|
|
99
|
+
`--- stderr ---\n${stderr}\noriginal error: ${String(err)}`);
|
|
100
|
+
}
|
|
101
|
+
const runMessages = [];
|
|
102
|
+
for (const run of report.runs ?? []) {
|
|
103
|
+
if (!run.messagesFile) {
|
|
104
|
+
runMessages.push([]);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const raw = await readFile(join(bundleDir, run.messagesFile), "utf8").catch(() => "");
|
|
108
|
+
runMessages.push(raw
|
|
109
|
+
.split("\n")
|
|
110
|
+
.map((line) => line.trim())
|
|
111
|
+
.filter((line) => line.length > 0)
|
|
112
|
+
.map((line) => JSON.parse(line)));
|
|
113
|
+
}
|
|
114
|
+
return runRecordFromAppDebugReport(report, runMessages, {
|
|
115
|
+
journeyId: journey.manifest.name
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
await rm(bundleDir, { recursive: true, force: true }).catch(() => { });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=app-headless.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-headless.js","sourceRoot":"","sources":["../../src/drivers/app-headless.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Journey } from "../core/journey.js";
|
|
2
|
+
import { type RunRecord } from "../core/record.js";
|
|
3
|
+
import type { RunDriver } from "./types.js";
|
|
4
|
+
/** Minimal shape this driver needs from a decoded `browser/record.json`
|
|
5
|
+
* (`web/src/e2e_runner/types.ts`'s `RunRecord`). */
|
|
6
|
+
export interface BrowserRunRecordLike {
|
|
7
|
+
status?: string;
|
|
8
|
+
error?: string | null;
|
|
9
|
+
durationMs?: number | null;
|
|
10
|
+
jobId?: string | null;
|
|
11
|
+
events?: Array<Record<string, unknown>>;
|
|
12
|
+
}
|
|
13
|
+
/** Folds a decoded `browser/record.json` into a `RunRecord`. Pure — the IO
|
|
14
|
+
* (spawning Playwright, reading the file back) lives in {@link BrowserDriver},
|
|
15
|
+
* so a captured fixture round-trips through this without a browser. */
|
|
16
|
+
export declare function runRecordFromBrowserRecord(record: BrowserRunRecordLike, options?: {
|
|
17
|
+
journeyId?: string;
|
|
18
|
+
startedAt?: number;
|
|
19
|
+
}): RunRecord;
|
|
20
|
+
export declare class BrowserDriver implements RunDriver {
|
|
21
|
+
readonly name = "browser";
|
|
22
|
+
/** A single, unscripted `run` (see the module doc comment) AND Playwright
|
|
23
|
+
* actually available in this environment. Folding `isAvailable()` in here
|
|
24
|
+
* — rather than leaving it a separate check `run()` fails on — is what lets
|
|
25
|
+
* `compare.ts` report a sandbox without a matching Chromium build as "not
|
|
26
|
+
* applicable" instead of a false "diverges from the oracle" failure. */
|
|
27
|
+
supports(journey: Journey): boolean;
|
|
28
|
+
/** True when Playwright and its Chromium binary are actually available in
|
|
29
|
+
* this environment — lets callers (`compare.ts`, tests) skip cleanly
|
|
30
|
+
* instead of failing the whole run. */
|
|
31
|
+
isAvailable(): boolean;
|
|
32
|
+
run(journey: Journey): Promise<RunRecord>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/drivers/browser.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAA4B,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAM5C;oDACoD;AACpD,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACzC;AAoCD;;uEAEuE;AACvE,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACvD,SAAS,CAoBX;AAED,qBAAa,aAAc,YAAW,SAAS;IAC7C,QAAQ,CAAC,IAAI,aAAa;IAE1B;;;;4EAIwE;IACxE,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAKlC;IAED;;2CAEuC;IACvC,WAAW,IAAI,OAAO,CAOrB;IAEK,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CA4E9C;CACF"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser driver (§12) — wraps the existing `e2e_runner` Playwright harness
|
|
3
|
+
* (`web/tests/debug-harness/debug.spec.ts`, the same spec `nodetool debug
|
|
4
|
+
* --browser` and `npm run test:debug-harness` drive) to run a journey's
|
|
5
|
+
* workflow through the real web stack: real ReactFlow canvas, real
|
|
6
|
+
* `GlobalWebSocketManager`, real WS protocol, hermetic backend
|
|
7
|
+
* (`packages/websocket/src/e2e-server.ts`, spawned by the spec's
|
|
8
|
+
* `globalSetup`).
|
|
9
|
+
*
|
|
10
|
+
* Spawns Playwright directly (root `node_modules/.bin/playwright`, `cwd:
|
|
11
|
+
* web/`) rather than importing `packages/cli/src/debug/browser-runner.ts`:
|
|
12
|
+
* that module lives in `@nodetool-ai/cli`, which depends on this package for
|
|
13
|
+
* the `reliability` command (item 6), so importing it here would be a
|
|
14
|
+
* circular workspace dependency — and going through the real Playwright
|
|
15
|
+
* binary is what "drivers speak entry points, never internals" (§12) means
|
|
16
|
+
* for a non-oracle surface anyway.
|
|
17
|
+
*
|
|
18
|
+
* Scripted interactions (`cancel`/`stream_input`/`end_input_stream`) are not
|
|
19
|
+
* yet wired through `NODETOOL_DEBUG_*` env — the spec runs one `runGraph`
|
|
20
|
+
* call start-to-finish, same limitation as the CLI driver. `supports()`
|
|
21
|
+
* reflects that.
|
|
22
|
+
*/
|
|
23
|
+
import { spawn } from "node:child_process";
|
|
24
|
+
import { existsSync } from "node:fs";
|
|
25
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
26
|
+
import { tmpdir } from "node:os";
|
|
27
|
+
import { join } from "node:path";
|
|
28
|
+
import { makeFrame } from "../core/record.js";
|
|
29
|
+
import { findRepoRoot } from "./repo-root.js";
|
|
30
|
+
const CONFIG_REL = "playwright.debug-harness.config.ts";
|
|
31
|
+
const DEFAULT_TIMEOUT_MS = 6 * 60_000;
|
|
32
|
+
const GROUP_KILL_GRACE_MS = 5_000;
|
|
33
|
+
/** Locates the workspace-hoisted Playwright binary a fresh `web/`
|
|
34
|
+
* `node_modules/.bin` doesn't necessarily carry (root `npm install` hoists
|
|
35
|
+
* it), falling back to `web`'s own copy when present. */
|
|
36
|
+
function resolvePlaywrightBin(repoRoot, webDir) {
|
|
37
|
+
const local = join(webDir, "node_modules", ".bin", "playwright");
|
|
38
|
+
if (existsSync(local))
|
|
39
|
+
return local;
|
|
40
|
+
const hoisted = join(repoRoot, "node_modules", ".bin", "playwright");
|
|
41
|
+
return existsSync(hoisted) ? hoisted : null;
|
|
42
|
+
}
|
|
43
|
+
function killChild(child) {
|
|
44
|
+
if (process.platform !== "win32" && child.pid !== undefined) {
|
|
45
|
+
const pgid = child.pid;
|
|
46
|
+
try {
|
|
47
|
+
process.kill(-pgid, "SIGTERM");
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Process group may already be gone.
|
|
51
|
+
}
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
try {
|
|
54
|
+
process.kill(-pgid, "SIGKILL");
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Process group may already be gone.
|
|
58
|
+
}
|
|
59
|
+
}, GROUP_KILL_GRACE_MS).unref();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
child.kill("SIGKILL");
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Already exited.
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Folds a decoded `browser/record.json` into a `RunRecord`. Pure — the IO
|
|
70
|
+
* (spawning Playwright, reading the file back) lives in {@link BrowserDriver},
|
|
71
|
+
* so a captured fixture round-trips through this without a browser. */
|
|
72
|
+
export function runRecordFromBrowserRecord(record, options = {}) {
|
|
73
|
+
const startedAt = options.startedAt ?? 0;
|
|
74
|
+
const events = record.events ?? [];
|
|
75
|
+
const frames = events.map((message, index) => makeFrame(index, "browser", "server_to_client", message, startedAt + index));
|
|
76
|
+
return {
|
|
77
|
+
journeyId: options.journeyId,
|
|
78
|
+
surface: "browser",
|
|
79
|
+
jobId: record.jobId ?? null,
|
|
80
|
+
workflowId: null,
|
|
81
|
+
startedAt: frames.length > 0 ? startedAt : null,
|
|
82
|
+
finishedAt: frames.length > 0 ? startedAt + (frames.length - 1) : null,
|
|
83
|
+
durationMs: record.durationMs ?? null,
|
|
84
|
+
status: record.status ?? "unknown",
|
|
85
|
+
error: record.error ?? null,
|
|
86
|
+
params: {},
|
|
87
|
+
frames
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export class BrowserDriver {
|
|
91
|
+
name = "browser";
|
|
92
|
+
/** A single, unscripted `run` (see the module doc comment) AND Playwright
|
|
93
|
+
* actually available in this environment. Folding `isAvailable()` in here
|
|
94
|
+
* — rather than leaving it a separate check `run()` fails on — is what lets
|
|
95
|
+
* `compare.ts` report a sandbox without a matching Chromium build as "not
|
|
96
|
+
* applicable" instead of a false "diverges from the oracle" failure. */
|
|
97
|
+
supports(journey) {
|
|
98
|
+
const interactions = journey.interactions.length > 0 ? journey.interactions : [{ action: "run" }];
|
|
99
|
+
const shapeOk = interactions.length === 1 && interactions[0].action === "run";
|
|
100
|
+
return shapeOk && this.isAvailable();
|
|
101
|
+
}
|
|
102
|
+
/** True when Playwright and its Chromium binary are actually available in
|
|
103
|
+
* this environment — lets callers (`compare.ts`, tests) skip cleanly
|
|
104
|
+
* instead of failing the whole run. */
|
|
105
|
+
isAvailable() {
|
|
106
|
+
const repoRoot = findRepoRoot();
|
|
107
|
+
const webDir = join(repoRoot, "web");
|
|
108
|
+
return (existsSync(join(webDir, CONFIG_REL)) &&
|
|
109
|
+
resolvePlaywrightBin(repoRoot, webDir) !== null);
|
|
110
|
+
}
|
|
111
|
+
async run(journey) {
|
|
112
|
+
if (!this.supports(journey)) {
|
|
113
|
+
throw new Error(`journey "${journey.manifest.name}": browser driver only supports a ` +
|
|
114
|
+
`single, unscripted "run" interaction`);
|
|
115
|
+
}
|
|
116
|
+
const repoRoot = findRepoRoot();
|
|
117
|
+
const webDir = join(repoRoot, "web");
|
|
118
|
+
const playwrightBin = resolvePlaywrightBin(repoRoot, webDir);
|
|
119
|
+
if (!playwrightBin || !existsSync(join(webDir, CONFIG_REL))) {
|
|
120
|
+
throw new Error(`journey "${journey.manifest.name}": browser driver unavailable — ` +
|
|
121
|
+
`Playwright is not installed (root and web node_modules/.bin both ` +
|
|
122
|
+
`missing "playwright") or ${CONFIG_REL} is missing from web/`);
|
|
123
|
+
}
|
|
124
|
+
const outDir = await mkdtemp(join(tmpdir(), "nodetool-reliability-browser-"));
|
|
125
|
+
try {
|
|
126
|
+
const graphPath = join(outDir, "_graph.json");
|
|
127
|
+
await writeFile(graphPath, JSON.stringify({ graph: journey.workflow }), "utf8");
|
|
128
|
+
const timeoutMs = journey.manifest.timeoutMs;
|
|
129
|
+
const env = {
|
|
130
|
+
...process.env,
|
|
131
|
+
NODETOOL_DEBUG_GRAPH: graphPath,
|
|
132
|
+
NODETOOL_DEBUG_OUT: outDir,
|
|
133
|
+
NODETOOL_DEBUG_PARAMS: JSON.stringify(journey.manifest.params ?? {}),
|
|
134
|
+
NODETOOL_DEBUG_TIMEOUT: String(timeoutMs)
|
|
135
|
+
};
|
|
136
|
+
const killTimeoutMs = Math.max(timeoutMs + 60_000, DEFAULT_TIMEOUT_MS);
|
|
137
|
+
const isWindows = process.platform === "win32";
|
|
138
|
+
let output = "";
|
|
139
|
+
await new Promise((resolvePromise) => {
|
|
140
|
+
const child = spawn(playwrightBin, ["test", "-c", CONFIG_REL], {
|
|
141
|
+
cwd: webDir,
|
|
142
|
+
env,
|
|
143
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
144
|
+
...(isWindows ? { shell: true } : { detached: true })
|
|
145
|
+
});
|
|
146
|
+
const onChunk = (buf) => (output += buf.toString());
|
|
147
|
+
child.stdout?.on("data", onChunk);
|
|
148
|
+
child.stderr?.on("data", onChunk);
|
|
149
|
+
const timer = setTimeout(() => {
|
|
150
|
+
killChild(child);
|
|
151
|
+
resolvePromise();
|
|
152
|
+
}, killTimeoutMs);
|
|
153
|
+
child.on("error", () => {
|
|
154
|
+
clearTimeout(timer);
|
|
155
|
+
resolvePromise();
|
|
156
|
+
});
|
|
157
|
+
child.on("exit", () => {
|
|
158
|
+
clearTimeout(timer);
|
|
159
|
+
resolvePromise();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
const recordPath = join(outDir, "record.json");
|
|
163
|
+
if (!existsSync(recordPath)) {
|
|
164
|
+
throw new Error(`journey "${journey.manifest.name}": browser driver produced no ` +
|
|
165
|
+
`record.json — check that the web app builds and Chromium is ` +
|
|
166
|
+
`installed\n--- playwright output ---\n${output.trimEnd()}`);
|
|
167
|
+
}
|
|
168
|
+
const record = JSON.parse(await readFile(recordPath, "utf8"));
|
|
169
|
+
return runRecordFromBrowserRecord(record, { journeyId: journey.manifest.name });
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
await rm(outDir, { recursive: true, force: true }).catch(() => { });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/drivers/browser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Journey } from "../core/journey.js";
|
|
2
|
+
import { type RunRecord } from "../core/record.js";
|
|
3
|
+
import type { RunDriver } from "./types.js";
|
|
4
|
+
/** True when a journey is a single, unscripted `run` — the only shape
|
|
5
|
+
* `nodetool debug` (and so this driver) can execute. Exported for `compare.ts`
|
|
6
|
+
* and tests to check without constructing a driver. */
|
|
7
|
+
export declare function cliDriverSupports(journey: Journey): boolean;
|
|
8
|
+
export declare class CliDriver implements RunDriver {
|
|
9
|
+
readonly name = "cli";
|
|
10
|
+
supports(journey: Journey): boolean;
|
|
11
|
+
run(journey: Journey): Promise<RunRecord>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/drivers/cli.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,OAAO,EAAsB,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAM5C;;uDAEuD;AACvD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAG3D;AAED,qBAAa,SAAU,YAAW,SAAS;IACzC,QAAQ,CAAC,IAAI,SAAS;IAEtB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAElC;IAEK,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAsD9C;CACF"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI driver (§12) — spawns the real `nodetool debug` command (via `tsx`,
|
|
3
|
+
* `spawn-cli.ts`) against the journey's workflow, then converts the debug
|
|
4
|
+
* bundle it writes (`report.json` + `server/messages.jsonl`) into a
|
|
5
|
+
* `RunRecord` with `loadRunRecordFromDebugBundle` (`core/record.ts`, grown by
|
|
6
|
+
* C1 for exactly this "debug bundle round-trips into a RunRecord" purpose).
|
|
7
|
+
*
|
|
8
|
+
* `nodetool debug` has no scripted-interaction surface of its own — it runs a
|
|
9
|
+
* workflow once, start to finish, with a fixed params bag. `supports()`
|
|
10
|
+
* reflects that: only journeys whose interactions are a single `"run"` (or
|
|
11
|
+
* the implicit one every driver assumes when a journey declares none) can
|
|
12
|
+
* run on this surface; `cancel`/`stream_input`/`reconnect` journeys report
|
|
13
|
+
* "not applicable" instead of erroring.
|
|
14
|
+
*/
|
|
15
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { loadRunRecordFromDebugBundle } from "../core/record.js";
|
|
19
|
+
import { spawnNodetoolCli } from "./spawn-cli.js";
|
|
20
|
+
function effectiveInteractions(journey) {
|
|
21
|
+
return journey.interactions.length > 0 ? journey.interactions : [{ action: "run" }];
|
|
22
|
+
}
|
|
23
|
+
/** True when a journey is a single, unscripted `run` — the only shape
|
|
24
|
+
* `nodetool debug` (and so this driver) can execute. Exported for `compare.ts`
|
|
25
|
+
* and tests to check without constructing a driver. */
|
|
26
|
+
export function cliDriverSupports(journey) {
|
|
27
|
+
const interactions = effectiveInteractions(journey);
|
|
28
|
+
return interactions.length === 1 && interactions[0].action === "run";
|
|
29
|
+
}
|
|
30
|
+
export class CliDriver {
|
|
31
|
+
name = "cli";
|
|
32
|
+
supports(journey) {
|
|
33
|
+
return cliDriverSupports(journey);
|
|
34
|
+
}
|
|
35
|
+
async run(journey) {
|
|
36
|
+
if (!this.supports(journey)) {
|
|
37
|
+
throw new Error(`journey "${journey.manifest.name}": cli driver only supports a single, ` +
|
|
38
|
+
`unscripted "run" interaction — "nodetool debug" has no cancel/` +
|
|
39
|
+
`stream_input/reconnect surface`);
|
|
40
|
+
}
|
|
41
|
+
const bundleDir = await mkdtemp(join(tmpdir(), "nodetool-reliability-cli-"));
|
|
42
|
+
try {
|
|
43
|
+
const workflowPath = join(bundleDir, "workflow.json");
|
|
44
|
+
await writeFile(workflowPath, JSON.stringify(journey.workflow), "utf8");
|
|
45
|
+
const timeoutMs = journey.manifest.timeoutMs + 30_000;
|
|
46
|
+
const { code, stdout, stderr } = await spawnNodetoolCli([
|
|
47
|
+
"debug",
|
|
48
|
+
workflowPath,
|
|
49
|
+
"--params",
|
|
50
|
+
JSON.stringify(journey.manifest.params ?? {}),
|
|
51
|
+
"--out",
|
|
52
|
+
bundleDir,
|
|
53
|
+
"--json"
|
|
54
|
+
], timeoutMs);
|
|
55
|
+
// `nodetool debug` exits non-zero on a failed/cancelled *journey*
|
|
56
|
+
// outcome too (e.g. error-in-one-branch legitimately expects "failed")
|
|
57
|
+
// — that isn't a driver failure. Only a bundle the harness can't read
|
|
58
|
+
// back is fatal here.
|
|
59
|
+
let record;
|
|
60
|
+
try {
|
|
61
|
+
record = await loadRunRecordFromDebugBundle(bundleDir, {
|
|
62
|
+
journeyId: journey.manifest.name
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
throw new Error(`journey "${journey.manifest.name}": cli driver produced no readable ` +
|
|
67
|
+
`debug bundle (exit ${code})\n--- stdout ---\n${stdout}\n` +
|
|
68
|
+
`--- stderr ---\n${stderr}\noriginal error: ${String(err)}`);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
...record,
|
|
72
|
+
surface: this.name,
|
|
73
|
+
params: journey.manifest.params,
|
|
74
|
+
frames: record.frames.map((frame) => ({ ...frame, surface: this.name }))
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
await rm(bundleDir, { recursive: true, force: true }).catch(() => { });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/drivers/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic fixture nodes for journeys the real node registry has no node
|
|
3
|
+
* for: a controllable long-running actor (mid-run cancellation while a node
|
|
4
|
+
* is `running`) and an externally-fed streaming input (mid-run cancellation
|
|
5
|
+
* while a node is mid-stream). Every other C2 journey uses real registered
|
|
6
|
+
* `nodetool.*` node classes from `@nodetool-ai/base-nodes` (§5: "prefer core
|
|
7
|
+
* nodes... so the kernel driver can run them too") — these three fill the
|
|
8
|
+
* one gap: no shipped node exposes a controllable busy-loop or a
|
|
9
|
+
* `pushInput`-fed external stream.
|
|
10
|
+
*
|
|
11
|
+
* Registered identically into both drivers' registries (`registry.ts` for
|
|
12
|
+
* the kernel driver, `configureRegistry` for the ws-server driver) so their
|
|
13
|
+
* behavior is byte-identical on both surfaces — modeled on the equivalent
|
|
14
|
+
* fixtures in `packages/execution/tests/fixtures.ts`.
|
|
15
|
+
*/
|
|
16
|
+
import { BaseNode, type NodeRegistry, type StreamingInputs, type StreamingOutputs } from "@nodetool-ai/node-sdk";
|
|
17
|
+
import type { ProcessingContext } from "@nodetool-ai/runtime";
|
|
18
|
+
/**
|
|
19
|
+
* External input node whose output is structurally streaming (the trivial
|
|
20
|
+
* `genProcess` override flips `hasStreamingOutput()`), so the kernel keeps
|
|
21
|
+
* its downstream inbox open for `pushInput`/`finishInputStream` (and the
|
|
22
|
+
* ws-server's `stream_input`/`end_input_stream` commands) instead of
|
|
23
|
+
* dispatching a one-shot default. The generator itself is never invoked when
|
|
24
|
+
* the node has no incoming edges — the kernel dispatches it as an external
|
|
25
|
+
* input, not a spawned actor.
|
|
26
|
+
*
|
|
27
|
+
* The `nodetool.input.` prefix is load-bearing, not cosmetic: the kernel's
|
|
28
|
+
* `_isExternalInputNode`/`_dispatchInputs` (`runner.ts`) recognize external
|
|
29
|
+
* inputs by type-prefix, not by the `genProcess` override alone. A type
|
|
30
|
+
* outside that prefix (e.g. an early `test.reliability.*` draft of this
|
|
31
|
+
* fixture) gets spawned as an ordinary zero-input actor instead — it runs
|
|
32
|
+
* `genProcess` once to completion immediately, which defeats the whole
|
|
33
|
+
* point of a `pushInput`-fed external stream.
|
|
34
|
+
*/
|
|
35
|
+
export declare class ReliabilityStreamingInputNode extends BaseNode {
|
|
36
|
+
static readonly nodeType = "nodetool.input.ReliabilityStreamingInput";
|
|
37
|
+
static readonly title = "Reliability Streaming Input";
|
|
38
|
+
static readonly description = "Test fixture: external input fed by pushInput/stream_input.";
|
|
39
|
+
value: unknown;
|
|
40
|
+
process(): Promise<Record<string, unknown>>;
|
|
41
|
+
genProcess(): AsyncGenerator<Record<string, unknown>>;
|
|
42
|
+
}
|
|
43
|
+
/** Collects every value streamed on its `value` input into one terminal array. */
|
|
44
|
+
export declare class ReliabilityStreamSinkNode extends BaseNode {
|
|
45
|
+
static readonly nodeType = "test.reliability.StreamSink";
|
|
46
|
+
static readonly title = "Reliability Stream Sink";
|
|
47
|
+
static readonly description = "Test fixture: collects streamed values.";
|
|
48
|
+
static readonly isStreamingInput = true;
|
|
49
|
+
/** Never invoked directly — the kernel dispatches streaming-input nodes
|
|
50
|
+
* through `run()`. Required only to satisfy `BaseNode`'s abstract member. */
|
|
51
|
+
process(): Promise<Record<string, unknown>>;
|
|
52
|
+
run(inputs: StreamingInputs, outputs: StreamingOutputs): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A busy node with no incoming edges: parked on the run's own
|
|
56
|
+
* `AbortSignal` — busy from the graph's perspective, distinct from journey
|
|
57
|
+
* 6b's `nodetool.input.*`-fed streaming node — until the run is cancelled.
|
|
58
|
+
*
|
|
59
|
+
* Two earlier designs both raced:
|
|
60
|
+
* - Polling `inputs.signal.aborted` in a `setTimeout`-paced loop raced the
|
|
61
|
+
* runner's own cancel bookkeeping — whether this actor's `run()`
|
|
62
|
+
* returned before or after `WorkflowRunner` finished processing
|
|
63
|
+
* `cancel()` depended on `setTimeout` scheduling granularity, so the
|
|
64
|
+
* exact frame sequence differed from run to run *on the same driver*,
|
|
65
|
+
* not just across drivers (§6: "two runs ... produce identical
|
|
66
|
+
* normalized streams").
|
|
67
|
+
* - Blocking on `inputs.stream()` for a handle no edge ever targets
|
|
68
|
+
* doesn't block at all: the inbox has zero registered writers for it
|
|
69
|
+
* from the start, so `stream()` resolves as an immediate EOS and the
|
|
70
|
+
* actor "completes" before any cancel command can possibly land.
|
|
71
|
+
*
|
|
72
|
+
* Listening for the signal's `abort` event is neither: it makes no progress
|
|
73
|
+
* of its own to race, and resolves only once, however long that takes.
|
|
74
|
+
*/
|
|
75
|
+
export declare class ReliabilityCancelProbeNode extends BaseNode {
|
|
76
|
+
static readonly nodeType = "test.reliability.CancelProbe";
|
|
77
|
+
static readonly title = "Reliability Cancel Probe";
|
|
78
|
+
static readonly description = "Test fixture: blocks until cancelled.";
|
|
79
|
+
static readonly isStreamingInput = true;
|
|
80
|
+
/** Never invoked directly — see {@link ReliabilityStreamSinkNode.process}. */
|
|
81
|
+
process(): Promise<Record<string, unknown>>;
|
|
82
|
+
run(inputs: StreamingInputs): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Writes bytes through `context.storage` — the one write op every real
|
|
86
|
+
* asset-producing node eventually calls. Exists purely as a deterministic,
|
|
87
|
+
* hermetic target for task D3's `host-disk-full` fault: no shipped node
|
|
88
|
+
* writes an asset without also touching media-ref plumbing this fixture
|
|
89
|
+
* doesn't need to exercise. A missing `context.storage` (the normal
|
|
90
|
+
* fault-free hermetic default — `ExecutionSession`'s own minimal context
|
|
91
|
+
* wires none) is a configuration error for a journey using this node, not a
|
|
92
|
+
* silent no-op, so it throws rather than skipping the write.
|
|
93
|
+
*/
|
|
94
|
+
export declare class ReliabilityStorageWriteNode extends BaseNode {
|
|
95
|
+
static readonly nodeType = "test.reliability.StorageWrite";
|
|
96
|
+
static readonly title = "Reliability Storage Write";
|
|
97
|
+
static readonly description = "Test fixture: writes bytes through context.storage (task D3's host-disk-full fault).";
|
|
98
|
+
key: string;
|
|
99
|
+
process(context?: ProcessingContext): Promise<Record<string, unknown>>;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Joins a `list[str]` property — the shared-list-handle multi-edge fan-in
|
|
103
|
+
* target for the `fan-in-shared-list-handle` journey (A5,
|
|
104
|
+
* docs/RELIABILITY_TASKS.md Track A). Unlike `fan-out-fan-in-dag`'s `join`
|
|
105
|
+
* node (two edges into two *distinct* handles, a workaround for the
|
|
106
|
+
* hydration gap where a hydration path without `propertyTypes` couldn't
|
|
107
|
+
* distinguish a genuine list fan-in from an invalid one), this node takes
|
|
108
|
+
* two edges into the SAME `items` handle — the case that gap used to reject
|
|
109
|
+
* outright on any surface hydrating without a metadata resolver.
|
|
110
|
+
*/
|
|
111
|
+
export declare class ReliabilityListJoinNode extends BaseNode {
|
|
112
|
+
static readonly nodeType = "test.reliability.ListJoin";
|
|
113
|
+
static readonly title = "Reliability List Join";
|
|
114
|
+
static readonly description = "Test fixture: joins a list[str] input fed by a shared multi-edge handle.";
|
|
115
|
+
items: unknown;
|
|
116
|
+
process(): Promise<Record<string, unknown>>;
|
|
117
|
+
}
|
|
118
|
+
export declare const RELIABILITY_FIXTURE_NODES: readonly [typeof ReliabilityStreamingInputNode, typeof ReliabilityStreamSinkNode, typeof ReliabilityCancelProbeNode, typeof ReliabilityStorageWriteNode, typeof ReliabilityListJoinNode];
|
|
119
|
+
/** Registers the harness's own fixture nodes into a live registry. Idempotent
|
|
120
|
+
* (last-write-wins, matching `NodeRegistry.register`), so it's safe to call
|
|
121
|
+
* on both a fresh kernel-driver registry and a shared ws-server registry via
|
|
122
|
+
* `configureRegistry`. */
|
|
123
|
+
export declare function registerFixtureNodes(registry: NodeRegistry): void;
|
|
124
|
+
//# sourceMappingURL=fixture-nodes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixture-nodes.d.ts","sourceRoot":"","sources":["../../src/drivers/fixture-nodes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EACL,QAAQ,EAER,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,6BAA8B,SAAQ,QAAQ;IACzD,MAAM,CAAC,QAAQ,CAAC,QAAQ,8CAA8C;IACtE,MAAM,CAAC,QAAQ,CAAC,KAAK,iCAAiC;IACtD,MAAM,CAAC,QAAQ,CAAC,WAAW,iEACqC;IAGxD,KAAK,EAAE,OAAO,CAAC;IAEjB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAEhD;IAEM,UAAU,IAAI,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAE3D;CACF;AAED,kFAAkF;AAClF,qBAAa,yBAA0B,SAAQ,QAAQ;IACrD,MAAM,CAAC,QAAQ,CAAC,QAAQ,iCAAiC;IACzD,MAAM,CAAC,QAAQ,CAAC,KAAK,6BAA6B;IAClD,MAAM,CAAC,QAAQ,CAAC,WAAW,6CAA6C;IACxE,MAAM,CAAC,QAAQ,CAAC,gBAAgB,QAAQ;IAExC;iFAC6E;IACvE,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAEhD;IAEK,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3E;CACF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,0BAA2B,SAAQ,QAAQ;IACtD,MAAM,CAAC,QAAQ,CAAC,QAAQ,kCAAkC;IAC1D,MAAM,CAAC,QAAQ,CAAC,KAAK,8BAA8B;IACnD,MAAM,CAAC,QAAQ,CAAC,WAAW,2CAA2C;IACtE,MAAM,CAAC,QAAQ,CAAC,gBAAgB,QAAQ;IAExC,8EAA8E;IACxE,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAEhD;IAEK,GAAG,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhD;CACF;AAED;;;;;;;;;GASG;AACH,qBAAa,2BAA4B,SAAQ,QAAQ;IACvD,MAAM,CAAC,QAAQ,CAAC,QAAQ,mCAAmC;IAC3D,MAAM,CAAC,QAAQ,CAAC,KAAK,+BAA+B;IACpD,MAAM,CAAC,QAAQ,CAAC,WAAW,0FAC8D;IAGjF,GAAG,EAAE,MAAM,CAAC;IAEd,OAAO,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAY3E;CACF;AAED;;;;;;;;;GASG;AACH,qBAAa,uBAAwB,SAAQ,QAAQ;IACnD,MAAM,CAAC,QAAQ,CAAC,QAAQ,+BAA+B;IACvD,MAAM,CAAC,QAAQ,CAAC,KAAK,2BAA2B;IAChD,MAAM,CAAC,QAAQ,CAAC,WAAW,8EACkD;IAGrE,KAAK,EAAE,OAAO,CAAC;IAEjB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAGhD;CACF;AAED,eAAO,MAAM,yBAAyB,0LAM5B,CAAC;AAEX;;;0BAG0B;AAC1B,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAIjE"}
|