@molecule/app-status-dashboard-http

Provider bond · status-dashboard · App (browser) · v1.0.2 · Apache-2.0

HTTP status dashboard provider

npm install @molecule/app-status-dashboard-http

npm · Source on GitHub · Implements @molecule/app-status-dashboard

How it works

@molecule/app-status-dashboard-http is a provider bond on the app (browser) side: it implements the status-dashboard core interface (@molecule/app-status-dashboard) with a concrete vendor or library behind it.

Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.

import { setProvider } from '@molecule/app-status-dashboard'
import { createProvider } from '@molecule/app-status-dashboard-http'

setProvider(createProvider({ baseUrl: '/api' }))
// Now GET /api/status, /api/status/incidents?status=&limit=,
// /api/status/uptime?serviceId= back the dashboard.

Works with: @molecule/app-i18n

Reference

HTTP status dashboard provider for molecule.dev.

Implements StatusDashboardProvider from @molecule/app-status-dashboard by fetching status data from the backend over plain HTTP, with automatic polling. Pairs with @molecule/api-resource-status-page, which serves the expected endpoints: GET /status, GET /status/incidents, and GET /status/uptime.

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Quick Start

import { setProvider } from '@molecule/app-status-dashboard'
import { createProvider } from '@molecule/app-status-dashboard-http'

setProvider(createProvider({ baseUrl: '/api' }))
// Now GET /api/status, /api/status/incidents?status=&limit=,
// /api/status/uptime?serviceId= back the dashboard.

Type

provider

Installation

npm install @molecule/app-status-dashboard-http @molecule/app-i18n @molecule/app-status-dashboard

API

Interfaces

HttpStatusDashboardConfig

Configuration for http status dashboard provider.

interface HttpStatusDashboardConfig {
  /** Base URL for API requests. Defaults to '' (same origin). */
  baseUrl?: string
  /** Custom headers to include in requests. */
  headers?: Record<string, string>
}

Classes

HttpStatusDashboardProvider

HTTP-based implementation of StatusDashboardProvider. Fetches system status, incidents, and uptime data from backend API endpoints via standard HTTP requests.

class HttpStatusDashboardProvider implements StatusDashboardProvider {
  readonly name = 'http'
  private config: HttpStatusDashboardConfig
  private pollingTimers: Set<ReturnType<typeof setInterval>> = new Set()

  constructor(config: HttpStatusDashboardConfig = {}) {
    this.config = config
  }

  /**
   * Fetches the current system status from the API.
   * @param config - Status dashboard configuration including API base URL and headers.
   * @returns The current system status including services and active incidents.
   */
  async fetchStatus(config: StatusDashboardConfig): Promise<SystemStatus> {
    const url = `${config.apiBaseUrl ?? this.config.baseUrl ?? ''}/status`
    const response = await fetch(url, { headers: { ...this.config.headers, ...config.headers } })
    if (!response.ok) {
      throw new Error(
        t(
          'statusDashboard.error.fetchFailed',
          { status: response.status },
          {
            defaultValue: 'Failed to fetch status: HTTP {{status}}',
          },
        ),
      )
    }
    return (await response.json()) as SystemStatus
  }

  /**
   * Fetches recent incidents from the API with optional filtering.
   * @param config - Status dashboard configuration including API base URL and headers.
   * @param options - Optional filters for incident status and result limit.
   * @param options.status - Filter incidents by status.
   * @param options.limit - Maximum number of incidents to return.
   * @returns An array of status incidents, or an empty array if the request fails.
   */
  async fetchIncidents(
    config: StatusDashboardConfig,
    options?: { status?: IncidentStatus; limit?: number },
  ): Promise<StatusIncident[]> {
    const params = new URLSearchParams()
    if (options?.status) params.set('status', options.status)
    if (options?.limit) params.set('limit', String(options.limit))
    const qs = params.toString()
    const url = `${config.apiBaseUrl ?? this.config.baseUrl ?? ''}/status/incidents${qs ? `?${qs}` : ''}`
    const response = await fetch(url, { headers: { ...this.config.headers, ...config.headers } })
    if (!response.ok) return []
    const data = (await response.json()) as { incidents?: StatusIncident[] }
    return data.incidents ?? []
  }

  /**
   * Fetches uptime data for all services or a specific service.
   * @param config - Status dashboard configuration including API base URL and headers.
   * @param serviceId - Optional service ID to filter uptime data.
   * @returns An array of service uptime records, or an empty array if the request fails.
   */
  async fetchUptime(config: StatusDashboardConfig, serviceId?: string): Promise<ServiceUptime[]> {
    const params = new URLSearchParams()
    if (serviceId) params.set('serviceId', serviceId)
    const qs = params.toString()
    const url = `${config.apiBaseUrl ?? this.config.baseUrl ?? ''}/status/uptime${qs ? `?${qs}` : ''}`
    const response = await fetch(url, { headers: { ...this.config.headers, ...config.headers } })
    if (!response.ok) return []
    const data = (await response.json()) as { uptime?: ServiceUptime[] }
    return data.uptime ?? []
  }

  /**
   * Starts polling for status updates at the configured interval.
   * Fetches immediately and then at regular intervals.
   * @param config - Status dashboard configuration including poll interval.
   * @param onUpdate - Callback invoked with the latest system status on each successful poll.
   * @returns A stop function that cancels this polling subscription.
   */
  startPolling(
    config: StatusDashboardConfig,
    onUpdate: (status: SystemStatus) => void,
  ): () => void {
    const intervalMs = config.pollIntervalMs ?? 30000
    const timer = setInterval(async () => {
      try {
        const status = await this.fetchStatus(config)
        onUpdate(status)
      } catch (_error) {
        // Best-effort poll — network blips are transient; the next interval will retry.
      }
    }, intervalMs)
    this.pollingTimers.add(timer)
    // Fetch immediately
    this.fetchStatus(config)
      .then(onUpdate)
      .catch((_error) => {
        // Best-effort immediate fetch — the polling interval will retry shortly.
      })
    return () => {
      clearInterval(timer)
      this.pollingTimers.delete(timer)
    }
  }

  /**
   * Stops all active polling timers.
   */
  stopPolling(): void {
    for (const timer of this.pollingTimers) {
      clearInterval(timer)
    }
    this.pollingTimers.clear()
  }
}

Functions

createProvider(config)

Creates an HttpStatusDashboardProvider instance with optional configuration.

function createProvider(config?: HttpStatusDashboardConfig): HttpStatusDashboardProvider
  • config — HTTP-specific status dashboard configuration (base URL, headers).

Returns: An HttpStatusDashboardProvider that communicates with the backend via HTTP.

Constants

provider

Pre-instantiated provider singleton.

const provider: HttpStatusDashboardProvider

Core Interface

Implements @molecule/app-status-dashboard interface.

Bond Wiring

Setup function to register this provider with the core interface:

import { setProvider } from '@molecule/app-status-dashboard'
import { provider } from '@molecule/app-status-dashboard-http'

export function setupStatusDashboardHttp(): void {
  setProvider(provider)
}

Injection Notes

Requirements

Peer dependencies:

  • @molecule/app-status-dashboard ^1.0.1
  • @molecule/app-i18n ^1.0.1

Runtime Dependencies

  • @molecule/app-i18n

  • @molecule/app-status-dashboard

  • The backend must serve the three endpoints (/status, /status/incidents, /status/uptime) relative to baseUrl — responses are SystemStatus, { incidents: StatusIncident[] }, and { uptime: ServiceUptime[] } respectively. @molecule/api-resource-status-page provides them.

  • Error semantics differ per method: fetchStatus() throws on non-2xx; fetchIncidents()/fetchUptime() return [] on ANY failure — an empty incidents list can mean "endpoint missing", not "all clear". Verify /status works first.

  • Polling defaults to every 30 s (pollIntervalMs), fetches immediately, and swallows per-tick errors (next tick retries). Keep the stop function returned by startPolling() and call it on unmount; stopPolling() cancels everything.

  • Per-call config.apiBaseUrl/headers override/merge over the constructor's baseUrl/headers.

E2E Tests

Integration checklist — drive the real status page in the live preview (no mocks), adapt each item to this app's actual screens, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:

  • Every monitored service returned by fetchStatus renders as its own tile/row showing the service name and a status indicator, grouped by groupName when present — no service silently missing from the page.
  • Each status indicator reflects that service's real ServiceStatus: an operational service shows the up/green treatment, down shows the down/red treatment, and degraded/unknown each show their own distinct state — never one uniform color regardless of status.
  • The overall system-status banner is DERIVED from the tiles, not hardcoded: with all services operational it reads operational; flip one service to down in the data and the banner flips to down/degraded on the next fetch — it never stays green while a tile is red.
  • Metric values render as real numbers with units — service latency as e.g. 142 ms and each UptimeWindow as a percentage (e.g. 99.98%) for the selected window (1h/24h/7d/30d/90d) — not 0, NaN, or a placeholder.
  • Active incidents render with title, severity, and status; when there are none the page shows an all-clear/empty state, not a blank or broken incidents area.
  • Polling updates the tiles without a manual reload: with startPolling running, change the underlying status and confirm the affected tile AND the overall banner update on the next poll; navigating away calls the returned stop function so polls don't pile up.
  • Loading and failure are observable: a loading indicator shows while fetchStatus is in flight, and a fetch error surfaces a visible message (from state.error) — never a blank page or a stale dashboard shown as fresh.