Skip to main content
Version: 3.1.0

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

  1. The frontend calls POST /config/reload-stream using @microsoft/fetch-event-source.
  2. The backend initStream() AsyncGenerator yields one chunk per source.
  3. The config validation result appears immediately before webhook init starts.
  4. The full report is persisted once at the end (no partial writes).
  5. The old POST /config/reload endpoint is removed.

Key files

  • src/protocol/source/source.service.tsinitStream() AsyncGenerator
  • src/app.controller.tsPOST /config/reload-stream + calculateStatus
  • ui/src/protocol/tower.api.tsreloadConfigStream() with fetchEventSource
  • ui/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 an async* generator; WebhookCapacityService.webhooksInitStream() and FtpCapacityService.ftpInitStream() yield per project/path, plus an initial empty chunk as a start marker.
  • SourceService.initStream() forwards sourceName + progress and no longer filters empty chunks (so start markers reach the UI).
  • app.controller.ts calls res.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() flattens Error objects to { message, stack, code } before emission — otherwise the non-enumerable message/stack are lost as {} over JSON.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 real fetchEventSource.
  • A guard ref in Status.tsx prevents React StrictMode double-render from triggering two concurrent reload streams.
  • Errors must be explicitly serialized before crossing the SSE boundary (non-enumerable Error fields).