Files
suaveplan/openspec/archive/2026-05-26-api-ws-broker-in-memory/proposal.md
2026-05-27 23:29:53 +01:00

3.2 KiB

@suaveplan/api-ws-broker-in-memory — In-memory WebSocket broker for tests

Package: @suaveplan/api-ws-broker-in-memory Status: PROPOSED Type: new-package Stage: 5 (Testing kit + conformance suites) — see umbrella openspec/changes/api-plugin-foundation/

Why

Per design doc §11, api-ws defines a WsBroker abstraction for subscription fan-out. Multi-host deployments wire this to Redis via api-ws-broker-redis (Stage 7). For tests AND single-host development, an in-memory broker is sufficient — it pumps published messages directly to every interested connection without crossing a network.

The package is a load-bearing piece of createTestHost: any plugin manifesting WebSocket endpoints can be tested end-to-end (connect → subscribe → publish → assert received) without spinning up Redis. The broker is production-grade for single-host topologies; "in-memory" describes implementation, not test-double status.

What changes

Net-new package depending on @suaveplan/api-ws (contract + ./testing conformance), @suaveplan/api-types, @suaveplan/error, zod.

  • ADDED: createInMemoryWsBroker(opts) — factory returning a WsBroker (per @suaveplan/api-types) that maintains a per-subscription-topic set of connection ids and a publish(topic, message) method that delivers to every subscriber in O(subscribers).
  • ADDED: InMemoryWsBrokerOptions — Zod schema (maxSubscribersPerTopic?: number (default unlimited), deliveryMode: "fire-and-forget" | "await-all" (default "fire-and-forget")).
  • ADDED: subscribe(connectionId, topic), unsubscribe(connectionId, topic), publish(topic, message), subscribersOf(topic), cleanupConnection(connectionId).
  • ADDED: WS_BROKER_IN_MEMORY_* error codes (WS_BROKER_IN_MEMORY_MAX_SUBSCRIBERS_EXCEEDED, WS_BROKER_IN_MEMORY_INVALID_OPTIONS).
  • ADDED: Registered in @suaveplan/api-host/testing's adapter table as ws.broker: "in-memory" (default for createTestHost).

Out of scope

  • Network distribution — by design.
  • Reconnect token persistence — that lives in the WebSocket engine (api-ws-bun Stage 7), not the broker.
  • Message persistence — broker is fan-out only; tests assert via host.ws.connectionsOf(topic) and direct connection observation.

Source mapping

Genesis-original. Source of design intent: docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md.

Requirement Design doc section
Subscription bus + broker abstraction §11.2, §11.3
Single-host + test broker §11.3 (final sentence)
Registered in api-host/testing adapter table §15 Stage 5

Verification approach

  • src/factory/create-in-memory-ws-broker.test.ts — subscribe, publish, fan-out count; unsubscribe; cleanupConnection on disconnect.
  • src/factory/create-in-memory-ws-broker.spec-conformance.test.ts — runWsBrokerConformance("in-memory-direct", ...).
  • src/delivery/delivery-mode.test.ts — "await-all" waits for every subscriber's handler to settle; "fire-and-forget" returns synchronously.
  • tests/integration/integration.test.ts — via createTestHost; full subscribe-then-publish flow against a plugin manifesting a WS endpoint.

Migration impact

None — additive only.