Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
# `client`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Caller-side binding for the worker-RPC protocol. `createRawWorkerRpcClient`
returns a low-level object exposing `.call(method, params, opts)` which
returns a `Promise<R>`; `createWorkerRpcClient<T>` wraps that in a typed
`Proxy<T>` so plugin authors invoke remote methods as if they were local
async methods on a typed handle.
The client handles every cross-cutting concern: monotonic correlation-id
allocation, request/response matching, optional `AbortSignal` forwarding
via cancel frames, optional per-call timeout, budget-enforced backpressure,
outbound queue management, structured-clone safety pre-checks, and
inbound frame validation with version negotiation.
## Features
- Typed `Proxy<T>` so `await client.method(arg)` works without a
generated stub.
- `AbortSignal` per call; aborting sends a `cancel` frame and rejects
the pending promise with an `AbortError`.
- Per-call timeout via `opts.timeoutMs` or `defaultTimeoutMs`.
- `callBudget` cap (default `1024`) — synchronous-throw exhaustion
with `WORKER_RPC.BACKPRESSURE`.
- Pre-send `assertCloneable` walk rejecting functions, class
instances, and symbols at the call site instead of letting the
platform throw a `DataCloneError` later.
- Inbound frames validated via the Zod protocol schema; invalid
payloads and version mismatches are surfaced through the
`onProtocolError` observer (never crash the listener).
- `onStreamFrame` hook to multiplex stream frames into the streaming
layer.
## Usage: Basic
```ts
import { createWorkerRpcClient } from "@suaveplan/api-worker-rpc/client";
interface Calc {
add(a: number, b: number): number;
}
const calc = createWorkerRpcClient<Calc>(workerPort);
const sum = await calc.add(2, 3);
calc.close();
```
## Usage: Advanced
```ts
import { createRawWorkerRpcClient } from "@suaveplan/api-worker-rpc/client";
const client = createRawWorkerRpcClient(port, {
callBudget: 256,
defaultTimeoutMs: 5_000,
onProtocolError: (err) => logger.warn({ err }, "rpc protocol error"),
});
const ac = new AbortController();
setTimeout(() => ac.abort(), 200);
try {
const result = await client.call("longRunning", [42], { signal: ac.signal });
} catch (err) {
if (err instanceof Error && err.name === "AbortError") {
// caller cancelled
}
}
client.close("shutdown");
```
## API Reference
- `createRawWorkerRpcClient(port, opts?): WorkerRpcClient` — low-level
binding.
- `createWorkerRpcClient<T>(port, opts?): WorkerProxyClient<T>` — typed
proxy whose `__raw__` field exposes the underlying client and whose
`close(reason?)` tears it down.
- `interface WorkerRpcClient { call, send, cancel, nextId, registry, inFlight, close }`.
- `interface WorkerRpcClientOptions { idGenerator?, callBudget?, outboundQueue?, defaultTimeoutMs?, onStreamFrame?, onProtocolError? }`.
- `assertCloneable(value, path?): void` — re-exported from
`./cloneable-guard`.
## Implementation Notes
- The proxy filters reserved keys (`__raw__`, `close`) so they
do NOT become remote methods.
- `call` acquires a budget slot synchronously before allocating
the id so exhaustion fails at the call site, not later in the
microtask queue.
- The inbound listener catches `parseFrame` errors and routes
them to `onProtocolError` rather than throwing — a malformed
frame should never crash the host loop.
- Cancel frames sent during abort do not require an ack from the
server (best-effort cooperative cancel).