(() => { if (typeof window === 'undefined') { return; } if (window.__tenantpilotUnhandledRejectionLoggerApplied) { return; } window.__tenantpilotUnhandledRejectionLoggerApplied = true; const recentKeys = new Map(); const recentTransportFailures = []; const cleanupRecentKeys = (nowMs) => { for (const [key, timestampMs] of recentKeys.entries()) { if (nowMs - timestampMs > 5_000) { recentKeys.delete(key); } } }; const cleanupRecentTransportFailures = (nowMs) => { while (recentTransportFailures.length > 0 && nowMs - recentTransportFailures[0].timestampMs > 15_000) { recentTransportFailures.shift(); } }; const normalizeUrl = (value) => { if (typeof value !== 'string' || value === '') { return null; } try { return new URL(value, window.location.href).href; } catch { return value; } }; const toBodySnippet = (body) => { if (typeof body !== 'string' || body === '') { return null; } return body.slice(0, 1_000); }; const recordTransportFailure = ({ requestUrl, method, status, body, transportType }) => { const nowMs = Date.now(); cleanupRecentTransportFailures(nowMs); recentTransportFailures.push({ requestUrl: normalizeUrl(requestUrl), method: typeof method === 'string' && method !== '' ? method.toUpperCase() : 'GET', status: Number.isFinite(status) ? status : null, bodySnippet: toBodySnippet(body), transportType: typeof transportType === 'string' && transportType !== '' ? transportType : 'unknown', timestampMs: nowMs, }); if (recentTransportFailures.length > 30) { recentTransportFailures.shift(); } }; const resolveTransportMetadata = (reason) => { if (reason === null || typeof reason !== 'object') { return null; } const directRequestUrl = typeof reason.requestUrl === 'string' ? normalizeUrl(reason.requestUrl) : (typeof reason.url === 'string' ? normalizeUrl(reason.url) : null); if (directRequestUrl) { return { requestUrl: directRequestUrl, method: typeof reason.method === 'string' ? reason.method.toUpperCase() : null, transportType: 'reason', }; } if (!isTransportEnvelope(reason)) { return null; } const nowMs = Date.now(); const reasonBodySnippet = toBodySnippet(reason.body); cleanupRecentTransportFailures(nowMs); for (let index = recentTransportFailures.length - 1; index >= 0; index -= 1) { const candidate = recentTransportFailures[index]; if (nowMs - candidate.timestampMs > 5_000) { break; } if (candidate.status !== reason.status) { continue; } if (reasonBodySnippet !== null && candidate.bodySnippet !== null && candidate.bodySnippet !== reasonBodySnippet) { continue; } return { requestUrl: candidate.requestUrl, method: candidate.method, transportType: candidate.transportType, }; } return null; }; const extractRequestMetadata = (input, init) => { if (input instanceof Request) { return { requestUrl: normalizeUrl(input.url), method: typeof input.method === 'string' && input.method !== '' ? input.method.toUpperCase() : 'GET', }; } return { requestUrl: normalizeUrl(typeof input === 'string' ? input : String(input ?? '')), method: typeof init?.method === 'string' && init.method !== '' ? init.method.toUpperCase() : 'GET', }; }; if (typeof window.fetch === 'function' && !window.__tenantpilotUnhandledRejectionFetchInstrumented) { const originalFetch = window.fetch.bind(window); window.fetch = async (...args) => { const [input, init] = args; const transport = extractRequestMetadata(input, init); try { const response = await originalFetch(...args); if (!response.ok) { const clonedResponse = typeof response.clone === 'function' ? response.clone() : null; if (clonedResponse && typeof clonedResponse.text === 'function') { clonedResponse.text() .then((body) => { recordTransportFailure({ requestUrl: transport.requestUrl, method: transport.method, status: response.status, body, transportType: 'fetch', }); }) .catch(() => { recordTransportFailure({ requestUrl: transport.requestUrl, method: transport.method, status: response.status, body: null, transportType: 'fetch', }); }); } else { recordTransportFailure({ requestUrl: transport.requestUrl, method: transport.method, status: response.status, body: null, transportType: 'fetch', }); } } return response; } catch (error) { recordTransportFailure({ requestUrl: transport.requestUrl, method: transport.method, status: null, body: error instanceof Error ? error.message : String(error ?? ''), transportType: 'fetch', }); throw error; } }; window.__tenantpilotUnhandledRejectionFetchInstrumented = true; } if (typeof XMLHttpRequest !== 'undefined' && !window.__tenantpilotUnhandledRejectionXhrInstrumented) { const originalOpen = XMLHttpRequest.prototype.open; const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (method, url, ...rest) { this.__tenantpilotRequestMethod = typeof method === 'string' && method !== '' ? method.toUpperCase() : 'GET'; this.__tenantpilotRequestUrl = normalizeUrl(typeof url === 'string' ? url : String(url ?? '')); return originalOpen.call(this, method, url, ...rest); }; XMLHttpRequest.prototype.send = function (...args) { if (!this.__tenantpilotTransportFailureListenerApplied) { this.addEventListener('loadend', () => { if (typeof this.status === 'number' && this.status >= 400) { recordTransportFailure({ requestUrl: this.__tenantpilotRequestUrl, method: this.__tenantpilotRequestMethod, status: this.status, body: typeof this.responseText === 'string' ? this.responseText : null, transportType: 'xhr', }); } }); this.__tenantpilotTransportFailureListenerApplied = true; } return originalSend.apply(this, args); }; window.__tenantpilotUnhandledRejectionXhrInstrumented = true; } const isTransportEnvelope = (value) => { return value !== null && typeof value === 'object' && Object.prototype.hasOwnProperty.call(value, 'status') && Object.prototype.hasOwnProperty.call(value, 'body') && Object.prototype.hasOwnProperty.call(value, 'json') && Object.prototype.hasOwnProperty.call(value, 'errors'); }; const isCancellationReason = (reason) => { if (!isTransportEnvelope(reason)) { return false; } return reason.status === null && reason.body === null && reason.json === null && reason.errors === null; }; const isPageHiddenOrInactive = () => { if (document.visibilityState !== 'visible') { return true; } return typeof document.hasFocus === 'function' ? document.hasFocus() === false : false; }; const isExpectedBackgroundTransportFailure = (reason) => { if (isCancellationReason(reason)) { return true; } if (!isTransportEnvelope(reason) || !isPageHiddenOrInactive()) { return false; } return (reason.status === 419 && typeof reason.body === 'string' && reason.body.includes('Page Expired')) || (reason.status === 404 && typeof reason.body === 'string' && reason.body.includes('Not Found')); }; const normalizeReason = (value, depth = 0) => { if (depth > 3) { return '[max-depth-reached]'; } if (value instanceof Error) { return { type: 'Error', name: value.name, message: value.message, stack: value.stack, }; } if (value === null || value === undefined) { return value; } if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { return value; } if (Array.isArray(value)) { return value.slice(0, 10).map((item) => normalizeReason(item, depth + 1)); } if (typeof value === 'object') { const result = {}; const allowedKeys = [ 'message', 'stack', 'name', 'type', 'status', 'body', 'json', 'errors', 'reason', 'code', 'url', 'requestUrl', 'method', ]; for (const key of allowedKeys) { if (Object.prototype.hasOwnProperty.call(value, key)) { result[key] = normalizeReason(value[key], depth + 1); } } if (Object.keys(result).length > 0) { return result; } const stringTag = Object.prototype.toString.call(value); return { type: stringTag, value: String(value), }; } return String(value); }; const toStableJson = (payload) => { try { return JSON.stringify(payload); } catch { return JSON.stringify({ source: payload.source, href: payload.href, timestamp: payload.timestamp, reason: '[unserializable]', }); } }; window.addEventListener('unhandledrejection', (event) => { const normalizedReason = normalizeReason(event.reason); const transport = resolveTransportMetadata(normalizedReason); const payload = { source: 'window.unhandledrejection', href: window.location.href, timestamp: new Date().toISOString(), requestUrl: transport?.requestUrl ?? null, requestMethod: transport?.method ?? null, transportType: transport?.transportType ?? null, reason: normalizedReason, }; if (isExpectedBackgroundTransportFailure(normalizedReason)) { event.preventDefault(); return; } const dedupeKey = toStableJson({ source: payload.source, href: payload.href, requestUrl: payload.requestUrl, reason: payload.reason, }); const payloadJson = toStableJson(payload); const nowMs = Date.now(); cleanupRecentKeys(nowMs); if (recentKeys.has(dedupeKey)) { return; } recentKeys.set(dedupeKey, nowMs); console.error(`TenantPilot unhandled promise rejection ${payloadJson}`); }); })();