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

6.7 KiB

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

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

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.