Concepts
Workers
Move expensive work — search, filtering, parsing — off the main thread with a tiny RPC layer over Web Workers and no message-plumbing boilerplate.
Three helpers cover the whole loop:
exposeWorker registers
handlers inside the worker,
createWorkerClient
calls them from the page, and
transferResult moves
large buffers instead of copying them.
Exposing handlers
In the worker module, pass
exposeWorker an object
of named handlers. Each handler receives the call payload and returns a
value (or a promise).
// client/search-worker.js
import { exposeWorker } from "@nativefragments/core/client/worker.js";
exposeWorker({
// "search" is the handler name the page will call.
search: ({ rows, query }) =>
rows.filter((row) =>
row.title.toLowerCase().includes(query.toLowerCase())
),
});
Calling from the page
createWorkerClient
spins up a module worker and returns a client.
call(name, payload) resolves with the handler's result and
rejects on error or timeout.
// client/index.js
import { createWorkerClient } from "@nativefragments/core/client/worker.js";
const search = createWorkerClient("/build/search-worker.js");
// Runs off the main thread — typing stays responsive.
const hits = await search.call("search", { rows, query: "native" });
search.dispose(); // detach listeners + reject pending calls when done
Transferring large buffers
By default the result is structured-cloned (copied). Wrap it in
transferResult to
transfer ownership of an ArrayBuffer or other
Transferable instead — no copy.
// client/decode-worker.js
import { exposeWorker, transferResult } from "@nativefragments/core/client/worker.js";
exposeWorker({
decode: (buffer) => {
const result = process(buffer); // returns an ArrayBuffer
// Move the buffer back to the page instead of copying it.
return transferResult(result, [result]);
},
});
See also
- Components — the ⌘K palette on this site runs its search in a worker.
- API Routes — for work that belongs on the server instead.
- Reference:
exposeWorker,createWorkerClient,transferResult.