import { html, render } from "/js/lib/lit-html.js";
const LEVEL_STYLES = {
error: {
background: "rgba(255, 0, 0, 0.9)",
color: "white",
},
warn: {
background: "rgba(255, 200, 0, 0.9)",
color: "black",
},
};
const SUPPRESSED_TAGS = ["[plugins:perf]"];
function isSuppressed(message) {
return SUPPRESSED_TAGS.some((tag) => message.startsWith(tag));
}
function formatArg(arg) {
if (typeof arg === "string") {
return arg;
}
if (arg instanceof Error || arg?.message || arg?.stack) {
const message = arg.message ?? String(arg);
const stack = arg.stack ?? "";
return stack.includes(message) ? stack : `${message}\n${stack}`;
}
try {
return JSON.stringify(arg);
} catch {
return String(arg);
}
}
export function enableErrorLogs() {
const errorLog = document.createElement("div");
errorLog.id = "error-log";
errorLog.style.cssText = `
position: fixed;
bottom: 0;
left: 0;
right: 0;
max-height: 200px;
font-family: monospace;
font-size: 12px;
z-index: 9999999;
display: none;
`;
document.body.appendChild(errorLog);
const entriesContainer = document.createElement("div");
entriesContainer.style.cssText = `
max-height: 200px;
overflow-y: auto;
`;
errorLog.appendChild(entriesContainer);
const clearAllButton = document.createElement("button");
clearAllButton.textContent = "Clear all";
clearAllButton.style.cssText = `
position: absolute;
top: 4px;
right: 4px;
padding: 4px 8px;
border: none;
background: rgba(0, 0, 0, 0.7);
color: white;
font-family: monospace;
font-size: 12px;
cursor: pointer;
z-index: 1;
`;
clearAllButton.addEventListener("click", () => {
entriesContainer.replaceChildren();
errorLog.style.display = "none";
});
errorLog.appendChild(clearAllButton);
function showMessage(message, level = "error") {
const { background, color } = LEVEL_STYLES[level];
errorLog.style.display = "block";
const entry = document.createElement("div");
entry.dataset.logLevel = level;
entriesContainer.appendChild(entry);
render(
html`
${message}
`,
entry,
);
}
window.addEventListener("error", (event) => {
const file = event.filename || "";
if (!file.startsWith(location.origin)) {
return; // probably browser or extension code
}
showMessage(`${event.message} at ${event.filename}:${event.lineno}`);
});
window.addEventListener("unhandledrejection", (event) => {
showMessage(`Promise rejection: ${String(event.reason)}`);
});
const consoleError = console.error;
console.error = (...args) => {
showMessage(args.map(formatArg).join(" "));
consoleError(...args);
};
const consoleWarn = console.warn;
console.warn = (...args) => {
const message = args.map(formatArg).join(" ");
if (!isSuppressed(message)) {
showMessage(message, "warn");
}
consoleWarn(...args);
};
window.logMessage = (...args) => {
showMessage(args.map(formatArg).join(" "));
};
}
const AUTH_DEBUG_FLAG_KEY = "debug-auth";
const AUTH_DEBUG_LOG_KEY = "debug-auth-log";
const AUTH_DEBUG_LOG_MAX_ENTRIES = 200;
function isAuthDebugEnabled() {
try {
return localStorage.getItem(AUTH_DEBUG_FLAG_KEY) === "true";
} catch {
return false;
}
}
export function readAuthDebugLog() {
try {
return JSON.parse(localStorage.getItem(AUTH_DEBUG_LOG_KEY)) ?? [];
} catch {
return [];
}
}
// Persisted to localStorage because most logout paths redirect, wiping the console.
export function logAuthEvent(event, details) {
if (!isAuthDebugEnabled()) return;
const entry = {
time: new Date().toISOString(),
event,
path: window.location.pathname,
...details,
};
console.info("[auth-debug]", entry);
try {
const entries = [...readAuthDebugLog(), entry].slice(
-AUTH_DEBUG_LOG_MAX_ENTRIES,
);
localStorage.setItem(AUTH_DEBUG_LOG_KEY, JSON.stringify(entries));
} catch (error) {
console.warn("Failed to persist auth debug log", error);
}
}
export function getStoredAuthKeys() {
return Object.keys(localStorage).filter(
(key) =>
key.startsWith("oauth_") || key === "accessJwt" || key === "refreshJwt",
);
}