Files
suaveplan/openspec/changes/enterprise-browser-web-workers/proposal.md
2026-05-27 23:29:53 +01:00

208 lines
6.7 KiB
Markdown

# 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<T>(name: string, type: string, payload: unknown, options?: RequestOptions): Promise<T>
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<T>(name: string, type: string, payload: unknown, options?: RequestOptions): Promise<T>
}
class WorkerPool { // new — implements WorkerPoolConfig
constructor(config: WorkerPoolConfig)
submit<T>(task: WorkerTask<T>): Promise<T>
resize(min: number, max: number): void
drain(): Promise<void>
terminate(): void
getMetrics(): PoolMetrics
}
class MessageBus {
subscribe<T>(topic: string, handler: MessageHandler<T>): () => void
subscribeOnce<T>(topic: string, handler: MessageHandler<T>): void
publish<T>(topic: string, payload: T): void
broadcast<T>(payload: T): void
request<T, R>(topic: string, payload: T, options?: RequestOptions): Promise<R>
registerHandler<T, R>(topic: string, handler: (payload: T) => R | Promise<R>): () => 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<T>(name, type, payload): AsyncIterable<T>` 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.