Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
import type {
Handshake,
WsServerCallbacks,
WsServerFactory,
WsServerLike,
WsServerStartOptions,
WsServerStartResult,
WsSocketLike,
} from "../../src/types/types.js";
/**
* In-memory deterministic `WsServerLike` implementation. The
* production wiring binds the real `ws.WebSocketServer`, but unit
* tests + the `tests/integration.test.ts` 3-client scenario drive
* lifecycle events through this real-implementation fake. Per the
* CLAUDE.md "no mocks" rule, the daemon never sees `vi.mock` — only
* an alternative concrete implementation.
*/
export class FakeWsServer implements WsServerLike {
public started = false;
public stopped = false;
public lastStartOptions: WsServerStartOptions | undefined;
public callbacks: WsServerCallbacks | undefined;
public boundAddress = "127.0.0.1";
public boundPort = 0;
public startError: Error | undefined;
private readonly sockets = new Map<string, FakeWsSocket>();
public async start(
options: WsServerStartOptions
): Promise<WsServerStartResult> {
if (this.startError) throw this.startError;
this.lastStartOptions = options;
this.callbacks = options.callbacks;
this.started = true;
return { address: this.boundAddress, port: this.boundPort };
}
public async stop(): Promise<void> {
this.stopped = true;
for (const id of [...this.sockets.keys()])
this.disconnect(id, 1001, "stop");
}
public async closeAll(code = 1001, reason = "shutdown"): Promise<void> {
for (const id of [...this.sockets.keys()])
this.disconnect(id, code, reason);
}
/** Test affordance: simulate a new client connecting. */
public connect(id: string, handshake?: Partial<Handshake>): FakeWsSocket {
const socket = new FakeWsSocket(id, () =>
this.disconnect(id, 1000, "client-close")
);
this.sockets.set(id, socket);
const h: Handshake = {
headers: handshake?.headers ?? {},
remoteAddress: handshake?.remoteAddress,
url: handshake?.url ?? "/",
query: handshake?.query ?? {},
};
this.callbacks?.onConnection(socket, h);
return socket;
}
public disconnect(id: string, code = 1000, reason = "close"): void {
const socket = this.sockets.get(id);
if (!socket) return;
this.sockets.delete(id);
socket.closed = true;
this.callbacks?.onClose(id, code, reason);
}
public message(id: string, payload: string): void {
this.callbacks?.onMessage(id, payload);
}
public errorOn(id: string, err: Error): void {
this.callbacks?.onError(id, err);
}
public getSocket(id: string): FakeWsSocket | undefined {
return this.sockets.get(id);
}
public socketCount(): number {
return this.sockets.size;
}
}
export class FakeWsSocket implements WsSocketLike {
public readonly id: string;
public readonly sent: string[] = [];
public closed = false;
public readonly remoteAddress = "127.0.0.1";
private readonly onClose: () => void;
public constructor(id: string, onClose: () => void) {
this.id = id;
this.onClose = onClose;
}
public send(payload: string): void {
if (!this.closed) this.sent.push(payload);
}
public close(): void {
if (this.closed) return;
this.closed = true;
this.onClose();
}
}
/** Factory producing a fresh `FakeWsServer` per construction. */
export function makeFakeWsServerFactory(): {
readonly instances: FakeWsServer[];
readonly factory: WsServerFactory;
} {
const instances: FakeWsServer[] = [];
return {
instances,
factory: () => {
const inst = new FakeWsServer();
instances.push(inst);
return inst;
},
};
}

View File

@@ -0,0 +1,7 @@
import { webcrypto } from "node:crypto";
type WritableGlobal = { crypto?: Crypto };
const g = globalThis as WritableGlobal;
if (g.crypto === undefined) {
g.crypto = webcrypto as unknown as Crypto;
}

View File

@@ -0,0 +1,142 @@
import { createServer, type Server as HttpServer } from "node:http";
import { type WebSocket, WebSocketServer } from "ws";
import { handleHealth } from "../../src/health/health.js";
import type {
Handshake,
WsServerFactory,
WsServerLike,
WsServerStartOptions,
WsServerStartResult,
WsSocketLike,
} from "../../src/types/types.js";
/**
* Production-grade `WsServerLike` implementation backed by the real
* `ws` package and a Node `http.Server`. The HTTP server also routes
* `/health` / `/ready` / `/live` probes through the daemon's snapshot
* so the integration test can hit those endpoints from the same port.
*/
export class RealWsServer implements WsServerLike {
#http: HttpServer | undefined;
#wss: WebSocketServer | undefined;
readonly #sockets = new Map<
string,
{ socket: WebSocket; like: WsSocketLike }
>();
readonly #snapshot: () => {
running: boolean;
disposed: boolean;
clientCount: number;
roomCount: number;
startedAt: number | undefined;
};
#nextId = 0;
public constructor(
snapshot: () => {
running: boolean;
disposed: boolean;
clientCount: number;
roomCount: number;
startedAt: number | undefined;
}
) {
this.#snapshot = snapshot;
}
public async start(
opts: WsServerStartOptions
): Promise<WsServerStartResult> {
const http = createServer((req, res) => {
const handled = handleHealth(req.url, this.#snapshot());
if (handled) {
res.writeHead(handled.status, {
"content-type": "application/json",
});
res.end(handled.body);
return;
}
res.writeHead(404, { "content-type": "text/plain" });
res.end("not found");
});
const wss = new WebSocketServer({ server: http });
wss.on("connection", (socket, request) => {
const id = `wsc-${++this.#nextId}`;
const url = request.url ?? "/";
const q = new URL(url, "http://x").searchParams;
const handshake: Handshake = {
headers: Object.fromEntries(
Object.entries(request.headers).map(([k, v]) => [
k,
Array.isArray(v) ? v.join(",") : v,
])
),
remoteAddress: request.socket.remoteAddress,
url,
query: Object.fromEntries(q.entries()),
};
const like: WsSocketLike = {
id,
remoteAddress: request.socket.remoteAddress,
send: (payload) => {
if (socket.readyState === socket.OPEN) socket.send(payload);
},
close: (code, reason) => socket.close(code, reason),
};
this.#sockets.set(id, { socket, like });
opts.callbacks.onConnection(like, handshake);
socket.on("message", (data) =>
opts.callbacks.onMessage(id, data.toString())
);
socket.on("close", (code, reason) => {
this.#sockets.delete(id);
opts.callbacks.onClose(id, code, reason.toString());
});
socket.on("error", (err) => opts.callbacks.onError(id, err));
});
await new Promise<void>((resolve, reject) => {
const onErr = (err: Error): void => reject(err);
http.once("error", onErr);
http.listen(opts.port, opts.host, () => {
http.removeListener("error", onErr);
resolve();
});
});
this.#http = http;
this.#wss = wss;
const addr = http.address() as { address: string; port: number };
return { address: addr.address, port: addr.port };
}
public async stop(): Promise<void> {
await new Promise<void>((resolve) => {
this.#wss?.close(() => resolve());
});
await new Promise<void>((resolve) => {
const http = this.#http;
if (!http) {
resolve();
return;
}
http.close(() => resolve());
});
}
public async closeAll(code = 1001, reason = "shutdown"): Promise<void> {
for (const { socket } of this.#sockets.values())
socket.close(code, reason);
}
}
/** Build a real-ws factory bound to a snapshot accessor. */
export function makeRealWsServerFactory(
snapshot: () => {
running: boolean;
disposed: boolean;
clientCount: number;
roomCount: number;
startedAt: number | undefined;
}
): WsServerFactory {
return () => new RealWsServer(snapshot);
}