Initial Commit
This commit is contained in:
207
openspec/changes/enterprise-browser-web-workers/proposal.md
Normal file
207
openspec/changes/enterprise-browser-web-workers/proposal.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user