# OpenSpec Change: enterprise-browser-web-workers - **Change ID**: enterprise-browser-web-workers - **Package**: `@suaveplan/web-workers` - **Category**: browser - **Target path**: `browser/platform/web-workers` - **Status**: IN PROGRESS - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/web-workers.md` --- ## Summary Deliver `@suaveplan/web-workers` on the genesis branch at enterprise quality. The package provides `WorkerManager` and `MessageBus`. Genesis ships the two advertised-but-missing features: `SharedWorkerManager` (separate `SharedWorker` lifecycle + per-port wiring) and `WorkerPool` (FIFO task queue, round-robin dispatch, min/max idle, health checks, backpressure). Also adds: `AbortSignal` cancellation on `sendRequest`/`MessageBus.request`, transfer-list support, `@suaveplan/events` runtime wiring for `WorkerManagerEventMap`/`MessageBusEventMap`, circuit breaker, streaming RPC, `defineWorker()` Vite helper, and OTel metrics. --- ## Current-state audit (genesis) Fresh build — does not exist on genesis yet. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/events` (`workspace:^`) | Runtime event emission for `WorkerManagerEventMap` | | `@suaveplan/error` (`workspace:^`) | Typed worker errors | | `@suaveplan/types` (`workspace:^`) | `LoggerLike` | | `@suaveplan/logger` (`workspace:^`) | Structured logging | | `@suaveplan/schema-registry` (`workspace:^`) | Worker message schema | | `zod` (`catalog:forms`) | `WorkerMessage` Zod schema | | `@opentelemetry/api` (`catalog:observability`) | Message counters, RPC latency | | `tslib` (`catalog:build`) | Runtime helpers | Dev: `vitest`, `@vitest/coverage-istanbul`, `happy-dom`, `@suaveplan/testing`. --- ## Preserved public API surface ```ts class WorkerManager { constructor(factory?: WorkerFactory) create(name: string, url: string | URL, options?: WorkerConfig): Worker postMessage(name: string, type: string, payload: unknown, options?: { transfer?: Transferable[] }): void sendRequest(name: string, type: string, payload: unknown, options?: RequestOptions): Promise terminate(name: string): void terminateAll(): void has(name: string): boolean getStatus(name: string): WorkerStatus getWorkerNames(): string[] onError(name: string, cb: (e: ErrorEvent) => void): () => void } class SharedWorkerManager { // new constructor(factory?: SharedWorkerFactory) connect(name: string, url: string | URL): MessagePort disconnect(name: string): void sendRequest(name: string, type: string, payload: unknown, options?: RequestOptions): Promise } class WorkerPool { // new — implements WorkerPoolConfig constructor(config: WorkerPoolConfig) submit(task: WorkerTask): Promise resize(min: number, max: number): void drain(): Promise terminate(): void getMetrics(): PoolMetrics } class MessageBus { subscribe(topic: string, handler: MessageHandler): () => void subscribeOnce(topic: string, handler: MessageHandler): void publish(topic: string, payload: T): void broadcast(payload: T): void request(topic: string, payload: T, options?: RequestOptions): Promise registerHandler(topic: string, handler: (payload: T) => R | Promise): () => void getSubscriberCount(topic: string): number getTopics(): string[] } // Vite helper function defineWorker(url: string, options?: WorkerOptions): Worker ``` Subpath exports: `"."`, `"./worker-manager"`, `"./shared-worker-manager"`, `"./worker-pool"`, `"./message-bus"`, `"./helpers/vite-worker"`. --- ## New additions for genesis ### 1. `SharedWorkerManager` `src/shared-worker-manager/` — `SharedWorkerManager` extends `BaseWorkerManager`; connects via `new SharedWorker(url)`; manages one `MessagePort` per named connection. ### 2. `WorkerPool` `src/worker-pool/` — `WorkerPool` implementing `WorkerPoolConfig`; FIFO task queue, round-robin / least-busy dispatch, `minIdle`/`maxIdle`, health checks, per-task timeouts, circuit breaker at pool level. ### 3. `AbortSignal` + transfer-list `sendRequest` and `MessageBus.request` accept `AbortSignal` in `RequestOptions`. `postMessage` accepts `{ transfer?: Transferable[] }` for zero-copy handoffs. ### 4. `@suaveplan/events` runtime wiring `WorkerManagerEventMap` and `MessageBusEventMap` are emitted at runtime via an injected `EventBus` (no longer types-only). ### 5. Streaming RPC `sendStream(name, type, payload): AsyncIterable` in `WorkerManager` for partial-result pushes without manual correlation. ### 6. Circuit breaker `src/worker-manager/circuit-breaker.ts` — opens after N consecutive timeouts; half-opens after cooldown; resets on success. ### 7. Backpressure ceiling `maxPendingRequests` on `WorkerConfig`; `sendRequest` rejects with `WORKER_QUEUE_FULL` when exceeded. ### 8. Error codes `WORKER_NOT_FOUND`, `WORKER_QUEUE_FULL`, `WORKER_REQUEST_TIMEOUT`, `WORKER_CIRCUIT_OPEN`. ### 9. OTel metrics Counter `workers.messages_sent_total`, counter `workers.messages_received_total`, histogram `workers.rpc_latency_ms`, gauge `workers.pool_size`, gauge `workers.pending_requests`. --- ## File layout ```text browser/platform/web-workers/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts core/ base-manager/ base-manager.ts base-manager.md index.ts worker-manager/ worker-manager/ worker-manager.ts worker-manager.test.ts worker-manager.md index.ts circuit-breaker/ circuit-breaker.ts circuit-breaker.test.ts circuit-breaker.md index.ts shared-worker-manager/ shared-worker-manager/ shared-worker-manager.ts shared-worker-manager.test.ts shared-worker-manager.md index.ts worker-pool/ worker-pool/ worker-pool.ts worker-pool.test.ts worker-pool.md worker-pool.types.ts index.ts message-bus/ message-bus/ message-bus.ts message-bus.test.ts message-bus.md index.ts streaming/ stream/ stream.ts stream.test.ts stream.md index.ts helpers/ vite-worker/ vite-worker.ts vite-worker.test.ts vite-worker.md index.ts types/ web-workers.types.ts web-workers.types.md constants/ index.ts tests/ integration.test.ts ``` --- ## Impact **Downstream**: any Suaveplan package using Web Worker orchestration. **Breaking changes**: None — all new APIs are additive; `sendRequest` gains optional `AbortSignal` param. **Prerequisites**: `@suaveplan/events`, `@suaveplan/error`, `@suaveplan/logger` on Verdaccio.