@ga-components/logger 2.0.25 → 2.0.26

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.js CHANGED
@@ -410,6 +410,7 @@ var Logger = class {
410
410
  const key = this.buildErrorKey("onerror", finalMessage);
411
411
  if (this.loggedErrorKeys.has(key)) return false;
412
412
  this.loggedErrorKeys.add(key);
413
+ this.addBreadcrumb("error", finalMessage, { source: trimmedSource, lineno, colno });
413
414
  void this.error({
414
415
  fnName: "window.onerror",
415
416
  messageError: new Error(finalMessage),
@@ -423,6 +424,7 @@ var Logger = class {
423
424
  const key = this.buildErrorKey("unhandledrejection", msg);
424
425
  if (this.loggedErrorKeys.has(key)) return;
425
426
  this.loggedErrorKeys.add(key);
427
+ this.addBreadcrumb("error", msg);
426
428
  void this.error({
427
429
  fnName: "window.unhandledrejection",
428
430
  messageError: new Error(msg)
@@ -437,6 +439,7 @@ var Logger = class {
437
439
  const key = this.buildErrorKey("console.error", trimmedMsg);
438
440
  if (this.loggedErrorKeys.has(key)) return;
439
441
  this.loggedErrorKeys.add(key);
442
+ this.addBreadcrumb("error", trimmedMsg);
440
443
  const originalError = args.find((a) => a instanceof Error);
441
444
  const errorToSend = originalError ?? new Error(trimmedMsg);
442
445
  void this.error({
@@ -716,6 +719,13 @@ var Logger = class {
716
719
  });
717
720
  const disabledAtClick = this.isElementDisabled(target);
718
721
  const invalidFieldAtClick = this.hasInvalidFieldNearby(target);
722
+ const ariaDisabledAtClick = target.getAttribute("aria-disabled") === "true";
723
+ const pointerEventsBlockedAtClick = window.getComputedStyle(target).pointerEvents === "none";
724
+ const overlayInterceptedAtClick = (() => {
725
+ const elAtPoint = document.elementFromPoint(event.clientX, event.clientY);
726
+ if (!elAtPoint || elAtPoint === target || target.contains(elAtPoint)) return null;
727
+ return elAtPoint;
728
+ })();
719
729
  const clickTime = Date.now();
720
730
  const domActivityAtClick = this.lastDomActivity;
721
731
  const DEAD_CLICK_DELAY_MS = 4e3;
@@ -784,7 +794,25 @@ var Logger = class {
784
794
  const recentFetch = this.breadcrumbs.filter(
785
795
  (b) => b.type === "fetch" && clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS
786
796
  ).slice(-1)[0];
787
- const possibleReason = disabledAtClick ? { type: "disabled", detail: "Button dalam kondisi disabled saat diklik" } : recentFetch ? { type: "api_error", detail: recentFetch.message } : invalidFieldAtClick ? { type: "validation_error", detail: "Field invalid terdeteksi di form terdekat" } : this.recentDomChanges.length ? { type: "dom_feedback", detail: this.recentDomChanges.slice(-1)[0] } : { type: "no_feedback", detail: "Tidak ada feedback UI terdeteksi setelah klik" };
797
+ const recentError = this.breadcrumbs.filter(
798
+ (b) => b.type === "error" && clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS
799
+ ).slice(-1)[0];
800
+ const possibleReason = disabledAtClick ? { type: "disabled", detail: "Button dalam kondisi disabled saat diklik" } : ariaDisabledAtClick ? {
801
+ type: "aria_disabled",
802
+ detail: 'Element memiliki aria-disabled="true" (disabled secara visual/aksesibilitas, bukan native disabled attribute)'
803
+ } : pointerEventsBlockedAtClick ? {
804
+ type: "pointer_events_blocked",
805
+ detail: "Element memiliki CSS pointer-events: none saat diklik (tidak bisa menerima event klik sama sekali)"
806
+ } : overlayInterceptedAtClick ? {
807
+ type: "overlay_intercepted",
808
+ detail: `Element lain (${getElementSelector(overlayInterceptedAtClick)}) menutupi target saat klik terjadi, klik tidak sampai ke target aslinya`
809
+ } : recentFetch ? { type: "api_error", detail: recentFetch.message } : invalidFieldAtClick ? {
810
+ type: "validation_error",
811
+ detail: "Field invalid terdeteksi di form terdekat"
812
+ } : recentError ? { type: "js_error", detail: recentError.message } : this.recentDomChanges.length ? { type: "dom_feedback", detail: this.recentDomChanges.slice(-1)[0] } : {
813
+ type: "no_feedback",
814
+ detail: "Tidak ada feedback UI terdeteksi setelah klik"
815
+ };
788
816
  const formattedLog = formatClickLog({
789
817
  fnName: "interceptClick.rageClick",
790
818
  tagName,
@@ -911,6 +939,7 @@ var Logger = class {
911
939
  y: window.scrollY,
912
940
  width: window.innerWidth,
913
941
  height: window.innerHeight,
942
+ imageTimeout: 3e3,
914
943
  onclone: (clonedDoc) => {
915
944
  if (!canApplyHighlight) return;
916
945
  const clonedTarget = clonedDoc.querySelector(`[data-ga-highlight="${HIGHLIGHT_MARKER}"]`);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/logger.ts","../src/config/firebase.ts","../src/core/environment.ts","../src/core/formatClickLog.ts","../src/components/LoggerErrorBoundary.tsx"],"sourcesContent":["export { logger } from \"./core/logger\";\nexport { LoggerErrorBoundary } from \"./components/LoggerErrorBoundary\";\nexport type { LogEntry, LoggerConfig, LogLevel, LogEnv, Breadcrumb, BreadcrumbType } from \"./types\";\n","import { doc, collection, setDoc, updateDoc, Timestamp } from \"firebase/firestore\";\r\nimport { initFirebase, getDb } from \"../config/firebase\";\r\nimport type { Breadcrumb, BreadcrumbType, ClickReason, LogEntry, LogLevel, LogParams, LoggerConfig } from \"../types\";\r\nimport { initEnvironment } from \"./environment\";\r\nimport { getEnvironmentPayload } from \"./environment\";\r\nimport { formatClickLog } from \"./formatClickLog\";\r\n\r\nconst sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\r\nconst MAX_BREADCRUMBS = 50;\r\n\r\nconst CLICK_SKIP_PATTERNS = [/^:r[a-z0-9]+:$/, /^\\[object/, /^#/];\r\n\r\n// Sanitize sebelum masuk Firestor\r\nconst sanitizeClickText = (text: string): string => {\r\n return text\r\n .replace(/\\b[\\w.-]+@[\\w.-]+\\.\\w+\\b/g, \"[email]\")\r\n .replace(/\\b\\d{10,}\\b/g, \"[number]\")\r\n .replace(/Rp[\\s]?\\d[\\d.,]*/gi, \"[amount]\")\r\n .trim();\r\n};\r\n\r\nconst getClosestClickText = (raw: HTMLElement, target: HTMLElement): string => {\r\n let el: HTMLElement | null = raw;\r\n\r\n // 1. innerText di sepanjang chain (existing)\r\n while (el) {\r\n const text = sanitizeClickText(el.innerText?.trim().slice(0, 50) ?? \"\");\r\n if (text) return text;\r\n if (el === target) break;\r\n el = el.parentElement;\r\n }\r\n\r\n // 2. aria-label / title di sepanjang chain\r\n el = raw;\r\n while (el) {\r\n const label = el.getAttribute(\"aria-label\") || el.getAttribute(\"title\");\r\n if (label) {\r\n const text = sanitizeClickText(label.trim().slice(0, 50));\r\n if (text) return text;\r\n }\r\n if (el === target) break;\r\n el = el.parentElement;\r\n }\r\n\r\n // 3. text dari child terdekat (span/p/div) di dalam target\r\n const childText = target.querySelector(\"span, p, div\")?.textContent?.trim();\r\n if (childText) {\r\n const text = sanitizeClickText(childText.slice(0, 50));\r\n if (text) return text;\r\n }\r\n\r\n // 4. text dari sibling terdekat\r\n const siblingText =\r\n target.previousElementSibling?.textContent?.trim() || target.nextElementSibling?.textContent?.trim();\r\n if (siblingText) {\r\n const text = sanitizeClickText(siblingText.slice(0, 50));\r\n if (text) return text;\r\n }\r\n\r\n return \"\";\r\n};\r\n\r\nconst getElementSelector = (el: HTMLElement): string => {\r\n const tag = el.tagName?.toLowerCase() ?? \"unknown\";\r\n\r\n if (el.id) return `${tag}#${el.id}`;\r\n\r\n const firstClass =\r\n typeof el.className === \"string\" && el.className.trim() ? el.className.trim().split(/\\s+/)[0] : \"\";\r\n if (firstClass) return `${tag}.${firstClass}`;\r\n\r\n const parent = el.parentElement;\r\n if (parent) {\r\n const index = Array.from(parent.children).indexOf(el) + 1;\r\n return `${tag}:nth-child(${index})`;\r\n }\r\n\r\n return tag;\r\n};\r\n\r\nconst parseRequestBody = (body: unknown): unknown => {\r\n if (body == null) return null;\r\n if (typeof body === \"string\") {\r\n try {\r\n return JSON.parse(body);\r\n } catch {\r\n return body;\r\n }\r\n }\r\n if (body instanceof FormData) {\r\n const result: Record<string, unknown> = {};\r\n body.forEach((value, key) => {\r\n result[key] = value;\r\n });\r\n return result;\r\n }\r\n if (body instanceof URLSearchParams) {\r\n const result: Record<string, string> = {};\r\n body.forEach((value, key) => {\r\n result[key] = value;\r\n });\r\n return result;\r\n }\r\n return \"[unsupported body type]\";\r\n};\r\n\r\nconst parseQueryParams = (url: string): Record<string, string> | null => {\r\n try {\r\n const params = new URL(url, window.location.origin).searchParams;\r\n const result: Record<string, string> = {};\r\n params.forEach((value, key) => {\r\n result[key] = value;\r\n });\r\n return Object.keys(result).length > 0 ? result : null;\r\n } catch {\r\n return null;\r\n }\r\n};\r\n\r\nclass Logger {\r\n private config: LoggerConfig | null = null;\r\n private initialized = false;\r\n private breadcrumbs: Breadcrumb[] = [];\r\n\r\n private lastDomActivity = Date.now();\r\n private clickHistory = new Map<string, number[]>();\r\n private rageClickFired = new Map<string, number>();\r\n private domObserverInitialized = false;\r\n\r\n private deadClickWarnTimestamps: number[] = [];\r\n private readonly DEAD_CLICK_RATE_LIMIT_WINDOW_MS = 60000;\r\n private readonly DEAD_CLICK_RATE_LIMIT_MAX = 5;\r\n\r\n private recentDomChanges: string[] = [];\r\n\r\n private isElementDisabled(target: HTMLElement): boolean {\r\n return (target as HTMLButtonElement).disabled === true || target.getAttribute(\"aria-disabled\") === \"true\";\r\n }\r\n\r\n // Helper: cek apakah ada field invalid di form terdekat dari target\r\n private hasInvalidFieldNearby(target: HTMLElement): boolean {\r\n const form = target.closest(\"form\");\r\n if (!form) return false;\r\n return form.querySelector('[aria-invalid=\"true\"]') !== null;\r\n }\r\n\r\n private initDomActivityObserver(): void {\r\n if (this.domObserverInitialized) return;\r\n this.domObserverInitialized = true;\r\n\r\n const ALERT_SELECTOR = '[role=\"alert\"], [aria-live], .MuiSnackbar-root, .MuiAlert-root';\r\n\r\n const getAlertElement = (el: HTMLElement | null): HTMLElement | null => {\r\n return el?.closest(ALERT_SELECTOR) ?? null;\r\n };\r\n\r\n const markActivity = (addedText?: string) => {\r\n this.lastDomActivity = Date.now();\r\n if (addedText) {\r\n this.recentDomChanges.push(addedText);\r\n setTimeout(() => {\r\n this.recentDomChanges = this.recentDomChanges.filter((t) => t !== addedText);\r\n }, 1000);\r\n }\r\n };\r\n\r\n const observer = new MutationObserver((mutations) => {\r\n markActivity();\r\n for (const m of mutations) {\r\n if (m.type === \"childList\") {\r\n m.addedNodes.forEach((node) => {\r\n if (node.nodeType !== Node.ELEMENT_NODE) return;\r\n const alertEl = getAlertElement(node as HTMLElement);\r\n if (!alertEl) return;\r\n const text = alertEl.innerText?.trim().slice(0, 200);\r\n if (text) markActivity(text);\r\n });\r\n } else if (m.type === \"characterData\") {\r\n const alertEl = getAlertElement(m.target.parentElement);\r\n if (!alertEl) continue;\r\n const text = alertEl.innerText?.trim().slice(0, 200);\r\n if (text) markActivity(text);\r\n }\r\n }\r\n });\r\n\r\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true });\r\n window.addEventListener(\"scroll\", () => markActivity(), { passive: true, capture: true });\r\n }\r\n\r\n init(config: LoggerConfig) {\r\n this.config = config;\r\n initFirebase(config);\r\n initEnvironment();\r\n this.initialized = true;\r\n console.info(\"[ga-logger] initialized —\", config.env);\r\n }\r\n setUserId(userId: string | null) {\r\n if (!this.config) {\r\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\r\n return;\r\n }\r\n this.config.userId = userId;\r\n }\r\n\r\n initAnonymousUser(): void {\r\n if (!this.config) {\r\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\r\n return;\r\n }\r\n\r\n const STORAGE_KEY = \"ga_anon_id\";\r\n\r\n try {\r\n let anonId = localStorage.getItem(STORAGE_KEY);\r\n\r\n if (!anonId) {\r\n anonId =\r\n typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\r\n ? crypto.randomUUID()\r\n : \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\r\n return v.toString(16);\r\n });\r\n\r\n localStorage.setItem(STORAGE_KEY, anonId);\r\n }\r\n\r\n const shortId = anonId.slice(-8);\r\n this.setUserId(`Anonymous #${shortId}`);\r\n } catch (e) {\r\n console.warn(\"[ga-logger] initAnonymousUser failed:\", e);\r\n this.setUserId(\"Anonymous\");\r\n }\r\n }\r\n\r\n // Add breadcrumb ke in-memory array\r\n addBreadcrumb(type: BreadcrumbType, message: string, data?: Record<string, unknown>) {\r\n const breadcrumb: Breadcrumb = {\r\n timestamp: new Date(),\r\n type,\r\n message,\r\n data,\r\n };\r\n\r\n this.breadcrumbs.push(breadcrumb);\r\n\r\n // Jaga memory — max 50 breadcrumbs, buang yang paling lama\r\n if (this.breadcrumbs.length > MAX_BREADCRUMBS) {\r\n this.breadcrumbs.shift();\r\n }\r\n }\r\n\r\n // Global error capture — hanya aktif di non-development\r\n private loggedErrorKeys = new Set<string>();\r\n\r\n // Helper: trim URL jadi domain/filename\r\n private trimUrl(url: string): string {\r\n try {\r\n const u = new URL(url);\r\n const filename = u.pathname.split(\"/\").filter(Boolean).pop() ?? u.pathname;\r\n return `${u.hostname}/${filename}`;\r\n } catch {\r\n // bukan valid URL, potong saja jika terlalu panjang\r\n return url.length > 80 ? url.slice(0, 80) + \"...\" : url;\r\n }\r\n }\r\n\r\n // Helper: build dedup key\r\n private buildErrorKey(type: string, message: string): string {\r\n return `${type}::${message}`;\r\n }\r\n\r\n // Helper: should skip resource error\r\n private shouldSkipResourceError(src: string, tagName: string): boolean {\r\n // Skip IMG — tidak sebabkan blank page\r\n if (tagName === \"IMG\") return true;\r\n\r\n // Skip base64 URL\r\n if (src.startsWith(\"data:\")) return true;\r\n\r\n // Skip known 3rd party trackers\r\n const THIRD_PARTY = [\r\n \"omguk.com\",\r\n \"track.\",\r\n \"doubleclick\",\r\n \"adservice\",\r\n \"googletagmanager\",\r\n \"google-analytics\",\r\n \"hotjar\",\r\n \"facebook.net\",\r\n \"analytics\",\r\n \"magnolia\",\r\n \"gtm.js\",\r\n ];\r\n if (THIRD_PARTY.some((p) => src.includes(p))) return true;\r\n\r\n return false;\r\n }\r\n\r\n private shouldSkipGlobalError(message: string): boolean {\r\n const NOISE = [\r\n \"CORS\",\r\n \"cross-origin\",\r\n \"ERR_FAILED\",\r\n \"Failed to load resource\",\r\n \"Failed to load image\",\r\n \"NetworkError\",\r\n \"ResizeObserver loop\",\r\n \"Script error\",\r\n \"ServiceWorker\",\r\n \"sw.js\",\r\n \"dev-sw.js\",\r\n \"Warning:\",\r\n \"Error loading image\",\r\n ];\r\n const THIRD_PARTY = [\r\n \"magnolia\",\r\n \"gtm.js\",\r\n \"analytics\",\r\n \"googletagmanager\",\r\n \"facebook\",\r\n \"hotjar\",\r\n \"omguk.com\",\r\n \"track.\",\r\n \"doubleclick\",\r\n \"adservice\",\r\n ];\r\n return NOISE.some((p) => message.includes(p)) || THIRD_PARTY.some((p) => message.includes(p));\r\n }\r\n\r\n initGlobalCapture(): void {\r\n const isLocalhost = window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n if (isLocalhost) return;\r\n\r\n window.onerror = (message, source, lineno, colno) => {\r\n const msg = typeof message === \"string\" ? message : \"Unknown error\";\r\n if (this.shouldSkipGlobalError(msg)) return false;\r\n\r\n const trimmedSource = source ? this.trimUrl(source) : \"unknown source\";\r\n const finalMessage = `${msg} at ${trimmedSource}:${lineno ?? 0}`;\r\n const key = this.buildErrorKey(\"onerror\", finalMessage);\r\n if (this.loggedErrorKeys.has(key)) return false;\r\n this.loggedErrorKeys.add(key);\r\n\r\n void this.error({\r\n fnName: \"window.onerror\",\r\n messageError: new Error(finalMessage),\r\n payload: { source: trimmedSource, lineno, colno },\r\n });\r\n\r\n return false;\r\n };\r\n\r\n // unhandledrejection\r\n window.addEventListener(\"unhandledrejection\", (event) => {\r\n const msg =\r\n event.reason instanceof Error\r\n ? event.reason.message\r\n : String(event.reason ?? \"Unhandled Promise rejection\");\r\n\r\n if (this.shouldSkipGlobalError(msg)) return;\r\n\r\n const key = this.buildErrorKey(\"unhandledrejection\", msg);\r\n if (this.loggedErrorKeys.has(key)) return;\r\n this.loggedErrorKeys.add(key);\r\n\r\n void this.error({\r\n fnName: \"window.unhandledrejection\",\r\n messageError: new Error(msg),\r\n });\r\n });\r\n\r\n // console.error override\r\n const originalConsoleError = console.error.bind(console);\r\n console.error = (...args: unknown[]) => {\r\n originalConsoleError(...args);\r\n\r\n const msg = args.map((a) => (a instanceof Error ? a.message : String(a))).join(\" \");\r\n\r\n if (this.shouldSkipGlobalError(msg)) return;\r\n\r\n const trimmedMsg = msg.length > 150 ? msg.slice(0, 150) + \"...\" : msg;\r\n const key = this.buildErrorKey(\"console.error\", trimmedMsg);\r\n if (this.loggedErrorKeys.has(key)) return;\r\n this.loggedErrorKeys.add(key);\r\n\r\n const originalError = args.find((a): a is Error => a instanceof Error);\r\n\r\n const errorToSend = originalError ?? new Error(trimmedMsg);\r\n\r\n void this.error({\r\n fnName: \"console.error\",\r\n messageError: errorToSend,\r\n });\r\n };\r\n\r\n // resource load error (CSS / JS / font)\r\n window.addEventListener(\r\n \"error\",\r\n (event) => {\r\n const target = event.target;\r\n if (!(target instanceof HTMLElement)) return;\r\n if (target === (window as unknown)) return;\r\n\r\n const tagName = target.tagName.toUpperCase();\r\n\r\n const src =\r\n tagName === \"LINK\"\r\n ? (target as HTMLLinkElement).href\r\n : tagName === \"SCRIPT\"\r\n ? (target as HTMLScriptElement).src\r\n : \"\";\r\n\r\n if (this.shouldSkipResourceError(src, tagName)) return;\r\n\r\n // Deteksi dari ekstensi file, bukan tagName\r\n const ext = src.split(\"?\")[0].split(\".\").pop()?.toLowerCase();\r\n const resourceType =\r\n ext === \"css\"\r\n ? \"CSS\"\r\n : ext === \"js\"\r\n ? \"JS\"\r\n : ext === \"woff\" || ext === \"woff2\" || ext === \"ttf\"\r\n ? \"FONT\"\r\n : tagName;\r\n\r\n const trimmedSrc = this.trimUrl(src);\r\n const finalMessage = `Failed to load ${resourceType}: ${trimmedSrc}`;\r\n const key = this.buildErrorKey(\"resource\", finalMessage);\r\n if (this.loggedErrorKeys.has(key)) return;\r\n this.loggedErrorKeys.add(key);\r\n\r\n void this.error({\r\n fnName: \"resource.onerror\",\r\n messageError: new Error(finalMessage),\r\n payload: { tagName, src: trimmedSrc },\r\n });\r\n },\r\n true,\r\n );\r\n }\r\n\r\n private sanitizePayload(obj: Record<string, unknown>): Record<string, unknown> {\r\n return Object.fromEntries(\r\n Object.entries(obj).map(([key, value]) => {\r\n if (value === undefined) return [key, null];\r\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\r\n return [key, this.sanitizePayload(value as Record<string, unknown>)];\r\n }\r\n return [key, value];\r\n }),\r\n );\r\n }\r\n\r\n private sanitizeForFirestore(obj: Record<string, unknown>): Record<string, unknown> {\r\n return Object.fromEntries(\r\n Object.entries(obj).map(([key, value]) => {\r\n if (value === undefined) return [key, null];\r\n if (typeof value === \"function\") return [key, \"[function]\"];\r\n if (value instanceof Date) return [key, value.toISOString()];\r\n if (Array.isArray(value)) {\r\n return [\r\n key,\r\n value.map((item) => {\r\n if (item instanceof Date) return item.toISOString();\r\n if (typeof item === \"function\") return \"[function]\";\r\n if (item !== null && typeof item === \"object\") {\r\n return this.sanitizeForFirestore(item as Record<string, unknown>);\r\n }\r\n return item ?? null;\r\n }),\r\n ];\r\n }\r\n if (value !== null && typeof value === \"object\") {\r\n return [key, this.sanitizeForFirestore(value as Record<string, unknown>)];\r\n }\r\n return [key, value];\r\n }),\r\n );\r\n }\r\n\r\n // interceptFetch — auto breadcrumb, ke Firestore\r\n interceptFetch() {\r\n if (typeof window === \"undefined\") return;\r\n\r\n const originalFetch = window.fetch;\r\n\r\n const skipPatterns = [\r\n \"google.com\",\r\n \"googletagmanager.com\",\r\n \"googleapis.com\",\r\n \"facebook.com\",\r\n \"hotjar.com\",\r\n \"analytics\",\r\n \"gtm\",\r\n \"ccm/collect\",\r\n \"on.aws\",\r\n \"/locales/\",\r\n \"/sw.js\",\r\n \"/manifest.json\",\r\n \"/sourcemaps/\",\r\n \"magnolia\",\r\n ];\r\n\r\n const shouldSkipUrl = (url: string): boolean => {\r\n return skipPatterns.some((pattern) => url.includes(pattern));\r\n };\r\n\r\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\r\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\r\n\r\n // Skip third-party domains\r\n if (shouldSkipUrl(url)) {\r\n return originalFetch(input, init);\r\n }\r\n\r\n const method = init?.method ?? \"GET\";\r\n const startTime = Date.now();\r\n\r\n try {\r\n const response = await originalFetch(input, init);\r\n const duration = Date.now() - startTime;\r\n\r\n this.addBreadcrumb(\"fetch\", `${method} ${url}`, {\r\n status: response.status,\r\n duration: `${duration}ms`,\r\n });\r\n\r\n if (response.status >= 400) {\r\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} ${response.status}`);\r\n\r\n if (!this.loggedErrorKeys.has(dedupKey)) {\r\n this.loggedErrorKeys.add(dedupKey);\r\n\r\n let responseBody: unknown = null;\r\n try {\r\n const cloned = response.clone();\r\n const contentType = cloned.headers.get(\"content-type\") ?? \"\";\r\n responseBody = contentType.includes(\"application/json\")\r\n ? await cloned.json()\r\n : await cloned.text();\r\n } catch {\r\n responseBody = \"[unable to read response body]\";\r\n }\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n\r\n if (!isLocalhost) {\r\n this.error({\r\n fnName: \"interceptFetch\",\r\n messageError: new Error(`HTTP ${response.status} ${response.statusText} — ${url}`),\r\n endpointName: url,\r\n payload: {\r\n method,\r\n status: response.status,\r\n statusText: response.statusText,\r\n duration: `${duration}ms`,\r\n query: parseQueryParams(url),\r\n request: parseRequestBody(init?.body),\r\n response: responseBody,\r\n },\r\n });\r\n }\r\n }\r\n }\r\n\r\n return response;\r\n } catch (networkError) {\r\n const duration = Date.now() - startTime;\r\n const error = networkError instanceof Error ? networkError : new Error(String(networkError));\r\n\r\n this.addBreadcrumb(\"fetch\", `${method} ${url} — NETWORK ERROR`, {\r\n duration: `${duration}ms`,\r\n error: error.message,\r\n });\r\n\r\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} NETWORK_ERROR`);\r\n\r\n if (!this.loggedErrorKeys.has(dedupKey)) {\r\n this.loggedErrorKeys.add(dedupKey);\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n if (!isLocalhost) {\r\n this.error({\r\n fnName: \"interceptFetch\",\r\n messageError: error,\r\n endpointName: url,\r\n payload: { method, duration: `${duration}ms` },\r\n });\r\n }\r\n }\r\n\r\n throw networkError;\r\n }\r\n };\r\n\r\n console.info(\"[ga-logger] fetch intercepted\");\r\n }\r\n\r\n // interceptXHR — auto breadcrumb + error capture untuk axios/XHR-based request\r\n interceptXHR(): void {\r\n if (typeof window === \"undefined\") return;\r\n\r\n const skipPatterns = [\r\n \"google.com\",\r\n \"googletagmanager.com\",\r\n \"googleapis.com\",\r\n \"facebook.com\",\r\n \"hotjar.com\",\r\n \"analytics\",\r\n \"ccm/collect\",\r\n \"on.aws\",\r\n \"/locales/\",\r\n \"/sw.js\",\r\n \"/manifest.json\",\r\n \"/sourcemaps/\",\r\n \"magnolia\",\r\n ];\r\n\r\n const shouldSkipUrl = (url: string): boolean => skipPatterns.some((p) => url.includes(p));\r\n\r\n const originalOpen = XMLHttpRequest.prototype.open;\r\n const originalSend = XMLHttpRequest.prototype.send;\r\n const self = this;\r\n\r\n type GaXHR = XMLHttpRequest & {\r\n _gaMethod?: string;\r\n _gaUrl?: string;\r\n _gaStartTime?: number;\r\n };\r\n\r\n XMLHttpRequest.prototype.open = function (this: GaXHR, method: string, url: string | URL, ...rest: unknown[]) {\r\n this._gaMethod = method;\r\n this._gaUrl = typeof url === \"string\" ? url : url.href;\r\n // @ts-expect-error - spreading native arguments\r\n return originalOpen.call(this, method, url, ...rest);\r\n };\r\n\r\n XMLHttpRequest.prototype.send = function (this: GaXHR, ...args: unknown[]) {\r\n const url = this._gaUrl ?? \"\";\r\n const method = this._gaMethod ?? \"GET\";\r\n\r\n if (shouldSkipUrl(url)) {\r\n // @ts-expect-error - spreading native arguments\r\n return originalSend.call(this, ...args);\r\n }\r\n\r\n this._gaStartTime = Date.now();\r\n const requestBody = parseRequestBody(args[0]);\r\n\r\n this.addEventListener(\"loadend\", () => {\r\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\r\n const status = this.status;\r\n\r\n self.addBreadcrumb(\"fetch\", `${method} ${url}`, {\r\n status,\r\n duration: `${duration}ms`,\r\n });\r\n\r\n if (status >= 400) {\r\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} ${status}`);\r\n if (self.loggedErrorKeys.has(dedupKey)) return;\r\n self.loggedErrorKeys.add(dedupKey);\r\n\r\n let responseBody: unknown = this.responseText;\r\n try {\r\n responseBody = JSON.parse(this.responseText);\r\n } catch {\r\n // keep as raw text\r\n }\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n\r\n if (!isLocalhost) {\r\n void self.error({\r\n fnName: \"interceptXHR\",\r\n messageError: new Error(`HTTP ${status} ${this.statusText} — ${url}`),\r\n endpointName: url,\r\n payload: {\r\n method,\r\n status,\r\n statusText: this.statusText,\r\n duration: `${duration}ms`,\r\n query: parseQueryParams(url),\r\n request: requestBody,\r\n response: responseBody,\r\n },\r\n });\r\n }\r\n }\r\n });\r\n\r\n this.addEventListener(\"error\", () => {\r\n // network-level failure (status selalu 0)\r\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\r\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} NETWORK_ERROR`);\r\n if (self.loggedErrorKeys.has(dedupKey)) return;\r\n self.loggedErrorKeys.add(dedupKey);\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n\r\n if (!isLocalhost) {\r\n void self.error({\r\n fnName: \"interceptXHR\",\r\n messageError: new Error(`Network error — ${method} ${url}`),\r\n endpointName: url,\r\n payload: { method, duration: `${duration}ms` },\r\n });\r\n }\r\n });\r\n\r\n // @ts-expect-error - spreading native arguments\r\n return originalSend.call(this, ...args);\r\n };\r\n\r\n console.info(\"[ga-logger] XHR intercepted\");\r\n }\r\n\r\n interceptClick(): void {\r\n if (typeof window === \"undefined\") return;\r\n\r\n if ((window as any).__ga_click_intercepted) return;\r\n (window as any).__ga_click_intercepted = true;\r\n this.initDomActivityObserver();\r\n\r\n document.addEventListener(\r\n \"click\",\r\n (event: MouseEvent) => {\r\n const raw = event.target as HTMLElement;\r\n if (!raw) return;\r\n\r\n const target = (raw.closest('button, a, [role=\"button\"], [data-log], input') as HTMLElement) ?? raw;\r\n\r\n const innerText = getClosestClickText(raw, target);\r\n const id = target.id ?? \"\";\r\n const tagName = target.tagName?.toLowerCase() ?? \"\";\r\n\r\n const isInteractive =\r\n [\"button\", \"a\", \"input\"].includes(tagName) ||\r\n target.hasAttribute(\"data-log\") ||\r\n target.getAttribute(\"role\") === \"button\";\r\n\r\n const selector = getElementSelector(target);\r\n const href = tagName === \"a\" ? target.getAttribute(\"href\") : null;\r\n\r\n if (!innerText && !id && !isInteractive) return;\r\n\r\n const message = innerText || id || `(no label: ${tagName || \"unknown\"})`;\r\n\r\n // Skip internal React/Router identifiers\r\n if (CLICK_SKIP_PATTERNS.some((p) => p.test(message))) return;\r\n\r\n this.addBreadcrumb(\"click\", message, {\r\n tagName,\r\n ...(id ? { id } : {}),\r\n ...(innerText ? { text: innerText } : {}),\r\n });\r\n\r\n const disabledAtClick = this.isElementDisabled(target);\r\n const invalidFieldAtClick = this.hasInvalidFieldNearby(target);\r\n // ── Dead click detection ──\r\n const clickTime = Date.now();\r\n const domActivityAtClick = this.lastDomActivity;\r\n const DEAD_CLICK_DELAY_MS = 4000;\r\n const RAGE_CLICK_WINDOW_MS = 2000;\r\n const RAGE_CLICK_COOLDOWN_MS = 5000;\r\n\r\n if ([\"button\", \"a\", \"input\"].includes(tagName)) {\r\n setTimeout(() => {\r\n\r\n if (this.lastDomActivity <= domActivityAtClick) {\r\n const recentFetch = this.breadcrumbs\r\n .filter(\r\n (b) =>\r\n b.type === \"fetch\" &&\r\n new Date(b.timestamp).getTime() >= clickTime &&\r\n new Date(b.timestamp).getTime() <= clickTime + DEAD_CLICK_DELAY_MS,\r\n )\r\n .slice(-1)[0];\r\n\r\n const reason: ClickReason = disabledAtClick\r\n ? { type: \"disabled\", detail: \"Button dalam kondisi disabled saat diklik\" }\r\n : recentFetch\r\n ? { type: \"api_error\", detail: recentFetch.message }\r\n : invalidFieldAtClick\r\n ? { type: \"validation_error\", detail: \"Field invalid terdeteksi di form terdekat\" }\r\n : {\r\n type: \"no_dom_activity\",\r\n detail: `Tidak ada aktivitas DOM/scroll dalam ${DEAD_CLICK_DELAY_MS / 1000} detik`,\r\n };\r\n\r\n const formattedLog = formatClickLog({\r\n fnName: \"interceptClick.deadClick\",\r\n tagName,\r\n text: message,\r\n selector,\r\n reason,\r\n });\r\n\r\n this.addBreadcrumb(\"click\", `Button tidak bisa diklik: ${message}`, {\r\n tagName,\r\n ...(id ? { id } : {}),\r\n reason,\r\n });\r\n\r\n // ── Rate-limit: max N dead-click warn per session per window ──\r\n const now = Date.now();\r\n this.deadClickWarnTimestamps = this.deadClickWarnTimestamps.filter(\r\n (t) => now - t < this.DEAD_CLICK_RATE_LIMIT_WINDOW_MS,\r\n );\r\n\r\n if (this.deadClickWarnTimestamps.length >= this.DEAD_CLICK_RATE_LIMIT_MAX) {\r\n return;\r\n }\r\n this.deadClickWarnTimestamps.push(now);\r\n\r\n void this.warn({\r\n fnName: \"interceptClick.deadClick\",\r\n messageError: new Error(`Button tidak bisa diklik: ${message}`),\r\n payload: {\r\n tagName,\r\n ...(id ? { id } : {}),\r\n reason,\r\n text: message,\r\n selector,\r\n ...(href ? { href } : {}),\r\n formattedLog,\r\n },\r\n excludeBreadcrumbTypes: [\"fetch\"],\r\n breadcrumbWindowMs: 10000,\r\n captureScreenshot: true,\r\n highlightTarget: target,\r\n });\r\n }\r\n }, DEAD_CLICK_DELAY_MS);\r\n }\r\n\r\n // ── Rage click detection 3x klik ──\r\n if (isInteractive) {\r\n const historyKey = `${tagName}::${message}`;\r\n const timestamps = (this.clickHistory.get(historyKey) ?? []).filter(\r\n (t) => clickTime - t < RAGE_CLICK_WINDOW_MS,\r\n );\r\n timestamps.push(clickTime);\r\n this.clickHistory.set(historyKey, timestamps);\r\n\r\n const lastFired = this.rageClickFired.get(historyKey) ?? 0;\r\n\r\n if (timestamps.length >= 3 && clickTime - lastFired > RAGE_CLICK_COOLDOWN_MS) {\r\n this.rageClickFired.set(historyKey, clickTime);\r\n\r\n const recentFetch = this.breadcrumbs\r\n .filter(\r\n (b) =>\r\n b.type === \"fetch\" &&\r\n clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS,\r\n )\r\n .slice(-1)[0];\r\n\r\n const possibleReason: ClickReason = disabledAtClick\r\n ? { type: \"disabled\", detail: \"Button dalam kondisi disabled saat diklik\" }\r\n : recentFetch\r\n ? { type: \"api_error\", detail: recentFetch.message }\r\n : invalidFieldAtClick\r\n ? { type: \"validation_error\", detail: \"Field invalid terdeteksi di form terdekat\" }\r\n : this.recentDomChanges.length\r\n ? { type: \"dom_feedback\", detail: this.recentDomChanges.slice(-1)[0] }\r\n : { type: \"no_feedback\", detail: \"Tidak ada feedback UI terdeteksi setelah klik\" };\r\n\r\n const formattedLog = formatClickLog({\r\n fnName: \"interceptClick.rageClick\",\r\n tagName,\r\n text: message,\r\n selector,\r\n clickCount: timestamps.length,\r\n windowMs: RAGE_CLICK_WINDOW_MS,\r\n reason: possibleReason,\r\n });\r\n\r\n this.addBreadcrumb(\"click\", `Klik Berulang kali: ${message}`, {\r\n tagName,\r\n clickCount: timestamps.length,\r\n windowMs: RAGE_CLICK_WINDOW_MS,\r\n possibleReason,\r\n });\r\n\r\n void this.warn({\r\n fnName: \"interceptClick.rageClick\",\r\n messageError: new Error(`Klik Berulang kali: ${message}`),\r\n payload: {\r\n tagName,\r\n clickCount: timestamps.length,\r\n windowMs: RAGE_CLICK_WINDOW_MS,\r\n possibleReason,\r\n text: message,\r\n selector,\r\n ...(href ? { href } : {}),\r\n formattedLog,\r\n },\r\n excludeBreadcrumbTypes: [\"fetch\"],\r\n breadcrumbWindowMs: 10000,\r\n captureScreenshot: true,\r\n highlightTarget: target,\r\n });\r\n }\r\n }\r\n },\r\n true,\r\n );\r\n }\r\n\r\n captureAction<T>(params: LogParams, fn: () => T | Promise<T>): Promise<T | undefined> {\r\n return Promise.resolve()\r\n .then(() => fn())\r\n .catch((error: unknown) => {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.send(\"error\", params.fnName, params.payload, err, params.endpointName);\r\n return undefined;\r\n });\r\n }\r\n\r\n withLogger<T extends (...args: any[]) => any>(\r\n fnName: string,\r\n fn: T,\r\n options?: {\r\n endpointName?: string;\r\n payload?: Record<string, unknown>;\r\n },\r\n ): T {\r\n return ((...args: Parameters<T>): ReturnType<T> | undefined => {\r\n try {\r\n const result = fn(...args);\r\n if (result instanceof Promise) {\r\n return result.catch((error: unknown) => {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.error({\r\n fnName,\r\n messageError: err,\r\n endpointName: options?.endpointName,\r\n payload: options?.payload,\r\n });\r\n return undefined;\r\n }) as ReturnType<T>;\r\n }\r\n\r\n return result;\r\n } catch (error: unknown) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.error({\r\n fnName,\r\n messageError: err,\r\n endpointName: options?.endpointName,\r\n payload: options?.payload,\r\n });\r\n return undefined;\r\n }\r\n }) as T;\r\n }\r\n\r\n private waitForScrollEnd(timeoutMs = 1500, idleMs = 100): Promise<void> {\r\n return new Promise((resolve) => {\r\n let idleTimer: ReturnType<typeof setTimeout>;\r\n let settled = false;\r\n\r\n const cleanup = () => {\r\n if (settled) return;\r\n settled = true;\r\n clearTimeout(idleTimer);\r\n clearTimeout(hardTimeout);\r\n window.removeEventListener(\"scroll\", onScroll, true);\r\n resolve();\r\n };\r\n\r\n const onScroll = () => {\r\n clearTimeout(idleTimer);\r\n idleTimer = setTimeout(cleanup, idleMs);\r\n };\r\n\r\n // fallback: if no scroll event ever fires (target already in view), don't hang\r\n const hardTimeout = setTimeout(cleanup, timeoutMs);\r\n\r\n window.addEventListener(\"scroll\", onScroll, true);\r\n // start the idle timer immediately too, in case scrollIntoView is a no-op\r\n idleTimer = setTimeout(cleanup, idleMs);\r\n });\r\n }\r\n\r\n // private createHighlightOverlay(target: HTMLElement): HTMLDivElement {\r\n // const rect = target.getBoundingClientRect();\r\n // const padding = 0;\r\n\r\n // const overlay = document.createElement(\"div\");\r\n // overlay.style.position = \"absolute\";\r\n // overlay.style.top = `${rect.top + window.scrollY - padding}px`;\r\n // overlay.style.left = `${rect.left + window.scrollX - padding}px`;\r\n // overlay.style.width = `${rect.width + padding * 2}px`;\r\n // overlay.style.height = `${rect.height + padding * 2}px`;\r\n // overlay.style.border = \"2px solid red\";\r\n // overlay.style.pointerEvents = \"none\";\r\n // overlay.style.zIndex = \"999999\";\r\n\r\n // document.body.appendChild(overlay);\r\n // return overlay;\r\n // }\r\n\r\n private async captureAndUploadScreenshot(highlightTarget?: HTMLElement): Promise<string | null> {\r\n const canApplyHighlight = !!highlightTarget && document.contains(highlightTarget);\r\n const HIGHLIGHT_MARKER = \"ga-logger-highlight-target\";\r\n\r\n try {\r\n if (canApplyHighlight) {\r\n highlightTarget!.scrollIntoView({ block: \"center\", inline: \"center\" });\r\n await this.waitForScrollEnd();\r\n highlightTarget!.setAttribute(\"data-ga-highlight\", HIGHLIGHT_MARKER);\r\n }\r\n\r\n const html2canvas = (await import(\"html2canvas-pro\")).default;\r\n\r\n const canvas = await html2canvas(document.body, {\r\n x: window.scrollX,\r\n y: window.scrollY,\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n onclone: (clonedDoc) => {\r\n if (!canApplyHighlight) return;\r\n const clonedTarget = clonedDoc.querySelector(`[data-ga-highlight=\"${HIGHLIGHT_MARKER}\"]`);\r\n if (clonedTarget instanceof HTMLElement) {\r\n clonedTarget.style.border = \"2px solid red\";\r\n clonedTarget.style.boxSizing = \"border-box\";\r\n }\r\n },\r\n });\r\n\r\n return canvas.toDataURL(\"image/jpeg\", 0.4);\r\n } catch (e) {\r\n console.error(\"[ga-logger] screenshot capture failed:\", e);\r\n return null;\r\n } finally {\r\n highlightTarget?.removeAttribute(\"data-ga-highlight\");\r\n }\r\n }\r\n\r\n private async send(\r\n level: LogLevel,\r\n message: string,\r\n payload: Record<string, unknown> = {},\r\n error?: Error,\r\n endpoint?: string,\r\n excludeBreadcrumbTypes?: BreadcrumbType[],\r\n breadcrumbWindowMs?: number,\r\n captureScreenshot?: boolean,\r\n highlightTarget?: HTMLElement,\r\n ) {\r\n if (!this.initialized || !this.config) {\r\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\r\n return;\r\n }\r\n\r\n const isLocalhost =\r\n typeof window !== \"undefined\" &&\r\n (window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\");\r\n\r\n if (isLocalhost) {\r\n console.error(`[ga-localhost][DEV] ${message}`, { payload, error });\r\n return;\r\n }\r\n\r\n const now = Date.now();\r\n\r\n let filteredBreadcrumbs = excludeBreadcrumbTypes?.length\r\n ? this.breadcrumbs.filter((b) => !excludeBreadcrumbTypes.includes(b.type))\r\n : this.breadcrumbs;\r\n\r\n if (breadcrumbWindowMs) {\r\n filteredBreadcrumbs = filteredBreadcrumbs.filter(\r\n (b) => now - new Date(b.timestamp).getTime() <= breadcrumbWindowMs,\r\n );\r\n }\r\n\r\n const entry: LogEntry = {\r\n timestamp: new Date(),\r\n level,\r\n message,\r\n env: this.config.env,\r\n userId: this.config.userId ?? null,\r\n sessionId,\r\n page: typeof window !== \"undefined\" ? window.location.pathname : \"\",\r\n stackTrace: error?.stack ?? null,\r\n payload: this.sanitizePayload(payload),\r\n repo: this.config.repo,\r\n endpoint: endpoint ?? null,\r\n breadcrumbs: [...filteredBreadcrumbs],\r\n ...getEnvironmentPayload(),\r\n };\r\n\r\n try {\r\n const db = getDb();\r\n const docRef = doc(collection(db, \"logs\"));\r\n\r\n const safeEntry = this.sanitizeForFirestore({ ...entry } as unknown as Record<string, unknown>);\r\n\r\n await setDoc(docRef, {\r\n ...safeEntry,\r\n timestamp: Timestamp.fromDate(entry.timestamp),\r\n });\r\n\r\n if (captureScreenshot) {\r\n void this.captureAndUploadScreenshot(highlightTarget).then((base64) => {\r\n if (base64) {\r\n void updateDoc(docRef, { screenshotBase64: base64 }).catch((e) =>\r\n console.error(\"[ga-logger] failed to update screenshotBase64:\", e),\r\n );\r\n }\r\n });\r\n }\r\n } catch (e) {\r\n console.error(\"[ga-logger] failed to send log:\", e);\r\n }\r\n }\r\n\r\n info(params: LogParams) {\r\n const { fnName, messageError, payload, endpointName } = params;\r\n return this.send(\"info\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\r\n }\r\n\r\n warn(params: LogParams) {\r\n const {\r\n fnName,\r\n messageError,\r\n payload,\r\n endpointName,\r\n excludeBreadcrumbTypes,\r\n breadcrumbWindowMs,\r\n captureScreenshot,\r\n highlightTarget,\r\n } = params;\r\n return this.send(\r\n \"warn\",\r\n messageError?.message ?? fnName,\r\n { ...payload, fnName },\r\n messageError,\r\n endpointName,\r\n excludeBreadcrumbTypes,\r\n breadcrumbWindowMs,\r\n captureScreenshot,\r\n highlightTarget,\r\n );\r\n }\r\n\r\n error(params: LogParams) {\r\n const { fnName, messageError, payload, endpointName } = params;\r\n return this.send(\"error\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\r\n }\r\n}\r\n\r\nexport const logger = new Logger();","import { initializeApp, getApps, getApp } from \"firebase/app\";\nimport { getFirestore } from \"firebase/firestore\";\nimport type { Firestore } from \"firebase/firestore\";\nimport type { LoggerConfig } from \"../types\";\n\nconst APP_NAME = \"ga-logger\";\nlet db: Firestore | null = null;\n\nexport const initFirebase = (config: LoggerConfig) => {\n // Cek apakah named instance \"ga-logger\" sudah ada\n const existingApp = getApps().find((app) => app.name === APP_NAME);\n\n const app =\n existingApp ??\n initializeApp(\n {\n apiKey: config.apiKey,\n authDomain: config.authDomain,\n projectId: config.projectId,\n storageBucket: config.storageBucket,\n messagingSenderId: config.messagingSenderId,\n appId: config.appId,\n },\n APP_NAME,\n );\n\n db = getFirestore(app);\n return db;\n};\n\nexport const getDb = (): Firestore => {\n if (!db) throw new Error(\"[ga-logger] Firebase not initialized. Call logger.init() first.\");\n return db;\n};\n","interface EnvironmentPayload {\n userAgent: string;\n deviceType: \"Mobile\" | \"Tablet\" | \"Desktop\";\n screenWidth: number;\n screenHeight: number;\n}\n\nlet cachedEnvironment: EnvironmentPayload | null = null;\n\nfunction getDeviceType(width: number): \"Mobile\" | \"Tablet\" | \"Desktop\" {\n if (width < 768) return \"Mobile\";\n if (width <= 1024) return \"Tablet\";\n return \"Desktop\";\n}\n\nexport function initEnvironment(): void {\n if (cachedEnvironment) return;\n\n const width = window.innerWidth;\n const height = window.innerHeight;\n\n cachedEnvironment = {\n userAgent: navigator.userAgent,\n deviceType: getDeviceType(width),\n screenWidth: width,\n screenHeight: height,\n };\n}\n\nexport function getEnvironmentPayload(): EnvironmentPayload {\n if (!cachedEnvironment) initEnvironment();\n return cachedEnvironment!;\n}","import type { ClickReason, ClickReasonType } from \"../types\";\n\nconst REASON_LABEL: Record<ClickReasonType, string> = {\n disabled: \"Button sedang disabled saat diklik\",\n api_error: \"Request API gagal\",\n validation_error: \"Ada field invalid di form terdekat (validasi gagal)\",\n dom_feedback: \"Muncul notifikasi\",\n no_feedback: \"Tidak ada notifikasi/feedback UI apapun setelah diklik\",\n no_dom_activity: \"Tidak ada aktivitas DOM/scroll setelah diklik\",\n};\n\nconst SCREENSHOT_HINT: Partial<Record<ClickReasonType, string>> = {\n dom_feedback: \"📷 Lihat screenshot untuk detail visual alert\",\n no_feedback: \"📷 Cek screenshot — kemungkinan UI freeze/silent fail\",\n no_dom_activity: \"📷 Cek screenshot — kondisi layar saat klik terjadi\",\n};\n\ninterface ClickLogPayload {\n fnName: string;\n tagName: string;\n text: string;\n selector: string;\n clickCount?: number;\n windowMs?: number;\n reason: ClickReason;\n}\n\nconst buildDiagnosis = (reason: ClickReason): string => {\n const label = REASON_LABEL[reason.type];\n const hint = SCREENSHOT_HINT[reason.type];\n\n const base =\n reason.type === \"api_error\"\n ? `${label} — ${reason.detail}`\n : reason.type === \"dom_feedback\"\n ? `${label} — \"${reason.detail}\"`\n : label;\n\n return hint ? `${base}\\n ${hint}` : base;\n};\n\nconst formatSelectorAsHtml = (selector: string, tagName: string): string => {\n if (selector.includes(\".\")) {\n const className = selector.split(\".\").slice(1).join(\" \");\n return `class=\"${className}\"`;\n }\n if (selector.includes(\"#\")) {\n const id = selector.split(\"#\")[1];\n return `id=\"${id}\"`;\n }\n return \"\";\n};\n\nexport const formatClickLog = (payload: ClickLogPayload): string => {\n const isRageClick = payload.fnName.includes(\"rageClick\");\n const clickTypeLabel = isRageClick ? \"Klik Berulang\" : \"Klik Tanpa Respon\";\n const clickTypeSuffix = isRageClick ? \"(Rage Click)\" : \"(Dead Click)\";\n\n const stats = isRageClick\n ? `${payload.clickCount}x klik dalam ${payload.windowMs}ms`\n : `Terdeteksi setelah klik tanpa aktivitas`;\n\n const attr = formatSelectorAsHtml(payload.selector, payload.tagName);\n const openTag = attr ? `<${payload.tagName} ${attr}>` : `<${payload.tagName}>`;\n const elementHtml = payload.text ? `${openTag}${payload.text}</${payload.tagName}>` : `${openTag}</${payload.tagName}>`;\n\n return [\n `Summary : ${clickTypeLabel} pada ${payload.tagName} \"${payload.text}\" ${clickTypeSuffix}`,\n `Diagnosis : ${buildDiagnosis(payload.reason)}`,\n `Stats : ${stats}`,\n `Element : ${elementHtml}`,\n `Source : ${payload.fnName}`,\n ].join(\"\\n\");\n};\n\n","import { Component, type ErrorInfo, type ReactNode } from \"react\";\nimport { logger } from \"../core/logger\";\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n}\n\ninterface State {\n hasError: boolean;\n}\n\nexport class LoggerErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(): State {\n return { hasError: true };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n logger.addBreadcrumb(\"error\", `React crash: ${error.message}`, {\n componentStack: info.componentStack ?? undefined,\n });\n\n logger.error({\n fnName: \"LoggerErrorBoundary\",\n messageError: error,\n payload: {\n componentStack: info.componentStack ?? undefined,\n },\n });\n }\n\n render() {\n if (this.state.hasError) {\n return this.props.fallback ?? (\n <div style={{ padding: \"1rem\", color: \"red\" }}>\n Something went wrong.\n </div>\n );\n }\n\n return this.props.children;\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,oBAA8D;;;ACA9D,iBAA+C;AAC/C,uBAA6B;AAI7B,IAAM,WAAW;AACjB,IAAI,KAAuB;AAEpB,IAAM,eAAe,CAAC,WAAyB;AAElD,QAAM,kBAAc,oBAAQ,EAAE,KAAK,CAACC,SAAQA,KAAI,SAAS,QAAQ;AAEjE,QAAM,MACF,mBACA;AAAA,IACI;AAAA,MACI,QAAQ,OAAO;AAAA,MACf,YAAY,OAAO;AAAA,MACnB,WAAW,OAAO;AAAA,MAClB,eAAe,OAAO;AAAA,MACtB,mBAAmB,OAAO;AAAA,MAC1B,OAAO,OAAO;AAAA,IAClB;AAAA,IACA;AAAA,EACJ;AAEJ,WAAK,+BAAa,GAAG;AACrB,SAAO;AACX;AAEO,IAAM,QAAQ,MAAiB;AAClC,MAAI,CAAC,GAAI,OAAM,IAAI,MAAM,iEAAiE;AAC1F,SAAO;AACX;;;AC1BA,IAAI,oBAA+C;AAEnD,SAAS,cAAc,OAAgD;AACnE,MAAI,QAAQ,IAAK,QAAO;AACxB,MAAI,SAAS,KAAM,QAAO;AAC1B,SAAO;AACX;AAEO,SAAS,kBAAwB;AACpC,MAAI,kBAAmB;AAEvB,QAAM,QAAQ,OAAO;AACrB,QAAM,SAAS,OAAO;AAEtB,sBAAoB;AAAA,IAChB,WAAW,UAAU;AAAA,IACrB,YAAY,cAAc,KAAK;AAAA,IAC/B,aAAa;AAAA,IACb,cAAc;AAAA,EAClB;AACJ;AAEO,SAAS,wBAA4C;AACxD,MAAI,CAAC,kBAAmB,iBAAgB;AACxC,SAAO;AACX;;;AC9BA,IAAM,eAAgD;AAAA,EAClD,UAAU;AAAA,EACV,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,aAAa;AAAA,EACb,iBAAiB;AACrB;AAEA,IAAM,kBAA4D;AAAA,EAC9D,cAAc;AAAA,EACd,aAAa;AAAA,EACb,iBAAiB;AACrB;AAYA,IAAM,iBAAiB,CAAC,WAAgC;AACpD,QAAM,QAAQ,aAAa,OAAO,IAAI;AACtC,QAAM,OAAO,gBAAgB,OAAO,IAAI;AAExC,QAAM,OACF,OAAO,SAAS,cACV,GAAG,KAAK,WAAM,OAAO,MAAM,KAC3B,OAAO,SAAS,iBACd,GAAG,KAAK,YAAO,OAAO,MAAM,MAC5B;AAEZ,SAAO,OAAO,GAAG,IAAI;AAAA,cAAiB,IAAI,KAAK;AACnD;AAEA,IAAM,uBAAuB,CAAC,UAAkB,YAA4B;AACxE,MAAI,SAAS,SAAS,GAAG,GAAG;AACxB,UAAM,YAAY,SAAS,MAAM,GAAG,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG;AACvD,WAAO,UAAU,SAAS;AAAA,EAC9B;AACA,MAAI,SAAS,SAAS,GAAG,GAAG;AACxB,UAAM,KAAK,SAAS,MAAM,GAAG,EAAE,CAAC;AAChC,WAAO,OAAO,EAAE;AAAA,EACpB;AACA,SAAO;AACX;AAEO,IAAM,iBAAiB,CAAC,YAAqC;AAChE,QAAM,cAAc,QAAQ,OAAO,SAAS,WAAW;AACvD,QAAM,iBAAiB,cAAc,kBAAkB;AACvD,QAAM,kBAAkB,cAAc,iBAAiB;AAEvD,QAAM,QAAQ,cACR,GAAG,QAAQ,UAAU,gBAAgB,QAAQ,QAAQ,OACrD;AAEN,QAAM,OAAO,qBAAqB,QAAQ,UAAU,QAAQ,OAAO;AACnE,QAAM,UAAU,OAAO,IAAI,QAAQ,OAAO,IAAI,IAAI,MAAM,IAAI,QAAQ,OAAO;AAC3E,QAAM,cAAc,QAAQ,OAAO,GAAG,OAAO,GAAG,QAAQ,IAAI,KAAK,QAAQ,OAAO,MAAM,GAAG,OAAO,KAAK,QAAQ,OAAO;AAEpH,SAAO;AAAA,IACH,eAAe,cAAc,SAAS,QAAQ,OAAO,KAAK,QAAQ,IAAI,KAAK,eAAe;AAAA,IAC1F,eAAe,eAAe,QAAQ,MAAM,CAAC;AAAA,IAC7C,eAAe,KAAK;AAAA,IACpB,eAAe,WAAW;AAAA,IAC1B,eAAe,QAAQ,MAAM;AAAA,EACjC,EAAE,KAAK,IAAI;AACf;;;AHlEA,IAAM,YAAY,QAAQ,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC9E,IAAM,kBAAkB;AAExB,IAAM,sBAAsB,CAAC,kBAAkB,aAAa,IAAI;AAGhE,IAAM,oBAAoB,CAAC,SAAyB;AAChD,SAAO,KACF,QAAQ,6BAA6B,SAAS,EAC9C,QAAQ,gBAAgB,UAAU,EAClC,QAAQ,sBAAsB,UAAU,EACxC,KAAK;AACd;AAEA,IAAM,sBAAsB,CAAC,KAAkB,WAAgC;AAC3E,MAAI,KAAyB;AAG7B,SAAO,IAAI;AACP,UAAM,OAAO,kBAAkB,GAAG,WAAW,KAAK,EAAE,MAAM,GAAG,EAAE,KAAK,EAAE;AACtE,QAAI,KAAM,QAAO;AACjB,QAAI,OAAO,OAAQ;AACnB,SAAK,GAAG;AAAA,EACZ;AAGA,OAAK;AACL,SAAO,IAAI;AACP,UAAM,QAAQ,GAAG,aAAa,YAAY,KAAK,GAAG,aAAa,OAAO;AACtE,QAAI,OAAO;AACP,YAAM,OAAO,kBAAkB,MAAM,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC;AACxD,UAAI,KAAM,QAAO;AAAA,IACrB;AACA,QAAI,OAAO,OAAQ;AACnB,SAAK,GAAG;AAAA,EACZ;AAGA,QAAM,YAAY,OAAO,cAAc,cAAc,GAAG,aAAa,KAAK;AAC1E,MAAI,WAAW;AACX,UAAM,OAAO,kBAAkB,UAAU,MAAM,GAAG,EAAE,CAAC;AACrD,QAAI,KAAM,QAAO;AAAA,EACrB;AAGA,QAAM,cACF,OAAO,wBAAwB,aAAa,KAAK,KAAK,OAAO,oBAAoB,aAAa,KAAK;AACvG,MAAI,aAAa;AACb,UAAM,OAAO,kBAAkB,YAAY,MAAM,GAAG,EAAE,CAAC;AACvD,QAAI,KAAM,QAAO;AAAA,EACrB;AAEA,SAAO;AACX;AAEA,IAAM,qBAAqB,CAAC,OAA4B;AACpD,QAAM,MAAM,GAAG,SAAS,YAAY,KAAK;AAEzC,MAAI,GAAG,GAAI,QAAO,GAAG,GAAG,IAAI,GAAG,EAAE;AAEjC,QAAM,aACF,OAAO,GAAG,cAAc,YAAY,GAAG,UAAU,KAAK,IAAI,GAAG,UAAU,KAAK,EAAE,MAAM,KAAK,EAAE,CAAC,IAAI;AACpG,MAAI,WAAY,QAAO,GAAG,GAAG,IAAI,UAAU;AAE3C,QAAM,SAAS,GAAG;AAClB,MAAI,QAAQ;AACR,UAAM,QAAQ,MAAM,KAAK,OAAO,QAAQ,EAAE,QAAQ,EAAE,IAAI;AACxD,WAAO,GAAG,GAAG,cAAc,KAAK;AAAA,EACpC;AAEA,SAAO;AACX;AAEA,IAAM,mBAAmB,CAAC,SAA2B;AACjD,MAAI,QAAQ,KAAM,QAAO;AACzB,MAAI,OAAO,SAAS,UAAU;AAC1B,QAAI;AACA,aAAO,KAAK,MAAM,IAAI;AAAA,IAC1B,QAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AACA,MAAI,gBAAgB,UAAU;AAC1B,UAAM,SAAkC,CAAC;AACzC,SAAK,QAAQ,CAAC,OAAO,QAAQ;AACzB,aAAO,GAAG,IAAI;AAAA,IAClB,CAAC;AACD,WAAO;AAAA,EACX;AACA,MAAI,gBAAgB,iBAAiB;AACjC,UAAM,SAAiC,CAAC;AACxC,SAAK,QAAQ,CAAC,OAAO,QAAQ;AACzB,aAAO,GAAG,IAAI;AAAA,IAClB,CAAC;AACD,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAEA,IAAM,mBAAmB,CAAC,QAA+C;AACrE,MAAI;AACA,UAAM,SAAS,IAAI,IAAI,KAAK,OAAO,SAAS,MAAM,EAAE;AACpD,UAAM,SAAiC,CAAC;AACxC,WAAO,QAAQ,CAAC,OAAO,QAAQ;AAC3B,aAAO,GAAG,IAAI;AAAA,IAClB,CAAC;AACD,WAAO,OAAO,KAAK,MAAM,EAAE,SAAS,IAAI,SAAS;AAAA,EACrD,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEA,IAAM,SAAN,MAAa;AAAA,EAAb;AACI,SAAQ,SAA8B;AACtC,SAAQ,cAAc;AACtB,SAAQ,cAA4B,CAAC;AAErC,SAAQ,kBAAkB,KAAK,IAAI;AACnC,SAAQ,eAAe,oBAAI,IAAsB;AACjD,SAAQ,iBAAiB,oBAAI,IAAoB;AACjD,SAAQ,yBAAyB;AAEjC,SAAQ,0BAAoC,CAAC;AAC7C,SAAiB,kCAAkC;AACnD,SAAiB,4BAA4B;AAE7C,SAAQ,mBAA6B,CAAC;AA0HtC;AAAA,SAAQ,kBAAkB,oBAAI,IAAY;AAAA;AAAA,EAxHlC,kBAAkB,QAA8B;AACpD,WAAQ,OAA6B,aAAa,QAAQ,OAAO,aAAa,eAAe,MAAM;AAAA,EACvG;AAAA;AAAA,EAGQ,sBAAsB,QAA8B;AACxD,UAAM,OAAO,OAAO,QAAQ,MAAM;AAClC,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO,KAAK,cAAc,uBAAuB,MAAM;AAAA,EAC3D;AAAA,EAEQ,0BAAgC;AACpC,QAAI,KAAK,uBAAwB;AACjC,SAAK,yBAAyB;AAE9B,UAAM,iBAAiB;AAEvB,UAAM,kBAAkB,CAAC,OAA+C;AACpE,aAAO,IAAI,QAAQ,cAAc,KAAK;AAAA,IAC1C;AAEA,UAAM,eAAe,CAAC,cAAuB;AACzC,WAAK,kBAAkB,KAAK,IAAI;AAChC,UAAI,WAAW;AACX,aAAK,iBAAiB,KAAK,SAAS;AACpC,mBAAW,MAAM;AACb,eAAK,mBAAmB,KAAK,iBAAiB,OAAO,CAAC,MAAM,MAAM,SAAS;AAAA,QAC/E,GAAG,GAAI;AAAA,MACX;AAAA,IACJ;AAEA,UAAM,WAAW,IAAI,iBAAiB,CAAC,cAAc;AACjD,mBAAa;AACb,iBAAW,KAAK,WAAW;AACvB,YAAI,EAAE,SAAS,aAAa;AACxB,YAAE,WAAW,QAAQ,CAAC,SAAS;AAC3B,gBAAI,KAAK,aAAa,KAAK,aAAc;AACzC,kBAAM,UAAU,gBAAgB,IAAmB;AACnD,gBAAI,CAAC,QAAS;AACd,kBAAM,OAAO,QAAQ,WAAW,KAAK,EAAE,MAAM,GAAG,GAAG;AACnD,gBAAI,KAAM,cAAa,IAAI;AAAA,UAC/B,CAAC;AAAA,QACL,WAAW,EAAE,SAAS,iBAAiB;AACnC,gBAAM,UAAU,gBAAgB,EAAE,OAAO,aAAa;AACtD,cAAI,CAAC,QAAS;AACd,gBAAM,OAAO,QAAQ,WAAW,KAAK,EAAE,MAAM,GAAG,GAAG;AACnD,cAAI,KAAM,cAAa,IAAI;AAAA,QAC/B;AAAA,MACJ;AAAA,IACJ,CAAC;AAED,aAAS,QAAQ,SAAS,MAAM,EAAE,WAAW,MAAM,SAAS,MAAM,YAAY,MAAM,eAAe,KAAK,CAAC;AACzG,WAAO,iBAAiB,UAAU,MAAM,aAAa,GAAG,EAAE,SAAS,MAAM,SAAS,KAAK,CAAC;AAAA,EAC5F;AAAA,EAEA,KAAK,QAAsB;AACvB,SAAK,SAAS;AACd,iBAAa,MAAM;AACnB,oBAAgB;AAChB,SAAK,cAAc;AACnB,YAAQ,KAAK,kCAA6B,OAAO,GAAG;AAAA,EACxD;AAAA,EACA,UAAU,QAAuB;AAC7B,QAAI,CAAC,KAAK,QAAQ;AACd,cAAQ,KAAK,wDAAwD;AACrE;AAAA,IACJ;AACA,SAAK,OAAO,SAAS;AAAA,EACzB;AAAA,EAEA,oBAA0B;AACtB,QAAI,CAAC,KAAK,QAAQ;AACd,cAAQ,KAAK,wDAAwD;AACrE;AAAA,IACJ;AAEA,UAAM,cAAc;AAEpB,QAAI;AACA,UAAI,SAAS,aAAa,QAAQ,WAAW;AAE7C,UAAI,CAAC,QAAQ;AACT,iBACI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,aACxD,OAAO,WAAW,IAClB,uCAAuC,QAAQ,SAAS,CAAC,MAAM;AAC3D,gBAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,gBAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,iBAAO,EAAE,SAAS,EAAE;AAAA,QACxB,CAAC;AAEX,qBAAa,QAAQ,aAAa,MAAM;AAAA,MAC5C;AAEA,YAAM,UAAU,OAAO,MAAM,EAAE;AAC/B,WAAK,UAAU,cAAc,OAAO,EAAE;AAAA,IAC1C,SAAS,GAAG;AACR,cAAQ,KAAK,yCAAyC,CAAC;AACvD,WAAK,UAAU,WAAW;AAAA,IAC9B;AAAA,EACJ;AAAA;AAAA,EAGA,cAAc,MAAsB,SAAiB,MAAgC;AACjF,UAAM,aAAyB;AAAA,MAC3B,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,SAAK,YAAY,KAAK,UAAU;AAGhC,QAAI,KAAK,YAAY,SAAS,iBAAiB;AAC3C,WAAK,YAAY,MAAM;AAAA,IAC3B;AAAA,EACJ;AAAA;AAAA,EAMQ,QAAQ,KAAqB;AACjC,QAAI;AACA,YAAM,IAAI,IAAI,IAAI,GAAG;AACrB,YAAM,WAAW,EAAE,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,IAAI,KAAK,EAAE;AAClE,aAAO,GAAG,EAAE,QAAQ,IAAI,QAAQ;AAAA,IACpC,QAAQ;AAEJ,aAAO,IAAI,SAAS,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,QAAQ;AAAA,IACxD;AAAA,EACJ;AAAA;AAAA,EAGQ,cAAc,MAAc,SAAyB;AACzD,WAAO,GAAG,IAAI,KAAK,OAAO;AAAA,EAC9B;AAAA;AAAA,EAGQ,wBAAwB,KAAa,SAA0B;AAEnE,QAAI,YAAY,MAAO,QAAO;AAG9B,QAAI,IAAI,WAAW,OAAO,EAAG,QAAO;AAGpC,UAAM,cAAc;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AACA,QAAI,YAAY,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC,EAAG,QAAO;AAErD,WAAO;AAAA,EACX;AAAA,EAEQ,sBAAsB,SAA0B;AACpD,UAAM,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AACA,UAAM,cAAc;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AACA,WAAO,MAAM,KAAK,CAAC,MAAM,QAAQ,SAAS,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,MAAM,QAAQ,SAAS,CAAC,CAAC;AAAA,EAChG;AAAA,EAEA,oBAA0B;AACtB,UAAM,cAAc,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,aAAa;AAC7F,QAAI,YAAa;AAEjB,WAAO,UAAU,CAAC,SAAS,QAAQ,QAAQ,UAAU;AACjD,YAAM,MAAM,OAAO,YAAY,WAAW,UAAU;AACpD,UAAI,KAAK,sBAAsB,GAAG,EAAG,QAAO;AAE5C,YAAM,gBAAgB,SAAS,KAAK,QAAQ,MAAM,IAAI;AACtD,YAAM,eAAe,GAAG,GAAG,OAAO,aAAa,IAAI,UAAU,CAAC;AAC9D,YAAM,MAAM,KAAK,cAAc,WAAW,YAAY;AACtD,UAAI,KAAK,gBAAgB,IAAI,GAAG,EAAG,QAAO;AAC1C,WAAK,gBAAgB,IAAI,GAAG;AAE5B,WAAK,KAAK,MAAM;AAAA,QACZ,QAAQ;AAAA,QACR,cAAc,IAAI,MAAM,YAAY;AAAA,QACpC,SAAS,EAAE,QAAQ,eAAe,QAAQ,MAAM;AAAA,MACpD,CAAC;AAED,aAAO;AAAA,IACX;AAGA,WAAO,iBAAiB,sBAAsB,CAAC,UAAU;AACrD,YAAM,MACF,MAAM,kBAAkB,QAClB,MAAM,OAAO,UACb,OAAO,MAAM,UAAU,6BAA6B;AAE9D,UAAI,KAAK,sBAAsB,GAAG,EAAG;AAErC,YAAM,MAAM,KAAK,cAAc,sBAAsB,GAAG;AACxD,UAAI,KAAK,gBAAgB,IAAI,GAAG,EAAG;AACnC,WAAK,gBAAgB,IAAI,GAAG;AAE5B,WAAK,KAAK,MAAM;AAAA,QACZ,QAAQ;AAAA,QACR,cAAc,IAAI,MAAM,GAAG;AAAA,MAC/B,CAAC;AAAA,IACL,CAAC;AAGD,UAAM,uBAAuB,QAAQ,MAAM,KAAK,OAAO;AACvD,YAAQ,QAAQ,IAAI,SAAoB;AACpC,2BAAqB,GAAG,IAAI;AAE5B,YAAM,MAAM,KAAK,IAAI,CAAC,MAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAE,EAAE,KAAK,GAAG;AAElF,UAAI,KAAK,sBAAsB,GAAG,EAAG;AAErC,YAAM,aAAa,IAAI,SAAS,MAAM,IAAI,MAAM,GAAG,GAAG,IAAI,QAAQ;AAClE,YAAM,MAAM,KAAK,cAAc,iBAAiB,UAAU;AAC1D,UAAI,KAAK,gBAAgB,IAAI,GAAG,EAAG;AACnC,WAAK,gBAAgB,IAAI,GAAG;AAE5B,YAAM,gBAAgB,KAAK,KAAK,CAAC,MAAkB,aAAa,KAAK;AAErE,YAAM,cAAc,iBAAiB,IAAI,MAAM,UAAU;AAEzD,WAAK,KAAK,MAAM;AAAA,QACZ,QAAQ;AAAA,QACR,cAAc;AAAA,MAClB,CAAC;AAAA,IACL;AAGA,WAAO;AAAA,MACH;AAAA,MACA,CAAC,UAAU;AACP,cAAM,SAAS,MAAM;AACrB,YAAI,EAAE,kBAAkB,aAAc;AACtC,YAAI,WAAY,OAAoB;AAEpC,cAAM,UAAU,OAAO,QAAQ,YAAY;AAE3C,cAAM,MACF,YAAY,SACL,OAA2B,OAC5B,YAAY,WACT,OAA6B,MAC9B;AAEZ,YAAI,KAAK,wBAAwB,KAAK,OAAO,EAAG;AAGhD,cAAM,MAAM,IAAI,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY;AAC5D,cAAM,eACF,QAAQ,QACF,QACA,QAAQ,OACN,OACA,QAAQ,UAAU,QAAQ,WAAW,QAAQ,QAC3C,SACA;AAEd,cAAM,aAAa,KAAK,QAAQ,GAAG;AACnC,cAAM,eAAe,kBAAkB,YAAY,KAAK,UAAU;AAClE,cAAM,MAAM,KAAK,cAAc,YAAY,YAAY;AACvD,YAAI,KAAK,gBAAgB,IAAI,GAAG,EAAG;AACnC,aAAK,gBAAgB,IAAI,GAAG;AAE5B,aAAK,KAAK,MAAM;AAAA,UACZ,QAAQ;AAAA,UACR,cAAc,IAAI,MAAM,YAAY;AAAA,UACpC,SAAS,EAAE,SAAS,KAAK,WAAW;AAAA,QACxC,CAAC;AAAA,MACL;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA,EAEQ,gBAAgB,KAAuD;AAC3E,WAAO,OAAO;AAAA,MACV,OAAO,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACtC,YAAI,UAAU,OAAW,QAAO,CAAC,KAAK,IAAI;AAC1C,YAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACtE,iBAAO,CAAC,KAAK,KAAK,gBAAgB,KAAgC,CAAC;AAAA,QACvE;AACA,eAAO,CAAC,KAAK,KAAK;AAAA,MACtB,CAAC;AAAA,IACL;AAAA,EACJ;AAAA,EAEQ,qBAAqB,KAAuD;AAChF,WAAO,OAAO;AAAA,MACV,OAAO,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACtC,YAAI,UAAU,OAAW,QAAO,CAAC,KAAK,IAAI;AAC1C,YAAI,OAAO,UAAU,WAAY,QAAO,CAAC,KAAK,YAAY;AAC1D,YAAI,iBAAiB,KAAM,QAAO,CAAC,KAAK,MAAM,YAAY,CAAC;AAC3D,YAAI,MAAM,QAAQ,KAAK,GAAG;AACtB,iBAAO;AAAA,YACH;AAAA,YACA,MAAM,IAAI,CAAC,SAAS;AAChB,kBAAI,gBAAgB,KAAM,QAAO,KAAK,YAAY;AAClD,kBAAI,OAAO,SAAS,WAAY,QAAO;AACvC,kBAAI,SAAS,QAAQ,OAAO,SAAS,UAAU;AAC3C,uBAAO,KAAK,qBAAqB,IAA+B;AAAA,cACpE;AACA,qBAAO,QAAQ;AAAA,YACnB,CAAC;AAAA,UACL;AAAA,QACJ;AACA,YAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC7C,iBAAO,CAAC,KAAK,KAAK,qBAAqB,KAAgC,CAAC;AAAA,QAC5E;AACA,eAAO,CAAC,KAAK,KAAK;AAAA,MACtB,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA,EAGA,iBAAiB;AACb,QAAI,OAAO,WAAW,YAAa;AAEnC,UAAM,gBAAgB,OAAO;AAE7B,UAAM,eAAe;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,UAAM,gBAAgB,CAAC,QAAyB;AAC5C,aAAO,aAAa,KAAK,CAAC,YAAY,IAAI,SAAS,OAAO,CAAC;AAAA,IAC/D;AAEA,WAAO,QAAQ,OAAO,OAA0B,SAA0C;AACtF,YAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,OAAO,MAAM;AAG1F,UAAI,cAAc,GAAG,GAAG;AACpB,eAAO,cAAc,OAAO,IAAI;AAAA,MACpC;AAEA,YAAM,SAAS,MAAM,UAAU;AAC/B,YAAM,YAAY,KAAK,IAAI;AAE3B,UAAI;AACA,cAAM,WAAW,MAAM,cAAc,OAAO,IAAI;AAChD,cAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,aAAK,cAAc,SAAS,GAAG,MAAM,IAAI,GAAG,IAAI;AAAA,UAC5C,QAAQ,SAAS;AAAA,UACjB,UAAU,GAAG,QAAQ;AAAA,QACzB,CAAC;AAED,YAAI,SAAS,UAAU,KAAK;AACxB,gBAAM,WAAW,KAAK,cAAc,kBAAkB,GAAG,MAAM,IAAI,GAAG,IAAI,SAAS,MAAM,EAAE;AAE3F,cAAI,CAAC,KAAK,gBAAgB,IAAI,QAAQ,GAAG;AACrC,iBAAK,gBAAgB,IAAI,QAAQ;AAEjC,gBAAI,eAAwB;AAC5B,gBAAI;AACA,oBAAM,SAAS,SAAS,MAAM;AAC9B,oBAAM,cAAc,OAAO,QAAQ,IAAI,cAAc,KAAK;AAC1D,6BAAe,YAAY,SAAS,kBAAkB,IAChD,MAAM,OAAO,KAAK,IAClB,MAAM,OAAO,KAAK;AAAA,YAC5B,QAAQ;AACJ,6BAAe;AAAA,YACnB;AAEA,kBAAM,cACF,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,aAAa;AAE7E,gBAAI,CAAC,aAAa;AACd,mBAAK,MAAM;AAAA,gBACP,QAAQ;AAAA,gBACR,cAAc,IAAI,MAAM,QAAQ,SAAS,MAAM,IAAI,SAAS,UAAU,WAAM,GAAG,EAAE;AAAA,gBACjF,cAAc;AAAA,gBACd,SAAS;AAAA,kBACL;AAAA,kBACA,QAAQ,SAAS;AAAA,kBACjB,YAAY,SAAS;AAAA,kBACrB,UAAU,GAAG,QAAQ;AAAA,kBACrB,OAAO,iBAAiB,GAAG;AAAA,kBAC3B,SAAS,iBAAiB,MAAM,IAAI;AAAA,kBACpC,UAAU;AAAA,gBACd;AAAA,cACJ,CAAC;AAAA,YACL;AAAA,UACJ;AAAA,QACJ;AAEA,eAAO;AAAA,MACX,SAAS,cAAc;AACnB,cAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,cAAM,QAAQ,wBAAwB,QAAQ,eAAe,IAAI,MAAM,OAAO,YAAY,CAAC;AAE3F,aAAK,cAAc,SAAS,GAAG,MAAM,IAAI,GAAG,yBAAoB;AAAA,UAC5D,UAAU,GAAG,QAAQ;AAAA,UACrB,OAAO,MAAM;AAAA,QACjB,CAAC;AAED,cAAM,WAAW,KAAK,cAAc,kBAAkB,GAAG,MAAM,IAAI,GAAG,gBAAgB;AAEtF,YAAI,CAAC,KAAK,gBAAgB,IAAI,QAAQ,GAAG;AACrC,eAAK,gBAAgB,IAAI,QAAQ;AAEjC,gBAAM,cACF,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,aAAa;AAC7E,cAAI,CAAC,aAAa;AACd,iBAAK,MAAM;AAAA,cACP,QAAQ;AAAA,cACR,cAAc;AAAA,cACd,cAAc;AAAA,cACd,SAAS,EAAE,QAAQ,UAAU,GAAG,QAAQ,KAAK;AAAA,YACjD,CAAC;AAAA,UACL;AAAA,QACJ;AAEA,cAAM;AAAA,MACV;AAAA,IACJ;AAEA,YAAQ,KAAK,+BAA+B;AAAA,EAChD;AAAA;AAAA,EAGA,eAAqB;AACjB,QAAI,OAAO,WAAW,YAAa;AAEnC,UAAM,eAAe;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,UAAM,gBAAgB,CAAC,QAAyB,aAAa,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;AAExF,UAAM,eAAe,eAAe,UAAU;AAC9C,UAAM,eAAe,eAAe,UAAU;AAC9C,UAAM,OAAO;AAQb,mBAAe,UAAU,OAAO,SAAuB,QAAgB,QAAsB,MAAiB;AAC1G,WAAK,YAAY;AACjB,WAAK,SAAS,OAAO,QAAQ,WAAW,MAAM,IAAI;AAElD,aAAO,aAAa,KAAK,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,IACvD;AAEA,mBAAe,UAAU,OAAO,YAA0B,MAAiB;AACvE,YAAM,MAAM,KAAK,UAAU;AAC3B,YAAM,SAAS,KAAK,aAAa;AAEjC,UAAI,cAAc,GAAG,GAAG;AAEpB,eAAO,aAAa,KAAK,MAAM,GAAG,IAAI;AAAA,MAC1C;AAEA,WAAK,eAAe,KAAK,IAAI;AAC7B,YAAM,cAAc,iBAAiB,KAAK,CAAC,CAAC;AAE5C,WAAK,iBAAiB,WAAW,MAAM;AACnC,cAAM,WAAW,KAAK,IAAI,KAAK,KAAK,gBAAgB,KAAK,IAAI;AAC7D,cAAM,SAAS,KAAK;AAEpB,aAAK,cAAc,SAAS,GAAG,MAAM,IAAI,GAAG,IAAI;AAAA,UAC5C;AAAA,UACA,UAAU,GAAG,QAAQ;AAAA,QACzB,CAAC;AAED,YAAI,UAAU,KAAK;AACf,gBAAM,WAAW,KAAK,cAAc,gBAAgB,GAAG,MAAM,IAAI,GAAG,IAAI,MAAM,EAAE;AAChF,cAAI,KAAK,gBAAgB,IAAI,QAAQ,EAAG;AACxC,eAAK,gBAAgB,IAAI,QAAQ;AAEjC,cAAI,eAAwB,KAAK;AACjC,cAAI;AACA,2BAAe,KAAK,MAAM,KAAK,YAAY;AAAA,UAC/C,QAAQ;AAAA,UAER;AAEA,gBAAM,cACF,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,aAAa;AAE7E,cAAI,CAAC,aAAa;AACd,iBAAK,KAAK,MAAM;AAAA,cACZ,QAAQ;AAAA,cACR,cAAc,IAAI,MAAM,QAAQ,MAAM,IAAI,KAAK,UAAU,WAAM,GAAG,EAAE;AAAA,cACpE,cAAc;AAAA,cACd,SAAS;AAAA,gBACL;AAAA,gBACA;AAAA,gBACA,YAAY,KAAK;AAAA,gBACjB,UAAU,GAAG,QAAQ;AAAA,gBACrB,OAAO,iBAAiB,GAAG;AAAA,gBAC3B,SAAS;AAAA,gBACT,UAAU;AAAA,cACd;AAAA,YACJ,CAAC;AAAA,UACL;AAAA,QACJ;AAAA,MACJ,CAAC;AAED,WAAK,iBAAiB,SAAS,MAAM;AAEjC,cAAM,WAAW,KAAK,IAAI,KAAK,KAAK,gBAAgB,KAAK,IAAI;AAC7D,cAAM,WAAW,KAAK,cAAc,gBAAgB,GAAG,MAAM,IAAI,GAAG,gBAAgB;AACpF,YAAI,KAAK,gBAAgB,IAAI,QAAQ,EAAG;AACxC,aAAK,gBAAgB,IAAI,QAAQ;AAEjC,cAAM,cACF,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,aAAa;AAE7E,YAAI,CAAC,aAAa;AACd,eAAK,KAAK,MAAM;AAAA,YACZ,QAAQ;AAAA,YACR,cAAc,IAAI,MAAM,wBAAmB,MAAM,IAAI,GAAG,EAAE;AAAA,YAC1D,cAAc;AAAA,YACd,SAAS,EAAE,QAAQ,UAAU,GAAG,QAAQ,KAAK;AAAA,UACjD,CAAC;AAAA,QACL;AAAA,MACJ,CAAC;AAGD,aAAO,aAAa,KAAK,MAAM,GAAG,IAAI;AAAA,IAC1C;AAEA,YAAQ,KAAK,6BAA6B;AAAA,EAC9C;AAAA,EAEA,iBAAuB;AACnB,QAAI,OAAO,WAAW,YAAa;AAEnC,QAAK,OAAe,uBAAwB;AAC5C,IAAC,OAAe,yBAAyB;AACzC,SAAK,wBAAwB;AAE7B,aAAS;AAAA,MACL;AAAA,MACA,CAAC,UAAsB;AACnB,cAAM,MAAM,MAAM;AAClB,YAAI,CAAC,IAAK;AAEV,cAAM,SAAU,IAAI,QAAQ,+CAA+C,KAAqB;AAEhG,cAAM,YAAY,oBAAoB,KAAK,MAAM;AACjD,cAAM,KAAK,OAAO,MAAM;AACxB,cAAM,UAAU,OAAO,SAAS,YAAY,KAAK;AAEjD,cAAM,gBACF,CAAC,UAAU,KAAK,OAAO,EAAE,SAAS,OAAO,KACzC,OAAO,aAAa,UAAU,KAC9B,OAAO,aAAa,MAAM,MAAM;AAEpC,cAAM,WAAW,mBAAmB,MAAM;AAC1C,cAAM,OAAO,YAAY,MAAM,OAAO,aAAa,MAAM,IAAI;AAE7D,YAAI,CAAC,aAAa,CAAC,MAAM,CAAC,cAAe;AAEzC,cAAM,UAAU,aAAa,MAAM,cAAc,WAAW,SAAS;AAGrE,YAAI,oBAAoB,KAAK,CAAC,MAAM,EAAE,KAAK,OAAO,CAAC,EAAG;AAEtD,aAAK,cAAc,SAAS,SAAS;AAAA,UACjC;AAAA,UACA,GAAI,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,UACnB,GAAI,YAAY,EAAE,MAAM,UAAU,IAAI,CAAC;AAAA,QAC3C,CAAC;AAED,cAAM,kBAAkB,KAAK,kBAAkB,MAAM;AACrD,cAAM,sBAAsB,KAAK,sBAAsB,MAAM;AAE7D,cAAM,YAAY,KAAK,IAAI;AAC3B,cAAM,qBAAqB,KAAK;AAChC,cAAM,sBAAsB;AAC5B,cAAM,uBAAuB;AAC7B,cAAM,yBAAyB;AAE/B,YAAI,CAAC,UAAU,KAAK,OAAO,EAAE,SAAS,OAAO,GAAG;AAC5C,qBAAW,MAAM;AAEb,gBAAI,KAAK,mBAAmB,oBAAoB;AAC5C,oBAAM,cAAc,KAAK,YACpB;AAAA,gBACG,CAAC,MACG,EAAE,SAAS,WACX,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK,aACnC,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK,YAAY;AAAA,cACvD,EACC,MAAM,EAAE,EAAE,CAAC;AAEhB,oBAAM,SAAsB,kBACtB,EAAE,MAAM,YAAY,QAAQ,4CAA4C,IACxE,cACE,EAAE,MAAM,aAAa,QAAQ,YAAY,QAAQ,IACjD,sBACE,EAAE,MAAM,oBAAoB,QAAQ,4CAA4C,IAChF;AAAA,gBACI,MAAM;AAAA,gBACN,QAAQ,wCAAwC,sBAAsB,GAAI;AAAA,cAC9E;AAEV,oBAAM,eAAe,eAAe;AAAA,gBAChC,QAAQ;AAAA,gBACR;AAAA,gBACA,MAAM;AAAA,gBACN;AAAA,gBACA;AAAA,cACJ,CAAC;AAED,mBAAK,cAAc,SAAS,6BAA6B,OAAO,IAAI;AAAA,gBAChE;AAAA,gBACA,GAAI,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,gBACnB;AAAA,cACJ,CAAC;AAGD,oBAAM,MAAM,KAAK,IAAI;AACrB,mBAAK,0BAA0B,KAAK,wBAAwB;AAAA,gBACxD,CAAC,MAAM,MAAM,IAAI,KAAK;AAAA,cAC1B;AAEA,kBAAI,KAAK,wBAAwB,UAAU,KAAK,2BAA2B;AACvE;AAAA,cACJ;AACA,mBAAK,wBAAwB,KAAK,GAAG;AAErC,mBAAK,KAAK,KAAK;AAAA,gBACX,QAAQ;AAAA,gBACR,cAAc,IAAI,MAAM,6BAA6B,OAAO,EAAE;AAAA,gBAC9D,SAAS;AAAA,kBACL;AAAA,kBACA,GAAI,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,kBACnB;AAAA,kBACA,MAAM;AAAA,kBACN;AAAA,kBACA,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,kBACvB;AAAA,gBACJ;AAAA,gBACA,wBAAwB,CAAC,OAAO;AAAA,gBAChC,oBAAoB;AAAA,gBACpB,mBAAmB;AAAA,gBACnB,iBAAiB;AAAA,cACrB,CAAC;AAAA,YACL;AAAA,UACJ,GAAG,mBAAmB;AAAA,QAC1B;AAGA,YAAI,eAAe;AACf,gBAAM,aAAa,GAAG,OAAO,KAAK,OAAO;AACzC,gBAAM,cAAc,KAAK,aAAa,IAAI,UAAU,KAAK,CAAC,GAAG;AAAA,YACzD,CAAC,MAAM,YAAY,IAAI;AAAA,UAC3B;AACA,qBAAW,KAAK,SAAS;AACzB,eAAK,aAAa,IAAI,YAAY,UAAU;AAE5C,gBAAM,YAAY,KAAK,eAAe,IAAI,UAAU,KAAK;AAEzD,cAAI,WAAW,UAAU,KAAK,YAAY,YAAY,wBAAwB;AAC1E,iBAAK,eAAe,IAAI,YAAY,SAAS;AAE7C,kBAAM,cAAc,KAAK,YACpB;AAAA,cACG,CAAC,MACG,EAAE,SAAS,WACX,YAAY,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK;AAAA,YACvD,EACC,MAAM,EAAE,EAAE,CAAC;AAEhB,kBAAM,iBAA8B,kBAC9B,EAAE,MAAM,YAAY,QAAQ,4CAA4C,IACxE,cACE,EAAE,MAAM,aAAa,QAAQ,YAAY,QAAQ,IACjD,sBACE,EAAE,MAAM,oBAAoB,QAAQ,4CAA4C,IAChF,KAAK,iBAAiB,SACpB,EAAE,MAAM,gBAAgB,QAAQ,KAAK,iBAAiB,MAAM,EAAE,EAAE,CAAC,EAAE,IACnE,EAAE,MAAM,eAAe,QAAQ,gDAAgD;AAE3F,kBAAM,eAAe,eAAe;AAAA,cAChC,QAAQ;AAAA,cACR;AAAA,cACA,MAAM;AAAA,cACN;AAAA,cACA,YAAY,WAAW;AAAA,cACvB,UAAU;AAAA,cACV,QAAQ;AAAA,YACZ,CAAC;AAED,iBAAK,cAAc,SAAS,uBAAuB,OAAO,IAAI;AAAA,cAC1D;AAAA,cACA,YAAY,WAAW;AAAA,cACvB,UAAU;AAAA,cACV;AAAA,YACJ,CAAC;AAED,iBAAK,KAAK,KAAK;AAAA,cACX,QAAQ;AAAA,cACR,cAAc,IAAI,MAAM,uBAAuB,OAAO,EAAE;AAAA,cACxD,SAAS;AAAA,gBACL;AAAA,gBACA,YAAY,WAAW;AAAA,gBACvB,UAAU;AAAA,gBACV;AAAA,gBACA,MAAM;AAAA,gBACN;AAAA,gBACA,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,gBACvB;AAAA,cACJ;AAAA,cACA,wBAAwB,CAAC,OAAO;AAAA,cAChC,oBAAoB;AAAA,cACpB,mBAAmB;AAAA,cACnB,iBAAiB;AAAA,YACrB,CAAC;AAAA,UACL;AAAA,QACJ;AAAA,MACJ;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,cAAiB,QAAmB,IAAkD;AAClF,WAAO,QAAQ,QAAQ,EAClB,KAAK,MAAM,GAAG,CAAC,EACf,MAAM,CAAC,UAAmB;AACvB,YAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,WAAK,KAAK,SAAS,OAAO,QAAQ,OAAO,SAAS,KAAK,OAAO,YAAY;AAC1E,aAAO;AAAA,IACX,CAAC;AAAA,EACT;AAAA,EAEA,WACI,QACA,IACA,SAIC;AACD,YAAQ,IAAI,SAAmD;AAC3D,UAAI;AACA,cAAM,SAAS,GAAG,GAAG,IAAI;AACzB,YAAI,kBAAkB,SAAS;AAC3B,iBAAO,OAAO,MAAM,CAAC,UAAmB;AACpC,kBAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,iBAAK,MAAM;AAAA,cACP;AAAA,cACA,cAAc;AAAA,cACd,cAAc,SAAS;AAAA,cACvB,SAAS,SAAS;AAAA,YACtB,CAAC;AACD,mBAAO;AAAA,UACX,CAAC;AAAA,QACL;AAEA,eAAO;AAAA,MACX,SAAS,OAAgB;AACrB,cAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,aAAK,MAAM;AAAA,UACP;AAAA,UACA,cAAc;AAAA,UACd,cAAc,SAAS;AAAA,UACvB,SAAS,SAAS;AAAA,QACtB,CAAC;AACD,eAAO;AAAA,MACX;AAAA,IACJ;AAAA,EACJ;AAAA,EAEQ,iBAAiB,YAAY,MAAM,SAAS,KAAoB;AACpE,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC5B,UAAI;AACJ,UAAI,UAAU;AAEd,YAAM,UAAU,MAAM;AAClB,YAAI,QAAS;AACb,kBAAU;AACV,qBAAa,SAAS;AACtB,qBAAa,WAAW;AACxB,eAAO,oBAAoB,UAAU,UAAU,IAAI;AACnD,gBAAQ;AAAA,MACZ;AAEA,YAAM,WAAW,MAAM;AACnB,qBAAa,SAAS;AACtB,oBAAY,WAAW,SAAS,MAAM;AAAA,MAC1C;AAGA,YAAM,cAAc,WAAW,SAAS,SAAS;AAEjD,aAAO,iBAAiB,UAAU,UAAU,IAAI;AAEhD,kBAAY,WAAW,SAAS,MAAM;AAAA,IAC1C,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAc,2BAA2B,iBAAuD;AAC5F,UAAM,oBAAoB,CAAC,CAAC,mBAAmB,SAAS,SAAS,eAAe;AAChF,UAAM,mBAAmB;AAEzB,QAAI;AACA,UAAI,mBAAmB;AACnB,wBAAiB,eAAe,EAAE,OAAO,UAAU,QAAQ,SAAS,CAAC;AACrE,cAAM,KAAK,iBAAiB;AAC5B,wBAAiB,aAAa,qBAAqB,gBAAgB;AAAA,MACvE;AAEA,YAAM,eAAe,MAAM,OAAO,iBAAiB,GAAG;AAEtD,YAAM,SAAS,MAAM,YAAY,SAAS,MAAM;AAAA,QAC5C,GAAG,OAAO;AAAA,QACV,GAAG,OAAO;AAAA,QACV,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,QACf,SAAS,CAAC,cAAc;AACpB,cAAI,CAAC,kBAAmB;AACxB,gBAAM,eAAe,UAAU,cAAc,uBAAuB,gBAAgB,IAAI;AACxF,cAAI,wBAAwB,aAAa;AACrC,yBAAa,MAAM,SAAS;AAC5B,yBAAa,MAAM,YAAY;AAAA,UACnC;AAAA,QACJ;AAAA,MACJ,CAAC;AAED,aAAO,OAAO,UAAU,cAAc,GAAG;AAAA,IAC7C,SAAS,GAAG;AACR,cAAQ,MAAM,0CAA0C,CAAC;AACzD,aAAO;AAAA,IACX,UAAE;AACE,uBAAiB,gBAAgB,mBAAmB;AAAA,IACxD;AAAA,EACJ;AAAA,EAEA,MAAc,KACV,OACA,SACA,UAAmC,CAAC,GACpC,OACA,UACA,wBACA,oBACA,mBACA,iBACF;AACE,QAAI,CAAC,KAAK,eAAe,CAAC,KAAK,QAAQ;AACnC,cAAQ,KAAK,wDAAwD;AACrE;AAAA,IACJ;AAEA,UAAM,cACF,OAAO,WAAW,gBACjB,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,aAAa;AAE9E,QAAI,aAAa;AACb,cAAQ,MAAM,uBAAuB,OAAO,IAAI,EAAE,SAAS,MAAM,CAAC;AAClE;AAAA,IACJ;AAEA,UAAM,MAAM,KAAK,IAAI;AAErB,QAAI,sBAAsB,wBAAwB,SAC5C,KAAK,YAAY,OAAO,CAAC,MAAM,CAAC,uBAAuB,SAAS,EAAE,IAAI,CAAC,IACvE,KAAK;AAEX,QAAI,oBAAoB;AACpB,4BAAsB,oBAAoB;AAAA,QACtC,CAAC,MAAM,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK;AAAA,MACpD;AAAA,IACJ;AAEA,UAAM,QAAkB;AAAA,MACpB,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA,KAAK,KAAK,OAAO;AAAA,MACjB,QAAQ,KAAK,OAAO,UAAU;AAAA,MAC9B;AAAA,MACA,MAAM,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAAA,MACjE,YAAY,OAAO,SAAS;AAAA,MAC5B,SAAS,KAAK,gBAAgB,OAAO;AAAA,MACrC,MAAM,KAAK,OAAO;AAAA,MAClB,UAAU,YAAY;AAAA,MACtB,aAAa,CAAC,GAAG,mBAAmB;AAAA,MACpC,GAAG,sBAAsB;AAAA,IAC7B;AAEA,QAAI;AACA,YAAMC,MAAK,MAAM;AACjB,YAAM,aAAS,2BAAI,8BAAWA,KAAI,MAAM,CAAC;AAEzC,YAAM,YAAY,KAAK,qBAAqB,EAAE,GAAG,MAAM,CAAuC;AAE9F,gBAAM,0BAAO,QAAQ;AAAA,QACjB,GAAG;AAAA,QACH,WAAW,4BAAU,SAAS,MAAM,SAAS;AAAA,MACjD,CAAC;AAED,UAAI,mBAAmB;AACnB,aAAK,KAAK,2BAA2B,eAAe,EAAE,KAAK,CAAC,WAAW;AACnE,cAAI,QAAQ;AACR,qBAAK,6BAAU,QAAQ,EAAE,kBAAkB,OAAO,CAAC,EAAE;AAAA,cAAM,CAAC,MACxD,QAAQ,MAAM,kDAAkD,CAAC;AAAA,YACrE;AAAA,UACJ;AAAA,QACJ,CAAC;AAAA,MACL;AAAA,IACJ,SAAS,GAAG;AACR,cAAQ,MAAM,mCAAmC,CAAC;AAAA,IACtD;AAAA,EACJ;AAAA,EAEA,KAAK,QAAmB;AACpB,UAAM,EAAE,QAAQ,cAAc,SAAS,aAAa,IAAI;AACxD,WAAO,KAAK,KAAK,QAAQ,cAAc,WAAW,QAAQ,EAAE,GAAG,SAAS,OAAO,GAAG,cAAc,YAAY;AAAA,EAChH;AAAA,EAEA,KAAK,QAAmB;AACpB,UAAM;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ,IAAI;AACJ,WAAO,KAAK;AAAA,MACR;AAAA,MACA,cAAc,WAAW;AAAA,MACzB,EAAE,GAAG,SAAS,OAAO;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,MAAM,QAAmB;AACrB,UAAM,EAAE,QAAQ,cAAc,SAAS,aAAa,IAAI;AACxD,WAAO,KAAK,KAAK,SAAS,cAAc,WAAW,QAAQ,EAAE,GAAG,SAAS,OAAO,GAAG,cAAc,YAAY;AAAA,EACjH;AACJ;AAEO,IAAM,SAAS,IAAI,OAAO;;;AIzoCjC,mBAA0D;AAuC1C;AA3BT,IAAM,sBAAN,cAAkC,uBAAwB;AAAA,EAC7D,YAAY,OAAc;AACtB,UAAM,KAAK;AACX,SAAK,QAAQ,EAAE,UAAU,MAAM;AAAA,EACnC;AAAA,EAEA,OAAO,2BAAkC;AACrC,WAAO,EAAE,UAAU,KAAK;AAAA,EAC5B;AAAA,EAEA,kBAAkB,OAAc,MAAiB;AAC7C,WAAO,cAAc,SAAS,gBAAgB,MAAM,OAAO,IAAI;AAAA,MAC3D,gBAAgB,KAAK,kBAAkB;AAAA,IAC3C,CAAC;AAED,WAAO,MAAM;AAAA,MACT,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,SAAS;AAAA,QACL,gBAAgB,KAAK,kBAAkB;AAAA,MAC3C;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EAEA,SAAS;AACL,QAAI,KAAK,MAAM,UAAU;AACrB,aAAO,KAAK,MAAM,YACd,4CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,OAAO,MAAM,GAAG,mCAE/C;AAAA,IAER;AAEA,WAAO,KAAK,MAAM;AAAA,EACtB;AACJ;","names":["import_firestore","app","db"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core/logger.ts","../src/config/firebase.ts","../src/core/environment.ts","../src/core/formatClickLog.ts","../src/components/LoggerErrorBoundary.tsx"],"sourcesContent":["export { logger } from \"./core/logger\";\nexport { LoggerErrorBoundary } from \"./components/LoggerErrorBoundary\";\nexport type { LogEntry, LoggerConfig, LogLevel, LogEnv, Breadcrumb, BreadcrumbType } from \"./types\";\n","import { doc, collection, setDoc, updateDoc, Timestamp } from \"firebase/firestore\";\r\nimport { initFirebase, getDb } from \"../config/firebase\";\r\nimport type { Breadcrumb, BreadcrumbType, ClickReason, LogEntry, LogLevel, LogParams, LoggerConfig } from \"../types\";\r\nimport { initEnvironment } from \"./environment\";\r\nimport { getEnvironmentPayload } from \"./environment\";\r\nimport { formatClickLog } from \"./formatClickLog\";\r\n\r\nconst sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\r\nconst MAX_BREADCRUMBS = 50;\r\n\r\nconst CLICK_SKIP_PATTERNS = [/^:r[a-z0-9]+:$/, /^\\[object/, /^#/];\r\n\r\n// Sanitize sebelum masuk Firestor\r\nconst sanitizeClickText = (text: string): string => {\r\n return text\r\n .replace(/\\b[\\w.-]+@[\\w.-]+\\.\\w+\\b/g, \"[email]\")\r\n .replace(/\\b\\d{10,}\\b/g, \"[number]\")\r\n .replace(/Rp[\\s]?\\d[\\d.,]*/gi, \"[amount]\")\r\n .trim();\r\n};\r\n\r\nconst getClosestClickText = (raw: HTMLElement, target: HTMLElement): string => {\r\n let el: HTMLElement | null = raw;\r\n\r\n // 1. innerText di sepanjang chain (existing)\r\n while (el) {\r\n const text = sanitizeClickText(el.innerText?.trim().slice(0, 50) ?? \"\");\r\n if (text) return text;\r\n if (el === target) break;\r\n el = el.parentElement;\r\n }\r\n\r\n // 2. aria-label / title di sepanjang chain\r\n el = raw;\r\n while (el) {\r\n const label = el.getAttribute(\"aria-label\") || el.getAttribute(\"title\");\r\n if (label) {\r\n const text = sanitizeClickText(label.trim().slice(0, 50));\r\n if (text) return text;\r\n }\r\n if (el === target) break;\r\n el = el.parentElement;\r\n }\r\n\r\n // 3. text dari child terdekat (span/p/div) di dalam target\r\n const childText = target.querySelector(\"span, p, div\")?.textContent?.trim();\r\n if (childText) {\r\n const text = sanitizeClickText(childText.slice(0, 50));\r\n if (text) return text;\r\n }\r\n\r\n // 4. text dari sibling terdekat\r\n const siblingText =\r\n target.previousElementSibling?.textContent?.trim() || target.nextElementSibling?.textContent?.trim();\r\n if (siblingText) {\r\n const text = sanitizeClickText(siblingText.slice(0, 50));\r\n if (text) return text;\r\n }\r\n\r\n return \"\";\r\n};\r\n\r\nconst getElementSelector = (el: HTMLElement): string => {\r\n const tag = el.tagName?.toLowerCase() ?? \"unknown\";\r\n\r\n if (el.id) return `${tag}#${el.id}`;\r\n\r\n const firstClass =\r\n typeof el.className === \"string\" && el.className.trim() ? el.className.trim().split(/\\s+/)[0] : \"\";\r\n if (firstClass) return `${tag}.${firstClass}`;\r\n\r\n const parent = el.parentElement;\r\n if (parent) {\r\n const index = Array.from(parent.children).indexOf(el) + 1;\r\n return `${tag}:nth-child(${index})`;\r\n }\r\n\r\n return tag;\r\n};\r\n\r\nconst parseRequestBody = (body: unknown): unknown => {\r\n if (body == null) return null;\r\n if (typeof body === \"string\") {\r\n try {\r\n return JSON.parse(body);\r\n } catch {\r\n return body;\r\n }\r\n }\r\n if (body instanceof FormData) {\r\n const result: Record<string, unknown> = {};\r\n body.forEach((value, key) => {\r\n result[key] = value;\r\n });\r\n return result;\r\n }\r\n if (body instanceof URLSearchParams) {\r\n const result: Record<string, string> = {};\r\n body.forEach((value, key) => {\r\n result[key] = value;\r\n });\r\n return result;\r\n }\r\n return \"[unsupported body type]\";\r\n};\r\n\r\nconst parseQueryParams = (url: string): Record<string, string> | null => {\r\n try {\r\n const params = new URL(url, window.location.origin).searchParams;\r\n const result: Record<string, string> = {};\r\n params.forEach((value, key) => {\r\n result[key] = value;\r\n });\r\n return Object.keys(result).length > 0 ? result : null;\r\n } catch {\r\n return null;\r\n }\r\n};\r\n\r\nclass Logger {\r\n private config: LoggerConfig | null = null;\r\n private initialized = false;\r\n private breadcrumbs: Breadcrumb[] = [];\r\n\r\n private lastDomActivity = Date.now();\r\n private clickHistory = new Map<string, number[]>();\r\n private rageClickFired = new Map<string, number>();\r\n private domObserverInitialized = false;\r\n\r\n private deadClickWarnTimestamps: number[] = [];\r\n private readonly DEAD_CLICK_RATE_LIMIT_WINDOW_MS = 60000;\r\n private readonly DEAD_CLICK_RATE_LIMIT_MAX = 5;\r\n\r\n private recentDomChanges: string[] = [];\r\n\r\n private isElementDisabled(target: HTMLElement): boolean {\r\n return (target as HTMLButtonElement).disabled === true || target.getAttribute(\"aria-disabled\") === \"true\";\r\n }\r\n\r\n // Helper: cek apakah ada field invalid di form terdekat dari target\r\n private hasInvalidFieldNearby(target: HTMLElement): boolean {\r\n const form = target.closest(\"form\");\r\n if (!form) return false;\r\n return form.querySelector('[aria-invalid=\"true\"]') !== null;\r\n }\r\n\r\n private initDomActivityObserver(): void {\r\n if (this.domObserverInitialized) return;\r\n this.domObserverInitialized = true;\r\n\r\n const ALERT_SELECTOR = '[role=\"alert\"], [aria-live], .MuiSnackbar-root, .MuiAlert-root';\r\n\r\n const getAlertElement = (el: HTMLElement | null): HTMLElement | null => {\r\n return el?.closest(ALERT_SELECTOR) ?? null;\r\n };\r\n\r\n const markActivity = (addedText?: string) => {\r\n this.lastDomActivity = Date.now();\r\n if (addedText) {\r\n this.recentDomChanges.push(addedText);\r\n setTimeout(() => {\r\n this.recentDomChanges = this.recentDomChanges.filter((t) => t !== addedText);\r\n }, 1000);\r\n }\r\n };\r\n\r\n const observer = new MutationObserver((mutations) => {\r\n markActivity();\r\n for (const m of mutations) {\r\n if (m.type === \"childList\") {\r\n m.addedNodes.forEach((node) => {\r\n if (node.nodeType !== Node.ELEMENT_NODE) return;\r\n const alertEl = getAlertElement(node as HTMLElement);\r\n if (!alertEl) return;\r\n const text = alertEl.innerText?.trim().slice(0, 200);\r\n if (text) markActivity(text);\r\n });\r\n } else if (m.type === \"characterData\") {\r\n const alertEl = getAlertElement(m.target.parentElement);\r\n if (!alertEl) continue;\r\n const text = alertEl.innerText?.trim().slice(0, 200);\r\n if (text) markActivity(text);\r\n }\r\n }\r\n });\r\n\r\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true });\r\n window.addEventListener(\"scroll\", () => markActivity(), { passive: true, capture: true });\r\n }\r\n\r\n init(config: LoggerConfig) {\r\n this.config = config;\r\n initFirebase(config);\r\n initEnvironment();\r\n this.initialized = true;\r\n console.info(\"[ga-logger] initialized —\", config.env);\r\n }\r\n setUserId(userId: string | null) {\r\n if (!this.config) {\r\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\r\n return;\r\n }\r\n this.config.userId = userId;\r\n }\r\n\r\n initAnonymousUser(): void {\r\n if (!this.config) {\r\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\r\n return;\r\n }\r\n\r\n const STORAGE_KEY = \"ga_anon_id\";\r\n\r\n try {\r\n let anonId = localStorage.getItem(STORAGE_KEY);\r\n\r\n if (!anonId) {\r\n anonId =\r\n typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\r\n ? crypto.randomUUID()\r\n : \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\r\n return v.toString(16);\r\n });\r\n\r\n localStorage.setItem(STORAGE_KEY, anonId);\r\n }\r\n\r\n const shortId = anonId.slice(-8);\r\n this.setUserId(`Anonymous #${shortId}`);\r\n } catch (e) {\r\n console.warn(\"[ga-logger] initAnonymousUser failed:\", e);\r\n this.setUserId(\"Anonymous\");\r\n }\r\n }\r\n\r\n // Add breadcrumb ke in-memory array\r\n addBreadcrumb(type: BreadcrumbType, message: string, data?: Record<string, unknown>) {\r\n const breadcrumb: Breadcrumb = {\r\n timestamp: new Date(),\r\n type,\r\n message,\r\n data,\r\n };\r\n\r\n this.breadcrumbs.push(breadcrumb);\r\n\r\n // Jaga memory — max 50 breadcrumbs, buang yang paling lama\r\n if (this.breadcrumbs.length > MAX_BREADCRUMBS) {\r\n this.breadcrumbs.shift();\r\n }\r\n }\r\n\r\n // Global error capture — hanya aktif di non-development\r\n private loggedErrorKeys = new Set<string>();\r\n\r\n // Helper: trim URL jadi domain/filename\r\n private trimUrl(url: string): string {\r\n try {\r\n const u = new URL(url);\r\n const filename = u.pathname.split(\"/\").filter(Boolean).pop() ?? u.pathname;\r\n return `${u.hostname}/${filename}`;\r\n } catch {\r\n // bukan valid URL, potong saja jika terlalu panjang\r\n return url.length > 80 ? url.slice(0, 80) + \"...\" : url;\r\n }\r\n }\r\n\r\n // Helper: build dedup key\r\n private buildErrorKey(type: string, message: string): string {\r\n return `${type}::${message}`;\r\n }\r\n\r\n // Helper: should skip resource error\r\n private shouldSkipResourceError(src: string, tagName: string): boolean {\r\n // Skip IMG — tidak sebabkan blank page\r\n if (tagName === \"IMG\") return true;\r\n\r\n // Skip base64 URL\r\n if (src.startsWith(\"data:\")) return true;\r\n\r\n // Skip known 3rd party trackers\r\n const THIRD_PARTY = [\r\n \"omguk.com\",\r\n \"track.\",\r\n \"doubleclick\",\r\n \"adservice\",\r\n \"googletagmanager\",\r\n \"google-analytics\",\r\n \"hotjar\",\r\n \"facebook.net\",\r\n \"analytics\",\r\n \"magnolia\",\r\n \"gtm.js\",\r\n ];\r\n if (THIRD_PARTY.some((p) => src.includes(p))) return true;\r\n\r\n return false;\r\n }\r\n\r\n private shouldSkipGlobalError(message: string): boolean {\r\n const NOISE = [\r\n \"CORS\",\r\n \"cross-origin\",\r\n \"ERR_FAILED\",\r\n \"Failed to load resource\",\r\n \"Failed to load image\",\r\n \"NetworkError\",\r\n \"ResizeObserver loop\",\r\n \"Script error\",\r\n \"ServiceWorker\",\r\n \"sw.js\",\r\n \"dev-sw.js\",\r\n \"Warning:\",\r\n \"Error loading image\",\r\n ];\r\n const THIRD_PARTY = [\r\n \"magnolia\",\r\n \"gtm.js\",\r\n \"analytics\",\r\n \"googletagmanager\",\r\n \"facebook\",\r\n \"hotjar\",\r\n \"omguk.com\",\r\n \"track.\",\r\n \"doubleclick\",\r\n \"adservice\",\r\n ];\r\n return NOISE.some((p) => message.includes(p)) || THIRD_PARTY.some((p) => message.includes(p));\r\n }\r\n\r\n initGlobalCapture(): void {\r\n const isLocalhost = window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n if (isLocalhost) return;\r\n\r\n window.onerror = (message, source, lineno, colno) => {\r\n const msg = typeof message === \"string\" ? message : \"Unknown error\";\r\n if (this.shouldSkipGlobalError(msg)) return false;\r\n\r\n const trimmedSource = source ? this.trimUrl(source) : \"unknown source\";\r\n const finalMessage = `${msg} at ${trimmedSource}:${lineno ?? 0}`;\r\n const key = this.buildErrorKey(\"onerror\", finalMessage);\r\n if (this.loggedErrorKeys.has(key)) return false;\r\n this.loggedErrorKeys.add(key);\r\n this.addBreadcrumb(\"error\", finalMessage, { source: trimmedSource, lineno, colno });\r\n\r\n void this.error({\r\n fnName: \"window.onerror\",\r\n messageError: new Error(finalMessage),\r\n payload: { source: trimmedSource, lineno, colno },\r\n });\r\n\r\n return false;\r\n };\r\n\r\n // unhandledrejection\r\n window.addEventListener(\"unhandledrejection\", (event) => {\r\n const msg =\r\n event.reason instanceof Error\r\n ? event.reason.message\r\n : String(event.reason ?? \"Unhandled Promise rejection\");\r\n\r\n if (this.shouldSkipGlobalError(msg)) return;\r\n\r\n const key = this.buildErrorKey(\"unhandledrejection\", msg);\r\n if (this.loggedErrorKeys.has(key)) return;\r\n this.loggedErrorKeys.add(key);\r\n this.addBreadcrumb(\"error\", msg);\r\n\r\n void this.error({\r\n fnName: \"window.unhandledrejection\",\r\n messageError: new Error(msg),\r\n });\r\n });\r\n\r\n // console.error override\r\n const originalConsoleError = console.error.bind(console);\r\n console.error = (...args: unknown[]) => {\r\n originalConsoleError(...args);\r\n\r\n const msg = args.map((a) => (a instanceof Error ? a.message : String(a))).join(\" \");\r\n\r\n if (this.shouldSkipGlobalError(msg)) return;\r\n\r\n const trimmedMsg = msg.length > 150 ? msg.slice(0, 150) + \"...\" : msg;\r\n const key = this.buildErrorKey(\"console.error\", trimmedMsg);\r\n if (this.loggedErrorKeys.has(key)) return;\r\n this.loggedErrorKeys.add(key);\r\n\r\n this.addBreadcrumb(\"error\", trimmedMsg);\r\n\r\n const originalError = args.find((a): a is Error => a instanceof Error);\r\n const errorToSend = originalError ?? new Error(trimmedMsg);\r\n\r\n void this.error({\r\n fnName: \"console.error\",\r\n messageError: errorToSend,\r\n });\r\n };\r\n\r\n // resource load error (CSS / JS / font)\r\n window.addEventListener(\r\n \"error\",\r\n (event) => {\r\n const target = event.target;\r\n if (!(target instanceof HTMLElement)) return;\r\n if (target === (window as unknown)) return;\r\n\r\n const tagName = target.tagName.toUpperCase();\r\n\r\n const src =\r\n tagName === \"LINK\"\r\n ? (target as HTMLLinkElement).href\r\n : tagName === \"SCRIPT\"\r\n ? (target as HTMLScriptElement).src\r\n : \"\";\r\n\r\n if (this.shouldSkipResourceError(src, tagName)) return;\r\n\r\n // Deteksi dari ekstensi file, bukan tagName\r\n const ext = src.split(\"?\")[0].split(\".\").pop()?.toLowerCase();\r\n const resourceType =\r\n ext === \"css\"\r\n ? \"CSS\"\r\n : ext === \"js\"\r\n ? \"JS\"\r\n : ext === \"woff\" || ext === \"woff2\" || ext === \"ttf\"\r\n ? \"FONT\"\r\n : tagName;\r\n\r\n const trimmedSrc = this.trimUrl(src);\r\n const finalMessage = `Failed to load ${resourceType}: ${trimmedSrc}`;\r\n const key = this.buildErrorKey(\"resource\", finalMessage);\r\n if (this.loggedErrorKeys.has(key)) return;\r\n this.loggedErrorKeys.add(key);\r\n\r\n void this.error({\r\n fnName: \"resource.onerror\",\r\n messageError: new Error(finalMessage),\r\n payload: { tagName, src: trimmedSrc },\r\n });\r\n },\r\n true,\r\n );\r\n }\r\n\r\n private sanitizePayload(obj: Record<string, unknown>): Record<string, unknown> {\r\n return Object.fromEntries(\r\n Object.entries(obj).map(([key, value]) => {\r\n if (value === undefined) return [key, null];\r\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\r\n return [key, this.sanitizePayload(value as Record<string, unknown>)];\r\n }\r\n return [key, value];\r\n }),\r\n );\r\n }\r\n\r\n private sanitizeForFirestore(obj: Record<string, unknown>): Record<string, unknown> {\r\n return Object.fromEntries(\r\n Object.entries(obj).map(([key, value]) => {\r\n if (value === undefined) return [key, null];\r\n if (typeof value === \"function\") return [key, \"[function]\"];\r\n if (value instanceof Date) return [key, value.toISOString()];\r\n if (Array.isArray(value)) {\r\n return [\r\n key,\r\n value.map((item) => {\r\n if (item instanceof Date) return item.toISOString();\r\n if (typeof item === \"function\") return \"[function]\";\r\n if (item !== null && typeof item === \"object\") {\r\n return this.sanitizeForFirestore(item as Record<string, unknown>);\r\n }\r\n return item ?? null;\r\n }),\r\n ];\r\n }\r\n if (value !== null && typeof value === \"object\") {\r\n return [key, this.sanitizeForFirestore(value as Record<string, unknown>)];\r\n }\r\n return [key, value];\r\n }),\r\n );\r\n }\r\n\r\n // interceptFetch — auto breadcrumb, ke Firestore\r\n interceptFetch() {\r\n if (typeof window === \"undefined\") return;\r\n\r\n const originalFetch = window.fetch;\r\n\r\n const skipPatterns = [\r\n \"google.com\",\r\n \"googletagmanager.com\",\r\n \"googleapis.com\",\r\n \"facebook.com\",\r\n \"hotjar.com\",\r\n \"analytics\",\r\n \"gtm\",\r\n \"ccm/collect\",\r\n \"on.aws\",\r\n \"/locales/\",\r\n \"/sw.js\",\r\n \"/manifest.json\",\r\n \"/sourcemaps/\",\r\n \"magnolia\",\r\n ];\r\n\r\n const shouldSkipUrl = (url: string): boolean => {\r\n return skipPatterns.some((pattern) => url.includes(pattern));\r\n };\r\n\r\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\r\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\r\n\r\n // Skip third-party domains\r\n if (shouldSkipUrl(url)) {\r\n return originalFetch(input, init);\r\n }\r\n\r\n const method = init?.method ?? \"GET\";\r\n const startTime = Date.now();\r\n\r\n try {\r\n const response = await originalFetch(input, init);\r\n const duration = Date.now() - startTime;\r\n\r\n this.addBreadcrumb(\"fetch\", `${method} ${url}`, {\r\n status: response.status,\r\n duration: `${duration}ms`,\r\n });\r\n\r\n if (response.status >= 400) {\r\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} ${response.status}`);\r\n\r\n if (!this.loggedErrorKeys.has(dedupKey)) {\r\n this.loggedErrorKeys.add(dedupKey);\r\n\r\n let responseBody: unknown = null;\r\n try {\r\n const cloned = response.clone();\r\n const contentType = cloned.headers.get(\"content-type\") ?? \"\";\r\n responseBody = contentType.includes(\"application/json\")\r\n ? await cloned.json()\r\n : await cloned.text();\r\n } catch {\r\n responseBody = \"[unable to read response body]\";\r\n }\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n\r\n if (!isLocalhost) {\r\n this.error({\r\n fnName: \"interceptFetch\",\r\n messageError: new Error(`HTTP ${response.status} ${response.statusText} — ${url}`),\r\n endpointName: url,\r\n payload: {\r\n method,\r\n status: response.status,\r\n statusText: response.statusText,\r\n duration: `${duration}ms`,\r\n query: parseQueryParams(url),\r\n request: parseRequestBody(init?.body),\r\n response: responseBody,\r\n },\r\n });\r\n }\r\n }\r\n }\r\n\r\n return response;\r\n } catch (networkError) {\r\n const duration = Date.now() - startTime;\r\n const error = networkError instanceof Error ? networkError : new Error(String(networkError));\r\n\r\n this.addBreadcrumb(\"fetch\", `${method} ${url} — NETWORK ERROR`, {\r\n duration: `${duration}ms`,\r\n error: error.message,\r\n });\r\n\r\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} NETWORK_ERROR`);\r\n\r\n if (!this.loggedErrorKeys.has(dedupKey)) {\r\n this.loggedErrorKeys.add(dedupKey);\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n if (!isLocalhost) {\r\n this.error({\r\n fnName: \"interceptFetch\",\r\n messageError: error,\r\n endpointName: url,\r\n payload: { method, duration: `${duration}ms` },\r\n });\r\n }\r\n }\r\n\r\n throw networkError;\r\n }\r\n };\r\n\r\n console.info(\"[ga-logger] fetch intercepted\");\r\n }\r\n\r\n // interceptXHR — auto breadcrumb + error capture untuk axios/XHR-based request\r\n interceptXHR(): void {\r\n if (typeof window === \"undefined\") return;\r\n\r\n const skipPatterns = [\r\n \"google.com\",\r\n \"googletagmanager.com\",\r\n \"googleapis.com\",\r\n \"facebook.com\",\r\n \"hotjar.com\",\r\n \"analytics\",\r\n \"ccm/collect\",\r\n \"on.aws\",\r\n \"/locales/\",\r\n \"/sw.js\",\r\n \"/manifest.json\",\r\n \"/sourcemaps/\",\r\n \"magnolia\",\r\n ];\r\n\r\n const shouldSkipUrl = (url: string): boolean => skipPatterns.some((p) => url.includes(p));\r\n\r\n const originalOpen = XMLHttpRequest.prototype.open;\r\n const originalSend = XMLHttpRequest.prototype.send;\r\n const self = this;\r\n\r\n type GaXHR = XMLHttpRequest & {\r\n _gaMethod?: string;\r\n _gaUrl?: string;\r\n _gaStartTime?: number;\r\n };\r\n\r\n XMLHttpRequest.prototype.open = function (this: GaXHR, method: string, url: string | URL, ...rest: unknown[]) {\r\n this._gaMethod = method;\r\n this._gaUrl = typeof url === \"string\" ? url : url.href;\r\n // @ts-expect-error - spreading native arguments\r\n return originalOpen.call(this, method, url, ...rest);\r\n };\r\n\r\n XMLHttpRequest.prototype.send = function (this: GaXHR, ...args: unknown[]) {\r\n const url = this._gaUrl ?? \"\";\r\n const method = this._gaMethod ?? \"GET\";\r\n\r\n if (shouldSkipUrl(url)) {\r\n // @ts-expect-error - spreading native arguments\r\n return originalSend.call(this, ...args);\r\n }\r\n\r\n this._gaStartTime = Date.now();\r\n const requestBody = parseRequestBody(args[0]);\r\n\r\n this.addEventListener(\"loadend\", () => {\r\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\r\n const status = this.status;\r\n\r\n self.addBreadcrumb(\"fetch\", `${method} ${url}`, {\r\n status,\r\n duration: `${duration}ms`,\r\n });\r\n\r\n if (status >= 400) {\r\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} ${status}`);\r\n if (self.loggedErrorKeys.has(dedupKey)) return;\r\n self.loggedErrorKeys.add(dedupKey);\r\n\r\n let responseBody: unknown = this.responseText;\r\n try {\r\n responseBody = JSON.parse(this.responseText);\r\n } catch {\r\n // keep as raw text\r\n }\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n\r\n if (!isLocalhost) {\r\n void self.error({\r\n fnName: \"interceptXHR\",\r\n messageError: new Error(`HTTP ${status} ${this.statusText} — ${url}`),\r\n endpointName: url,\r\n payload: {\r\n method,\r\n status,\r\n statusText: this.statusText,\r\n duration: `${duration}ms`,\r\n query: parseQueryParams(url),\r\n request: requestBody,\r\n response: responseBody,\r\n },\r\n });\r\n }\r\n }\r\n });\r\n\r\n this.addEventListener(\"error\", () => {\r\n // network-level failure (status selalu 0)\r\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\r\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} NETWORK_ERROR`);\r\n if (self.loggedErrorKeys.has(dedupKey)) return;\r\n self.loggedErrorKeys.add(dedupKey);\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n\r\n if (!isLocalhost) {\r\n void self.error({\r\n fnName: \"interceptXHR\",\r\n messageError: new Error(`Network error — ${method} ${url}`),\r\n endpointName: url,\r\n payload: { method, duration: `${duration}ms` },\r\n });\r\n }\r\n });\r\n\r\n // @ts-expect-error - spreading native arguments\r\n return originalSend.call(this, ...args);\r\n };\r\n\r\n console.info(\"[ga-logger] XHR intercepted\");\r\n }\r\n\r\n interceptClick(): void {\r\n if (typeof window === \"undefined\") return;\r\n\r\n if ((window as any).__ga_click_intercepted) return;\r\n (window as any).__ga_click_intercepted = true;\r\n this.initDomActivityObserver();\r\n\r\n document.addEventListener(\r\n \"click\",\r\n (event: MouseEvent) => {\r\n const raw = event.target as HTMLElement;\r\n if (!raw) return;\r\n\r\n const target = (raw.closest('button, a, [role=\"button\"], [data-log], input') as HTMLElement) ?? raw;\r\n\r\n const innerText = getClosestClickText(raw, target);\r\n const id = target.id ?? \"\";\r\n const tagName = target.tagName?.toLowerCase() ?? \"\";\r\n\r\n const isInteractive =\r\n [\"button\", \"a\", \"input\"].includes(tagName) ||\r\n target.hasAttribute(\"data-log\") ||\r\n target.getAttribute(\"role\") === \"button\";\r\n\r\n const selector = getElementSelector(target);\r\n const href = tagName === \"a\" ? target.getAttribute(\"href\") : null;\r\n\r\n if (!innerText && !id && !isInteractive) return;\r\n\r\n const message = innerText || id || `(no label: ${tagName || \"unknown\"})`;\r\n\r\n // Skip internal React/Router identifiers\r\n if (CLICK_SKIP_PATTERNS.some((p) => p.test(message))) return;\r\n\r\n this.addBreadcrumb(\"click\", message, {\r\n tagName,\r\n ...(id ? { id } : {}),\r\n ...(innerText ? { text: innerText } : {}),\r\n });\r\n\r\n const disabledAtClick = this.isElementDisabled(target);\r\n const invalidFieldAtClick = this.hasInvalidFieldNearby(target);\r\n const ariaDisabledAtClick = target.getAttribute(\"aria-disabled\") === \"true\";\r\n const pointerEventsBlockedAtClick = window.getComputedStyle(target).pointerEvents === \"none\";\r\n const overlayInterceptedAtClick = (() => {\r\n const elAtPoint = document.elementFromPoint(event.clientX, event.clientY);\r\n if (!elAtPoint || elAtPoint === target || target.contains(elAtPoint)) return null;\r\n return elAtPoint as HTMLElement;\r\n })();\r\n // ── Dead click detection ──\r\n const clickTime = Date.now();\r\n const domActivityAtClick = this.lastDomActivity;\r\n const DEAD_CLICK_DELAY_MS = 4000;\r\n const RAGE_CLICK_WINDOW_MS = 2000;\r\n const RAGE_CLICK_COOLDOWN_MS = 5000;\r\n\r\n if ([\"button\", \"a\", \"input\"].includes(tagName)) {\r\n setTimeout(() => {\r\n if (this.lastDomActivity <= domActivityAtClick) {\r\n const recentFetch = this.breadcrumbs\r\n .filter(\r\n (b) =>\r\n b.type === \"fetch\" &&\r\n new Date(b.timestamp).getTime() >= clickTime &&\r\n new Date(b.timestamp).getTime() <= clickTime + DEAD_CLICK_DELAY_MS,\r\n )\r\n .slice(-1)[0];\r\n\r\n const reason: ClickReason = disabledAtClick\r\n ? { type: \"disabled\", detail: \"Button dalam kondisi disabled saat diklik\" }\r\n : recentFetch\r\n ? { type: \"api_error\", detail: recentFetch.message }\r\n : invalidFieldAtClick\r\n ? { type: \"validation_error\", detail: \"Field invalid terdeteksi di form terdekat\" }\r\n : {\r\n type: \"no_dom_activity\",\r\n detail: `Tidak ada aktivitas DOM/scroll dalam ${DEAD_CLICK_DELAY_MS / 1000} detik`,\r\n };\r\n\r\n const formattedLog = formatClickLog({\r\n fnName: \"interceptClick.deadClick\",\r\n tagName,\r\n text: message,\r\n selector,\r\n reason,\r\n });\r\n\r\n this.addBreadcrumb(\"click\", `Button tidak bisa diklik: ${message}`, {\r\n tagName,\r\n ...(id ? { id } : {}),\r\n reason,\r\n });\r\n\r\n // ── Rate-limit: max N dead-click warn per session per window ──\r\n const now = Date.now();\r\n this.deadClickWarnTimestamps = this.deadClickWarnTimestamps.filter(\r\n (t) => now - t < this.DEAD_CLICK_RATE_LIMIT_WINDOW_MS,\r\n );\r\n\r\n if (this.deadClickWarnTimestamps.length >= this.DEAD_CLICK_RATE_LIMIT_MAX) {\r\n return;\r\n }\r\n this.deadClickWarnTimestamps.push(now);\r\n\r\n void this.warn({\r\n fnName: \"interceptClick.deadClick\",\r\n messageError: new Error(`Button tidak bisa diklik: ${message}`),\r\n payload: {\r\n tagName,\r\n ...(id ? { id } : {}),\r\n reason,\r\n text: message,\r\n selector,\r\n ...(href ? { href } : {}),\r\n formattedLog,\r\n },\r\n excludeBreadcrumbTypes: [\"fetch\"],\r\n breadcrumbWindowMs: 10000,\r\n captureScreenshot: true,\r\n highlightTarget: target,\r\n });\r\n }\r\n }, DEAD_CLICK_DELAY_MS);\r\n }\r\n\r\n // ── Rage click detection 3x klik ──\r\n if (isInteractive) {\r\n const historyKey = `${tagName}::${message}`;\r\n const timestamps = (this.clickHistory.get(historyKey) ?? []).filter(\r\n (t) => clickTime - t < RAGE_CLICK_WINDOW_MS,\r\n );\r\n timestamps.push(clickTime);\r\n this.clickHistory.set(historyKey, timestamps);\r\n\r\n const lastFired = this.rageClickFired.get(historyKey) ?? 0;\r\n\r\n if (timestamps.length >= 3 && clickTime - lastFired > RAGE_CLICK_COOLDOWN_MS) {\r\n this.rageClickFired.set(historyKey, clickTime);\r\n\r\n const recentFetch = this.breadcrumbs\r\n .filter(\r\n (b) =>\r\n b.type === \"fetch\" &&\r\n clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS,\r\n )\r\n .slice(-1)[0];\r\n\r\n const recentError = this.breadcrumbs\r\n .filter(\r\n (b) =>\r\n b.type === \"error\" &&\r\n clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS,\r\n )\r\n .slice(-1)[0];\r\n\r\n // rage-click possibleReason chain (urutan final)\r\n const possibleReason: ClickReason = disabledAtClick\r\n ? { type: \"disabled\", detail: \"Button dalam kondisi disabled saat diklik\" }\r\n : ariaDisabledAtClick\r\n ? {\r\n type: \"aria_disabled\",\r\n detail: 'Element memiliki aria-disabled=\"true\" (disabled secara visual/aksesibilitas, bukan native disabled attribute)',\r\n }\r\n : pointerEventsBlockedAtClick\r\n ? {\r\n type: \"pointer_events_blocked\",\r\n detail: \"Element memiliki CSS pointer-events: none saat diklik (tidak bisa menerima event klik sama sekali)\",\r\n }\r\n : overlayInterceptedAtClick\r\n ? {\r\n type: \"overlay_intercepted\",\r\n detail: `Element lain (${getElementSelector(overlayInterceptedAtClick)}) menutupi target saat klik terjadi, klik tidak sampai ke target aslinya`,\r\n }\r\n : recentFetch\r\n ? { type: \"api_error\", detail: recentFetch.message }\r\n : invalidFieldAtClick\r\n ? {\r\n type: \"validation_error\",\r\n detail: \"Field invalid terdeteksi di form terdekat\",\r\n }\r\n : recentError\r\n ? { type: \"js_error\", detail: recentError.message }\r\n : this.recentDomChanges.length\r\n ? { type: \"dom_feedback\", detail: this.recentDomChanges.slice(-1)[0] }\r\n : {\r\n type: \"no_feedback\",\r\n detail: \"Tidak ada feedback UI terdeteksi setelah klik\",\r\n };\r\n\r\n const formattedLog = formatClickLog({\r\n fnName: \"interceptClick.rageClick\",\r\n tagName,\r\n text: message,\r\n selector,\r\n clickCount: timestamps.length,\r\n windowMs: RAGE_CLICK_WINDOW_MS,\r\n reason: possibleReason,\r\n });\r\n\r\n this.addBreadcrumb(\"click\", `Klik Berulang kali: ${message}`, {\r\n tagName,\r\n clickCount: timestamps.length,\r\n windowMs: RAGE_CLICK_WINDOW_MS,\r\n possibleReason,\r\n });\r\n\r\n void this.warn({\r\n fnName: \"interceptClick.rageClick\",\r\n messageError: new Error(`Klik Berulang kali: ${message}`),\r\n payload: {\r\n tagName,\r\n clickCount: timestamps.length,\r\n windowMs: RAGE_CLICK_WINDOW_MS,\r\n possibleReason,\r\n text: message,\r\n selector,\r\n ...(href ? { href } : {}),\r\n formattedLog,\r\n },\r\n excludeBreadcrumbTypes: [\"fetch\"],\r\n breadcrumbWindowMs: 10000,\r\n captureScreenshot: true,\r\n highlightTarget: target,\r\n });\r\n }\r\n }\r\n },\r\n true,\r\n );\r\n }\r\n\r\n captureAction<T>(params: LogParams, fn: () => T | Promise<T>): Promise<T | undefined> {\r\n return Promise.resolve()\r\n .then(() => fn())\r\n .catch((error: unknown) => {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.send(\"error\", params.fnName, params.payload, err, params.endpointName);\r\n return undefined;\r\n });\r\n }\r\n\r\n withLogger<T extends (...args: any[]) => any>(\r\n fnName: string,\r\n fn: T,\r\n options?: {\r\n endpointName?: string;\r\n payload?: Record<string, unknown>;\r\n },\r\n ): T {\r\n return ((...args: Parameters<T>): ReturnType<T> | undefined => {\r\n try {\r\n const result = fn(...args);\r\n if (result instanceof Promise) {\r\n return result.catch((error: unknown) => {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.error({\r\n fnName,\r\n messageError: err,\r\n endpointName: options?.endpointName,\r\n payload: options?.payload,\r\n });\r\n return undefined;\r\n }) as ReturnType<T>;\r\n }\r\n\r\n return result;\r\n } catch (error: unknown) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.error({\r\n fnName,\r\n messageError: err,\r\n endpointName: options?.endpointName,\r\n payload: options?.payload,\r\n });\r\n return undefined;\r\n }\r\n }) as T;\r\n }\r\n\r\n private waitForScrollEnd(timeoutMs = 1500, idleMs = 100): Promise<void> {\r\n return new Promise((resolve) => {\r\n let idleTimer: ReturnType<typeof setTimeout>;\r\n let settled = false;\r\n\r\n const cleanup = () => {\r\n if (settled) return;\r\n settled = true;\r\n clearTimeout(idleTimer);\r\n clearTimeout(hardTimeout);\r\n window.removeEventListener(\"scroll\", onScroll, true);\r\n resolve();\r\n };\r\n\r\n const onScroll = () => {\r\n clearTimeout(idleTimer);\r\n idleTimer = setTimeout(cleanup, idleMs);\r\n };\r\n\r\n // fallback: if no scroll event ever fires (target already in view), don't hang\r\n const hardTimeout = setTimeout(cleanup, timeoutMs);\r\n\r\n window.addEventListener(\"scroll\", onScroll, true);\r\n // start the idle timer immediately too, in case scrollIntoView is a no-op\r\n idleTimer = setTimeout(cleanup, idleMs);\r\n });\r\n }\r\n\r\n // private createHighlightOverlay(target: HTMLElement): HTMLDivElement {\r\n // const rect = target.getBoundingClientRect();\r\n // const padding = 0;\r\n\r\n // const overlay = document.createElement(\"div\");\r\n // overlay.style.position = \"absolute\";\r\n // overlay.style.top = `${rect.top + window.scrollY - padding}px`;\r\n // overlay.style.left = `${rect.left + window.scrollX - padding}px`;\r\n // overlay.style.width = `${rect.width + padding * 2}px`;\r\n // overlay.style.height = `${rect.height + padding * 2}px`;\r\n // overlay.style.border = \"2px solid red\";\r\n // overlay.style.pointerEvents = \"none\";\r\n // overlay.style.zIndex = \"999999\";\r\n\r\n // document.body.appendChild(overlay);\r\n // return overlay;\r\n // }\r\n\r\n private async captureAndUploadScreenshot(highlightTarget?: HTMLElement): Promise<string | null> {\r\n const canApplyHighlight = !!highlightTarget && document.contains(highlightTarget);\r\n const HIGHLIGHT_MARKER = \"ga-logger-highlight-target\";\r\n\r\n try {\r\n if (canApplyHighlight) {\r\n highlightTarget!.scrollIntoView({ block: \"center\", inline: \"center\" });\r\n await this.waitForScrollEnd();\r\n highlightTarget!.setAttribute(\"data-ga-highlight\", HIGHLIGHT_MARKER);\r\n }\r\n\r\n const html2canvas = (await import(\"html2canvas-pro\")).default;\r\n\r\n const canvas = await html2canvas(document.body, {\r\n x: window.scrollX,\r\n y: window.scrollY,\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n imageTimeout: 3000,\r\n onclone: (clonedDoc) => {\r\n if (!canApplyHighlight) return;\r\n const clonedTarget = clonedDoc.querySelector(`[data-ga-highlight=\"${HIGHLIGHT_MARKER}\"]`);\r\n if (clonedTarget instanceof HTMLElement) {\r\n clonedTarget.style.border = \"2px solid red\";\r\n clonedTarget.style.boxSizing = \"border-box\";\r\n }\r\n },\r\n });\r\n return canvas.toDataURL(\"image/jpeg\", 0.4);\r\n } catch (e) {\r\n console.error(\"[ga-logger] screenshot capture failed:\", e);\r\n return null;\r\n } finally {\r\n highlightTarget?.removeAttribute(\"data-ga-highlight\");\r\n }\r\n }\r\n\r\n private async send(\r\n level: LogLevel,\r\n message: string,\r\n payload: Record<string, unknown> = {},\r\n error?: Error,\r\n endpoint?: string,\r\n excludeBreadcrumbTypes?: BreadcrumbType[],\r\n breadcrumbWindowMs?: number,\r\n captureScreenshot?: boolean,\r\n highlightTarget?: HTMLElement,\r\n ) {\r\n if (!this.initialized || !this.config) {\r\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\r\n return;\r\n }\r\n\r\n const isLocalhost =\r\n typeof window !== \"undefined\" &&\r\n (window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\");\r\n\r\n if (isLocalhost) {\r\n console.error(`[ga-localhost][DEV] ${message}`, { payload, error });\r\n return;\r\n }\r\n\r\n const now = Date.now();\r\n\r\n let filteredBreadcrumbs = excludeBreadcrumbTypes?.length\r\n ? this.breadcrumbs.filter((b) => !excludeBreadcrumbTypes.includes(b.type))\r\n : this.breadcrumbs;\r\n\r\n if (breadcrumbWindowMs) {\r\n filteredBreadcrumbs = filteredBreadcrumbs.filter(\r\n (b) => now - new Date(b.timestamp).getTime() <= breadcrumbWindowMs,\r\n );\r\n }\r\n\r\n const entry: LogEntry = {\r\n timestamp: new Date(),\r\n level,\r\n message,\r\n env: this.config.env,\r\n userId: this.config.userId ?? null,\r\n sessionId,\r\n page: typeof window !== \"undefined\" ? window.location.pathname : \"\",\r\n stackTrace: error?.stack ?? null,\r\n payload: this.sanitizePayload(payload),\r\n repo: this.config.repo,\r\n endpoint: endpoint ?? null,\r\n breadcrumbs: [...filteredBreadcrumbs],\r\n ...getEnvironmentPayload(),\r\n };\r\n\r\n try {\r\n const db = getDb();\r\n const docRef = doc(collection(db, \"logs\"));\r\n\r\n const safeEntry = this.sanitizeForFirestore({ ...entry } as unknown as Record<string, unknown>);\r\n\r\n await setDoc(docRef, {\r\n ...safeEntry,\r\n timestamp: Timestamp.fromDate(entry.timestamp),\r\n });\r\n\r\n if (captureScreenshot) {\r\n void this.captureAndUploadScreenshot(highlightTarget).then((base64) => {\r\n if (base64) {\r\n void updateDoc(docRef, { screenshotBase64: base64 }).catch((e) =>\r\n console.error(\"[ga-logger] failed to update screenshotBase64:\", e),\r\n );\r\n }\r\n });\r\n }\r\n } catch (e) {\r\n console.error(\"[ga-logger] failed to send log:\", e);\r\n }\r\n }\r\n\r\n info(params: LogParams) {\r\n const { fnName, messageError, payload, endpointName } = params;\r\n return this.send(\"info\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\r\n }\r\n\r\n warn(params: LogParams) {\r\n const {\r\n fnName,\r\n messageError,\r\n payload,\r\n endpointName,\r\n excludeBreadcrumbTypes,\r\n breadcrumbWindowMs,\r\n captureScreenshot,\r\n highlightTarget,\r\n } = params;\r\n return this.send(\r\n \"warn\",\r\n messageError?.message ?? fnName,\r\n { ...payload, fnName },\r\n messageError,\r\n endpointName,\r\n excludeBreadcrumbTypes,\r\n breadcrumbWindowMs,\r\n captureScreenshot,\r\n highlightTarget,\r\n );\r\n }\r\n\r\n error(params: LogParams) {\r\n const { fnName, messageError, payload, endpointName } = params;\r\n return this.send(\"error\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\r\n }\r\n}\r\n\r\nexport const logger = new Logger();","import { initializeApp, getApps, getApp } from \"firebase/app\";\nimport { getFirestore } from \"firebase/firestore\";\nimport type { Firestore } from \"firebase/firestore\";\nimport type { LoggerConfig } from \"../types\";\n\nconst APP_NAME = \"ga-logger\";\nlet db: Firestore | null = null;\n\nexport const initFirebase = (config: LoggerConfig) => {\n // Cek apakah named instance \"ga-logger\" sudah ada\n const existingApp = getApps().find((app) => app.name === APP_NAME);\n\n const app =\n existingApp ??\n initializeApp(\n {\n apiKey: config.apiKey,\n authDomain: config.authDomain,\n projectId: config.projectId,\n storageBucket: config.storageBucket,\n messagingSenderId: config.messagingSenderId,\n appId: config.appId,\n },\n APP_NAME,\n );\n\n db = getFirestore(app);\n return db;\n};\n\nexport const getDb = (): Firestore => {\n if (!db) throw new Error(\"[ga-logger] Firebase not initialized. Call logger.init() first.\");\n return db;\n};\n","interface EnvironmentPayload {\n userAgent: string;\n deviceType: \"Mobile\" | \"Tablet\" | \"Desktop\";\n screenWidth: number;\n screenHeight: number;\n}\n\nlet cachedEnvironment: EnvironmentPayload | null = null;\n\nfunction getDeviceType(width: number): \"Mobile\" | \"Tablet\" | \"Desktop\" {\n if (width < 768) return \"Mobile\";\n if (width <= 1024) return \"Tablet\";\n return \"Desktop\";\n}\n\nexport function initEnvironment(): void {\n if (cachedEnvironment) return;\n\n const width = window.innerWidth;\n const height = window.innerHeight;\n\n cachedEnvironment = {\n userAgent: navigator.userAgent,\n deviceType: getDeviceType(width),\n screenWidth: width,\n screenHeight: height,\n };\n}\n\nexport function getEnvironmentPayload(): EnvironmentPayload {\n if (!cachedEnvironment) initEnvironment();\n return cachedEnvironment!;\n}","import type { ClickReason, ClickReasonType } from \"../types\";\n\nconst REASON_LABEL: Record<ClickReasonType, string> = {\n disabled: \"Button sedang disabled saat diklik\",\n api_error: \"Request API gagal\",\n validation_error: \"Ada field invalid di form terdekat (validasi gagal)\",\n dom_feedback: \"Muncul notifikasi\",\n no_feedback: \"Tidak ada notifikasi/feedback UI apapun setelah diklik\",\n no_dom_activity: \"Tidak ada aktivitas DOM/scroll setelah diklik\",\n};\n\nconst SCREENSHOT_HINT: Partial<Record<ClickReasonType, string>> = {\n dom_feedback: \"📷 Lihat screenshot untuk detail visual alert\",\n no_feedback: \"📷 Cek screenshot — kemungkinan UI freeze/silent fail\",\n no_dom_activity: \"📷 Cek screenshot — kondisi layar saat klik terjadi\",\n};\n\ninterface ClickLogPayload {\n fnName: string;\n tagName: string;\n text: string;\n selector: string;\n clickCount?: number;\n windowMs?: number;\n reason: ClickReason;\n}\n\nconst buildDiagnosis = (reason: ClickReason): string => {\n const label = REASON_LABEL[reason.type];\n const hint = SCREENSHOT_HINT[reason.type];\n\n const base =\n reason.type === \"api_error\"\n ? `${label} — ${reason.detail}`\n : reason.type === \"dom_feedback\"\n ? `${label} — \"${reason.detail}\"`\n : label;\n\n return hint ? `${base}\\n ${hint}` : base;\n};\n\nconst formatSelectorAsHtml = (selector: string, tagName: string): string => {\n if (selector.includes(\".\")) {\n const className = selector.split(\".\").slice(1).join(\" \");\n return `class=\"${className}\"`;\n }\n if (selector.includes(\"#\")) {\n const id = selector.split(\"#\")[1];\n return `id=\"${id}\"`;\n }\n return \"\";\n};\n\nexport const formatClickLog = (payload: ClickLogPayload): string => {\n const isRageClick = payload.fnName.includes(\"rageClick\");\n const clickTypeLabel = isRageClick ? \"Klik Berulang\" : \"Klik Tanpa Respon\";\n const clickTypeSuffix = isRageClick ? \"(Rage Click)\" : \"(Dead Click)\";\n\n const stats = isRageClick\n ? `${payload.clickCount}x klik dalam ${payload.windowMs}ms`\n : `Terdeteksi setelah klik tanpa aktivitas`;\n\n const attr = formatSelectorAsHtml(payload.selector, payload.tagName);\n const openTag = attr ? `<${payload.tagName} ${attr}>` : `<${payload.tagName}>`;\n const elementHtml = payload.text ? `${openTag}${payload.text}</${payload.tagName}>` : `${openTag}</${payload.tagName}>`;\n\n return [\n `Summary : ${clickTypeLabel} pada ${payload.tagName} \"${payload.text}\" ${clickTypeSuffix}`,\n `Diagnosis : ${buildDiagnosis(payload.reason)}`,\n `Stats : ${stats}`,\n `Element : ${elementHtml}`,\n `Source : ${payload.fnName}`,\n ].join(\"\\n\");\n};\n\n","import { Component, type ErrorInfo, type ReactNode } from \"react\";\nimport { logger } from \"../core/logger\";\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n}\n\ninterface State {\n hasError: boolean;\n}\n\nexport class LoggerErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(): State {\n return { hasError: true };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n logger.addBreadcrumb(\"error\", `React crash: ${error.message}`, {\n componentStack: info.componentStack ?? undefined,\n });\n\n logger.error({\n fnName: \"LoggerErrorBoundary\",\n messageError: error,\n payload: {\n componentStack: info.componentStack ?? undefined,\n },\n });\n }\n\n render() {\n if (this.state.hasError) {\n return this.props.fallback ?? (\n <div style={{ padding: \"1rem\", color: \"red\" }}>\n Something went wrong.\n </div>\n );\n }\n\n return this.props.children;\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,oBAA8D;;;ACA9D,iBAA+C;AAC/C,uBAA6B;AAI7B,IAAM,WAAW;AACjB,IAAI,KAAuB;AAEpB,IAAM,eAAe,CAAC,WAAyB;AAElD,QAAM,kBAAc,oBAAQ,EAAE,KAAK,CAACC,SAAQA,KAAI,SAAS,QAAQ;AAEjE,QAAM,MACF,mBACA;AAAA,IACI;AAAA,MACI,QAAQ,OAAO;AAAA,MACf,YAAY,OAAO;AAAA,MACnB,WAAW,OAAO;AAAA,MAClB,eAAe,OAAO;AAAA,MACtB,mBAAmB,OAAO;AAAA,MAC1B,OAAO,OAAO;AAAA,IAClB;AAAA,IACA;AAAA,EACJ;AAEJ,WAAK,+BAAa,GAAG;AACrB,SAAO;AACX;AAEO,IAAM,QAAQ,MAAiB;AAClC,MAAI,CAAC,GAAI,OAAM,IAAI,MAAM,iEAAiE;AAC1F,SAAO;AACX;;;AC1BA,IAAI,oBAA+C;AAEnD,SAAS,cAAc,OAAgD;AACnE,MAAI,QAAQ,IAAK,QAAO;AACxB,MAAI,SAAS,KAAM,QAAO;AAC1B,SAAO;AACX;AAEO,SAAS,kBAAwB;AACpC,MAAI,kBAAmB;AAEvB,QAAM,QAAQ,OAAO;AACrB,QAAM,SAAS,OAAO;AAEtB,sBAAoB;AAAA,IAChB,WAAW,UAAU;AAAA,IACrB,YAAY,cAAc,KAAK;AAAA,IAC/B,aAAa;AAAA,IACb,cAAc;AAAA,EAClB;AACJ;AAEO,SAAS,wBAA4C;AACxD,MAAI,CAAC,kBAAmB,iBAAgB;AACxC,SAAO;AACX;;;AC9BA,IAAM,eAAgD;AAAA,EAClD,UAAU;AAAA,EACV,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,aAAa;AAAA,EACb,iBAAiB;AACrB;AAEA,IAAM,kBAA4D;AAAA,EAC9D,cAAc;AAAA,EACd,aAAa;AAAA,EACb,iBAAiB;AACrB;AAYA,IAAM,iBAAiB,CAAC,WAAgC;AACpD,QAAM,QAAQ,aAAa,OAAO,IAAI;AACtC,QAAM,OAAO,gBAAgB,OAAO,IAAI;AAExC,QAAM,OACF,OAAO,SAAS,cACV,GAAG,KAAK,WAAM,OAAO,MAAM,KAC3B,OAAO,SAAS,iBACd,GAAG,KAAK,YAAO,OAAO,MAAM,MAC5B;AAEZ,SAAO,OAAO,GAAG,IAAI;AAAA,cAAiB,IAAI,KAAK;AACnD;AAEA,IAAM,uBAAuB,CAAC,UAAkB,YAA4B;AACxE,MAAI,SAAS,SAAS,GAAG,GAAG;AACxB,UAAM,YAAY,SAAS,MAAM,GAAG,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG;AACvD,WAAO,UAAU,SAAS;AAAA,EAC9B;AACA,MAAI,SAAS,SAAS,GAAG,GAAG;AACxB,UAAM,KAAK,SAAS,MAAM,GAAG,EAAE,CAAC;AAChC,WAAO,OAAO,EAAE;AAAA,EACpB;AACA,SAAO;AACX;AAEO,IAAM,iBAAiB,CAAC,YAAqC;AAChE,QAAM,cAAc,QAAQ,OAAO,SAAS,WAAW;AACvD,QAAM,iBAAiB,cAAc,kBAAkB;AACvD,QAAM,kBAAkB,cAAc,iBAAiB;AAEvD,QAAM,QAAQ,cACR,GAAG,QAAQ,UAAU,gBAAgB,QAAQ,QAAQ,OACrD;AAEN,QAAM,OAAO,qBAAqB,QAAQ,UAAU,QAAQ,OAAO;AACnE,QAAM,UAAU,OAAO,IAAI,QAAQ,OAAO,IAAI,IAAI,MAAM,IAAI,QAAQ,OAAO;AAC3E,QAAM,cAAc,QAAQ,OAAO,GAAG,OAAO,GAAG,QAAQ,IAAI,KAAK,QAAQ,OAAO,MAAM,GAAG,OAAO,KAAK,QAAQ,OAAO;AAEpH,SAAO;AAAA,IACH,eAAe,cAAc,SAAS,QAAQ,OAAO,KAAK,QAAQ,IAAI,KAAK,eAAe;AAAA,IAC1F,eAAe,eAAe,QAAQ,MAAM,CAAC;AAAA,IAC7C,eAAe,KAAK;AAAA,IACpB,eAAe,WAAW;AAAA,IAC1B,eAAe,QAAQ,MAAM;AAAA,EACjC,EAAE,KAAK,IAAI;AACf;;;AHlEA,IAAM,YAAY,QAAQ,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC9E,IAAM,kBAAkB;AAExB,IAAM,sBAAsB,CAAC,kBAAkB,aAAa,IAAI;AAGhE,IAAM,oBAAoB,CAAC,SAAyB;AAChD,SAAO,KACF,QAAQ,6BAA6B,SAAS,EAC9C,QAAQ,gBAAgB,UAAU,EAClC,QAAQ,sBAAsB,UAAU,EACxC,KAAK;AACd;AAEA,IAAM,sBAAsB,CAAC,KAAkB,WAAgC;AAC3E,MAAI,KAAyB;AAG7B,SAAO,IAAI;AACP,UAAM,OAAO,kBAAkB,GAAG,WAAW,KAAK,EAAE,MAAM,GAAG,EAAE,KAAK,EAAE;AACtE,QAAI,KAAM,QAAO;AACjB,QAAI,OAAO,OAAQ;AACnB,SAAK,GAAG;AAAA,EACZ;AAGA,OAAK;AACL,SAAO,IAAI;AACP,UAAM,QAAQ,GAAG,aAAa,YAAY,KAAK,GAAG,aAAa,OAAO;AACtE,QAAI,OAAO;AACP,YAAM,OAAO,kBAAkB,MAAM,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC;AACxD,UAAI,KAAM,QAAO;AAAA,IACrB;AACA,QAAI,OAAO,OAAQ;AACnB,SAAK,GAAG;AAAA,EACZ;AAGA,QAAM,YAAY,OAAO,cAAc,cAAc,GAAG,aAAa,KAAK;AAC1E,MAAI,WAAW;AACX,UAAM,OAAO,kBAAkB,UAAU,MAAM,GAAG,EAAE,CAAC;AACrD,QAAI,KAAM,QAAO;AAAA,EACrB;AAGA,QAAM,cACF,OAAO,wBAAwB,aAAa,KAAK,KAAK,OAAO,oBAAoB,aAAa,KAAK;AACvG,MAAI,aAAa;AACb,UAAM,OAAO,kBAAkB,YAAY,MAAM,GAAG,EAAE,CAAC;AACvD,QAAI,KAAM,QAAO;AAAA,EACrB;AAEA,SAAO;AACX;AAEA,IAAM,qBAAqB,CAAC,OAA4B;AACpD,QAAM,MAAM,GAAG,SAAS,YAAY,KAAK;AAEzC,MAAI,GAAG,GAAI,QAAO,GAAG,GAAG,IAAI,GAAG,EAAE;AAEjC,QAAM,aACF,OAAO,GAAG,cAAc,YAAY,GAAG,UAAU,KAAK,IAAI,GAAG,UAAU,KAAK,EAAE,MAAM,KAAK,EAAE,CAAC,IAAI;AACpG,MAAI,WAAY,QAAO,GAAG,GAAG,IAAI,UAAU;AAE3C,QAAM,SAAS,GAAG;AAClB,MAAI,QAAQ;AACR,UAAM,QAAQ,MAAM,KAAK,OAAO,QAAQ,EAAE,QAAQ,EAAE,IAAI;AACxD,WAAO,GAAG,GAAG,cAAc,KAAK;AAAA,EACpC;AAEA,SAAO;AACX;AAEA,IAAM,mBAAmB,CAAC,SAA2B;AACjD,MAAI,QAAQ,KAAM,QAAO;AACzB,MAAI,OAAO,SAAS,UAAU;AAC1B,QAAI;AACA,aAAO,KAAK,MAAM,IAAI;AAAA,IAC1B,QAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AACA,MAAI,gBAAgB,UAAU;AAC1B,UAAM,SAAkC,CAAC;AACzC,SAAK,QAAQ,CAAC,OAAO,QAAQ;AACzB,aAAO,GAAG,IAAI;AAAA,IAClB,CAAC;AACD,WAAO;AAAA,EACX;AACA,MAAI,gBAAgB,iBAAiB;AACjC,UAAM,SAAiC,CAAC;AACxC,SAAK,QAAQ,CAAC,OAAO,QAAQ;AACzB,aAAO,GAAG,IAAI;AAAA,IAClB,CAAC;AACD,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAEA,IAAM,mBAAmB,CAAC,QAA+C;AACrE,MAAI;AACA,UAAM,SAAS,IAAI,IAAI,KAAK,OAAO,SAAS,MAAM,EAAE;AACpD,UAAM,SAAiC,CAAC;AACxC,WAAO,QAAQ,CAAC,OAAO,QAAQ;AAC3B,aAAO,GAAG,IAAI;AAAA,IAClB,CAAC;AACD,WAAO,OAAO,KAAK,MAAM,EAAE,SAAS,IAAI,SAAS;AAAA,EACrD,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEA,IAAM,SAAN,MAAa;AAAA,EAAb;AACI,SAAQ,SAA8B;AACtC,SAAQ,cAAc;AACtB,SAAQ,cAA4B,CAAC;AAErC,SAAQ,kBAAkB,KAAK,IAAI;AACnC,SAAQ,eAAe,oBAAI,IAAsB;AACjD,SAAQ,iBAAiB,oBAAI,IAAoB;AACjD,SAAQ,yBAAyB;AAEjC,SAAQ,0BAAoC,CAAC;AAC7C,SAAiB,kCAAkC;AACnD,SAAiB,4BAA4B;AAE7C,SAAQ,mBAA6B,CAAC;AA0HtC;AAAA,SAAQ,kBAAkB,oBAAI,IAAY;AAAA;AAAA,EAxHlC,kBAAkB,QAA8B;AACpD,WAAQ,OAA6B,aAAa,QAAQ,OAAO,aAAa,eAAe,MAAM;AAAA,EACvG;AAAA;AAAA,EAGQ,sBAAsB,QAA8B;AACxD,UAAM,OAAO,OAAO,QAAQ,MAAM;AAClC,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO,KAAK,cAAc,uBAAuB,MAAM;AAAA,EAC3D;AAAA,EAEQ,0BAAgC;AACpC,QAAI,KAAK,uBAAwB;AACjC,SAAK,yBAAyB;AAE9B,UAAM,iBAAiB;AAEvB,UAAM,kBAAkB,CAAC,OAA+C;AACpE,aAAO,IAAI,QAAQ,cAAc,KAAK;AAAA,IAC1C;AAEA,UAAM,eAAe,CAAC,cAAuB;AACzC,WAAK,kBAAkB,KAAK,IAAI;AAChC,UAAI,WAAW;AACX,aAAK,iBAAiB,KAAK,SAAS;AACpC,mBAAW,MAAM;AACb,eAAK,mBAAmB,KAAK,iBAAiB,OAAO,CAAC,MAAM,MAAM,SAAS;AAAA,QAC/E,GAAG,GAAI;AAAA,MACX;AAAA,IACJ;AAEA,UAAM,WAAW,IAAI,iBAAiB,CAAC,cAAc;AACjD,mBAAa;AACb,iBAAW,KAAK,WAAW;AACvB,YAAI,EAAE,SAAS,aAAa;AACxB,YAAE,WAAW,QAAQ,CAAC,SAAS;AAC3B,gBAAI,KAAK,aAAa,KAAK,aAAc;AACzC,kBAAM,UAAU,gBAAgB,IAAmB;AACnD,gBAAI,CAAC,QAAS;AACd,kBAAM,OAAO,QAAQ,WAAW,KAAK,EAAE,MAAM,GAAG,GAAG;AACnD,gBAAI,KAAM,cAAa,IAAI;AAAA,UAC/B,CAAC;AAAA,QACL,WAAW,EAAE,SAAS,iBAAiB;AACnC,gBAAM,UAAU,gBAAgB,EAAE,OAAO,aAAa;AACtD,cAAI,CAAC,QAAS;AACd,gBAAM,OAAO,QAAQ,WAAW,KAAK,EAAE,MAAM,GAAG,GAAG;AACnD,cAAI,KAAM,cAAa,IAAI;AAAA,QAC/B;AAAA,MACJ;AAAA,IACJ,CAAC;AAED,aAAS,QAAQ,SAAS,MAAM,EAAE,WAAW,MAAM,SAAS,MAAM,YAAY,MAAM,eAAe,KAAK,CAAC;AACzG,WAAO,iBAAiB,UAAU,MAAM,aAAa,GAAG,EAAE,SAAS,MAAM,SAAS,KAAK,CAAC;AAAA,EAC5F;AAAA,EAEA,KAAK,QAAsB;AACvB,SAAK,SAAS;AACd,iBAAa,MAAM;AACnB,oBAAgB;AAChB,SAAK,cAAc;AACnB,YAAQ,KAAK,kCAA6B,OAAO,GAAG;AAAA,EACxD;AAAA,EACA,UAAU,QAAuB;AAC7B,QAAI,CAAC,KAAK,QAAQ;AACd,cAAQ,KAAK,wDAAwD;AACrE;AAAA,IACJ;AACA,SAAK,OAAO,SAAS;AAAA,EACzB;AAAA,EAEA,oBAA0B;AACtB,QAAI,CAAC,KAAK,QAAQ;AACd,cAAQ,KAAK,wDAAwD;AACrE;AAAA,IACJ;AAEA,UAAM,cAAc;AAEpB,QAAI;AACA,UAAI,SAAS,aAAa,QAAQ,WAAW;AAE7C,UAAI,CAAC,QAAQ;AACT,iBACI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,aACxD,OAAO,WAAW,IAClB,uCAAuC,QAAQ,SAAS,CAAC,MAAM;AAC3D,gBAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,gBAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,iBAAO,EAAE,SAAS,EAAE;AAAA,QACxB,CAAC;AAEX,qBAAa,QAAQ,aAAa,MAAM;AAAA,MAC5C;AAEA,YAAM,UAAU,OAAO,MAAM,EAAE;AAC/B,WAAK,UAAU,cAAc,OAAO,EAAE;AAAA,IAC1C,SAAS,GAAG;AACR,cAAQ,KAAK,yCAAyC,CAAC;AACvD,WAAK,UAAU,WAAW;AAAA,IAC9B;AAAA,EACJ;AAAA;AAAA,EAGA,cAAc,MAAsB,SAAiB,MAAgC;AACjF,UAAM,aAAyB;AAAA,MAC3B,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,SAAK,YAAY,KAAK,UAAU;AAGhC,QAAI,KAAK,YAAY,SAAS,iBAAiB;AAC3C,WAAK,YAAY,MAAM;AAAA,IAC3B;AAAA,EACJ;AAAA;AAAA,EAMQ,QAAQ,KAAqB;AACjC,QAAI;AACA,YAAM,IAAI,IAAI,IAAI,GAAG;AACrB,YAAM,WAAW,EAAE,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,IAAI,KAAK,EAAE;AAClE,aAAO,GAAG,EAAE,QAAQ,IAAI,QAAQ;AAAA,IACpC,QAAQ;AAEJ,aAAO,IAAI,SAAS,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,QAAQ;AAAA,IACxD;AAAA,EACJ;AAAA;AAAA,EAGQ,cAAc,MAAc,SAAyB;AACzD,WAAO,GAAG,IAAI,KAAK,OAAO;AAAA,EAC9B;AAAA;AAAA,EAGQ,wBAAwB,KAAa,SAA0B;AAEnE,QAAI,YAAY,MAAO,QAAO;AAG9B,QAAI,IAAI,WAAW,OAAO,EAAG,QAAO;AAGpC,UAAM,cAAc;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AACA,QAAI,YAAY,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC,EAAG,QAAO;AAErD,WAAO;AAAA,EACX;AAAA,EAEQ,sBAAsB,SAA0B;AACpD,UAAM,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AACA,UAAM,cAAc;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AACA,WAAO,MAAM,KAAK,CAAC,MAAM,QAAQ,SAAS,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,MAAM,QAAQ,SAAS,CAAC,CAAC;AAAA,EAChG;AAAA,EAEA,oBAA0B;AACtB,UAAM,cAAc,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,aAAa;AAC7F,QAAI,YAAa;AAEjB,WAAO,UAAU,CAAC,SAAS,QAAQ,QAAQ,UAAU;AACjD,YAAM,MAAM,OAAO,YAAY,WAAW,UAAU;AACpD,UAAI,KAAK,sBAAsB,GAAG,EAAG,QAAO;AAE5C,YAAM,gBAAgB,SAAS,KAAK,QAAQ,MAAM,IAAI;AACtD,YAAM,eAAe,GAAG,GAAG,OAAO,aAAa,IAAI,UAAU,CAAC;AAC9D,YAAM,MAAM,KAAK,cAAc,WAAW,YAAY;AACtD,UAAI,KAAK,gBAAgB,IAAI,GAAG,EAAG,QAAO;AAC1C,WAAK,gBAAgB,IAAI,GAAG;AAC5B,WAAK,cAAc,SAAS,cAAc,EAAE,QAAQ,eAAe,QAAQ,MAAM,CAAC;AAElF,WAAK,KAAK,MAAM;AAAA,QACZ,QAAQ;AAAA,QACR,cAAc,IAAI,MAAM,YAAY;AAAA,QACpC,SAAS,EAAE,QAAQ,eAAe,QAAQ,MAAM;AAAA,MACpD,CAAC;AAED,aAAO;AAAA,IACX;AAGA,WAAO,iBAAiB,sBAAsB,CAAC,UAAU;AACrD,YAAM,MACF,MAAM,kBAAkB,QAClB,MAAM,OAAO,UACb,OAAO,MAAM,UAAU,6BAA6B;AAE9D,UAAI,KAAK,sBAAsB,GAAG,EAAG;AAErC,YAAM,MAAM,KAAK,cAAc,sBAAsB,GAAG;AACxD,UAAI,KAAK,gBAAgB,IAAI,GAAG,EAAG;AACnC,WAAK,gBAAgB,IAAI,GAAG;AAC5B,WAAK,cAAc,SAAS,GAAG;AAE/B,WAAK,KAAK,MAAM;AAAA,QACZ,QAAQ;AAAA,QACR,cAAc,IAAI,MAAM,GAAG;AAAA,MAC/B,CAAC;AAAA,IACL,CAAC;AAGD,UAAM,uBAAuB,QAAQ,MAAM,KAAK,OAAO;AACvD,YAAQ,QAAQ,IAAI,SAAoB;AACpC,2BAAqB,GAAG,IAAI;AAE5B,YAAM,MAAM,KAAK,IAAI,CAAC,MAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAE,EAAE,KAAK,GAAG;AAElF,UAAI,KAAK,sBAAsB,GAAG,EAAG;AAErC,YAAM,aAAa,IAAI,SAAS,MAAM,IAAI,MAAM,GAAG,GAAG,IAAI,QAAQ;AAClE,YAAM,MAAM,KAAK,cAAc,iBAAiB,UAAU;AAC1D,UAAI,KAAK,gBAAgB,IAAI,GAAG,EAAG;AACnC,WAAK,gBAAgB,IAAI,GAAG;AAE5B,WAAK,cAAc,SAAS,UAAU;AAEtC,YAAM,gBAAgB,KAAK,KAAK,CAAC,MAAkB,aAAa,KAAK;AACrE,YAAM,cAAc,iBAAiB,IAAI,MAAM,UAAU;AAEzD,WAAK,KAAK,MAAM;AAAA,QACZ,QAAQ;AAAA,QACR,cAAc;AAAA,MAClB,CAAC;AAAA,IACL;AAGA,WAAO;AAAA,MACH;AAAA,MACA,CAAC,UAAU;AACP,cAAM,SAAS,MAAM;AACrB,YAAI,EAAE,kBAAkB,aAAc;AACtC,YAAI,WAAY,OAAoB;AAEpC,cAAM,UAAU,OAAO,QAAQ,YAAY;AAE3C,cAAM,MACF,YAAY,SACL,OAA2B,OAC5B,YAAY,WACT,OAA6B,MAC9B;AAEZ,YAAI,KAAK,wBAAwB,KAAK,OAAO,EAAG;AAGhD,cAAM,MAAM,IAAI,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY;AAC5D,cAAM,eACF,QAAQ,QACF,QACA,QAAQ,OACN,OACA,QAAQ,UAAU,QAAQ,WAAW,QAAQ,QAC3C,SACA;AAEd,cAAM,aAAa,KAAK,QAAQ,GAAG;AACnC,cAAM,eAAe,kBAAkB,YAAY,KAAK,UAAU;AAClE,cAAM,MAAM,KAAK,cAAc,YAAY,YAAY;AACvD,YAAI,KAAK,gBAAgB,IAAI,GAAG,EAAG;AACnC,aAAK,gBAAgB,IAAI,GAAG;AAE5B,aAAK,KAAK,MAAM;AAAA,UACZ,QAAQ;AAAA,UACR,cAAc,IAAI,MAAM,YAAY;AAAA,UACpC,SAAS,EAAE,SAAS,KAAK,WAAW;AAAA,QACxC,CAAC;AAAA,MACL;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA,EAEQ,gBAAgB,KAAuD;AAC3E,WAAO,OAAO;AAAA,MACV,OAAO,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACtC,YAAI,UAAU,OAAW,QAAO,CAAC,KAAK,IAAI;AAC1C,YAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACtE,iBAAO,CAAC,KAAK,KAAK,gBAAgB,KAAgC,CAAC;AAAA,QACvE;AACA,eAAO,CAAC,KAAK,KAAK;AAAA,MACtB,CAAC;AAAA,IACL;AAAA,EACJ;AAAA,EAEQ,qBAAqB,KAAuD;AAChF,WAAO,OAAO;AAAA,MACV,OAAO,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACtC,YAAI,UAAU,OAAW,QAAO,CAAC,KAAK,IAAI;AAC1C,YAAI,OAAO,UAAU,WAAY,QAAO,CAAC,KAAK,YAAY;AAC1D,YAAI,iBAAiB,KAAM,QAAO,CAAC,KAAK,MAAM,YAAY,CAAC;AAC3D,YAAI,MAAM,QAAQ,KAAK,GAAG;AACtB,iBAAO;AAAA,YACH;AAAA,YACA,MAAM,IAAI,CAAC,SAAS;AAChB,kBAAI,gBAAgB,KAAM,QAAO,KAAK,YAAY;AAClD,kBAAI,OAAO,SAAS,WAAY,QAAO;AACvC,kBAAI,SAAS,QAAQ,OAAO,SAAS,UAAU;AAC3C,uBAAO,KAAK,qBAAqB,IAA+B;AAAA,cACpE;AACA,qBAAO,QAAQ;AAAA,YACnB,CAAC;AAAA,UACL;AAAA,QACJ;AACA,YAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC7C,iBAAO,CAAC,KAAK,KAAK,qBAAqB,KAAgC,CAAC;AAAA,QAC5E;AACA,eAAO,CAAC,KAAK,KAAK;AAAA,MACtB,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA,EAGA,iBAAiB;AACb,QAAI,OAAO,WAAW,YAAa;AAEnC,UAAM,gBAAgB,OAAO;AAE7B,UAAM,eAAe;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,UAAM,gBAAgB,CAAC,QAAyB;AAC5C,aAAO,aAAa,KAAK,CAAC,YAAY,IAAI,SAAS,OAAO,CAAC;AAAA,IAC/D;AAEA,WAAO,QAAQ,OAAO,OAA0B,SAA0C;AACtF,YAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,OAAO,MAAM;AAG1F,UAAI,cAAc,GAAG,GAAG;AACpB,eAAO,cAAc,OAAO,IAAI;AAAA,MACpC;AAEA,YAAM,SAAS,MAAM,UAAU;AAC/B,YAAM,YAAY,KAAK,IAAI;AAE3B,UAAI;AACA,cAAM,WAAW,MAAM,cAAc,OAAO,IAAI;AAChD,cAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,aAAK,cAAc,SAAS,GAAG,MAAM,IAAI,GAAG,IAAI;AAAA,UAC5C,QAAQ,SAAS;AAAA,UACjB,UAAU,GAAG,QAAQ;AAAA,QACzB,CAAC;AAED,YAAI,SAAS,UAAU,KAAK;AACxB,gBAAM,WAAW,KAAK,cAAc,kBAAkB,GAAG,MAAM,IAAI,GAAG,IAAI,SAAS,MAAM,EAAE;AAE3F,cAAI,CAAC,KAAK,gBAAgB,IAAI,QAAQ,GAAG;AACrC,iBAAK,gBAAgB,IAAI,QAAQ;AAEjC,gBAAI,eAAwB;AAC5B,gBAAI;AACA,oBAAM,SAAS,SAAS,MAAM;AAC9B,oBAAM,cAAc,OAAO,QAAQ,IAAI,cAAc,KAAK;AAC1D,6BAAe,YAAY,SAAS,kBAAkB,IAChD,MAAM,OAAO,KAAK,IAClB,MAAM,OAAO,KAAK;AAAA,YAC5B,QAAQ;AACJ,6BAAe;AAAA,YACnB;AAEA,kBAAM,cACF,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,aAAa;AAE7E,gBAAI,CAAC,aAAa;AACd,mBAAK,MAAM;AAAA,gBACP,QAAQ;AAAA,gBACR,cAAc,IAAI,MAAM,QAAQ,SAAS,MAAM,IAAI,SAAS,UAAU,WAAM,GAAG,EAAE;AAAA,gBACjF,cAAc;AAAA,gBACd,SAAS;AAAA,kBACL;AAAA,kBACA,QAAQ,SAAS;AAAA,kBACjB,YAAY,SAAS;AAAA,kBACrB,UAAU,GAAG,QAAQ;AAAA,kBACrB,OAAO,iBAAiB,GAAG;AAAA,kBAC3B,SAAS,iBAAiB,MAAM,IAAI;AAAA,kBACpC,UAAU;AAAA,gBACd;AAAA,cACJ,CAAC;AAAA,YACL;AAAA,UACJ;AAAA,QACJ;AAEA,eAAO;AAAA,MACX,SAAS,cAAc;AACnB,cAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,cAAM,QAAQ,wBAAwB,QAAQ,eAAe,IAAI,MAAM,OAAO,YAAY,CAAC;AAE3F,aAAK,cAAc,SAAS,GAAG,MAAM,IAAI,GAAG,yBAAoB;AAAA,UAC5D,UAAU,GAAG,QAAQ;AAAA,UACrB,OAAO,MAAM;AAAA,QACjB,CAAC;AAED,cAAM,WAAW,KAAK,cAAc,kBAAkB,GAAG,MAAM,IAAI,GAAG,gBAAgB;AAEtF,YAAI,CAAC,KAAK,gBAAgB,IAAI,QAAQ,GAAG;AACrC,eAAK,gBAAgB,IAAI,QAAQ;AAEjC,gBAAM,cACF,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,aAAa;AAC7E,cAAI,CAAC,aAAa;AACd,iBAAK,MAAM;AAAA,cACP,QAAQ;AAAA,cACR,cAAc;AAAA,cACd,cAAc;AAAA,cACd,SAAS,EAAE,QAAQ,UAAU,GAAG,QAAQ,KAAK;AAAA,YACjD,CAAC;AAAA,UACL;AAAA,QACJ;AAEA,cAAM;AAAA,MACV;AAAA,IACJ;AAEA,YAAQ,KAAK,+BAA+B;AAAA,EAChD;AAAA;AAAA,EAGA,eAAqB;AACjB,QAAI,OAAO,WAAW,YAAa;AAEnC,UAAM,eAAe;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,UAAM,gBAAgB,CAAC,QAAyB,aAAa,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;AAExF,UAAM,eAAe,eAAe,UAAU;AAC9C,UAAM,eAAe,eAAe,UAAU;AAC9C,UAAM,OAAO;AAQb,mBAAe,UAAU,OAAO,SAAuB,QAAgB,QAAsB,MAAiB;AAC1G,WAAK,YAAY;AACjB,WAAK,SAAS,OAAO,QAAQ,WAAW,MAAM,IAAI;AAElD,aAAO,aAAa,KAAK,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,IACvD;AAEA,mBAAe,UAAU,OAAO,YAA0B,MAAiB;AACvE,YAAM,MAAM,KAAK,UAAU;AAC3B,YAAM,SAAS,KAAK,aAAa;AAEjC,UAAI,cAAc,GAAG,GAAG;AAEpB,eAAO,aAAa,KAAK,MAAM,GAAG,IAAI;AAAA,MAC1C;AAEA,WAAK,eAAe,KAAK,IAAI;AAC7B,YAAM,cAAc,iBAAiB,KAAK,CAAC,CAAC;AAE5C,WAAK,iBAAiB,WAAW,MAAM;AACnC,cAAM,WAAW,KAAK,IAAI,KAAK,KAAK,gBAAgB,KAAK,IAAI;AAC7D,cAAM,SAAS,KAAK;AAEpB,aAAK,cAAc,SAAS,GAAG,MAAM,IAAI,GAAG,IAAI;AAAA,UAC5C;AAAA,UACA,UAAU,GAAG,QAAQ;AAAA,QACzB,CAAC;AAED,YAAI,UAAU,KAAK;AACf,gBAAM,WAAW,KAAK,cAAc,gBAAgB,GAAG,MAAM,IAAI,GAAG,IAAI,MAAM,EAAE;AAChF,cAAI,KAAK,gBAAgB,IAAI,QAAQ,EAAG;AACxC,eAAK,gBAAgB,IAAI,QAAQ;AAEjC,cAAI,eAAwB,KAAK;AACjC,cAAI;AACA,2BAAe,KAAK,MAAM,KAAK,YAAY;AAAA,UAC/C,QAAQ;AAAA,UAER;AAEA,gBAAM,cACF,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,aAAa;AAE7E,cAAI,CAAC,aAAa;AACd,iBAAK,KAAK,MAAM;AAAA,cACZ,QAAQ;AAAA,cACR,cAAc,IAAI,MAAM,QAAQ,MAAM,IAAI,KAAK,UAAU,WAAM,GAAG,EAAE;AAAA,cACpE,cAAc;AAAA,cACd,SAAS;AAAA,gBACL;AAAA,gBACA;AAAA,gBACA,YAAY,KAAK;AAAA,gBACjB,UAAU,GAAG,QAAQ;AAAA,gBACrB,OAAO,iBAAiB,GAAG;AAAA,gBAC3B,SAAS;AAAA,gBACT,UAAU;AAAA,cACd;AAAA,YACJ,CAAC;AAAA,UACL;AAAA,QACJ;AAAA,MACJ,CAAC;AAED,WAAK,iBAAiB,SAAS,MAAM;AAEjC,cAAM,WAAW,KAAK,IAAI,KAAK,KAAK,gBAAgB,KAAK,IAAI;AAC7D,cAAM,WAAW,KAAK,cAAc,gBAAgB,GAAG,MAAM,IAAI,GAAG,gBAAgB;AACpF,YAAI,KAAK,gBAAgB,IAAI,QAAQ,EAAG;AACxC,aAAK,gBAAgB,IAAI,QAAQ;AAEjC,cAAM,cACF,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,aAAa;AAE7E,YAAI,CAAC,aAAa;AACd,eAAK,KAAK,MAAM;AAAA,YACZ,QAAQ;AAAA,YACR,cAAc,IAAI,MAAM,wBAAmB,MAAM,IAAI,GAAG,EAAE;AAAA,YAC1D,cAAc;AAAA,YACd,SAAS,EAAE,QAAQ,UAAU,GAAG,QAAQ,KAAK;AAAA,UACjD,CAAC;AAAA,QACL;AAAA,MACJ,CAAC;AAGD,aAAO,aAAa,KAAK,MAAM,GAAG,IAAI;AAAA,IAC1C;AAEA,YAAQ,KAAK,6BAA6B;AAAA,EAC9C;AAAA,EAEA,iBAAuB;AACnB,QAAI,OAAO,WAAW,YAAa;AAEnC,QAAK,OAAe,uBAAwB;AAC5C,IAAC,OAAe,yBAAyB;AACzC,SAAK,wBAAwB;AAE7B,aAAS;AAAA,MACL;AAAA,MACA,CAAC,UAAsB;AACnB,cAAM,MAAM,MAAM;AAClB,YAAI,CAAC,IAAK;AAEV,cAAM,SAAU,IAAI,QAAQ,+CAA+C,KAAqB;AAEhG,cAAM,YAAY,oBAAoB,KAAK,MAAM;AACjD,cAAM,KAAK,OAAO,MAAM;AACxB,cAAM,UAAU,OAAO,SAAS,YAAY,KAAK;AAEjD,cAAM,gBACF,CAAC,UAAU,KAAK,OAAO,EAAE,SAAS,OAAO,KACzC,OAAO,aAAa,UAAU,KAC9B,OAAO,aAAa,MAAM,MAAM;AAEpC,cAAM,WAAW,mBAAmB,MAAM;AAC1C,cAAM,OAAO,YAAY,MAAM,OAAO,aAAa,MAAM,IAAI;AAE7D,YAAI,CAAC,aAAa,CAAC,MAAM,CAAC,cAAe;AAEzC,cAAM,UAAU,aAAa,MAAM,cAAc,WAAW,SAAS;AAGrE,YAAI,oBAAoB,KAAK,CAAC,MAAM,EAAE,KAAK,OAAO,CAAC,EAAG;AAEtD,aAAK,cAAc,SAAS,SAAS;AAAA,UACjC;AAAA,UACA,GAAI,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,UACnB,GAAI,YAAY,EAAE,MAAM,UAAU,IAAI,CAAC;AAAA,QAC3C,CAAC;AAED,cAAM,kBAAkB,KAAK,kBAAkB,MAAM;AACrD,cAAM,sBAAsB,KAAK,sBAAsB,MAAM;AAC7D,cAAM,sBAAsB,OAAO,aAAa,eAAe,MAAM;AACrE,cAAM,8BAA8B,OAAO,iBAAiB,MAAM,EAAE,kBAAkB;AACtF,cAAM,6BAA6B,MAAM;AACrC,gBAAM,YAAY,SAAS,iBAAiB,MAAM,SAAS,MAAM,OAAO;AACxE,cAAI,CAAC,aAAa,cAAc,UAAU,OAAO,SAAS,SAAS,EAAG,QAAO;AAC7E,iBAAO;AAAA,QACX,GAAG;AAEH,cAAM,YAAY,KAAK,IAAI;AAC3B,cAAM,qBAAqB,KAAK;AAChC,cAAM,sBAAsB;AAC5B,cAAM,uBAAuB;AAC7B,cAAM,yBAAyB;AAE/B,YAAI,CAAC,UAAU,KAAK,OAAO,EAAE,SAAS,OAAO,GAAG;AAC5C,qBAAW,MAAM;AACb,gBAAI,KAAK,mBAAmB,oBAAoB;AAC5C,oBAAM,cAAc,KAAK,YACpB;AAAA,gBACG,CAAC,MACG,EAAE,SAAS,WACX,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK,aACnC,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK,YAAY;AAAA,cACvD,EACC,MAAM,EAAE,EAAE,CAAC;AAEhB,oBAAM,SAAsB,kBACtB,EAAE,MAAM,YAAY,QAAQ,4CAA4C,IACxE,cACE,EAAE,MAAM,aAAa,QAAQ,YAAY,QAAQ,IACjD,sBACE,EAAE,MAAM,oBAAoB,QAAQ,4CAA4C,IAChF;AAAA,gBACI,MAAM;AAAA,gBACN,QAAQ,wCAAwC,sBAAsB,GAAI;AAAA,cAC9E;AAEV,oBAAM,eAAe,eAAe;AAAA,gBAChC,QAAQ;AAAA,gBACR;AAAA,gBACA,MAAM;AAAA,gBACN;AAAA,gBACA;AAAA,cACJ,CAAC;AAED,mBAAK,cAAc,SAAS,6BAA6B,OAAO,IAAI;AAAA,gBAChE;AAAA,gBACA,GAAI,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,gBACnB;AAAA,cACJ,CAAC;AAGD,oBAAM,MAAM,KAAK,IAAI;AACrB,mBAAK,0BAA0B,KAAK,wBAAwB;AAAA,gBACxD,CAAC,MAAM,MAAM,IAAI,KAAK;AAAA,cAC1B;AAEA,kBAAI,KAAK,wBAAwB,UAAU,KAAK,2BAA2B;AACvE;AAAA,cACJ;AACA,mBAAK,wBAAwB,KAAK,GAAG;AAErC,mBAAK,KAAK,KAAK;AAAA,gBACX,QAAQ;AAAA,gBACR,cAAc,IAAI,MAAM,6BAA6B,OAAO,EAAE;AAAA,gBAC9D,SAAS;AAAA,kBACL;AAAA,kBACA,GAAI,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,kBACnB;AAAA,kBACA,MAAM;AAAA,kBACN;AAAA,kBACA,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,kBACvB;AAAA,gBACJ;AAAA,gBACA,wBAAwB,CAAC,OAAO;AAAA,gBAChC,oBAAoB;AAAA,gBACpB,mBAAmB;AAAA,gBACnB,iBAAiB;AAAA,cACrB,CAAC;AAAA,YACL;AAAA,UACJ,GAAG,mBAAmB;AAAA,QAC1B;AAGA,YAAI,eAAe;AACf,gBAAM,aAAa,GAAG,OAAO,KAAK,OAAO;AACzC,gBAAM,cAAc,KAAK,aAAa,IAAI,UAAU,KAAK,CAAC,GAAG;AAAA,YACzD,CAAC,MAAM,YAAY,IAAI;AAAA,UAC3B;AACA,qBAAW,KAAK,SAAS;AACzB,eAAK,aAAa,IAAI,YAAY,UAAU;AAE5C,gBAAM,YAAY,KAAK,eAAe,IAAI,UAAU,KAAK;AAEzD,cAAI,WAAW,UAAU,KAAK,YAAY,YAAY,wBAAwB;AAC1E,iBAAK,eAAe,IAAI,YAAY,SAAS;AAE7C,kBAAM,cAAc,KAAK,YACpB;AAAA,cACG,CAAC,MACG,EAAE,SAAS,WACX,YAAY,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK;AAAA,YACvD,EACC,MAAM,EAAE,EAAE,CAAC;AAEhB,kBAAM,cAAc,KAAK,YACpB;AAAA,cACG,CAAC,MACG,EAAE,SAAS,WACX,YAAY,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK;AAAA,YACvD,EACC,MAAM,EAAE,EAAE,CAAC;AAGhB,kBAAM,iBAA8B,kBAC9B,EAAE,MAAM,YAAY,QAAQ,4CAA4C,IACxE,sBACE;AAAA,cACI,MAAM;AAAA,cACN,QAAQ;AAAA,YACZ,IACA,8BACE;AAAA,cACI,MAAM;AAAA,cACN,QAAQ;AAAA,YACZ,IACA,4BACE;AAAA,cACI,MAAM;AAAA,cACN,QAAQ,iBAAiB,mBAAmB,yBAAyB,CAAC;AAAA,YAC1E,IACA,cACE,EAAE,MAAM,aAAa,QAAQ,YAAY,QAAQ,IACjD,sBACE;AAAA,cACI,MAAM;AAAA,cACN,QAAQ;AAAA,YACZ,IACA,cACE,EAAE,MAAM,YAAY,QAAQ,YAAY,QAAQ,IAChD,KAAK,iBAAiB,SACpB,EAAE,MAAM,gBAAgB,QAAQ,KAAK,iBAAiB,MAAM,EAAE,EAAE,CAAC,EAAE,IACnE;AAAA,cACI,MAAM;AAAA,cACN,QAAQ;AAAA,YACZ;AAEpB,kBAAM,eAAe,eAAe;AAAA,cAChC,QAAQ;AAAA,cACR;AAAA,cACA,MAAM;AAAA,cACN;AAAA,cACA,YAAY,WAAW;AAAA,cACvB,UAAU;AAAA,cACV,QAAQ;AAAA,YACZ,CAAC;AAED,iBAAK,cAAc,SAAS,uBAAuB,OAAO,IAAI;AAAA,cAC1D;AAAA,cACA,YAAY,WAAW;AAAA,cACvB,UAAU;AAAA,cACV;AAAA,YACJ,CAAC;AAED,iBAAK,KAAK,KAAK;AAAA,cACX,QAAQ;AAAA,cACR,cAAc,IAAI,MAAM,uBAAuB,OAAO,EAAE;AAAA,cACxD,SAAS;AAAA,gBACL;AAAA,gBACA,YAAY,WAAW;AAAA,gBACvB,UAAU;AAAA,gBACV;AAAA,gBACA,MAAM;AAAA,gBACN;AAAA,gBACA,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,gBACvB;AAAA,cACJ;AAAA,cACA,wBAAwB,CAAC,OAAO;AAAA,cAChC,oBAAoB;AAAA,cACpB,mBAAmB;AAAA,cACnB,iBAAiB;AAAA,YACrB,CAAC;AAAA,UACL;AAAA,QACJ;AAAA,MACJ;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,cAAiB,QAAmB,IAAkD;AAClF,WAAO,QAAQ,QAAQ,EAClB,KAAK,MAAM,GAAG,CAAC,EACf,MAAM,CAAC,UAAmB;AACvB,YAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,WAAK,KAAK,SAAS,OAAO,QAAQ,OAAO,SAAS,KAAK,OAAO,YAAY;AAC1E,aAAO;AAAA,IACX,CAAC;AAAA,EACT;AAAA,EAEA,WACI,QACA,IACA,SAIC;AACD,YAAQ,IAAI,SAAmD;AAC3D,UAAI;AACA,cAAM,SAAS,GAAG,GAAG,IAAI;AACzB,YAAI,kBAAkB,SAAS;AAC3B,iBAAO,OAAO,MAAM,CAAC,UAAmB;AACpC,kBAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,iBAAK,MAAM;AAAA,cACP;AAAA,cACA,cAAc;AAAA,cACd,cAAc,SAAS;AAAA,cACvB,SAAS,SAAS;AAAA,YACtB,CAAC;AACD,mBAAO;AAAA,UACX,CAAC;AAAA,QACL;AAEA,eAAO;AAAA,MACX,SAAS,OAAgB;AACrB,cAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,aAAK,MAAM;AAAA,UACP;AAAA,UACA,cAAc;AAAA,UACd,cAAc,SAAS;AAAA,UACvB,SAAS,SAAS;AAAA,QACtB,CAAC;AACD,eAAO;AAAA,MACX;AAAA,IACJ;AAAA,EACJ;AAAA,EAEQ,iBAAiB,YAAY,MAAM,SAAS,KAAoB;AACpE,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC5B,UAAI;AACJ,UAAI,UAAU;AAEd,YAAM,UAAU,MAAM;AAClB,YAAI,QAAS;AACb,kBAAU;AACV,qBAAa,SAAS;AACtB,qBAAa,WAAW;AACxB,eAAO,oBAAoB,UAAU,UAAU,IAAI;AACnD,gBAAQ;AAAA,MACZ;AAEA,YAAM,WAAW,MAAM;AACnB,qBAAa,SAAS;AACtB,oBAAY,WAAW,SAAS,MAAM;AAAA,MAC1C;AAGA,YAAM,cAAc,WAAW,SAAS,SAAS;AAEjD,aAAO,iBAAiB,UAAU,UAAU,IAAI;AAEhD,kBAAY,WAAW,SAAS,MAAM;AAAA,IAC1C,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAc,2BAA2B,iBAAuD;AAC5F,UAAM,oBAAoB,CAAC,CAAC,mBAAmB,SAAS,SAAS,eAAe;AAChF,UAAM,mBAAmB;AAEzB,QAAI;AACA,UAAI,mBAAmB;AACnB,wBAAiB,eAAe,EAAE,OAAO,UAAU,QAAQ,SAAS,CAAC;AACrE,cAAM,KAAK,iBAAiB;AAC5B,wBAAiB,aAAa,qBAAqB,gBAAgB;AAAA,MACvE;AAEA,YAAM,eAAe,MAAM,OAAO,iBAAiB,GAAG;AAEtD,YAAM,SAAS,MAAM,YAAY,SAAS,MAAM;AAAA,QAC5C,GAAG,OAAO;AAAA,QACV,GAAG,OAAO;AAAA,QACV,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,QACf,cAAc;AAAA,QACd,SAAS,CAAC,cAAc;AACpB,cAAI,CAAC,kBAAmB;AACxB,gBAAM,eAAe,UAAU,cAAc,uBAAuB,gBAAgB,IAAI;AACxF,cAAI,wBAAwB,aAAa;AACrC,yBAAa,MAAM,SAAS;AAC5B,yBAAa,MAAM,YAAY;AAAA,UACnC;AAAA,QACJ;AAAA,MACJ,CAAC;AACD,aAAO,OAAO,UAAU,cAAc,GAAG;AAAA,IAC7C,SAAS,GAAG;AACR,cAAQ,MAAM,0CAA0C,CAAC;AACzD,aAAO;AAAA,IACX,UAAE;AACE,uBAAiB,gBAAgB,mBAAmB;AAAA,IACxD;AAAA,EACJ;AAAA,EAEA,MAAc,KACV,OACA,SACA,UAAmC,CAAC,GACpC,OACA,UACA,wBACA,oBACA,mBACA,iBACF;AACE,QAAI,CAAC,KAAK,eAAe,CAAC,KAAK,QAAQ;AACnC,cAAQ,KAAK,wDAAwD;AACrE;AAAA,IACJ;AAEA,UAAM,cACF,OAAO,WAAW,gBACjB,OAAO,SAAS,aAAa,eAAe,OAAO,SAAS,aAAa;AAE9E,QAAI,aAAa;AACb,cAAQ,MAAM,uBAAuB,OAAO,IAAI,EAAE,SAAS,MAAM,CAAC;AAClE;AAAA,IACJ;AAEA,UAAM,MAAM,KAAK,IAAI;AAErB,QAAI,sBAAsB,wBAAwB,SAC5C,KAAK,YAAY,OAAO,CAAC,MAAM,CAAC,uBAAuB,SAAS,EAAE,IAAI,CAAC,IACvE,KAAK;AAEX,QAAI,oBAAoB;AACpB,4BAAsB,oBAAoB;AAAA,QACtC,CAAC,MAAM,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK;AAAA,MACpD;AAAA,IACJ;AAEA,UAAM,QAAkB;AAAA,MACpB,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA,KAAK,KAAK,OAAO;AAAA,MACjB,QAAQ,KAAK,OAAO,UAAU;AAAA,MAC9B;AAAA,MACA,MAAM,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAAA,MACjE,YAAY,OAAO,SAAS;AAAA,MAC5B,SAAS,KAAK,gBAAgB,OAAO;AAAA,MACrC,MAAM,KAAK,OAAO;AAAA,MAClB,UAAU,YAAY;AAAA,MACtB,aAAa,CAAC,GAAG,mBAAmB;AAAA,MACpC,GAAG,sBAAsB;AAAA,IAC7B;AAEA,QAAI;AACA,YAAMC,MAAK,MAAM;AACjB,YAAM,aAAS,2BAAI,8BAAWA,KAAI,MAAM,CAAC;AAEzC,YAAM,YAAY,KAAK,qBAAqB,EAAE,GAAG,MAAM,CAAuC;AAE9F,gBAAM,0BAAO,QAAQ;AAAA,QACjB,GAAG;AAAA,QACH,WAAW,4BAAU,SAAS,MAAM,SAAS;AAAA,MACjD,CAAC;AAED,UAAI,mBAAmB;AACnB,aAAK,KAAK,2BAA2B,eAAe,EAAE,KAAK,CAAC,WAAW;AACnE,cAAI,QAAQ;AACR,qBAAK,6BAAU,QAAQ,EAAE,kBAAkB,OAAO,CAAC,EAAE;AAAA,cAAM,CAAC,MACxD,QAAQ,MAAM,kDAAkD,CAAC;AAAA,YACrE;AAAA,UACJ;AAAA,QACJ,CAAC;AAAA,MACL;AAAA,IACJ,SAAS,GAAG;AACR,cAAQ,MAAM,mCAAmC,CAAC;AAAA,IACtD;AAAA,EACJ;AAAA,EAEA,KAAK,QAAmB;AACpB,UAAM,EAAE,QAAQ,cAAc,SAAS,aAAa,IAAI;AACxD,WAAO,KAAK,KAAK,QAAQ,cAAc,WAAW,QAAQ,EAAE,GAAG,SAAS,OAAO,GAAG,cAAc,YAAY;AAAA,EAChH;AAAA,EAEA,KAAK,QAAmB;AACpB,UAAM;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ,IAAI;AACJ,WAAO,KAAK;AAAA,MACR;AAAA,MACA,cAAc,WAAW;AAAA,MACzB,EAAE,GAAG,SAAS,OAAO;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,MAAM,QAAmB;AACrB,UAAM,EAAE,QAAQ,cAAc,SAAS,aAAa,IAAI;AACxD,WAAO,KAAK,KAAK,SAAS,cAAc,WAAW,QAAQ,EAAE,GAAG,SAAS,OAAO,GAAG,cAAc,YAAY;AAAA,EACjH;AACJ;AAEO,IAAM,SAAS,IAAI,OAAO;;;AIlrCjC,mBAA0D;AAuC1C;AA3BT,IAAM,sBAAN,cAAkC,uBAAwB;AAAA,EAC7D,YAAY,OAAc;AACtB,UAAM,KAAK;AACX,SAAK,QAAQ,EAAE,UAAU,MAAM;AAAA,EACnC;AAAA,EAEA,OAAO,2BAAkC;AACrC,WAAO,EAAE,UAAU,KAAK;AAAA,EAC5B;AAAA,EAEA,kBAAkB,OAAc,MAAiB;AAC7C,WAAO,cAAc,SAAS,gBAAgB,MAAM,OAAO,IAAI;AAAA,MAC3D,gBAAgB,KAAK,kBAAkB;AAAA,IAC3C,CAAC;AAED,WAAO,MAAM;AAAA,MACT,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,SAAS;AAAA,QACL,gBAAgB,KAAK,kBAAkB;AAAA,MAC3C;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EAEA,SAAS;AACL,QAAI,KAAK,MAAM,UAAU;AACrB,aAAO,KAAK,MAAM,YACd,4CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,OAAO,MAAM,GAAG,mCAE/C;AAAA,IAER;AAEA,WAAO,KAAK,MAAM;AAAA,EACtB;AACJ;","names":["import_firestore","app","db"]}
package/dist/index.mjs CHANGED
@@ -373,6 +373,7 @@ var Logger = class {
373
373
  const key = this.buildErrorKey("onerror", finalMessage);
374
374
  if (this.loggedErrorKeys.has(key)) return false;
375
375
  this.loggedErrorKeys.add(key);
376
+ this.addBreadcrumb("error", finalMessage, { source: trimmedSource, lineno, colno });
376
377
  void this.error({
377
378
  fnName: "window.onerror",
378
379
  messageError: new Error(finalMessage),
@@ -386,6 +387,7 @@ var Logger = class {
386
387
  const key = this.buildErrorKey("unhandledrejection", msg);
387
388
  if (this.loggedErrorKeys.has(key)) return;
388
389
  this.loggedErrorKeys.add(key);
390
+ this.addBreadcrumb("error", msg);
389
391
  void this.error({
390
392
  fnName: "window.unhandledrejection",
391
393
  messageError: new Error(msg)
@@ -400,6 +402,7 @@ var Logger = class {
400
402
  const key = this.buildErrorKey("console.error", trimmedMsg);
401
403
  if (this.loggedErrorKeys.has(key)) return;
402
404
  this.loggedErrorKeys.add(key);
405
+ this.addBreadcrumb("error", trimmedMsg);
403
406
  const originalError = args.find((a) => a instanceof Error);
404
407
  const errorToSend = originalError ?? new Error(trimmedMsg);
405
408
  void this.error({
@@ -679,6 +682,13 @@ var Logger = class {
679
682
  });
680
683
  const disabledAtClick = this.isElementDisabled(target);
681
684
  const invalidFieldAtClick = this.hasInvalidFieldNearby(target);
685
+ const ariaDisabledAtClick = target.getAttribute("aria-disabled") === "true";
686
+ const pointerEventsBlockedAtClick = window.getComputedStyle(target).pointerEvents === "none";
687
+ const overlayInterceptedAtClick = (() => {
688
+ const elAtPoint = document.elementFromPoint(event.clientX, event.clientY);
689
+ if (!elAtPoint || elAtPoint === target || target.contains(elAtPoint)) return null;
690
+ return elAtPoint;
691
+ })();
682
692
  const clickTime = Date.now();
683
693
  const domActivityAtClick = this.lastDomActivity;
684
694
  const DEAD_CLICK_DELAY_MS = 4e3;
@@ -747,7 +757,25 @@ var Logger = class {
747
757
  const recentFetch = this.breadcrumbs.filter(
748
758
  (b) => b.type === "fetch" && clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS
749
759
  ).slice(-1)[0];
750
- const possibleReason = disabledAtClick ? { type: "disabled", detail: "Button dalam kondisi disabled saat diklik" } : recentFetch ? { type: "api_error", detail: recentFetch.message } : invalidFieldAtClick ? { type: "validation_error", detail: "Field invalid terdeteksi di form terdekat" } : this.recentDomChanges.length ? { type: "dom_feedback", detail: this.recentDomChanges.slice(-1)[0] } : { type: "no_feedback", detail: "Tidak ada feedback UI terdeteksi setelah klik" };
760
+ const recentError = this.breadcrumbs.filter(
761
+ (b) => b.type === "error" && clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS
762
+ ).slice(-1)[0];
763
+ const possibleReason = disabledAtClick ? { type: "disabled", detail: "Button dalam kondisi disabled saat diklik" } : ariaDisabledAtClick ? {
764
+ type: "aria_disabled",
765
+ detail: 'Element memiliki aria-disabled="true" (disabled secara visual/aksesibilitas, bukan native disabled attribute)'
766
+ } : pointerEventsBlockedAtClick ? {
767
+ type: "pointer_events_blocked",
768
+ detail: "Element memiliki CSS pointer-events: none saat diklik (tidak bisa menerima event klik sama sekali)"
769
+ } : overlayInterceptedAtClick ? {
770
+ type: "overlay_intercepted",
771
+ detail: `Element lain (${getElementSelector(overlayInterceptedAtClick)}) menutupi target saat klik terjadi, klik tidak sampai ke target aslinya`
772
+ } : recentFetch ? { type: "api_error", detail: recentFetch.message } : invalidFieldAtClick ? {
773
+ type: "validation_error",
774
+ detail: "Field invalid terdeteksi di form terdekat"
775
+ } : recentError ? { type: "js_error", detail: recentError.message } : this.recentDomChanges.length ? { type: "dom_feedback", detail: this.recentDomChanges.slice(-1)[0] } : {
776
+ type: "no_feedback",
777
+ detail: "Tidak ada feedback UI terdeteksi setelah klik"
778
+ };
751
779
  const formattedLog = formatClickLog({
752
780
  fnName: "interceptClick.rageClick",
753
781
  tagName,
@@ -874,6 +902,7 @@ var Logger = class {
874
902
  y: window.scrollY,
875
903
  width: window.innerWidth,
876
904
  height: window.innerHeight,
905
+ imageTimeout: 3e3,
877
906
  onclone: (clonedDoc) => {
878
907
  if (!canApplyHighlight) return;
879
908
  const clonedTarget = clonedDoc.querySelector(`[data-ga-highlight="${HIGHLIGHT_MARKER}"]`);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/logger.ts","../src/config/firebase.ts","../src/core/environment.ts","../src/core/formatClickLog.ts","../src/components/LoggerErrorBoundary.tsx"],"sourcesContent":["import { doc, collection, setDoc, updateDoc, Timestamp } from \"firebase/firestore\";\r\nimport { initFirebase, getDb } from \"../config/firebase\";\r\nimport type { Breadcrumb, BreadcrumbType, ClickReason, LogEntry, LogLevel, LogParams, LoggerConfig } from \"../types\";\r\nimport { initEnvironment } from \"./environment\";\r\nimport { getEnvironmentPayload } from \"./environment\";\r\nimport { formatClickLog } from \"./formatClickLog\";\r\n\r\nconst sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\r\nconst MAX_BREADCRUMBS = 50;\r\n\r\nconst CLICK_SKIP_PATTERNS = [/^:r[a-z0-9]+:$/, /^\\[object/, /^#/];\r\n\r\n// Sanitize sebelum masuk Firestor\r\nconst sanitizeClickText = (text: string): string => {\r\n return text\r\n .replace(/\\b[\\w.-]+@[\\w.-]+\\.\\w+\\b/g, \"[email]\")\r\n .replace(/\\b\\d{10,}\\b/g, \"[number]\")\r\n .replace(/Rp[\\s]?\\d[\\d.,]*/gi, \"[amount]\")\r\n .trim();\r\n};\r\n\r\nconst getClosestClickText = (raw: HTMLElement, target: HTMLElement): string => {\r\n let el: HTMLElement | null = raw;\r\n\r\n // 1. innerText di sepanjang chain (existing)\r\n while (el) {\r\n const text = sanitizeClickText(el.innerText?.trim().slice(0, 50) ?? \"\");\r\n if (text) return text;\r\n if (el === target) break;\r\n el = el.parentElement;\r\n }\r\n\r\n // 2. aria-label / title di sepanjang chain\r\n el = raw;\r\n while (el) {\r\n const label = el.getAttribute(\"aria-label\") || el.getAttribute(\"title\");\r\n if (label) {\r\n const text = sanitizeClickText(label.trim().slice(0, 50));\r\n if (text) return text;\r\n }\r\n if (el === target) break;\r\n el = el.parentElement;\r\n }\r\n\r\n // 3. text dari child terdekat (span/p/div) di dalam target\r\n const childText = target.querySelector(\"span, p, div\")?.textContent?.trim();\r\n if (childText) {\r\n const text = sanitizeClickText(childText.slice(0, 50));\r\n if (text) return text;\r\n }\r\n\r\n // 4. text dari sibling terdekat\r\n const siblingText =\r\n target.previousElementSibling?.textContent?.trim() || target.nextElementSibling?.textContent?.trim();\r\n if (siblingText) {\r\n const text = sanitizeClickText(siblingText.slice(0, 50));\r\n if (text) return text;\r\n }\r\n\r\n return \"\";\r\n};\r\n\r\nconst getElementSelector = (el: HTMLElement): string => {\r\n const tag = el.tagName?.toLowerCase() ?? \"unknown\";\r\n\r\n if (el.id) return `${tag}#${el.id}`;\r\n\r\n const firstClass =\r\n typeof el.className === \"string\" && el.className.trim() ? el.className.trim().split(/\\s+/)[0] : \"\";\r\n if (firstClass) return `${tag}.${firstClass}`;\r\n\r\n const parent = el.parentElement;\r\n if (parent) {\r\n const index = Array.from(parent.children).indexOf(el) + 1;\r\n return `${tag}:nth-child(${index})`;\r\n }\r\n\r\n return tag;\r\n};\r\n\r\nconst parseRequestBody = (body: unknown): unknown => {\r\n if (body == null) return null;\r\n if (typeof body === \"string\") {\r\n try {\r\n return JSON.parse(body);\r\n } catch {\r\n return body;\r\n }\r\n }\r\n if (body instanceof FormData) {\r\n const result: Record<string, unknown> = {};\r\n body.forEach((value, key) => {\r\n result[key] = value;\r\n });\r\n return result;\r\n }\r\n if (body instanceof URLSearchParams) {\r\n const result: Record<string, string> = {};\r\n body.forEach((value, key) => {\r\n result[key] = value;\r\n });\r\n return result;\r\n }\r\n return \"[unsupported body type]\";\r\n};\r\n\r\nconst parseQueryParams = (url: string): Record<string, string> | null => {\r\n try {\r\n const params = new URL(url, window.location.origin).searchParams;\r\n const result: Record<string, string> = {};\r\n params.forEach((value, key) => {\r\n result[key] = value;\r\n });\r\n return Object.keys(result).length > 0 ? result : null;\r\n } catch {\r\n return null;\r\n }\r\n};\r\n\r\nclass Logger {\r\n private config: LoggerConfig | null = null;\r\n private initialized = false;\r\n private breadcrumbs: Breadcrumb[] = [];\r\n\r\n private lastDomActivity = Date.now();\r\n private clickHistory = new Map<string, number[]>();\r\n private rageClickFired = new Map<string, number>();\r\n private domObserverInitialized = false;\r\n\r\n private deadClickWarnTimestamps: number[] = [];\r\n private readonly DEAD_CLICK_RATE_LIMIT_WINDOW_MS = 60000;\r\n private readonly DEAD_CLICK_RATE_LIMIT_MAX = 5;\r\n\r\n private recentDomChanges: string[] = [];\r\n\r\n private isElementDisabled(target: HTMLElement): boolean {\r\n return (target as HTMLButtonElement).disabled === true || target.getAttribute(\"aria-disabled\") === \"true\";\r\n }\r\n\r\n // Helper: cek apakah ada field invalid di form terdekat dari target\r\n private hasInvalidFieldNearby(target: HTMLElement): boolean {\r\n const form = target.closest(\"form\");\r\n if (!form) return false;\r\n return form.querySelector('[aria-invalid=\"true\"]') !== null;\r\n }\r\n\r\n private initDomActivityObserver(): void {\r\n if (this.domObserverInitialized) return;\r\n this.domObserverInitialized = true;\r\n\r\n const ALERT_SELECTOR = '[role=\"alert\"], [aria-live], .MuiSnackbar-root, .MuiAlert-root';\r\n\r\n const getAlertElement = (el: HTMLElement | null): HTMLElement | null => {\r\n return el?.closest(ALERT_SELECTOR) ?? null;\r\n };\r\n\r\n const markActivity = (addedText?: string) => {\r\n this.lastDomActivity = Date.now();\r\n if (addedText) {\r\n this.recentDomChanges.push(addedText);\r\n setTimeout(() => {\r\n this.recentDomChanges = this.recentDomChanges.filter((t) => t !== addedText);\r\n }, 1000);\r\n }\r\n };\r\n\r\n const observer = new MutationObserver((mutations) => {\r\n markActivity();\r\n for (const m of mutations) {\r\n if (m.type === \"childList\") {\r\n m.addedNodes.forEach((node) => {\r\n if (node.nodeType !== Node.ELEMENT_NODE) return;\r\n const alertEl = getAlertElement(node as HTMLElement);\r\n if (!alertEl) return;\r\n const text = alertEl.innerText?.trim().slice(0, 200);\r\n if (text) markActivity(text);\r\n });\r\n } else if (m.type === \"characterData\") {\r\n const alertEl = getAlertElement(m.target.parentElement);\r\n if (!alertEl) continue;\r\n const text = alertEl.innerText?.trim().slice(0, 200);\r\n if (text) markActivity(text);\r\n }\r\n }\r\n });\r\n\r\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true });\r\n window.addEventListener(\"scroll\", () => markActivity(), { passive: true, capture: true });\r\n }\r\n\r\n init(config: LoggerConfig) {\r\n this.config = config;\r\n initFirebase(config);\r\n initEnvironment();\r\n this.initialized = true;\r\n console.info(\"[ga-logger] initialized —\", config.env);\r\n }\r\n setUserId(userId: string | null) {\r\n if (!this.config) {\r\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\r\n return;\r\n }\r\n this.config.userId = userId;\r\n }\r\n\r\n initAnonymousUser(): void {\r\n if (!this.config) {\r\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\r\n return;\r\n }\r\n\r\n const STORAGE_KEY = \"ga_anon_id\";\r\n\r\n try {\r\n let anonId = localStorage.getItem(STORAGE_KEY);\r\n\r\n if (!anonId) {\r\n anonId =\r\n typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\r\n ? crypto.randomUUID()\r\n : \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\r\n return v.toString(16);\r\n });\r\n\r\n localStorage.setItem(STORAGE_KEY, anonId);\r\n }\r\n\r\n const shortId = anonId.slice(-8);\r\n this.setUserId(`Anonymous #${shortId}`);\r\n } catch (e) {\r\n console.warn(\"[ga-logger] initAnonymousUser failed:\", e);\r\n this.setUserId(\"Anonymous\");\r\n }\r\n }\r\n\r\n // Add breadcrumb ke in-memory array\r\n addBreadcrumb(type: BreadcrumbType, message: string, data?: Record<string, unknown>) {\r\n const breadcrumb: Breadcrumb = {\r\n timestamp: new Date(),\r\n type,\r\n message,\r\n data,\r\n };\r\n\r\n this.breadcrumbs.push(breadcrumb);\r\n\r\n // Jaga memory — max 50 breadcrumbs, buang yang paling lama\r\n if (this.breadcrumbs.length > MAX_BREADCRUMBS) {\r\n this.breadcrumbs.shift();\r\n }\r\n }\r\n\r\n // Global error capture — hanya aktif di non-development\r\n private loggedErrorKeys = new Set<string>();\r\n\r\n // Helper: trim URL jadi domain/filename\r\n private trimUrl(url: string): string {\r\n try {\r\n const u = new URL(url);\r\n const filename = u.pathname.split(\"/\").filter(Boolean).pop() ?? u.pathname;\r\n return `${u.hostname}/${filename}`;\r\n } catch {\r\n // bukan valid URL, potong saja jika terlalu panjang\r\n return url.length > 80 ? url.slice(0, 80) + \"...\" : url;\r\n }\r\n }\r\n\r\n // Helper: build dedup key\r\n private buildErrorKey(type: string, message: string): string {\r\n return `${type}::${message}`;\r\n }\r\n\r\n // Helper: should skip resource error\r\n private shouldSkipResourceError(src: string, tagName: string): boolean {\r\n // Skip IMG — tidak sebabkan blank page\r\n if (tagName === \"IMG\") return true;\r\n\r\n // Skip base64 URL\r\n if (src.startsWith(\"data:\")) return true;\r\n\r\n // Skip known 3rd party trackers\r\n const THIRD_PARTY = [\r\n \"omguk.com\",\r\n \"track.\",\r\n \"doubleclick\",\r\n \"adservice\",\r\n \"googletagmanager\",\r\n \"google-analytics\",\r\n \"hotjar\",\r\n \"facebook.net\",\r\n \"analytics\",\r\n \"magnolia\",\r\n \"gtm.js\",\r\n ];\r\n if (THIRD_PARTY.some((p) => src.includes(p))) return true;\r\n\r\n return false;\r\n }\r\n\r\n private shouldSkipGlobalError(message: string): boolean {\r\n const NOISE = [\r\n \"CORS\",\r\n \"cross-origin\",\r\n \"ERR_FAILED\",\r\n \"Failed to load resource\",\r\n \"Failed to load image\",\r\n \"NetworkError\",\r\n \"ResizeObserver loop\",\r\n \"Script error\",\r\n \"ServiceWorker\",\r\n \"sw.js\",\r\n \"dev-sw.js\",\r\n \"Warning:\",\r\n \"Error loading image\",\r\n ];\r\n const THIRD_PARTY = [\r\n \"magnolia\",\r\n \"gtm.js\",\r\n \"analytics\",\r\n \"googletagmanager\",\r\n \"facebook\",\r\n \"hotjar\",\r\n \"omguk.com\",\r\n \"track.\",\r\n \"doubleclick\",\r\n \"adservice\",\r\n ];\r\n return NOISE.some((p) => message.includes(p)) || THIRD_PARTY.some((p) => message.includes(p));\r\n }\r\n\r\n initGlobalCapture(): void {\r\n const isLocalhost = window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n if (isLocalhost) return;\r\n\r\n window.onerror = (message, source, lineno, colno) => {\r\n const msg = typeof message === \"string\" ? message : \"Unknown error\";\r\n if (this.shouldSkipGlobalError(msg)) return false;\r\n\r\n const trimmedSource = source ? this.trimUrl(source) : \"unknown source\";\r\n const finalMessage = `${msg} at ${trimmedSource}:${lineno ?? 0}`;\r\n const key = this.buildErrorKey(\"onerror\", finalMessage);\r\n if (this.loggedErrorKeys.has(key)) return false;\r\n this.loggedErrorKeys.add(key);\r\n\r\n void this.error({\r\n fnName: \"window.onerror\",\r\n messageError: new Error(finalMessage),\r\n payload: { source: trimmedSource, lineno, colno },\r\n });\r\n\r\n return false;\r\n };\r\n\r\n // unhandledrejection\r\n window.addEventListener(\"unhandledrejection\", (event) => {\r\n const msg =\r\n event.reason instanceof Error\r\n ? event.reason.message\r\n : String(event.reason ?? \"Unhandled Promise rejection\");\r\n\r\n if (this.shouldSkipGlobalError(msg)) return;\r\n\r\n const key = this.buildErrorKey(\"unhandledrejection\", msg);\r\n if (this.loggedErrorKeys.has(key)) return;\r\n this.loggedErrorKeys.add(key);\r\n\r\n void this.error({\r\n fnName: \"window.unhandledrejection\",\r\n messageError: new Error(msg),\r\n });\r\n });\r\n\r\n // console.error override\r\n const originalConsoleError = console.error.bind(console);\r\n console.error = (...args: unknown[]) => {\r\n originalConsoleError(...args);\r\n\r\n const msg = args.map((a) => (a instanceof Error ? a.message : String(a))).join(\" \");\r\n\r\n if (this.shouldSkipGlobalError(msg)) return;\r\n\r\n const trimmedMsg = msg.length > 150 ? msg.slice(0, 150) + \"...\" : msg;\r\n const key = this.buildErrorKey(\"console.error\", trimmedMsg);\r\n if (this.loggedErrorKeys.has(key)) return;\r\n this.loggedErrorKeys.add(key);\r\n\r\n const originalError = args.find((a): a is Error => a instanceof Error);\r\n\r\n const errorToSend = originalError ?? new Error(trimmedMsg);\r\n\r\n void this.error({\r\n fnName: \"console.error\",\r\n messageError: errorToSend,\r\n });\r\n };\r\n\r\n // resource load error (CSS / JS / font)\r\n window.addEventListener(\r\n \"error\",\r\n (event) => {\r\n const target = event.target;\r\n if (!(target instanceof HTMLElement)) return;\r\n if (target === (window as unknown)) return;\r\n\r\n const tagName = target.tagName.toUpperCase();\r\n\r\n const src =\r\n tagName === \"LINK\"\r\n ? (target as HTMLLinkElement).href\r\n : tagName === \"SCRIPT\"\r\n ? (target as HTMLScriptElement).src\r\n : \"\";\r\n\r\n if (this.shouldSkipResourceError(src, tagName)) return;\r\n\r\n // Deteksi dari ekstensi file, bukan tagName\r\n const ext = src.split(\"?\")[0].split(\".\").pop()?.toLowerCase();\r\n const resourceType =\r\n ext === \"css\"\r\n ? \"CSS\"\r\n : ext === \"js\"\r\n ? \"JS\"\r\n : ext === \"woff\" || ext === \"woff2\" || ext === \"ttf\"\r\n ? \"FONT\"\r\n : tagName;\r\n\r\n const trimmedSrc = this.trimUrl(src);\r\n const finalMessage = `Failed to load ${resourceType}: ${trimmedSrc}`;\r\n const key = this.buildErrorKey(\"resource\", finalMessage);\r\n if (this.loggedErrorKeys.has(key)) return;\r\n this.loggedErrorKeys.add(key);\r\n\r\n void this.error({\r\n fnName: \"resource.onerror\",\r\n messageError: new Error(finalMessage),\r\n payload: { tagName, src: trimmedSrc },\r\n });\r\n },\r\n true,\r\n );\r\n }\r\n\r\n private sanitizePayload(obj: Record<string, unknown>): Record<string, unknown> {\r\n return Object.fromEntries(\r\n Object.entries(obj).map(([key, value]) => {\r\n if (value === undefined) return [key, null];\r\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\r\n return [key, this.sanitizePayload(value as Record<string, unknown>)];\r\n }\r\n return [key, value];\r\n }),\r\n );\r\n }\r\n\r\n private sanitizeForFirestore(obj: Record<string, unknown>): Record<string, unknown> {\r\n return Object.fromEntries(\r\n Object.entries(obj).map(([key, value]) => {\r\n if (value === undefined) return [key, null];\r\n if (typeof value === \"function\") return [key, \"[function]\"];\r\n if (value instanceof Date) return [key, value.toISOString()];\r\n if (Array.isArray(value)) {\r\n return [\r\n key,\r\n value.map((item) => {\r\n if (item instanceof Date) return item.toISOString();\r\n if (typeof item === \"function\") return \"[function]\";\r\n if (item !== null && typeof item === \"object\") {\r\n return this.sanitizeForFirestore(item as Record<string, unknown>);\r\n }\r\n return item ?? null;\r\n }),\r\n ];\r\n }\r\n if (value !== null && typeof value === \"object\") {\r\n return [key, this.sanitizeForFirestore(value as Record<string, unknown>)];\r\n }\r\n return [key, value];\r\n }),\r\n );\r\n }\r\n\r\n // interceptFetch — auto breadcrumb, ke Firestore\r\n interceptFetch() {\r\n if (typeof window === \"undefined\") return;\r\n\r\n const originalFetch = window.fetch;\r\n\r\n const skipPatterns = [\r\n \"google.com\",\r\n \"googletagmanager.com\",\r\n \"googleapis.com\",\r\n \"facebook.com\",\r\n \"hotjar.com\",\r\n \"analytics\",\r\n \"gtm\",\r\n \"ccm/collect\",\r\n \"on.aws\",\r\n \"/locales/\",\r\n \"/sw.js\",\r\n \"/manifest.json\",\r\n \"/sourcemaps/\",\r\n \"magnolia\",\r\n ];\r\n\r\n const shouldSkipUrl = (url: string): boolean => {\r\n return skipPatterns.some((pattern) => url.includes(pattern));\r\n };\r\n\r\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\r\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\r\n\r\n // Skip third-party domains\r\n if (shouldSkipUrl(url)) {\r\n return originalFetch(input, init);\r\n }\r\n\r\n const method = init?.method ?? \"GET\";\r\n const startTime = Date.now();\r\n\r\n try {\r\n const response = await originalFetch(input, init);\r\n const duration = Date.now() - startTime;\r\n\r\n this.addBreadcrumb(\"fetch\", `${method} ${url}`, {\r\n status: response.status,\r\n duration: `${duration}ms`,\r\n });\r\n\r\n if (response.status >= 400) {\r\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} ${response.status}`);\r\n\r\n if (!this.loggedErrorKeys.has(dedupKey)) {\r\n this.loggedErrorKeys.add(dedupKey);\r\n\r\n let responseBody: unknown = null;\r\n try {\r\n const cloned = response.clone();\r\n const contentType = cloned.headers.get(\"content-type\") ?? \"\";\r\n responseBody = contentType.includes(\"application/json\")\r\n ? await cloned.json()\r\n : await cloned.text();\r\n } catch {\r\n responseBody = \"[unable to read response body]\";\r\n }\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n\r\n if (!isLocalhost) {\r\n this.error({\r\n fnName: \"interceptFetch\",\r\n messageError: new Error(`HTTP ${response.status} ${response.statusText} — ${url}`),\r\n endpointName: url,\r\n payload: {\r\n method,\r\n status: response.status,\r\n statusText: response.statusText,\r\n duration: `${duration}ms`,\r\n query: parseQueryParams(url),\r\n request: parseRequestBody(init?.body),\r\n response: responseBody,\r\n },\r\n });\r\n }\r\n }\r\n }\r\n\r\n return response;\r\n } catch (networkError) {\r\n const duration = Date.now() - startTime;\r\n const error = networkError instanceof Error ? networkError : new Error(String(networkError));\r\n\r\n this.addBreadcrumb(\"fetch\", `${method} ${url} — NETWORK ERROR`, {\r\n duration: `${duration}ms`,\r\n error: error.message,\r\n });\r\n\r\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} NETWORK_ERROR`);\r\n\r\n if (!this.loggedErrorKeys.has(dedupKey)) {\r\n this.loggedErrorKeys.add(dedupKey);\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n if (!isLocalhost) {\r\n this.error({\r\n fnName: \"interceptFetch\",\r\n messageError: error,\r\n endpointName: url,\r\n payload: { method, duration: `${duration}ms` },\r\n });\r\n }\r\n }\r\n\r\n throw networkError;\r\n }\r\n };\r\n\r\n console.info(\"[ga-logger] fetch intercepted\");\r\n }\r\n\r\n // interceptXHR — auto breadcrumb + error capture untuk axios/XHR-based request\r\n interceptXHR(): void {\r\n if (typeof window === \"undefined\") return;\r\n\r\n const skipPatterns = [\r\n \"google.com\",\r\n \"googletagmanager.com\",\r\n \"googleapis.com\",\r\n \"facebook.com\",\r\n \"hotjar.com\",\r\n \"analytics\",\r\n \"ccm/collect\",\r\n \"on.aws\",\r\n \"/locales/\",\r\n \"/sw.js\",\r\n \"/manifest.json\",\r\n \"/sourcemaps/\",\r\n \"magnolia\",\r\n ];\r\n\r\n const shouldSkipUrl = (url: string): boolean => skipPatterns.some((p) => url.includes(p));\r\n\r\n const originalOpen = XMLHttpRequest.prototype.open;\r\n const originalSend = XMLHttpRequest.prototype.send;\r\n const self = this;\r\n\r\n type GaXHR = XMLHttpRequest & {\r\n _gaMethod?: string;\r\n _gaUrl?: string;\r\n _gaStartTime?: number;\r\n };\r\n\r\n XMLHttpRequest.prototype.open = function (this: GaXHR, method: string, url: string | URL, ...rest: unknown[]) {\r\n this._gaMethod = method;\r\n this._gaUrl = typeof url === \"string\" ? url : url.href;\r\n // @ts-expect-error - spreading native arguments\r\n return originalOpen.call(this, method, url, ...rest);\r\n };\r\n\r\n XMLHttpRequest.prototype.send = function (this: GaXHR, ...args: unknown[]) {\r\n const url = this._gaUrl ?? \"\";\r\n const method = this._gaMethod ?? \"GET\";\r\n\r\n if (shouldSkipUrl(url)) {\r\n // @ts-expect-error - spreading native arguments\r\n return originalSend.call(this, ...args);\r\n }\r\n\r\n this._gaStartTime = Date.now();\r\n const requestBody = parseRequestBody(args[0]);\r\n\r\n this.addEventListener(\"loadend\", () => {\r\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\r\n const status = this.status;\r\n\r\n self.addBreadcrumb(\"fetch\", `${method} ${url}`, {\r\n status,\r\n duration: `${duration}ms`,\r\n });\r\n\r\n if (status >= 400) {\r\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} ${status}`);\r\n if (self.loggedErrorKeys.has(dedupKey)) return;\r\n self.loggedErrorKeys.add(dedupKey);\r\n\r\n let responseBody: unknown = this.responseText;\r\n try {\r\n responseBody = JSON.parse(this.responseText);\r\n } catch {\r\n // keep as raw text\r\n }\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n\r\n if (!isLocalhost) {\r\n void self.error({\r\n fnName: \"interceptXHR\",\r\n messageError: new Error(`HTTP ${status} ${this.statusText} — ${url}`),\r\n endpointName: url,\r\n payload: {\r\n method,\r\n status,\r\n statusText: this.statusText,\r\n duration: `${duration}ms`,\r\n query: parseQueryParams(url),\r\n request: requestBody,\r\n response: responseBody,\r\n },\r\n });\r\n }\r\n }\r\n });\r\n\r\n this.addEventListener(\"error\", () => {\r\n // network-level failure (status selalu 0)\r\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\r\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} NETWORK_ERROR`);\r\n if (self.loggedErrorKeys.has(dedupKey)) return;\r\n self.loggedErrorKeys.add(dedupKey);\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n\r\n if (!isLocalhost) {\r\n void self.error({\r\n fnName: \"interceptXHR\",\r\n messageError: new Error(`Network error — ${method} ${url}`),\r\n endpointName: url,\r\n payload: { method, duration: `${duration}ms` },\r\n });\r\n }\r\n });\r\n\r\n // @ts-expect-error - spreading native arguments\r\n return originalSend.call(this, ...args);\r\n };\r\n\r\n console.info(\"[ga-logger] XHR intercepted\");\r\n }\r\n\r\n interceptClick(): void {\r\n if (typeof window === \"undefined\") return;\r\n\r\n if ((window as any).__ga_click_intercepted) return;\r\n (window as any).__ga_click_intercepted = true;\r\n this.initDomActivityObserver();\r\n\r\n document.addEventListener(\r\n \"click\",\r\n (event: MouseEvent) => {\r\n const raw = event.target as HTMLElement;\r\n if (!raw) return;\r\n\r\n const target = (raw.closest('button, a, [role=\"button\"], [data-log], input') as HTMLElement) ?? raw;\r\n\r\n const innerText = getClosestClickText(raw, target);\r\n const id = target.id ?? \"\";\r\n const tagName = target.tagName?.toLowerCase() ?? \"\";\r\n\r\n const isInteractive =\r\n [\"button\", \"a\", \"input\"].includes(tagName) ||\r\n target.hasAttribute(\"data-log\") ||\r\n target.getAttribute(\"role\") === \"button\";\r\n\r\n const selector = getElementSelector(target);\r\n const href = tagName === \"a\" ? target.getAttribute(\"href\") : null;\r\n\r\n if (!innerText && !id && !isInteractive) return;\r\n\r\n const message = innerText || id || `(no label: ${tagName || \"unknown\"})`;\r\n\r\n // Skip internal React/Router identifiers\r\n if (CLICK_SKIP_PATTERNS.some((p) => p.test(message))) return;\r\n\r\n this.addBreadcrumb(\"click\", message, {\r\n tagName,\r\n ...(id ? { id } : {}),\r\n ...(innerText ? { text: innerText } : {}),\r\n });\r\n\r\n const disabledAtClick = this.isElementDisabled(target);\r\n const invalidFieldAtClick = this.hasInvalidFieldNearby(target);\r\n // ── Dead click detection ──\r\n const clickTime = Date.now();\r\n const domActivityAtClick = this.lastDomActivity;\r\n const DEAD_CLICK_DELAY_MS = 4000;\r\n const RAGE_CLICK_WINDOW_MS = 2000;\r\n const RAGE_CLICK_COOLDOWN_MS = 5000;\r\n\r\n if ([\"button\", \"a\", \"input\"].includes(tagName)) {\r\n setTimeout(() => {\r\n\r\n if (this.lastDomActivity <= domActivityAtClick) {\r\n const recentFetch = this.breadcrumbs\r\n .filter(\r\n (b) =>\r\n b.type === \"fetch\" &&\r\n new Date(b.timestamp).getTime() >= clickTime &&\r\n new Date(b.timestamp).getTime() <= clickTime + DEAD_CLICK_DELAY_MS,\r\n )\r\n .slice(-1)[0];\r\n\r\n const reason: ClickReason = disabledAtClick\r\n ? { type: \"disabled\", detail: \"Button dalam kondisi disabled saat diklik\" }\r\n : recentFetch\r\n ? { type: \"api_error\", detail: recentFetch.message }\r\n : invalidFieldAtClick\r\n ? { type: \"validation_error\", detail: \"Field invalid terdeteksi di form terdekat\" }\r\n : {\r\n type: \"no_dom_activity\",\r\n detail: `Tidak ada aktivitas DOM/scroll dalam ${DEAD_CLICK_DELAY_MS / 1000} detik`,\r\n };\r\n\r\n const formattedLog = formatClickLog({\r\n fnName: \"interceptClick.deadClick\",\r\n tagName,\r\n text: message,\r\n selector,\r\n reason,\r\n });\r\n\r\n this.addBreadcrumb(\"click\", `Button tidak bisa diklik: ${message}`, {\r\n tagName,\r\n ...(id ? { id } : {}),\r\n reason,\r\n });\r\n\r\n // ── Rate-limit: max N dead-click warn per session per window ──\r\n const now = Date.now();\r\n this.deadClickWarnTimestamps = this.deadClickWarnTimestamps.filter(\r\n (t) => now - t < this.DEAD_CLICK_RATE_LIMIT_WINDOW_MS,\r\n );\r\n\r\n if (this.deadClickWarnTimestamps.length >= this.DEAD_CLICK_RATE_LIMIT_MAX) {\r\n return;\r\n }\r\n this.deadClickWarnTimestamps.push(now);\r\n\r\n void this.warn({\r\n fnName: \"interceptClick.deadClick\",\r\n messageError: new Error(`Button tidak bisa diklik: ${message}`),\r\n payload: {\r\n tagName,\r\n ...(id ? { id } : {}),\r\n reason,\r\n text: message,\r\n selector,\r\n ...(href ? { href } : {}),\r\n formattedLog,\r\n },\r\n excludeBreadcrumbTypes: [\"fetch\"],\r\n breadcrumbWindowMs: 10000,\r\n captureScreenshot: true,\r\n highlightTarget: target,\r\n });\r\n }\r\n }, DEAD_CLICK_DELAY_MS);\r\n }\r\n\r\n // ── Rage click detection 3x klik ──\r\n if (isInteractive) {\r\n const historyKey = `${tagName}::${message}`;\r\n const timestamps = (this.clickHistory.get(historyKey) ?? []).filter(\r\n (t) => clickTime - t < RAGE_CLICK_WINDOW_MS,\r\n );\r\n timestamps.push(clickTime);\r\n this.clickHistory.set(historyKey, timestamps);\r\n\r\n const lastFired = this.rageClickFired.get(historyKey) ?? 0;\r\n\r\n if (timestamps.length >= 3 && clickTime - lastFired > RAGE_CLICK_COOLDOWN_MS) {\r\n this.rageClickFired.set(historyKey, clickTime);\r\n\r\n const recentFetch = this.breadcrumbs\r\n .filter(\r\n (b) =>\r\n b.type === \"fetch\" &&\r\n clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS,\r\n )\r\n .slice(-1)[0];\r\n\r\n const possibleReason: ClickReason = disabledAtClick\r\n ? { type: \"disabled\", detail: \"Button dalam kondisi disabled saat diklik\" }\r\n : recentFetch\r\n ? { type: \"api_error\", detail: recentFetch.message }\r\n : invalidFieldAtClick\r\n ? { type: \"validation_error\", detail: \"Field invalid terdeteksi di form terdekat\" }\r\n : this.recentDomChanges.length\r\n ? { type: \"dom_feedback\", detail: this.recentDomChanges.slice(-1)[0] }\r\n : { type: \"no_feedback\", detail: \"Tidak ada feedback UI terdeteksi setelah klik\" };\r\n\r\n const formattedLog = formatClickLog({\r\n fnName: \"interceptClick.rageClick\",\r\n tagName,\r\n text: message,\r\n selector,\r\n clickCount: timestamps.length,\r\n windowMs: RAGE_CLICK_WINDOW_MS,\r\n reason: possibleReason,\r\n });\r\n\r\n this.addBreadcrumb(\"click\", `Klik Berulang kali: ${message}`, {\r\n tagName,\r\n clickCount: timestamps.length,\r\n windowMs: RAGE_CLICK_WINDOW_MS,\r\n possibleReason,\r\n });\r\n\r\n void this.warn({\r\n fnName: \"interceptClick.rageClick\",\r\n messageError: new Error(`Klik Berulang kali: ${message}`),\r\n payload: {\r\n tagName,\r\n clickCount: timestamps.length,\r\n windowMs: RAGE_CLICK_WINDOW_MS,\r\n possibleReason,\r\n text: message,\r\n selector,\r\n ...(href ? { href } : {}),\r\n formattedLog,\r\n },\r\n excludeBreadcrumbTypes: [\"fetch\"],\r\n breadcrumbWindowMs: 10000,\r\n captureScreenshot: true,\r\n highlightTarget: target,\r\n });\r\n }\r\n }\r\n },\r\n true,\r\n );\r\n }\r\n\r\n captureAction<T>(params: LogParams, fn: () => T | Promise<T>): Promise<T | undefined> {\r\n return Promise.resolve()\r\n .then(() => fn())\r\n .catch((error: unknown) => {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.send(\"error\", params.fnName, params.payload, err, params.endpointName);\r\n return undefined;\r\n });\r\n }\r\n\r\n withLogger<T extends (...args: any[]) => any>(\r\n fnName: string,\r\n fn: T,\r\n options?: {\r\n endpointName?: string;\r\n payload?: Record<string, unknown>;\r\n },\r\n ): T {\r\n return ((...args: Parameters<T>): ReturnType<T> | undefined => {\r\n try {\r\n const result = fn(...args);\r\n if (result instanceof Promise) {\r\n return result.catch((error: unknown) => {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.error({\r\n fnName,\r\n messageError: err,\r\n endpointName: options?.endpointName,\r\n payload: options?.payload,\r\n });\r\n return undefined;\r\n }) as ReturnType<T>;\r\n }\r\n\r\n return result;\r\n } catch (error: unknown) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.error({\r\n fnName,\r\n messageError: err,\r\n endpointName: options?.endpointName,\r\n payload: options?.payload,\r\n });\r\n return undefined;\r\n }\r\n }) as T;\r\n }\r\n\r\n private waitForScrollEnd(timeoutMs = 1500, idleMs = 100): Promise<void> {\r\n return new Promise((resolve) => {\r\n let idleTimer: ReturnType<typeof setTimeout>;\r\n let settled = false;\r\n\r\n const cleanup = () => {\r\n if (settled) return;\r\n settled = true;\r\n clearTimeout(idleTimer);\r\n clearTimeout(hardTimeout);\r\n window.removeEventListener(\"scroll\", onScroll, true);\r\n resolve();\r\n };\r\n\r\n const onScroll = () => {\r\n clearTimeout(idleTimer);\r\n idleTimer = setTimeout(cleanup, idleMs);\r\n };\r\n\r\n // fallback: if no scroll event ever fires (target already in view), don't hang\r\n const hardTimeout = setTimeout(cleanup, timeoutMs);\r\n\r\n window.addEventListener(\"scroll\", onScroll, true);\r\n // start the idle timer immediately too, in case scrollIntoView is a no-op\r\n idleTimer = setTimeout(cleanup, idleMs);\r\n });\r\n }\r\n\r\n // private createHighlightOverlay(target: HTMLElement): HTMLDivElement {\r\n // const rect = target.getBoundingClientRect();\r\n // const padding = 0;\r\n\r\n // const overlay = document.createElement(\"div\");\r\n // overlay.style.position = \"absolute\";\r\n // overlay.style.top = `${rect.top + window.scrollY - padding}px`;\r\n // overlay.style.left = `${rect.left + window.scrollX - padding}px`;\r\n // overlay.style.width = `${rect.width + padding * 2}px`;\r\n // overlay.style.height = `${rect.height + padding * 2}px`;\r\n // overlay.style.border = \"2px solid red\";\r\n // overlay.style.pointerEvents = \"none\";\r\n // overlay.style.zIndex = \"999999\";\r\n\r\n // document.body.appendChild(overlay);\r\n // return overlay;\r\n // }\r\n\r\n private async captureAndUploadScreenshot(highlightTarget?: HTMLElement): Promise<string | null> {\r\n const canApplyHighlight = !!highlightTarget && document.contains(highlightTarget);\r\n const HIGHLIGHT_MARKER = \"ga-logger-highlight-target\";\r\n\r\n try {\r\n if (canApplyHighlight) {\r\n highlightTarget!.scrollIntoView({ block: \"center\", inline: \"center\" });\r\n await this.waitForScrollEnd();\r\n highlightTarget!.setAttribute(\"data-ga-highlight\", HIGHLIGHT_MARKER);\r\n }\r\n\r\n const html2canvas = (await import(\"html2canvas-pro\")).default;\r\n\r\n const canvas = await html2canvas(document.body, {\r\n x: window.scrollX,\r\n y: window.scrollY,\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n onclone: (clonedDoc) => {\r\n if (!canApplyHighlight) return;\r\n const clonedTarget = clonedDoc.querySelector(`[data-ga-highlight=\"${HIGHLIGHT_MARKER}\"]`);\r\n if (clonedTarget instanceof HTMLElement) {\r\n clonedTarget.style.border = \"2px solid red\";\r\n clonedTarget.style.boxSizing = \"border-box\";\r\n }\r\n },\r\n });\r\n\r\n return canvas.toDataURL(\"image/jpeg\", 0.4);\r\n } catch (e) {\r\n console.error(\"[ga-logger] screenshot capture failed:\", e);\r\n return null;\r\n } finally {\r\n highlightTarget?.removeAttribute(\"data-ga-highlight\");\r\n }\r\n }\r\n\r\n private async send(\r\n level: LogLevel,\r\n message: string,\r\n payload: Record<string, unknown> = {},\r\n error?: Error,\r\n endpoint?: string,\r\n excludeBreadcrumbTypes?: BreadcrumbType[],\r\n breadcrumbWindowMs?: number,\r\n captureScreenshot?: boolean,\r\n highlightTarget?: HTMLElement,\r\n ) {\r\n if (!this.initialized || !this.config) {\r\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\r\n return;\r\n }\r\n\r\n const isLocalhost =\r\n typeof window !== \"undefined\" &&\r\n (window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\");\r\n\r\n if (isLocalhost) {\r\n console.error(`[ga-localhost][DEV] ${message}`, { payload, error });\r\n return;\r\n }\r\n\r\n const now = Date.now();\r\n\r\n let filteredBreadcrumbs = excludeBreadcrumbTypes?.length\r\n ? this.breadcrumbs.filter((b) => !excludeBreadcrumbTypes.includes(b.type))\r\n : this.breadcrumbs;\r\n\r\n if (breadcrumbWindowMs) {\r\n filteredBreadcrumbs = filteredBreadcrumbs.filter(\r\n (b) => now - new Date(b.timestamp).getTime() <= breadcrumbWindowMs,\r\n );\r\n }\r\n\r\n const entry: LogEntry = {\r\n timestamp: new Date(),\r\n level,\r\n message,\r\n env: this.config.env,\r\n userId: this.config.userId ?? null,\r\n sessionId,\r\n page: typeof window !== \"undefined\" ? window.location.pathname : \"\",\r\n stackTrace: error?.stack ?? null,\r\n payload: this.sanitizePayload(payload),\r\n repo: this.config.repo,\r\n endpoint: endpoint ?? null,\r\n breadcrumbs: [...filteredBreadcrumbs],\r\n ...getEnvironmentPayload(),\r\n };\r\n\r\n try {\r\n const db = getDb();\r\n const docRef = doc(collection(db, \"logs\"));\r\n\r\n const safeEntry = this.sanitizeForFirestore({ ...entry } as unknown as Record<string, unknown>);\r\n\r\n await setDoc(docRef, {\r\n ...safeEntry,\r\n timestamp: Timestamp.fromDate(entry.timestamp),\r\n });\r\n\r\n if (captureScreenshot) {\r\n void this.captureAndUploadScreenshot(highlightTarget).then((base64) => {\r\n if (base64) {\r\n void updateDoc(docRef, { screenshotBase64: base64 }).catch((e) =>\r\n console.error(\"[ga-logger] failed to update screenshotBase64:\", e),\r\n );\r\n }\r\n });\r\n }\r\n } catch (e) {\r\n console.error(\"[ga-logger] failed to send log:\", e);\r\n }\r\n }\r\n\r\n info(params: LogParams) {\r\n const { fnName, messageError, payload, endpointName } = params;\r\n return this.send(\"info\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\r\n }\r\n\r\n warn(params: LogParams) {\r\n const {\r\n fnName,\r\n messageError,\r\n payload,\r\n endpointName,\r\n excludeBreadcrumbTypes,\r\n breadcrumbWindowMs,\r\n captureScreenshot,\r\n highlightTarget,\r\n } = params;\r\n return this.send(\r\n \"warn\",\r\n messageError?.message ?? fnName,\r\n { ...payload, fnName },\r\n messageError,\r\n endpointName,\r\n excludeBreadcrumbTypes,\r\n breadcrumbWindowMs,\r\n captureScreenshot,\r\n highlightTarget,\r\n );\r\n }\r\n\r\n error(params: LogParams) {\r\n const { fnName, messageError, payload, endpointName } = params;\r\n return this.send(\"error\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\r\n }\r\n}\r\n\r\nexport const logger = new Logger();","import { initializeApp, getApps, getApp } from \"firebase/app\";\nimport { getFirestore } from \"firebase/firestore\";\nimport type { Firestore } from \"firebase/firestore\";\nimport type { LoggerConfig } from \"../types\";\n\nconst APP_NAME = \"ga-logger\";\nlet db: Firestore | null = null;\n\nexport const initFirebase = (config: LoggerConfig) => {\n // Cek apakah named instance \"ga-logger\" sudah ada\n const existingApp = getApps().find((app) => app.name === APP_NAME);\n\n const app =\n existingApp ??\n initializeApp(\n {\n apiKey: config.apiKey,\n authDomain: config.authDomain,\n projectId: config.projectId,\n storageBucket: config.storageBucket,\n messagingSenderId: config.messagingSenderId,\n appId: config.appId,\n },\n APP_NAME,\n );\n\n db = getFirestore(app);\n return db;\n};\n\nexport const getDb = (): Firestore => {\n if (!db) throw new Error(\"[ga-logger] Firebase not initialized. Call logger.init() first.\");\n return db;\n};\n","interface EnvironmentPayload {\n userAgent: string;\n deviceType: \"Mobile\" | \"Tablet\" | \"Desktop\";\n screenWidth: number;\n screenHeight: number;\n}\n\nlet cachedEnvironment: EnvironmentPayload | null = null;\n\nfunction getDeviceType(width: number): \"Mobile\" | \"Tablet\" | \"Desktop\" {\n if (width < 768) return \"Mobile\";\n if (width <= 1024) return \"Tablet\";\n return \"Desktop\";\n}\n\nexport function initEnvironment(): void {\n if (cachedEnvironment) return;\n\n const width = window.innerWidth;\n const height = window.innerHeight;\n\n cachedEnvironment = {\n userAgent: navigator.userAgent,\n deviceType: getDeviceType(width),\n screenWidth: width,\n screenHeight: height,\n };\n}\n\nexport function getEnvironmentPayload(): EnvironmentPayload {\n if (!cachedEnvironment) initEnvironment();\n return cachedEnvironment!;\n}","import type { ClickReason, ClickReasonType } from \"../types\";\n\nconst REASON_LABEL: Record<ClickReasonType, string> = {\n disabled: \"Button sedang disabled saat diklik\",\n api_error: \"Request API gagal\",\n validation_error: \"Ada field invalid di form terdekat (validasi gagal)\",\n dom_feedback: \"Muncul notifikasi\",\n no_feedback: \"Tidak ada notifikasi/feedback UI apapun setelah diklik\",\n no_dom_activity: \"Tidak ada aktivitas DOM/scroll setelah diklik\",\n};\n\nconst SCREENSHOT_HINT: Partial<Record<ClickReasonType, string>> = {\n dom_feedback: \"📷 Lihat screenshot untuk detail visual alert\",\n no_feedback: \"📷 Cek screenshot — kemungkinan UI freeze/silent fail\",\n no_dom_activity: \"📷 Cek screenshot — kondisi layar saat klik terjadi\",\n};\n\ninterface ClickLogPayload {\n fnName: string;\n tagName: string;\n text: string;\n selector: string;\n clickCount?: number;\n windowMs?: number;\n reason: ClickReason;\n}\n\nconst buildDiagnosis = (reason: ClickReason): string => {\n const label = REASON_LABEL[reason.type];\n const hint = SCREENSHOT_HINT[reason.type];\n\n const base =\n reason.type === \"api_error\"\n ? `${label} — ${reason.detail}`\n : reason.type === \"dom_feedback\"\n ? `${label} — \"${reason.detail}\"`\n : label;\n\n return hint ? `${base}\\n ${hint}` : base;\n};\n\nconst formatSelectorAsHtml = (selector: string, tagName: string): string => {\n if (selector.includes(\".\")) {\n const className = selector.split(\".\").slice(1).join(\" \");\n return `class=\"${className}\"`;\n }\n if (selector.includes(\"#\")) {\n const id = selector.split(\"#\")[1];\n return `id=\"${id}\"`;\n }\n return \"\";\n};\n\nexport const formatClickLog = (payload: ClickLogPayload): string => {\n const isRageClick = payload.fnName.includes(\"rageClick\");\n const clickTypeLabel = isRageClick ? \"Klik Berulang\" : \"Klik Tanpa Respon\";\n const clickTypeSuffix = isRageClick ? \"(Rage Click)\" : \"(Dead Click)\";\n\n const stats = isRageClick\n ? `${payload.clickCount}x klik dalam ${payload.windowMs}ms`\n : `Terdeteksi setelah klik tanpa aktivitas`;\n\n const attr = formatSelectorAsHtml(payload.selector, payload.tagName);\n const openTag = attr ? `<${payload.tagName} ${attr}>` : `<${payload.tagName}>`;\n const elementHtml = payload.text ? `${openTag}${payload.text}</${payload.tagName}>` : `${openTag}</${payload.tagName}>`;\n\n return [\n `Summary : ${clickTypeLabel} pada ${payload.tagName} \"${payload.text}\" ${clickTypeSuffix}`,\n `Diagnosis : ${buildDiagnosis(payload.reason)}`,\n `Stats : ${stats}`,\n `Element : ${elementHtml}`,\n `Source : ${payload.fnName}`,\n ].join(\"\\n\");\n};\n\n","import { Component, type ErrorInfo, type ReactNode } from \"react\";\nimport { logger } from \"../core/logger\";\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n}\n\ninterface State {\n hasError: boolean;\n}\n\nexport class LoggerErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(): State {\n return { hasError: true };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n logger.addBreadcrumb(\"error\", `React crash: ${error.message}`, {\n componentStack: info.componentStack ?? undefined,\n });\n\n logger.error({\n fnName: \"LoggerErrorBoundary\",\n messageError: error,\n payload: {\n componentStack: info.componentStack ?? undefined,\n },\n });\n }\n\n render() {\n if (this.state.hasError) {\n return this.props.fallback ?? (\n <div style={{ padding: \"1rem\", color: \"red\" }}>\n Something went wrong.\n </div>\n );\n }\n\n return this.props.children;\n }\n}"],"mappings":"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","names":["app","db"]}
1
+ {"version":3,"sources":["../src/core/logger.ts","../src/config/firebase.ts","../src/core/environment.ts","../src/core/formatClickLog.ts","../src/components/LoggerErrorBoundary.tsx"],"sourcesContent":["import { doc, collection, setDoc, updateDoc, Timestamp } from \"firebase/firestore\";\r\nimport { initFirebase, getDb } from \"../config/firebase\";\r\nimport type { Breadcrumb, BreadcrumbType, ClickReason, LogEntry, LogLevel, LogParams, LoggerConfig } from \"../types\";\r\nimport { initEnvironment } from \"./environment\";\r\nimport { getEnvironmentPayload } from \"./environment\";\r\nimport { formatClickLog } from \"./formatClickLog\";\r\n\r\nconst sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\r\nconst MAX_BREADCRUMBS = 50;\r\n\r\nconst CLICK_SKIP_PATTERNS = [/^:r[a-z0-9]+:$/, /^\\[object/, /^#/];\r\n\r\n// Sanitize sebelum masuk Firestor\r\nconst sanitizeClickText = (text: string): string => {\r\n return text\r\n .replace(/\\b[\\w.-]+@[\\w.-]+\\.\\w+\\b/g, \"[email]\")\r\n .replace(/\\b\\d{10,}\\b/g, \"[number]\")\r\n .replace(/Rp[\\s]?\\d[\\d.,]*/gi, \"[amount]\")\r\n .trim();\r\n};\r\n\r\nconst getClosestClickText = (raw: HTMLElement, target: HTMLElement): string => {\r\n let el: HTMLElement | null = raw;\r\n\r\n // 1. innerText di sepanjang chain (existing)\r\n while (el) {\r\n const text = sanitizeClickText(el.innerText?.trim().slice(0, 50) ?? \"\");\r\n if (text) return text;\r\n if (el === target) break;\r\n el = el.parentElement;\r\n }\r\n\r\n // 2. aria-label / title di sepanjang chain\r\n el = raw;\r\n while (el) {\r\n const label = el.getAttribute(\"aria-label\") || el.getAttribute(\"title\");\r\n if (label) {\r\n const text = sanitizeClickText(label.trim().slice(0, 50));\r\n if (text) return text;\r\n }\r\n if (el === target) break;\r\n el = el.parentElement;\r\n }\r\n\r\n // 3. text dari child terdekat (span/p/div) di dalam target\r\n const childText = target.querySelector(\"span, p, div\")?.textContent?.trim();\r\n if (childText) {\r\n const text = sanitizeClickText(childText.slice(0, 50));\r\n if (text) return text;\r\n }\r\n\r\n // 4. text dari sibling terdekat\r\n const siblingText =\r\n target.previousElementSibling?.textContent?.trim() || target.nextElementSibling?.textContent?.trim();\r\n if (siblingText) {\r\n const text = sanitizeClickText(siblingText.slice(0, 50));\r\n if (text) return text;\r\n }\r\n\r\n return \"\";\r\n};\r\n\r\nconst getElementSelector = (el: HTMLElement): string => {\r\n const tag = el.tagName?.toLowerCase() ?? \"unknown\";\r\n\r\n if (el.id) return `${tag}#${el.id}`;\r\n\r\n const firstClass =\r\n typeof el.className === \"string\" && el.className.trim() ? el.className.trim().split(/\\s+/)[0] : \"\";\r\n if (firstClass) return `${tag}.${firstClass}`;\r\n\r\n const parent = el.parentElement;\r\n if (parent) {\r\n const index = Array.from(parent.children).indexOf(el) + 1;\r\n return `${tag}:nth-child(${index})`;\r\n }\r\n\r\n return tag;\r\n};\r\n\r\nconst parseRequestBody = (body: unknown): unknown => {\r\n if (body == null) return null;\r\n if (typeof body === \"string\") {\r\n try {\r\n return JSON.parse(body);\r\n } catch {\r\n return body;\r\n }\r\n }\r\n if (body instanceof FormData) {\r\n const result: Record<string, unknown> = {};\r\n body.forEach((value, key) => {\r\n result[key] = value;\r\n });\r\n return result;\r\n }\r\n if (body instanceof URLSearchParams) {\r\n const result: Record<string, string> = {};\r\n body.forEach((value, key) => {\r\n result[key] = value;\r\n });\r\n return result;\r\n }\r\n return \"[unsupported body type]\";\r\n};\r\n\r\nconst parseQueryParams = (url: string): Record<string, string> | null => {\r\n try {\r\n const params = new URL(url, window.location.origin).searchParams;\r\n const result: Record<string, string> = {};\r\n params.forEach((value, key) => {\r\n result[key] = value;\r\n });\r\n return Object.keys(result).length > 0 ? result : null;\r\n } catch {\r\n return null;\r\n }\r\n};\r\n\r\nclass Logger {\r\n private config: LoggerConfig | null = null;\r\n private initialized = false;\r\n private breadcrumbs: Breadcrumb[] = [];\r\n\r\n private lastDomActivity = Date.now();\r\n private clickHistory = new Map<string, number[]>();\r\n private rageClickFired = new Map<string, number>();\r\n private domObserverInitialized = false;\r\n\r\n private deadClickWarnTimestamps: number[] = [];\r\n private readonly DEAD_CLICK_RATE_LIMIT_WINDOW_MS = 60000;\r\n private readonly DEAD_CLICK_RATE_LIMIT_MAX = 5;\r\n\r\n private recentDomChanges: string[] = [];\r\n\r\n private isElementDisabled(target: HTMLElement): boolean {\r\n return (target as HTMLButtonElement).disabled === true || target.getAttribute(\"aria-disabled\") === \"true\";\r\n }\r\n\r\n // Helper: cek apakah ada field invalid di form terdekat dari target\r\n private hasInvalidFieldNearby(target: HTMLElement): boolean {\r\n const form = target.closest(\"form\");\r\n if (!form) return false;\r\n return form.querySelector('[aria-invalid=\"true\"]') !== null;\r\n }\r\n\r\n private initDomActivityObserver(): void {\r\n if (this.domObserverInitialized) return;\r\n this.domObserverInitialized = true;\r\n\r\n const ALERT_SELECTOR = '[role=\"alert\"], [aria-live], .MuiSnackbar-root, .MuiAlert-root';\r\n\r\n const getAlertElement = (el: HTMLElement | null): HTMLElement | null => {\r\n return el?.closest(ALERT_SELECTOR) ?? null;\r\n };\r\n\r\n const markActivity = (addedText?: string) => {\r\n this.lastDomActivity = Date.now();\r\n if (addedText) {\r\n this.recentDomChanges.push(addedText);\r\n setTimeout(() => {\r\n this.recentDomChanges = this.recentDomChanges.filter((t) => t !== addedText);\r\n }, 1000);\r\n }\r\n };\r\n\r\n const observer = new MutationObserver((mutations) => {\r\n markActivity();\r\n for (const m of mutations) {\r\n if (m.type === \"childList\") {\r\n m.addedNodes.forEach((node) => {\r\n if (node.nodeType !== Node.ELEMENT_NODE) return;\r\n const alertEl = getAlertElement(node as HTMLElement);\r\n if (!alertEl) return;\r\n const text = alertEl.innerText?.trim().slice(0, 200);\r\n if (text) markActivity(text);\r\n });\r\n } else if (m.type === \"characterData\") {\r\n const alertEl = getAlertElement(m.target.parentElement);\r\n if (!alertEl) continue;\r\n const text = alertEl.innerText?.trim().slice(0, 200);\r\n if (text) markActivity(text);\r\n }\r\n }\r\n });\r\n\r\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true });\r\n window.addEventListener(\"scroll\", () => markActivity(), { passive: true, capture: true });\r\n }\r\n\r\n init(config: LoggerConfig) {\r\n this.config = config;\r\n initFirebase(config);\r\n initEnvironment();\r\n this.initialized = true;\r\n console.info(\"[ga-logger] initialized —\", config.env);\r\n }\r\n setUserId(userId: string | null) {\r\n if (!this.config) {\r\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\r\n return;\r\n }\r\n this.config.userId = userId;\r\n }\r\n\r\n initAnonymousUser(): void {\r\n if (!this.config) {\r\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\r\n return;\r\n }\r\n\r\n const STORAGE_KEY = \"ga_anon_id\";\r\n\r\n try {\r\n let anonId = localStorage.getItem(STORAGE_KEY);\r\n\r\n if (!anonId) {\r\n anonId =\r\n typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\r\n ? crypto.randomUUID()\r\n : \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\r\n return v.toString(16);\r\n });\r\n\r\n localStorage.setItem(STORAGE_KEY, anonId);\r\n }\r\n\r\n const shortId = anonId.slice(-8);\r\n this.setUserId(`Anonymous #${shortId}`);\r\n } catch (e) {\r\n console.warn(\"[ga-logger] initAnonymousUser failed:\", e);\r\n this.setUserId(\"Anonymous\");\r\n }\r\n }\r\n\r\n // Add breadcrumb ke in-memory array\r\n addBreadcrumb(type: BreadcrumbType, message: string, data?: Record<string, unknown>) {\r\n const breadcrumb: Breadcrumb = {\r\n timestamp: new Date(),\r\n type,\r\n message,\r\n data,\r\n };\r\n\r\n this.breadcrumbs.push(breadcrumb);\r\n\r\n // Jaga memory — max 50 breadcrumbs, buang yang paling lama\r\n if (this.breadcrumbs.length > MAX_BREADCRUMBS) {\r\n this.breadcrumbs.shift();\r\n }\r\n }\r\n\r\n // Global error capture — hanya aktif di non-development\r\n private loggedErrorKeys = new Set<string>();\r\n\r\n // Helper: trim URL jadi domain/filename\r\n private trimUrl(url: string): string {\r\n try {\r\n const u = new URL(url);\r\n const filename = u.pathname.split(\"/\").filter(Boolean).pop() ?? u.pathname;\r\n return `${u.hostname}/${filename}`;\r\n } catch {\r\n // bukan valid URL, potong saja jika terlalu panjang\r\n return url.length > 80 ? url.slice(0, 80) + \"...\" : url;\r\n }\r\n }\r\n\r\n // Helper: build dedup key\r\n private buildErrorKey(type: string, message: string): string {\r\n return `${type}::${message}`;\r\n }\r\n\r\n // Helper: should skip resource error\r\n private shouldSkipResourceError(src: string, tagName: string): boolean {\r\n // Skip IMG — tidak sebabkan blank page\r\n if (tagName === \"IMG\") return true;\r\n\r\n // Skip base64 URL\r\n if (src.startsWith(\"data:\")) return true;\r\n\r\n // Skip known 3rd party trackers\r\n const THIRD_PARTY = [\r\n \"omguk.com\",\r\n \"track.\",\r\n \"doubleclick\",\r\n \"adservice\",\r\n \"googletagmanager\",\r\n \"google-analytics\",\r\n \"hotjar\",\r\n \"facebook.net\",\r\n \"analytics\",\r\n \"magnolia\",\r\n \"gtm.js\",\r\n ];\r\n if (THIRD_PARTY.some((p) => src.includes(p))) return true;\r\n\r\n return false;\r\n }\r\n\r\n private shouldSkipGlobalError(message: string): boolean {\r\n const NOISE = [\r\n \"CORS\",\r\n \"cross-origin\",\r\n \"ERR_FAILED\",\r\n \"Failed to load resource\",\r\n \"Failed to load image\",\r\n \"NetworkError\",\r\n \"ResizeObserver loop\",\r\n \"Script error\",\r\n \"ServiceWorker\",\r\n \"sw.js\",\r\n \"dev-sw.js\",\r\n \"Warning:\",\r\n \"Error loading image\",\r\n ];\r\n const THIRD_PARTY = [\r\n \"magnolia\",\r\n \"gtm.js\",\r\n \"analytics\",\r\n \"googletagmanager\",\r\n \"facebook\",\r\n \"hotjar\",\r\n \"omguk.com\",\r\n \"track.\",\r\n \"doubleclick\",\r\n \"adservice\",\r\n ];\r\n return NOISE.some((p) => message.includes(p)) || THIRD_PARTY.some((p) => message.includes(p));\r\n }\r\n\r\n initGlobalCapture(): void {\r\n const isLocalhost = window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n if (isLocalhost) return;\r\n\r\n window.onerror = (message, source, lineno, colno) => {\r\n const msg = typeof message === \"string\" ? message : \"Unknown error\";\r\n if (this.shouldSkipGlobalError(msg)) return false;\r\n\r\n const trimmedSource = source ? this.trimUrl(source) : \"unknown source\";\r\n const finalMessage = `${msg} at ${trimmedSource}:${lineno ?? 0}`;\r\n const key = this.buildErrorKey(\"onerror\", finalMessage);\r\n if (this.loggedErrorKeys.has(key)) return false;\r\n this.loggedErrorKeys.add(key);\r\n this.addBreadcrumb(\"error\", finalMessage, { source: trimmedSource, lineno, colno });\r\n\r\n void this.error({\r\n fnName: \"window.onerror\",\r\n messageError: new Error(finalMessage),\r\n payload: { source: trimmedSource, lineno, colno },\r\n });\r\n\r\n return false;\r\n };\r\n\r\n // unhandledrejection\r\n window.addEventListener(\"unhandledrejection\", (event) => {\r\n const msg =\r\n event.reason instanceof Error\r\n ? event.reason.message\r\n : String(event.reason ?? \"Unhandled Promise rejection\");\r\n\r\n if (this.shouldSkipGlobalError(msg)) return;\r\n\r\n const key = this.buildErrorKey(\"unhandledrejection\", msg);\r\n if (this.loggedErrorKeys.has(key)) return;\r\n this.loggedErrorKeys.add(key);\r\n this.addBreadcrumb(\"error\", msg);\r\n\r\n void this.error({\r\n fnName: \"window.unhandledrejection\",\r\n messageError: new Error(msg),\r\n });\r\n });\r\n\r\n // console.error override\r\n const originalConsoleError = console.error.bind(console);\r\n console.error = (...args: unknown[]) => {\r\n originalConsoleError(...args);\r\n\r\n const msg = args.map((a) => (a instanceof Error ? a.message : String(a))).join(\" \");\r\n\r\n if (this.shouldSkipGlobalError(msg)) return;\r\n\r\n const trimmedMsg = msg.length > 150 ? msg.slice(0, 150) + \"...\" : msg;\r\n const key = this.buildErrorKey(\"console.error\", trimmedMsg);\r\n if (this.loggedErrorKeys.has(key)) return;\r\n this.loggedErrorKeys.add(key);\r\n\r\n this.addBreadcrumb(\"error\", trimmedMsg);\r\n\r\n const originalError = args.find((a): a is Error => a instanceof Error);\r\n const errorToSend = originalError ?? new Error(trimmedMsg);\r\n\r\n void this.error({\r\n fnName: \"console.error\",\r\n messageError: errorToSend,\r\n });\r\n };\r\n\r\n // resource load error (CSS / JS / font)\r\n window.addEventListener(\r\n \"error\",\r\n (event) => {\r\n const target = event.target;\r\n if (!(target instanceof HTMLElement)) return;\r\n if (target === (window as unknown)) return;\r\n\r\n const tagName = target.tagName.toUpperCase();\r\n\r\n const src =\r\n tagName === \"LINK\"\r\n ? (target as HTMLLinkElement).href\r\n : tagName === \"SCRIPT\"\r\n ? (target as HTMLScriptElement).src\r\n : \"\";\r\n\r\n if (this.shouldSkipResourceError(src, tagName)) return;\r\n\r\n // Deteksi dari ekstensi file, bukan tagName\r\n const ext = src.split(\"?\")[0].split(\".\").pop()?.toLowerCase();\r\n const resourceType =\r\n ext === \"css\"\r\n ? \"CSS\"\r\n : ext === \"js\"\r\n ? \"JS\"\r\n : ext === \"woff\" || ext === \"woff2\" || ext === \"ttf\"\r\n ? \"FONT\"\r\n : tagName;\r\n\r\n const trimmedSrc = this.trimUrl(src);\r\n const finalMessage = `Failed to load ${resourceType}: ${trimmedSrc}`;\r\n const key = this.buildErrorKey(\"resource\", finalMessage);\r\n if (this.loggedErrorKeys.has(key)) return;\r\n this.loggedErrorKeys.add(key);\r\n\r\n void this.error({\r\n fnName: \"resource.onerror\",\r\n messageError: new Error(finalMessage),\r\n payload: { tagName, src: trimmedSrc },\r\n });\r\n },\r\n true,\r\n );\r\n }\r\n\r\n private sanitizePayload(obj: Record<string, unknown>): Record<string, unknown> {\r\n return Object.fromEntries(\r\n Object.entries(obj).map(([key, value]) => {\r\n if (value === undefined) return [key, null];\r\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\r\n return [key, this.sanitizePayload(value as Record<string, unknown>)];\r\n }\r\n return [key, value];\r\n }),\r\n );\r\n }\r\n\r\n private sanitizeForFirestore(obj: Record<string, unknown>): Record<string, unknown> {\r\n return Object.fromEntries(\r\n Object.entries(obj).map(([key, value]) => {\r\n if (value === undefined) return [key, null];\r\n if (typeof value === \"function\") return [key, \"[function]\"];\r\n if (value instanceof Date) return [key, value.toISOString()];\r\n if (Array.isArray(value)) {\r\n return [\r\n key,\r\n value.map((item) => {\r\n if (item instanceof Date) return item.toISOString();\r\n if (typeof item === \"function\") return \"[function]\";\r\n if (item !== null && typeof item === \"object\") {\r\n return this.sanitizeForFirestore(item as Record<string, unknown>);\r\n }\r\n return item ?? null;\r\n }),\r\n ];\r\n }\r\n if (value !== null && typeof value === \"object\") {\r\n return [key, this.sanitizeForFirestore(value as Record<string, unknown>)];\r\n }\r\n return [key, value];\r\n }),\r\n );\r\n }\r\n\r\n // interceptFetch — auto breadcrumb, ke Firestore\r\n interceptFetch() {\r\n if (typeof window === \"undefined\") return;\r\n\r\n const originalFetch = window.fetch;\r\n\r\n const skipPatterns = [\r\n \"google.com\",\r\n \"googletagmanager.com\",\r\n \"googleapis.com\",\r\n \"facebook.com\",\r\n \"hotjar.com\",\r\n \"analytics\",\r\n \"gtm\",\r\n \"ccm/collect\",\r\n \"on.aws\",\r\n \"/locales/\",\r\n \"/sw.js\",\r\n \"/manifest.json\",\r\n \"/sourcemaps/\",\r\n \"magnolia\",\r\n ];\r\n\r\n const shouldSkipUrl = (url: string): boolean => {\r\n return skipPatterns.some((pattern) => url.includes(pattern));\r\n };\r\n\r\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\r\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\r\n\r\n // Skip third-party domains\r\n if (shouldSkipUrl(url)) {\r\n return originalFetch(input, init);\r\n }\r\n\r\n const method = init?.method ?? \"GET\";\r\n const startTime = Date.now();\r\n\r\n try {\r\n const response = await originalFetch(input, init);\r\n const duration = Date.now() - startTime;\r\n\r\n this.addBreadcrumb(\"fetch\", `${method} ${url}`, {\r\n status: response.status,\r\n duration: `${duration}ms`,\r\n });\r\n\r\n if (response.status >= 400) {\r\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} ${response.status}`);\r\n\r\n if (!this.loggedErrorKeys.has(dedupKey)) {\r\n this.loggedErrorKeys.add(dedupKey);\r\n\r\n let responseBody: unknown = null;\r\n try {\r\n const cloned = response.clone();\r\n const contentType = cloned.headers.get(\"content-type\") ?? \"\";\r\n responseBody = contentType.includes(\"application/json\")\r\n ? await cloned.json()\r\n : await cloned.text();\r\n } catch {\r\n responseBody = \"[unable to read response body]\";\r\n }\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n\r\n if (!isLocalhost) {\r\n this.error({\r\n fnName: \"interceptFetch\",\r\n messageError: new Error(`HTTP ${response.status} ${response.statusText} — ${url}`),\r\n endpointName: url,\r\n payload: {\r\n method,\r\n status: response.status,\r\n statusText: response.statusText,\r\n duration: `${duration}ms`,\r\n query: parseQueryParams(url),\r\n request: parseRequestBody(init?.body),\r\n response: responseBody,\r\n },\r\n });\r\n }\r\n }\r\n }\r\n\r\n return response;\r\n } catch (networkError) {\r\n const duration = Date.now() - startTime;\r\n const error = networkError instanceof Error ? networkError : new Error(String(networkError));\r\n\r\n this.addBreadcrumb(\"fetch\", `${method} ${url} — NETWORK ERROR`, {\r\n duration: `${duration}ms`,\r\n error: error.message,\r\n });\r\n\r\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} NETWORK_ERROR`);\r\n\r\n if (!this.loggedErrorKeys.has(dedupKey)) {\r\n this.loggedErrorKeys.add(dedupKey);\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n if (!isLocalhost) {\r\n this.error({\r\n fnName: \"interceptFetch\",\r\n messageError: error,\r\n endpointName: url,\r\n payload: { method, duration: `${duration}ms` },\r\n });\r\n }\r\n }\r\n\r\n throw networkError;\r\n }\r\n };\r\n\r\n console.info(\"[ga-logger] fetch intercepted\");\r\n }\r\n\r\n // interceptXHR — auto breadcrumb + error capture untuk axios/XHR-based request\r\n interceptXHR(): void {\r\n if (typeof window === \"undefined\") return;\r\n\r\n const skipPatterns = [\r\n \"google.com\",\r\n \"googletagmanager.com\",\r\n \"googleapis.com\",\r\n \"facebook.com\",\r\n \"hotjar.com\",\r\n \"analytics\",\r\n \"ccm/collect\",\r\n \"on.aws\",\r\n \"/locales/\",\r\n \"/sw.js\",\r\n \"/manifest.json\",\r\n \"/sourcemaps/\",\r\n \"magnolia\",\r\n ];\r\n\r\n const shouldSkipUrl = (url: string): boolean => skipPatterns.some((p) => url.includes(p));\r\n\r\n const originalOpen = XMLHttpRequest.prototype.open;\r\n const originalSend = XMLHttpRequest.prototype.send;\r\n const self = this;\r\n\r\n type GaXHR = XMLHttpRequest & {\r\n _gaMethod?: string;\r\n _gaUrl?: string;\r\n _gaStartTime?: number;\r\n };\r\n\r\n XMLHttpRequest.prototype.open = function (this: GaXHR, method: string, url: string | URL, ...rest: unknown[]) {\r\n this._gaMethod = method;\r\n this._gaUrl = typeof url === \"string\" ? url : url.href;\r\n // @ts-expect-error - spreading native arguments\r\n return originalOpen.call(this, method, url, ...rest);\r\n };\r\n\r\n XMLHttpRequest.prototype.send = function (this: GaXHR, ...args: unknown[]) {\r\n const url = this._gaUrl ?? \"\";\r\n const method = this._gaMethod ?? \"GET\";\r\n\r\n if (shouldSkipUrl(url)) {\r\n // @ts-expect-error - spreading native arguments\r\n return originalSend.call(this, ...args);\r\n }\r\n\r\n this._gaStartTime = Date.now();\r\n const requestBody = parseRequestBody(args[0]);\r\n\r\n this.addEventListener(\"loadend\", () => {\r\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\r\n const status = this.status;\r\n\r\n self.addBreadcrumb(\"fetch\", `${method} ${url}`, {\r\n status,\r\n duration: `${duration}ms`,\r\n });\r\n\r\n if (status >= 400) {\r\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} ${status}`);\r\n if (self.loggedErrorKeys.has(dedupKey)) return;\r\n self.loggedErrorKeys.add(dedupKey);\r\n\r\n let responseBody: unknown = this.responseText;\r\n try {\r\n responseBody = JSON.parse(this.responseText);\r\n } catch {\r\n // keep as raw text\r\n }\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n\r\n if (!isLocalhost) {\r\n void self.error({\r\n fnName: \"interceptXHR\",\r\n messageError: new Error(`HTTP ${status} ${this.statusText} — ${url}`),\r\n endpointName: url,\r\n payload: {\r\n method,\r\n status,\r\n statusText: this.statusText,\r\n duration: `${duration}ms`,\r\n query: parseQueryParams(url),\r\n request: requestBody,\r\n response: responseBody,\r\n },\r\n });\r\n }\r\n }\r\n });\r\n\r\n this.addEventListener(\"error\", () => {\r\n // network-level failure (status selalu 0)\r\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\r\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} NETWORK_ERROR`);\r\n if (self.loggedErrorKeys.has(dedupKey)) return;\r\n self.loggedErrorKeys.add(dedupKey);\r\n\r\n const isLocalhost =\r\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\r\n\r\n if (!isLocalhost) {\r\n void self.error({\r\n fnName: \"interceptXHR\",\r\n messageError: new Error(`Network error — ${method} ${url}`),\r\n endpointName: url,\r\n payload: { method, duration: `${duration}ms` },\r\n });\r\n }\r\n });\r\n\r\n // @ts-expect-error - spreading native arguments\r\n return originalSend.call(this, ...args);\r\n };\r\n\r\n console.info(\"[ga-logger] XHR intercepted\");\r\n }\r\n\r\n interceptClick(): void {\r\n if (typeof window === \"undefined\") return;\r\n\r\n if ((window as any).__ga_click_intercepted) return;\r\n (window as any).__ga_click_intercepted = true;\r\n this.initDomActivityObserver();\r\n\r\n document.addEventListener(\r\n \"click\",\r\n (event: MouseEvent) => {\r\n const raw = event.target as HTMLElement;\r\n if (!raw) return;\r\n\r\n const target = (raw.closest('button, a, [role=\"button\"], [data-log], input') as HTMLElement) ?? raw;\r\n\r\n const innerText = getClosestClickText(raw, target);\r\n const id = target.id ?? \"\";\r\n const tagName = target.tagName?.toLowerCase() ?? \"\";\r\n\r\n const isInteractive =\r\n [\"button\", \"a\", \"input\"].includes(tagName) ||\r\n target.hasAttribute(\"data-log\") ||\r\n target.getAttribute(\"role\") === \"button\";\r\n\r\n const selector = getElementSelector(target);\r\n const href = tagName === \"a\" ? target.getAttribute(\"href\") : null;\r\n\r\n if (!innerText && !id && !isInteractive) return;\r\n\r\n const message = innerText || id || `(no label: ${tagName || \"unknown\"})`;\r\n\r\n // Skip internal React/Router identifiers\r\n if (CLICK_SKIP_PATTERNS.some((p) => p.test(message))) return;\r\n\r\n this.addBreadcrumb(\"click\", message, {\r\n tagName,\r\n ...(id ? { id } : {}),\r\n ...(innerText ? { text: innerText } : {}),\r\n });\r\n\r\n const disabledAtClick = this.isElementDisabled(target);\r\n const invalidFieldAtClick = this.hasInvalidFieldNearby(target);\r\n const ariaDisabledAtClick = target.getAttribute(\"aria-disabled\") === \"true\";\r\n const pointerEventsBlockedAtClick = window.getComputedStyle(target).pointerEvents === \"none\";\r\n const overlayInterceptedAtClick = (() => {\r\n const elAtPoint = document.elementFromPoint(event.clientX, event.clientY);\r\n if (!elAtPoint || elAtPoint === target || target.contains(elAtPoint)) return null;\r\n return elAtPoint as HTMLElement;\r\n })();\r\n // ── Dead click detection ──\r\n const clickTime = Date.now();\r\n const domActivityAtClick = this.lastDomActivity;\r\n const DEAD_CLICK_DELAY_MS = 4000;\r\n const RAGE_CLICK_WINDOW_MS = 2000;\r\n const RAGE_CLICK_COOLDOWN_MS = 5000;\r\n\r\n if ([\"button\", \"a\", \"input\"].includes(tagName)) {\r\n setTimeout(() => {\r\n if (this.lastDomActivity <= domActivityAtClick) {\r\n const recentFetch = this.breadcrumbs\r\n .filter(\r\n (b) =>\r\n b.type === \"fetch\" &&\r\n new Date(b.timestamp).getTime() >= clickTime &&\r\n new Date(b.timestamp).getTime() <= clickTime + DEAD_CLICK_DELAY_MS,\r\n )\r\n .slice(-1)[0];\r\n\r\n const reason: ClickReason = disabledAtClick\r\n ? { type: \"disabled\", detail: \"Button dalam kondisi disabled saat diklik\" }\r\n : recentFetch\r\n ? { type: \"api_error\", detail: recentFetch.message }\r\n : invalidFieldAtClick\r\n ? { type: \"validation_error\", detail: \"Field invalid terdeteksi di form terdekat\" }\r\n : {\r\n type: \"no_dom_activity\",\r\n detail: `Tidak ada aktivitas DOM/scroll dalam ${DEAD_CLICK_DELAY_MS / 1000} detik`,\r\n };\r\n\r\n const formattedLog = formatClickLog({\r\n fnName: \"interceptClick.deadClick\",\r\n tagName,\r\n text: message,\r\n selector,\r\n reason,\r\n });\r\n\r\n this.addBreadcrumb(\"click\", `Button tidak bisa diklik: ${message}`, {\r\n tagName,\r\n ...(id ? { id } : {}),\r\n reason,\r\n });\r\n\r\n // ── Rate-limit: max N dead-click warn per session per window ──\r\n const now = Date.now();\r\n this.deadClickWarnTimestamps = this.deadClickWarnTimestamps.filter(\r\n (t) => now - t < this.DEAD_CLICK_RATE_LIMIT_WINDOW_MS,\r\n );\r\n\r\n if (this.deadClickWarnTimestamps.length >= this.DEAD_CLICK_RATE_LIMIT_MAX) {\r\n return;\r\n }\r\n this.deadClickWarnTimestamps.push(now);\r\n\r\n void this.warn({\r\n fnName: \"interceptClick.deadClick\",\r\n messageError: new Error(`Button tidak bisa diklik: ${message}`),\r\n payload: {\r\n tagName,\r\n ...(id ? { id } : {}),\r\n reason,\r\n text: message,\r\n selector,\r\n ...(href ? { href } : {}),\r\n formattedLog,\r\n },\r\n excludeBreadcrumbTypes: [\"fetch\"],\r\n breadcrumbWindowMs: 10000,\r\n captureScreenshot: true,\r\n highlightTarget: target,\r\n });\r\n }\r\n }, DEAD_CLICK_DELAY_MS);\r\n }\r\n\r\n // ── Rage click detection 3x klik ──\r\n if (isInteractive) {\r\n const historyKey = `${tagName}::${message}`;\r\n const timestamps = (this.clickHistory.get(historyKey) ?? []).filter(\r\n (t) => clickTime - t < RAGE_CLICK_WINDOW_MS,\r\n );\r\n timestamps.push(clickTime);\r\n this.clickHistory.set(historyKey, timestamps);\r\n\r\n const lastFired = this.rageClickFired.get(historyKey) ?? 0;\r\n\r\n if (timestamps.length >= 3 && clickTime - lastFired > RAGE_CLICK_COOLDOWN_MS) {\r\n this.rageClickFired.set(historyKey, clickTime);\r\n\r\n const recentFetch = this.breadcrumbs\r\n .filter(\r\n (b) =>\r\n b.type === \"fetch\" &&\r\n clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS,\r\n )\r\n .slice(-1)[0];\r\n\r\n const recentError = this.breadcrumbs\r\n .filter(\r\n (b) =>\r\n b.type === \"error\" &&\r\n clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS,\r\n )\r\n .slice(-1)[0];\r\n\r\n // rage-click possibleReason chain (urutan final)\r\n const possibleReason: ClickReason = disabledAtClick\r\n ? { type: \"disabled\", detail: \"Button dalam kondisi disabled saat diklik\" }\r\n : ariaDisabledAtClick\r\n ? {\r\n type: \"aria_disabled\",\r\n detail: 'Element memiliki aria-disabled=\"true\" (disabled secara visual/aksesibilitas, bukan native disabled attribute)',\r\n }\r\n : pointerEventsBlockedAtClick\r\n ? {\r\n type: \"pointer_events_blocked\",\r\n detail: \"Element memiliki CSS pointer-events: none saat diklik (tidak bisa menerima event klik sama sekali)\",\r\n }\r\n : overlayInterceptedAtClick\r\n ? {\r\n type: \"overlay_intercepted\",\r\n detail: `Element lain (${getElementSelector(overlayInterceptedAtClick)}) menutupi target saat klik terjadi, klik tidak sampai ke target aslinya`,\r\n }\r\n : recentFetch\r\n ? { type: \"api_error\", detail: recentFetch.message }\r\n : invalidFieldAtClick\r\n ? {\r\n type: \"validation_error\",\r\n detail: \"Field invalid terdeteksi di form terdekat\",\r\n }\r\n : recentError\r\n ? { type: \"js_error\", detail: recentError.message }\r\n : this.recentDomChanges.length\r\n ? { type: \"dom_feedback\", detail: this.recentDomChanges.slice(-1)[0] }\r\n : {\r\n type: \"no_feedback\",\r\n detail: \"Tidak ada feedback UI terdeteksi setelah klik\",\r\n };\r\n\r\n const formattedLog = formatClickLog({\r\n fnName: \"interceptClick.rageClick\",\r\n tagName,\r\n text: message,\r\n selector,\r\n clickCount: timestamps.length,\r\n windowMs: RAGE_CLICK_WINDOW_MS,\r\n reason: possibleReason,\r\n });\r\n\r\n this.addBreadcrumb(\"click\", `Klik Berulang kali: ${message}`, {\r\n tagName,\r\n clickCount: timestamps.length,\r\n windowMs: RAGE_CLICK_WINDOW_MS,\r\n possibleReason,\r\n });\r\n\r\n void this.warn({\r\n fnName: \"interceptClick.rageClick\",\r\n messageError: new Error(`Klik Berulang kali: ${message}`),\r\n payload: {\r\n tagName,\r\n clickCount: timestamps.length,\r\n windowMs: RAGE_CLICK_WINDOW_MS,\r\n possibleReason,\r\n text: message,\r\n selector,\r\n ...(href ? { href } : {}),\r\n formattedLog,\r\n },\r\n excludeBreadcrumbTypes: [\"fetch\"],\r\n breadcrumbWindowMs: 10000,\r\n captureScreenshot: true,\r\n highlightTarget: target,\r\n });\r\n }\r\n }\r\n },\r\n true,\r\n );\r\n }\r\n\r\n captureAction<T>(params: LogParams, fn: () => T | Promise<T>): Promise<T | undefined> {\r\n return Promise.resolve()\r\n .then(() => fn())\r\n .catch((error: unknown) => {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.send(\"error\", params.fnName, params.payload, err, params.endpointName);\r\n return undefined;\r\n });\r\n }\r\n\r\n withLogger<T extends (...args: any[]) => any>(\r\n fnName: string,\r\n fn: T,\r\n options?: {\r\n endpointName?: string;\r\n payload?: Record<string, unknown>;\r\n },\r\n ): T {\r\n return ((...args: Parameters<T>): ReturnType<T> | undefined => {\r\n try {\r\n const result = fn(...args);\r\n if (result instanceof Promise) {\r\n return result.catch((error: unknown) => {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.error({\r\n fnName,\r\n messageError: err,\r\n endpointName: options?.endpointName,\r\n payload: options?.payload,\r\n });\r\n return undefined;\r\n }) as ReturnType<T>;\r\n }\r\n\r\n return result;\r\n } catch (error: unknown) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.error({\r\n fnName,\r\n messageError: err,\r\n endpointName: options?.endpointName,\r\n payload: options?.payload,\r\n });\r\n return undefined;\r\n }\r\n }) as T;\r\n }\r\n\r\n private waitForScrollEnd(timeoutMs = 1500, idleMs = 100): Promise<void> {\r\n return new Promise((resolve) => {\r\n let idleTimer: ReturnType<typeof setTimeout>;\r\n let settled = false;\r\n\r\n const cleanup = () => {\r\n if (settled) return;\r\n settled = true;\r\n clearTimeout(idleTimer);\r\n clearTimeout(hardTimeout);\r\n window.removeEventListener(\"scroll\", onScroll, true);\r\n resolve();\r\n };\r\n\r\n const onScroll = () => {\r\n clearTimeout(idleTimer);\r\n idleTimer = setTimeout(cleanup, idleMs);\r\n };\r\n\r\n // fallback: if no scroll event ever fires (target already in view), don't hang\r\n const hardTimeout = setTimeout(cleanup, timeoutMs);\r\n\r\n window.addEventListener(\"scroll\", onScroll, true);\r\n // start the idle timer immediately too, in case scrollIntoView is a no-op\r\n idleTimer = setTimeout(cleanup, idleMs);\r\n });\r\n }\r\n\r\n // private createHighlightOverlay(target: HTMLElement): HTMLDivElement {\r\n // const rect = target.getBoundingClientRect();\r\n // const padding = 0;\r\n\r\n // const overlay = document.createElement(\"div\");\r\n // overlay.style.position = \"absolute\";\r\n // overlay.style.top = `${rect.top + window.scrollY - padding}px`;\r\n // overlay.style.left = `${rect.left + window.scrollX - padding}px`;\r\n // overlay.style.width = `${rect.width + padding * 2}px`;\r\n // overlay.style.height = `${rect.height + padding * 2}px`;\r\n // overlay.style.border = \"2px solid red\";\r\n // overlay.style.pointerEvents = \"none\";\r\n // overlay.style.zIndex = \"999999\";\r\n\r\n // document.body.appendChild(overlay);\r\n // return overlay;\r\n // }\r\n\r\n private async captureAndUploadScreenshot(highlightTarget?: HTMLElement): Promise<string | null> {\r\n const canApplyHighlight = !!highlightTarget && document.contains(highlightTarget);\r\n const HIGHLIGHT_MARKER = \"ga-logger-highlight-target\";\r\n\r\n try {\r\n if (canApplyHighlight) {\r\n highlightTarget!.scrollIntoView({ block: \"center\", inline: \"center\" });\r\n await this.waitForScrollEnd();\r\n highlightTarget!.setAttribute(\"data-ga-highlight\", HIGHLIGHT_MARKER);\r\n }\r\n\r\n const html2canvas = (await import(\"html2canvas-pro\")).default;\r\n\r\n const canvas = await html2canvas(document.body, {\r\n x: window.scrollX,\r\n y: window.scrollY,\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n imageTimeout: 3000,\r\n onclone: (clonedDoc) => {\r\n if (!canApplyHighlight) return;\r\n const clonedTarget = clonedDoc.querySelector(`[data-ga-highlight=\"${HIGHLIGHT_MARKER}\"]`);\r\n if (clonedTarget instanceof HTMLElement) {\r\n clonedTarget.style.border = \"2px solid red\";\r\n clonedTarget.style.boxSizing = \"border-box\";\r\n }\r\n },\r\n });\r\n return canvas.toDataURL(\"image/jpeg\", 0.4);\r\n } catch (e) {\r\n console.error(\"[ga-logger] screenshot capture failed:\", e);\r\n return null;\r\n } finally {\r\n highlightTarget?.removeAttribute(\"data-ga-highlight\");\r\n }\r\n }\r\n\r\n private async send(\r\n level: LogLevel,\r\n message: string,\r\n payload: Record<string, unknown> = {},\r\n error?: Error,\r\n endpoint?: string,\r\n excludeBreadcrumbTypes?: BreadcrumbType[],\r\n breadcrumbWindowMs?: number,\r\n captureScreenshot?: boolean,\r\n highlightTarget?: HTMLElement,\r\n ) {\r\n if (!this.initialized || !this.config) {\r\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\r\n return;\r\n }\r\n\r\n const isLocalhost =\r\n typeof window !== \"undefined\" &&\r\n (window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\");\r\n\r\n if (isLocalhost) {\r\n console.error(`[ga-localhost][DEV] ${message}`, { payload, error });\r\n return;\r\n }\r\n\r\n const now = Date.now();\r\n\r\n let filteredBreadcrumbs = excludeBreadcrumbTypes?.length\r\n ? this.breadcrumbs.filter((b) => !excludeBreadcrumbTypes.includes(b.type))\r\n : this.breadcrumbs;\r\n\r\n if (breadcrumbWindowMs) {\r\n filteredBreadcrumbs = filteredBreadcrumbs.filter(\r\n (b) => now - new Date(b.timestamp).getTime() <= breadcrumbWindowMs,\r\n );\r\n }\r\n\r\n const entry: LogEntry = {\r\n timestamp: new Date(),\r\n level,\r\n message,\r\n env: this.config.env,\r\n userId: this.config.userId ?? null,\r\n sessionId,\r\n page: typeof window !== \"undefined\" ? window.location.pathname : \"\",\r\n stackTrace: error?.stack ?? null,\r\n payload: this.sanitizePayload(payload),\r\n repo: this.config.repo,\r\n endpoint: endpoint ?? null,\r\n breadcrumbs: [...filteredBreadcrumbs],\r\n ...getEnvironmentPayload(),\r\n };\r\n\r\n try {\r\n const db = getDb();\r\n const docRef = doc(collection(db, \"logs\"));\r\n\r\n const safeEntry = this.sanitizeForFirestore({ ...entry } as unknown as Record<string, unknown>);\r\n\r\n await setDoc(docRef, {\r\n ...safeEntry,\r\n timestamp: Timestamp.fromDate(entry.timestamp),\r\n });\r\n\r\n if (captureScreenshot) {\r\n void this.captureAndUploadScreenshot(highlightTarget).then((base64) => {\r\n if (base64) {\r\n void updateDoc(docRef, { screenshotBase64: base64 }).catch((e) =>\r\n console.error(\"[ga-logger] failed to update screenshotBase64:\", e),\r\n );\r\n }\r\n });\r\n }\r\n } catch (e) {\r\n console.error(\"[ga-logger] failed to send log:\", e);\r\n }\r\n }\r\n\r\n info(params: LogParams) {\r\n const { fnName, messageError, payload, endpointName } = params;\r\n return this.send(\"info\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\r\n }\r\n\r\n warn(params: LogParams) {\r\n const {\r\n fnName,\r\n messageError,\r\n payload,\r\n endpointName,\r\n excludeBreadcrumbTypes,\r\n breadcrumbWindowMs,\r\n captureScreenshot,\r\n highlightTarget,\r\n } = params;\r\n return this.send(\r\n \"warn\",\r\n messageError?.message ?? fnName,\r\n { ...payload, fnName },\r\n messageError,\r\n endpointName,\r\n excludeBreadcrumbTypes,\r\n breadcrumbWindowMs,\r\n captureScreenshot,\r\n highlightTarget,\r\n );\r\n }\r\n\r\n error(params: LogParams) {\r\n const { fnName, messageError, payload, endpointName } = params;\r\n return this.send(\"error\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\r\n }\r\n}\r\n\r\nexport const logger = new Logger();","import { initializeApp, getApps, getApp } from \"firebase/app\";\nimport { getFirestore } from \"firebase/firestore\";\nimport type { Firestore } from \"firebase/firestore\";\nimport type { LoggerConfig } from \"../types\";\n\nconst APP_NAME = \"ga-logger\";\nlet db: Firestore | null = null;\n\nexport const initFirebase = (config: LoggerConfig) => {\n // Cek apakah named instance \"ga-logger\" sudah ada\n const existingApp = getApps().find((app) => app.name === APP_NAME);\n\n const app =\n existingApp ??\n initializeApp(\n {\n apiKey: config.apiKey,\n authDomain: config.authDomain,\n projectId: config.projectId,\n storageBucket: config.storageBucket,\n messagingSenderId: config.messagingSenderId,\n appId: config.appId,\n },\n APP_NAME,\n );\n\n db = getFirestore(app);\n return db;\n};\n\nexport const getDb = (): Firestore => {\n if (!db) throw new Error(\"[ga-logger] Firebase not initialized. Call logger.init() first.\");\n return db;\n};\n","interface EnvironmentPayload {\n userAgent: string;\n deviceType: \"Mobile\" | \"Tablet\" | \"Desktop\";\n screenWidth: number;\n screenHeight: number;\n}\n\nlet cachedEnvironment: EnvironmentPayload | null = null;\n\nfunction getDeviceType(width: number): \"Mobile\" | \"Tablet\" | \"Desktop\" {\n if (width < 768) return \"Mobile\";\n if (width <= 1024) return \"Tablet\";\n return \"Desktop\";\n}\n\nexport function initEnvironment(): void {\n if (cachedEnvironment) return;\n\n const width = window.innerWidth;\n const height = window.innerHeight;\n\n cachedEnvironment = {\n userAgent: navigator.userAgent,\n deviceType: getDeviceType(width),\n screenWidth: width,\n screenHeight: height,\n };\n}\n\nexport function getEnvironmentPayload(): EnvironmentPayload {\n if (!cachedEnvironment) initEnvironment();\n return cachedEnvironment!;\n}","import type { ClickReason, ClickReasonType } from \"../types\";\n\nconst REASON_LABEL: Record<ClickReasonType, string> = {\n disabled: \"Button sedang disabled saat diklik\",\n api_error: \"Request API gagal\",\n validation_error: \"Ada field invalid di form terdekat (validasi gagal)\",\n dom_feedback: \"Muncul notifikasi\",\n no_feedback: \"Tidak ada notifikasi/feedback UI apapun setelah diklik\",\n no_dom_activity: \"Tidak ada aktivitas DOM/scroll setelah diklik\",\n};\n\nconst SCREENSHOT_HINT: Partial<Record<ClickReasonType, string>> = {\n dom_feedback: \"📷 Lihat screenshot untuk detail visual alert\",\n no_feedback: \"📷 Cek screenshot — kemungkinan UI freeze/silent fail\",\n no_dom_activity: \"📷 Cek screenshot — kondisi layar saat klik terjadi\",\n};\n\ninterface ClickLogPayload {\n fnName: string;\n tagName: string;\n text: string;\n selector: string;\n clickCount?: number;\n windowMs?: number;\n reason: ClickReason;\n}\n\nconst buildDiagnosis = (reason: ClickReason): string => {\n const label = REASON_LABEL[reason.type];\n const hint = SCREENSHOT_HINT[reason.type];\n\n const base =\n reason.type === \"api_error\"\n ? `${label} — ${reason.detail}`\n : reason.type === \"dom_feedback\"\n ? `${label} — \"${reason.detail}\"`\n : label;\n\n return hint ? `${base}\\n ${hint}` : base;\n};\n\nconst formatSelectorAsHtml = (selector: string, tagName: string): string => {\n if (selector.includes(\".\")) {\n const className = selector.split(\".\").slice(1).join(\" \");\n return `class=\"${className}\"`;\n }\n if (selector.includes(\"#\")) {\n const id = selector.split(\"#\")[1];\n return `id=\"${id}\"`;\n }\n return \"\";\n};\n\nexport const formatClickLog = (payload: ClickLogPayload): string => {\n const isRageClick = payload.fnName.includes(\"rageClick\");\n const clickTypeLabel = isRageClick ? \"Klik Berulang\" : \"Klik Tanpa Respon\";\n const clickTypeSuffix = isRageClick ? \"(Rage Click)\" : \"(Dead Click)\";\n\n const stats = isRageClick\n ? `${payload.clickCount}x klik dalam ${payload.windowMs}ms`\n : `Terdeteksi setelah klik tanpa aktivitas`;\n\n const attr = formatSelectorAsHtml(payload.selector, payload.tagName);\n const openTag = attr ? `<${payload.tagName} ${attr}>` : `<${payload.tagName}>`;\n const elementHtml = payload.text ? `${openTag}${payload.text}</${payload.tagName}>` : `${openTag}</${payload.tagName}>`;\n\n return [\n `Summary : ${clickTypeLabel} pada ${payload.tagName} \"${payload.text}\" ${clickTypeSuffix}`,\n `Diagnosis : ${buildDiagnosis(payload.reason)}`,\n `Stats : ${stats}`,\n `Element : ${elementHtml}`,\n `Source : ${payload.fnName}`,\n ].join(\"\\n\");\n};\n\n","import { Component, type ErrorInfo, type ReactNode } from \"react\";\nimport { logger } from \"../core/logger\";\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode;\n}\n\ninterface State {\n hasError: boolean;\n}\n\nexport class LoggerErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(): State {\n return { hasError: true };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n logger.addBreadcrumb(\"error\", `React crash: ${error.message}`, {\n componentStack: info.componentStack ?? undefined,\n });\n\n logger.error({\n fnName: \"LoggerErrorBoundary\",\n messageError: error,\n payload: {\n componentStack: info.componentStack ?? undefined,\n },\n });\n }\n\n render() {\n if (this.state.hasError) {\n return this.props.fallback ?? (\n <div style={{ padding: \"1rem\", color: \"red\" }}>\n Something went wrong.\n </div>\n );\n }\n\n return this.props.children;\n }\n}"],"mappings":"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","names":["app","db"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ga-components/logger",
3
- "version": "2.0.25",
3
+ "version": "2.0.26",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",