@ait-co/devtools 0.1.128 → 0.1.131
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/dist/{attach-orchestrator-CPMQWkGV.js → attach-orchestrator-BiaYiNOr.js} +34 -107
- package/dist/attach-orchestrator-BiaYiNOr.js.map +1 -0
- package/dist/{attach-orchestrator-RhOdO3DS.js → attach-orchestrator-Cjdhir2U.js} +31 -13
- package/dist/{attach-orchestrator-RhOdO3DS.js.map → attach-orchestrator-Cjdhir2U.js.map} +1 -1
- package/dist/{attach-orchestrator-CpE0dMew.js → attach-orchestrator-ca34r46y.js} +33 -106
- package/dist/attach-orchestrator-ca34r46y.js.map +1 -0
- package/dist/{bundle-Cb3GFuw6.d.ts → bundle-BKqyhEK9.d.ts} +1 -1
- package/dist/{bundle-Cb3GFuw6.d.ts.map → bundle-BKqyhEK9.d.ts.map} +1 -1
- package/dist/{capture-B1zfGyTo.d.ts → capture-ltuV0gZa.d.ts} +1 -1
- package/dist/{capture-B1zfGyTo.d.ts.map → capture-ltuV0gZa.d.ts.map} +1 -1
- package/dist/{cdp-connection-Dxw_EY8s.d.ts → cdp-connection-D7AYQck2.d.ts} +1 -1
- package/dist/{cdp-connection-Dxw_EY8s.d.ts.map → cdp-connection-D7AYQck2.d.ts.map} +1 -1
- package/dist/{cell-D0dJSGHi.js → cell-Bi8MAPA8.js} +11 -7
- package/dist/cell-Bi8MAPA8.js.map +1 -0
- package/dist/{cell-Ba9lCI3B.js → cell-Cw4fVOl2.js} +11 -7
- package/dist/cell-Cw4fVOl2.js.map +1 -0
- package/dist/{cell-CX92lDI0.js → cell-rr6gL9AT.js} +11 -7
- package/dist/cell-rr6gL9AT.js.map +1 -0
- package/dist/debug-server-BSbfnFR5.js +436 -0
- package/dist/debug-server-BSbfnFR5.js.map +1 -0
- package/dist/debug-server-CD4zYBq4.js +379 -0
- package/dist/{debug-server-BzzNkETg.js.map → debug-server-CD4zYBq4.js.map} +1 -1
- package/dist/{debug-server-Dctc9GAh.js → debug-server-qRSRG7YZ.js} +68 -65
- package/dist/debug-server-qRSRG7YZ.js.map +1 -0
- package/dist/in-app/auto.js +15 -0
- package/dist/in-app/auto.js.map +1 -1
- package/dist/in-app/index.d.ts +27 -0
- package/dist/in-app/index.d.ts.map +1 -1
- package/dist/in-app/index.js +15 -0
- package/dist/in-app/index.js.map +1 -1
- package/dist/log-CUik_tHp.js +95 -0
- package/dist/log-CUik_tHp.js.map +1 -0
- package/dist/mcp/cli.js +1272 -1111
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/panel/index.js +11 -1
- package/dist/panel/index.js.map +1 -1
- package/dist/{pool-DBLKNeRC.d.ts → pool-BaSCZALv.d.ts} +12 -4
- package/dist/{pool-DBLKNeRC.d.ts.map → pool-BaSCZALv.d.ts.map} +1 -1
- package/dist/{qr-http-server-Bo8LMOJQ.js → qr-http-server-BdNGPRoL.js} +76 -6
- package/dist/qr-http-server-BdNGPRoL.js.map +1 -0
- package/dist/{qr-http-server-CrGohOwC.js → qr-http-server-Bq9zR7-z.js} +76 -6
- package/dist/qr-http-server-Bq9zR7-z.js.map +1 -0
- package/dist/{qr-http-server-Bt_RAxFn.cjs → qr-http-server-BxoRp2w7.cjs} +76 -6
- package/dist/qr-http-server-BxoRp2w7.cjs.map +1 -0
- package/dist/{qr-http-server-DM2R25jP.cjs → qr-http-server-CV6TA9ZN.cjs} +76 -6
- package/dist/qr-http-server-CV6TA9ZN.cjs.map +1 -0
- package/dist/{qr-http-server-s1iW_5VQ.js → qr-http-server-DCbjORFa.js} +76 -6
- package/dist/qr-http-server-DCbjORFa.js.map +1 -0
- package/dist/{qr-http-server-BH0UFeUn.js → qr-http-server-cKbciY5k.js} +76 -6
- package/dist/qr-http-server-cKbciY5k.js.map +1 -0
- package/dist/{relay-factory-Dug7W6Ao.js → relay-factory-Cloog1jb.js} +34 -14
- package/dist/relay-factory-Cloog1jb.js.map +1 -0
- package/dist/{relay-secret-store-DKxs7zwq.js → relay-secret-store-DzxDsK6S.js} +1 -1
- package/dist/{relay-secret-store-DKxs7zwq.js.map → relay-secret-store-DzxDsK6S.js.map} +1 -1
- package/dist/{relay-url-store-xmUuTjXA.js → relay-url-store-D3rY-GJJ.js} +2 -2
- package/dist/{relay-url-store-xmUuTjXA.js.map → relay-url-store-D3rY-GJJ.js.map} +1 -1
- package/dist/{debug-server-BzzNkETg.js → relay-worker-CCAHLC4a.js} +220 -350
- package/dist/relay-worker-CCAHLC4a.js.map +1 -0
- package/dist/{relay-worker-TReZtzGl.d.ts → relay-worker-o0M2ndyJ.d.ts} +7 -7
- package/dist/relay-worker-o0M2ndyJ.d.ts.map +1 -0
- package/dist/{runtime-ozyPnRbU.d.ts → runtime-9xhN9pr8.d.ts} +1 -1
- package/dist/{runtime-ozyPnRbU.d.ts.map → runtime-9xhN9pr8.d.ts.map} +1 -1
- package/dist/test-runner/bin.js +744 -25
- package/dist/test-runner/bin.js.map +1 -1
- package/dist/test-runner/bundle.d.ts +1 -1
- package/dist/test-runner/capture.d.ts +1 -1
- package/dist/test-runner/config.d.ts +15 -9
- package/dist/test-runner/config.d.ts.map +1 -1
- package/dist/test-runner/config.js +1 -1
- package/dist/test-runner/pool.d.ts +1 -1
- package/dist/test-runner/pool.js +1 -1
- package/dist/test-runner/pool.js.map +1 -1
- package/dist/test-runner/relay-factory.d.ts +23 -9
- package/dist/test-runner/relay-factory.d.ts.map +1 -1
- package/dist/test-runner/relay-factory.js +32 -12
- package/dist/test-runner/relay-factory.js.map +1 -1
- package/dist/test-runner/relay-worker.d.ts +1 -1
- package/dist/test-runner/relay-worker.js +1 -187
- package/dist/test-runner/report.d.ts +3 -3
- package/dist/test-runner/rpc.d.ts +2 -2
- package/dist/test-runner/runtime.d.ts +1 -1
- package/dist/test-runner/task-graph.d.ts +1 -1
- package/dist/{tunnel-DyEV6WwL.cjs → tunnel-B2UCKAc7.cjs} +4 -3
- package/dist/{tunnel-DyEV6WwL.cjs.map → tunnel-B2UCKAc7.cjs.map} +1 -1
- package/dist/{tunnel-BFwOSwT4.js → tunnel-C-rQ34dM.js} +4 -3
- package/dist/{tunnel-BFwOSwT4.js.map → tunnel-C-rQ34dM.js.map} +1 -1
- package/dist/unplugin/index.cjs +1 -1
- package/dist/unplugin/index.js +1 -1
- package/dist/unplugin/tunnel.cjs +3 -2
- package/dist/unplugin/tunnel.cjs.map +1 -1
- package/dist/unplugin/tunnel.d.cts.map +1 -1
- package/dist/unplugin/tunnel.d.ts.map +1 -1
- package/dist/unplugin/tunnel.js +3 -2
- package/dist/unplugin/tunnel.js.map +1 -1
- package/package.json +1 -1
- package/dist/attach-orchestrator-CPMQWkGV.js.map +0 -1
- package/dist/attach-orchestrator-CpE0dMew.js.map +0 -1
- package/dist/cell-Ba9lCI3B.js.map +0 -1
- package/dist/cell-CX92lDI0.js.map +0 -1
- package/dist/cell-D0dJSGHi.js.map +0 -1
- package/dist/debug-server-1dhDA3aw.js +0 -980
- package/dist/debug-server-1dhDA3aw.js.map +0 -1
- package/dist/debug-server-Dctc9GAh.js.map +0 -1
- package/dist/qr-http-server-BH0UFeUn.js.map +0 -1
- package/dist/qr-http-server-Bo8LMOJQ.js.map +0 -1
- package/dist/qr-http-server-Bt_RAxFn.cjs.map +0 -1
- package/dist/qr-http-server-CrGohOwC.js.map +0 -1
- package/dist/qr-http-server-DM2R25jP.cjs.map +0 -1
- package/dist/qr-http-server-s1iW_5VQ.js.map +0 -1
- package/dist/relay-factory-Dug7W6Ao.js.map +0 -1
- package/dist/relay-worker-TReZtzGl.d.ts.map +0 -1
- package/dist/test-runner/relay-worker.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as BundleResult, r as bundleTestFile, t as BundleOptions } from "../bundle-
|
|
1
|
+
import { n as BundleResult, r as bundleTestFile, t as BundleOptions } from "../bundle-BKqyhEK9.js";
|
|
2
2
|
export { BundleOptions, BundleResult, bundleTestFile };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as parseCaptureLines, t as AitCaptureLine } from "../capture-
|
|
1
|
+
import { n as parseCaptureLines, t as AitCaptureLine } from "../capture-ltuV0gZa.js";
|
|
2
2
|
export { AitCaptureLine, parseCaptureLines };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as createRelayPool, n as RelayConnectionFactory, t as RELAY_POOL_NAME } from "../pool-
|
|
1
|
+
import { i as createRelayPool, n as RelayConnectionFactory, t as RELAY_POOL_NAME } from "../pool-BaSCZALv.js";
|
|
2
2
|
|
|
3
3
|
//#region src/test-runner/relay-factory.d.ts
|
|
4
4
|
/** Options for {@link createRelayConnectionFactory}. */
|
|
@@ -16,9 +16,12 @@ interface RelayConnectionFactoryOptions {
|
|
|
16
16
|
projectRoot?: string;
|
|
17
17
|
/**
|
|
18
18
|
* Attach wait timeout in ms — how long `open()` waits for the phone to scan
|
|
19
|
-
* the QR and attach.
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* the QR and attach. Omitted (the default) means **wait indefinitely** —
|
|
20
|
+
* the runner stays up until the user stops it (Ctrl-C/SIGTERM). QR-scan
|
|
21
|
+
* wait is a human-paced action; there is no sound default bound for it
|
|
22
|
+
* (devtools#735). Pass an explicit value to opt into a bounded wait (CI/
|
|
23
|
+
* headless callers — `--attach-timeout` on the CLI). (The per-file evaluate
|
|
24
|
+
* timeout is separate, passed via the pool's `run` options.)
|
|
22
25
|
*/
|
|
23
26
|
timeoutMs?: number;
|
|
24
27
|
/** Disable browser auto-open of the QR dashboard (text QR only). */
|
|
@@ -54,11 +57,14 @@ interface RelayConnectionFactoryOptions {
|
|
|
54
57
|
* Builds a {@link RelayConnectionFactory} that opens a standalone env3 relay
|
|
55
58
|
* connection.
|
|
56
59
|
*
|
|
57
|
-
* `open()` performs the full attach lifecycle and BLOCKS
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
60
|
+
* `open()` performs the full attach lifecycle and BLOCKS while a human scans
|
|
61
|
+
* the rendered QR with their phone — there is no way around the manual scan
|
|
62
|
+
* for env3. By default the wait is UNBOUNDED (`opts.timeoutMs` omitted): the
|
|
63
|
+
* runner stays up until the user stops it (Ctrl-C/SIGTERM), since QR-scan is
|
|
64
|
+
* a human-paced action with no sound default bound (devtools#735). Passing an
|
|
65
|
+
* explicit `timeoutMs` opts into the old bounded behavior (CI/headless
|
|
66
|
+
* callers). It resolves with the live `CdpConnection` once a matching page
|
|
67
|
+
* attaches; `close()` tears the relay family down.
|
|
62
68
|
*
|
|
63
69
|
* The factory holds the booted relay family in a closure so `close()` can stop
|
|
64
70
|
* it. A second `open()` on the same factory boots a fresh family (the previous
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","names":[],"sources":["../../src/test-runner/relay-factory.ts","../../src/test-runner/config.ts"],"mappings":";;;;UAiDiB,6BAAA;ECHA;;;;;EDSf,SAAA;ECNA;;;AAgBF;EDLE,WAAA
|
|
1
|
+
{"version":3,"file":"config.d.ts","names":[],"sources":["../../src/test-runner/relay-factory.ts","../../src/test-runner/config.ts"],"mappings":";;;;UAiDiB,6BAAA;ECHA;;;;;EDSf,SAAA;ECNA;;;AAgBF;EDLE,WAAA;;;;;;;;ACmBF;;EDTE,SAAA;ECWgB;EDThB,QAAA;ECSM;;;;;EDHN,IAAA;IAAS,OAAA;IAAiB,QAAA;EAAA;ECWsC;;;;;;;AAkClE;;;;;;;;;ED5BE,WAAA,GAAc,UAAA;AAAA;;;;;;;;;;;;;;;;;;iBAoBA,4BAAA,CACd,IAAA,EAAM,6BAAA,GACL,sBAAA;;;;;;UCzFc,eAAA;EDyFd;;;;ECpFD,OAAA;;AALF;;EASE,SAAA;EAT8B;;;;EAc9B,cAAA;AAAA;AAIF;AAAA,UAAiB,mBAAA;EACf,OAAA;EACA,SAAA;EACA,cAAA;AAAA;;;;AAgBF;;;;iBAAgB,qBAAA,CAAsB,UAAA,GAAa,mBAAA,GAAsB,eAAA;;;;;AAczE;;UAAiB,qBAAA;EAEC;EAAhB,IAAA,EAAM,UAAA,QAAkB,eAAA;EAAlB;EAEN,OAAA;EAAA;EAEA,WAAA;AAAA;;UAIe,qBAAA,SAA8B,mBAAA;EAAR;;;;;;;EAQrC,UAAA,EAAY,sBAAA;EAQG;AAkBjB;;;;;;EAlBE,eAAA;AAAA;;;;;;;;;;;;;;;;iBAkBc,uBAAA,CAAwB,UAAA,EAAY,qBAAA,GAAwB,qBAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RELAY_POOL_NAME, createRelayPool } from "./pool.js";
|
|
2
|
-
import { t as createRelayConnectionFactory } from "../relay-factory-
|
|
2
|
+
import { t as createRelayConnectionFactory } from "../relay-factory-Cloog1jb.js";
|
|
3
3
|
//#region src/test-runner/config.ts
|
|
4
4
|
const DEFAULT_CONFIG = {
|
|
5
5
|
include: ["**/*.ait.test.ts"],
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as createRelayPool, n as RelayConnectionFactory, r as RelayPoolOptions, t as RELAY_POOL_NAME } from "../pool-
|
|
1
|
+
import { i as createRelayPool, n as RelayConnectionFactory, r as RelayPoolOptions, t as RELAY_POOL_NAME } from "../pool-BaSCZALv.js";
|
|
2
2
|
export { RELAY_POOL_NAME, RelayConnectionFactory, RelayPoolOptions, createRelayPool };
|
package/dist/test-runner/pool.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { runTestFilesOverRelay } from "
|
|
1
|
+
import { r as runTestFilesOverRelay } from "../relay-worker-CCAHLC4a.js";
|
|
2
2
|
import { synthesizeFileTask, toTaskEventPacks, toTaskResultPacks } from "./task-graph.js";
|
|
3
3
|
//#region src/test-runner/pool.ts
|
|
4
4
|
/** The pool name Vitest matches against `getFilePoolName(project)`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pool.js","names":[],"sources":["../../src/test-runner/pool.ts"],"sourcesContent":["/**\n * Vitest 4.x custom pool: runs mini-app tests on a real device WebView over a\n * CDP relay instead of in a Node child process.\n *\n * Vitest's pool abstraction lets a `PoolRunnerInitializer` create a `PoolWorker`\n * that the core drives via a `WorkerRequest`/`WorkerResponse` message protocol.\n * Built-in pools (`forks`/`threads`) fork a child process; the `typecheck` pool\n * runs in-process and handles messages synchronously. We follow the in-process\n * `TypecheckPoolWorker` shape: a single long-lived \"worker\" that, on a `run`\n * request, bundles + injects + collects each file over the relay (#644's\n * `runTestFilesOverRelay`), synthesises a Vitest task graph (`task-graph.ts`),\n * and reports it through `vitest.state` so reporters/watch/UI/snapshot all work.\n *\n * Single-attach constraint: the relay supports one active page, so we keep one\n * worker for the whole run (`isolate: false`) rather than starting/stopping per\n * file — per-file start/stop would re-attach the relay on every file.\n *\n * SECRET-HANDLING: the relay connection (wss/TOTP) is supplied by the caller's\n * factory and never logged here; bundle code and result values are not logged.\n */\n\nimport type { PoolOptions, PoolWorker, TestProject, WorkerRequest } from 'vitest/node';\nimport type { CdpConnection } from '../mcp/cdp-connection.js';\nimport type { RelayRunOptions } from './relay-worker.js';\nimport { runTestFilesOverRelay } from './relay-worker.js';\nimport { synthesizeFileTask, toTaskEventPacks, toTaskResultPacks } from './task-graph.js';\n\n/** The pool name Vitest matches against `getFilePoolName(project)`. */\nexport const RELAY_POOL_NAME = 'ait-relay';\n\n/**\n * Factory the caller provides to open (and later close) the CDP relay\n * connection. Called once per worker lifecycle (`start`). The returned\n * `close` runs on `stop`.\n *\n * Kept as a factory (not a live connection) so the pool can be constructed in\n * config before any device is attached, and so `start`/`stop` own the lifecycle.\n */\nexport interface RelayConnectionFactory {\n /** Opens the relay connection. Resolves once the page is attached & ready. */\n open(): Promise<CdpConnection>;\n /** Closes the relay connection opened by {@link open}. */\n close(connection: CdpConnection): Promise<void>;\n}\n\n/** Options for {@link createRelayPool}. */\nexport interface RelayPoolOptions {\n /** Opens/closes the relay connection. */\n connection: RelayConnectionFactory;\n /** Forwarded to `runTestFilesOverRelay` (bundle options, per-file timeout). */\n run?: RelayRunOptions;\n}\n\n/* -------------------------------------------------------------------------- */\n/* WorkerResponse helpers */\n/* -------------------------------------------------------------------------- */\n\n/** Minimal `WorkerResponse` shape we emit. Matches vitest/node's union. */\ntype EmittedResponse =\n | { __vitest_worker_response__: true; type: 'started'; error?: unknown }\n | { __vitest_worker_response__: true; type: 'stopped'; error?: unknown }\n | { __vitest_worker_response__: true; type: 'testfileFinished'; error?: unknown };\n\n/* -------------------------------------------------------------------------- */\n/* The in-process relay PoolWorker */\n/* -------------------------------------------------------------------------- */\n\n/**\n * In-process `PoolWorker` backed by a CDP relay. Mirrors `TypecheckPoolWorker`:\n * `send` handles the message in-process and emits a `WorkerResponse` via the\n * `'message'` listener registered by the core's pool driver.\n */\nclass RelayPoolWorker implements PoolWorker {\n readonly name = RELAY_POOL_NAME;\n readonly cacheFs = false;\n\n private readonly project: TestProject;\n private readonly factory: RelayConnectionFactory;\n private readonly runOpts: RelayRunOptions | undefined;\n private readonly listeners = new Map<string, Set<(arg: unknown) => void>>();\n private connection: CdpConnection | undefined;\n\n constructor(project: TestProject, opts: RelayPoolOptions) {\n this.project = project;\n this.factory = opts.connection;\n this.runOpts = opts.run;\n }\n\n on(event: string, callback: (arg: unknown) => void): void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(callback);\n }\n\n off(event: string, callback: (arg: unknown) => void): void {\n this.listeners.get(event)?.delete(callback);\n }\n\n private emit(event: string, arg: unknown): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const cb of set) cb(arg);\n }\n\n deserialize(data: unknown): unknown {\n // Relay results arrive as plain JSON objects already — identity is correct.\n return data;\n }\n\n async start(): Promise<void> {\n // Lifecycle ownership lives here, but the connection is opened lazily on the\n // first `run`/`collect` so a never-run worker never touches the device.\n }\n\n async stop(): Promise<void> {\n if (this.connection) {\n const conn = this.connection;\n this.connection = undefined;\n await this.factory.close(conn);\n }\n }\n\n send(message: WorkerRequest): void {\n // Fire-and-forget like TypecheckPoolWorker: handle async, emit on settle.\n void this.handle(message).then((response) => {\n if (response) this.emit('message', response);\n });\n }\n\n private async handle(message: WorkerRequest): Promise<EmittedResponse | undefined> {\n switch (message.type) {\n case 'start':\n return { __vitest_worker_response__: true, type: 'started' };\n case 'run':\n case 'collect': {\n const files = message.context.files.map((s) => s.filepath);\n const error = await this.runFiles(files).catch((e) => e);\n return { __vitest_worker_response__: true, type: 'testfileFinished', error };\n }\n case 'stop':\n return { __vitest_worker_response__: true, type: 'stopped' };\n case 'cancel':\n return undefined;\n }\n }\n\n /**\n * Bundles + injects + collects the given files over the relay, synthesises a\n * Vitest task graph per file, and reports it through `vitest.state` so the\n * reporter/watch/UI ecosystem sees real results.\n *\n * Returns nothing on success; a thrown error is surfaced as the\n * `testfileFinished` error (a whole-pool failure, e.g. relay unreachable).\n */\n private async runFiles(files: string[]): Promise<void> {\n const conn = await this.ensureConnection();\n const report = await runTestFilesOverRelay(conn, files, this.runOpts);\n\n const root = this.project.config.root;\n const projectName = this.project.name || undefined;\n const state = this.project.vitest.state;\n\n for (const fileResult of report.files) {\n const perFile =\n 'error' in fileResult.result\n ? // Whole-file bundle/inject error → synthesise a failed file with no tests.\n {\n startedAt: report.startedAt,\n duration: 0,\n passed: 0,\n failed: 1,\n skipped: 0,\n tests: [],\n }\n : fileResult.result;\n\n const fileTask = synthesizeFileTask(\n fileResult.file,\n root,\n projectName,\n RELAY_POOL_NAME,\n perFile,\n );\n\n state.collectFiles(this.project, [fileTask]);\n state.updateTasks(toTaskResultPacks(fileTask));\n // Event packs drive reporter progress; updateTasks already recorded\n // results, so events are advisory but keep watch/UI output coherent.\n void toTaskEventPacks(fileTask);\n }\n }\n\n private async ensureConnection(): Promise<CdpConnection> {\n if (!this.connection) {\n this.connection = await this.factory.open();\n }\n return this.connection;\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* PoolRunnerInitializer factory */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Creates the Vitest `PoolRunnerInitializer` for the relay pool.\n *\n * Wire it into a Vitest config as `pool: createRelayPool({ connection })`.\n * Vitest moves an object `pool` into `config.poolRunner` and dispatches files\n * whose pool name equals `RELAY_POOL_NAME` to our `createPoolWorker`.\n *\n * @example\n * // vitest.config.ts\n * import { createRelayPool } from '@ait-co/devtools/test-runner';\n * export default defineConfig({\n * test: { pool: createRelayPool({ connection: myFactory }) },\n * });\n */\nexport function createRelayPool(opts: RelayPoolOptions): {\n readonly name: string;\n createPoolWorker: (options: PoolOptions) => PoolWorker;\n} {\n return {\n name: RELAY_POOL_NAME,\n createPoolWorker(options: PoolOptions): PoolWorker {\n return new RelayPoolWorker(options.project, opts);\n },\n };\n}\n"],"mappings":";;;;AA4BA,MAAa,kBAAkB;;;;;;AA4C/B,IAAM,kBAAN,MAA4C;CAC1C,OAAgB;CAChB,UAAmB;CAEnB;CACA;CACA;CACA,4BAA6B,IAAI,KAA0C;CAC3E;CAEA,YAAY,SAAsB,MAAwB;AACxD,OAAK,UAAU;AACf,OAAK,UAAU,KAAK;AACpB,OAAK,UAAU,KAAK;;CAGtB,GAAG,OAAe,UAAwC;EACxD,IAAI,MAAM,KAAK,UAAU,IAAI,MAAM;AACnC,MAAI,CAAC,KAAK;AACR,yBAAM,IAAI,KAAK;AACf,QAAK,UAAU,IAAI,OAAO,IAAI;;AAEhC,MAAI,IAAI,SAAS;;CAGnB,IAAI,OAAe,UAAwC;AACzD,OAAK,UAAU,IAAI,MAAM,EAAE,OAAO,SAAS;;CAG7C,KAAa,OAAe,KAAoB;EAC9C,MAAM,MAAM,KAAK,UAAU,IAAI,MAAM;AACrC,MAAI,CAAC,IAAK;AACV,OAAK,MAAM,MAAM,IAAK,IAAG,IAAI;;CAG/B,YAAY,MAAwB;AAElC,SAAO;;CAGT,MAAM,QAAuB;CAK7B,MAAM,OAAsB;AAC1B,MAAI,KAAK,YAAY;GACnB,MAAM,OAAO,KAAK;AAClB,QAAK,aAAa,KAAA;AAClB,SAAM,KAAK,QAAQ,MAAM,KAAK;;;CAIlC,KAAK,SAA8B;AAE5B,OAAK,OAAO,QAAQ,CAAC,MAAM,aAAa;AAC3C,OAAI,SAAU,MAAK,KAAK,WAAW,SAAS;IAC5C;;CAGJ,MAAc,OAAO,SAA8D;AACjF,UAAQ,QAAQ,MAAhB;GACE,KAAK,QACH,QAAO;IAAE,4BAA4B;IAAM,MAAM;IAAW;GAC9D,KAAK;GACL,KAAK,WAAW;IACd,MAAM,QAAQ,QAAQ,QAAQ,MAAM,KAAK,MAAM,EAAE,SAAS;AAE1D,WAAO;KAAE,4BAA4B;KAAM,MAAM;KAAoB,OADvD,MAAM,KAAK,SAAS,MAAM,CAAC,OAAO,MAAM,EAAE;KACoB;;GAE9E,KAAK,OACH,QAAO;IAAE,4BAA4B;IAAM,MAAM;IAAW;GAC9D,KAAK,SACH;;;;;;;;;;;CAYN,MAAc,SAAS,OAAgC;EAErD,MAAM,SAAS,MAAM,sBADR,MAAM,KAAK,kBAAkB,EACO,OAAO,KAAK,QAAQ;EAErE,MAAM,OAAO,KAAK,QAAQ,OAAO;EACjC,MAAM,cAAc,KAAK,QAAQ,QAAQ,KAAA;EACzC,MAAM,QAAQ,KAAK,QAAQ,OAAO;AAElC,OAAK,MAAM,cAAc,OAAO,OAAO;GACrC,MAAM,UACJ,WAAW,WAAW,SAElB;IACE,WAAW,OAAO;IAClB,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,OAAO,EAAE;IACV,GACD,WAAW;GAEjB,MAAM,WAAW,mBACf,WAAW,MACX,MACA,aACA,iBACA,QACD;AAED,SAAM,aAAa,KAAK,SAAS,CAAC,SAAS,CAAC;AAC5C,SAAM,YAAY,kBAAkB,SAAS,CAAC;AAGzC,oBAAiB,SAAS;;;CAInC,MAAc,mBAA2C;AACvD,MAAI,CAAC,KAAK,WACR,MAAK,aAAa,MAAM,KAAK,QAAQ,MAAM;AAE7C,SAAO,KAAK;;;;;;;;;;;;;;;;;AAsBhB,SAAgB,gBAAgB,MAG9B;AACA,QAAO;EACL,MAAM;EACN,iBAAiB,SAAkC;AACjD,UAAO,IAAI,gBAAgB,QAAQ,SAAS,KAAK;;EAEpD"}
|
|
1
|
+
{"version":3,"file":"pool.js","names":[],"sources":["../../src/test-runner/pool.ts"],"sourcesContent":["/**\n * Vitest 4.x custom pool: runs mini-app tests on a real device WebView over a\n * CDP relay instead of in a Node child process.\n *\n * Vitest's pool abstraction lets a `PoolRunnerInitializer` create a `PoolWorker`\n * that the core drives via a `WorkerRequest`/`WorkerResponse` message protocol.\n * Built-in pools (`forks`/`threads`) fork a child process; the `typecheck` pool\n * runs in-process and handles messages synchronously. We follow the in-process\n * `TypecheckPoolWorker` shape: a single long-lived \"worker\" that, on a `run`\n * request, bundles + injects + collects each file over the relay (#644's\n * `runTestFilesOverRelay`), synthesises a Vitest task graph (`task-graph.ts`),\n * and reports it through `vitest.state` so reporters/watch/UI/snapshot all work.\n *\n * Single-attach constraint: the relay supports one active page, so we keep one\n * worker for the whole run (`isolate: false`) rather than starting/stopping per\n * file — per-file start/stop would re-attach the relay on every file.\n *\n * SECRET-HANDLING: the relay connection (wss/TOTP) is supplied by the caller's\n * factory and never logged here; bundle code and result values are not logged.\n */\n\nimport type { PoolOptions, PoolWorker, TestProject, WorkerRequest } from 'vitest/node';\nimport type { CdpConnection } from '../mcp/cdp-connection.js';\nimport type { RelayRunOptions } from './relay-worker.js';\nimport { runTestFilesOverRelay } from './relay-worker.js';\nimport { synthesizeFileTask, toTaskEventPacks, toTaskResultPacks } from './task-graph.js';\n\n/** The pool name Vitest matches against `getFilePoolName(project)`. */\nexport const RELAY_POOL_NAME = 'ait-relay';\n\n/**\n * Factory the caller provides to open (and later close) the CDP relay\n * connection. Called once per worker lifecycle (`start`). The returned\n * `close` runs on `stop`.\n *\n * Kept as a factory (not a live connection) so the pool can be constructed in\n * config before any device is attached, and so `start`/`stop` own the lifecycle.\n */\nexport interface RelayConnectionFactory {\n /** Opens the relay connection. Resolves once the page is attached & ready. */\n open(): Promise<CdpConnection>;\n /** Closes the relay connection opened by {@link open}. */\n close(connection: CdpConnection): Promise<void>;\n /**\n * CLI-only session-phase hook (#730) — drives the QR dashboard's `phase`\n * field (`'running'` on run start, `'complete'` on run end) so the\n * dashboard can push an immediate SSE update instead of waiting for a\n * poll/watchdog. The pool (this file) has no equivalent lifecycle and\n * omits it; only `createRelayConnectionFactory` (relay-factory.ts) sets it.\n */\n onSessionPhase?(phase: 'running' | 'complete'): void;\n}\n\n/** Options for {@link createRelayPool}. */\nexport interface RelayPoolOptions {\n /** Opens/closes the relay connection. */\n connection: RelayConnectionFactory;\n /** Forwarded to `runTestFilesOverRelay` (bundle options, per-file timeout). */\n run?: RelayRunOptions;\n}\n\n/* -------------------------------------------------------------------------- */\n/* WorkerResponse helpers */\n/* -------------------------------------------------------------------------- */\n\n/** Minimal `WorkerResponse` shape we emit. Matches vitest/node's union. */\ntype EmittedResponse =\n | { __vitest_worker_response__: true; type: 'started'; error?: unknown }\n | { __vitest_worker_response__: true; type: 'stopped'; error?: unknown }\n | { __vitest_worker_response__: true; type: 'testfileFinished'; error?: unknown };\n\n/* -------------------------------------------------------------------------- */\n/* The in-process relay PoolWorker */\n/* -------------------------------------------------------------------------- */\n\n/**\n * In-process `PoolWorker` backed by a CDP relay. Mirrors `TypecheckPoolWorker`:\n * `send` handles the message in-process and emits a `WorkerResponse` via the\n * `'message'` listener registered by the core's pool driver.\n */\nclass RelayPoolWorker implements PoolWorker {\n readonly name = RELAY_POOL_NAME;\n readonly cacheFs = false;\n\n private readonly project: TestProject;\n private readonly factory: RelayConnectionFactory;\n private readonly runOpts: RelayRunOptions | undefined;\n private readonly listeners = new Map<string, Set<(arg: unknown) => void>>();\n private connection: CdpConnection | undefined;\n\n constructor(project: TestProject, opts: RelayPoolOptions) {\n this.project = project;\n this.factory = opts.connection;\n this.runOpts = opts.run;\n }\n\n on(event: string, callback: (arg: unknown) => void): void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(callback);\n }\n\n off(event: string, callback: (arg: unknown) => void): void {\n this.listeners.get(event)?.delete(callback);\n }\n\n private emit(event: string, arg: unknown): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const cb of set) cb(arg);\n }\n\n deserialize(data: unknown): unknown {\n // Relay results arrive as plain JSON objects already — identity is correct.\n return data;\n }\n\n async start(): Promise<void> {\n // Lifecycle ownership lives here, but the connection is opened lazily on the\n // first `run`/`collect` so a never-run worker never touches the device.\n }\n\n async stop(): Promise<void> {\n if (this.connection) {\n const conn = this.connection;\n this.connection = undefined;\n await this.factory.close(conn);\n }\n }\n\n send(message: WorkerRequest): void {\n // Fire-and-forget like TypecheckPoolWorker: handle async, emit on settle.\n void this.handle(message).then((response) => {\n if (response) this.emit('message', response);\n });\n }\n\n private async handle(message: WorkerRequest): Promise<EmittedResponse | undefined> {\n switch (message.type) {\n case 'start':\n return { __vitest_worker_response__: true, type: 'started' };\n case 'run':\n case 'collect': {\n const files = message.context.files.map((s) => s.filepath);\n const error = await this.runFiles(files).catch((e) => e);\n return { __vitest_worker_response__: true, type: 'testfileFinished', error };\n }\n case 'stop':\n return { __vitest_worker_response__: true, type: 'stopped' };\n case 'cancel':\n return undefined;\n }\n }\n\n /**\n * Bundles + injects + collects the given files over the relay, synthesises a\n * Vitest task graph per file, and reports it through `vitest.state` so the\n * reporter/watch/UI ecosystem sees real results.\n *\n * Returns nothing on success; a thrown error is surfaced as the\n * `testfileFinished` error (a whole-pool failure, e.g. relay unreachable).\n */\n private async runFiles(files: string[]): Promise<void> {\n const conn = await this.ensureConnection();\n const report = await runTestFilesOverRelay(conn, files, this.runOpts);\n\n const root = this.project.config.root;\n const projectName = this.project.name || undefined;\n const state = this.project.vitest.state;\n\n for (const fileResult of report.files) {\n const perFile =\n 'error' in fileResult.result\n ? // Whole-file bundle/inject error → synthesise a failed file with no tests.\n {\n startedAt: report.startedAt,\n duration: 0,\n passed: 0,\n failed: 1,\n skipped: 0,\n tests: [],\n }\n : fileResult.result;\n\n const fileTask = synthesizeFileTask(\n fileResult.file,\n root,\n projectName,\n RELAY_POOL_NAME,\n perFile,\n );\n\n state.collectFiles(this.project, [fileTask]);\n state.updateTasks(toTaskResultPacks(fileTask));\n // Event packs drive reporter progress; updateTasks already recorded\n // results, so events are advisory but keep watch/UI output coherent.\n void toTaskEventPacks(fileTask);\n }\n }\n\n private async ensureConnection(): Promise<CdpConnection> {\n if (!this.connection) {\n this.connection = await this.factory.open();\n }\n return this.connection;\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* PoolRunnerInitializer factory */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Creates the Vitest `PoolRunnerInitializer` for the relay pool.\n *\n * Wire it into a Vitest config as `pool: createRelayPool({ connection })`.\n * Vitest moves an object `pool` into `config.poolRunner` and dispatches files\n * whose pool name equals `RELAY_POOL_NAME` to our `createPoolWorker`.\n *\n * @example\n * // vitest.config.ts\n * import { createRelayPool } from '@ait-co/devtools/test-runner';\n * export default defineConfig({\n * test: { pool: createRelayPool({ connection: myFactory }) },\n * });\n */\nexport function createRelayPool(opts: RelayPoolOptions): {\n readonly name: string;\n createPoolWorker: (options: PoolOptions) => PoolWorker;\n} {\n return {\n name: RELAY_POOL_NAME,\n createPoolWorker(options: PoolOptions): PoolWorker {\n return new RelayPoolWorker(options.project, opts);\n },\n };\n}\n"],"mappings":";;;;AA4BA,MAAa,kBAAkB;;;;;;AAoD/B,IAAM,kBAAN,MAA4C;CAC1C,OAAgB;CAChB,UAAmB;CAEnB;CACA;CACA;CACA,4BAA6B,IAAI,KAA0C;CAC3E;CAEA,YAAY,SAAsB,MAAwB;AACxD,OAAK,UAAU;AACf,OAAK,UAAU,KAAK;AACpB,OAAK,UAAU,KAAK;;CAGtB,GAAG,OAAe,UAAwC;EACxD,IAAI,MAAM,KAAK,UAAU,IAAI,MAAM;AACnC,MAAI,CAAC,KAAK;AACR,yBAAM,IAAI,KAAK;AACf,QAAK,UAAU,IAAI,OAAO,IAAI;;AAEhC,MAAI,IAAI,SAAS;;CAGnB,IAAI,OAAe,UAAwC;AACzD,OAAK,UAAU,IAAI,MAAM,EAAE,OAAO,SAAS;;CAG7C,KAAa,OAAe,KAAoB;EAC9C,MAAM,MAAM,KAAK,UAAU,IAAI,MAAM;AACrC,MAAI,CAAC,IAAK;AACV,OAAK,MAAM,MAAM,IAAK,IAAG,IAAI;;CAG/B,YAAY,MAAwB;AAElC,SAAO;;CAGT,MAAM,QAAuB;CAK7B,MAAM,OAAsB;AAC1B,MAAI,KAAK,YAAY;GACnB,MAAM,OAAO,KAAK;AAClB,QAAK,aAAa,KAAA;AAClB,SAAM,KAAK,QAAQ,MAAM,KAAK;;;CAIlC,KAAK,SAA8B;AAE5B,OAAK,OAAO,QAAQ,CAAC,MAAM,aAAa;AAC3C,OAAI,SAAU,MAAK,KAAK,WAAW,SAAS;IAC5C;;CAGJ,MAAc,OAAO,SAA8D;AACjF,UAAQ,QAAQ,MAAhB;GACE,KAAK,QACH,QAAO;IAAE,4BAA4B;IAAM,MAAM;IAAW;GAC9D,KAAK;GACL,KAAK,WAAW;IACd,MAAM,QAAQ,QAAQ,QAAQ,MAAM,KAAK,MAAM,EAAE,SAAS;AAE1D,WAAO;KAAE,4BAA4B;KAAM,MAAM;KAAoB,OADvD,MAAM,KAAK,SAAS,MAAM,CAAC,OAAO,MAAM,EAAE;KACoB;;GAE9E,KAAK,OACH,QAAO;IAAE,4BAA4B;IAAM,MAAM;IAAW;GAC9D,KAAK,SACH;;;;;;;;;;;CAYN,MAAc,SAAS,OAAgC;EAErD,MAAM,SAAS,MAAM,sBADR,MAAM,KAAK,kBAAkB,EACO,OAAO,KAAK,QAAQ;EAErE,MAAM,OAAO,KAAK,QAAQ,OAAO;EACjC,MAAM,cAAc,KAAK,QAAQ,QAAQ,KAAA;EACzC,MAAM,QAAQ,KAAK,QAAQ,OAAO;AAElC,OAAK,MAAM,cAAc,OAAO,OAAO;GACrC,MAAM,UACJ,WAAW,WAAW,SAElB;IACE,WAAW,OAAO;IAClB,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,OAAO,EAAE;IACV,GACD,WAAW;GAEjB,MAAM,WAAW,mBACf,WAAW,MACX,MACA,aACA,iBACA,QACD;AAED,SAAM,aAAa,KAAK,SAAS,CAAC,SAAS,CAAC;AAC5C,SAAM,YAAY,kBAAkB,SAAS,CAAC;AAGzC,oBAAiB,SAAS;;;CAInC,MAAc,mBAA2C;AACvD,MAAI,CAAC,KAAK,WACR,MAAK,aAAa,MAAM,KAAK,QAAQ,MAAM;AAE7C,SAAO,KAAK;;;;;;;;;;;;;;;;;AAsBhB,SAAgB,gBAAgB,MAG9B;AACA,QAAO;EACL,MAAM;EACN,iBAAiB,SAAkC;AACjD,UAAO,IAAI,gBAAgB,QAAQ,SAAS,KAAK;;EAEpD"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
-
import { URL as URL$1 } from "node:url";
|
|
4
3
|
import { EventEmitter } from "node:events";
|
|
4
|
+
import { URL as URL$1 } from "node:url";
|
|
5
5
|
import * as http from "node:http";
|
|
6
6
|
import { Agent, ClientRequest, ClientRequestArgs, OutgoingHttpHeaders } from "node:http";
|
|
7
7
|
import * as net from "node:net";
|
|
@@ -11064,6 +11064,14 @@ interface RelayConnectionFactory {
|
|
|
11064
11064
|
open(): Promise<CdpConnection>;
|
|
11065
11065
|
/** Closes the relay connection opened by {@link open}. */
|
|
11066
11066
|
close(connection: CdpConnection): Promise<void>;
|
|
11067
|
+
/**
|
|
11068
|
+
* CLI-only session-phase hook (#730) — drives the QR dashboard's `phase`
|
|
11069
|
+
* field (`'running'` on run start, `'complete'` on run end) so the
|
|
11070
|
+
* dashboard can push an immediate SSE update instead of waiting for a
|
|
11071
|
+
* poll/watchdog. The pool (this file) has no equivalent lifecycle and
|
|
11072
|
+
* omits it; only `createRelayConnectionFactory` (relay-factory.ts) sets it.
|
|
11073
|
+
*/
|
|
11074
|
+
onSessionPhase?(phase: 'running' | 'complete'): void;
|
|
11067
11075
|
}
|
|
11068
11076
|
//#endregion
|
|
11069
11077
|
//#region src/test-runner/relay-factory.d.ts
|
|
@@ -11082,9 +11090,12 @@ interface RelayConnectionFactoryOptions {
|
|
|
11082
11090
|
projectRoot?: string;
|
|
11083
11091
|
/**
|
|
11084
11092
|
* Attach wait timeout in ms — how long `open()` waits for the phone to scan
|
|
11085
|
-
* the QR and attach.
|
|
11086
|
-
*
|
|
11087
|
-
*
|
|
11093
|
+
* the QR and attach. Omitted (the default) means **wait indefinitely** —
|
|
11094
|
+
* the runner stays up until the user stops it (Ctrl-C/SIGTERM). QR-scan
|
|
11095
|
+
* wait is a human-paced action; there is no sound default bound for it
|
|
11096
|
+
* (devtools#735). Pass an explicit value to opt into a bounded wait (CI/
|
|
11097
|
+
* headless callers — `--attach-timeout` on the CLI). (The per-file evaluate
|
|
11098
|
+
* timeout is separate, passed via the pool's `run` options.)
|
|
11088
11099
|
*/
|
|
11089
11100
|
timeoutMs?: number;
|
|
11090
11101
|
/** Disable browser auto-open of the QR dashboard (text QR only). */
|
|
@@ -11120,11 +11131,14 @@ interface RelayConnectionFactoryOptions {
|
|
|
11120
11131
|
* Builds a {@link RelayConnectionFactory} that opens a standalone env3 relay
|
|
11121
11132
|
* connection.
|
|
11122
11133
|
*
|
|
11123
|
-
* `open()` performs the full attach lifecycle and BLOCKS
|
|
11124
|
-
*
|
|
11125
|
-
*
|
|
11126
|
-
*
|
|
11127
|
-
*
|
|
11134
|
+
* `open()` performs the full attach lifecycle and BLOCKS while a human scans
|
|
11135
|
+
* the rendered QR with their phone — there is no way around the manual scan
|
|
11136
|
+
* for env3. By default the wait is UNBOUNDED (`opts.timeoutMs` omitted): the
|
|
11137
|
+
* runner stays up until the user stops it (Ctrl-C/SIGTERM), since QR-scan is
|
|
11138
|
+
* a human-paced action with no sound default bound (devtools#735). Passing an
|
|
11139
|
+
* explicit `timeoutMs` opts into the old bounded behavior (CI/headless
|
|
11140
|
+
* callers). It resolves with the live `CdpConnection` once a matching page
|
|
11141
|
+
* attaches; `close()` tears the relay family down.
|
|
11128
11142
|
*
|
|
11129
11143
|
* The factory holds the booted relay family in a closure so `close()` can stop
|
|
11130
11144
|
* it. A second `open()` on the same factory boots a fresh family (the previous
|