Skip to content

feat(events): SPA consumes /api/events for live task updates (part 2 of #93)#117

Merged
imran31415 merged 1 commit into
mainfrom
feat/sse-events-spa
Jun 17, 2026
Merged

feat(events): SPA consumes /api/events for live task updates (part 2 of #93)#117
imran31415 merged 1 commit into
mainfrom
feat/sse-events-spa

Conversation

@umi-appcoder

@umi-appcoder umi-appcoder Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

What

Wires the SPA to the /api/events SSE 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, 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 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.*). The memory / triggers pollers stay until the server emits memory.changed / trigger.fired (future work noted on #93). This delivers the highest-value, most-active poller first.

Verification

  • tsc --noEmit clean.
  • src/api/events.test.ts (6): open/close ref-counting, shared source, connect/disconnect, JSON dispatch, malformed-payload safety, throwing-subscriber isolation.
  • Full SPA suite 119 passing; yarn build OK (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

#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>
@imran31415 imran31415 merged commit f8ff5d8 into main Jun 17, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant