Skip to content

Payloads

The transport supports the following payloads:

  • number (including NaN, Infinity, and -Infinity)
  • string
  • boolean
  • bigint
  • undefined and null
  • plain JSON-like Object and Array
  • Envelope<H, B> where H is JSON-like and B is ArrayBuffer (default), SharedArrayBuffer, ProcessSharedBuffer, or BufferReference
  • Buffer (Node.js), ArrayBuffer
  • Uint8Array, Int32Array, Float64Array, BigInt64Array, BigUint64Array
  • DataView
  • ProcessSharedBuffer for zero-copy shared memory (see Shared memory)
  • BufferReference from knitting/unsafe for zero-copy buffers to thread workers (see Buffer reference)
  • Error (name, message, stack, and cause chain)
  • Date
  • symbol from Symbol.for(...) only
  • native Promise<supported> values at the host call boundary

If you need multiple values, pass a tuple or object as the single argument. These types are not supported directly:

  • Map, Set, WeakMap, and custom class instances (except Envelope and subclasses)
  • non-global symbols
  • Blob
  • functions

Promise values are accepted at the call.*() boundary, but promises themselves are runtime state and are not transferred through IPC as payloads. Only resolved values are serialized and sent to workers. If a promise input rejects, the host call rejects and the worker task is not executed. Only native Promise is accepted; thenables are treated as regular values.

See Promise inputs and awaited outputs for details.

type JSONValue =
| string
| number
| boolean
| null
| JSONArray
| JSONObject;
interface JSONObject {
[key: string]: JSONValue;
}
interface JSONArray extends Array<JSONValue> {}
type ValidInput =
| bigint
| void
| JSONValue
| symbol
| Envelope<JSONValue | string, ArrayBuffer | SharedArrayBuffer>
| Uint8Array
| Int32Array
| Float64Array
| BigInt64Array
| BigUint64Array
| DataView
| ArrayBuffer
| Error
| Date;
type Args = ValidInput | Serializable;
type TaskInput = NoBlob<Args> | Promise<NoBlob<Args>>;

Different types take different code paths and have very different costs. Use the cheapest type that fits your data.

Header-only (fastest): number, boolean, undefined, null, Date, small string, small bigint. These fit in the call header with near-zero overhead. Prefer these whenever possible.

Static payload (fast): Symbol.for(...), large bigint, typed arrays (Uint8Array, Int32Array, etc.). Reuses a small buffer region alongside the header.

Dynamic payload (allocator path): Object, Array (JSON-serialized), Error, Date, and larger strings. These need allocation, serialization, and copying. Still faster than postMessage, but measurably heavier in hot loops.

See the Performance guide for per-type benchmarks and batching guidance.

Envelope<H, B> pairs a JSON-serializable header with a binary body. Use it when a call needs both structured metadata and raw bytes — the transport carries one special binary value per call, so an envelope is how you attach a header to one.

import { Envelope } from "knitting";
const message = new Envelope(
{ route: "/upload", contentType: "application/octet-stream" },
new Uint8Array([1, 2, 3]).buffer,
);

The second type parameter B sets the body type and defaults to ArrayBuffer. The supported body types are:

BodyCopy?Works withNotes
ArrayBuffercopiedthread + processDefault. Works everywhere.
SharedArrayBufferzero-copy, sharedthread onlyShared by reference; process workers reject it.
ProcessSharedBufferzero-copy, sharedthread + processCross-process shared memory.
BufferReferencezero-copy, movedthread onlyFrom knitting/unsafe. Source is detached on construction.

Envelope is disposable — [Symbol.dispose]() disposes a disposable body (a BufferReference) and is a no-op for ArrayBuffer and SharedArrayBuffer. Use using to dispose automatically when the envelope goes out of scope:

using result = await pool.call.processImage(
new Envelope({ format: "png" }, buffer),
);
console.log(result.header);
// result is disposed here when the `using` scope exits

For BufferReference bodies, see Buffer reference. For ProcessSharedBuffer bodies, see Shared memory.

Error payloads preserve name, message, stack, and recursive cause chains. Both sync throws and async rejections inside tasks propagate back to the host as Error objects.

See also: knitting/utils for buffer serialization helpers and Buffer reference for zero-copy thread payloads.