Share upstream event stream hub
This commit is contained in:
@@ -5,9 +5,12 @@ This module contains the OpenChamber message-stream WebSocket protocol and runti
|
||||
|
||||
## Entrypoints and structure
|
||||
- `packages/web/server/lib/event-stream/index.js`: public entrypoint re-exporting protocol and runtime helpers.
|
||||
- `packages/web/server/lib/event-stream/global-hub.js`: shared global upstream SSE hub for server-side subscribers and browser WS fan-out.
|
||||
- `packages/web/server/lib/event-stream/protocol.js`: path constants, SSE envelope parsing, and WebSocket frame serialization helpers.
|
||||
- `packages/web/server/lib/event-stream/runtime.js`: WebSocket server runtime, upgrade handling, SSE-to-WS bridging, and global event broadcasting.
|
||||
- `packages/web/server/lib/event-stream/upstream-reader.js`: reusable upstream SSE reader with event-id tracking, stall recovery, and reconnect handling.
|
||||
- `packages/web/server/lib/event-stream/runtime.js`: WebSocket server runtime, upgrade handling, shared global upstream hub, per-directory upstream reader orchestration, and global event broadcasting.
|
||||
- `packages/web/server/lib/event-stream/protocol.test.js`: unit tests for protocol helpers.
|
||||
- `packages/web/server/lib/event-stream/upstream-reader.test.js`: unit tests for upstream SSE reader behavior.
|
||||
- `packages/web/server/lib/event-stream/runtime.test.js`: unit tests for runtime-side broadcaster behavior.
|
||||
|
||||
## Public exports
|
||||
@@ -21,25 +24,35 @@ This module contains the OpenChamber message-stream WebSocket protocol and runti
|
||||
- `sendMessageStreamWsEvent(socket, payload, options)`: sends an event frame with optional `eventId` and `directory`.
|
||||
|
||||
### Runtime helpers
|
||||
- `createGlobalMessageStreamHub(...)`: creates a shared `/global/event` upstream SSE hub with event/status subscribers and bounded event-id replay.
|
||||
- `createGlobalUiEventBroadcaster({ sseClients, wsClients, writeSseEvent })`: returns a broadcaster that fans out the same synthetic UI event to SSE and WS clients.
|
||||
- `createMessageStreamWsRuntime(...)`: mounts the message-stream WS server, upgrade handler, and SSE-to-WS bridge onto the web HTTP server.
|
||||
|
||||
### Upstream reader helpers
|
||||
- `DEFAULT_UPSTREAM_STALL_TIMEOUT_MS`: default idle timeout before an attached upstream SSE fetch is aborted for reconnect.
|
||||
- `DEFAULT_UPSTREAM_RECONNECT_DELAY_MS`: default delay between upstream reconnect attempts.
|
||||
- `createUpstreamSseReader(...)`: creates a start/stop reader for OpenCode SSE streams. The reader parses SSE blocks, tracks the latest `Last-Event-ID`, reconnects after closed or stalled upstream streams, and reports events through callbacks.
|
||||
|
||||
## Runtime behavior
|
||||
- Browser clients connect to the WS endpoints above.
|
||||
- OpenChamber still fetches OpenCode upstream event streams over SSE.
|
||||
- Each WS connection proxies one upstream SSE stream.
|
||||
- If an upstream SSE stream stalls after the browser WS is already ready, the runtime aborts that upstream fetch and reconnects upstream with `Last-Event-ID`, keeping the browser WS alive when recovery is fast.
|
||||
- The web server creates one shared global message-stream hub. OpenCode watcher side effects and global WS clients subscribe to that hub, so there is one upstream `/global/event` SSE reader for both server-side processing and browser fan-out.
|
||||
- The global hub keeps a bounded replay buffer keyed by SSE `eventId` so reconnecting browser clients can receive buffered events after their requested `Last-Event-ID`.
|
||||
- Directory WS clients still attach one upstream `/event?directory=...` SSE reader per connection because directory streams are scoped.
|
||||
- If an upstream SSE stream stalls after the browser WS is already ready, the reader aborts that upstream fetch and reconnects upstream with `Last-Event-ID`, keeping the browser WS alive when recovery is fast.
|
||||
- Health checks are reserved for initial upstream connect failures and explicit upstream-unavailable responses, not for ordinary stall recovery on an already-established stream.
|
||||
- Global synthetic events such as `openchamber:session-status`, `openchamber:session-activity`, `openchamber:notification`, and `openchamber:heartbeat` are preserved on the WS path, but heartbeat frames are emitted only while an upstream SSE stream is actively attached.
|
||||
- Global UI broadcasts are fan-out capable across both SSE and WS clients.
|
||||
- The reusable upstream reader centralizes SSE fetch/parsing/reconnect behavior for the WS runtime and OpenCode watcher. Additional event consumers should move to it only with parity tests for their lifecycle and error semantics.
|
||||
|
||||
## Notes for contributors
|
||||
- Keep protocol helpers pure and small so they can be unit tested without spinning up a server.
|
||||
- Keep runtime wiring in this module instead of `packages/web/server/index.js` unless the logic is strictly route-local glue.
|
||||
- Do not change upstream OpenCode transport assumptions here; OpenCode remains SSE-based.
|
||||
- If replay support is added later, add it here rather than growing `index.js`.
|
||||
- Keep global replay bounded; do not turn it into an unbounded event log.
|
||||
|
||||
## Testing
|
||||
- Run `bun test packages/web/server/lib/event-stream/protocol.test.js`
|
||||
- Run `bun test packages/web/server/lib/event-stream/upstream-reader.test.js`
|
||||
- Run `bun test packages/web/server/lib/event-stream/runtime.test.js`
|
||||
- Run repo validation before finalizing: `bun run type-check`, `bun run lint`, `bun run build`
|
||||
|
||||
Reference in New Issue
Block a user