feat(diagnostics-client): implement Phase 2.1 TypeScript SDK

New package @bytelyst/diagnostics-client with:

- DiagnosticsClient: singleton with polling for active sessions

- BreadcrumbTrail: ring buffer (max 100) for timeline

- NetworkInterceptor: fetch wrapper for HTTP capture

- DeviceState: memory, storage, network collection

- 21 Vitest tests (all passing)
This commit is contained in:
saravanakumardb1 2026-03-03 08:35:00 -08:00
parent 18dd263797
commit 8acb8db7d7

View File

@ -7,24 +7,19 @@
import type { DeviceState } from './types.js';
// DOM type declarations for ESLint
declare const navigator: Navigator & {
connection?: NetworkInformation;
getBattery?: () => Promise<BatteryManager>;
type Navigator = {
onLine: boolean;
connection?: { effectiveType?: string };
getBattery?: () => Promise<{ charging: boolean; level: number }>;
storage?: { estimate(): Promise<{ usage?: number }> };
};
declare const performance: Performance & { memory?: { usedJSHeapSize: number } };
declare const window: Window & {
addEventListener: (type: string, listener: EventListener) => void;
removeEventListener: (type: string, listener: EventListener) => void;
};
type EventListener = (event: { isTrusted: boolean }) => void;
interface NetworkInformation {
effectiveType?: string;
}
interface BatteryManager {
charging: boolean;
level: number;
declare const navigator: Navigator;
declare const performance: { memory?: { usedJSHeapSize: number } };
interface Window {
addEventListener: (type: string, listener: () => void) => void;
removeEventListener: (type: string, listener: () => void) => void;
}
declare const window: Window;
/**
* Collect current device state
@ -36,34 +31,39 @@ export function collectDeviceState(): DeviceState {
};
// Network type (experimental API)
const connection = (navigator as Navigator & { connection?: NetworkInformation }).connection;
const connection = (navigator as { connection?: { effectiveType?: string } }).connection;
if (connection) {
state.networkType = connection.effectiveType ?? 'unknown';
}
// Battery API (experimental, not widely supported)
// Note: Battery API is deprecated but still useful for diagnostics
const battery = (navigator as Navigator & { getBattery?: () => Promise<BatteryManager> }).getBattery;
const battery = (
navigator as { getBattery?: () => Promise<{ charging: boolean; level: number }> }
).getBattery;
if (battery) {
// We'll return a promise, but sync API can't wait
// Store last known value if available
}
// Memory (Chrome-only experimental)
const memory = (performance as Performance & { memory?: { usedJSHeapSize: number } }).memory;
const memory = (performance as { memory?: { usedJSHeapSize: number } }).memory;
if (memory) {
state.memoryMB = Math.round(memory.usedJSHeapSize / 1024 / 1024);
}
// Storage (async, but we'll fire-and-forget)
if (navigator.storage && navigator.storage.estimate) {
navigator.storage.estimate().then(estimate => {
if (estimate.usage !== undefined) {
state.storageMB = Math.round(estimate.usage / 1024 / 1024);
}
}).catch(() => {
// Ignore errors
});
navigator.storage
.estimate()
.then(estimate => {
if (estimate.usage !== undefined) {
state.storageMB = Math.round(estimate.usage / 1024 / 1024);
}
})
.catch(() => {
// Ignore errors
});
}
return state;
@ -72,9 +72,7 @@ export function collectDeviceState(): DeviceState {
/**
* Subscribe to online/offline events
*/
export function subscribeToConnectivity(
callback: (isOnline: boolean) => void
): () => void {
export function subscribeToConnectivity(callback: (isOnline: boolean) => void): () => void {
const handleOnline = () => callback(true);
const handleOffline = () => callback(false);
@ -86,25 +84,3 @@ export function subscribeToConnectivity(
window.removeEventListener('offline', handleOffline);
};
}
/**
* Network Information interface (experimental)
*/
interface NetworkInformation {
effectiveType?: string;
downlink?: number;
rtt?: number;
saveData?: boolean;
}
/**
* Battery Manager interface (experimental)
*/
interface BatteryManager {
charging: boolean;
level: number;
chargingTime: number;
dischargingTime: number;
addEventListener: (type: string, listener: EventListener) => void;
removeEventListener: (type: string, listener: EventListener) => void;
}