# Handler Bridge _(no submodules)_ ## Purpose This module owns the shared wire schemas, the worker-side `HandlerRegistry`, and the `runWithTimeout` helper used by both ends of the proxy. The bridge is the only place that decides how a registered cron/queued handler is exercised on the worker side; isolating it keeps the client and server modules small and lets the same logic be unit tested without spinning up a `MessageChannel`. Schemas use `zod` so any bad payload that crosses the boundary fails fast at the parse step rather than producing an obscure runtime error downstream. ## Features - `registerJobRequestSchema` / `runJobRequestSchema` — Zod-validated wire formats for registration and dispatch frames. - `HandlerRegistry` — type-safe storage of cron + queued closures with idempotent set / run / clear semantics and a `sizes` accessor. - `runWithTimeout` — runs a closure with an optional millisecond budget and wraps every failure into a typed `JobsProxyError` (`HANDLER_THREW` or `JOB_TIMEOUT`). ## Usage: Basic ```ts import { HandlerRegistry } from "@suaveplan/api-jobs-worker-proxy/handler-bridge"; const reg = new HandlerRegistry(); reg.setCron("daily-rotate", async () => rotate()); await reg.runCron("daily-rotate"); ``` ## Usage: Advanced ```ts import { runWithTimeout } from "@suaveplan/api-jobs-worker-proxy/handler-bridge"; await runWithTimeout( () => longRunningExport(), "export-csv", 30_000, ); // throws JobsProxyError("JOBS_PROXY.JOB_TIMEOUT") after 30s ``` ## API Reference - `registerJobRequestSchema: ZodType` — wire schema. - `runJobRequestSchema: ZodType` — wire schema. - `HandlerRegistry` — `setCron`, `setQueued`, `hasCron`, `hasQueued`, `runCron`, `runQueued`, `clear`, `sizes`. - `runWithTimeout(fn, name, timeoutMs)` — runs `fn`, wraps failures. ## Implementation Notes `runWithTimeout` uses `Promise.race` between the handler invocation and a timeout-rejecting promise. The timeout branch always installs a timer (non-nullable) when `timeoutMs > 0`; the finally-clear is unconditional once that branch is entered. A `0` budget skips the race entirely and falls back to a direct `await` so successful handlers do not pay the extra microtask cost. `HandlerRegistry.setCron`/`setQueued` return a boolean indicating whether the name already existed, allowing callers to detect (and warn on) duplicate registrations without forbidding the replacement.