Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
<!-- markdownlint-disable MD024 -->
# 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"`
<!-- markdownlint-enable MD024 -->