TenantAtlas/public/js/tenantpilot/unhandled-rejection-logger.js
ahmido 7d4d607475 feat: add baseline gap details surfaces (#192)
## Summary
- add baseline compare evidence gap detail modeling and a dedicated Livewire table surface
- extend baseline compare landing and operation run detail surfaces to expose evidence gap details and stats
- add spec artifacts for feature 162 and expand feature coverage with focused Filament and baseline tests

## Notes
- branch: `162-baseline-gap-details`
- commit: `a92dd812`
- working tree was clean after push

## Validation
- tests were not run in this step

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #192
2026-03-24 19:05:23 +00:00

118 lines
3.0 KiB
JavaScript

(() => {
if (typeof window === 'undefined') {
return;
}
if (window.__tenantpilotUnhandledRejectionLoggerApplied) {
return;
}
window.__tenantpilotUnhandledRejectionLoggerApplied = true;
const recentKeys = new Map();
const cleanupRecentKeys = (nowMs) => {
for (const [key, timestampMs] of recentKeys.entries()) {
if (nowMs - timestampMs > 5_000) {
recentKeys.delete(key);
}
}
};
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',
];
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 payload = {
source: 'window.unhandledrejection',
href: window.location.href,
timestamp: new Date().toISOString(),
reason: normalizeReason(event.reason),
};
const payloadJson = toStableJson(payload);
const nowMs = Date.now();
cleanupRecentKeys(nowMs);
if (recentKeys.has(payloadJson)) {
return;
}
recentKeys.set(payloadJson, nowMs);
console.error(`TenantPilot unhandled promise rejection ${payloadJson}`);
});
})();