/** * Convenience factory for web dashboard diagnostics. * * Eliminates ~40 lines of boilerplate per web app by wrapping * DiagnosticsClient.getInstance() with sensible web defaults. * * @example * ```ts * import { createWebDiagnostics } from '@bytelyst/diagnostics-client'; * * const { init, stop } = createWebDiagnostics({ * productId: 'nomgap', * channel: 'nomgap_web', * serverUrl: 'http://localhost:4003', * getAuthToken: () => localStorage.getItem('nomgap_access_token') ?? '', * }); * export { init as initDiagnostics, stop as stopDiagnostics }; * ``` */ import { DiagnosticsClient } from './client.js'; export interface WebDiagnosticsConfig { /** Product identifier (e.g. 'nomgap', 'chronomind'). */ productId: string; /** Channel identifier (e.g. 'nomgap_web', 'pwa'). */ channel: string; /** Platform-service origin URL (no trailing /api). */ serverUrl: string; /** Function that returns the current auth token. */ getAuthToken: () => string; /** App version string. Default: '0.1.0'. */ appVersion?: string; /** Build number. Default: '1'. */ buildNumber?: string; /** Release channel. Default: 'dev'. */ releaseChannel?: string; /** OS family. Default: 'unknown'. */ osFamily?: string; /** Poll interval in ms. Default: 30000. */ pollIntervalMs?: number; /** Capture console logs. Default: false. */ captureConsole?: boolean; /** Capture JS errors. Default: true. */ captureErrors?: boolean; /** Capture network requests. Default: false. */ captureNetwork?: boolean; } export interface WebDiagnostics { /** Initialize diagnostics. Safe to call on server (no-ops). Idempotent. */ init(): void; /** Stop diagnostics polling. */ stop(): void; } function getOrCreateInstallId(productId: string): string { const key = `${productId}_diag_install_id`; let id = localStorage.getItem(key); if (!id) { id = typeof crypto?.randomUUID === 'function' ? crypto.randomUUID() : Math.random().toString(36).slice(2) + Date.now().toString(36); localStorage.setItem(key, id); } return id; } export function createWebDiagnostics(config: WebDiagnosticsConfig): WebDiagnostics { let started = false; function init(): void { if (typeof window === 'undefined') return; if (started) return; DiagnosticsClient.getInstance({ productId: config.productId, anonymousInstallId: getOrCreateInstallId(config.productId), platform: 'web', channel: config.channel, osFamily: config.osFamily ?? 'unknown', appVersion: config.appVersion ?? '0.1.0', buildNumber: config.buildNumber ?? '1', releaseChannel: config.releaseChannel ?? 'dev', serverUrl: config.serverUrl, getAuthToken: config.getAuthToken, pollIntervalMs: config.pollIntervalMs ?? 30_000, captureConsole: config.captureConsole ?? false, captureErrors: config.captureErrors ?? true, captureNetwork: config.captureNetwork ?? false, }); DiagnosticsClient.getInstance() .start() .catch(() => { // Diagnostics is best-effort }); started = true; } function stop(): void { try { DiagnosticsClient.getInstance().stop(); } catch { // not initialized } } return { init, stop }; }