@ga-components/logger 2.0.19 → 2.0.20
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.d.mts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +65 -48
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +65 -48
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -659,35 +659,44 @@ var Logger = class {
|
|
|
659
659
|
timestamps.push(clickTime);
|
|
660
660
|
this.clickHistory.set(historyKey, timestamps);
|
|
661
661
|
const lastFired = this.rageClickFired.get(historyKey) ?? 0;
|
|
662
|
-
if (
|
|
663
|
-
|
|
664
|
-
const
|
|
665
|
-
(
|
|
666
|
-
)
|
|
667
|
-
|
|
668
|
-
this.
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
payload: {
|
|
662
|
+
if (isInteractive) {
|
|
663
|
+
const historyKey2 = `${tagName}::${message}`;
|
|
664
|
+
const timestamps2 = (this.clickHistory.get(historyKey2) ?? []).filter(
|
|
665
|
+
(t) => clickTime - t < RAGE_CLICK_WINDOW_MS
|
|
666
|
+
);
|
|
667
|
+
timestamps2.push(clickTime);
|
|
668
|
+
this.clickHistory.set(historyKey2, timestamps2);
|
|
669
|
+
const lastFired2 = this.rageClickFired.get(historyKey2) ?? 0;
|
|
670
|
+
if (timestamps2.length >= 3 && clickTime - lastFired2 > RAGE_CLICK_COOLDOWN_MS) {
|
|
671
|
+
this.rageClickFired.set(historyKey2, clickTime);
|
|
672
|
+
const recentFetch = this.breadcrumbs.filter(
|
|
673
|
+
(b) => b.type === "fetch" && clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS
|
|
674
|
+
).slice(-1)[0];
|
|
675
|
+
const possibleReason = disabledAtClick ? "button is disabled" : recentFetch ? `recent request: ${recentFetch.message}` : invalidFieldAtClick ? "validation error detected nearby" : this.recentDomChanges.length ? this.recentDomChanges.slice(-1)[0] : "no visible UI feedback detected";
|
|
676
|
+
this.addBreadcrumb("click", `Loop click: ${message}`, {
|
|
678
677
|
tagName,
|
|
679
|
-
clickCount:
|
|
678
|
+
clickCount: timestamps2.length,
|
|
680
679
|
windowMs: RAGE_CLICK_WINDOW_MS,
|
|
681
|
-
possibleReason
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
680
|
+
possibleReason
|
|
681
|
+
});
|
|
682
|
+
void this.warn({
|
|
683
|
+
fnName: "interceptClick.rageClick",
|
|
684
|
+
messageError: new Error(`Loop click: ${message}`),
|
|
685
|
+
payload: {
|
|
686
|
+
tagName,
|
|
687
|
+
clickCount: timestamps2.length,
|
|
688
|
+
windowMs: RAGE_CLICK_WINDOW_MS,
|
|
689
|
+
possibleReason,
|
|
690
|
+
text: message,
|
|
691
|
+
selector,
|
|
692
|
+
...href ? { href } : {}
|
|
693
|
+
},
|
|
694
|
+
excludeBreadcrumbTypes: ["fetch"],
|
|
695
|
+
breadcrumbWindowMs: 1e4,
|
|
696
|
+
captureScreenshot: true,
|
|
697
|
+
highlightTarget: target
|
|
698
|
+
});
|
|
699
|
+
}
|
|
691
700
|
}
|
|
692
701
|
},
|
|
693
702
|
true
|
|
@@ -750,44 +759,51 @@ var Logger = class {
|
|
|
750
759
|
idleTimer = setTimeout(cleanup, idleMs);
|
|
751
760
|
});
|
|
752
761
|
}
|
|
753
|
-
createHighlightOverlay(target) {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
}
|
|
762
|
+
// private createHighlightOverlay(target: HTMLElement): HTMLDivElement {
|
|
763
|
+
// const rect = target.getBoundingClientRect();
|
|
764
|
+
// const padding = 0;
|
|
765
|
+
// const overlay = document.createElement("div");
|
|
766
|
+
// overlay.style.position = "absolute";
|
|
767
|
+
// overlay.style.top = `${rect.top + window.scrollY - padding}px`;
|
|
768
|
+
// overlay.style.left = `${rect.left + window.scrollX - padding}px`;
|
|
769
|
+
// overlay.style.width = `${rect.width + padding * 2}px`;
|
|
770
|
+
// overlay.style.height = `${rect.height + padding * 2}px`;
|
|
771
|
+
// overlay.style.border = "2px solid red";
|
|
772
|
+
// overlay.style.pointerEvents = "none";
|
|
773
|
+
// overlay.style.zIndex = "999999";
|
|
774
|
+
// document.body.appendChild(overlay);
|
|
775
|
+
// return overlay;
|
|
776
|
+
// }
|
|
768
777
|
async captureAndUploadScreenshot(highlightTarget) {
|
|
769
778
|
const canApplyHighlight = !!highlightTarget && document.contains(highlightTarget);
|
|
770
|
-
|
|
779
|
+
const HIGHLIGHT_MARKER = "ga-logger-highlight-target";
|
|
771
780
|
try {
|
|
772
781
|
if (canApplyHighlight) {
|
|
773
782
|
highlightTarget.scrollIntoView({ block: "center", inline: "center" });
|
|
774
783
|
await this.waitForScrollEnd();
|
|
775
|
-
|
|
776
|
-
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
784
|
+
highlightTarget.setAttribute("data-ga-highlight", HIGHLIGHT_MARKER);
|
|
777
785
|
}
|
|
778
786
|
const html2canvas = (await import("html2canvas-pro")).default;
|
|
779
787
|
const canvas = await html2canvas(document.body, {
|
|
780
788
|
x: window.scrollX,
|
|
781
789
|
y: window.scrollY,
|
|
782
790
|
width: window.innerWidth,
|
|
783
|
-
height: window.innerHeight
|
|
791
|
+
height: window.innerHeight,
|
|
792
|
+
onclone: (clonedDoc) => {
|
|
793
|
+
if (!canApplyHighlight) return;
|
|
794
|
+
const clonedTarget = clonedDoc.querySelector(`[data-ga-highlight="${HIGHLIGHT_MARKER}"]`);
|
|
795
|
+
if (clonedTarget instanceof HTMLElement) {
|
|
796
|
+
clonedTarget.style.border = "2px solid red";
|
|
797
|
+
clonedTarget.style.boxSizing = "border-box";
|
|
798
|
+
}
|
|
799
|
+
}
|
|
784
800
|
});
|
|
785
801
|
return canvas.toDataURL("image/jpeg", 0.4);
|
|
786
802
|
} catch (e) {
|
|
787
803
|
console.error("[ga-logger] screenshot capture failed:", e);
|
|
788
804
|
return null;
|
|
789
805
|
} finally {
|
|
790
|
-
|
|
806
|
+
highlightTarget?.removeAttribute("data-ga-highlight");
|
|
791
807
|
}
|
|
792
808
|
}
|
|
793
809
|
async send(level, message, payload = {}, error, endpoint, excludeBreadcrumbTypes, breadcrumbWindowMs, captureScreenshot, highlightTarget) {
|
|
@@ -795,7 +811,8 @@ var Logger = class {
|
|
|
795
811
|
console.warn("[ga-logger] not initialized. Call logger.init() first.");
|
|
796
812
|
return;
|
|
797
813
|
}
|
|
798
|
-
|
|
814
|
+
const isLocalhost = typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1");
|
|
815
|
+
if (isLocalhost) {
|
|
799
816
|
console.error(`[ga-localhost][DEV] ${message}`, { payload, error });
|
|
800
817
|
return;
|
|
801
818
|
}
|
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/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, LogEntry, LogLevel, LogParams, LoggerConfig } from \"../types\";\r\nimport { initEnvironment } from \"./environment\";\r\nimport { getEnvironmentPayload } from \"./environment\";\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 Firestore\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 while (el) {\r\n const text = sanitizeClickText(el.innerText?.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 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\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 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) {\r\n const text = (node as HTMLElement).innerText?.trim();\r\n if (text && text.length < 200) markActivity(text);\r\n }\r\n });\r\n } else if (m.type === \"characterData\") {\r\n const parent = m.target.parentElement;\r\n const text = parent?.innerText?.trim();\r\n if (text && text.length < 200) 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 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\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 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 // TEMP DEBUG — hapus setelah root cause ketemu\r\n console.log(\"[dead-click-debug]\", {\r\n message,\r\n clickTime,\r\n domActivityAtClick,\r\n lastDomActivityNow: this.lastDomActivity,\r\n actualElapsedMs: Date.now() - clickTime,\r\n willFire: this.lastDomActivity <= domActivityAtClick,\r\n });\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 = disabledAtClick\r\n ? \"button is disabled\"\r\n : recentFetch\r\n ? `recent request: ${recentFetch.message}`\r\n : invalidFieldAtClick\r\n ? \"validation error detected nearby\"\r\n : `no DOM/scroll activity within ${DEAD_CLICK_DELAY_MS / 1000}s`;\r\n\r\n this.addBreadcrumb(\"click\", `Dead click: ${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 console.log(\"[dead-click-debug] rate-limited, skip warn()\", { message });\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(`Dead click: ${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 },\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 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 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 = disabledAtClick\r\n ? \"button is disabled\"\r\n : recentFetch\r\n ? `recent request: ${recentFetch.message}`\r\n : invalidFieldAtClick\r\n ? \"validation error detected nearby\"\r\n : this.recentDomChanges.length\r\n ? this.recentDomChanges.slice(-1)[0]\r\n : \"no visible UI feedback detected\";\r\n\r\n this.addBreadcrumb(\"click\", `Loop click: ${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(`Loop click: ${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 },\r\n excludeBreadcrumbTypes: [\"fetch\"],\r\n breadcrumbWindowMs: 10000,\r\n captureScreenshot: true,\r\n highlightTarget: target,\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 let overlay: HTMLDivElement | null = null;\r\n\r\n try {\r\n if (canApplyHighlight) {\r\n highlightTarget!.scrollIntoView({ block: \"center\", inline: \"center\" });\r\n await this.waitForScrollEnd();\r\n\r\n overlay = this.createHighlightOverlay(highlightTarget!);\r\n await new Promise((resolve) => requestAnimationFrame(resolve));\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 });\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 overlay?.remove();\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 if (this.config.env === \"localhost\") {\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();\r\n\r\n\r\n","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 { 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":["import_firestore","app","db"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/logger.ts","../src/config/firebase.ts","../src/core/environment.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, LogEntry, LogLevel, LogParams, LoggerConfig } from \"../types\";\r\nimport { initEnvironment } from \"./environment\";\r\nimport { getEnvironmentPayload } from \"./environment\";\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 Firestore\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 while (el) {\r\n const text = sanitizeClickText(el.innerText?.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 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\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 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) {\r\n const text = (node as HTMLElement).innerText?.trim();\r\n if (text && text.length < 200) markActivity(text);\r\n }\r\n });\r\n } else if (m.type === \"characterData\") {\r\n const parent = m.target.parentElement;\r\n const text = parent?.innerText?.trim();\r\n if (text && text.length < 200) 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 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\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 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 // TEMP DEBUG — hapus setelah root cause ketemu\r\n console.log(\"[dead-click-debug]\", {\r\n message,\r\n clickTime,\r\n domActivityAtClick,\r\n lastDomActivityNow: this.lastDomActivity,\r\n actualElapsedMs: Date.now() - clickTime,\r\n willFire: this.lastDomActivity <= domActivityAtClick,\r\n });\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 = disabledAtClick\r\n ? \"button is disabled\"\r\n : recentFetch\r\n ? `recent request: ${recentFetch.message}`\r\n : invalidFieldAtClick\r\n ? \"validation error detected nearby\"\r\n : `no DOM/scroll activity within ${DEAD_CLICK_DELAY_MS / 1000}s`;\r\n\r\n this.addBreadcrumb(\"click\", `Dead click: ${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 console.log(\"[dead-click-debug] rate-limited, skip warn()\", { message });\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(`Dead click: ${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 },\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 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 (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 = disabledAtClick\r\n ? \"button is disabled\"\r\n : recentFetch\r\n ? `recent request: ${recentFetch.message}`\r\n : invalidFieldAtClick\r\n ? \"validation error detected nearby\"\r\n : this.recentDomChanges.length\r\n ? this.recentDomChanges.slice(-1)[0]\r\n : \"no visible UI feedback detected\";\r\n\r\n this.addBreadcrumb(\"click\", `Loop click: ${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(`Loop click: ${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 },\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();\r\n\r\n\r\n","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 { 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":["import_firestore","app","historyKey","timestamps","lastFired","db"]}
|
package/dist/index.mjs
CHANGED
|
@@ -622,35 +622,44 @@ var Logger = class {
|
|
|
622
622
|
timestamps.push(clickTime);
|
|
623
623
|
this.clickHistory.set(historyKey, timestamps);
|
|
624
624
|
const lastFired = this.rageClickFired.get(historyKey) ?? 0;
|
|
625
|
-
if (
|
|
626
|
-
|
|
627
|
-
const
|
|
628
|
-
(
|
|
629
|
-
)
|
|
630
|
-
|
|
631
|
-
this.
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
payload: {
|
|
625
|
+
if (isInteractive) {
|
|
626
|
+
const historyKey2 = `${tagName}::${message}`;
|
|
627
|
+
const timestamps2 = (this.clickHistory.get(historyKey2) ?? []).filter(
|
|
628
|
+
(t) => clickTime - t < RAGE_CLICK_WINDOW_MS
|
|
629
|
+
);
|
|
630
|
+
timestamps2.push(clickTime);
|
|
631
|
+
this.clickHistory.set(historyKey2, timestamps2);
|
|
632
|
+
const lastFired2 = this.rageClickFired.get(historyKey2) ?? 0;
|
|
633
|
+
if (timestamps2.length >= 3 && clickTime - lastFired2 > RAGE_CLICK_COOLDOWN_MS) {
|
|
634
|
+
this.rageClickFired.set(historyKey2, clickTime);
|
|
635
|
+
const recentFetch = this.breadcrumbs.filter(
|
|
636
|
+
(b) => b.type === "fetch" && clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS
|
|
637
|
+
).slice(-1)[0];
|
|
638
|
+
const possibleReason = disabledAtClick ? "button is disabled" : recentFetch ? `recent request: ${recentFetch.message}` : invalidFieldAtClick ? "validation error detected nearby" : this.recentDomChanges.length ? this.recentDomChanges.slice(-1)[0] : "no visible UI feedback detected";
|
|
639
|
+
this.addBreadcrumb("click", `Loop click: ${message}`, {
|
|
641
640
|
tagName,
|
|
642
|
-
clickCount:
|
|
641
|
+
clickCount: timestamps2.length,
|
|
643
642
|
windowMs: RAGE_CLICK_WINDOW_MS,
|
|
644
|
-
possibleReason
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
643
|
+
possibleReason
|
|
644
|
+
});
|
|
645
|
+
void this.warn({
|
|
646
|
+
fnName: "interceptClick.rageClick",
|
|
647
|
+
messageError: new Error(`Loop click: ${message}`),
|
|
648
|
+
payload: {
|
|
649
|
+
tagName,
|
|
650
|
+
clickCount: timestamps2.length,
|
|
651
|
+
windowMs: RAGE_CLICK_WINDOW_MS,
|
|
652
|
+
possibleReason,
|
|
653
|
+
text: message,
|
|
654
|
+
selector,
|
|
655
|
+
...href ? { href } : {}
|
|
656
|
+
},
|
|
657
|
+
excludeBreadcrumbTypes: ["fetch"],
|
|
658
|
+
breadcrumbWindowMs: 1e4,
|
|
659
|
+
captureScreenshot: true,
|
|
660
|
+
highlightTarget: target
|
|
661
|
+
});
|
|
662
|
+
}
|
|
654
663
|
}
|
|
655
664
|
},
|
|
656
665
|
true
|
|
@@ -713,44 +722,51 @@ var Logger = class {
|
|
|
713
722
|
idleTimer = setTimeout(cleanup, idleMs);
|
|
714
723
|
});
|
|
715
724
|
}
|
|
716
|
-
createHighlightOverlay(target) {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
}
|
|
725
|
+
// private createHighlightOverlay(target: HTMLElement): HTMLDivElement {
|
|
726
|
+
// const rect = target.getBoundingClientRect();
|
|
727
|
+
// const padding = 0;
|
|
728
|
+
// const overlay = document.createElement("div");
|
|
729
|
+
// overlay.style.position = "absolute";
|
|
730
|
+
// overlay.style.top = `${rect.top + window.scrollY - padding}px`;
|
|
731
|
+
// overlay.style.left = `${rect.left + window.scrollX - padding}px`;
|
|
732
|
+
// overlay.style.width = `${rect.width + padding * 2}px`;
|
|
733
|
+
// overlay.style.height = `${rect.height + padding * 2}px`;
|
|
734
|
+
// overlay.style.border = "2px solid red";
|
|
735
|
+
// overlay.style.pointerEvents = "none";
|
|
736
|
+
// overlay.style.zIndex = "999999";
|
|
737
|
+
// document.body.appendChild(overlay);
|
|
738
|
+
// return overlay;
|
|
739
|
+
// }
|
|
731
740
|
async captureAndUploadScreenshot(highlightTarget) {
|
|
732
741
|
const canApplyHighlight = !!highlightTarget && document.contains(highlightTarget);
|
|
733
|
-
|
|
742
|
+
const HIGHLIGHT_MARKER = "ga-logger-highlight-target";
|
|
734
743
|
try {
|
|
735
744
|
if (canApplyHighlight) {
|
|
736
745
|
highlightTarget.scrollIntoView({ block: "center", inline: "center" });
|
|
737
746
|
await this.waitForScrollEnd();
|
|
738
|
-
|
|
739
|
-
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
747
|
+
highlightTarget.setAttribute("data-ga-highlight", HIGHLIGHT_MARKER);
|
|
740
748
|
}
|
|
741
749
|
const html2canvas = (await import("html2canvas-pro")).default;
|
|
742
750
|
const canvas = await html2canvas(document.body, {
|
|
743
751
|
x: window.scrollX,
|
|
744
752
|
y: window.scrollY,
|
|
745
753
|
width: window.innerWidth,
|
|
746
|
-
height: window.innerHeight
|
|
754
|
+
height: window.innerHeight,
|
|
755
|
+
onclone: (clonedDoc) => {
|
|
756
|
+
if (!canApplyHighlight) return;
|
|
757
|
+
const clonedTarget = clonedDoc.querySelector(`[data-ga-highlight="${HIGHLIGHT_MARKER}"]`);
|
|
758
|
+
if (clonedTarget instanceof HTMLElement) {
|
|
759
|
+
clonedTarget.style.border = "2px solid red";
|
|
760
|
+
clonedTarget.style.boxSizing = "border-box";
|
|
761
|
+
}
|
|
762
|
+
}
|
|
747
763
|
});
|
|
748
764
|
return canvas.toDataURL("image/jpeg", 0.4);
|
|
749
765
|
} catch (e) {
|
|
750
766
|
console.error("[ga-logger] screenshot capture failed:", e);
|
|
751
767
|
return null;
|
|
752
768
|
} finally {
|
|
753
|
-
|
|
769
|
+
highlightTarget?.removeAttribute("data-ga-highlight");
|
|
754
770
|
}
|
|
755
771
|
}
|
|
756
772
|
async send(level, message, payload = {}, error, endpoint, excludeBreadcrumbTypes, breadcrumbWindowMs, captureScreenshot, highlightTarget) {
|
|
@@ -758,7 +774,8 @@ var Logger = class {
|
|
|
758
774
|
console.warn("[ga-logger] not initialized. Call logger.init() first.");
|
|
759
775
|
return;
|
|
760
776
|
}
|
|
761
|
-
|
|
777
|
+
const isLocalhost = typeof window !== "undefined" && (window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1");
|
|
778
|
+
if (isLocalhost) {
|
|
762
779
|
console.error(`[ga-localhost][DEV] ${message}`, { payload, error });
|
|
763
780
|
return;
|
|
764
781
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/logger.ts","../src/config/firebase.ts","../src/core/environment.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, LogEntry, LogLevel, LogParams, LoggerConfig } from \"../types\";\r\nimport { initEnvironment } from \"./environment\";\r\nimport { getEnvironmentPayload } from \"./environment\";\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 Firestore\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 while (el) {\r\n const text = sanitizeClickText(el.innerText?.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 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\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 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) {\r\n const text = (node as HTMLElement).innerText?.trim();\r\n if (text && text.length < 200) markActivity(text);\r\n }\r\n });\r\n } else if (m.type === \"characterData\") {\r\n const parent = m.target.parentElement;\r\n const text = parent?.innerText?.trim();\r\n if (text && text.length < 200) 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 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\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 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 // TEMP DEBUG — hapus setelah root cause ketemu\r\n console.log(\"[dead-click-debug]\", {\r\n message,\r\n clickTime,\r\n domActivityAtClick,\r\n lastDomActivityNow: this.lastDomActivity,\r\n actualElapsedMs: Date.now() - clickTime,\r\n willFire: this.lastDomActivity <= domActivityAtClick,\r\n });\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 = disabledAtClick\r\n ? \"button is disabled\"\r\n : recentFetch\r\n ? `recent request: ${recentFetch.message}`\r\n : invalidFieldAtClick\r\n ? \"validation error detected nearby\"\r\n : `no DOM/scroll activity within ${DEAD_CLICK_DELAY_MS / 1000}s`;\r\n\r\n this.addBreadcrumb(\"click\", `Dead click: ${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 console.log(\"[dead-click-debug] rate-limited, skip warn()\", { message });\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(`Dead click: ${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 },\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 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 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 = disabledAtClick\r\n ? \"button is disabled\"\r\n : recentFetch\r\n ? `recent request: ${recentFetch.message}`\r\n : invalidFieldAtClick\r\n ? \"validation error detected nearby\"\r\n : this.recentDomChanges.length\r\n ? this.recentDomChanges.slice(-1)[0]\r\n : \"no visible UI feedback detected\";\r\n\r\n this.addBreadcrumb(\"click\", `Loop click: ${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(`Loop click: ${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 },\r\n excludeBreadcrumbTypes: [\"fetch\"],\r\n breadcrumbWindowMs: 10000,\r\n captureScreenshot: true,\r\n highlightTarget: target,\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 let overlay: HTMLDivElement | null = null;\r\n\r\n try {\r\n if (canApplyHighlight) {\r\n highlightTarget!.scrollIntoView({ block: \"center\", inline: \"center\" });\r\n await this.waitForScrollEnd();\r\n\r\n overlay = this.createHighlightOverlay(highlightTarget!);\r\n await new Promise((resolve) => requestAnimationFrame(resolve));\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 });\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 overlay?.remove();\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 if (this.config.env === \"localhost\") {\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();\r\n\r\n\r\n","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 { 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/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, LogEntry, LogLevel, LogParams, LoggerConfig } from \"../types\";\r\nimport { initEnvironment } from \"./environment\";\r\nimport { getEnvironmentPayload } from \"./environment\";\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 Firestore\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 while (el) {\r\n const text = sanitizeClickText(el.innerText?.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 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\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 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) {\r\n const text = (node as HTMLElement).innerText?.trim();\r\n if (text && text.length < 200) markActivity(text);\r\n }\r\n });\r\n } else if (m.type === \"characterData\") {\r\n const parent = m.target.parentElement;\r\n const text = parent?.innerText?.trim();\r\n if (text && text.length < 200) 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 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\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 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 // TEMP DEBUG — hapus setelah root cause ketemu\r\n console.log(\"[dead-click-debug]\", {\r\n message,\r\n clickTime,\r\n domActivityAtClick,\r\n lastDomActivityNow: this.lastDomActivity,\r\n actualElapsedMs: Date.now() - clickTime,\r\n willFire: this.lastDomActivity <= domActivityAtClick,\r\n });\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 = disabledAtClick\r\n ? \"button is disabled\"\r\n : recentFetch\r\n ? `recent request: ${recentFetch.message}`\r\n : invalidFieldAtClick\r\n ? \"validation error detected nearby\"\r\n : `no DOM/scroll activity within ${DEAD_CLICK_DELAY_MS / 1000}s`;\r\n\r\n this.addBreadcrumb(\"click\", `Dead click: ${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 console.log(\"[dead-click-debug] rate-limited, skip warn()\", { message });\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(`Dead click: ${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 },\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 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 (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 = disabledAtClick\r\n ? \"button is disabled\"\r\n : recentFetch\r\n ? `recent request: ${recentFetch.message}`\r\n : invalidFieldAtClick\r\n ? \"validation error detected nearby\"\r\n : this.recentDomChanges.length\r\n ? this.recentDomChanges.slice(-1)[0]\r\n : \"no visible UI feedback detected\";\r\n\r\n this.addBreadcrumb(\"click\", `Loop click: ${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(`Loop click: ${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 },\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();\r\n\r\n\r\n","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 { 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","historyKey","timestamps","lastFired","db"]}
|