@ait-co/devtools 0.1.143 → 0.2.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/README.en.md +65 -163
- package/README.md +65 -192
- package/dist/in-app/auto.d.ts +1 -138
- package/dist/in-app/auto.js +28 -1102
- package/dist/in-app/auto.js.map +1 -1
- package/dist/in-app/index.d.ts +38 -547
- package/dist/in-app/index.d.ts.map +1 -1
- package/dist/in-app/index.js +62 -939
- package/dist/in-app/index.js.map +1 -1
- package/dist/mcp/cli.d.ts +1 -54
- package/dist/mcp/cli.js +33 -9720
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -88
- package/dist/mcp/server.js +36 -1076
- package/dist/mcp/server.js.map +1 -1
- package/dist/mock/index.d.ts +19 -20
- package/dist/mock/index.d.ts.map +1 -1
- package/dist/mock/index.js.map +1 -1
- package/dist/optional-peers-CDEFhlhJ.cjs +131 -0
- package/dist/optional-peers-CDEFhlhJ.cjs.map +1 -0
- package/dist/optional-peers-FdVbUJst.js +96 -0
- package/dist/optional-peers-FdVbUJst.js.map +1 -0
- package/dist/panel/index.js +1 -103
- package/dist/panel/index.js.map +1 -1
- package/dist/relay-url-store-CPZAn-T5.js +107 -0
- package/dist/relay-url-store-CPZAn-T5.js.map +1 -0
- package/dist/relay-url-store-DLjlvMSA.cjs +108 -0
- package/dist/relay-url-store-DLjlvMSA.cjs.map +1 -0
- package/dist/stubs/bin-devtools-mcp.js +58 -0
- package/dist/stubs/bin-devtools-mcp.js.map +1 -0
- package/dist/stubs/bin-devtools-test.d.ts +2 -0
- package/dist/stubs/bin-devtools-test.js +55 -0
- package/dist/stubs/bin-devtools-test.js.map +1 -0
- package/dist/test-runner/config.d.ts +1 -231
- package/dist/test-runner/config.js +41 -45
- package/dist/test-runner/config.js.map +1 -1
- package/dist/{tunnel-BVSMXctM.cjs → tunnel-BKZkOyQp.cjs} +27 -75
- package/dist/tunnel-BKZkOyQp.cjs.map +1 -0
- package/dist/{tunnel-D7xkimBu.js → tunnel-CqSCIrdU.js} +27 -75
- package/dist/tunnel-CqSCIrdU.js.map +1 -0
- package/dist/unplugin/index.cjs +17 -30
- package/dist/unplugin/index.cjs.map +1 -1
- package/dist/unplugin/index.d.cts +34 -5
- package/dist/unplugin/index.d.cts.map +1 -1
- package/dist/unplugin/index.d.ts +35 -6
- package/dist/unplugin/index.d.ts.map +1 -1
- package/dist/unplugin/index.js +17 -30
- package/dist/unplugin/index.js.map +1 -1
- package/dist/unplugin/tunnel.cjs +61 -74
- package/dist/unplugin/tunnel.cjs.map +1 -1
- package/dist/unplugin/tunnel.d.cts +22 -16
- package/dist/unplugin/tunnel.d.cts.map +1 -1
- package/dist/unplugin/tunnel.d.ts +22 -16
- package/dist/unplugin/tunnel.d.ts.map +1 -1
- package/dist/unplugin/tunnel.js +61 -74
- package/dist/unplugin/tunnel.js.map +1 -1
- package/package.json +17 -22
- package/dist/attach-orchestrator-0F0m_UqQ.js +0 -1845
- package/dist/attach-orchestrator-0F0m_UqQ.js.map +0 -1
- package/dist/attach-orchestrator-D65KxFy_.js +0 -1831
- package/dist/attach-orchestrator-D65KxFy_.js.map +0 -1
- package/dist/attach-orchestrator-DL3NQ9ca.js +0 -1846
- package/dist/attach-orchestrator-DL3NQ9ca.js.map +0 -1
- package/dist/bundle-C796JIwG.d.ts +0 -159
- package/dist/bundle-C796JIwG.d.ts.map +0 -1
- package/dist/capture-DsP525OZ.d.ts +0 -58
- package/dist/capture-DsP525OZ.d.ts.map +0 -1
- package/dist/cdp-connection-rP1WdnH5.d.ts +0 -287
- package/dist/cdp-connection-rP1WdnH5.d.ts.map +0 -1
- package/dist/cell-BaLvusOl.js +0 -68
- package/dist/cell-BaLvusOl.js.map +0 -1
- package/dist/cell-CBUS3-nT.js +0 -274
- package/dist/cell-CBUS3-nT.js.map +0 -1
- package/dist/cell-EBKKpAAT.js +0 -307
- package/dist/cell-EBKKpAAT.js.map +0 -1
- package/dist/chii-relay-BZ3HqWL5.js +0 -304
- package/dist/chii-relay-BZ3HqWL5.js.map +0 -1
- package/dist/chii-relay-D7eK2acz.cjs +0 -304
- package/dist/chii-relay-D7eK2acz.cjs.map +0 -1
- package/dist/debug-server-B3ABDrRI.js +0 -456
- package/dist/debug-server-B3ABDrRI.js.map +0 -1
- package/dist/debug-server-BWhwrVXa.js +0 -1158
- package/dist/debug-server-BWhwrVXa.js.map +0 -1
- package/dist/debug-server-CfQNxxGW.js +0 -600
- package/dist/debug-server-CfQNxxGW.js.map +0 -1
- package/dist/deeplink-B5-Hxu0Q.js +0 -62
- package/dist/deeplink-B5-Hxu0Q.js.map +0 -1
- package/dist/deeplink-BpO9qc-D.js +0 -62
- package/dist/deeplink-BpO9qc-D.js.map +0 -1
- package/dist/deeplink-BzdbA1gV.cjs +0 -62
- package/dist/deeplink-BzdbA1gV.cjs.map +0 -1
- package/dist/deeplink-DCScMYcp.cjs +0 -62
- package/dist/deeplink-DCScMYcp.cjs.map +0 -1
- package/dist/devtools-opener-3Drge_RJ.js +0 -75
- package/dist/devtools-opener-3Drge_RJ.js.map +0 -1
- package/dist/devtools-opener-B8nxrxqu.js +0 -71
- package/dist/devtools-opener-B8nxrxqu.js.map +0 -1
- package/dist/devtools-opener-BDY0w3_0.cjs +0 -68
- package/dist/devtools-opener-BDY0w3_0.cjs.map +0 -1
- package/dist/devtools-opener-BTl5A6Cd.js +0 -71
- package/dist/devtools-opener-BTl5A6Cd.js.map +0 -1
- package/dist/devtools-opener-CJpEsXXQ.js +0 -76
- package/dist/devtools-opener-CJpEsXXQ.js.map +0 -1
- package/dist/devtools-opener-CxtryS8c.js +0 -75
- package/dist/devtools-opener-CxtryS8c.js.map +0 -1
- package/dist/devtools-opener-iv1OwfJN.cjs +0 -68
- package/dist/devtools-opener-iv1OwfJN.cjs.map +0 -1
- package/dist/in-app/auto.d.ts.map +0 -1
- package/dist/mcp/cli.d.ts.map +0 -1
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/pool-DcaaOwUq.d.ts +0 -14761
- package/dist/pool-DcaaOwUq.d.ts.map +0 -1
- package/dist/qr-http-server--gl2-WKc.cjs +0 -1644
- package/dist/qr-http-server--gl2-WKc.cjs.map +0 -1
- package/dist/qr-http-server-Bb7lMeqi.js +0 -1644
- package/dist/qr-http-server-Bb7lMeqi.js.map +0 -1
- package/dist/qr-http-server-C_lqOrgc.js +0 -1644
- package/dist/qr-http-server-C_lqOrgc.js.map +0 -1
- package/dist/qr-http-server-CopuMbub.js +0 -1644
- package/dist/qr-http-server-CopuMbub.js.map +0 -1
- package/dist/qr-http-server-D-Off6K1.cjs +0 -1644
- package/dist/qr-http-server-D-Off6K1.cjs.map +0 -1
- package/dist/qr-http-server-DrbIVDjO.js +0 -1645
- package/dist/qr-http-server-DrbIVDjO.js.map +0 -1
- package/dist/qr-http-server-n1twN18z.js +0 -1644
- package/dist/qr-http-server-n1twN18z.js.map +0 -1
- package/dist/relay-factory-N9QobQxG.js +0 -206
- package/dist/relay-factory-N9QobQxG.js.map +0 -1
- package/dist/relay-secret-store-BPhN1upr.js +0 -240
- package/dist/relay-secret-store-BPhN1upr.js.map +0 -1
- package/dist/relay-secret-store-Bmyleu0A.js +0 -154
- package/dist/relay-secret-store-Bmyleu0A.js.map +0 -1
- package/dist/relay-secret-store-CQenfcSL.js +0 -154
- package/dist/relay-secret-store-CQenfcSL.js.map +0 -1
- package/dist/relay-secret-store-DKxs7zwq.js +0 -153
- package/dist/relay-secret-store-DKxs7zwq.js.map +0 -1
- package/dist/relay-secret-store-DWKdV-eY.cjs +0 -241
- package/dist/relay-secret-store-DWKdV-eY.cjs.map +0 -1
- package/dist/relay-secret-store-WJ8EGkIl.js +0 -153
- package/dist/relay-secret-store-WJ8EGkIl.js.map +0 -1
- package/dist/relay-url-store-1FGuSYAn.cjs +0 -115
- package/dist/relay-url-store-1FGuSYAn.cjs.map +0 -1
- package/dist/relay-url-store-BR2XodiO.js +0 -123
- package/dist/relay-url-store-BR2XodiO.js.map +0 -1
- package/dist/relay-url-store-Bskcyeg8.js +0 -114
- package/dist/relay-url-store-Bskcyeg8.js.map +0 -1
- package/dist/relay-url-store-CH63fVCm.js +0 -122
- package/dist/relay-url-store-CH63fVCm.js.map +0 -1
- package/dist/relay-url-store-DaY1QPes.js +0 -123
- package/dist/relay-url-store-DaY1QPes.js.map +0 -1
- package/dist/relay-url-store-xmUuTjXA.js +0 -122
- package/dist/relay-url-store-xmUuTjXA.js.map +0 -1
- package/dist/relay-worker-B5HKkGUY.js +0 -832
- package/dist/relay-worker-B5HKkGUY.js.map +0 -1
- package/dist/relay-worker-YdlpZQl9.d.ts +0 -214
- package/dist/relay-worker-YdlpZQl9.d.ts.map +0 -1
- package/dist/rolldown-runtime-DGkTqVfb.js +0 -15
- package/dist/rolldown-runtime-DUslC3ob.js +0 -14
- package/dist/runtime-kn9DxOeg.d.ts +0 -249
- package/dist/runtime-kn9DxOeg.d.ts.map +0 -1
- package/dist/test-runner/bin.js +0 -2584
- package/dist/test-runner/bin.js.map +0 -1
- package/dist/test-runner/bridge-stub.d.ts +0 -125
- package/dist/test-runner/bridge-stub.d.ts.map +0 -1
- package/dist/test-runner/bridge-stub.js +0 -92
- package/dist/test-runner/bridge-stub.js.map +0 -1
- package/dist/test-runner/bundle.d.ts +0 -2
- package/dist/test-runner/bundle.js +0 -439
- package/dist/test-runner/bundle.js.map +0 -1
- package/dist/test-runner/capture.d.ts +0 -2
- package/dist/test-runner/capture.js +0 -44
- package/dist/test-runner/capture.js.map +0 -1
- package/dist/test-runner/config.d.ts.map +0 -1
- package/dist/test-runner/method-pace.d.ts +0 -82
- package/dist/test-runner/method-pace.d.ts.map +0 -1
- package/dist/test-runner/method-pace.js +0 -120
- package/dist/test-runner/method-pace.js.map +0 -1
- package/dist/test-runner/pool.d.ts +0 -2
- package/dist/test-runner/pool.js +0 -136
- package/dist/test-runner/pool.js.map +0 -1
- package/dist/test-runner/relay-factory.d.ts +0 -11245
- package/dist/test-runner/relay-factory.d.ts.map +0 -1
- package/dist/test-runner/relay-factory.js +0 -206
- package/dist/test-runner/relay-factory.js.map +0 -1
- package/dist/test-runner/relay-worker.d.ts +0 -2
- package/dist/test-runner/relay-worker.js +0 -2
- package/dist/test-runner/report.d.ts +0 -163
- package/dist/test-runner/report.d.ts.map +0 -1
- package/dist/test-runner/report.js +0 -198
- package/dist/test-runner/report.js.map +0 -1
- package/dist/test-runner/rpc.d.ts +0 -56
- package/dist/test-runner/rpc.d.ts.map +0 -1
- package/dist/test-runner/rpc.js +0 -98
- package/dist/test-runner/rpc.js.map +0 -1
- package/dist/test-runner/runtime.d.ts +0 -2
- package/dist/test-runner/runtime.js +0 -659
- package/dist/test-runner/runtime.js.map +0 -1
- package/dist/test-runner/task-graph.d.ts +0 -38
- package/dist/test-runner/task-graph.d.ts.map +0 -1
- package/dist/test-runner/task-graph.js +0 -182
- package/dist/test-runner/task-graph.js.map +0 -1
- package/dist/throttle-DKKzX1qC.js +0 -59
- package/dist/throttle-DKKzX1qC.js.map +0 -1
- package/dist/totp-95OAa20j.js +0 -64
- package/dist/totp-95OAa20j.js.map +0 -1
- package/dist/totp-BjtoQNfu.cjs +0 -64
- package/dist/totp-BjtoQNfu.cjs.map +0 -1
- package/dist/totp-CZLLKfOC.js +0 -200
- package/dist/totp-CZLLKfOC.js.map +0 -1
- package/dist/totp-DAxys-r0.js +0 -199
- package/dist/totp-DAxys-r0.js.map +0 -1
- package/dist/totp-DIbrZtI7.js +0 -189
- package/dist/totp-DIbrZtI7.js.map +0 -1
- package/dist/totp-Df252ZdA.cjs +0 -192
- package/dist/totp-Df252ZdA.cjs.map +0 -1
- package/dist/totp-DfekTBk3.js +0 -211
- package/dist/totp-DfekTBk3.js.map +0 -1
- package/dist/totp-Dwft0Kz7.js +0 -3
- package/dist/totp-WY6l0ysP.js +0 -190
- package/dist/totp-WY6l0ysP.js.map +0 -1
- package/dist/tunnel-BVSMXctM.cjs.map +0 -1
- package/dist/tunnel-D7xkimBu.js.map +0 -1
- /package/dist/{test-runner/bin.d.ts → stubs/bin-devtools-mcp.d.ts} +0 -0
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
//#region src/test-runner/report.ts
|
|
4
|
-
/**
|
|
5
|
-
* Runner-agnostic report serialisation for env3 test runs (devtools#696).
|
|
6
|
-
*
|
|
7
|
-
* Both env3 execution paths — the Vitest custom pool (`pool.ts`) and the
|
|
8
|
-
* standalone `devtools-test` CLI (`cli.ts`) — call the same core
|
|
9
|
-
* `runTestFilesOverRelay` and so produce the same {@link RelayRunReport}. This
|
|
10
|
-
* module is the single, runner-neutral place that turns that in-memory report
|
|
11
|
-
* into a stable on-disk artifact so a 2.x run and a 3.0 run can be diffed
|
|
12
|
-
* cell-by-cell after the fact.
|
|
13
|
-
*
|
|
14
|
-
* The serialised schema is deliberately MINIMAL and secret-free:
|
|
15
|
-
*
|
|
16
|
-
* - file paths are stored RELATIVE to `projectRoot` (no absolute `/Users/...`
|
|
17
|
-
* leakage — see {@link RunnerAgnosticReport.files});
|
|
18
|
-
* - the cell metadata (sdkLine/platform) is baked INTO the body, not only the
|
|
19
|
-
* filename, so a moved artifact never loses its provenance;
|
|
20
|
-
* - NO relay wss / scheme / TOTP / relayUrl fields exist in the schema at all
|
|
21
|
-
* (enforced by the type + this comment) — error strings are the matcher
|
|
22
|
-
* message only, inherited from rpc.ts which already strips expression/value.
|
|
23
|
-
*
|
|
24
|
-
* react-free — depends only on the type-level `RelayRunReport` and `node:fs` /
|
|
25
|
-
* `node:path`. Safe to bundle without pulling the chii/cloudflared graph.
|
|
26
|
-
*/
|
|
27
|
-
/**
|
|
28
|
-
* Converts an absolute (or already-relative) file path to a projectRoot-relative
|
|
29
|
-
* one. `path.relative` returns `''` when the paths are equal — guard that to the
|
|
30
|
-
* basename so the field is never empty.
|
|
31
|
-
*
|
|
32
|
-
* SECRET-HANDLING: this is the single choke point that strips absolute project
|
|
33
|
-
* paths from the artifact.
|
|
34
|
-
*/
|
|
35
|
-
function relativise(projectRoot, file) {
|
|
36
|
-
const rel = path.relative(projectRoot, file);
|
|
37
|
-
if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) return path.basename(file);
|
|
38
|
-
return rel;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Serialises a {@link RelayRunReport} into the runner-agnostic, secret-free
|
|
42
|
-
* on-disk shape. Pure — no IO; testable with a plain report + meta.
|
|
43
|
-
*
|
|
44
|
-
* @param report - The core relay run report.
|
|
45
|
-
* @param meta - Cell axes + projectRoot (projectRoot is consumed, not stored).
|
|
46
|
-
*/
|
|
47
|
-
function serializeRelayReport(report, meta) {
|
|
48
|
-
return {
|
|
49
|
-
cell: {
|
|
50
|
-
sdkLine: meta.sdkLine,
|
|
51
|
-
platform: meta.platform
|
|
52
|
-
},
|
|
53
|
-
startedAt: report.startedAt,
|
|
54
|
-
duration: report.duration,
|
|
55
|
-
totals: report.totals,
|
|
56
|
-
...report.preflight ? { preflight: report.preflight } : {},
|
|
57
|
-
files: report.files.map((f) => {
|
|
58
|
-
const file = relativise(meta.projectRoot, f.file);
|
|
59
|
-
const modeField = f.mode === "manual" || f.mode === "stubbed" ? { mode: f.mode } : {};
|
|
60
|
-
if ("error" in f.result) return {
|
|
61
|
-
file,
|
|
62
|
-
error: f.result.error,
|
|
63
|
-
...modeField
|
|
64
|
-
};
|
|
65
|
-
return {
|
|
66
|
-
file,
|
|
67
|
-
duration: f.result.duration,
|
|
68
|
-
passed: f.result.passed,
|
|
69
|
-
failed: f.result.failed,
|
|
70
|
-
skipped: f.result.skipped,
|
|
71
|
-
tests: f.result.tests,
|
|
72
|
-
...modeField
|
|
73
|
-
};
|
|
74
|
-
})
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Writes the serialised report to `<dir>/<sdkLine>.<platform>.json`, creating
|
|
79
|
-
* `dir` if needed. Returns the absolute path(s) written.
|
|
80
|
-
*
|
|
81
|
-
* The cell-suffixed filename keeps 2.x and 3.0 (and per-platform) runs as
|
|
82
|
-
* distinct artifacts in the same directory; the same cell metadata is also baked
|
|
83
|
-
* into the body so a renamed/moved file still carries its provenance.
|
|
84
|
-
*
|
|
85
|
-
* Manual-run provenance (devtools#741): when `report.files` contains ANY
|
|
86
|
-
* `mode: 'manual'` entry (i.e. this run included `--manual-blocking` files),
|
|
87
|
-
* the manual-tagged files are written to a SEPARATE
|
|
88
|
-
* `<dir>/<sdkLine>.<platform>.manual.json` artifact instead of the standard
|
|
89
|
-
* one — the standard `<sdkLine>.<platform>.json` filename is reserved for the
|
|
90
|
-
* regular (unattended) files only, so a manual run's presence never mutates
|
|
91
|
-
* what the unattended-baseline filename means.
|
|
92
|
-
*
|
|
93
|
-
* Stub-blocking provenance (devtools#740, DT-2): `mode: 'stubbed'` files are
|
|
94
|
-
* written to a THIRD, separate `<dir>/<sdkLine>.<platform>.stubbed.json`
|
|
95
|
-
* artifact — never merged into the standard file (it is unattended, like the
|
|
96
|
-
* standard file, so a naive filename split could conflate the two) and never
|
|
97
|
-
* merged into `.manual.json` (it did NOT have a human present). Its body is
|
|
98
|
-
* ALSO stamped `cell.bridgeStub: true` — the mandatory HYBRID-cell provenance
|
|
99
|
-
* from the issue: a stubbed result must never be silently mixed into the
|
|
100
|
-
* real-device baseline report.
|
|
101
|
-
*
|
|
102
|
-
* If ALL files in the run are regular, only the standard artifact is written
|
|
103
|
-
* (today's behavior, unchanged). Any combination of the three modes present
|
|
104
|
-
* in a single run writes ONLY the corresponding artifacts — each is additive,
|
|
105
|
-
* never replacing another.
|
|
106
|
-
*
|
|
107
|
-
* SECRET-HANDLING: the written body contains no relay/secret fields (the schema
|
|
108
|
-
* has none). `dir`/`projectRoot` are local filesystem paths, never logged here.
|
|
109
|
-
*
|
|
110
|
-
* @param report - The core relay run report.
|
|
111
|
-
* @param dir - Output directory (created recursively if missing).
|
|
112
|
-
* @param meta - Cell axes + projectRoot.
|
|
113
|
-
* @returns The absolute path(s) written, in order: standard first (if any
|
|
114
|
-
* regular files ran), then manual (if any manual files ran), then stubbed
|
|
115
|
-
* (if any stubbed files ran). At least one path is always returned when
|
|
116
|
-
* `report.files` is non-empty.
|
|
117
|
-
*/
|
|
118
|
-
async function writeReportArtifact(report, dir, meta) {
|
|
119
|
-
const serialised = serializeRelayReport(report, meta);
|
|
120
|
-
await mkdir(dir, { recursive: true });
|
|
121
|
-
const regularFiles = serialised.files.filter((f) => f.mode === void 0);
|
|
122
|
-
const manualFiles = serialised.files.filter((f) => f.mode === "manual");
|
|
123
|
-
const stubbedFiles = serialised.files.filter((f) => f.mode === "stubbed");
|
|
124
|
-
const written = [];
|
|
125
|
-
if (regularFiles.length > 0 || serialised.files.length === 0) {
|
|
126
|
-
const outFile = path.join(dir, `${meta.sdkLine}.${meta.platform}.json`);
|
|
127
|
-
await writeFile(outFile, `${JSON.stringify({
|
|
128
|
-
...serialised,
|
|
129
|
-
files: regularFiles
|
|
130
|
-
}, null, 2)}\n`, "utf8");
|
|
131
|
-
written.push(outFile);
|
|
132
|
-
}
|
|
133
|
-
if (manualFiles.length > 0) {
|
|
134
|
-
const manualOutFile = path.join(dir, `${meta.sdkLine}.${meta.platform}.manual.json`);
|
|
135
|
-
await writeFile(manualOutFile, `${JSON.stringify({
|
|
136
|
-
...serialised,
|
|
137
|
-
files: manualFiles
|
|
138
|
-
}, null, 2)}\n`, "utf8");
|
|
139
|
-
written.push(manualOutFile);
|
|
140
|
-
}
|
|
141
|
-
if (stubbedFiles.length > 0) {
|
|
142
|
-
const stubbedOutFile = path.join(dir, `${meta.sdkLine}.${meta.platform}.stubbed.json`);
|
|
143
|
-
await writeFile(stubbedOutFile, `${JSON.stringify({
|
|
144
|
-
...serialised,
|
|
145
|
-
cell: {
|
|
146
|
-
...serialised.cell,
|
|
147
|
-
bridgeStub: true
|
|
148
|
-
},
|
|
149
|
-
files: stubbedFiles
|
|
150
|
-
}, null, 2)}\n`, "utf8");
|
|
151
|
-
written.push(stubbedOutFile);
|
|
152
|
-
}
|
|
153
|
-
return written;
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Writes harvested `__AIT_CAPTURE__` lines to per-category files under `dir`,
|
|
157
|
-
* named `<category>.<sdkLine>.<platform>.json` — the SAME convention
|
|
158
|
-
* sdk-example's env1 `flushCapture` uses on the filesystem, so env1 and env3
|
|
159
|
-
* capture artifacts line up for diffing.
|
|
160
|
-
*
|
|
161
|
-
* Each line's `json` payload is an opaque JSON array of capture records. Lines
|
|
162
|
-
* sharing a category are concatenated into one array, in harvest order.
|
|
163
|
-
*
|
|
164
|
-
* SECRET-HANDLING: only allowlist-prefixed capture lines reach here (the parser
|
|
165
|
-
* dropped wss/scheme noise); the `json` payload is written verbatim but is a
|
|
166
|
-
* capture record array, not a relay/secret.
|
|
167
|
-
*
|
|
168
|
-
* @param captures - Parsed capture lines (from `RelayRunReport.captures`).
|
|
169
|
-
* @param dir - Output directory (created recursively if missing).
|
|
170
|
-
* @param cell - Cell axes for the filename suffix.
|
|
171
|
-
* @returns The absolute paths written (one per category), in category order.
|
|
172
|
-
*/
|
|
173
|
-
async function writeCaptureArtifacts(captures, dir, cell) {
|
|
174
|
-
if (captures.length === 0) return [];
|
|
175
|
-
const byCategory = /* @__PURE__ */ new Map();
|
|
176
|
-
for (const { category, json } of captures) {
|
|
177
|
-
let merged = byCategory.get(category);
|
|
178
|
-
if (!merged) {
|
|
179
|
-
merged = [];
|
|
180
|
-
byCategory.set(category, merged);
|
|
181
|
-
}
|
|
182
|
-
const parsed = JSON.parse(json);
|
|
183
|
-
if (Array.isArray(parsed)) merged.push(...parsed);
|
|
184
|
-
else merged.push(parsed);
|
|
185
|
-
}
|
|
186
|
-
await mkdir(dir, { recursive: true });
|
|
187
|
-
const written = [];
|
|
188
|
-
for (const [category, records] of byCategory) {
|
|
189
|
-
const outFile = path.join(dir, `${category}.${cell.sdkLine}.${cell.platform}.json`);
|
|
190
|
-
await writeFile(outFile, `${JSON.stringify(records, null, 2)}\n`, "utf8");
|
|
191
|
-
written.push(outFile);
|
|
192
|
-
}
|
|
193
|
-
return written;
|
|
194
|
-
}
|
|
195
|
-
//#endregion
|
|
196
|
-
export { serializeRelayReport, writeCaptureArtifacts, writeReportArtifact };
|
|
197
|
-
|
|
198
|
-
//# sourceMappingURL=report.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","names":[],"sources":["../../src/test-runner/report.ts"],"sourcesContent":["/**\n * Runner-agnostic report serialisation for env3 test runs (devtools#696).\n *\n * Both env3 execution paths — the Vitest custom pool (`pool.ts`) and the\n * standalone `devtools-test` CLI (`cli.ts`) — call the same core\n * `runTestFilesOverRelay` and so produce the same {@link RelayRunReport}. This\n * module is the single, runner-neutral place that turns that in-memory report\n * into a stable on-disk artifact so a 2.x run and a 3.0 run can be diffed\n * cell-by-cell after the fact.\n *\n * The serialised schema is deliberately MINIMAL and secret-free:\n *\n * - file paths are stored RELATIVE to `projectRoot` (no absolute `/Users/...`\n * leakage — see {@link RunnerAgnosticReport.files});\n * - the cell metadata (sdkLine/platform) is baked INTO the body, not only the\n * filename, so a moved artifact never loses its provenance;\n * - NO relay wss / scheme / TOTP / relayUrl fields exist in the schema at all\n * (enforced by the type + this comment) — error strings are the matcher\n * message only, inherited from rpc.ts which already strips expression/value.\n *\n * react-free — depends only on the type-level `RelayRunReport` and `node:fs` /\n * `node:path`. Safe to bundle without pulling the chii/cloudflared graph.\n */\n\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { AitCaptureLine } from './capture.js';\nimport type { RelayRunReport } from './relay-worker.js';\nimport type { TestResult } from './runtime.js';\n\n/** The cell axes a report is stamped with — the test-matrix coordinates. */\nexport interface ReportCellMeta {\n /** SDK line under test (e.g. `'2.x'` / `'3.x'`). */\n sdkLine: string;\n /** Platform under test (e.g. `'ios'` / `'android'` / `'mock'`). */\n platform: string;\n /**\n * Project root the run was launched from. Used ONLY to relativise file paths\n * out of the serialised report — never stored in the output. SECRET-HANDLING:\n * absolute project paths must not leak into artifacts.\n */\n projectRoot: string;\n}\n\n/** Per-file slice of a {@link RunnerAgnosticReport}. */\nexport interface RunnerAgnosticFileReport {\n /**\n * Test file path, RELATIVE to `projectRoot`. Never absolute — `path.relative`\n * strips the machine-specific prefix so the artifact is portable and leaks no\n * local filesystem layout.\n */\n file: string;\n /** Whole-file bundle/inject error (matcher message only), when the file failed. */\n error?: string;\n /** In-page run duration (ms) for this file, when it ran. */\n duration?: number;\n passed?: number;\n failed?: number;\n skipped?: number;\n /** Per-test results, when the file ran. Error strings are matcher messages only. */\n tests?: TestResult[];\n /**\n * Present only when this file ran under `--manual-blocking` or\n * `--stub-blocking`. Absent (never `false`) for unattended files:\n * absence-means-unattended is the contract, mirroring `FileResult.mode` in\n * relay-worker.ts.\n *\n * - `'manual'` — devtools#741, human-attended, real native envelopes.\n * - `'stubbed'` — devtools#740 (DT-2), unattended, blocking-UI calls\n * answered from fixtures. A manual/stubbed-stamped record must never be\n * diffed against an unattended (or each other's) baseline as if\n * equivalent.\n */\n mode?: 'manual' | 'stubbed';\n}\n\n/**\n * The runner-neutral, secret-free report written to disk. There are\n * intentionally NO wss/scheme/TOTP/relayUrl fields on this type — the absence is\n * load-bearing (SECRET-HANDLING) and must not be \"completed\" by a future edit.\n */\nexport interface RunnerAgnosticReport {\n /**\n * Cell axes this run belongs to — baked into the body for portability.\n * `bridgeStub` is present + `true` ONLY on the artifact written for\n * stub-blocking files (devtools#740, DT-2) — see `writeReportArtifact`'s\n * doc for why this is a SEPARATE `.stubbed.json` artifact rather than a\n * flag inside the standard/`.manual.json` report. Absent (never `false`)\n * on every other artifact — this is the mandatory HYBRID-cell provenance\n * stamp the issue calls for: a stubbed run must never be silently mixed\n * into the real-device baseline report.\n */\n cell: { sdkLine: string; platform: string; bridgeStub?: true };\n /** ISO timestamp of when the run started (from the core report). */\n startedAt: string;\n /** Total wall-clock ms (bundling + sequential injection). */\n duration: number;\n /** Flattened totals across all files. */\n totals: RelayRunReport['totals'];\n /** Per-file results with projectRoot-relative paths. */\n files: RunnerAgnosticFileReport[];\n /**\n * Permission-state preflight result (devtools#739), mirrored verbatim from\n * `RelayRunReport.preflight` — see that field's docblock. Absent when the\n * preflight did not complete (non-fatal; the run still succeeds). No\n * secrets — permission-state strings only.\n */\n preflight?: { permissions: Record<string, string> };\n}\n\n/**\n * Converts an absolute (or already-relative) file path to a projectRoot-relative\n * one. `path.relative` returns `''` when the paths are equal — guard that to the\n * basename so the field is never empty.\n *\n * SECRET-HANDLING: this is the single choke point that strips absolute project\n * paths from the artifact.\n */\nfunction relativise(projectRoot: string, file: string): string {\n const rel = path.relative(projectRoot, file);\n if (rel === '' || rel.startsWith('..') || path.isAbsolute(rel)) {\n // Outside the project root (or equal) — fall back to the basename rather\n // than emitting an absolute or `../../..` traversal path.\n return path.basename(file);\n }\n return rel;\n}\n\n/**\n * Serialises a {@link RelayRunReport} into the runner-agnostic, secret-free\n * on-disk shape. Pure — no IO; testable with a plain report + meta.\n *\n * @param report - The core relay run report.\n * @param meta - Cell axes + projectRoot (projectRoot is consumed, not stored).\n */\nexport function serializeRelayReport(\n report: RelayRunReport,\n meta: ReportCellMeta,\n): RunnerAgnosticReport {\n return {\n cell: { sdkLine: meta.sdkLine, platform: meta.platform },\n startedAt: report.startedAt,\n duration: report.duration,\n totals: report.totals,\n ...(report.preflight ? { preflight: report.preflight } : {}),\n files: report.files.map((f): RunnerAgnosticFileReport => {\n const file = relativise(meta.projectRoot, f.file);\n const modeField =\n f.mode === 'manual' || f.mode === 'stubbed' ? ({ mode: f.mode } as const) : {};\n if ('error' in f.result) {\n // Matcher/inject error message only — rpc.ts already stripped the\n // expression/value upstream.\n return { file, error: f.result.error, ...modeField };\n }\n return {\n file,\n duration: f.result.duration,\n passed: f.result.passed,\n failed: f.result.failed,\n skipped: f.result.skipped,\n tests: f.result.tests,\n ...modeField,\n };\n }),\n };\n}\n\n/**\n * Writes the serialised report to `<dir>/<sdkLine>.<platform>.json`, creating\n * `dir` if needed. Returns the absolute path(s) written.\n *\n * The cell-suffixed filename keeps 2.x and 3.0 (and per-platform) runs as\n * distinct artifacts in the same directory; the same cell metadata is also baked\n * into the body so a renamed/moved file still carries its provenance.\n *\n * Manual-run provenance (devtools#741): when `report.files` contains ANY\n * `mode: 'manual'` entry (i.e. this run included `--manual-blocking` files),\n * the manual-tagged files are written to a SEPARATE\n * `<dir>/<sdkLine>.<platform>.manual.json` artifact instead of the standard\n * one — the standard `<sdkLine>.<platform>.json` filename is reserved for the\n * regular (unattended) files only, so a manual run's presence never mutates\n * what the unattended-baseline filename means.\n *\n * Stub-blocking provenance (devtools#740, DT-2): `mode: 'stubbed'` files are\n * written to a THIRD, separate `<dir>/<sdkLine>.<platform>.stubbed.json`\n * artifact — never merged into the standard file (it is unattended, like the\n * standard file, so a naive filename split could conflate the two) and never\n * merged into `.manual.json` (it did NOT have a human present). Its body is\n * ALSO stamped `cell.bridgeStub: true` — the mandatory HYBRID-cell provenance\n * from the issue: a stubbed result must never be silently mixed into the\n * real-device baseline report.\n *\n * If ALL files in the run are regular, only the standard artifact is written\n * (today's behavior, unchanged). Any combination of the three modes present\n * in a single run writes ONLY the corresponding artifacts — each is additive,\n * never replacing another.\n *\n * SECRET-HANDLING: the written body contains no relay/secret fields (the schema\n * has none). `dir`/`projectRoot` are local filesystem paths, never logged here.\n *\n * @param report - The core relay run report.\n * @param dir - Output directory (created recursively if missing).\n * @param meta - Cell axes + projectRoot.\n * @returns The absolute path(s) written, in order: standard first (if any\n * regular files ran), then manual (if any manual files ran), then stubbed\n * (if any stubbed files ran). At least one path is always returned when\n * `report.files` is non-empty.\n */\nexport async function writeReportArtifact(\n report: RelayRunReport,\n dir: string,\n meta: ReportCellMeta,\n): Promise<string[]> {\n const serialised = serializeRelayReport(report, meta);\n await mkdir(dir, { recursive: true });\n\n const regularFiles = serialised.files.filter((f) => f.mode === undefined);\n const manualFiles = serialised.files.filter((f) => f.mode === 'manual');\n const stubbedFiles = serialised.files.filter((f) => f.mode === 'stubbed');\n\n const written: string[] = [];\n\n // Standard artifact: written whenever there are regular files, OR when the\n // whole run is empty (preserves the pre-#741 \"always write one file\"\n // behavior for a run with zero files, e.g. an empty glob match).\n if (regularFiles.length > 0 || serialised.files.length === 0) {\n const outFile = path.join(dir, `${meta.sdkLine}.${meta.platform}.json`);\n await writeFile(\n outFile,\n `${JSON.stringify({ ...serialised, files: regularFiles }, null, 2)}\\n`,\n 'utf8',\n );\n written.push(outFile);\n }\n\n if (manualFiles.length > 0) {\n const manualOutFile = path.join(dir, `${meta.sdkLine}.${meta.platform}.manual.json`);\n await writeFile(\n manualOutFile,\n `${JSON.stringify({ ...serialised, files: manualFiles }, null, 2)}\\n`,\n 'utf8',\n );\n written.push(manualOutFile);\n }\n\n if (stubbedFiles.length > 0) {\n const stubbedOutFile = path.join(dir, `${meta.sdkLine}.${meta.platform}.stubbed.json`);\n await writeFile(\n stubbedOutFile,\n `${JSON.stringify(\n {\n ...serialised,\n cell: { ...serialised.cell, bridgeStub: true as const },\n files: stubbedFiles,\n },\n null,\n 2,\n )}\\n`,\n 'utf8',\n );\n written.push(stubbedOutFile);\n }\n\n return written;\n}\n\n/**\n * Writes harvested `__AIT_CAPTURE__` lines to per-category files under `dir`,\n * named `<category>.<sdkLine>.<platform>.json` — the SAME convention\n * sdk-example's env1 `flushCapture` uses on the filesystem, so env1 and env3\n * capture artifacts line up for diffing.\n *\n * Each line's `json` payload is an opaque JSON array of capture records. Lines\n * sharing a category are concatenated into one array, in harvest order.\n *\n * SECRET-HANDLING: only allowlist-prefixed capture lines reach here (the parser\n * dropped wss/scheme noise); the `json` payload is written verbatim but is a\n * capture record array, not a relay/secret.\n *\n * @param captures - Parsed capture lines (from `RelayRunReport.captures`).\n * @param dir - Output directory (created recursively if missing).\n * @param cell - Cell axes for the filename suffix.\n * @returns The absolute paths written (one per category), in category order.\n */\nexport async function writeCaptureArtifacts(\n captures: ReadonlyArray<AitCaptureLine>,\n dir: string,\n cell: { sdkLine: string; platform: string },\n): Promise<string[]> {\n if (captures.length === 0) return [];\n\n // Group payloads by category, preserving harvest order. Each payload is an\n // opaque JSON array string; concatenate parsed arrays under the same category.\n const byCategory = new Map<string, unknown[]>();\n for (const { category, json } of captures) {\n let merged = byCategory.get(category);\n if (!merged) {\n merged = [];\n byCategory.set(category, merged);\n }\n // The parser already validated `json` parses; an array payload is expected.\n const parsed = JSON.parse(json) as unknown;\n if (Array.isArray(parsed)) {\n merged.push(...parsed);\n } else {\n merged.push(parsed);\n }\n }\n\n await mkdir(dir, { recursive: true });\n const written: string[] = [];\n for (const [category, records] of byCategory) {\n const outFile = path.join(dir, `${category}.${cell.sdkLine}.${cell.platform}.json`);\n await writeFile(outFile, `${JSON.stringify(records, null, 2)}\\n`, 'utf8');\n written.push(outFile);\n }\n return written;\n}\n"],"mappings":"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"}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { t as CdpConnection } from "../cdp-connection-rP1WdnH5.js";
|
|
2
|
-
import { t as RunReport } from "../runtime-kn9DxOeg.js";
|
|
3
|
-
|
|
4
|
-
//#region src/test-runner/rpc.d.ts
|
|
5
|
-
/**
|
|
6
|
-
* Wraps bundle code in a self-executing IIFE that:
|
|
7
|
-
* 1. Evaluates the bundle (registering describe/it/test).
|
|
8
|
-
* 2. Calls `__testBundle.runTestModule(...)` — the entry the runtime exports.
|
|
9
|
-
* 3. Returns a JSON-serialised `RunReport` string.
|
|
10
|
-
*
|
|
11
|
-
* The double-serialisation (RunReport → JSON string → returnByValue string)
|
|
12
|
-
* is intentional: CDP `returnByValue` reliably transports strings; deeply
|
|
13
|
-
* nested objects can lose fidelity across the Chii relay.
|
|
14
|
-
*
|
|
15
|
-
* SECRET-HANDLING: `bundleCode` MUST NOT be logged by callers.
|
|
16
|
-
*/
|
|
17
|
-
declare function buildRunTestsExpression(bundleCode: string): string;
|
|
18
|
-
/**
|
|
19
|
-
* Result of `injectAndRunBundle`.
|
|
20
|
-
*/
|
|
21
|
-
type RpcRunResult = {
|
|
22
|
-
ok: true;
|
|
23
|
-
report: RunReport;
|
|
24
|
-
} | {
|
|
25
|
-
ok: false;
|
|
26
|
-
error: string;
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* Parses the raw CDP `returnByValue` result from a `buildRunTestsExpression`
|
|
30
|
-
* evaluate call into a typed `RpcRunResult`.
|
|
31
|
-
*
|
|
32
|
-
* Throws only on parse failure — an `ok:false` envelope is a normal result.
|
|
33
|
-
*
|
|
34
|
-
* SECRET-HANDLING: `rawValue` is not included in error messages.
|
|
35
|
-
*/
|
|
36
|
-
declare function parseRunTestsResult(rawValue: unknown): RpcRunResult;
|
|
37
|
-
/**
|
|
38
|
-
* Injects `bundleCode` into the attached page and awaits test execution.
|
|
39
|
-
*
|
|
40
|
-
* Uses `Runtime.evaluate` with `awaitPromise: true` to wait for the
|
|
41
|
-
* async IIFE to settle. `timeoutMs` (default 60s) is the authoritative file
|
|
42
|
-
* timeout — this function races it against the evaluate call AND (devtools#747)
|
|
43
|
-
* threads it down to the underlying `CdpConnection`'s own per-command watchdog
|
|
44
|
-
* (plus a small margin) so that watchdog can never undercut this race. Split
|
|
45
|
-
* into smaller files if you hit the timeout.
|
|
46
|
-
*
|
|
47
|
-
* @param connection - Active CDP connection (relay or local).
|
|
48
|
-
* @param bundleCode - IIFE bundle string from `bundleTestFile`.
|
|
49
|
-
* @param timeoutMs - Override the default 60 s timeout.
|
|
50
|
-
*
|
|
51
|
-
* SECRET-HANDLING: `bundleCode` and the raw CDP result value are never logged.
|
|
52
|
-
*/
|
|
53
|
-
declare function injectAndRunBundle(connection: CdpConnection, bundleCode: string, timeoutMs?: number): Promise<RpcRunResult>;
|
|
54
|
-
//#endregion
|
|
55
|
-
export { RpcRunResult, buildRunTestsExpression, injectAndRunBundle, parseRunTestsResult };
|
|
56
|
-
//# sourceMappingURL=rpc.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rpc.d.ts","names":[],"sources":["../../src/test-runner/rpc.ts"],"mappings":";;;;;;;;;;;;;AA4EA;;;iBAvCgB,uBAAA,CAAwB,UAAA;;AAoFxC;;KAvDY,YAAA;EAAiB,EAAA;EAAU,MAAA,EAAQ,SAAA;AAAA;EAAgB,EAAA;EAAW,KAAA;AAAA;;;;;;;;;iBAU1D,mBAAA,CAAoB,QAAA,YAAoB,YAAA;;;;;;;;;;;;;;;;;iBA6ClC,kBAAA,CACpB,UAAA,EAAY,aAAA,EACZ,UAAA,UACA,SAAA,YACC,OAAA,CAAQ,YAAA"}
|
package/dist/test-runner/rpc.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
//#region src/test-runner/rpc.ts
|
|
2
|
-
/** Maximum milliseconds to wait for a single evaluate round-trip. */
|
|
3
|
-
const DEFAULT_TIMEOUT_MS = 6e4;
|
|
4
|
-
/**
|
|
5
|
-
* Extra headroom (ms) added on top of the rpc-level `timeoutMs` when telling
|
|
6
|
-
* the underlying `CdpConnection` its per-command watchdog budget (devtools#747).
|
|
7
|
-
* The rpc-level race above is the authoritative file timeout — this margin
|
|
8
|
-
* only ensures the connection's own (shorter-by-default) watchdog never fires
|
|
9
|
-
* first and masks the rpc-level timeout error with a connection-level one.
|
|
10
|
-
*/
|
|
11
|
-
const CONNECTION_TIMEOUT_MARGIN_MS = 5e3;
|
|
12
|
-
/**
|
|
13
|
-
* Wraps bundle code in a self-executing IIFE that:
|
|
14
|
-
* 1. Evaluates the bundle (registering describe/it/test).
|
|
15
|
-
* 2. Calls `__testBundle.runTestModule(...)` — the entry the runtime exports.
|
|
16
|
-
* 3. Returns a JSON-serialised `RunReport` string.
|
|
17
|
-
*
|
|
18
|
-
* The double-serialisation (RunReport → JSON string → returnByValue string)
|
|
19
|
-
* is intentional: CDP `returnByValue` reliably transports strings; deeply
|
|
20
|
-
* nested objects can lose fidelity across the Chii relay.
|
|
21
|
-
*
|
|
22
|
-
* SECRET-HANDLING: `bundleCode` MUST NOT be logged by callers.
|
|
23
|
-
*/
|
|
24
|
-
function buildRunTestsExpression(bundleCode) {
|
|
25
|
-
return `(async () => { try { ${bundleCode} } catch(e) { return JSON.stringify({ok:false,error:'bundle-eval: ' + String(e && e.message || e)}); } if (typeof globalThis.__testBundle !== 'object' || typeof globalThis.__testBundle.runTestModule !== 'function' || typeof globalThis.__testBundle.__userFactory !== 'function') { return JSON.stringify({ok:false,error:'bundle-missing-export: __testBundle.runTestModule or __userFactory is not a function'}); } try { const report = await globalThis.__testBundle.runTestModule(globalThis.__testBundle.__userFactory); return JSON.stringify({ok:true,value:report}); } catch(e) { return JSON.stringify({ok:false,error:'test-run: ' + String(e && e.message || e)}); }})()`;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Parses the raw CDP `returnByValue` result from a `buildRunTestsExpression`
|
|
29
|
-
* evaluate call into a typed `RpcRunResult`.
|
|
30
|
-
*
|
|
31
|
-
* Throws only on parse failure — an `ok:false` envelope is a normal result.
|
|
32
|
-
*
|
|
33
|
-
* SECRET-HANDLING: `rawValue` is not included in error messages.
|
|
34
|
-
*/
|
|
35
|
-
function parseRunTestsResult(rawValue) {
|
|
36
|
-
if (typeof rawValue !== "string") throw new Error(`rpc.parseRunTestsResult: unexpected return type "${typeof rawValue}" — expected JSON string`);
|
|
37
|
-
let parsed;
|
|
38
|
-
try {
|
|
39
|
-
parsed = JSON.parse(rawValue);
|
|
40
|
-
} catch {
|
|
41
|
-
throw new Error("rpc.parseRunTestsResult: bridge returned non-JSON string");
|
|
42
|
-
}
|
|
43
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("rpc.parseRunTestsResult: parsed result is not an object");
|
|
44
|
-
const obj = parsed;
|
|
45
|
-
if (obj.ok === true) return {
|
|
46
|
-
ok: true,
|
|
47
|
-
report: obj.value
|
|
48
|
-
};
|
|
49
|
-
if (obj.ok === false) return {
|
|
50
|
-
ok: false,
|
|
51
|
-
error: typeof obj.error === "string" ? obj.error : String(obj.error)
|
|
52
|
-
};
|
|
53
|
-
throw new Error("rpc.parseRunTestsResult: result missing \"ok\" field");
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Injects `bundleCode` into the attached page and awaits test execution.
|
|
57
|
-
*
|
|
58
|
-
* Uses `Runtime.evaluate` with `awaitPromise: true` to wait for the
|
|
59
|
-
* async IIFE to settle. `timeoutMs` (default 60s) is the authoritative file
|
|
60
|
-
* timeout — this function races it against the evaluate call AND (devtools#747)
|
|
61
|
-
* threads it down to the underlying `CdpConnection`'s own per-command watchdog
|
|
62
|
-
* (plus a small margin) so that watchdog can never undercut this race. Split
|
|
63
|
-
* into smaller files if you hit the timeout.
|
|
64
|
-
*
|
|
65
|
-
* @param connection - Active CDP connection (relay or local).
|
|
66
|
-
* @param bundleCode - IIFE bundle string from `bundleTestFile`.
|
|
67
|
-
* @param timeoutMs - Override the default 60 s timeout.
|
|
68
|
-
*
|
|
69
|
-
* SECRET-HANDLING: `bundleCode` and the raw CDP result value are never logged.
|
|
70
|
-
*/
|
|
71
|
-
async function injectAndRunBundle(connection, bundleCode, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
72
|
-
const expression = buildRunTestsExpression(bundleCode);
|
|
73
|
-
const TIMEOUT_SENTINEL = Symbol("timeout");
|
|
74
|
-
const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve(TIMEOUT_SENTINEL), timeoutMs));
|
|
75
|
-
const evalPromise = connection.send("Runtime.evaluate", {
|
|
76
|
-
expression,
|
|
77
|
-
returnByValue: true,
|
|
78
|
-
awaitPromise: true
|
|
79
|
-
}, { timeoutMs: timeoutMs + CONNECTION_TIMEOUT_MARGIN_MS });
|
|
80
|
-
const raceResult = await Promise.race([evalPromise.then((v) => ({
|
|
81
|
-
tag: "eval",
|
|
82
|
-
v
|
|
83
|
-
})), timeoutPromise.then(() => ({ tag: "timeout" }))]);
|
|
84
|
-
if (raceResult.tag === "timeout") return {
|
|
85
|
-
ok: false,
|
|
86
|
-
error: `rpc: evaluate timed out after ${timeoutMs}ms`
|
|
87
|
-
};
|
|
88
|
-
const cdpResult = raceResult.v;
|
|
89
|
-
if (cdpResult.exceptionDetails) {
|
|
90
|
-
const msg = cdpResult.exceptionDetails.exception?.description ?? cdpResult.exceptionDetails.text ?? "Runtime.evaluate threw an exception";
|
|
91
|
-
throw new Error(`rpc.injectAndRunBundle: ${msg}`);
|
|
92
|
-
}
|
|
93
|
-
return parseRunTestsResult(cdpResult.result.value);
|
|
94
|
-
}
|
|
95
|
-
//#endregion
|
|
96
|
-
export { buildRunTestsExpression, injectAndRunBundle, parseRunTestsResult };
|
|
97
|
-
|
|
98
|
-
//# sourceMappingURL=rpc.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rpc.js","names":[],"sources":["../../src/test-runner/rpc.ts"],"sourcesContent":["/**\n * Node-side RPC helpers for injecting and collecting test execution over CDP.\n *\n * Uses the same IIFE + JSON.stringify envelope pattern as `buildCallSdkExpression`\n * in `src/mcp/tools.ts` to reliably shuttle structured results through\n * `Runtime.evaluate`'s `returnByValue: true` boundary.\n *\n * SECRET-HANDLING: bundle code, relay URLs, and result values are NOT logged.\n */\n\nimport type { CdpConnection } from '../mcp/cdp-connection.js';\nimport type { RunReport } from './runtime.js';\n\n/** Maximum milliseconds to wait for a single evaluate round-trip. */\nconst DEFAULT_TIMEOUT_MS = 60_000;\n\n/**\n * Extra headroom (ms) added on top of the rpc-level `timeoutMs` when telling\n * the underlying `CdpConnection` its per-command watchdog budget (devtools#747).\n * The rpc-level race above is the authoritative file timeout — this margin\n * only ensures the connection's own (shorter-by-default) watchdog never fires\n * first and masks the rpc-level timeout error with a connection-level one.\n */\nconst CONNECTION_TIMEOUT_MARGIN_MS = 5_000;\n\n/**\n * Wraps bundle code in a self-executing IIFE that:\n * 1. Evaluates the bundle (registering describe/it/test).\n * 2. Calls `__testBundle.runTestModule(...)` — the entry the runtime exports.\n * 3. Returns a JSON-serialised `RunReport` string.\n *\n * The double-serialisation (RunReport → JSON string → returnByValue string)\n * is intentional: CDP `returnByValue` reliably transports strings; deeply\n * nested objects can lose fidelity across the Chii relay.\n *\n * SECRET-HANDLING: `bundleCode` MUST NOT be logged by callers.\n */\nexport function buildRunTestsExpression(bundleCode: string): string {\n // We trust bundleCode is already a self-contained IIFE that installs\n // `window.__testBundle` (or `globalThis.__testBundle`).\n // We then call `__testBundle.runTestModule()` and return a JSON string.\n return (\n `(async () => {` +\n // Step 1: evaluate the bundle to register tests\n ` try { ${bundleCode} } catch(e) {` +\n ` return JSON.stringify({ok:false,error:'bundle-eval: ' + String(e && e.message || e)});` +\n ` }` +\n // Step 2: check that the expected exports are present\n ` if (typeof globalThis.__testBundle !== 'object' || typeof globalThis.__testBundle.runTestModule !== 'function' || typeof globalThis.__testBundle.__userFactory !== 'function') {` +\n ` return JSON.stringify({ok:false,error:'bundle-missing-export: __testBundle.runTestModule or __userFactory is not a function'});` +\n ` }` +\n // Step 3: run tests — pass the factory so runTestModule installs globals\n // first, then invokes the factory to register describe/it/test blocks.\n ` try {` +\n ` const report = await globalThis.__testBundle.runTestModule(globalThis.__testBundle.__userFactory);` +\n ` return JSON.stringify({ok:true,value:report});` +\n ` } catch(e) {` +\n ` return JSON.stringify({ok:false,error:'test-run: ' + String(e && e.message || e)});` +\n ` }` +\n `})()`\n );\n}\n\n/**\n * Result of `injectAndRunBundle`.\n */\nexport type RpcRunResult = { ok: true; report: RunReport } | { ok: false; error: string };\n\n/**\n * Parses the raw CDP `returnByValue` result from a `buildRunTestsExpression`\n * evaluate call into a typed `RpcRunResult`.\n *\n * Throws only on parse failure — an `ok:false` envelope is a normal result.\n *\n * SECRET-HANDLING: `rawValue` is not included in error messages.\n */\nexport function parseRunTestsResult(rawValue: unknown): RpcRunResult {\n if (typeof rawValue !== 'string') {\n throw new Error(\n `rpc.parseRunTestsResult: unexpected return type \"${typeof rawValue}\" — expected JSON string`,\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawValue);\n } catch {\n // Do NOT include rawValue — could contain secrets.\n throw new Error('rpc.parseRunTestsResult: bridge returned non-JSON string');\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new Error('rpc.parseRunTestsResult: parsed result is not an object');\n }\n const obj = parsed as Record<string, unknown>;\n if (obj.ok === true) {\n return { ok: true, report: obj.value as RunReport };\n }\n if (obj.ok === false) {\n return {\n ok: false,\n error: typeof obj.error === 'string' ? obj.error : String(obj.error),\n };\n }\n throw new Error('rpc.parseRunTestsResult: result missing \"ok\" field');\n}\n\n/**\n * Injects `bundleCode` into the attached page and awaits test execution.\n *\n * Uses `Runtime.evaluate` with `awaitPromise: true` to wait for the\n * async IIFE to settle. `timeoutMs` (default 60s) is the authoritative file\n * timeout — this function races it against the evaluate call AND (devtools#747)\n * threads it down to the underlying `CdpConnection`'s own per-command watchdog\n * (plus a small margin) so that watchdog can never undercut this race. Split\n * into smaller files if you hit the timeout.\n *\n * @param connection - Active CDP connection (relay or local).\n * @param bundleCode - IIFE bundle string from `bundleTestFile`.\n * @param timeoutMs - Override the default 60 s timeout.\n *\n * SECRET-HANDLING: `bundleCode` and the raw CDP result value are never logged.\n */\nexport async function injectAndRunBundle(\n connection: CdpConnection,\n bundleCode: string,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n): Promise<RpcRunResult> {\n const expression = buildRunTestsExpression(bundleCode);\n\n // Use a tagged race so we can distinguish \"timeout won\" from \"evaluate won\"\n // without relying on exception identity. The timeout arm RETURNS a typed\n // RpcRunResult (ok:false + EVALUATE_TIMEOUT_MARKER) so the caller (relay-\n // worker's attempt()) can detect it via `.error.includes(EVALUATE_TIMEOUT_MARKER)`\n // and trigger the one-retry path.\n //\n // Genuine CDP `exceptionDetails` (engine exceptions thrown inside the page)\n // still THROW — those are deterministic failures that a retry cannot fix and\n // relay-worker's catch block finalises them correctly.\n //\n // DO NOT change the literal string below — relay-worker.ts imports\n // EVALUATE_TIMEOUT_MARKER = 'rpc: evaluate timed out after' and uses\n // `.includes()` to recognise this exact prefix.\n const TIMEOUT_SENTINEL = Symbol('timeout');\n const timeoutPromise = new Promise<typeof TIMEOUT_SENTINEL>((resolve) =>\n setTimeout(() => resolve(TIMEOUT_SENTINEL), timeoutMs),\n );\n\n // devtools#747: thread our own timeout budget down to the connection's\n // per-command watchdog (ChiiCdpConnection's default is 30s and used to fire\n // before this rpc-level race no matter how large `timeoutMs` was). A small\n // margin keeps the connection watchdog from firing a beat before our own\n // race settles — the rpc-level race above remains the authoritative file\n // timeout; this override only ensures the connection layer never undercuts\n // it. `LocalCdpConnection` ignores the option (no watchdog of its own).\n const evalPromise = connection.send(\n 'Runtime.evaluate',\n {\n expression,\n returnByValue: true,\n awaitPromise: true,\n },\n { timeoutMs: timeoutMs + CONNECTION_TIMEOUT_MARGIN_MS },\n );\n\n const raceResult = await Promise.race([\n evalPromise.then((v) => ({ tag: 'eval' as const, v })),\n timeoutPromise.then(() => ({ tag: 'timeout' as const })),\n ]);\n\n if (raceResult.tag === 'timeout') {\n return { ok: false, error: `rpc: evaluate timed out after ${timeoutMs}ms` };\n }\n\n const cdpResult = raceResult.v;\n\n if (cdpResult.exceptionDetails) {\n // Surface only the engine error string — not the expression or value.\n const msg =\n cdpResult.exceptionDetails.exception?.description ??\n cdpResult.exceptionDetails.text ??\n 'Runtime.evaluate threw an exception';\n throw new Error(`rpc.injectAndRunBundle: ${msg}`);\n }\n\n return parseRunTestsResult(cdpResult.result.value);\n}\n"],"mappings":";;AAcA,MAAM,qBAAqB;;;;;;;;AAS3B,MAAM,+BAA+B;;;;;;;;;;;;;AAcrC,SAAgB,wBAAwB,YAA4B;AAIlE,QACE,yBAEW,WAAW;;;;;;;;;;AAgC1B,SAAgB,oBAAoB,UAAiC;AACnE,KAAI,OAAO,aAAa,SACtB,OAAM,IAAI,MACR,oDAAoD,OAAO,SAAS,0BACrE;CAEH,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,MAAM,SAAS;SACvB;AAEN,QAAM,IAAI,MAAM,2DAA2D;;AAE7E,KAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,OAAO,CACxE,OAAM,IAAI,MAAM,0DAA0D;CAE5E,MAAM,MAAM;AACZ,KAAI,IAAI,OAAO,KACb,QAAO;EAAE,IAAI;EAAM,QAAQ,IAAI;EAAoB;AAErD,KAAI,IAAI,OAAO,MACb,QAAO;EACL,IAAI;EACJ,OAAO,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ,OAAO,IAAI,MAAM;EACrE;AAEH,OAAM,IAAI,MAAM,uDAAqD;;;;;;;;;;;;;;;;;;AAmBvE,eAAsB,mBACpB,YACA,YACA,YAAY,oBACW;CACvB,MAAM,aAAa,wBAAwB,WAAW;CAetD,MAAM,mBAAmB,OAAO,UAAU;CAC1C,MAAM,iBAAiB,IAAI,SAAkC,YAC3D,iBAAiB,QAAQ,iBAAiB,EAAE,UAAU,CACvD;CASD,MAAM,cAAc,WAAW,KAC7B,oBACA;EACE;EACA,eAAe;EACf,cAAc;EACf,EACD,EAAE,WAAW,YAAY,8BAA8B,CACxD;CAED,MAAM,aAAa,MAAM,QAAQ,KAAK,CACpC,YAAY,MAAM,OAAO;EAAE,KAAK;EAAiB;EAAG,EAAE,EACtD,eAAe,YAAY,EAAE,KAAK,WAAoB,EAAE,CACzD,CAAC;AAEF,KAAI,WAAW,QAAQ,UACrB,QAAO;EAAE,IAAI;EAAO,OAAO,iCAAiC,UAAU;EAAK;CAG7E,MAAM,YAAY,WAAW;AAE7B,KAAI,UAAU,kBAAkB;EAE9B,MAAM,MACJ,UAAU,iBAAiB,WAAW,eACtC,UAAU,iBAAiB,QAC3B;AACF,QAAM,IAAI,MAAM,2BAA2B,MAAM;;AAGnD,QAAO,oBAAoB,UAAU,OAAO,MAAM"}
|