ADR 004: Generic SSE with marked and Shiki
Status: Accepted
Date: 2026-05-23
Context
The MVP includes AiStreamContainer, which renders incremental markdown from streaming AI responses. We need to decide:
- How the UI layer receives streamed content (protocol)
- Which markdown parser to use
- Which syntax highlighter to use
- Whether to bundle a specific LLM provider SDK
The library targets open-source consumers who may use any backend (self-hosted, Azure OpenAI, Anthropic, etc.).
Decision
- Streaming protocol: Generic Server-Sent Events (SSE). The library provides parsing utilities in
@synapse-ui/corebut does not manage the HTTP connection — consumers connect to their own backend. - Markdown parser: marked — lightweight, extensible, GFM support.
- Syntax highlighting: Shiki — accurate TextMate grammar-based highlighting with theme support.
- No LLM SDK in the UI layer.
Consequences
Positive
- Backend-agnostic — works with any SSE-compliant server.
- marked is small and well-maintained with a simple API.
- Shiki produces high-quality highlighting that matches VS Code themes.
- Clear separation of concerns: app handles auth/connection, library handles rendering.
Negative
- Consumers must implement their own SSE connection and error handling.
- Shiki has a larger bundle size than highlight.js (mitigated by lazy-loading grammars).
- marked output must be sanitized before DOM insertion (XSS risk).
- Token counting in
AiPromptBaris heuristic-only — no server-side validation.
Alternatives Considered
| Alternative | Why rejected |
|---|---|
| WebSocket streaming | More complex; SSE is sufficient for unidirectional AI text streams |
| ngx-markdown | Adds Angular-specific wrapper dependency; marked is simpler to integrate |
| highlight.js | Lower quality highlighting; harder to match theme tokens |
| Prism.js | Similar to highlight.js; Shiki preferred for accuracy |
| Azure OpenAI SDK in core | Couples library to one provider; violates open-source neutrality |
| Custom markdown parser | Unnecessary reinventing; marked is battle-tested |
Security Note
All markdown rendered by AiStreamContainer must pass through Angular's sanitizer or DOMPurify before DOM insertion. Raw HTML in markdown input is stripped.