208 lines
6.7 KiB
Markdown
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.
|