| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- import type { WebSocketFactory, WebSocketLike } from "../src/app-server-client.js";
- import { isRecord } from "../src/domain.js";
- export type FakeRequestHandler = (
- message: Record<string, unknown>,
- server: FakeAppServer,
- ) => void;
- class FakeWebSocket extends EventTarget implements WebSocketLike {
- readyState = 0;
- readonly #server: FakeAppServer;
- constructor(server: FakeAppServer) {
- super();
- this.#server = server;
- }
- open(): void {
- if (this.readyState !== 0) return;
- this.readyState = 1;
- this.dispatchEvent(new Event("open"));
- }
- send(data: string): void {
- if (this.readyState !== 1) throw new Error("fake socket is not open");
- const parsed: unknown = JSON.parse(data);
- if (!isRecord(parsed)) throw new Error("expected request object");
- this.#server.receive(parsed);
- }
- deliver(value: unknown): void {
- if (this.readyState !== 1) return;
- const data = typeof value === "string" ? value : JSON.stringify(value);
- this.dispatchEvent(new MessageEvent("message", { data }));
- }
- close(): void {
- if (this.readyState === 3) return;
- this.readyState = 3;
- this.dispatchEvent(new Event("close"));
- }
- }
- export class FakeAppServer {
- readonly frames: Record<string, unknown>[] = [];
- readonly authorizationHeaders: Array<string | null> = [];
- readonly #handler: FakeRequestHandler;
- readonly #autoOpen: boolean;
- socket: FakeWebSocket | null = null;
- constructor(handler: FakeRequestHandler, autoOpen = true) {
- this.#handler = handler;
- this.#autoOpen = autoOpen;
- }
- readonly factory: WebSocketFactory = (_url, authorizationHeader) => {
- this.authorizationHeaders.push(authorizationHeader);
- const socket = new FakeWebSocket(this);
- this.socket = socket;
- if (this.#autoOpen) queueMicrotask(() => socket.open());
- return socket;
- };
- receive(message: Record<string, unknown>): void {
- this.frames.push(message);
- this.#handler(message, this);
- }
- respond(request: Record<string, unknown>, result: unknown): void {
- this.socket?.deliver({ id: request.id, result });
- }
- fail(request: Record<string, unknown>, code: number, message: string): void {
- this.socket?.deliver({ id: request.id, error: { code, message } });
- }
- notify(method: string, params: unknown): void {
- this.socket?.deliver({ method, params });
- }
- deliverRaw(value: string): void {
- this.socket?.deliver(value);
- }
- disconnect(): void {
- this.socket?.close();
- }
- }
- export function successfulHandshake(
- handler: FakeRequestHandler = (request, server) => server.respond(request, {}),
- ): FakeRequestHandler {
- return (request, server) => {
- if (request.method === "initialize") {
- server.respond(request, { userAgent: "fake", platformFamily: "unix", platformOs: "macos" });
- return;
- }
- if (request.method === "initialized") return;
- handler(request, server);
- };
- }
- export function rawThread(
- id: string,
- overrides: Record<string, unknown> = {},
- ): Record<string, unknown> {
- return {
- id,
- sessionId: id,
- parentThreadId: null,
- forkedFromId: null,
- preview: `preview-${id}`,
- ephemeral: false,
- modelProvider: "openai",
- createdAt: 10,
- updatedAt: 20,
- recencyAt: 20,
- cwd: "/work/project",
- source: "cli",
- status: { type: "notLoaded" },
- name: null,
- agentNickname: null,
- agentRole: null,
- turns: [],
- ...overrides,
- };
- }
|