@diegotsi/flint-core 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
package/dist/index.cjs
CHANGED
|
@@ -347,7 +347,7 @@ function createFormErrorCollector() {
|
|
|
347
347
|
const recentRecords = /* @__PURE__ */ new Map();
|
|
348
348
|
const DEDUP_MS = 500;
|
|
349
349
|
function push(entry) {
|
|
350
|
-
const
|
|
350
|
+
const _key = `${entry.formId}:${entry.timestamp}`;
|
|
351
351
|
const last = recentRecords.get(entry.formId);
|
|
352
352
|
if (last && entry.timestamp - last < DEDUP_MS) return;
|
|
353
353
|
recentRecords.set(entry.formId, entry.timestamp);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/api.ts","../src/collectors/console.ts","../src/collectors/environment.ts","../src/collectors/formErrors.ts","../src/collectors/frustration.ts","../src/collectors/network.ts","../src/store.ts","../src/singleton.ts","../src/theme.ts"],"sourcesContent":["// Collectors\n\n// API\nexport { submitReplay, submitReport } from \"./api.js\";\n// Collector types\nexport type { ConsoleCollector } from \"./collectors/console.js\";\nexport { createConsoleCollector } from \"./collectors/console.js\";\nexport { collectEnvironment } from \"./collectors/environment.js\";\nexport type { FormErrorCollector } from \"./collectors/formErrors.js\";\nexport { createFormErrorCollector } from \"./collectors/formErrors.js\";\nexport type { FrustrationCollector, FrustrationEvent } from \"./collectors/frustration.js\";\nexport { createFrustrationCollector } from \"./collectors/frustration.js\";\nexport type { NetworkCollector } from \"./collectors/network.js\";\nexport { createNetworkCollector } from \"./collectors/network.js\";\n// Singleton\nexport { Flint } from \"./singleton.js\";\nexport type { FlintState } from \"./store.js\";\n// State\nexport { _setFormErrorCollector, flint, getSnapshot, subscribe } from \"./store.js\";\nexport type { ResolvedTheme } from \"./theme.js\";\n// Theme\nexport { resolveTheme } from \"./theme.js\";\n// Types (re-export)\nexport type {\n CollectedMeta,\n ConsoleEntry,\n EnvironmentInfo,\n FlintConfig,\n FlintExtraFields,\n FlintUser,\n FlintWidgetProps,\n FormErrorEntry,\n Locale,\n NetworkEntry,\n ReportPayload,\n ReportResult,\n Severity,\n Theme,\n ThemeOverride,\n} from \"./types.js\";\n","import type { ReportPayload, ReportResult } from \"./types.js\";\nimport { gzipSync } from \"fflate\";\n\nasync function fetchWithRetry(url: string, init: RequestInit, retries = 3, baseDelay = 1000): Promise<Response> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n const res = await fetch(url, init);\n if (res.ok || attempt === retries) return res;\n // Don't retry client errors (4xx) except 429\n if (res.status >= 400 && res.status < 500 && res.status !== 429) return res;\n } catch (err) {\n if (attempt === retries) throw err;\n }\n await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));\n }\n throw new Error(\"Max retries exceeded\");\n}\n\nexport async function submitReport(\n serverUrl: string,\n projectKey: string,\n payload: ReportPayload,\n screenshot?: File,\n): Promise<ReportResult> {\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports`;\n\n let body: BodyInit;\n const headers: Record<string, string> = {\n \"x-project-key\": projectKey,\n };\n\n if (screenshot) {\n const form = new FormData();\n form.append(\"reporterId\", payload.reporterId);\n form.append(\"reporterName\", payload.reporterName);\n if (payload.reporterEmail) form.append(\"reporterEmail\", payload.reporterEmail);\n form.append(\"description\", payload.description);\n if (payload.expectedBehavior) form.append(\"expectedBehavior\", payload.expectedBehavior);\n if (payload.stepsToReproduce) form.append(\"stepsToReproduce\", JSON.stringify(payload.stepsToReproduce));\n if (payload.externalReplayUrl) form.append(\"externalReplayUrl\", payload.externalReplayUrl);\n if (payload.additionalContext) form.append(\"additionalContext\", payload.additionalContext);\n form.append(\"severity\", payload.severity);\n if (payload.url) form.append(\"url\", payload.url);\n if (payload.meta) form.append(\"meta\", JSON.stringify(payload.meta));\n form.append(\"screenshot\", screenshot);\n body = form;\n } else {\n body = JSON.stringify(payload);\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const res = await fetchWithRetry(url, { method: \"POST\", headers, body });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: \"Unknown error\" }));\n throw new Error((err as { error?: string }).error ?? `HTTP ${res.status}`);\n }\n\n return res.json() as Promise<ReportResult>;\n}\n\nexport async function submitReplay(\n serverUrl: string,\n projectKey: string,\n reportId: string,\n events: unknown[],\n): Promise<void> {\n const json = JSON.stringify(events);\n const encoded = new TextEncoder().encode(json);\n const compressed = gzipSync(encoded);\n\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports/${reportId}/replay`;\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"x-project-key\": projectKey,\n \"Content-Type\": \"application/octet-stream\",\n },\n body: compressed.buffer as ArrayBuffer,\n });\n}\n","import type { ConsoleEntry } from \"../types.js\";\n\nconst MAX_ENTRIES = 50;\n\n// Redact values that look like tokens, passwords, secrets, or API keys\nconst SENSITIVE_PATTERNS = [\n /(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|authorization|bearer)\\s*[:=]\\s*[\"']?[^\\s\"',]{4,}/gi,\n /\\b(sk-[a-zA-Z0-9_-]{20,})\\b/g, // API keys (e.g. sk-ant-...)\n /\\b(ghp_[a-zA-Z0-9]{36,})\\b/g, // GitHub tokens\n /\\b(xoxb-[a-zA-Z0-9-]+)\\b/g, // Slack tokens\n /\\b(eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,})\\b/g, // JWTs\n];\n\nfunction sanitize(str: string): string {\n let result = str;\n for (const pattern of SENSITIVE_PATTERNS) {\n result = result.replace(pattern, \"[REDACTED]\");\n }\n return result;\n}\n\nexport interface ConsoleCollector {\n start(): void;\n stop(): void;\n getEntries(): ConsoleEntry[];\n}\n\nexport function createConsoleCollector(): ConsoleCollector {\n const entries: ConsoleEntry[] = [];\n let active = false;\n\n const originals = {\n log: console.log.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n };\n\n let origOnerror: typeof window.onerror = null;\n let origUnhandled: typeof window.onunhandledrejection = null;\n\n function push(level: ConsoleEntry[\"level\"], args: unknown[]) {\n let str: string;\n try {\n str = args.map((a) => (typeof a === \"string\" ? a : JSON.stringify(a))).join(\" \");\n } catch {\n str = String(args[0]);\n }\n if (str.length > 500) str = str.slice(0, 500) + \"\\u2026\";\n str = sanitize(str);\n entries.push({ level, args: str, timestamp: Date.now() });\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n console.log = (...args: unknown[]) => {\n push(\"log\", args);\n originals.log(...args);\n };\n console.warn = (...args: unknown[]) => {\n push(\"warn\", args);\n originals.warn(...args);\n };\n console.error = (...args: unknown[]) => {\n push(\"error\", args);\n originals.error(...args);\n };\n\n origOnerror = window.onerror;\n window.onerror = (msg, src, line, col, err) => {\n push(\"error\", [err?.message ?? String(msg), `${src}:${line}:${col}`]);\n if (typeof origOnerror === \"function\") return origOnerror(msg, src, line, col, err);\n return false;\n };\n\n origUnhandled = window.onunhandledrejection;\n window.onunhandledrejection = (event) => {\n const reason = event.reason instanceof Error ? event.reason.message : JSON.stringify(event.reason);\n push(\"error\", [\"UnhandledRejection:\", reason]);\n if (typeof origUnhandled === \"function\") origUnhandled.call(window, event);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n console.log = originals.log;\n console.warn = originals.warn;\n console.error = originals.error;\n window.onerror = origOnerror;\n window.onunhandledrejection = origUnhandled;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { EnvironmentInfo } from \"../types.js\";\n\nexport function collectEnvironment(): EnvironmentInfo {\n const ua = navigator.userAgent;\n\n // -- Browser --\n let browser = \"Unknown\";\n const chromeM = ua.match(/Chrome\\/(\\d+)/);\n const firefoxM = ua.match(/Firefox\\/(\\d+)/);\n const edgeM = ua.match(/Edg\\/(\\d+)/);\n const safariM = ua.match(/Version\\/(\\d+)/);\n const operaM = ua.match(/OPR\\/(\\d+)/);\n\n if (operaM) {\n browser = `Opera ${operaM[1]}`;\n } else if (edgeM) {\n browser = `Edge ${edgeM[1]}`;\n } else if (chromeM && !/Edg|OPR/.test(ua)) {\n browser = `Chrome ${chromeM[1]}`;\n } else if (firefoxM) {\n browser = `Firefox ${firefoxM[1]}`;\n } else if (safariM && /Safari\\//.test(ua)) {\n browser = `Safari ${safariM[1]}`;\n }\n\n // -- OS --\n let os = \"Unknown\";\n const macM = ua.match(/Mac OS X (\\d+[._]\\d+)/);\n const winM = ua.match(/Windows NT (\\d+\\.\\d+)/);\n const androidM = ua.match(/Android (\\d+)/);\n const iosM = ua.match(/iPhone OS (\\d+[._]\\d+)/);\n\n if (macM) {\n os = `macOS ${macM[1].replace(\"_\", \".\")}`;\n } else if (winM) {\n const winMap: Record<string, string> = {\n \"10.0\": \"10/11\",\n \"6.3\": \"8.1\",\n \"6.2\": \"8\",\n \"6.1\": \"7\",\n };\n os = `Windows ${winMap[winM[1]] ?? winM[1]}`;\n } else if (androidM) {\n os = `Android ${androidM[1]}`;\n } else if (iosM) {\n os = `iOS ${iosM[1].replace(/_/g, \".\")}`;\n } else if (/Linux/.test(ua)) {\n os = \"Linux\";\n }\n\n return {\n browser,\n os,\n viewport: `${window.innerWidth}x${window.innerHeight}`,\n screen: `${screen.width}x${screen.height}`,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n online: navigator.onLine,\n };\n}\n","import type { FormErrorEntry, FormErrorField } from \"../types.js\";\n\nconst MAX_ENTRIES = 30;\n\n/**\n * Time to wait after a submit event before checking the DOM for error\n * indicators. RHF/zod validate asynchronously — 300ms covers most cases\n * while keeping the detection snappy.\n */\nconst POST_SUBMIT_CHECK_MS = 300;\n\n/**\n * After a submit button click, if no submit event fires within this window\n * it means the form library swallowed the submit entirely (e.g. RHF's\n * handleSubmit called preventDefault before the native event propagated,\n * or the button isn't even inside a <form>). We treat this as a potential\n * silent failure and scan the page for error indicators.\n */\nconst SILENT_SUBMIT_WINDOW_MS = 400;\n\nexport interface FormErrorCollector {\n start(): void;\n stop(): void;\n getEntries(): FormErrorEntry[];\n /**\n * Manually report a form validation failure. Call this from your form\n * library's error handler to capture exact field-level errors.\n *\n * @example\n * // React Hook Form + Zod\n * <form onSubmit={handleSubmit(onValid, (errors) => {\n * flint.reportFormError('checkout-form', errors);\n * })}>\n */\n report(formId: string, errors: Record<string, { message?: string } | undefined>): void;\n}\n\n// ── DOM helpers ────────────────────────────────────────────────────────────────\n\nfunction getFormId(form: HTMLFormElement): string {\n if (form.id) return `#${form.id}`;\n if (form.getAttribute(\"name\")) return `form[name=\"${form.getAttribute(\"name\")}\"]`;\n if (form.action && form.action !== location.href) return form.action;\n return `form:${Array.from(document.forms).indexOf(form)}`;\n}\n\nfunction getFieldLabel(field: HTMLElement, root: HTMLElement): string {\n const ariaLabel = field.getAttribute(\"aria-label\");\n if (ariaLabel) return ariaLabel;\n\n const id = field.id;\n if (id) {\n const label = root.querySelector<HTMLLabelElement>(`label[for=\"${id}\"]`);\n if (label?.textContent) return label.textContent.trim();\n }\n\n const parentLabel = field.closest(\"label\");\n if (parentLabel?.textContent) {\n const text = parentLabel.textContent.trim();\n if (text.length < 60) return text;\n }\n\n return (field as HTMLInputElement).name || (field as HTMLInputElement).placeholder || field.tagName.toLowerCase();\n}\n\nfunction getErrorMessage(field: HTMLElement): string | undefined {\n // aria-errormessage\n const errId = field.getAttribute(\"aria-errormessage\");\n if (errId) {\n const el = document.getElementById(errId);\n if (el?.textContent) return el.textContent.trim();\n }\n\n // aria-describedby (RHF wires error messages here)\n const descId = field.getAttribute(\"aria-describedby\");\n if (descId) {\n for (const id of descId.split(/\\s+/)) {\n const el = document.getElementById(id);\n if (el?.textContent?.trim()) return el.textContent.trim();\n }\n }\n\n // native validationMessage\n if (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement) {\n if (field.validationMessage) return field.validationMessage;\n }\n\n // adjacent role=\"alert\"\n const next = field.nextElementSibling;\n if (next?.getAttribute(\"role\") === \"alert\" && next.textContent) {\n return next.textContent.trim();\n }\n\n return undefined;\n}\n\n/**\n * Collect fields with `aria-invalid=\"true\"` or native `:invalid` inside a\n * root element (a <form> or the whole document).\n */\nfunction collectInvalidFields(root: HTMLElement): FormErrorField[] {\n const fields: FormErrorField[] = [];\n const invalidEls = root.querySelectorAll<HTMLElement>('[aria-invalid=\"true\"], :invalid');\n\n for (const el of invalidEls) {\n if (el instanceof HTMLFormElement) continue;\n if (el.tagName === \"FIELDSET\") continue;\n\n fields.push({\n name: getFieldLabel(el, root),\n message: getErrorMessage(el),\n });\n }\n\n return fields;\n}\n\n/**\n * Scan the page for visible error indicators that appeared after a submit\n * attempt. This catches errors even when:\n * - The errored field is in another tab/step of a multi-step form\n * - The form doesn't use aria-invalid at all\n * - Errors are rendered as standalone alert divs\n */\nfunction collectVisibleErrors(root: HTMLElement): FormErrorField[] {\n const fields: FormErrorField[] = [];\n const seen = new Set<string>();\n\n // 1. role=\"alert\" elements that appeared (error toasts, inline messages)\n const alerts = root.querySelectorAll<HTMLElement>('[role=\"alert\"]');\n for (const el of alerts) {\n const text = el.textContent?.trim();\n if (!text || text.length > 200) continue;\n if (seen.has(text)) continue;\n seen.add(text);\n fields.push({ name: \"alert\", message: text });\n }\n\n // 2. Common error class patterns (data-error, .error, .field-error, etc.)\n const errorEls = root.querySelectorAll<HTMLElement>(\n \"[data-error], [data-field-error], .error-message, .field-error, .form-error\",\n );\n for (const el of errorEls) {\n const text = el.textContent?.trim();\n if (!text || text.length > 200) continue;\n if (seen.has(text)) continue;\n seen.add(text);\n fields.push({ name: \"error\", message: text });\n }\n\n return fields;\n}\n\nfunction isSubmitButton(el: HTMLElement): el is HTMLButtonElement {\n if (el.tagName === \"BUTTON\") {\n const type = el.getAttribute(\"type\");\n // Buttons default to type=\"submit\" inside forms when no type is set\n return type === \"submit\" || type === null || type === \"\";\n }\n if (el.tagName === \"INPUT\") {\n return (el as HTMLInputElement).type === \"submit\";\n }\n return false;\n}\n\nfunction findClosestForm(el: HTMLElement): HTMLFormElement | null {\n // Direct form association\n if (\"form\" in el && (el as HTMLButtonElement).form) {\n return (el as HTMLButtonElement).form;\n }\n return el.closest(\"form\");\n}\n\n// ── Collector ──────────────────────────────────────────────────────────────────\n\nexport function createFormErrorCollector(): FormErrorCollector {\n const entries: FormErrorEntry[] = [];\n const attemptCounts = new Map<string, number>();\n let active = false;\n\n // Handlers we need to clean up\n let submitHandler: ((e: Event) => void) | null = null;\n let invalidHandler: ((e: Event) => void) | null = null;\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n\n // Track whether a real submit event followed a button click\n let lastSubmitTime = 0;\n\n // Dedup: avoid recording the same form twice within a short window\n const recentRecords = new Map<string, number>();\n const DEDUP_MS = 500;\n\n function push(entry: FormErrorEntry) {\n // Dedup check\n const key = `${entry.formId}:${entry.timestamp}`;\n const last = recentRecords.get(entry.formId);\n if (last && entry.timestamp - last < DEDUP_MS) return;\n recentRecords.set(entry.formId, entry.timestamp);\n\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n function recordFormErrors(form: HTMLFormElement, type: FormErrorEntry[\"type\"]) {\n const formId = getFormId(form);\n\n // Strategy 1: aria-invalid / :invalid fields\n let fields = collectInvalidFields(form);\n\n // Strategy 2: if no aria-invalid fields found, look for visible error messages\n if (fields.length === 0) {\n fields = collectVisibleErrors(form);\n }\n\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type,\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Handle clicks on submit buttons. If no submit event follows within\n * SILENT_SUBMIT_WINDOW_MS, it means the form library blocked the submit\n * entirely — scan for errors.\n */\n function handleSubmitButtonClick(button: HTMLElement) {\n const form = findClosestForm(button);\n\n setTimeout(() => {\n if (!active) return;\n // A real submit event fired — the submitHandler already took care of it\n if (Date.now() - lastSubmitTime < SILENT_SUBMIT_WINDOW_MS) return;\n\n // No submit event → silent failure\n const root = form ?? (document.body as HTMLElement);\n const formId = form ? getFormId(form) : `page:${location.pathname}`;\n\n let fields = form ? collectInvalidFields(form) : [];\n if (fields.length === 0) {\n fields = collectVisibleErrors(root);\n }\n\n // Also scan full page — the error might be in a toast or outside the form\n if (fields.length === 0 && form) {\n fields = collectVisibleErrors(document.body);\n }\n\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type: \"silent_submit\",\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n }, SILENT_SUBMIT_WINDOW_MS);\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // 1. Capture submit events (fires even with e.preventDefault())\n submitHandler = (e: Event) => {\n lastSubmitTime = Date.now();\n const form = e.target as HTMLFormElement;\n if (!(form instanceof HTMLFormElement)) return;\n\n setTimeout(() => {\n if (!active) return;\n recordFormErrors(form, \"validation_failed\");\n }, POST_SUBMIT_CHECK_MS);\n };\n document.addEventListener(\"submit\", submitHandler, true);\n\n // 2. Capture native invalid events (HTML5 constraint validation)\n invalidHandler = (e: Event) => {\n lastSubmitTime = Date.now();\n const field = e.target as HTMLElement;\n const form = field.closest(\"form\");\n if (!form) return;\n\n setTimeout(() => {\n if (!active) return;\n recordFormErrors(form, \"validation_failed\");\n }, POST_SUBMIT_CHECK_MS);\n };\n document.addEventListener(\"invalid\", invalidHandler, true);\n\n // 3. Capture clicks on submit buttons — catches silent failures\n // where the form lib eats the submit and shows nothing\n clickHandler = (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n if (!target) return;\n\n // Walk up to find the actual button (click might be on an icon/span inside)\n const button = target.closest(\"button, input[type=submit]\") as HTMLElement | null;\n if (!button) return;\n if (!isSubmitButton(button)) return;\n\n handleSubmitButtonClick(button);\n };\n document.addEventListener(\"click\", clickHandler, true);\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (submitHandler) document.removeEventListener(\"submit\", submitHandler, true);\n if (invalidHandler) document.removeEventListener(\"invalid\", invalidHandler, true);\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n attemptCounts.clear();\n recentRecords.clear();\n },\n\n getEntries() {\n return [...entries];\n },\n\n report(formId: string, errors: Record<string, { message?: string } | undefined>) {\n const fields: FormErrorField[] = [];\n for (const [name, err] of Object.entries(errors)) {\n if (!err) continue;\n fields.push({ name, message: err.message });\n }\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type: \"validation_failed\",\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n },\n };\n}\n","export interface FrustrationEvent {\n type: \"rage_click\" | \"dead_click\" | \"error_loop\";\n timestamp: number;\n target: string;\n details: string;\n url: string;\n}\n\nexport interface FrustrationCollector {\n start(): void;\n stop(): void;\n getEvents(): FrustrationEvent[];\n onFrustration(callback: (event: FrustrationEvent) => void): () => void;\n}\n\nexport function createFrustrationCollector(opts?: {\n rageClickThreshold?: number;\n rageClickWindow?: number;\n errorLoopThreshold?: number;\n errorLoopWindow?: number;\n enableDeadClicks?: boolean;\n}): FrustrationCollector {\n const threshold = opts?.rageClickThreshold ?? 3;\n const window = opts?.rageClickWindow ?? 500;\n const errorThreshold = opts?.errorLoopThreshold ?? 3;\n const errorWindow = opts?.errorLoopWindow ?? 30_000;\n const deadClicksEnabled = opts?.enableDeadClicks ?? true;\n\n const events: FrustrationEvent[] = [];\n const listeners = new Set<(event: FrustrationEvent) => void>();\n const clickHistory: { target: EventTarget | null; time: number }[] = [];\n const errorCounts = new Map<string, { count: number; firstSeen: number }>();\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n let origConsoleError: typeof console.error | null = null;\n\n function emit(event: FrustrationEvent) {\n events.push(event);\n if (events.length > 50) events.shift();\n for (const cb of listeners) cb(event);\n }\n\n function getCSSSelector(el: Element): string {\n if (el.id) return `#${el.id}`;\n const tag = el.tagName.toLowerCase();\n const cls = [...el.classList].slice(0, 3).join(\".\");\n if (cls) return `${tag}.${cls}`;\n return tag;\n }\n\n function isInteractive(el: HTMLElement): boolean {\n const tag = el.tagName.toLowerCase();\n if ([\"a\", \"button\", \"input\", \"select\", \"textarea\", \"label\", \"summary\"].includes(tag)) return true;\n if (el.getAttribute(\"role\") === \"button\" || el.getAttribute(\"tabindex\")) return true;\n if (el.onclick || el.getAttribute(\"onclick\")) return true;\n if (el.closest(\"a, button, [role=button], [onclick]\")) return true;\n try {\n if (getComputedStyle(el).cursor === \"pointer\") return true;\n } catch {\n /* getComputedStyle may throw */\n }\n return false;\n }\n\n function handleClick(e: MouseEvent) {\n const target = e.target as HTMLElement;\n if (!target) return;\n const now = Date.now();\n\n // --- Rage click detection ---\n clickHistory.push({ target, time: now });\n // Remove old clicks\n while (clickHistory.length > 0 && now - clickHistory[0].time > 1000) {\n clickHistory.shift();\n }\n const recentOnSame = clickHistory.filter((c) => c.target === target && now - c.time < window);\n if (recentOnSame.length >= threshold) {\n emit({\n type: \"rage_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `${recentOnSame.length} clicks in ${now - recentOnSame[0].time}ms`,\n url: globalThis.location?.href ?? \"\",\n });\n clickHistory.length = 0;\n }\n\n // --- Dead click detection ---\n if (deadClicksEnabled && !isInteractive(target)) {\n emit({\n type: \"dead_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `Click on non-interactive <${target.tagName.toLowerCase()}>`,\n url: globalThis.location?.href ?? \"\",\n });\n }\n }\n\n function patchConsoleError() {\n origConsoleError = console.error;\n console.error = (...args: unknown[]) => {\n origConsoleError?.apply(console, args);\n // Check for error loop\n const key = String(args[0]).slice(0, 100);\n const now = Date.now();\n const existing = errorCounts.get(key);\n if (existing && now - existing.firstSeen < errorWindow) {\n existing.count++;\n if (existing.count >= errorThreshold) {\n emit({\n type: \"error_loop\",\n timestamp: now,\n target: \"console\",\n details: `Same error ${existing.count}x in ${Math.round((now - existing.firstSeen) / 1000)}s: ${key}`,\n url: globalThis.location?.href ?? \"\",\n });\n errorCounts.delete(key);\n }\n } else {\n errorCounts.set(key, { count: 1, firstSeen: now });\n }\n };\n }\n\n return {\n start() {\n clickHandler = handleClick;\n document.addEventListener(\"click\", clickHandler, true);\n patchConsoleError();\n },\n stop() {\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n if (origConsoleError) console.error = origConsoleError;\n clickHistory.length = 0;\n errorCounts.clear();\n },\n getEvents() {\n return [...events];\n },\n onFrustration(callback) {\n listeners.add(callback);\n return () => listeners.delete(callback);\n },\n };\n}\n","import type { NetworkEntry } from \"../types.js\";\n\nconst MAX_ENTRIES = 50;\n\nconst BLOCKED_HOSTS = new Set([\n \"browser-intake-datadoghq.com\",\n \"rum.browser-intake-datadoghq.com\",\n \"logs.browser-intake-datadoghq.com\",\n \"session-replay.browser-intake-datadoghq.com\",\n]);\n\nfunction isBlockedUrl(url: string, extra: Set<string>): boolean {\n try {\n const host = new URL(url, location.href).hostname;\n const all = [...BLOCKED_HOSTS, ...extra];\n return all.some((b) => host === b || host.endsWith(\".\" + b));\n } catch {\n return false;\n }\n}\n\nexport interface NetworkCollector {\n start(): void;\n stop(): void;\n getEntries(): NetworkEntry[];\n}\n\nfunction truncateUrl(url: string): string {\n try {\n const u = new URL(url, location.href);\n const base = `${u.origin}${u.pathname}`;\n return base.length > 200 ? base.slice(0, 200) + \"\\u2026\" : base;\n } catch {\n return url.length > 200 ? url.slice(0, 200) + \"\\u2026\" : url;\n }\n}\n\nexport function createNetworkCollector(extraBlockedHosts: string[] = []): NetworkCollector {\n const entries: NetworkEntry[] = [];\n const blocked = new Set(extraBlockedHosts);\n let origFetch: typeof window.fetch | null = null;\n let origXHROpen: typeof XMLHttpRequest.prototype.open | null = null;\n let active = false;\n\n function push(entry: NetworkEntry) {\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // -- Patch fetch --\n origFetch = window.fetch;\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const method = ((init?.method ?? \"GET\") as string).toUpperCase();\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : (input as Request).url;\n const startTime = Date.now();\n const res = await origFetch!.call(window, input, init);\n if (res.status >= 400 && !isBlockedUrl(url, blocked)) {\n push({\n method,\n url: truncateUrl(url),\n status: res.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n return res;\n };\n\n // -- Patch XHR --\n origXHROpen = XMLHttpRequest.prototype.open;\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL,\n async?: boolean,\n username?: string | null,\n password?: string | null,\n ) {\n const startTime = Date.now();\n const urlStr = typeof url === \"string\" ? url : (url as URL).href;\n\n this.addEventListener(\"load\", () => {\n if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {\n push({\n method: method.toUpperCase(),\n url: truncateUrl(urlStr),\n status: this.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n });\n\n return origXHROpen!.apply(this, [method, url, async ?? true, username, password] as Parameters<\n typeof XMLHttpRequest.prototype.open\n >);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (origFetch) window.fetch = origFetch;\n if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { FormErrorCollector } from \"./collectors/formErrors.js\";\nimport type { FlintUser } from \"./types.js\";\n\nexport interface FlintState {\n user: FlintUser | undefined;\n sessionReplay: string | (() => string) | undefined;\n}\n\n// Holds a reference to the active form error collector so flint.reportFormError\n// can forward calls without the consumer needing to manage the collector.\nlet formErrorCollectorRef: FormErrorCollector | null = null;\n\nexport function _setFormErrorCollector(collector: FormErrorCollector | null) {\n formErrorCollectorRef = collector;\n}\n\nlet state: FlintState = { user: undefined, sessionReplay: undefined };\nconst listeners = new Set<() => void>();\n\nfunction emit() {\n for (const l of listeners) l();\n}\n\nexport function subscribe(listener: () => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getSnapshot(): FlintState {\n return state;\n}\n\nexport const flint = {\n setUser(user: FlintUser | null) {\n state = { ...state, user: user ?? undefined };\n emit();\n },\n setSessionReplay(url: string | (() => string) | null) {\n state = { ...state, sessionReplay: url ?? undefined };\n emit();\n },\n\n /**\n * Report a form validation failure with exact field-level errors.\n * Call this from your form library's error callback.\n *\n * @example\n * // React Hook Form + Zod\n * const onSubmit = handleSubmit(onValid, (errors) => {\n * flint.reportFormError('checkout-form', errors);\n * });\n */\n reportFormError(formId: string, errors: Record<string, { message?: string } | undefined>) {\n formErrorCollectorRef?.report(formId, errors);\n },\n};\n","import { submitReport } from \"./api.js\";\nimport type { ConsoleCollector } from \"./collectors/console.js\";\nimport { createConsoleCollector } from \"./collectors/console.js\";\nimport { collectEnvironment } from \"./collectors/environment.js\";\nimport type { FormErrorCollector } from \"./collectors/formErrors.js\";\nimport { createFormErrorCollector } from \"./collectors/formErrors.js\";\nimport type { FrustrationCollector } from \"./collectors/frustration.js\";\nimport { createFrustrationCollector } from \"./collectors/frustration.js\";\nimport type { NetworkCollector } from \"./collectors/network.js\";\nimport { createNetworkCollector } from \"./collectors/network.js\";\nimport { _setFormErrorCollector, flint, getSnapshot } from \"./store.js\";\nimport type { CollectedMeta, FlintConfig } from \"./types.js\";\n\nconst DEFAULT_REPLAY_BUFFER_MS = 60_000;\n\ninterface FlintInstance {\n config: Readonly<FlintConfig>;\n console: ConsoleCollector | null;\n network: NetworkCollector | null;\n formErrors: FormErrorCollector | null;\n frustration: FrustrationCollector | null;\n replayEvents: unknown[];\n stopReplay: (() => void) | null;\n}\n\nlet instance: FlintInstance | null = null;\n\nfunction init(config: FlintConfig): void {\n if (instance) {\n console.warn(\"[Flint] Already initialized. Call Flint.shutdown() first to re-initialize.\");\n return;\n }\n\n const {\n enableConsole = true,\n enableNetwork = true,\n enableFormErrors = true,\n enableFrustration = false,\n autoReportFrustration = false,\n enableReplay = false,\n replayBufferMs = DEFAULT_REPLAY_BUFFER_MS,\n blockedHosts = [],\n frustration: frustrationOpts,\n onFrustration,\n _replayRecorder,\n } = config;\n\n // Determine Flint server host to block from network capture\n const flintHost = (() => {\n try {\n return new URL(config.serverUrl).hostname;\n } catch {\n return \"\";\n }\n })();\n\n // Create and start collectors\n const consoleCol = enableConsole ? createConsoleCollector() : null;\n consoleCol?.start();\n\n const networkCol = enableNetwork\n ? createNetworkCollector([...blockedHosts, ...(flintHost ? [flintHost] : [])])\n : null;\n networkCol?.start();\n\n const formErrorsCol = enableFormErrors ? createFormErrorCollector() : null;\n if (formErrorsCol) {\n formErrorsCol.start();\n _setFormErrorCollector(formErrorsCol);\n }\n\n const frustrationCol = enableFrustration ? createFrustrationCollector(frustrationOpts) : null;\n frustrationCol?.start();\n\n // Set user if provided\n if (config.user) {\n flint.setUser(config.user);\n }\n\n const replayEvents: unknown[] = [];\n let stopReplay: (() => void) | null = null;\n\n instance = {\n config,\n console: consoleCol,\n network: networkCol,\n formErrors: formErrorsCol,\n frustration: frustrationCol,\n replayEvents,\n stopReplay: null,\n };\n\n // Start replay recording (async, via injected recorder)\n if (enableReplay && _replayRecorder) {\n _replayRecorder((event: unknown) => {\n replayEvents.push(event);\n const cutoff = Date.now() - replayBufferMs;\n while (replayEvents.length > 0 && (replayEvents[0] as { timestamp: number }).timestamp < cutoff) {\n replayEvents.shift();\n }\n }).then((stop) => {\n stopReplay = stop ?? null;\n if (instance) instance.stopReplay = stopReplay;\n });\n }\n\n // Wire frustration auto-report\n if (frustrationCol && autoReportFrustration) {\n frustrationCol.onFrustration(async (event) => {\n onFrustration?.(event);\n const user = getSnapshot().user ?? config.user;\n await submitReport(config.serverUrl, config.projectKey, {\n reporterId: user?.id ?? \"anonymous\",\n reporterName: user?.name ?? \"Anonymous\",\n reporterEmail: user?.email,\n description: `[Auto-detected] ${event.type.replace(/_/g, \" \")}: ${event.details}`,\n severity: event.type === \"error_loop\" ? \"P1\" : event.type === \"rage_click\" ? \"P2\" : \"P3\",\n url: event.url,\n meta: {\n ...config.meta,\n environment: collectEnvironment(),\n consoleLogs: consoleCol?.getEntries() ?? [],\n networkErrors: networkCol?.getEntries() ?? [],\n formErrors: formErrorsCol?.getEntries() ?? [],\n frustrationEvent: event,\n },\n }).catch(() => {});\n });\n } else if (frustrationCol && onFrustration) {\n frustrationCol.onFrustration(onFrustration);\n }\n}\n\nfunction shutdown(): void {\n if (!instance) return;\n\n instance.console?.stop();\n instance.network?.stop();\n instance.formErrors?.stop();\n _setFormErrorCollector(null);\n instance.frustration?.stop();\n instance.stopReplay?.();\n instance = null;\n}\n\nfunction isInitialized(): boolean {\n return instance !== null;\n}\n\nfunction getInstance(): FlintInstance | null {\n return instance;\n}\n\nfunction getMeta(extraMeta?: Record<string, unknown>): CollectedMeta {\n return {\n ...extraMeta,\n environment: collectEnvironment(),\n consoleLogs: instance?.console?.getEntries() ?? [],\n networkErrors: instance?.network?.getEntries() ?? [],\n formErrors: instance?.formErrors?.getEntries() ?? [],\n };\n}\n\nfunction getReplayEvents(): unknown[] {\n return instance ? [...instance.replayEvents] : [];\n}\n\nfunction getConfig(): Readonly<FlintConfig> | null {\n return instance?.config ?? null;\n}\n\nexport const Flint = {\n init,\n shutdown,\n isInitialized,\n getInstance,\n getMeta,\n getReplayEvents,\n getConfig,\n setUser: flint.setUser,\n setSessionReplay: flint.setSessionReplay,\n reportFormError: flint.reportFormError,\n};\n","import type { Theme, ThemeOverride } from \"./types.js\";\n\nexport interface ResolvedTheme {\n background: string;\n backgroundSecondary: string;\n accent: string;\n accentHover: string;\n text: string;\n textMuted: string;\n border: string;\n shadow: string;\n buttonText: string;\n backdropFilter: string;\n}\n\nconst light: ResolvedTheme = {\n background: \"rgba(255,255,255,0.90)\",\n backgroundSecondary: \"rgba(249,250,251,0.75)\",\n accent: \"#2563eb\",\n accentHover: \"#1d4ed8\",\n text: \"#111827\",\n textMuted: \"#6b7280\",\n border: \"rgba(255,255,255,0.9)\",\n shadow: \"0 32px 80px rgba(0,0,0,0.18), 0 8px 32px rgba(0,0,0,0.1), 0 0 0 1px rgba(0,0,0,0.04)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.8)\",\n};\n\nconst dark: ResolvedTheme = {\n background: \"rgba(15,20,35,0.88)\",\n backgroundSecondary: \"rgba(5,8,18,0.65)\",\n accent: \"#4d8aff\",\n accentHover: \"#3b6fdb\",\n text: \"#dde3ef\",\n textMuted: \"#6b7a93\",\n border: \"rgba(255,255,255,0.08)\",\n shadow: \"0 24px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.6)\",\n};\n\nexport function resolveTheme(theme: Theme): ResolvedTheme {\n if (theme === \"dark\") return dark;\n if (theme === \"light\") return light;\n // ThemeOverride — merge over light base\n const override = theme as ThemeOverride;\n return {\n ...light,\n background: override.background ?? light.background,\n accent: override.accent ?? light.accent,\n accentHover: override.accent ?? light.accentHover,\n text: override.text ?? light.text,\n border: override.border ?? light.border,\n };\n}\n"],"mappings":"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","names":["init","MAX_ENTRIES","window","listeners","emit","MAX_ENTRIES","init"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/api.ts","../src/collectors/console.ts","../src/collectors/environment.ts","../src/collectors/formErrors.ts","../src/collectors/frustration.ts","../src/collectors/network.ts","../src/store.ts","../src/singleton.ts","../src/theme.ts"],"sourcesContent":["// Collectors\n\n// API\nexport { submitReplay, submitReport } from \"./api.js\";\n// Collector types\nexport type { ConsoleCollector } from \"./collectors/console.js\";\nexport { createConsoleCollector } from \"./collectors/console.js\";\nexport { collectEnvironment } from \"./collectors/environment.js\";\nexport type { FormErrorCollector } from \"./collectors/formErrors.js\";\nexport { createFormErrorCollector } from \"./collectors/formErrors.js\";\nexport type { FrustrationCollector, FrustrationEvent } from \"./collectors/frustration.js\";\nexport { createFrustrationCollector } from \"./collectors/frustration.js\";\nexport type { NetworkCollector } from \"./collectors/network.js\";\nexport { createNetworkCollector } from \"./collectors/network.js\";\n// Singleton\nexport { Flint } from \"./singleton.js\";\nexport type { FlintState } from \"./store.js\";\n// State\nexport { _setFormErrorCollector, flint, getSnapshot, subscribe } from \"./store.js\";\nexport type { ResolvedTheme } from \"./theme.js\";\n// Theme\nexport { resolveTheme } from \"./theme.js\";\n// Types (re-export)\nexport type {\n CollectedMeta,\n ConsoleEntry,\n EnvironmentInfo,\n FlintConfig,\n FlintExtraFields,\n FlintUser,\n FlintWidgetProps,\n FormErrorEntry,\n Locale,\n NetworkEntry,\n ReportPayload,\n ReportResult,\n Severity,\n Theme,\n ThemeOverride,\n} from \"./types.js\";\n","import { gzipSync } from \"fflate\";\nimport type { ReportPayload, ReportResult } from \"./types.js\";\n\nasync function fetchWithRetry(url: string, init: RequestInit, retries = 3, baseDelay = 1000): Promise<Response> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n const res = await fetch(url, init);\n if (res.ok || attempt === retries) return res;\n // Don't retry client errors (4xx) except 429\n if (res.status >= 400 && res.status < 500 && res.status !== 429) return res;\n } catch (err) {\n if (attempt === retries) throw err;\n }\n await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));\n }\n throw new Error(\"Max retries exceeded\");\n}\n\nexport async function submitReport(\n serverUrl: string,\n projectKey: string,\n payload: ReportPayload,\n screenshot?: File,\n): Promise<ReportResult> {\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports`;\n\n let body: BodyInit;\n const headers: Record<string, string> = {\n \"x-project-key\": projectKey,\n };\n\n if (screenshot) {\n const form = new FormData();\n form.append(\"reporterId\", payload.reporterId);\n form.append(\"reporterName\", payload.reporterName);\n if (payload.reporterEmail) form.append(\"reporterEmail\", payload.reporterEmail);\n form.append(\"description\", payload.description);\n if (payload.expectedBehavior) form.append(\"expectedBehavior\", payload.expectedBehavior);\n if (payload.stepsToReproduce) form.append(\"stepsToReproduce\", JSON.stringify(payload.stepsToReproduce));\n if (payload.externalReplayUrl) form.append(\"externalReplayUrl\", payload.externalReplayUrl);\n if (payload.additionalContext) form.append(\"additionalContext\", payload.additionalContext);\n form.append(\"severity\", payload.severity);\n if (payload.url) form.append(\"url\", payload.url);\n if (payload.meta) form.append(\"meta\", JSON.stringify(payload.meta));\n form.append(\"screenshot\", screenshot);\n body = form;\n } else {\n body = JSON.stringify(payload);\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const res = await fetchWithRetry(url, { method: \"POST\", headers, body });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: \"Unknown error\" }));\n throw new Error((err as { error?: string }).error ?? `HTTP ${res.status}`);\n }\n\n return res.json() as Promise<ReportResult>;\n}\n\nexport async function submitReplay(\n serverUrl: string,\n projectKey: string,\n reportId: string,\n events: unknown[],\n): Promise<void> {\n const json = JSON.stringify(events);\n const encoded = new TextEncoder().encode(json);\n const compressed = gzipSync(encoded);\n\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports/${reportId}/replay`;\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"x-project-key\": projectKey,\n \"Content-Type\": \"application/octet-stream\",\n },\n body: compressed.buffer as ArrayBuffer,\n });\n}\n","import type { ConsoleEntry } from \"../types.js\";\n\nconst MAX_ENTRIES = 50;\n\n// Redact values that look like tokens, passwords, secrets, or API keys\nconst SENSITIVE_PATTERNS = [\n /(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|authorization|bearer)\\s*[:=]\\s*[\"']?[^\\s\"',]{4,}/gi,\n /\\b(sk-[a-zA-Z0-9_-]{20,})\\b/g, // API keys (e.g. sk-ant-...)\n /\\b(ghp_[a-zA-Z0-9]{36,})\\b/g, // GitHub tokens\n /\\b(xoxb-[a-zA-Z0-9-]+)\\b/g, // Slack tokens\n /\\b(eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,})\\b/g, // JWTs\n];\n\nfunction sanitize(str: string): string {\n let result = str;\n for (const pattern of SENSITIVE_PATTERNS) {\n result = result.replace(pattern, \"[REDACTED]\");\n }\n return result;\n}\n\nexport interface ConsoleCollector {\n start(): void;\n stop(): void;\n getEntries(): ConsoleEntry[];\n}\n\nexport function createConsoleCollector(): ConsoleCollector {\n const entries: ConsoleEntry[] = [];\n let active = false;\n\n const originals = {\n log: console.log.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n };\n\n let origOnerror: typeof window.onerror = null;\n let origUnhandled: typeof window.onunhandledrejection = null;\n\n function push(level: ConsoleEntry[\"level\"], args: unknown[]) {\n let str: string;\n try {\n str = args.map((a) => (typeof a === \"string\" ? a : JSON.stringify(a))).join(\" \");\n } catch {\n str = String(args[0]);\n }\n if (str.length > 500) str = str.slice(0, 500) + \"\\u2026\";\n str = sanitize(str);\n entries.push({ level, args: str, timestamp: Date.now() });\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n console.log = (...args: unknown[]) => {\n push(\"log\", args);\n originals.log(...args);\n };\n console.warn = (...args: unknown[]) => {\n push(\"warn\", args);\n originals.warn(...args);\n };\n console.error = (...args: unknown[]) => {\n push(\"error\", args);\n originals.error(...args);\n };\n\n origOnerror = window.onerror;\n window.onerror = (msg, src, line, col, err) => {\n push(\"error\", [err?.message ?? String(msg), `${src}:${line}:${col}`]);\n if (typeof origOnerror === \"function\") return origOnerror(msg, src, line, col, err);\n return false;\n };\n\n origUnhandled = window.onunhandledrejection;\n window.onunhandledrejection = (event) => {\n const reason = event.reason instanceof Error ? event.reason.message : JSON.stringify(event.reason);\n push(\"error\", [\"UnhandledRejection:\", reason]);\n if (typeof origUnhandled === \"function\") origUnhandled.call(window, event);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n console.log = originals.log;\n console.warn = originals.warn;\n console.error = originals.error;\n window.onerror = origOnerror;\n window.onunhandledrejection = origUnhandled;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { EnvironmentInfo } from \"../types.js\";\n\nexport function collectEnvironment(): EnvironmentInfo {\n const ua = navigator.userAgent;\n\n // -- Browser --\n let browser = \"Unknown\";\n const chromeM = ua.match(/Chrome\\/(\\d+)/);\n const firefoxM = ua.match(/Firefox\\/(\\d+)/);\n const edgeM = ua.match(/Edg\\/(\\d+)/);\n const safariM = ua.match(/Version\\/(\\d+)/);\n const operaM = ua.match(/OPR\\/(\\d+)/);\n\n if (operaM) {\n browser = `Opera ${operaM[1]}`;\n } else if (edgeM) {\n browser = `Edge ${edgeM[1]}`;\n } else if (chromeM && !/Edg|OPR/.test(ua)) {\n browser = `Chrome ${chromeM[1]}`;\n } else if (firefoxM) {\n browser = `Firefox ${firefoxM[1]}`;\n } else if (safariM && /Safari\\//.test(ua)) {\n browser = `Safari ${safariM[1]}`;\n }\n\n // -- OS --\n let os = \"Unknown\";\n const macM = ua.match(/Mac OS X (\\d+[._]\\d+)/);\n const winM = ua.match(/Windows NT (\\d+\\.\\d+)/);\n const androidM = ua.match(/Android (\\d+)/);\n const iosM = ua.match(/iPhone OS (\\d+[._]\\d+)/);\n\n if (macM) {\n os = `macOS ${macM[1].replace(\"_\", \".\")}`;\n } else if (winM) {\n const winMap: Record<string, string> = {\n \"10.0\": \"10/11\",\n \"6.3\": \"8.1\",\n \"6.2\": \"8\",\n \"6.1\": \"7\",\n };\n os = `Windows ${winMap[winM[1]] ?? winM[1]}`;\n } else if (androidM) {\n os = `Android ${androidM[1]}`;\n } else if (iosM) {\n os = `iOS ${iosM[1].replace(/_/g, \".\")}`;\n } else if (/Linux/.test(ua)) {\n os = \"Linux\";\n }\n\n return {\n browser,\n os,\n viewport: `${window.innerWidth}x${window.innerHeight}`,\n screen: `${screen.width}x${screen.height}`,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n online: navigator.onLine,\n };\n}\n","import type { FormErrorEntry, FormErrorField } from \"../types.js\";\n\nconst MAX_ENTRIES = 30;\n\n/**\n * Time to wait after a submit event before checking the DOM for error\n * indicators. RHF/zod validate asynchronously — 300ms covers most cases\n * while keeping the detection snappy.\n */\nconst POST_SUBMIT_CHECK_MS = 300;\n\n/**\n * After a submit button click, if no submit event fires within this window\n * it means the form library swallowed the submit entirely (e.g. RHF's\n * handleSubmit called preventDefault before the native event propagated,\n * or the button isn't even inside a <form>). We treat this as a potential\n * silent failure and scan the page for error indicators.\n */\nconst SILENT_SUBMIT_WINDOW_MS = 400;\n\nexport interface FormErrorCollector {\n start(): void;\n stop(): void;\n getEntries(): FormErrorEntry[];\n /**\n * Manually report a form validation failure. Call this from your form\n * library's error handler to capture exact field-level errors.\n *\n * @example\n * // React Hook Form + Zod\n * <form onSubmit={handleSubmit(onValid, (errors) => {\n * flint.reportFormError('checkout-form', errors);\n * })}>\n */\n report(formId: string, errors: Record<string, { message?: string } | undefined>): void;\n}\n\n// ── DOM helpers ────────────────────────────────────────────────────────────────\n\nfunction getFormId(form: HTMLFormElement): string {\n if (form.id) return `#${form.id}`;\n if (form.getAttribute(\"name\")) return `form[name=\"${form.getAttribute(\"name\")}\"]`;\n if (form.action && form.action !== location.href) return form.action;\n return `form:${Array.from(document.forms).indexOf(form)}`;\n}\n\nfunction getFieldLabel(field: HTMLElement, root: HTMLElement): string {\n const ariaLabel = field.getAttribute(\"aria-label\");\n if (ariaLabel) return ariaLabel;\n\n const id = field.id;\n if (id) {\n const label = root.querySelector<HTMLLabelElement>(`label[for=\"${id}\"]`);\n if (label?.textContent) return label.textContent.trim();\n }\n\n const parentLabel = field.closest(\"label\");\n if (parentLabel?.textContent) {\n const text = parentLabel.textContent.trim();\n if (text.length < 60) return text;\n }\n\n return (field as HTMLInputElement).name || (field as HTMLInputElement).placeholder || field.tagName.toLowerCase();\n}\n\nfunction getErrorMessage(field: HTMLElement): string | undefined {\n // aria-errormessage\n const errId = field.getAttribute(\"aria-errormessage\");\n if (errId) {\n const el = document.getElementById(errId);\n if (el?.textContent) return el.textContent.trim();\n }\n\n // aria-describedby (RHF wires error messages here)\n const descId = field.getAttribute(\"aria-describedby\");\n if (descId) {\n for (const id of descId.split(/\\s+/)) {\n const el = document.getElementById(id);\n if (el?.textContent?.trim()) return el.textContent.trim();\n }\n }\n\n // native validationMessage\n if (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement) {\n if (field.validationMessage) return field.validationMessage;\n }\n\n // adjacent role=\"alert\"\n const next = field.nextElementSibling;\n if (next?.getAttribute(\"role\") === \"alert\" && next.textContent) {\n return next.textContent.trim();\n }\n\n return undefined;\n}\n\n/**\n * Collect fields with `aria-invalid=\"true\"` or native `:invalid` inside a\n * root element (a <form> or the whole document).\n */\nfunction collectInvalidFields(root: HTMLElement): FormErrorField[] {\n const fields: FormErrorField[] = [];\n const invalidEls = root.querySelectorAll<HTMLElement>('[aria-invalid=\"true\"], :invalid');\n\n for (const el of invalidEls) {\n if (el instanceof HTMLFormElement) continue;\n if (el.tagName === \"FIELDSET\") continue;\n\n fields.push({\n name: getFieldLabel(el, root),\n message: getErrorMessage(el),\n });\n }\n\n return fields;\n}\n\n/**\n * Scan the page for visible error indicators that appeared after a submit\n * attempt. This catches errors even when:\n * - The errored field is in another tab/step of a multi-step form\n * - The form doesn't use aria-invalid at all\n * - Errors are rendered as standalone alert divs\n */\nfunction collectVisibleErrors(root: HTMLElement): FormErrorField[] {\n const fields: FormErrorField[] = [];\n const seen = new Set<string>();\n\n // 1. role=\"alert\" elements that appeared (error toasts, inline messages)\n const alerts = root.querySelectorAll<HTMLElement>('[role=\"alert\"]');\n for (const el of alerts) {\n const text = el.textContent?.trim();\n if (!text || text.length > 200) continue;\n if (seen.has(text)) continue;\n seen.add(text);\n fields.push({ name: \"alert\", message: text });\n }\n\n // 2. Common error class patterns (data-error, .error, .field-error, etc.)\n const errorEls = root.querySelectorAll<HTMLElement>(\n \"[data-error], [data-field-error], .error-message, .field-error, .form-error\",\n );\n for (const el of errorEls) {\n const text = el.textContent?.trim();\n if (!text || text.length > 200) continue;\n if (seen.has(text)) continue;\n seen.add(text);\n fields.push({ name: \"error\", message: text });\n }\n\n return fields;\n}\n\nfunction isSubmitButton(el: HTMLElement): el is HTMLButtonElement {\n if (el.tagName === \"BUTTON\") {\n const type = el.getAttribute(\"type\");\n // Buttons default to type=\"submit\" inside forms when no type is set\n return type === \"submit\" || type === null || type === \"\";\n }\n if (el.tagName === \"INPUT\") {\n return (el as HTMLInputElement).type === \"submit\";\n }\n return false;\n}\n\nfunction findClosestForm(el: HTMLElement): HTMLFormElement | null {\n // Direct form association\n if (\"form\" in el && (el as HTMLButtonElement).form) {\n return (el as HTMLButtonElement).form;\n }\n return el.closest(\"form\");\n}\n\n// ── Collector ──────────────────────────────────────────────────────────────────\n\nexport function createFormErrorCollector(): FormErrorCollector {\n const entries: FormErrorEntry[] = [];\n const attemptCounts = new Map<string, number>();\n let active = false;\n\n // Handlers we need to clean up\n let submitHandler: ((e: Event) => void) | null = null;\n let invalidHandler: ((e: Event) => void) | null = null;\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n\n // Track whether a real submit event followed a button click\n let lastSubmitTime = 0;\n\n // Dedup: avoid recording the same form twice within a short window\n const recentRecords = new Map<string, number>();\n const DEDUP_MS = 500;\n\n function push(entry: FormErrorEntry) {\n // Dedup check\n const _key = `${entry.formId}:${entry.timestamp}`;\n const last = recentRecords.get(entry.formId);\n if (last && entry.timestamp - last < DEDUP_MS) return;\n recentRecords.set(entry.formId, entry.timestamp);\n\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n function recordFormErrors(form: HTMLFormElement, type: FormErrorEntry[\"type\"]) {\n const formId = getFormId(form);\n\n // Strategy 1: aria-invalid / :invalid fields\n let fields = collectInvalidFields(form);\n\n // Strategy 2: if no aria-invalid fields found, look for visible error messages\n if (fields.length === 0) {\n fields = collectVisibleErrors(form);\n }\n\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type,\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Handle clicks on submit buttons. If no submit event follows within\n * SILENT_SUBMIT_WINDOW_MS, it means the form library blocked the submit\n * entirely — scan for errors.\n */\n function handleSubmitButtonClick(button: HTMLElement) {\n const form = findClosestForm(button);\n\n setTimeout(() => {\n if (!active) return;\n // A real submit event fired — the submitHandler already took care of it\n if (Date.now() - lastSubmitTime < SILENT_SUBMIT_WINDOW_MS) return;\n\n // No submit event → silent failure\n const root = form ?? (document.body as HTMLElement);\n const formId = form ? getFormId(form) : `page:${location.pathname}`;\n\n let fields = form ? collectInvalidFields(form) : [];\n if (fields.length === 0) {\n fields = collectVisibleErrors(root);\n }\n\n // Also scan full page — the error might be in a toast or outside the form\n if (fields.length === 0 && form) {\n fields = collectVisibleErrors(document.body);\n }\n\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type: \"silent_submit\",\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n }, SILENT_SUBMIT_WINDOW_MS);\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // 1. Capture submit events (fires even with e.preventDefault())\n submitHandler = (e: Event) => {\n lastSubmitTime = Date.now();\n const form = e.target as HTMLFormElement;\n if (!(form instanceof HTMLFormElement)) return;\n\n setTimeout(() => {\n if (!active) return;\n recordFormErrors(form, \"validation_failed\");\n }, POST_SUBMIT_CHECK_MS);\n };\n document.addEventListener(\"submit\", submitHandler, true);\n\n // 2. Capture native invalid events (HTML5 constraint validation)\n invalidHandler = (e: Event) => {\n lastSubmitTime = Date.now();\n const field = e.target as HTMLElement;\n const form = field.closest(\"form\");\n if (!form) return;\n\n setTimeout(() => {\n if (!active) return;\n recordFormErrors(form, \"validation_failed\");\n }, POST_SUBMIT_CHECK_MS);\n };\n document.addEventListener(\"invalid\", invalidHandler, true);\n\n // 3. Capture clicks on submit buttons — catches silent failures\n // where the form lib eats the submit and shows nothing\n clickHandler = (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n if (!target) return;\n\n // Walk up to find the actual button (click might be on an icon/span inside)\n const button = target.closest(\"button, input[type=submit]\") as HTMLElement | null;\n if (!button) return;\n if (!isSubmitButton(button)) return;\n\n handleSubmitButtonClick(button);\n };\n document.addEventListener(\"click\", clickHandler, true);\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (submitHandler) document.removeEventListener(\"submit\", submitHandler, true);\n if (invalidHandler) document.removeEventListener(\"invalid\", invalidHandler, true);\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n attemptCounts.clear();\n recentRecords.clear();\n },\n\n getEntries() {\n return [...entries];\n },\n\n report(formId: string, errors: Record<string, { message?: string } | undefined>) {\n const fields: FormErrorField[] = [];\n for (const [name, err] of Object.entries(errors)) {\n if (!err) continue;\n fields.push({ name, message: err.message });\n }\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type: \"validation_failed\",\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n },\n };\n}\n","export interface FrustrationEvent {\n type: \"rage_click\" | \"dead_click\" | \"error_loop\";\n timestamp: number;\n target: string;\n details: string;\n url: string;\n}\n\nexport interface FrustrationCollector {\n start(): void;\n stop(): void;\n getEvents(): FrustrationEvent[];\n onFrustration(callback: (event: FrustrationEvent) => void): () => void;\n}\n\nexport function createFrustrationCollector(opts?: {\n rageClickThreshold?: number;\n rageClickWindow?: number;\n errorLoopThreshold?: number;\n errorLoopWindow?: number;\n enableDeadClicks?: boolean;\n}): FrustrationCollector {\n const threshold = opts?.rageClickThreshold ?? 3;\n const window = opts?.rageClickWindow ?? 500;\n const errorThreshold = opts?.errorLoopThreshold ?? 3;\n const errorWindow = opts?.errorLoopWindow ?? 30_000;\n const deadClicksEnabled = opts?.enableDeadClicks ?? true;\n\n const events: FrustrationEvent[] = [];\n const listeners = new Set<(event: FrustrationEvent) => void>();\n const clickHistory: { target: EventTarget | null; time: number }[] = [];\n const errorCounts = new Map<string, { count: number; firstSeen: number }>();\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n let origConsoleError: typeof console.error | null = null;\n\n function emit(event: FrustrationEvent) {\n events.push(event);\n if (events.length > 50) events.shift();\n for (const cb of listeners) cb(event);\n }\n\n function getCSSSelector(el: Element): string {\n if (el.id) return `#${el.id}`;\n const tag = el.tagName.toLowerCase();\n const cls = [...el.classList].slice(0, 3).join(\".\");\n if (cls) return `${tag}.${cls}`;\n return tag;\n }\n\n function isInteractive(el: HTMLElement): boolean {\n const tag = el.tagName.toLowerCase();\n if ([\"a\", \"button\", \"input\", \"select\", \"textarea\", \"label\", \"summary\"].includes(tag)) return true;\n if (el.getAttribute(\"role\") === \"button\" || el.getAttribute(\"tabindex\")) return true;\n if (el.onclick || el.getAttribute(\"onclick\")) return true;\n if (el.closest(\"a, button, [role=button], [onclick]\")) return true;\n try {\n if (getComputedStyle(el).cursor === \"pointer\") return true;\n } catch {\n /* getComputedStyle may throw */\n }\n return false;\n }\n\n function handleClick(e: MouseEvent) {\n const target = e.target as HTMLElement;\n if (!target) return;\n const now = Date.now();\n\n // --- Rage click detection ---\n clickHistory.push({ target, time: now });\n // Remove old clicks\n while (clickHistory.length > 0 && now - clickHistory[0].time > 1000) {\n clickHistory.shift();\n }\n const recentOnSame = clickHistory.filter((c) => c.target === target && now - c.time < window);\n if (recentOnSame.length >= threshold) {\n emit({\n type: \"rage_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `${recentOnSame.length} clicks in ${now - recentOnSame[0].time}ms`,\n url: globalThis.location?.href ?? \"\",\n });\n clickHistory.length = 0;\n }\n\n // --- Dead click detection ---\n if (deadClicksEnabled && !isInteractive(target)) {\n emit({\n type: \"dead_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `Click on non-interactive <${target.tagName.toLowerCase()}>`,\n url: globalThis.location?.href ?? \"\",\n });\n }\n }\n\n function patchConsoleError() {\n origConsoleError = console.error;\n console.error = (...args: unknown[]) => {\n origConsoleError?.apply(console, args);\n // Check for error loop\n const key = String(args[0]).slice(0, 100);\n const now = Date.now();\n const existing = errorCounts.get(key);\n if (existing && now - existing.firstSeen < errorWindow) {\n existing.count++;\n if (existing.count >= errorThreshold) {\n emit({\n type: \"error_loop\",\n timestamp: now,\n target: \"console\",\n details: `Same error ${existing.count}x in ${Math.round((now - existing.firstSeen) / 1000)}s: ${key}`,\n url: globalThis.location?.href ?? \"\",\n });\n errorCounts.delete(key);\n }\n } else {\n errorCounts.set(key, { count: 1, firstSeen: now });\n }\n };\n }\n\n return {\n start() {\n clickHandler = handleClick;\n document.addEventListener(\"click\", clickHandler, true);\n patchConsoleError();\n },\n stop() {\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n if (origConsoleError) console.error = origConsoleError;\n clickHistory.length = 0;\n errorCounts.clear();\n },\n getEvents() {\n return [...events];\n },\n onFrustration(callback) {\n listeners.add(callback);\n return () => listeners.delete(callback);\n },\n };\n}\n","import type { NetworkEntry } from \"../types.js\";\n\nconst MAX_ENTRIES = 50;\n\nconst BLOCKED_HOSTS = new Set([\n \"browser-intake-datadoghq.com\",\n \"rum.browser-intake-datadoghq.com\",\n \"logs.browser-intake-datadoghq.com\",\n \"session-replay.browser-intake-datadoghq.com\",\n]);\n\nfunction isBlockedUrl(url: string, extra: Set<string>): boolean {\n try {\n const host = new URL(url, location.href).hostname;\n const all = [...BLOCKED_HOSTS, ...extra];\n return all.some((b) => host === b || host.endsWith(\".\" + b));\n } catch {\n return false;\n }\n}\n\nexport interface NetworkCollector {\n start(): void;\n stop(): void;\n getEntries(): NetworkEntry[];\n}\n\nfunction truncateUrl(url: string): string {\n try {\n const u = new URL(url, location.href);\n const base = `${u.origin}${u.pathname}`;\n return base.length > 200 ? base.slice(0, 200) + \"\\u2026\" : base;\n } catch {\n return url.length > 200 ? url.slice(0, 200) + \"\\u2026\" : url;\n }\n}\n\nexport function createNetworkCollector(extraBlockedHosts: string[] = []): NetworkCollector {\n const entries: NetworkEntry[] = [];\n const blocked = new Set(extraBlockedHosts);\n let origFetch: typeof window.fetch | null = null;\n let origXHROpen: typeof XMLHttpRequest.prototype.open | null = null;\n let active = false;\n\n function push(entry: NetworkEntry) {\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // -- Patch fetch --\n origFetch = window.fetch;\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const method = ((init?.method ?? \"GET\") as string).toUpperCase();\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : (input as Request).url;\n const startTime = Date.now();\n const res = await origFetch!.call(window, input, init);\n if (res.status >= 400 && !isBlockedUrl(url, blocked)) {\n push({\n method,\n url: truncateUrl(url),\n status: res.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n return res;\n };\n\n // -- Patch XHR --\n origXHROpen = XMLHttpRequest.prototype.open;\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL,\n async?: boolean,\n username?: string | null,\n password?: string | null,\n ) {\n const startTime = Date.now();\n const urlStr = typeof url === \"string\" ? url : (url as URL).href;\n\n this.addEventListener(\"load\", () => {\n if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {\n push({\n method: method.toUpperCase(),\n url: truncateUrl(urlStr),\n status: this.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n });\n\n return origXHROpen!.apply(this, [method, url, async ?? true, username, password] as Parameters<\n typeof XMLHttpRequest.prototype.open\n >);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (origFetch) window.fetch = origFetch;\n if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { FormErrorCollector } from \"./collectors/formErrors.js\";\nimport type { FlintUser } from \"./types.js\";\n\nexport interface FlintState {\n user: FlintUser | undefined;\n sessionReplay: string | (() => string) | undefined;\n}\n\n// Holds a reference to the active form error collector so flint.reportFormError\n// can forward calls without the consumer needing to manage the collector.\nlet formErrorCollectorRef: FormErrorCollector | null = null;\n\nexport function _setFormErrorCollector(collector: FormErrorCollector | null) {\n formErrorCollectorRef = collector;\n}\n\nlet state: FlintState = { user: undefined, sessionReplay: undefined };\nconst listeners = new Set<() => void>();\n\nfunction emit() {\n for (const l of listeners) l();\n}\n\nexport function subscribe(listener: () => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getSnapshot(): FlintState {\n return state;\n}\n\nexport const flint = {\n setUser(user: FlintUser | null) {\n state = { ...state, user: user ?? undefined };\n emit();\n },\n setSessionReplay(url: string | (() => string) | null) {\n state = { ...state, sessionReplay: url ?? undefined };\n emit();\n },\n\n /**\n * Report a form validation failure with exact field-level errors.\n * Call this from your form library's error callback.\n *\n * @example\n * // React Hook Form + Zod\n * const onSubmit = handleSubmit(onValid, (errors) => {\n * flint.reportFormError('checkout-form', errors);\n * });\n */\n reportFormError(formId: string, errors: Record<string, { message?: string } | undefined>) {\n formErrorCollectorRef?.report(formId, errors);\n },\n};\n","// Flint singleton — manages global collector lifecycle\nimport { submitReport } from \"./api.js\";\nimport type { ConsoleCollector } from \"./collectors/console.js\";\nimport { createConsoleCollector } from \"./collectors/console.js\";\nimport { collectEnvironment } from \"./collectors/environment.js\";\nimport type { FormErrorCollector } from \"./collectors/formErrors.js\";\nimport { createFormErrorCollector } from \"./collectors/formErrors.js\";\nimport type { FrustrationCollector } from \"./collectors/frustration.js\";\nimport { createFrustrationCollector } from \"./collectors/frustration.js\";\nimport type { NetworkCollector } from \"./collectors/network.js\";\nimport { createNetworkCollector } from \"./collectors/network.js\";\nimport { _setFormErrorCollector, flint, getSnapshot } from \"./store.js\";\nimport type { CollectedMeta, FlintConfig } from \"./types.js\";\n\nconst DEFAULT_REPLAY_BUFFER_MS = 60_000;\n\ninterface FlintInstance {\n config: Readonly<FlintConfig>;\n console: ConsoleCollector | null;\n network: NetworkCollector | null;\n formErrors: FormErrorCollector | null;\n frustration: FrustrationCollector | null;\n replayEvents: unknown[];\n stopReplay: (() => void) | null;\n}\n\nlet instance: FlintInstance | null = null;\n\nfunction init(config: FlintConfig): void {\n if (instance) {\n console.warn(\"[Flint] Already initialized. Call Flint.shutdown() first to re-initialize.\");\n return;\n }\n\n const {\n enableConsole = true,\n enableNetwork = true,\n enableFormErrors = true,\n enableFrustration = false,\n autoReportFrustration = false,\n enableReplay = false,\n replayBufferMs = DEFAULT_REPLAY_BUFFER_MS,\n blockedHosts = [],\n frustration: frustrationOpts,\n onFrustration,\n _replayRecorder,\n } = config;\n\n // Determine Flint server host to block from network capture\n const flintHost = (() => {\n try {\n return new URL(config.serverUrl).hostname;\n } catch {\n return \"\";\n }\n })();\n\n // Create and start collectors\n const consoleCol = enableConsole ? createConsoleCollector() : null;\n consoleCol?.start();\n\n const networkCol = enableNetwork\n ? createNetworkCollector([...blockedHosts, ...(flintHost ? [flintHost] : [])])\n : null;\n networkCol?.start();\n\n const formErrorsCol = enableFormErrors ? createFormErrorCollector() : null;\n if (formErrorsCol) {\n formErrorsCol.start();\n _setFormErrorCollector(formErrorsCol);\n }\n\n const frustrationCol = enableFrustration ? createFrustrationCollector(frustrationOpts) : null;\n frustrationCol?.start();\n\n // Set user if provided\n if (config.user) {\n flint.setUser(config.user);\n }\n\n const replayEvents: unknown[] = [];\n let stopReplay: (() => void) | null = null;\n\n instance = {\n config,\n console: consoleCol,\n network: networkCol,\n formErrors: formErrorsCol,\n frustration: frustrationCol,\n replayEvents,\n stopReplay: null,\n };\n\n // Start replay recording (async, via injected recorder)\n if (enableReplay && _replayRecorder) {\n _replayRecorder((event: unknown) => {\n replayEvents.push(event);\n const cutoff = Date.now() - replayBufferMs;\n while (replayEvents.length > 0 && (replayEvents[0] as { timestamp: number }).timestamp < cutoff) {\n replayEvents.shift();\n }\n }).then((stop) => {\n stopReplay = stop ?? null;\n if (instance) instance.stopReplay = stopReplay;\n });\n }\n\n // Wire frustration auto-report\n if (frustrationCol && autoReportFrustration) {\n frustrationCol.onFrustration(async (event) => {\n onFrustration?.(event);\n const user = getSnapshot().user ?? config.user;\n await submitReport(config.serverUrl, config.projectKey, {\n reporterId: user?.id ?? \"anonymous\",\n reporterName: user?.name ?? \"Anonymous\",\n reporterEmail: user?.email,\n description: `[Auto-detected] ${event.type.replace(/_/g, \" \")}: ${event.details}`,\n severity: event.type === \"error_loop\" ? \"P1\" : event.type === \"rage_click\" ? \"P2\" : \"P3\",\n url: event.url,\n meta: {\n ...config.meta,\n environment: collectEnvironment(),\n consoleLogs: consoleCol?.getEntries() ?? [],\n networkErrors: networkCol?.getEntries() ?? [],\n formErrors: formErrorsCol?.getEntries() ?? [],\n frustrationEvent: event,\n },\n }).catch(() => {});\n });\n } else if (frustrationCol && onFrustration) {\n frustrationCol.onFrustration(onFrustration);\n }\n}\n\nfunction shutdown(): void {\n if (!instance) return;\n\n instance.console?.stop();\n instance.network?.stop();\n instance.formErrors?.stop();\n _setFormErrorCollector(null);\n instance.frustration?.stop();\n instance.stopReplay?.();\n instance = null;\n}\n\nfunction isInitialized(): boolean {\n return instance !== null;\n}\n\nfunction getInstance(): FlintInstance | null {\n return instance;\n}\n\nfunction getMeta(extraMeta?: Record<string, unknown>): CollectedMeta {\n return {\n ...extraMeta,\n environment: collectEnvironment(),\n consoleLogs: instance?.console?.getEntries() ?? [],\n networkErrors: instance?.network?.getEntries() ?? [],\n formErrors: instance?.formErrors?.getEntries() ?? [],\n };\n}\n\nfunction getReplayEvents(): unknown[] {\n return instance ? [...instance.replayEvents] : [];\n}\n\nfunction getConfig(): Readonly<FlintConfig> | null {\n return instance?.config ?? null;\n}\n\nexport const Flint = {\n init,\n shutdown,\n isInitialized,\n getInstance,\n getMeta,\n getReplayEvents,\n getConfig,\n setUser: flint.setUser,\n setSessionReplay: flint.setSessionReplay,\n reportFormError: flint.reportFormError,\n};\n","import type { Theme, ThemeOverride } from \"./types.js\";\n\nexport interface ResolvedTheme {\n background: string;\n backgroundSecondary: string;\n accent: string;\n accentHover: string;\n text: string;\n textMuted: string;\n border: string;\n shadow: string;\n buttonText: string;\n backdropFilter: string;\n}\n\nconst light: ResolvedTheme = {\n background: \"rgba(255,255,255,0.90)\",\n backgroundSecondary: \"rgba(249,250,251,0.75)\",\n accent: \"#2563eb\",\n accentHover: \"#1d4ed8\",\n text: \"#111827\",\n textMuted: \"#6b7280\",\n border: \"rgba(255,255,255,0.9)\",\n shadow: \"0 32px 80px rgba(0,0,0,0.18), 0 8px 32px rgba(0,0,0,0.1), 0 0 0 1px rgba(0,0,0,0.04)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.8)\",\n};\n\nconst dark: ResolvedTheme = {\n background: \"rgba(15,20,35,0.88)\",\n backgroundSecondary: \"rgba(5,8,18,0.65)\",\n accent: \"#4d8aff\",\n accentHover: \"#3b6fdb\",\n text: \"#dde3ef\",\n textMuted: \"#6b7a93\",\n border: \"rgba(255,255,255,0.08)\",\n shadow: \"0 24px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.6)\",\n};\n\nexport function resolveTheme(theme: Theme): ResolvedTheme {\n if (theme === \"dark\") return dark;\n if (theme === \"light\") return light;\n // ThemeOverride — merge over light base\n const override = theme as ThemeOverride;\n return {\n ...light,\n background: override.background ?? light.background,\n accent: override.accent ?? light.accent,\n accentHover: override.accent ?? light.accentHover,\n text: override.text ?? light.text,\n border: override.border ?? light.border,\n };\n}\n"],"mappings":"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","names":["init","MAX_ENTRIES","window","listeners","emit","MAX_ENTRIES","init"]}
|
package/dist/index.js
CHANGED
|
@@ -309,7 +309,7 @@ function createFormErrorCollector() {
|
|
|
309
309
|
const recentRecords = /* @__PURE__ */ new Map();
|
|
310
310
|
const DEDUP_MS = 500;
|
|
311
311
|
function push(entry) {
|
|
312
|
-
const
|
|
312
|
+
const _key = `${entry.formId}:${entry.timestamp}`;
|
|
313
313
|
const last = recentRecords.get(entry.formId);
|
|
314
314
|
if (last && entry.timestamp - last < DEDUP_MS) return;
|
|
315
315
|
recentRecords.set(entry.formId, entry.timestamp);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/api.ts","../src/collectors/console.ts","../src/collectors/environment.ts","../src/collectors/formErrors.ts","../src/collectors/frustration.ts","../src/collectors/network.ts","../src/store.ts","../src/singleton.ts","../src/theme.ts"],"sourcesContent":["import type { ReportPayload, ReportResult } from \"./types.js\";\nimport { gzipSync } from \"fflate\";\n\nasync function fetchWithRetry(url: string, init: RequestInit, retries = 3, baseDelay = 1000): Promise<Response> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n const res = await fetch(url, init);\n if (res.ok || attempt === retries) return res;\n // Don't retry client errors (4xx) except 429\n if (res.status >= 400 && res.status < 500 && res.status !== 429) return res;\n } catch (err) {\n if (attempt === retries) throw err;\n }\n await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));\n }\n throw new Error(\"Max retries exceeded\");\n}\n\nexport async function submitReport(\n serverUrl: string,\n projectKey: string,\n payload: ReportPayload,\n screenshot?: File,\n): Promise<ReportResult> {\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports`;\n\n let body: BodyInit;\n const headers: Record<string, string> = {\n \"x-project-key\": projectKey,\n };\n\n if (screenshot) {\n const form = new FormData();\n form.append(\"reporterId\", payload.reporterId);\n form.append(\"reporterName\", payload.reporterName);\n if (payload.reporterEmail) form.append(\"reporterEmail\", payload.reporterEmail);\n form.append(\"description\", payload.description);\n if (payload.expectedBehavior) form.append(\"expectedBehavior\", payload.expectedBehavior);\n if (payload.stepsToReproduce) form.append(\"stepsToReproduce\", JSON.stringify(payload.stepsToReproduce));\n if (payload.externalReplayUrl) form.append(\"externalReplayUrl\", payload.externalReplayUrl);\n if (payload.additionalContext) form.append(\"additionalContext\", payload.additionalContext);\n form.append(\"severity\", payload.severity);\n if (payload.url) form.append(\"url\", payload.url);\n if (payload.meta) form.append(\"meta\", JSON.stringify(payload.meta));\n form.append(\"screenshot\", screenshot);\n body = form;\n } else {\n body = JSON.stringify(payload);\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const res = await fetchWithRetry(url, { method: \"POST\", headers, body });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: \"Unknown error\" }));\n throw new Error((err as { error?: string }).error ?? `HTTP ${res.status}`);\n }\n\n return res.json() as Promise<ReportResult>;\n}\n\nexport async function submitReplay(\n serverUrl: string,\n projectKey: string,\n reportId: string,\n events: unknown[],\n): Promise<void> {\n const json = JSON.stringify(events);\n const encoded = new TextEncoder().encode(json);\n const compressed = gzipSync(encoded);\n\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports/${reportId}/replay`;\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"x-project-key\": projectKey,\n \"Content-Type\": \"application/octet-stream\",\n },\n body: compressed.buffer as ArrayBuffer,\n });\n}\n","import type { ConsoleEntry } from \"../types.js\";\n\nconst MAX_ENTRIES = 50;\n\n// Redact values that look like tokens, passwords, secrets, or API keys\nconst SENSITIVE_PATTERNS = [\n /(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|authorization|bearer)\\s*[:=]\\s*[\"']?[^\\s\"',]{4,}/gi,\n /\\b(sk-[a-zA-Z0-9_-]{20,})\\b/g, // API keys (e.g. sk-ant-...)\n /\\b(ghp_[a-zA-Z0-9]{36,})\\b/g, // GitHub tokens\n /\\b(xoxb-[a-zA-Z0-9-]+)\\b/g, // Slack tokens\n /\\b(eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,})\\b/g, // JWTs\n];\n\nfunction sanitize(str: string): string {\n let result = str;\n for (const pattern of SENSITIVE_PATTERNS) {\n result = result.replace(pattern, \"[REDACTED]\");\n }\n return result;\n}\n\nexport interface ConsoleCollector {\n start(): void;\n stop(): void;\n getEntries(): ConsoleEntry[];\n}\n\nexport function createConsoleCollector(): ConsoleCollector {\n const entries: ConsoleEntry[] = [];\n let active = false;\n\n const originals = {\n log: console.log.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n };\n\n let origOnerror: typeof window.onerror = null;\n let origUnhandled: typeof window.onunhandledrejection = null;\n\n function push(level: ConsoleEntry[\"level\"], args: unknown[]) {\n let str: string;\n try {\n str = args.map((a) => (typeof a === \"string\" ? a : JSON.stringify(a))).join(\" \");\n } catch {\n str = String(args[0]);\n }\n if (str.length > 500) str = str.slice(0, 500) + \"\\u2026\";\n str = sanitize(str);\n entries.push({ level, args: str, timestamp: Date.now() });\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n console.log = (...args: unknown[]) => {\n push(\"log\", args);\n originals.log(...args);\n };\n console.warn = (...args: unknown[]) => {\n push(\"warn\", args);\n originals.warn(...args);\n };\n console.error = (...args: unknown[]) => {\n push(\"error\", args);\n originals.error(...args);\n };\n\n origOnerror = window.onerror;\n window.onerror = (msg, src, line, col, err) => {\n push(\"error\", [err?.message ?? String(msg), `${src}:${line}:${col}`]);\n if (typeof origOnerror === \"function\") return origOnerror(msg, src, line, col, err);\n return false;\n };\n\n origUnhandled = window.onunhandledrejection;\n window.onunhandledrejection = (event) => {\n const reason = event.reason instanceof Error ? event.reason.message : JSON.stringify(event.reason);\n push(\"error\", [\"UnhandledRejection:\", reason]);\n if (typeof origUnhandled === \"function\") origUnhandled.call(window, event);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n console.log = originals.log;\n console.warn = originals.warn;\n console.error = originals.error;\n window.onerror = origOnerror;\n window.onunhandledrejection = origUnhandled;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { EnvironmentInfo } from \"../types.js\";\n\nexport function collectEnvironment(): EnvironmentInfo {\n const ua = navigator.userAgent;\n\n // -- Browser --\n let browser = \"Unknown\";\n const chromeM = ua.match(/Chrome\\/(\\d+)/);\n const firefoxM = ua.match(/Firefox\\/(\\d+)/);\n const edgeM = ua.match(/Edg\\/(\\d+)/);\n const safariM = ua.match(/Version\\/(\\d+)/);\n const operaM = ua.match(/OPR\\/(\\d+)/);\n\n if (operaM) {\n browser = `Opera ${operaM[1]}`;\n } else if (edgeM) {\n browser = `Edge ${edgeM[1]}`;\n } else if (chromeM && !/Edg|OPR/.test(ua)) {\n browser = `Chrome ${chromeM[1]}`;\n } else if (firefoxM) {\n browser = `Firefox ${firefoxM[1]}`;\n } else if (safariM && /Safari\\//.test(ua)) {\n browser = `Safari ${safariM[1]}`;\n }\n\n // -- OS --\n let os = \"Unknown\";\n const macM = ua.match(/Mac OS X (\\d+[._]\\d+)/);\n const winM = ua.match(/Windows NT (\\d+\\.\\d+)/);\n const androidM = ua.match(/Android (\\d+)/);\n const iosM = ua.match(/iPhone OS (\\d+[._]\\d+)/);\n\n if (macM) {\n os = `macOS ${macM[1].replace(\"_\", \".\")}`;\n } else if (winM) {\n const winMap: Record<string, string> = {\n \"10.0\": \"10/11\",\n \"6.3\": \"8.1\",\n \"6.2\": \"8\",\n \"6.1\": \"7\",\n };\n os = `Windows ${winMap[winM[1]] ?? winM[1]}`;\n } else if (androidM) {\n os = `Android ${androidM[1]}`;\n } else if (iosM) {\n os = `iOS ${iosM[1].replace(/_/g, \".\")}`;\n } else if (/Linux/.test(ua)) {\n os = \"Linux\";\n }\n\n return {\n browser,\n os,\n viewport: `${window.innerWidth}x${window.innerHeight}`,\n screen: `${screen.width}x${screen.height}`,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n online: navigator.onLine,\n };\n}\n","import type { FormErrorEntry, FormErrorField } from \"../types.js\";\n\nconst MAX_ENTRIES = 30;\n\n/**\n * Time to wait after a submit event before checking the DOM for error\n * indicators. RHF/zod validate asynchronously — 300ms covers most cases\n * while keeping the detection snappy.\n */\nconst POST_SUBMIT_CHECK_MS = 300;\n\n/**\n * After a submit button click, if no submit event fires within this window\n * it means the form library swallowed the submit entirely (e.g. RHF's\n * handleSubmit called preventDefault before the native event propagated,\n * or the button isn't even inside a <form>). We treat this as a potential\n * silent failure and scan the page for error indicators.\n */\nconst SILENT_SUBMIT_WINDOW_MS = 400;\n\nexport interface FormErrorCollector {\n start(): void;\n stop(): void;\n getEntries(): FormErrorEntry[];\n /**\n * Manually report a form validation failure. Call this from your form\n * library's error handler to capture exact field-level errors.\n *\n * @example\n * // React Hook Form + Zod\n * <form onSubmit={handleSubmit(onValid, (errors) => {\n * flint.reportFormError('checkout-form', errors);\n * })}>\n */\n report(formId: string, errors: Record<string, { message?: string } | undefined>): void;\n}\n\n// ── DOM helpers ────────────────────────────────────────────────────────────────\n\nfunction getFormId(form: HTMLFormElement): string {\n if (form.id) return `#${form.id}`;\n if (form.getAttribute(\"name\")) return `form[name=\"${form.getAttribute(\"name\")}\"]`;\n if (form.action && form.action !== location.href) return form.action;\n return `form:${Array.from(document.forms).indexOf(form)}`;\n}\n\nfunction getFieldLabel(field: HTMLElement, root: HTMLElement): string {\n const ariaLabel = field.getAttribute(\"aria-label\");\n if (ariaLabel) return ariaLabel;\n\n const id = field.id;\n if (id) {\n const label = root.querySelector<HTMLLabelElement>(`label[for=\"${id}\"]`);\n if (label?.textContent) return label.textContent.trim();\n }\n\n const parentLabel = field.closest(\"label\");\n if (parentLabel?.textContent) {\n const text = parentLabel.textContent.trim();\n if (text.length < 60) return text;\n }\n\n return (field as HTMLInputElement).name || (field as HTMLInputElement).placeholder || field.tagName.toLowerCase();\n}\n\nfunction getErrorMessage(field: HTMLElement): string | undefined {\n // aria-errormessage\n const errId = field.getAttribute(\"aria-errormessage\");\n if (errId) {\n const el = document.getElementById(errId);\n if (el?.textContent) return el.textContent.trim();\n }\n\n // aria-describedby (RHF wires error messages here)\n const descId = field.getAttribute(\"aria-describedby\");\n if (descId) {\n for (const id of descId.split(/\\s+/)) {\n const el = document.getElementById(id);\n if (el?.textContent?.trim()) return el.textContent.trim();\n }\n }\n\n // native validationMessage\n if (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement) {\n if (field.validationMessage) return field.validationMessage;\n }\n\n // adjacent role=\"alert\"\n const next = field.nextElementSibling;\n if (next?.getAttribute(\"role\") === \"alert\" && next.textContent) {\n return next.textContent.trim();\n }\n\n return undefined;\n}\n\n/**\n * Collect fields with `aria-invalid=\"true\"` or native `:invalid` inside a\n * root element (a <form> or the whole document).\n */\nfunction collectInvalidFields(root: HTMLElement): FormErrorField[] {\n const fields: FormErrorField[] = [];\n const invalidEls = root.querySelectorAll<HTMLElement>('[aria-invalid=\"true\"], :invalid');\n\n for (const el of invalidEls) {\n if (el instanceof HTMLFormElement) continue;\n if (el.tagName === \"FIELDSET\") continue;\n\n fields.push({\n name: getFieldLabel(el, root),\n message: getErrorMessage(el),\n });\n }\n\n return fields;\n}\n\n/**\n * Scan the page for visible error indicators that appeared after a submit\n * attempt. This catches errors even when:\n * - The errored field is in another tab/step of a multi-step form\n * - The form doesn't use aria-invalid at all\n * - Errors are rendered as standalone alert divs\n */\nfunction collectVisibleErrors(root: HTMLElement): FormErrorField[] {\n const fields: FormErrorField[] = [];\n const seen = new Set<string>();\n\n // 1. role=\"alert\" elements that appeared (error toasts, inline messages)\n const alerts = root.querySelectorAll<HTMLElement>('[role=\"alert\"]');\n for (const el of alerts) {\n const text = el.textContent?.trim();\n if (!text || text.length > 200) continue;\n if (seen.has(text)) continue;\n seen.add(text);\n fields.push({ name: \"alert\", message: text });\n }\n\n // 2. Common error class patterns (data-error, .error, .field-error, etc.)\n const errorEls = root.querySelectorAll<HTMLElement>(\n \"[data-error], [data-field-error], .error-message, .field-error, .form-error\",\n );\n for (const el of errorEls) {\n const text = el.textContent?.trim();\n if (!text || text.length > 200) continue;\n if (seen.has(text)) continue;\n seen.add(text);\n fields.push({ name: \"error\", message: text });\n }\n\n return fields;\n}\n\nfunction isSubmitButton(el: HTMLElement): el is HTMLButtonElement {\n if (el.tagName === \"BUTTON\") {\n const type = el.getAttribute(\"type\");\n // Buttons default to type=\"submit\" inside forms when no type is set\n return type === \"submit\" || type === null || type === \"\";\n }\n if (el.tagName === \"INPUT\") {\n return (el as HTMLInputElement).type === \"submit\";\n }\n return false;\n}\n\nfunction findClosestForm(el: HTMLElement): HTMLFormElement | null {\n // Direct form association\n if (\"form\" in el && (el as HTMLButtonElement).form) {\n return (el as HTMLButtonElement).form;\n }\n return el.closest(\"form\");\n}\n\n// ── Collector ──────────────────────────────────────────────────────────────────\n\nexport function createFormErrorCollector(): FormErrorCollector {\n const entries: FormErrorEntry[] = [];\n const attemptCounts = new Map<string, number>();\n let active = false;\n\n // Handlers we need to clean up\n let submitHandler: ((e: Event) => void) | null = null;\n let invalidHandler: ((e: Event) => void) | null = null;\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n\n // Track whether a real submit event followed a button click\n let lastSubmitTime = 0;\n\n // Dedup: avoid recording the same form twice within a short window\n const recentRecords = new Map<string, number>();\n const DEDUP_MS = 500;\n\n function push(entry: FormErrorEntry) {\n // Dedup check\n const key = `${entry.formId}:${entry.timestamp}`;\n const last = recentRecords.get(entry.formId);\n if (last && entry.timestamp - last < DEDUP_MS) return;\n recentRecords.set(entry.formId, entry.timestamp);\n\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n function recordFormErrors(form: HTMLFormElement, type: FormErrorEntry[\"type\"]) {\n const formId = getFormId(form);\n\n // Strategy 1: aria-invalid / :invalid fields\n let fields = collectInvalidFields(form);\n\n // Strategy 2: if no aria-invalid fields found, look for visible error messages\n if (fields.length === 0) {\n fields = collectVisibleErrors(form);\n }\n\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type,\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Handle clicks on submit buttons. If no submit event follows within\n * SILENT_SUBMIT_WINDOW_MS, it means the form library blocked the submit\n * entirely — scan for errors.\n */\n function handleSubmitButtonClick(button: HTMLElement) {\n const form = findClosestForm(button);\n\n setTimeout(() => {\n if (!active) return;\n // A real submit event fired — the submitHandler already took care of it\n if (Date.now() - lastSubmitTime < SILENT_SUBMIT_WINDOW_MS) return;\n\n // No submit event → silent failure\n const root = form ?? (document.body as HTMLElement);\n const formId = form ? getFormId(form) : `page:${location.pathname}`;\n\n let fields = form ? collectInvalidFields(form) : [];\n if (fields.length === 0) {\n fields = collectVisibleErrors(root);\n }\n\n // Also scan full page — the error might be in a toast or outside the form\n if (fields.length === 0 && form) {\n fields = collectVisibleErrors(document.body);\n }\n\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type: \"silent_submit\",\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n }, SILENT_SUBMIT_WINDOW_MS);\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // 1. Capture submit events (fires even with e.preventDefault())\n submitHandler = (e: Event) => {\n lastSubmitTime = Date.now();\n const form = e.target as HTMLFormElement;\n if (!(form instanceof HTMLFormElement)) return;\n\n setTimeout(() => {\n if (!active) return;\n recordFormErrors(form, \"validation_failed\");\n }, POST_SUBMIT_CHECK_MS);\n };\n document.addEventListener(\"submit\", submitHandler, true);\n\n // 2. Capture native invalid events (HTML5 constraint validation)\n invalidHandler = (e: Event) => {\n lastSubmitTime = Date.now();\n const field = e.target as HTMLElement;\n const form = field.closest(\"form\");\n if (!form) return;\n\n setTimeout(() => {\n if (!active) return;\n recordFormErrors(form, \"validation_failed\");\n }, POST_SUBMIT_CHECK_MS);\n };\n document.addEventListener(\"invalid\", invalidHandler, true);\n\n // 3. Capture clicks on submit buttons — catches silent failures\n // where the form lib eats the submit and shows nothing\n clickHandler = (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n if (!target) return;\n\n // Walk up to find the actual button (click might be on an icon/span inside)\n const button = target.closest(\"button, input[type=submit]\") as HTMLElement | null;\n if (!button) return;\n if (!isSubmitButton(button)) return;\n\n handleSubmitButtonClick(button);\n };\n document.addEventListener(\"click\", clickHandler, true);\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (submitHandler) document.removeEventListener(\"submit\", submitHandler, true);\n if (invalidHandler) document.removeEventListener(\"invalid\", invalidHandler, true);\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n attemptCounts.clear();\n recentRecords.clear();\n },\n\n getEntries() {\n return [...entries];\n },\n\n report(formId: string, errors: Record<string, { message?: string } | undefined>) {\n const fields: FormErrorField[] = [];\n for (const [name, err] of Object.entries(errors)) {\n if (!err) continue;\n fields.push({ name, message: err.message });\n }\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type: \"validation_failed\",\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n },\n };\n}\n","export interface FrustrationEvent {\n type: \"rage_click\" | \"dead_click\" | \"error_loop\";\n timestamp: number;\n target: string;\n details: string;\n url: string;\n}\n\nexport interface FrustrationCollector {\n start(): void;\n stop(): void;\n getEvents(): FrustrationEvent[];\n onFrustration(callback: (event: FrustrationEvent) => void): () => void;\n}\n\nexport function createFrustrationCollector(opts?: {\n rageClickThreshold?: number;\n rageClickWindow?: number;\n errorLoopThreshold?: number;\n errorLoopWindow?: number;\n enableDeadClicks?: boolean;\n}): FrustrationCollector {\n const threshold = opts?.rageClickThreshold ?? 3;\n const window = opts?.rageClickWindow ?? 500;\n const errorThreshold = opts?.errorLoopThreshold ?? 3;\n const errorWindow = opts?.errorLoopWindow ?? 30_000;\n const deadClicksEnabled = opts?.enableDeadClicks ?? true;\n\n const events: FrustrationEvent[] = [];\n const listeners = new Set<(event: FrustrationEvent) => void>();\n const clickHistory: { target: EventTarget | null; time: number }[] = [];\n const errorCounts = new Map<string, { count: number; firstSeen: number }>();\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n let origConsoleError: typeof console.error | null = null;\n\n function emit(event: FrustrationEvent) {\n events.push(event);\n if (events.length > 50) events.shift();\n for (const cb of listeners) cb(event);\n }\n\n function getCSSSelector(el: Element): string {\n if (el.id) return `#${el.id}`;\n const tag = el.tagName.toLowerCase();\n const cls = [...el.classList].slice(0, 3).join(\".\");\n if (cls) return `${tag}.${cls}`;\n return tag;\n }\n\n function isInteractive(el: HTMLElement): boolean {\n const tag = el.tagName.toLowerCase();\n if ([\"a\", \"button\", \"input\", \"select\", \"textarea\", \"label\", \"summary\"].includes(tag)) return true;\n if (el.getAttribute(\"role\") === \"button\" || el.getAttribute(\"tabindex\")) return true;\n if (el.onclick || el.getAttribute(\"onclick\")) return true;\n if (el.closest(\"a, button, [role=button], [onclick]\")) return true;\n try {\n if (getComputedStyle(el).cursor === \"pointer\") return true;\n } catch {\n /* getComputedStyle may throw */\n }\n return false;\n }\n\n function handleClick(e: MouseEvent) {\n const target = e.target as HTMLElement;\n if (!target) return;\n const now = Date.now();\n\n // --- Rage click detection ---\n clickHistory.push({ target, time: now });\n // Remove old clicks\n while (clickHistory.length > 0 && now - clickHistory[0].time > 1000) {\n clickHistory.shift();\n }\n const recentOnSame = clickHistory.filter((c) => c.target === target && now - c.time < window);\n if (recentOnSame.length >= threshold) {\n emit({\n type: \"rage_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `${recentOnSame.length} clicks in ${now - recentOnSame[0].time}ms`,\n url: globalThis.location?.href ?? \"\",\n });\n clickHistory.length = 0;\n }\n\n // --- Dead click detection ---\n if (deadClicksEnabled && !isInteractive(target)) {\n emit({\n type: \"dead_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `Click on non-interactive <${target.tagName.toLowerCase()}>`,\n url: globalThis.location?.href ?? \"\",\n });\n }\n }\n\n function patchConsoleError() {\n origConsoleError = console.error;\n console.error = (...args: unknown[]) => {\n origConsoleError?.apply(console, args);\n // Check for error loop\n const key = String(args[0]).slice(0, 100);\n const now = Date.now();\n const existing = errorCounts.get(key);\n if (existing && now - existing.firstSeen < errorWindow) {\n existing.count++;\n if (existing.count >= errorThreshold) {\n emit({\n type: \"error_loop\",\n timestamp: now,\n target: \"console\",\n details: `Same error ${existing.count}x in ${Math.round((now - existing.firstSeen) / 1000)}s: ${key}`,\n url: globalThis.location?.href ?? \"\",\n });\n errorCounts.delete(key);\n }\n } else {\n errorCounts.set(key, { count: 1, firstSeen: now });\n }\n };\n }\n\n return {\n start() {\n clickHandler = handleClick;\n document.addEventListener(\"click\", clickHandler, true);\n patchConsoleError();\n },\n stop() {\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n if (origConsoleError) console.error = origConsoleError;\n clickHistory.length = 0;\n errorCounts.clear();\n },\n getEvents() {\n return [...events];\n },\n onFrustration(callback) {\n listeners.add(callback);\n return () => listeners.delete(callback);\n },\n };\n}\n","import type { NetworkEntry } from \"../types.js\";\n\nconst MAX_ENTRIES = 50;\n\nconst BLOCKED_HOSTS = new Set([\n \"browser-intake-datadoghq.com\",\n \"rum.browser-intake-datadoghq.com\",\n \"logs.browser-intake-datadoghq.com\",\n \"session-replay.browser-intake-datadoghq.com\",\n]);\n\nfunction isBlockedUrl(url: string, extra: Set<string>): boolean {\n try {\n const host = new URL(url, location.href).hostname;\n const all = [...BLOCKED_HOSTS, ...extra];\n return all.some((b) => host === b || host.endsWith(\".\" + b));\n } catch {\n return false;\n }\n}\n\nexport interface NetworkCollector {\n start(): void;\n stop(): void;\n getEntries(): NetworkEntry[];\n}\n\nfunction truncateUrl(url: string): string {\n try {\n const u = new URL(url, location.href);\n const base = `${u.origin}${u.pathname}`;\n return base.length > 200 ? base.slice(0, 200) + \"\\u2026\" : base;\n } catch {\n return url.length > 200 ? url.slice(0, 200) + \"\\u2026\" : url;\n }\n}\n\nexport function createNetworkCollector(extraBlockedHosts: string[] = []): NetworkCollector {\n const entries: NetworkEntry[] = [];\n const blocked = new Set(extraBlockedHosts);\n let origFetch: typeof window.fetch | null = null;\n let origXHROpen: typeof XMLHttpRequest.prototype.open | null = null;\n let active = false;\n\n function push(entry: NetworkEntry) {\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // -- Patch fetch --\n origFetch = window.fetch;\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const method = ((init?.method ?? \"GET\") as string).toUpperCase();\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : (input as Request).url;\n const startTime = Date.now();\n const res = await origFetch!.call(window, input, init);\n if (res.status >= 400 && !isBlockedUrl(url, blocked)) {\n push({\n method,\n url: truncateUrl(url),\n status: res.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n return res;\n };\n\n // -- Patch XHR --\n origXHROpen = XMLHttpRequest.prototype.open;\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL,\n async?: boolean,\n username?: string | null,\n password?: string | null,\n ) {\n const startTime = Date.now();\n const urlStr = typeof url === \"string\" ? url : (url as URL).href;\n\n this.addEventListener(\"load\", () => {\n if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {\n push({\n method: method.toUpperCase(),\n url: truncateUrl(urlStr),\n status: this.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n });\n\n return origXHROpen!.apply(this, [method, url, async ?? true, username, password] as Parameters<\n typeof XMLHttpRequest.prototype.open\n >);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (origFetch) window.fetch = origFetch;\n if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { FormErrorCollector } from \"./collectors/formErrors.js\";\nimport type { FlintUser } from \"./types.js\";\n\nexport interface FlintState {\n user: FlintUser | undefined;\n sessionReplay: string | (() => string) | undefined;\n}\n\n// Holds a reference to the active form error collector so flint.reportFormError\n// can forward calls without the consumer needing to manage the collector.\nlet formErrorCollectorRef: FormErrorCollector | null = null;\n\nexport function _setFormErrorCollector(collector: FormErrorCollector | null) {\n formErrorCollectorRef = collector;\n}\n\nlet state: FlintState = { user: undefined, sessionReplay: undefined };\nconst listeners = new Set<() => void>();\n\nfunction emit() {\n for (const l of listeners) l();\n}\n\nexport function subscribe(listener: () => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getSnapshot(): FlintState {\n return state;\n}\n\nexport const flint = {\n setUser(user: FlintUser | null) {\n state = { ...state, user: user ?? undefined };\n emit();\n },\n setSessionReplay(url: string | (() => string) | null) {\n state = { ...state, sessionReplay: url ?? undefined };\n emit();\n },\n\n /**\n * Report a form validation failure with exact field-level errors.\n * Call this from your form library's error callback.\n *\n * @example\n * // React Hook Form + Zod\n * const onSubmit = handleSubmit(onValid, (errors) => {\n * flint.reportFormError('checkout-form', errors);\n * });\n */\n reportFormError(formId: string, errors: Record<string, { message?: string } | undefined>) {\n formErrorCollectorRef?.report(formId, errors);\n },\n};\n","import { submitReport } from \"./api.js\";\nimport type { ConsoleCollector } from \"./collectors/console.js\";\nimport { createConsoleCollector } from \"./collectors/console.js\";\nimport { collectEnvironment } from \"./collectors/environment.js\";\nimport type { FormErrorCollector } from \"./collectors/formErrors.js\";\nimport { createFormErrorCollector } from \"./collectors/formErrors.js\";\nimport type { FrustrationCollector } from \"./collectors/frustration.js\";\nimport { createFrustrationCollector } from \"./collectors/frustration.js\";\nimport type { NetworkCollector } from \"./collectors/network.js\";\nimport { createNetworkCollector } from \"./collectors/network.js\";\nimport { _setFormErrorCollector, flint, getSnapshot } from \"./store.js\";\nimport type { CollectedMeta, FlintConfig } from \"./types.js\";\n\nconst DEFAULT_REPLAY_BUFFER_MS = 60_000;\n\ninterface FlintInstance {\n config: Readonly<FlintConfig>;\n console: ConsoleCollector | null;\n network: NetworkCollector | null;\n formErrors: FormErrorCollector | null;\n frustration: FrustrationCollector | null;\n replayEvents: unknown[];\n stopReplay: (() => void) | null;\n}\n\nlet instance: FlintInstance | null = null;\n\nfunction init(config: FlintConfig): void {\n if (instance) {\n console.warn(\"[Flint] Already initialized. Call Flint.shutdown() first to re-initialize.\");\n return;\n }\n\n const {\n enableConsole = true,\n enableNetwork = true,\n enableFormErrors = true,\n enableFrustration = false,\n autoReportFrustration = false,\n enableReplay = false,\n replayBufferMs = DEFAULT_REPLAY_BUFFER_MS,\n blockedHosts = [],\n frustration: frustrationOpts,\n onFrustration,\n _replayRecorder,\n } = config;\n\n // Determine Flint server host to block from network capture\n const flintHost = (() => {\n try {\n return new URL(config.serverUrl).hostname;\n } catch {\n return \"\";\n }\n })();\n\n // Create and start collectors\n const consoleCol = enableConsole ? createConsoleCollector() : null;\n consoleCol?.start();\n\n const networkCol = enableNetwork\n ? createNetworkCollector([...blockedHosts, ...(flintHost ? [flintHost] : [])])\n : null;\n networkCol?.start();\n\n const formErrorsCol = enableFormErrors ? createFormErrorCollector() : null;\n if (formErrorsCol) {\n formErrorsCol.start();\n _setFormErrorCollector(formErrorsCol);\n }\n\n const frustrationCol = enableFrustration ? createFrustrationCollector(frustrationOpts) : null;\n frustrationCol?.start();\n\n // Set user if provided\n if (config.user) {\n flint.setUser(config.user);\n }\n\n const replayEvents: unknown[] = [];\n let stopReplay: (() => void) | null = null;\n\n instance = {\n config,\n console: consoleCol,\n network: networkCol,\n formErrors: formErrorsCol,\n frustration: frustrationCol,\n replayEvents,\n stopReplay: null,\n };\n\n // Start replay recording (async, via injected recorder)\n if (enableReplay && _replayRecorder) {\n _replayRecorder((event: unknown) => {\n replayEvents.push(event);\n const cutoff = Date.now() - replayBufferMs;\n while (replayEvents.length > 0 && (replayEvents[0] as { timestamp: number }).timestamp < cutoff) {\n replayEvents.shift();\n }\n }).then((stop) => {\n stopReplay = stop ?? null;\n if (instance) instance.stopReplay = stopReplay;\n });\n }\n\n // Wire frustration auto-report\n if (frustrationCol && autoReportFrustration) {\n frustrationCol.onFrustration(async (event) => {\n onFrustration?.(event);\n const user = getSnapshot().user ?? config.user;\n await submitReport(config.serverUrl, config.projectKey, {\n reporterId: user?.id ?? \"anonymous\",\n reporterName: user?.name ?? \"Anonymous\",\n reporterEmail: user?.email,\n description: `[Auto-detected] ${event.type.replace(/_/g, \" \")}: ${event.details}`,\n severity: event.type === \"error_loop\" ? \"P1\" : event.type === \"rage_click\" ? \"P2\" : \"P3\",\n url: event.url,\n meta: {\n ...config.meta,\n environment: collectEnvironment(),\n consoleLogs: consoleCol?.getEntries() ?? [],\n networkErrors: networkCol?.getEntries() ?? [],\n formErrors: formErrorsCol?.getEntries() ?? [],\n frustrationEvent: event,\n },\n }).catch(() => {});\n });\n } else if (frustrationCol && onFrustration) {\n frustrationCol.onFrustration(onFrustration);\n }\n}\n\nfunction shutdown(): void {\n if (!instance) return;\n\n instance.console?.stop();\n instance.network?.stop();\n instance.formErrors?.stop();\n _setFormErrorCollector(null);\n instance.frustration?.stop();\n instance.stopReplay?.();\n instance = null;\n}\n\nfunction isInitialized(): boolean {\n return instance !== null;\n}\n\nfunction getInstance(): FlintInstance | null {\n return instance;\n}\n\nfunction getMeta(extraMeta?: Record<string, unknown>): CollectedMeta {\n return {\n ...extraMeta,\n environment: collectEnvironment(),\n consoleLogs: instance?.console?.getEntries() ?? [],\n networkErrors: instance?.network?.getEntries() ?? [],\n formErrors: instance?.formErrors?.getEntries() ?? [],\n };\n}\n\nfunction getReplayEvents(): unknown[] {\n return instance ? [...instance.replayEvents] : [];\n}\n\nfunction getConfig(): Readonly<FlintConfig> | null {\n return instance?.config ?? null;\n}\n\nexport const Flint = {\n init,\n shutdown,\n isInitialized,\n getInstance,\n getMeta,\n getReplayEvents,\n getConfig,\n setUser: flint.setUser,\n setSessionReplay: flint.setSessionReplay,\n reportFormError: flint.reportFormError,\n};\n","import type { Theme, ThemeOverride } from \"./types.js\";\n\nexport interface ResolvedTheme {\n background: string;\n backgroundSecondary: string;\n accent: string;\n accentHover: string;\n text: string;\n textMuted: string;\n border: string;\n shadow: string;\n buttonText: string;\n backdropFilter: string;\n}\n\nconst light: ResolvedTheme = {\n background: \"rgba(255,255,255,0.90)\",\n backgroundSecondary: \"rgba(249,250,251,0.75)\",\n accent: \"#2563eb\",\n accentHover: \"#1d4ed8\",\n text: \"#111827\",\n textMuted: \"#6b7280\",\n border: \"rgba(255,255,255,0.9)\",\n shadow: \"0 32px 80px rgba(0,0,0,0.18), 0 8px 32px rgba(0,0,0,0.1), 0 0 0 1px rgba(0,0,0,0.04)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.8)\",\n};\n\nconst dark: ResolvedTheme = {\n background: \"rgba(15,20,35,0.88)\",\n backgroundSecondary: \"rgba(5,8,18,0.65)\",\n accent: \"#4d8aff\",\n accentHover: \"#3b6fdb\",\n text: \"#dde3ef\",\n textMuted: \"#6b7a93\",\n border: \"rgba(255,255,255,0.08)\",\n shadow: \"0 24px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.6)\",\n};\n\nexport function resolveTheme(theme: Theme): ResolvedTheme {\n if (theme === \"dark\") return dark;\n if (theme === \"light\") return light;\n // ThemeOverride — merge over light base\n const override = theme as ThemeOverride;\n return {\n ...light,\n background: override.background ?? light.background,\n accent: override.accent ?? light.accent,\n accentHover: override.accent ?? light.accentHover,\n text: override.text ?? light.text,\n border: override.border ?? light.border,\n };\n}\n"],"mappings":"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","names":["init","MAX_ENTRIES","window","listeners","emit","MAX_ENTRIES","init"]}
|
|
1
|
+
{"version":3,"sources":["../src/api.ts","../src/collectors/console.ts","../src/collectors/environment.ts","../src/collectors/formErrors.ts","../src/collectors/frustration.ts","../src/collectors/network.ts","../src/store.ts","../src/singleton.ts","../src/theme.ts"],"sourcesContent":["import { gzipSync } from \"fflate\";\nimport type { ReportPayload, ReportResult } from \"./types.js\";\n\nasync function fetchWithRetry(url: string, init: RequestInit, retries = 3, baseDelay = 1000): Promise<Response> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n const res = await fetch(url, init);\n if (res.ok || attempt === retries) return res;\n // Don't retry client errors (4xx) except 429\n if (res.status >= 400 && res.status < 500 && res.status !== 429) return res;\n } catch (err) {\n if (attempt === retries) throw err;\n }\n await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));\n }\n throw new Error(\"Max retries exceeded\");\n}\n\nexport async function submitReport(\n serverUrl: string,\n projectKey: string,\n payload: ReportPayload,\n screenshot?: File,\n): Promise<ReportResult> {\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports`;\n\n let body: BodyInit;\n const headers: Record<string, string> = {\n \"x-project-key\": projectKey,\n };\n\n if (screenshot) {\n const form = new FormData();\n form.append(\"reporterId\", payload.reporterId);\n form.append(\"reporterName\", payload.reporterName);\n if (payload.reporterEmail) form.append(\"reporterEmail\", payload.reporterEmail);\n form.append(\"description\", payload.description);\n if (payload.expectedBehavior) form.append(\"expectedBehavior\", payload.expectedBehavior);\n if (payload.stepsToReproduce) form.append(\"stepsToReproduce\", JSON.stringify(payload.stepsToReproduce));\n if (payload.externalReplayUrl) form.append(\"externalReplayUrl\", payload.externalReplayUrl);\n if (payload.additionalContext) form.append(\"additionalContext\", payload.additionalContext);\n form.append(\"severity\", payload.severity);\n if (payload.url) form.append(\"url\", payload.url);\n if (payload.meta) form.append(\"meta\", JSON.stringify(payload.meta));\n form.append(\"screenshot\", screenshot);\n body = form;\n } else {\n body = JSON.stringify(payload);\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const res = await fetchWithRetry(url, { method: \"POST\", headers, body });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: \"Unknown error\" }));\n throw new Error((err as { error?: string }).error ?? `HTTP ${res.status}`);\n }\n\n return res.json() as Promise<ReportResult>;\n}\n\nexport async function submitReplay(\n serverUrl: string,\n projectKey: string,\n reportId: string,\n events: unknown[],\n): Promise<void> {\n const json = JSON.stringify(events);\n const encoded = new TextEncoder().encode(json);\n const compressed = gzipSync(encoded);\n\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports/${reportId}/replay`;\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"x-project-key\": projectKey,\n \"Content-Type\": \"application/octet-stream\",\n },\n body: compressed.buffer as ArrayBuffer,\n });\n}\n","import type { ConsoleEntry } from \"../types.js\";\n\nconst MAX_ENTRIES = 50;\n\n// Redact values that look like tokens, passwords, secrets, or API keys\nconst SENSITIVE_PATTERNS = [\n /(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|authorization|bearer)\\s*[:=]\\s*[\"']?[^\\s\"',]{4,}/gi,\n /\\b(sk-[a-zA-Z0-9_-]{20,})\\b/g, // API keys (e.g. sk-ant-...)\n /\\b(ghp_[a-zA-Z0-9]{36,})\\b/g, // GitHub tokens\n /\\b(xoxb-[a-zA-Z0-9-]+)\\b/g, // Slack tokens\n /\\b(eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,})\\b/g, // JWTs\n];\n\nfunction sanitize(str: string): string {\n let result = str;\n for (const pattern of SENSITIVE_PATTERNS) {\n result = result.replace(pattern, \"[REDACTED]\");\n }\n return result;\n}\n\nexport interface ConsoleCollector {\n start(): void;\n stop(): void;\n getEntries(): ConsoleEntry[];\n}\n\nexport function createConsoleCollector(): ConsoleCollector {\n const entries: ConsoleEntry[] = [];\n let active = false;\n\n const originals = {\n log: console.log.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n };\n\n let origOnerror: typeof window.onerror = null;\n let origUnhandled: typeof window.onunhandledrejection = null;\n\n function push(level: ConsoleEntry[\"level\"], args: unknown[]) {\n let str: string;\n try {\n str = args.map((a) => (typeof a === \"string\" ? a : JSON.stringify(a))).join(\" \");\n } catch {\n str = String(args[0]);\n }\n if (str.length > 500) str = str.slice(0, 500) + \"\\u2026\";\n str = sanitize(str);\n entries.push({ level, args: str, timestamp: Date.now() });\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n console.log = (...args: unknown[]) => {\n push(\"log\", args);\n originals.log(...args);\n };\n console.warn = (...args: unknown[]) => {\n push(\"warn\", args);\n originals.warn(...args);\n };\n console.error = (...args: unknown[]) => {\n push(\"error\", args);\n originals.error(...args);\n };\n\n origOnerror = window.onerror;\n window.onerror = (msg, src, line, col, err) => {\n push(\"error\", [err?.message ?? String(msg), `${src}:${line}:${col}`]);\n if (typeof origOnerror === \"function\") return origOnerror(msg, src, line, col, err);\n return false;\n };\n\n origUnhandled = window.onunhandledrejection;\n window.onunhandledrejection = (event) => {\n const reason = event.reason instanceof Error ? event.reason.message : JSON.stringify(event.reason);\n push(\"error\", [\"UnhandledRejection:\", reason]);\n if (typeof origUnhandled === \"function\") origUnhandled.call(window, event);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n console.log = originals.log;\n console.warn = originals.warn;\n console.error = originals.error;\n window.onerror = origOnerror;\n window.onunhandledrejection = origUnhandled;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { EnvironmentInfo } from \"../types.js\";\n\nexport function collectEnvironment(): EnvironmentInfo {\n const ua = navigator.userAgent;\n\n // -- Browser --\n let browser = \"Unknown\";\n const chromeM = ua.match(/Chrome\\/(\\d+)/);\n const firefoxM = ua.match(/Firefox\\/(\\d+)/);\n const edgeM = ua.match(/Edg\\/(\\d+)/);\n const safariM = ua.match(/Version\\/(\\d+)/);\n const operaM = ua.match(/OPR\\/(\\d+)/);\n\n if (operaM) {\n browser = `Opera ${operaM[1]}`;\n } else if (edgeM) {\n browser = `Edge ${edgeM[1]}`;\n } else if (chromeM && !/Edg|OPR/.test(ua)) {\n browser = `Chrome ${chromeM[1]}`;\n } else if (firefoxM) {\n browser = `Firefox ${firefoxM[1]}`;\n } else if (safariM && /Safari\\//.test(ua)) {\n browser = `Safari ${safariM[1]}`;\n }\n\n // -- OS --\n let os = \"Unknown\";\n const macM = ua.match(/Mac OS X (\\d+[._]\\d+)/);\n const winM = ua.match(/Windows NT (\\d+\\.\\d+)/);\n const androidM = ua.match(/Android (\\d+)/);\n const iosM = ua.match(/iPhone OS (\\d+[._]\\d+)/);\n\n if (macM) {\n os = `macOS ${macM[1].replace(\"_\", \".\")}`;\n } else if (winM) {\n const winMap: Record<string, string> = {\n \"10.0\": \"10/11\",\n \"6.3\": \"8.1\",\n \"6.2\": \"8\",\n \"6.1\": \"7\",\n };\n os = `Windows ${winMap[winM[1]] ?? winM[1]}`;\n } else if (androidM) {\n os = `Android ${androidM[1]}`;\n } else if (iosM) {\n os = `iOS ${iosM[1].replace(/_/g, \".\")}`;\n } else if (/Linux/.test(ua)) {\n os = \"Linux\";\n }\n\n return {\n browser,\n os,\n viewport: `${window.innerWidth}x${window.innerHeight}`,\n screen: `${screen.width}x${screen.height}`,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n online: navigator.onLine,\n };\n}\n","import type { FormErrorEntry, FormErrorField } from \"../types.js\";\n\nconst MAX_ENTRIES = 30;\n\n/**\n * Time to wait after a submit event before checking the DOM for error\n * indicators. RHF/zod validate asynchronously — 300ms covers most cases\n * while keeping the detection snappy.\n */\nconst POST_SUBMIT_CHECK_MS = 300;\n\n/**\n * After a submit button click, if no submit event fires within this window\n * it means the form library swallowed the submit entirely (e.g. RHF's\n * handleSubmit called preventDefault before the native event propagated,\n * or the button isn't even inside a <form>). We treat this as a potential\n * silent failure and scan the page for error indicators.\n */\nconst SILENT_SUBMIT_WINDOW_MS = 400;\n\nexport interface FormErrorCollector {\n start(): void;\n stop(): void;\n getEntries(): FormErrorEntry[];\n /**\n * Manually report a form validation failure. Call this from your form\n * library's error handler to capture exact field-level errors.\n *\n * @example\n * // React Hook Form + Zod\n * <form onSubmit={handleSubmit(onValid, (errors) => {\n * flint.reportFormError('checkout-form', errors);\n * })}>\n */\n report(formId: string, errors: Record<string, { message?: string } | undefined>): void;\n}\n\n// ── DOM helpers ────────────────────────────────────────────────────────────────\n\nfunction getFormId(form: HTMLFormElement): string {\n if (form.id) return `#${form.id}`;\n if (form.getAttribute(\"name\")) return `form[name=\"${form.getAttribute(\"name\")}\"]`;\n if (form.action && form.action !== location.href) return form.action;\n return `form:${Array.from(document.forms).indexOf(form)}`;\n}\n\nfunction getFieldLabel(field: HTMLElement, root: HTMLElement): string {\n const ariaLabel = field.getAttribute(\"aria-label\");\n if (ariaLabel) return ariaLabel;\n\n const id = field.id;\n if (id) {\n const label = root.querySelector<HTMLLabelElement>(`label[for=\"${id}\"]`);\n if (label?.textContent) return label.textContent.trim();\n }\n\n const parentLabel = field.closest(\"label\");\n if (parentLabel?.textContent) {\n const text = parentLabel.textContent.trim();\n if (text.length < 60) return text;\n }\n\n return (field as HTMLInputElement).name || (field as HTMLInputElement).placeholder || field.tagName.toLowerCase();\n}\n\nfunction getErrorMessage(field: HTMLElement): string | undefined {\n // aria-errormessage\n const errId = field.getAttribute(\"aria-errormessage\");\n if (errId) {\n const el = document.getElementById(errId);\n if (el?.textContent) return el.textContent.trim();\n }\n\n // aria-describedby (RHF wires error messages here)\n const descId = field.getAttribute(\"aria-describedby\");\n if (descId) {\n for (const id of descId.split(/\\s+/)) {\n const el = document.getElementById(id);\n if (el?.textContent?.trim()) return el.textContent.trim();\n }\n }\n\n // native validationMessage\n if (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement) {\n if (field.validationMessage) return field.validationMessage;\n }\n\n // adjacent role=\"alert\"\n const next = field.nextElementSibling;\n if (next?.getAttribute(\"role\") === \"alert\" && next.textContent) {\n return next.textContent.trim();\n }\n\n return undefined;\n}\n\n/**\n * Collect fields with `aria-invalid=\"true\"` or native `:invalid` inside a\n * root element (a <form> or the whole document).\n */\nfunction collectInvalidFields(root: HTMLElement): FormErrorField[] {\n const fields: FormErrorField[] = [];\n const invalidEls = root.querySelectorAll<HTMLElement>('[aria-invalid=\"true\"], :invalid');\n\n for (const el of invalidEls) {\n if (el instanceof HTMLFormElement) continue;\n if (el.tagName === \"FIELDSET\") continue;\n\n fields.push({\n name: getFieldLabel(el, root),\n message: getErrorMessage(el),\n });\n }\n\n return fields;\n}\n\n/**\n * Scan the page for visible error indicators that appeared after a submit\n * attempt. This catches errors even when:\n * - The errored field is in another tab/step of a multi-step form\n * - The form doesn't use aria-invalid at all\n * - Errors are rendered as standalone alert divs\n */\nfunction collectVisibleErrors(root: HTMLElement): FormErrorField[] {\n const fields: FormErrorField[] = [];\n const seen = new Set<string>();\n\n // 1. role=\"alert\" elements that appeared (error toasts, inline messages)\n const alerts = root.querySelectorAll<HTMLElement>('[role=\"alert\"]');\n for (const el of alerts) {\n const text = el.textContent?.trim();\n if (!text || text.length > 200) continue;\n if (seen.has(text)) continue;\n seen.add(text);\n fields.push({ name: \"alert\", message: text });\n }\n\n // 2. Common error class patterns (data-error, .error, .field-error, etc.)\n const errorEls = root.querySelectorAll<HTMLElement>(\n \"[data-error], [data-field-error], .error-message, .field-error, .form-error\",\n );\n for (const el of errorEls) {\n const text = el.textContent?.trim();\n if (!text || text.length > 200) continue;\n if (seen.has(text)) continue;\n seen.add(text);\n fields.push({ name: \"error\", message: text });\n }\n\n return fields;\n}\n\nfunction isSubmitButton(el: HTMLElement): el is HTMLButtonElement {\n if (el.tagName === \"BUTTON\") {\n const type = el.getAttribute(\"type\");\n // Buttons default to type=\"submit\" inside forms when no type is set\n return type === \"submit\" || type === null || type === \"\";\n }\n if (el.tagName === \"INPUT\") {\n return (el as HTMLInputElement).type === \"submit\";\n }\n return false;\n}\n\nfunction findClosestForm(el: HTMLElement): HTMLFormElement | null {\n // Direct form association\n if (\"form\" in el && (el as HTMLButtonElement).form) {\n return (el as HTMLButtonElement).form;\n }\n return el.closest(\"form\");\n}\n\n// ── Collector ──────────────────────────────────────────────────────────────────\n\nexport function createFormErrorCollector(): FormErrorCollector {\n const entries: FormErrorEntry[] = [];\n const attemptCounts = new Map<string, number>();\n let active = false;\n\n // Handlers we need to clean up\n let submitHandler: ((e: Event) => void) | null = null;\n let invalidHandler: ((e: Event) => void) | null = null;\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n\n // Track whether a real submit event followed a button click\n let lastSubmitTime = 0;\n\n // Dedup: avoid recording the same form twice within a short window\n const recentRecords = new Map<string, number>();\n const DEDUP_MS = 500;\n\n function push(entry: FormErrorEntry) {\n // Dedup check\n const _key = `${entry.formId}:${entry.timestamp}`;\n const last = recentRecords.get(entry.formId);\n if (last && entry.timestamp - last < DEDUP_MS) return;\n recentRecords.set(entry.formId, entry.timestamp);\n\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n function recordFormErrors(form: HTMLFormElement, type: FormErrorEntry[\"type\"]) {\n const formId = getFormId(form);\n\n // Strategy 1: aria-invalid / :invalid fields\n let fields = collectInvalidFields(form);\n\n // Strategy 2: if no aria-invalid fields found, look for visible error messages\n if (fields.length === 0) {\n fields = collectVisibleErrors(form);\n }\n\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type,\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Handle clicks on submit buttons. If no submit event follows within\n * SILENT_SUBMIT_WINDOW_MS, it means the form library blocked the submit\n * entirely — scan for errors.\n */\n function handleSubmitButtonClick(button: HTMLElement) {\n const form = findClosestForm(button);\n\n setTimeout(() => {\n if (!active) return;\n // A real submit event fired — the submitHandler already took care of it\n if (Date.now() - lastSubmitTime < SILENT_SUBMIT_WINDOW_MS) return;\n\n // No submit event → silent failure\n const root = form ?? (document.body as HTMLElement);\n const formId = form ? getFormId(form) : `page:${location.pathname}`;\n\n let fields = form ? collectInvalidFields(form) : [];\n if (fields.length === 0) {\n fields = collectVisibleErrors(root);\n }\n\n // Also scan full page — the error might be in a toast or outside the form\n if (fields.length === 0 && form) {\n fields = collectVisibleErrors(document.body);\n }\n\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type: \"silent_submit\",\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n }, SILENT_SUBMIT_WINDOW_MS);\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // 1. Capture submit events (fires even with e.preventDefault())\n submitHandler = (e: Event) => {\n lastSubmitTime = Date.now();\n const form = e.target as HTMLFormElement;\n if (!(form instanceof HTMLFormElement)) return;\n\n setTimeout(() => {\n if (!active) return;\n recordFormErrors(form, \"validation_failed\");\n }, POST_SUBMIT_CHECK_MS);\n };\n document.addEventListener(\"submit\", submitHandler, true);\n\n // 2. Capture native invalid events (HTML5 constraint validation)\n invalidHandler = (e: Event) => {\n lastSubmitTime = Date.now();\n const field = e.target as HTMLElement;\n const form = field.closest(\"form\");\n if (!form) return;\n\n setTimeout(() => {\n if (!active) return;\n recordFormErrors(form, \"validation_failed\");\n }, POST_SUBMIT_CHECK_MS);\n };\n document.addEventListener(\"invalid\", invalidHandler, true);\n\n // 3. Capture clicks on submit buttons — catches silent failures\n // where the form lib eats the submit and shows nothing\n clickHandler = (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n if (!target) return;\n\n // Walk up to find the actual button (click might be on an icon/span inside)\n const button = target.closest(\"button, input[type=submit]\") as HTMLElement | null;\n if (!button) return;\n if (!isSubmitButton(button)) return;\n\n handleSubmitButtonClick(button);\n };\n document.addEventListener(\"click\", clickHandler, true);\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (submitHandler) document.removeEventListener(\"submit\", submitHandler, true);\n if (invalidHandler) document.removeEventListener(\"invalid\", invalidHandler, true);\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n attemptCounts.clear();\n recentRecords.clear();\n },\n\n getEntries() {\n return [...entries];\n },\n\n report(formId: string, errors: Record<string, { message?: string } | undefined>) {\n const fields: FormErrorField[] = [];\n for (const [name, err] of Object.entries(errors)) {\n if (!err) continue;\n fields.push({ name, message: err.message });\n }\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type: \"validation_failed\",\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n },\n };\n}\n","export interface FrustrationEvent {\n type: \"rage_click\" | \"dead_click\" | \"error_loop\";\n timestamp: number;\n target: string;\n details: string;\n url: string;\n}\n\nexport interface FrustrationCollector {\n start(): void;\n stop(): void;\n getEvents(): FrustrationEvent[];\n onFrustration(callback: (event: FrustrationEvent) => void): () => void;\n}\n\nexport function createFrustrationCollector(opts?: {\n rageClickThreshold?: number;\n rageClickWindow?: number;\n errorLoopThreshold?: number;\n errorLoopWindow?: number;\n enableDeadClicks?: boolean;\n}): FrustrationCollector {\n const threshold = opts?.rageClickThreshold ?? 3;\n const window = opts?.rageClickWindow ?? 500;\n const errorThreshold = opts?.errorLoopThreshold ?? 3;\n const errorWindow = opts?.errorLoopWindow ?? 30_000;\n const deadClicksEnabled = opts?.enableDeadClicks ?? true;\n\n const events: FrustrationEvent[] = [];\n const listeners = new Set<(event: FrustrationEvent) => void>();\n const clickHistory: { target: EventTarget | null; time: number }[] = [];\n const errorCounts = new Map<string, { count: number; firstSeen: number }>();\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n let origConsoleError: typeof console.error | null = null;\n\n function emit(event: FrustrationEvent) {\n events.push(event);\n if (events.length > 50) events.shift();\n for (const cb of listeners) cb(event);\n }\n\n function getCSSSelector(el: Element): string {\n if (el.id) return `#${el.id}`;\n const tag = el.tagName.toLowerCase();\n const cls = [...el.classList].slice(0, 3).join(\".\");\n if (cls) return `${tag}.${cls}`;\n return tag;\n }\n\n function isInteractive(el: HTMLElement): boolean {\n const tag = el.tagName.toLowerCase();\n if ([\"a\", \"button\", \"input\", \"select\", \"textarea\", \"label\", \"summary\"].includes(tag)) return true;\n if (el.getAttribute(\"role\") === \"button\" || el.getAttribute(\"tabindex\")) return true;\n if (el.onclick || el.getAttribute(\"onclick\")) return true;\n if (el.closest(\"a, button, [role=button], [onclick]\")) return true;\n try {\n if (getComputedStyle(el).cursor === \"pointer\") return true;\n } catch {\n /* getComputedStyle may throw */\n }\n return false;\n }\n\n function handleClick(e: MouseEvent) {\n const target = e.target as HTMLElement;\n if (!target) return;\n const now = Date.now();\n\n // --- Rage click detection ---\n clickHistory.push({ target, time: now });\n // Remove old clicks\n while (clickHistory.length > 0 && now - clickHistory[0].time > 1000) {\n clickHistory.shift();\n }\n const recentOnSame = clickHistory.filter((c) => c.target === target && now - c.time < window);\n if (recentOnSame.length >= threshold) {\n emit({\n type: \"rage_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `${recentOnSame.length} clicks in ${now - recentOnSame[0].time}ms`,\n url: globalThis.location?.href ?? \"\",\n });\n clickHistory.length = 0;\n }\n\n // --- Dead click detection ---\n if (deadClicksEnabled && !isInteractive(target)) {\n emit({\n type: \"dead_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `Click on non-interactive <${target.tagName.toLowerCase()}>`,\n url: globalThis.location?.href ?? \"\",\n });\n }\n }\n\n function patchConsoleError() {\n origConsoleError = console.error;\n console.error = (...args: unknown[]) => {\n origConsoleError?.apply(console, args);\n // Check for error loop\n const key = String(args[0]).slice(0, 100);\n const now = Date.now();\n const existing = errorCounts.get(key);\n if (existing && now - existing.firstSeen < errorWindow) {\n existing.count++;\n if (existing.count >= errorThreshold) {\n emit({\n type: \"error_loop\",\n timestamp: now,\n target: \"console\",\n details: `Same error ${existing.count}x in ${Math.round((now - existing.firstSeen) / 1000)}s: ${key}`,\n url: globalThis.location?.href ?? \"\",\n });\n errorCounts.delete(key);\n }\n } else {\n errorCounts.set(key, { count: 1, firstSeen: now });\n }\n };\n }\n\n return {\n start() {\n clickHandler = handleClick;\n document.addEventListener(\"click\", clickHandler, true);\n patchConsoleError();\n },\n stop() {\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n if (origConsoleError) console.error = origConsoleError;\n clickHistory.length = 0;\n errorCounts.clear();\n },\n getEvents() {\n return [...events];\n },\n onFrustration(callback) {\n listeners.add(callback);\n return () => listeners.delete(callback);\n },\n };\n}\n","import type { NetworkEntry } from \"../types.js\";\n\nconst MAX_ENTRIES = 50;\n\nconst BLOCKED_HOSTS = new Set([\n \"browser-intake-datadoghq.com\",\n \"rum.browser-intake-datadoghq.com\",\n \"logs.browser-intake-datadoghq.com\",\n \"session-replay.browser-intake-datadoghq.com\",\n]);\n\nfunction isBlockedUrl(url: string, extra: Set<string>): boolean {\n try {\n const host = new URL(url, location.href).hostname;\n const all = [...BLOCKED_HOSTS, ...extra];\n return all.some((b) => host === b || host.endsWith(\".\" + b));\n } catch {\n return false;\n }\n}\n\nexport interface NetworkCollector {\n start(): void;\n stop(): void;\n getEntries(): NetworkEntry[];\n}\n\nfunction truncateUrl(url: string): string {\n try {\n const u = new URL(url, location.href);\n const base = `${u.origin}${u.pathname}`;\n return base.length > 200 ? base.slice(0, 200) + \"\\u2026\" : base;\n } catch {\n return url.length > 200 ? url.slice(0, 200) + \"\\u2026\" : url;\n }\n}\n\nexport function createNetworkCollector(extraBlockedHosts: string[] = []): NetworkCollector {\n const entries: NetworkEntry[] = [];\n const blocked = new Set(extraBlockedHosts);\n let origFetch: typeof window.fetch | null = null;\n let origXHROpen: typeof XMLHttpRequest.prototype.open | null = null;\n let active = false;\n\n function push(entry: NetworkEntry) {\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // -- Patch fetch --\n origFetch = window.fetch;\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const method = ((init?.method ?? \"GET\") as string).toUpperCase();\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : (input as Request).url;\n const startTime = Date.now();\n const res = await origFetch!.call(window, input, init);\n if (res.status >= 400 && !isBlockedUrl(url, blocked)) {\n push({\n method,\n url: truncateUrl(url),\n status: res.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n return res;\n };\n\n // -- Patch XHR --\n origXHROpen = XMLHttpRequest.prototype.open;\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL,\n async?: boolean,\n username?: string | null,\n password?: string | null,\n ) {\n const startTime = Date.now();\n const urlStr = typeof url === \"string\" ? url : (url as URL).href;\n\n this.addEventListener(\"load\", () => {\n if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {\n push({\n method: method.toUpperCase(),\n url: truncateUrl(urlStr),\n status: this.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n });\n\n return origXHROpen!.apply(this, [method, url, async ?? true, username, password] as Parameters<\n typeof XMLHttpRequest.prototype.open\n >);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (origFetch) window.fetch = origFetch;\n if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { FormErrorCollector } from \"./collectors/formErrors.js\";\nimport type { FlintUser } from \"./types.js\";\n\nexport interface FlintState {\n user: FlintUser | undefined;\n sessionReplay: string | (() => string) | undefined;\n}\n\n// Holds a reference to the active form error collector so flint.reportFormError\n// can forward calls without the consumer needing to manage the collector.\nlet formErrorCollectorRef: FormErrorCollector | null = null;\n\nexport function _setFormErrorCollector(collector: FormErrorCollector | null) {\n formErrorCollectorRef = collector;\n}\n\nlet state: FlintState = { user: undefined, sessionReplay: undefined };\nconst listeners = new Set<() => void>();\n\nfunction emit() {\n for (const l of listeners) l();\n}\n\nexport function subscribe(listener: () => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getSnapshot(): FlintState {\n return state;\n}\n\nexport const flint = {\n setUser(user: FlintUser | null) {\n state = { ...state, user: user ?? undefined };\n emit();\n },\n setSessionReplay(url: string | (() => string) | null) {\n state = { ...state, sessionReplay: url ?? undefined };\n emit();\n },\n\n /**\n * Report a form validation failure with exact field-level errors.\n * Call this from your form library's error callback.\n *\n * @example\n * // React Hook Form + Zod\n * const onSubmit = handleSubmit(onValid, (errors) => {\n * flint.reportFormError('checkout-form', errors);\n * });\n */\n reportFormError(formId: string, errors: Record<string, { message?: string } | undefined>) {\n formErrorCollectorRef?.report(formId, errors);\n },\n};\n","// Flint singleton — manages global collector lifecycle\nimport { submitReport } from \"./api.js\";\nimport type { ConsoleCollector } from \"./collectors/console.js\";\nimport { createConsoleCollector } from \"./collectors/console.js\";\nimport { collectEnvironment } from \"./collectors/environment.js\";\nimport type { FormErrorCollector } from \"./collectors/formErrors.js\";\nimport { createFormErrorCollector } from \"./collectors/formErrors.js\";\nimport type { FrustrationCollector } from \"./collectors/frustration.js\";\nimport { createFrustrationCollector } from \"./collectors/frustration.js\";\nimport type { NetworkCollector } from \"./collectors/network.js\";\nimport { createNetworkCollector } from \"./collectors/network.js\";\nimport { _setFormErrorCollector, flint, getSnapshot } from \"./store.js\";\nimport type { CollectedMeta, FlintConfig } from \"./types.js\";\n\nconst DEFAULT_REPLAY_BUFFER_MS = 60_000;\n\ninterface FlintInstance {\n config: Readonly<FlintConfig>;\n console: ConsoleCollector | null;\n network: NetworkCollector | null;\n formErrors: FormErrorCollector | null;\n frustration: FrustrationCollector | null;\n replayEvents: unknown[];\n stopReplay: (() => void) | null;\n}\n\nlet instance: FlintInstance | null = null;\n\nfunction init(config: FlintConfig): void {\n if (instance) {\n console.warn(\"[Flint] Already initialized. Call Flint.shutdown() first to re-initialize.\");\n return;\n }\n\n const {\n enableConsole = true,\n enableNetwork = true,\n enableFormErrors = true,\n enableFrustration = false,\n autoReportFrustration = false,\n enableReplay = false,\n replayBufferMs = DEFAULT_REPLAY_BUFFER_MS,\n blockedHosts = [],\n frustration: frustrationOpts,\n onFrustration,\n _replayRecorder,\n } = config;\n\n // Determine Flint server host to block from network capture\n const flintHost = (() => {\n try {\n return new URL(config.serverUrl).hostname;\n } catch {\n return \"\";\n }\n })();\n\n // Create and start collectors\n const consoleCol = enableConsole ? createConsoleCollector() : null;\n consoleCol?.start();\n\n const networkCol = enableNetwork\n ? createNetworkCollector([...blockedHosts, ...(flintHost ? [flintHost] : [])])\n : null;\n networkCol?.start();\n\n const formErrorsCol = enableFormErrors ? createFormErrorCollector() : null;\n if (formErrorsCol) {\n formErrorsCol.start();\n _setFormErrorCollector(formErrorsCol);\n }\n\n const frustrationCol = enableFrustration ? createFrustrationCollector(frustrationOpts) : null;\n frustrationCol?.start();\n\n // Set user if provided\n if (config.user) {\n flint.setUser(config.user);\n }\n\n const replayEvents: unknown[] = [];\n let stopReplay: (() => void) | null = null;\n\n instance = {\n config,\n console: consoleCol,\n network: networkCol,\n formErrors: formErrorsCol,\n frustration: frustrationCol,\n replayEvents,\n stopReplay: null,\n };\n\n // Start replay recording (async, via injected recorder)\n if (enableReplay && _replayRecorder) {\n _replayRecorder((event: unknown) => {\n replayEvents.push(event);\n const cutoff = Date.now() - replayBufferMs;\n while (replayEvents.length > 0 && (replayEvents[0] as { timestamp: number }).timestamp < cutoff) {\n replayEvents.shift();\n }\n }).then((stop) => {\n stopReplay = stop ?? null;\n if (instance) instance.stopReplay = stopReplay;\n });\n }\n\n // Wire frustration auto-report\n if (frustrationCol && autoReportFrustration) {\n frustrationCol.onFrustration(async (event) => {\n onFrustration?.(event);\n const user = getSnapshot().user ?? config.user;\n await submitReport(config.serverUrl, config.projectKey, {\n reporterId: user?.id ?? \"anonymous\",\n reporterName: user?.name ?? \"Anonymous\",\n reporterEmail: user?.email,\n description: `[Auto-detected] ${event.type.replace(/_/g, \" \")}: ${event.details}`,\n severity: event.type === \"error_loop\" ? \"P1\" : event.type === \"rage_click\" ? \"P2\" : \"P3\",\n url: event.url,\n meta: {\n ...config.meta,\n environment: collectEnvironment(),\n consoleLogs: consoleCol?.getEntries() ?? [],\n networkErrors: networkCol?.getEntries() ?? [],\n formErrors: formErrorsCol?.getEntries() ?? [],\n frustrationEvent: event,\n },\n }).catch(() => {});\n });\n } else if (frustrationCol && onFrustration) {\n frustrationCol.onFrustration(onFrustration);\n }\n}\n\nfunction shutdown(): void {\n if (!instance) return;\n\n instance.console?.stop();\n instance.network?.stop();\n instance.formErrors?.stop();\n _setFormErrorCollector(null);\n instance.frustration?.stop();\n instance.stopReplay?.();\n instance = null;\n}\n\nfunction isInitialized(): boolean {\n return instance !== null;\n}\n\nfunction getInstance(): FlintInstance | null {\n return instance;\n}\n\nfunction getMeta(extraMeta?: Record<string, unknown>): CollectedMeta {\n return {\n ...extraMeta,\n environment: collectEnvironment(),\n consoleLogs: instance?.console?.getEntries() ?? [],\n networkErrors: instance?.network?.getEntries() ?? [],\n formErrors: instance?.formErrors?.getEntries() ?? [],\n };\n}\n\nfunction getReplayEvents(): unknown[] {\n return instance ? [...instance.replayEvents] : [];\n}\n\nfunction getConfig(): Readonly<FlintConfig> | null {\n return instance?.config ?? null;\n}\n\nexport const Flint = {\n init,\n shutdown,\n isInitialized,\n getInstance,\n getMeta,\n getReplayEvents,\n getConfig,\n setUser: flint.setUser,\n setSessionReplay: flint.setSessionReplay,\n reportFormError: flint.reportFormError,\n};\n","import type { Theme, ThemeOverride } from \"./types.js\";\n\nexport interface ResolvedTheme {\n background: string;\n backgroundSecondary: string;\n accent: string;\n accentHover: string;\n text: string;\n textMuted: string;\n border: string;\n shadow: string;\n buttonText: string;\n backdropFilter: string;\n}\n\nconst light: ResolvedTheme = {\n background: \"rgba(255,255,255,0.90)\",\n backgroundSecondary: \"rgba(249,250,251,0.75)\",\n accent: \"#2563eb\",\n accentHover: \"#1d4ed8\",\n text: \"#111827\",\n textMuted: \"#6b7280\",\n border: \"rgba(255,255,255,0.9)\",\n shadow: \"0 32px 80px rgba(0,0,0,0.18), 0 8px 32px rgba(0,0,0,0.1), 0 0 0 1px rgba(0,0,0,0.04)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.8)\",\n};\n\nconst dark: ResolvedTheme = {\n background: \"rgba(15,20,35,0.88)\",\n backgroundSecondary: \"rgba(5,8,18,0.65)\",\n accent: \"#4d8aff\",\n accentHover: \"#3b6fdb\",\n text: \"#dde3ef\",\n textMuted: \"#6b7a93\",\n border: \"rgba(255,255,255,0.08)\",\n shadow: \"0 24px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.6)\",\n};\n\nexport function resolveTheme(theme: Theme): ResolvedTheme {\n if (theme === \"dark\") return dark;\n if (theme === \"light\") return light;\n // ThemeOverride — merge over light base\n const override = theme as ThemeOverride;\n return {\n ...light,\n background: override.background ?? light.background,\n accent: override.accent ?? light.accent,\n accentHover: override.accent ?? light.accentHover,\n text: override.text ?? light.text,\n border: override.border ?? light.border,\n };\n}\n"],"mappings":"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","names":["init","MAX_ENTRIES","window","listeners","emit","MAX_ENTRIES","init"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegotsi/flint-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -12,7 +12,12 @@
|
|
|
12
12
|
"require": "./dist/index.cjs"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
|
-
"
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
16
21
|
"scripts": {
|
|
17
22
|
"build": "tsup",
|
|
18
23
|
"typecheck": "tsc --noEmit"
|