@nolag/dash

Live dashboards with real-time metrics, time-series aggregation, and widgets.

Overview

@nolag/dash powers live operational dashboards with streaming metrics and interactive widgets. Agents or servers publish metric data points and widget state snapshots to panels; viewers connected to the same panel receive updates instantly and can query rolling aggregations (average, min, max, sum, count) over any time window without hitting a separate time-series database. Your app owns one core NoLag client and injects it into NoLagDash; the wrapper attaches its behaviour to that connection.

Key Features

  • Streaming metric ingestion with per-stream data point buffering
  • Client-side rolling aggregation over configurable time windows
  • Widget state publishing for KPI cards, gauges, tables, and custom types
  • 1-hour replay for both metrics and widgets so new viewers see recent history immediately
  • Tag-based metadata on metrics for multi-dimensional filtering
  • Automatic reconnection with panel restoration

How It Works

NoLagDash attaches to an injected @nolag/js-sdk client. Calling joinPanel() creates a DashboardPanel that subscribes to two topics: metrics for time-series data points and widgets for widget state snapshots. A MetricBuffer inside the panel accumulates data points per stream ID and provides efficient rolling-window aggregation, while a WidgetStore keeps the latest state for each widget keyed by widget ID. The app owns the socket lifecycle; the wrapper never opens or closes it.

TopicPurposeReplay
metricsTime-series data points: value, timestamp, unit, tags1 hour
widgetsWidget state snapshots: type, data payload, last updated1 hour

Installation

npm install @nolag/dash @nolag/js-sdk

One core NoLag client can back several wrapper SDKs at once, for example a dashboard, chat, and notify on a single socket, as long as each wrapper uses a distinct appName. Each wrapper attaches its handlers on construction and releases them with detach(), and never touches the socket itself. Your app owns connect() and disconnect().

Quick Start

import { NoLag } from '@nolag/js-sdk'
import { NoLagDash } from '@nolag/dash'

// The app owns one core client. In a browser, pass a token provider so the
// SDK can mint fresh short-lived client tokens from your backend.
const client = NoLag(async () => (await (await fetch('/api/nolag-token')).json()).token)

// Inject the client into the dash wrapper
const dash = new NoLagDash({ client, username: 'Alice' })

await client.connect()   // the app owns the connection
await dash.ready()       // wrapper setup complete

// Join a dashboard panel
const panel = await dash.joinPanel('ops-overview')

// Publish a metric (typically called from a server or agent)
await panel.publishMetric('cpu.usage', 74.2, {
  unit: 'percent',
  tags: { host: 'web-01', region: 'us-east-1' },
})

// Listen for incoming metrics
panel.on('metric', ({ streamId, value, timestamp, tags }) => {
  console.log(`[${streamId}] ${value} @ ${new Date(timestamp).toISOString()}`)
})

// Get all buffered data points for a metric stream
const points = panel.getMetrics('cpu.usage')
console.log('Data points:', points.length)

// Get a rolling aggregation over the last 5 minutes
const agg = panel.getAggregation('cpu.usage', 5 * 60_000)
console.log('Avg CPU:', agg.avg.toFixed(1) + '%')
console.log('Max CPU:', agg.max.toFixed(1) + '%')

// Publish a widget state (e.g. a status card)
await panel.publishWidget('status-card', 'kpi', {
  label: 'Active Sessions',
  value: 1_248,
  trend: 'up',
})

// Listen for widget updates
panel.on('widgetUpdate', ({ widgetId, type, data }) => {
  console.log(`Widget ${widgetId} (${type}):`, data)
})

// Read current widget state
const widget = panel.getWidget('status-card')

// Teardown: the wrapper releases its handlers; the app closes the socket.
dash.detach()
client.disconnect()

API Reference

NoLagDash

The main class. Attaches to the injected core client and manages the dashboard panel lifecycle.

Constructor Options

OptionTypeDescription
clientNoLagSocketRequired. The injected core NoLag client the app owns and connects.
usernamestringOptional display name for this viewer.
metadataRecord<string, unknown>Optional custom viewer data attached to presence.
appNamestringNoLag app for topic prefixes (default 'dash').
panelsstring[]Panels to subscribe to once the wrapper is ready.
maxMetricPointsnumberMax metric points kept in memory per stream (default 1000).
aggregationWindownumberDefault aggregation window in ms (default 60000).
debugbooleanEnable wrapper debug logging (default false).
MethodDescription
ready()Resolves once wrapper setup completed
detach()Release this wrapper's handlers and topics; terminal, never closes the socket
joinPanel(name)Join a dashboard panel; returns a DashboardPanel instance
leavePanel(name)Leave a panel and unsubscribe from its topics

Events: NoLagDash

EventPayloadDescription
connectednoneWebSocket connection established
disconnectedreason: stringConnection closed
reconnectednoneReconnection successful; panels are restored automatically
errorerror: ErrorUnrecoverable error occurred
viewerOnlineviewer: DashViewerA viewer joined any panel
viewerOfflineviewer: DashViewerA viewer left any panel

DashboardPanel

Returned by joinPanel(). Handles metric ingestion, aggregation, widget state, and per-panel viewer presence.

MethodDescription
publishMetric(streamId, value, opts)Publish a single data point to a metric stream; opts accepts unit, tags, and timestamp
publishWidget(widgetId, type, data)Publish a widget state snapshot; replaces the previous state for that widget ID
getMetrics(streamId)Return all buffered data points for the given metric stream, ordered by timestamp
getAggregation(streamId, windowMs)Return { avg, min, max, sum, count } for all data points within the last windowMs milliseconds
getWidget(widgetId)Return the latest state snapshot for a single widget
getWidgets()Return the latest state snapshots for all widgets in this panel

Events: DashboardPanel

EventPayloadDescription
metric{ streamId: string, value: number, timestamp: number, unit?: string, tags?: Record<string, string> }A new data point arrived for a metric stream
widgetUpdate{ widgetId: string, type: string, data: unknown }A widget state snapshot was updated
viewerJoinedviewer: DashViewerA viewer joined this panel
viewerLeftviewer: DashViewerA viewer left this panel
replayStart{ count: number }Historical data replay is beginning
replayEnd{ replayed: number }Historical data replay is complete