Streaming Init Report (SSE)
Overview
Config reload now streams the init report in real time via Server-Sent Events (SSE), so each source appears in the UI as soon as it is processed — instead of waiting for the entire init to complete.
How it works
- The frontend calls
POST /config/reload-streamusing@microsoft/fetch-event-source. - The backend
initStream()AsyncGenerator yields one chunk per source. - The config validation result appears immediately before webhook init starts.
- The full report is persisted once at the end (no partial writes).
- The old
POST /config/reloadendpoint is removed.
Key files
src/protocol/source/source.service.ts—initStream()AsyncGeneratorsrc/app.controller.ts—POST /config/reload-stream+calculateStatusui/src/protocol/tower.api.ts—reloadConfigStream()withfetchEventSourceui/src/pages/settings/Status.tsx— Streaming reload, guard ref for React StrictMode
Per-source progress & status badges (since 3.1.0)
The stream was refactored to yield one chunk per project/path (instead of one per source provider), each carrying sourceName and progress { current, total }. Combined with per-source start markers, this powers a live status bar in the UI:
- Each provider's
initSources()is anasync*generator;WebhookCapacityService.webhooksInitStream()andFtpCapacityService.ftpInitStream()yield per project/path, plus an initial empty chunk as a start marker. SourceService.initStream()forwardssourceName+progressand no longer filters empty chunks (so start markers reach the UI).app.controller.tscallsres.flush()after each SSE write to defeat response buffering.- The UI renders a fixed-shape status badge per source (
⏳ N→✓ N/✗ N), groups entries by repo/path with full error blocks, and clears instantly on reload. serializeError()flattensErrorobjects to{ message, stack, code }before emission — otherwise the non-enumerablemessage/stackare lost as{}overJSON.stringify, hiding the real FTP/HTTP cause.
Technical decisions
- Report is written once at the end to avoid partial/inconsistent state on disk.
- Demo mode detects the mock path and uses
mockReloadStream(simulates SSE with progressive delays) instead of realfetchEventSource. - A guard ref in
Status.tsxprevents React StrictMode double-render from triggering two concurrent reload streams. - Errors must be explicitly serialized before crossing the SSE boundary (non-enumerable
Errorfields).