import config from "../config.ts"; import { MAX_DIFF_CACHE } from "../constants.ts"; import type { ParsedFile, RenderedDiffFile } from "./diffHighlight.ts"; import { parseDiff } from "./diffHighlight.ts"; import type { FileView } from "./highlight.ts"; // ─── Worker pool ────────────────────────────────────────────────────────────── interface WorkerHandle { worker: Worker; ready: Promise; pending: number; handlers: Map< number, { resolve: (v: unknown) => void; reject: (e: unknown) => void } >; } let nextId = 0; function createHandle(): WorkerHandle { const handlers = new Map< number, { resolve: (v: unknown) => void; reject: (e: unknown) => void } >(); const handle: WorkerHandle = { worker: null!, ready: null!, pending: 0, handlers, }; handle.worker = new Worker( new URL("../workers/highlight.worker.ts", import.meta.url).href, ); handle.ready = new Promise((resolve) => { handle.worker.onmessage = (event: MessageEvent) => { if (event.data.type === "ready") { resolve(); handle.worker.onmessage = (e) => onMessage(handle, e); return; } onMessage(handle, event); }; }); return handle; } function onMessage(handle: WorkerHandle, event: MessageEvent) { const { id, result, error } = event.data; const p = handle.handlers.get(id); if (!p) return; handle.handlers.delete(id); handle.pending--; if (error !== undefined) p.reject(new Error(error)); else p.resolve(result); } let pool: WorkerHandle[] | null = null; function leastBusy(): WorkerHandle { pool ??= Array.from({ length: config.HIGHLIGHT_WORKERS }, createHandle); return pool.reduce((a, b) => (a.pending <= b.pending ? a : b)); } function request(msg: Record): Promise { const handle = leastBusy(); const id = nextId++; handle.pending++; return new Promise((resolve, reject) => { handle.handlers.set(id, { resolve: resolve as (v: unknown) => void, reject, }); handle.ready.then(() => handle.worker.postMessage({ id, ...msg })); }); } // ─── Diff cache (lives on main thread now that dispatch is per-file) ────────── const diffCache = new Map(); // ─── Public API ─────────────────────────────────────────────────────────────── export function serveFile( content: Buffer, filename: string, cacheKey: string, ): Promise { return request({ type: "serveFile", content: content.buffer, filename, cacheKey, }); } export async function prepareDiff( rawDiff: string, cacheKey: string, repoName?: string, ): Promise { const cached = diffCache.get(cacheKey); if (cached) return cached; const parsed = await parseDiff(rawDiff, repoName); const result = await Promise.all( parsed.map((file: ParsedFile) => request({ type: "highlightFile", file }), ), ); if (cacheKey) { if (diffCache.size >= MAX_DIFF_CACHE) diffCache.delete(diffCache.keys().next().value!); diffCache.set(cacheKey, result); } return result; }