Initial Commit
This commit is contained in:
@@ -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 -->
|
||||
Reference in New Issue
Block a user