Back to the module table.
worker_threads wraps the webview’s module web workers. A worker shares its page’s context, so it sees the same project files, environment and tunnels, and it can use every core-backed module. Its bridge calls are relayed through the main thread.
// index.ts (main thread)
import { Worker } from "node:worker_threads";
const worker = new Worker("./worker.ts.js");
worker.on("message", (event) => console.log(event.data));
worker.postMessage({ file: "/data.csv" });
// worker.ts
import { parentPort } from "node:worker_threads";
import fs from "node:fs";
parentPort.on("message", (event) => {
const csv = fs.readFileSync(event.data.file, "utf8");
parentPort.postMessage(csv.split("\n").length);
});
Workers aren’t bundled with the page. bundle() follows imports, not new Worker(url), so build each worker file on its own with bundleFile. That writes worker.ts.js next to worker.ts, and the page loads it through the static file fallback. See Project structure → Workers.
| API | Behavior |
|---|---|
new Worker(url) |
new globalThis.Worker(url, { type: "module" }). url is resolved against the page’s URL, not the project directory. Options (workerData, env, eval, …) are not supported. |
worker.postMessage(data) |
Structured clone, as with web workers |
worker.on("message", fn) |
fn receives the MessageEvent, not the data. Read event.data. |
worker.on("exit", fn) |
Fires when the worker calls process.exit() |
worker.terminate() |
Terminates the worker and drops its streams |
parentPort |
In a worker, an EventEmitter whose message listeners receive the MessageEvent, plus postMessage. null on the main thread. |
Not provided: isMainThread, workerData, threadId, MessageChannel/MessagePort from the module (the global MessageChannel works), SharedArrayBuffer sharing options, resourceLimits, receiveMessageOnPort, and BroadcastChannel from the module. The module has no default export, so use named imports: import { Worker, parentPort } from "worker_threads".
The web Worker constructor can be used directly too. The bridge initializes in any module worker that imports a runtime module.
node:readline and readline/promises are ported in full: createInterface, Interface (question, prompt, line events, history, completer, [Symbol.asyncIterator]), emitKeypressEvents, clearLine, clearScreenDown, cursorTo and moveCursor. They work over any streams you give them. In the FullStacked app, process.stdin and process.stdout are the shell’s terminal (xterm.js), so CLI-style programs that prompt with readline run inside the app. Under fullstacked --file they are Node’s real stdio.
The CLI keeps a --file script alive while one of its readline interfaces is open (waitForInterfaces).
isatty() always returns false. The ReadStream and WriteStream constructors throw. Terminal-aware packages fall back to their non-TTY output.