feat(events): SPA consumes /api/events for live task updates (part 2 of #93)#117
Merged
Conversation
#93) Wires the SPA to the SSE firehose added in part 1, so the Build view updates in real time instead of polling every 10s. - src/api/events.ts: a ref-counted shared EventSource client for /api/events. Opens on the first subscriber, closes on the last; relies on EventSource's built-in reconnect; tracks `eventStreamConnected` and fans `task.created` / `task.status` out to subscribers. Auth via withOauthPrefix (EventSource can't send a Bearer header, so it uses the same cookie/ingress auth as the rest of the client). No-ops where EventSource is unavailable (SSR / jsdom). - store/tasks.ts: subscribes on startTaskPolling; a coalesced refresh fires on task events (and reloads the open task when it's the one that changed). The existing interval becomes a safety-net: when the stream is connected it only refreshes if stale (>45s), otherwise it polls at the normal cadence — so behavior is unchanged when SSE is unavailable. Scope: only the tasks store is event-driven, matching the backend's current emit surface (task.*). memory/triggers pollers stay until the server emits memory.changed / trigger.fired (future work on #93). This also completes the tasks half of #101's polling unification. Tests: src/api/events.test.ts (6) — open/close ref-counting, shared source, connect/disconnect, JSON dispatch, malformed-payload safety, subscriber isolation. tsc clean; full SPA suite 119 passing; build OK. Part 2 of #93. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Wires the SPA to the
/api/eventsSSE firehose from part 1 (#116), so the Build view updates in real time instead of polling every 10s. Part 2 of #93 — and completes the tasks half of #101's polling unification.Pieces
src/api/events.ts— a ref-counted, sharedEventSourceclient for/api/events. Opens on the first subscriber, closes on the last; relies on EventSource's built-in reconnect; trackseventStreamConnectedand fanstask.created/task.statusout to subscribers. Auth viawithOauthPrefix(EventSource can't send a Bearer header, so it uses the same cookie/ingress auth as the rest of the client). No-ops whereEventSourceis unavailable (SSR / jsdom).store/tasks.ts— subscribes onstartTaskPolling; a coalesced refresh fires on task events (and reloads the open task when it's the one that changed). The existing interval becomes a safety net: when the stream is connected it only refreshes if data is stale (>45s); otherwise it polls at the normal cadence. So behavior is unchanged when SSE is unavailable — this is purely additive.Scope
Only the tasks store is event-driven, matching the backend's current emit surface (
task.*). Thememory/triggerspollers stay until the server emitsmemory.changed/trigger.fired(future work noted on #93). This delivers the highest-value, most-active poller first.Verification
tsc --noEmitclean.src/api/events.test.ts(6): open/close ref-counting, shared source, connect/disconnect, JSON dispatch, malformed-payload safety, throwing-subscriber isolation.yarn buildOK (route code-split intact).Result
With #116 + this, a task finishing or hitting a prompt reflects in the Build list/detail in <1s (event-driven), and the steady 10s background poll is suppressed while the stream is healthy — fewer requests, lower mobile battery cost — with a graceful poll fallback when the stream drops.
Part 2 of #93.
🤖 Generated with Claude Code