learning_ai_common_plat/packages/diagnostics-client/src/web.ts
saravanakumardb1 b9e37bb8b4 feat(telemetry-client,diagnostics-client): add createWebTelemetry + createWebDiagnostics convenience factories
- telemetry-client: createWebTelemetry() wraps createTelemetryClient() with web defaults
- diagnostics-client: createWebDiagnostics() wraps DiagnosticsClient with install ID + web defaults
- Eliminates ~70 lines of boilerplate per web app
- 11/11 telemetry tests pass, 21/21 diagnostics tests pass
2026-03-20 18:47:18 -07:00

112 lines
3.2 KiB
TypeScript

/**
* 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 };
}