Files
suaveplan/packages/api-stack/api/api-router-worker-bridge/src/bridge/bridge.md
2026-05-27 23:29:53 +01:00

2.9 KiB

bridge

(no submodules)

Purpose

This module ties marshal-request, marshal-response, and streaming together into the two halves of a working Fetch-Request-to-Response bridge over a single MessagePort. createMainBridge hands back a dispatch(Request) → Promise<Response> function that worker-isolated plugins consume; createWorkerBridge runs inside the worker and routes incoming envelopes to a user-supplied handler.

Features

  • Single bridge dispatch RPC method (http.dispatch); the main-side listener correlates responses by id so multiple in-flight requests multiplex over one port.
  • Optional request- and response-body streaming when inlineBodyLimit is set to 0, threading through the streaming module.
  • Worker-side error rethrows are forwarded via errorToEnvelope, so the main side observes the original SuaveplanError.code chain.
  • close() on either side drops pending calls with a deterministic ROUTER_BRIDGE.STREAM_INTERRUPTED error rather than hanging.
  • Strict envelope validation on both ends — a malformed payload fails with ROUTER_BRIDGE.MARSHAL_FAILED or RESPONSE_INVALID.

Usage: Basic

import {
    createMainBridge,
    createWorkerBridge,
} from "@suaveplan/api-router-worker-bridge/bridge";

const { dispatch } = createMainBridge(mainPort);
createWorkerBridge(workerPort, async (req) =>
    new Response(`hello ${new URL(req.url).pathname}`)
);

const res = await dispatch(new Request("https://api.local/world"));
await res.text(); // "hello /world"

Usage: Advanced

Wire a streaming-only bridge for large uploads/downloads:

import {
    createMainBridge,
    createWorkerBridge,
} from "@suaveplan/api-router-worker-bridge/bridge";

const { dispatch } = createMainBridge(mainPort, {
    inlineBodyLimit: 0,
    streamWindow: 32,
});
createWorkerBridge(
    workerPort,
    async (req) => new Response(req.body, { headers: req.headers }),
    { inlineBodyLimit: 0, streamWindow: 32 }
);

API Reference

  • createMainBridge(port, options?) → { dispatch(Request): Promise<Response>, close(): void }.
  • createWorkerBridge(port, handler, options?) → { close(): void }.
  • BRIDGE_DISPATCH_METHOD = "http.dispatch".
  • BridgeOptions { inlineBodyLimit?, maxBodyBytes?, headerDenyList?, streamWindow? }.
  • WorkerHandler = (Request) => Promise<Response> | Response.

Implementation Notes

The bridge intentionally re-uses api-worker-rpc's request/response and stream frame shapes (and PROTOCOL_VERSION) so its traffic shares the same wire dialect — a downstream channel can multiplex both ordinary RPC and HTTP traffic without parsing ambiguity. When the request body is streamed, the same numeric id is used for both the RPC correlation and the stream id; that is safe because the streaming-frame kind is disjoint from the response frame and never collides.