Skip to content
File

Blob: src/lib/exportClient.ts

typescript47 lines
1import type { ExportRequest, ExportResult, WorkerInbound, WorkerOutbound } from "../types";
2 
3/**
4 * Main-thread handle to the export worker. The worker owns the heavy pixel work
5 * (decode → transform → encode); here we just correlate requests/responses by id
6 * and hand back a promise. The source bytes are transferred (not copied).
7 */
8 
9let worker: Worker | null = null;
10let seq = 0;
11const pending = new Map<number, { resolve: (r: ExportResult) => void; reject: (e: Error) => void }>();
12 
13function getWorker(): Worker {
14 if (worker) return worker;
15 worker = new Worker(new URL("../workers/export.worker.ts", import.meta.url), {
16 type: "module",
17 name: "wimp-export",
18 });
19 worker.onmessage = (e: MessageEvent<WorkerOutbound>) => {
20 const { id, response } = e.data;
21 const entry = pending.get(id);
22 if (!entry) return;
23 pending.delete(id);
24 if (response.ok) entry.resolve(response);
25 else entry.reject(new Error(response.error));
26 };
27 worker.onerror = (e: ErrorEvent) => {
28 const message = e.message || "The export worker crashed.";
29 for (const [, entry] of pending) entry.reject(new Error(message));
30 pending.clear();
31 // Drop the broken worker; the next request spins up a fresh one.
32 worker?.terminate();
33 worker = null;
34 };
35 return worker;
36}
37 
38export function exportImage(req: ExportRequest): Promise<ExportResult> {
39 const w = getWorker();
40 const id = ++seq;
41 const message: WorkerInbound = { id, request: req };
42 return new Promise<ExportResult>((resolve, reject) => {
43 pending.set(id, { resolve, reject });
44 w.postMessage(message, { transfer: [req.bytes] });
45 });
46}