Files
2026-05-27 23:29:53 +01:00

4.0 KiB

Capability Spec Delta: api-ws-broker-in-memory (api-ws-broker-in-memory)

Package: @suaveplan/api-ws-broker-in-memory Status: PROPOSED

ADDED Requirements

Requirement: Fan-out to every subscriber on publish

The broker SHALL deliver publish(topic, message) to every connection currently subscribed to topic. Delivery order across subscribers is unspecified; per-subscriber message order is FIFO.

Scenario: every subscriber receives every message

  • WHEN three connections subscribe to "orders.created" AND two messages are published
  • THEN each connection receives both messages in publish order

Verified by: src/factory/create-in-memory-ws-broker.test.ts::"every subscriber receives every published message in FIFO order"


Requirement: Subscription lifecycle

The broker SHALL expose subscribe(connectionId, topic), unsubscribe(connectionId, topic), and cleanupConnection(connectionId). cleanupConnection removes the connection from every topic it was subscribed to.

Scenario: unsubscribe stops delivery

  • WHEN a connection subscribes, then unsubscribes, then a message is published
  • THEN the connection does NOT receive the message

Scenario: cleanupConnection removes from every topic

  • WHEN a connection is subscribed to ["a", "b", "c"] AND cleanupConnection(id) is called
  • THEN subscribersOf("a"), subscribersOf("b"), subscribersOf("c") no longer include id

Scenario: cleanup is idempotent

  • WHEN cleanupConnection(id) is called twice
  • THEN the second call is a no-op AND no error is thrown

Verified by: src/factory/create-in-memory-ws-broker.test.ts::"unsubscribe stops delivery" AND src/factory/create-in-memory-ws-broker.test.ts::"cleanupConnection removes from every topic" AND src/factory/create-in-memory-ws-broker.test.ts::"cleanupConnection is idempotent"


Requirement: maxSubscribersPerTopic enforced when set

When options.maxSubscribersPerTopic is set AND a new subscribe would exceed it, the broker SHALL throw WS_BROKER_IN_MEMORY_MAX_SUBSCRIBERS_EXCEEDED.

Scenario: subscribe rejected at cap

  • WHEN maxSubscribersPerTopic: 2 AND two connections subscribe AND a third tries to subscribe
  • THEN the third call throws WS_BROKER_IN_MEMORY_MAX_SUBSCRIBERS_EXCEEDED

Verified by: src/factory/create-in-memory-ws-broker.test.ts::"subscribe throws when maxSubscribersPerTopic is exceeded"


Requirement: Delivery mode semantics

deliveryMode: "await-all" SHALL await every subscriber's handler before publish resolves. "fire-and-forget" SHALL return immediately after dispatch.

Scenario: await-all awaits every handler

  • WHEN deliveryMode: "await-all" AND two subscribers each return a 50ms-delayed promise
  • THEN publish resolves only after both promises settle (measurable timing)

Scenario: fire-and-forget returns synchronously

  • WHEN deliveryMode: "fire-and-forget"
  • THEN publish resolves before any subscriber's delayed promise has settled

Verified by: src/delivery/delivery-mode.test.ts::"await-all blocks publish until every handler settles" AND src/delivery/delivery-mode.test.ts::"fire-and-forget resolves publish synchronously"


Requirement: Conforms to WsBroker contract

Scenario: conformance suite passes

  • WHEN runWsBrokerConformance("in-memory-direct", () => createInMemoryWsBroker(...)) runs
  • THEN every invariant passes

Verified by: src/factory/create-in-memory-ws-broker.spec-conformance.test.ts::"in-memory-direct conforms to WsBroker contract"


Requirement: Registered in api-host/testing adapter table

Scenario: createTestHost resolves the sentinel

  • WHEN createTestHost({ adapters: { "ws.broker": "in-memory" } })
  • THEN the resulting host.ws.broker is an instance produced by this factory

Verified by: tests/integration/integration.test.ts::"createTestHost resolves ws.broker:\"in-memory\" to this adapter"