Files
skills/plugins/document-navigator/docs/api-stack/api/api-worker-rpc
admin 9bf56a806b Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 01:10:00 +01:00
..

@suaveplan/api-worker-rpc - MessagePort-based RPC protocol for worker-isolated API plugins. Provides Zod-validated wire frames, monotonic correlation ids, AbortSignal forwarding, streaming with backpressure, structured-clone safety guards, SuaveplanError reconstruction across the boundary, and paired client/server bindings. Universal; Stage-3 worker-proxy packages depend on this.

MessagePort-based RPC protocol for worker-isolated API plugins. Provides the shared protocol — Zod-validated wire frames, monotonic correlation ids, AbortSignal forwarding, streaming with backpressure, structured- clone safety guards, SuaveplanError reconstruction across the boundary, and paired client/server bindings — that every Stage-3 worker-proxy package builds on.

The package is universal: it depends only on a structural PortLike shape that matches both Node/Bun's worker_threads MessagePort and the Web platform's MessagePort. No platform globals are imported at module load.

Documentation tree

  • abort-forwarding — Wires caller-side AbortSignal instances across the MessagePort boundary so an in-flight RPC can be cancelled cooper…
    • abort-forwarding — Wires caller-side AbortSignal instances across the MessagePort boundary so an in-flight RPC can be cancelled cooper…
  • backpressure — Bounds memory growth on a busy RPC channel by exposing two primitives: the logical CallBudget for outstanding in-flig…
    • backpressure — Bounds memory growth on a busy RPC channel by exposing two primitives: the logical CallBudget for outstanding in-flig…
  • client — Caller-side binding for the worker-RPC protocol. createRawWorkerRpcClient returns a low-level object exposing `.call(…
    • client — Caller-side binding for the worker-RPC protocol. createRawWorkerRpcClient returns a low-level object exposing `.call(…
    • cloneable-guard — Walks a value (typically an RPC argument list or return value) and throws WORKER_RPC.NOT_CLONEABLE at the call site i…
  • correlation — Provides the monotonic correlation-id generator and the in-memory pending-call registry that backs every outstanding RP…
    • correlation — Provides the monotonic correlation-id generator and the in-memory pending-call registry that backs every outstanding RP…
  • error-codes — Defines the stable WORKER_RPC.* code registry and the WorkerRpcError class. Every public failure path in `@suavepla…
    • error-codes — Defines the stable WORKER_RPC.* code registry and the WorkerRpcError class. Every public failure path in `@suavepla…
  • error-reconstruction — Translates thrown values into wire-safe ErrorEnvelopes and back into typed Error instances on the receiving side. `…
    • error-reconstruction — Translates thrown values into wire-safe ErrorEnvelopes and back into typed Error instances on the receiving side. `…
  • protocol — Defines the wire-format of every message that crosses a MessagePort between an api-host and an isolated/sandboxed p…
    • protocol — Defines the wire-format of every message that crosses a MessagePort between an api-host and an isolated/sandboxed p…
  • server — Worker-side binding for the RPC protocol. serveWorkerRpc(port, handlers) mounts a handlers object on a MessagePort,…
    • server — Worker-side binding for the RPC protocol. serveWorkerRpc(port, handlers) mounts a handlers object on a MessagePort,…
  • streaming — Caller-side primitives for consuming streaming RPC methods. The server publishes a stream-chunk frame per yielded val…
    • streaming — Caller-side primitives for consuming streaming RPC methods. The server publishes a stream-chunk frame per yielded val…
  • testing — Reusable conformance harness and an in-process paired-port factory used by every Stage-3 worker-proxy package's test su…
    • conformance — Builds a paired client/server harness over an in-process MessageChannel for conformance tests. The harness is intende…
    • testing — Reusable conformance harness and an in-process paired-port factory used by every Stage-3 worker-proxy package's test su…

Why

Per the api-plugin system design (§2.2 isolation tiers, §5.2a worker boundary), every capability adapter ships in two surfaces: a direct in-process binding for trusted plugins and a MessagePort- marshalled binding for isolated / sandboxed plugins. Hand-rolling that marshalling once per capability is how plugin frameworks accrete subtly-different RPC layers that diverge on cancellation, streaming, and error reconstruction. api-worker-rpc is the single shared protocol every worker-proxy package builds on; getting it right once means every capability — db, events, secrets, jobs, logger, future ones — gets cancellation, backpressure, streaming, and error reconstruction for free.

Install

bun add @suaveplan/api-worker-rpc

Quick start

import { createWorkerRpcClient } from "@suaveplan/api-worker-rpc";
import { serveWorkerRpc } from "@suaveplan/api-worker-rpc";
import { createInProcessPortPair } from "@suaveplan/api-worker-rpc/testing";

interface Calc { add(a: number, b: number): number; }

const { clientPort, serverPort, close } = createInProcessPortPair();
const server = serveWorkerRpc(serverPort, {
    add: ([a, b]) => (a as number) + (b as number),
});
const calc = createWorkerRpcClient<Calc>(clientPort);

console.log(await calc.add(2, 3)); // 5

calc.close();
server.close();
close();

Modules

  • protocol — Zod-validated wire frames (request, response, cancel, progress, stream-open, stream-chunk, stream-end, stream-ack, hello, goodbye) plus parseFrame and the ErrorEnvelope type.
  • correlation — monotonic id generator + PendingRegistry (orphan detection, reuse detection, timeout eviction).
  • client — createWorkerRpcClient<T> typed proxy and createRawWorkerRpcClient low-level binding.
  • client/cloneable-guard — pre-send structured-clone safety walk.
  • server — serveWorkerRpc(port, handlers) mounts handler maps with automatic streaming and cancel-via-AbortController.
  • streaming — caller-side streamRequest returning an AsyncIterable + StreamDispatcher for inbound-frame routing.
  • abort-forwarding — attachAbortSignal, createRemoteAbort, and the platform-independent AbortError class.
  • backpressure — CallBudget (logical) and BoundedQueue (physical) with three policies.
  • error-reconstruction — errorToEnvelope / envelopeToError with depth-bounded cause chains.
  • error-codes — the WORKER_RPC_CODES registry and WorkerRpcError class.
  • testing — createInProcessPortPair
    • runWorkerRpcConformance harness.

Error codes

Every code in WORKER_RPC_CODES is reachable from at least one production path:

Code Raised from
WORKER_RPC.PORT_CLOSED client.close() reject; post-close send
WORKER_RPC.REQUEST_TIMEOUT per-call timeout eviction
WORKER_RPC.RESPONSE_TIMEOUT (reserved for response-only timeout policy)
WORKER_RPC.INVALID_FRAME malformed inbound frame
WORKER_RPC.UNKNOWN_METHOD server dispatch of missing handler
WORKER_RPC.CORRELATION_ID_REUSED double-register on same id
WORKER_RPC.STREAM_BACKPRESSURE_EXCEEDED reserved for stream-explicit overflow
WORKER_RPC.HANDLER_THREW server-side stream handler not iterable
WORKER_RPC.CANCELLED registry cancel
WORKER_RPC.VERSION_MISMATCH inbound v !== PROTOCOL_VERSION
WORKER_RPC.ORPHAN_RESPONSE settle() on unknown id
WORKER_RPC.NOT_CLONEABLE assertCloneable pre-send walk
WORKER_RPC.REMOTE_THROW wrapping foreign throws on the wire
WORKER_RPC.BACKPRESSURE CallBudget or fail-mode BoundedQueue

Verification

  • 100% test coverage gated in CI (bunx turbo test:coverage --filter=@suaveplan/api-worker-rpc).
  • Full lint+typecheck+test+build gate per package rules.

License

MIT