Files
web/e2e/fixtures/peer.ts
dependabot[bot] aec3ad9ed3 chore(deps-dev): bump oxfmt from 0.58.0 to 0.59.0 (#1306)
* chore(deps-dev): bump oxfmt from 0.58.0 to 0.59.0

Bumps [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) from 0.58.0 to 0.59.0.
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.59.0/npm/oxfmt)

---
updated-dependencies:
- dependency-name: oxfmt
  dependency-version: 0.59.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: fix formatting issues for oxfmt 0.59.0

* fix: add vitest.config.ts for packages/ui to avoid unplugin-dts TypeScript 7 crash

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Dan Ditomaso <dan.ditomaso@gmail.com>
Co-authored-by: Hunter Thornsberry <hunter@hunterthornsberry.com>
2026-07-22 12:43:26 -04:00

138 lines
4.2 KiB
TypeScript

import { type ChildProcess, spawn } from "node:child_process";
import path from "node:path";
/**
* Thin TypeScript wrapper around e2e/peer/peer.py — the off-browser "mesh peer"
* that talks to the non-browser node over the TCP phone API. It sends text,
* blocks until a specific text is received, or reports the node's number.
*/
const PYTHON =
process.env.E2E_PEER_PYTHON ?? path.resolve("e2e/peer/.venv/bin/python");
const SCRIPT = path.resolve("e2e/peer/peer.py");
const HOST = process.env.E2E_PEER_HOST ?? "127.0.0.1";
const PORT = process.env.E2E_PEER_PORT ?? "14404";
function spawnPeer(args: string[]): ChildProcess {
return spawn(PYTHON, [SCRIPT, "--host", HOST, "--port", PORT, ...args]);
}
/** Invoke `onLine` for each complete stdout line. */
function onStdoutLines(
child: ChildProcess,
onLine: (line: string) => void,
): void {
let buf = "";
child.stdout?.on("data", (chunk: Buffer) => {
buf += chunk.toString();
let idx: number;
while ((idx = buf.indexOf("\n")) >= 0) {
const line = buf.slice(0, idx).trim();
buf = buf.slice(idx + 1);
if (line) onLine(line);
}
});
}
/** Send a text from the peer node. Broadcast unless `to` (a node number) is given. */
export function peerSend(
text: string,
opts: { to?: number; wantAck?: boolean } = {},
): Promise<void> {
const args = ["send", text];
if (opts.to != null) args.push("--to", String(opts.to));
if (opts.wantAck) args.push("--want-ack");
const child = spawnPeer(args);
let stderr = "";
child.stderr?.on("data", (d) => {
stderr += d.toString();
});
return new Promise<void>((resolve, reject) => {
child.on("exit", (code) =>
code === 0
? resolve()
: reject(new Error(`peer send exited ${code}: ${stderr.trim()}`)),
);
child.on("error", reject);
});
}
/** Read the peer node's own node number. */
export function peerNodeNum(): Promise<number> {
const child = spawnPeer(["node-num"]);
let num: number | null = null;
let stderr = "";
child.stderr?.on("data", (d) => {
stderr += d.toString();
});
onStdoutLines(child, (line) => {
const m = line.match(/^NODE_NUM=(\d+)/);
if (m) num = Number(m[1]);
});
return new Promise<number>((resolve, reject) => {
child.on("exit", (code) =>
num != null
? resolve(num)
: reject(new Error(`peer node-num failed (${code}): ${stderr.trim()}`)),
);
child.on("error", reject);
});
}
export type RecvHandle = {
/** Resolves with the sender node number once the awaited text arrives. */
received: Promise<number>;
/** Kill the listener early. */
stop: () => void;
};
/**
* Start listening on the peer node for `text`. The returned promise resolves
* once the listener is subscribed (so the caller can then trigger the browser
* send without racing). The handle's `received` resolves when the text lands.
*/
export function startPeerRecv(
text: string,
opts: { fromNode?: number; timeout?: number } = {},
): Promise<RecvHandle> {
const { fromNode, timeout = 60 } = opts;
const args = ["recv", text, "--timeout", String(timeout)];
if (fromNode != null) args.push("--from-node", String(fromNode));
const child = spawnPeer(args);
let stderr = "";
child.stderr?.on("data", (d) => {
stderr += d.toString();
});
let resolveReceived!: (n: number) => void;
let rejectReceived!: (e: Error) => void;
const received = new Promise<number>((res, rej) => {
resolveReceived = res;
rejectReceived = rej;
});
const handle: RecvHandle = { received, stop: () => child.kill() };
return new Promise<RecvHandle>((resolveReady, rejectReady) => {
let from: number | null = null;
onStdoutLines(child, (line) => {
if (line === "READY") resolveReady(handle);
const m = line.match(/^RECEIVED=(\d+)/);
if (m) from = Number(m[1]);
});
child.on("exit", (code) => {
if (code === 0 && from != null) {
resolveReceived(from);
} else {
const err = new Error(
`peer recv exited ${code} (no match for "${text}"): ${stderr.trim()}`,
);
rejectReady(err);
rejectReceived(err);
}
});
child.on("error", (e) => {
rejectReady(e);
rejectReceived(e);
});
});
}