Initial Commit
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
# `bridge`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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
|
||||
|
||||
```ts
|
||||
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:
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user