Skip to content

[Research] WebSocket and Streaming Patterns in Web Components #227

@AlexMikhalev

Description

@AlexMikhalev

Research Objective

Investigate WebSocket management and SSE streaming patterns for Chat and Search Web Components.

Current State

  • Chat uses WebSocket for real-time messaging (Chat.svelte)
  • Search uses EventSource (SSE) for summarization updates (Search.svelte)
  • Connection state management in Svelte lifecycle
  • Tauri uses polling fallback (no EventSource support)
  • Automatic reconnection on error

References:

  • Chat WebSocket: desktop/src/lib/Chat/Chat.svelte
  • Search SSE: desktop/src/lib/Search/Search.svelte:102-224

Research Questions

  1. How to manage WebSocket/SSE lifecycle in Web Components?
  2. Connection state management patterns?
  3. Automatic reconnection strategies?
  4. Memory leak prevention?
  5. Multiple component instances sharing connections?
  6. Fallback strategies (polling for Tauri)?

Streaming Patterns to Evaluate

Pattern 1: Component-Owned Connection

Each component manages its own connection.

Pros:

  • Simple, self-contained
  • Component controls lifecycle
  • Easy to reason about

Cons:

  • Multiple connections for same data
  • Resource waste
  • Reconnection complexity

Implementation:

class TerraphimChat extends HTMLElement {
  connectedCallback() {
    this.ws = new WebSocket(this.wsUrl);
    this.ws.onmessage = (e) => this.handleMessage(e);
    this.ws.onerror = () => this.reconnect();
  }
  
  disconnectedCallback() {
    this.ws?.close();
  }
}

Pattern 2: Shared Connection Service

Singleton service manages connections, components subscribe.

Pros:

  • Single connection shared
  • Centralized error handling
  • Efficient resource use

Cons:

  • Need pub/sub system
  • State management complexity
  • Service lifecycle management

Implementation:

// Singleton service
class WebSocketService {
  subscribe(channel, handler) { ... }
  unsubscribe(channel, handler) { ... }
}

// Component uses service
class TerraphimChat extends HTMLElement {
  connectedCallback() {
    this.handler = (msg) => this.handleMessage(msg);
    wsService.subscribe('chat', this.handler);
  }
  
  disconnectedCallback() {
    wsService.unsubscribe('chat', this.handler);
  }
}

Pattern 3: Custom Element with Connection Attribute

Parent manages connection, children receive via attribute/property.

Pros:

  • Flexible composition
  • Parent controls connection
  • Children remain simple

Cons:

  • Tight coupling
  • Complex prop passing

Implementation:

<terraphim-connection url="ws://...">
  <terraphim-chat></terraphim-chat>
  <terraphim-notifications></terraphim-notifications>
</terraphim-connection>

Lifecycle Management

Connection States

  1. Disconnected - Initial state
  2. Connecting - Establishing connection
  3. Connected - Active connection
  4. Reconnecting - Attempting to reconnect
  5. Failed - Permanent failure

State Machine

Disconnected -> Connecting -> Connected
                     ↓             ↓
                  Failed      Reconnecting
                                   ↓
                              Connected

Reconnection Strategy

  • Exponential backoff: 1s, 2s, 4s, 8s, 16s, max 30s
  • Max retries: Configurable (default: infinite)
  • Jitter: Random delay to prevent thundering herd
  • Health check: Ping/pong to detect stale connections

Memory Leak Prevention

Common Pitfalls:

  1. Not closing connections on component removal
  2. Event listener leaks - not removing listeners
  3. Circular references - component refs in closures
  4. Pending reconnect timers - not clearing timeouts

Solutions:

class TerraphimChat extends HTMLElement {
  disconnectedCallback() {
    // Close connection
    this.ws?.close();
    
    // Clear timers
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
    }
    
    // Remove listeners
    this.ws = null;
  }
}

Tauri Fallback Strategy

Problem:

Tauri doesn't support EventSource (SSE), needs polling fallback.

Solution:

Detect environment and use appropriate strategy.

class TerraphimSearch extends HTMLElement {
  startStreaming() {
    if (this.isTauri) {
      this.startPolling();
    } else {
      this.startSSE();
    }
  }
  
  startPolling() {
    this.pollInterval = setInterval(
      () => this.fetchUpdate(),
      2000
    );
  }
  
  startSSE() {
    this.eventSource = new EventSource(this.url);
    this.eventSource.onmessage = (e) => this.handleUpdate(e);
  }
}

Features to Maintain

Chat WebSocket:

  • Real-time message delivery
  • Connection state indicators
  • Automatic reconnection
  • Message queuing during disconnect
  • Error handling

Search SSE:

  • Real-time summarization updates
  • Fallback to polling (Tauri)
  • Connection health monitoring
  • Graceful degradation

New Requirements:

  • Memory leak prevention
  • Multiple component instances
  • Configurable reconnection
  • Connection sharing (optional)
  • Observable state

Technical Challenges

Challenge 1: Reconnection UX

Problem: How to indicate reconnecting state to user?
Solution:

  • Emit custom events for state changes
  • Parent can show notification/banner
  • Component-level indicators

Challenge 2: Message Ordering

Problem: Messages may arrive out of order during reconnect
Solution:

  • Sequence numbers
  • Message buffering
  • Conflict resolution

Challenge 3: Shared State

Problem: Multiple components need same stream data
Solution:

  • Pub/sub pattern
  • State management integration
  • Observable streams

Challenge 4: Testing

Problem: Hard to test WebSocket/SSE behavior
Solution:

  • Mock WebSocket/EventSource
  • Dependency injection
  • Simulate network conditions

Acceptance Criteria

  • Connection management pattern selected
  • Lifecycle hooks documented
  • Reconnection strategy implemented
  • Memory leak prevention verified
  • Tauri polling fallback tested
  • State machine documented
  • Error handling comprehensive
  • Prototype with WebSocket + SSE

References

Documentation

Findings will be documented in: .docs/research-websocket-patterns.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    researchResearch and investigation tasksweb-componentsWeb Components migration

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions