@runtimescope/sdk 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -3
- package/dist/index.d.ts +47 -3
- package/dist/index.global.js +2 -2
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/transport.ts","../src/utils/id.ts","../src/interceptors/fetch.ts","../src/utils/serialize.ts","../src/interceptors/console.ts","../src/interceptors/xhr.ts","../src/interceptors/state-stores.ts","../src/interceptors/performance.ts","../src/interceptors/react-renders.ts","../src/interceptors/errors.ts","../src/interceptors/navigation.ts"],"sourcesContent":["import { Transport } from './transport.js';\nimport { interceptFetch } from './interceptors/fetch.js';\nimport { interceptConsole } from './interceptors/console.js';\nimport { interceptXhr } from './interceptors/xhr.js';\nimport { interceptStateStores } from './interceptors/state-stores.js';\nimport { interceptPerformance } from './interceptors/performance.js';\nimport { interceptReactRenders } from './interceptors/react-renders.js';\nimport { interceptErrors } from './interceptors/errors.js';\nimport { interceptNavigation } from './interceptors/navigation.js';\nimport { generateId, generateSessionId } from './utils/id.js';\nimport type { RuntimeScopeConfig, RuntimeEvent, DomSnapshotEvent, CustomEvent } from './types.js';\n\nconst SDK_VERSION = '0.7.2';\n\n// Save original console.debug BEFORE interceptors patch it.\n// debug-level messages are hidden by default in Chrome DevTools.\nconst _log = console.debug.bind(console);\n\n// --- Double-init safety: Symbol.for() originals store ---\n// Survives HMR module reloads because Symbol.for() returns the same symbol across realms.\n// True originals are saved exactly once — never overwritten by subsequent connect() calls.\nconst ORIGINALS_KEY = Symbol.for('__runtimescope_originals__');\n\ninterface SavedOriginals {\n fetch: typeof globalThis.fetch;\n xhrOpen: typeof XMLHttpRequest.prototype.open;\n xhrSend: typeof XMLHttpRequest.prototype.send;\n xhrSetRequestHeader: typeof XMLHttpRequest.prototype.setRequestHeader;\n consoleMethods: Record<string, (...args: unknown[]) => void>;\n}\n\nfunction getOrSaveOriginals(): SavedOriginals {\n const g = globalThis as Record<symbol, SavedOriginals>;\n if (!g[ORIGINALS_KEY]) {\n g[ORIGINALS_KEY] = {\n fetch: window.fetch,\n xhrOpen: XMLHttpRequest.prototype.open,\n xhrSend: XMLHttpRequest.prototype.send,\n xhrSetRequestHeader: XMLHttpRequest.prototype.setRequestHeader,\n consoleMethods: {\n log: console.log.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n info: console.info.bind(console),\n debug: console.debug.bind(console),\n trace: console.trace.bind(console),\n },\n };\n }\n return g[ORIGINALS_KEY];\n}\n\nexport class RuntimeScope {\n private static transport: Transport | null = null;\n private static restoreFns: (() => void)[] = [];\n private static _sessionId: string | null = null;\n private static _state: 'stopped' | 'started' = 'stopped';\n\n static get sessionId(): string | null {\n return this._sessionId;\n }\n\n /** Returns true if the SDK is currently connected */\n static get isConnected(): boolean {\n return this._state === 'started';\n }\n\n static connect(config: RuntimeScopeConfig = {}): void {\n if (config.enabled === false) return;\n\n // Guard against double-init: disconnect cleanly first\n if (this._state === 'started') {\n _log('[RuntimeScope] Already connected — disconnecting before re-init');\n this.disconnect();\n }\n\n const resolved = {\n serverUrl: config.serverUrl ?? config.endpoint ?? 'ws://localhost:9090',\n appName: config.appName ?? 'unknown',\n captureNetwork: config.captureNetwork ?? true,\n captureConsole: config.captureConsole ?? true,\n captureXhr: config.captureXhr ?? true,\n captureBody: config.captureBody ?? false,\n maxBodySize: config.maxBodySize ?? 65536,\n capturePerformance: config.capturePerformance ?? true,\n captureRenders: config.captureRenders ?? true,\n captureNavigation: config.captureNavigation ?? true,\n stores: config.stores ?? {},\n beforeSend: config.beforeSend,\n redactHeaders: config.redactHeaders ?? ['authorization', 'cookie'],\n batchSize: config.batchSize ?? 50,\n flushIntervalMs: config.flushIntervalMs ?? 100,\n };\n\n // Save true originals before any patching (idempotent — only saves on first call ever)\n getOrSaveOriginals();\n\n this._sessionId = generateSessionId();\n this._state = 'started';\n\n this.transport = new Transport({\n serverUrl: resolved.serverUrl,\n appName: resolved.appName,\n sessionId: this._sessionId,\n sdkVersion: SDK_VERSION,\n authToken: config.authToken,\n batchSize: resolved.batchSize,\n flushIntervalMs: resolved.flushIntervalMs,\n });\n\n this.transport.connect();\n _log(`[RuntimeScope] SDK v${SDK_VERSION} initializing — app: ${resolved.appName}, server: ${resolved.serverUrl}`);\n\n const emit = (event: RuntimeEvent) => this.transport?.send(event);\n\n // Send session event\n emit({\n eventId: generateId(),\n sessionId: this._sessionId,\n timestamp: Date.now(),\n eventType: 'session',\n appName: resolved.appName,\n connectedAt: Date.now(),\n sdkVersion: SDK_VERSION,\n buildMeta: config.buildMeta,\n });\n\n // Register command handler for server→SDK commands\n const sessionId = this._sessionId;\n this.transport.onCommand((cmd: { command: string; requestId: string; params?: Record<string, unknown> }) => {\n this.handleCommand(cmd, emit, sessionId);\n });\n\n // Fetch interceptor\n if (resolved.captureNetwork) {\n this.restoreFns.push(\n interceptFetch(emit, this._sessionId, resolved.redactHeaders, {\n captureBody: resolved.captureBody,\n maxBodySize: resolved.maxBodySize,\n beforeSend: resolved.beforeSend,\n })\n );\n }\n\n // XHR interceptor\n if (resolved.captureXhr) {\n this.restoreFns.push(\n interceptXhr(emit, this._sessionId, resolved.redactHeaders, {\n captureBody: resolved.captureBody,\n maxBodySize: resolved.maxBodySize,\n beforeSend: resolved.beforeSend,\n })\n );\n }\n\n // Console interceptor\n if (resolved.captureConsole) {\n this.restoreFns.push(\n interceptConsole(emit, this._sessionId, resolved.beforeSend)\n );\n // Also capture uncaught errors, resource load failures, and unhandled rejections\n // These appear in DevTools console but don't go through console.* API\n this.restoreFns.push(\n interceptErrors(emit, this._sessionId, resolved.beforeSend)\n );\n }\n\n // State store inspection\n if (Object.keys(resolved.stores).length > 0) {\n this.restoreFns.push(\n interceptStateStores(emit, this._sessionId, resolved.stores, {\n beforeSend: resolved.beforeSend,\n })\n );\n }\n\n // Performance metrics (Web Vitals)\n if (resolved.capturePerformance) {\n this.restoreFns.push(\n interceptPerformance(emit, this._sessionId, {\n beforeSend: resolved.beforeSend,\n })\n );\n }\n\n // React render tracking\n if (resolved.captureRenders) {\n this.restoreFns.push(\n interceptReactRenders(emit, this._sessionId, {\n beforeSend: resolved.beforeSend,\n })\n );\n }\n\n // Navigation / routing events (pushState, popstate, hashchange)\n if (resolved.captureNavigation) {\n this.restoreFns.push(\n interceptNavigation(emit, this._sessionId)\n );\n }\n\n const features = [\n resolved.captureNetwork && 'fetch',\n resolved.captureXhr && 'xhr',\n resolved.captureConsole && 'console, errors',\n Object.keys(resolved.stores).length > 0 && 'state',\n resolved.capturePerformance && 'performance',\n resolved.captureRenders && 'renders',\n resolved.captureNavigation && 'navigation',\n ].filter(Boolean);\n _log(`[RuntimeScope] Interceptors active — ${features.join(', ')}`);\n }\n\n private static handleCommand(\n cmd: { command: string; requestId: string; params?: Record<string, unknown> },\n emit: (event: RuntimeEvent) => void,\n sessionId: string\n ): void {\n switch (cmd.command) {\n case 'capture_dom_snapshot': {\n const maxSize = (cmd.params?.maxSize as number) ?? 500_000;\n let html = document.documentElement.outerHTML;\n const truncated = html.length > maxSize;\n if (truncated) html = html.slice(0, maxSize);\n\n const snapshot: DomSnapshotEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'dom_snapshot',\n html,\n url: window.location.href,\n viewport: { width: window.innerWidth, height: window.innerHeight },\n scrollPosition: { x: window.scrollX, y: window.scrollY },\n elementCount: document.querySelectorAll('*').length,\n truncated,\n };\n\n emit(snapshot);\n this.transport?.sendCommandResponse(cmd.requestId, cmd.command, snapshot);\n break;\n }\n default:\n this.transport?.sendCommandResponse(cmd.requestId, cmd.command, { error: 'Unknown command' });\n }\n }\n\n /** Alias for `connect` — used by script-tag snippets */\n static init(config: RuntimeScopeConfig = {}): void {\n return this.connect(config);\n }\n\n /**\n * Track a custom business/product event.\n * Use this to mark meaningful moments in your app (e.g. user actions, conversions).\n * These events are correlated with all other telemetry (network, console, state)\n * to build causal chains and detect failures.\n */\n static track(name: string, properties?: Record<string, unknown>): void {\n if (!this.transport || !this._sessionId) return;\n\n const event: CustomEvent = {\n eventId: generateId(),\n sessionId: this._sessionId,\n timestamp: Date.now(),\n eventType: 'custom',\n name,\n properties,\n };\n\n this.transport.send(event);\n }\n\n static disconnect(): void {\n // Run all interceptor restore functions\n for (const fn of this.restoreFns) {\n try { fn(); } catch { /* ensure all restores run */ }\n }\n this.restoreFns = [];\n\n // Safety net: if restore functions didn't fully unwind (e.g. another library\n // patched over us), fall back to true originals saved via Symbol.for()\n const originals = (globalThis as Record<symbol, SavedOriginals>)[ORIGINALS_KEY];\n if (originals) {\n // Only restore if current value is NOT the original (i.e. we or nobody else patched)\n if (window.fetch !== originals.fetch) {\n window.fetch = originals.fetch;\n }\n for (const [level, fn] of Object.entries(originals.consoleMethods)) {\n const c = console as unknown as Record<string, unknown>;\n if (c[level] !== fn) {\n c[level] = fn;\n }\n }\n }\n\n this.transport?.disconnect();\n this.transport = null;\n this._sessionId = null;\n this._state = 'stopped';\n }\n}\n\nexport default RuntimeScope;\nexport type {\n RuntimeScopeConfig,\n BuildMeta,\n NetworkEvent,\n ConsoleEvent,\n SessionEvent,\n StateEvent,\n RenderEvent,\n DomSnapshotEvent,\n PerformanceEvent,\n NavigationEvent,\n CustomEvent,\n RuntimeEvent,\n} from './types.js';\n","import type { RuntimeEvent } from './types.js';\n\n// Save original console.debug BEFORE any interceptors patch it.\n// debug-level messages are hidden by default in Chrome DevTools.\nconst _log = console.debug.bind(console);\n\ninterface TransportConfig {\n serverUrl: string;\n appName: string;\n sessionId: string;\n sdkVersion: string;\n authToken?: string;\n batchSize: number;\n flushIntervalMs: number;\n}\n\nexport class Transport {\n private ws: WebSocket | null = null;\n private batch: RuntimeEvent[] = [];\n private offlineQueue: RuntimeEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private reconnectDelay = 500;\n private reconnectAttempt = 0;\n private connected = false;\n private stopped = false;\n private config: TransportConfig;\n private commandHandler: ((cmd: { command: string; requestId: string; params?: Record<string, unknown> }) => void) | null = null;\n private hasEverConnected = false;\n private connectionWarningTimer: ReturnType<typeof setTimeout> | null = null;\n private visibilityHandler: (() => void) | null = null;\n private onlineHandler: (() => void) | null = null;\n\n private static readonly MAX_OFFLINE_QUEUE = 1000;\n private static readonly MAX_RECONNECT_DELAY = 30_000;\n private static readonly CONNECTION_WARNING_DELAY = 10_000;\n /** First N retries use a fast 500ms delay before switching to exponential backoff */\n private static readonly FAST_RETRY_COUNT = 3;\n private static readonly FAST_RETRY_DELAY = 500;\n\n constructor(config: TransportConfig) {\n this.config = config;\n }\n\n connect(): void {\n this.stopped = false;\n this.doConnect();\n\n // Warn if we never establish a connection within 10 seconds\n this.connectionWarningTimer = setTimeout(() => {\n if (!this.hasEverConnected && !this.stopped) {\n console.warn(\n `[RuntimeScope] SDK has not connected to ${this.config.serverUrl} after 10s. ` +\n `Is the collector running? Start it with: npx @runtimescope/collector`\n );\n }\n }, Transport.CONNECTION_WARNING_DELAY);\n\n // When the user switches back to this tab, try to reconnect immediately\n if (typeof document !== 'undefined') {\n this.visibilityHandler = () => {\n if (document.visibilityState === 'visible' && !this.connected && !this.stopped) {\n _log('[RuntimeScope] Tab visible — attempting immediate reconnect');\n this.resetReconnectState();\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.doConnect();\n }\n };\n document.addEventListener('visibilitychange', this.visibilityHandler);\n }\n\n // Reconnect immediately when network comes back online\n if (typeof window !== 'undefined') {\n this.onlineHandler = () => {\n if (!this.connected && !this.stopped) {\n _log('[RuntimeScope] Network online — attempting immediate reconnect');\n this.resetReconnectState();\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.doConnect();\n }\n };\n window.addEventListener('online', this.onlineHandler);\n }\n }\n\n private doConnect(): void {\n if (this.stopped) return;\n\n try {\n this.ws = new WebSocket(this.config.serverUrl);\n } catch {\n this.scheduleReconnect();\n return;\n }\n\n this.ws.onmessage = (event: MessageEvent) => {\n try {\n const msg = JSON.parse(String(event.data));\n if (msg.type === 'command' && msg.payload && this.commandHandler) {\n this.commandHandler(msg.payload);\n }\n // Handle server restart notice — reset backoff for immediate reconnect\n if (msg.type === '__server_restart') {\n _log('[RuntimeScope] Server restart notice received — will reconnect immediately');\n this.resetReconnectState();\n }\n // Handle auth rejection — stop reconnecting\n if (msg.type === 'error' && msg.payload?.code === 'AUTH_FAILED') {\n _log('[RuntimeScope] Authentication failed — stopping reconnection');\n this.stopped = true;\n }\n } catch {\n // Ignore unparseable messages\n }\n };\n\n this.ws.onopen = () => {\n this.connected = true;\n this.hasEverConnected = true;\n this.resetReconnectState();\n _log(`[RuntimeScope] Connected to ${this.config.serverUrl}`);\n\n // Send handshake\n this.sendRaw({\n type: 'handshake',\n payload: {\n appName: this.config.appName,\n sdkVersion: this.config.sdkVersion,\n sessionId: this.config.sessionId,\n ...(this.config.authToken ? { authToken: this.config.authToken } : {}),\n },\n timestamp: Date.now(),\n sessionId: this.config.sessionId,\n });\n\n // Flush offline queue\n if (this.offlineQueue.length > 0) {\n const queued = this.offlineQueue.splice(0);\n for (const event of queued) {\n this.batch.push(event);\n }\n this.flush();\n }\n\n // Start periodic flush\n this.flushTimer = setInterval(() => this.flush(), this.config.flushIntervalMs);\n };\n\n this.ws.onclose = () => {\n this.connected = false;\n this.clearFlushTimer();\n if (!this.stopped) {\n _log(`[RuntimeScope] Disconnected, will reconnect...`);\n this.scheduleReconnect();\n }\n };\n\n this.ws.onerror = () => {\n _log(`[RuntimeScope] WebSocket error connecting to ${this.config.serverUrl}`);\n };\n }\n\n send(event: RuntimeEvent): void {\n if (this.connected) {\n this.batch.push(event);\n if (this.batch.length >= this.config.batchSize) {\n this.flush();\n }\n } else {\n // Queue for when we reconnect\n if (this.offlineQueue.length < Transport.MAX_OFFLINE_QUEUE) {\n this.offlineQueue.push(event);\n }\n }\n }\n\n private flush(): void {\n if (this.batch.length === 0 || !this.connected || !this.ws) return;\n\n const events = this.batch.splice(0);\n this.sendRaw({\n type: 'event',\n payload: { events },\n timestamp: Date.now(),\n sessionId: this.config.sessionId,\n });\n }\n\n private sendRaw(msg: unknown): void {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n try {\n this.ws.send(JSON.stringify(msg));\n } catch {\n // Drop silently — reconnect will handle it\n }\n }\n }\n\n private scheduleReconnect(): void {\n if (this.stopped || this.reconnectTimer) return;\n\n this.reconnectAttempt++;\n\n // Fast retries for the first few attempts (collector restart gap is typically 1-3s)\n let delay: number;\n if (this.reconnectAttempt <= Transport.FAST_RETRY_COUNT) {\n delay = Transport.FAST_RETRY_DELAY;\n } else {\n // Exponential backoff with jitter after fast retries exhausted\n const jitter = this.reconnectDelay * 0.25 * (Math.random() * 2 - 1);\n delay = Math.min(this.reconnectDelay + jitter, Transport.MAX_RECONNECT_DELAY);\n this.reconnectDelay = Math.min(this.reconnectDelay * 2, Transport.MAX_RECONNECT_DELAY);\n }\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.doConnect();\n }, delay);\n }\n\n private resetReconnectState(): void {\n this.reconnectDelay = 500;\n this.reconnectAttempt = 0;\n }\n\n private clearFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n }\n\n onCommand(handler: (cmd: { command: string; requestId: string; params?: Record<string, unknown> }) => void): void {\n this.commandHandler = handler;\n }\n\n sendCommandResponse(requestId: string, command: string, payload: unknown): void {\n this.sendRaw({\n type: 'command_response',\n requestId,\n command,\n payload,\n timestamp: Date.now(),\n sessionId: this.config.sessionId,\n });\n }\n\n disconnect(): void {\n this.stopped = true;\n this.clearFlushTimer();\n\n if (this.connectionWarningTimer) {\n clearTimeout(this.connectionWarningTimer);\n this.connectionWarningTimer = null;\n }\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.visibilityHandler && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', this.visibilityHandler);\n this.visibilityHandler = null;\n }\n\n if (this.onlineHandler && typeof window !== 'undefined') {\n window.removeEventListener('online', this.onlineHandler);\n this.onlineHandler = null;\n }\n\n // Final flush before closing\n this.flush();\n\n if (this.ws) {\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.close();\n this.ws = null;\n }\n\n this.connected = false;\n this.batch = [];\n this.offlineQueue = [];\n }\n}\n","/** 8-char random hex string for event IDs. */\nexport function generateId(): string {\n const arr = new Uint8Array(4);\n crypto.getRandomValues(arr);\n return Array.from(arr, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/** 32-char random hex string for session IDs. */\nexport function generateSessionId(): string {\n const arr = new Uint8Array(16);\n crypto.getRandomValues(arr);\n return Array.from(arr, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n","import { generateId } from '../utils/id.js';\nimport type { NetworkEvent, GraphQLOperation, RuntimeEvent } from '../types.js';\n\ntype EmitFn = (event: NetworkEvent) => void;\n\nexport interface FetchInterceptorOptions {\n captureBody?: boolean;\n maxBodySize?: number;\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null;\n}\n\n// Track URLs intercepted by fetch to prevent XHR double-counting (fetch polyfill case).\n// Uses a Map with timestamps + single sweeper instead of per-request setTimeout.\nconst MAX_INFLIGHT = 1000;\nconst INFLIGHT_TTL_MS = 10_000;\nconst SWEEP_INTERVAL_MS = 5_000;\n\nexport const fetchInterceptedRequests = new Map<string, number>();\nlet sweepTimer: ReturnType<typeof setInterval> | null = null;\nlet sweepRefCount = 0;\n\nfunction startSweeper(): void {\n sweepRefCount++;\n if (sweepTimer) return;\n sweepTimer = setInterval(() => {\n const cutoff = Date.now() - INFLIGHT_TTL_MS;\n for (const [key, ts] of fetchInterceptedRequests) {\n if (ts < cutoff) fetchInterceptedRequests.delete(key);\n }\n }, SWEEP_INTERVAL_MS);\n}\n\nfunction stopSweeper(): void {\n sweepRefCount--;\n if (sweepRefCount <= 0 && sweepTimer) {\n clearInterval(sweepTimer);\n sweepTimer = null;\n sweepRefCount = 0;\n }\n}\n\nexport function interceptFetch(\n emit: EmitFn,\n sessionId: string,\n redactHeaders: string[],\n options?: FetchInterceptorOptions\n): () => void {\n const originalFetch = window.fetch;\n const redactSet = new Set(redactHeaders.map((h) => h.toLowerCase()));\n const captureBody = options?.captureBody ?? false;\n const maxBodySize = options?.maxBodySize ?? 65536;\n\n startSweeper();\n\n window.fetch = async function (\n input: RequestInfo | URL,\n init?: RequestInit\n ): Promise<Response> {\n const startTime = performance.now();\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n const method = (init?.method ?? 'GET').toUpperCase();\n\n const requestHeaders = extractHeaders(init?.headers, redactSet);\n const requestBodySize = estimateBodySize(init?.body);\n const graphqlOperation = detectGraphQL(init?.body);\n\n // Capture request body if enabled\n let requestBody: string | undefined;\n if (captureBody && init?.body) {\n requestBody = serializeBody(init.body, maxBodySize);\n }\n\n // Mark request to prevent XHR interceptor double-counting (fetch polyfill case)\n const requestKey = `${method}:${url}:${startTime}`;\n // Evict oldest entry if at capacity\n if (fetchInterceptedRequests.size >= MAX_INFLIGHT) {\n const oldest = fetchInterceptedRequests.keys().next().value;\n if (oldest !== undefined) fetchInterceptedRequests.delete(oldest);\n }\n fetchInterceptedRequests.set(requestKey, Date.now());\n\n try {\n const response = await originalFetch.call(window, input, init);\n const duration = performance.now() - startTime;\n\n const responseBodySize = parseInt(\n response.headers.get('content-length') || '0',\n 10\n );\n const responseHeaders = extractResponseHeaders(response.headers, redactSet);\n\n // Capture response body if enabled\n let responseBody: string | undefined;\n if (captureBody) {\n try {\n const clone = response.clone();\n const text = await clone.text();\n responseBody = text.length > maxBodySize ? text.slice(0, maxBodySize) : text;\n } catch {\n // CORS or stream-locked — skip body capture\n }\n }\n\n const event: NetworkEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'network',\n url,\n method,\n status: response.status,\n requestHeaders,\n responseHeaders,\n requestBodySize,\n responseBodySize,\n duration,\n ttfb: duration,\n graphqlOperation,\n requestBody,\n responseBody,\n source: 'fetch',\n };\n\n if (options?.beforeSend) {\n const filtered = options.beforeSend(event);\n if (filtered) emit(filtered as NetworkEvent);\n } else {\n emit(event);\n }\n\n return response;\n } catch (error) {\n const duration = performance.now() - startTime;\n\n let errorPhase: 'error' | 'abort' | 'timeout' = 'error';\n let errorMessage = '';\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n errorPhase = 'abort';\n errorMessage = 'Request aborted';\n } else if (error instanceof Error) {\n errorMessage = error.message;\n }\n\n const event: NetworkEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'network',\n url,\n method,\n status: 0,\n requestHeaders,\n responseHeaders: {},\n requestBodySize,\n responseBodySize: 0,\n duration,\n ttfb: 0,\n graphqlOperation,\n requestBody,\n errorPhase,\n errorMessage,\n source: 'fetch',\n };\n\n if (options?.beforeSend) {\n const filtered = options.beforeSend(event);\n if (filtered) emit(filtered as NetworkEvent);\n } else {\n emit(event);\n }\n\n throw error;\n }\n };\n\n return () => {\n window.fetch = originalFetch;\n stopSweeper();\n };\n}\n\nfunction extractHeaders(\n headers: HeadersInit | undefined,\n redactSet: Set<string>\n): Record<string, string> {\n const result: Record<string, string> = {};\n if (!headers) return result;\n\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n result[key] = redactSet.has(key.toLowerCase()) ? '[REDACTED]' : value;\n });\n } else if (Array.isArray(headers)) {\n for (const [key, value] of headers) {\n result[key] = redactSet.has(key.toLowerCase()) ? '[REDACTED]' : value;\n }\n } else {\n for (const [key, value] of Object.entries(headers)) {\n result[key] = redactSet.has(key.toLowerCase()) ? '[REDACTED]' : value;\n }\n }\n\n return result;\n}\n\nfunction extractResponseHeaders(\n headers: Headers,\n redactSet: Set<string>\n): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key] = redactSet.has(key.toLowerCase()) ? '[REDACTED]' : value;\n });\n return result;\n}\n\nfunction estimateBodySize(body: BodyInit | null | undefined): number {\n if (!body) return 0;\n if (typeof body === 'string') return new Blob([body]).size;\n if (body instanceof Blob) return body.size;\n if (body instanceof ArrayBuffer) return body.byteLength;\n if (ArrayBuffer.isView(body)) return body.byteLength;\n if (body instanceof FormData) return 0;\n if (body instanceof URLSearchParams) return new Blob([body.toString()]).size;\n return 0;\n}\n\nfunction serializeBody(body: BodyInit | null | undefined, maxSize: number): string | undefined {\n if (!body) return undefined;\n if (typeof body === 'string') return body.length > maxSize ? body.slice(0, maxSize) : body;\n if (body instanceof URLSearchParams) {\n const s = body.toString();\n return s.length > maxSize ? s.slice(0, maxSize) : s;\n }\n if (body instanceof FormData) return '[FormData]';\n if (body instanceof Blob) return `[Blob ${body.size} bytes]`;\n if (body instanceof ArrayBuffer) return `[ArrayBuffer ${body.byteLength} bytes]`;\n if (ArrayBuffer.isView(body)) return `[TypedArray ${body.byteLength} bytes]`;\n return undefined;\n}\n\nfunction detectGraphQL(body: BodyInit | null | undefined): GraphQLOperation | undefined {\n if (!body || typeof body !== 'string') return undefined;\n\n try {\n const parsed = JSON.parse(body);\n if (typeof parsed.query === 'string') {\n const trimmed = parsed.query.trim();\n let type: GraphQLOperation['type'] = 'query';\n if (trimmed.startsWith('mutation')) type = 'mutation';\n else if (trimmed.startsWith('subscription')) type = 'subscription';\n\n const name = parsed.operationName || extractOperationName(trimmed) || 'anonymous';\n return { type, name };\n }\n } catch {\n // Not GraphQL\n }\n\n return undefined;\n}\n\nfunction extractOperationName(query: string): string | undefined {\n const match = query.match(/^(?:query|mutation|subscription)\\s+(\\w+)/);\n return match?.[1];\n}\n","/** Safely serialize a value, handling circular references, functions, symbols, and errors. */\nexport function safeSerialize(value: unknown, maxDepth = 5): unknown {\n const seen = new WeakSet();\n\n function walk(val: unknown, depth: number): unknown {\n if (depth > maxDepth) return '[max depth]';\n if (val === null || val === undefined) return val;\n if (typeof val === 'function') return `[Function: ${val.name || 'anonymous'}]`;\n if (typeof val === 'symbol') return val.toString();\n if (typeof val === 'bigint') return val.toString();\n if (typeof val !== 'object') return val;\n\n if (val instanceof Error) {\n return { name: val.name, message: val.message, stack: val.stack };\n }\n if (val instanceof Date) {\n return val.toISOString();\n }\n if (val instanceof RegExp) {\n return val.toString();\n }\n\n if (seen.has(val as object)) return '[Circular]';\n seen.add(val as object);\n\n if (Array.isArray(val)) {\n return val.map((v) => walk(v, depth + 1));\n }\n\n const result: Record<string, unknown> = {};\n let keys: string[];\n try {\n keys = Object.keys(val as Record<string, unknown>);\n } catch {\n return '[Object]';\n }\n // Limit keys to avoid huge React fiber-like objects\n const maxKeys = 50;\n for (let i = 0; i < Math.min(keys.length, maxKeys); i++) {\n try {\n result[keys[i]] = walk((val as Record<string, unknown>)[keys[i]], depth + 1);\n } catch {\n result[keys[i]] = '[Error accessing property]';\n }\n }\n if (keys.length > maxKeys) result['...'] = `${keys.length - maxKeys} more keys`;\n return result;\n }\n\n return walk(value, 0);\n}\n","import { generateId } from '../utils/id.js';\nimport { safeSerialize } from '../utils/serialize.js';\nimport type { ConsoleEvent, ConsoleLevel, RuntimeEvent } from '../types.js';\n\ntype EmitFn = (event: ConsoleEvent) => void;\n\nconst LEVELS: ConsoleLevel[] = ['log', 'warn', 'error', 'info', 'debug', 'trace'];\n\nexport function interceptConsole(\n emit: EmitFn,\n sessionId: string,\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null\n): () => void {\n const originals: Record<string, (...args: unknown[]) => void> = {};\n\n for (const level of LEVELS) {\n originals[level] = console[level].bind(console);\n\n console[level] = (...args: unknown[]) => {\n const message = args\n .map((a) => (typeof a === 'string' ? a : stringifyArg(a)))\n .join(' ');\n\n const event: ConsoleEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'console',\n level,\n message,\n args: args.map((a) => safeSerialize(a, 3)),\n stackTrace:\n level === 'error' || level === 'trace'\n ? new Error().stack?.split('\\n').slice(2).join('\\n')\n : undefined,\n sourceFile: undefined,\n };\n\n if (beforeSend) {\n const filtered = beforeSend(event);\n if (filtered) emit(filtered as ConsoleEvent);\n } else {\n emit(event);\n }\n\n // Call original — MUST come after emit, and MUST use saved reference\n originals[level](...args);\n };\n }\n\n return () => {\n for (const level of LEVELS) {\n console[level] = originals[level];\n }\n };\n}\n\nfunction stringifyArg(arg: unknown): string {\n try {\n return JSON.stringify(arg);\n } catch {\n return String(arg);\n }\n}\n","import { generateId } from '../utils/id.js';\nimport type { NetworkEvent, GraphQLOperation, RuntimeEvent } from '../types.js';\nimport { fetchInterceptedRequests } from './fetch.js';\n\ntype EmitFn = (event: NetworkEvent) => void;\n\nexport interface XhrInterceptorOptions {\n captureBody?: boolean;\n maxBodySize?: number;\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null;\n}\n\ninterface AugmentedXHR extends XMLHttpRequest {\n __rs_method?: string;\n __rs_url?: string;\n __rs_headers?: Record<string, string>;\n __rs_start?: number;\n __rs_body?: string;\n __rs_fetchIntercepted?: boolean;\n}\n\nexport function interceptXhr(\n emit: EmitFn,\n sessionId: string,\n redactHeaders: string[],\n options?: XhrInterceptorOptions\n): () => void {\n const redactSet = new Set(redactHeaders.map((h) => h.toLowerCase()));\n const captureBody = options?.captureBody ?? false;\n const maxBodySize = options?.maxBodySize ?? 65536;\n\n // Global AbortController for teardown — aborts all outstanding XHR listeners on disconnect\n const globalAbort = new AbortController();\n\n const origOpen = XMLHttpRequest.prototype.open;\n const origSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;\n const origSend = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (\n this: AugmentedXHR,\n method: string,\n url: string | URL\n ) {\n this.__rs_method = method.toUpperCase();\n this.__rs_url = typeof url === 'string' ? url : url.href;\n this.__rs_headers = {};\n // eslint-disable-next-line prefer-rest-params\n return origOpen.apply(this, arguments as unknown as Parameters<typeof origOpen>);\n };\n\n XMLHttpRequest.prototype.setRequestHeader = function (\n this: AugmentedXHR,\n name: string,\n value: string\n ) {\n if (this.__rs_headers) {\n this.__rs_headers[name.toLowerCase()] = redactSet.has(name.toLowerCase())\n ? '[REDACTED]'\n : value;\n }\n return origSetRequestHeader.call(this, name, value);\n };\n\n XMLHttpRequest.prototype.send = function (\n this: AugmentedXHR,\n body?: Document | XMLHttpRequestBodyInit | null\n ) {\n // Skip if this request was already captured by the fetch interceptor\n const method = this.__rs_method ?? 'GET';\n const url = this.__rs_url ?? '';\n const requestKey = `${method}:${url}`;\n let alreadyIntercepted = false;\n for (const k of fetchInterceptedRequests.keys()) {\n if (k.startsWith(requestKey)) { alreadyIntercepted = true; break; }\n }\n if (alreadyIntercepted) {\n this.__rs_fetchIntercepted = true;\n return origSend.call(this, body);\n }\n\n const requestHeaders = { ...(this.__rs_headers ?? {}) };\n const startTime = performance.now();\n\n // Capture request body\n let requestBody: string | undefined;\n let requestBodySize = 0;\n if (body) {\n if (typeof body === 'string') {\n requestBodySize = new Blob([body]).size;\n if (captureBody) {\n requestBody = body.length > maxBodySize ? body.slice(0, maxBodySize) : body;\n }\n } else if (body instanceof Blob) {\n requestBodySize = body.size;\n if (captureBody) requestBody = `[Blob ${body.size} bytes]`;\n } else if (body instanceof ArrayBuffer) {\n requestBodySize = body.byteLength;\n if (captureBody) requestBody = `[ArrayBuffer ${body.byteLength} bytes]`;\n } else if (body instanceof FormData) {\n if (captureBody) requestBody = '[FormData]';\n } else if (body instanceof URLSearchParams) {\n const s = body.toString();\n requestBodySize = new Blob([s]).size;\n if (captureBody) requestBody = s.length > maxBodySize ? s.slice(0, maxBodySize) : s;\n }\n }\n\n // Detect GraphQL\n const graphqlOperation = detectGraphQL(body);\n\n const emitEvent = (overrides: Partial<NetworkEvent>) => {\n const event: NetworkEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'network',\n url,\n method,\n status: 0,\n requestHeaders,\n responseHeaders: {},\n requestBodySize,\n responseBodySize: 0,\n duration: 0,\n ttfb: 0,\n graphqlOperation,\n requestBody,\n source: 'xhr',\n ...overrides,\n };\n\n if (options?.beforeSend) {\n const filtered = options.beforeSend(event);\n if (filtered) emit(filtered as NetworkEvent);\n } else {\n emit(event);\n }\n };\n\n // Single loadend listener covers all terminal states (load, error, abort, timeout).\n // { once: true } auto-removes per-request; globalAbort.signal enables bulk teardown.\n this.addEventListener('loadend', () => {\n const duration = performance.now() - startTime;\n\n if (this.status > 0) {\n // Successful completion (includes HTTP errors like 4xx/5xx)\n const responseHeaders = parseResponseHeaders(this.getAllResponseHeaders(), redactSet);\n const responseBodySize = parseInt(\n this.getResponseHeader('content-length') || '0',\n 10\n );\n\n let responseBody: string | undefined;\n if (captureBody && (this.responseType === '' || this.responseType === 'text')) {\n try {\n const text = this.responseText;\n responseBody = text.length > maxBodySize ? text.slice(0, maxBodySize) : text;\n } catch {\n // responseText not available for non-text types\n }\n }\n\n emitEvent({\n status: this.status,\n responseHeaders,\n responseBodySize,\n responseBody,\n duration,\n ttfb: duration,\n });\n } else {\n // Network error, abort, or timeout — status is 0\n let errorPhase: 'error' | 'abort' | 'timeout' = 'error';\n let errorMessage = 'Network error';\n\n if (this.readyState === 0 || (this as XMLHttpRequest).status === 0) {\n // Distinguish abort vs timeout vs network error\n // XHR sets readyState to UNSENT (0) on abort\n }\n\n // Check specific failure modes\n if (this.timeout > 0 && duration >= this.timeout) {\n errorPhase = 'timeout';\n errorMessage = `Request timed out after ${this.timeout}ms`;\n }\n\n emitEvent({ duration, errorPhase, errorMessage });\n }\n }, { once: true, signal: globalAbort.signal });\n\n return origSend.call(this, body);\n };\n\n return () => {\n // Abort all outstanding XHR listeners from this interceptor instance\n globalAbort.abort();\n XMLHttpRequest.prototype.open = origOpen;\n XMLHttpRequest.prototype.setRequestHeader = origSetRequestHeader;\n XMLHttpRequest.prototype.send = origSend;\n };\n}\n\nfunction parseResponseHeaders(\n raw: string,\n redactSet: Set<string>\n): Record<string, string> {\n const result: Record<string, string> = {};\n if (!raw) return result;\n\n for (const line of raw.trim().split(/[\\r\\n]+/)) {\n const idx = line.indexOf(':');\n if (idx === -1) continue;\n const key = line.slice(0, idx).trim().toLowerCase();\n const value = line.slice(idx + 1).trim();\n result[key] = redactSet.has(key) ? '[REDACTED]' : value;\n }\n\n return result;\n}\n\nfunction detectGraphQL(\n body: Document | XMLHttpRequestBodyInit | null | undefined\n): GraphQLOperation | undefined {\n if (!body || typeof body !== 'string') return undefined;\n\n try {\n const parsed = JSON.parse(body);\n if (typeof parsed.query === 'string') {\n const trimmed = parsed.query.trim();\n let type: GraphQLOperation['type'] = 'query';\n if (trimmed.startsWith('mutation')) type = 'mutation';\n else if (trimmed.startsWith('subscription')) type = 'subscription';\n\n const name =\n parsed.operationName || extractOperationName(trimmed) || 'anonymous';\n return { type, name };\n }\n } catch {\n // Not GraphQL\n }\n\n return undefined;\n}\n\nfunction extractOperationName(query: string): string | undefined {\n const match = query.match(/^(?:query|mutation|subscription)\\s+(\\w+)/);\n return match?.[1];\n}\n","import { generateId } from '../utils/id.js';\nimport { safeSerialize } from '../utils/serialize.js';\nimport type { StateEvent, RuntimeEvent } from '../types.js';\n\ntype EmitFn = (event: StateEvent) => void;\n\nexport interface StateStoreOptions {\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null;\n}\n\ninterface ZustandStore {\n getState: () => unknown;\n setState: (partial: unknown) => void;\n subscribe: (listener: (state: unknown, prevState: unknown) => void) => () => void;\n}\n\ninterface ReduxStore {\n getState: () => unknown;\n dispatch: (action: unknown) => unknown;\n subscribe: (listener: () => void) => () => void;\n}\n\ntype DetectedLibrary = 'zustand' | 'redux' | 'unknown';\n\nexport function interceptStateStores(\n emit: EmitFn,\n sessionId: string,\n stores: Record<string, unknown>,\n options?: StateStoreOptions\n): () => void {\n const unsubscribers: (() => void)[] = [];\n const originalDispatches: Map<string, ReduxStore['dispatch']> = new Map();\n\n for (const [storeId, store] of Object.entries(stores)) {\n const library = detectLibrary(store);\n\n if (library === 'zustand') {\n const zustand = store as ZustandStore;\n\n // Emit initial state\n emitState(emit, sessionId, options?.beforeSend, {\n storeId,\n library,\n phase: 'init',\n state: safeSerialize(zustand.getState(), 4),\n });\n\n // Subscribe to changes\n const unsub = zustand.subscribe((state, prevState) => {\n const diff = shallowDiff(prevState, state);\n emitState(emit, sessionId, options?.beforeSend, {\n storeId,\n library,\n phase: 'update',\n state: safeSerialize(state, 4),\n previousState: safeSerialize(prevState, 4),\n diff: diff ? safeSerialize(diff, 3) as Record<string, { from: unknown; to: unknown }> : undefined,\n });\n });\n\n unsubscribers.push(unsub);\n } else if (library === 'redux') {\n const redux = store as ReduxStore;\n\n // Emit initial state\n emitState(emit, sessionId, options?.beforeSend, {\n storeId,\n library,\n phase: 'init',\n state: safeSerialize(redux.getState(), 4),\n });\n\n // Wrap dispatch to capture actions\n let lastAction: { type: string; payload?: unknown } | undefined;\n const origDispatch = redux.dispatch.bind(redux);\n originalDispatches.set(storeId, origDispatch);\n\n (redux as { dispatch: ReduxStore['dispatch'] }).dispatch = (action: unknown) => {\n if (action && typeof action === 'object' && 'type' in action) {\n lastAction = {\n type: String((action as { type: unknown }).type),\n payload: (action as { payload?: unknown }).payload,\n };\n }\n return origDispatch(action);\n };\n\n // Subscribe to state changes\n let prevState = redux.getState();\n const unsub = redux.subscribe(() => {\n const state = redux.getState();\n const diff = shallowDiff(prevState, state);\n\n emitState(emit, sessionId, options?.beforeSend, {\n storeId,\n library,\n phase: 'update',\n state: safeSerialize(state, 4),\n previousState: safeSerialize(prevState, 4),\n diff: diff ? safeSerialize(diff, 3) as Record<string, { from: unknown; to: unknown }> : undefined,\n action: lastAction ? safeSerialize(lastAction, 3) as { type: string; payload?: unknown } : undefined,\n });\n\n prevState = state;\n lastAction = undefined;\n });\n\n unsubscribers.push(unsub);\n }\n }\n\n return () => {\n for (const unsub of unsubscribers) unsub();\n\n // Restore original Redux dispatch functions\n for (const [storeId, origDispatch] of originalDispatches) {\n const store = stores[storeId] as ReduxStore;\n if (store) {\n (store as { dispatch: ReduxStore['dispatch'] }).dispatch = origDispatch;\n }\n }\n };\n}\n\nfunction detectLibrary(store: unknown): DetectedLibrary {\n if (!store || typeof store !== 'object') return 'unknown';\n const s = store as Record<string, unknown>;\n\n // Zustand: function-like with getState, setState, subscribe\n if (\n typeof s.getState === 'function' &&\n typeof s.setState === 'function' &&\n typeof s.subscribe === 'function'\n ) {\n return 'zustand';\n }\n\n // Redux: object with dispatch, getState, subscribe\n if (\n typeof s.dispatch === 'function' &&\n typeof s.getState === 'function' &&\n typeof s.subscribe === 'function'\n ) {\n return 'redux';\n }\n\n return 'unknown';\n}\n\nfunction shallowDiff(\n prev: unknown,\n curr: unknown\n): Record<string, { from: unknown; to: unknown }> | null {\n if (!prev || !curr || typeof prev !== 'object' || typeof curr !== 'object') {\n return null;\n }\n\n const diff: Record<string, { from: unknown; to: unknown }> = {};\n const prevObj = prev as Record<string, unknown>;\n const currObj = curr as Record<string, unknown>;\n const allKeys = new Set([...Object.keys(prevObj), ...Object.keys(currObj)]);\n\n for (const key of allKeys) {\n if (prevObj[key] !== currObj[key]) {\n diff[key] = { from: prevObj[key], to: currObj[key] };\n }\n }\n\n return Object.keys(diff).length > 0 ? diff : null;\n}\n\nfunction emitState(\n emit: EmitFn,\n sessionId: string,\n beforeSend: ((event: RuntimeEvent) => RuntimeEvent | null) | undefined,\n data: Omit<StateEvent, 'eventId' | 'sessionId' | 'timestamp' | 'eventType'>\n): void {\n const event: StateEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'state',\n ...data,\n };\n\n if (beforeSend) {\n const filtered = beforeSend(event);\n if (filtered) emit(filtered as StateEvent);\n } else {\n emit(event);\n }\n}\n","import { generateId } from '../utils/id.js';\nimport type { PerformanceEvent, WebVitalRating, RuntimeEvent } from '../types.js';\n\ntype EmitFn = (event: PerformanceEvent) => void;\n\nexport interface PerformanceInterceptorOptions {\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null;\n}\n\n// Web Vitals thresholds from web.dev\nconst THRESHOLDS: Record<string, [number, number]> = {\n LCP: [2500, 4000],\n FCP: [1800, 3000],\n CLS: [0.1, 0.25],\n TTFB: [800, 1800],\n FID: [100, 300],\n INP: [200, 500],\n};\n\nfunction rate(metric: string, value: number): WebVitalRating {\n const [good, poor] = THRESHOLDS[metric] ?? [Infinity, Infinity];\n if (value <= good) return 'good';\n if (value <= poor) return 'needs-improvement';\n return 'poor';\n}\n\n// Module-level singleton tracking — prevents duplicate observers on double-init (HMR)\nlet activeObservers: PerformanceObserver[] | null = null;\n\nexport function interceptPerformance(\n emit: EmitFn,\n sessionId: string,\n options?: PerformanceInterceptorOptions\n): () => void {\n // Disconnect any existing observers from a previous init (HMR / double-init safety)\n if (activeObservers) {\n for (const obs of activeObservers) {\n try { obs.disconnect(); } catch { /* already disconnected */ }\n }\n }\n\n const observers: PerformanceObserver[] = [];\n activeObservers = observers;\n\n const emitMetric = (\n metricName: PerformanceEvent['metricName'],\n value: number,\n element?: string\n ) => {\n const event: PerformanceEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'performance',\n metricName,\n value: Math.round(value * 100) / 100,\n rating: rate(metricName, value),\n element,\n };\n\n if (options?.beforeSend) {\n const filtered = options.beforeSend(event);\n if (filtered) emit(filtered as PerformanceEvent);\n } else {\n emit(event);\n }\n };\n\n // LCP — Largest Contentful Paint\n tryObserve(observers, 'largest-contentful-paint', (entries) => {\n const last = entries[entries.length - 1];\n if (!last) return;\n const el = (last as PerformanceEntry & { element?: Element }).element;\n emitMetric('LCP', last.startTime, el?.tagName?.toLowerCase());\n });\n\n // FCP — First Contentful Paint\n tryObserve(observers, 'paint', (entries) => {\n for (const entry of entries) {\n if (entry.name === 'first-contentful-paint') {\n emitMetric('FCP', entry.startTime);\n }\n }\n });\n\n // CLS — Cumulative Layout Shift\n let clsValue = 0;\n tryObserve(observers, 'layout-shift', (entries) => {\n for (const entry of entries) {\n const ls = entry as PerformanceEntry & { hadRecentInput?: boolean; value?: number };\n if (!ls.hadRecentInput && ls.value) {\n clsValue += ls.value;\n emitMetric('CLS', clsValue);\n }\n }\n });\n\n // FID — First Input Delay\n tryObserve(observers, 'first-input', (entries) => {\n const first = entries[0];\n if (!first) return;\n const fi = first as PerformanceEntry & { processingStart?: number };\n if (fi.processingStart) {\n emitMetric('FID', fi.processingStart - first.startTime);\n }\n });\n\n // TTFB — Time to First Byte\n tryObserve(observers, 'navigation', (entries) => {\n const nav = entries[0] as PerformanceNavigationTiming | undefined;\n if (nav) {\n emitMetric('TTFB', nav.responseStart - nav.requestStart);\n }\n });\n\n // INP — Interaction to Next Paint\n let inpMax = 0;\n tryObserve(\n observers,\n 'event',\n (entries) => {\n for (const entry of entries) {\n if (entry.duration > inpMax) {\n inpMax = entry.duration;\n emitMetric('INP', inpMax);\n }\n }\n },\n { durationThreshold: 16 }\n );\n\n return () => {\n for (const obs of observers) {\n try {\n obs.disconnect();\n } catch {\n // Already disconnected\n }\n }\n if (activeObservers === observers) {\n activeObservers = null;\n }\n };\n}\n\nfunction tryObserve(\n observers: PerformanceObserver[],\n entryType: string,\n callback: (entries: PerformanceEntryList) => void,\n observeOptions?: Record<string, unknown>\n): void {\n try {\n const obs = new PerformanceObserver((list) => {\n callback(list.getEntries());\n });\n obs.observe({\n type: entryType,\n buffered: true,\n ...observeOptions,\n } as PerformanceObserverInit);\n observers.push(obs);\n } catch {\n // Observer type not supported in this browser\n }\n}\n","import { generateId } from '../utils/id.js';\nimport type { RenderEvent, RenderComponentProfile, RuntimeEvent } from '../types.js';\n\ntype EmitFn = (event: RenderEvent) => void;\n\nexport interface RenderInterceptorOptions {\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null;\n snapshotIntervalMs?: number;\n}\n\ninterface Fiber {\n tag: number;\n type: { displayName?: string; name?: string } | string | null;\n child: Fiber | null;\n sibling: Fiber | null;\n alternate: Fiber | null;\n memoizedProps: unknown;\n memoizedState: unknown;\n actualDuration?: number;\n stateNode: unknown;\n}\n\ninterface ComponentTracker {\n renderCount: number;\n totalDuration: number;\n lastRenderTime: number;\n lastRenderPhase: 'mount' | 'update' | 'unmount';\n lastRenderCause: 'props' | 'state' | 'context' | 'parent' | 'unknown';\n renderTimestamps: number[];\n}\n\ninterface DevToolsHook {\n onCommitFiberRoot?: (id: number, root: { current?: Fiber }) => void;\n _runtimescope_original?: (id: number, root: { current?: Fiber }) => void;\n}\n\nconst FUNCTION_COMPONENT = 0;\nconst CLASS_COMPONENT = 1;\nconst SNAPSHOT_WINDOW_MS = 10_000;\nconst MAX_TIMESTAMPS = 100;\nconst TRACKER_TTL_MS = 60_000;\nconst MAX_TRACKED_COMPONENTS = 500;\n\nexport function interceptReactRenders(\n emit: EmitFn,\n sessionId: string,\n options?: RenderInterceptorOptions\n): () => void {\n const trackers = new Map<string, ComponentTracker>();\n const snapshotIntervalMs = options?.snapshotIntervalMs ?? 5000;\n let snapshotTimer: ReturnType<typeof setInterval> | null = null;\n\n // Access or create the React DevTools global hook\n const hook = getOrCreateDevToolsHook();\n if (!hook) {\n return () => {}; // Not in a browser environment\n }\n\n // Save original if React DevTools is installed\n const originalOnCommit = hook.onCommitFiberRoot;\n hook._runtimescope_original = originalOnCommit;\n\n hook.onCommitFiberRoot = (id: number, root: { current?: Fiber }) => {\n // Call original first (React DevTools compatibility)\n if (originalOnCommit) {\n try {\n originalOnCommit(id, root);\n } catch {\n // Don't let DevTools errors break our tracking\n }\n }\n\n if (root.current) {\n walkFiber(root.current, trackers);\n }\n };\n\n // Emit snapshots periodically\n snapshotTimer = setInterval(() => {\n emitSnapshot(trackers, emit, sessionId, options?.beforeSend);\n }, snapshotIntervalMs);\n\n return () => {\n if (snapshotTimer) {\n clearInterval(snapshotTimer);\n snapshotTimer = null;\n }\n\n // Restore original hook\n if (hook) {\n if (hook._runtimescope_original) {\n hook.onCommitFiberRoot = hook._runtimescope_original;\n } else {\n delete hook.onCommitFiberRoot;\n }\n delete hook._runtimescope_original;\n }\n };\n}\n\nfunction getOrCreateDevToolsHook(): DevToolsHook | null {\n if (typeof window === 'undefined') return null;\n\n const w = window as unknown as { __REACT_DEVTOOLS_GLOBAL_HOOK__?: DevToolsHook };\n\n if (!w.__REACT_DEVTOOLS_GLOBAL_HOOK__) {\n // Create a minimal shim that React will pick up on init\n w.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {\n // React checks for these methods to decide if DevTools is present\n } as DevToolsHook;\n }\n\n return w.__REACT_DEVTOOLS_GLOBAL_HOOK__!;\n}\n\nfunction walkFiber(fiber: Fiber, trackers: Map<string, ComponentTracker>): void {\n processNode(fiber, trackers);\n\n if (fiber.child) walkFiber(fiber.child, trackers);\n if (fiber.sibling) walkFiber(fiber.sibling, trackers);\n}\n\nfunction processNode(fiber: Fiber, trackers: Map<string, ComponentTracker>): void {\n // Only track function components (0) and class components (1)\n if (fiber.tag !== FUNCTION_COMPONENT && fiber.tag !== CLASS_COMPONENT) return;\n\n const name = getComponentName(fiber);\n if (!name) return;\n\n const now = Date.now();\n const isMount = fiber.alternate === null;\n const duration = fiber.actualDuration ?? 0;\n const cause = inferRenderCause(fiber, isMount);\n\n let tracker = trackers.get(name);\n if (!tracker) {\n tracker = {\n renderCount: 0,\n totalDuration: 0,\n lastRenderTime: 0,\n lastRenderPhase: 'mount',\n lastRenderCause: 'unknown',\n renderTimestamps: [],\n };\n trackers.set(name, tracker);\n }\n\n tracker.renderCount++;\n tracker.totalDuration += duration;\n tracker.lastRenderTime = now;\n tracker.lastRenderPhase = isMount ? 'mount' : 'update';\n tracker.lastRenderCause = cause ?? 'unknown';\n tracker.renderTimestamps.push(now);\n\n // Trim old timestamps\n if (tracker.renderTimestamps.length > MAX_TIMESTAMPS) {\n tracker.renderTimestamps = tracker.renderTimestamps.slice(-MAX_TIMESTAMPS);\n }\n}\n\nfunction getComponentName(fiber: Fiber): string | undefined {\n if (!fiber.type) return undefined;\n if (typeof fiber.type === 'string') return undefined; // HTML elements\n return fiber.type.displayName || fiber.type.name || undefined;\n}\n\nfunction inferRenderCause(\n fiber: Fiber,\n isMount: boolean\n): RenderComponentProfile['lastRenderCause'] {\n if (isMount) return 'props'; // Initial mount, driven by parent passing props\n\n if (!fiber.alternate) return 'unknown';\n\n // Check if props changed\n if (fiber.memoizedProps !== fiber.alternate.memoizedProps) {\n return 'props';\n }\n\n // Check if state changed\n if (fiber.memoizedState !== fiber.alternate.memoizedState) {\n return 'state';\n }\n\n // If neither props nor state changed, likely parent re-rendered\n return 'parent';\n}\n\nfunction computeRenderVelocity(timestamps: number[]): number {\n if (timestamps.length < 2) return 0;\n const now = Date.now();\n const windowStart = now - SNAPSHOT_WINDOW_MS;\n const recent = timestamps.filter((t) => t >= windowStart);\n if (recent.length < 2) return 0;\n const windowMs = now - recent[0];\n if (windowMs === 0) return 0;\n return recent.length / (windowMs / 1000);\n}\n\nfunction emitSnapshot(\n trackers: Map<string, ComponentTracker>,\n emit: EmitFn,\n sessionId: string,\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null\n): void {\n if (trackers.size === 0) return;\n\n const profiles: RenderComponentProfile[] = [];\n const suspiciousComponents: string[] = [];\n let totalRenders = 0;\n\n for (const [componentName, tracker] of trackers) {\n const velocity = computeRenderVelocity(tracker.renderTimestamps);\n const suspicious = velocity > 4 || tracker.renderCount > 20;\n\n profiles.push({\n componentName,\n renderCount: tracker.renderCount,\n totalDuration: Math.round(tracker.totalDuration * 100) / 100,\n avgDuration:\n tracker.renderCount > 0\n ? Math.round((tracker.totalDuration / tracker.renderCount) * 100) / 100\n : 0,\n lastRenderPhase: tracker.lastRenderPhase,\n lastRenderCause: tracker.lastRenderCause,\n renderVelocity: Math.round(velocity * 100) / 100,\n suspicious,\n });\n\n if (suspicious) suspiciousComponents.push(componentName);\n totalRenders += tracker.renderCount;\n }\n\n // Sort by render count descending\n profiles.sort((a, b) => b.renderCount - a.renderCount);\n\n const event: RenderEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'render',\n profiles,\n snapshotWindowMs: SNAPSHOT_WINDOW_MS,\n totalRenders,\n suspiciousComponents,\n };\n\n if (beforeSend) {\n const filtered = beforeSend(event);\n if (filtered) emit(filtered as RenderEvent);\n } else {\n emit(event);\n }\n\n // Reset counters and prune stale trackers\n const now = Date.now();\n for (const [name, tracker] of trackers) {\n if (now - tracker.lastRenderTime > TRACKER_TTL_MS) {\n // Component hasn't rendered in 60s — remove to prevent unbounded growth\n trackers.delete(name);\n } else {\n tracker.renderCount = 0;\n tracker.totalDuration = 0;\n }\n }\n\n // Hard cap: if still over limit after TTL pruning, evict least-recently-rendered\n if (trackers.size > MAX_TRACKED_COMPONENTS) {\n const sorted = [...trackers.entries()]\n .sort((a, b) => a[1].lastRenderTime - b[1].lastRenderTime);\n const excess = trackers.size - MAX_TRACKED_COMPONENTS;\n for (let i = 0; i < excess; i++) {\n trackers.delete(sorted[i][0]);\n }\n }\n}\n","import { generateId } from '../utils/id.js';\nimport { safeSerialize } from '../utils/serialize.js';\nimport type { ConsoleEvent, RuntimeEvent } from '../types.js';\n\ntype EmitFn = (event: ConsoleEvent) => void;\n\n/**\n * Captures uncaught errors and unhandled promise rejections that appear\n * in DevTools but don't go through the console.* API.\n *\n * - window 'error' (capture phase) — JS runtime errors + resource load failures\n * - window 'unhandledrejection' — unhandled async/await and Promise rejections\n *\n * Events are emitted as ConsoleEvents with level 'error' so they appear\n * alongside console.error() output in get_console_messages.\n */\nexport function interceptErrors(\n emit: EmitFn,\n sessionId: string,\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null\n): () => void {\n // Capture uncaught JS errors and resource load failures\n const onError = (e: ErrorEvent | Event) => {\n let message: string;\n let stackTrace: string | undefined;\n let sourceFile: string | undefined;\n\n if (e instanceof ErrorEvent) {\n // Uncaught JS error\n message = e.message || 'Uncaught error';\n stackTrace = e.error?.stack;\n sourceFile = e.filename\n ? `${e.filename}:${e.lineno}:${e.colno}`\n : undefined;\n } else {\n // Resource load error (img, script, link, etc.)\n const target = e.target as HTMLElement | null;\n if (target && target !== window as unknown) {\n const tagName = target.tagName?.toLowerCase() ?? 'unknown';\n const src =\n (target as HTMLImageElement).src ??\n (target as HTMLScriptElement).src ??\n (target as HTMLLinkElement).href ??\n 'unknown';\n message = `Failed to load resource: <${tagName}> ${src}`;\n } else {\n return; // Not a resource error we can identify\n }\n }\n\n const event: ConsoleEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'console',\n level: 'error',\n message: `[Uncaught] ${message}`,\n args: [safeSerialize(message, 3)],\n stackTrace,\n sourceFile,\n };\n\n if (beforeSend) {\n const filtered = beforeSend(event);\n if (filtered) emit(filtered as ConsoleEvent);\n } else {\n emit(event);\n }\n };\n\n // Capture unhandled promise rejections\n const onUnhandledRejection = (e: PromiseRejectionEvent) => {\n const reason = e.reason;\n let message: string;\n let stackTrace: string | undefined;\n\n if (reason instanceof Error) {\n message = reason.message;\n stackTrace = reason.stack;\n } else if (typeof reason === 'string') {\n message = reason;\n } else {\n try {\n message = JSON.stringify(reason);\n } catch {\n message = String(reason);\n }\n }\n\n const event: ConsoleEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'console',\n level: 'error',\n message: `[Unhandled Rejection] ${message}`,\n args: [safeSerialize(reason, 3)],\n stackTrace,\n sourceFile: undefined,\n };\n\n if (beforeSend) {\n const filtered = beforeSend(event);\n if (filtered) emit(filtered as ConsoleEvent);\n } else {\n emit(event);\n }\n };\n\n // Use capture phase for 'error' to catch resource load failures on child elements\n window.addEventListener('error', onError, true);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n return () => {\n window.removeEventListener('error', onError, true);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n };\n}\n","import type { RuntimeEvent, NavigationEvent } from '../types.js';\nimport { generateId } from '../utils/id.js';\n\n/**\n * Intercepts SPA navigation events:\n * - history.pushState / replaceState (client-side routing)\n * - popstate (browser back/forward)\n * - hashchange (hash-based routing)\n *\n * Emits a NavigationEvent with from/to URLs and the trigger type.\n */\nexport function interceptNavigation(\n emit: (event: RuntimeEvent) => void,\n sessionId: string\n): () => void {\n let currentUrl = window.location.href;\n\n function emitNav(to: string, trigger: NavigationEvent['trigger']): void {\n const from = currentUrl;\n if (from === to) return; // Skip no-op navigations\n currentUrl = to;\n\n emit({\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'navigation',\n from,\n to,\n trigger,\n });\n }\n\n // Patch pushState and replaceState\n const origPushState = history.pushState.bind(history);\n const origReplaceState = history.replaceState.bind(history);\n\n history.pushState = function (...args: Parameters<typeof origPushState>) {\n origPushState(...args);\n emitNav(window.location.href, 'pushState');\n };\n\n history.replaceState = function (...args: Parameters<typeof origReplaceState>) {\n origReplaceState(...args);\n emitNav(window.location.href, 'replaceState');\n };\n\n // Listen for back/forward navigation\n const onPopState = () => emitNav(window.location.href, 'popstate');\n window.addEventListener('popstate', onPopState);\n\n // Listen for hash changes (covers hash-based routers)\n const onHashChange = () => emitNav(window.location.href, 'hashchange');\n window.addEventListener('hashchange', onHashChange);\n\n // Restore function\n return () => {\n history.pushState = origPushState;\n history.replaceState = origReplaceState;\n window.removeEventListener('popstate', onPopState);\n window.removeEventListener('hashchange', onHashChange);\n };\n}\n"],"mappings":"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","names":["index_exports","__export","RuntimeScope","index_default","__toCommonJS","_log","_Transport","config","__publicField","event","msg","queued","events","delay","jitter","handler","requestId","command","payload","Transport","generateId","arr","b","generateSessionId","MAX_INFLIGHT","INFLIGHT_TTL_MS","SWEEP_INTERVAL_MS","fetchInterceptedRequests","sweepTimer","sweepRefCount","startSweeper","cutoff","key","ts","stopSweeper","interceptFetch","emit","sessionId","redactHeaders","options","originalFetch","redactSet","h","captureBody","maxBodySize","input","init","startTime","url","method","requestHeaders","extractHeaders","requestBodySize","estimateBodySize","graphqlOperation","detectGraphQL","requestBody","serializeBody","requestKey","oldest","response","duration","responseBodySize","responseHeaders","extractResponseHeaders","responseBody","text","event","generateId","filtered","error","errorPhase","errorMessage","headers","result","value","body","maxSize","s","parsed","trimmed","type","name","extractOperationName","query","safeSerialize","value","maxDepth","seen","walk","val","depth","v","result","keys","maxKeys","i","LEVELS","interceptConsole","emit","sessionId","beforeSend","originals","level","args","message","stringifyArg","event","generateId","safeSerialize","filtered","arg","interceptXhr","emit","sessionId","redactHeaders","options","redactSet","h","captureBody","maxBodySize","globalAbort","origOpen","origSetRequestHeader","origSend","method","url","name","value","body","requestKey","alreadyIntercepted","k","fetchInterceptedRequests","requestHeaders","startTime","requestBody","requestBodySize","s","graphqlOperation","detectGraphQL","emitEvent","overrides","event","generateId","filtered","duration","responseHeaders","parseResponseHeaders","responseBodySize","responseBody","text","errorPhase","errorMessage","raw","result","line","idx","key","parsed","trimmed","type","extractOperationName","query","interceptStateStores","emit","sessionId","stores","options","unsubscribers","originalDispatches","storeId","store","library","detectLibrary","zustand","emitState","safeSerialize","unsub","state","prevState","diff","shallowDiff","redux","lastAction","origDispatch","action","s","prev","curr","prevObj","currObj","allKeys","key","beforeSend","data","event","generateId","filtered","THRESHOLDS","rate","metric","value","good","poor","activeObservers","interceptPerformance","emit","sessionId","options","obs","observers","emitMetric","metricName","element","event","generateId","filtered","tryObserve","entries","last","el","entry","clsValue","ls","first","fi","nav","inpMax","entryType","callback","observeOptions","list","FUNCTION_COMPONENT","CLASS_COMPONENT","SNAPSHOT_WINDOW_MS","MAX_TIMESTAMPS","TRACKER_TTL_MS","MAX_TRACKED_COMPONENTS","interceptReactRenders","emit","sessionId","options","trackers","snapshotIntervalMs","snapshotTimer","hook","getOrCreateDevToolsHook","originalOnCommit","id","root","walkFiber","emitSnapshot","w","fiber","processNode","name","getComponentName","now","isMount","duration","cause","inferRenderCause","tracker","computeRenderVelocity","timestamps","windowStart","recent","t","windowMs","beforeSend","profiles","suspiciousComponents","totalRenders","componentName","velocity","suspicious","a","b","event","generateId","filtered","sorted","excess","i","interceptErrors","emit","sessionId","beforeSend","onError","e","message","stackTrace","sourceFile","target","tagName","src","event","generateId","safeSerialize","filtered","onUnhandledRejection","reason","interceptNavigation","emit","sessionId","currentUrl","emitNav","to","trigger","from","generateId","origPushState","origReplaceState","args","onPopState","onHashChange","SDK_VERSION","_log","ORIGINALS_KEY","getOrSaveOriginals","g","RuntimeScope","config","resolved","generateSessionId","Transport","emit","event","generateId","sessionId","cmd","interceptFetch","interceptXhr","interceptConsole","interceptErrors","interceptStateStores","interceptPerformance","interceptReactRenders","interceptNavigation","features","maxSize","html","truncated","snapshot","name","properties","fn","originals","level","c","__publicField","index_default"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/transport.ts","../src/utils/id.ts","../src/interceptors/fetch.ts","../src/utils/serialize.ts","../src/interceptors/console.ts","../src/interceptors/xhr.ts","../src/interceptors/state-stores.ts","../src/interceptors/performance.ts","../src/interceptors/react-renders.ts","../src/interceptors/errors.ts","../src/interceptors/navigation.ts","../src/interceptors/clicks.ts"],"sourcesContent":["import { Transport } from './transport.js';\nimport { interceptFetch } from './interceptors/fetch.js';\nimport { interceptConsole } from './interceptors/console.js';\nimport { interceptXhr } from './interceptors/xhr.js';\nimport { interceptStateStores } from './interceptors/state-stores.js';\nimport { interceptPerformance } from './interceptors/performance.js';\nimport { interceptReactRenders } from './interceptors/react-renders.js';\nimport { interceptErrors } from './interceptors/errors.js';\nimport { interceptNavigation } from './interceptors/navigation.js';\nimport { interceptClicks } from './interceptors/clicks.js';\nimport { generateId, generateSessionId } from './utils/id.js';\nimport type { RuntimeScopeConfig, RuntimeEvent, DomSnapshotEvent, CustomEvent, UIInteractionEvent, UserContext } from './types.js';\n\nconst SDK_VERSION = '0.8.0';\n\n// Save original console.debug BEFORE interceptors patch it.\n// debug-level messages are hidden by default in Chrome DevTools.\nconst _log = console.debug.bind(console);\n\n// --- Double-init safety: Symbol.for() originals store ---\n// Survives HMR module reloads because Symbol.for() returns the same symbol across realms.\n// True originals are saved exactly once — never overwritten by subsequent connect() calls.\nconst ORIGINALS_KEY = Symbol.for('__runtimescope_originals__');\n\ninterface SavedOriginals {\n fetch: typeof globalThis.fetch;\n xhrOpen: typeof XMLHttpRequest.prototype.open;\n xhrSend: typeof XMLHttpRequest.prototype.send;\n xhrSetRequestHeader: typeof XMLHttpRequest.prototype.setRequestHeader;\n consoleMethods: Record<string, (...args: unknown[]) => void>;\n}\n\nfunction getOrSaveOriginals(): SavedOriginals {\n const g = globalThis as Record<symbol, SavedOriginals>;\n if (!g[ORIGINALS_KEY]) {\n g[ORIGINALS_KEY] = {\n fetch: window.fetch,\n xhrOpen: XMLHttpRequest.prototype.open,\n xhrSend: XMLHttpRequest.prototype.send,\n xhrSetRequestHeader: XMLHttpRequest.prototype.setRequestHeader,\n consoleMethods: {\n log: console.log.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n info: console.info.bind(console),\n debug: console.debug.bind(console),\n trace: console.trace.bind(console),\n },\n };\n }\n return g[ORIGINALS_KEY];\n}\n\nexport class RuntimeScope {\n private static transport: Transport | null = null;\n private static restoreFns: (() => void)[] = [];\n private static _sessionId: string | null = null;\n private static _state: 'stopped' | 'started' = 'stopped';\n private static _sampleRate: number = 1;\n private static _maxEventsPerSecond: number | undefined;\n private static _windowCount = 0;\n private static _windowStart = 0;\n private static _user: UserContext | undefined;\n\n static get sessionId(): string | null {\n return this._sessionId;\n }\n\n /** Returns true if the SDK is currently connected */\n static get isConnected(): boolean {\n return this._state === 'started';\n }\n\n /** Probabilistic + rate-limit sampling. Returns true if the event should be sent. */\n private static shouldSample(): boolean {\n // Probabilistic sampling\n if (this._sampleRate < 1 && Math.random() > this._sampleRate) {\n return false;\n }\n // Rate limiting\n if (this._maxEventsPerSecond !== undefined) {\n const now = Date.now();\n if (now - this._windowStart >= 1000) {\n this._windowCount = 0;\n this._windowStart = now;\n }\n if (this._windowCount >= this._maxEventsPerSecond) {\n return false;\n }\n this._windowCount++;\n }\n return true;\n }\n\n static connect(config: RuntimeScopeConfig = {}): void {\n if (config.enabled === false) return;\n\n // Guard against double-init: disconnect cleanly first\n if (this._state === 'started') {\n _log('[RuntimeScope] Already connected — disconnecting before re-init');\n this.disconnect();\n }\n\n const resolved = {\n serverUrl: config.serverUrl ?? config.endpoint ?? 'ws://localhost:9090',\n appName: config.appName ?? 'unknown',\n captureNetwork: config.captureNetwork ?? true,\n captureConsole: config.captureConsole ?? true,\n captureXhr: config.captureXhr ?? true,\n captureBody: config.captureBody ?? false,\n maxBodySize: config.maxBodySize ?? 65536,\n capturePerformance: config.capturePerformance ?? true,\n captureRenders: config.captureRenders ?? true,\n captureNavigation: config.captureNavigation ?? true,\n captureClicks: config.captureClicks ?? true,\n stores: config.stores ?? {},\n beforeSend: config.beforeSend,\n redactHeaders: config.redactHeaders ?? ['authorization', 'cookie'],\n batchSize: config.batchSize ?? 50,\n flushIntervalMs: config.flushIntervalMs ?? 100,\n };\n\n // Save true originals before any patching (idempotent — only saves on first call ever)\n getOrSaveOriginals();\n\n this._sessionId = generateSessionId();\n this._state = 'started';\n\n this.transport = new Transport({\n serverUrl: resolved.serverUrl,\n appName: resolved.appName,\n sessionId: this._sessionId,\n sdkVersion: SDK_VERSION,\n authToken: config.authToken,\n batchSize: resolved.batchSize,\n flushIntervalMs: resolved.flushIntervalMs,\n });\n\n // Sampling config\n this._sampleRate = config.sampleRate ?? 1;\n this._maxEventsPerSecond = config.maxEventsPerSecond;\n this._windowCount = 0;\n this._windowStart = Date.now();\n this._user = config.user;\n\n this.transport.connect();\n _log(`[RuntimeScope] SDK v${SDK_VERSION} initializing — app: ${resolved.appName}, server: ${resolved.serverUrl}`);\n\n const emit = (event: RuntimeEvent) => {\n // Session, custom, and UI events always pass through (never sampled out)\n if (event.eventType !== 'session' && event.eventType !== 'custom' && event.eventType !== 'ui') {\n if (!this.shouldSample()) return;\n }\n this.transport?.send(event);\n };\n\n // Send session event\n emit({\n eventId: generateId(),\n sessionId: this._sessionId,\n timestamp: Date.now(),\n eventType: 'session',\n appName: resolved.appName,\n connectedAt: Date.now(),\n sdkVersion: SDK_VERSION,\n buildMeta: config.buildMeta,\n user: this._user,\n });\n\n // Register command handler for server→SDK commands\n const sessionId = this._sessionId;\n this.transport.onCommand((cmd: { command: string; requestId: string; params?: Record<string, unknown> }) => {\n this.handleCommand(cmd, emit, sessionId);\n });\n\n // Fetch interceptor\n if (resolved.captureNetwork) {\n this.restoreFns.push(\n interceptFetch(emit, this._sessionId, resolved.redactHeaders, {\n captureBody: resolved.captureBody,\n maxBodySize: resolved.maxBodySize,\n beforeSend: resolved.beforeSend,\n })\n );\n }\n\n // XHR interceptor\n if (resolved.captureXhr) {\n this.restoreFns.push(\n interceptXhr(emit, this._sessionId, resolved.redactHeaders, {\n captureBody: resolved.captureBody,\n maxBodySize: resolved.maxBodySize,\n beforeSend: resolved.beforeSend,\n })\n );\n }\n\n // Console interceptor\n if (resolved.captureConsole) {\n this.restoreFns.push(\n interceptConsole(emit, this._sessionId, resolved.beforeSend)\n );\n // Also capture uncaught errors, resource load failures, and unhandled rejections\n // These appear in DevTools console but don't go through console.* API\n this.restoreFns.push(\n interceptErrors(emit, this._sessionId, resolved.beforeSend)\n );\n }\n\n // State store inspection\n if (Object.keys(resolved.stores).length > 0) {\n this.restoreFns.push(\n interceptStateStores(emit, this._sessionId, resolved.stores, {\n beforeSend: resolved.beforeSend,\n })\n );\n }\n\n // Performance metrics (Web Vitals)\n if (resolved.capturePerformance) {\n this.restoreFns.push(\n interceptPerformance(emit, this._sessionId, {\n beforeSend: resolved.beforeSend,\n })\n );\n }\n\n // React render tracking\n if (resolved.captureRenders) {\n this.restoreFns.push(\n interceptReactRenders(emit, this._sessionId, {\n beforeSend: resolved.beforeSend,\n })\n );\n }\n\n // Navigation / routing events (pushState, popstate, hashchange)\n if (resolved.captureNavigation) {\n this.restoreFns.push(\n interceptNavigation(emit, this._sessionId)\n );\n }\n\n // Click tracking (UI interaction breadcrumbs)\n if (resolved.captureClicks) {\n this.restoreFns.push(\n interceptClicks(emit, this._sessionId)\n );\n }\n\n const features = [\n resolved.captureNetwork && 'fetch',\n resolved.captureXhr && 'xhr',\n resolved.captureConsole && 'console, errors',\n Object.keys(resolved.stores).length > 0 && 'state',\n resolved.capturePerformance && 'performance',\n resolved.captureRenders && 'renders',\n resolved.captureNavigation && 'navigation',\n resolved.captureClicks && 'clicks',\n ].filter(Boolean);\n _log(`[RuntimeScope] Interceptors active — ${features.join(', ')}`);\n if (this._sampleRate < 1) {\n _log(`[RuntimeScope] Sampling at ${(this._sampleRate * 100).toFixed(0)}%`);\n }\n if (this._maxEventsPerSecond !== undefined) {\n _log(`[RuntimeScope] Rate limited to ${this._maxEventsPerSecond} events/sec`);\n }\n }\n\n private static handleCommand(\n cmd: { command: string; requestId: string; params?: Record<string, unknown> },\n emit: (event: RuntimeEvent) => void,\n sessionId: string\n ): void {\n switch (cmd.command) {\n case 'capture_dom_snapshot': {\n const maxSize = (cmd.params?.maxSize as number) ?? 500_000;\n let html = document.documentElement.outerHTML;\n const truncated = html.length > maxSize;\n if (truncated) html = html.slice(0, maxSize);\n\n const snapshot: DomSnapshotEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'dom_snapshot',\n html,\n url: window.location.href,\n viewport: { width: window.innerWidth, height: window.innerHeight },\n scrollPosition: { x: window.scrollX, y: window.scrollY },\n elementCount: document.querySelectorAll('*').length,\n truncated,\n };\n\n emit(snapshot);\n this.transport?.sendCommandResponse(cmd.requestId, cmd.command, snapshot);\n break;\n }\n default:\n this.transport?.sendCommandResponse(cmd.requestId, cmd.command, { error: 'Unknown command' });\n }\n }\n\n /** Alias for `connect` — used by script-tag snippets */\n static init(config: RuntimeScopeConfig = {}): void {\n return this.connect(config);\n }\n\n /**\n * Track a custom business/product event.\n * Use this to mark meaningful moments in your app (e.g. user actions, conversions).\n * These events are correlated with all other telemetry (network, console, state)\n * to build causal chains and detect failures.\n */\n static track(name: string, properties?: Record<string, unknown>): void {\n if (!this.transport || !this._sessionId) return;\n\n const event: CustomEvent = {\n eventId: generateId(),\n sessionId: this._sessionId,\n timestamp: Date.now(),\n eventType: 'custom',\n name,\n properties,\n };\n\n this.transport.send(event);\n }\n\n /**\n * Set user context attached to the current session.\n * Call this after login to associate events with a specific user.\n * Pass null to clear user context.\n */\n static setUser(user: UserContext | null): void {\n this._user = user ?? undefined;\n\n // Send an updated session event so the collector picks up the user context\n if (this.transport && this._sessionId) {\n this.transport.send({\n eventId: generateId(),\n sessionId: this._sessionId,\n timestamp: Date.now(),\n eventType: 'session',\n appName: 'user_update',\n connectedAt: Date.now(),\n sdkVersion: SDK_VERSION,\n user: this._user,\n });\n }\n }\n\n /**\n * Add a manual breadcrumb to the event trail.\n * Use this to mark meaningful moments that help debug errors\n * (e.g., \"user opened settings\", \"form validated\", \"retry attempt 2\").\n */\n static addBreadcrumb(\n message: string,\n data?: Record<string, unknown>\n ): void {\n if (!this.transport || !this._sessionId) return;\n\n const event: UIInteractionEvent = {\n eventId: generateId(),\n sessionId: this._sessionId,\n timestamp: Date.now(),\n eventType: 'ui',\n action: 'breadcrumb',\n target: 'manual',\n text: message,\n ...(data && { data }),\n };\n\n this.transport.send(event);\n }\n\n static disconnect(): void {\n // Run all interceptor restore functions\n for (const fn of this.restoreFns) {\n try { fn(); } catch { /* ensure all restores run */ }\n }\n this.restoreFns = [];\n\n // Safety net: if restore functions didn't fully unwind (e.g. another library\n // patched over us), fall back to true originals saved via Symbol.for()\n const originals = (globalThis as Record<symbol, SavedOriginals>)[ORIGINALS_KEY];\n if (originals) {\n // Only restore if current value is NOT the original (i.e. we or nobody else patched)\n if (window.fetch !== originals.fetch) {\n window.fetch = originals.fetch;\n }\n for (const [level, fn] of Object.entries(originals.consoleMethods)) {\n const c = console as unknown as Record<string, unknown>;\n if (c[level] !== fn) {\n c[level] = fn;\n }\n }\n }\n\n this.transport?.disconnect();\n this.transport = null;\n this._sessionId = null;\n this._state = 'stopped';\n }\n}\n\nexport default RuntimeScope;\nexport type {\n RuntimeScopeConfig,\n BuildMeta,\n UserContext,\n NetworkEvent,\n ConsoleEvent,\n SessionEvent,\n StateEvent,\n RenderEvent,\n DomSnapshotEvent,\n PerformanceEvent,\n NavigationEvent,\n CustomEvent,\n UIInteractionEvent,\n RuntimeEvent,\n} from './types.js';\n","import type { RuntimeEvent } from './types.js';\n\n// Save original console.debug BEFORE any interceptors patch it.\n// debug-level messages are hidden by default in Chrome DevTools.\nconst _log = console.debug.bind(console);\n\ninterface TransportConfig {\n serverUrl: string;\n appName: string;\n sessionId: string;\n sdkVersion: string;\n authToken?: string;\n batchSize: number;\n flushIntervalMs: number;\n}\n\nexport class Transport {\n private ws: WebSocket | null = null;\n private batch: RuntimeEvent[] = [];\n private offlineQueue: RuntimeEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private reconnectDelay = 500;\n private reconnectAttempt = 0;\n private connected = false;\n private stopped = false;\n private config: TransportConfig;\n private commandHandler: ((cmd: { command: string; requestId: string; params?: Record<string, unknown> }) => void) | null = null;\n private hasEverConnected = false;\n private connectionWarningTimer: ReturnType<typeof setTimeout> | null = null;\n private visibilityHandler: (() => void) | null = null;\n private onlineHandler: (() => void) | null = null;\n\n private static readonly MAX_OFFLINE_QUEUE = 1000;\n private static readonly MAX_RECONNECT_DELAY = 30_000;\n private static readonly CONNECTION_WARNING_DELAY = 10_000;\n /** First N retries use a fast 500ms delay before switching to exponential backoff */\n private static readonly FAST_RETRY_COUNT = 3;\n private static readonly FAST_RETRY_DELAY = 500;\n\n constructor(config: TransportConfig) {\n this.config = config;\n }\n\n connect(): void {\n this.stopped = false;\n this.doConnect();\n\n // Warn if we never establish a connection within 10 seconds\n this.connectionWarningTimer = setTimeout(() => {\n if (!this.hasEverConnected && !this.stopped) {\n console.warn(\n `[RuntimeScope] SDK has not connected to ${this.config.serverUrl} after 10s. ` +\n `Is the collector running? Start it with: npx @runtimescope/collector`\n );\n }\n }, Transport.CONNECTION_WARNING_DELAY);\n\n // When the user switches back to this tab, try to reconnect immediately\n if (typeof document !== 'undefined') {\n this.visibilityHandler = () => {\n if (document.visibilityState === 'visible' && !this.connected && !this.stopped) {\n _log('[RuntimeScope] Tab visible — attempting immediate reconnect');\n this.resetReconnectState();\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.doConnect();\n }\n };\n document.addEventListener('visibilitychange', this.visibilityHandler);\n }\n\n // Reconnect immediately when network comes back online\n if (typeof window !== 'undefined') {\n this.onlineHandler = () => {\n if (!this.connected && !this.stopped) {\n _log('[RuntimeScope] Network online — attempting immediate reconnect');\n this.resetReconnectState();\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.doConnect();\n }\n };\n window.addEventListener('online', this.onlineHandler);\n }\n }\n\n private doConnect(): void {\n if (this.stopped) return;\n\n try {\n this.ws = new WebSocket(this.config.serverUrl);\n } catch {\n this.scheduleReconnect();\n return;\n }\n\n this.ws.onmessage = (event: MessageEvent) => {\n try {\n const msg = JSON.parse(String(event.data));\n if (msg.type === 'command' && msg.payload && this.commandHandler) {\n this.commandHandler(msg.payload);\n }\n // Handle server restart notice — reset backoff for immediate reconnect\n if (msg.type === '__server_restart') {\n _log('[RuntimeScope] Server restart notice received — will reconnect immediately');\n this.resetReconnectState();\n }\n // Handle auth rejection — stop reconnecting\n if (msg.type === 'error' && msg.payload?.code === 'AUTH_FAILED') {\n _log('[RuntimeScope] Authentication failed — stopping reconnection');\n this.stopped = true;\n }\n } catch {\n // Ignore unparseable messages\n }\n };\n\n this.ws.onopen = () => {\n this.connected = true;\n this.hasEverConnected = true;\n this.resetReconnectState();\n _log(`[RuntimeScope] Connected to ${this.config.serverUrl}`);\n\n // Send handshake\n this.sendRaw({\n type: 'handshake',\n payload: {\n appName: this.config.appName,\n sdkVersion: this.config.sdkVersion,\n sessionId: this.config.sessionId,\n ...(this.config.authToken ? { authToken: this.config.authToken } : {}),\n },\n timestamp: Date.now(),\n sessionId: this.config.sessionId,\n });\n\n // Flush offline queue\n if (this.offlineQueue.length > 0) {\n const queued = this.offlineQueue.splice(0);\n for (const event of queued) {\n this.batch.push(event);\n }\n this.flush();\n }\n\n // Start periodic flush\n this.flushTimer = setInterval(() => this.flush(), this.config.flushIntervalMs);\n };\n\n this.ws.onclose = () => {\n this.connected = false;\n this.clearFlushTimer();\n if (!this.stopped) {\n _log(`[RuntimeScope] Disconnected, will reconnect...`);\n this.scheduleReconnect();\n }\n };\n\n this.ws.onerror = () => {\n _log(`[RuntimeScope] WebSocket error connecting to ${this.config.serverUrl}`);\n };\n }\n\n send(event: RuntimeEvent): void {\n if (this.connected) {\n this.batch.push(event);\n if (this.batch.length >= this.config.batchSize) {\n this.flush();\n }\n } else {\n // Queue for when we reconnect\n if (this.offlineQueue.length < Transport.MAX_OFFLINE_QUEUE) {\n this.offlineQueue.push(event);\n }\n }\n }\n\n private flush(): void {\n if (this.batch.length === 0 || !this.connected || !this.ws) return;\n\n const events = this.batch.splice(0);\n this.sendRaw({\n type: 'event',\n payload: { events },\n timestamp: Date.now(),\n sessionId: this.config.sessionId,\n });\n }\n\n private sendRaw(msg: unknown): void {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n try {\n this.ws.send(JSON.stringify(msg));\n } catch {\n // Drop silently — reconnect will handle it\n }\n }\n }\n\n private scheduleReconnect(): void {\n if (this.stopped || this.reconnectTimer) return;\n\n this.reconnectAttempt++;\n\n // Fast retries for the first few attempts (collector restart gap is typically 1-3s)\n let delay: number;\n if (this.reconnectAttempt <= Transport.FAST_RETRY_COUNT) {\n delay = Transport.FAST_RETRY_DELAY;\n } else {\n // Exponential backoff with jitter after fast retries exhausted\n const jitter = this.reconnectDelay * 0.25 * (Math.random() * 2 - 1);\n delay = Math.min(this.reconnectDelay + jitter, Transport.MAX_RECONNECT_DELAY);\n this.reconnectDelay = Math.min(this.reconnectDelay * 2, Transport.MAX_RECONNECT_DELAY);\n }\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.doConnect();\n }, delay);\n }\n\n private resetReconnectState(): void {\n this.reconnectDelay = 500;\n this.reconnectAttempt = 0;\n }\n\n private clearFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n }\n\n onCommand(handler: (cmd: { command: string; requestId: string; params?: Record<string, unknown> }) => void): void {\n this.commandHandler = handler;\n }\n\n sendCommandResponse(requestId: string, command: string, payload: unknown): void {\n this.sendRaw({\n type: 'command_response',\n requestId,\n command,\n payload,\n timestamp: Date.now(),\n sessionId: this.config.sessionId,\n });\n }\n\n disconnect(): void {\n this.stopped = true;\n this.clearFlushTimer();\n\n if (this.connectionWarningTimer) {\n clearTimeout(this.connectionWarningTimer);\n this.connectionWarningTimer = null;\n }\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.visibilityHandler && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', this.visibilityHandler);\n this.visibilityHandler = null;\n }\n\n if (this.onlineHandler && typeof window !== 'undefined') {\n window.removeEventListener('online', this.onlineHandler);\n this.onlineHandler = null;\n }\n\n // Final flush before closing\n this.flush();\n\n if (this.ws) {\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.close();\n this.ws = null;\n }\n\n this.connected = false;\n this.batch = [];\n this.offlineQueue = [];\n }\n}\n","/** 16-char random hex string for event IDs. */\nexport function generateId(): string {\n const arr = new Uint8Array(8);\n crypto.getRandomValues(arr);\n return Array.from(arr, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/** 32-char random hex string for session IDs. */\nexport function generateSessionId(): string {\n const arr = new Uint8Array(16);\n crypto.getRandomValues(arr);\n return Array.from(arr, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n","import { generateId } from '../utils/id.js';\nimport type { NetworkEvent, GraphQLOperation, RuntimeEvent } from '../types.js';\n\ntype EmitFn = (event: NetworkEvent) => void;\n\nexport interface FetchInterceptorOptions {\n captureBody?: boolean;\n maxBodySize?: number;\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null;\n}\n\n// Track URLs intercepted by fetch to prevent XHR double-counting (fetch polyfill case).\n// Uses a Map with timestamps + single sweeper instead of per-request setTimeout.\nconst MAX_INFLIGHT = 1000;\nconst INFLIGHT_TTL_MS = 10_000;\nconst SWEEP_INTERVAL_MS = 5_000;\n\nexport const fetchInterceptedRequests = new Map<string, number>();\nlet sweepTimer: ReturnType<typeof setInterval> | null = null;\nlet sweepRefCount = 0;\n\nfunction startSweeper(): void {\n sweepRefCount++;\n if (sweepTimer) return;\n sweepTimer = setInterval(() => {\n const cutoff = Date.now() - INFLIGHT_TTL_MS;\n for (const [key, ts] of fetchInterceptedRequests) {\n if (ts < cutoff) fetchInterceptedRequests.delete(key);\n }\n }, SWEEP_INTERVAL_MS);\n}\n\nfunction stopSweeper(): void {\n sweepRefCount--;\n if (sweepRefCount <= 0 && sweepTimer) {\n clearInterval(sweepTimer);\n sweepTimer = null;\n sweepRefCount = 0;\n }\n}\n\nexport function interceptFetch(\n emit: EmitFn,\n sessionId: string,\n redactHeaders: string[],\n options?: FetchInterceptorOptions\n): () => void {\n const originalFetch = window.fetch;\n const redactSet = new Set(redactHeaders.map((h) => h.toLowerCase()));\n const captureBody = options?.captureBody ?? false;\n const maxBodySize = options?.maxBodySize ?? 65536;\n\n startSweeper();\n\n window.fetch = async function (\n input: RequestInfo | URL,\n init?: RequestInit\n ): Promise<Response> {\n const startTime = performance.now();\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n const method = (init?.method ?? 'GET').toUpperCase();\n\n const requestHeaders = extractHeaders(init?.headers, redactSet);\n const requestBodySize = estimateBodySize(init?.body);\n const graphqlOperation = detectGraphQL(init?.body);\n\n // Capture request body if enabled\n let requestBody: string | undefined;\n if (captureBody && init?.body) {\n requestBody = serializeBody(init.body, maxBodySize);\n }\n\n // Mark request to prevent XHR interceptor double-counting (fetch polyfill case)\n const requestKey = `${method}:${url}:${startTime}`;\n // Evict oldest entry if at capacity\n if (fetchInterceptedRequests.size >= MAX_INFLIGHT) {\n const oldest = fetchInterceptedRequests.keys().next().value;\n if (oldest !== undefined) fetchInterceptedRequests.delete(oldest);\n }\n fetchInterceptedRequests.set(requestKey, Date.now());\n\n try {\n const response = await originalFetch.call(window, input, init);\n const duration = performance.now() - startTime;\n\n const responseBodySize = parseInt(\n response.headers.get('content-length') || '0',\n 10\n );\n const responseHeaders = extractResponseHeaders(response.headers, redactSet);\n\n // Capture response body if enabled\n let responseBody: string | undefined;\n if (captureBody) {\n try {\n const clone = response.clone();\n const text = await clone.text();\n responseBody = text.length > maxBodySize ? text.slice(0, maxBodySize) : text;\n } catch {\n // CORS or stream-locked — skip body capture\n }\n }\n\n const event: NetworkEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'network',\n url,\n method,\n status: response.status,\n requestHeaders,\n responseHeaders,\n requestBodySize,\n responseBodySize,\n duration,\n ttfb: duration,\n graphqlOperation,\n requestBody,\n responseBody,\n source: 'fetch',\n };\n\n if (options?.beforeSend) {\n const filtered = options.beforeSend(event);\n if (filtered) emit(filtered as NetworkEvent);\n } else {\n emit(event);\n }\n\n return response;\n } catch (error) {\n const duration = performance.now() - startTime;\n\n let errorPhase: 'error' | 'abort' | 'timeout' = 'error';\n let errorMessage = '';\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n errorPhase = 'abort';\n errorMessage = 'Request aborted';\n } else if (error instanceof Error) {\n errorMessage = error.message;\n }\n\n const event: NetworkEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'network',\n url,\n method,\n status: 0,\n requestHeaders,\n responseHeaders: {},\n requestBodySize,\n responseBodySize: 0,\n duration,\n ttfb: 0,\n graphqlOperation,\n requestBody,\n errorPhase,\n errorMessage,\n source: 'fetch',\n };\n\n if (options?.beforeSend) {\n const filtered = options.beforeSend(event);\n if (filtered) emit(filtered as NetworkEvent);\n } else {\n emit(event);\n }\n\n throw error;\n }\n };\n\n return () => {\n window.fetch = originalFetch;\n stopSweeper();\n };\n}\n\nfunction extractHeaders(\n headers: HeadersInit | undefined,\n redactSet: Set<string>\n): Record<string, string> {\n const result: Record<string, string> = {};\n if (!headers) return result;\n\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n result[key] = redactSet.has(key.toLowerCase()) ? '[REDACTED]' : value;\n });\n } else if (Array.isArray(headers)) {\n for (const [key, value] of headers) {\n result[key] = redactSet.has(key.toLowerCase()) ? '[REDACTED]' : value;\n }\n } else {\n for (const [key, value] of Object.entries(headers)) {\n result[key] = redactSet.has(key.toLowerCase()) ? '[REDACTED]' : value;\n }\n }\n\n return result;\n}\n\nfunction extractResponseHeaders(\n headers: Headers,\n redactSet: Set<string>\n): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key] = redactSet.has(key.toLowerCase()) ? '[REDACTED]' : value;\n });\n return result;\n}\n\nfunction estimateBodySize(body: BodyInit | null | undefined): number {\n if (!body) return 0;\n if (typeof body === 'string') return new Blob([body]).size;\n if (body instanceof Blob) return body.size;\n if (body instanceof ArrayBuffer) return body.byteLength;\n if (ArrayBuffer.isView(body)) return body.byteLength;\n if (body instanceof FormData) return 0;\n if (body instanceof URLSearchParams) return new Blob([body.toString()]).size;\n return 0;\n}\n\nfunction serializeBody(body: BodyInit | null | undefined, maxSize: number): string | undefined {\n if (!body) return undefined;\n if (typeof body === 'string') return body.length > maxSize ? body.slice(0, maxSize) : body;\n if (body instanceof URLSearchParams) {\n const s = body.toString();\n return s.length > maxSize ? s.slice(0, maxSize) : s;\n }\n if (body instanceof FormData) return '[FormData]';\n if (body instanceof Blob) return `[Blob ${body.size} bytes]`;\n if (body instanceof ArrayBuffer) return `[ArrayBuffer ${body.byteLength} bytes]`;\n if (ArrayBuffer.isView(body)) return `[TypedArray ${body.byteLength} bytes]`;\n return undefined;\n}\n\nfunction detectGraphQL(body: BodyInit | null | undefined): GraphQLOperation | undefined {\n if (!body || typeof body !== 'string') return undefined;\n\n try {\n const parsed = JSON.parse(body);\n if (typeof parsed.query === 'string') {\n const trimmed = parsed.query.trim();\n let type: GraphQLOperation['type'] = 'query';\n if (trimmed.startsWith('mutation')) type = 'mutation';\n else if (trimmed.startsWith('subscription')) type = 'subscription';\n\n const name = parsed.operationName || extractOperationName(trimmed) || 'anonymous';\n return { type, name };\n }\n } catch {\n // Not GraphQL\n }\n\n return undefined;\n}\n\nfunction extractOperationName(query: string): string | undefined {\n const match = query.match(/^(?:query|mutation|subscription)\\s+(\\w+)/);\n return match?.[1];\n}\n","/** Safely serialize a value, handling circular references, functions, symbols, and errors. */\nexport function safeSerialize(value: unknown, maxDepth = 5): unknown {\n const seen = new WeakSet();\n\n function walk(val: unknown, depth: number): unknown {\n if (depth > maxDepth) return '[max depth]';\n if (val === null || val === undefined) return val;\n if (typeof val === 'function') return `[Function: ${val.name || 'anonymous'}]`;\n if (typeof val === 'symbol') return val.toString();\n if (typeof val === 'bigint') return val.toString();\n if (typeof val !== 'object') return val;\n\n if (val instanceof Error) {\n return { name: val.name, message: val.message, stack: val.stack };\n }\n if (val instanceof Date) {\n return val.toISOString();\n }\n if (val instanceof RegExp) {\n return val.toString();\n }\n\n if (seen.has(val as object)) return '[Circular]';\n seen.add(val as object);\n\n if (Array.isArray(val)) {\n return val.map((v) => walk(v, depth + 1));\n }\n\n const result: Record<string, unknown> = {};\n let keys: string[];\n try {\n keys = Object.keys(val as Record<string, unknown>);\n } catch {\n return '[Object]';\n }\n // Limit keys to avoid huge React fiber-like objects\n const maxKeys = 50;\n for (let i = 0; i < Math.min(keys.length, maxKeys); i++) {\n try {\n result[keys[i]] = walk((val as Record<string, unknown>)[keys[i]], depth + 1);\n } catch {\n result[keys[i]] = '[Error accessing property]';\n }\n }\n if (keys.length > maxKeys) result['...'] = `${keys.length - maxKeys} more keys`;\n return result;\n }\n\n return walk(value, 0);\n}\n","import { generateId } from '../utils/id.js';\nimport { safeSerialize } from '../utils/serialize.js';\nimport type { ConsoleEvent, ConsoleLevel, RuntimeEvent } from '../types.js';\n\ntype EmitFn = (event: ConsoleEvent) => void;\n\nconst LEVELS: ConsoleLevel[] = ['log', 'warn', 'error', 'info', 'debug', 'trace'];\n\nexport function interceptConsole(\n emit: EmitFn,\n sessionId: string,\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null\n): () => void {\n const originals: Record<string, (...args: unknown[]) => void> = {};\n\n for (const level of LEVELS) {\n originals[level] = console[level].bind(console);\n\n console[level] = (...args: unknown[]) => {\n const message = args\n .map((a) => (typeof a === 'string' ? a : stringifyArg(a)))\n .join(' ');\n\n const event: ConsoleEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'console',\n level,\n message,\n args: args.map((a) => safeSerialize(a, 3)),\n stackTrace:\n level === 'error' || level === 'trace'\n ? new Error().stack?.split('\\n').slice(2).join('\\n')\n : undefined,\n sourceFile: undefined,\n };\n\n if (beforeSend) {\n const filtered = beforeSend(event);\n if (filtered) emit(filtered as ConsoleEvent);\n } else {\n emit(event);\n }\n\n // Call original — MUST come after emit, and MUST use saved reference\n originals[level](...args);\n };\n }\n\n return () => {\n for (const level of LEVELS) {\n console[level] = originals[level];\n }\n };\n}\n\nfunction stringifyArg(arg: unknown): string {\n try {\n return JSON.stringify(arg);\n } catch {\n return String(arg);\n }\n}\n","import { generateId } from '../utils/id.js';\nimport type { NetworkEvent, GraphQLOperation, RuntimeEvent } from '../types.js';\nimport { fetchInterceptedRequests } from './fetch.js';\n\ntype EmitFn = (event: NetworkEvent) => void;\n\nexport interface XhrInterceptorOptions {\n captureBody?: boolean;\n maxBodySize?: number;\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null;\n}\n\ninterface AugmentedXHR extends XMLHttpRequest {\n __rs_method?: string;\n __rs_url?: string;\n __rs_headers?: Record<string, string>;\n __rs_start?: number;\n __rs_body?: string;\n __rs_fetchIntercepted?: boolean;\n}\n\nexport function interceptXhr(\n emit: EmitFn,\n sessionId: string,\n redactHeaders: string[],\n options?: XhrInterceptorOptions\n): () => void {\n const redactSet = new Set(redactHeaders.map((h) => h.toLowerCase()));\n const captureBody = options?.captureBody ?? false;\n const maxBodySize = options?.maxBodySize ?? 65536;\n\n // Global AbortController for teardown — aborts all outstanding XHR listeners on disconnect\n const globalAbort = new AbortController();\n\n const origOpen = XMLHttpRequest.prototype.open;\n const origSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;\n const origSend = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (\n this: AugmentedXHR,\n method: string,\n url: string | URL\n ) {\n this.__rs_method = method.toUpperCase();\n this.__rs_url = typeof url === 'string' ? url : url.href;\n this.__rs_headers = {};\n // eslint-disable-next-line prefer-rest-params\n return origOpen.apply(this, arguments as unknown as Parameters<typeof origOpen>);\n };\n\n XMLHttpRequest.prototype.setRequestHeader = function (\n this: AugmentedXHR,\n name: string,\n value: string\n ) {\n if (this.__rs_headers) {\n this.__rs_headers[name.toLowerCase()] = redactSet.has(name.toLowerCase())\n ? '[REDACTED]'\n : value;\n }\n return origSetRequestHeader.call(this, name, value);\n };\n\n XMLHttpRequest.prototype.send = function (\n this: AugmentedXHR,\n body?: Document | XMLHttpRequestBodyInit | null\n ) {\n // Skip if this request was already captured by the fetch interceptor\n const method = this.__rs_method ?? 'GET';\n const url = this.__rs_url ?? '';\n const requestKey = `${method}:${url}`;\n let alreadyIntercepted = false;\n for (const k of fetchInterceptedRequests.keys()) {\n if (k.startsWith(requestKey)) { alreadyIntercepted = true; break; }\n }\n if (alreadyIntercepted) {\n this.__rs_fetchIntercepted = true;\n return origSend.call(this, body);\n }\n\n const requestHeaders = { ...(this.__rs_headers ?? {}) };\n const startTime = performance.now();\n\n // Capture request body\n let requestBody: string | undefined;\n let requestBodySize = 0;\n if (body) {\n if (typeof body === 'string') {\n requestBodySize = new Blob([body]).size;\n if (captureBody) {\n requestBody = body.length > maxBodySize ? body.slice(0, maxBodySize) : body;\n }\n } else if (body instanceof Blob) {\n requestBodySize = body.size;\n if (captureBody) requestBody = `[Blob ${body.size} bytes]`;\n } else if (body instanceof ArrayBuffer) {\n requestBodySize = body.byteLength;\n if (captureBody) requestBody = `[ArrayBuffer ${body.byteLength} bytes]`;\n } else if (body instanceof FormData) {\n if (captureBody) requestBody = '[FormData]';\n } else if (body instanceof URLSearchParams) {\n const s = body.toString();\n requestBodySize = new Blob([s]).size;\n if (captureBody) requestBody = s.length > maxBodySize ? s.slice(0, maxBodySize) : s;\n }\n }\n\n // Detect GraphQL\n const graphqlOperation = detectGraphQL(body);\n\n const emitEvent = (overrides: Partial<NetworkEvent>) => {\n const event: NetworkEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'network',\n url,\n method,\n status: 0,\n requestHeaders,\n responseHeaders: {},\n requestBodySize,\n responseBodySize: 0,\n duration: 0,\n ttfb: 0,\n graphqlOperation,\n requestBody,\n source: 'xhr',\n ...overrides,\n };\n\n if (options?.beforeSend) {\n const filtered = options.beforeSend(event);\n if (filtered) emit(filtered as NetworkEvent);\n } else {\n emit(event);\n }\n };\n\n // Single loadend listener covers all terminal states (load, error, abort, timeout).\n // { once: true } auto-removes per-request; globalAbort.signal enables bulk teardown.\n this.addEventListener('loadend', () => {\n const duration = performance.now() - startTime;\n\n if (this.status > 0) {\n // Successful completion (includes HTTP errors like 4xx/5xx)\n const responseHeaders = parseResponseHeaders(this.getAllResponseHeaders(), redactSet);\n const responseBodySize = parseInt(\n this.getResponseHeader('content-length') || '0',\n 10\n );\n\n let responseBody: string | undefined;\n if (captureBody && (this.responseType === '' || this.responseType === 'text')) {\n try {\n const text = this.responseText;\n responseBody = text.length > maxBodySize ? text.slice(0, maxBodySize) : text;\n } catch {\n // responseText not available for non-text types\n }\n }\n\n emitEvent({\n status: this.status,\n responseHeaders,\n responseBodySize,\n responseBody,\n duration,\n ttfb: duration,\n });\n } else {\n // Network error, abort, or timeout — status is 0\n let errorPhase: 'error' | 'abort' | 'timeout' = 'error';\n let errorMessage = 'Network error';\n\n if (this.readyState === 0 || (this as XMLHttpRequest).status === 0) {\n // Distinguish abort vs timeout vs network error\n // XHR sets readyState to UNSENT (0) on abort\n }\n\n // Check specific failure modes\n if (this.timeout > 0 && duration >= this.timeout) {\n errorPhase = 'timeout';\n errorMessage = `Request timed out after ${this.timeout}ms`;\n }\n\n emitEvent({ duration, errorPhase, errorMessage });\n }\n }, { once: true, signal: globalAbort.signal });\n\n return origSend.call(this, body);\n };\n\n return () => {\n // Abort all outstanding XHR listeners from this interceptor instance\n globalAbort.abort();\n XMLHttpRequest.prototype.open = origOpen;\n XMLHttpRequest.prototype.setRequestHeader = origSetRequestHeader;\n XMLHttpRequest.prototype.send = origSend;\n };\n}\n\nfunction parseResponseHeaders(\n raw: string,\n redactSet: Set<string>\n): Record<string, string> {\n const result: Record<string, string> = {};\n if (!raw) return result;\n\n for (const line of raw.trim().split(/[\\r\\n]+/)) {\n const idx = line.indexOf(':');\n if (idx === -1) continue;\n const key = line.slice(0, idx).trim().toLowerCase();\n const value = line.slice(idx + 1).trim();\n result[key] = redactSet.has(key) ? '[REDACTED]' : value;\n }\n\n return result;\n}\n\nfunction detectGraphQL(\n body: Document | XMLHttpRequestBodyInit | null | undefined\n): GraphQLOperation | undefined {\n if (!body || typeof body !== 'string') return undefined;\n\n try {\n const parsed = JSON.parse(body);\n if (typeof parsed.query === 'string') {\n const trimmed = parsed.query.trim();\n let type: GraphQLOperation['type'] = 'query';\n if (trimmed.startsWith('mutation')) type = 'mutation';\n else if (trimmed.startsWith('subscription')) type = 'subscription';\n\n const name =\n parsed.operationName || extractOperationName(trimmed) || 'anonymous';\n return { type, name };\n }\n } catch {\n // Not GraphQL\n }\n\n return undefined;\n}\n\nfunction extractOperationName(query: string): string | undefined {\n const match = query.match(/^(?:query|mutation|subscription)\\s+(\\w+)/);\n return match?.[1];\n}\n","import { generateId } from '../utils/id.js';\nimport { safeSerialize } from '../utils/serialize.js';\nimport type { StateEvent, RuntimeEvent } from '../types.js';\n\ntype EmitFn = (event: StateEvent) => void;\n\nexport interface StateStoreOptions {\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null;\n}\n\ninterface ZustandStore {\n getState: () => unknown;\n setState: (partial: unknown) => void;\n subscribe: (listener: (state: unknown, prevState: unknown) => void) => () => void;\n}\n\ninterface ReduxStore {\n getState: () => unknown;\n dispatch: (action: unknown) => unknown;\n subscribe: (listener: () => void) => () => void;\n}\n\ntype DetectedLibrary = 'zustand' | 'redux' | 'unknown';\n\nexport function interceptStateStores(\n emit: EmitFn,\n sessionId: string,\n stores: Record<string, unknown>,\n options?: StateStoreOptions\n): () => void {\n const unsubscribers: (() => void)[] = [];\n const originalDispatches: Map<string, ReduxStore['dispatch']> = new Map();\n\n for (const [storeId, store] of Object.entries(stores)) {\n const library = detectLibrary(store);\n\n if (library === 'zustand') {\n const zustand = store as ZustandStore;\n\n // Emit initial state\n emitState(emit, sessionId, options?.beforeSend, {\n storeId,\n library,\n phase: 'init',\n state: safeSerialize(zustand.getState(), 4),\n });\n\n // Subscribe to changes\n const unsub = zustand.subscribe((state, prevState) => {\n const diff = shallowDiff(prevState, state);\n emitState(emit, sessionId, options?.beforeSend, {\n storeId,\n library,\n phase: 'update',\n state: safeSerialize(state, 4),\n previousState: safeSerialize(prevState, 4),\n diff: diff ? safeSerialize(diff, 3) as Record<string, { from: unknown; to: unknown }> : undefined,\n });\n });\n\n unsubscribers.push(unsub);\n } else if (library === 'redux') {\n const redux = store as ReduxStore;\n\n // Emit initial state\n emitState(emit, sessionId, options?.beforeSend, {\n storeId,\n library,\n phase: 'init',\n state: safeSerialize(redux.getState(), 4),\n });\n\n // Wrap dispatch to capture actions\n let lastAction: { type: string; payload?: unknown } | undefined;\n const origDispatch = redux.dispatch.bind(redux);\n originalDispatches.set(storeId, origDispatch);\n\n (redux as { dispatch: ReduxStore['dispatch'] }).dispatch = (action: unknown) => {\n if (action && typeof action === 'object' && 'type' in action) {\n lastAction = {\n type: String((action as { type: unknown }).type),\n payload: (action as { payload?: unknown }).payload,\n };\n }\n return origDispatch(action);\n };\n\n // Subscribe to state changes\n let prevState = redux.getState();\n const unsub = redux.subscribe(() => {\n const state = redux.getState();\n const diff = shallowDiff(prevState, state);\n\n emitState(emit, sessionId, options?.beforeSend, {\n storeId,\n library,\n phase: 'update',\n state: safeSerialize(state, 4),\n previousState: safeSerialize(prevState, 4),\n diff: diff ? safeSerialize(diff, 3) as Record<string, { from: unknown; to: unknown }> : undefined,\n action: lastAction ? safeSerialize(lastAction, 3) as { type: string; payload?: unknown } : undefined,\n });\n\n prevState = state;\n lastAction = undefined;\n });\n\n unsubscribers.push(unsub);\n }\n }\n\n return () => {\n for (const unsub of unsubscribers) unsub();\n\n // Restore original Redux dispatch functions\n for (const [storeId, origDispatch] of originalDispatches) {\n const store = stores[storeId] as ReduxStore;\n if (store) {\n (store as { dispatch: ReduxStore['dispatch'] }).dispatch = origDispatch;\n }\n }\n };\n}\n\nfunction detectLibrary(store: unknown): DetectedLibrary {\n if (!store || typeof store !== 'object') return 'unknown';\n const s = store as Record<string, unknown>;\n\n // Zustand: function-like with getState, setState, subscribe\n if (\n typeof s.getState === 'function' &&\n typeof s.setState === 'function' &&\n typeof s.subscribe === 'function'\n ) {\n return 'zustand';\n }\n\n // Redux: object with dispatch, getState, subscribe\n if (\n typeof s.dispatch === 'function' &&\n typeof s.getState === 'function' &&\n typeof s.subscribe === 'function'\n ) {\n return 'redux';\n }\n\n return 'unknown';\n}\n\nfunction shallowDiff(\n prev: unknown,\n curr: unknown\n): Record<string, { from: unknown; to: unknown }> | null {\n if (!prev || !curr || typeof prev !== 'object' || typeof curr !== 'object') {\n return null;\n }\n\n const diff: Record<string, { from: unknown; to: unknown }> = {};\n const prevObj = prev as Record<string, unknown>;\n const currObj = curr as Record<string, unknown>;\n const allKeys = new Set([...Object.keys(prevObj), ...Object.keys(currObj)]);\n\n for (const key of allKeys) {\n if (prevObj[key] !== currObj[key]) {\n diff[key] = { from: prevObj[key], to: currObj[key] };\n }\n }\n\n return Object.keys(diff).length > 0 ? diff : null;\n}\n\nfunction emitState(\n emit: EmitFn,\n sessionId: string,\n beforeSend: ((event: RuntimeEvent) => RuntimeEvent | null) | undefined,\n data: Omit<StateEvent, 'eventId' | 'sessionId' | 'timestamp' | 'eventType'>\n): void {\n const event: StateEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'state',\n ...data,\n };\n\n if (beforeSend) {\n const filtered = beforeSend(event);\n if (filtered) emit(filtered as StateEvent);\n } else {\n emit(event);\n }\n}\n","import { generateId } from '../utils/id.js';\nimport type { PerformanceEvent, WebVitalRating, RuntimeEvent } from '../types.js';\n\ntype EmitFn = (event: PerformanceEvent) => void;\n\nexport interface PerformanceInterceptorOptions {\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null;\n}\n\n// Web Vitals thresholds from web.dev\nconst THRESHOLDS: Record<string, [number, number]> = {\n LCP: [2500, 4000],\n FCP: [1800, 3000],\n CLS: [0.1, 0.25],\n TTFB: [800, 1800],\n FID: [100, 300],\n INP: [200, 500],\n};\n\nfunction rate(metric: string, value: number): WebVitalRating {\n const [good, poor] = THRESHOLDS[metric] ?? [Infinity, Infinity];\n if (value <= good) return 'good';\n if (value <= poor) return 'needs-improvement';\n return 'poor';\n}\n\n// Module-level singleton tracking — prevents duplicate observers on double-init (HMR)\nlet activeObservers: PerformanceObserver[] | null = null;\n\nexport function interceptPerformance(\n emit: EmitFn,\n sessionId: string,\n options?: PerformanceInterceptorOptions\n): () => void {\n // Disconnect any existing observers from a previous init (HMR / double-init safety)\n if (activeObservers) {\n for (const obs of activeObservers) {\n try { obs.disconnect(); } catch { /* already disconnected */ }\n }\n }\n\n const observers: PerformanceObserver[] = [];\n activeObservers = observers;\n\n const emitMetric = (\n metricName: PerformanceEvent['metricName'],\n value: number,\n element?: string\n ) => {\n const event: PerformanceEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'performance',\n metricName,\n value: Math.round(value * 100) / 100,\n rating: rate(metricName, value),\n element,\n };\n\n if (options?.beforeSend) {\n const filtered = options.beforeSend(event);\n if (filtered) emit(filtered as PerformanceEvent);\n } else {\n emit(event);\n }\n };\n\n // LCP — Largest Contentful Paint\n tryObserve(observers, 'largest-contentful-paint', (entries) => {\n const last = entries[entries.length - 1];\n if (!last) return;\n const el = (last as PerformanceEntry & { element?: Element }).element;\n emitMetric('LCP', last.startTime, el?.tagName?.toLowerCase());\n });\n\n // FCP — First Contentful Paint\n tryObserve(observers, 'paint', (entries) => {\n for (const entry of entries) {\n if (entry.name === 'first-contentful-paint') {\n emitMetric('FCP', entry.startTime);\n }\n }\n });\n\n // CLS — Cumulative Layout Shift\n let clsValue = 0;\n tryObserve(observers, 'layout-shift', (entries) => {\n for (const entry of entries) {\n const ls = entry as PerformanceEntry & { hadRecentInput?: boolean; value?: number };\n if (!ls.hadRecentInput && ls.value) {\n clsValue += ls.value;\n emitMetric('CLS', clsValue);\n }\n }\n });\n\n // FID — First Input Delay\n tryObserve(observers, 'first-input', (entries) => {\n const first = entries[0];\n if (!first) return;\n const fi = first as PerformanceEntry & { processingStart?: number };\n if (fi.processingStart) {\n emitMetric('FID', fi.processingStart - first.startTime);\n }\n });\n\n // TTFB — Time to First Byte\n tryObserve(observers, 'navigation', (entries) => {\n const nav = entries[0] as PerformanceNavigationTiming | undefined;\n if (nav) {\n emitMetric('TTFB', nav.responseStart - nav.requestStart);\n }\n });\n\n // INP — Interaction to Next Paint\n let inpMax = 0;\n tryObserve(\n observers,\n 'event',\n (entries) => {\n for (const entry of entries) {\n if (entry.duration > inpMax) {\n inpMax = entry.duration;\n emitMetric('INP', inpMax);\n }\n }\n },\n { durationThreshold: 16 }\n );\n\n return () => {\n for (const obs of observers) {\n try {\n obs.disconnect();\n } catch {\n // Already disconnected\n }\n }\n if (activeObservers === observers) {\n activeObservers = null;\n }\n };\n}\n\nfunction tryObserve(\n observers: PerformanceObserver[],\n entryType: string,\n callback: (entries: PerformanceEntryList) => void,\n observeOptions?: Record<string, unknown>\n): void {\n try {\n const obs = new PerformanceObserver((list) => {\n callback(list.getEntries());\n });\n obs.observe({\n type: entryType,\n buffered: true,\n ...observeOptions,\n } as PerformanceObserverInit);\n observers.push(obs);\n } catch {\n // Observer type not supported in this browser\n }\n}\n","import { generateId } from '../utils/id.js';\nimport type { RenderEvent, RenderComponentProfile, RuntimeEvent } from '../types.js';\n\ntype EmitFn = (event: RenderEvent) => void;\n\nexport interface RenderInterceptorOptions {\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null;\n snapshotIntervalMs?: number;\n}\n\ninterface Fiber {\n tag: number;\n type: { displayName?: string; name?: string } | string | null;\n child: Fiber | null;\n sibling: Fiber | null;\n alternate: Fiber | null;\n memoizedProps: unknown;\n memoizedState: unknown;\n actualDuration?: number;\n stateNode: unknown;\n}\n\ninterface ComponentTracker {\n renderCount: number;\n totalDuration: number;\n lastRenderTime: number;\n lastRenderPhase: 'mount' | 'update' | 'unmount';\n lastRenderCause: 'props' | 'state' | 'context' | 'parent' | 'unknown';\n renderTimestamps: number[];\n}\n\ninterface DevToolsHook {\n onCommitFiberRoot?: (id: number, root: { current?: Fiber }) => void;\n _runtimescope_original?: (id: number, root: { current?: Fiber }) => void;\n}\n\nconst FUNCTION_COMPONENT = 0;\nconst CLASS_COMPONENT = 1;\nconst SNAPSHOT_WINDOW_MS = 10_000;\nconst MAX_TIMESTAMPS = 100;\nconst TRACKER_TTL_MS = 60_000;\nconst MAX_TRACKED_COMPONENTS = 500;\n\nexport function interceptReactRenders(\n emit: EmitFn,\n sessionId: string,\n options?: RenderInterceptorOptions\n): () => void {\n const trackers = new Map<string, ComponentTracker>();\n const snapshotIntervalMs = options?.snapshotIntervalMs ?? 5000;\n let snapshotTimer: ReturnType<typeof setInterval> | null = null;\n\n // Access or create the React DevTools global hook\n const hook = getOrCreateDevToolsHook();\n if (!hook) {\n return () => {}; // Not in a browser environment\n }\n\n // Save original if React DevTools is installed\n const originalOnCommit = hook.onCommitFiberRoot;\n hook._runtimescope_original = originalOnCommit;\n\n hook.onCommitFiberRoot = (id: number, root: { current?: Fiber }) => {\n // Call original first (React DevTools compatibility)\n if (originalOnCommit) {\n try {\n originalOnCommit(id, root);\n } catch {\n // Don't let DevTools errors break our tracking\n }\n }\n\n if (root.current) {\n walkFiber(root.current, trackers);\n }\n };\n\n // Emit snapshots periodically\n snapshotTimer = setInterval(() => {\n emitSnapshot(trackers, emit, sessionId, options?.beforeSend);\n }, snapshotIntervalMs);\n\n return () => {\n if (snapshotTimer) {\n clearInterval(snapshotTimer);\n snapshotTimer = null;\n }\n\n // Restore original hook\n if (hook) {\n if (hook._runtimescope_original) {\n hook.onCommitFiberRoot = hook._runtimescope_original;\n } else {\n delete hook.onCommitFiberRoot;\n }\n delete hook._runtimescope_original;\n }\n };\n}\n\nfunction getOrCreateDevToolsHook(): DevToolsHook | null {\n if (typeof window === 'undefined') return null;\n\n const w = window as unknown as { __REACT_DEVTOOLS_GLOBAL_HOOK__?: DevToolsHook };\n\n if (!w.__REACT_DEVTOOLS_GLOBAL_HOOK__) {\n // Create a minimal shim that React will pick up on init\n w.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {\n // React checks for these methods to decide if DevTools is present\n } as DevToolsHook;\n }\n\n return w.__REACT_DEVTOOLS_GLOBAL_HOOK__!;\n}\n\nfunction walkFiber(fiber: Fiber, trackers: Map<string, ComponentTracker>): void {\n processNode(fiber, trackers);\n\n if (fiber.child) walkFiber(fiber.child, trackers);\n if (fiber.sibling) walkFiber(fiber.sibling, trackers);\n}\n\nfunction processNode(fiber: Fiber, trackers: Map<string, ComponentTracker>): void {\n // Only track function components (0) and class components (1)\n if (fiber.tag !== FUNCTION_COMPONENT && fiber.tag !== CLASS_COMPONENT) return;\n\n const name = getComponentName(fiber);\n if (!name) return;\n\n const now = Date.now();\n const isMount = fiber.alternate === null;\n const duration = fiber.actualDuration ?? 0;\n const cause = inferRenderCause(fiber, isMount);\n\n let tracker = trackers.get(name);\n if (!tracker) {\n tracker = {\n renderCount: 0,\n totalDuration: 0,\n lastRenderTime: 0,\n lastRenderPhase: 'mount',\n lastRenderCause: 'unknown',\n renderTimestamps: [],\n };\n trackers.set(name, tracker);\n }\n\n tracker.renderCount++;\n tracker.totalDuration += duration;\n tracker.lastRenderTime = now;\n tracker.lastRenderPhase = isMount ? 'mount' : 'update';\n tracker.lastRenderCause = cause ?? 'unknown';\n tracker.renderTimestamps.push(now);\n\n // Trim old timestamps\n if (tracker.renderTimestamps.length > MAX_TIMESTAMPS) {\n tracker.renderTimestamps = tracker.renderTimestamps.slice(-MAX_TIMESTAMPS);\n }\n}\n\nfunction getComponentName(fiber: Fiber): string | undefined {\n if (!fiber.type) return undefined;\n if (typeof fiber.type === 'string') return undefined; // HTML elements\n return fiber.type.displayName || fiber.type.name || undefined;\n}\n\nfunction inferRenderCause(\n fiber: Fiber,\n isMount: boolean\n): RenderComponentProfile['lastRenderCause'] {\n if (isMount) return 'props'; // Initial mount, driven by parent passing props\n\n if (!fiber.alternate) return 'unknown';\n\n // Check if props changed\n if (fiber.memoizedProps !== fiber.alternate.memoizedProps) {\n return 'props';\n }\n\n // Check if state changed\n if (fiber.memoizedState !== fiber.alternate.memoizedState) {\n return 'state';\n }\n\n // If neither props nor state changed, likely parent re-rendered\n return 'parent';\n}\n\nfunction computeRenderVelocity(timestamps: number[]): number {\n if (timestamps.length < 2) return 0;\n const now = Date.now();\n const windowStart = now - SNAPSHOT_WINDOW_MS;\n const recent = timestamps.filter((t) => t >= windowStart);\n if (recent.length < 2) return 0;\n const windowMs = now - recent[0];\n if (windowMs === 0) return 0;\n return recent.length / (windowMs / 1000);\n}\n\nfunction emitSnapshot(\n trackers: Map<string, ComponentTracker>,\n emit: EmitFn,\n sessionId: string,\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null\n): void {\n if (trackers.size === 0) return;\n\n const profiles: RenderComponentProfile[] = [];\n const suspiciousComponents: string[] = [];\n let totalRenders = 0;\n\n for (const [componentName, tracker] of trackers) {\n const velocity = computeRenderVelocity(tracker.renderTimestamps);\n const suspicious = velocity > 4 || tracker.renderCount > 20;\n\n profiles.push({\n componentName,\n renderCount: tracker.renderCount,\n totalDuration: Math.round(tracker.totalDuration * 100) / 100,\n avgDuration:\n tracker.renderCount > 0\n ? Math.round((tracker.totalDuration / tracker.renderCount) * 100) / 100\n : 0,\n lastRenderPhase: tracker.lastRenderPhase,\n lastRenderCause: tracker.lastRenderCause,\n renderVelocity: Math.round(velocity * 100) / 100,\n suspicious,\n });\n\n if (suspicious) suspiciousComponents.push(componentName);\n totalRenders += tracker.renderCount;\n }\n\n // Sort by render count descending\n profiles.sort((a, b) => b.renderCount - a.renderCount);\n\n const event: RenderEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'render',\n profiles,\n snapshotWindowMs: SNAPSHOT_WINDOW_MS,\n totalRenders,\n suspiciousComponents,\n };\n\n if (beforeSend) {\n const filtered = beforeSend(event);\n if (filtered) emit(filtered as RenderEvent);\n } else {\n emit(event);\n }\n\n // Reset counters and prune stale trackers\n const now = Date.now();\n for (const [name, tracker] of trackers) {\n if (now - tracker.lastRenderTime > TRACKER_TTL_MS) {\n // Component hasn't rendered in 60s — remove to prevent unbounded growth\n trackers.delete(name);\n } else {\n tracker.renderCount = 0;\n tracker.totalDuration = 0;\n }\n }\n\n // Hard cap: if still over limit after TTL pruning, evict least-recently-rendered\n if (trackers.size > MAX_TRACKED_COMPONENTS) {\n const sorted = [...trackers.entries()]\n .sort((a, b) => a[1].lastRenderTime - b[1].lastRenderTime);\n const excess = trackers.size - MAX_TRACKED_COMPONENTS;\n for (let i = 0; i < excess; i++) {\n trackers.delete(sorted[i][0]);\n }\n }\n}\n","import { generateId } from '../utils/id.js';\nimport { safeSerialize } from '../utils/serialize.js';\nimport type { ConsoleEvent, RuntimeEvent } from '../types.js';\n\ntype EmitFn = (event: ConsoleEvent) => void;\n\n/**\n * Captures uncaught errors and unhandled promise rejections that appear\n * in DevTools but don't go through the console.* API.\n *\n * - window 'error' (capture phase) — JS runtime errors + resource load failures\n * - window 'unhandledrejection' — unhandled async/await and Promise rejections\n *\n * Events are emitted as ConsoleEvents with level 'error' so they appear\n * alongside console.error() output in get_console_messages.\n */\nexport function interceptErrors(\n emit: EmitFn,\n sessionId: string,\n beforeSend?: (event: RuntimeEvent) => RuntimeEvent | null\n): () => void {\n // Capture uncaught JS errors and resource load failures\n const onError = (e: ErrorEvent | Event) => {\n let message: string;\n let stackTrace: string | undefined;\n let sourceFile: string | undefined;\n\n if (e instanceof ErrorEvent) {\n // Uncaught JS error\n message = e.message || 'Uncaught error';\n stackTrace = e.error?.stack;\n sourceFile = e.filename\n ? `${e.filename}:${e.lineno}:${e.colno}`\n : undefined;\n } else {\n // Resource load error (img, script, link, etc.)\n const target = e.target as HTMLElement | null;\n if (target && target !== window as unknown) {\n const tagName = target.tagName?.toLowerCase() ?? 'unknown';\n const src =\n (target as HTMLImageElement).src ??\n (target as HTMLScriptElement).src ??\n (target as HTMLLinkElement).href ??\n 'unknown';\n message = `Failed to load resource: <${tagName}> ${src}`;\n } else {\n return; // Not a resource error we can identify\n }\n }\n\n const event: ConsoleEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'console',\n level: 'error',\n message: `[Uncaught] ${message}`,\n args: [safeSerialize(message, 3)],\n stackTrace,\n sourceFile,\n };\n\n if (beforeSend) {\n const filtered = beforeSend(event);\n if (filtered) emit(filtered as ConsoleEvent);\n } else {\n emit(event);\n }\n };\n\n // Capture unhandled promise rejections\n const onUnhandledRejection = (e: PromiseRejectionEvent) => {\n const reason = e.reason;\n let message: string;\n let stackTrace: string | undefined;\n\n if (reason instanceof Error) {\n message = reason.message;\n stackTrace = reason.stack;\n } else if (typeof reason === 'string') {\n message = reason;\n } else {\n try {\n message = JSON.stringify(reason);\n } catch {\n message = String(reason);\n }\n }\n\n const event: ConsoleEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'console',\n level: 'error',\n message: `[Unhandled Rejection] ${message}`,\n args: [safeSerialize(reason, 3)],\n stackTrace,\n sourceFile: undefined,\n };\n\n if (beforeSend) {\n const filtered = beforeSend(event);\n if (filtered) emit(filtered as ConsoleEvent);\n } else {\n emit(event);\n }\n };\n\n // Use capture phase for 'error' to catch resource load failures on child elements\n window.addEventListener('error', onError, true);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n\n return () => {\n window.removeEventListener('error', onError, true);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n };\n}\n","import type { RuntimeEvent, NavigationEvent } from '../types.js';\nimport { generateId } from '../utils/id.js';\n\n/**\n * Intercepts SPA navigation events:\n * - history.pushState / replaceState (client-side routing)\n * - popstate (browser back/forward)\n * - hashchange (hash-based routing)\n *\n * Emits a NavigationEvent with from/to URLs and the trigger type.\n */\nexport function interceptNavigation(\n emit: (event: RuntimeEvent) => void,\n sessionId: string\n): () => void {\n let currentUrl = window.location.href;\n\n function emitNav(to: string, trigger: NavigationEvent['trigger']): void {\n const from = currentUrl;\n if (from === to) return; // Skip no-op navigations\n currentUrl = to;\n\n emit({\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'navigation',\n from,\n to,\n trigger,\n });\n }\n\n // Patch pushState and replaceState\n const origPushState = history.pushState.bind(history);\n const origReplaceState = history.replaceState.bind(history);\n\n history.pushState = function (...args: Parameters<typeof origPushState>) {\n origPushState(...args);\n emitNav(window.location.href, 'pushState');\n };\n\n history.replaceState = function (...args: Parameters<typeof origReplaceState>) {\n origReplaceState(...args);\n emitNav(window.location.href, 'replaceState');\n };\n\n // Listen for back/forward navigation\n const onPopState = () => emitNav(window.location.href, 'popstate');\n window.addEventListener('popstate', onPopState);\n\n // Listen for hash changes (covers hash-based routers)\n const onHashChange = () => emitNav(window.location.href, 'hashchange');\n window.addEventListener('hashchange', onHashChange);\n\n // Restore function\n return () => {\n history.pushState = origPushState;\n history.replaceState = origReplaceState;\n window.removeEventListener('popstate', onPopState);\n window.removeEventListener('hashchange', onHashChange);\n };\n}\n","import type { RuntimeEvent, UIInteractionEvent } from '../types.js';\nimport { generateId } from '../utils/id.js';\n\n/**\n * Intercepts user clicks via event delegation on `document`.\n * Emits a lightweight UIInteractionEvent with the target element's\n * CSS selector and visible text.\n *\n * Uses a single capture-phase listener — no DOM mutation, no patching.\n */\nexport function interceptClicks(\n emit: (event: RuntimeEvent) => void,\n sessionId: string\n): () => void {\n const onClick = (e: MouseEvent) => {\n const target = e.target;\n if (!(target instanceof Element)) return;\n\n // Skip RuntimeScope's own UI (if any)\n if (target.closest('[data-runtimescope]')) return;\n\n const selector = buildSelector(target);\n const text = getVisibleText(target);\n\n const event: UIInteractionEvent = {\n eventId: generateId(),\n sessionId,\n timestamp: Date.now(),\n eventType: 'ui',\n action: 'click',\n target: selector,\n ...(text && { text }),\n };\n\n emit(event);\n };\n\n // Capture phase so we see clicks even if stopPropagation is called\n document.addEventListener('click', onClick, true);\n\n return () => {\n document.removeEventListener('click', onClick, true);\n };\n}\n\n/**\n * Build a short, human-readable CSS selector for the clicked element.\n * Prioritizes: tag + id > tag + data-testid > tag + class (first meaningful one).\n * Keeps it concise — not a full path.\n */\nfunction buildSelector(el: Element): string {\n const tag = el.tagName.toLowerCase();\n\n if (el.id) return `${tag}#${el.id}`;\n\n const testId = el.getAttribute('data-testid') ?? el.getAttribute('data-test-id');\n if (testId) return `${tag}[data-testid=\"${testId}\"]`;\n\n const role = el.getAttribute('role');\n const ariaLabel = el.getAttribute('aria-label');\n if (role && ariaLabel) return `${tag}[role=\"${role}\"][aria-label=\"${ariaLabel}\"]`;\n\n const className = el.className;\n if (typeof className === 'string' && className.trim()) {\n // Take the first non-utility class (skip single-letter or hash-based classes)\n const meaningful = className.split(/\\s+/).find(\n (c) => c.length > 2 && !c.startsWith('_') && !c.includes('__')\n );\n if (meaningful) return `${tag}.${meaningful}`;\n }\n\n // Fallback: tag with nth-child if parent has multiple same-tag children\n const parent = el.parentElement;\n if (parent) {\n const siblings = parent.children;\n const sameTag = Array.from(siblings).filter((s) => s.tagName === el.tagName);\n if (sameTag.length > 1) {\n const idx = sameTag.indexOf(el) + 1;\n return `${tag}:nth-child(${idx})`;\n }\n }\n\n return tag;\n}\n\n/**\n * Get visible text from an element, truncated to 80 chars.\n * Prefers aria-label, then textContent.\n */\nfunction getVisibleText(el: Element): string | undefined {\n const ariaLabel = el.getAttribute('aria-label');\n if (ariaLabel) return ariaLabel.slice(0, 80);\n\n const text = (el as HTMLElement).innerText ?? el.textContent;\n if (!text) return undefined;\n\n const trimmed = text.trim().replace(/\\s+/g, ' ');\n if (trimmed.length === 0) return undefined;\n return trimmed.length > 80 ? trimmed.slice(0, 77) + '...' : trimmed;\n}\n"],"mappings":"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","names":["index_exports","__export","RuntimeScope","index_default","__toCommonJS","_log","_Transport","config","__publicField","event","msg","queued","events","delay","jitter","handler","requestId","command","payload","Transport","generateId","arr","b","generateSessionId","MAX_INFLIGHT","INFLIGHT_TTL_MS","SWEEP_INTERVAL_MS","fetchInterceptedRequests","sweepTimer","sweepRefCount","startSweeper","cutoff","key","ts","stopSweeper","interceptFetch","emit","sessionId","redactHeaders","options","originalFetch","redactSet","h","captureBody","maxBodySize","input","init","startTime","url","method","requestHeaders","extractHeaders","requestBodySize","estimateBodySize","graphqlOperation","detectGraphQL","requestBody","serializeBody","requestKey","oldest","response","duration","responseBodySize","responseHeaders","extractResponseHeaders","responseBody","text","event","generateId","filtered","error","errorPhase","errorMessage","headers","result","value","body","maxSize","s","parsed","trimmed","type","name","extractOperationName","query","safeSerialize","value","maxDepth","seen","walk","val","depth","v","result","keys","maxKeys","i","LEVELS","interceptConsole","emit","sessionId","beforeSend","originals","level","args","message","stringifyArg","event","generateId","safeSerialize","filtered","arg","interceptXhr","emit","sessionId","redactHeaders","options","redactSet","h","captureBody","maxBodySize","globalAbort","origOpen","origSetRequestHeader","origSend","method","url","name","value","body","requestKey","alreadyIntercepted","k","fetchInterceptedRequests","requestHeaders","startTime","requestBody","requestBodySize","s","graphqlOperation","detectGraphQL","emitEvent","overrides","event","generateId","filtered","duration","responseHeaders","parseResponseHeaders","responseBodySize","responseBody","text","errorPhase","errorMessage","raw","result","line","idx","key","parsed","trimmed","type","extractOperationName","query","interceptStateStores","emit","sessionId","stores","options","unsubscribers","originalDispatches","storeId","store","library","detectLibrary","zustand","emitState","safeSerialize","unsub","state","prevState","diff","shallowDiff","redux","lastAction","origDispatch","action","s","prev","curr","prevObj","currObj","allKeys","key","beforeSend","data","event","generateId","filtered","THRESHOLDS","rate","metric","value","good","poor","activeObservers","interceptPerformance","emit","sessionId","options","obs","observers","emitMetric","metricName","element","event","generateId","filtered","tryObserve","entries","last","el","entry","clsValue","ls","first","fi","nav","inpMax","entryType","callback","observeOptions","list","FUNCTION_COMPONENT","CLASS_COMPONENT","SNAPSHOT_WINDOW_MS","MAX_TIMESTAMPS","TRACKER_TTL_MS","MAX_TRACKED_COMPONENTS","interceptReactRenders","emit","sessionId","options","trackers","snapshotIntervalMs","snapshotTimer","hook","getOrCreateDevToolsHook","originalOnCommit","id","root","walkFiber","emitSnapshot","w","fiber","processNode","name","getComponentName","now","isMount","duration","cause","inferRenderCause","tracker","computeRenderVelocity","timestamps","windowStart","recent","t","windowMs","beforeSend","profiles","suspiciousComponents","totalRenders","componentName","velocity","suspicious","a","b","event","generateId","filtered","sorted","excess","i","interceptErrors","emit","sessionId","beforeSend","onError","e","message","stackTrace","sourceFile","target","tagName","src","event","generateId","safeSerialize","filtered","onUnhandledRejection","reason","interceptNavigation","emit","sessionId","currentUrl","emitNav","to","trigger","from","generateId","origPushState","origReplaceState","args","onPopState","onHashChange","interceptClicks","emit","sessionId","onClick","e","target","selector","buildSelector","text","getVisibleText","event","generateId","el","tag","testId","role","ariaLabel","className","meaningful","c","parent","siblings","sameTag","s","idx","trimmed","SDK_VERSION","_log","ORIGINALS_KEY","getOrSaveOriginals","g","RuntimeScope","now","config","resolved","generateSessionId","Transport","emit","event","generateId","sessionId","cmd","interceptFetch","interceptXhr","interceptConsole","interceptErrors","interceptStateStores","interceptPerformance","interceptReactRenders","interceptNavigation","interceptClicks","features","maxSize","html","truncated","snapshot","name","properties","user","message","data","fn","originals","level","c","__publicField","index_default"]}
|