first commit
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
# Worker-Side Jobs Proxy Client
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`createJobsProxyClient(port, opts)` is the worker-tier entry point for the
|
||||
`@suaveplan/api-jobs-worker-proxy` package. It returns an object that
|
||||
implements the `JobRegistrationHandle` capability slot from
|
||||
`@suaveplan/api-types`, letting a worker-isolated API plugin register
|
||||
cron and queued jobs even though the actual scheduler runs in the host
|
||||
main process. Every `registerCron` and `registerQueued` call stores the
|
||||
handler in a local `HandlerRegistry` and asynchronously marshals a
|
||||
`registerJob` RPC to the main side via the supplied `MessagePort`. When
|
||||
the main scheduler ticks a job, the same port carries a `runJob`
|
||||
dispatch back to the worker; the local serve-side listener invokes the
|
||||
stored handler under an optional timeout.
|
||||
|
||||
## Features
|
||||
|
||||
- Implements the exact `JobRegistrationHandle` surface (`registerCron`,
|
||||
`registerQueued`) — no proxy-specific extensions on the public type.
|
||||
- Bidirectional over a single port: outbound registration RPCs +
|
||||
inbound `runJob` dispatch via paired raw RPC client + serve handler.
|
||||
- Fire-and-forget registration semantics with an `onHandlerError`
|
||||
observer for asynchronous rejections (job-not-declared, port closed).
|
||||
- Per-handler timeout via `handlerTimeoutMs`; long-running closures
|
||||
reject with `JOBS_PROXY.JOB_TIMEOUT`.
|
||||
- Idempotent `close(reason?)` tears down both the RPC client and the
|
||||
serve listener.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { MessageChannel } from "node:worker_threads";
|
||||
import { createJobsProxyClient } from "@suaveplan/api-jobs-worker-proxy/worker-side";
|
||||
|
||||
const channel = new MessageChannel();
|
||||
const client = createJobsProxyClient(channel.port1);
|
||||
|
||||
client.registerCron("rotate-keys", async () => {
|
||||
await rotate();
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { createJobsProxyClient } from "@suaveplan/api-jobs-worker-proxy/worker-side";
|
||||
|
||||
const client = createJobsProxyClient(workerPort, {
|
||||
handlerTimeoutMs: 30_000,
|
||||
onHandlerError: (err, ctx) => {
|
||||
logger.warn({ code: err.code, name: ctx.name, kind: ctx.kind });
|
||||
},
|
||||
});
|
||||
client.registerQueued("send-email", async (payload) => {
|
||||
await mailer.send(payload as MailPayload);
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `createJobsProxyClient(port, opts?) -> JobsProxyClient` — factory.
|
||||
- `JobsProxyClient extends JobRegistrationHandle` — adds `close()` and a
|
||||
`sizes` accessor reporting registration counts.
|
||||
- `JobsProxyClientOptions`:
|
||||
- `handlerTimeoutMs?: number` — per-dispatch timeout, `0` disables.
|
||||
- `onHandlerError?: (err, ctx) => void` — observer hook.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The client multiplexes a `createRawWorkerRpcClient` (for outbound
|
||||
`registerJob` calls) and a `serveWorkerRpc` handlers object (for inbound
|
||||
`runJob`) over the same `PortLike`. Protocol frames partition cleanly:
|
||||
`response` frames target the RPC client's pending registry, and
|
||||
`request` frames target the serve dispatcher; each listener silently
|
||||
drops the other's traffic. The local `HandlerRegistry` is the single
|
||||
source of truth for which handlers can be dispatched — the main side
|
||||
relies on the worker holding the closure and we never serialize the
|
||||
function across the wire. The fire-and-forget `registerJob` call uses
|
||||
`.catch` to funnel rejections (including `JOB_NOT_DECLARED` from main)
|
||||
through `onHandlerError`, preserving the original `JobRegistrationHandle`
|
||||
synchronous-`void` return contract.
|
||||
Reference in New Issue
Block a user