## 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
118 lines
3.0 KiB
JavaScript
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}`);
|
|
});
|
|
})(); |