Sign In

tracebug-sdk

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tracebug-sdk - npm Package Compare versions

Comparing version
1.7.0
to
1.8.0
dist/chunk-4RKCUHIO.js

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

+1162
"use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/storage.ts
var SESSIONS_KEY = "tracebug_sessions";
var ACTIVE_SESSION_KEY = "tracebug_active_session";
var ACTIVE_CAPTURE_MODE_KEY = "tracebug_active_capture_mode";
function generateSessionId() {
return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : "bt_" + Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
}
function getActiveSessionId() {
try {
return localStorage.getItem(ACTIVE_SESSION_KEY);
} catch (e) {
return null;
}
}
function setActiveSessionId(id) {
try {
localStorage.setItem(ACTIVE_SESSION_KEY, id);
} catch (e) {
}
}
function clearActiveSessionId() {
try {
localStorage.removeItem(ACTIVE_SESSION_KEY);
} catch (e) {
}
try {
localStorage.removeItem(ACTIVE_CAPTURE_MODE_KEY);
} catch (e) {
}
}
function getActiveCaptureMode() {
try {
const v = localStorage.getItem(ACTIVE_CAPTURE_MODE_KEY);
return v === "events" || v === "video" ? v : null;
} catch (e) {
return null;
}
}
function setActiveCaptureMode(mode) {
try {
localStorage.setItem(ACTIVE_CAPTURE_MODE_KEY, mode);
} catch (e) {
}
}
function getAllSessions() {
try {
const raw = localStorage.getItem(SESSIONS_KEY);
return raw ? JSON.parse(raw) : [];
} catch (e) {
return [];
}
}
function saveSessions(sessions) {
try {
localStorage.setItem(SESSIONS_KEY, JSON.stringify(sessions));
return;
} catch (e) {
}
const commit = (next) => {
try {
localStorage.setItem(SESSIONS_KEY, JSON.stringify(next));
sessions.length = 0;
sessions.push(...next);
return true;
} catch (e) {
return false;
}
};
const working = sessions.slice();
while (working.length > 1) {
working.shift();
if (commit(working)) {
if (typeof console !== "undefined") console.warn("[TraceBug] Storage full \u2014 dropped oldest session(s) to fit.");
return;
}
}
const last = working[0];
if (last && Array.isArray(last.events)) {
while (last.events.length > 1) {
last.events = last.events.slice(Math.ceil(last.events.length / 2));
if (commit(working)) {
if (typeof console !== "undefined") console.warn("[TraceBug] Storage full \u2014 trimmed older events from the current session to fit.");
return;
}
}
}
if (typeof console !== "undefined") console.error("[TraceBug] Could not persist sessions: localStorage quota exceeded.");
}
var _cachedSessions = null;
var _pendingFlush = null;
var _dirty = false;
var FLUSH_INTERVAL_MS = 1e3;
function getCachedSessions() {
if (!_cachedSessions) {
_cachedSessions = getAllSessions();
}
return _cachedSessions;
}
function scheduleFlush() {
_dirty = true;
if (_pendingFlush) return;
_pendingFlush = setTimeout(() => {
_pendingFlush = null;
if (_cachedSessions && _dirty) {
saveSessions(_cachedSessions);
_dirty = false;
}
}, FLUSH_INTERVAL_MS);
}
function flushPendingEvents() {
if (_pendingFlush) {
clearTimeout(_pendingFlush);
_pendingFlush = null;
}
if (_cachedSessions) {
saveSessions(_cachedSessions);
_dirty = false;
}
}
function invalidateCache() {
if (_pendingFlush) {
clearTimeout(_pendingFlush);
_pendingFlush = null;
}
_cachedSessions = null;
_dirty = false;
}
if (typeof window !== "undefined") {
window.addEventListener("beforeunload", flushPendingEvents);
window.addEventListener("pagehide", flushPendingEvents);
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") flushPendingEvents();
});
}
function appendEvent(sessionId, event, maxEvents, maxSessions) {
let sessions = getCachedSessions();
let session = sessions.find((s) => s.sessionId === sessionId);
if (!session) {
session = {
sessionId,
projectId: event.projectId,
createdAt: Date.now(),
updatedAt: Date.now(),
errorMessage: null,
errorStack: null,
reproSteps: null,
errorSummary: null,
events: [],
annotations: [],
environment: null
};
sessions.push(session);
}
session.events.push(event);
session.updatedAt = Date.now();
if (session.events.length > maxEvents) {
session.events = session.events.slice(-maxEvents);
}
if (sessions.length > maxSessions) {
sessions = sessions.slice(-maxSessions);
_cachedSessions = sessions;
}
scheduleFlush();
}
function updateSessionError(sessionId, errorMessage, errorStack, reproSteps, errorSummary) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.errorMessage = errorMessage;
session.errorStack = errorStack || null;
session.reproSteps = reproSteps;
session.errorSummary = errorSummary;
session.updatedAt = Date.now();
scheduleFlush();
}
function deleteSession(sessionId) {
flushPendingEvents();
const remaining = getAllSessions().filter((s) => s.sessionId !== sessionId);
invalidateCache();
saveSessions(remaining);
}
function addAnnotation(sessionId, annotation) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
if (!session.annotations) session.annotations = [];
session.annotations.push(annotation);
session.updatedAt = Date.now();
scheduleFlush();
}
function saveEnvironment(sessionId, env) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.environment = env;
scheduleFlush();
}
function setSessionPriority(sessionId, priority) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.priority = priority;
session.updatedAt = Date.now();
scheduleFlush();
}
function markSessionSaved(sessionId) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.saved = true;
session.updatedAt = Date.now();
flushPendingEvents();
}
function clearAllSessions() {
invalidateCache();
try {
localStorage.removeItem(SESSIONS_KEY);
} catch (e) {
}
}
// src/sanitize/custom-redaction.ts
var REDACTED = "[REDACTED]";
var _fieldRe = null;
var _fieldTextRes = [];
var _patterns = [];
function escapeRe(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function setRedactRules(rules) {
_fieldRe = null;
_fieldTextRes = [];
_patterns = [];
if (!rules) return;
const fields = (rules.fields || []).filter((f) => typeof f === "string" && f.trim().length > 0);
if (fields.length > 0) {
const alts = fields.map((f) => escapeRe(f.trim())).join("|");
_fieldRe = new RegExp(alts, "i");
for (const f of fields) {
const k = escapeRe(f.trim());
_fieldTextRes.push({
re: new RegExp(`("([^"]*${k}[^"]*)"\\s*:\\s*)("(?:[^"\\\\]|\\\\.)*"|-?\\d[\\d.eE+-]*|true|false)`, "gi"),
replace: `$1"${REDACTED}"`
});
_fieldTextRes.push({
re: new RegExp(`\\b([\\w.-]*${k}[\\w.-]*)=([^&\\s"']+)`, "gi"),
replace: `$1=${REDACTED}`
});
}
}
for (const p of rules.patterns || []) {
try {
if (typeof p === "string") {
_patterns.push(new RegExp(p, "gi"));
} else if (p instanceof RegExp) {
_patterns.push(p.flags.includes("g") ? p : new RegExp(p.source, p.flags + "g"));
}
} catch (e) {
}
}
}
function isCustomSensitiveKey(key) {
if (!key || !_fieldRe) return false;
return _fieldRe.test(key);
}
function applyCustomRedaction(s) {
if (!s || _fieldTextRes.length === 0 && _patterns.length === 0) return s;
let out = s;
for (const { re, replace } of _fieldTextRes) out = out.replace(re, replace);
for (const re of _patterns) out = out.replace(re, REDACTED);
return out;
}
// src/sanitize/cloud-upload.ts
var REDACTED2 = "[REDACTED]";
var TOKEN_PATTERNS = [
// Bearer <token> in headers, console output, anywhere
{ name: "bearer", re: /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}/gi, replace: () => "Bearer " + REDACTED2 },
// JWT (3 base64url segments separated by dots, leading with eyJ which is `{"` in base64)
{ name: "jwt", re: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g, replace: mask },
// OpenAI / Stripe sk_*
{ name: "sk_prefix", re: /\bsk-[A-Za-z0-9_-]{20,}\b/g, replace: mask },
// Stripe secret + publishable (live/test, secret + publishable + restricted)
{ name: "stripe", re: /\b(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g, replace: mask },
// GitHub PATs (classic + fine-grained + OAuth + server tokens)
{ name: "github_pat", re: /\bghp_[A-Za-z0-9]{30,}\b/g, replace: mask },
{ name: "github_fine", re: /\bgithub_pat_[A-Za-z0-9_]{60,}\b/g, replace: mask },
{ name: "github_oauth", re: /\bgho_[A-Za-z0-9]{30,}\b/g, replace: mask },
{ name: "github_server", re: /\bghs_[A-Za-z0-9]{30,}\b/g, replace: mask },
// AWS access keys (begins with AKIA, ASIA, AGPA, AIDA, etc.) + secret key (40-char base64)
{ name: "aws_access", re: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|ANPA|ANVA)[A-Z0-9]{16}\b/g, replace: mask },
{ name: "aws_secret", re: /\b(?:aws.{0,20})?[A-Za-z0-9/+]{40}\b(?=.*aws|.*secret|.*key)/gi, replace: mask },
// Slack — broader than before (xoxa-z covers all known prefixes)
{ name: "slack", re: /\bxox[abeprs]-[A-Za-z0-9-]{10,}\b/g, replace: mask },
// Google API keys
{ name: "google_api", re: /\bAIza[A-Za-z0-9_-]{35}\b/g, replace: mask },
// Twilio — Account SID + Auth tokens
{ name: "twilio_sid", re: /\b(?:AC|SK)[a-f0-9]{32}\b/g, replace: mask },
// SendGrid
{ name: "sendgrid", re: /\bSG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\b/g, replace: mask },
// Mailgun
{ name: "mailgun", re: /\bkey-[a-f0-9]{32}\b/g, replace: mask },
// Postmark
{ name: "postmark", re: /\b[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\b(?=.{0,30}(postmark|server-token|api-token))/gi, replace: mask },
// Linear / Vercel / Cloudflare / Discord
{ name: "linear", re: /\blin_api_[A-Za-z0-9]{40,}\b/g, replace: mask },
{ name: "discord_bot", re: /\b[MN][A-Za-z\d]{23}\.[A-Za-z\d_-]{6}\.[A-Za-z\d_-]{27,}\b/g, replace: mask },
// Generic high-entropy hex (≥32 chars). Catches webhook signing secrets,
// session IDs, etc. that don't carry a recognizable prefix. Conservative:
// only triggers when preceded by a common secret-y keyword to avoid
// mangling legitimate hex like git SHAs.
{ name: "labeled_hex", re: /\b(?:secret|token|key|password|api[_-]?key|auth)["':\s=]{1,5}([a-fA-F0-9]{32,})\b/gi, replace: (s) => s.replace(/[a-fA-F0-9]{32,}/, REDACTED2) }
];
function mask(s) {
if (s.length <= 12) return REDACTED2;
return `${s.slice(0, 4)}\u2026${REDACTED2}\u2026${s.slice(-4)}`;
}
var SENSITIVE_QUERY_KEYS = /* @__PURE__ */ new Set([
"token",
"access_token",
"id_token",
"refresh_token",
"api_key",
"apikey",
"secret",
"password",
"passwd",
"pwd",
"auth",
"authorization",
"x-api-key",
"session",
"sid",
"csrf"
]);
function sanitizeUrl(url) {
if (typeof url !== "string" || url.length === 0) return url;
try {
const isAbs = /^[a-z][a-z0-9+.-]*:/i.test(url);
const u = new URL(isAbs ? url : `http://_placeholder_${url.startsWith("/") ? "" : "/"}${url}`);
let changed = false;
u.searchParams.forEach((_v, k) => {
if (SENSITIVE_QUERY_KEYS.has(k.toLowerCase()) || isCustomSensitiveKey(k)) {
u.searchParams.set(k, REDACTED2);
changed = true;
}
});
if (!changed) return sanitizeText(url);
const out = isAbs ? u.toString() : u.pathname + u.search + u.hash;
return sanitizeText(out);
} catch (e) {
return sanitizeText(url);
}
}
function sanitizeText(s) {
if (s == null) return s;
let out = String(s);
for (const p of TOKEN_PATTERNS) out = out.replace(p.re, p.replace);
return applyCustomRedaction(out);
}
function sanitizeTokenShapes(s) {
return sanitizeText(s);
}
function sanitizeReportForUpload(report) {
var _a;
const out = typeof structuredClone === "function" ? structuredClone(report) : JSON.parse(JSON.stringify(report));
if (out.consoleErrors) {
out.consoleErrors = out.consoleErrors.map((e) => {
var _a2;
return {
...e,
message: sanitizeText(e.message),
stack: sanitizeText((_a2 = e.stack) != null ? _a2 : void 0)
};
});
}
if (out.consoleLogs) {
out.consoleLogs = out.consoleLogs.map((e) => {
var _a2;
return {
...e,
message: sanitizeText(e.message),
stack: sanitizeText((_a2 = e.stack) != null ? _a2 : void 0)
};
});
}
if (out.networkErrors) {
out.networkErrors = out.networkErrors.map((e) => {
var _a2;
return {
...e,
url: sanitizeUrl(e.url),
response: sanitizeText((_a2 = e.response) != null ? _a2 : void 0)
};
});
}
if (out.networkRequests) {
out.networkRequests = out.networkRequests.map((e) => {
var _a2;
return {
...e,
url: sanitizeUrl(e.url),
response: sanitizeText((_a2 = e.response) != null ? _a2 : void 0)
};
});
}
if (out.steps) out.steps = sanitizeText(out.steps);
if (out.summary) out.summary = sanitizeText(out.summary);
if (out.title) out.title = sanitizeText(out.title);
if (Array.isArray(out.sessionSteps)) out.sessionSteps = out.sessionSteps.map((s) => sanitizeText(s));
if (out.actionChips) {
out.actionChips = out.actionChips.map((c) => {
var _a2, _b;
return {
...c,
target: sanitizeText((_a2 = c.target) != null ? _a2 : void 0),
detail: sanitizeText((_b = c.detail) != null ? _b : void 0)
};
});
}
if (out.storage) {
const scrub = (entries) => entries.map((e) => ({ ...e, value: sanitizeText(e.value) }));
out.storage.local = scrub(out.storage.local || []);
out.storage.session = scrub(out.storage.session || []);
if (out.storage.cookies) out.storage.cookies = scrub(out.storage.cookies);
}
if ((_a = out.environment) == null ? void 0 : _a.url) out.environment.url = sanitizeUrl(out.environment.url);
if (out.context && typeof out.context === "object") {
const ctx = {};
for (const [k, v] of Object.entries(out.context)) {
ctx[k] = typeof v === "string" ? sanitizeText(v) : v;
}
out.context = ctx;
}
return out;
}
// src/collectors.ts
var ROOT_ID = "tracebug-root";
var PANEL_ID = "tracebug-dashboard-panel";
var BTN_ID = "tracebug-dashboard-btn";
var NETWORK_FAILURE_LIMIT = 10;
var RESPONSE_SNIPPET_CHARS = 200;
var _networkFailures = [];
function pushNetworkFailure(failure) {
try {
if (failure.response) failure.response = sanitizeTokenShapes(failure.response);
_networkFailures.push(failure);
if (_networkFailures.length > NETWORK_FAILURE_LIMIT) {
_networkFailures.splice(0, _networkFailures.length - NETWORK_FAILURE_LIMIT);
}
} catch (e) {
}
}
function getNetworkFailures() {
return _networkFailures.slice();
}
function clearNetworkFailures() {
_networkFailures.length = 0;
}
var SENSITIVE_PARAM_RE = /token|key|secret|auth|password|sig|signature/i;
function sanitizeUrl2(url) {
if (!url) return url;
try {
const qIdx = url.indexOf("?");
if (qIdx === -1) return url;
const base = url.slice(0, qIdx);
const afterQ = url.slice(qIdx + 1);
const hashIdx = afterQ.indexOf("#");
const query = hashIdx === -1 ? afterQ : afterQ.slice(0, hashIdx);
const hash = hashIdx === -1 ? "" : afterQ.slice(hashIdx);
const redacted = query.split("&").map((part) => {
const eqIdx = part.indexOf("=");
if (eqIdx === -1) return part;
const key = part.slice(0, eqIdx);
if (SENSITIVE_PARAM_RE.test(key) || isCustomSensitiveKey(key)) return `${key}=[REDACTED]`;
return part;
}).join("&");
return `${base}?${redacted}${hash}`;
} catch (e) {
return url;
}
}
var MAX_BODY_BYTES = 10 * 1024;
var BINARY_CONTENT_TYPE_RE = /^(image|video|audio)\/|^application\/(octet-stream|pdf|zip|x-protobuf|x-msgpack|wasm|vnd\.)/i;
async function readResponseBodySafe(response) {
try {
const ct = response.headers.get("content-type") || "";
if (BINARY_CONTENT_TYPE_RE.test(ct)) return "";
if (!response.body || typeof response.body.getReader !== "function") {
try {
const text = await response.text();
return typeof text === "string" ? text.slice(0, RESPONSE_SNIPPET_CHARS) : "";
} catch (e) {
return "";
}
}
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8", { fatal: false });
let collected = "";
let bytesRead = 0;
while (bytesRead < MAX_BODY_BYTES && collected.length < RESPONSE_SNIPPET_CHARS) {
const { value, done } = await reader.read();
if (done) break;
if (value) {
bytesRead += value.byteLength;
collected += decoder.decode(value, { stream: true });
}
}
try {
await reader.cancel();
} catch (e) {
}
return collected.slice(0, RESPONSE_SNIPPET_CHARS);
} catch (e) {
return "";
}
}
var INTERNAL_URL_PATTERNS = [
/__nextjs_original-stack-frame/,
/\/_next\/static\/webpack/,
/\/__webpack_hmr/,
/\.hot-update\./,
/\/sockjs-node\//,
/\/turbopack-hmr\//,
/\/_next\/webpack-hmr/,
/\/webpack-dev-server\//,
/\/__vite_ping/,
/\/@vite\/client/,
/\/@react-refresh/
];
function isInternalUrl(url) {
return INTERNAL_URL_PATTERNS.some((pattern) => pattern.test(url));
}
var _rootCache;
function getRoot() {
if (_rootCache === void 0) {
_rootCache = document.getElementById(ROOT_ID);
}
if (_rootCache && !_rootCache.isConnected) {
_rootCache = document.getElementById(ROOT_ID);
}
return _rootCache;
}
function isTraceBugElement(el) {
if (!el) return false;
if (el.id === ROOT_ID || el.id === BTN_ID || el.id === PANEL_ID) return true;
if (el.dataset && el.dataset.tracebug) return true;
const root = getRoot();
if (root && root.contains(el)) return true;
let node = el;
while (node) {
const id = node.id || "";
if (id.startsWith("tracebug-") || id.startsWith("bt-")) return true;
const cn = typeof node.className === "string" ? node.className : "";
if (cn.includes("tracebug-") || cn.includes("bt-ann") || cn.includes("bt-voice")) return true;
if (node.dataset && node.dataset.tracebug) return true;
node = node.parentElement;
}
return false;
}
function collectClicks(emit) {
const handler = (e) => {
try {
const t = e.target;
if (!t || isTraceBugElement(t)) return;
const tag = t.tagName.toLowerCase();
const el = {
tag,
text: (t.innerText || "").slice(0, 120),
id: t.id || "",
className: typeof t.className === "string" ? t.className : ""
};
const data = { element: el };
if (tag === "a") el.href = t.href || "";
if (tag === "button" || t.type === "submit") {
el.buttonType = t.type || "button";
el.disabled = t.disabled;
}
if (tag === "label") el.forField = t.htmlFor || "";
const ariaLabel = t.getAttribute("aria-label");
if (ariaLabel) el.ariaLabel = ariaLabel;
const role = t.getAttribute("role");
if (role) el.role = role;
const testId = t.getAttribute("data-testid");
if (testId) el.testId = testId;
const form = t.closest("form");
if (form) {
el.formId = form.id || "";
el.formAction = form.action || "";
}
try {
el.selector = buildSelector(t);
} catch (e2) {
}
try {
const r = t.getBoundingClientRect();
el.boundingBox = { x: Math.round(r.left), y: Math.round(r.top), width: Math.round(r.width), height: Math.round(r.height) };
} catch (e2) {
}
emit("click", data);
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Click capture error:", err);
}
};
document.addEventListener("click", handler, { capture: true });
return () => document.removeEventListener("click", handler, { capture: true });
}
function buildSelector(el) {
if (!el) return "";
if (el.id) return `#${CSS.escape(el.id)}`;
const testId = el.getAttribute("data-testid");
if (testId) return `[data-testid="${testId}"]`;
const parts = [];
let node = el;
let depth = 0;
while (node && node !== document.body && depth < 4) {
let part = node.tagName.toLowerCase();
if (node.id) {
parts.unshift(`#${CSS.escape(node.id)}`);
break;
}
const cls = typeof node.className === "string" ? node.className.trim().split(/\s+/).filter(Boolean)[0] : "";
if (cls) part += `.${CSS.escape(cls)}`;
const parent = node.parentElement;
const currentTag = node.tagName;
const currentNode = node;
if (parent) {
const sameTag = Array.from(parent.children).filter((c) => c.tagName === currentTag);
if (sameTag.length > 1) part += `:nth-of-type(${sameTag.indexOf(currentNode) + 1})`;
}
parts.unshift(part);
node = parent;
depth++;
}
return parts.join(" > ");
}
function collectInputs(emit) {
const timers = /* @__PURE__ */ new Map();
const handler = (e) => {
try {
const t = e.target;
if (!t || !("value" in t) || isTraceBugElement(t)) return;
if (t.tagName.toLowerCase() === "select") return;
const prev = timers.get(t);
if (prev) clearTimeout(prev);
timers.set(
t,
setTimeout(() => {
try {
const tag = t.tagName.toLowerCase();
const inputType = t.type || "";
const isSensitive = ["password", "credit-card", "ssn"].includes(inputType) || /password|secret|token|ssn|credit/i.test(t.name || t.id || "") || isCustomSensitiveKey(t.name || t.id);
const element = {
tag,
name: t.name || t.id || "",
type: inputType,
valueLength: (t.value || "").length,
value: isSensitive ? "[REDACTED]" : (t.value || "").slice(0, 200),
placeholder: t.placeholder || ""
};
const data = { element };
if (inputType === "checkbox" || inputType === "radio") {
element.checked = t.checked;
element.value = t.checked ? "checked" : "unchecked";
}
if (inputType === "number" || inputType === "range") {
element.value = t.value;
}
emit("input", data);
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Input capture error:", err);
}
timers.delete(t);
}, 300)
);
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Input capture error:", err);
}
};
document.addEventListener("input", handler, { capture: true });
return () => {
document.removeEventListener("input", handler, { capture: true });
timers.forEach((t) => clearTimeout(t));
};
}
function collectSelectChanges(emit) {
const handler = (e) => {
try {
const t = e.target;
if (!t || t.tagName.toLowerCase() !== "select" || isTraceBugElement(t)) return;
const selectedOption = t.options[t.selectedIndex];
emit("select_change", {
element: {
tag: "select",
name: t.name || t.id || "",
value: t.value,
selectedText: selectedOption ? selectedOption.text : "",
selectedIndex: t.selectedIndex,
optionCount: t.options.length,
allOptions: Array.from(t.options).map((o) => o.text).slice(0, 20)
}
});
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Select capture error:", err);
}
};
document.addEventListener("change", handler, { capture: true });
return () => document.removeEventListener("change", handler, { capture: true });
}
function collectFormSubmits(emit) {
const handler = (e) => {
try {
const form = e.target;
if (!form || form.tagName.toLowerCase() !== "form" || isTraceBugElement(form)) return;
const formData = {};
const elements = form.elements;
for (let i = 0; i < elements.length; i++) {
const el = elements[i];
if (!el.name) continue;
const isSensitive = ["password"].includes(el.type) || /password|secret|token|ssn|credit/i.test(el.name) || isCustomSensitiveKey(el.name);
if (el.type === "submit" || el.type === "button") continue;
formData[el.name] = isSensitive ? "[REDACTED]" : (el.value || "").slice(0, 200);
}
emit("form_submit", {
form: { id: form.id || "", action: form.action || "", method: form.method || "GET", fieldCount: elements.length, fields: formData }
});
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Form capture error:", err);
}
};
document.addEventListener("submit", handler, { capture: true });
return () => document.removeEventListener("submit", handler, { capture: true });
}
function collectRouteChanges(emit) {
let lastPath = window.location.pathname;
const check = () => {
const current = window.location.pathname;
if (current !== lastPath) {
const from = lastPath;
lastPath = current;
emit("route_change", { from, to: current });
}
};
window.addEventListener("popstate", check);
const origPush = history.pushState.bind(history);
const origReplace = history.replaceState.bind(history);
history.pushState = function(...args) {
origPush(...args);
check();
};
history.replaceState = function(...args) {
origReplace(...args);
check();
};
return () => {
window.removeEventListener("popstate", check);
history.pushState = origPush;
history.replaceState = origReplace;
};
}
function collectApiRequests(emit) {
const originalFetch = window.fetch;
window.fetch = async function(input, init) {
var _a;
let url = "";
let method = "GET";
try {
if (typeof input === "string") {
url = input;
} else if (input instanceof URL) {
url = input.href;
} else if (input && typeof input === "object" && "url" in input) {
url = input.url;
method = input.method || "GET";
}
if (init == null ? void 0 : init.method) method = init.method;
} catch (e) {
}
const start = Date.now();
try {
if (url && isInternalUrl(url)) return originalFetch.call(window, input, init);
} catch (e) {
}
const safeUrl = sanitizeUrl2(url).slice(0, 500);
try {
const response = await originalFetch.call(window, input, init);
try {
emit("api_request", {
request: { url: safeUrl, method: method.toUpperCase(), statusCode: response.status, durationMs: Date.now() - start }
});
} catch (e) {
}
try {
if (response.status >= 400 || response.status === 0) {
const clone = response.clone();
readResponseBodySafe(clone).then((snippet) => {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: response.status,
response: snippet,
timestamp: Date.now()
});
}).catch(() => {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: response.status,
response: "",
timestamp: Date.now()
});
});
}
} catch (e) {
}
return response;
} catch (err) {
try {
emit("api_request", {
request: { url: safeUrl, method: method.toUpperCase(), statusCode: 0, durationMs: Date.now() - start }
});
} catch (e) {
}
try {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: 0,
response: ((_a = err == null ? void 0 : err.message) == null ? void 0 : _a.slice(0, RESPONSE_SNIPPET_CHARS)) || "",
timestamp: Date.now()
});
} catch (e) {
}
throw err;
}
};
return () => {
window.fetch = originalFetch;
};
}
function collectXhrRequests(emit) {
const OrigXHR = window.XMLHttpRequest;
const origOpen = OrigXHR.prototype.open;
const origSend = OrigXHR.prototype.send;
const xhrMeta = /* @__PURE__ */ new WeakMap();
OrigXHR.prototype.open = function(method, url, ...rest) {
try {
xhrMeta.set(this, { method, url: typeof url === "string" ? url : url.toString() });
} catch (e) {
}
return origOpen.apply(this, [method, url, ...rest]);
};
OrigXHR.prototype.send = function(body) {
try {
const xhr = this;
const start = Date.now();
const meta = xhrMeta.get(xhr);
const method = (meta == null ? void 0 : meta.method) || "GET";
const url = (meta == null ? void 0 : meta.url) || "";
if (isInternalUrl(url)) return origSend.call(this, body);
const safeUrl = sanitizeUrl2(url).slice(0, 500);
xhr.addEventListener("loadend", function() {
try {
emit("api_request", { request: { url: safeUrl, method: method.toUpperCase(), statusCode: xhr.status, durationMs: Date.now() - start } });
} catch (e) {
}
try {
if (xhr.status >= 400 || xhr.status === 0) {
let body2 = "";
try {
const ct = xhr.getResponseHeader && xhr.getResponseHeader("content-type") || "";
if (!BINARY_CONTENT_TYPE_RE.test(ct)) {
body2 = typeof xhr.responseText === "string" ? xhr.responseText : "";
}
} catch (e) {
}
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: xhr.status,
response: body2.slice(0, RESPONSE_SNIPPET_CHARS),
timestamp: Date.now()
});
}
} catch (e) {
}
});
xhr.addEventListener("error", function() {
try {
emit("api_request", { request: { url: safeUrl, method: method.toUpperCase(), statusCode: 0, durationMs: Date.now() - start } });
} catch (e) {
}
try {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: 0,
response: "",
timestamp: Date.now()
});
} catch (e) {
}
});
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] XHR capture error:", err);
}
return origSend.call(this, body);
};
return () => {
OrigXHR.prototype.open = origOpen;
OrigXHR.prototype.send = origSend;
};
}
var _perfSeen = /* @__PURE__ */ new Set();
function _emitPerfEntry(emit, e) {
if (!e || !e.name) return;
try {
if (isInternalUrl(e.name)) return;
} catch (e2) {
}
if (typeof e.name === "string" && e.name.indexOf("tracebug") !== -1) return;
const key = `${e.name}|${Math.round(e.startTime)}`;
if (_perfSeen.has(key)) return;
_perfSeen.add(key);
const navStart = typeof performance.timeOrigin === "number" ? performance.timeOrigin : Date.now();
const ext = e;
const initiator = e.initiatorType || "";
const method = (ext.method || "GET").toUpperCase();
const status = ext.responseStatus || 0;
const url = sanitizeUrl2(e.name).slice(0, 500);
const timestamp = Math.round(navStart + e.startTime);
const durationMs = Math.round(e.duration || 0);
try {
emit("api_request", {
request: { url, method, statusCode: status, durationMs, initiatorType: initiator },
_ts: timestamp
});
} catch (e2) {
}
}
function drainPerformanceNetwork(emit) {
if (typeof performance === "undefined") return;
try {
const entries = performance.getEntriesByType("resource");
for (const e of entries) _emitPerfEntry(emit, e);
} catch (e) {
}
}
function collectPerformanceNetwork(emit) {
if (typeof performance === "undefined" || typeof PerformanceObserver === "undefined") {
return () => {
};
}
drainPerformanceNetwork(emit);
let observer = null;
try {
observer = new PerformanceObserver((list) => {
for (const e of list.getEntries()) {
_emitPerfEntry(emit, e);
}
});
observer.observe({ type: "resource", buffered: false });
} catch (e) {
}
return () => {
try {
observer == null ? void 0 : observer.disconnect();
} catch (e) {
}
};
}
function collectErrors(emit) {
const prevOnError = window.onerror;
window.onerror = (msg, source, line, col, error) => {
try {
emit("error", {
error: {
message: sanitizeTokenShapes(typeof msg === "string" ? msg : "Unknown error"),
stack: (error == null ? void 0 : error.stack) && sanitizeTokenShapes(error.stack),
source,
line,
column: col
}
});
} catch (e) {
}
if (prevOnError) {
try {
prevOnError(msg, source, line, col, error);
} catch (e) {
}
}
};
const onRejection = (e) => {
var _a, _b;
try {
emit("unhandled_rejection", {
error: {
message: sanitizeTokenShapes(((_a = e.reason) == null ? void 0 : _a.message) || String(e.reason)),
stack: ((_b = e.reason) == null ? void 0 : _b.stack) && sanitizeTokenShapes(e.reason.stack)
}
});
} catch (e2) {
}
};
window.addEventListener("unhandledrejection", onRejection);
return () => {
window.onerror = prevOnError;
window.removeEventListener("unhandledrejection", onRejection);
};
}
function collectConsoleErrors(emit) {
const origConsoleError = console.error;
let _insideEmit = false;
console.error = function(...args) {
if (_insideEmit) {
origConsoleError.apply(console, args);
return;
}
_insideEmit = true;
try {
emit("console_error", {
// Token-shape scrub at capture — a token logged to the console must
// never reach the offline .html export unmasked (the cloud sanitizer
// only covers the upload path).
error: { message: sanitizeTokenShapes(args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")) }
});
} catch (e) {
} finally {
_insideEmit = false;
}
origConsoleError.apply(console, args);
};
return () => {
console.error = origConsoleError;
};
}
var CONSOLE_LEVEL_CAP = 50;
function wrapConsoleLevel(method, type, emit) {
const orig = console[method];
let _inside = false;
let _count = 0;
console[method] = function(...args) {
const own = typeof args[0] === "string" && args[0].startsWith("[TraceBug]");
if (_inside || own || _count >= CONSOLE_LEVEL_CAP) {
orig.apply(console, args);
return;
}
_inside = true;
_count++;
try {
emit(type, {
// Same capture-time token scrub as console_error — the offline
// export path never runs the cloud sanitizer.
error: { message: sanitizeTokenShapes(args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")) }
});
} catch (e) {
} finally {
_inside = false;
}
orig.apply(console, args);
};
return () => {
console[method] = orig;
};
}
function collectConsoleWarnings(emit) {
return wrapConsoleLevel("warn", "console_warn", emit);
}
function collectConsoleInfo(emit) {
return wrapConsoleLevel("info", "console_info", emit);
}
function collectConsoleLogs(emit) {
return wrapConsoleLevel("log", "console_log", emit);
}
// src/ui/helpers.ts
function tbIsolationCss(root) {
return `
${root} {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 14px; font-weight: 400; line-height: 1.5; font-style: normal;
letter-spacing: normal; text-transform: none; text-align: left;
text-indent: 0; white-space: normal; word-spacing: normal; text-shadow: none;
-webkit-font-smoothing: antialiased;
}
${root} *, ${root} *::before, ${root} *::after { box-sizing: border-box; }
${root} button, ${root} input, ${root} select, ${root} textarea {
font-family: inherit; font-size: inherit; letter-spacing: normal;
text-transform: none; margin: 0;
}
${root} svg { max-width: none; max-height: none; vertical-align: middle; }
${root} img { max-width: none; }
${root} a { text-decoration: none; }
`;
}
function parseShortcut(shortcut) {
const parts = (shortcut || "").toLowerCase().split("+").map((s) => s.trim());
return {
mod: parts.includes("ctrl") || parts.includes("control") || parts.includes("cmd") || parts.includes("meta"),
shift: parts.includes("shift"),
alt: parts.includes("alt") || parts.includes("option"),
key: parts[parts.length - 1] || ""
};
}
function matchesShortcut(e, shortcut) {
if (!shortcut) return false;
const s = parseShortcut(shortcut);
const mod = e.ctrlKey || e.metaKey;
const key = (e.key || "").toLowerCase();
return mod === s.mod && e.shiftKey === s.shift && e.altKey === s.alt && key === s.key;
}
function escapeHtml(str) {
return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
exports.generateSessionId = generateSessionId; exports.getActiveSessionId = getActiveSessionId; exports.setActiveSessionId = setActiveSessionId; exports.clearActiveSessionId = clearActiveSessionId; exports.getActiveCaptureMode = getActiveCaptureMode; exports.setActiveCaptureMode = setActiveCaptureMode; exports.getAllSessions = getAllSessions; exports.getCachedSessions = getCachedSessions; exports.scheduleFlush = scheduleFlush; exports.flushPendingEvents = flushPendingEvents; exports.appendEvent = appendEvent; exports.updateSessionError = updateSessionError; exports.deleteSession = deleteSession; exports.addAnnotation = addAnnotation; exports.saveEnvironment = saveEnvironment; exports.setSessionPriority = setSessionPriority; exports.markSessionSaved = markSessionSaved; exports.clearAllSessions = clearAllSessions; exports.setRedactRules = setRedactRules; exports.isCustomSensitiveKey = isCustomSensitiveKey; exports.sanitizeTokenShapes = sanitizeTokenShapes; exports.sanitizeReportForUpload = sanitizeReportForUpload; exports.getNetworkFailures = getNetworkFailures; exports.clearNetworkFailures = clearNetworkFailures; exports.collectClicks = collectClicks; exports.collectInputs = collectInputs; exports.collectSelectChanges = collectSelectChanges; exports.collectFormSubmits = collectFormSubmits; exports.collectRouteChanges = collectRouteChanges; exports.collectApiRequests = collectApiRequests; exports.collectXhrRequests = collectXhrRequests; exports.drainPerformanceNetwork = drainPerformanceNetwork; exports.collectPerformanceNetwork = collectPerformanceNetwork; exports.collectErrors = collectErrors; exports.collectConsoleErrors = collectConsoleErrors; exports.collectConsoleWarnings = collectConsoleWarnings; exports.collectConsoleInfo = collectConsoleInfo; exports.collectConsoleLogs = collectConsoleLogs; exports.tbIsolationCss = tbIsolationCss; exports.matchesShortcut = matchesShortcut; exports.escapeHtml = escapeHtml;
//# sourceMappingURL=chunk-4SLN5LQD.cjs.map
{"version":3,"sources":["d:\\Project\\TraceBug-ai\\dist\\chunk-4SLN5LQD.cjs"],"names":[],"mappings":"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;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,UAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,gBAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA;AACA;AACA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA;AACA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAA;AACA,IAAA;AACA;AACA;AACA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"D:\\Project\\TraceBug-ai\\dist\\chunk-4SLN5LQD.cjs","sourcesContent":[null]}
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }
var _chunk4SLN5LQDcjs = require('./chunk-4SLN5LQD.cjs');
// src/scanner/helpers.ts
var _issueCounter = 0;
function makeIssueId(detector) {
_issueCounter += 1;
return `${detector}_${Date.now().toString(36)}_${_issueCounter}`;
}
function buildSelector(el) {
if (!el || el.nodeType !== 1) return "";
if (el.id) return `#${cssEscape(el.id)}`;
const testId = el.getAttribute("data-testid") || el.getAttribute("data-test-id");
if (testId) return `[data-testid="${cssEscape(testId)}"]`;
const parts = [];
let cur = el;
let depth = 0;
while (cur && cur.nodeType === 1 && cur.tagName !== "BODY" && depth < 5) {
const tag = cur.tagName.toLowerCase();
const parent = cur.parentElement;
if (!parent) {
parts.unshift(tag);
break;
}
const siblings = Array.from(parent.children).filter((c) => c.tagName === cur.tagName);
if (siblings.length > 1) {
const idx = siblings.indexOf(cur) + 1;
parts.unshift(`${tag}:nth-of-type(${idx})`);
} else {
parts.unshift(tag);
}
cur = parent;
depth += 1;
}
return parts.join(" > ");
}
function coerceSeverity(impact) {
switch ((impact || "").toLowerCase()) {
case "critical":
return "critical";
case "serious":
return "serious";
case "moderate":
return "moderate";
case "minor":
return "minor";
default:
return "minor";
}
}
function cssEscape(value) {
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
return CSS.escape(value);
}
return value.replace(/[^\w-]/g, (ch) => `\\${ch}`);
}
// src/scanner/detectors/broken-images.ts
async function detectBrokenImages() {
const issues = [];
const imgs = Array.from(document.images);
for (const img of imgs) {
if (img.closest("#tracebug-root")) continue;
if (!img.complete) continue;
if (img.naturalWidth > 0) continue;
const src = img.currentSrc || img.src;
if (!src) continue;
issues.push({
id: makeIssueId("broken-image"),
detector: "broken-image",
severity: "moderate",
title: `Broken image: ${truncateUrl(src)}`,
description: `<img> element failed to load. The browser tried to fetch \`${src}\` and got a network error or a non-image response. ${img.alt ? `Alt text: "${img.alt}"` : "No alt text \u2014 also fails accessibility."}`,
selector: buildSelector(img),
url: src,
page: window.location.pathname,
detectedAt: Date.now()
});
}
return issues;
}
function truncateUrl(url) {
if (url.length <= 60) return url;
const tail = url.split("/").pop() || url.slice(-40);
return `\u2026/${tail}`;
}
// src/scanner/detectors/mixed-content.ts
var ATTR_TARGETS = [
{ tag: "img", attr: "src" },
{ tag: "script", attr: "src" },
{ tag: "iframe", attr: "src" },
{ tag: "link", attr: "href" },
{ tag: "audio", attr: "src" },
{ tag: "video", attr: "src" },
{ tag: "source", attr: "src" },
{ tag: "embed", attr: "src" },
{ tag: "object", attr: "data" }
];
async function detectMixedContent() {
if (typeof window === "undefined" || window.location.protocol !== "https:") {
return [];
}
const issues = [];
for (const { tag, attr } of ATTR_TARGETS) {
const elements = document.querySelectorAll(`${tag}[${attr}]`);
for (const el of Array.from(elements)) {
if (el.closest("#tracebug-root")) continue;
const value = el.getAttribute(attr) || "";
if (!value.startsWith("http://")) continue;
if (tag === "link") {
const rel = (el.rel || "").toLowerCase();
const fetchableRels = ["stylesheet", "preload", "prefetch", "manifest", "icon", "shortcut icon"];
if (!fetchableRels.some((r) => rel.includes(r))) continue;
}
issues.push({
id: makeIssueId("mixed-content"),
detector: "mixed-content",
severity: tag === "script" || tag === "iframe" ? "serious" : "moderate",
title: `Mixed content: ${tag} loads over HTTP`,
description: `<${tag}> on an HTTPS page references \`${value}\`. Browsers block or downgrade this \u2014 the resource usually fails to load and breaks the page's secure-context indicator.`,
selector: buildSelector(el),
url: value,
page: window.location.pathname,
detectedAt: Date.now()
});
}
}
return issues;
}
// src/fingerprint.ts
async function computeFingerprint(errorMessage, errorStack, page) {
const errorType = extractErrorType(errorMessage);
const topFrames = extractTopFrames(errorStack || "", 3);
const input = `${errorType}|${topFrames.join("\n")}|${page}`;
if (typeof crypto !== "undefined" && crypto.subtle && typeof crypto.subtle.digest === "function") {
try {
const buf = new TextEncoder().encode(input);
const hash = await crypto.subtle.digest("SHA-1", buf);
return bufferToHex(hash).slice(0, 16);
} catch (e) {
}
}
return djb2(input).toString(16).padStart(8, "0");
}
function extractErrorType(message) {
const m = message.match(/^([A-Z][a-zA-Z]+Error|Error)\b/);
return m ? m[1] : "Error";
}
function extractTopFrames(stack, n) {
const frames = [];
const lines = stack.split("\n");
for (const line of lines) {
const m = line.match(/(https?:\/\/[^):\s]+|[^():\s]+\.[a-z]+):(\d+):(\d+)/i);
if (m) {
const url = m[1];
const path = url.includes("://") ? new URL(url, typeof window !== "undefined" ? window.location.origin : "http://localhost").pathname : url;
frames.push(`${path}:${m[2]}:${m[3]}`);
if (frames.length >= n) break;
}
}
return frames;
}
function bufferToHex(buf) {
const bytes = new Uint8Array(buf);
let out = "";
for (let i = 0; i < bytes.length; i++) out += bytes[i].toString(16).padStart(2, "0");
return out;
}
function djb2(str) {
let hash = 5381;
for (let i = 0; i < str.length; i++) hash = (hash << 5) + hash + str.charCodeAt(i) | 0;
return hash >>> 0;
}
// src/scanner/detectors/session-data.ts
var SLOW_API_MS = 2e3;
var MAX_CONTEXT_SAMPLES = 10;
async function detectConsoleErrors(session) {
var _a, _b, _c;
if (!session) return [];
const groups = /* @__PURE__ */ new Map();
for (let i = 0; i < session.events.length; i++) {
const e = session.events[i];
if (e.type !== "error" && e.type !== "unhandled_rejection" && e.type !== "console_error") continue;
const message = ((_a = e.data.error) == null ? void 0 : _a.message) || e.data.message || "";
if (!message) continue;
const stack = ((_b = e.data.error) == null ? void 0 : _b.stack) || "";
const page = e.page || (typeof window !== "undefined" ? window.location.pathname : "");
const fp = await computeFingerprint(message, stack, page);
const precedingAction = describePrecedingAction(session.events, i);
const existing = groups.get(fp);
if (existing) {
existing.issue.occurrences = (existing.issue.occurrences || 1) + 1;
existing.issue.lastSeenAt = e.timestamp;
if (existing.samples.length < MAX_CONTEXT_SAMPLES) {
existing.samples.push({ timestamp: e.timestamp, precedingAction });
}
continue;
}
const firstFrame = ((_c = stack.split("\n").find((l) => l.trim().startsWith("at "))) == null ? void 0 : _c.trim()) || "";
const issue = {
id: makeIssueId("console-error"),
detector: "console-error",
severity: classifyErrorSeverity(message),
title: `JS error: ${message.slice(0, 70)}${message.length > 70 ? "\u2026" : ""}`,
description: firstFrame ? `${message}
First frame: ${firstFrame}` : message,
page,
detectedAt: e.timestamp,
fingerprint: fp,
occurrences: 1,
firstSeenAt: e.timestamp,
lastSeenAt: e.timestamp
};
groups.set(fp, { issue, samples: [{ timestamp: e.timestamp, precedingAction }] });
}
const out = [];
for (const g of groups.values()) {
const n = g.issue.occurrences || 1;
if (n > 1) {
g.issue.title = `${g.issue.title} [\xD7${n}]`;
g.issue.contextSamples = g.samples;
}
out.push(g.issue);
}
return out;
}
function describePrecedingAction(events, i) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
for (let j = i - 1; j >= 0; j--) {
const e = events[j];
if (e.type === "click") {
const t = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.text) || ((_d = (_c = e.data) == null ? void 0 : _c.element) == null ? void 0 : _d.ariaLabel) || ((_f = (_e = e.data) == null ? void 0 : _e.element) == null ? void 0 : _f.tag) || "element";
return `clicked "${String(t).slice(0, 40)}"`;
}
if (e.type === "input") {
const n = ((_h = (_g = e.data) == null ? void 0 : _g.element) == null ? void 0 : _h.name) || ((_j = (_i = e.data) == null ? void 0 : _i.element) == null ? void 0 : _j.id) || "field";
return `typed in ${n}`;
}
if (e.type === "select_change") return "selected an option";
if (e.type === "form_submit") return "submitted a form";
if (e.type === "route_change") return `navigated to ${((_k = e.data) == null ? void 0 : _k.to) || "page"}`;
}
return void 0;
}
async function detectFailedRequests(session) {
if (!session) return [];
const issues = [];
const buffer = _chunk4SLN5LQDcjs.getNetworkFailures.call(void 0, );
for (const e of session.events) {
if (e.type !== "api_request") continue;
const req = e.data.request;
if (!req) continue;
const status = req.statusCode || 0;
if (status >= 200 && status < 400) continue;
if (status === 0 && req.method === "HEAD") continue;
const match = buffer.find(
(b) => b.url === req.url && b.method === req.method && b.status === status && Math.abs(b.timestamp - e.timestamp) < 5e3
);
const snippet = (match == null ? void 0 : match.response) ? `
Response: ${match.response.slice(0, 160)}` : "";
issues.push({
id: makeIssueId("failed-request"),
detector: "failed-request",
severity: status >= 500 ? "critical" : status === 0 ? "serious" : "moderate",
title: `${req.method} ${truncatePath(req.url)} \u2192 ${status === 0 ? "Network Error" : status}`,
description: `Request failed in ${req.durationMs || 0}ms.${snippet}`,
url: req.url,
page: e.page || window.location.pathname,
detectedAt: e.timestamp
});
}
return issues;
}
async function detectSlowApis(session) {
if (!session) return [];
const issues = [];
for (const e of session.events) {
if (e.type !== "api_request") continue;
const req = e.data.request;
if (!req) continue;
const status = req.statusCode || 0;
if (status < 200 || status >= 400) continue;
const duration = req.durationMs || 0;
if (duration < SLOW_API_MS) continue;
issues.push({
id: makeIssueId("slow-api"),
detector: "slow-api",
severity: duration > 5e3 ? "serious" : "moderate",
title: `Slow API: ${req.method} ${truncatePath(req.url)} (${duration}ms)`,
description: `This request took ${(duration / 1e3).toFixed(1)}s \u2014 over the ${SLOW_API_MS / 1e3}s threshold. Slow APIs are a common UX complaint and a leading cause of perceived bugs ("the page is frozen").`,
url: req.url,
page: e.page || window.location.pathname,
detectedAt: e.timestamp
});
}
return issues;
}
function truncatePath(url) {
try {
const u = new URL(url, window.location.origin);
const p = u.pathname.length > 50 ? u.pathname.slice(0, 47) + "\u2026" : u.pathname;
return p;
} catch (e) {
return url.length > 50 ? url.slice(0, 47) + "\u2026" : url;
}
}
function classifyErrorSeverity(message) {
if (/TypeError|ReferenceError|SyntaxError/i.test(message)) return "critical";
if (/Network|fetch|failed to/i.test(message)) return "serious";
return "moderate";
}
// src/scanner/detectors/a11y.ts
var _axePromise = null;
function loadAxe() {
if (_axePromise) return _axePromise;
_axePromise = Promise.resolve().then(() => _interopRequireWildcard(require("axe-core"))).then((mod) => mod.default || mod).catch((err) => {
console.warn("[TraceBug] axe-core failed to load:", err);
return null;
});
return _axePromise;
}
async function detectA11yViolations() {
const axe = await loadAxe();
if (!axe || typeof axe.run !== "function") return [];
let results;
try {
results = await axe.run(document, {
// Only WCAG-tagged rules — keeps signal-to-noise high. Best-practice
// rules add ~30% more noise without proportional value for QA.
runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] },
// Skip our own UI so QA isn't told their toolbar fails contrast checks.
// axe accepts a context with exclude — we pass { exclude: [...] } via
// the second-argument options-shaped form below to keep types loose.
resultTypes: ["violations"]
});
} catch (err) {
console.warn("[TraceBug] axe.run failed:", err);
return [];
}
const issues = [];
const violations = (results == null ? void 0 : results.violations) || [];
for (const v of violations) {
const nodes = v.nodes || [];
const firstNode = nodes[0];
const selector = Array.isArray(firstNode == null ? void 0 : firstNode.target) ? firstNode.target.join(" ") : "";
const exampleSnippet = ((firstNode == null ? void 0 : firstNode.html) || "").slice(0, 120);
const moreSuffix = nodes.length > 1 ? ` (+ ${nodes.length - 1} more element${nodes.length === 2 ? "" : "s"})` : "";
issues.push({
id: makeIssueId("axe-a11y"),
detector: "axe-a11y",
severity: coerceSeverity(v.impact),
title: `${v.help || v.id}${moreSuffix}`,
description: `${v.description || v.id}
First element: \`${exampleSnippet}\``,
selector: selector || void 0,
helpUrl: v.helpUrl,
page: window.location.pathname,
detectedAt: Date.now()
});
}
return issues;
}
// src/scanner/detectors/frustration.ts
var RAGE_WINDOW_MS = 1500;
var RAGE_MIN_CLICKS = 3;
var DEAD_RESPONSE_WINDOW_MS = 1500;
var ABANDON_WINDOW_MS = 6e4;
var ERROR_CORRELATION_WINDOW_MS = 2500;
async function detectFrustration(session) {
var _a;
if (!session) return [];
const events = session.events;
if (events.length === 0) return [];
const issues = [];
const page = ((_a = session.events[0]) == null ? void 0 : _a.page) || window.location.pathname;
issues.push(...detectRageClicks(events, page));
issues.push(...detectDeadClicks(events, page));
issues.push(...detectFormAbandonment(events, page));
issues.push(...detectErrorCorrelated(events, page));
return issues;
}
function detectRageClicks(events, page) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const out = [];
const seenGroups = /* @__PURE__ */ new Set();
for (let i = 0; i < events.length; i++) {
const e = events[i];
if (e.type !== "click") continue;
const sel = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.selector) || ((_d = (_c = e.data) == null ? void 0 : _c.element) == null ? void 0 : _d.testId) || "";
if (!sel) continue;
const cluster = [e];
let j = i + 1;
while (j < events.length) {
const next = events[j];
if (next.timestamp - e.timestamp > RAGE_WINDOW_MS) break;
if (isResponseEvent(next)) break;
if (next.type === "click") {
const nextSel = ((_f = (_e = next.data) == null ? void 0 : _e.element) == null ? void 0 : _f.selector) || ((_h = (_g = next.data) == null ? void 0 : _g.element) == null ? void 0 : _h.testId) || "";
if (nextSel === sel) cluster.push(next);
}
j++;
}
if (cluster.length >= RAGE_MIN_CLICKS) {
const key = `${sel}@${e.timestamp}`;
if (seenGroups.has(key)) continue;
seenGroups.add(key);
const label = clickLabel(cluster[0]);
out.push({
id: makeIssueId("frustration-rage"),
detector: "frustration-rage",
severity: "serious",
title: `Rage clicks on ${label} (${cluster.length}\xD7 in ${Math.round(cluster[cluster.length - 1].timestamp - cluster[0].timestamp)}ms)`,
description: `User clicked the same element ${cluster.length} times within ${RAGE_WINDOW_MS}ms with no observable response (no API call, navigation, or DOM update). The element either doesn't respond to clicks or feels broken.`,
selector: sel,
page,
detectedAt: cluster[0].timestamp,
firstSeenAt: cluster[0].timestamp,
lastSeenAt: cluster[cluster.length - 1].timestamp,
occurrences: cluster.length
});
i = j - 1;
}
}
return out;
}
function detectDeadClicks(events, page) {
var _a, _b;
const out = [];
const MAX = 5;
for (let i = 0; i < events.length && out.length < MAX; i++) {
const e = events[i];
if (e.type !== "click") continue;
let responsive = false;
for (let j = i + 1; j < events.length; j++) {
const next = events[j];
if (next.timestamp - e.timestamp > DEAD_RESPONSE_WINDOW_MS) break;
if (isResponseEvent(next)) {
responsive = true;
break;
}
}
if (responsive) continue;
const sel = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.selector) || "";
const label = clickLabel(e);
out.push({
id: makeIssueId("frustration-dead"),
detector: "frustration-dead",
severity: "moderate",
title: `Dead click on ${label}`,
description: `Clicked but nothing happened within ${DEAD_RESPONSE_WINDOW_MS}ms (no API call, navigation, or DOM input). The element may have an unbound handler, a swallowed event, or be visually clickable but disabled.`,
selector: sel,
page: e.page || page,
detectedAt: e.timestamp
});
}
return out;
}
function detectFormAbandonment(events, _page) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
const out = [];
const formActivity = {};
for (const e of events) {
if (e.type === "input") {
const formId = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.formId) || ((_d = (_c = e.data) == null ? void 0 : _c.element) == null ? void 0 : _d.formAction) || "_default";
if (!formActivity[formId]) {
formActivity[formId] = { firstInputAt: e.timestamp, fieldsSeen: /* @__PURE__ */ new Set(), lastInputAt: e.timestamp, page: e.page };
}
const name = ((_f = (_e = e.data) == null ? void 0 : _e.element) == null ? void 0 : _f.name) || ((_h = (_g = e.data) == null ? void 0 : _g.element) == null ? void 0 : _h.id) || "field";
formActivity[formId].fieldsSeen.add(name);
formActivity[formId].lastInputAt = e.timestamp;
} else if (e.type === "form_submit") {
const formId = ((_j = (_i = e.data) == null ? void 0 : _i.form) == null ? void 0 : _j.id) || "_default";
delete formActivity[formId];
} else if (e.type === "route_change") {
for (const formId of Object.keys(formActivity)) {
const a = formActivity[formId];
if (e.timestamp - a.lastInputAt > ABANDON_WINDOW_MS) continue;
if (a.fieldsSeen.size === 0) continue;
out.push({
id: makeIssueId("frustration-abandon"),
detector: "frustration-abandon",
severity: "moderate",
title: `Form abandoned on ${a.page} (${a.fieldsSeen.size} field${a.fieldsSeen.size === 1 ? "" : "s"} filled)`,
description: `User typed into ${a.fieldsSeen.size} field${a.fieldsSeen.size === 1 ? "" : "s"} (${Array.from(a.fieldsSeen).slice(0, 5).join(", ")}) and then navigated away without submitting. Likely a UX problem: the submit button is unclear, the form requires too much info, or it's failing silently.`,
page: a.page,
detectedAt: a.lastInputAt,
firstSeenAt: a.firstInputAt,
lastSeenAt: a.lastInputAt
});
delete formActivity[formId];
}
}
}
return out;
}
function detectErrorCorrelated(events, page) {
var _a, _b, _c, _d, _e, _f;
const out = [];
const seen = /* @__PURE__ */ new Set();
for (let i = 0; i < events.length; i++) {
const e = events[i];
const isError = e.type === "error" || e.type === "unhandled_rejection" || e.type === "console_error";
if (!isError) continue;
let click = null;
for (let j = i - 1; j >= 0; j--) {
const prev = events[j];
if (e.timestamp - prev.timestamp > ERROR_CORRELATION_WINDOW_MS) break;
if (prev.type === "click") {
click = prev;
break;
}
}
if (!click) continue;
const errMsg = ((_b = (_a = e.data) == null ? void 0 : _a.error) == null ? void 0 : _b.message) || "";
if (!errMsg) continue;
const key = `${errMsg}::${((_d = (_c = click.data) == null ? void 0 : _c.element) == null ? void 0 : _d.selector) || ""}`;
if (seen.has(key)) continue;
seen.add(key);
const label = clickLabel(click);
const truncMsg = errMsg.length > 60 ? errMsg.slice(0, 57) + "\u2026" : errMsg;
const delta = Math.round(e.timestamp - click.timestamp);
out.push({
id: makeIssueId("frustration-error-correlated"),
detector: "frustration-error-correlated",
severity: "critical",
title: `Click on ${label} triggered: ${truncMsg}`,
description: `An error fired ${delta}ms after the user clicked ${label}. This is almost certainly the offending interaction \u2014 the click handler threw, or its async path failed.`,
selector: (_f = (_e = click.data) == null ? void 0 : _e.element) == null ? void 0 : _f.selector,
page: e.page || page,
detectedAt: e.timestamp
});
}
return out;
}
function isResponseEvent(e) {
return e.type === "api_request" || e.type === "route_change" || e.type === "input" || e.type === "form_submit" || e.type === "select_change";
}
function clickLabel(e) {
var _a;
const el = (_a = e.data) == null ? void 0 : _a.element;
const raw = (el == null ? void 0 : el.text) || (el == null ? void 0 : el.ariaLabel) || (el == null ? void 0 : el.testId) || (el == null ? void 0 : el.id) || (el == null ? void 0 : el.tag) || "element";
const trimmed = String(raw).replace(/\s+/g, " ").trim();
return trimmed.length > 40 ? `"${trimmed.slice(0, 37)}\u2026"` : `"${trimmed}"`;
}
// src/scanner/index.ts
var _issues = [];
var _scanInFlight = null;
var _lastScanAt = 0;
var SEVERITY_ORDER = {
critical: 0,
serious: 1,
moderate: 2,
minor: 3
};
async function scan() {
if (_scanInFlight) {
const issues = await _scanInFlight;
return { issues, durationMs: 0, scannedAt: _lastScanAt };
}
const startedAt = Date.now();
const sessions = _chunk4SLN5LQDcjs.getAllSessions.call(void 0, ).sort((a, b) => b.updatedAt - a.updatedAt);
const session = sessions[0] || null;
const safeRun = (p) => p.catch((err) => {
console.warn("[TraceBug] Detector failed:", err);
return [];
});
_scanInFlight = Promise.all([
safeRun(detectBrokenImages()),
safeRun(detectMixedContent()),
safeRun(detectConsoleErrors(session)),
safeRun(detectFailedRequests(session)),
safeRun(detectSlowApis(session)),
safeRun(detectA11yViolations()),
safeRun(detectFrustration(session))
]).then((results) => {
const all = [].concat(...results);
all.sort((a, b) => {
const sev = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];
if (sev !== 0) return sev;
return a.detectedAt - b.detectedAt;
});
_issues = all;
return all;
});
try {
const issues = await _scanInFlight;
_lastScanAt = Date.now();
return { issues, durationMs: _lastScanAt - startedAt, scannedAt: _lastScanAt };
} finally {
_scanInFlight = null;
}
}
function getIssues(options) {
var _a;
const includeDismissed = (_a = options == null ? void 0 : options.includeDismissed) != null ? _a : false;
return includeDismissed ? _issues.slice() : _issues.filter((i) => !i.dismissed);
}
function dismissIssue(id) {
const issue = _issues.find((i) => i.id === id);
if (!issue) return false;
issue.dismissed = true;
return true;
}
function undismissIssue(id) {
const issue = _issues.find((i) => i.id === id);
if (!issue) return false;
issue.dismissed = false;
return true;
}
function clearIssues() {
_issues = [];
_lastScanAt = 0;
}
function getIssueCountsByDetector() {
const counts = {
"axe-a11y": 0,
"broken-image": 0,
"mixed-content": 0,
"console-error": 0,
"slow-api": 0,
"failed-request": 0,
"frustration-rage": 0,
"frustration-dead": 0,
"frustration-abandon": 0,
"frustration-error-correlated": 0
};
for (const i of _issues) {
if (i.dismissed) continue;
counts[i.detector] = (counts[i.detector] || 0) + 1;
}
return counts;
}
function getIssueCountsBySeverity() {
const counts = {
critical: 0,
serious: 0,
moderate: 0,
minor: 0
};
for (const i of _issues) {
if (i.dismissed) continue;
counts[i.severity] += 1;
}
return counts;
}
function getIssueById(id) {
return _issues.find((i) => i.id === id) || null;
}
exports.scan = scan; exports.getIssues = getIssues; exports.dismissIssue = dismissIssue; exports.undismissIssue = undismissIssue; exports.clearIssues = clearIssues; exports.getIssueCountsByDetector = getIssueCountsByDetector; exports.getIssueCountsBySeverity = getIssueCountsBySeverity; exports.getIssueById = getIssueById;
//# sourceMappingURL=chunk-FQTNMBGV.cjs.map
{"version":3,"sources":["d:\\Project\\TraceBug-ai\\dist\\chunk-FQTNMBGV.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,wDAA6B;AAC7B;AACA;AACA,IAAI,cAAc,EAAE,CAAC;AACrB,SAAS,WAAW,CAAC,QAAQ,EAAE;AAC/B,EAAE,cAAc,GAAG,CAAC;AACpB,EAAE,OAAO,CAAC,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA;AACA,aAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA;AACA,UAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA;AACA;AACA,MAAA;AACA;AACA;AACA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA;AACA,iBAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"D:\\Project\\TraceBug-ai\\dist\\chunk-FQTNMBGV.cjs","sourcesContent":[null]}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

// src/storage.ts
var SESSIONS_KEY = "tracebug_sessions";
var ACTIVE_SESSION_KEY = "tracebug_active_session";
var ACTIVE_CAPTURE_MODE_KEY = "tracebug_active_capture_mode";
function generateSessionId() {
return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : "bt_" + Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
}
function getActiveSessionId() {
try {
return localStorage.getItem(ACTIVE_SESSION_KEY);
} catch (e) {
return null;
}
}
function setActiveSessionId(id) {
try {
localStorage.setItem(ACTIVE_SESSION_KEY, id);
} catch (e) {
}
}
function clearActiveSessionId() {
try {
localStorage.removeItem(ACTIVE_SESSION_KEY);
} catch (e) {
}
try {
localStorage.removeItem(ACTIVE_CAPTURE_MODE_KEY);
} catch (e) {
}
}
function getActiveCaptureMode() {
try {
const v = localStorage.getItem(ACTIVE_CAPTURE_MODE_KEY);
return v === "events" || v === "video" ? v : null;
} catch (e) {
return null;
}
}
function setActiveCaptureMode(mode) {
try {
localStorage.setItem(ACTIVE_CAPTURE_MODE_KEY, mode);
} catch (e) {
}
}
function getAllSessions() {
try {
const raw = localStorage.getItem(SESSIONS_KEY);
return raw ? JSON.parse(raw) : [];
} catch (e) {
return [];
}
}
function saveSessions(sessions) {
try {
localStorage.setItem(SESSIONS_KEY, JSON.stringify(sessions));
return;
} catch (e) {
}
const commit = (next) => {
try {
localStorage.setItem(SESSIONS_KEY, JSON.stringify(next));
sessions.length = 0;
sessions.push(...next);
return true;
} catch (e) {
return false;
}
};
const working = sessions.slice();
while (working.length > 1) {
working.shift();
if (commit(working)) {
if (typeof console !== "undefined") console.warn("[TraceBug] Storage full \u2014 dropped oldest session(s) to fit.");
return;
}
}
const last = working[0];
if (last && Array.isArray(last.events)) {
while (last.events.length > 1) {
last.events = last.events.slice(Math.ceil(last.events.length / 2));
if (commit(working)) {
if (typeof console !== "undefined") console.warn("[TraceBug] Storage full \u2014 trimmed older events from the current session to fit.");
return;
}
}
}
if (typeof console !== "undefined") console.error("[TraceBug] Could not persist sessions: localStorage quota exceeded.");
}
var _cachedSessions = null;
var _pendingFlush = null;
var _dirty = false;
var FLUSH_INTERVAL_MS = 1e3;
function getCachedSessions() {
if (!_cachedSessions) {
_cachedSessions = getAllSessions();
}
return _cachedSessions;
}
function scheduleFlush() {
_dirty = true;
if (_pendingFlush) return;
_pendingFlush = setTimeout(() => {
_pendingFlush = null;
if (_cachedSessions && _dirty) {
saveSessions(_cachedSessions);
_dirty = false;
}
}, FLUSH_INTERVAL_MS);
}
function flushPendingEvents() {
if (_pendingFlush) {
clearTimeout(_pendingFlush);
_pendingFlush = null;
}
if (_cachedSessions) {
saveSessions(_cachedSessions);
_dirty = false;
}
}
function invalidateCache() {
if (_pendingFlush) {
clearTimeout(_pendingFlush);
_pendingFlush = null;
}
_cachedSessions = null;
_dirty = false;
}
if (typeof window !== "undefined") {
window.addEventListener("beforeunload", flushPendingEvents);
window.addEventListener("pagehide", flushPendingEvents);
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") flushPendingEvents();
});
}
function appendEvent(sessionId, event, maxEvents, maxSessions) {
let sessions = getCachedSessions();
let session = sessions.find((s) => s.sessionId === sessionId);
if (!session) {
session = {
sessionId,
projectId: event.projectId,
createdAt: Date.now(),
updatedAt: Date.now(),
errorMessage: null,
errorStack: null,
reproSteps: null,
errorSummary: null,
events: [],
annotations: [],
environment: null
};
sessions.push(session);
}
session.events.push(event);
session.updatedAt = Date.now();
if (session.events.length > maxEvents) {
session.events = session.events.slice(-maxEvents);
}
if (sessions.length > maxSessions) {
sessions = sessions.slice(-maxSessions);
_cachedSessions = sessions;
}
scheduleFlush();
}
function updateSessionError(sessionId, errorMessage, errorStack, reproSteps, errorSummary) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.errorMessage = errorMessage;
session.errorStack = errorStack || null;
session.reproSteps = reproSteps;
session.errorSummary = errorSummary;
session.updatedAt = Date.now();
scheduleFlush();
}
function deleteSession(sessionId) {
flushPendingEvents();
const remaining = getAllSessions().filter((s) => s.sessionId !== sessionId);
invalidateCache();
saveSessions(remaining);
}
function addAnnotation(sessionId, annotation) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
if (!session.annotations) session.annotations = [];
session.annotations.push(annotation);
session.updatedAt = Date.now();
scheduleFlush();
}
function saveEnvironment(sessionId, env) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.environment = env;
scheduleFlush();
}
function setSessionPriority(sessionId, priority) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.priority = priority;
session.updatedAt = Date.now();
scheduleFlush();
}
function markSessionSaved(sessionId) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.saved = true;
session.updatedAt = Date.now();
flushPendingEvents();
}
function clearAllSessions() {
invalidateCache();
try {
localStorage.removeItem(SESSIONS_KEY);
} catch (e) {
}
}
// src/sanitize/custom-redaction.ts
var REDACTED = "[REDACTED]";
var _fieldRe = null;
var _fieldTextRes = [];
var _patterns = [];
function escapeRe(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function setRedactRules(rules) {
_fieldRe = null;
_fieldTextRes = [];
_patterns = [];
if (!rules) return;
const fields = (rules.fields || []).filter((f) => typeof f === "string" && f.trim().length > 0);
if (fields.length > 0) {
const alts = fields.map((f) => escapeRe(f.trim())).join("|");
_fieldRe = new RegExp(alts, "i");
for (const f of fields) {
const k = escapeRe(f.trim());
_fieldTextRes.push({
re: new RegExp(`("([^"]*${k}[^"]*)"\\s*:\\s*)("(?:[^"\\\\]|\\\\.)*"|-?\\d[\\d.eE+-]*|true|false)`, "gi"),
replace: `$1"${REDACTED}"`
});
_fieldTextRes.push({
re: new RegExp(`\\b([\\w.-]*${k}[\\w.-]*)=([^&\\s"']+)`, "gi"),
replace: `$1=${REDACTED}`
});
}
}
for (const p of rules.patterns || []) {
try {
if (typeof p === "string") {
_patterns.push(new RegExp(p, "gi"));
} else if (p instanceof RegExp) {
_patterns.push(p.flags.includes("g") ? p : new RegExp(p.source, p.flags + "g"));
}
} catch (e) {
}
}
}
function isCustomSensitiveKey(key) {
if (!key || !_fieldRe) return false;
return _fieldRe.test(key);
}
function applyCustomRedaction(s) {
if (!s || _fieldTextRes.length === 0 && _patterns.length === 0) return s;
let out = s;
for (const { re, replace } of _fieldTextRes) out = out.replace(re, replace);
for (const re of _patterns) out = out.replace(re, REDACTED);
return out;
}
// src/sanitize/cloud-upload.ts
var REDACTED2 = "[REDACTED]";
var TOKEN_PATTERNS = [
// Bearer <token> in headers, console output, anywhere
{ name: "bearer", re: /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}/gi, replace: () => "Bearer " + REDACTED2 },
// JWT (3 base64url segments separated by dots, leading with eyJ which is `{"` in base64)
{ name: "jwt", re: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g, replace: mask },
// OpenAI / Stripe sk_*
{ name: "sk_prefix", re: /\bsk-[A-Za-z0-9_-]{20,}\b/g, replace: mask },
// Stripe secret + publishable (live/test, secret + publishable + restricted)
{ name: "stripe", re: /\b(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g, replace: mask },
// GitHub PATs (classic + fine-grained + OAuth + server tokens)
{ name: "github_pat", re: /\bghp_[A-Za-z0-9]{30,}\b/g, replace: mask },
{ name: "github_fine", re: /\bgithub_pat_[A-Za-z0-9_]{60,}\b/g, replace: mask },
{ name: "github_oauth", re: /\bgho_[A-Za-z0-9]{30,}\b/g, replace: mask },
{ name: "github_server", re: /\bghs_[A-Za-z0-9]{30,}\b/g, replace: mask },
// AWS access keys (begins with AKIA, ASIA, AGPA, AIDA, etc.) + secret key (40-char base64)
{ name: "aws_access", re: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|ANPA|ANVA)[A-Z0-9]{16}\b/g, replace: mask },
{ name: "aws_secret", re: /\b(?:aws.{0,20})?[A-Za-z0-9/+]{40}\b(?=.*aws|.*secret|.*key)/gi, replace: mask },
// Slack — broader than before (xoxa-z covers all known prefixes)
{ name: "slack", re: /\bxox[abeprs]-[A-Za-z0-9-]{10,}\b/g, replace: mask },
// Google API keys
{ name: "google_api", re: /\bAIza[A-Za-z0-9_-]{35}\b/g, replace: mask },
// Twilio — Account SID + Auth tokens
{ name: "twilio_sid", re: /\b(?:AC|SK)[a-f0-9]{32}\b/g, replace: mask },
// SendGrid
{ name: "sendgrid", re: /\bSG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\b/g, replace: mask },
// Mailgun
{ name: "mailgun", re: /\bkey-[a-f0-9]{32}\b/g, replace: mask },
// Postmark
{ name: "postmark", re: /\b[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\b(?=.{0,30}(postmark|server-token|api-token))/gi, replace: mask },
// Linear / Vercel / Cloudflare / Discord
{ name: "linear", re: /\blin_api_[A-Za-z0-9]{40,}\b/g, replace: mask },
{ name: "discord_bot", re: /\b[MN][A-Za-z\d]{23}\.[A-Za-z\d_-]{6}\.[A-Za-z\d_-]{27,}\b/g, replace: mask },
// Generic high-entropy hex (≥32 chars). Catches webhook signing secrets,
// session IDs, etc. that don't carry a recognizable prefix. Conservative:
// only triggers when preceded by a common secret-y keyword to avoid
// mangling legitimate hex like git SHAs.
{ name: "labeled_hex", re: /\b(?:secret|token|key|password|api[_-]?key|auth)["':\s=]{1,5}([a-fA-F0-9]{32,})\b/gi, replace: (s) => s.replace(/[a-fA-F0-9]{32,}/, REDACTED2) }
];
function mask(s) {
if (s.length <= 12) return REDACTED2;
return `${s.slice(0, 4)}\u2026${REDACTED2}\u2026${s.slice(-4)}`;
}
var SENSITIVE_QUERY_KEYS = /* @__PURE__ */ new Set([
"token",
"access_token",
"id_token",
"refresh_token",
"api_key",
"apikey",
"secret",
"password",
"passwd",
"pwd",
"auth",
"authorization",
"x-api-key",
"session",
"sid",
"csrf"
]);
function sanitizeUrl(url) {
if (typeof url !== "string" || url.length === 0) return url;
try {
const isAbs = /^[a-z][a-z0-9+.-]*:/i.test(url);
const u = new URL(isAbs ? url : `http://_placeholder_${url.startsWith("/") ? "" : "/"}${url}`);
let changed = false;
u.searchParams.forEach((_v, k) => {
if (SENSITIVE_QUERY_KEYS.has(k.toLowerCase()) || isCustomSensitiveKey(k)) {
u.searchParams.set(k, REDACTED2);
changed = true;
}
});
if (!changed) return sanitizeText(url);
const out = isAbs ? u.toString() : u.pathname + u.search + u.hash;
return sanitizeText(out);
} catch (e) {
return sanitizeText(url);
}
}
function sanitizeText(s) {
if (s == null) return s;
let out = String(s);
for (const p of TOKEN_PATTERNS) out = out.replace(p.re, p.replace);
return applyCustomRedaction(out);
}
function sanitizeTokenShapes(s) {
return sanitizeText(s);
}
function sanitizeReportForUpload(report) {
var _a;
const out = typeof structuredClone === "function" ? structuredClone(report) : JSON.parse(JSON.stringify(report));
if (out.consoleErrors) {
out.consoleErrors = out.consoleErrors.map((e) => {
var _a2;
return {
...e,
message: sanitizeText(e.message),
stack: sanitizeText((_a2 = e.stack) != null ? _a2 : void 0)
};
});
}
if (out.consoleLogs) {
out.consoleLogs = out.consoleLogs.map((e) => {
var _a2;
return {
...e,
message: sanitizeText(e.message),
stack: sanitizeText((_a2 = e.stack) != null ? _a2 : void 0)
};
});
}
if (out.networkErrors) {
out.networkErrors = out.networkErrors.map((e) => {
var _a2;
return {
...e,
url: sanitizeUrl(e.url),
response: sanitizeText((_a2 = e.response) != null ? _a2 : void 0)
};
});
}
if (out.networkRequests) {
out.networkRequests = out.networkRequests.map((e) => {
var _a2;
return {
...e,
url: sanitizeUrl(e.url),
response: sanitizeText((_a2 = e.response) != null ? _a2 : void 0)
};
});
}
if (out.steps) out.steps = sanitizeText(out.steps);
if (out.summary) out.summary = sanitizeText(out.summary);
if (out.title) out.title = sanitizeText(out.title);
if (Array.isArray(out.sessionSteps)) out.sessionSteps = out.sessionSteps.map((s) => sanitizeText(s));
if (out.actionChips) {
out.actionChips = out.actionChips.map((c) => {
var _a2, _b;
return {
...c,
target: sanitizeText((_a2 = c.target) != null ? _a2 : void 0),
detail: sanitizeText((_b = c.detail) != null ? _b : void 0)
};
});
}
if (out.storage) {
const scrub = (entries) => entries.map((e) => ({ ...e, value: sanitizeText(e.value) }));
out.storage.local = scrub(out.storage.local || []);
out.storage.session = scrub(out.storage.session || []);
if (out.storage.cookies) out.storage.cookies = scrub(out.storage.cookies);
}
if ((_a = out.environment) == null ? void 0 : _a.url) out.environment.url = sanitizeUrl(out.environment.url);
if (out.context && typeof out.context === "object") {
const ctx = {};
for (const [k, v] of Object.entries(out.context)) {
ctx[k] = typeof v === "string" ? sanitizeText(v) : v;
}
out.context = ctx;
}
return out;
}
// src/collectors.ts
var ROOT_ID = "tracebug-root";
var PANEL_ID = "tracebug-dashboard-panel";
var BTN_ID = "tracebug-dashboard-btn";
var NETWORK_FAILURE_LIMIT = 10;
var RESPONSE_SNIPPET_CHARS = 200;
var _networkFailures = [];
function pushNetworkFailure(failure) {
try {
if (failure.response) failure.response = sanitizeTokenShapes(failure.response);
_networkFailures.push(failure);
if (_networkFailures.length > NETWORK_FAILURE_LIMIT) {
_networkFailures.splice(0, _networkFailures.length - NETWORK_FAILURE_LIMIT);
}
} catch (e) {
}
}
function getNetworkFailures() {
return _networkFailures.slice();
}
function clearNetworkFailures() {
_networkFailures.length = 0;
}
var SENSITIVE_PARAM_RE = /token|key|secret|auth|password|sig|signature/i;
function sanitizeUrl2(url) {
if (!url) return url;
try {
const qIdx = url.indexOf("?");
if (qIdx === -1) return url;
const base = url.slice(0, qIdx);
const afterQ = url.slice(qIdx + 1);
const hashIdx = afterQ.indexOf("#");
const query = hashIdx === -1 ? afterQ : afterQ.slice(0, hashIdx);
const hash = hashIdx === -1 ? "" : afterQ.slice(hashIdx);
const redacted = query.split("&").map((part) => {
const eqIdx = part.indexOf("=");
if (eqIdx === -1) return part;
const key = part.slice(0, eqIdx);
if (SENSITIVE_PARAM_RE.test(key) || isCustomSensitiveKey(key)) return `${key}=[REDACTED]`;
return part;
}).join("&");
return `${base}?${redacted}${hash}`;
} catch (e) {
return url;
}
}
var MAX_BODY_BYTES = 10 * 1024;
var BINARY_CONTENT_TYPE_RE = /^(image|video|audio)\/|^application\/(octet-stream|pdf|zip|x-protobuf|x-msgpack|wasm|vnd\.)/i;
async function readResponseBodySafe(response) {
try {
const ct = response.headers.get("content-type") || "";
if (BINARY_CONTENT_TYPE_RE.test(ct)) return "";
if (!response.body || typeof response.body.getReader !== "function") {
try {
const text = await response.text();
return typeof text === "string" ? text.slice(0, RESPONSE_SNIPPET_CHARS) : "";
} catch (e) {
return "";
}
}
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8", { fatal: false });
let collected = "";
let bytesRead = 0;
while (bytesRead < MAX_BODY_BYTES && collected.length < RESPONSE_SNIPPET_CHARS) {
const { value, done } = await reader.read();
if (done) break;
if (value) {
bytesRead += value.byteLength;
collected += decoder.decode(value, { stream: true });
}
}
try {
await reader.cancel();
} catch (e) {
}
return collected.slice(0, RESPONSE_SNIPPET_CHARS);
} catch (e) {
return "";
}
}
var INTERNAL_URL_PATTERNS = [
/__nextjs_original-stack-frame/,
/\/_next\/static\/webpack/,
/\/__webpack_hmr/,
/\.hot-update\./,
/\/sockjs-node\//,
/\/turbopack-hmr\//,
/\/_next\/webpack-hmr/,
/\/webpack-dev-server\//,
/\/__vite_ping/,
/\/@vite\/client/,
/\/@react-refresh/
];
function isInternalUrl(url) {
return INTERNAL_URL_PATTERNS.some((pattern) => pattern.test(url));
}
var _rootCache;
function getRoot() {
if (_rootCache === void 0) {
_rootCache = document.getElementById(ROOT_ID);
}
if (_rootCache && !_rootCache.isConnected) {
_rootCache = document.getElementById(ROOT_ID);
}
return _rootCache;
}
function isTraceBugElement(el) {
if (!el) return false;
if (el.id === ROOT_ID || el.id === BTN_ID || el.id === PANEL_ID) return true;
if (el.dataset && el.dataset.tracebug) return true;
const root = getRoot();
if (root && root.contains(el)) return true;
let node = el;
while (node) {
const id = node.id || "";
if (id.startsWith("tracebug-") || id.startsWith("bt-")) return true;
const cn = typeof node.className === "string" ? node.className : "";
if (cn.includes("tracebug-") || cn.includes("bt-ann") || cn.includes("bt-voice")) return true;
if (node.dataset && node.dataset.tracebug) return true;
node = node.parentElement;
}
return false;
}
function collectClicks(emit) {
const handler = (e) => {
try {
const t = e.target;
if (!t || isTraceBugElement(t)) return;
const tag = t.tagName.toLowerCase();
const el = {
tag,
text: (t.innerText || "").slice(0, 120),
id: t.id || "",
className: typeof t.className === "string" ? t.className : ""
};
const data = { element: el };
if (tag === "a") el.href = t.href || "";
if (tag === "button" || t.type === "submit") {
el.buttonType = t.type || "button";
el.disabled = t.disabled;
}
if (tag === "label") el.forField = t.htmlFor || "";
const ariaLabel = t.getAttribute("aria-label");
if (ariaLabel) el.ariaLabel = ariaLabel;
const role = t.getAttribute("role");
if (role) el.role = role;
const testId = t.getAttribute("data-testid");
if (testId) el.testId = testId;
const form = t.closest("form");
if (form) {
el.formId = form.id || "";
el.formAction = form.action || "";
}
try {
el.selector = buildSelector(t);
} catch (e2) {
}
try {
const r = t.getBoundingClientRect();
el.boundingBox = { x: Math.round(r.left), y: Math.round(r.top), width: Math.round(r.width), height: Math.round(r.height) };
} catch (e2) {
}
emit("click", data);
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Click capture error:", err);
}
};
document.addEventListener("click", handler, { capture: true });
return () => document.removeEventListener("click", handler, { capture: true });
}
function buildSelector(el) {
if (!el) return "";
if (el.id) return `#${CSS.escape(el.id)}`;
const testId = el.getAttribute("data-testid");
if (testId) return `[data-testid="${testId}"]`;
const parts = [];
let node = el;
let depth = 0;
while (node && node !== document.body && depth < 4) {
let part = node.tagName.toLowerCase();
if (node.id) {
parts.unshift(`#${CSS.escape(node.id)}`);
break;
}
const cls = typeof node.className === "string" ? node.className.trim().split(/\s+/).filter(Boolean)[0] : "";
if (cls) part += `.${CSS.escape(cls)}`;
const parent = node.parentElement;
const currentTag = node.tagName;
const currentNode = node;
if (parent) {
const sameTag = Array.from(parent.children).filter((c) => c.tagName === currentTag);
if (sameTag.length > 1) part += `:nth-of-type(${sameTag.indexOf(currentNode) + 1})`;
}
parts.unshift(part);
node = parent;
depth++;
}
return parts.join(" > ");
}
function collectInputs(emit) {
const timers = /* @__PURE__ */ new Map();
const handler = (e) => {
try {
const t = e.target;
if (!t || !("value" in t) || isTraceBugElement(t)) return;
if (t.tagName.toLowerCase() === "select") return;
const prev = timers.get(t);
if (prev) clearTimeout(prev);
timers.set(
t,
setTimeout(() => {
try {
const tag = t.tagName.toLowerCase();
const inputType = t.type || "";
const isSensitive = ["password", "credit-card", "ssn"].includes(inputType) || /password|secret|token|ssn|credit/i.test(t.name || t.id || "") || isCustomSensitiveKey(t.name || t.id);
const element = {
tag,
name: t.name || t.id || "",
type: inputType,
valueLength: (t.value || "").length,
value: isSensitive ? "[REDACTED]" : (t.value || "").slice(0, 200),
placeholder: t.placeholder || ""
};
const data = { element };
if (inputType === "checkbox" || inputType === "radio") {
element.checked = t.checked;
element.value = t.checked ? "checked" : "unchecked";
}
if (inputType === "number" || inputType === "range") {
element.value = t.value;
}
emit("input", data);
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Input capture error:", err);
}
timers.delete(t);
}, 300)
);
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Input capture error:", err);
}
};
document.addEventListener("input", handler, { capture: true });
return () => {
document.removeEventListener("input", handler, { capture: true });
timers.forEach((t) => clearTimeout(t));
};
}
function collectSelectChanges(emit) {
const handler = (e) => {
try {
const t = e.target;
if (!t || t.tagName.toLowerCase() !== "select" || isTraceBugElement(t)) return;
const selectedOption = t.options[t.selectedIndex];
emit("select_change", {
element: {
tag: "select",
name: t.name || t.id || "",
value: t.value,
selectedText: selectedOption ? selectedOption.text : "",
selectedIndex: t.selectedIndex,
optionCount: t.options.length,
allOptions: Array.from(t.options).map((o) => o.text).slice(0, 20)
}
});
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Select capture error:", err);
}
};
document.addEventListener("change", handler, { capture: true });
return () => document.removeEventListener("change", handler, { capture: true });
}
function collectFormSubmits(emit) {
const handler = (e) => {
try {
const form = e.target;
if (!form || form.tagName.toLowerCase() !== "form" || isTraceBugElement(form)) return;
const formData = {};
const elements = form.elements;
for (let i = 0; i < elements.length; i++) {
const el = elements[i];
if (!el.name) continue;
const isSensitive = ["password"].includes(el.type) || /password|secret|token|ssn|credit/i.test(el.name) || isCustomSensitiveKey(el.name);
if (el.type === "submit" || el.type === "button") continue;
formData[el.name] = isSensitive ? "[REDACTED]" : (el.value || "").slice(0, 200);
}
emit("form_submit", {
form: { id: form.id || "", action: form.action || "", method: form.method || "GET", fieldCount: elements.length, fields: formData }
});
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Form capture error:", err);
}
};
document.addEventListener("submit", handler, { capture: true });
return () => document.removeEventListener("submit", handler, { capture: true });
}
function collectRouteChanges(emit) {
let lastPath = window.location.pathname;
const check = () => {
const current = window.location.pathname;
if (current !== lastPath) {
const from = lastPath;
lastPath = current;
emit("route_change", { from, to: current });
}
};
window.addEventListener("popstate", check);
const origPush = history.pushState.bind(history);
const origReplace = history.replaceState.bind(history);
history.pushState = function(...args) {
origPush(...args);
check();
};
history.replaceState = function(...args) {
origReplace(...args);
check();
};
return () => {
window.removeEventListener("popstate", check);
history.pushState = origPush;
history.replaceState = origReplace;
};
}
function collectApiRequests(emit) {
const originalFetch = window.fetch;
window.fetch = async function(input, init) {
var _a;
let url = "";
let method = "GET";
try {
if (typeof input === "string") {
url = input;
} else if (input instanceof URL) {
url = input.href;
} else if (input && typeof input === "object" && "url" in input) {
url = input.url;
method = input.method || "GET";
}
if (init == null ? void 0 : init.method) method = init.method;
} catch (e) {
}
const start = Date.now();
try {
if (url && isInternalUrl(url)) return originalFetch.call(window, input, init);
} catch (e) {
}
const safeUrl = sanitizeUrl2(url).slice(0, 500);
try {
const response = await originalFetch.call(window, input, init);
try {
emit("api_request", {
request: { url: safeUrl, method: method.toUpperCase(), statusCode: response.status, durationMs: Date.now() - start }
});
} catch (e) {
}
try {
if (response.status >= 400 || response.status === 0) {
const clone = response.clone();
readResponseBodySafe(clone).then((snippet) => {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: response.status,
response: snippet,
timestamp: Date.now()
});
}).catch(() => {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: response.status,
response: "",
timestamp: Date.now()
});
});
}
} catch (e) {
}
return response;
} catch (err) {
try {
emit("api_request", {
request: { url: safeUrl, method: method.toUpperCase(), statusCode: 0, durationMs: Date.now() - start }
});
} catch (e) {
}
try {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: 0,
response: ((_a = err == null ? void 0 : err.message) == null ? void 0 : _a.slice(0, RESPONSE_SNIPPET_CHARS)) || "",
timestamp: Date.now()
});
} catch (e) {
}
throw err;
}
};
return () => {
window.fetch = originalFetch;
};
}
function collectXhrRequests(emit) {
const OrigXHR = window.XMLHttpRequest;
const origOpen = OrigXHR.prototype.open;
const origSend = OrigXHR.prototype.send;
const xhrMeta = /* @__PURE__ */ new WeakMap();
OrigXHR.prototype.open = function(method, url, ...rest) {
try {
xhrMeta.set(this, { method, url: typeof url === "string" ? url : url.toString() });
} catch (e) {
}
return origOpen.apply(this, [method, url, ...rest]);
};
OrigXHR.prototype.send = function(body) {
try {
const xhr = this;
const start = Date.now();
const meta = xhrMeta.get(xhr);
const method = (meta == null ? void 0 : meta.method) || "GET";
const url = (meta == null ? void 0 : meta.url) || "";
if (isInternalUrl(url)) return origSend.call(this, body);
const safeUrl = sanitizeUrl2(url).slice(0, 500);
xhr.addEventListener("loadend", function() {
try {
emit("api_request", { request: { url: safeUrl, method: method.toUpperCase(), statusCode: xhr.status, durationMs: Date.now() - start } });
} catch (e) {
}
try {
if (xhr.status >= 400 || xhr.status === 0) {
let body2 = "";
try {
const ct = xhr.getResponseHeader && xhr.getResponseHeader("content-type") || "";
if (!BINARY_CONTENT_TYPE_RE.test(ct)) {
body2 = typeof xhr.responseText === "string" ? xhr.responseText : "";
}
} catch (e) {
}
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: xhr.status,
response: body2.slice(0, RESPONSE_SNIPPET_CHARS),
timestamp: Date.now()
});
}
} catch (e) {
}
});
xhr.addEventListener("error", function() {
try {
emit("api_request", { request: { url: safeUrl, method: method.toUpperCase(), statusCode: 0, durationMs: Date.now() - start } });
} catch (e) {
}
try {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: 0,
response: "",
timestamp: Date.now()
});
} catch (e) {
}
});
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] XHR capture error:", err);
}
return origSend.call(this, body);
};
return () => {
OrigXHR.prototype.open = origOpen;
OrigXHR.prototype.send = origSend;
};
}
var _perfSeen = /* @__PURE__ */ new Set();
function _emitPerfEntry(emit, e) {
if (!e || !e.name) return;
try {
if (isInternalUrl(e.name)) return;
} catch (e2) {
}
if (typeof e.name === "string" && e.name.indexOf("tracebug") !== -1) return;
const key = `${e.name}|${Math.round(e.startTime)}`;
if (_perfSeen.has(key)) return;
_perfSeen.add(key);
const navStart = typeof performance.timeOrigin === "number" ? performance.timeOrigin : Date.now();
const ext = e;
const initiator = e.initiatorType || "";
const method = (ext.method || "GET").toUpperCase();
const status = ext.responseStatus || 0;
const url = sanitizeUrl2(e.name).slice(0, 500);
const timestamp = Math.round(navStart + e.startTime);
const durationMs = Math.round(e.duration || 0);
try {
emit("api_request", {
request: { url, method, statusCode: status, durationMs, initiatorType: initiator },
_ts: timestamp
});
} catch (e2) {
}
}
function drainPerformanceNetwork(emit) {
if (typeof performance === "undefined") return;
try {
const entries = performance.getEntriesByType("resource");
for (const e of entries) _emitPerfEntry(emit, e);
} catch (e) {
}
}
function collectPerformanceNetwork(emit) {
if (typeof performance === "undefined" || typeof PerformanceObserver === "undefined") {
return () => {
};
}
drainPerformanceNetwork(emit);
let observer = null;
try {
observer = new PerformanceObserver((list) => {
for (const e of list.getEntries()) {
_emitPerfEntry(emit, e);
}
});
observer.observe({ type: "resource", buffered: false });
} catch (e) {
}
return () => {
try {
observer == null ? void 0 : observer.disconnect();
} catch (e) {
}
};
}
function collectErrors(emit) {
const prevOnError = window.onerror;
window.onerror = (msg, source, line, col, error) => {
try {
emit("error", {
error: {
message: sanitizeTokenShapes(typeof msg === "string" ? msg : "Unknown error"),
stack: (error == null ? void 0 : error.stack) && sanitizeTokenShapes(error.stack),
source,
line,
column: col
}
});
} catch (e) {
}
if (prevOnError) {
try {
prevOnError(msg, source, line, col, error);
} catch (e) {
}
}
};
const onRejection = (e) => {
var _a, _b;
try {
emit("unhandled_rejection", {
error: {
message: sanitizeTokenShapes(((_a = e.reason) == null ? void 0 : _a.message) || String(e.reason)),
stack: ((_b = e.reason) == null ? void 0 : _b.stack) && sanitizeTokenShapes(e.reason.stack)
}
});
} catch (e2) {
}
};
window.addEventListener("unhandledrejection", onRejection);
return () => {
window.onerror = prevOnError;
window.removeEventListener("unhandledrejection", onRejection);
};
}
function collectConsoleErrors(emit) {
const origConsoleError = console.error;
let _insideEmit = false;
console.error = function(...args) {
if (_insideEmit) {
origConsoleError.apply(console, args);
return;
}
_insideEmit = true;
try {
emit("console_error", {
// Token-shape scrub at capture — a token logged to the console must
// never reach the offline .html export unmasked (the cloud sanitizer
// only covers the upload path).
error: { message: sanitizeTokenShapes(args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")) }
});
} catch (e) {
} finally {
_insideEmit = false;
}
origConsoleError.apply(console, args);
};
return () => {
console.error = origConsoleError;
};
}
var CONSOLE_LEVEL_CAP = 50;
function wrapConsoleLevel(method, type, emit) {
const orig = console[method];
let _inside = false;
let _count = 0;
console[method] = function(...args) {
const own = typeof args[0] === "string" && args[0].startsWith("[TraceBug]");
if (_inside || own || _count >= CONSOLE_LEVEL_CAP) {
orig.apply(console, args);
return;
}
_inside = true;
_count++;
try {
emit(type, {
// Same capture-time token scrub as console_error — the offline
// export path never runs the cloud sanitizer.
error: { message: sanitizeTokenShapes(args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")) }
});
} catch (e) {
} finally {
_inside = false;
}
orig.apply(console, args);
};
return () => {
console[method] = orig;
};
}
function collectConsoleWarnings(emit) {
return wrapConsoleLevel("warn", "console_warn", emit);
}
function collectConsoleInfo(emit) {
return wrapConsoleLevel("info", "console_info", emit);
}
function collectConsoleLogs(emit) {
return wrapConsoleLevel("log", "console_log", emit);
}
// src/ui/helpers.ts
function tbIsolationCss(root) {
return `
${root} {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 14px; font-weight: 400; line-height: 1.5; font-style: normal;
letter-spacing: normal; text-transform: none; text-align: left;
text-indent: 0; white-space: normal; word-spacing: normal; text-shadow: none;
-webkit-font-smoothing: antialiased;
}
${root} *, ${root} *::before, ${root} *::after { box-sizing: border-box; }
${root} button, ${root} input, ${root} select, ${root} textarea {
font-family: inherit; font-size: inherit; letter-spacing: normal;
text-transform: none; margin: 0;
}
${root} svg { max-width: none; max-height: none; vertical-align: middle; }
${root} img { max-width: none; }
${root} a { text-decoration: none; }
`;
}
function parseShortcut(shortcut) {
const parts = (shortcut || "").toLowerCase().split("+").map((s) => s.trim());
return {
mod: parts.includes("ctrl") || parts.includes("control") || parts.includes("cmd") || parts.includes("meta"),
shift: parts.includes("shift"),
alt: parts.includes("alt") || parts.includes("option"),
key: parts[parts.length - 1] || ""
};
}
function matchesShortcut(e, shortcut) {
if (!shortcut) return false;
const s = parseShortcut(shortcut);
const mod = e.ctrlKey || e.metaKey;
const key = (e.key || "").toLowerCase();
return mod === s.mod && e.shiftKey === s.shift && e.altKey === s.alt && key === s.key;
}
function escapeHtml(str) {
return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
export {
generateSessionId,
getActiveSessionId,
setActiveSessionId,
clearActiveSessionId,
getActiveCaptureMode,
setActiveCaptureMode,
getAllSessions,
getCachedSessions,
scheduleFlush,
flushPendingEvents,
appendEvent,
updateSessionError,
deleteSession,
addAnnotation,
saveEnvironment,
setSessionPriority,
markSessionSaved,
clearAllSessions,
setRedactRules,
isCustomSensitiveKey,
sanitizeTokenShapes,
sanitizeReportForUpload,
getNetworkFailures,
clearNetworkFailures,
collectClicks,
collectInputs,
collectSelectChanges,
collectFormSubmits,
collectRouteChanges,
collectApiRequests,
collectXhrRequests,
drainPerformanceNetwork,
collectPerformanceNetwork,
collectErrors,
collectConsoleErrors,
collectConsoleWarnings,
collectConsoleInfo,
collectConsoleLogs,
tbIsolationCss,
matchesShortcut,
escapeHtml
};
//# sourceMappingURL=chunk-LQO44M5L.js.map

Sorry, the diff of this file is too big to display

import {
getAllSessions,
getNetworkFailures
} from "./chunk-LQO44M5L.js";
// src/scanner/helpers.ts
var _issueCounter = 0;
function makeIssueId(detector) {
_issueCounter += 1;
return `${detector}_${Date.now().toString(36)}_${_issueCounter}`;
}
function buildSelector(el) {
if (!el || el.nodeType !== 1) return "";
if (el.id) return `#${cssEscape(el.id)}`;
const testId = el.getAttribute("data-testid") || el.getAttribute("data-test-id");
if (testId) return `[data-testid="${cssEscape(testId)}"]`;
const parts = [];
let cur = el;
let depth = 0;
while (cur && cur.nodeType === 1 && cur.tagName !== "BODY" && depth < 5) {
const tag = cur.tagName.toLowerCase();
const parent = cur.parentElement;
if (!parent) {
parts.unshift(tag);
break;
}
const siblings = Array.from(parent.children).filter((c) => c.tagName === cur.tagName);
if (siblings.length > 1) {
const idx = siblings.indexOf(cur) + 1;
parts.unshift(`${tag}:nth-of-type(${idx})`);
} else {
parts.unshift(tag);
}
cur = parent;
depth += 1;
}
return parts.join(" > ");
}
function coerceSeverity(impact) {
switch ((impact || "").toLowerCase()) {
case "critical":
return "critical";
case "serious":
return "serious";
case "moderate":
return "moderate";
case "minor":
return "minor";
default:
return "minor";
}
}
function cssEscape(value) {
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
return CSS.escape(value);
}
return value.replace(/[^\w-]/g, (ch) => `\\${ch}`);
}
// src/scanner/detectors/broken-images.ts
async function detectBrokenImages() {
const issues = [];
const imgs = Array.from(document.images);
for (const img of imgs) {
if (img.closest("#tracebug-root")) continue;
if (!img.complete) continue;
if (img.naturalWidth > 0) continue;
const src = img.currentSrc || img.src;
if (!src) continue;
issues.push({
id: makeIssueId("broken-image"),
detector: "broken-image",
severity: "moderate",
title: `Broken image: ${truncateUrl(src)}`,
description: `<img> element failed to load. The browser tried to fetch \`${src}\` and got a network error or a non-image response. ${img.alt ? `Alt text: "${img.alt}"` : "No alt text \u2014 also fails accessibility."}`,
selector: buildSelector(img),
url: src,
page: window.location.pathname,
detectedAt: Date.now()
});
}
return issues;
}
function truncateUrl(url) {
if (url.length <= 60) return url;
const tail = url.split("/").pop() || url.slice(-40);
return `\u2026/${tail}`;
}
// src/scanner/detectors/mixed-content.ts
var ATTR_TARGETS = [
{ tag: "img", attr: "src" },
{ tag: "script", attr: "src" },
{ tag: "iframe", attr: "src" },
{ tag: "link", attr: "href" },
{ tag: "audio", attr: "src" },
{ tag: "video", attr: "src" },
{ tag: "source", attr: "src" },
{ tag: "embed", attr: "src" },
{ tag: "object", attr: "data" }
];
async function detectMixedContent() {
if (typeof window === "undefined" || window.location.protocol !== "https:") {
return [];
}
const issues = [];
for (const { tag, attr } of ATTR_TARGETS) {
const elements = document.querySelectorAll(`${tag}[${attr}]`);
for (const el of Array.from(elements)) {
if (el.closest("#tracebug-root")) continue;
const value = el.getAttribute(attr) || "";
if (!value.startsWith("http://")) continue;
if (tag === "link") {
const rel = (el.rel || "").toLowerCase();
const fetchableRels = ["stylesheet", "preload", "prefetch", "manifest", "icon", "shortcut icon"];
if (!fetchableRels.some((r) => rel.includes(r))) continue;
}
issues.push({
id: makeIssueId("mixed-content"),
detector: "mixed-content",
severity: tag === "script" || tag === "iframe" ? "serious" : "moderate",
title: `Mixed content: ${tag} loads over HTTP`,
description: `<${tag}> on an HTTPS page references \`${value}\`. Browsers block or downgrade this \u2014 the resource usually fails to load and breaks the page's secure-context indicator.`,
selector: buildSelector(el),
url: value,
page: window.location.pathname,
detectedAt: Date.now()
});
}
}
return issues;
}
// src/fingerprint.ts
async function computeFingerprint(errorMessage, errorStack, page) {
const errorType = extractErrorType(errorMessage);
const topFrames = extractTopFrames(errorStack || "", 3);
const input = `${errorType}|${topFrames.join("\n")}|${page}`;
if (typeof crypto !== "undefined" && crypto.subtle && typeof crypto.subtle.digest === "function") {
try {
const buf = new TextEncoder().encode(input);
const hash = await crypto.subtle.digest("SHA-1", buf);
return bufferToHex(hash).slice(0, 16);
} catch (e) {
}
}
return djb2(input).toString(16).padStart(8, "0");
}
function extractErrorType(message) {
const m = message.match(/^([A-Z][a-zA-Z]+Error|Error)\b/);
return m ? m[1] : "Error";
}
function extractTopFrames(stack, n) {
const frames = [];
const lines = stack.split("\n");
for (const line of lines) {
const m = line.match(/(https?:\/\/[^):\s]+|[^():\s]+\.[a-z]+):(\d+):(\d+)/i);
if (m) {
const url = m[1];
const path = url.includes("://") ? new URL(url, typeof window !== "undefined" ? window.location.origin : "http://localhost").pathname : url;
frames.push(`${path}:${m[2]}:${m[3]}`);
if (frames.length >= n) break;
}
}
return frames;
}
function bufferToHex(buf) {
const bytes = new Uint8Array(buf);
let out = "";
for (let i = 0; i < bytes.length; i++) out += bytes[i].toString(16).padStart(2, "0");
return out;
}
function djb2(str) {
let hash = 5381;
for (let i = 0; i < str.length; i++) hash = (hash << 5) + hash + str.charCodeAt(i) | 0;
return hash >>> 0;
}
// src/scanner/detectors/session-data.ts
var SLOW_API_MS = 2e3;
var MAX_CONTEXT_SAMPLES = 10;
async function detectConsoleErrors(session) {
var _a, _b, _c;
if (!session) return [];
const groups = /* @__PURE__ */ new Map();
for (let i = 0; i < session.events.length; i++) {
const e = session.events[i];
if (e.type !== "error" && e.type !== "unhandled_rejection" && e.type !== "console_error") continue;
const message = ((_a = e.data.error) == null ? void 0 : _a.message) || e.data.message || "";
if (!message) continue;
const stack = ((_b = e.data.error) == null ? void 0 : _b.stack) || "";
const page = e.page || (typeof window !== "undefined" ? window.location.pathname : "");
const fp = await computeFingerprint(message, stack, page);
const precedingAction = describePrecedingAction(session.events, i);
const existing = groups.get(fp);
if (existing) {
existing.issue.occurrences = (existing.issue.occurrences || 1) + 1;
existing.issue.lastSeenAt = e.timestamp;
if (existing.samples.length < MAX_CONTEXT_SAMPLES) {
existing.samples.push({ timestamp: e.timestamp, precedingAction });
}
continue;
}
const firstFrame = ((_c = stack.split("\n").find((l) => l.trim().startsWith("at "))) == null ? void 0 : _c.trim()) || "";
const issue = {
id: makeIssueId("console-error"),
detector: "console-error",
severity: classifyErrorSeverity(message),
title: `JS error: ${message.slice(0, 70)}${message.length > 70 ? "\u2026" : ""}`,
description: firstFrame ? `${message}
First frame: ${firstFrame}` : message,
page,
detectedAt: e.timestamp,
fingerprint: fp,
occurrences: 1,
firstSeenAt: e.timestamp,
lastSeenAt: e.timestamp
};
groups.set(fp, { issue, samples: [{ timestamp: e.timestamp, precedingAction }] });
}
const out = [];
for (const g of groups.values()) {
const n = g.issue.occurrences || 1;
if (n > 1) {
g.issue.title = `${g.issue.title} [\xD7${n}]`;
g.issue.contextSamples = g.samples;
}
out.push(g.issue);
}
return out;
}
function describePrecedingAction(events, i) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
for (let j = i - 1; j >= 0; j--) {
const e = events[j];
if (e.type === "click") {
const t = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.text) || ((_d = (_c = e.data) == null ? void 0 : _c.element) == null ? void 0 : _d.ariaLabel) || ((_f = (_e = e.data) == null ? void 0 : _e.element) == null ? void 0 : _f.tag) || "element";
return `clicked "${String(t).slice(0, 40)}"`;
}
if (e.type === "input") {
const n = ((_h = (_g = e.data) == null ? void 0 : _g.element) == null ? void 0 : _h.name) || ((_j = (_i = e.data) == null ? void 0 : _i.element) == null ? void 0 : _j.id) || "field";
return `typed in ${n}`;
}
if (e.type === "select_change") return "selected an option";
if (e.type === "form_submit") return "submitted a form";
if (e.type === "route_change") return `navigated to ${((_k = e.data) == null ? void 0 : _k.to) || "page"}`;
}
return void 0;
}
async function detectFailedRequests(session) {
if (!session) return [];
const issues = [];
const buffer = getNetworkFailures();
for (const e of session.events) {
if (e.type !== "api_request") continue;
const req = e.data.request;
if (!req) continue;
const status = req.statusCode || 0;
if (status >= 200 && status < 400) continue;
if (status === 0 && req.method === "HEAD") continue;
const match = buffer.find(
(b) => b.url === req.url && b.method === req.method && b.status === status && Math.abs(b.timestamp - e.timestamp) < 5e3
);
const snippet = (match == null ? void 0 : match.response) ? `
Response: ${match.response.slice(0, 160)}` : "";
issues.push({
id: makeIssueId("failed-request"),
detector: "failed-request",
severity: status >= 500 ? "critical" : status === 0 ? "serious" : "moderate",
title: `${req.method} ${truncatePath(req.url)} \u2192 ${status === 0 ? "Network Error" : status}`,
description: `Request failed in ${req.durationMs || 0}ms.${snippet}`,
url: req.url,
page: e.page || window.location.pathname,
detectedAt: e.timestamp
});
}
return issues;
}
async function detectSlowApis(session) {
if (!session) return [];
const issues = [];
for (const e of session.events) {
if (e.type !== "api_request") continue;
const req = e.data.request;
if (!req) continue;
const status = req.statusCode || 0;
if (status < 200 || status >= 400) continue;
const duration = req.durationMs || 0;
if (duration < SLOW_API_MS) continue;
issues.push({
id: makeIssueId("slow-api"),
detector: "slow-api",
severity: duration > 5e3 ? "serious" : "moderate",
title: `Slow API: ${req.method} ${truncatePath(req.url)} (${duration}ms)`,
description: `This request took ${(duration / 1e3).toFixed(1)}s \u2014 over the ${SLOW_API_MS / 1e3}s threshold. Slow APIs are a common UX complaint and a leading cause of perceived bugs ("the page is frozen").`,
url: req.url,
page: e.page || window.location.pathname,
detectedAt: e.timestamp
});
}
return issues;
}
function truncatePath(url) {
try {
const u = new URL(url, window.location.origin);
const p = u.pathname.length > 50 ? u.pathname.slice(0, 47) + "\u2026" : u.pathname;
return p;
} catch (e) {
return url.length > 50 ? url.slice(0, 47) + "\u2026" : url;
}
}
function classifyErrorSeverity(message) {
if (/TypeError|ReferenceError|SyntaxError/i.test(message)) return "critical";
if (/Network|fetch|failed to/i.test(message)) return "serious";
return "moderate";
}
// src/scanner/detectors/a11y.ts
var _axePromise = null;
function loadAxe() {
if (_axePromise) return _axePromise;
_axePromise = import("axe-core").then((mod) => mod.default || mod).catch((err) => {
console.warn("[TraceBug] axe-core failed to load:", err);
return null;
});
return _axePromise;
}
async function detectA11yViolations() {
const axe = await loadAxe();
if (!axe || typeof axe.run !== "function") return [];
let results;
try {
results = await axe.run(document, {
// Only WCAG-tagged rules — keeps signal-to-noise high. Best-practice
// rules add ~30% more noise without proportional value for QA.
runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] },
// Skip our own UI so QA isn't told their toolbar fails contrast checks.
// axe accepts a context with exclude — we pass { exclude: [...] } via
// the second-argument options-shaped form below to keep types loose.
resultTypes: ["violations"]
});
} catch (err) {
console.warn("[TraceBug] axe.run failed:", err);
return [];
}
const issues = [];
const violations = (results == null ? void 0 : results.violations) || [];
for (const v of violations) {
const nodes = v.nodes || [];
const firstNode = nodes[0];
const selector = Array.isArray(firstNode == null ? void 0 : firstNode.target) ? firstNode.target.join(" ") : "";
const exampleSnippet = ((firstNode == null ? void 0 : firstNode.html) || "").slice(0, 120);
const moreSuffix = nodes.length > 1 ? ` (+ ${nodes.length - 1} more element${nodes.length === 2 ? "" : "s"})` : "";
issues.push({
id: makeIssueId("axe-a11y"),
detector: "axe-a11y",
severity: coerceSeverity(v.impact),
title: `${v.help || v.id}${moreSuffix}`,
description: `${v.description || v.id}
First element: \`${exampleSnippet}\``,
selector: selector || void 0,
helpUrl: v.helpUrl,
page: window.location.pathname,
detectedAt: Date.now()
});
}
return issues;
}
// src/scanner/detectors/frustration.ts
var RAGE_WINDOW_MS = 1500;
var RAGE_MIN_CLICKS = 3;
var DEAD_RESPONSE_WINDOW_MS = 1500;
var ABANDON_WINDOW_MS = 6e4;
var ERROR_CORRELATION_WINDOW_MS = 2500;
async function detectFrustration(session) {
var _a;
if (!session) return [];
const events = session.events;
if (events.length === 0) return [];
const issues = [];
const page = ((_a = session.events[0]) == null ? void 0 : _a.page) || window.location.pathname;
issues.push(...detectRageClicks(events, page));
issues.push(...detectDeadClicks(events, page));
issues.push(...detectFormAbandonment(events, page));
issues.push(...detectErrorCorrelated(events, page));
return issues;
}
function detectRageClicks(events, page) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const out = [];
const seenGroups = /* @__PURE__ */ new Set();
for (let i = 0; i < events.length; i++) {
const e = events[i];
if (e.type !== "click") continue;
const sel = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.selector) || ((_d = (_c = e.data) == null ? void 0 : _c.element) == null ? void 0 : _d.testId) || "";
if (!sel) continue;
const cluster = [e];
let j = i + 1;
while (j < events.length) {
const next = events[j];
if (next.timestamp - e.timestamp > RAGE_WINDOW_MS) break;
if (isResponseEvent(next)) break;
if (next.type === "click") {
const nextSel = ((_f = (_e = next.data) == null ? void 0 : _e.element) == null ? void 0 : _f.selector) || ((_h = (_g = next.data) == null ? void 0 : _g.element) == null ? void 0 : _h.testId) || "";
if (nextSel === sel) cluster.push(next);
}
j++;
}
if (cluster.length >= RAGE_MIN_CLICKS) {
const key = `${sel}@${e.timestamp}`;
if (seenGroups.has(key)) continue;
seenGroups.add(key);
const label = clickLabel(cluster[0]);
out.push({
id: makeIssueId("frustration-rage"),
detector: "frustration-rage",
severity: "serious",
title: `Rage clicks on ${label} (${cluster.length}\xD7 in ${Math.round(cluster[cluster.length - 1].timestamp - cluster[0].timestamp)}ms)`,
description: `User clicked the same element ${cluster.length} times within ${RAGE_WINDOW_MS}ms with no observable response (no API call, navigation, or DOM update). The element either doesn't respond to clicks or feels broken.`,
selector: sel,
page,
detectedAt: cluster[0].timestamp,
firstSeenAt: cluster[0].timestamp,
lastSeenAt: cluster[cluster.length - 1].timestamp,
occurrences: cluster.length
});
i = j - 1;
}
}
return out;
}
function detectDeadClicks(events, page) {
var _a, _b;
const out = [];
const MAX = 5;
for (let i = 0; i < events.length && out.length < MAX; i++) {
const e = events[i];
if (e.type !== "click") continue;
let responsive = false;
for (let j = i + 1; j < events.length; j++) {
const next = events[j];
if (next.timestamp - e.timestamp > DEAD_RESPONSE_WINDOW_MS) break;
if (isResponseEvent(next)) {
responsive = true;
break;
}
}
if (responsive) continue;
const sel = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.selector) || "";
const label = clickLabel(e);
out.push({
id: makeIssueId("frustration-dead"),
detector: "frustration-dead",
severity: "moderate",
title: `Dead click on ${label}`,
description: `Clicked but nothing happened within ${DEAD_RESPONSE_WINDOW_MS}ms (no API call, navigation, or DOM input). The element may have an unbound handler, a swallowed event, or be visually clickable but disabled.`,
selector: sel,
page: e.page || page,
detectedAt: e.timestamp
});
}
return out;
}
function detectFormAbandonment(events, _page) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
const out = [];
const formActivity = {};
for (const e of events) {
if (e.type === "input") {
const formId = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.formId) || ((_d = (_c = e.data) == null ? void 0 : _c.element) == null ? void 0 : _d.formAction) || "_default";
if (!formActivity[formId]) {
formActivity[formId] = { firstInputAt: e.timestamp, fieldsSeen: /* @__PURE__ */ new Set(), lastInputAt: e.timestamp, page: e.page };
}
const name = ((_f = (_e = e.data) == null ? void 0 : _e.element) == null ? void 0 : _f.name) || ((_h = (_g = e.data) == null ? void 0 : _g.element) == null ? void 0 : _h.id) || "field";
formActivity[formId].fieldsSeen.add(name);
formActivity[formId].lastInputAt = e.timestamp;
} else if (e.type === "form_submit") {
const formId = ((_j = (_i = e.data) == null ? void 0 : _i.form) == null ? void 0 : _j.id) || "_default";
delete formActivity[formId];
} else if (e.type === "route_change") {
for (const formId of Object.keys(formActivity)) {
const a = formActivity[formId];
if (e.timestamp - a.lastInputAt > ABANDON_WINDOW_MS) continue;
if (a.fieldsSeen.size === 0) continue;
out.push({
id: makeIssueId("frustration-abandon"),
detector: "frustration-abandon",
severity: "moderate",
title: `Form abandoned on ${a.page} (${a.fieldsSeen.size} field${a.fieldsSeen.size === 1 ? "" : "s"} filled)`,
description: `User typed into ${a.fieldsSeen.size} field${a.fieldsSeen.size === 1 ? "" : "s"} (${Array.from(a.fieldsSeen).slice(0, 5).join(", ")}) and then navigated away without submitting. Likely a UX problem: the submit button is unclear, the form requires too much info, or it's failing silently.`,
page: a.page,
detectedAt: a.lastInputAt,
firstSeenAt: a.firstInputAt,
lastSeenAt: a.lastInputAt
});
delete formActivity[formId];
}
}
}
return out;
}
function detectErrorCorrelated(events, page) {
var _a, _b, _c, _d, _e, _f;
const out = [];
const seen = /* @__PURE__ */ new Set();
for (let i = 0; i < events.length; i++) {
const e = events[i];
const isError = e.type === "error" || e.type === "unhandled_rejection" || e.type === "console_error";
if (!isError) continue;
let click = null;
for (let j = i - 1; j >= 0; j--) {
const prev = events[j];
if (e.timestamp - prev.timestamp > ERROR_CORRELATION_WINDOW_MS) break;
if (prev.type === "click") {
click = prev;
break;
}
}
if (!click) continue;
const errMsg = ((_b = (_a = e.data) == null ? void 0 : _a.error) == null ? void 0 : _b.message) || "";
if (!errMsg) continue;
const key = `${errMsg}::${((_d = (_c = click.data) == null ? void 0 : _c.element) == null ? void 0 : _d.selector) || ""}`;
if (seen.has(key)) continue;
seen.add(key);
const label = clickLabel(click);
const truncMsg = errMsg.length > 60 ? errMsg.slice(0, 57) + "\u2026" : errMsg;
const delta = Math.round(e.timestamp - click.timestamp);
out.push({
id: makeIssueId("frustration-error-correlated"),
detector: "frustration-error-correlated",
severity: "critical",
title: `Click on ${label} triggered: ${truncMsg}`,
description: `An error fired ${delta}ms after the user clicked ${label}. This is almost certainly the offending interaction \u2014 the click handler threw, or its async path failed.`,
selector: (_f = (_e = click.data) == null ? void 0 : _e.element) == null ? void 0 : _f.selector,
page: e.page || page,
detectedAt: e.timestamp
});
}
return out;
}
function isResponseEvent(e) {
return e.type === "api_request" || e.type === "route_change" || e.type === "input" || e.type === "form_submit" || e.type === "select_change";
}
function clickLabel(e) {
var _a;
const el = (_a = e.data) == null ? void 0 : _a.element;
const raw = (el == null ? void 0 : el.text) || (el == null ? void 0 : el.ariaLabel) || (el == null ? void 0 : el.testId) || (el == null ? void 0 : el.id) || (el == null ? void 0 : el.tag) || "element";
const trimmed = String(raw).replace(/\s+/g, " ").trim();
return trimmed.length > 40 ? `"${trimmed.slice(0, 37)}\u2026"` : `"${trimmed}"`;
}
// src/scanner/index.ts
var _issues = [];
var _scanInFlight = null;
var _lastScanAt = 0;
var SEVERITY_ORDER = {
critical: 0,
serious: 1,
moderate: 2,
minor: 3
};
async function scan() {
if (_scanInFlight) {
const issues = await _scanInFlight;
return { issues, durationMs: 0, scannedAt: _lastScanAt };
}
const startedAt = Date.now();
const sessions = getAllSessions().sort((a, b) => b.updatedAt - a.updatedAt);
const session = sessions[0] || null;
const safeRun = (p) => p.catch((err) => {
console.warn("[TraceBug] Detector failed:", err);
return [];
});
_scanInFlight = Promise.all([
safeRun(detectBrokenImages()),
safeRun(detectMixedContent()),
safeRun(detectConsoleErrors(session)),
safeRun(detectFailedRequests(session)),
safeRun(detectSlowApis(session)),
safeRun(detectA11yViolations()),
safeRun(detectFrustration(session))
]).then((results) => {
const all = [].concat(...results);
all.sort((a, b) => {
const sev = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];
if (sev !== 0) return sev;
return a.detectedAt - b.detectedAt;
});
_issues = all;
return all;
});
try {
const issues = await _scanInFlight;
_lastScanAt = Date.now();
return { issues, durationMs: _lastScanAt - startedAt, scannedAt: _lastScanAt };
} finally {
_scanInFlight = null;
}
}
function getIssues(options) {
var _a;
const includeDismissed = (_a = options == null ? void 0 : options.includeDismissed) != null ? _a : false;
return includeDismissed ? _issues.slice() : _issues.filter((i) => !i.dismissed);
}
function dismissIssue(id) {
const issue = _issues.find((i) => i.id === id);
if (!issue) return false;
issue.dismissed = true;
return true;
}
function undismissIssue(id) {
const issue = _issues.find((i) => i.id === id);
if (!issue) return false;
issue.dismissed = false;
return true;
}
function clearIssues() {
_issues = [];
_lastScanAt = 0;
}
function getIssueCountsByDetector() {
const counts = {
"axe-a11y": 0,
"broken-image": 0,
"mixed-content": 0,
"console-error": 0,
"slow-api": 0,
"failed-request": 0,
"frustration-rage": 0,
"frustration-dead": 0,
"frustration-abandon": 0,
"frustration-error-correlated": 0
};
for (const i of _issues) {
if (i.dismissed) continue;
counts[i.detector] = (counts[i.detector] || 0) + 1;
}
return counts;
}
function getIssueCountsBySeverity() {
const counts = {
critical: 0,
serious: 0,
moderate: 0,
minor: 0
};
for (const i of _issues) {
if (i.dismissed) continue;
counts[i.severity] += 1;
}
return counts;
}
function getIssueById(id) {
return _issues.find((i) => i.id === id) || null;
}
export {
scan,
getIssues,
dismissIssue,
undismissIssue,
clearIssues,
getIssueCountsByDetector,
getIssueCountsBySeverity,
getIssueById
};
//# sourceMappingURL=chunk-URDH7OBN.js.map
{"version":3,"sources":["../src/scanner/helpers.ts","../src/scanner/detectors/broken-images.ts","../src/scanner/detectors/mixed-content.ts","../src/fingerprint.ts","../src/scanner/detectors/session-data.ts","../src/scanner/detectors/a11y.ts","../src/scanner/detectors/frustration.ts","../src/scanner/index.ts"],"sourcesContent":["// ── Scanner helpers ───────────────────────────────────────────────────────\r\n// Shared utilities for detectors: stable issue IDs, robust CSS selectors,\r\n// severity coercion.\r\n\r\nimport { IssueDetector, IssueSeverity } from \"../types\";\r\n\r\nlet _issueCounter = 0;\r\n\r\n/** Stable, unique issue ID across detectors and runs. */\r\nexport function makeIssueId(detector: IssueDetector): string {\r\n _issueCounter += 1;\r\n return `${detector}_${Date.now().toString(36)}_${_issueCounter}`;\r\n}\r\n\r\n/**\r\n * Build a \"good enough\" CSS selector for an element. Prefers id, then\r\n * data-testid, then a tag+nth-of-type chain bounded to depth 5. Not\r\n * guaranteed unique on pathological pages but unique enough for clicking\r\n * back to the offending node.\r\n */\r\nexport function buildSelector(el: Element): string {\r\n if (!el || el.nodeType !== 1) return \"\";\r\n if (el.id) return `#${cssEscape(el.id)}`;\r\n\r\n const testId = el.getAttribute(\"data-testid\") || el.getAttribute(\"data-test-id\");\r\n if (testId) return `[data-testid=\"${cssEscape(testId)}\"]`;\r\n\r\n const parts: string[] = [];\r\n let cur: Element | null = el;\r\n let depth = 0;\r\n while (cur && cur.nodeType === 1 && cur.tagName !== \"BODY\" && depth < 5) {\r\n const tag = cur.tagName.toLowerCase();\r\n // Explicit annotation breaks the circular inference from `cur = parent`\r\n // below (which otherwise makes `parent` — and `parent.children` — `any`).\r\n const parent: HTMLElement | null = cur.parentElement;\r\n if (!parent) {\r\n parts.unshift(tag);\r\n break;\r\n }\r\n const siblings = Array.from(parent.children).filter(c => c.tagName === cur!.tagName);\r\n if (siblings.length > 1) {\r\n const idx = siblings.indexOf(cur) + 1;\r\n parts.unshift(`${tag}:nth-of-type(${idx})`);\r\n } else {\r\n parts.unshift(tag);\r\n }\r\n cur = parent;\r\n depth += 1;\r\n }\r\n return parts.join(\" > \");\r\n}\r\n\r\n/**\r\n * Map axe-core's impact strings to our IssueSeverity enum. Axe's \"serious\"\r\n * is our highest non-critical bucket.\r\n */\r\nexport function coerceSeverity(impact: string | null | undefined): IssueSeverity {\r\n switch ((impact || \"\").toLowerCase()) {\r\n case \"critical\": return \"critical\";\r\n case \"serious\": return \"serious\";\r\n case \"moderate\": return \"moderate\";\r\n case \"minor\": return \"minor\";\r\n default: return \"minor\";\r\n }\r\n}\r\n\r\n/**\r\n * Minimal CSS.escape polyfill — needed when an id contains characters that\r\n * would break a selector (colons, brackets, dots in framework-generated ids).\r\n */\r\nfunction cssEscape(value: string): string {\r\n if (typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\") {\r\n return CSS.escape(value);\r\n }\r\n return value.replace(/[^\\w-]/g, ch => `\\\\${ch}`);\r\n}\r\n","// ── Broken-image detector ─────────────────────────────────────────────────\r\n// Walks every <img> on the page and flags ones that failed to load. Uses\r\n// `naturalWidth === 0 && complete === true` — the standard signal for\r\n// \"image attempted to load and failed.\" Skips images that haven't finished\r\n// loading yet (we can't tell if they're broken until they settle).\r\n\r\nimport { Issue } from \"../../types\";\r\nimport { buildSelector, makeIssueId } from \"../helpers\";\r\n\r\nexport async function detectBrokenImages(): Promise<Issue[]> {\r\n const issues: Issue[] = [];\r\n const imgs = Array.from(document.images);\r\n\r\n for (const img of imgs) {\r\n // Skip TraceBug's own UI.\r\n if (img.closest(\"#tracebug-root\")) continue;\r\n // Image still loading — can't decide yet.\r\n if (!img.complete) continue;\r\n // Decoded successfully — naturalWidth is non-zero.\r\n if (img.naturalWidth > 0) continue;\r\n\r\n // No src is a different problem (missing asset, not a broken load).\r\n const src = img.currentSrc || img.src;\r\n if (!src) continue;\r\n\r\n issues.push({\r\n id: makeIssueId(\"broken-image\"),\r\n detector: \"broken-image\",\r\n severity: \"moderate\",\r\n title: `Broken image: ${truncateUrl(src)}`,\r\n description: `<img> element failed to load. The browser tried to fetch \\`${src}\\` and got a network error or a non-image response. ${\r\n img.alt ? `Alt text: \"${img.alt}\"` : \"No alt text — also fails accessibility.\"\r\n }`,\r\n selector: buildSelector(img),\r\n url: src,\r\n page: window.location.pathname,\r\n detectedAt: Date.now(),\r\n });\r\n }\r\n\r\n return issues;\r\n}\r\n\r\nfunction truncateUrl(url: string): string {\r\n if (url.length <= 60) return url;\r\n // Keep the filename — easier to identify than the host.\r\n const tail = url.split(\"/\").pop() || url.slice(-40);\r\n return `…/${tail}`;\r\n}\r\n","// ── Mixed-content detector ────────────────────────────────────────────────\r\n// Flags every `http://` resource on a `https://` page. Browsers block most\r\n// of these silently (active content) or downgrade them (passive content),\r\n// so users rarely notice — but the asset usually fails or breaks the lock\r\n// icon. Worth surfacing.\r\n\r\nimport { Issue } from \"../../types\";\r\nimport { buildSelector, makeIssueId } from \"../helpers\";\r\n\r\nconst ATTR_TARGETS: Array<{ tag: string; attr: string }> = [\r\n { tag: \"img\", attr: \"src\" },\r\n { tag: \"script\", attr: \"src\" },\r\n { tag: \"iframe\", attr: \"src\" },\r\n { tag: \"link\", attr: \"href\" },\r\n { tag: \"audio\", attr: \"src\" },\r\n { tag: \"video\", attr: \"src\" },\r\n { tag: \"source\", attr: \"src\" },\r\n { tag: \"embed\", attr: \"src\" },\r\n { tag: \"object\", attr: \"data\" },\r\n];\r\n\r\nexport async function detectMixedContent(): Promise<Issue[]> {\r\n // Only relevant on HTTPS pages — skip on plain HTTP and file:// origins.\r\n if (typeof window === \"undefined\" || window.location.protocol !== \"https:\") {\r\n return [];\r\n }\r\n\r\n const issues: Issue[] = [];\r\n for (const { tag, attr } of ATTR_TARGETS) {\r\n const elements = document.querySelectorAll(`${tag}[${attr}]`);\r\n for (const el of Array.from(elements)) {\r\n if (el.closest(\"#tracebug-root\")) continue;\r\n const value = (el as HTMLElement).getAttribute(attr) || \"\";\r\n if (!value.startsWith(\"http://\")) continue;\r\n\r\n // <link> only matters when it's loading something the browser fetches —\r\n // stylesheets, preloads, manifests, icons. Skip rel=\"canonical\" etc.\r\n if (tag === \"link\") {\r\n const rel = ((el as HTMLLinkElement).rel || \"\").toLowerCase();\r\n const fetchableRels = [\"stylesheet\", \"preload\", \"prefetch\", \"manifest\", \"icon\", \"shortcut icon\"];\r\n if (!fetchableRels.some(r => rel.includes(r))) continue;\r\n }\r\n\r\n issues.push({\r\n id: makeIssueId(\"mixed-content\"),\r\n detector: \"mixed-content\",\r\n severity: tag === \"script\" || tag === \"iframe\" ? \"serious\" : \"moderate\",\r\n title: `Mixed content: ${tag} loads over HTTP`,\r\n description: `<${tag}> on an HTTPS page references \\`${value}\\`. Browsers block or downgrade this — the resource usually fails to load and breaks the page's secure-context indicator.`,\r\n selector: buildSelector(el as HTMLElement),\r\n url: value,\r\n page: window.location.pathname,\r\n detectedAt: Date.now(),\r\n });\r\n }\r\n }\r\n return issues;\r\n}\r\n","// ── Bug Fingerprint ───────────────────────────────────────────────────────\r\n// Group identical errors locally so 14× the same TypeError collapses into\r\n// one issue with `[×14]` instead of 14 near-duplicate rows.\r\n//\r\n// Fingerprint inputs (in priority order):\r\n// 1. Error type/class extracted from the message (\"TypeError\", etc.)\r\n// 2. Top three \"at ...\" stack frames (location-only — line numbers\r\n// stable across invocations within the same build)\r\n// 3. Page path\r\n//\r\n// We use SHA-1 via `crypto.subtle.digest` when available — falls back to\r\n// a tiny non-cryptographic hash on older contexts. Fingerprint is not\r\n// security-sensitive; collision rate of djb2 is good enough for this.\r\n\r\n/** Compute a stable fingerprint string for an error + page combo. */\r\nexport async function computeFingerprint(\r\n errorMessage: string,\r\n errorStack: string | undefined,\r\n page: string\r\n): Promise<string> {\r\n const errorType = extractErrorType(errorMessage);\r\n const topFrames = extractTopFrames(errorStack || \"\", 3);\r\n const input = `${errorType}|${topFrames.join(\"\\n\")}|${page}`;\r\n\r\n // Prefer SHA-1 from the Subtle Crypto API for stronger uniqueness.\r\n if (typeof crypto !== \"undefined\" && crypto.subtle && typeof crypto.subtle.digest === \"function\") {\r\n try {\r\n const buf = new TextEncoder().encode(input);\r\n const hash = await crypto.subtle.digest(\"SHA-1\", buf);\r\n return bufferToHex(hash).slice(0, 16);\r\n } catch {}\r\n }\r\n return djb2(input).toString(16).padStart(8, \"0\");\r\n}\r\n\r\n/**\r\n * Synchronous fallback fingerprint — used in code paths that can't `await`.\r\n * Collision rate higher than SHA-1 but acceptable for in-session grouping.\r\n */\r\nexport function computeFingerprintSync(\r\n errorMessage: string,\r\n errorStack: string | undefined,\r\n page: string\r\n): string {\r\n const errorType = extractErrorType(errorMessage);\r\n const topFrames = extractTopFrames(errorStack || \"\", 3);\r\n const input = `${errorType}|${topFrames.join(\"\\n\")}|${page}`;\r\n return djb2(input).toString(16).padStart(8, \"0\");\r\n}\r\n\r\n/** Pick out the JS error class — TypeError, ReferenceError, etc. */\r\nfunction extractErrorType(message: string): string {\r\n const m = message.match(/^([A-Z][a-zA-Z]+Error|Error)\\b/);\r\n return m ? m[1] : \"Error\";\r\n}\r\n\r\n/**\r\n * Extract the location parts (\"foo.js:42:13\") of the top N stack frames,\r\n * dropping function names. Same call site → same fingerprint, even if the\r\n * function gets renamed between minified/dev builds.\r\n */\r\nfunction extractTopFrames(stack: string, n: number): string[] {\r\n const frames: string[] = [];\r\n const lines = stack.split(\"\\n\");\r\n for (const line of lines) {\r\n const m = line.match(/(https?:\\/\\/[^):\\s]+|[^():\\s]+\\.[a-z]+):(\\d+):(\\d+)/i);\r\n if (m) {\r\n const url = m[1];\r\n const path = url.includes(\"://\") ? new URL(url, typeof window !== \"undefined\" ? window.location.origin : \"http://localhost\").pathname : url;\r\n frames.push(`${path}:${m[2]}:${m[3]}`);\r\n if (frames.length >= n) break;\r\n }\r\n }\r\n return frames;\r\n}\r\n\r\nfunction bufferToHex(buf: ArrayBuffer): string {\r\n const bytes = new Uint8Array(buf);\r\n let out = \"\";\r\n for (let i = 0; i < bytes.length; i++) out += bytes[i].toString(16).padStart(2, \"0\");\r\n return out;\r\n}\r\n\r\n/** djb2 — tiny non-cryptographic hash. Stable, deterministic, no deps. */\r\nfunction djb2(str: string): number {\r\n let hash = 5381;\r\n for (let i = 0; i < str.length; i++) hash = ((hash << 5) + hash + str.charCodeAt(i)) | 0;\r\n return hash >>> 0; // unsigned 32-bit\r\n}\r\n","// ── Session-data detectors ────────────────────────────────────────────────\r\n// Three detectors that read from the SDK's existing event buffer and\r\n// network-failure ring buffer — no new tracking, just classification.\r\n//\r\n// console-error: every distinct error/unhandled rejection in the session\r\n// failed-request: every 4xx/5xx/network-error from the api_request stream\r\n// slow-api: every successful api_request that took longer than the threshold\r\n\r\nimport { Issue, StoredSession, TraceBugEvent } from \"../../types\";\r\nimport { getNetworkFailures } from \"../../collectors\";\r\nimport { makeIssueId } from \"../helpers\";\r\nimport { computeFingerprint } from \"../../fingerprint\";\r\n\r\nconst SLOW_API_MS = 2000;\r\nconst MAX_CONTEXT_SAMPLES = 10;\r\n\r\n/**\r\n * Console errors collapsed by fingerprint (errorType + top-3 frames + page).\r\n * Repeats accumulate `occurrences` + `firstSeenAt`/`lastSeenAt` + a few\r\n * `contextSamples` so the UI can show the count and let the user expand\r\n * to see distinct preceding actions.\r\n */\r\nexport async function detectConsoleErrors(session: StoredSession | null): Promise<Issue[]> {\r\n if (!session) return [];\r\n\r\n // Build groups by fingerprint.\r\n type Group = {\r\n issue: Issue;\r\n samples: Array<{ timestamp: number; precedingAction?: string }>;\r\n };\r\n const groups = new Map<string, Group>();\r\n\r\n for (let i = 0; i < session.events.length; i++) {\r\n const e = session.events[i];\r\n if (e.type !== \"error\" && e.type !== \"unhandled_rejection\" && e.type !== \"console_error\") continue;\r\n const message = e.data.error?.message || e.data.message || \"\";\r\n if (!message) continue;\r\n\r\n const stack = e.data.error?.stack || \"\";\r\n const page = e.page || (typeof window !== \"undefined\" ? window.location.pathname : \"\");\r\n const fp = await computeFingerprint(message, stack, page);\r\n\r\n const precedingAction = describePrecedingAction(session.events, i);\r\n\r\n const existing = groups.get(fp);\r\n if (existing) {\r\n existing.issue.occurrences = (existing.issue.occurrences || 1) + 1;\r\n existing.issue.lastSeenAt = e.timestamp;\r\n if (existing.samples.length < MAX_CONTEXT_SAMPLES) {\r\n existing.samples.push({ timestamp: e.timestamp, precedingAction });\r\n }\r\n continue;\r\n }\r\n\r\n const firstFrame = stack.split(\"\\n\").find((l: string) => l.trim().startsWith(\"at \"))?.trim() || \"\";\r\n const issue: Issue = {\r\n id: makeIssueId(\"console-error\"),\r\n detector: \"console-error\",\r\n severity: classifyErrorSeverity(message),\r\n title: `JS error: ${message.slice(0, 70)}${message.length > 70 ? \"…\" : \"\"}`,\r\n description: firstFrame ? `${message}\\n\\nFirst frame: ${firstFrame}` : message,\r\n page,\r\n detectedAt: e.timestamp,\r\n fingerprint: fp,\r\n occurrences: 1,\r\n firstSeenAt: e.timestamp,\r\n lastSeenAt: e.timestamp,\r\n };\r\n groups.set(fp, { issue, samples: [{ timestamp: e.timestamp, precedingAction }] });\r\n }\r\n\r\n // Finalize: attach context samples + adjust title to reflect repeat count.\r\n const out: Issue[] = [];\r\n for (const g of groups.values()) {\r\n const n = g.issue.occurrences || 1;\r\n if (n > 1) {\r\n g.issue.title = `${g.issue.title} [×${n}]`;\r\n g.issue.contextSamples = g.samples;\r\n }\r\n out.push(g.issue);\r\n }\r\n return out;\r\n}\r\n\r\n/** Look back from index `i` for the most recent click/input/navigation. */\r\nfunction describePrecedingAction(events: TraceBugEvent[], i: number): string | undefined {\r\n for (let j = i - 1; j >= 0; j--) {\r\n const e = events[j];\r\n if (e.type === \"click\") {\r\n const t = e.data?.element?.text || e.data?.element?.ariaLabel || e.data?.element?.tag || \"element\";\r\n return `clicked \"${String(t).slice(0, 40)}\"`;\r\n }\r\n if (e.type === \"input\") {\r\n const n = e.data?.element?.name || e.data?.element?.id || \"field\";\r\n return `typed in ${n}`;\r\n }\r\n if (e.type === \"select_change\") return \"selected an option\";\r\n if (e.type === \"form_submit\") return \"submitted a form\";\r\n if (e.type === \"route_change\") return `navigated to ${e.data?.to || \"page\"}`;\r\n }\r\n return undefined;\r\n}\r\n\r\nexport async function detectFailedRequests(session: StoredSession | null): Promise<Issue[]> {\r\n if (!session) return [];\r\n const issues: Issue[] = [];\r\n const buffer = getNetworkFailures();\r\n\r\n for (const e of session.events) {\r\n if (e.type !== \"api_request\") continue;\r\n const req = e.data.request;\r\n if (!req) continue;\r\n const status = req.statusCode || 0;\r\n if (status >= 200 && status < 400) continue; // success\r\n if (status === 0 && req.method === \"HEAD\") continue; // skip our own probe noise\r\n\r\n // Try to find a matching response body snippet from the failure buffer.\r\n const match = buffer.find(b =>\r\n b.url === req.url &&\r\n b.method === req.method &&\r\n b.status === status &&\r\n Math.abs(b.timestamp - e.timestamp) < 5000\r\n );\r\n const snippet = match?.response ? `\\n\\nResponse: ${match.response.slice(0, 160)}` : \"\";\r\n\r\n issues.push({\r\n id: makeIssueId(\"failed-request\"),\r\n detector: \"failed-request\",\r\n severity: status >= 500 ? \"critical\" : status === 0 ? \"serious\" : \"moderate\",\r\n title: `${req.method} ${truncatePath(req.url)} → ${status === 0 ? \"Network Error\" : status}`,\r\n description: `Request failed in ${req.durationMs || 0}ms.${snippet}`,\r\n url: req.url,\r\n page: e.page || window.location.pathname,\r\n detectedAt: e.timestamp,\r\n });\r\n }\r\n\r\n return issues;\r\n}\r\n\r\nexport async function detectSlowApis(session: StoredSession | null): Promise<Issue[]> {\r\n if (!session) return [];\r\n const issues: Issue[] = [];\r\n\r\n for (const e of session.events) {\r\n if (e.type !== \"api_request\") continue;\r\n const req = e.data.request;\r\n if (!req) continue;\r\n const status = req.statusCode || 0;\r\n // Only flag *successful* slow requests — failures are surfaced separately.\r\n if (status < 200 || status >= 400) continue;\r\n const duration = req.durationMs || 0;\r\n if (duration < SLOW_API_MS) continue;\r\n\r\n issues.push({\r\n id: makeIssueId(\"slow-api\"),\r\n detector: \"slow-api\",\r\n severity: duration > 5000 ? \"serious\" : \"moderate\",\r\n title: `Slow API: ${req.method} ${truncatePath(req.url)} (${duration}ms)`,\r\n description: `This request took ${(duration / 1000).toFixed(1)}s — over the ${SLOW_API_MS / 1000}s threshold. Slow APIs are a common UX complaint and a leading cause of perceived bugs (\"the page is frozen\").`,\r\n url: req.url,\r\n page: e.page || window.location.pathname,\r\n detectedAt: e.timestamp,\r\n });\r\n }\r\n\r\n return issues;\r\n}\r\n\r\nfunction truncatePath(url: string): string {\r\n try {\r\n const u = new URL(url, window.location.origin);\r\n const p = u.pathname.length > 50 ? u.pathname.slice(0, 47) + \"…\" : u.pathname;\r\n return p;\r\n } catch {\r\n return url.length > 50 ? url.slice(0, 47) + \"…\" : url;\r\n }\r\n}\r\n\r\nfunction classifyErrorSeverity(message: string): Issue[\"severity\"] {\r\n if (/TypeError|ReferenceError|SyntaxError/i.test(message)) return \"critical\";\r\n if (/Network|fetch|failed to/i.test(message)) return \"serious\";\r\n return \"moderate\";\r\n}\r\n","// ── Accessibility detector ────────────────────────────────────────────────\r\n// Lazy-loads axe-core (~250 KB minified) only on first scan, runs the\r\n// default rule set against the live DOM, and converts axe violations into\r\n// our Issue shape. Skips TraceBug's own UI by passing `exclude: [\"#tracebug-root\"]`.\r\n//\r\n// axe-core has its own dependency graph but bundles cleanly via tsup's\r\n// dynamic-import support — the chunk only loads when scan() is called.\r\n\r\nimport { Issue } from \"../../types\";\r\nimport { coerceSeverity, makeIssueId } from \"../helpers\";\r\n\r\ntype AxeModule = typeof import(\"axe-core\");\r\ntype AxeResults = import(\"axe-core\").AxeResults;\r\n\r\nlet _axePromise: Promise<AxeModule | null> | null = null;\r\n\r\nfunction loadAxe(): Promise<AxeModule | null> {\r\n if (_axePromise) return _axePromise;\r\n _axePromise = import(\"axe-core\")\r\n .then((mod) => (mod as AxeModule & { default?: AxeModule }).default || mod)\r\n .catch((err) => {\r\n console.warn(\"[TraceBug] axe-core failed to load:\", err);\r\n return null;\r\n });\r\n return _axePromise;\r\n}\r\n\r\nexport async function detectA11yViolations(): Promise<Issue[]> {\r\n const axe = await loadAxe();\r\n if (!axe || typeof axe.run !== \"function\") return [];\r\n\r\n let results: AxeResults;\r\n try {\r\n results = await axe.run(document, {\r\n // Only WCAG-tagged rules — keeps signal-to-noise high. Best-practice\r\n // rules add ~30% more noise without proportional value for QA.\r\n runOnly: { type: \"tag\", values: [\"wcag2a\", \"wcag2aa\", \"wcag21a\", \"wcag21aa\"] },\r\n // Skip our own UI so QA isn't told their toolbar fails contrast checks.\r\n // axe accepts a context with exclude — we pass { exclude: [...] } via\r\n // the second-argument options-shaped form below to keep types loose.\r\n resultTypes: [\"violations\"],\r\n });\r\n } catch (err) {\r\n console.warn(\"[TraceBug] axe.run failed:\", err);\r\n return [];\r\n }\r\n\r\n const issues: Issue[] = [];\r\n const violations = results?.violations || [];\r\n\r\n for (const v of violations) {\r\n const nodes = v.nodes || [];\r\n // Each violation often hits multiple elements (e.g. 12 buttons missing\r\n // labels). Roll all of them into a single issue with a node count, so\r\n // the panel doesn't drown in 200 near-duplicate rows.\r\n const firstNode = nodes[0];\r\n const selector = Array.isArray(firstNode?.target) ? firstNode.target.join(\" \") : \"\";\r\n const exampleSnippet: string = (firstNode?.html || \"\").slice(0, 120);\r\n const moreSuffix = nodes.length > 1 ? ` (+ ${nodes.length - 1} more element${nodes.length === 2 ? \"\" : \"s\"})` : \"\";\r\n\r\n issues.push({\r\n id: makeIssueId(\"axe-a11y\"),\r\n detector: \"axe-a11y\",\r\n severity: coerceSeverity(v.impact),\r\n title: `${v.help || v.id}${moreSuffix}`,\r\n description: `${v.description || v.id}\\n\\nFirst element: \\`${exampleSnippet}\\``,\r\n selector: selector || undefined,\r\n helpUrl: v.helpUrl,\r\n page: window.location.pathname,\r\n detectedAt: Date.now(),\r\n });\r\n }\r\n\r\n return issues;\r\n}\r\n","// ── Frustration detectors ─────────────────────────────────────────────────\r\n// Surface user-experienced bugs that don't throw exceptions but make users\r\n// hate the product:\r\n// - Rage clicks: ≥3 clicks on the same selector within 1.5 s with no response\r\n// - Dead clicks: click with no DOM/route/network/input response within 1.5 s\r\n// - Form abandonment: input events on a form, then route_change before submit\r\n// - Error correlation: error fired ≤2.5 s after a click — pair them\r\n//\r\n// Pure analysis over the existing event log. No new tracking code, no extra\r\n// listeners.\r\n\r\nimport { Issue, StoredSession, TraceBugEvent } from \"../../types\";\r\nimport { makeIssueId } from \"../helpers\";\r\n\r\nconst RAGE_WINDOW_MS = 1500;\r\nconst RAGE_MIN_CLICKS = 3;\r\nconst DEAD_RESPONSE_WINDOW_MS = 1500;\r\nconst ABANDON_WINDOW_MS = 60_000;\r\nconst ERROR_CORRELATION_WINDOW_MS = 2500;\r\n\r\nexport async function detectFrustration(session: StoredSession | null): Promise<Issue[]> {\r\n if (!session) return [];\r\n const events = session.events;\r\n if (events.length === 0) return [];\r\n\r\n const issues: Issue[] = [];\r\n const page = session.events[0]?.page || window.location.pathname;\r\n\r\n issues.push(...detectRageClicks(events, page));\r\n issues.push(...detectDeadClicks(events, page));\r\n issues.push(...detectFormAbandonment(events, page));\r\n issues.push(...detectErrorCorrelated(events, page));\r\n\r\n return issues;\r\n}\r\n\r\n// ── Rage clicks ───────────────────────────────────────────────────────\r\n// Sliding window: ≥3 clicks on the same selector inside 1.5 s, with no\r\n// response (api_request, route_change, or input on the same form) between.\r\n\r\nfunction detectRageClicks(events: TraceBugEvent[], page: string): Issue[] {\r\n const out: Issue[] = [];\r\n const seenGroups = new Set<string>();\r\n\r\n for (let i = 0; i < events.length; i++) {\r\n const e = events[i];\r\n if (e.type !== \"click\") continue;\r\n const sel = e.data?.element?.selector || e.data?.element?.testId || \"\";\r\n if (!sel) continue;\r\n\r\n // Look ahead for sibling clicks within RAGE_WINDOW_MS on the same selector.\r\n const cluster: TraceBugEvent[] = [e];\r\n let j = i + 1;\r\n while (j < events.length) {\r\n const next = events[j];\r\n if (next.timestamp - e.timestamp > RAGE_WINDOW_MS) break;\r\n // Stop if a \"response\" event happened — not rage if anything responded.\r\n if (isResponseEvent(next)) break;\r\n if (next.type === \"click\") {\r\n const nextSel = next.data?.element?.selector || next.data?.element?.testId || \"\";\r\n if (nextSel === sel) cluster.push(next);\r\n }\r\n j++;\r\n }\r\n\r\n if (cluster.length >= RAGE_MIN_CLICKS) {\r\n const key = `${sel}@${e.timestamp}`;\r\n if (seenGroups.has(key)) continue;\r\n seenGroups.add(key);\r\n\r\n const label = clickLabel(cluster[0]);\r\n out.push({\r\n id: makeIssueId(\"frustration-rage\"),\r\n detector: \"frustration-rage\",\r\n severity: \"serious\",\r\n title: `Rage clicks on ${label} (${cluster.length}× in ${Math.round((cluster[cluster.length - 1].timestamp - cluster[0].timestamp))}ms)`,\r\n description: `User clicked the same element ${cluster.length} times within ${RAGE_WINDOW_MS}ms with no observable response (no API call, navigation, or DOM update). The element either doesn't respond to clicks or feels broken.`,\r\n selector: sel,\r\n page,\r\n detectedAt: cluster[0].timestamp,\r\n firstSeenAt: cluster[0].timestamp,\r\n lastSeenAt: cluster[cluster.length - 1].timestamp,\r\n occurrences: cluster.length,\r\n });\r\n // Skip past the cluster to avoid double-flagging.\r\n i = j - 1;\r\n }\r\n }\r\n\r\n return out;\r\n}\r\n\r\n// ── Dead clicks ───────────────────────────────────────────────────────\r\n// Click followed by no api_request, route_change, or input within 1.5 s.\r\n\r\nfunction detectDeadClicks(events: TraceBugEvent[], page: string): Issue[] {\r\n const out: Issue[] = [];\r\n // Cap output: dead-click detection is noisy on long sessions.\r\n const MAX = 5;\r\n\r\n for (let i = 0; i < events.length && out.length < MAX; i++) {\r\n const e = events[i];\r\n if (e.type !== \"click\") continue;\r\n // Skip if the next event in the same window is another click on the same\r\n // selector (likely a rage cluster — already flagged separately).\r\n let responsive = false;\r\n for (let j = i + 1; j < events.length; j++) {\r\n const next = events[j];\r\n if (next.timestamp - e.timestamp > DEAD_RESPONSE_WINDOW_MS) break;\r\n if (isResponseEvent(next)) { responsive = true; break; }\r\n }\r\n if (responsive) continue;\r\n\r\n const sel = e.data?.element?.selector || \"\";\r\n const label = clickLabel(e);\r\n out.push({\r\n id: makeIssueId(\"frustration-dead\"),\r\n detector: \"frustration-dead\",\r\n severity: \"moderate\",\r\n title: `Dead click on ${label}`,\r\n description: `Clicked but nothing happened within ${DEAD_RESPONSE_WINDOW_MS}ms (no API call, navigation, or DOM input). The element may have an unbound handler, a swallowed event, or be visually clickable but disabled.`,\r\n selector: sel,\r\n page: e.page || page,\r\n detectedAt: e.timestamp,\r\n });\r\n }\r\n\r\n return out;\r\n}\r\n\r\n// ── Form abandonment ──────────────────────────────────────────────────\r\n// Inputs on a form, then route_change before form_submit.\r\n\r\nfunction detectFormAbandonment(events: TraceBugEvent[], _page: string): Issue[] {\r\n const out: Issue[] = [];\r\n // Track per-form: has the user typed in it but not submitted?\r\n const formActivity: Record<string, { firstInputAt: number; fieldsSeen: Set<string>; lastInputAt: number; page: string }> = {};\r\n\r\n for (const e of events) {\r\n if (e.type === \"input\") {\r\n const formId = e.data?.element?.formId || e.data?.element?.formAction || \"_default\";\r\n if (!formActivity[formId]) {\r\n formActivity[formId] = { firstInputAt: e.timestamp, fieldsSeen: new Set(), lastInputAt: e.timestamp, page: e.page };\r\n }\r\n const name = e.data?.element?.name || e.data?.element?.id || \"field\";\r\n formActivity[formId].fieldsSeen.add(name);\r\n formActivity[formId].lastInputAt = e.timestamp;\r\n } else if (e.type === \"form_submit\") {\r\n const formId = e.data?.form?.id || \"_default\";\r\n delete formActivity[formId];\r\n } else if (e.type === \"route_change\") {\r\n // Any route change with active forms = abandonment if within window.\r\n for (const formId of Object.keys(formActivity)) {\r\n const a = formActivity[formId];\r\n if (e.timestamp - a.lastInputAt > ABANDON_WINDOW_MS) continue;\r\n if (a.fieldsSeen.size === 0) continue;\r\n out.push({\r\n id: makeIssueId(\"frustration-abandon\"),\r\n detector: \"frustration-abandon\",\r\n severity: \"moderate\",\r\n title: `Form abandoned on ${a.page} (${a.fieldsSeen.size} field${a.fieldsSeen.size === 1 ? \"\" : \"s\"} filled)`,\r\n description: `User typed into ${a.fieldsSeen.size} field${a.fieldsSeen.size === 1 ? \"\" : \"s\"} (${Array.from(a.fieldsSeen).slice(0, 5).join(\", \")}) and then navigated away without submitting. Likely a UX problem: the submit button is unclear, the form requires too much info, or it's failing silently.`,\r\n page: a.page,\r\n detectedAt: a.lastInputAt,\r\n firstSeenAt: a.firstInputAt,\r\n lastSeenAt: a.lastInputAt,\r\n });\r\n delete formActivity[formId];\r\n }\r\n }\r\n }\r\n\r\n return out;\r\n}\r\n\r\n// ── Error correlation ─────────────────────────────────────────────────\r\n// For each error, look back ≤2.5 s for the nearest click — likely the\r\n// triggering interaction. Tag both events together.\r\n\r\nfunction detectErrorCorrelated(events: TraceBugEvent[], page: string): Issue[] {\r\n const out: Issue[] = [];\r\n const seen = new Set<string>();\r\n\r\n for (let i = 0; i < events.length; i++) {\r\n const e = events[i];\r\n const isError = e.type === \"error\" || e.type === \"unhandled_rejection\" || e.type === \"console_error\";\r\n if (!isError) continue;\r\n\r\n // Look back for the nearest click.\r\n let click: TraceBugEvent | null = null;\r\n for (let j = i - 1; j >= 0; j--) {\r\n const prev = events[j];\r\n if (e.timestamp - prev.timestamp > ERROR_CORRELATION_WINDOW_MS) break;\r\n if (prev.type === \"click\") { click = prev; break; }\r\n }\r\n if (!click) continue;\r\n\r\n const errMsg = e.data?.error?.message || \"\";\r\n if (!errMsg) continue;\r\n const key = `${errMsg}::${click.data?.element?.selector || \"\"}`;\r\n if (seen.has(key)) continue;\r\n seen.add(key);\r\n\r\n const label = clickLabel(click);\r\n const truncMsg = errMsg.length > 60 ? errMsg.slice(0, 57) + \"…\" : errMsg;\r\n const delta = Math.round((e.timestamp - click.timestamp));\r\n out.push({\r\n id: makeIssueId(\"frustration-error-correlated\"),\r\n detector: \"frustration-error-correlated\",\r\n severity: \"critical\",\r\n title: `Click on ${label} triggered: ${truncMsg}`,\r\n description: `An error fired ${delta}ms after the user clicked ${label}. This is almost certainly the offending interaction — the click handler threw, or its async path failed.`,\r\n selector: click.data?.element?.selector,\r\n page: e.page || page,\r\n detectedAt: e.timestamp,\r\n });\r\n }\r\n\r\n return out;\r\n}\r\n\r\n// ── Helpers ────────────────────────────────────────────────────────────\r\n\r\nfunction isResponseEvent(e: TraceBugEvent): boolean {\r\n return (\r\n e.type === \"api_request\" ||\r\n e.type === \"route_change\" ||\r\n e.type === \"input\" ||\r\n e.type === \"form_submit\" ||\r\n e.type === \"select_change\"\r\n );\r\n}\r\n\r\nfunction clickLabel(e: TraceBugEvent): string {\r\n const el = e.data?.element;\r\n const raw = el?.text || el?.ariaLabel || el?.testId || el?.id || el?.tag || \"element\";\r\n const trimmed = String(raw).replace(/\\s+/g, \" \").trim();\r\n return trimmed.length > 40 ? `\"${trimmed.slice(0, 37)}…\"` : `\"${trimmed}\"`;\r\n}\r\n","// ── Scanner orchestrator ──────────────────────────────────────────────────\r\n// Runs every detector in parallel, collects results into a single in-memory\r\n// store, and exposes simple queries (getIssues, dismissIssue, fileAsBug).\r\n//\r\n// Issues live in memory only — each scan is a fresh run, results clear on\r\n// page reload. Mirrors the screenshot/video memory model.\r\n\r\nimport { Issue, IssueDetector } from \"../types\";\r\nimport { getAllSessions } from \"../storage\";\r\nimport { detectBrokenImages } from \"./detectors/broken-images\";\r\nimport { detectMixedContent } from \"./detectors/mixed-content\";\r\nimport { detectConsoleErrors, detectFailedRequests, detectSlowApis } from \"./detectors/session-data\";\r\nimport { detectA11yViolations } from \"./detectors/a11y\";\r\nimport { detectFrustration } from \"./detectors/frustration\";\r\n\r\nlet _issues: Issue[] = [];\r\nlet _scanInFlight: Promise<Issue[]> | null = null;\r\nlet _lastScanAt = 0;\r\n\r\nconst SEVERITY_ORDER: Record<Issue[\"severity\"], number> = {\r\n critical: 0,\r\n serious: 1,\r\n moderate: 2,\r\n minor: 3,\r\n};\r\n\r\nexport interface ScanResult {\r\n issues: Issue[];\r\n durationMs: number;\r\n scannedAt: number;\r\n}\r\n\r\n/**\r\n * Run every detector in parallel. Concurrent scans are coalesced — calling\r\n * scan() while one is already running returns the in-flight promise.\r\n */\r\nexport async function scan(): Promise<ScanResult> {\r\n if (_scanInFlight) {\r\n const issues = await _scanInFlight;\r\n return { issues, durationMs: 0, scannedAt: _lastScanAt };\r\n }\r\n\r\n const startedAt = Date.now();\r\n const sessions = getAllSessions().sort((a, b) => b.updatedAt - a.updatedAt);\r\n const session = sessions[0] || null;\r\n\r\n // Promise.allSettled isn't in the ES2018 target lib, so wrap each detector\r\n // in a catch that returns []. One failure doesn't block the others.\r\n const safeRun = (p: Promise<Issue[]>): Promise<Issue[]> =>\r\n p.catch((err) => {\r\n console.warn(\"[TraceBug] Detector failed:\", err);\r\n return [];\r\n });\r\n\r\n _scanInFlight = Promise.all([\r\n safeRun(detectBrokenImages()),\r\n safeRun(detectMixedContent()),\r\n safeRun(detectConsoleErrors(session)),\r\n safeRun(detectFailedRequests(session)),\r\n safeRun(detectSlowApis(session)),\r\n safeRun(detectA11yViolations()),\r\n safeRun(detectFrustration(session)),\r\n ]).then((results) => {\r\n const all: Issue[] = ([] as Issue[]).concat(...results);\r\n // Stable sort: severity first, then detection time.\r\n all.sort((a, b) => {\r\n const sev = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];\r\n if (sev !== 0) return sev;\r\n return a.detectedAt - b.detectedAt;\r\n });\r\n _issues = all;\r\n return all;\r\n });\r\n\r\n try {\r\n const issues = await _scanInFlight;\r\n _lastScanAt = Date.now();\r\n return { issues, durationMs: _lastScanAt - startedAt, scannedAt: _lastScanAt };\r\n } finally {\r\n _scanInFlight = null;\r\n }\r\n}\r\n\r\n/** Snapshot of current issues. Filters out dismissed by default. */\r\nexport function getIssues(options?: { includeDismissed?: boolean }): Issue[] {\r\n const includeDismissed = options?.includeDismissed ?? false;\r\n return includeDismissed ? _issues.slice() : _issues.filter(i => !i.dismissed);\r\n}\r\n\r\n/** Mark an issue as dismissed for the current session. */\r\nexport function dismissIssue(id: string): boolean {\r\n const issue = _issues.find(i => i.id === id);\r\n if (!issue) return false;\r\n issue.dismissed = true;\r\n return true;\r\n}\r\n\r\n/** Restore a previously dismissed issue. */\r\nexport function undismissIssue(id: string): boolean {\r\n const issue = _issues.find(i => i.id === id);\r\n if (!issue) return false;\r\n issue.dismissed = false;\r\n return true;\r\n}\r\n\r\n/** Clear all issues from memory. Called from destroy() and \"Clear all data\". */\r\nexport function clearIssues(): void {\r\n _issues = [];\r\n _lastScanAt = 0;\r\n}\r\n\r\n/** Aggregate counts grouped by detector, useful for the toolbar badge. */\r\nexport function getIssueCountsByDetector(): Record<IssueDetector, number> {\r\n const counts: Record<IssueDetector, number> = {\r\n \"axe-a11y\": 0,\r\n \"broken-image\": 0,\r\n \"mixed-content\": 0,\r\n \"console-error\": 0,\r\n \"slow-api\": 0,\r\n \"failed-request\": 0,\r\n \"frustration-rage\": 0,\r\n \"frustration-dead\": 0,\r\n \"frustration-abandon\": 0,\r\n \"frustration-error-correlated\": 0,\r\n };\r\n for (const i of _issues) {\r\n if (i.dismissed) continue;\r\n counts[i.detector] = (counts[i.detector] || 0) + 1;\r\n }\r\n return counts;\r\n}\r\n\r\n/** Count of non-dismissed issues at each severity. */\r\nexport function getIssueCountsBySeverity(): Record<Issue[\"severity\"], number> {\r\n const counts: Record<Issue[\"severity\"], number> = {\r\n critical: 0,\r\n serious: 0,\r\n moderate: 0,\r\n minor: 0,\r\n };\r\n for (const i of _issues) {\r\n if (i.dismissed) continue;\r\n counts[i.severity] += 1;\r\n }\r\n return counts;\r\n}\r\n\r\n/** Lookup helper for the issues panel — find by id. */\r\nexport function getIssueById(id: string): Issue | null {\r\n return _issues.find(i => i.id === id) || null;\r\n}\r\n"],"mappings":";;;;;;AAMA,IAAI,gBAAgB;AAGb,SAAS,YAAY,UAAiC;AAC3D,mBAAiB;AACjB,SAAO,GAAG,QAAQ,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,aAAa;AAChE;AAQO,SAAS,cAAc,IAAqB;AACjD,MAAI,CAAC,MAAM,GAAG,aAAa,EAAG,QAAO;AACrC,MAAI,GAAG,GAAI,QAAO,IAAI,UAAU,GAAG,EAAE,CAAC;AAEtC,QAAM,SAAS,GAAG,aAAa,aAAa,KAAK,GAAG,aAAa,cAAc;AAC/E,MAAI,OAAQ,QAAO,iBAAiB,UAAU,MAAM,CAAC;AAErD,QAAM,QAAkB,CAAC;AACzB,MAAI,MAAsB;AAC1B,MAAI,QAAQ;AACZ,SAAO,OAAO,IAAI,aAAa,KAAK,IAAI,YAAY,UAAU,QAAQ,GAAG;AACvE,UAAM,MAAM,IAAI,QAAQ,YAAY;AAGpC,UAAM,SAA6B,IAAI;AACvC,QAAI,CAAC,QAAQ;AACX,YAAM,QAAQ,GAAG;AACjB;AAAA,IACF;AACA,UAAM,WAAW,MAAM,KAAK,OAAO,QAAQ,EAAE,OAAO,OAAK,EAAE,YAAY,IAAK,OAAO;AACnF,QAAI,SAAS,SAAS,GAAG;AACvB,YAAM,MAAM,SAAS,QAAQ,GAAG,IAAI;AACpC,YAAM,QAAQ,GAAG,GAAG,gBAAgB,GAAG,GAAG;AAAA,IAC5C,OAAO;AACL,YAAM,QAAQ,GAAG;AAAA,IACnB;AACA,UAAM;AACN,aAAS;AAAA,EACX;AACA,SAAO,MAAM,KAAK,KAAK;AACzB;AAMO,SAAS,eAAe,QAAkD;AAC/E,WAAS,UAAU,IAAI,YAAY,GAAG;AAAA,IACpC,KAAK;AAAY,aAAO;AAAA,IACxB,KAAK;AAAW,aAAO;AAAA,IACvB,KAAK;AAAY,aAAO;AAAA,IACxB,KAAK;AAAS,aAAO;AAAA,IACrB;AAAS,aAAO;AAAA,EAClB;AACF;AAMA,SAAS,UAAU,OAAuB;AACxC,MAAI,OAAO,QAAQ,eAAe,OAAO,IAAI,WAAW,YAAY;AAClE,WAAO,IAAI,OAAO,KAAK;AAAA,EACzB;AACA,SAAO,MAAM,QAAQ,WAAW,QAAM,KAAK,EAAE,EAAE;AACjD;;;AClEA,eAAsB,qBAAuC;AAC3D,QAAM,SAAkB,CAAC;AACzB,QAAM,OAAO,MAAM,KAAK,SAAS,MAAM;AAEvC,aAAW,OAAO,MAAM;AAEtB,QAAI,IAAI,QAAQ,gBAAgB,EAAG;AAEnC,QAAI,CAAC,IAAI,SAAU;AAEnB,QAAI,IAAI,eAAe,EAAG;AAG1B,UAAM,MAAM,IAAI,cAAc,IAAI;AAClC,QAAI,CAAC,IAAK;AAEV,WAAO,KAAK;AAAA,MACV,IAAI,YAAY,cAAc;AAAA,MAC9B,UAAU;AAAA,MACV,UAAU;AAAA,MACV,OAAO,iBAAiB,YAAY,GAAG,CAAC;AAAA,MACxC,aAAa,8DAA8D,GAAG,uDAC5E,IAAI,MAAM,cAAc,IAAI,GAAG,MAAM,8CACvC;AAAA,MACA,UAAU,cAAc,GAAG;AAAA,MAC3B,KAAK;AAAA,MACL,MAAM,OAAO,SAAS;AAAA,MACtB,YAAY,KAAK,IAAI;AAAA,IACvB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEA,SAAS,YAAY,KAAqB;AACxC,MAAI,IAAI,UAAU,GAAI,QAAO;AAE7B,QAAM,OAAO,IAAI,MAAM,GAAG,EAAE,IAAI,KAAK,IAAI,MAAM,GAAG;AAClD,SAAO,UAAK,IAAI;AAClB;;;ACvCA,IAAM,eAAqD;AAAA,EACzD,EAAE,KAAK,OAAO,MAAM,MAAM;AAAA,EAC1B,EAAE,KAAK,UAAU,MAAM,MAAM;AAAA,EAC7B,EAAE,KAAK,UAAU,MAAM,MAAM;AAAA,EAC7B,EAAE,KAAK,QAAQ,MAAM,OAAO;AAAA,EAC5B,EAAE,KAAK,SAAS,MAAM,MAAM;AAAA,EAC5B,EAAE,KAAK,SAAS,MAAM,MAAM;AAAA,EAC5B,EAAE,KAAK,UAAU,MAAM,MAAM;AAAA,EAC7B,EAAE,KAAK,SAAS,MAAM,MAAM;AAAA,EAC5B,EAAE,KAAK,UAAU,MAAM,OAAO;AAChC;AAEA,eAAsB,qBAAuC;AAE3D,MAAI,OAAO,WAAW,eAAe,OAAO,SAAS,aAAa,UAAU;AAC1E,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAkB,CAAC;AACzB,aAAW,EAAE,KAAK,KAAK,KAAK,cAAc;AACxC,UAAM,WAAW,SAAS,iBAAiB,GAAG,GAAG,IAAI,IAAI,GAAG;AAC5D,eAAW,MAAM,MAAM,KAAK,QAAQ,GAAG;AACrC,UAAI,GAAG,QAAQ,gBAAgB,EAAG;AAClC,YAAM,QAAS,GAAmB,aAAa,IAAI,KAAK;AACxD,UAAI,CAAC,MAAM,WAAW,SAAS,EAAG;AAIlC,UAAI,QAAQ,QAAQ;AAClB,cAAM,OAAQ,GAAuB,OAAO,IAAI,YAAY;AAC5D,cAAM,gBAAgB,CAAC,cAAc,WAAW,YAAY,YAAY,QAAQ,eAAe;AAC/F,YAAI,CAAC,cAAc,KAAK,OAAK,IAAI,SAAS,CAAC,CAAC,EAAG;AAAA,MACjD;AAEA,aAAO,KAAK;AAAA,QACV,IAAI,YAAY,eAAe;AAAA,QAC/B,UAAU;AAAA,QACV,UAAU,QAAQ,YAAY,QAAQ,WAAW,YAAY;AAAA,QAC7D,OAAO,kBAAkB,GAAG;AAAA,QAC5B,aAAa,IAAI,GAAG,mCAAmC,KAAK;AAAA,QAC5D,UAAU,cAAc,EAAiB;AAAA,QACzC,KAAK;AAAA,QACL,MAAM,OAAO,SAAS;AAAA,QACtB,YAAY,KAAK,IAAI;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;;;AC1CA,eAAsB,mBACpB,cACA,YACA,MACiB;AACjB,QAAM,YAAY,iBAAiB,YAAY;AAC/C,QAAM,YAAY,iBAAiB,cAAc,IAAI,CAAC;AACtD,QAAM,QAAQ,GAAG,SAAS,IAAI,UAAU,KAAK,IAAI,CAAC,IAAI,IAAI;AAG1D,MAAI,OAAO,WAAW,eAAe,OAAO,UAAU,OAAO,OAAO,OAAO,WAAW,YAAY;AAChG,QAAI;AACF,YAAM,MAAM,IAAI,YAAY,EAAE,OAAO,KAAK;AAC1C,YAAM,OAAO,MAAM,OAAO,OAAO,OAAO,SAAS,GAAG;AACpD,aAAO,YAAY,IAAI,EAAE,MAAM,GAAG,EAAE;AAAA,IACtC,SAAQ;AAAA,IAAC;AAAA,EACX;AACA,SAAO,KAAK,KAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AACjD;AAkBA,SAAS,iBAAiB,SAAyB;AACjD,QAAM,IAAI,QAAQ,MAAM,gCAAgC;AACxD,SAAO,IAAI,EAAE,CAAC,IAAI;AACpB;AAOA,SAAS,iBAAiB,OAAe,GAAqB;AAC5D,QAAM,SAAmB,CAAC;AAC1B,QAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,KAAK,MAAM,sDAAsD;AAC3E,QAAI,GAAG;AACL,YAAM,MAAM,EAAE,CAAC;AACf,YAAM,OAAO,IAAI,SAAS,KAAK,IAAI,IAAI,IAAI,KAAK,OAAO,WAAW,cAAc,OAAO,SAAS,SAAS,kBAAkB,EAAE,WAAW;AACxI,aAAO,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE;AACrC,UAAI,OAAO,UAAU,EAAG;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,KAA0B;AAC7C,QAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,QAAO,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AACnF,SAAO;AACT;AAGA,SAAS,KAAK,KAAqB;AACjC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,SAAS,QAAQ,KAAK,OAAO,IAAI,WAAW,CAAC,IAAK;AACvF,SAAO,SAAS;AAClB;;;AC3EA,IAAM,cAAc;AACpB,IAAM,sBAAsB;AAQ5B,eAAsB,oBAAoB,SAAiD;AAtB3F;AAuBE,MAAI,CAAC,QAAS,QAAO,CAAC;AAOtB,QAAM,SAAS,oBAAI,IAAmB;AAEtC,WAAS,IAAI,GAAG,IAAI,QAAQ,OAAO,QAAQ,KAAK;AAC9C,UAAM,IAAI,QAAQ,OAAO,CAAC;AAC1B,QAAI,EAAE,SAAS,WAAW,EAAE,SAAS,yBAAyB,EAAE,SAAS,gBAAiB;AAC1F,UAAM,YAAU,OAAE,KAAK,UAAP,mBAAc,YAAW,EAAE,KAAK,WAAW;AAC3D,QAAI,CAAC,QAAS;AAEd,UAAM,UAAQ,OAAE,KAAK,UAAP,mBAAc,UAAS;AACrC,UAAM,OAAO,EAAE,SAAS,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AACnF,UAAM,KAAK,MAAM,mBAAmB,SAAS,OAAO,IAAI;AAExD,UAAM,kBAAkB,wBAAwB,QAAQ,QAAQ,CAAC;AAEjE,UAAM,WAAW,OAAO,IAAI,EAAE;AAC9B,QAAI,UAAU;AACZ,eAAS,MAAM,eAAe,SAAS,MAAM,eAAe,KAAK;AACjE,eAAS,MAAM,aAAa,EAAE;AAC9B,UAAI,SAAS,QAAQ,SAAS,qBAAqB;AACjD,iBAAS,QAAQ,KAAK,EAAE,WAAW,EAAE,WAAW,gBAAgB,CAAC;AAAA,MACnE;AACA;AAAA,IACF;AAEA,UAAM,eAAa,WAAM,MAAM,IAAI,EAAE,KAAK,CAAC,MAAc,EAAE,KAAK,EAAE,WAAW,KAAK,CAAC,MAAhE,mBAAmE,WAAU;AAChG,UAAM,QAAe;AAAA,MACnB,IAAI,YAAY,eAAe;AAAA,MAC/B,UAAU;AAAA,MACV,UAAU,sBAAsB,OAAO;AAAA,MACvC,OAAO,aAAa,QAAQ,MAAM,GAAG,EAAE,CAAC,GAAG,QAAQ,SAAS,KAAK,WAAM,EAAE;AAAA,MACzE,aAAa,aAAa,GAAG,OAAO;AAAA;AAAA,eAAoB,UAAU,KAAK;AAAA,MACvE;AAAA,MACA,YAAY,EAAE;AAAA,MACd,aAAa;AAAA,MACb,aAAa;AAAA,MACb,aAAa,EAAE;AAAA,MACf,YAAY,EAAE;AAAA,IAChB;AACA,WAAO,IAAI,IAAI,EAAE,OAAO,SAAS,CAAC,EAAE,WAAW,EAAE,WAAW,gBAAgB,CAAC,EAAE,CAAC;AAAA,EAClF;AAGA,QAAM,MAAe,CAAC;AACtB,aAAW,KAAK,OAAO,OAAO,GAAG;AAC/B,UAAM,IAAI,EAAE,MAAM,eAAe;AACjC,QAAI,IAAI,GAAG;AACT,QAAE,MAAM,QAAQ,GAAG,EAAE,MAAM,KAAK,SAAM,CAAC;AACvC,QAAE,MAAM,iBAAiB,EAAE;AAAA,IAC7B;AACA,QAAI,KAAK,EAAE,KAAK;AAAA,EAClB;AACA,SAAO;AACT;AAGA,SAAS,wBAAwB,QAAyB,GAA+B;AArFzF;AAsFE,WAAS,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK;AAC/B,UAAM,IAAI,OAAO,CAAC;AAClB,QAAI,EAAE,SAAS,SAAS;AACtB,YAAM,MAAI,aAAE,SAAF,mBAAQ,YAAR,mBAAiB,WAAQ,aAAE,SAAF,mBAAQ,YAAR,mBAAiB,gBAAa,aAAE,SAAF,mBAAQ,YAAR,mBAAiB,QAAO;AACzF,aAAO,YAAY,OAAO,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,IAC3C;AACA,QAAI,EAAE,SAAS,SAAS;AACtB,YAAM,MAAI,aAAE,SAAF,mBAAQ,YAAR,mBAAiB,WAAQ,aAAE,SAAF,mBAAQ,YAAR,mBAAiB,OAAM;AAC1D,aAAO,YAAY,CAAC;AAAA,IACtB;AACA,QAAI,EAAE,SAAS,gBAAiB,QAAO;AACvC,QAAI,EAAE,SAAS,cAAe,QAAO;AACrC,QAAI,EAAE,SAAS,eAAgB,QAAO,kBAAgB,OAAE,SAAF,mBAAQ,OAAM,MAAM;AAAA,EAC5E;AACA,SAAO;AACT;AAEA,eAAsB,qBAAqB,SAAiD;AAC1F,MAAI,CAAC,QAAS,QAAO,CAAC;AACtB,QAAM,SAAkB,CAAC;AACzB,QAAM,SAAS,mBAAmB;AAElC,aAAW,KAAK,QAAQ,QAAQ;AAC9B,QAAI,EAAE,SAAS,cAAe;AAC9B,UAAM,MAAM,EAAE,KAAK;AACnB,QAAI,CAAC,IAAK;AACV,UAAM,SAAS,IAAI,cAAc;AACjC,QAAI,UAAU,OAAO,SAAS,IAAK;AACnC,QAAI,WAAW,KAAK,IAAI,WAAW,OAAQ;AAG3C,UAAM,QAAQ,OAAO;AAAA,MAAK,OACxB,EAAE,QAAQ,IAAI,OACd,EAAE,WAAW,IAAI,UACjB,EAAE,WAAW,UACb,KAAK,IAAI,EAAE,YAAY,EAAE,SAAS,IAAI;AAAA,IACxC;AACA,UAAM,WAAU,+BAAO,YAAW;AAAA;AAAA,YAAiB,MAAM,SAAS,MAAM,GAAG,GAAG,CAAC,KAAK;AAEpF,WAAO,KAAK;AAAA,MACV,IAAI,YAAY,gBAAgB;AAAA,MAChC,UAAU;AAAA,MACV,UAAU,UAAU,MAAM,aAAa,WAAW,IAAI,YAAY;AAAA,MAClE,OAAO,GAAG,IAAI,MAAM,IAAI,aAAa,IAAI,GAAG,CAAC,WAAM,WAAW,IAAI,kBAAkB,MAAM;AAAA,MAC1F,aAAa,qBAAqB,IAAI,cAAc,CAAC,MAAM,OAAO;AAAA,MAClE,KAAK,IAAI;AAAA,MACT,MAAM,EAAE,QAAQ,OAAO,SAAS;AAAA,MAChC,YAAY,EAAE;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEA,eAAsB,eAAe,SAAiD;AACpF,MAAI,CAAC,QAAS,QAAO,CAAC;AACtB,QAAM,SAAkB,CAAC;AAEzB,aAAW,KAAK,QAAQ,QAAQ;AAC9B,QAAI,EAAE,SAAS,cAAe;AAC9B,UAAM,MAAM,EAAE,KAAK;AACnB,QAAI,CAAC,IAAK;AACV,UAAM,SAAS,IAAI,cAAc;AAEjC,QAAI,SAAS,OAAO,UAAU,IAAK;AACnC,UAAM,WAAW,IAAI,cAAc;AACnC,QAAI,WAAW,YAAa;AAE5B,WAAO,KAAK;AAAA,MACV,IAAI,YAAY,UAAU;AAAA,MAC1B,UAAU;AAAA,MACV,UAAU,WAAW,MAAO,YAAY;AAAA,MACxC,OAAO,aAAa,IAAI,MAAM,IAAI,aAAa,IAAI,GAAG,CAAC,KAAK,QAAQ;AAAA,MACpE,aAAa,sBAAsB,WAAW,KAAM,QAAQ,CAAC,CAAC,qBAAgB,cAAc,GAAI;AAAA,MAChG,KAAK,IAAI;AAAA,MACT,MAAM,EAAE,QAAQ,OAAO,SAAS;AAAA,MAChC,YAAY,EAAE;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEA,SAAS,aAAa,KAAqB;AACzC,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,KAAK,OAAO,SAAS,MAAM;AAC7C,UAAM,IAAI,EAAE,SAAS,SAAS,KAAK,EAAE,SAAS,MAAM,GAAG,EAAE,IAAI,WAAM,EAAE;AACrE,WAAO;AAAA,EACT,SAAQ;AACN,WAAO,IAAI,SAAS,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,WAAM;AAAA,EACpD;AACF;AAEA,SAAS,sBAAsB,SAAoC;AACjE,MAAI,wCAAwC,KAAK,OAAO,EAAG,QAAO;AAClE,MAAI,2BAA2B,KAAK,OAAO,EAAG,QAAO;AACrD,SAAO;AACT;;;ACzKA,IAAI,cAAgD;AAEpD,SAAS,UAAqC;AAC5C,MAAI,YAAa,QAAO;AACxB,gBAAc,OAAO,UAAU,EAC5B,KAAK,CAAC,QAAS,IAA4C,WAAW,GAAG,EACzE,MAAM,CAAC,QAAQ;AACd,YAAQ,KAAK,uCAAuC,GAAG;AACvD,WAAO;AAAA,EACT,CAAC;AACH,SAAO;AACT;AAEA,eAAsB,uBAAyC;AAC7D,QAAM,MAAM,MAAM,QAAQ;AAC1B,MAAI,CAAC,OAAO,OAAO,IAAI,QAAQ,WAAY,QAAO,CAAC;AAEnD,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,IAAI,IAAI,UAAU;AAAA;AAAA;AAAA,MAGhC,SAAS,EAAE,MAAM,OAAO,QAAQ,CAAC,UAAU,WAAW,WAAW,UAAU,EAAE;AAAA;AAAA;AAAA;AAAA,MAI7E,aAAa,CAAC,YAAY;AAAA,IAC5B,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,YAAQ,KAAK,8BAA8B,GAAG;AAC9C,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAkB,CAAC;AACzB,QAAM,cAAa,mCAAS,eAAc,CAAC;AAE3C,aAAW,KAAK,YAAY;AAC1B,UAAM,QAAQ,EAAE,SAAS,CAAC;AAI1B,UAAM,YAAY,MAAM,CAAC;AACzB,UAAM,WAAW,MAAM,QAAQ,uCAAW,MAAM,IAAI,UAAU,OAAO,KAAK,GAAG,IAAI;AACjF,UAAM,mBAA0B,uCAAW,SAAQ,IAAI,MAAM,GAAG,GAAG;AACnE,UAAM,aAAa,MAAM,SAAS,IAAI,OAAO,MAAM,SAAS,CAAC,gBAAgB,MAAM,WAAW,IAAI,KAAK,GAAG,MAAM;AAEhH,WAAO,KAAK;AAAA,MACV,IAAI,YAAY,UAAU;AAAA,MAC1B,UAAU;AAAA,MACV,UAAU,eAAe,EAAE,MAAM;AAAA,MACjC,OAAO,GAAG,EAAE,QAAQ,EAAE,EAAE,GAAG,UAAU;AAAA,MACrC,aAAa,GAAG,EAAE,eAAe,EAAE,EAAE;AAAA;AAAA,mBAAwB,cAAc;AAAA,MAC3E,UAAU,YAAY;AAAA,MACtB,SAAS,EAAE;AAAA,MACX,MAAM,OAAO,SAAS;AAAA,MACtB,YAAY,KAAK,IAAI;AAAA,IACvB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;AC5DA,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AACxB,IAAM,0BAA0B;AAChC,IAAM,oBAAoB;AAC1B,IAAM,8BAA8B;AAEpC,eAAsB,kBAAkB,SAAiD;AApBzF;AAqBE,MAAI,CAAC,QAAS,QAAO,CAAC;AACtB,QAAM,SAAS,QAAQ;AACvB,MAAI,OAAO,WAAW,EAAG,QAAO,CAAC;AAEjC,QAAM,SAAkB,CAAC;AACzB,QAAM,SAAO,aAAQ,OAAO,CAAC,MAAhB,mBAAmB,SAAQ,OAAO,SAAS;AAExD,SAAO,KAAK,GAAG,iBAAiB,QAAQ,IAAI,CAAC;AAC7C,SAAO,KAAK,GAAG,iBAAiB,QAAQ,IAAI,CAAC;AAC7C,SAAO,KAAK,GAAG,sBAAsB,QAAQ,IAAI,CAAC;AAClD,SAAO,KAAK,GAAG,sBAAsB,QAAQ,IAAI,CAAC;AAElD,SAAO;AACT;AAMA,SAAS,iBAAiB,QAAyB,MAAuB;AAxC1E;AAyCE,QAAM,MAAe,CAAC;AACtB,QAAM,aAAa,oBAAI,IAAY;AAEnC,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,IAAI,OAAO,CAAC;AAClB,QAAI,EAAE,SAAS,QAAS;AACxB,UAAM,QAAM,aAAE,SAAF,mBAAQ,YAAR,mBAAiB,eAAY,aAAE,SAAF,mBAAQ,YAAR,mBAAiB,WAAU;AACpE,QAAI,CAAC,IAAK;AAGV,UAAM,UAA2B,CAAC,CAAC;AACnC,QAAI,IAAI,IAAI;AACZ,WAAO,IAAI,OAAO,QAAQ;AACxB,YAAM,OAAO,OAAO,CAAC;AACrB,UAAI,KAAK,YAAY,EAAE,YAAY,eAAgB;AAEnD,UAAI,gBAAgB,IAAI,EAAG;AAC3B,UAAI,KAAK,SAAS,SAAS;AACzB,cAAM,YAAU,gBAAK,SAAL,mBAAW,YAAX,mBAAoB,eAAY,gBAAK,SAAL,mBAAW,YAAX,mBAAoB,WAAU;AAC9E,YAAI,YAAY,IAAK,SAAQ,KAAK,IAAI;AAAA,MACxC;AACA;AAAA,IACF;AAEA,QAAI,QAAQ,UAAU,iBAAiB;AACrC,YAAM,MAAM,GAAG,GAAG,IAAI,EAAE,SAAS;AACjC,UAAI,WAAW,IAAI,GAAG,EAAG;AACzB,iBAAW,IAAI,GAAG;AAElB,YAAM,QAAQ,WAAW,QAAQ,CAAC,CAAC;AACnC,UAAI,KAAK;AAAA,QACP,IAAI,YAAY,kBAAkB;AAAA,QAClC,UAAU;AAAA,QACV,UAAU;AAAA,QACV,OAAO,kBAAkB,KAAK,KAAK,QAAQ,MAAM,WAAQ,KAAK,MAAO,QAAQ,QAAQ,SAAS,CAAC,EAAE,YAAY,QAAQ,CAAC,EAAE,SAAU,CAAC;AAAA,QACnI,aAAa,iCAAiC,QAAQ,MAAM,iBAAiB,cAAc;AAAA,QAC3F,UAAU;AAAA,QACV;AAAA,QACA,YAAY,QAAQ,CAAC,EAAE;AAAA,QACvB,aAAa,QAAQ,CAAC,EAAE;AAAA,QACxB,YAAY,QAAQ,QAAQ,SAAS,CAAC,EAAE;AAAA,QACxC,aAAa,QAAQ;AAAA,MACvB,CAAC;AAED,UAAI,IAAI;AAAA,IACV;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,iBAAiB,QAAyB,MAAuB;AA/F1E;AAgGE,QAAM,MAAe,CAAC;AAEtB,QAAM,MAAM;AAEZ,WAAS,IAAI,GAAG,IAAI,OAAO,UAAU,IAAI,SAAS,KAAK,KAAK;AAC1D,UAAM,IAAI,OAAO,CAAC;AAClB,QAAI,EAAE,SAAS,QAAS;AAGxB,QAAI,aAAa;AACjB,aAAS,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AAC1C,YAAM,OAAO,OAAO,CAAC;AACrB,UAAI,KAAK,YAAY,EAAE,YAAY,wBAAyB;AAC5D,UAAI,gBAAgB,IAAI,GAAG;AAAE,qBAAa;AAAM;AAAA,MAAO;AAAA,IACzD;AACA,QAAI,WAAY;AAEhB,UAAM,QAAM,aAAE,SAAF,mBAAQ,YAAR,mBAAiB,aAAY;AACzC,UAAM,QAAQ,WAAW,CAAC;AAC1B,QAAI,KAAK;AAAA,MACP,IAAI,YAAY,kBAAkB;AAAA,MAClC,UAAU;AAAA,MACV,UAAU;AAAA,MACV,OAAO,iBAAiB,KAAK;AAAA,MAC7B,aAAa,uCAAuC,uBAAuB;AAAA,MAC3E,UAAU;AAAA,MACV,MAAM,EAAE,QAAQ;AAAA,MAChB,YAAY,EAAE;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAKA,SAAS,sBAAsB,QAAyB,OAAwB;AArIhF;AAsIE,QAAM,MAAe,CAAC;AAEtB,QAAM,eAAqH,CAAC;AAE5H,aAAW,KAAK,QAAQ;AACtB,QAAI,EAAE,SAAS,SAAS;AACtB,YAAM,WAAS,aAAE,SAAF,mBAAQ,YAAR,mBAAiB,aAAU,aAAE,SAAF,mBAAQ,YAAR,mBAAiB,eAAc;AACzE,UAAI,CAAC,aAAa,MAAM,GAAG;AACzB,qBAAa,MAAM,IAAI,EAAE,cAAc,EAAE,WAAW,YAAY,oBAAI,IAAI,GAAG,aAAa,EAAE,WAAW,MAAM,EAAE,KAAK;AAAA,MACpH;AACA,YAAM,SAAO,aAAE,SAAF,mBAAQ,YAAR,mBAAiB,WAAQ,aAAE,SAAF,mBAAQ,YAAR,mBAAiB,OAAM;AAC7D,mBAAa,MAAM,EAAE,WAAW,IAAI,IAAI;AACxC,mBAAa,MAAM,EAAE,cAAc,EAAE;AAAA,IACvC,WAAW,EAAE,SAAS,eAAe;AACnC,YAAM,WAAS,aAAE,SAAF,mBAAQ,SAAR,mBAAc,OAAM;AACnC,aAAO,aAAa,MAAM;AAAA,IAC5B,WAAW,EAAE,SAAS,gBAAgB;AAEpC,iBAAW,UAAU,OAAO,KAAK,YAAY,GAAG;AAC9C,cAAM,IAAI,aAAa,MAAM;AAC7B,YAAI,EAAE,YAAY,EAAE,cAAc,kBAAmB;AACrD,YAAI,EAAE,WAAW,SAAS,EAAG;AAC7B,YAAI,KAAK;AAAA,UACP,IAAI,YAAY,qBAAqB;AAAA,UACrC,UAAU;AAAA,UACV,UAAU;AAAA,UACV,OAAO,qBAAqB,EAAE,IAAI,KAAK,EAAE,WAAW,IAAI,SAAS,EAAE,WAAW,SAAS,IAAI,KAAK,GAAG;AAAA,UACnG,aAAa,mBAAmB,EAAE,WAAW,IAAI,SAAS,EAAE,WAAW,SAAS,IAAI,KAAK,GAAG,KAAK,MAAM,KAAK,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,UAChJ,MAAM,EAAE;AAAA,UACR,YAAY,EAAE;AAAA,UACd,aAAa,EAAE;AAAA,UACf,YAAY,EAAE;AAAA,QAChB,CAAC;AACD,eAAO,aAAa,MAAM;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMA,SAAS,sBAAsB,QAAyB,MAAuB;AAnL/E;AAoLE,QAAM,MAAe,CAAC;AACtB,QAAM,OAAO,oBAAI,IAAY;AAE7B,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,IAAI,OAAO,CAAC;AAClB,UAAM,UAAU,EAAE,SAAS,WAAW,EAAE,SAAS,yBAAyB,EAAE,SAAS;AACrF,QAAI,CAAC,QAAS;AAGd,QAAI,QAA8B;AAClC,aAAS,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK;AAC/B,YAAM,OAAO,OAAO,CAAC;AACrB,UAAI,EAAE,YAAY,KAAK,YAAY,4BAA6B;AAChE,UAAI,KAAK,SAAS,SAAS;AAAE,gBAAQ;AAAM;AAAA,MAAO;AAAA,IACpD;AACA,QAAI,CAAC,MAAO;AAEZ,UAAM,WAAS,aAAE,SAAF,mBAAQ,UAAR,mBAAe,YAAW;AACzC,QAAI,CAAC,OAAQ;AACb,UAAM,MAAM,GAAG,MAAM,OAAK,iBAAM,SAAN,mBAAY,YAAZ,mBAAqB,aAAY,EAAE;AAC7D,QAAI,KAAK,IAAI,GAAG,EAAG;AACnB,SAAK,IAAI,GAAG;AAEZ,UAAM,QAAQ,WAAW,KAAK;AAC9B,UAAM,WAAW,OAAO,SAAS,KAAK,OAAO,MAAM,GAAG,EAAE,IAAI,WAAM;AAClE,UAAM,QAAQ,KAAK,MAAO,EAAE,YAAY,MAAM,SAAU;AACxD,QAAI,KAAK;AAAA,MACP,IAAI,YAAY,8BAA8B;AAAA,MAC9C,UAAU;AAAA,MACV,UAAU;AAAA,MACV,OAAO,YAAY,KAAK,eAAe,QAAQ;AAAA,MAC/C,aAAa,kBAAkB,KAAK,6BAA6B,KAAK;AAAA,MACtE,WAAU,iBAAM,SAAN,mBAAY,YAAZ,mBAAqB;AAAA,MAC/B,MAAM,EAAE,QAAQ;AAAA,MAChB,YAAY,EAAE;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAIA,SAAS,gBAAgB,GAA2B;AAClD,SACE,EAAE,SAAS,iBACX,EAAE,SAAS,kBACX,EAAE,SAAS,WACX,EAAE,SAAS,iBACX,EAAE,SAAS;AAEf;AAEA,SAAS,WAAW,GAA0B;AAzO9C;AA0OE,QAAM,MAAK,OAAE,SAAF,mBAAQ;AACnB,QAAM,OAAM,yBAAI,UAAQ,yBAAI,eAAa,yBAAI,YAAU,yBAAI,QAAM,yBAAI,QAAO;AAC5E,QAAM,UAAU,OAAO,GAAG,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACtD,SAAO,QAAQ,SAAS,KAAK,IAAI,QAAQ,MAAM,GAAG,EAAE,CAAC,YAAO,IAAI,OAAO;AACzE;;;AC/NA,IAAI,UAAmB,CAAC;AACxB,IAAI,gBAAyC;AAC7C,IAAI,cAAc;AAElB,IAAM,iBAAoD;AAAA,EACxD,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AACT;AAYA,eAAsB,OAA4B;AAChD,MAAI,eAAe;AACjB,UAAM,SAAS,MAAM;AACrB,WAAO,EAAE,QAAQ,YAAY,GAAG,WAAW,YAAY;AAAA,EACzD;AAEA,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,WAAW,eAAe,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAC1E,QAAM,UAAU,SAAS,CAAC,KAAK;AAI/B,QAAM,UAAU,CAAC,MACf,EAAE,MAAM,CAAC,QAAQ;AACf,YAAQ,KAAK,+BAA+B,GAAG;AAC/C,WAAO,CAAC;AAAA,EACV,CAAC;AAEH,kBAAgB,QAAQ,IAAI;AAAA,IAC1B,QAAQ,mBAAmB,CAAC;AAAA,IAC5B,QAAQ,mBAAmB,CAAC;AAAA,IAC5B,QAAQ,oBAAoB,OAAO,CAAC;AAAA,IACpC,QAAQ,qBAAqB,OAAO,CAAC;AAAA,IACrC,QAAQ,eAAe,OAAO,CAAC;AAAA,IAC/B,QAAQ,qBAAqB,CAAC;AAAA,IAC9B,QAAQ,kBAAkB,OAAO,CAAC;AAAA,EACpC,CAAC,EAAE,KAAK,CAAC,YAAY;AACnB,UAAM,MAAgB,CAAC,EAAc,OAAO,GAAG,OAAO;AAEtD,QAAI,KAAK,CAAC,GAAG,MAAM;AACjB,YAAM,MAAM,eAAe,EAAE,QAAQ,IAAI,eAAe,EAAE,QAAQ;AAClE,UAAI,QAAQ,EAAG,QAAO;AACtB,aAAO,EAAE,aAAa,EAAE;AAAA,IAC1B,CAAC;AACD,cAAU;AACV,WAAO;AAAA,EACT,CAAC;AAED,MAAI;AACF,UAAM,SAAS,MAAM;AACrB,kBAAc,KAAK,IAAI;AACvB,WAAO,EAAE,QAAQ,YAAY,cAAc,WAAW,WAAW,YAAY;AAAA,EAC/E,UAAE;AACA,oBAAgB;AAAA,EAClB;AACF;AAGO,SAAS,UAAU,SAAmD;AApF7E;AAqFE,QAAM,oBAAmB,wCAAS,qBAAT,YAA6B;AACtD,SAAO,mBAAmB,QAAQ,MAAM,IAAI,QAAQ,OAAO,OAAK,CAAC,EAAE,SAAS;AAC9E;AAGO,SAAS,aAAa,IAAqB;AAChD,QAAM,QAAQ,QAAQ,KAAK,OAAK,EAAE,OAAO,EAAE;AAC3C,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,YAAY;AAClB,SAAO;AACT;AAGO,SAAS,eAAe,IAAqB;AAClD,QAAM,QAAQ,QAAQ,KAAK,OAAK,EAAE,OAAO,EAAE;AAC3C,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,YAAY;AAClB,SAAO;AACT;AAGO,SAAS,cAAoB;AAClC,YAAU,CAAC;AACX,gBAAc;AAChB;AAGO,SAAS,2BAA0D;AACxE,QAAM,SAAwC;AAAA,IAC5C,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,IACvB,gCAAgC;AAAA,EAClC;AACA,aAAW,KAAK,SAAS;AACvB,QAAI,EAAE,UAAW;AACjB,WAAO,EAAE,QAAQ,KAAK,OAAO,EAAE,QAAQ,KAAK,KAAK;AAAA,EACnD;AACA,SAAO;AACT;AAGO,SAAS,2BAA8D;AAC5E,QAAM,SAA4C;AAAA,IAChD,UAAU;AAAA,IACV,SAAS;AAAA,IACT,UAAU;AAAA,IACV,OAAO;AAAA,EACT;AACA,aAAW,KAAK,SAAS;AACvB,QAAI,EAAE,UAAW;AACjB,WAAO,EAAE,QAAQ,KAAK;AAAA,EACxB;AACA,SAAO;AACT;AAGO,SAAS,aAAa,IAA0B;AACrD,SAAO,QAAQ,KAAK,OAAK,EAAE,OAAO,EAAE,KAAK;AAC3C;","names":[]}
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }
var _chunkFQTNMBGVcjs = require('./chunk-FQTNMBGV.cjs');
var _chunk4SLN5LQDcjs = require('./chunk-4SLN5LQD.cjs');
// src/ui/issues-panel.ts
var PANEL_ID = "tracebug-issues-panel";
var STYLE_ID = "tracebug-issues-panel-styles";
var _isOpen = false;
var _root = null;
var SEVERITY_COLORS = {
critical: { bg: "#7f1d1d", fg: "#fee2e2", border: "#dc2626" },
serious: { bg: "#7c2d12", fg: "#fed7aa", border: "#ea580c" },
moderate: { bg: "#713f12", fg: "#fde68a", border: "#ca8a04" },
minor: { bg: "#1e3a8a", fg: "#bfdbfe", border: "#2563eb" }
};
var DETECTOR_LABELS = {
"axe-a11y": "A11y",
"broken-image": "Broken image",
"mixed-content": "Mixed content",
"console-error": "JS error",
"slow-api": "Slow API",
"failed-request": "Failed request",
"frustration-rage": "Rage clicks",
"frustration-dead": "Dead click",
"frustration-abandon": "Form abandoned",
"frustration-error-correlated": "Click \u2192 error"
};
function isIssuesPanelOpen() {
return _isOpen;
}
async function showIssuesPanel(root, options) {
var _a;
if (_isOpen) return;
_root = root;
_injectStyles();
_open(root, { issues: [], loading: true });
try {
if ((_a = options == null ? void 0 : options.rescan) != null ? _a : true) {
await _chunkFQTNMBGVcjs.scan.call(void 0, );
}
} catch (err) {
console.warn("[TraceBug] Scan failed:", err);
}
_renderBody(_chunkFQTNMBGVcjs.getIssues.call(void 0, ));
}
function _injectStyles() {
if (document.getElementById(STYLE_ID)) return;
const style = document.createElement("style");
style.id = STYLE_ID;
style.textContent = `
@keyframes tracebug-issue-locate-flash {
0%, 100% { box-shadow: 0 0 0 0 rgba(99,102,241,0.0); outline: 2px solid transparent; }
30% { box-shadow: 0 0 0 6px rgba(99,102,241,0.5); outline: 2px solid #6366F1; }
}
#${PANEL_ID}-overlay {
position: fixed !important;
inset: 0 !important;
z-index: 2147483647 !important;
background: rgba(0,0,0,0.75) !important;
backdrop-filter: blur(6px) !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
padding: 20px !important;
pointer-events: auto !important;
box-sizing: border-box !important;
}
#${PANEL_ID} {
background: var(--tb-bg-secondary, #1a1a2e) !important;
border: 1px solid var(--tb-border-hover, #3a3a5e) !important;
border-radius: var(--tb-radius-lg, 12px) !important;
width: 100% !important;
max-width: 720px !important;
max-height: 90vh !important;
display: flex !important;
flex-direction: column !important;
overflow: hidden !important;
font-family: var(--tb-font-family, system-ui, -apple-system, sans-serif) !important;
color: var(--tb-text-primary, #e0e0e0) !important;
box-sizing: border-box !important;
box-shadow: 0 20px 60px rgba(0,0,0,0.5) !important;
}
#${PANEL_ID} *, #${PANEL_ID} *::before, #${PANEL_ID} *::after { box-sizing: border-box !important; }
#${PANEL_ID} button { font-family: inherit !important; cursor: pointer !important; }
#${PANEL_ID} .tb-issue-row {
padding: 12px 14px;
border-top: 1px solid var(--tb-border, #2a2a3e);
display: flex;
gap: 12px;
align-items: flex-start;
}
#${PANEL_ID} .tb-issue-row:hover { background: var(--tb-bg-primary, #0f0f1a); }
#${PANEL_ID} .tb-sev-badge {
font-size: 10px;
font-weight: 700;
padding: 3px 7px;
border-radius: 4px;
letter-spacing: 0.4px;
text-transform: uppercase;
flex-shrink: 0;
border: 1px solid;
}
#${PANEL_ID} .tb-detector-tag {
font-size: 10px;
color: var(--tb-text-muted, #888);
background: var(--tb-bg-primary, #0f0f1a);
border: 1px solid var(--tb-border, #2a2a3e);
padding: 2px 6px;
border-radius: 4px;
flex-shrink: 0;
}
#${PANEL_ID} .tb-issue-actions {
display: flex;
gap: 6px;
flex-shrink: 0;
}
#${PANEL_ID} .tb-issue-actions button {
background: transparent;
color: var(--tb-text-secondary, #aaa);
border: 1px solid var(--tb-border, #2a2a3e);
border-radius: 4px;
padding: 4px 10px;
font-size: 11px;
transition: all 0.15s;
}
#${PANEL_ID} .tb-issue-actions button:hover {
background: var(--tb-btn-hover, #ffffff15);
color: var(--tb-text-primary, #fff);
}
#${PANEL_ID} .tb-issue-actions button[data-action="file"] {
background: var(--tb-accent, #6366F1);
color: #fff;
border-color: transparent;
}
#${PANEL_ID} .tb-issue-actions button[data-action="file"]:hover { opacity: 0.9; }
`;
document.head.appendChild(style);
}
function _open(root, state) {
var _a;
_isOpen = true;
(_a = document.getElementById(`${PANEL_ID}-overlay`)) == null ? void 0 : _a.remove();
const overlay = document.createElement("div");
overlay.id = `${PANEL_ID}-overlay`;
overlay.dataset.tracebug = "issues-panel-overlay";
overlay.setAttribute("role", "dialog");
overlay.setAttribute("aria-modal", "true");
overlay.setAttribute("aria-label", "Page issues");
const panel = document.createElement("div");
panel.id = PANEL_ID;
panel.dataset.tracebug = "issues-panel";
panel.innerHTML = _shellHtml(state);
overlay.appendChild(panel);
root.appendChild(overlay);
_wireShellHandlers(overlay, panel);
}
function _shellHtml(state) {
return `
<div data-tb-issues="header" style="padding:16px 18px;display:flex;align-items:center;gap:12px;border-bottom:1px solid var(--tb-border, #2a2a3e)">
<span style="font-size:20px">\u{1F50D}</span>
<div style="flex:1;min-width:0">
<div style="font-size:16px;font-weight:700;color:var(--tb-text-primary, #fff)">Page Issues</div>
<div data-tb-issues="subtitle" style="font-size:11px;color:var(--tb-text-muted, #888);margin-top:2px">${state.loading ? "Scanning\u2026" : "No scan yet"}</div>
</div>
<button data-tb-issues="rescan" style="background:transparent;color:var(--tb-text-secondary, #aaa);border:1px solid var(--tb-border, #2a2a3e);border-radius:6px;padding:6px 12px;font-size:12px;display:flex;align-items:center;gap:6px">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"/><polyline points="21 3 21 9 15 9"/></svg>
Rescan
</button>
<button data-tb-issues="close" aria-label="Close" style="background:none;border:none;color:var(--tb-text-muted, #888);font-size:20px;padding:4px 8px;border-radius:6px">&times;</button>
</div>
<div data-tb-issues="body" style="flex:1;overflow-y:auto;min-height:200px">
${state.loading ? _loadingHtml() : ""}
</div>
`;
}
function _loadingHtml() {
return `
<div style="padding:48px 20px;text-align:center;color:var(--tb-text-muted, #888);font-size:13px">
<div style="font-size:24px;margin-bottom:8px">\u{1F50D}</div>
Scanning page for issues\u2026
<div style="font-size:11px;margin-top:6px;opacity:0.7">Loading axe-core, checking images, network calls, JS errors\u2026</div>
</div>
`;
}
function _wireShellHandlers(overlay, panel) {
const close = () => {
_isOpen = false;
overlay.remove();
document.removeEventListener("keydown", escHandler);
};
panel.querySelector('[data-tb-issues="close"]').addEventListener("click", close);
overlay.addEventListener("click", (e) => {
if (e.target === overlay) close();
});
panel.querySelector('[data-tb-issues="rescan"]').addEventListener("click", async () => {
const body = panel.querySelector('[data-tb-issues="body"]');
body.innerHTML = _loadingHtml();
const sub = panel.querySelector('[data-tb-issues="subtitle"]');
sub.textContent = "Scanning\u2026";
await _chunkFQTNMBGVcjs.scan.call(void 0, );
_renderBody(_chunkFQTNMBGVcjs.getIssues.call(void 0, ));
});
const escHandler = (e) => {
if (e.key === "Escape") close();
};
document.addEventListener("keydown", escHandler);
}
function _renderBody(issues) {
const panel = document.getElementById(PANEL_ID);
if (!panel) return;
const body = panel.querySelector('[data-tb-issues="body"]');
const subtitle = panel.querySelector('[data-tb-issues="subtitle"]');
if (issues.length === 0) {
subtitle.textContent = "No issues found";
body.innerHTML = `
<div style="padding:48px 20px;text-align:center;color:var(--tb-text-muted, #888);font-size:13px">
<div style="font-size:32px;margin-bottom:8px">\u2713</div>
Clean scan \u2014 no issues detected on this page.
<div style="font-size:11px;margin-top:6px;opacity:0.7">Includes a11y \xB7 broken images \xB7 mixed content \xB7 JS errors \xB7 failed/slow API calls</div>
</div>
`;
return;
}
const counts = {};
for (const i of issues) counts[i.severity] = (counts[i.severity] || 0) + 1;
const summary = ["critical", "serious", "moderate", "minor"].filter((s) => counts[s]).map((s) => `${counts[s]} ${s}`).join(" \xB7 ");
subtitle.textContent = `${issues.length} issue${issues.length === 1 ? "" : "s"} \xB7 ${summary}`;
body.innerHTML = issues.map((issue) => _issueRowHtml(issue)).join("");
body.querySelectorAll("[data-tb-issue-id]").forEach((row) => {
var _a, _b, _c;
const id = row.dataset.tbIssueId;
const issue = issues.find((i) => i.id === id);
if (!issue) return;
(_a = row.querySelector('[data-action="locate"]')) == null ? void 0 : _a.addEventListener("click", () => _locate(issue));
(_b = row.querySelector('[data-action="dismiss"]')) == null ? void 0 : _b.addEventListener("click", () => {
_chunkFQTNMBGVcjs.dismissIssue.call(void 0, id);
_renderBody(_chunkFQTNMBGVcjs.getIssues.call(void 0, ));
});
(_c = row.querySelector('[data-action="file"]')) == null ? void 0 : _c.addEventListener("click", () => _fileAsBug(issue));
});
}
function _issueRowHtml(issue) {
const colors = SEVERITY_COLORS[issue.severity];
const detectorLabel = DETECTOR_LABELS[issue.detector];
const desc = issue.description.length > 240 ? issue.description.slice(0, 237) + "\u2026" : issue.description;
const repeats = (issue.occurrences || 1) > 1;
const samplesHtml = repeats && issue.contextSamples && issue.contextSamples.length > 0 ? `<details style="margin-top:6px;font-size:11px">
<summary style="cursor:pointer;color:var(--tb-text-muted, #888)">
View all ${issue.occurrences} contexts
</summary>
<ol style="margin:6px 0 0 20px;padding:0;color:var(--tb-text-secondary, #aaa);line-height:1.5">
${issue.contextSamples.map((s) => `
<li>${new Date(s.timestamp).toLocaleTimeString()}${s.precedingAction ? ` \xB7 ${_chunk4SLN5LQDcjs.escapeHtml.call(void 0, s.precedingAction)}` : ""}</li>
`).join("")}
</ol>
</details>` : "";
return `
<div class="tb-issue-row" data-tb-issue-id="${issue.id}">
<span class="tb-sev-badge" style="background:${colors.bg};color:${colors.fg};border-color:${colors.border}">${issue.severity}</span>
<div style="flex:1;min-width:0">
<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px;flex-wrap:wrap">
<span class="tb-detector-tag">${detectorLabel}</span>
<span style="font-size:13px;color:var(--tb-text-primary, #e0e0e0);font-weight:500">${_chunk4SLN5LQDcjs.escapeHtml.call(void 0, issue.title)}</span>
</div>
<div style="font-size:11px;color:var(--tb-text-muted, #888);line-height:1.5;white-space:pre-wrap">${_chunk4SLN5LQDcjs.escapeHtml.call(void 0, desc)}</div>
${issue.helpUrl ? `<a href="${issue.helpUrl}" target="_blank" rel="noopener noreferrer" style="font-size:11px;color:var(--tb-accent, #6366F1);margin-top:4px;display:inline-block">Learn more \u2192</a>` : ""}
${samplesHtml}
</div>
<div class="tb-issue-actions">
${issue.selector ? `<button data-action="locate" title="Highlight on page">\u{1F4CD} Locate</button>` : ""}
<button data-action="file" title="File as bug ticket">File ticket</button>
<button data-action="dismiss" title="Dismiss for this session">Dismiss</button>
</div>
</div>
`;
}
function _locate(issue) {
var _a;
if (!issue.selector) return;
let el = null;
try {
el = document.querySelector(issue.selector);
} catch (e) {
el = null;
}
if (!el) return;
(_a = document.getElementById(`${PANEL_ID}-overlay`)) == null ? void 0 : _a.remove();
_isOpen = false;
el.scrollIntoView({ behavior: "smooth", block: "center" });
const htmlEl = el;
const prevOutline = htmlEl.style.outline;
const prevTransition = htmlEl.style.transition;
htmlEl.style.transition = "outline 0.2s, box-shadow 0.2s";
htmlEl.style.outline = "3px solid #6366F1";
htmlEl.style.boxShadow = "0 0 0 6px rgba(99,102,241,0.35)";
setTimeout(() => {
htmlEl.style.outline = prevOutline;
htmlEl.style.boxShadow = "";
htmlEl.style.transition = prevTransition;
}, 2400);
}
function _fileAsBug(issue) {
var _a;
const root = _root || document.getElementById("tracebug-root");
if (!root) return;
(_a = document.getElementById(`${PANEL_ID}-overlay`)) == null ? void 0 : _a.remove();
_isOpen = false;
Promise.resolve().then(() => _interopRequireWildcard(require("./quick-bug-LFLO2PSW.cjs"))).then((m) => {
m.showQuickBugCapture(root, {
prefilledTitle: issue.title,
prefilledDescription: _bugDescriptionFromIssue(issue)
}).catch(() => {
});
});
}
function _bugDescriptionFromIssue(issue) {
const lines = [];
lines.push(`> Detected by TraceBug auto-scanner: **${DETECTOR_LABELS[issue.detector]}** (${issue.severity})`);
lines.push("");
lines.push(issue.description);
if (issue.selector) lines.push(`
**Selector:** \`${issue.selector}\``);
if (issue.url) lines.push(`**URL:** \`${issue.url}\``);
if (issue.helpUrl) lines.push(`**Reference:** ${issue.helpUrl}`);
return lines.join("\n");
}
exports.isIssuesPanelOpen = isIssuesPanelOpen; exports.showIssuesPanel = showIssuesPanel;
//# sourceMappingURL=issues-panel-LXTIYXNM.cjs.map
{"version":3,"sources":["d:\\Project\\TraceBug-ai\\dist\\issues-panel-LXTIYXNM.cjs"],"names":[],"mappings":"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E,QAAQ,CAAC;AAChB,EAAE,CAAC;AACH,EAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;AAClC;AACA,SAAS,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;AAC5B,EAAE,IAAI,EAAE;AACR,EAAE,QAAQ,EAAE,IAAI;AAChB,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,8GAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA;AACA;AACA;AACA;AACA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA,oBAAA;AACA;AACA;AACA,WAAA;AACA,iBAAA;AACA,WAAA;AACA;AACA,iBAAA;AACA,EAAA;AACA,gDAAA;AACA,mDAAA;AACA;AACA;AACA,wCAAA;AACA,6FAAA;AACA;AACA,0GAAA;AACA,QAAA;AACA,QAAA;AACA;AACA;AACA,QAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,gBAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA","file":"D:\\Project\\TraceBug-ai\\dist\\issues-panel-LXTIYXNM.cjs","sourcesContent":[null]}
import {
dismissIssue,
getIssues,
scan
} from "./chunk-URDH7OBN.js";
import {
escapeHtml
} from "./chunk-LQO44M5L.js";
// src/ui/issues-panel.ts
var PANEL_ID = "tracebug-issues-panel";
var STYLE_ID = "tracebug-issues-panel-styles";
var _isOpen = false;
var _root = null;
var SEVERITY_COLORS = {
critical: { bg: "#7f1d1d", fg: "#fee2e2", border: "#dc2626" },
serious: { bg: "#7c2d12", fg: "#fed7aa", border: "#ea580c" },
moderate: { bg: "#713f12", fg: "#fde68a", border: "#ca8a04" },
minor: { bg: "#1e3a8a", fg: "#bfdbfe", border: "#2563eb" }
};
var DETECTOR_LABELS = {
"axe-a11y": "A11y",
"broken-image": "Broken image",
"mixed-content": "Mixed content",
"console-error": "JS error",
"slow-api": "Slow API",
"failed-request": "Failed request",
"frustration-rage": "Rage clicks",
"frustration-dead": "Dead click",
"frustration-abandon": "Form abandoned",
"frustration-error-correlated": "Click \u2192 error"
};
function isIssuesPanelOpen() {
return _isOpen;
}
async function showIssuesPanel(root, options) {
var _a;
if (_isOpen) return;
_root = root;
_injectStyles();
_open(root, { issues: [], loading: true });
try {
if ((_a = options == null ? void 0 : options.rescan) != null ? _a : true) {
await scan();
}
} catch (err) {
console.warn("[TraceBug] Scan failed:", err);
}
_renderBody(getIssues());
}
function _injectStyles() {
if (document.getElementById(STYLE_ID)) return;
const style = document.createElement("style");
style.id = STYLE_ID;
style.textContent = `
@keyframes tracebug-issue-locate-flash {
0%, 100% { box-shadow: 0 0 0 0 rgba(99,102,241,0.0); outline: 2px solid transparent; }
30% { box-shadow: 0 0 0 6px rgba(99,102,241,0.5); outline: 2px solid #6366F1; }
}
#${PANEL_ID}-overlay {
position: fixed !important;
inset: 0 !important;
z-index: 2147483647 !important;
background: rgba(0,0,0,0.75) !important;
backdrop-filter: blur(6px) !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
padding: 20px !important;
pointer-events: auto !important;
box-sizing: border-box !important;
}
#${PANEL_ID} {
background: var(--tb-bg-secondary, #1a1a2e) !important;
border: 1px solid var(--tb-border-hover, #3a3a5e) !important;
border-radius: var(--tb-radius-lg, 12px) !important;
width: 100% !important;
max-width: 720px !important;
max-height: 90vh !important;
display: flex !important;
flex-direction: column !important;
overflow: hidden !important;
font-family: var(--tb-font-family, system-ui, -apple-system, sans-serif) !important;
color: var(--tb-text-primary, #e0e0e0) !important;
box-sizing: border-box !important;
box-shadow: 0 20px 60px rgba(0,0,0,0.5) !important;
}
#${PANEL_ID} *, #${PANEL_ID} *::before, #${PANEL_ID} *::after { box-sizing: border-box !important; }
#${PANEL_ID} button { font-family: inherit !important; cursor: pointer !important; }
#${PANEL_ID} .tb-issue-row {
padding: 12px 14px;
border-top: 1px solid var(--tb-border, #2a2a3e);
display: flex;
gap: 12px;
align-items: flex-start;
}
#${PANEL_ID} .tb-issue-row:hover { background: var(--tb-bg-primary, #0f0f1a); }
#${PANEL_ID} .tb-sev-badge {
font-size: 10px;
font-weight: 700;
padding: 3px 7px;
border-radius: 4px;
letter-spacing: 0.4px;
text-transform: uppercase;
flex-shrink: 0;
border: 1px solid;
}
#${PANEL_ID} .tb-detector-tag {
font-size: 10px;
color: var(--tb-text-muted, #888);
background: var(--tb-bg-primary, #0f0f1a);
border: 1px solid var(--tb-border, #2a2a3e);
padding: 2px 6px;
border-radius: 4px;
flex-shrink: 0;
}
#${PANEL_ID} .tb-issue-actions {
display: flex;
gap: 6px;
flex-shrink: 0;
}
#${PANEL_ID} .tb-issue-actions button {
background: transparent;
color: var(--tb-text-secondary, #aaa);
border: 1px solid var(--tb-border, #2a2a3e);
border-radius: 4px;
padding: 4px 10px;
font-size: 11px;
transition: all 0.15s;
}
#${PANEL_ID} .tb-issue-actions button:hover {
background: var(--tb-btn-hover, #ffffff15);
color: var(--tb-text-primary, #fff);
}
#${PANEL_ID} .tb-issue-actions button[data-action="file"] {
background: var(--tb-accent, #6366F1);
color: #fff;
border-color: transparent;
}
#${PANEL_ID} .tb-issue-actions button[data-action="file"]:hover { opacity: 0.9; }
`;
document.head.appendChild(style);
}
function _open(root, state) {
var _a;
_isOpen = true;
(_a = document.getElementById(`${PANEL_ID}-overlay`)) == null ? void 0 : _a.remove();
const overlay = document.createElement("div");
overlay.id = `${PANEL_ID}-overlay`;
overlay.dataset.tracebug = "issues-panel-overlay";
overlay.setAttribute("role", "dialog");
overlay.setAttribute("aria-modal", "true");
overlay.setAttribute("aria-label", "Page issues");
const panel = document.createElement("div");
panel.id = PANEL_ID;
panel.dataset.tracebug = "issues-panel";
panel.innerHTML = _shellHtml(state);
overlay.appendChild(panel);
root.appendChild(overlay);
_wireShellHandlers(overlay, panel);
}
function _shellHtml(state) {
return `
<div data-tb-issues="header" style="padding:16px 18px;display:flex;align-items:center;gap:12px;border-bottom:1px solid var(--tb-border, #2a2a3e)">
<span style="font-size:20px">\u{1F50D}</span>
<div style="flex:1;min-width:0">
<div style="font-size:16px;font-weight:700;color:var(--tb-text-primary, #fff)">Page Issues</div>
<div data-tb-issues="subtitle" style="font-size:11px;color:var(--tb-text-muted, #888);margin-top:2px">${state.loading ? "Scanning\u2026" : "No scan yet"}</div>
</div>
<button data-tb-issues="rescan" style="background:transparent;color:var(--tb-text-secondary, #aaa);border:1px solid var(--tb-border, #2a2a3e);border-radius:6px;padding:6px 12px;font-size:12px;display:flex;align-items:center;gap:6px">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"/><polyline points="21 3 21 9 15 9"/></svg>
Rescan
</button>
<button data-tb-issues="close" aria-label="Close" style="background:none;border:none;color:var(--tb-text-muted, #888);font-size:20px;padding:4px 8px;border-radius:6px">&times;</button>
</div>
<div data-tb-issues="body" style="flex:1;overflow-y:auto;min-height:200px">
${state.loading ? _loadingHtml() : ""}
</div>
`;
}
function _loadingHtml() {
return `
<div style="padding:48px 20px;text-align:center;color:var(--tb-text-muted, #888);font-size:13px">
<div style="font-size:24px;margin-bottom:8px">\u{1F50D}</div>
Scanning page for issues\u2026
<div style="font-size:11px;margin-top:6px;opacity:0.7">Loading axe-core, checking images, network calls, JS errors\u2026</div>
</div>
`;
}
function _wireShellHandlers(overlay, panel) {
const close = () => {
_isOpen = false;
overlay.remove();
document.removeEventListener("keydown", escHandler);
};
panel.querySelector('[data-tb-issues="close"]').addEventListener("click", close);
overlay.addEventListener("click", (e) => {
if (e.target === overlay) close();
});
panel.querySelector('[data-tb-issues="rescan"]').addEventListener("click", async () => {
const body = panel.querySelector('[data-tb-issues="body"]');
body.innerHTML = _loadingHtml();
const sub = panel.querySelector('[data-tb-issues="subtitle"]');
sub.textContent = "Scanning\u2026";
await scan();
_renderBody(getIssues());
});
const escHandler = (e) => {
if (e.key === "Escape") close();
};
document.addEventListener("keydown", escHandler);
}
function _renderBody(issues) {
const panel = document.getElementById(PANEL_ID);
if (!panel) return;
const body = panel.querySelector('[data-tb-issues="body"]');
const subtitle = panel.querySelector('[data-tb-issues="subtitle"]');
if (issues.length === 0) {
subtitle.textContent = "No issues found";
body.innerHTML = `
<div style="padding:48px 20px;text-align:center;color:var(--tb-text-muted, #888);font-size:13px">
<div style="font-size:32px;margin-bottom:8px">\u2713</div>
Clean scan \u2014 no issues detected on this page.
<div style="font-size:11px;margin-top:6px;opacity:0.7">Includes a11y \xB7 broken images \xB7 mixed content \xB7 JS errors \xB7 failed/slow API calls</div>
</div>
`;
return;
}
const counts = {};
for (const i of issues) counts[i.severity] = (counts[i.severity] || 0) + 1;
const summary = ["critical", "serious", "moderate", "minor"].filter((s) => counts[s]).map((s) => `${counts[s]} ${s}`).join(" \xB7 ");
subtitle.textContent = `${issues.length} issue${issues.length === 1 ? "" : "s"} \xB7 ${summary}`;
body.innerHTML = issues.map((issue) => _issueRowHtml(issue)).join("");
body.querySelectorAll("[data-tb-issue-id]").forEach((row) => {
var _a, _b, _c;
const id = row.dataset.tbIssueId;
const issue = issues.find((i) => i.id === id);
if (!issue) return;
(_a = row.querySelector('[data-action="locate"]')) == null ? void 0 : _a.addEventListener("click", () => _locate(issue));
(_b = row.querySelector('[data-action="dismiss"]')) == null ? void 0 : _b.addEventListener("click", () => {
dismissIssue(id);
_renderBody(getIssues());
});
(_c = row.querySelector('[data-action="file"]')) == null ? void 0 : _c.addEventListener("click", () => _fileAsBug(issue));
});
}
function _issueRowHtml(issue) {
const colors = SEVERITY_COLORS[issue.severity];
const detectorLabel = DETECTOR_LABELS[issue.detector];
const desc = issue.description.length > 240 ? issue.description.slice(0, 237) + "\u2026" : issue.description;
const repeats = (issue.occurrences || 1) > 1;
const samplesHtml = repeats && issue.contextSamples && issue.contextSamples.length > 0 ? `<details style="margin-top:6px;font-size:11px">
<summary style="cursor:pointer;color:var(--tb-text-muted, #888)">
View all ${issue.occurrences} contexts
</summary>
<ol style="margin:6px 0 0 20px;padding:0;color:var(--tb-text-secondary, #aaa);line-height:1.5">
${issue.contextSamples.map((s) => `
<li>${new Date(s.timestamp).toLocaleTimeString()}${s.precedingAction ? ` \xB7 ${escapeHtml(s.precedingAction)}` : ""}</li>
`).join("")}
</ol>
</details>` : "";
return `
<div class="tb-issue-row" data-tb-issue-id="${issue.id}">
<span class="tb-sev-badge" style="background:${colors.bg};color:${colors.fg};border-color:${colors.border}">${issue.severity}</span>
<div style="flex:1;min-width:0">
<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px;flex-wrap:wrap">
<span class="tb-detector-tag">${detectorLabel}</span>
<span style="font-size:13px;color:var(--tb-text-primary, #e0e0e0);font-weight:500">${escapeHtml(issue.title)}</span>
</div>
<div style="font-size:11px;color:var(--tb-text-muted, #888);line-height:1.5;white-space:pre-wrap">${escapeHtml(desc)}</div>
${issue.helpUrl ? `<a href="${issue.helpUrl}" target="_blank" rel="noopener noreferrer" style="font-size:11px;color:var(--tb-accent, #6366F1);margin-top:4px;display:inline-block">Learn more \u2192</a>` : ""}
${samplesHtml}
</div>
<div class="tb-issue-actions">
${issue.selector ? `<button data-action="locate" title="Highlight on page">\u{1F4CD} Locate</button>` : ""}
<button data-action="file" title="File as bug ticket">File ticket</button>
<button data-action="dismiss" title="Dismiss for this session">Dismiss</button>
</div>
</div>
`;
}
function _locate(issue) {
var _a;
if (!issue.selector) return;
let el = null;
try {
el = document.querySelector(issue.selector);
} catch (e) {
el = null;
}
if (!el) return;
(_a = document.getElementById(`${PANEL_ID}-overlay`)) == null ? void 0 : _a.remove();
_isOpen = false;
el.scrollIntoView({ behavior: "smooth", block: "center" });
const htmlEl = el;
const prevOutline = htmlEl.style.outline;
const prevTransition = htmlEl.style.transition;
htmlEl.style.transition = "outline 0.2s, box-shadow 0.2s";
htmlEl.style.outline = "3px solid #6366F1";
htmlEl.style.boxShadow = "0 0 0 6px rgba(99,102,241,0.35)";
setTimeout(() => {
htmlEl.style.outline = prevOutline;
htmlEl.style.boxShadow = "";
htmlEl.style.transition = prevTransition;
}, 2400);
}
function _fileAsBug(issue) {
var _a;
const root = _root || document.getElementById("tracebug-root");
if (!root) return;
(_a = document.getElementById(`${PANEL_ID}-overlay`)) == null ? void 0 : _a.remove();
_isOpen = false;
import("./quick-bug-MX3Y3FR3.js").then((m) => {
m.showQuickBugCapture(root, {
prefilledTitle: issue.title,
prefilledDescription: _bugDescriptionFromIssue(issue)
}).catch(() => {
});
});
}
function _bugDescriptionFromIssue(issue) {
const lines = [];
lines.push(`> Detected by TraceBug auto-scanner: **${DETECTOR_LABELS[issue.detector]}** (${issue.severity})`);
lines.push("");
lines.push(issue.description);
if (issue.selector) lines.push(`
**Selector:** \`${issue.selector}\``);
if (issue.url) lines.push(`**URL:** \`${issue.url}\``);
if (issue.helpUrl) lines.push(`**Reference:** ${issue.helpUrl}`);
return lines.join("\n");
}
export {
isIssuesPanelOpen,
showIssuesPanel
};
//# sourceMappingURL=issues-panel-SNP2F6VR.js.map
{"version":3,"sources":["../src/ui/issues-panel.ts"],"sourcesContent":["// ── Issues Panel ──────────────────────────────────────────────────────────\r\n// Modal that lists scanner findings grouped by severity. Each row offers\r\n// \"Locate\" (flash the offending element on the page) and \"File ticket\"\r\n// (open the Quick Bug modal pre-filled with the issue's context).\r\n//\r\n// Styles are injected in <head> with !important so host-page CSS resets\r\n// (Tailwind preflight, Bootstrap, etc.) can't squish the layout.\r\n\r\nimport { Issue } from \"../types\";\r\nimport { dismissIssue, getIssues, scan } from \"../scanner\";\r\nimport { escapeHtml } from \"./helpers\";\r\n\r\nconst PANEL_ID = \"tracebug-issues-panel\";\r\nconst STYLE_ID = \"tracebug-issues-panel-styles\";\r\n\r\nlet _isOpen = false;\r\nlet _root: HTMLElement | null = null;\r\n\r\nconst SEVERITY_COLORS: Record<Issue[\"severity\"], { bg: string; fg: string; border: string }> = {\r\n critical: { bg: \"#7f1d1d\", fg: \"#fee2e2\", border: \"#dc2626\" },\r\n serious: { bg: \"#7c2d12\", fg: \"#fed7aa\", border: \"#ea580c\" },\r\n moderate: { bg: \"#713f12\", fg: \"#fde68a\", border: \"#ca8a04\" },\r\n minor: { bg: \"#1e3a8a\", fg: \"#bfdbfe\", border: \"#2563eb\" },\r\n};\r\n\r\nconst DETECTOR_LABELS: Record<Issue[\"detector\"], string> = {\r\n \"axe-a11y\": \"A11y\",\r\n \"broken-image\": \"Broken image\",\r\n \"mixed-content\": \"Mixed content\",\r\n \"console-error\": \"JS error\",\r\n \"slow-api\": \"Slow API\",\r\n \"failed-request\": \"Failed request\",\r\n \"frustration-rage\": \"Rage clicks\",\r\n \"frustration-dead\": \"Dead click\",\r\n \"frustration-abandon\": \"Form abandoned\",\r\n \"frustration-error-correlated\": \"Click → error\",\r\n};\r\n\r\nexport function isIssuesPanelOpen(): boolean {\r\n return _isOpen;\r\n}\r\n\r\n/**\r\n * Run a scan (or use cached results) and open the panel. The scan promise\r\n * resolves before we render so the panel never flashes empty.\r\n */\r\nexport async function showIssuesPanel(\r\n root: HTMLElement,\r\n options?: { rescan?: boolean }\r\n): Promise<void> {\r\n if (_isOpen) return;\r\n _root = root;\r\n _injectStyles();\r\n\r\n // Render shell with a loading state so the user gets immediate feedback.\r\n _open(root, { issues: [], loading: true });\r\n try {\r\n if (options?.rescan ?? true) {\r\n await scan();\r\n }\r\n } catch (err) {\r\n console.warn(\"[TraceBug] Scan failed:\", err);\r\n }\r\n _renderBody(getIssues());\r\n}\r\n\r\nfunction _injectStyles(): void {\r\n if (document.getElementById(STYLE_ID)) return;\r\n const style = document.createElement(\"style\");\r\n style.id = STYLE_ID;\r\n style.textContent = `\r\n @keyframes tracebug-issue-locate-flash {\r\n 0%, 100% { box-shadow: 0 0 0 0 rgba(99,102,241,0.0); outline: 2px solid transparent; }\r\n 30% { box-shadow: 0 0 0 6px rgba(99,102,241,0.5); outline: 2px solid #6366F1; }\r\n }\r\n #${PANEL_ID}-overlay {\r\n position: fixed !important;\r\n inset: 0 !important;\r\n z-index: 2147483647 !important;\r\n background: rgba(0,0,0,0.75) !important;\r\n backdrop-filter: blur(6px) !important;\r\n display: flex !important;\r\n align-items: center !important;\r\n justify-content: center !important;\r\n padding: 20px !important;\r\n pointer-events: auto !important;\r\n box-sizing: border-box !important;\r\n }\r\n #${PANEL_ID} {\r\n background: var(--tb-bg-secondary, #1a1a2e) !important;\r\n border: 1px solid var(--tb-border-hover, #3a3a5e) !important;\r\n border-radius: var(--tb-radius-lg, 12px) !important;\r\n width: 100% !important;\r\n max-width: 720px !important;\r\n max-height: 90vh !important;\r\n display: flex !important;\r\n flex-direction: column !important;\r\n overflow: hidden !important;\r\n font-family: var(--tb-font-family, system-ui, -apple-system, sans-serif) !important;\r\n color: var(--tb-text-primary, #e0e0e0) !important;\r\n box-sizing: border-box !important;\r\n box-shadow: 0 20px 60px rgba(0,0,0,0.5) !important;\r\n }\r\n #${PANEL_ID} *, #${PANEL_ID} *::before, #${PANEL_ID} *::after { box-sizing: border-box !important; }\r\n #${PANEL_ID} button { font-family: inherit !important; cursor: pointer !important; }\r\n #${PANEL_ID} .tb-issue-row {\r\n padding: 12px 14px;\r\n border-top: 1px solid var(--tb-border, #2a2a3e);\r\n display: flex;\r\n gap: 12px;\r\n align-items: flex-start;\r\n }\r\n #${PANEL_ID} .tb-issue-row:hover { background: var(--tb-bg-primary, #0f0f1a); }\r\n #${PANEL_ID} .tb-sev-badge {\r\n font-size: 10px;\r\n font-weight: 700;\r\n padding: 3px 7px;\r\n border-radius: 4px;\r\n letter-spacing: 0.4px;\r\n text-transform: uppercase;\r\n flex-shrink: 0;\r\n border: 1px solid;\r\n }\r\n #${PANEL_ID} .tb-detector-tag {\r\n font-size: 10px;\r\n color: var(--tb-text-muted, #888);\r\n background: var(--tb-bg-primary, #0f0f1a);\r\n border: 1px solid var(--tb-border, #2a2a3e);\r\n padding: 2px 6px;\r\n border-radius: 4px;\r\n flex-shrink: 0;\r\n }\r\n #${PANEL_ID} .tb-issue-actions {\r\n display: flex;\r\n gap: 6px;\r\n flex-shrink: 0;\r\n }\r\n #${PANEL_ID} .tb-issue-actions button {\r\n background: transparent;\r\n color: var(--tb-text-secondary, #aaa);\r\n border: 1px solid var(--tb-border, #2a2a3e);\r\n border-radius: 4px;\r\n padding: 4px 10px;\r\n font-size: 11px;\r\n transition: all 0.15s;\r\n }\r\n #${PANEL_ID} .tb-issue-actions button:hover {\r\n background: var(--tb-btn-hover, #ffffff15);\r\n color: var(--tb-text-primary, #fff);\r\n }\r\n #${PANEL_ID} .tb-issue-actions button[data-action=\"file\"] {\r\n background: var(--tb-accent, #6366F1);\r\n color: #fff;\r\n border-color: transparent;\r\n }\r\n #${PANEL_ID} .tb-issue-actions button[data-action=\"file\"]:hover { opacity: 0.9; }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\nfunction _open(root: HTMLElement, state: { issues: Issue[]; loading: boolean }): void {\r\n _isOpen = true;\r\n // Remove any prior overlay (defensive — should not happen).\r\n document.getElementById(`${PANEL_ID}-overlay`)?.remove();\r\n\r\n const overlay = document.createElement(\"div\");\r\n overlay.id = `${PANEL_ID}-overlay`;\r\n overlay.dataset.tracebug = \"issues-panel-overlay\";\r\n overlay.setAttribute(\"role\", \"dialog\");\r\n overlay.setAttribute(\"aria-modal\", \"true\");\r\n overlay.setAttribute(\"aria-label\", \"Page issues\");\r\n\r\n const panel = document.createElement(\"div\");\r\n panel.id = PANEL_ID;\r\n panel.dataset.tracebug = \"issues-panel\";\r\n\r\n panel.innerHTML = _shellHtml(state);\r\n\r\n overlay.appendChild(panel);\r\n root.appendChild(overlay);\r\n\r\n _wireShellHandlers(overlay, panel);\r\n}\r\n\r\nfunction _shellHtml(state: { issues: Issue[]; loading: boolean }): string {\r\n return `\r\n <div data-tb-issues=\"header\" style=\"padding:16px 18px;display:flex;align-items:center;gap:12px;border-bottom:1px solid var(--tb-border, #2a2a3e)\">\r\n <span style=\"font-size:20px\">🔍</span>\r\n <div style=\"flex:1;min-width:0\">\r\n <div style=\"font-size:16px;font-weight:700;color:var(--tb-text-primary, #fff)\">Page Issues</div>\r\n <div data-tb-issues=\"subtitle\" style=\"font-size:11px;color:var(--tb-text-muted, #888);margin-top:2px\">${state.loading ? \"Scanning…\" : \"No scan yet\"}</div>\r\n </div>\r\n <button data-tb-issues=\"rescan\" style=\"background:transparent;color:var(--tb-text-secondary, #aaa);border:1px solid var(--tb-border, #2a2a3e);border-radius:6px;padding:6px 12px;font-size:12px;display:flex;align-items:center;gap:6px\">\r\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 12a9 9 0 1 1-3-6.7\"/><polyline points=\"21 3 21 9 15 9\"/></svg>\r\n Rescan\r\n </button>\r\n <button data-tb-issues=\"close\" aria-label=\"Close\" style=\"background:none;border:none;color:var(--tb-text-muted, #888);font-size:20px;padding:4px 8px;border-radius:6px\">&times;</button>\r\n </div>\r\n <div data-tb-issues=\"body\" style=\"flex:1;overflow-y:auto;min-height:200px\">\r\n ${state.loading ? _loadingHtml() : \"\"}\r\n </div>\r\n `;\r\n}\r\n\r\nfunction _loadingHtml(): string {\r\n return `\r\n <div style=\"padding:48px 20px;text-align:center;color:var(--tb-text-muted, #888);font-size:13px\">\r\n <div style=\"font-size:24px;margin-bottom:8px\">🔍</div>\r\n Scanning page for issues…\r\n <div style=\"font-size:11px;margin-top:6px;opacity:0.7\">Loading axe-core, checking images, network calls, JS errors…</div>\r\n </div>\r\n `;\r\n}\r\n\r\nfunction _wireShellHandlers(overlay: HTMLElement, panel: HTMLElement): void {\r\n const close = () => {\r\n _isOpen = false;\r\n overlay.remove();\r\n document.removeEventListener(\"keydown\", escHandler);\r\n };\r\n panel.querySelector('[data-tb-issues=\"close\"]')!.addEventListener(\"click\", close);\r\n overlay.addEventListener(\"click\", (e) => { if (e.target === overlay) close(); });\r\n\r\n panel.querySelector('[data-tb-issues=\"rescan\"]')!.addEventListener(\"click\", async () => {\r\n const body = panel.querySelector('[data-tb-issues=\"body\"]') as HTMLElement;\r\n body.innerHTML = _loadingHtml();\r\n const sub = panel.querySelector('[data-tb-issues=\"subtitle\"]') as HTMLElement;\r\n sub.textContent = \"Scanning…\";\r\n await scan();\r\n _renderBody(getIssues());\r\n });\r\n\r\n const escHandler = (e: KeyboardEvent) => { if (e.key === \"Escape\") close(); };\r\n document.addEventListener(\"keydown\", escHandler);\r\n}\r\n\r\nfunction _renderBody(issues: Issue[]): void {\r\n const panel = document.getElementById(PANEL_ID);\r\n if (!panel) return;\r\n const body = panel.querySelector('[data-tb-issues=\"body\"]') as HTMLElement;\r\n const subtitle = panel.querySelector('[data-tb-issues=\"subtitle\"]') as HTMLElement;\r\n\r\n if (issues.length === 0) {\r\n subtitle.textContent = \"No issues found\";\r\n body.innerHTML = `\r\n <div style=\"padding:48px 20px;text-align:center;color:var(--tb-text-muted, #888);font-size:13px\">\r\n <div style=\"font-size:32px;margin-bottom:8px\">✓</div>\r\n Clean scan — no issues detected on this page.\r\n <div style=\"font-size:11px;margin-top:6px;opacity:0.7\">Includes a11y · broken images · mixed content · JS errors · failed/slow API calls</div>\r\n </div>\r\n `;\r\n return;\r\n }\r\n\r\n // Severity counts in subtitle\r\n const counts: Record<string, number> = {};\r\n for (const i of issues) counts[i.severity] = (counts[i.severity] || 0) + 1;\r\n const summary = [\"critical\", \"serious\", \"moderate\", \"minor\"]\r\n .filter(s => counts[s])\r\n .map(s => `${counts[s]} ${s}`)\r\n .join(\" · \");\r\n subtitle.textContent = `${issues.length} issue${issues.length === 1 ? \"\" : \"s\"} · ${summary}`;\r\n\r\n body.innerHTML = issues.map(issue => _issueRowHtml(issue)).join(\"\");\r\n\r\n // Wire row actions\r\n body.querySelectorAll<HTMLElement>(\"[data-tb-issue-id]\").forEach(row => {\r\n const id = row.dataset.tbIssueId!;\r\n const issue = issues.find(i => i.id === id);\r\n if (!issue) return;\r\n\r\n row.querySelector('[data-action=\"locate\"]')?.addEventListener(\"click\", () => _locate(issue));\r\n row.querySelector('[data-action=\"dismiss\"]')?.addEventListener(\"click\", () => {\r\n dismissIssue(id);\r\n _renderBody(getIssues());\r\n });\r\n row.querySelector('[data-action=\"file\"]')?.addEventListener(\"click\", () => _fileAsBug(issue));\r\n });\r\n}\r\n\r\nfunction _issueRowHtml(issue: Issue): string {\r\n const colors = SEVERITY_COLORS[issue.severity];\r\n const detectorLabel = DETECTOR_LABELS[issue.detector];\r\n const desc = issue.description.length > 240\r\n ? issue.description.slice(0, 237) + \"…\"\r\n : issue.description;\r\n\r\n // Fingerprint dedup: when occurrences > 1 show a collapsible <details>\r\n // listing each occurrence with its preceding action.\r\n const repeats = (issue.occurrences || 1) > 1;\r\n const samplesHtml = repeats && issue.contextSamples && issue.contextSamples.length > 0\r\n ? `<details style=\"margin-top:6px;font-size:11px\">\r\n <summary style=\"cursor:pointer;color:var(--tb-text-muted, #888)\">\r\n View all ${issue.occurrences} contexts\r\n </summary>\r\n <ol style=\"margin:6px 0 0 20px;padding:0;color:var(--tb-text-secondary, #aaa);line-height:1.5\">\r\n ${issue.contextSamples.map(s => `\r\n <li>${new Date(s.timestamp).toLocaleTimeString()}${s.precedingAction ? ` · ${escapeHtml(s.precedingAction)}` : \"\"}</li>\r\n `).join(\"\")}\r\n </ol>\r\n </details>`\r\n : \"\";\r\n\r\n return `\r\n <div class=\"tb-issue-row\" data-tb-issue-id=\"${issue.id}\">\r\n <span class=\"tb-sev-badge\" style=\"background:${colors.bg};color:${colors.fg};border-color:${colors.border}\">${issue.severity}</span>\r\n <div style=\"flex:1;min-width:0\">\r\n <div style=\"display:flex;align-items:center;gap:6px;margin-bottom:4px;flex-wrap:wrap\">\r\n <span class=\"tb-detector-tag\">${detectorLabel}</span>\r\n <span style=\"font-size:13px;color:var(--tb-text-primary, #e0e0e0);font-weight:500\">${escapeHtml(issue.title)}</span>\r\n </div>\r\n <div style=\"font-size:11px;color:var(--tb-text-muted, #888);line-height:1.5;white-space:pre-wrap\">${escapeHtml(desc)}</div>\r\n ${issue.helpUrl ? `<a href=\"${issue.helpUrl}\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"font-size:11px;color:var(--tb-accent, #6366F1);margin-top:4px;display:inline-block\">Learn more →</a>` : \"\"}\r\n ${samplesHtml}\r\n </div>\r\n <div class=\"tb-issue-actions\">\r\n ${issue.selector ? `<button data-action=\"locate\" title=\"Highlight on page\">📍 Locate</button>` : \"\"}\r\n <button data-action=\"file\" title=\"File as bug ticket\">File ticket</button>\r\n <button data-action=\"dismiss\" title=\"Dismiss for this session\">Dismiss</button>\r\n </div>\r\n </div>\r\n `;\r\n}\r\n\r\n/**\r\n * Briefly outline the offending element. Closes the panel so the page is\r\n * visible. Restores after 2.4s — non-destructive flash.\r\n */\r\nfunction _locate(issue: Issue): void {\r\n if (!issue.selector) return;\r\n let el: Element | null = null;\r\n try {\r\n el = document.querySelector(issue.selector);\r\n } catch {\r\n el = null;\r\n }\r\n if (!el) return;\r\n\r\n // Close the panel so the user can see the page.\r\n document.getElementById(`${PANEL_ID}-overlay`)?.remove();\r\n _isOpen = false;\r\n\r\n el.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\r\n const htmlEl = el as HTMLElement;\r\n const prevOutline = htmlEl.style.outline;\r\n const prevTransition = htmlEl.style.transition;\r\n htmlEl.style.transition = \"outline 0.2s, box-shadow 0.2s\";\r\n htmlEl.style.outline = \"3px solid #6366F1\";\r\n htmlEl.style.boxShadow = \"0 0 0 6px rgba(99,102,241,0.35)\";\r\n setTimeout(() => {\r\n htmlEl.style.outline = prevOutline;\r\n htmlEl.style.boxShadow = \"\";\r\n htmlEl.style.transition = prevTransition;\r\n }, 2400);\r\n}\r\n\r\n/**\r\n * Pre-fill the Quick Bug modal with the issue's title + description as the\r\n * starting point for a ticket. Reuses the existing ticket-export pipeline.\r\n */\r\nfunction _fileAsBug(issue: Issue): void {\r\n const root = _root || document.getElementById(\"tracebug-root\");\r\n if (!root) return;\r\n // Close the issues panel.\r\n document.getElementById(`${PANEL_ID}-overlay`)?.remove();\r\n _isOpen = false;\r\n\r\n import(\"./quick-bug\").then(m => {\r\n m.showQuickBugCapture(root, {\r\n prefilledTitle: issue.title,\r\n prefilledDescription: _bugDescriptionFromIssue(issue),\r\n }).catch(() => {});\r\n });\r\n}\r\n\r\nfunction _bugDescriptionFromIssue(issue: Issue): string {\r\n const lines: string[] = [];\r\n lines.push(`> Detected by TraceBug auto-scanner: **${DETECTOR_LABELS[issue.detector]}** (${issue.severity})`);\r\n lines.push(\"\");\r\n lines.push(issue.description);\r\n if (issue.selector) lines.push(`\\n**Selector:** \\`${issue.selector}\\``);\r\n if (issue.url) lines.push(`**URL:** \\`${issue.url}\\``);\r\n if (issue.helpUrl) lines.push(`**Reference:** ${issue.helpUrl}`);\r\n return lines.join(\"\\n\");\r\n}\r\n"],"mappings":"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","names":[]}
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
var _chunkIXAICYFUcjs = require('./chunk-IXAICYFU.cjs');
require('./chunk-3HHUTYZ5.cjs');
require('./chunk-4SLN5LQD.cjs');
exports.isQuickBugOpen = _chunkIXAICYFUcjs.isQuickBugOpen; exports.refreshQuickBugCapture = _chunkIXAICYFUcjs.refreshQuickBugCapture; exports.setCloudEndpoint = _chunkIXAICYFUcjs.setCloudEndpoint; exports.setGithubRepo = _chunkIXAICYFUcjs.setGithubRepo; exports.showQuickBugCapture = _chunkIXAICYFUcjs.showQuickBugCapture;
//# sourceMappingURL=quick-bug-LFLO2PSW.cjs.map
{"version":3,"sources":["d:\\Project\\TraceBug-ai\\dist\\quick-bug-LFLO2PSW.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACF,kUAAC","file":"D:\\Project\\TraceBug-ai\\dist\\quick-bug-LFLO2PSW.cjs"}
import {
isQuickBugOpen,
refreshQuickBugCapture,
setCloudEndpoint,
setGithubRepo,
showQuickBugCapture
} from "./chunk-4RKCUHIO.js";
import "./chunk-L3Q7Y6QP.js";
import "./chunk-LQO44M5L.js";
export {
isQuickBugOpen,
refreshQuickBugCapture,
setCloudEndpoint,
setGithubRepo,
showQuickBugCapture
};
//# sourceMappingURL=quick-bug-MX3Y3FR3.js.map
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
+1
-1

@@ -1,1 +0,1 @@

{"version":3,"sources":["d:\\Project\\TraceBug-ai\\dist\\index.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACA;AACA,IAAI,uBAAuB,EAAE,sBAAsB;AACnD,SAAS,oBAAoB,CAAC,QAAQ,EAAE;AACxC,EAAE,MAAM,IAAI,EAAE,SAAS,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;AACzD,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,sBAAsB;AACzC,EAAE,IAAI;AACN,IAAI,MAAM,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC;AAC5B,IAAI,MAAM,QAAQ,EAAE,GAAG,CAAC,SAAS,IAAI,YAAY,GAAG,GAAG,CAAC,SAAS,IAAI,YAAY,GAAG,GAAG,CAAC,SAAS,IAAI,OAAO;AAC5G,IAAI,GAAG,CAAC,GAAG,CAAC,SAAS,IAAI,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,IAAI,QAAQ,GAAG,OAAO,CAAC,EAAE;AAC7E,MAAM,GAAG,CAAC,OAAO,QAAQ,IAAI,WAAW,EAAE;AAC1C,QAAQ,OAAO,CAAC,IAAI,CAAC,CAAC,2EAA2E,EAAE,sBAAsB,CAAC,CAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA;AACA;AACA;AACA,IAAA;AACA;AACA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,YAAA;AACA,cAAA;AACA,YAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,gBAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,gBAAA;AACA,gBAAA;AACA,cAAA;AACA,cAAA;AACA,gBAAA;AACA,gBAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"D:\\Project\\TraceBug-ai\\dist\\index.cjs","sourcesContent":[null]}
{"version":3,"sources":["d:\\Project\\TraceBug-ai\\dist\\index.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACA;AACA,IAAI,uBAAuB,EAAE,sBAAsB;AACnD,SAAS,oBAAoB,CAAC,QAAQ,EAAE;AACxC,EAAE,MAAM,IAAI,EAAE,SAAS,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;AACzD,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,sBAAsB;AACzC,EAAE,IAAI;AACN,IAAI,MAAM,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC;AAC5B,IAAI,MAAM,QAAQ,EAAE,GAAG,CAAC,SAAS,IAAI,YAAY,GAAG,GAAG,CAAC,SAAS,IAAI,YAAY,GAAG,GAAG,CAAC,SAAS,IAAI,OAAO;AAC5G,IAAI,GAAG,CAAC,GAAG,CAAC,SAAS,IAAI,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,IAAI,QAAQ,GAAG,OAAO,CAAC,EAAE;AAC7E,MAAM,GAAG,CAAC,OAAO,QAAQ,IAAI,WAAW,EAAE;AAC1C,QAAQ,OAAO,CAAC,IAAI,CAAC,CAAC,2EAA2E,EAAE,sBAAsB,CAAC,CAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA;AACA;AACA;AACA,IAAA;AACA;AACA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,YAAA;AACA,cAAA;AACA,YAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,gBAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,gBAAA;AACA,gBAAA;AACA,cAAA;AACA,cAAA;AACA,gBAAA;AACA,gBAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"D:\\Project\\TraceBug-ai\\dist\\index.cjs","sourcesContent":[null]}

@@ -17,4 +17,5 @@ interface TraceBugConfig {

* - string[] → Custom list of allowed hostnames, e.g. ["localhost", "staging.myapp.com"]
* - true / false → Aliases for "all" / "off" — the values people reach for first
*/
enabled?: "auto" | "development" | "staging" | "all" | "off" | string[];
enabled?: "auto" | "development" | "staging" | "all" | "off" | string[] | boolean;
/**

@@ -56,6 +57,9 @@ * Color theme. Default: "dark"

/**
* Console log capture level. Default: "errors"
* - "errors" → Only console.error (backward compatible)
* - "warnings" → console.error + console.warn
* - "all" → console.error + console.warn + console.log (capped at last 50)
* Console log capture level. Default: "all" — warn/info often hold the
* state-transition breadcrumbs that explain an error, so the repro
* timeline and Console tab get DevTools parity out of the box. Drop to
* "warnings" or "errors" for chatty or PII-heavy apps.
* - "errors" → Only console.error
* - "warnings" → console.error + console.warn + console.info
* - "all" → adds console.log (default; each non-error level capped at 50/session)
* - "none" → No console interception

@@ -65,2 +69,11 @@ */

/**
* App-specific redaction rules, applied at capture time on top of the
* built-in token/secret masking. Use for PII the built-ins can't know
* about — customer emails, account numbers, internal IDs.
*
* Example: TraceBug.init({ projectId: "my-app",
* redact: { fields: ["email", "customer_id"], patterns: ["\\b\\d{10}\\b"] } })
*/
redact?: RedactRules;
/**
* Capture a snapshot of localStorage + sessionStorage in each report.

@@ -87,3 +100,16 @@ * Default: true. Values under sensitive-looking keys (token, secret, auth,

}
type EventType = "click" | "input" | "select_change" | "form_submit" | "route_change" | "api_request" | "error" | "console_error" | "console_warn" | "console_log" | "unhandled_rejection"
/**
* User-declared redaction rules — see `TraceBugConfig.redact`.
* Enforced by sanitize/custom-redaction.ts at capture time.
*/
interface RedactRules {
/** Sensitive field names, matched case-insensitively as substrings against
* form/input names, storage keys, URL query params, and JSON/urlencoded
* keys inside captured text ("email" also covers "customer_email"). */
fields?: string[];
/** Custom regexes (string or RegExp) masked wherever they appear in
* captured text. Strings compile case-insensitive; invalid ones are skipped. */
patterns?: (string | RegExp)[];
}
type EventType = "click" | "input" | "select_change" | "form_submit" | "route_change" | "api_request" | "error" | "console_error" | "console_warn" | "console_info" | "console_log" | "unhandled_rejection"
/** Developer breadcrumb (`TraceBug.mark()`) — manually-placed semantic checkpoint. */

@@ -705,2 +731,6 @@ | "mark";

/** Install rules from config (call with undefined to clear). Invalid custom
* patterns are skipped silently — a typo in one rule must not disable capture. */
declare function setRedactRules(rules: RedactRules | undefined): void;
/**

@@ -821,2 +851,39 @@ * Generate a prefilled GitHub Issue URL that opens in a new tab with title +

interface HtmlReplayOptions {
/** Include the video blob in the bundle (can be 50+ MB). Default: true if present. */
includeVideo?: boolean;
/** Override the default filename pattern. */
filename?: string;
/** Additional description (markdown) to prepend to the report panel. */
descriptionOverride?: string;
/** "owner/repo" — when set, the exported viewer gets an "Open GitHub
* issue" button with a prefilled URL. The copy-markdown button is
* included regardless. */
githubRepo?: string;
}
interface ExportedReplay {
filename: string;
blob: Blob;
url: string;
sizeBytes: number;
}
interface ZipEntry {
name: string;
data: Uint8Array;
/** Timestamp stamped into the archive (default: now). */
mtime?: number;
}
/**
* Build a ZIP archive Blob from the given entries. Standard zip32 layout:
* [local header + data]* → central directory → end-of-central-directory.
*/
declare function buildZipBlob(entries: ZipEntry[]): Promise<Blob>;
/**
* Export the session replay as a GitHub-attachable .zip containing the
* self-contained .html. Triggers a browser download, mirroring
* `exportSessionAsHtml`.
*/
declare function exportSessionAsZip(session: StoredSession, report: BugReport, options?: HtmlReplayOptions): Promise<ExportedReplay>;
type AIProvider = "anthropic" | "openai" | "ollama";

@@ -915,2 +982,20 @@ interface AIConfig {

interface RedactionSummary {
/** Sensitive query params masked in captured request URLs. */
urlParams: number;
/** Password/secret form + input field values masked at capture. */
formFields: number;
/** localStorage / sessionStorage / cookie values masked at capture. */
storageKeys: number;
/** Token shapes (JWT, Bearer, sk-, cloud keys…) masked in console output,
* error messages, and network response snippets. */
tokens: number;
total: number;
}
declare function summarizeRedactions(report: BugReport): RedactionSummary;
/** "4 sensitive values auto-masked (2 tokens, 1 URL param, 1 form field)" —
* null when nothing was masked, so callers can omit the line entirely
* rather than claim a clean bill of health the regexes can't promise. */
declare function formatRedactionSummary(s: RedactionSummary): string | null;
declare class TraceBugSDK {

@@ -1301,2 +1386,2 @@ private config;

export { type AIAnalysisResult, type AIConfig, type AIPromptOptions, type AIProvider, ANTHROPIC_MODEL_CHOICES, type Annotation, type AnnotationIntent, type BugReport, type ClickedElementSummary, type CreateIssueResult, DEFAULT_MODELS, type DrawRegion, type ElementAnnotation, type EnvironmentInfo, FREE_LIMITS, type GitHubConfig, type HarExportResult, type HarLog, type IntegrationsConfig, type Issue, type IssueDetector, type IssueSeverity, type JiraTicket, type LinearConfig, type NetworkErrorEntry, type NetworkFailure, PROVIDER_LABELS, type Plan, type RootCauseHint, type ScanResult, type ScreenshotData, type SlackConfig, type StoredSession, TRACKER_LABELS, type TraceBugConfig, type TraceBugEvent, type TraceBugPlugin, type TraceBugUser, type TrackerProvider, type UIAnnotationReport, type VideoComment, type VideoRecording, type VoiceTranscript, buildAnalysisPrompt, buildHar, buildReport, buildTimeline, captureEnvironment, captureRegionScreenshot, captureRollingBuffer, captureScreenshot, clearAIConfig, clearAllSessions, clearIntegrationsConfig, clearIssues, clearVideoRecording, clearVoiceTranscripts, createGitHubIssue, createLinearIssue, createTrackerIssue, TraceBug as default, deleteSession, dismissIssue, downloadAllScreenshots, downloadPdfAsHtml, downloadVideoRecording, exportSessionAsHar, extractClickedElement, formatRootCauseLine, formatTimelineText, generateAIPrompt, generateBugTitle, generateFlowSummary, generateGitHubIssue, generateGitHubIssueUrl, generateJiraTicket, generateMcpPrompt, generatePdfReport, generateReproSteps, generateRootCauseHint, generateSessionSteps, generateSmartSummary, getAIConfig, getAllSessions, getCaptureCount, getIntegrationsConfig, getIssueById, getIssueCountsByDetector, getIssueCountsBySeverity, getIssues, getLastVideoRecording, getNetworkFailures, getPlan, getScreenshots, getVoiceTranscripts, hasAIKey, hasIntegration, hydratePlan, isPremium, isRollingMode, isVideoRecording, isVideoSupported as isVideoSupportedFn, isVoiceRecording, isVoiceSupported, openGitHubIssue, openInChatGPT, openInClaude, runLLMAnalysis, scan, sendSlackMessage, setAIConfig, setIntegrationsConfig, setPlan, startVideoRecording, startVoiceRecording, stopVideoRecording, stopVoiceRecording, undismissIssue };
export { type AIAnalysisResult, type AIConfig, type AIPromptOptions, type AIProvider, ANTHROPIC_MODEL_CHOICES, type Annotation, type AnnotationIntent, type BugReport, type ClickedElementSummary, type CreateIssueResult, DEFAULT_MODELS, type DrawRegion, type ElementAnnotation, type EnvironmentInfo, FREE_LIMITS, type GitHubConfig, type HarExportResult, type HarLog, type IntegrationsConfig, type Issue, type IssueDetector, type IssueSeverity, type JiraTicket, type LinearConfig, type NetworkErrorEntry, type NetworkFailure, PROVIDER_LABELS, type Plan, type RedactRules, type RedactionSummary, type RootCauseHint, type ScanResult, type ScreenshotData, type SlackConfig, type StoredSession, TRACKER_LABELS, type TraceBugConfig, type TraceBugEvent, type TraceBugPlugin, type TraceBugUser, type TrackerProvider, type UIAnnotationReport, type VideoComment, type VideoRecording, type VoiceTranscript, buildAnalysisPrompt, buildHar, buildReport, buildTimeline, buildZipBlob, captureEnvironment, captureRegionScreenshot, captureRollingBuffer, captureScreenshot, clearAIConfig, clearAllSessions, clearIntegrationsConfig, clearIssues, clearVideoRecording, clearVoiceTranscripts, createGitHubIssue, createLinearIssue, createTrackerIssue, TraceBug as default, deleteSession, dismissIssue, downloadAllScreenshots, downloadPdfAsHtml, downloadVideoRecording, exportSessionAsHar, exportSessionAsZip, extractClickedElement, formatRedactionSummary, formatRootCauseLine, formatTimelineText, generateAIPrompt, generateBugTitle, generateFlowSummary, generateGitHubIssue, generateGitHubIssueUrl, generateJiraTicket, generateMcpPrompt, generatePdfReport, generateReproSteps, generateRootCauseHint, generateSessionSteps, generateSmartSummary, getAIConfig, getAllSessions, getCaptureCount, getIntegrationsConfig, getIssueById, getIssueCountsByDetector, getIssueCountsBySeverity, getIssues, getLastVideoRecording, getNetworkFailures, getPlan, getScreenshots, getVoiceTranscripts, hasAIKey, hasIntegration, hydratePlan, isPremium, isRollingMode, isVideoRecording, isVideoSupported as isVideoSupportedFn, isVoiceRecording, isVoiceSupported, openGitHubIssue, openInChatGPT, openInClaude, runLLMAnalysis, scan, sendSlackMessage, setAIConfig, setIntegrationsConfig, setPlan, setRedactRules, startVideoRecording, startVoiceRecording, stopVideoRecording, stopVoiceRecording, summarizeRedactions, undismissIssue };

@@ -17,4 +17,5 @@ interface TraceBugConfig {

* - string[] → Custom list of allowed hostnames, e.g. ["localhost", "staging.myapp.com"]
* - true / false → Aliases for "all" / "off" — the values people reach for first
*/
enabled?: "auto" | "development" | "staging" | "all" | "off" | string[];
enabled?: "auto" | "development" | "staging" | "all" | "off" | string[] | boolean;
/**

@@ -56,6 +57,9 @@ * Color theme. Default: "dark"

/**
* Console log capture level. Default: "errors"
* - "errors" → Only console.error (backward compatible)
* - "warnings" → console.error + console.warn
* - "all" → console.error + console.warn + console.log (capped at last 50)
* Console log capture level. Default: "all" — warn/info often hold the
* state-transition breadcrumbs that explain an error, so the repro
* timeline and Console tab get DevTools parity out of the box. Drop to
* "warnings" or "errors" for chatty or PII-heavy apps.
* - "errors" → Only console.error
* - "warnings" → console.error + console.warn + console.info
* - "all" → adds console.log (default; each non-error level capped at 50/session)
* - "none" → No console interception

@@ -65,2 +69,11 @@ */

/**
* App-specific redaction rules, applied at capture time on top of the
* built-in token/secret masking. Use for PII the built-ins can't know
* about — customer emails, account numbers, internal IDs.
*
* Example: TraceBug.init({ projectId: "my-app",
* redact: { fields: ["email", "customer_id"], patterns: ["\\b\\d{10}\\b"] } })
*/
redact?: RedactRules;
/**
* Capture a snapshot of localStorage + sessionStorage in each report.

@@ -87,3 +100,16 @@ * Default: true. Values under sensitive-looking keys (token, secret, auth,

}
type EventType = "click" | "input" | "select_change" | "form_submit" | "route_change" | "api_request" | "error" | "console_error" | "console_warn" | "console_log" | "unhandled_rejection"
/**
* User-declared redaction rules — see `TraceBugConfig.redact`.
* Enforced by sanitize/custom-redaction.ts at capture time.
*/
interface RedactRules {
/** Sensitive field names, matched case-insensitively as substrings against
* form/input names, storage keys, URL query params, and JSON/urlencoded
* keys inside captured text ("email" also covers "customer_email"). */
fields?: string[];
/** Custom regexes (string or RegExp) masked wherever they appear in
* captured text. Strings compile case-insensitive; invalid ones are skipped. */
patterns?: (string | RegExp)[];
}
type EventType = "click" | "input" | "select_change" | "form_submit" | "route_change" | "api_request" | "error" | "console_error" | "console_warn" | "console_info" | "console_log" | "unhandled_rejection"
/** Developer breadcrumb (`TraceBug.mark()`) — manually-placed semantic checkpoint. */

@@ -705,2 +731,6 @@ | "mark";

/** Install rules from config (call with undefined to clear). Invalid custom
* patterns are skipped silently — a typo in one rule must not disable capture. */
declare function setRedactRules(rules: RedactRules | undefined): void;
/**

@@ -821,2 +851,39 @@ * Generate a prefilled GitHub Issue URL that opens in a new tab with title +

interface HtmlReplayOptions {
/** Include the video blob in the bundle (can be 50+ MB). Default: true if present. */
includeVideo?: boolean;
/** Override the default filename pattern. */
filename?: string;
/** Additional description (markdown) to prepend to the report panel. */
descriptionOverride?: string;
/** "owner/repo" — when set, the exported viewer gets an "Open GitHub
* issue" button with a prefilled URL. The copy-markdown button is
* included regardless. */
githubRepo?: string;
}
interface ExportedReplay {
filename: string;
blob: Blob;
url: string;
sizeBytes: number;
}
interface ZipEntry {
name: string;
data: Uint8Array;
/** Timestamp stamped into the archive (default: now). */
mtime?: number;
}
/**
* Build a ZIP archive Blob from the given entries. Standard zip32 layout:
* [local header + data]* → central directory → end-of-central-directory.
*/
declare function buildZipBlob(entries: ZipEntry[]): Promise<Blob>;
/**
* Export the session replay as a GitHub-attachable .zip containing the
* self-contained .html. Triggers a browser download, mirroring
* `exportSessionAsHtml`.
*/
declare function exportSessionAsZip(session: StoredSession, report: BugReport, options?: HtmlReplayOptions): Promise<ExportedReplay>;
type AIProvider = "anthropic" | "openai" | "ollama";

@@ -915,2 +982,20 @@ interface AIConfig {

interface RedactionSummary {
/** Sensitive query params masked in captured request URLs. */
urlParams: number;
/** Password/secret form + input field values masked at capture. */
formFields: number;
/** localStorage / sessionStorage / cookie values masked at capture. */
storageKeys: number;
/** Token shapes (JWT, Bearer, sk-, cloud keys…) masked in console output,
* error messages, and network response snippets. */
tokens: number;
total: number;
}
declare function summarizeRedactions(report: BugReport): RedactionSummary;
/** "4 sensitive values auto-masked (2 tokens, 1 URL param, 1 form field)" —
* null when nothing was masked, so callers can omit the line entirely
* rather than claim a clean bill of health the regexes can't promise. */
declare function formatRedactionSummary(s: RedactionSummary): string | null;
declare class TraceBugSDK {

@@ -1301,2 +1386,2 @@ private config;

export { type AIAnalysisResult, type AIConfig, type AIPromptOptions, type AIProvider, ANTHROPIC_MODEL_CHOICES, type Annotation, type AnnotationIntent, type BugReport, type ClickedElementSummary, type CreateIssueResult, DEFAULT_MODELS, type DrawRegion, type ElementAnnotation, type EnvironmentInfo, FREE_LIMITS, type GitHubConfig, type HarExportResult, type HarLog, type IntegrationsConfig, type Issue, type IssueDetector, type IssueSeverity, type JiraTicket, type LinearConfig, type NetworkErrorEntry, type NetworkFailure, PROVIDER_LABELS, type Plan, type RootCauseHint, type ScanResult, type ScreenshotData, type SlackConfig, type StoredSession, TRACKER_LABELS, type TraceBugConfig, type TraceBugEvent, type TraceBugPlugin, type TraceBugUser, type TrackerProvider, type UIAnnotationReport, type VideoComment, type VideoRecording, type VoiceTranscript, buildAnalysisPrompt, buildHar, buildReport, buildTimeline, captureEnvironment, captureRegionScreenshot, captureRollingBuffer, captureScreenshot, clearAIConfig, clearAllSessions, clearIntegrationsConfig, clearIssues, clearVideoRecording, clearVoiceTranscripts, createGitHubIssue, createLinearIssue, createTrackerIssue, TraceBug as default, deleteSession, dismissIssue, downloadAllScreenshots, downloadPdfAsHtml, downloadVideoRecording, exportSessionAsHar, extractClickedElement, formatRootCauseLine, formatTimelineText, generateAIPrompt, generateBugTitle, generateFlowSummary, generateGitHubIssue, generateGitHubIssueUrl, generateJiraTicket, generateMcpPrompt, generatePdfReport, generateReproSteps, generateRootCauseHint, generateSessionSteps, generateSmartSummary, getAIConfig, getAllSessions, getCaptureCount, getIntegrationsConfig, getIssueById, getIssueCountsByDetector, getIssueCountsBySeverity, getIssues, getLastVideoRecording, getNetworkFailures, getPlan, getScreenshots, getVoiceTranscripts, hasAIKey, hasIntegration, hydratePlan, isPremium, isRollingMode, isVideoRecording, isVideoSupported as isVideoSupportedFn, isVoiceRecording, isVoiceSupported, openGitHubIssue, openInChatGPT, openInClaude, runLLMAnalysis, scan, sendSlackMessage, setAIConfig, setIntegrationsConfig, setPlan, startVideoRecording, startVoiceRecording, stopVideoRecording, stopVoiceRecording, undismissIssue };
export { type AIAnalysisResult, type AIConfig, type AIPromptOptions, type AIProvider, ANTHROPIC_MODEL_CHOICES, type Annotation, type AnnotationIntent, type BugReport, type ClickedElementSummary, type CreateIssueResult, DEFAULT_MODELS, type DrawRegion, type ElementAnnotation, type EnvironmentInfo, FREE_LIMITS, type GitHubConfig, type HarExportResult, type HarLog, type IntegrationsConfig, type Issue, type IssueDetector, type IssueSeverity, type JiraTicket, type LinearConfig, type NetworkErrorEntry, type NetworkFailure, PROVIDER_LABELS, type Plan, type RedactRules, type RedactionSummary, type RootCauseHint, type ScanResult, type ScreenshotData, type SlackConfig, type StoredSession, TRACKER_LABELS, type TraceBugConfig, type TraceBugEvent, type TraceBugPlugin, type TraceBugUser, type TrackerProvider, type UIAnnotationReport, type VideoComment, type VideoRecording, type VoiceTranscript, buildAnalysisPrompt, buildHar, buildReport, buildTimeline, buildZipBlob, captureEnvironment, captureRegionScreenshot, captureRollingBuffer, captureScreenshot, clearAIConfig, clearAllSessions, clearIntegrationsConfig, clearIssues, clearVideoRecording, clearVoiceTranscripts, createGitHubIssue, createLinearIssue, createTrackerIssue, TraceBug as default, deleteSession, dismissIssue, downloadAllScreenshots, downloadPdfAsHtml, downloadVideoRecording, exportSessionAsHar, exportSessionAsZip, extractClickedElement, formatRedactionSummary, formatRootCauseLine, formatTimelineText, generateAIPrompt, generateBugTitle, generateFlowSummary, generateGitHubIssue, generateGitHubIssueUrl, generateJiraTicket, generateMcpPrompt, generatePdfReport, generateReproSteps, generateRootCauseHint, generateSessionSteps, generateSmartSummary, getAIConfig, getAllSessions, getCaptureCount, getIntegrationsConfig, getIssueById, getIssueCountsByDetector, getIssueCountsBySeverity, getIssues, getLastVideoRecording, getNetworkFailures, getPlan, getScreenshots, getVoiceTranscripts, hasAIKey, hasIntegration, hydratePlan, isPremium, isRollingMode, isVideoRecording, isVideoSupported as isVideoSupportedFn, isVoiceRecording, isVoiceSupported, openGitHubIssue, openInChatGPT, openInClaude, runLLMAnalysis, scan, sendSlackMessage, setAIConfig, setIntegrationsConfig, setPlan, setRedactRules, startVideoRecording, startVoiceRecording, stopVideoRecording, stopVoiceRecording, summarizeRedactions, undismissIssue };
{
"name": "tracebug-sdk",
"version": "1.7.0",
"version": "1.8.0",
"description": "Capture a bug, see the root cause, and create a GitHub issue in 5 seconds. Zero-backend, browser-only debugging assistant. Free Sentry/LogRocket alternative.",

@@ -5,0 +5,0 @@ "type": "module",

@@ -12,3 +12,3 @@ <h1 align="center">TraceBug</h1>

<br>
<a href="https://tracebug.dev/#demo"><b>▶ Watch the 15-second demo</b></a> · <a href="https://tracebug.dev/try.html">try it live in the sandbox</a>
<a href="https://tracebug.dev/#demo"><b>▶ Watch the 15-second demo</b></a> · <a href="https://tracebug.dev/proof"><b>watch Claude debug a real bug</b></a> · <a href="https://tracebug.dev/try.html">try it live in the sandbox</a>
</p>

@@ -19,8 +19,8 @@

<a href="https://www.npmjs.com/package/tracebug-sdk"><img src="https://img.shields.io/badge/npm_install-tracebug--sdk-CB3837?style=for-the-badge&logo=npm&logoColor=white" alt="npm install tracebug-sdk"></a>
<a href="https://tracebug.dev"><img src="https://img.shields.io/badge/Live_Demo-tracebug.dev-6C5CE7?style=for-the-badge&logoColor=white" alt="Live Demo"></a>
<a href="https://tracebug.dev"><img src="https://img.shields.io/badge/Live_Demo-tracebug.dev-6366F1?style=for-the-badge&logoColor=white" alt="Live Demo"></a>
</p>
<p align="center">
<a href="https://www.npmjs.com/package/tracebug-sdk"><img src="https://img.shields.io/npm/v/tracebug-sdk?color=7B61FF" alt="npm version"></a>
<a href="https://www.npmjs.com/package/tracebug-sdk"><img src="https://img.shields.io/npm/dm/tracebug-sdk?color=00D4FF" alt="npm downloads"></a>
<a href="https://www.npmjs.com/package/tracebug-sdk"><img src="https://img.shields.io/npm/v/tracebug-sdk?color=6366F1" alt="npm version"></a>
<a href="https://www.npmjs.com/package/tracebug-sdk"><img src="https://img.shields.io/npm/dm/tracebug-sdk?color=818CF8" alt="npm downloads"></a>
<a href="https://github.com/prashantsinghmangat/tracebug-ai"><img src="https://img.shields.io/github/stars/prashantsinghmangat/tracebug-ai?color=fbbf24" alt="GitHub stars"></a>

@@ -27,0 +27,0 @@ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License: MIT"></a>

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

import {
getAllSessions,
getNetworkFailures
} from "./chunk-AJX6BTPE.js";
// src/scanner/helpers.ts
var _issueCounter = 0;
function makeIssueId(detector) {
_issueCounter += 1;
return `${detector}_${Date.now().toString(36)}_${_issueCounter}`;
}
function buildSelector(el) {
if (!el || el.nodeType !== 1) return "";
if (el.id) return `#${cssEscape(el.id)}`;
const testId = el.getAttribute("data-testid") || el.getAttribute("data-test-id");
if (testId) return `[data-testid="${cssEscape(testId)}"]`;
const parts = [];
let cur = el;
let depth = 0;
while (cur && cur.nodeType === 1 && cur.tagName !== "BODY" && depth < 5) {
const tag = cur.tagName.toLowerCase();
const parent = cur.parentElement;
if (!parent) {
parts.unshift(tag);
break;
}
const siblings = Array.from(parent.children).filter((c) => c.tagName === cur.tagName);
if (siblings.length > 1) {
const idx = siblings.indexOf(cur) + 1;
parts.unshift(`${tag}:nth-of-type(${idx})`);
} else {
parts.unshift(tag);
}
cur = parent;
depth += 1;
}
return parts.join(" > ");
}
function coerceSeverity(impact) {
switch ((impact || "").toLowerCase()) {
case "critical":
return "critical";
case "serious":
return "serious";
case "moderate":
return "moderate";
case "minor":
return "minor";
default:
return "minor";
}
}
function cssEscape(value) {
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
return CSS.escape(value);
}
return value.replace(/[^\w-]/g, (ch) => `\\${ch}`);
}
// src/scanner/detectors/broken-images.ts
async function detectBrokenImages() {
const issues = [];
const imgs = Array.from(document.images);
for (const img of imgs) {
if (img.closest("#tracebug-root")) continue;
if (!img.complete) continue;
if (img.naturalWidth > 0) continue;
const src = img.currentSrc || img.src;
if (!src) continue;
issues.push({
id: makeIssueId("broken-image"),
detector: "broken-image",
severity: "moderate",
title: `Broken image: ${truncateUrl(src)}`,
description: `<img> element failed to load. The browser tried to fetch \`${src}\` and got a network error or a non-image response. ${img.alt ? `Alt text: "${img.alt}"` : "No alt text \u2014 also fails accessibility."}`,
selector: buildSelector(img),
url: src,
page: window.location.pathname,
detectedAt: Date.now()
});
}
return issues;
}
function truncateUrl(url) {
if (url.length <= 60) return url;
const tail = url.split("/").pop() || url.slice(-40);
return `\u2026/${tail}`;
}
// src/scanner/detectors/mixed-content.ts
var ATTR_TARGETS = [
{ tag: "img", attr: "src" },
{ tag: "script", attr: "src" },
{ tag: "iframe", attr: "src" },
{ tag: "link", attr: "href" },
{ tag: "audio", attr: "src" },
{ tag: "video", attr: "src" },
{ tag: "source", attr: "src" },
{ tag: "embed", attr: "src" },
{ tag: "object", attr: "data" }
];
async function detectMixedContent() {
if (typeof window === "undefined" || window.location.protocol !== "https:") {
return [];
}
const issues = [];
for (const { tag, attr } of ATTR_TARGETS) {
const elements = document.querySelectorAll(`${tag}[${attr}]`);
for (const el of Array.from(elements)) {
if (el.closest("#tracebug-root")) continue;
const value = el.getAttribute(attr) || "";
if (!value.startsWith("http://")) continue;
if (tag === "link") {
const rel = (el.rel || "").toLowerCase();
const fetchableRels = ["stylesheet", "preload", "prefetch", "manifest", "icon", "shortcut icon"];
if (!fetchableRels.some((r) => rel.includes(r))) continue;
}
issues.push({
id: makeIssueId("mixed-content"),
detector: "mixed-content",
severity: tag === "script" || tag === "iframe" ? "serious" : "moderate",
title: `Mixed content: ${tag} loads over HTTP`,
description: `<${tag}> on an HTTPS page references \`${value}\`. Browsers block or downgrade this \u2014 the resource usually fails to load and breaks the page's secure-context indicator.`,
selector: buildSelector(el),
url: value,
page: window.location.pathname,
detectedAt: Date.now()
});
}
}
return issues;
}
// src/fingerprint.ts
async function computeFingerprint(errorMessage, errorStack, page) {
const errorType = extractErrorType(errorMessage);
const topFrames = extractTopFrames(errorStack || "", 3);
const input = `${errorType}|${topFrames.join("\n")}|${page}`;
if (typeof crypto !== "undefined" && crypto.subtle && typeof crypto.subtle.digest === "function") {
try {
const buf = new TextEncoder().encode(input);
const hash = await crypto.subtle.digest("SHA-1", buf);
return bufferToHex(hash).slice(0, 16);
} catch (e) {
}
}
return djb2(input).toString(16).padStart(8, "0");
}
function extractErrorType(message) {
const m = message.match(/^([A-Z][a-zA-Z]+Error|Error)\b/);
return m ? m[1] : "Error";
}
function extractTopFrames(stack, n) {
const frames = [];
const lines = stack.split("\n");
for (const line of lines) {
const m = line.match(/(https?:\/\/[^):\s]+|[^():\s]+\.[a-z]+):(\d+):(\d+)/i);
if (m) {
const url = m[1];
const path = url.includes("://") ? new URL(url, typeof window !== "undefined" ? window.location.origin : "http://localhost").pathname : url;
frames.push(`${path}:${m[2]}:${m[3]}`);
if (frames.length >= n) break;
}
}
return frames;
}
function bufferToHex(buf) {
const bytes = new Uint8Array(buf);
let out = "";
for (let i = 0; i < bytes.length; i++) out += bytes[i].toString(16).padStart(2, "0");
return out;
}
function djb2(str) {
let hash = 5381;
for (let i = 0; i < str.length; i++) hash = (hash << 5) + hash + str.charCodeAt(i) | 0;
return hash >>> 0;
}
// src/scanner/detectors/session-data.ts
var SLOW_API_MS = 2e3;
var MAX_CONTEXT_SAMPLES = 10;
async function detectConsoleErrors(session) {
var _a, _b, _c;
if (!session) return [];
const groups = /* @__PURE__ */ new Map();
for (let i = 0; i < session.events.length; i++) {
const e = session.events[i];
if (e.type !== "error" && e.type !== "unhandled_rejection" && e.type !== "console_error") continue;
const message = ((_a = e.data.error) == null ? void 0 : _a.message) || e.data.message || "";
if (!message) continue;
const stack = ((_b = e.data.error) == null ? void 0 : _b.stack) || "";
const page = e.page || (typeof window !== "undefined" ? window.location.pathname : "");
const fp = await computeFingerprint(message, stack, page);
const precedingAction = describePrecedingAction(session.events, i);
const existing = groups.get(fp);
if (existing) {
existing.issue.occurrences = (existing.issue.occurrences || 1) + 1;
existing.issue.lastSeenAt = e.timestamp;
if (existing.samples.length < MAX_CONTEXT_SAMPLES) {
existing.samples.push({ timestamp: e.timestamp, precedingAction });
}
continue;
}
const firstFrame = ((_c = stack.split("\n").find((l) => l.trim().startsWith("at "))) == null ? void 0 : _c.trim()) || "";
const issue = {
id: makeIssueId("console-error"),
detector: "console-error",
severity: classifyErrorSeverity(message),
title: `JS error: ${message.slice(0, 70)}${message.length > 70 ? "\u2026" : ""}`,
description: firstFrame ? `${message}
First frame: ${firstFrame}` : message,
page,
detectedAt: e.timestamp,
fingerprint: fp,
occurrences: 1,
firstSeenAt: e.timestamp,
lastSeenAt: e.timestamp
};
groups.set(fp, { issue, samples: [{ timestamp: e.timestamp, precedingAction }] });
}
const out = [];
for (const g of groups.values()) {
const n = g.issue.occurrences || 1;
if (n > 1) {
g.issue.title = `${g.issue.title} [\xD7${n}]`;
g.issue.contextSamples = g.samples;
}
out.push(g.issue);
}
return out;
}
function describePrecedingAction(events, i) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
for (let j = i - 1; j >= 0; j--) {
const e = events[j];
if (e.type === "click") {
const t = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.text) || ((_d = (_c = e.data) == null ? void 0 : _c.element) == null ? void 0 : _d.ariaLabel) || ((_f = (_e = e.data) == null ? void 0 : _e.element) == null ? void 0 : _f.tag) || "element";
return `clicked "${String(t).slice(0, 40)}"`;
}
if (e.type === "input") {
const n = ((_h = (_g = e.data) == null ? void 0 : _g.element) == null ? void 0 : _h.name) || ((_j = (_i = e.data) == null ? void 0 : _i.element) == null ? void 0 : _j.id) || "field";
return `typed in ${n}`;
}
if (e.type === "select_change") return "selected an option";
if (e.type === "form_submit") return "submitted a form";
if (e.type === "route_change") return `navigated to ${((_k = e.data) == null ? void 0 : _k.to) || "page"}`;
}
return void 0;
}
async function detectFailedRequests(session) {
if (!session) return [];
const issues = [];
const buffer = getNetworkFailures();
for (const e of session.events) {
if (e.type !== "api_request") continue;
const req = e.data.request;
if (!req) continue;
const status = req.statusCode || 0;
if (status >= 200 && status < 400) continue;
if (status === 0 && req.method === "HEAD") continue;
const match = buffer.find(
(b) => b.url === req.url && b.method === req.method && b.status === status && Math.abs(b.timestamp - e.timestamp) < 5e3
);
const snippet = (match == null ? void 0 : match.response) ? `
Response: ${match.response.slice(0, 160)}` : "";
issues.push({
id: makeIssueId("failed-request"),
detector: "failed-request",
severity: status >= 500 ? "critical" : status === 0 ? "serious" : "moderate",
title: `${req.method} ${truncatePath(req.url)} \u2192 ${status === 0 ? "Network Error" : status}`,
description: `Request failed in ${req.durationMs || 0}ms.${snippet}`,
url: req.url,
page: e.page || window.location.pathname,
detectedAt: e.timestamp
});
}
return issues;
}
async function detectSlowApis(session) {
if (!session) return [];
const issues = [];
for (const e of session.events) {
if (e.type !== "api_request") continue;
const req = e.data.request;
if (!req) continue;
const status = req.statusCode || 0;
if (status < 200 || status >= 400) continue;
const duration = req.durationMs || 0;
if (duration < SLOW_API_MS) continue;
issues.push({
id: makeIssueId("slow-api"),
detector: "slow-api",
severity: duration > 5e3 ? "serious" : "moderate",
title: `Slow API: ${req.method} ${truncatePath(req.url)} (${duration}ms)`,
description: `This request took ${(duration / 1e3).toFixed(1)}s \u2014 over the ${SLOW_API_MS / 1e3}s threshold. Slow APIs are a common UX complaint and a leading cause of perceived bugs ("the page is frozen").`,
url: req.url,
page: e.page || window.location.pathname,
detectedAt: e.timestamp
});
}
return issues;
}
function truncatePath(url) {
try {
const u = new URL(url, window.location.origin);
const p = u.pathname.length > 50 ? u.pathname.slice(0, 47) + "\u2026" : u.pathname;
return p;
} catch (e) {
return url.length > 50 ? url.slice(0, 47) + "\u2026" : url;
}
}
function classifyErrorSeverity(message) {
if (/TypeError|ReferenceError|SyntaxError/i.test(message)) return "critical";
if (/Network|fetch|failed to/i.test(message)) return "serious";
return "moderate";
}
// src/scanner/detectors/a11y.ts
var _axePromise = null;
function loadAxe() {
if (_axePromise) return _axePromise;
_axePromise = import("axe-core").then((mod) => mod.default || mod).catch((err) => {
console.warn("[TraceBug] axe-core failed to load:", err);
return null;
});
return _axePromise;
}
async function detectA11yViolations() {
const axe = await loadAxe();
if (!axe || typeof axe.run !== "function") return [];
let results;
try {
results = await axe.run(document, {
// Only WCAG-tagged rules — keeps signal-to-noise high. Best-practice
// rules add ~30% more noise without proportional value for QA.
runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] },
// Skip our own UI so QA isn't told their toolbar fails contrast checks.
// axe accepts a context with exclude — we pass { exclude: [...] } via
// the second-argument options-shaped form below to keep types loose.
resultTypes: ["violations"]
});
} catch (err) {
console.warn("[TraceBug] axe.run failed:", err);
return [];
}
const issues = [];
const violations = (results == null ? void 0 : results.violations) || [];
for (const v of violations) {
const nodes = v.nodes || [];
const firstNode = nodes[0];
const selector = Array.isArray(firstNode == null ? void 0 : firstNode.target) ? firstNode.target.join(" ") : "";
const exampleSnippet = ((firstNode == null ? void 0 : firstNode.html) || "").slice(0, 120);
const moreSuffix = nodes.length > 1 ? ` (+ ${nodes.length - 1} more element${nodes.length === 2 ? "" : "s"})` : "";
issues.push({
id: makeIssueId("axe-a11y"),
detector: "axe-a11y",
severity: coerceSeverity(v.impact),
title: `${v.help || v.id}${moreSuffix}`,
description: `${v.description || v.id}
First element: \`${exampleSnippet}\``,
selector: selector || void 0,
helpUrl: v.helpUrl,
page: window.location.pathname,
detectedAt: Date.now()
});
}
return issues;
}
// src/scanner/detectors/frustration.ts
var RAGE_WINDOW_MS = 1500;
var RAGE_MIN_CLICKS = 3;
var DEAD_RESPONSE_WINDOW_MS = 1500;
var ABANDON_WINDOW_MS = 6e4;
var ERROR_CORRELATION_WINDOW_MS = 2500;
async function detectFrustration(session) {
var _a;
if (!session) return [];
const events = session.events;
if (events.length === 0) return [];
const issues = [];
const page = ((_a = session.events[0]) == null ? void 0 : _a.page) || window.location.pathname;
issues.push(...detectRageClicks(events, page));
issues.push(...detectDeadClicks(events, page));
issues.push(...detectFormAbandonment(events, page));
issues.push(...detectErrorCorrelated(events, page));
return issues;
}
function detectRageClicks(events, page) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const out = [];
const seenGroups = /* @__PURE__ */ new Set();
for (let i = 0; i < events.length; i++) {
const e = events[i];
if (e.type !== "click") continue;
const sel = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.selector) || ((_d = (_c = e.data) == null ? void 0 : _c.element) == null ? void 0 : _d.testId) || "";
if (!sel) continue;
const cluster = [e];
let j = i + 1;
while (j < events.length) {
const next = events[j];
if (next.timestamp - e.timestamp > RAGE_WINDOW_MS) break;
if (isResponseEvent(next)) break;
if (next.type === "click") {
const nextSel = ((_f = (_e = next.data) == null ? void 0 : _e.element) == null ? void 0 : _f.selector) || ((_h = (_g = next.data) == null ? void 0 : _g.element) == null ? void 0 : _h.testId) || "";
if (nextSel === sel) cluster.push(next);
}
j++;
}
if (cluster.length >= RAGE_MIN_CLICKS) {
const key = `${sel}@${e.timestamp}`;
if (seenGroups.has(key)) continue;
seenGroups.add(key);
const label = clickLabel(cluster[0]);
out.push({
id: makeIssueId("frustration-rage"),
detector: "frustration-rage",
severity: "serious",
title: `Rage clicks on ${label} (${cluster.length}\xD7 in ${Math.round(cluster[cluster.length - 1].timestamp - cluster[0].timestamp)}ms)`,
description: `User clicked the same element ${cluster.length} times within ${RAGE_WINDOW_MS}ms with no observable response (no API call, navigation, or DOM update). The element either doesn't respond to clicks or feels broken.`,
selector: sel,
page,
detectedAt: cluster[0].timestamp,
firstSeenAt: cluster[0].timestamp,
lastSeenAt: cluster[cluster.length - 1].timestamp,
occurrences: cluster.length
});
i = j - 1;
}
}
return out;
}
function detectDeadClicks(events, page) {
var _a, _b;
const out = [];
const MAX = 5;
for (let i = 0; i < events.length && out.length < MAX; i++) {
const e = events[i];
if (e.type !== "click") continue;
let responsive = false;
for (let j = i + 1; j < events.length; j++) {
const next = events[j];
if (next.timestamp - e.timestamp > DEAD_RESPONSE_WINDOW_MS) break;
if (isResponseEvent(next)) {
responsive = true;
break;
}
}
if (responsive) continue;
const sel = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.selector) || "";
const label = clickLabel(e);
out.push({
id: makeIssueId("frustration-dead"),
detector: "frustration-dead",
severity: "moderate",
title: `Dead click on ${label}`,
description: `Clicked but nothing happened within ${DEAD_RESPONSE_WINDOW_MS}ms (no API call, navigation, or DOM input). The element may have an unbound handler, a swallowed event, or be visually clickable but disabled.`,
selector: sel,
page: e.page || page,
detectedAt: e.timestamp
});
}
return out;
}
function detectFormAbandonment(events, _page) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
const out = [];
const formActivity = {};
for (const e of events) {
if (e.type === "input") {
const formId = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.formId) || ((_d = (_c = e.data) == null ? void 0 : _c.element) == null ? void 0 : _d.formAction) || "_default";
if (!formActivity[formId]) {
formActivity[formId] = { firstInputAt: e.timestamp, fieldsSeen: /* @__PURE__ */ new Set(), lastInputAt: e.timestamp, page: e.page };
}
const name = ((_f = (_e = e.data) == null ? void 0 : _e.element) == null ? void 0 : _f.name) || ((_h = (_g = e.data) == null ? void 0 : _g.element) == null ? void 0 : _h.id) || "field";
formActivity[formId].fieldsSeen.add(name);
formActivity[formId].lastInputAt = e.timestamp;
} else if (e.type === "form_submit") {
const formId = ((_j = (_i = e.data) == null ? void 0 : _i.form) == null ? void 0 : _j.id) || "_default";
delete formActivity[formId];
} else if (e.type === "route_change") {
for (const formId of Object.keys(formActivity)) {
const a = formActivity[formId];
if (e.timestamp - a.lastInputAt > ABANDON_WINDOW_MS) continue;
if (a.fieldsSeen.size === 0) continue;
out.push({
id: makeIssueId("frustration-abandon"),
detector: "frustration-abandon",
severity: "moderate",
title: `Form abandoned on ${a.page} (${a.fieldsSeen.size} field${a.fieldsSeen.size === 1 ? "" : "s"} filled)`,
description: `User typed into ${a.fieldsSeen.size} field${a.fieldsSeen.size === 1 ? "" : "s"} (${Array.from(a.fieldsSeen).slice(0, 5).join(", ")}) and then navigated away without submitting. Likely a UX problem: the submit button is unclear, the form requires too much info, or it's failing silently.`,
page: a.page,
detectedAt: a.lastInputAt,
firstSeenAt: a.firstInputAt,
lastSeenAt: a.lastInputAt
});
delete formActivity[formId];
}
}
}
return out;
}
function detectErrorCorrelated(events, page) {
var _a, _b, _c, _d, _e, _f;
const out = [];
const seen = /* @__PURE__ */ new Set();
for (let i = 0; i < events.length; i++) {
const e = events[i];
const isError = e.type === "error" || e.type === "unhandled_rejection" || e.type === "console_error";
if (!isError) continue;
let click = null;
for (let j = i - 1; j >= 0; j--) {
const prev = events[j];
if (e.timestamp - prev.timestamp > ERROR_CORRELATION_WINDOW_MS) break;
if (prev.type === "click") {
click = prev;
break;
}
}
if (!click) continue;
const errMsg = ((_b = (_a = e.data) == null ? void 0 : _a.error) == null ? void 0 : _b.message) || "";
if (!errMsg) continue;
const key = `${errMsg}::${((_d = (_c = click.data) == null ? void 0 : _c.element) == null ? void 0 : _d.selector) || ""}`;
if (seen.has(key)) continue;
seen.add(key);
const label = clickLabel(click);
const truncMsg = errMsg.length > 60 ? errMsg.slice(0, 57) + "\u2026" : errMsg;
const delta = Math.round(e.timestamp - click.timestamp);
out.push({
id: makeIssueId("frustration-error-correlated"),
detector: "frustration-error-correlated",
severity: "critical",
title: `Click on ${label} triggered: ${truncMsg}`,
description: `An error fired ${delta}ms after the user clicked ${label}. This is almost certainly the offending interaction \u2014 the click handler threw, or its async path failed.`,
selector: (_f = (_e = click.data) == null ? void 0 : _e.element) == null ? void 0 : _f.selector,
page: e.page || page,
detectedAt: e.timestamp
});
}
return out;
}
function isResponseEvent(e) {
return e.type === "api_request" || e.type === "route_change" || e.type === "input" || e.type === "form_submit" || e.type === "select_change";
}
function clickLabel(e) {
var _a;
const el = (_a = e.data) == null ? void 0 : _a.element;
const raw = (el == null ? void 0 : el.text) || (el == null ? void 0 : el.ariaLabel) || (el == null ? void 0 : el.testId) || (el == null ? void 0 : el.id) || (el == null ? void 0 : el.tag) || "element";
const trimmed = String(raw).replace(/\s+/g, " ").trim();
return trimmed.length > 40 ? `"${trimmed.slice(0, 37)}\u2026"` : `"${trimmed}"`;
}
// src/scanner/index.ts
var _issues = [];
var _scanInFlight = null;
var _lastScanAt = 0;
var SEVERITY_ORDER = {
critical: 0,
serious: 1,
moderate: 2,
minor: 3
};
async function scan() {
if (_scanInFlight) {
const issues = await _scanInFlight;
return { issues, durationMs: 0, scannedAt: _lastScanAt };
}
const startedAt = Date.now();
const sessions = getAllSessions().sort((a, b) => b.updatedAt - a.updatedAt);
const session = sessions[0] || null;
const safeRun = (p) => p.catch((err) => {
console.warn("[TraceBug] Detector failed:", err);
return [];
});
_scanInFlight = Promise.all([
safeRun(detectBrokenImages()),
safeRun(detectMixedContent()),
safeRun(detectConsoleErrors(session)),
safeRun(detectFailedRequests(session)),
safeRun(detectSlowApis(session)),
safeRun(detectA11yViolations()),
safeRun(detectFrustration(session))
]).then((results) => {
const all = [].concat(...results);
all.sort((a, b) => {
const sev = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];
if (sev !== 0) return sev;
return a.detectedAt - b.detectedAt;
});
_issues = all;
return all;
});
try {
const issues = await _scanInFlight;
_lastScanAt = Date.now();
return { issues, durationMs: _lastScanAt - startedAt, scannedAt: _lastScanAt };
} finally {
_scanInFlight = null;
}
}
function getIssues(options) {
var _a;
const includeDismissed = (_a = options == null ? void 0 : options.includeDismissed) != null ? _a : false;
return includeDismissed ? _issues.slice() : _issues.filter((i) => !i.dismissed);
}
function dismissIssue(id) {
const issue = _issues.find((i) => i.id === id);
if (!issue) return false;
issue.dismissed = true;
return true;
}
function undismissIssue(id) {
const issue = _issues.find((i) => i.id === id);
if (!issue) return false;
issue.dismissed = false;
return true;
}
function clearIssues() {
_issues = [];
_lastScanAt = 0;
}
function getIssueCountsByDetector() {
const counts = {
"axe-a11y": 0,
"broken-image": 0,
"mixed-content": 0,
"console-error": 0,
"slow-api": 0,
"failed-request": 0,
"frustration-rage": 0,
"frustration-dead": 0,
"frustration-abandon": 0,
"frustration-error-correlated": 0
};
for (const i of _issues) {
if (i.dismissed) continue;
counts[i.detector] = (counts[i.detector] || 0) + 1;
}
return counts;
}
function getIssueCountsBySeverity() {
const counts = {
critical: 0,
serious: 0,
moderate: 0,
minor: 0
};
for (const i of _issues) {
if (i.dismissed) continue;
counts[i.severity] += 1;
}
return counts;
}
function getIssueById(id) {
return _issues.find((i) => i.id === id) || null;
}
export {
scan,
getIssues,
dismissIssue,
undismissIssue,
clearIssues,
getIssueCountsByDetector,
getIssueCountsBySeverity,
getIssueById
};
//# sourceMappingURL=chunk-AAHK2MC7.js.map
{"version":3,"sources":["../src/scanner/helpers.ts","../src/scanner/detectors/broken-images.ts","../src/scanner/detectors/mixed-content.ts","../src/fingerprint.ts","../src/scanner/detectors/session-data.ts","../src/scanner/detectors/a11y.ts","../src/scanner/detectors/frustration.ts","../src/scanner/index.ts"],"sourcesContent":["// ── Scanner helpers ───────────────────────────────────────────────────────\r\n// Shared utilities for detectors: stable issue IDs, robust CSS selectors,\r\n// severity coercion.\r\n\r\nimport { IssueDetector, IssueSeverity } from \"../types\";\r\n\r\nlet _issueCounter = 0;\r\n\r\n/** Stable, unique issue ID across detectors and runs. */\r\nexport function makeIssueId(detector: IssueDetector): string {\r\n _issueCounter += 1;\r\n return `${detector}_${Date.now().toString(36)}_${_issueCounter}`;\r\n}\r\n\r\n/**\r\n * Build a \"good enough\" CSS selector for an element. Prefers id, then\r\n * data-testid, then a tag+nth-of-type chain bounded to depth 5. Not\r\n * guaranteed unique on pathological pages but unique enough for clicking\r\n * back to the offending node.\r\n */\r\nexport function buildSelector(el: Element): string {\r\n if (!el || el.nodeType !== 1) return \"\";\r\n if (el.id) return `#${cssEscape(el.id)}`;\r\n\r\n const testId = el.getAttribute(\"data-testid\") || el.getAttribute(\"data-test-id\");\r\n if (testId) return `[data-testid=\"${cssEscape(testId)}\"]`;\r\n\r\n const parts: string[] = [];\r\n let cur: Element | null = el;\r\n let depth = 0;\r\n while (cur && cur.nodeType === 1 && cur.tagName !== \"BODY\" && depth < 5) {\r\n const tag = cur.tagName.toLowerCase();\r\n // Explicit annotation breaks the circular inference from `cur = parent`\r\n // below (which otherwise makes `parent` — and `parent.children` — `any`).\r\n const parent: HTMLElement | null = cur.parentElement;\r\n if (!parent) {\r\n parts.unshift(tag);\r\n break;\r\n }\r\n const siblings = Array.from(parent.children).filter(c => c.tagName === cur!.tagName);\r\n if (siblings.length > 1) {\r\n const idx = siblings.indexOf(cur) + 1;\r\n parts.unshift(`${tag}:nth-of-type(${idx})`);\r\n } else {\r\n parts.unshift(tag);\r\n }\r\n cur = parent;\r\n depth += 1;\r\n }\r\n return parts.join(\" > \");\r\n}\r\n\r\n/**\r\n * Map axe-core's impact strings to our IssueSeverity enum. Axe's \"serious\"\r\n * is our highest non-critical bucket.\r\n */\r\nexport function coerceSeverity(impact: string | null | undefined): IssueSeverity {\r\n switch ((impact || \"\").toLowerCase()) {\r\n case \"critical\": return \"critical\";\r\n case \"serious\": return \"serious\";\r\n case \"moderate\": return \"moderate\";\r\n case \"minor\": return \"minor\";\r\n default: return \"minor\";\r\n }\r\n}\r\n\r\n/**\r\n * Minimal CSS.escape polyfill — needed when an id contains characters that\r\n * would break a selector (colons, brackets, dots in framework-generated ids).\r\n */\r\nfunction cssEscape(value: string): string {\r\n if (typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\") {\r\n return CSS.escape(value);\r\n }\r\n return value.replace(/[^\\w-]/g, ch => `\\\\${ch}`);\r\n}\r\n","// ── Broken-image detector ─────────────────────────────────────────────────\r\n// Walks every <img> on the page and flags ones that failed to load. Uses\r\n// `naturalWidth === 0 && complete === true` — the standard signal for\r\n// \"image attempted to load and failed.\" Skips images that haven't finished\r\n// loading yet (we can't tell if they're broken until they settle).\r\n\r\nimport { Issue } from \"../../types\";\r\nimport { buildSelector, makeIssueId } from \"../helpers\";\r\n\r\nexport async function detectBrokenImages(): Promise<Issue[]> {\r\n const issues: Issue[] = [];\r\n const imgs = Array.from(document.images);\r\n\r\n for (const img of imgs) {\r\n // Skip TraceBug's own UI.\r\n if (img.closest(\"#tracebug-root\")) continue;\r\n // Image still loading — can't decide yet.\r\n if (!img.complete) continue;\r\n // Decoded successfully — naturalWidth is non-zero.\r\n if (img.naturalWidth > 0) continue;\r\n\r\n // No src is a different problem (missing asset, not a broken load).\r\n const src = img.currentSrc || img.src;\r\n if (!src) continue;\r\n\r\n issues.push({\r\n id: makeIssueId(\"broken-image\"),\r\n detector: \"broken-image\",\r\n severity: \"moderate\",\r\n title: `Broken image: ${truncateUrl(src)}`,\r\n description: `<img> element failed to load. The browser tried to fetch \\`${src}\\` and got a network error or a non-image response. ${\r\n img.alt ? `Alt text: \"${img.alt}\"` : \"No alt text — also fails accessibility.\"\r\n }`,\r\n selector: buildSelector(img),\r\n url: src,\r\n page: window.location.pathname,\r\n detectedAt: Date.now(),\r\n });\r\n }\r\n\r\n return issues;\r\n}\r\n\r\nfunction truncateUrl(url: string): string {\r\n if (url.length <= 60) return url;\r\n // Keep the filename — easier to identify than the host.\r\n const tail = url.split(\"/\").pop() || url.slice(-40);\r\n return `…/${tail}`;\r\n}\r\n","// ── Mixed-content detector ────────────────────────────────────────────────\r\n// Flags every `http://` resource on a `https://` page. Browsers block most\r\n// of these silently (active content) or downgrade them (passive content),\r\n// so users rarely notice — but the asset usually fails or breaks the lock\r\n// icon. Worth surfacing.\r\n\r\nimport { Issue } from \"../../types\";\r\nimport { buildSelector, makeIssueId } from \"../helpers\";\r\n\r\nconst ATTR_TARGETS: Array<{ tag: string; attr: string }> = [\r\n { tag: \"img\", attr: \"src\" },\r\n { tag: \"script\", attr: \"src\" },\r\n { tag: \"iframe\", attr: \"src\" },\r\n { tag: \"link\", attr: \"href\" },\r\n { tag: \"audio\", attr: \"src\" },\r\n { tag: \"video\", attr: \"src\" },\r\n { tag: \"source\", attr: \"src\" },\r\n { tag: \"embed\", attr: \"src\" },\r\n { tag: \"object\", attr: \"data\" },\r\n];\r\n\r\nexport async function detectMixedContent(): Promise<Issue[]> {\r\n // Only relevant on HTTPS pages — skip on plain HTTP and file:// origins.\r\n if (typeof window === \"undefined\" || window.location.protocol !== \"https:\") {\r\n return [];\r\n }\r\n\r\n const issues: Issue[] = [];\r\n for (const { tag, attr } of ATTR_TARGETS) {\r\n const elements = document.querySelectorAll(`${tag}[${attr}]`);\r\n for (const el of Array.from(elements)) {\r\n if (el.closest(\"#tracebug-root\")) continue;\r\n const value = (el as HTMLElement).getAttribute(attr) || \"\";\r\n if (!value.startsWith(\"http://\")) continue;\r\n\r\n // <link> only matters when it's loading something the browser fetches —\r\n // stylesheets, preloads, manifests, icons. Skip rel=\"canonical\" etc.\r\n if (tag === \"link\") {\r\n const rel = ((el as HTMLLinkElement).rel || \"\").toLowerCase();\r\n const fetchableRels = [\"stylesheet\", \"preload\", \"prefetch\", \"manifest\", \"icon\", \"shortcut icon\"];\r\n if (!fetchableRels.some(r => rel.includes(r))) continue;\r\n }\r\n\r\n issues.push({\r\n id: makeIssueId(\"mixed-content\"),\r\n detector: \"mixed-content\",\r\n severity: tag === \"script\" || tag === \"iframe\" ? \"serious\" : \"moderate\",\r\n title: `Mixed content: ${tag} loads over HTTP`,\r\n description: `<${tag}> on an HTTPS page references \\`${value}\\`. Browsers block or downgrade this — the resource usually fails to load and breaks the page's secure-context indicator.`,\r\n selector: buildSelector(el as HTMLElement),\r\n url: value,\r\n page: window.location.pathname,\r\n detectedAt: Date.now(),\r\n });\r\n }\r\n }\r\n return issues;\r\n}\r\n","// ── Bug Fingerprint ───────────────────────────────────────────────────────\r\n// Group identical errors locally so 14× the same TypeError collapses into\r\n// one issue with `[×14]` instead of 14 near-duplicate rows.\r\n//\r\n// Fingerprint inputs (in priority order):\r\n// 1. Error type/class extracted from the message (\"TypeError\", etc.)\r\n// 2. Top three \"at ...\" stack frames (location-only — line numbers\r\n// stable across invocations within the same build)\r\n// 3. Page path\r\n//\r\n// We use SHA-1 via `crypto.subtle.digest` when available — falls back to\r\n// a tiny non-cryptographic hash on older contexts. Fingerprint is not\r\n// security-sensitive; collision rate of djb2 is good enough for this.\r\n\r\n/** Compute a stable fingerprint string for an error + page combo. */\r\nexport async function computeFingerprint(\r\n errorMessage: string,\r\n errorStack: string | undefined,\r\n page: string\r\n): Promise<string> {\r\n const errorType = extractErrorType(errorMessage);\r\n const topFrames = extractTopFrames(errorStack || \"\", 3);\r\n const input = `${errorType}|${topFrames.join(\"\\n\")}|${page}`;\r\n\r\n // Prefer SHA-1 from the Subtle Crypto API for stronger uniqueness.\r\n if (typeof crypto !== \"undefined\" && crypto.subtle && typeof crypto.subtle.digest === \"function\") {\r\n try {\r\n const buf = new TextEncoder().encode(input);\r\n const hash = await crypto.subtle.digest(\"SHA-1\", buf);\r\n return bufferToHex(hash).slice(0, 16);\r\n } catch {}\r\n }\r\n return djb2(input).toString(16).padStart(8, \"0\");\r\n}\r\n\r\n/**\r\n * Synchronous fallback fingerprint — used in code paths that can't `await`.\r\n * Collision rate higher than SHA-1 but acceptable for in-session grouping.\r\n */\r\nexport function computeFingerprintSync(\r\n errorMessage: string,\r\n errorStack: string | undefined,\r\n page: string\r\n): string {\r\n const errorType = extractErrorType(errorMessage);\r\n const topFrames = extractTopFrames(errorStack || \"\", 3);\r\n const input = `${errorType}|${topFrames.join(\"\\n\")}|${page}`;\r\n return djb2(input).toString(16).padStart(8, \"0\");\r\n}\r\n\r\n/** Pick out the JS error class — TypeError, ReferenceError, etc. */\r\nfunction extractErrorType(message: string): string {\r\n const m = message.match(/^([A-Z][a-zA-Z]+Error|Error)\\b/);\r\n return m ? m[1] : \"Error\";\r\n}\r\n\r\n/**\r\n * Extract the location parts (\"foo.js:42:13\") of the top N stack frames,\r\n * dropping function names. Same call site → same fingerprint, even if the\r\n * function gets renamed between minified/dev builds.\r\n */\r\nfunction extractTopFrames(stack: string, n: number): string[] {\r\n const frames: string[] = [];\r\n const lines = stack.split(\"\\n\");\r\n for (const line of lines) {\r\n const m = line.match(/(https?:\\/\\/[^):\\s]+|[^():\\s]+\\.[a-z]+):(\\d+):(\\d+)/i);\r\n if (m) {\r\n const url = m[1];\r\n const path = url.includes(\"://\") ? new URL(url, typeof window !== \"undefined\" ? window.location.origin : \"http://localhost\").pathname : url;\r\n frames.push(`${path}:${m[2]}:${m[3]}`);\r\n if (frames.length >= n) break;\r\n }\r\n }\r\n return frames;\r\n}\r\n\r\nfunction bufferToHex(buf: ArrayBuffer): string {\r\n const bytes = new Uint8Array(buf);\r\n let out = \"\";\r\n for (let i = 0; i < bytes.length; i++) out += bytes[i].toString(16).padStart(2, \"0\");\r\n return out;\r\n}\r\n\r\n/** djb2 — tiny non-cryptographic hash. Stable, deterministic, no deps. */\r\nfunction djb2(str: string): number {\r\n let hash = 5381;\r\n for (let i = 0; i < str.length; i++) hash = ((hash << 5) + hash + str.charCodeAt(i)) | 0;\r\n return hash >>> 0; // unsigned 32-bit\r\n}\r\n","// ── Session-data detectors ────────────────────────────────────────────────\r\n// Three detectors that read from the SDK's existing event buffer and\r\n// network-failure ring buffer — no new tracking, just classification.\r\n//\r\n// console-error: every distinct error/unhandled rejection in the session\r\n// failed-request: every 4xx/5xx/network-error from the api_request stream\r\n// slow-api: every successful api_request that took longer than the threshold\r\n\r\nimport { Issue, StoredSession, TraceBugEvent } from \"../../types\";\r\nimport { getNetworkFailures } from \"../../collectors\";\r\nimport { makeIssueId } from \"../helpers\";\r\nimport { computeFingerprint } from \"../../fingerprint\";\r\n\r\nconst SLOW_API_MS = 2000;\r\nconst MAX_CONTEXT_SAMPLES = 10;\r\n\r\n/**\r\n * Console errors collapsed by fingerprint (errorType + top-3 frames + page).\r\n * Repeats accumulate `occurrences` + `firstSeenAt`/`lastSeenAt` + a few\r\n * `contextSamples` so the UI can show the count and let the user expand\r\n * to see distinct preceding actions.\r\n */\r\nexport async function detectConsoleErrors(session: StoredSession | null): Promise<Issue[]> {\r\n if (!session) return [];\r\n\r\n // Build groups by fingerprint.\r\n type Group = {\r\n issue: Issue;\r\n samples: Array<{ timestamp: number; precedingAction?: string }>;\r\n };\r\n const groups = new Map<string, Group>();\r\n\r\n for (let i = 0; i < session.events.length; i++) {\r\n const e = session.events[i];\r\n if (e.type !== \"error\" && e.type !== \"unhandled_rejection\" && e.type !== \"console_error\") continue;\r\n const message = e.data.error?.message || e.data.message || \"\";\r\n if (!message) continue;\r\n\r\n const stack = e.data.error?.stack || \"\";\r\n const page = e.page || (typeof window !== \"undefined\" ? window.location.pathname : \"\");\r\n const fp = await computeFingerprint(message, stack, page);\r\n\r\n const precedingAction = describePrecedingAction(session.events, i);\r\n\r\n const existing = groups.get(fp);\r\n if (existing) {\r\n existing.issue.occurrences = (existing.issue.occurrences || 1) + 1;\r\n existing.issue.lastSeenAt = e.timestamp;\r\n if (existing.samples.length < MAX_CONTEXT_SAMPLES) {\r\n existing.samples.push({ timestamp: e.timestamp, precedingAction });\r\n }\r\n continue;\r\n }\r\n\r\n const firstFrame = stack.split(\"\\n\").find((l: string) => l.trim().startsWith(\"at \"))?.trim() || \"\";\r\n const issue: Issue = {\r\n id: makeIssueId(\"console-error\"),\r\n detector: \"console-error\",\r\n severity: classifyErrorSeverity(message),\r\n title: `JS error: ${message.slice(0, 70)}${message.length > 70 ? \"…\" : \"\"}`,\r\n description: firstFrame ? `${message}\\n\\nFirst frame: ${firstFrame}` : message,\r\n page,\r\n detectedAt: e.timestamp,\r\n fingerprint: fp,\r\n occurrences: 1,\r\n firstSeenAt: e.timestamp,\r\n lastSeenAt: e.timestamp,\r\n };\r\n groups.set(fp, { issue, samples: [{ timestamp: e.timestamp, precedingAction }] });\r\n }\r\n\r\n // Finalize: attach context samples + adjust title to reflect repeat count.\r\n const out: Issue[] = [];\r\n for (const g of groups.values()) {\r\n const n = g.issue.occurrences || 1;\r\n if (n > 1) {\r\n g.issue.title = `${g.issue.title} [×${n}]`;\r\n g.issue.contextSamples = g.samples;\r\n }\r\n out.push(g.issue);\r\n }\r\n return out;\r\n}\r\n\r\n/** Look back from index `i` for the most recent click/input/navigation. */\r\nfunction describePrecedingAction(events: TraceBugEvent[], i: number): string | undefined {\r\n for (let j = i - 1; j >= 0; j--) {\r\n const e = events[j];\r\n if (e.type === \"click\") {\r\n const t = e.data?.element?.text || e.data?.element?.ariaLabel || e.data?.element?.tag || \"element\";\r\n return `clicked \"${String(t).slice(0, 40)}\"`;\r\n }\r\n if (e.type === \"input\") {\r\n const n = e.data?.element?.name || e.data?.element?.id || \"field\";\r\n return `typed in ${n}`;\r\n }\r\n if (e.type === \"select_change\") return \"selected an option\";\r\n if (e.type === \"form_submit\") return \"submitted a form\";\r\n if (e.type === \"route_change\") return `navigated to ${e.data?.to || \"page\"}`;\r\n }\r\n return undefined;\r\n}\r\n\r\nexport async function detectFailedRequests(session: StoredSession | null): Promise<Issue[]> {\r\n if (!session) return [];\r\n const issues: Issue[] = [];\r\n const buffer = getNetworkFailures();\r\n\r\n for (const e of session.events) {\r\n if (e.type !== \"api_request\") continue;\r\n const req = e.data.request;\r\n if (!req) continue;\r\n const status = req.statusCode || 0;\r\n if (status >= 200 && status < 400) continue; // success\r\n if (status === 0 && req.method === \"HEAD\") continue; // skip our own probe noise\r\n\r\n // Try to find a matching response body snippet from the failure buffer.\r\n const match = buffer.find(b =>\r\n b.url === req.url &&\r\n b.method === req.method &&\r\n b.status === status &&\r\n Math.abs(b.timestamp - e.timestamp) < 5000\r\n );\r\n const snippet = match?.response ? `\\n\\nResponse: ${match.response.slice(0, 160)}` : \"\";\r\n\r\n issues.push({\r\n id: makeIssueId(\"failed-request\"),\r\n detector: \"failed-request\",\r\n severity: status >= 500 ? \"critical\" : status === 0 ? \"serious\" : \"moderate\",\r\n title: `${req.method} ${truncatePath(req.url)} → ${status === 0 ? \"Network Error\" : status}`,\r\n description: `Request failed in ${req.durationMs || 0}ms.${snippet}`,\r\n url: req.url,\r\n page: e.page || window.location.pathname,\r\n detectedAt: e.timestamp,\r\n });\r\n }\r\n\r\n return issues;\r\n}\r\n\r\nexport async function detectSlowApis(session: StoredSession | null): Promise<Issue[]> {\r\n if (!session) return [];\r\n const issues: Issue[] = [];\r\n\r\n for (const e of session.events) {\r\n if (e.type !== \"api_request\") continue;\r\n const req = e.data.request;\r\n if (!req) continue;\r\n const status = req.statusCode || 0;\r\n // Only flag *successful* slow requests — failures are surfaced separately.\r\n if (status < 200 || status >= 400) continue;\r\n const duration = req.durationMs || 0;\r\n if (duration < SLOW_API_MS) continue;\r\n\r\n issues.push({\r\n id: makeIssueId(\"slow-api\"),\r\n detector: \"slow-api\",\r\n severity: duration > 5000 ? \"serious\" : \"moderate\",\r\n title: `Slow API: ${req.method} ${truncatePath(req.url)} (${duration}ms)`,\r\n description: `This request took ${(duration / 1000).toFixed(1)}s — over the ${SLOW_API_MS / 1000}s threshold. Slow APIs are a common UX complaint and a leading cause of perceived bugs (\"the page is frozen\").`,\r\n url: req.url,\r\n page: e.page || window.location.pathname,\r\n detectedAt: e.timestamp,\r\n });\r\n }\r\n\r\n return issues;\r\n}\r\n\r\nfunction truncatePath(url: string): string {\r\n try {\r\n const u = new URL(url, window.location.origin);\r\n const p = u.pathname.length > 50 ? u.pathname.slice(0, 47) + \"…\" : u.pathname;\r\n return p;\r\n } catch {\r\n return url.length > 50 ? url.slice(0, 47) + \"…\" : url;\r\n }\r\n}\r\n\r\nfunction classifyErrorSeverity(message: string): Issue[\"severity\"] {\r\n if (/TypeError|ReferenceError|SyntaxError/i.test(message)) return \"critical\";\r\n if (/Network|fetch|failed to/i.test(message)) return \"serious\";\r\n return \"moderate\";\r\n}\r\n","// ── Accessibility detector ────────────────────────────────────────────────\r\n// Lazy-loads axe-core (~250 KB minified) only on first scan, runs the\r\n// default rule set against the live DOM, and converts axe violations into\r\n// our Issue shape. Skips TraceBug's own UI by passing `exclude: [\"#tracebug-root\"]`.\r\n//\r\n// axe-core has its own dependency graph but bundles cleanly via tsup's\r\n// dynamic-import support — the chunk only loads when scan() is called.\r\n\r\nimport { Issue } from \"../../types\";\r\nimport { coerceSeverity, makeIssueId } from \"../helpers\";\r\n\r\ntype AxeModule = typeof import(\"axe-core\");\r\ntype AxeResults = import(\"axe-core\").AxeResults;\r\n\r\nlet _axePromise: Promise<AxeModule | null> | null = null;\r\n\r\nfunction loadAxe(): Promise<AxeModule | null> {\r\n if (_axePromise) return _axePromise;\r\n _axePromise = import(\"axe-core\")\r\n .then((mod) => (mod as AxeModule & { default?: AxeModule }).default || mod)\r\n .catch((err) => {\r\n console.warn(\"[TraceBug] axe-core failed to load:\", err);\r\n return null;\r\n });\r\n return _axePromise;\r\n}\r\n\r\nexport async function detectA11yViolations(): Promise<Issue[]> {\r\n const axe = await loadAxe();\r\n if (!axe || typeof axe.run !== \"function\") return [];\r\n\r\n let results: AxeResults;\r\n try {\r\n results = await axe.run(document, {\r\n // Only WCAG-tagged rules — keeps signal-to-noise high. Best-practice\r\n // rules add ~30% more noise without proportional value for QA.\r\n runOnly: { type: \"tag\", values: [\"wcag2a\", \"wcag2aa\", \"wcag21a\", \"wcag21aa\"] },\r\n // Skip our own UI so QA isn't told their toolbar fails contrast checks.\r\n // axe accepts a context with exclude — we pass { exclude: [...] } via\r\n // the second-argument options-shaped form below to keep types loose.\r\n resultTypes: [\"violations\"],\r\n });\r\n } catch (err) {\r\n console.warn(\"[TraceBug] axe.run failed:\", err);\r\n return [];\r\n }\r\n\r\n const issues: Issue[] = [];\r\n const violations = results?.violations || [];\r\n\r\n for (const v of violations) {\r\n const nodes = v.nodes || [];\r\n // Each violation often hits multiple elements (e.g. 12 buttons missing\r\n // labels). Roll all of them into a single issue with a node count, so\r\n // the panel doesn't drown in 200 near-duplicate rows.\r\n const firstNode = nodes[0];\r\n const selector = Array.isArray(firstNode?.target) ? firstNode.target.join(\" \") : \"\";\r\n const exampleSnippet: string = (firstNode?.html || \"\").slice(0, 120);\r\n const moreSuffix = nodes.length > 1 ? ` (+ ${nodes.length - 1} more element${nodes.length === 2 ? \"\" : \"s\"})` : \"\";\r\n\r\n issues.push({\r\n id: makeIssueId(\"axe-a11y\"),\r\n detector: \"axe-a11y\",\r\n severity: coerceSeverity(v.impact),\r\n title: `${v.help || v.id}${moreSuffix}`,\r\n description: `${v.description || v.id}\\n\\nFirst element: \\`${exampleSnippet}\\``,\r\n selector: selector || undefined,\r\n helpUrl: v.helpUrl,\r\n page: window.location.pathname,\r\n detectedAt: Date.now(),\r\n });\r\n }\r\n\r\n return issues;\r\n}\r\n","// ── Frustration detectors ─────────────────────────────────────────────────\r\n// Surface user-experienced bugs that don't throw exceptions but make users\r\n// hate the product:\r\n// - Rage clicks: ≥3 clicks on the same selector within 1.5 s with no response\r\n// - Dead clicks: click with no DOM/route/network/input response within 1.5 s\r\n// - Form abandonment: input events on a form, then route_change before submit\r\n// - Error correlation: error fired ≤2.5 s after a click — pair them\r\n//\r\n// Pure analysis over the existing event log. No new tracking code, no extra\r\n// listeners.\r\n\r\nimport { Issue, StoredSession, TraceBugEvent } from \"../../types\";\r\nimport { makeIssueId } from \"../helpers\";\r\n\r\nconst RAGE_WINDOW_MS = 1500;\r\nconst RAGE_MIN_CLICKS = 3;\r\nconst DEAD_RESPONSE_WINDOW_MS = 1500;\r\nconst ABANDON_WINDOW_MS = 60_000;\r\nconst ERROR_CORRELATION_WINDOW_MS = 2500;\r\n\r\nexport async function detectFrustration(session: StoredSession | null): Promise<Issue[]> {\r\n if (!session) return [];\r\n const events = session.events;\r\n if (events.length === 0) return [];\r\n\r\n const issues: Issue[] = [];\r\n const page = session.events[0]?.page || window.location.pathname;\r\n\r\n issues.push(...detectRageClicks(events, page));\r\n issues.push(...detectDeadClicks(events, page));\r\n issues.push(...detectFormAbandonment(events, page));\r\n issues.push(...detectErrorCorrelated(events, page));\r\n\r\n return issues;\r\n}\r\n\r\n// ── Rage clicks ───────────────────────────────────────────────────────\r\n// Sliding window: ≥3 clicks on the same selector inside 1.5 s, with no\r\n// response (api_request, route_change, or input on the same form) between.\r\n\r\nfunction detectRageClicks(events: TraceBugEvent[], page: string): Issue[] {\r\n const out: Issue[] = [];\r\n const seenGroups = new Set<string>();\r\n\r\n for (let i = 0; i < events.length; i++) {\r\n const e = events[i];\r\n if (e.type !== \"click\") continue;\r\n const sel = e.data?.element?.selector || e.data?.element?.testId || \"\";\r\n if (!sel) continue;\r\n\r\n // Look ahead for sibling clicks within RAGE_WINDOW_MS on the same selector.\r\n const cluster: TraceBugEvent[] = [e];\r\n let j = i + 1;\r\n while (j < events.length) {\r\n const next = events[j];\r\n if (next.timestamp - e.timestamp > RAGE_WINDOW_MS) break;\r\n // Stop if a \"response\" event happened — not rage if anything responded.\r\n if (isResponseEvent(next)) break;\r\n if (next.type === \"click\") {\r\n const nextSel = next.data?.element?.selector || next.data?.element?.testId || \"\";\r\n if (nextSel === sel) cluster.push(next);\r\n }\r\n j++;\r\n }\r\n\r\n if (cluster.length >= RAGE_MIN_CLICKS) {\r\n const key = `${sel}@${e.timestamp}`;\r\n if (seenGroups.has(key)) continue;\r\n seenGroups.add(key);\r\n\r\n const label = clickLabel(cluster[0]);\r\n out.push({\r\n id: makeIssueId(\"frustration-rage\"),\r\n detector: \"frustration-rage\",\r\n severity: \"serious\",\r\n title: `Rage clicks on ${label} (${cluster.length}× in ${Math.round((cluster[cluster.length - 1].timestamp - cluster[0].timestamp))}ms)`,\r\n description: `User clicked the same element ${cluster.length} times within ${RAGE_WINDOW_MS}ms with no observable response (no API call, navigation, or DOM update). The element either doesn't respond to clicks or feels broken.`,\r\n selector: sel,\r\n page,\r\n detectedAt: cluster[0].timestamp,\r\n firstSeenAt: cluster[0].timestamp,\r\n lastSeenAt: cluster[cluster.length - 1].timestamp,\r\n occurrences: cluster.length,\r\n });\r\n // Skip past the cluster to avoid double-flagging.\r\n i = j - 1;\r\n }\r\n }\r\n\r\n return out;\r\n}\r\n\r\n// ── Dead clicks ───────────────────────────────────────────────────────\r\n// Click followed by no api_request, route_change, or input within 1.5 s.\r\n\r\nfunction detectDeadClicks(events: TraceBugEvent[], page: string): Issue[] {\r\n const out: Issue[] = [];\r\n // Cap output: dead-click detection is noisy on long sessions.\r\n const MAX = 5;\r\n\r\n for (let i = 0; i < events.length && out.length < MAX; i++) {\r\n const e = events[i];\r\n if (e.type !== \"click\") continue;\r\n // Skip if the next event in the same window is another click on the same\r\n // selector (likely a rage cluster — already flagged separately).\r\n let responsive = false;\r\n for (let j = i + 1; j < events.length; j++) {\r\n const next = events[j];\r\n if (next.timestamp - e.timestamp > DEAD_RESPONSE_WINDOW_MS) break;\r\n if (isResponseEvent(next)) { responsive = true; break; }\r\n }\r\n if (responsive) continue;\r\n\r\n const sel = e.data?.element?.selector || \"\";\r\n const label = clickLabel(e);\r\n out.push({\r\n id: makeIssueId(\"frustration-dead\"),\r\n detector: \"frustration-dead\",\r\n severity: \"moderate\",\r\n title: `Dead click on ${label}`,\r\n description: `Clicked but nothing happened within ${DEAD_RESPONSE_WINDOW_MS}ms (no API call, navigation, or DOM input). The element may have an unbound handler, a swallowed event, or be visually clickable but disabled.`,\r\n selector: sel,\r\n page: e.page || page,\r\n detectedAt: e.timestamp,\r\n });\r\n }\r\n\r\n return out;\r\n}\r\n\r\n// ── Form abandonment ──────────────────────────────────────────────────\r\n// Inputs on a form, then route_change before form_submit.\r\n\r\nfunction detectFormAbandonment(events: TraceBugEvent[], _page: string): Issue[] {\r\n const out: Issue[] = [];\r\n // Track per-form: has the user typed in it but not submitted?\r\n const formActivity: Record<string, { firstInputAt: number; fieldsSeen: Set<string>; lastInputAt: number; page: string }> = {};\r\n\r\n for (const e of events) {\r\n if (e.type === \"input\") {\r\n const formId = e.data?.element?.formId || e.data?.element?.formAction || \"_default\";\r\n if (!formActivity[formId]) {\r\n formActivity[formId] = { firstInputAt: e.timestamp, fieldsSeen: new Set(), lastInputAt: e.timestamp, page: e.page };\r\n }\r\n const name = e.data?.element?.name || e.data?.element?.id || \"field\";\r\n formActivity[formId].fieldsSeen.add(name);\r\n formActivity[formId].lastInputAt = e.timestamp;\r\n } else if (e.type === \"form_submit\") {\r\n const formId = e.data?.form?.id || \"_default\";\r\n delete formActivity[formId];\r\n } else if (e.type === \"route_change\") {\r\n // Any route change with active forms = abandonment if within window.\r\n for (const formId of Object.keys(formActivity)) {\r\n const a = formActivity[formId];\r\n if (e.timestamp - a.lastInputAt > ABANDON_WINDOW_MS) continue;\r\n if (a.fieldsSeen.size === 0) continue;\r\n out.push({\r\n id: makeIssueId(\"frustration-abandon\"),\r\n detector: \"frustration-abandon\",\r\n severity: \"moderate\",\r\n title: `Form abandoned on ${a.page} (${a.fieldsSeen.size} field${a.fieldsSeen.size === 1 ? \"\" : \"s\"} filled)`,\r\n description: `User typed into ${a.fieldsSeen.size} field${a.fieldsSeen.size === 1 ? \"\" : \"s\"} (${Array.from(a.fieldsSeen).slice(0, 5).join(\", \")}) and then navigated away without submitting. Likely a UX problem: the submit button is unclear, the form requires too much info, or it's failing silently.`,\r\n page: a.page,\r\n detectedAt: a.lastInputAt,\r\n firstSeenAt: a.firstInputAt,\r\n lastSeenAt: a.lastInputAt,\r\n });\r\n delete formActivity[formId];\r\n }\r\n }\r\n }\r\n\r\n return out;\r\n}\r\n\r\n// ── Error correlation ─────────────────────────────────────────────────\r\n// For each error, look back ≤2.5 s for the nearest click — likely the\r\n// triggering interaction. Tag both events together.\r\n\r\nfunction detectErrorCorrelated(events: TraceBugEvent[], page: string): Issue[] {\r\n const out: Issue[] = [];\r\n const seen = new Set<string>();\r\n\r\n for (let i = 0; i < events.length; i++) {\r\n const e = events[i];\r\n const isError = e.type === \"error\" || e.type === \"unhandled_rejection\" || e.type === \"console_error\";\r\n if (!isError) continue;\r\n\r\n // Look back for the nearest click.\r\n let click: TraceBugEvent | null = null;\r\n for (let j = i - 1; j >= 0; j--) {\r\n const prev = events[j];\r\n if (e.timestamp - prev.timestamp > ERROR_CORRELATION_WINDOW_MS) break;\r\n if (prev.type === \"click\") { click = prev; break; }\r\n }\r\n if (!click) continue;\r\n\r\n const errMsg = e.data?.error?.message || \"\";\r\n if (!errMsg) continue;\r\n const key = `${errMsg}::${click.data?.element?.selector || \"\"}`;\r\n if (seen.has(key)) continue;\r\n seen.add(key);\r\n\r\n const label = clickLabel(click);\r\n const truncMsg = errMsg.length > 60 ? errMsg.slice(0, 57) + \"…\" : errMsg;\r\n const delta = Math.round((e.timestamp - click.timestamp));\r\n out.push({\r\n id: makeIssueId(\"frustration-error-correlated\"),\r\n detector: \"frustration-error-correlated\",\r\n severity: \"critical\",\r\n title: `Click on ${label} triggered: ${truncMsg}`,\r\n description: `An error fired ${delta}ms after the user clicked ${label}. This is almost certainly the offending interaction — the click handler threw, or its async path failed.`,\r\n selector: click.data?.element?.selector,\r\n page: e.page || page,\r\n detectedAt: e.timestamp,\r\n });\r\n }\r\n\r\n return out;\r\n}\r\n\r\n// ── Helpers ────────────────────────────────────────────────────────────\r\n\r\nfunction isResponseEvent(e: TraceBugEvent): boolean {\r\n return (\r\n e.type === \"api_request\" ||\r\n e.type === \"route_change\" ||\r\n e.type === \"input\" ||\r\n e.type === \"form_submit\" ||\r\n e.type === \"select_change\"\r\n );\r\n}\r\n\r\nfunction clickLabel(e: TraceBugEvent): string {\r\n const el = e.data?.element;\r\n const raw = el?.text || el?.ariaLabel || el?.testId || el?.id || el?.tag || \"element\";\r\n const trimmed = String(raw).replace(/\\s+/g, \" \").trim();\r\n return trimmed.length > 40 ? `\"${trimmed.slice(0, 37)}…\"` : `\"${trimmed}\"`;\r\n}\r\n","// ── Scanner orchestrator ──────────────────────────────────────────────────\r\n// Runs every detector in parallel, collects results into a single in-memory\r\n// store, and exposes simple queries (getIssues, dismissIssue, fileAsBug).\r\n//\r\n// Issues live in memory only — each scan is a fresh run, results clear on\r\n// page reload. Mirrors the screenshot/video memory model.\r\n\r\nimport { Issue, IssueDetector } from \"../types\";\r\nimport { getAllSessions } from \"../storage\";\r\nimport { detectBrokenImages } from \"./detectors/broken-images\";\r\nimport { detectMixedContent } from \"./detectors/mixed-content\";\r\nimport { detectConsoleErrors, detectFailedRequests, detectSlowApis } from \"./detectors/session-data\";\r\nimport { detectA11yViolations } from \"./detectors/a11y\";\r\nimport { detectFrustration } from \"./detectors/frustration\";\r\n\r\nlet _issues: Issue[] = [];\r\nlet _scanInFlight: Promise<Issue[]> | null = null;\r\nlet _lastScanAt = 0;\r\n\r\nconst SEVERITY_ORDER: Record<Issue[\"severity\"], number> = {\r\n critical: 0,\r\n serious: 1,\r\n moderate: 2,\r\n minor: 3,\r\n};\r\n\r\nexport interface ScanResult {\r\n issues: Issue[];\r\n durationMs: number;\r\n scannedAt: number;\r\n}\r\n\r\n/**\r\n * Run every detector in parallel. Concurrent scans are coalesced — calling\r\n * scan() while one is already running returns the in-flight promise.\r\n */\r\nexport async function scan(): Promise<ScanResult> {\r\n if (_scanInFlight) {\r\n const issues = await _scanInFlight;\r\n return { issues, durationMs: 0, scannedAt: _lastScanAt };\r\n }\r\n\r\n const startedAt = Date.now();\r\n const sessions = getAllSessions().sort((a, b) => b.updatedAt - a.updatedAt);\r\n const session = sessions[0] || null;\r\n\r\n // Promise.allSettled isn't in the ES2018 target lib, so wrap each detector\r\n // in a catch that returns []. One failure doesn't block the others.\r\n const safeRun = (p: Promise<Issue[]>): Promise<Issue[]> =>\r\n p.catch((err) => {\r\n console.warn(\"[TraceBug] Detector failed:\", err);\r\n return [];\r\n });\r\n\r\n _scanInFlight = Promise.all([\r\n safeRun(detectBrokenImages()),\r\n safeRun(detectMixedContent()),\r\n safeRun(detectConsoleErrors(session)),\r\n safeRun(detectFailedRequests(session)),\r\n safeRun(detectSlowApis(session)),\r\n safeRun(detectA11yViolations()),\r\n safeRun(detectFrustration(session)),\r\n ]).then((results) => {\r\n const all: Issue[] = ([] as Issue[]).concat(...results);\r\n // Stable sort: severity first, then detection time.\r\n all.sort((a, b) => {\r\n const sev = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];\r\n if (sev !== 0) return sev;\r\n return a.detectedAt - b.detectedAt;\r\n });\r\n _issues = all;\r\n return all;\r\n });\r\n\r\n try {\r\n const issues = await _scanInFlight;\r\n _lastScanAt = Date.now();\r\n return { issues, durationMs: _lastScanAt - startedAt, scannedAt: _lastScanAt };\r\n } finally {\r\n _scanInFlight = null;\r\n }\r\n}\r\n\r\n/** Snapshot of current issues. Filters out dismissed by default. */\r\nexport function getIssues(options?: { includeDismissed?: boolean }): Issue[] {\r\n const includeDismissed = options?.includeDismissed ?? false;\r\n return includeDismissed ? _issues.slice() : _issues.filter(i => !i.dismissed);\r\n}\r\n\r\n/** Mark an issue as dismissed for the current session. */\r\nexport function dismissIssue(id: string): boolean {\r\n const issue = _issues.find(i => i.id === id);\r\n if (!issue) return false;\r\n issue.dismissed = true;\r\n return true;\r\n}\r\n\r\n/** Restore a previously dismissed issue. */\r\nexport function undismissIssue(id: string): boolean {\r\n const issue = _issues.find(i => i.id === id);\r\n if (!issue) return false;\r\n issue.dismissed = false;\r\n return true;\r\n}\r\n\r\n/** Clear all issues from memory. Called from destroy() and \"Clear all data\". */\r\nexport function clearIssues(): void {\r\n _issues = [];\r\n _lastScanAt = 0;\r\n}\r\n\r\n/** Aggregate counts grouped by detector, useful for the toolbar badge. */\r\nexport function getIssueCountsByDetector(): Record<IssueDetector, number> {\r\n const counts: Record<IssueDetector, number> = {\r\n \"axe-a11y\": 0,\r\n \"broken-image\": 0,\r\n \"mixed-content\": 0,\r\n \"console-error\": 0,\r\n \"slow-api\": 0,\r\n \"failed-request\": 0,\r\n \"frustration-rage\": 0,\r\n \"frustration-dead\": 0,\r\n \"frustration-abandon\": 0,\r\n \"frustration-error-correlated\": 0,\r\n };\r\n for (const i of _issues) {\r\n if (i.dismissed) continue;\r\n counts[i.detector] = (counts[i.detector] || 0) + 1;\r\n }\r\n return counts;\r\n}\r\n\r\n/** Count of non-dismissed issues at each severity. */\r\nexport function getIssueCountsBySeverity(): Record<Issue[\"severity\"], number> {\r\n const counts: Record<Issue[\"severity\"], number> = {\r\n critical: 0,\r\n serious: 0,\r\n moderate: 0,\r\n minor: 0,\r\n };\r\n for (const i of _issues) {\r\n if (i.dismissed) continue;\r\n counts[i.severity] += 1;\r\n }\r\n return counts;\r\n}\r\n\r\n/** Lookup helper for the issues panel — find by id. */\r\nexport function getIssueById(id: string): Issue | null {\r\n return _issues.find(i => i.id === id) || null;\r\n}\r\n"],"mappings":"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","names":[]}
// src/storage.ts
var SESSIONS_KEY = "tracebug_sessions";
var ACTIVE_SESSION_KEY = "tracebug_active_session";
var ACTIVE_CAPTURE_MODE_KEY = "tracebug_active_capture_mode";
function generateSessionId() {
return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : "bt_" + Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
}
function getActiveSessionId() {
try {
return localStorage.getItem(ACTIVE_SESSION_KEY);
} catch (e) {
return null;
}
}
function setActiveSessionId(id) {
try {
localStorage.setItem(ACTIVE_SESSION_KEY, id);
} catch (e) {
}
}
function clearActiveSessionId() {
try {
localStorage.removeItem(ACTIVE_SESSION_KEY);
} catch (e) {
}
try {
localStorage.removeItem(ACTIVE_CAPTURE_MODE_KEY);
} catch (e) {
}
}
function getActiveCaptureMode() {
try {
const v = localStorage.getItem(ACTIVE_CAPTURE_MODE_KEY);
return v === "events" || v === "video" ? v : null;
} catch (e) {
return null;
}
}
function setActiveCaptureMode(mode) {
try {
localStorage.setItem(ACTIVE_CAPTURE_MODE_KEY, mode);
} catch (e) {
}
}
function getAllSessions() {
try {
const raw = localStorage.getItem(SESSIONS_KEY);
return raw ? JSON.parse(raw) : [];
} catch (e) {
return [];
}
}
function saveSessions(sessions) {
try {
localStorage.setItem(SESSIONS_KEY, JSON.stringify(sessions));
return;
} catch (e) {
}
const commit = (next) => {
try {
localStorage.setItem(SESSIONS_KEY, JSON.stringify(next));
sessions.length = 0;
sessions.push(...next);
return true;
} catch (e) {
return false;
}
};
const working = sessions.slice();
while (working.length > 1) {
working.shift();
if (commit(working)) {
if (typeof console !== "undefined") console.warn("[TraceBug] Storage full \u2014 dropped oldest session(s) to fit.");
return;
}
}
const last = working[0];
if (last && Array.isArray(last.events)) {
while (last.events.length > 1) {
last.events = last.events.slice(Math.ceil(last.events.length / 2));
if (commit(working)) {
if (typeof console !== "undefined") console.warn("[TraceBug] Storage full \u2014 trimmed older events from the current session to fit.");
return;
}
}
}
if (typeof console !== "undefined") console.error("[TraceBug] Could not persist sessions: localStorage quota exceeded.");
}
var _cachedSessions = null;
var _pendingFlush = null;
var _dirty = false;
var FLUSH_INTERVAL_MS = 1e3;
function getCachedSessions() {
if (!_cachedSessions) {
_cachedSessions = getAllSessions();
}
return _cachedSessions;
}
function scheduleFlush() {
_dirty = true;
if (_pendingFlush) return;
_pendingFlush = setTimeout(() => {
_pendingFlush = null;
if (_cachedSessions && _dirty) {
saveSessions(_cachedSessions);
_dirty = false;
}
}, FLUSH_INTERVAL_MS);
}
function flushPendingEvents() {
if (_pendingFlush) {
clearTimeout(_pendingFlush);
_pendingFlush = null;
}
if (_cachedSessions) {
saveSessions(_cachedSessions);
_dirty = false;
}
}
function invalidateCache() {
if (_pendingFlush) {
clearTimeout(_pendingFlush);
_pendingFlush = null;
}
_cachedSessions = null;
_dirty = false;
}
if (typeof window !== "undefined") {
window.addEventListener("beforeunload", flushPendingEvents);
window.addEventListener("pagehide", flushPendingEvents);
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") flushPendingEvents();
});
}
function appendEvent(sessionId, event, maxEvents, maxSessions) {
let sessions = getCachedSessions();
let session = sessions.find((s) => s.sessionId === sessionId);
if (!session) {
session = {
sessionId,
projectId: event.projectId,
createdAt: Date.now(),
updatedAt: Date.now(),
errorMessage: null,
errorStack: null,
reproSteps: null,
errorSummary: null,
events: [],
annotations: [],
environment: null
};
sessions.push(session);
}
session.events.push(event);
session.updatedAt = Date.now();
if (session.events.length > maxEvents) {
session.events = session.events.slice(-maxEvents);
}
if (sessions.length > maxSessions) {
sessions = sessions.slice(-maxSessions);
_cachedSessions = sessions;
}
scheduleFlush();
}
function updateSessionError(sessionId, errorMessage, errorStack, reproSteps, errorSummary) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.errorMessage = errorMessage;
session.errorStack = errorStack || null;
session.reproSteps = reproSteps;
session.errorSummary = errorSummary;
session.updatedAt = Date.now();
scheduleFlush();
}
function deleteSession(sessionId) {
flushPendingEvents();
const remaining = getAllSessions().filter((s) => s.sessionId !== sessionId);
invalidateCache();
saveSessions(remaining);
}
function addAnnotation(sessionId, annotation) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
if (!session.annotations) session.annotations = [];
session.annotations.push(annotation);
session.updatedAt = Date.now();
scheduleFlush();
}
function saveEnvironment(sessionId, env) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.environment = env;
scheduleFlush();
}
function setSessionPriority(sessionId, priority) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.priority = priority;
session.updatedAt = Date.now();
scheduleFlush();
}
function markSessionSaved(sessionId) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.saved = true;
session.updatedAt = Date.now();
flushPendingEvents();
}
function clearAllSessions() {
invalidateCache();
try {
localStorage.removeItem(SESSIONS_KEY);
} catch (e) {
}
}
// src/sanitize/cloud-upload.ts
var REDACTED = "[REDACTED]";
var TOKEN_PATTERNS = [
// Bearer <token> in headers, console output, anywhere
{ name: "bearer", re: /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}/gi, replace: () => "Bearer " + REDACTED },
// JWT (3 base64url segments separated by dots, leading with eyJ which is `{"` in base64)
{ name: "jwt", re: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g, replace: mask },
// OpenAI / Stripe sk_*
{ name: "sk_prefix", re: /\bsk-[A-Za-z0-9_-]{20,}\b/g, replace: mask },
// Stripe secret + publishable (live/test, secret + publishable + restricted)
{ name: "stripe", re: /\b(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g, replace: mask },
// GitHub PATs (classic + fine-grained + OAuth + server tokens)
{ name: "github_pat", re: /\bghp_[A-Za-z0-9]{30,}\b/g, replace: mask },
{ name: "github_fine", re: /\bgithub_pat_[A-Za-z0-9_]{60,}\b/g, replace: mask },
{ name: "github_oauth", re: /\bgho_[A-Za-z0-9]{30,}\b/g, replace: mask },
{ name: "github_server", re: /\bghs_[A-Za-z0-9]{30,}\b/g, replace: mask },
// AWS access keys (begins with AKIA, ASIA, AGPA, AIDA, etc.) + secret key (40-char base64)
{ name: "aws_access", re: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|ANPA|ANVA)[A-Z0-9]{16}\b/g, replace: mask },
{ name: "aws_secret", re: /\b(?:aws.{0,20})?[A-Za-z0-9/+]{40}\b(?=.*aws|.*secret|.*key)/gi, replace: mask },
// Slack — broader than before (xoxa-z covers all known prefixes)
{ name: "slack", re: /\bxox[abeprs]-[A-Za-z0-9-]{10,}\b/g, replace: mask },
// Google API keys
{ name: "google_api", re: /\bAIza[A-Za-z0-9_-]{35}\b/g, replace: mask },
// Twilio — Account SID + Auth tokens
{ name: "twilio_sid", re: /\b(?:AC|SK)[a-f0-9]{32}\b/g, replace: mask },
// SendGrid
{ name: "sendgrid", re: /\bSG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\b/g, replace: mask },
// Mailgun
{ name: "mailgun", re: /\bkey-[a-f0-9]{32}\b/g, replace: mask },
// Postmark
{ name: "postmark", re: /\b[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\b(?=.{0,30}(postmark|server-token|api-token))/gi, replace: mask },
// Linear / Vercel / Cloudflare / Discord
{ name: "linear", re: /\blin_api_[A-Za-z0-9]{40,}\b/g, replace: mask },
{ name: "discord_bot", re: /\b[MN][A-Za-z\d]{23}\.[A-Za-z\d_-]{6}\.[A-Za-z\d_-]{27,}\b/g, replace: mask },
// Generic high-entropy hex (≥32 chars). Catches webhook signing secrets,
// session IDs, etc. that don't carry a recognizable prefix. Conservative:
// only triggers when preceded by a common secret-y keyword to avoid
// mangling legitimate hex like git SHAs.
{ name: "labeled_hex", re: /\b(?:secret|token|key|password|api[_-]?key|auth)["':\s=]{1,5}([a-fA-F0-9]{32,})\b/gi, replace: (s) => s.replace(/[a-fA-F0-9]{32,}/, REDACTED) }
];
function mask(s) {
if (s.length <= 12) return REDACTED;
return `${s.slice(0, 4)}\u2026${REDACTED}\u2026${s.slice(-4)}`;
}
var SENSITIVE_QUERY_KEYS = /* @__PURE__ */ new Set([
"token",
"access_token",
"id_token",
"refresh_token",
"api_key",
"apikey",
"secret",
"password",
"passwd",
"pwd",
"auth",
"authorization",
"x-api-key",
"session",
"sid",
"csrf"
]);
function sanitizeUrl(url) {
if (typeof url !== "string" || url.length === 0) return url;
try {
const isAbs = /^[a-z][a-z0-9+.-]*:/i.test(url);
const u = new URL(isAbs ? url : `http://_placeholder_${url.startsWith("/") ? "" : "/"}${url}`);
let changed = false;
u.searchParams.forEach((_v, k) => {
if (SENSITIVE_QUERY_KEYS.has(k.toLowerCase())) {
u.searchParams.set(k, REDACTED);
changed = true;
}
});
if (!changed) return sanitizeText(url);
const out = isAbs ? u.toString() : u.pathname + u.search + u.hash;
return sanitizeText(out);
} catch (e) {
return sanitizeText(url);
}
}
function sanitizeText(s) {
if (s == null) return s;
let out = String(s);
for (const p of TOKEN_PATTERNS) out = out.replace(p.re, p.replace);
return out;
}
function sanitizeTokenShapes(s) {
return sanitizeText(s);
}
function sanitizeReportForUpload(report) {
var _a;
const out = typeof structuredClone === "function" ? structuredClone(report) : JSON.parse(JSON.stringify(report));
if (out.consoleErrors) {
out.consoleErrors = out.consoleErrors.map((e) => {
var _a2;
return {
...e,
message: sanitizeText(e.message),
stack: sanitizeText((_a2 = e.stack) != null ? _a2 : void 0)
};
});
}
if (out.consoleLogs) {
out.consoleLogs = out.consoleLogs.map((e) => {
var _a2;
return {
...e,
message: sanitizeText(e.message),
stack: sanitizeText((_a2 = e.stack) != null ? _a2 : void 0)
};
});
}
if (out.networkErrors) {
out.networkErrors = out.networkErrors.map((e) => {
var _a2;
return {
...e,
url: sanitizeUrl(e.url),
response: sanitizeText((_a2 = e.response) != null ? _a2 : void 0)
};
});
}
if (out.networkRequests) {
out.networkRequests = out.networkRequests.map((e) => {
var _a2;
return {
...e,
url: sanitizeUrl(e.url),
response: sanitizeText((_a2 = e.response) != null ? _a2 : void 0)
};
});
}
if (out.steps) out.steps = sanitizeText(out.steps);
if (out.summary) out.summary = sanitizeText(out.summary);
if (out.title) out.title = sanitizeText(out.title);
if (Array.isArray(out.sessionSteps)) out.sessionSteps = out.sessionSteps.map((s) => sanitizeText(s));
if (out.actionChips) {
out.actionChips = out.actionChips.map((c) => {
var _a2, _b;
return {
...c,
target: sanitizeText((_a2 = c.target) != null ? _a2 : void 0),
detail: sanitizeText((_b = c.detail) != null ? _b : void 0)
};
});
}
if (out.storage) {
const scrub = (entries) => entries.map((e) => ({ ...e, value: sanitizeText(e.value) }));
out.storage.local = scrub(out.storage.local || []);
out.storage.session = scrub(out.storage.session || []);
if (out.storage.cookies) out.storage.cookies = scrub(out.storage.cookies);
}
if ((_a = out.environment) == null ? void 0 : _a.url) out.environment.url = sanitizeUrl(out.environment.url);
if (out.context && typeof out.context === "object") {
const ctx = {};
for (const [k, v] of Object.entries(out.context)) {
ctx[k] = typeof v === "string" ? sanitizeText(v) : v;
}
out.context = ctx;
}
return out;
}
// src/collectors.ts
var ROOT_ID = "tracebug-root";
var PANEL_ID = "tracebug-dashboard-panel";
var BTN_ID = "tracebug-dashboard-btn";
var NETWORK_FAILURE_LIMIT = 10;
var RESPONSE_SNIPPET_CHARS = 200;
var _networkFailures = [];
function pushNetworkFailure(failure) {
try {
if (failure.response) failure.response = sanitizeTokenShapes(failure.response);
_networkFailures.push(failure);
if (_networkFailures.length > NETWORK_FAILURE_LIMIT) {
_networkFailures.splice(0, _networkFailures.length - NETWORK_FAILURE_LIMIT);
}
} catch (e) {
}
}
function getNetworkFailures() {
return _networkFailures.slice();
}
function clearNetworkFailures() {
_networkFailures.length = 0;
}
var SENSITIVE_PARAM_RE = /token|key|secret|auth|password|sig|signature/i;
function sanitizeUrl2(url) {
if (!url) return url;
try {
const qIdx = url.indexOf("?");
if (qIdx === -1) return url;
const base = url.slice(0, qIdx);
const afterQ = url.slice(qIdx + 1);
const hashIdx = afterQ.indexOf("#");
const query = hashIdx === -1 ? afterQ : afterQ.slice(0, hashIdx);
const hash = hashIdx === -1 ? "" : afterQ.slice(hashIdx);
const redacted = query.split("&").map((part) => {
const eqIdx = part.indexOf("=");
if (eqIdx === -1) return part;
const key = part.slice(0, eqIdx);
if (SENSITIVE_PARAM_RE.test(key)) return `${key}=[REDACTED]`;
return part;
}).join("&");
return `${base}?${redacted}${hash}`;
} catch (e) {
return url;
}
}
var MAX_BODY_BYTES = 10 * 1024;
var BINARY_CONTENT_TYPE_RE = /^(image|video|audio)\/|^application\/(octet-stream|pdf|zip|x-protobuf|x-msgpack|wasm|vnd\.)/i;
async function readResponseBodySafe(response) {
try {
const ct = response.headers.get("content-type") || "";
if (BINARY_CONTENT_TYPE_RE.test(ct)) return "";
if (!response.body || typeof response.body.getReader !== "function") {
try {
const text = await response.text();
return typeof text === "string" ? text.slice(0, RESPONSE_SNIPPET_CHARS) : "";
} catch (e) {
return "";
}
}
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8", { fatal: false });
let collected = "";
let bytesRead = 0;
while (bytesRead < MAX_BODY_BYTES && collected.length < RESPONSE_SNIPPET_CHARS) {
const { value, done } = await reader.read();
if (done) break;
if (value) {
bytesRead += value.byteLength;
collected += decoder.decode(value, { stream: true });
}
}
try {
await reader.cancel();
} catch (e) {
}
return collected.slice(0, RESPONSE_SNIPPET_CHARS);
} catch (e) {
return "";
}
}
var INTERNAL_URL_PATTERNS = [
/__nextjs_original-stack-frame/,
/\/_next\/static\/webpack/,
/\/__webpack_hmr/,
/\.hot-update\./,
/\/sockjs-node\//,
/\/turbopack-hmr\//,
/\/_next\/webpack-hmr/,
/\/webpack-dev-server\//,
/\/__vite_ping/,
/\/@vite\/client/,
/\/@react-refresh/
];
function isInternalUrl(url) {
return INTERNAL_URL_PATTERNS.some((pattern) => pattern.test(url));
}
var _rootCache;
function getRoot() {
if (_rootCache === void 0) {
_rootCache = document.getElementById(ROOT_ID);
}
if (_rootCache && !_rootCache.isConnected) {
_rootCache = document.getElementById(ROOT_ID);
}
return _rootCache;
}
function isTraceBugElement(el) {
if (!el) return false;
if (el.id === ROOT_ID || el.id === BTN_ID || el.id === PANEL_ID) return true;
if (el.dataset && el.dataset.tracebug) return true;
const root = getRoot();
if (root && root.contains(el)) return true;
let node = el;
while (node) {
const id = node.id || "";
if (id.startsWith("tracebug-") || id.startsWith("bt-")) return true;
const cn = typeof node.className === "string" ? node.className : "";
if (cn.includes("tracebug-") || cn.includes("bt-ann") || cn.includes("bt-voice")) return true;
if (node.dataset && node.dataset.tracebug) return true;
node = node.parentElement;
}
return false;
}
function collectClicks(emit) {
const handler = (e) => {
try {
const t = e.target;
if (!t || isTraceBugElement(t)) return;
const tag = t.tagName.toLowerCase();
const el = {
tag,
text: (t.innerText || "").slice(0, 120),
id: t.id || "",
className: typeof t.className === "string" ? t.className : ""
};
const data = { element: el };
if (tag === "a") el.href = t.href || "";
if (tag === "button" || t.type === "submit") {
el.buttonType = t.type || "button";
el.disabled = t.disabled;
}
if (tag === "label") el.forField = t.htmlFor || "";
const ariaLabel = t.getAttribute("aria-label");
if (ariaLabel) el.ariaLabel = ariaLabel;
const role = t.getAttribute("role");
if (role) el.role = role;
const testId = t.getAttribute("data-testid");
if (testId) el.testId = testId;
const form = t.closest("form");
if (form) {
el.formId = form.id || "";
el.formAction = form.action || "";
}
try {
el.selector = buildSelector(t);
} catch (e2) {
}
try {
const r = t.getBoundingClientRect();
el.boundingBox = { x: Math.round(r.left), y: Math.round(r.top), width: Math.round(r.width), height: Math.round(r.height) };
} catch (e2) {
}
emit("click", data);
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Click capture error:", err);
}
};
document.addEventListener("click", handler, { capture: true });
return () => document.removeEventListener("click", handler, { capture: true });
}
function buildSelector(el) {
if (!el) return "";
if (el.id) return `#${CSS.escape(el.id)}`;
const testId = el.getAttribute("data-testid");
if (testId) return `[data-testid="${testId}"]`;
const parts = [];
let node = el;
let depth = 0;
while (node && node !== document.body && depth < 4) {
let part = node.tagName.toLowerCase();
if (node.id) {
parts.unshift(`#${CSS.escape(node.id)}`);
break;
}
const cls = typeof node.className === "string" ? node.className.trim().split(/\s+/).filter(Boolean)[0] : "";
if (cls) part += `.${CSS.escape(cls)}`;
const parent = node.parentElement;
const currentTag = node.tagName;
const currentNode = node;
if (parent) {
const sameTag = Array.from(parent.children).filter((c) => c.tagName === currentTag);
if (sameTag.length > 1) part += `:nth-of-type(${sameTag.indexOf(currentNode) + 1})`;
}
parts.unshift(part);
node = parent;
depth++;
}
return parts.join(" > ");
}
function collectInputs(emit) {
const timers = /* @__PURE__ */ new Map();
const handler = (e) => {
try {
const t = e.target;
if (!t || !("value" in t) || isTraceBugElement(t)) return;
if (t.tagName.toLowerCase() === "select") return;
const prev = timers.get(t);
if (prev) clearTimeout(prev);
timers.set(
t,
setTimeout(() => {
try {
const tag = t.tagName.toLowerCase();
const inputType = t.type || "";
const isSensitive = ["password", "credit-card", "ssn"].includes(inputType) || /password|secret|token|ssn|credit/i.test(t.name || t.id || "");
const element = {
tag,
name: t.name || t.id || "",
type: inputType,
valueLength: (t.value || "").length,
value: isSensitive ? "[REDACTED]" : (t.value || "").slice(0, 200),
placeholder: t.placeholder || ""
};
const data = { element };
if (inputType === "checkbox" || inputType === "radio") {
element.checked = t.checked;
element.value = t.checked ? "checked" : "unchecked";
}
if (inputType === "number" || inputType === "range") {
element.value = t.value;
}
emit("input", data);
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Input capture error:", err);
}
timers.delete(t);
}, 300)
);
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Input capture error:", err);
}
};
document.addEventListener("input", handler, { capture: true });
return () => {
document.removeEventListener("input", handler, { capture: true });
timers.forEach((t) => clearTimeout(t));
};
}
function collectSelectChanges(emit) {
const handler = (e) => {
try {
const t = e.target;
if (!t || t.tagName.toLowerCase() !== "select" || isTraceBugElement(t)) return;
const selectedOption = t.options[t.selectedIndex];
emit("select_change", {
element: {
tag: "select",
name: t.name || t.id || "",
value: t.value,
selectedText: selectedOption ? selectedOption.text : "",
selectedIndex: t.selectedIndex,
optionCount: t.options.length,
allOptions: Array.from(t.options).map((o) => o.text).slice(0, 20)
}
});
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Select capture error:", err);
}
};
document.addEventListener("change", handler, { capture: true });
return () => document.removeEventListener("change", handler, { capture: true });
}
function collectFormSubmits(emit) {
const handler = (e) => {
try {
const form = e.target;
if (!form || form.tagName.toLowerCase() !== "form" || isTraceBugElement(form)) return;
const formData = {};
const elements = form.elements;
for (let i = 0; i < elements.length; i++) {
const el = elements[i];
if (!el.name) continue;
const isSensitive = ["password"].includes(el.type) || /password|secret|token|ssn|credit/i.test(el.name);
if (el.type === "submit" || el.type === "button") continue;
formData[el.name] = isSensitive ? "[REDACTED]" : (el.value || "").slice(0, 200);
}
emit("form_submit", {
form: { id: form.id || "", action: form.action || "", method: form.method || "GET", fieldCount: elements.length, fields: formData }
});
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Form capture error:", err);
}
};
document.addEventListener("submit", handler, { capture: true });
return () => document.removeEventListener("submit", handler, { capture: true });
}
function collectRouteChanges(emit) {
let lastPath = window.location.pathname;
const check = () => {
const current = window.location.pathname;
if (current !== lastPath) {
const from = lastPath;
lastPath = current;
emit("route_change", { from, to: current });
}
};
window.addEventListener("popstate", check);
const origPush = history.pushState.bind(history);
const origReplace = history.replaceState.bind(history);
history.pushState = function(...args) {
origPush(...args);
check();
};
history.replaceState = function(...args) {
origReplace(...args);
check();
};
return () => {
window.removeEventListener("popstate", check);
history.pushState = origPush;
history.replaceState = origReplace;
};
}
function collectApiRequests(emit) {
const originalFetch = window.fetch;
window.fetch = async function(input, init) {
var _a;
let url = "";
let method = "GET";
try {
if (typeof input === "string") {
url = input;
} else if (input instanceof URL) {
url = input.href;
} else if (input && typeof input === "object" && "url" in input) {
url = input.url;
method = input.method || "GET";
}
if (init == null ? void 0 : init.method) method = init.method;
} catch (e) {
}
const start = Date.now();
try {
if (url && isInternalUrl(url)) return originalFetch.call(window, input, init);
} catch (e) {
}
const safeUrl = sanitizeUrl2(url).slice(0, 500);
try {
const response = await originalFetch.call(window, input, init);
try {
emit("api_request", {
request: { url: safeUrl, method: method.toUpperCase(), statusCode: response.status, durationMs: Date.now() - start }
});
} catch (e) {
}
try {
if (response.status >= 400 || response.status === 0) {
const clone = response.clone();
readResponseBodySafe(clone).then((snippet) => {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: response.status,
response: snippet,
timestamp: Date.now()
});
}).catch(() => {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: response.status,
response: "",
timestamp: Date.now()
});
});
}
} catch (e) {
}
return response;
} catch (err) {
try {
emit("api_request", {
request: { url: safeUrl, method: method.toUpperCase(), statusCode: 0, durationMs: Date.now() - start }
});
} catch (e) {
}
try {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: 0,
response: ((_a = err == null ? void 0 : err.message) == null ? void 0 : _a.slice(0, RESPONSE_SNIPPET_CHARS)) || "",
timestamp: Date.now()
});
} catch (e) {
}
throw err;
}
};
return () => {
window.fetch = originalFetch;
};
}
function collectXhrRequests(emit) {
const OrigXHR = window.XMLHttpRequest;
const origOpen = OrigXHR.prototype.open;
const origSend = OrigXHR.prototype.send;
const xhrMeta = /* @__PURE__ */ new WeakMap();
OrigXHR.prototype.open = function(method, url, ...rest) {
try {
xhrMeta.set(this, { method, url: typeof url === "string" ? url : url.toString() });
} catch (e) {
}
return origOpen.apply(this, [method, url, ...rest]);
};
OrigXHR.prototype.send = function(body) {
try {
const xhr = this;
const start = Date.now();
const meta = xhrMeta.get(xhr);
const method = (meta == null ? void 0 : meta.method) || "GET";
const url = (meta == null ? void 0 : meta.url) || "";
if (isInternalUrl(url)) return origSend.call(this, body);
const safeUrl = sanitizeUrl2(url).slice(0, 500);
xhr.addEventListener("loadend", function() {
try {
emit("api_request", { request: { url: safeUrl, method: method.toUpperCase(), statusCode: xhr.status, durationMs: Date.now() - start } });
} catch (e) {
}
try {
if (xhr.status >= 400 || xhr.status === 0) {
let body2 = "";
try {
const ct = xhr.getResponseHeader && xhr.getResponseHeader("content-type") || "";
if (!BINARY_CONTENT_TYPE_RE.test(ct)) {
body2 = typeof xhr.responseText === "string" ? xhr.responseText : "";
}
} catch (e) {
}
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: xhr.status,
response: body2.slice(0, RESPONSE_SNIPPET_CHARS),
timestamp: Date.now()
});
}
} catch (e) {
}
});
xhr.addEventListener("error", function() {
try {
emit("api_request", { request: { url: safeUrl, method: method.toUpperCase(), statusCode: 0, durationMs: Date.now() - start } });
} catch (e) {
}
try {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: 0,
response: "",
timestamp: Date.now()
});
} catch (e) {
}
});
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] XHR capture error:", err);
}
return origSend.call(this, body);
};
return () => {
OrigXHR.prototype.open = origOpen;
OrigXHR.prototype.send = origSend;
};
}
var _perfSeen = /* @__PURE__ */ new Set();
function _emitPerfEntry(emit, e) {
if (!e || !e.name) return;
try {
if (isInternalUrl(e.name)) return;
} catch (e2) {
}
if (typeof e.name === "string" && e.name.indexOf("tracebug") !== -1) return;
const key = `${e.name}|${Math.round(e.startTime)}`;
if (_perfSeen.has(key)) return;
_perfSeen.add(key);
const navStart = typeof performance.timeOrigin === "number" ? performance.timeOrigin : Date.now();
const ext = e;
const initiator = e.initiatorType || "";
const method = (ext.method || "GET").toUpperCase();
const status = ext.responseStatus || 0;
const url = sanitizeUrl2(e.name).slice(0, 500);
const timestamp = Math.round(navStart + e.startTime);
const durationMs = Math.round(e.duration || 0);
try {
emit("api_request", {
request: { url, method, statusCode: status, durationMs, initiatorType: initiator },
_ts: timestamp
});
} catch (e2) {
}
}
function drainPerformanceNetwork(emit) {
if (typeof performance === "undefined") return;
try {
const entries = performance.getEntriesByType("resource");
for (const e of entries) _emitPerfEntry(emit, e);
} catch (e) {
}
}
function collectPerformanceNetwork(emit) {
if (typeof performance === "undefined" || typeof PerformanceObserver === "undefined") {
return () => {
};
}
drainPerformanceNetwork(emit);
let observer = null;
try {
observer = new PerformanceObserver((list) => {
for (const e of list.getEntries()) {
_emitPerfEntry(emit, e);
}
});
observer.observe({ type: "resource", buffered: false });
} catch (e) {
}
return () => {
try {
observer == null ? void 0 : observer.disconnect();
} catch (e) {
}
};
}
function collectErrors(emit) {
const prevOnError = window.onerror;
window.onerror = (msg, source, line, col, error) => {
try {
emit("error", {
error: {
message: typeof msg === "string" ? msg : "Unknown error",
stack: error == null ? void 0 : error.stack,
source,
line,
column: col
}
});
} catch (e) {
}
if (prevOnError) {
try {
prevOnError(msg, source, line, col, error);
} catch (e) {
}
}
};
const onRejection = (e) => {
var _a, _b;
try {
emit("unhandled_rejection", {
error: { message: ((_a = e.reason) == null ? void 0 : _a.message) || String(e.reason), stack: (_b = e.reason) == null ? void 0 : _b.stack }
});
} catch (e2) {
}
};
window.addEventListener("unhandledrejection", onRejection);
return () => {
window.onerror = prevOnError;
window.removeEventListener("unhandledrejection", onRejection);
};
}
function collectConsoleErrors(emit) {
const origConsoleError = console.error;
let _insideEmit = false;
console.error = function(...args) {
if (_insideEmit) {
origConsoleError.apply(console, args);
return;
}
_insideEmit = true;
try {
emit("console_error", {
error: { message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") }
});
} catch (e) {
} finally {
_insideEmit = false;
}
origConsoleError.apply(console, args);
};
return () => {
console.error = origConsoleError;
};
}
function collectConsoleWarnings(emit) {
const origWarn = console.warn;
let _inside = false;
console.warn = function(...args) {
if (_inside) {
origWarn.apply(console, args);
return;
}
_inside = true;
try {
emit("console_warn", {
error: { message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") }
});
} catch (e) {
} finally {
_inside = false;
}
origWarn.apply(console, args);
};
return () => {
console.warn = origWarn;
};
}
function collectConsoleLogs(emit) {
const origLog = console.log;
let _inside = false;
let _count = 0;
console.log = function(...args) {
if (_inside || _count >= 50) {
origLog.apply(console, args);
return;
}
_inside = true;
_count++;
try {
emit("console_log", {
error: { message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") }
});
} catch (e) {
} finally {
_inside = false;
}
origLog.apply(console, args);
};
return () => {
console.log = origLog;
};
}
// src/ui/helpers.ts
function tbIsolationCss(root) {
return `
${root} {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 14px; font-weight: 400; line-height: 1.5; font-style: normal;
letter-spacing: normal; text-transform: none; text-align: left;
text-indent: 0; white-space: normal; word-spacing: normal; text-shadow: none;
-webkit-font-smoothing: antialiased;
}
${root} *, ${root} *::before, ${root} *::after { box-sizing: border-box; }
${root} button, ${root} input, ${root} select, ${root} textarea {
font-family: inherit; font-size: inherit; letter-spacing: normal;
text-transform: none; margin: 0;
}
${root} svg { max-width: none; max-height: none; vertical-align: middle; }
${root} img { max-width: none; }
${root} a { text-decoration: none; }
`;
}
function parseShortcut(shortcut) {
const parts = (shortcut || "").toLowerCase().split("+").map((s) => s.trim());
return {
mod: parts.includes("ctrl") || parts.includes("control") || parts.includes("cmd") || parts.includes("meta"),
shift: parts.includes("shift"),
alt: parts.includes("alt") || parts.includes("option"),
key: parts[parts.length - 1] || ""
};
}
function matchesShortcut(e, shortcut) {
if (!shortcut) return false;
const s = parseShortcut(shortcut);
const mod = e.ctrlKey || e.metaKey;
const key = (e.key || "").toLowerCase();
return mod === s.mod && e.shiftKey === s.shift && e.altKey === s.alt && key === s.key;
}
function escapeHtml(str) {
return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
export {
generateSessionId,
getActiveSessionId,
setActiveSessionId,
clearActiveSessionId,
getActiveCaptureMode,
setActiveCaptureMode,
getAllSessions,
getCachedSessions,
scheduleFlush,
flushPendingEvents,
appendEvent,
updateSessionError,
deleteSession,
addAnnotation,
saveEnvironment,
setSessionPriority,
markSessionSaved,
clearAllSessions,
sanitizeTokenShapes,
sanitizeReportForUpload,
getNetworkFailures,
clearNetworkFailures,
collectClicks,
collectInputs,
collectSelectChanges,
collectFormSubmits,
collectRouteChanges,
collectApiRequests,
collectXhrRequests,
drainPerformanceNetwork,
collectPerformanceNetwork,
collectErrors,
collectConsoleErrors,
collectConsoleWarnings,
collectConsoleLogs,
tbIsolationCss,
matchesShortcut,
escapeHtml
};
//# sourceMappingURL=chunk-AJX6BTPE.js.map

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

"use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/storage.ts
var SESSIONS_KEY = "tracebug_sessions";
var ACTIVE_SESSION_KEY = "tracebug_active_session";
var ACTIVE_CAPTURE_MODE_KEY = "tracebug_active_capture_mode";
function generateSessionId() {
return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : "bt_" + Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
}
function getActiveSessionId() {
try {
return localStorage.getItem(ACTIVE_SESSION_KEY);
} catch (e) {
return null;
}
}
function setActiveSessionId(id) {
try {
localStorage.setItem(ACTIVE_SESSION_KEY, id);
} catch (e) {
}
}
function clearActiveSessionId() {
try {
localStorage.removeItem(ACTIVE_SESSION_KEY);
} catch (e) {
}
try {
localStorage.removeItem(ACTIVE_CAPTURE_MODE_KEY);
} catch (e) {
}
}
function getActiveCaptureMode() {
try {
const v = localStorage.getItem(ACTIVE_CAPTURE_MODE_KEY);
return v === "events" || v === "video" ? v : null;
} catch (e) {
return null;
}
}
function setActiveCaptureMode(mode) {
try {
localStorage.setItem(ACTIVE_CAPTURE_MODE_KEY, mode);
} catch (e) {
}
}
function getAllSessions() {
try {
const raw = localStorage.getItem(SESSIONS_KEY);
return raw ? JSON.parse(raw) : [];
} catch (e) {
return [];
}
}
function saveSessions(sessions) {
try {
localStorage.setItem(SESSIONS_KEY, JSON.stringify(sessions));
return;
} catch (e) {
}
const commit = (next) => {
try {
localStorage.setItem(SESSIONS_KEY, JSON.stringify(next));
sessions.length = 0;
sessions.push(...next);
return true;
} catch (e) {
return false;
}
};
const working = sessions.slice();
while (working.length > 1) {
working.shift();
if (commit(working)) {
if (typeof console !== "undefined") console.warn("[TraceBug] Storage full \u2014 dropped oldest session(s) to fit.");
return;
}
}
const last = working[0];
if (last && Array.isArray(last.events)) {
while (last.events.length > 1) {
last.events = last.events.slice(Math.ceil(last.events.length / 2));
if (commit(working)) {
if (typeof console !== "undefined") console.warn("[TraceBug] Storage full \u2014 trimmed older events from the current session to fit.");
return;
}
}
}
if (typeof console !== "undefined") console.error("[TraceBug] Could not persist sessions: localStorage quota exceeded.");
}
var _cachedSessions = null;
var _pendingFlush = null;
var _dirty = false;
var FLUSH_INTERVAL_MS = 1e3;
function getCachedSessions() {
if (!_cachedSessions) {
_cachedSessions = getAllSessions();
}
return _cachedSessions;
}
function scheduleFlush() {
_dirty = true;
if (_pendingFlush) return;
_pendingFlush = setTimeout(() => {
_pendingFlush = null;
if (_cachedSessions && _dirty) {
saveSessions(_cachedSessions);
_dirty = false;
}
}, FLUSH_INTERVAL_MS);
}
function flushPendingEvents() {
if (_pendingFlush) {
clearTimeout(_pendingFlush);
_pendingFlush = null;
}
if (_cachedSessions) {
saveSessions(_cachedSessions);
_dirty = false;
}
}
function invalidateCache() {
if (_pendingFlush) {
clearTimeout(_pendingFlush);
_pendingFlush = null;
}
_cachedSessions = null;
_dirty = false;
}
if (typeof window !== "undefined") {
window.addEventListener("beforeunload", flushPendingEvents);
window.addEventListener("pagehide", flushPendingEvents);
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") flushPendingEvents();
});
}
function appendEvent(sessionId, event, maxEvents, maxSessions) {
let sessions = getCachedSessions();
let session = sessions.find((s) => s.sessionId === sessionId);
if (!session) {
session = {
sessionId,
projectId: event.projectId,
createdAt: Date.now(),
updatedAt: Date.now(),
errorMessage: null,
errorStack: null,
reproSteps: null,
errorSummary: null,
events: [],
annotations: [],
environment: null
};
sessions.push(session);
}
session.events.push(event);
session.updatedAt = Date.now();
if (session.events.length > maxEvents) {
session.events = session.events.slice(-maxEvents);
}
if (sessions.length > maxSessions) {
sessions = sessions.slice(-maxSessions);
_cachedSessions = sessions;
}
scheduleFlush();
}
function updateSessionError(sessionId, errorMessage, errorStack, reproSteps, errorSummary) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.errorMessage = errorMessage;
session.errorStack = errorStack || null;
session.reproSteps = reproSteps;
session.errorSummary = errorSummary;
session.updatedAt = Date.now();
scheduleFlush();
}
function deleteSession(sessionId) {
flushPendingEvents();
const remaining = getAllSessions().filter((s) => s.sessionId !== sessionId);
invalidateCache();
saveSessions(remaining);
}
function addAnnotation(sessionId, annotation) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
if (!session.annotations) session.annotations = [];
session.annotations.push(annotation);
session.updatedAt = Date.now();
scheduleFlush();
}
function saveEnvironment(sessionId, env) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.environment = env;
scheduleFlush();
}
function setSessionPriority(sessionId, priority) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.priority = priority;
session.updatedAt = Date.now();
scheduleFlush();
}
function markSessionSaved(sessionId) {
const sessions = getCachedSessions();
const session = sessions.find((s) => s.sessionId === sessionId);
if (!session) return;
session.saved = true;
session.updatedAt = Date.now();
flushPendingEvents();
}
function clearAllSessions() {
invalidateCache();
try {
localStorage.removeItem(SESSIONS_KEY);
} catch (e) {
}
}
// src/sanitize/cloud-upload.ts
var REDACTED = "[REDACTED]";
var TOKEN_PATTERNS = [
// Bearer <token> in headers, console output, anywhere
{ name: "bearer", re: /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}/gi, replace: () => "Bearer " + REDACTED },
// JWT (3 base64url segments separated by dots, leading with eyJ which is `{"` in base64)
{ name: "jwt", re: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g, replace: mask },
// OpenAI / Stripe sk_*
{ name: "sk_prefix", re: /\bsk-[A-Za-z0-9_-]{20,}\b/g, replace: mask },
// Stripe secret + publishable (live/test, secret + publishable + restricted)
{ name: "stripe", re: /\b(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g, replace: mask },
// GitHub PATs (classic + fine-grained + OAuth + server tokens)
{ name: "github_pat", re: /\bghp_[A-Za-z0-9]{30,}\b/g, replace: mask },
{ name: "github_fine", re: /\bgithub_pat_[A-Za-z0-9_]{60,}\b/g, replace: mask },
{ name: "github_oauth", re: /\bgho_[A-Za-z0-9]{30,}\b/g, replace: mask },
{ name: "github_server", re: /\bghs_[A-Za-z0-9]{30,}\b/g, replace: mask },
// AWS access keys (begins with AKIA, ASIA, AGPA, AIDA, etc.) + secret key (40-char base64)
{ name: "aws_access", re: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|ANPA|ANVA)[A-Z0-9]{16}\b/g, replace: mask },
{ name: "aws_secret", re: /\b(?:aws.{0,20})?[A-Za-z0-9/+]{40}\b(?=.*aws|.*secret|.*key)/gi, replace: mask },
// Slack — broader than before (xoxa-z covers all known prefixes)
{ name: "slack", re: /\bxox[abeprs]-[A-Za-z0-9-]{10,}\b/g, replace: mask },
// Google API keys
{ name: "google_api", re: /\bAIza[A-Za-z0-9_-]{35}\b/g, replace: mask },
// Twilio — Account SID + Auth tokens
{ name: "twilio_sid", re: /\b(?:AC|SK)[a-f0-9]{32}\b/g, replace: mask },
// SendGrid
{ name: "sendgrid", re: /\bSG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\b/g, replace: mask },
// Mailgun
{ name: "mailgun", re: /\bkey-[a-f0-9]{32}\b/g, replace: mask },
// Postmark
{ name: "postmark", re: /\b[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\b(?=.{0,30}(postmark|server-token|api-token))/gi, replace: mask },
// Linear / Vercel / Cloudflare / Discord
{ name: "linear", re: /\blin_api_[A-Za-z0-9]{40,}\b/g, replace: mask },
{ name: "discord_bot", re: /\b[MN][A-Za-z\d]{23}\.[A-Za-z\d_-]{6}\.[A-Za-z\d_-]{27,}\b/g, replace: mask },
// Generic high-entropy hex (≥32 chars). Catches webhook signing secrets,
// session IDs, etc. that don't carry a recognizable prefix. Conservative:
// only triggers when preceded by a common secret-y keyword to avoid
// mangling legitimate hex like git SHAs.
{ name: "labeled_hex", re: /\b(?:secret|token|key|password|api[_-]?key|auth)["':\s=]{1,5}([a-fA-F0-9]{32,})\b/gi, replace: (s) => s.replace(/[a-fA-F0-9]{32,}/, REDACTED) }
];
function mask(s) {
if (s.length <= 12) return REDACTED;
return `${s.slice(0, 4)}\u2026${REDACTED}\u2026${s.slice(-4)}`;
}
var SENSITIVE_QUERY_KEYS = /* @__PURE__ */ new Set([
"token",
"access_token",
"id_token",
"refresh_token",
"api_key",
"apikey",
"secret",
"password",
"passwd",
"pwd",
"auth",
"authorization",
"x-api-key",
"session",
"sid",
"csrf"
]);
function sanitizeUrl(url) {
if (typeof url !== "string" || url.length === 0) return url;
try {
const isAbs = /^[a-z][a-z0-9+.-]*:/i.test(url);
const u = new URL(isAbs ? url : `http://_placeholder_${url.startsWith("/") ? "" : "/"}${url}`);
let changed = false;
u.searchParams.forEach((_v, k) => {
if (SENSITIVE_QUERY_KEYS.has(k.toLowerCase())) {
u.searchParams.set(k, REDACTED);
changed = true;
}
});
if (!changed) return sanitizeText(url);
const out = isAbs ? u.toString() : u.pathname + u.search + u.hash;
return sanitizeText(out);
} catch (e) {
return sanitizeText(url);
}
}
function sanitizeText(s) {
if (s == null) return s;
let out = String(s);
for (const p of TOKEN_PATTERNS) out = out.replace(p.re, p.replace);
return out;
}
function sanitizeTokenShapes(s) {
return sanitizeText(s);
}
function sanitizeReportForUpload(report) {
var _a;
const out = typeof structuredClone === "function" ? structuredClone(report) : JSON.parse(JSON.stringify(report));
if (out.consoleErrors) {
out.consoleErrors = out.consoleErrors.map((e) => {
var _a2;
return {
...e,
message: sanitizeText(e.message),
stack: sanitizeText((_a2 = e.stack) != null ? _a2 : void 0)
};
});
}
if (out.consoleLogs) {
out.consoleLogs = out.consoleLogs.map((e) => {
var _a2;
return {
...e,
message: sanitizeText(e.message),
stack: sanitizeText((_a2 = e.stack) != null ? _a2 : void 0)
};
});
}
if (out.networkErrors) {
out.networkErrors = out.networkErrors.map((e) => {
var _a2;
return {
...e,
url: sanitizeUrl(e.url),
response: sanitizeText((_a2 = e.response) != null ? _a2 : void 0)
};
});
}
if (out.networkRequests) {
out.networkRequests = out.networkRequests.map((e) => {
var _a2;
return {
...e,
url: sanitizeUrl(e.url),
response: sanitizeText((_a2 = e.response) != null ? _a2 : void 0)
};
});
}
if (out.steps) out.steps = sanitizeText(out.steps);
if (out.summary) out.summary = sanitizeText(out.summary);
if (out.title) out.title = sanitizeText(out.title);
if (Array.isArray(out.sessionSteps)) out.sessionSteps = out.sessionSteps.map((s) => sanitizeText(s));
if (out.actionChips) {
out.actionChips = out.actionChips.map((c) => {
var _a2, _b;
return {
...c,
target: sanitizeText((_a2 = c.target) != null ? _a2 : void 0),
detail: sanitizeText((_b = c.detail) != null ? _b : void 0)
};
});
}
if (out.storage) {
const scrub = (entries) => entries.map((e) => ({ ...e, value: sanitizeText(e.value) }));
out.storage.local = scrub(out.storage.local || []);
out.storage.session = scrub(out.storage.session || []);
if (out.storage.cookies) out.storage.cookies = scrub(out.storage.cookies);
}
if ((_a = out.environment) == null ? void 0 : _a.url) out.environment.url = sanitizeUrl(out.environment.url);
if (out.context && typeof out.context === "object") {
const ctx = {};
for (const [k, v] of Object.entries(out.context)) {
ctx[k] = typeof v === "string" ? sanitizeText(v) : v;
}
out.context = ctx;
}
return out;
}
// src/collectors.ts
var ROOT_ID = "tracebug-root";
var PANEL_ID = "tracebug-dashboard-panel";
var BTN_ID = "tracebug-dashboard-btn";
var NETWORK_FAILURE_LIMIT = 10;
var RESPONSE_SNIPPET_CHARS = 200;
var _networkFailures = [];
function pushNetworkFailure(failure) {
try {
if (failure.response) failure.response = sanitizeTokenShapes(failure.response);
_networkFailures.push(failure);
if (_networkFailures.length > NETWORK_FAILURE_LIMIT) {
_networkFailures.splice(0, _networkFailures.length - NETWORK_FAILURE_LIMIT);
}
} catch (e) {
}
}
function getNetworkFailures() {
return _networkFailures.slice();
}
function clearNetworkFailures() {
_networkFailures.length = 0;
}
var SENSITIVE_PARAM_RE = /token|key|secret|auth|password|sig|signature/i;
function sanitizeUrl2(url) {
if (!url) return url;
try {
const qIdx = url.indexOf("?");
if (qIdx === -1) return url;
const base = url.slice(0, qIdx);
const afterQ = url.slice(qIdx + 1);
const hashIdx = afterQ.indexOf("#");
const query = hashIdx === -1 ? afterQ : afterQ.slice(0, hashIdx);
const hash = hashIdx === -1 ? "" : afterQ.slice(hashIdx);
const redacted = query.split("&").map((part) => {
const eqIdx = part.indexOf("=");
if (eqIdx === -1) return part;
const key = part.slice(0, eqIdx);
if (SENSITIVE_PARAM_RE.test(key)) return `${key}=[REDACTED]`;
return part;
}).join("&");
return `${base}?${redacted}${hash}`;
} catch (e) {
return url;
}
}
var MAX_BODY_BYTES = 10 * 1024;
var BINARY_CONTENT_TYPE_RE = /^(image|video|audio)\/|^application\/(octet-stream|pdf|zip|x-protobuf|x-msgpack|wasm|vnd\.)/i;
async function readResponseBodySafe(response) {
try {
const ct = response.headers.get("content-type") || "";
if (BINARY_CONTENT_TYPE_RE.test(ct)) return "";
if (!response.body || typeof response.body.getReader !== "function") {
try {
const text = await response.text();
return typeof text === "string" ? text.slice(0, RESPONSE_SNIPPET_CHARS) : "";
} catch (e) {
return "";
}
}
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8", { fatal: false });
let collected = "";
let bytesRead = 0;
while (bytesRead < MAX_BODY_BYTES && collected.length < RESPONSE_SNIPPET_CHARS) {
const { value, done } = await reader.read();
if (done) break;
if (value) {
bytesRead += value.byteLength;
collected += decoder.decode(value, { stream: true });
}
}
try {
await reader.cancel();
} catch (e) {
}
return collected.slice(0, RESPONSE_SNIPPET_CHARS);
} catch (e) {
return "";
}
}
var INTERNAL_URL_PATTERNS = [
/__nextjs_original-stack-frame/,
/\/_next\/static\/webpack/,
/\/__webpack_hmr/,
/\.hot-update\./,
/\/sockjs-node\//,
/\/turbopack-hmr\//,
/\/_next\/webpack-hmr/,
/\/webpack-dev-server\//,
/\/__vite_ping/,
/\/@vite\/client/,
/\/@react-refresh/
];
function isInternalUrl(url) {
return INTERNAL_URL_PATTERNS.some((pattern) => pattern.test(url));
}
var _rootCache;
function getRoot() {
if (_rootCache === void 0) {
_rootCache = document.getElementById(ROOT_ID);
}
if (_rootCache && !_rootCache.isConnected) {
_rootCache = document.getElementById(ROOT_ID);
}
return _rootCache;
}
function isTraceBugElement(el) {
if (!el) return false;
if (el.id === ROOT_ID || el.id === BTN_ID || el.id === PANEL_ID) return true;
if (el.dataset && el.dataset.tracebug) return true;
const root = getRoot();
if (root && root.contains(el)) return true;
let node = el;
while (node) {
const id = node.id || "";
if (id.startsWith("tracebug-") || id.startsWith("bt-")) return true;
const cn = typeof node.className === "string" ? node.className : "";
if (cn.includes("tracebug-") || cn.includes("bt-ann") || cn.includes("bt-voice")) return true;
if (node.dataset && node.dataset.tracebug) return true;
node = node.parentElement;
}
return false;
}
function collectClicks(emit) {
const handler = (e) => {
try {
const t = e.target;
if (!t || isTraceBugElement(t)) return;
const tag = t.tagName.toLowerCase();
const el = {
tag,
text: (t.innerText || "").slice(0, 120),
id: t.id || "",
className: typeof t.className === "string" ? t.className : ""
};
const data = { element: el };
if (tag === "a") el.href = t.href || "";
if (tag === "button" || t.type === "submit") {
el.buttonType = t.type || "button";
el.disabled = t.disabled;
}
if (tag === "label") el.forField = t.htmlFor || "";
const ariaLabel = t.getAttribute("aria-label");
if (ariaLabel) el.ariaLabel = ariaLabel;
const role = t.getAttribute("role");
if (role) el.role = role;
const testId = t.getAttribute("data-testid");
if (testId) el.testId = testId;
const form = t.closest("form");
if (form) {
el.formId = form.id || "";
el.formAction = form.action || "";
}
try {
el.selector = buildSelector(t);
} catch (e2) {
}
try {
const r = t.getBoundingClientRect();
el.boundingBox = { x: Math.round(r.left), y: Math.round(r.top), width: Math.round(r.width), height: Math.round(r.height) };
} catch (e2) {
}
emit("click", data);
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Click capture error:", err);
}
};
document.addEventListener("click", handler, { capture: true });
return () => document.removeEventListener("click", handler, { capture: true });
}
function buildSelector(el) {
if (!el) return "";
if (el.id) return `#${CSS.escape(el.id)}`;
const testId = el.getAttribute("data-testid");
if (testId) return `[data-testid="${testId}"]`;
const parts = [];
let node = el;
let depth = 0;
while (node && node !== document.body && depth < 4) {
let part = node.tagName.toLowerCase();
if (node.id) {
parts.unshift(`#${CSS.escape(node.id)}`);
break;
}
const cls = typeof node.className === "string" ? node.className.trim().split(/\s+/).filter(Boolean)[0] : "";
if (cls) part += `.${CSS.escape(cls)}`;
const parent = node.parentElement;
const currentTag = node.tagName;
const currentNode = node;
if (parent) {
const sameTag = Array.from(parent.children).filter((c) => c.tagName === currentTag);
if (sameTag.length > 1) part += `:nth-of-type(${sameTag.indexOf(currentNode) + 1})`;
}
parts.unshift(part);
node = parent;
depth++;
}
return parts.join(" > ");
}
function collectInputs(emit) {
const timers = /* @__PURE__ */ new Map();
const handler = (e) => {
try {
const t = e.target;
if (!t || !("value" in t) || isTraceBugElement(t)) return;
if (t.tagName.toLowerCase() === "select") return;
const prev = timers.get(t);
if (prev) clearTimeout(prev);
timers.set(
t,
setTimeout(() => {
try {
const tag = t.tagName.toLowerCase();
const inputType = t.type || "";
const isSensitive = ["password", "credit-card", "ssn"].includes(inputType) || /password|secret|token|ssn|credit/i.test(t.name || t.id || "");
const element = {
tag,
name: t.name || t.id || "",
type: inputType,
valueLength: (t.value || "").length,
value: isSensitive ? "[REDACTED]" : (t.value || "").slice(0, 200),
placeholder: t.placeholder || ""
};
const data = { element };
if (inputType === "checkbox" || inputType === "radio") {
element.checked = t.checked;
element.value = t.checked ? "checked" : "unchecked";
}
if (inputType === "number" || inputType === "range") {
element.value = t.value;
}
emit("input", data);
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Input capture error:", err);
}
timers.delete(t);
}, 300)
);
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Input capture error:", err);
}
};
document.addEventListener("input", handler, { capture: true });
return () => {
document.removeEventListener("input", handler, { capture: true });
timers.forEach((t) => clearTimeout(t));
};
}
function collectSelectChanges(emit) {
const handler = (e) => {
try {
const t = e.target;
if (!t || t.tagName.toLowerCase() !== "select" || isTraceBugElement(t)) return;
const selectedOption = t.options[t.selectedIndex];
emit("select_change", {
element: {
tag: "select",
name: t.name || t.id || "",
value: t.value,
selectedText: selectedOption ? selectedOption.text : "",
selectedIndex: t.selectedIndex,
optionCount: t.options.length,
allOptions: Array.from(t.options).map((o) => o.text).slice(0, 20)
}
});
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Select capture error:", err);
}
};
document.addEventListener("change", handler, { capture: true });
return () => document.removeEventListener("change", handler, { capture: true });
}
function collectFormSubmits(emit) {
const handler = (e) => {
try {
const form = e.target;
if (!form || form.tagName.toLowerCase() !== "form" || isTraceBugElement(form)) return;
const formData = {};
const elements = form.elements;
for (let i = 0; i < elements.length; i++) {
const el = elements[i];
if (!el.name) continue;
const isSensitive = ["password"].includes(el.type) || /password|secret|token|ssn|credit/i.test(el.name);
if (el.type === "submit" || el.type === "button") continue;
formData[el.name] = isSensitive ? "[REDACTED]" : (el.value || "").slice(0, 200);
}
emit("form_submit", {
form: { id: form.id || "", action: form.action || "", method: form.method || "GET", fieldCount: elements.length, fields: formData }
});
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] Form capture error:", err);
}
};
document.addEventListener("submit", handler, { capture: true });
return () => document.removeEventListener("submit", handler, { capture: true });
}
function collectRouteChanges(emit) {
let lastPath = window.location.pathname;
const check = () => {
const current = window.location.pathname;
if (current !== lastPath) {
const from = lastPath;
lastPath = current;
emit("route_change", { from, to: current });
}
};
window.addEventListener("popstate", check);
const origPush = history.pushState.bind(history);
const origReplace = history.replaceState.bind(history);
history.pushState = function(...args) {
origPush(...args);
check();
};
history.replaceState = function(...args) {
origReplace(...args);
check();
};
return () => {
window.removeEventListener("popstate", check);
history.pushState = origPush;
history.replaceState = origReplace;
};
}
function collectApiRequests(emit) {
const originalFetch = window.fetch;
window.fetch = async function(input, init) {
var _a;
let url = "";
let method = "GET";
try {
if (typeof input === "string") {
url = input;
} else if (input instanceof URL) {
url = input.href;
} else if (input && typeof input === "object" && "url" in input) {
url = input.url;
method = input.method || "GET";
}
if (init == null ? void 0 : init.method) method = init.method;
} catch (e) {
}
const start = Date.now();
try {
if (url && isInternalUrl(url)) return originalFetch.call(window, input, init);
} catch (e) {
}
const safeUrl = sanitizeUrl2(url).slice(0, 500);
try {
const response = await originalFetch.call(window, input, init);
try {
emit("api_request", {
request: { url: safeUrl, method: method.toUpperCase(), statusCode: response.status, durationMs: Date.now() - start }
});
} catch (e) {
}
try {
if (response.status >= 400 || response.status === 0) {
const clone = response.clone();
readResponseBodySafe(clone).then((snippet) => {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: response.status,
response: snippet,
timestamp: Date.now()
});
}).catch(() => {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: response.status,
response: "",
timestamp: Date.now()
});
});
}
} catch (e) {
}
return response;
} catch (err) {
try {
emit("api_request", {
request: { url: safeUrl, method: method.toUpperCase(), statusCode: 0, durationMs: Date.now() - start }
});
} catch (e) {
}
try {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: 0,
response: ((_a = err == null ? void 0 : err.message) == null ? void 0 : _a.slice(0, RESPONSE_SNIPPET_CHARS)) || "",
timestamp: Date.now()
});
} catch (e) {
}
throw err;
}
};
return () => {
window.fetch = originalFetch;
};
}
function collectXhrRequests(emit) {
const OrigXHR = window.XMLHttpRequest;
const origOpen = OrigXHR.prototype.open;
const origSend = OrigXHR.prototype.send;
const xhrMeta = /* @__PURE__ */ new WeakMap();
OrigXHR.prototype.open = function(method, url, ...rest) {
try {
xhrMeta.set(this, { method, url: typeof url === "string" ? url : url.toString() });
} catch (e) {
}
return origOpen.apply(this, [method, url, ...rest]);
};
OrigXHR.prototype.send = function(body) {
try {
const xhr = this;
const start = Date.now();
const meta = xhrMeta.get(xhr);
const method = (meta == null ? void 0 : meta.method) || "GET";
const url = (meta == null ? void 0 : meta.url) || "";
if (isInternalUrl(url)) return origSend.call(this, body);
const safeUrl = sanitizeUrl2(url).slice(0, 500);
xhr.addEventListener("loadend", function() {
try {
emit("api_request", { request: { url: safeUrl, method: method.toUpperCase(), statusCode: xhr.status, durationMs: Date.now() - start } });
} catch (e) {
}
try {
if (xhr.status >= 400 || xhr.status === 0) {
let body2 = "";
try {
const ct = xhr.getResponseHeader && xhr.getResponseHeader("content-type") || "";
if (!BINARY_CONTENT_TYPE_RE.test(ct)) {
body2 = typeof xhr.responseText === "string" ? xhr.responseText : "";
}
} catch (e) {
}
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: xhr.status,
response: body2.slice(0, RESPONSE_SNIPPET_CHARS),
timestamp: Date.now()
});
}
} catch (e) {
}
});
xhr.addEventListener("error", function() {
try {
emit("api_request", { request: { url: safeUrl, method: method.toUpperCase(), statusCode: 0, durationMs: Date.now() - start } });
} catch (e) {
}
try {
pushNetworkFailure({
url: safeUrl,
method: method.toUpperCase(),
status: 0,
response: "",
timestamp: Date.now()
});
} catch (e) {
}
});
} catch (err) {
if (typeof console !== "undefined") console.warn("[TraceBug] XHR capture error:", err);
}
return origSend.call(this, body);
};
return () => {
OrigXHR.prototype.open = origOpen;
OrigXHR.prototype.send = origSend;
};
}
var _perfSeen = /* @__PURE__ */ new Set();
function _emitPerfEntry(emit, e) {
if (!e || !e.name) return;
try {
if (isInternalUrl(e.name)) return;
} catch (e2) {
}
if (typeof e.name === "string" && e.name.indexOf("tracebug") !== -1) return;
const key = `${e.name}|${Math.round(e.startTime)}`;
if (_perfSeen.has(key)) return;
_perfSeen.add(key);
const navStart = typeof performance.timeOrigin === "number" ? performance.timeOrigin : Date.now();
const ext = e;
const initiator = e.initiatorType || "";
const method = (ext.method || "GET").toUpperCase();
const status = ext.responseStatus || 0;
const url = sanitizeUrl2(e.name).slice(0, 500);
const timestamp = Math.round(navStart + e.startTime);
const durationMs = Math.round(e.duration || 0);
try {
emit("api_request", {
request: { url, method, statusCode: status, durationMs, initiatorType: initiator },
_ts: timestamp
});
} catch (e2) {
}
}
function drainPerformanceNetwork(emit) {
if (typeof performance === "undefined") return;
try {
const entries = performance.getEntriesByType("resource");
for (const e of entries) _emitPerfEntry(emit, e);
} catch (e) {
}
}
function collectPerformanceNetwork(emit) {
if (typeof performance === "undefined" || typeof PerformanceObserver === "undefined") {
return () => {
};
}
drainPerformanceNetwork(emit);
let observer = null;
try {
observer = new PerformanceObserver((list) => {
for (const e of list.getEntries()) {
_emitPerfEntry(emit, e);
}
});
observer.observe({ type: "resource", buffered: false });
} catch (e) {
}
return () => {
try {
observer == null ? void 0 : observer.disconnect();
} catch (e) {
}
};
}
function collectErrors(emit) {
const prevOnError = window.onerror;
window.onerror = (msg, source, line, col, error) => {
try {
emit("error", {
error: {
message: typeof msg === "string" ? msg : "Unknown error",
stack: error == null ? void 0 : error.stack,
source,
line,
column: col
}
});
} catch (e) {
}
if (prevOnError) {
try {
prevOnError(msg, source, line, col, error);
} catch (e) {
}
}
};
const onRejection = (e) => {
var _a, _b;
try {
emit("unhandled_rejection", {
error: { message: ((_a = e.reason) == null ? void 0 : _a.message) || String(e.reason), stack: (_b = e.reason) == null ? void 0 : _b.stack }
});
} catch (e2) {
}
};
window.addEventListener("unhandledrejection", onRejection);
return () => {
window.onerror = prevOnError;
window.removeEventListener("unhandledrejection", onRejection);
};
}
function collectConsoleErrors(emit) {
const origConsoleError = console.error;
let _insideEmit = false;
console.error = function(...args) {
if (_insideEmit) {
origConsoleError.apply(console, args);
return;
}
_insideEmit = true;
try {
emit("console_error", {
error: { message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") }
});
} catch (e) {
} finally {
_insideEmit = false;
}
origConsoleError.apply(console, args);
};
return () => {
console.error = origConsoleError;
};
}
function collectConsoleWarnings(emit) {
const origWarn = console.warn;
let _inside = false;
console.warn = function(...args) {
if (_inside) {
origWarn.apply(console, args);
return;
}
_inside = true;
try {
emit("console_warn", {
error: { message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") }
});
} catch (e) {
} finally {
_inside = false;
}
origWarn.apply(console, args);
};
return () => {
console.warn = origWarn;
};
}
function collectConsoleLogs(emit) {
const origLog = console.log;
let _inside = false;
let _count = 0;
console.log = function(...args) {
if (_inside || _count >= 50) {
origLog.apply(console, args);
return;
}
_inside = true;
_count++;
try {
emit("console_log", {
error: { message: args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") }
});
} catch (e) {
} finally {
_inside = false;
}
origLog.apply(console, args);
};
return () => {
console.log = origLog;
};
}
// src/ui/helpers.ts
function tbIsolationCss(root) {
return `
${root} {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 14px; font-weight: 400; line-height: 1.5; font-style: normal;
letter-spacing: normal; text-transform: none; text-align: left;
text-indent: 0; white-space: normal; word-spacing: normal; text-shadow: none;
-webkit-font-smoothing: antialiased;
}
${root} *, ${root} *::before, ${root} *::after { box-sizing: border-box; }
${root} button, ${root} input, ${root} select, ${root} textarea {
font-family: inherit; font-size: inherit; letter-spacing: normal;
text-transform: none; margin: 0;
}
${root} svg { max-width: none; max-height: none; vertical-align: middle; }
${root} img { max-width: none; }
${root} a { text-decoration: none; }
`;
}
function parseShortcut(shortcut) {
const parts = (shortcut || "").toLowerCase().split("+").map((s) => s.trim());
return {
mod: parts.includes("ctrl") || parts.includes("control") || parts.includes("cmd") || parts.includes("meta"),
shift: parts.includes("shift"),
alt: parts.includes("alt") || parts.includes("option"),
key: parts[parts.length - 1] || ""
};
}
function matchesShortcut(e, shortcut) {
if (!shortcut) return false;
const s = parseShortcut(shortcut);
const mod = e.ctrlKey || e.metaKey;
const key = (e.key || "").toLowerCase();
return mod === s.mod && e.shiftKey === s.shift && e.altKey === s.alt && key === s.key;
}
function escapeHtml(str) {
return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
exports.generateSessionId = generateSessionId; exports.getActiveSessionId = getActiveSessionId; exports.setActiveSessionId = setActiveSessionId; exports.clearActiveSessionId = clearActiveSessionId; exports.getActiveCaptureMode = getActiveCaptureMode; exports.setActiveCaptureMode = setActiveCaptureMode; exports.getAllSessions = getAllSessions; exports.getCachedSessions = getCachedSessions; exports.scheduleFlush = scheduleFlush; exports.flushPendingEvents = flushPendingEvents; exports.appendEvent = appendEvent; exports.updateSessionError = updateSessionError; exports.deleteSession = deleteSession; exports.addAnnotation = addAnnotation; exports.saveEnvironment = saveEnvironment; exports.setSessionPriority = setSessionPriority; exports.markSessionSaved = markSessionSaved; exports.clearAllSessions = clearAllSessions; exports.sanitizeTokenShapes = sanitizeTokenShapes; exports.sanitizeReportForUpload = sanitizeReportForUpload; exports.getNetworkFailures = getNetworkFailures; exports.clearNetworkFailures = clearNetworkFailures; exports.collectClicks = collectClicks; exports.collectInputs = collectInputs; exports.collectSelectChanges = collectSelectChanges; exports.collectFormSubmits = collectFormSubmits; exports.collectRouteChanges = collectRouteChanges; exports.collectApiRequests = collectApiRequests; exports.collectXhrRequests = collectXhrRequests; exports.drainPerformanceNetwork = drainPerformanceNetwork; exports.collectPerformanceNetwork = collectPerformanceNetwork; exports.collectErrors = collectErrors; exports.collectConsoleErrors = collectConsoleErrors; exports.collectConsoleWarnings = collectConsoleWarnings; exports.collectConsoleLogs = collectConsoleLogs; exports.tbIsolationCss = tbIsolationCss; exports.matchesShortcut = matchesShortcut; exports.escapeHtml = escapeHtml;
//# sourceMappingURL=chunk-FO62QDYE.cjs.map
{"version":3,"sources":["d:\\Project\\TraceBug-ai\\dist\\chunk-FO62QDYE.cjs"],"names":[],"mappings":"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IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,UAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,gBAAA;AACA,cAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,cAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAA;AACA,IAAA;AACA;AACA;AACA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"D:\\Project\\TraceBug-ai\\dist\\chunk-FO62QDYE.cjs","sourcesContent":[null]}
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }
var _chunkFO62QDYEcjs = require('./chunk-FO62QDYE.cjs');
// src/scanner/helpers.ts
var _issueCounter = 0;
function makeIssueId(detector) {
_issueCounter += 1;
return `${detector}_${Date.now().toString(36)}_${_issueCounter}`;
}
function buildSelector(el) {
if (!el || el.nodeType !== 1) return "";
if (el.id) return `#${cssEscape(el.id)}`;
const testId = el.getAttribute("data-testid") || el.getAttribute("data-test-id");
if (testId) return `[data-testid="${cssEscape(testId)}"]`;
const parts = [];
let cur = el;
let depth = 0;
while (cur && cur.nodeType === 1 && cur.tagName !== "BODY" && depth < 5) {
const tag = cur.tagName.toLowerCase();
const parent = cur.parentElement;
if (!parent) {
parts.unshift(tag);
break;
}
const siblings = Array.from(parent.children).filter((c) => c.tagName === cur.tagName);
if (siblings.length > 1) {
const idx = siblings.indexOf(cur) + 1;
parts.unshift(`${tag}:nth-of-type(${idx})`);
} else {
parts.unshift(tag);
}
cur = parent;
depth += 1;
}
return parts.join(" > ");
}
function coerceSeverity(impact) {
switch ((impact || "").toLowerCase()) {
case "critical":
return "critical";
case "serious":
return "serious";
case "moderate":
return "moderate";
case "minor":
return "minor";
default:
return "minor";
}
}
function cssEscape(value) {
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
return CSS.escape(value);
}
return value.replace(/[^\w-]/g, (ch) => `\\${ch}`);
}
// src/scanner/detectors/broken-images.ts
async function detectBrokenImages() {
const issues = [];
const imgs = Array.from(document.images);
for (const img of imgs) {
if (img.closest("#tracebug-root")) continue;
if (!img.complete) continue;
if (img.naturalWidth > 0) continue;
const src = img.currentSrc || img.src;
if (!src) continue;
issues.push({
id: makeIssueId("broken-image"),
detector: "broken-image",
severity: "moderate",
title: `Broken image: ${truncateUrl(src)}`,
description: `<img> element failed to load. The browser tried to fetch \`${src}\` and got a network error or a non-image response. ${img.alt ? `Alt text: "${img.alt}"` : "No alt text \u2014 also fails accessibility."}`,
selector: buildSelector(img),
url: src,
page: window.location.pathname,
detectedAt: Date.now()
});
}
return issues;
}
function truncateUrl(url) {
if (url.length <= 60) return url;
const tail = url.split("/").pop() || url.slice(-40);
return `\u2026/${tail}`;
}
// src/scanner/detectors/mixed-content.ts
var ATTR_TARGETS = [
{ tag: "img", attr: "src" },
{ tag: "script", attr: "src" },
{ tag: "iframe", attr: "src" },
{ tag: "link", attr: "href" },
{ tag: "audio", attr: "src" },
{ tag: "video", attr: "src" },
{ tag: "source", attr: "src" },
{ tag: "embed", attr: "src" },
{ tag: "object", attr: "data" }
];
async function detectMixedContent() {
if (typeof window === "undefined" || window.location.protocol !== "https:") {
return [];
}
const issues = [];
for (const { tag, attr } of ATTR_TARGETS) {
const elements = document.querySelectorAll(`${tag}[${attr}]`);
for (const el of Array.from(elements)) {
if (el.closest("#tracebug-root")) continue;
const value = el.getAttribute(attr) || "";
if (!value.startsWith("http://")) continue;
if (tag === "link") {
const rel = (el.rel || "").toLowerCase();
const fetchableRels = ["stylesheet", "preload", "prefetch", "manifest", "icon", "shortcut icon"];
if (!fetchableRels.some((r) => rel.includes(r))) continue;
}
issues.push({
id: makeIssueId("mixed-content"),
detector: "mixed-content",
severity: tag === "script" || tag === "iframe" ? "serious" : "moderate",
title: `Mixed content: ${tag} loads over HTTP`,
description: `<${tag}> on an HTTPS page references \`${value}\`. Browsers block or downgrade this \u2014 the resource usually fails to load and breaks the page's secure-context indicator.`,
selector: buildSelector(el),
url: value,
page: window.location.pathname,
detectedAt: Date.now()
});
}
}
return issues;
}
// src/fingerprint.ts
async function computeFingerprint(errorMessage, errorStack, page) {
const errorType = extractErrorType(errorMessage);
const topFrames = extractTopFrames(errorStack || "", 3);
const input = `${errorType}|${topFrames.join("\n")}|${page}`;
if (typeof crypto !== "undefined" && crypto.subtle && typeof crypto.subtle.digest === "function") {
try {
const buf = new TextEncoder().encode(input);
const hash = await crypto.subtle.digest("SHA-1", buf);
return bufferToHex(hash).slice(0, 16);
} catch (e) {
}
}
return djb2(input).toString(16).padStart(8, "0");
}
function extractErrorType(message) {
const m = message.match(/^([A-Z][a-zA-Z]+Error|Error)\b/);
return m ? m[1] : "Error";
}
function extractTopFrames(stack, n) {
const frames = [];
const lines = stack.split("\n");
for (const line of lines) {
const m = line.match(/(https?:\/\/[^):\s]+|[^():\s]+\.[a-z]+):(\d+):(\d+)/i);
if (m) {
const url = m[1];
const path = url.includes("://") ? new URL(url, typeof window !== "undefined" ? window.location.origin : "http://localhost").pathname : url;
frames.push(`${path}:${m[2]}:${m[3]}`);
if (frames.length >= n) break;
}
}
return frames;
}
function bufferToHex(buf) {
const bytes = new Uint8Array(buf);
let out = "";
for (let i = 0; i < bytes.length; i++) out += bytes[i].toString(16).padStart(2, "0");
return out;
}
function djb2(str) {
let hash = 5381;
for (let i = 0; i < str.length; i++) hash = (hash << 5) + hash + str.charCodeAt(i) | 0;
return hash >>> 0;
}
// src/scanner/detectors/session-data.ts
var SLOW_API_MS = 2e3;
var MAX_CONTEXT_SAMPLES = 10;
async function detectConsoleErrors(session) {
var _a, _b, _c;
if (!session) return [];
const groups = /* @__PURE__ */ new Map();
for (let i = 0; i < session.events.length; i++) {
const e = session.events[i];
if (e.type !== "error" && e.type !== "unhandled_rejection" && e.type !== "console_error") continue;
const message = ((_a = e.data.error) == null ? void 0 : _a.message) || e.data.message || "";
if (!message) continue;
const stack = ((_b = e.data.error) == null ? void 0 : _b.stack) || "";
const page = e.page || (typeof window !== "undefined" ? window.location.pathname : "");
const fp = await computeFingerprint(message, stack, page);
const precedingAction = describePrecedingAction(session.events, i);
const existing = groups.get(fp);
if (existing) {
existing.issue.occurrences = (existing.issue.occurrences || 1) + 1;
existing.issue.lastSeenAt = e.timestamp;
if (existing.samples.length < MAX_CONTEXT_SAMPLES) {
existing.samples.push({ timestamp: e.timestamp, precedingAction });
}
continue;
}
const firstFrame = ((_c = stack.split("\n").find((l) => l.trim().startsWith("at "))) == null ? void 0 : _c.trim()) || "";
const issue = {
id: makeIssueId("console-error"),
detector: "console-error",
severity: classifyErrorSeverity(message),
title: `JS error: ${message.slice(0, 70)}${message.length > 70 ? "\u2026" : ""}`,
description: firstFrame ? `${message}
First frame: ${firstFrame}` : message,
page,
detectedAt: e.timestamp,
fingerprint: fp,
occurrences: 1,
firstSeenAt: e.timestamp,
lastSeenAt: e.timestamp
};
groups.set(fp, { issue, samples: [{ timestamp: e.timestamp, precedingAction }] });
}
const out = [];
for (const g of groups.values()) {
const n = g.issue.occurrences || 1;
if (n > 1) {
g.issue.title = `${g.issue.title} [\xD7${n}]`;
g.issue.contextSamples = g.samples;
}
out.push(g.issue);
}
return out;
}
function describePrecedingAction(events, i) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
for (let j = i - 1; j >= 0; j--) {
const e = events[j];
if (e.type === "click") {
const t = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.text) || ((_d = (_c = e.data) == null ? void 0 : _c.element) == null ? void 0 : _d.ariaLabel) || ((_f = (_e = e.data) == null ? void 0 : _e.element) == null ? void 0 : _f.tag) || "element";
return `clicked "${String(t).slice(0, 40)}"`;
}
if (e.type === "input") {
const n = ((_h = (_g = e.data) == null ? void 0 : _g.element) == null ? void 0 : _h.name) || ((_j = (_i = e.data) == null ? void 0 : _i.element) == null ? void 0 : _j.id) || "field";
return `typed in ${n}`;
}
if (e.type === "select_change") return "selected an option";
if (e.type === "form_submit") return "submitted a form";
if (e.type === "route_change") return `navigated to ${((_k = e.data) == null ? void 0 : _k.to) || "page"}`;
}
return void 0;
}
async function detectFailedRequests(session) {
if (!session) return [];
const issues = [];
const buffer = _chunkFO62QDYEcjs.getNetworkFailures.call(void 0, );
for (const e of session.events) {
if (e.type !== "api_request") continue;
const req = e.data.request;
if (!req) continue;
const status = req.statusCode || 0;
if (status >= 200 && status < 400) continue;
if (status === 0 && req.method === "HEAD") continue;
const match = buffer.find(
(b) => b.url === req.url && b.method === req.method && b.status === status && Math.abs(b.timestamp - e.timestamp) < 5e3
);
const snippet = (match == null ? void 0 : match.response) ? `
Response: ${match.response.slice(0, 160)}` : "";
issues.push({
id: makeIssueId("failed-request"),
detector: "failed-request",
severity: status >= 500 ? "critical" : status === 0 ? "serious" : "moderate",
title: `${req.method} ${truncatePath(req.url)} \u2192 ${status === 0 ? "Network Error" : status}`,
description: `Request failed in ${req.durationMs || 0}ms.${snippet}`,
url: req.url,
page: e.page || window.location.pathname,
detectedAt: e.timestamp
});
}
return issues;
}
async function detectSlowApis(session) {
if (!session) return [];
const issues = [];
for (const e of session.events) {
if (e.type !== "api_request") continue;
const req = e.data.request;
if (!req) continue;
const status = req.statusCode || 0;
if (status < 200 || status >= 400) continue;
const duration = req.durationMs || 0;
if (duration < SLOW_API_MS) continue;
issues.push({
id: makeIssueId("slow-api"),
detector: "slow-api",
severity: duration > 5e3 ? "serious" : "moderate",
title: `Slow API: ${req.method} ${truncatePath(req.url)} (${duration}ms)`,
description: `This request took ${(duration / 1e3).toFixed(1)}s \u2014 over the ${SLOW_API_MS / 1e3}s threshold. Slow APIs are a common UX complaint and a leading cause of perceived bugs ("the page is frozen").`,
url: req.url,
page: e.page || window.location.pathname,
detectedAt: e.timestamp
});
}
return issues;
}
function truncatePath(url) {
try {
const u = new URL(url, window.location.origin);
const p = u.pathname.length > 50 ? u.pathname.slice(0, 47) + "\u2026" : u.pathname;
return p;
} catch (e) {
return url.length > 50 ? url.slice(0, 47) + "\u2026" : url;
}
}
function classifyErrorSeverity(message) {
if (/TypeError|ReferenceError|SyntaxError/i.test(message)) return "critical";
if (/Network|fetch|failed to/i.test(message)) return "serious";
return "moderate";
}
// src/scanner/detectors/a11y.ts
var _axePromise = null;
function loadAxe() {
if (_axePromise) return _axePromise;
_axePromise = Promise.resolve().then(() => _interopRequireWildcard(require("axe-core"))).then((mod) => mod.default || mod).catch((err) => {
console.warn("[TraceBug] axe-core failed to load:", err);
return null;
});
return _axePromise;
}
async function detectA11yViolations() {
const axe = await loadAxe();
if (!axe || typeof axe.run !== "function") return [];
let results;
try {
results = await axe.run(document, {
// Only WCAG-tagged rules — keeps signal-to-noise high. Best-practice
// rules add ~30% more noise without proportional value for QA.
runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] },
// Skip our own UI so QA isn't told their toolbar fails contrast checks.
// axe accepts a context with exclude — we pass { exclude: [...] } via
// the second-argument options-shaped form below to keep types loose.
resultTypes: ["violations"]
});
} catch (err) {
console.warn("[TraceBug] axe.run failed:", err);
return [];
}
const issues = [];
const violations = (results == null ? void 0 : results.violations) || [];
for (const v of violations) {
const nodes = v.nodes || [];
const firstNode = nodes[0];
const selector = Array.isArray(firstNode == null ? void 0 : firstNode.target) ? firstNode.target.join(" ") : "";
const exampleSnippet = ((firstNode == null ? void 0 : firstNode.html) || "").slice(0, 120);
const moreSuffix = nodes.length > 1 ? ` (+ ${nodes.length - 1} more element${nodes.length === 2 ? "" : "s"})` : "";
issues.push({
id: makeIssueId("axe-a11y"),
detector: "axe-a11y",
severity: coerceSeverity(v.impact),
title: `${v.help || v.id}${moreSuffix}`,
description: `${v.description || v.id}
First element: \`${exampleSnippet}\``,
selector: selector || void 0,
helpUrl: v.helpUrl,
page: window.location.pathname,
detectedAt: Date.now()
});
}
return issues;
}
// src/scanner/detectors/frustration.ts
var RAGE_WINDOW_MS = 1500;
var RAGE_MIN_CLICKS = 3;
var DEAD_RESPONSE_WINDOW_MS = 1500;
var ABANDON_WINDOW_MS = 6e4;
var ERROR_CORRELATION_WINDOW_MS = 2500;
async function detectFrustration(session) {
var _a;
if (!session) return [];
const events = session.events;
if (events.length === 0) return [];
const issues = [];
const page = ((_a = session.events[0]) == null ? void 0 : _a.page) || window.location.pathname;
issues.push(...detectRageClicks(events, page));
issues.push(...detectDeadClicks(events, page));
issues.push(...detectFormAbandonment(events, page));
issues.push(...detectErrorCorrelated(events, page));
return issues;
}
function detectRageClicks(events, page) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const out = [];
const seenGroups = /* @__PURE__ */ new Set();
for (let i = 0; i < events.length; i++) {
const e = events[i];
if (e.type !== "click") continue;
const sel = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.selector) || ((_d = (_c = e.data) == null ? void 0 : _c.element) == null ? void 0 : _d.testId) || "";
if (!sel) continue;
const cluster = [e];
let j = i + 1;
while (j < events.length) {
const next = events[j];
if (next.timestamp - e.timestamp > RAGE_WINDOW_MS) break;
if (isResponseEvent(next)) break;
if (next.type === "click") {
const nextSel = ((_f = (_e = next.data) == null ? void 0 : _e.element) == null ? void 0 : _f.selector) || ((_h = (_g = next.data) == null ? void 0 : _g.element) == null ? void 0 : _h.testId) || "";
if (nextSel === sel) cluster.push(next);
}
j++;
}
if (cluster.length >= RAGE_MIN_CLICKS) {
const key = `${sel}@${e.timestamp}`;
if (seenGroups.has(key)) continue;
seenGroups.add(key);
const label = clickLabel(cluster[0]);
out.push({
id: makeIssueId("frustration-rage"),
detector: "frustration-rage",
severity: "serious",
title: `Rage clicks on ${label} (${cluster.length}\xD7 in ${Math.round(cluster[cluster.length - 1].timestamp - cluster[0].timestamp)}ms)`,
description: `User clicked the same element ${cluster.length} times within ${RAGE_WINDOW_MS}ms with no observable response (no API call, navigation, or DOM update). The element either doesn't respond to clicks or feels broken.`,
selector: sel,
page,
detectedAt: cluster[0].timestamp,
firstSeenAt: cluster[0].timestamp,
lastSeenAt: cluster[cluster.length - 1].timestamp,
occurrences: cluster.length
});
i = j - 1;
}
}
return out;
}
function detectDeadClicks(events, page) {
var _a, _b;
const out = [];
const MAX = 5;
for (let i = 0; i < events.length && out.length < MAX; i++) {
const e = events[i];
if (e.type !== "click") continue;
let responsive = false;
for (let j = i + 1; j < events.length; j++) {
const next = events[j];
if (next.timestamp - e.timestamp > DEAD_RESPONSE_WINDOW_MS) break;
if (isResponseEvent(next)) {
responsive = true;
break;
}
}
if (responsive) continue;
const sel = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.selector) || "";
const label = clickLabel(e);
out.push({
id: makeIssueId("frustration-dead"),
detector: "frustration-dead",
severity: "moderate",
title: `Dead click on ${label}`,
description: `Clicked but nothing happened within ${DEAD_RESPONSE_WINDOW_MS}ms (no API call, navigation, or DOM input). The element may have an unbound handler, a swallowed event, or be visually clickable but disabled.`,
selector: sel,
page: e.page || page,
detectedAt: e.timestamp
});
}
return out;
}
function detectFormAbandonment(events, _page) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
const out = [];
const formActivity = {};
for (const e of events) {
if (e.type === "input") {
const formId = ((_b = (_a = e.data) == null ? void 0 : _a.element) == null ? void 0 : _b.formId) || ((_d = (_c = e.data) == null ? void 0 : _c.element) == null ? void 0 : _d.formAction) || "_default";
if (!formActivity[formId]) {
formActivity[formId] = { firstInputAt: e.timestamp, fieldsSeen: /* @__PURE__ */ new Set(), lastInputAt: e.timestamp, page: e.page };
}
const name = ((_f = (_e = e.data) == null ? void 0 : _e.element) == null ? void 0 : _f.name) || ((_h = (_g = e.data) == null ? void 0 : _g.element) == null ? void 0 : _h.id) || "field";
formActivity[formId].fieldsSeen.add(name);
formActivity[formId].lastInputAt = e.timestamp;
} else if (e.type === "form_submit") {
const formId = ((_j = (_i = e.data) == null ? void 0 : _i.form) == null ? void 0 : _j.id) || "_default";
delete formActivity[formId];
} else if (e.type === "route_change") {
for (const formId of Object.keys(formActivity)) {
const a = formActivity[formId];
if (e.timestamp - a.lastInputAt > ABANDON_WINDOW_MS) continue;
if (a.fieldsSeen.size === 0) continue;
out.push({
id: makeIssueId("frustration-abandon"),
detector: "frustration-abandon",
severity: "moderate",
title: `Form abandoned on ${a.page} (${a.fieldsSeen.size} field${a.fieldsSeen.size === 1 ? "" : "s"} filled)`,
description: `User typed into ${a.fieldsSeen.size} field${a.fieldsSeen.size === 1 ? "" : "s"} (${Array.from(a.fieldsSeen).slice(0, 5).join(", ")}) and then navigated away without submitting. Likely a UX problem: the submit button is unclear, the form requires too much info, or it's failing silently.`,
page: a.page,
detectedAt: a.lastInputAt,
firstSeenAt: a.firstInputAt,
lastSeenAt: a.lastInputAt
});
delete formActivity[formId];
}
}
}
return out;
}
function detectErrorCorrelated(events, page) {
var _a, _b, _c, _d, _e, _f;
const out = [];
const seen = /* @__PURE__ */ new Set();
for (let i = 0; i < events.length; i++) {
const e = events[i];
const isError = e.type === "error" || e.type === "unhandled_rejection" || e.type === "console_error";
if (!isError) continue;
let click = null;
for (let j = i - 1; j >= 0; j--) {
const prev = events[j];
if (e.timestamp - prev.timestamp > ERROR_CORRELATION_WINDOW_MS) break;
if (prev.type === "click") {
click = prev;
break;
}
}
if (!click) continue;
const errMsg = ((_b = (_a = e.data) == null ? void 0 : _a.error) == null ? void 0 : _b.message) || "";
if (!errMsg) continue;
const key = `${errMsg}::${((_d = (_c = click.data) == null ? void 0 : _c.element) == null ? void 0 : _d.selector) || ""}`;
if (seen.has(key)) continue;
seen.add(key);
const label = clickLabel(click);
const truncMsg = errMsg.length > 60 ? errMsg.slice(0, 57) + "\u2026" : errMsg;
const delta = Math.round(e.timestamp - click.timestamp);
out.push({
id: makeIssueId("frustration-error-correlated"),
detector: "frustration-error-correlated",
severity: "critical",
title: `Click on ${label} triggered: ${truncMsg}`,
description: `An error fired ${delta}ms after the user clicked ${label}. This is almost certainly the offending interaction \u2014 the click handler threw, or its async path failed.`,
selector: (_f = (_e = click.data) == null ? void 0 : _e.element) == null ? void 0 : _f.selector,
page: e.page || page,
detectedAt: e.timestamp
});
}
return out;
}
function isResponseEvent(e) {
return e.type === "api_request" || e.type === "route_change" || e.type === "input" || e.type === "form_submit" || e.type === "select_change";
}
function clickLabel(e) {
var _a;
const el = (_a = e.data) == null ? void 0 : _a.element;
const raw = (el == null ? void 0 : el.text) || (el == null ? void 0 : el.ariaLabel) || (el == null ? void 0 : el.testId) || (el == null ? void 0 : el.id) || (el == null ? void 0 : el.tag) || "element";
const trimmed = String(raw).replace(/\s+/g, " ").trim();
return trimmed.length > 40 ? `"${trimmed.slice(0, 37)}\u2026"` : `"${trimmed}"`;
}
// src/scanner/index.ts
var _issues = [];
var _scanInFlight = null;
var _lastScanAt = 0;
var SEVERITY_ORDER = {
critical: 0,
serious: 1,
moderate: 2,
minor: 3
};
async function scan() {
if (_scanInFlight) {
const issues = await _scanInFlight;
return { issues, durationMs: 0, scannedAt: _lastScanAt };
}
const startedAt = Date.now();
const sessions = _chunkFO62QDYEcjs.getAllSessions.call(void 0, ).sort((a, b) => b.updatedAt - a.updatedAt);
const session = sessions[0] || null;
const safeRun = (p) => p.catch((err) => {
console.warn("[TraceBug] Detector failed:", err);
return [];
});
_scanInFlight = Promise.all([
safeRun(detectBrokenImages()),
safeRun(detectMixedContent()),
safeRun(detectConsoleErrors(session)),
safeRun(detectFailedRequests(session)),
safeRun(detectSlowApis(session)),
safeRun(detectA11yViolations()),
safeRun(detectFrustration(session))
]).then((results) => {
const all = [].concat(...results);
all.sort((a, b) => {
const sev = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity];
if (sev !== 0) return sev;
return a.detectedAt - b.detectedAt;
});
_issues = all;
return all;
});
try {
const issues = await _scanInFlight;
_lastScanAt = Date.now();
return { issues, durationMs: _lastScanAt - startedAt, scannedAt: _lastScanAt };
} finally {
_scanInFlight = null;
}
}
function getIssues(options) {
var _a;
const includeDismissed = (_a = options == null ? void 0 : options.includeDismissed) != null ? _a : false;
return includeDismissed ? _issues.slice() : _issues.filter((i) => !i.dismissed);
}
function dismissIssue(id) {
const issue = _issues.find((i) => i.id === id);
if (!issue) return false;
issue.dismissed = true;
return true;
}
function undismissIssue(id) {
const issue = _issues.find((i) => i.id === id);
if (!issue) return false;
issue.dismissed = false;
return true;
}
function clearIssues() {
_issues = [];
_lastScanAt = 0;
}
function getIssueCountsByDetector() {
const counts = {
"axe-a11y": 0,
"broken-image": 0,
"mixed-content": 0,
"console-error": 0,
"slow-api": 0,
"failed-request": 0,
"frustration-rage": 0,
"frustration-dead": 0,
"frustration-abandon": 0,
"frustration-error-correlated": 0
};
for (const i of _issues) {
if (i.dismissed) continue;
counts[i.detector] = (counts[i.detector] || 0) + 1;
}
return counts;
}
function getIssueCountsBySeverity() {
const counts = {
critical: 0,
serious: 0,
moderate: 0,
minor: 0
};
for (const i of _issues) {
if (i.dismissed) continue;
counts[i.severity] += 1;
}
return counts;
}
function getIssueById(id) {
return _issues.find((i) => i.id === id) || null;
}
exports.scan = scan; exports.getIssues = getIssues; exports.dismissIssue = dismissIssue; exports.undismissIssue = undismissIssue; exports.clearIssues = clearIssues; exports.getIssueCountsByDetector = getIssueCountsByDetector; exports.getIssueCountsBySeverity = getIssueCountsBySeverity; exports.getIssueById = getIssueById;
//# sourceMappingURL=chunk-V4KZ4KC5.cjs.map
{"version":3,"sources":["d:\\Project\\TraceBug-ai\\dist\\chunk-V4KZ4KC5.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,wDAA6B;AAC7B;AACA;AACA,IAAI,cAAc,EAAE,CAAC;AACrB,SAAS,WAAW,CAAC,QAAQ,EAAE;AAC/B,EAAE,cAAc,GAAG,CAAC;AACpB,EAAE,OAAO,CAAC,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA;AACA,aAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA;AACA,UAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA;AACA;AACA,MAAA;AACA;AACA;AACA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA;AACA,iBAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"D:\\Project\\TraceBug-ai\\dist\\chunk-V4KZ4KC5.cjs","sourcesContent":[null]}
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }
var _chunkV4KZ4KC5cjs = require('./chunk-V4KZ4KC5.cjs');
var _chunkFO62QDYEcjs = require('./chunk-FO62QDYE.cjs');
// src/ui/issues-panel.ts
var PANEL_ID = "tracebug-issues-panel";
var STYLE_ID = "tracebug-issues-panel-styles";
var _isOpen = false;
var _root = null;
var SEVERITY_COLORS = {
critical: { bg: "#7f1d1d", fg: "#fee2e2", border: "#dc2626" },
serious: { bg: "#7c2d12", fg: "#fed7aa", border: "#ea580c" },
moderate: { bg: "#713f12", fg: "#fde68a", border: "#ca8a04" },
minor: { bg: "#1e3a8a", fg: "#bfdbfe", border: "#2563eb" }
};
var DETECTOR_LABELS = {
"axe-a11y": "A11y",
"broken-image": "Broken image",
"mixed-content": "Mixed content",
"console-error": "JS error",
"slow-api": "Slow API",
"failed-request": "Failed request",
"frustration-rage": "Rage clicks",
"frustration-dead": "Dead click",
"frustration-abandon": "Form abandoned",
"frustration-error-correlated": "Click \u2192 error"
};
function isIssuesPanelOpen() {
return _isOpen;
}
async function showIssuesPanel(root, options) {
var _a;
if (_isOpen) return;
_root = root;
_injectStyles();
_open(root, { issues: [], loading: true });
try {
if ((_a = options == null ? void 0 : options.rescan) != null ? _a : true) {
await _chunkV4KZ4KC5cjs.scan.call(void 0, );
}
} catch (err) {
console.warn("[TraceBug] Scan failed:", err);
}
_renderBody(_chunkV4KZ4KC5cjs.getIssues.call(void 0, ));
}
function _injectStyles() {
if (document.getElementById(STYLE_ID)) return;
const style = document.createElement("style");
style.id = STYLE_ID;
style.textContent = `
@keyframes tracebug-issue-locate-flash {
0%, 100% { box-shadow: 0 0 0 0 rgba(99,102,241,0.0); outline: 2px solid transparent; }
30% { box-shadow: 0 0 0 6px rgba(99,102,241,0.5); outline: 2px solid #6366F1; }
}
#${PANEL_ID}-overlay {
position: fixed !important;
inset: 0 !important;
z-index: 2147483647 !important;
background: rgba(0,0,0,0.75) !important;
backdrop-filter: blur(6px) !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
padding: 20px !important;
pointer-events: auto !important;
box-sizing: border-box !important;
}
#${PANEL_ID} {
background: var(--tb-bg-secondary, #1a1a2e) !important;
border: 1px solid var(--tb-border-hover, #3a3a5e) !important;
border-radius: var(--tb-radius-lg, 12px) !important;
width: 100% !important;
max-width: 720px !important;
max-height: 90vh !important;
display: flex !important;
flex-direction: column !important;
overflow: hidden !important;
font-family: var(--tb-font-family, system-ui, -apple-system, sans-serif) !important;
color: var(--tb-text-primary, #e0e0e0) !important;
box-sizing: border-box !important;
box-shadow: 0 20px 60px rgba(0,0,0,0.5) !important;
}
#${PANEL_ID} *, #${PANEL_ID} *::before, #${PANEL_ID} *::after { box-sizing: border-box !important; }
#${PANEL_ID} button { font-family: inherit !important; cursor: pointer !important; }
#${PANEL_ID} .tb-issue-row {
padding: 12px 14px;
border-top: 1px solid var(--tb-border, #2a2a3e);
display: flex;
gap: 12px;
align-items: flex-start;
}
#${PANEL_ID} .tb-issue-row:hover { background: var(--tb-bg-primary, #0f0f1a); }
#${PANEL_ID} .tb-sev-badge {
font-size: 10px;
font-weight: 700;
padding: 3px 7px;
border-radius: 4px;
letter-spacing: 0.4px;
text-transform: uppercase;
flex-shrink: 0;
border: 1px solid;
}
#${PANEL_ID} .tb-detector-tag {
font-size: 10px;
color: var(--tb-text-muted, #888);
background: var(--tb-bg-primary, #0f0f1a);
border: 1px solid var(--tb-border, #2a2a3e);
padding: 2px 6px;
border-radius: 4px;
flex-shrink: 0;
}
#${PANEL_ID} .tb-issue-actions {
display: flex;
gap: 6px;
flex-shrink: 0;
}
#${PANEL_ID} .tb-issue-actions button {
background: transparent;
color: var(--tb-text-secondary, #aaa);
border: 1px solid var(--tb-border, #2a2a3e);
border-radius: 4px;
padding: 4px 10px;
font-size: 11px;
transition: all 0.15s;
}
#${PANEL_ID} .tb-issue-actions button:hover {
background: var(--tb-btn-hover, #ffffff15);
color: var(--tb-text-primary, #fff);
}
#${PANEL_ID} .tb-issue-actions button[data-action="file"] {
background: var(--tb-accent, #6366F1);
color: #fff;
border-color: transparent;
}
#${PANEL_ID} .tb-issue-actions button[data-action="file"]:hover { opacity: 0.9; }
`;
document.head.appendChild(style);
}
function _open(root, state) {
var _a;
_isOpen = true;
(_a = document.getElementById(`${PANEL_ID}-overlay`)) == null ? void 0 : _a.remove();
const overlay = document.createElement("div");
overlay.id = `${PANEL_ID}-overlay`;
overlay.dataset.tracebug = "issues-panel-overlay";
overlay.setAttribute("role", "dialog");
overlay.setAttribute("aria-modal", "true");
overlay.setAttribute("aria-label", "Page issues");
const panel = document.createElement("div");
panel.id = PANEL_ID;
panel.dataset.tracebug = "issues-panel";
panel.innerHTML = _shellHtml(state);
overlay.appendChild(panel);
root.appendChild(overlay);
_wireShellHandlers(overlay, panel);
}
function _shellHtml(state) {
return `
<div data-tb-issues="header" style="padding:16px 18px;display:flex;align-items:center;gap:12px;border-bottom:1px solid var(--tb-border, #2a2a3e)">
<span style="font-size:20px">\u{1F50D}</span>
<div style="flex:1;min-width:0">
<div style="font-size:16px;font-weight:700;color:var(--tb-text-primary, #fff)">Page Issues</div>
<div data-tb-issues="subtitle" style="font-size:11px;color:var(--tb-text-muted, #888);margin-top:2px">${state.loading ? "Scanning\u2026" : "No scan yet"}</div>
</div>
<button data-tb-issues="rescan" style="background:transparent;color:var(--tb-text-secondary, #aaa);border:1px solid var(--tb-border, #2a2a3e);border-radius:6px;padding:6px 12px;font-size:12px;display:flex;align-items:center;gap:6px">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"/><polyline points="21 3 21 9 15 9"/></svg>
Rescan
</button>
<button data-tb-issues="close" aria-label="Close" style="background:none;border:none;color:var(--tb-text-muted, #888);font-size:20px;padding:4px 8px;border-radius:6px">&times;</button>
</div>
<div data-tb-issues="body" style="flex:1;overflow-y:auto;min-height:200px">
${state.loading ? _loadingHtml() : ""}
</div>
`;
}
function _loadingHtml() {
return `
<div style="padding:48px 20px;text-align:center;color:var(--tb-text-muted, #888);font-size:13px">
<div style="font-size:24px;margin-bottom:8px">\u{1F50D}</div>
Scanning page for issues\u2026
<div style="font-size:11px;margin-top:6px;opacity:0.7">Loading axe-core, checking images, network calls, JS errors\u2026</div>
</div>
`;
}
function _wireShellHandlers(overlay, panel) {
const close = () => {
_isOpen = false;
overlay.remove();
document.removeEventListener("keydown", escHandler);
};
panel.querySelector('[data-tb-issues="close"]').addEventListener("click", close);
overlay.addEventListener("click", (e) => {
if (e.target === overlay) close();
});
panel.querySelector('[data-tb-issues="rescan"]').addEventListener("click", async () => {
const body = panel.querySelector('[data-tb-issues="body"]');
body.innerHTML = _loadingHtml();
const sub = panel.querySelector('[data-tb-issues="subtitle"]');
sub.textContent = "Scanning\u2026";
await _chunkV4KZ4KC5cjs.scan.call(void 0, );
_renderBody(_chunkV4KZ4KC5cjs.getIssues.call(void 0, ));
});
const escHandler = (e) => {
if (e.key === "Escape") close();
};
document.addEventListener("keydown", escHandler);
}
function _renderBody(issues) {
const panel = document.getElementById(PANEL_ID);
if (!panel) return;
const body = panel.querySelector('[data-tb-issues="body"]');
const subtitle = panel.querySelector('[data-tb-issues="subtitle"]');
if (issues.length === 0) {
subtitle.textContent = "No issues found";
body.innerHTML = `
<div style="padding:48px 20px;text-align:center;color:var(--tb-text-muted, #888);font-size:13px">
<div style="font-size:32px;margin-bottom:8px">\u2713</div>
Clean scan \u2014 no issues detected on this page.
<div style="font-size:11px;margin-top:6px;opacity:0.7">Includes a11y \xB7 broken images \xB7 mixed content \xB7 JS errors \xB7 failed/slow API calls</div>
</div>
`;
return;
}
const counts = {};
for (const i of issues) counts[i.severity] = (counts[i.severity] || 0) + 1;
const summary = ["critical", "serious", "moderate", "minor"].filter((s) => counts[s]).map((s) => `${counts[s]} ${s}`).join(" \xB7 ");
subtitle.textContent = `${issues.length} issue${issues.length === 1 ? "" : "s"} \xB7 ${summary}`;
body.innerHTML = issues.map((issue) => _issueRowHtml(issue)).join("");
body.querySelectorAll("[data-tb-issue-id]").forEach((row) => {
var _a, _b, _c;
const id = row.dataset.tbIssueId;
const issue = issues.find((i) => i.id === id);
if (!issue) return;
(_a = row.querySelector('[data-action="locate"]')) == null ? void 0 : _a.addEventListener("click", () => _locate(issue));
(_b = row.querySelector('[data-action="dismiss"]')) == null ? void 0 : _b.addEventListener("click", () => {
_chunkV4KZ4KC5cjs.dismissIssue.call(void 0, id);
_renderBody(_chunkV4KZ4KC5cjs.getIssues.call(void 0, ));
});
(_c = row.querySelector('[data-action="file"]')) == null ? void 0 : _c.addEventListener("click", () => _fileAsBug(issue));
});
}
function _issueRowHtml(issue) {
const colors = SEVERITY_COLORS[issue.severity];
const detectorLabel = DETECTOR_LABELS[issue.detector];
const desc = issue.description.length > 240 ? issue.description.slice(0, 237) + "\u2026" : issue.description;
const repeats = (issue.occurrences || 1) > 1;
const samplesHtml = repeats && issue.contextSamples && issue.contextSamples.length > 0 ? `<details style="margin-top:6px;font-size:11px">
<summary style="cursor:pointer;color:var(--tb-text-muted, #888)">
View all ${issue.occurrences} contexts
</summary>
<ol style="margin:6px 0 0 20px;padding:0;color:var(--tb-text-secondary, #aaa);line-height:1.5">
${issue.contextSamples.map((s) => `
<li>${new Date(s.timestamp).toLocaleTimeString()}${s.precedingAction ? ` \xB7 ${_chunkFO62QDYEcjs.escapeHtml.call(void 0, s.precedingAction)}` : ""}</li>
`).join("")}
</ol>
</details>` : "";
return `
<div class="tb-issue-row" data-tb-issue-id="${issue.id}">
<span class="tb-sev-badge" style="background:${colors.bg};color:${colors.fg};border-color:${colors.border}">${issue.severity}</span>
<div style="flex:1;min-width:0">
<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px;flex-wrap:wrap">
<span class="tb-detector-tag">${detectorLabel}</span>
<span style="font-size:13px;color:var(--tb-text-primary, #e0e0e0);font-weight:500">${_chunkFO62QDYEcjs.escapeHtml.call(void 0, issue.title)}</span>
</div>
<div style="font-size:11px;color:var(--tb-text-muted, #888);line-height:1.5;white-space:pre-wrap">${_chunkFO62QDYEcjs.escapeHtml.call(void 0, desc)}</div>
${issue.helpUrl ? `<a href="${issue.helpUrl}" target="_blank" rel="noopener noreferrer" style="font-size:11px;color:var(--tb-accent, #6366F1);margin-top:4px;display:inline-block">Learn more \u2192</a>` : ""}
${samplesHtml}
</div>
<div class="tb-issue-actions">
${issue.selector ? `<button data-action="locate" title="Highlight on page">\u{1F4CD} Locate</button>` : ""}
<button data-action="file" title="File as bug ticket">File ticket</button>
<button data-action="dismiss" title="Dismiss for this session">Dismiss</button>
</div>
</div>
`;
}
function _locate(issue) {
var _a;
if (!issue.selector) return;
let el = null;
try {
el = document.querySelector(issue.selector);
} catch (e) {
el = null;
}
if (!el) return;
(_a = document.getElementById(`${PANEL_ID}-overlay`)) == null ? void 0 : _a.remove();
_isOpen = false;
el.scrollIntoView({ behavior: "smooth", block: "center" });
const htmlEl = el;
const prevOutline = htmlEl.style.outline;
const prevTransition = htmlEl.style.transition;
htmlEl.style.transition = "outline 0.2s, box-shadow 0.2s";
htmlEl.style.outline = "3px solid #6366F1";
htmlEl.style.boxShadow = "0 0 0 6px rgba(99,102,241,0.35)";
setTimeout(() => {
htmlEl.style.outline = prevOutline;
htmlEl.style.boxShadow = "";
htmlEl.style.transition = prevTransition;
}, 2400);
}
function _fileAsBug(issue) {
var _a;
const root = _root || document.getElementById("tracebug-root");
if (!root) return;
(_a = document.getElementById(`${PANEL_ID}-overlay`)) == null ? void 0 : _a.remove();
_isOpen = false;
Promise.resolve().then(() => _interopRequireWildcard(require("./quick-bug-GSX3ETVF.cjs"))).then((m) => {
m.showQuickBugCapture(root, {
prefilledTitle: issue.title,
prefilledDescription: _bugDescriptionFromIssue(issue)
}).catch(() => {
});
});
}
function _bugDescriptionFromIssue(issue) {
const lines = [];
lines.push(`> Detected by TraceBug auto-scanner: **${DETECTOR_LABELS[issue.detector]}** (${issue.severity})`);
lines.push("");
lines.push(issue.description);
if (issue.selector) lines.push(`
**Selector:** \`${issue.selector}\``);
if (issue.url) lines.push(`**URL:** \`${issue.url}\``);
if (issue.helpUrl) lines.push(`**Reference:** ${issue.helpUrl}`);
return lines.join("\n");
}
exports.isIssuesPanelOpen = isIssuesPanelOpen; exports.showIssuesPanel = showIssuesPanel;
//# sourceMappingURL=issues-panel-BECZINZD.cjs.map
{"version":3,"sources":["d:\\Project\\TraceBug-ai\\dist\\issues-panel-BECZINZD.cjs"],"names":[],"mappings":"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E,QAAQ,CAAC;AAChB,EAAE,CAAC;AACH,EAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;AAClC;AACA,SAAS,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;AAC5B,EAAE,IAAI,EAAE;AACR,EAAE,QAAQ,EAAE,IAAI;AAChB,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,8GAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA;AACA;AACA;AACA;AACA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA,oBAAA;AACA;AACA;AACA,WAAA;AACA,iBAAA;AACA,WAAA;AACA;AACA,iBAAA;AACA,EAAA;AACA,gDAAA;AACA,mDAAA;AACA;AACA;AACA,wCAAA;AACA,6FAAA;AACA;AACA,0GAAA;AACA,QAAA;AACA,QAAA;AACA;AACA;AACA,QAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,gBAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA","file":"D:\\Project\\TraceBug-ai\\dist\\issues-panel-BECZINZD.cjs","sourcesContent":[null]}
import {
dismissIssue,
getIssues,
scan
} from "./chunk-AAHK2MC7.js";
import {
escapeHtml
} from "./chunk-AJX6BTPE.js";
// src/ui/issues-panel.ts
var PANEL_ID = "tracebug-issues-panel";
var STYLE_ID = "tracebug-issues-panel-styles";
var _isOpen = false;
var _root = null;
var SEVERITY_COLORS = {
critical: { bg: "#7f1d1d", fg: "#fee2e2", border: "#dc2626" },
serious: { bg: "#7c2d12", fg: "#fed7aa", border: "#ea580c" },
moderate: { bg: "#713f12", fg: "#fde68a", border: "#ca8a04" },
minor: { bg: "#1e3a8a", fg: "#bfdbfe", border: "#2563eb" }
};
var DETECTOR_LABELS = {
"axe-a11y": "A11y",
"broken-image": "Broken image",
"mixed-content": "Mixed content",
"console-error": "JS error",
"slow-api": "Slow API",
"failed-request": "Failed request",
"frustration-rage": "Rage clicks",
"frustration-dead": "Dead click",
"frustration-abandon": "Form abandoned",
"frustration-error-correlated": "Click \u2192 error"
};
function isIssuesPanelOpen() {
return _isOpen;
}
async function showIssuesPanel(root, options) {
var _a;
if (_isOpen) return;
_root = root;
_injectStyles();
_open(root, { issues: [], loading: true });
try {
if ((_a = options == null ? void 0 : options.rescan) != null ? _a : true) {
await scan();
}
} catch (err) {
console.warn("[TraceBug] Scan failed:", err);
}
_renderBody(getIssues());
}
function _injectStyles() {
if (document.getElementById(STYLE_ID)) return;
const style = document.createElement("style");
style.id = STYLE_ID;
style.textContent = `
@keyframes tracebug-issue-locate-flash {
0%, 100% { box-shadow: 0 0 0 0 rgba(99,102,241,0.0); outline: 2px solid transparent; }
30% { box-shadow: 0 0 0 6px rgba(99,102,241,0.5); outline: 2px solid #6366F1; }
}
#${PANEL_ID}-overlay {
position: fixed !important;
inset: 0 !important;
z-index: 2147483647 !important;
background: rgba(0,0,0,0.75) !important;
backdrop-filter: blur(6px) !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
padding: 20px !important;
pointer-events: auto !important;
box-sizing: border-box !important;
}
#${PANEL_ID} {
background: var(--tb-bg-secondary, #1a1a2e) !important;
border: 1px solid var(--tb-border-hover, #3a3a5e) !important;
border-radius: var(--tb-radius-lg, 12px) !important;
width: 100% !important;
max-width: 720px !important;
max-height: 90vh !important;
display: flex !important;
flex-direction: column !important;
overflow: hidden !important;
font-family: var(--tb-font-family, system-ui, -apple-system, sans-serif) !important;
color: var(--tb-text-primary, #e0e0e0) !important;
box-sizing: border-box !important;
box-shadow: 0 20px 60px rgba(0,0,0,0.5) !important;
}
#${PANEL_ID} *, #${PANEL_ID} *::before, #${PANEL_ID} *::after { box-sizing: border-box !important; }
#${PANEL_ID} button { font-family: inherit !important; cursor: pointer !important; }
#${PANEL_ID} .tb-issue-row {
padding: 12px 14px;
border-top: 1px solid var(--tb-border, #2a2a3e);
display: flex;
gap: 12px;
align-items: flex-start;
}
#${PANEL_ID} .tb-issue-row:hover { background: var(--tb-bg-primary, #0f0f1a); }
#${PANEL_ID} .tb-sev-badge {
font-size: 10px;
font-weight: 700;
padding: 3px 7px;
border-radius: 4px;
letter-spacing: 0.4px;
text-transform: uppercase;
flex-shrink: 0;
border: 1px solid;
}
#${PANEL_ID} .tb-detector-tag {
font-size: 10px;
color: var(--tb-text-muted, #888);
background: var(--tb-bg-primary, #0f0f1a);
border: 1px solid var(--tb-border, #2a2a3e);
padding: 2px 6px;
border-radius: 4px;
flex-shrink: 0;
}
#${PANEL_ID} .tb-issue-actions {
display: flex;
gap: 6px;
flex-shrink: 0;
}
#${PANEL_ID} .tb-issue-actions button {
background: transparent;
color: var(--tb-text-secondary, #aaa);
border: 1px solid var(--tb-border, #2a2a3e);
border-radius: 4px;
padding: 4px 10px;
font-size: 11px;
transition: all 0.15s;
}
#${PANEL_ID} .tb-issue-actions button:hover {
background: var(--tb-btn-hover, #ffffff15);
color: var(--tb-text-primary, #fff);
}
#${PANEL_ID} .tb-issue-actions button[data-action="file"] {
background: var(--tb-accent, #6366F1);
color: #fff;
border-color: transparent;
}
#${PANEL_ID} .tb-issue-actions button[data-action="file"]:hover { opacity: 0.9; }
`;
document.head.appendChild(style);
}
function _open(root, state) {
var _a;
_isOpen = true;
(_a = document.getElementById(`${PANEL_ID}-overlay`)) == null ? void 0 : _a.remove();
const overlay = document.createElement("div");
overlay.id = `${PANEL_ID}-overlay`;
overlay.dataset.tracebug = "issues-panel-overlay";
overlay.setAttribute("role", "dialog");
overlay.setAttribute("aria-modal", "true");
overlay.setAttribute("aria-label", "Page issues");
const panel = document.createElement("div");
panel.id = PANEL_ID;
panel.dataset.tracebug = "issues-panel";
panel.innerHTML = _shellHtml(state);
overlay.appendChild(panel);
root.appendChild(overlay);
_wireShellHandlers(overlay, panel);
}
function _shellHtml(state) {
return `
<div data-tb-issues="header" style="padding:16px 18px;display:flex;align-items:center;gap:12px;border-bottom:1px solid var(--tb-border, #2a2a3e)">
<span style="font-size:20px">\u{1F50D}</span>
<div style="flex:1;min-width:0">
<div style="font-size:16px;font-weight:700;color:var(--tb-text-primary, #fff)">Page Issues</div>
<div data-tb-issues="subtitle" style="font-size:11px;color:var(--tb-text-muted, #888);margin-top:2px">${state.loading ? "Scanning\u2026" : "No scan yet"}</div>
</div>
<button data-tb-issues="rescan" style="background:transparent;color:var(--tb-text-secondary, #aaa);border:1px solid var(--tb-border, #2a2a3e);border-radius:6px;padding:6px 12px;font-size:12px;display:flex;align-items:center;gap:6px">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"/><polyline points="21 3 21 9 15 9"/></svg>
Rescan
</button>
<button data-tb-issues="close" aria-label="Close" style="background:none;border:none;color:var(--tb-text-muted, #888);font-size:20px;padding:4px 8px;border-radius:6px">&times;</button>
</div>
<div data-tb-issues="body" style="flex:1;overflow-y:auto;min-height:200px">
${state.loading ? _loadingHtml() : ""}
</div>
`;
}
function _loadingHtml() {
return `
<div style="padding:48px 20px;text-align:center;color:var(--tb-text-muted, #888);font-size:13px">
<div style="font-size:24px;margin-bottom:8px">\u{1F50D}</div>
Scanning page for issues\u2026
<div style="font-size:11px;margin-top:6px;opacity:0.7">Loading axe-core, checking images, network calls, JS errors\u2026</div>
</div>
`;
}
function _wireShellHandlers(overlay, panel) {
const close = () => {
_isOpen = false;
overlay.remove();
document.removeEventListener("keydown", escHandler);
};
panel.querySelector('[data-tb-issues="close"]').addEventListener("click", close);
overlay.addEventListener("click", (e) => {
if (e.target === overlay) close();
});
panel.querySelector('[data-tb-issues="rescan"]').addEventListener("click", async () => {
const body = panel.querySelector('[data-tb-issues="body"]');
body.innerHTML = _loadingHtml();
const sub = panel.querySelector('[data-tb-issues="subtitle"]');
sub.textContent = "Scanning\u2026";
await scan();
_renderBody(getIssues());
});
const escHandler = (e) => {
if (e.key === "Escape") close();
};
document.addEventListener("keydown", escHandler);
}
function _renderBody(issues) {
const panel = document.getElementById(PANEL_ID);
if (!panel) return;
const body = panel.querySelector('[data-tb-issues="body"]');
const subtitle = panel.querySelector('[data-tb-issues="subtitle"]');
if (issues.length === 0) {
subtitle.textContent = "No issues found";
body.innerHTML = `
<div style="padding:48px 20px;text-align:center;color:var(--tb-text-muted, #888);font-size:13px">
<div style="font-size:32px;margin-bottom:8px">\u2713</div>
Clean scan \u2014 no issues detected on this page.
<div style="font-size:11px;margin-top:6px;opacity:0.7">Includes a11y \xB7 broken images \xB7 mixed content \xB7 JS errors \xB7 failed/slow API calls</div>
</div>
`;
return;
}
const counts = {};
for (const i of issues) counts[i.severity] = (counts[i.severity] || 0) + 1;
const summary = ["critical", "serious", "moderate", "minor"].filter((s) => counts[s]).map((s) => `${counts[s]} ${s}`).join(" \xB7 ");
subtitle.textContent = `${issues.length} issue${issues.length === 1 ? "" : "s"} \xB7 ${summary}`;
body.innerHTML = issues.map((issue) => _issueRowHtml(issue)).join("");
body.querySelectorAll("[data-tb-issue-id]").forEach((row) => {
var _a, _b, _c;
const id = row.dataset.tbIssueId;
const issue = issues.find((i) => i.id === id);
if (!issue) return;
(_a = row.querySelector('[data-action="locate"]')) == null ? void 0 : _a.addEventListener("click", () => _locate(issue));
(_b = row.querySelector('[data-action="dismiss"]')) == null ? void 0 : _b.addEventListener("click", () => {
dismissIssue(id);
_renderBody(getIssues());
});
(_c = row.querySelector('[data-action="file"]')) == null ? void 0 : _c.addEventListener("click", () => _fileAsBug(issue));
});
}
function _issueRowHtml(issue) {
const colors = SEVERITY_COLORS[issue.severity];
const detectorLabel = DETECTOR_LABELS[issue.detector];
const desc = issue.description.length > 240 ? issue.description.slice(0, 237) + "\u2026" : issue.description;
const repeats = (issue.occurrences || 1) > 1;
const samplesHtml = repeats && issue.contextSamples && issue.contextSamples.length > 0 ? `<details style="margin-top:6px;font-size:11px">
<summary style="cursor:pointer;color:var(--tb-text-muted, #888)">
View all ${issue.occurrences} contexts
</summary>
<ol style="margin:6px 0 0 20px;padding:0;color:var(--tb-text-secondary, #aaa);line-height:1.5">
${issue.contextSamples.map((s) => `
<li>${new Date(s.timestamp).toLocaleTimeString()}${s.precedingAction ? ` \xB7 ${escapeHtml(s.precedingAction)}` : ""}</li>
`).join("")}
</ol>
</details>` : "";
return `
<div class="tb-issue-row" data-tb-issue-id="${issue.id}">
<span class="tb-sev-badge" style="background:${colors.bg};color:${colors.fg};border-color:${colors.border}">${issue.severity}</span>
<div style="flex:1;min-width:0">
<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px;flex-wrap:wrap">
<span class="tb-detector-tag">${detectorLabel}</span>
<span style="font-size:13px;color:var(--tb-text-primary, #e0e0e0);font-weight:500">${escapeHtml(issue.title)}</span>
</div>
<div style="font-size:11px;color:var(--tb-text-muted, #888);line-height:1.5;white-space:pre-wrap">${escapeHtml(desc)}</div>
${issue.helpUrl ? `<a href="${issue.helpUrl}" target="_blank" rel="noopener noreferrer" style="font-size:11px;color:var(--tb-accent, #6366F1);margin-top:4px;display:inline-block">Learn more \u2192</a>` : ""}
${samplesHtml}
</div>
<div class="tb-issue-actions">
${issue.selector ? `<button data-action="locate" title="Highlight on page">\u{1F4CD} Locate</button>` : ""}
<button data-action="file" title="File as bug ticket">File ticket</button>
<button data-action="dismiss" title="Dismiss for this session">Dismiss</button>
</div>
</div>
`;
}
function _locate(issue) {
var _a;
if (!issue.selector) return;
let el = null;
try {
el = document.querySelector(issue.selector);
} catch (e) {
el = null;
}
if (!el) return;
(_a = document.getElementById(`${PANEL_ID}-overlay`)) == null ? void 0 : _a.remove();
_isOpen = false;
el.scrollIntoView({ behavior: "smooth", block: "center" });
const htmlEl = el;
const prevOutline = htmlEl.style.outline;
const prevTransition = htmlEl.style.transition;
htmlEl.style.transition = "outline 0.2s, box-shadow 0.2s";
htmlEl.style.outline = "3px solid #6366F1";
htmlEl.style.boxShadow = "0 0 0 6px rgba(99,102,241,0.35)";
setTimeout(() => {
htmlEl.style.outline = prevOutline;
htmlEl.style.boxShadow = "";
htmlEl.style.transition = prevTransition;
}, 2400);
}
function _fileAsBug(issue) {
var _a;
const root = _root || document.getElementById("tracebug-root");
if (!root) return;
(_a = document.getElementById(`${PANEL_ID}-overlay`)) == null ? void 0 : _a.remove();
_isOpen = false;
import("./quick-bug-PO6IHZ5N.js").then((m) => {
m.showQuickBugCapture(root, {
prefilledTitle: issue.title,
prefilledDescription: _bugDescriptionFromIssue(issue)
}).catch(() => {
});
});
}
function _bugDescriptionFromIssue(issue) {
const lines = [];
lines.push(`> Detected by TraceBug auto-scanner: **${DETECTOR_LABELS[issue.detector]}** (${issue.severity})`);
lines.push("");
lines.push(issue.description);
if (issue.selector) lines.push(`
**Selector:** \`${issue.selector}\``);
if (issue.url) lines.push(`**URL:** \`${issue.url}\``);
if (issue.helpUrl) lines.push(`**Reference:** ${issue.helpUrl}`);
return lines.join("\n");
}
export {
isIssuesPanelOpen,
showIssuesPanel
};
//# sourceMappingURL=issues-panel-L6FDC5CB.js.map
{"version":3,"sources":["../src/ui/issues-panel.ts"],"sourcesContent":["// ── Issues Panel ──────────────────────────────────────────────────────────\r\n// Modal that lists scanner findings grouped by severity. Each row offers\r\n// \"Locate\" (flash the offending element on the page) and \"File ticket\"\r\n// (open the Quick Bug modal pre-filled with the issue's context).\r\n//\r\n// Styles are injected in <head> with !important so host-page CSS resets\r\n// (Tailwind preflight, Bootstrap, etc.) can't squish the layout.\r\n\r\nimport { Issue } from \"../types\";\r\nimport { dismissIssue, getIssues, scan } from \"../scanner\";\r\nimport { escapeHtml } from \"./helpers\";\r\n\r\nconst PANEL_ID = \"tracebug-issues-panel\";\r\nconst STYLE_ID = \"tracebug-issues-panel-styles\";\r\n\r\nlet _isOpen = false;\r\nlet _root: HTMLElement | null = null;\r\n\r\nconst SEVERITY_COLORS: Record<Issue[\"severity\"], { bg: string; fg: string; border: string }> = {\r\n critical: { bg: \"#7f1d1d\", fg: \"#fee2e2\", border: \"#dc2626\" },\r\n serious: { bg: \"#7c2d12\", fg: \"#fed7aa\", border: \"#ea580c\" },\r\n moderate: { bg: \"#713f12\", fg: \"#fde68a\", border: \"#ca8a04\" },\r\n minor: { bg: \"#1e3a8a\", fg: \"#bfdbfe\", border: \"#2563eb\" },\r\n};\r\n\r\nconst DETECTOR_LABELS: Record<Issue[\"detector\"], string> = {\r\n \"axe-a11y\": \"A11y\",\r\n \"broken-image\": \"Broken image\",\r\n \"mixed-content\": \"Mixed content\",\r\n \"console-error\": \"JS error\",\r\n \"slow-api\": \"Slow API\",\r\n \"failed-request\": \"Failed request\",\r\n \"frustration-rage\": \"Rage clicks\",\r\n \"frustration-dead\": \"Dead click\",\r\n \"frustration-abandon\": \"Form abandoned\",\r\n \"frustration-error-correlated\": \"Click → error\",\r\n};\r\n\r\nexport function isIssuesPanelOpen(): boolean {\r\n return _isOpen;\r\n}\r\n\r\n/**\r\n * Run a scan (or use cached results) and open the panel. The scan promise\r\n * resolves before we render so the panel never flashes empty.\r\n */\r\nexport async function showIssuesPanel(\r\n root: HTMLElement,\r\n options?: { rescan?: boolean }\r\n): Promise<void> {\r\n if (_isOpen) return;\r\n _root = root;\r\n _injectStyles();\r\n\r\n // Render shell with a loading state so the user gets immediate feedback.\r\n _open(root, { issues: [], loading: true });\r\n try {\r\n if (options?.rescan ?? true) {\r\n await scan();\r\n }\r\n } catch (err) {\r\n console.warn(\"[TraceBug] Scan failed:\", err);\r\n }\r\n _renderBody(getIssues());\r\n}\r\n\r\nfunction _injectStyles(): void {\r\n if (document.getElementById(STYLE_ID)) return;\r\n const style = document.createElement(\"style\");\r\n style.id = STYLE_ID;\r\n style.textContent = `\r\n @keyframes tracebug-issue-locate-flash {\r\n 0%, 100% { box-shadow: 0 0 0 0 rgba(99,102,241,0.0); outline: 2px solid transparent; }\r\n 30% { box-shadow: 0 0 0 6px rgba(99,102,241,0.5); outline: 2px solid #6366F1; }\r\n }\r\n #${PANEL_ID}-overlay {\r\n position: fixed !important;\r\n inset: 0 !important;\r\n z-index: 2147483647 !important;\r\n background: rgba(0,0,0,0.75) !important;\r\n backdrop-filter: blur(6px) !important;\r\n display: flex !important;\r\n align-items: center !important;\r\n justify-content: center !important;\r\n padding: 20px !important;\r\n pointer-events: auto !important;\r\n box-sizing: border-box !important;\r\n }\r\n #${PANEL_ID} {\r\n background: var(--tb-bg-secondary, #1a1a2e) !important;\r\n border: 1px solid var(--tb-border-hover, #3a3a5e) !important;\r\n border-radius: var(--tb-radius-lg, 12px) !important;\r\n width: 100% !important;\r\n max-width: 720px !important;\r\n max-height: 90vh !important;\r\n display: flex !important;\r\n flex-direction: column !important;\r\n overflow: hidden !important;\r\n font-family: var(--tb-font-family, system-ui, -apple-system, sans-serif) !important;\r\n color: var(--tb-text-primary, #e0e0e0) !important;\r\n box-sizing: border-box !important;\r\n box-shadow: 0 20px 60px rgba(0,0,0,0.5) !important;\r\n }\r\n #${PANEL_ID} *, #${PANEL_ID} *::before, #${PANEL_ID} *::after { box-sizing: border-box !important; }\r\n #${PANEL_ID} button { font-family: inherit !important; cursor: pointer !important; }\r\n #${PANEL_ID} .tb-issue-row {\r\n padding: 12px 14px;\r\n border-top: 1px solid var(--tb-border, #2a2a3e);\r\n display: flex;\r\n gap: 12px;\r\n align-items: flex-start;\r\n }\r\n #${PANEL_ID} .tb-issue-row:hover { background: var(--tb-bg-primary, #0f0f1a); }\r\n #${PANEL_ID} .tb-sev-badge {\r\n font-size: 10px;\r\n font-weight: 700;\r\n padding: 3px 7px;\r\n border-radius: 4px;\r\n letter-spacing: 0.4px;\r\n text-transform: uppercase;\r\n flex-shrink: 0;\r\n border: 1px solid;\r\n }\r\n #${PANEL_ID} .tb-detector-tag {\r\n font-size: 10px;\r\n color: var(--tb-text-muted, #888);\r\n background: var(--tb-bg-primary, #0f0f1a);\r\n border: 1px solid var(--tb-border, #2a2a3e);\r\n padding: 2px 6px;\r\n border-radius: 4px;\r\n flex-shrink: 0;\r\n }\r\n #${PANEL_ID} .tb-issue-actions {\r\n display: flex;\r\n gap: 6px;\r\n flex-shrink: 0;\r\n }\r\n #${PANEL_ID} .tb-issue-actions button {\r\n background: transparent;\r\n color: var(--tb-text-secondary, #aaa);\r\n border: 1px solid var(--tb-border, #2a2a3e);\r\n border-radius: 4px;\r\n padding: 4px 10px;\r\n font-size: 11px;\r\n transition: all 0.15s;\r\n }\r\n #${PANEL_ID} .tb-issue-actions button:hover {\r\n background: var(--tb-btn-hover, #ffffff15);\r\n color: var(--tb-text-primary, #fff);\r\n }\r\n #${PANEL_ID} .tb-issue-actions button[data-action=\"file\"] {\r\n background: var(--tb-accent, #6366F1);\r\n color: #fff;\r\n border-color: transparent;\r\n }\r\n #${PANEL_ID} .tb-issue-actions button[data-action=\"file\"]:hover { opacity: 0.9; }\r\n `;\r\n document.head.appendChild(style);\r\n}\r\n\r\nfunction _open(root: HTMLElement, state: { issues: Issue[]; loading: boolean }): void {\r\n _isOpen = true;\r\n // Remove any prior overlay (defensive — should not happen).\r\n document.getElementById(`${PANEL_ID}-overlay`)?.remove();\r\n\r\n const overlay = document.createElement(\"div\");\r\n overlay.id = `${PANEL_ID}-overlay`;\r\n overlay.dataset.tracebug = \"issues-panel-overlay\";\r\n overlay.setAttribute(\"role\", \"dialog\");\r\n overlay.setAttribute(\"aria-modal\", \"true\");\r\n overlay.setAttribute(\"aria-label\", \"Page issues\");\r\n\r\n const panel = document.createElement(\"div\");\r\n panel.id = PANEL_ID;\r\n panel.dataset.tracebug = \"issues-panel\";\r\n\r\n panel.innerHTML = _shellHtml(state);\r\n\r\n overlay.appendChild(panel);\r\n root.appendChild(overlay);\r\n\r\n _wireShellHandlers(overlay, panel);\r\n}\r\n\r\nfunction _shellHtml(state: { issues: Issue[]; loading: boolean }): string {\r\n return `\r\n <div data-tb-issues=\"header\" style=\"padding:16px 18px;display:flex;align-items:center;gap:12px;border-bottom:1px solid var(--tb-border, #2a2a3e)\">\r\n <span style=\"font-size:20px\">🔍</span>\r\n <div style=\"flex:1;min-width:0\">\r\n <div style=\"font-size:16px;font-weight:700;color:var(--tb-text-primary, #fff)\">Page Issues</div>\r\n <div data-tb-issues=\"subtitle\" style=\"font-size:11px;color:var(--tb-text-muted, #888);margin-top:2px\">${state.loading ? \"Scanning…\" : \"No scan yet\"}</div>\r\n </div>\r\n <button data-tb-issues=\"rescan\" style=\"background:transparent;color:var(--tb-text-secondary, #aaa);border:1px solid var(--tb-border, #2a2a3e);border-radius:6px;padding:6px 12px;font-size:12px;display:flex;align-items:center;gap:6px\">\r\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 12a9 9 0 1 1-3-6.7\"/><polyline points=\"21 3 21 9 15 9\"/></svg>\r\n Rescan\r\n </button>\r\n <button data-tb-issues=\"close\" aria-label=\"Close\" style=\"background:none;border:none;color:var(--tb-text-muted, #888);font-size:20px;padding:4px 8px;border-radius:6px\">&times;</button>\r\n </div>\r\n <div data-tb-issues=\"body\" style=\"flex:1;overflow-y:auto;min-height:200px\">\r\n ${state.loading ? _loadingHtml() : \"\"}\r\n </div>\r\n `;\r\n}\r\n\r\nfunction _loadingHtml(): string {\r\n return `\r\n <div style=\"padding:48px 20px;text-align:center;color:var(--tb-text-muted, #888);font-size:13px\">\r\n <div style=\"font-size:24px;margin-bottom:8px\">🔍</div>\r\n Scanning page for issues…\r\n <div style=\"font-size:11px;margin-top:6px;opacity:0.7\">Loading axe-core, checking images, network calls, JS errors…</div>\r\n </div>\r\n `;\r\n}\r\n\r\nfunction _wireShellHandlers(overlay: HTMLElement, panel: HTMLElement): void {\r\n const close = () => {\r\n _isOpen = false;\r\n overlay.remove();\r\n document.removeEventListener(\"keydown\", escHandler);\r\n };\r\n panel.querySelector('[data-tb-issues=\"close\"]')!.addEventListener(\"click\", close);\r\n overlay.addEventListener(\"click\", (e) => { if (e.target === overlay) close(); });\r\n\r\n panel.querySelector('[data-tb-issues=\"rescan\"]')!.addEventListener(\"click\", async () => {\r\n const body = panel.querySelector('[data-tb-issues=\"body\"]') as HTMLElement;\r\n body.innerHTML = _loadingHtml();\r\n const sub = panel.querySelector('[data-tb-issues=\"subtitle\"]') as HTMLElement;\r\n sub.textContent = \"Scanning…\";\r\n await scan();\r\n _renderBody(getIssues());\r\n });\r\n\r\n const escHandler = (e: KeyboardEvent) => { if (e.key === \"Escape\") close(); };\r\n document.addEventListener(\"keydown\", escHandler);\r\n}\r\n\r\nfunction _renderBody(issues: Issue[]): void {\r\n const panel = document.getElementById(PANEL_ID);\r\n if (!panel) return;\r\n const body = panel.querySelector('[data-tb-issues=\"body\"]') as HTMLElement;\r\n const subtitle = panel.querySelector('[data-tb-issues=\"subtitle\"]') as HTMLElement;\r\n\r\n if (issues.length === 0) {\r\n subtitle.textContent = \"No issues found\";\r\n body.innerHTML = `\r\n <div style=\"padding:48px 20px;text-align:center;color:var(--tb-text-muted, #888);font-size:13px\">\r\n <div style=\"font-size:32px;margin-bottom:8px\">✓</div>\r\n Clean scan — no issues detected on this page.\r\n <div style=\"font-size:11px;margin-top:6px;opacity:0.7\">Includes a11y · broken images · mixed content · JS errors · failed/slow API calls</div>\r\n </div>\r\n `;\r\n return;\r\n }\r\n\r\n // Severity counts in subtitle\r\n const counts: Record<string, number> = {};\r\n for (const i of issues) counts[i.severity] = (counts[i.severity] || 0) + 1;\r\n const summary = [\"critical\", \"serious\", \"moderate\", \"minor\"]\r\n .filter(s => counts[s])\r\n .map(s => `${counts[s]} ${s}`)\r\n .join(\" · \");\r\n subtitle.textContent = `${issues.length} issue${issues.length === 1 ? \"\" : \"s\"} · ${summary}`;\r\n\r\n body.innerHTML = issues.map(issue => _issueRowHtml(issue)).join(\"\");\r\n\r\n // Wire row actions\r\n body.querySelectorAll<HTMLElement>(\"[data-tb-issue-id]\").forEach(row => {\r\n const id = row.dataset.tbIssueId!;\r\n const issue = issues.find(i => i.id === id);\r\n if (!issue) return;\r\n\r\n row.querySelector('[data-action=\"locate\"]')?.addEventListener(\"click\", () => _locate(issue));\r\n row.querySelector('[data-action=\"dismiss\"]')?.addEventListener(\"click\", () => {\r\n dismissIssue(id);\r\n _renderBody(getIssues());\r\n });\r\n row.querySelector('[data-action=\"file\"]')?.addEventListener(\"click\", () => _fileAsBug(issue));\r\n });\r\n}\r\n\r\nfunction _issueRowHtml(issue: Issue): string {\r\n const colors = SEVERITY_COLORS[issue.severity];\r\n const detectorLabel = DETECTOR_LABELS[issue.detector];\r\n const desc = issue.description.length > 240\r\n ? issue.description.slice(0, 237) + \"…\"\r\n : issue.description;\r\n\r\n // Fingerprint dedup: when occurrences > 1 show a collapsible <details>\r\n // listing each occurrence with its preceding action.\r\n const repeats = (issue.occurrences || 1) > 1;\r\n const samplesHtml = repeats && issue.contextSamples && issue.contextSamples.length > 0\r\n ? `<details style=\"margin-top:6px;font-size:11px\">\r\n <summary style=\"cursor:pointer;color:var(--tb-text-muted, #888)\">\r\n View all ${issue.occurrences} contexts\r\n </summary>\r\n <ol style=\"margin:6px 0 0 20px;padding:0;color:var(--tb-text-secondary, #aaa);line-height:1.5\">\r\n ${issue.contextSamples.map(s => `\r\n <li>${new Date(s.timestamp).toLocaleTimeString()}${s.precedingAction ? ` · ${escapeHtml(s.precedingAction)}` : \"\"}</li>\r\n `).join(\"\")}\r\n </ol>\r\n </details>`\r\n : \"\";\r\n\r\n return `\r\n <div class=\"tb-issue-row\" data-tb-issue-id=\"${issue.id}\">\r\n <span class=\"tb-sev-badge\" style=\"background:${colors.bg};color:${colors.fg};border-color:${colors.border}\">${issue.severity}</span>\r\n <div style=\"flex:1;min-width:0\">\r\n <div style=\"display:flex;align-items:center;gap:6px;margin-bottom:4px;flex-wrap:wrap\">\r\n <span class=\"tb-detector-tag\">${detectorLabel}</span>\r\n <span style=\"font-size:13px;color:var(--tb-text-primary, #e0e0e0);font-weight:500\">${escapeHtml(issue.title)}</span>\r\n </div>\r\n <div style=\"font-size:11px;color:var(--tb-text-muted, #888);line-height:1.5;white-space:pre-wrap\">${escapeHtml(desc)}</div>\r\n ${issue.helpUrl ? `<a href=\"${issue.helpUrl}\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"font-size:11px;color:var(--tb-accent, #6366F1);margin-top:4px;display:inline-block\">Learn more →</a>` : \"\"}\r\n ${samplesHtml}\r\n </div>\r\n <div class=\"tb-issue-actions\">\r\n ${issue.selector ? `<button data-action=\"locate\" title=\"Highlight on page\">📍 Locate</button>` : \"\"}\r\n <button data-action=\"file\" title=\"File as bug ticket\">File ticket</button>\r\n <button data-action=\"dismiss\" title=\"Dismiss for this session\">Dismiss</button>\r\n </div>\r\n </div>\r\n `;\r\n}\r\n\r\n/**\r\n * Briefly outline the offending element. Closes the panel so the page is\r\n * visible. Restores after 2.4s — non-destructive flash.\r\n */\r\nfunction _locate(issue: Issue): void {\r\n if (!issue.selector) return;\r\n let el: Element | null = null;\r\n try {\r\n el = document.querySelector(issue.selector);\r\n } catch {\r\n el = null;\r\n }\r\n if (!el) return;\r\n\r\n // Close the panel so the user can see the page.\r\n document.getElementById(`${PANEL_ID}-overlay`)?.remove();\r\n _isOpen = false;\r\n\r\n el.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\r\n const htmlEl = el as HTMLElement;\r\n const prevOutline = htmlEl.style.outline;\r\n const prevTransition = htmlEl.style.transition;\r\n htmlEl.style.transition = \"outline 0.2s, box-shadow 0.2s\";\r\n htmlEl.style.outline = \"3px solid #6366F1\";\r\n htmlEl.style.boxShadow = \"0 0 0 6px rgba(99,102,241,0.35)\";\r\n setTimeout(() => {\r\n htmlEl.style.outline = prevOutline;\r\n htmlEl.style.boxShadow = \"\";\r\n htmlEl.style.transition = prevTransition;\r\n }, 2400);\r\n}\r\n\r\n/**\r\n * Pre-fill the Quick Bug modal with the issue's title + description as the\r\n * starting point for a ticket. Reuses the existing ticket-export pipeline.\r\n */\r\nfunction _fileAsBug(issue: Issue): void {\r\n const root = _root || document.getElementById(\"tracebug-root\");\r\n if (!root) return;\r\n // Close the issues panel.\r\n document.getElementById(`${PANEL_ID}-overlay`)?.remove();\r\n _isOpen = false;\r\n\r\n import(\"./quick-bug\").then(m => {\r\n m.showQuickBugCapture(root, {\r\n prefilledTitle: issue.title,\r\n prefilledDescription: _bugDescriptionFromIssue(issue),\r\n }).catch(() => {});\r\n });\r\n}\r\n\r\nfunction _bugDescriptionFromIssue(issue: Issue): string {\r\n const lines: string[] = [];\r\n lines.push(`> Detected by TraceBug auto-scanner: **${DETECTOR_LABELS[issue.detector]}** (${issue.severity})`);\r\n lines.push(\"\");\r\n lines.push(issue.description);\r\n if (issue.selector) lines.push(`\\n**Selector:** \\`${issue.selector}\\``);\r\n if (issue.url) lines.push(`**URL:** \\`${issue.url}\\``);\r\n if (issue.helpUrl) lines.push(`**Reference:** ${issue.helpUrl}`);\r\n return lines.join(\"\\n\");\r\n}\r\n"],"mappings":"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","names":[]}
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
var _chunk56ONUZG6cjs = require('./chunk-56ONUZG6.cjs');
require('./chunk-3HHUTYZ5.cjs');
require('./chunk-FO62QDYE.cjs');
exports.isQuickBugOpen = _chunk56ONUZG6cjs.isQuickBugOpen; exports.refreshQuickBugCapture = _chunk56ONUZG6cjs.refreshQuickBugCapture; exports.setCloudEndpoint = _chunk56ONUZG6cjs.setCloudEndpoint; exports.setGithubRepo = _chunk56ONUZG6cjs.setGithubRepo; exports.showQuickBugCapture = _chunk56ONUZG6cjs.showQuickBugCapture;
//# sourceMappingURL=quick-bug-GSX3ETVF.cjs.map
{"version":3,"sources":["d:\\Project\\TraceBug-ai\\dist\\quick-bug-GSX3ETVF.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACF,kUAAC","file":"D:\\Project\\TraceBug-ai\\dist\\quick-bug-GSX3ETVF.cjs"}
import {
isQuickBugOpen,
refreshQuickBugCapture,
setCloudEndpoint,
setGithubRepo,
showQuickBugCapture
} from "./chunk-AMKNELCV.js";
import "./chunk-L3Q7Y6QP.js";
import "./chunk-AJX6BTPE.js";
export {
isQuickBugOpen,
refreshQuickBugCapture,
setCloudEndpoint,
setGithubRepo,
showQuickBugCapture
};
//# sourceMappingURL=quick-bug-PO6IHZ5N.js.map
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display