@ga-components/logger 1.0.64 → 1.0.66
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 +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +62 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +62 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -46,6 +46,7 @@ type LogParams = {
|
|
|
46
46
|
payload?: Record<string, unknown>;
|
|
47
47
|
endpointName?: string;
|
|
48
48
|
excludeBreadcrumbTypes?: BreadcrumbType[];
|
|
49
|
+
breadcrumbWindowMs?: number;
|
|
49
50
|
};
|
|
50
51
|
|
|
51
52
|
declare class Logger {
|
|
@@ -57,6 +58,8 @@ declare class Logger {
|
|
|
57
58
|
private rageClickFired;
|
|
58
59
|
private domObserverInitialized;
|
|
59
60
|
private recentDomChanges;
|
|
61
|
+
private isElementDisabled;
|
|
62
|
+
private hasInvalidFieldNearby;
|
|
60
63
|
private initDomActivityObserver;
|
|
61
64
|
init(config: LoggerConfig): void;
|
|
62
65
|
setUserId(userId: string | null): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ type LogParams = {
|
|
|
46
46
|
payload?: Record<string, unknown>;
|
|
47
47
|
endpointName?: string;
|
|
48
48
|
excludeBreadcrumbTypes?: BreadcrumbType[];
|
|
49
|
+
breadcrumbWindowMs?: number;
|
|
49
50
|
};
|
|
50
51
|
|
|
51
52
|
declare class Logger {
|
|
@@ -57,6 +58,8 @@ declare class Logger {
|
|
|
57
58
|
private rageClickFired;
|
|
58
59
|
private domObserverInitialized;
|
|
59
60
|
private recentDomChanges;
|
|
61
|
+
private isElementDisabled;
|
|
62
|
+
private hasInvalidFieldNearby;
|
|
60
63
|
private initDomActivityObserver;
|
|
61
64
|
init(config: LoggerConfig): void;
|
|
62
65
|
setUserId(userId: string | null): void;
|
package/dist/index.js
CHANGED
|
@@ -97,6 +97,15 @@ var Logger = class {
|
|
|
97
97
|
// Global error capture — hanya aktif di non-development
|
|
98
98
|
this.loggedErrorKeys = /* @__PURE__ */ new Set();
|
|
99
99
|
}
|
|
100
|
+
isElementDisabled(target) {
|
|
101
|
+
return target.disabled === true || target.getAttribute("aria-disabled") === "true";
|
|
102
|
+
}
|
|
103
|
+
// Helper: cek apakah ada field invalid di form terdekat dari target
|
|
104
|
+
hasInvalidFieldNearby(target) {
|
|
105
|
+
const form = target.closest("form");
|
|
106
|
+
if (!form) return false;
|
|
107
|
+
return form.querySelector('[aria-invalid="true"]') !== null;
|
|
108
|
+
}
|
|
100
109
|
initDomActivityObserver() {
|
|
101
110
|
if (this.domObserverInitialized) return;
|
|
102
111
|
this.domObserverInitialized = true;
|
|
@@ -112,15 +121,21 @@ var Logger = class {
|
|
|
112
121
|
const observer = new MutationObserver((mutations) => {
|
|
113
122
|
markActivity();
|
|
114
123
|
for (const m of mutations) {
|
|
115
|
-
m.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
if (m.type === "childList") {
|
|
125
|
+
m.addedNodes.forEach((node) => {
|
|
126
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
127
|
+
const text = node.innerText?.trim();
|
|
128
|
+
if (text && text.length < 200) markActivity(text);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
} else if (m.type === "characterData") {
|
|
132
|
+
const parent = m.target.parentElement;
|
|
133
|
+
const text = parent?.innerText?.trim();
|
|
134
|
+
if (text && text.length < 200) markActivity(text);
|
|
135
|
+
}
|
|
121
136
|
}
|
|
122
137
|
});
|
|
123
|
-
observer.observe(document.body, { childList: true, subtree: true, attributes: true });
|
|
138
|
+
observer.observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true });
|
|
124
139
|
window.addEventListener("scroll", () => markActivity(), { passive: true, capture: true });
|
|
125
140
|
}
|
|
126
141
|
init(config) {
|
|
@@ -543,12 +558,20 @@ var Logger = class {
|
|
|
543
558
|
...id ? { id } : {},
|
|
544
559
|
...innerText ? { text: innerText } : {}
|
|
545
560
|
});
|
|
561
|
+
const disabledAtClick = this.isElementDisabled(target);
|
|
562
|
+
const invalidFieldAtClick = this.hasInvalidFieldNearby(target);
|
|
546
563
|
const clickTime = Date.now();
|
|
547
564
|
const domActivityAtClick = this.lastDomActivity;
|
|
565
|
+
const DEAD_CLICK_DELAY_MS = 4e3;
|
|
566
|
+
const RAGE_CLICK_WINDOW_MS = 2e3;
|
|
567
|
+
const RAGE_CLICK_COOLDOWN_MS = 5e3;
|
|
548
568
|
if (["button", "a", "input"].includes(tagName)) {
|
|
549
569
|
setTimeout(() => {
|
|
550
570
|
if (this.lastDomActivity <= domActivityAtClick) {
|
|
551
|
-
const
|
|
571
|
+
const recentFetch = this.breadcrumbs.filter(
|
|
572
|
+
(b) => b.type === "fetch" && new Date(b.timestamp).getTime() >= clickTime && new Date(b.timestamp).getTime() <= clickTime + DEAD_CLICK_DELAY_MS
|
|
573
|
+
).slice(-1)[0];
|
|
574
|
+
const reason = disabledAtClick ? "button is disabled" : recentFetch ? `recent request: ${recentFetch.message}` : invalidFieldAtClick ? "validation error detected nearby" : `no DOM/scroll activity within ${DEAD_CLICK_DELAY_MS / 1e3}s`;
|
|
552
575
|
this.addBreadcrumb("click", `Dead click: ${message}`, {
|
|
553
576
|
tagName,
|
|
554
577
|
...id ? { id } : {},
|
|
@@ -558,30 +581,42 @@ var Logger = class {
|
|
|
558
581
|
fnName: "interceptClick.deadClick",
|
|
559
582
|
messageError: new Error(`Dead click: ${message}`),
|
|
560
583
|
payload: { tagName, ...id ? { id } : {}, reason },
|
|
561
|
-
excludeBreadcrumbTypes: ["fetch"]
|
|
584
|
+
excludeBreadcrumbTypes: ["fetch"],
|
|
585
|
+
breadcrumbWindowMs: 1e4
|
|
562
586
|
});
|
|
563
587
|
}
|
|
564
|
-
},
|
|
588
|
+
}, DEAD_CLICK_DELAY_MS);
|
|
565
589
|
}
|
|
566
590
|
const historyKey = `${tagName}::${message}`;
|
|
567
|
-
const timestamps = (this.clickHistory.get(historyKey) ?? []).filter(
|
|
591
|
+
const timestamps = (this.clickHistory.get(historyKey) ?? []).filter(
|
|
592
|
+
(t) => clickTime - t < RAGE_CLICK_WINDOW_MS
|
|
593
|
+
);
|
|
568
594
|
timestamps.push(clickTime);
|
|
569
595
|
this.clickHistory.set(historyKey, timestamps);
|
|
570
596
|
const lastFired = this.rageClickFired.get(historyKey) ?? 0;
|
|
571
|
-
if (timestamps.length >= 3 && clickTime - lastFired >
|
|
597
|
+
if (timestamps.length >= 3 && clickTime - lastFired > RAGE_CLICK_COOLDOWN_MS) {
|
|
572
598
|
this.rageClickFired.set(historyKey, clickTime);
|
|
573
|
-
const
|
|
599
|
+
const recentFetch = this.breadcrumbs.filter(
|
|
600
|
+
(b) => b.type === "fetch" && clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS
|
|
601
|
+
).slice(-1)[0];
|
|
602
|
+
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";
|
|
574
603
|
this.addBreadcrumb("click", `Loop click: ${message}`, {
|
|
575
604
|
tagName,
|
|
576
605
|
clickCount: timestamps.length,
|
|
577
|
-
windowMs:
|
|
606
|
+
windowMs: RAGE_CLICK_WINDOW_MS,
|
|
578
607
|
possibleReason
|
|
579
608
|
});
|
|
580
609
|
void this.warn({
|
|
581
610
|
fnName: "interceptClick.rageClick",
|
|
582
611
|
messageError: new Error(`Loop click: ${message}`),
|
|
583
|
-
payload: {
|
|
584
|
-
|
|
612
|
+
payload: {
|
|
613
|
+
tagName,
|
|
614
|
+
clickCount: timestamps.length,
|
|
615
|
+
windowMs: RAGE_CLICK_WINDOW_MS,
|
|
616
|
+
possibleReason
|
|
617
|
+
},
|
|
618
|
+
excludeBreadcrumbTypes: ["fetch"],
|
|
619
|
+
breadcrumbWindowMs: 1e4
|
|
585
620
|
});
|
|
586
621
|
}
|
|
587
622
|
},
|
|
@@ -624,7 +659,7 @@ var Logger = class {
|
|
|
624
659
|
}
|
|
625
660
|
});
|
|
626
661
|
}
|
|
627
|
-
async send(level, message, payload = {}, error, endpoint, excludeBreadcrumbTypes) {
|
|
662
|
+
async send(level, message, payload = {}, error, endpoint, excludeBreadcrumbTypes, breadcrumbWindowMs) {
|
|
628
663
|
if (!this.initialized || !this.config) {
|
|
629
664
|
console.warn("[ga-logger] not initialized. Call logger.init() first.");
|
|
630
665
|
return;
|
|
@@ -633,7 +668,13 @@ var Logger = class {
|
|
|
633
668
|
console.error(`[ga-localhost][DEV] ${message}`, { payload, error });
|
|
634
669
|
return;
|
|
635
670
|
}
|
|
636
|
-
const
|
|
671
|
+
const now = Date.now();
|
|
672
|
+
let filteredBreadcrumbs = excludeBreadcrumbTypes?.length ? this.breadcrumbs.filter((b) => !excludeBreadcrumbTypes.includes(b.type)) : this.breadcrumbs;
|
|
673
|
+
if (breadcrumbWindowMs) {
|
|
674
|
+
filteredBreadcrumbs = filteredBreadcrumbs.filter(
|
|
675
|
+
(b) => now - new Date(b.timestamp).getTime() <= breadcrumbWindowMs
|
|
676
|
+
);
|
|
677
|
+
}
|
|
637
678
|
const entry = {
|
|
638
679
|
timestamp: /* @__PURE__ */ new Date(),
|
|
639
680
|
level,
|
|
@@ -665,14 +706,15 @@ var Logger = class {
|
|
|
665
706
|
return this.send("info", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);
|
|
666
707
|
}
|
|
667
708
|
warn(params) {
|
|
668
|
-
const { fnName, messageError, payload, endpointName, excludeBreadcrumbTypes } = params;
|
|
709
|
+
const { fnName, messageError, payload, endpointName, excludeBreadcrumbTypes, breadcrumbWindowMs } = params;
|
|
669
710
|
return this.send(
|
|
670
711
|
"warn",
|
|
671
712
|
messageError?.message ?? fnName,
|
|
672
713
|
{ ...payload, fnName },
|
|
673
714
|
messageError,
|
|
674
715
|
endpointName,
|
|
675
|
-
excludeBreadcrumbTypes
|
|
716
|
+
excludeBreadcrumbTypes,
|
|
717
|
+
breadcrumbWindowMs
|
|
676
718
|
);
|
|
677
719
|
}
|
|
678
720
|
error(params) {
|
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 { collection, addDoc, Timestamp } from \"firebase/firestore\";\nimport { initFirebase, getDb } from \"../config/firebase\";\nimport type { Breadcrumb, BreadcrumbType, LogEntry, LogLevel, LogParams, LoggerConfig } from \"../types\";\nimport { initEnvironment } from \"./environment\";\nimport { getEnvironmentPayload } from \"./environment\";\n\nconst sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\nconst MAX_BREADCRUMBS = 50;\n\nconst CLICK_SKIP_PATTERNS = [/^:r[a-z0-9]+:$/, /^\\[object/, /^#/];\n\n// Sanitize PII sebelum masuk Firestore\nconst sanitizeClickText = (text: string): string => {\n return text\n .replace(/\\b[\\w.-]+@[\\w.-]+\\.\\w+\\b/g, \"[email]\")\n .replace(/\\b\\d{10,}\\b/g, \"[number]\")\n .replace(/Rp[\\s]?\\d[\\d.,]*/gi, \"[amount]\")\n .trim();\n};\n\nclass Logger {\n private config: LoggerConfig | null = null;\n private initialized = false;\n private breadcrumbs: Breadcrumb[] = [];\n\n private lastDomActivity = Date.now();\n private clickHistory = new Map<string, number[]>();\n private rageClickFired = new Map<string, number>();\n private domObserverInitialized = false;\n\n \n private recentDomChanges: string[] = [];\n\n private initDomActivityObserver(): void {\n if (this.domObserverInitialized) return;\n this.domObserverInitialized = true;\n\n const markActivity = (addedText?: string) => {\n this.lastDomActivity = Date.now();\n if (addedText) {\n this.recentDomChanges.push(addedText);\n // keep only last few seconds worth\n setTimeout(() => {\n this.recentDomChanges = this.recentDomChanges.filter((t) => t !== addedText);\n }, 1000); // ⚠️ perlu konfirmasi durasi\n }\n };\n\n const observer = new MutationObserver((mutations) => {\n markActivity();\n for (const m of mutations) {\n m.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const text = (node as HTMLElement).innerText?.trim();\n if (text && text.length < 200) markActivity(text);\n }\n });\n }\n });\n\n observer.observe(document.body, { childList: true, subtree: true, attributes: true });\n window.addEventListener(\"scroll\", () => markActivity(), { passive: true, capture: true });\n }\n\n init(config: LoggerConfig) {\n this.config = config;\n initFirebase(config);\n initEnvironment();\n this.initialized = true;\n console.info(\"[ga-logger] initialized —\", config.env);\n }\n setUserId(userId: string | null) {\n if (!this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n this.config.userId = userId;\n }\n\n initAnonymousUser(): void {\n if (!this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n\n const STORAGE_KEY = \"ga_anon_id\";\n\n try {\n let anonId = localStorage.getItem(STORAGE_KEY);\n\n if (!anonId) {\n anonId =\n typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\n ? crypto.randomUUID()\n : \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n\n localStorage.setItem(STORAGE_KEY, anonId);\n }\n\n const shortId = anonId.slice(-8);\n this.setUserId(`Anonymous #${shortId}`);\n } catch (e) {\n // localStorage blocked (private mode / HTTP restriction)\n console.warn(\"[ga-logger] initAnonymousUser failed:\", e);\n this.setUserId(\"Anonymous\");\n }\n }\n\n // Add breadcrumb ke in-memory array\n addBreadcrumb(type: BreadcrumbType, message: string, data?: Record<string, unknown>) {\n const breadcrumb: Breadcrumb = {\n timestamp: new Date(),\n type,\n message,\n data,\n };\n\n this.breadcrumbs.push(breadcrumb);\n\n // Jaga memory — max 50 breadcrumbs, buang yang paling lama\n if (this.breadcrumbs.length > MAX_BREADCRUMBS) {\n this.breadcrumbs.shift();\n }\n }\n\n // Global error capture — hanya aktif di non-development\n private loggedErrorKeys = new Set<string>();\n\n // Helper: trim URL jadi domain/filename\n private trimUrl(url: string): string {\n try {\n const u = new URL(url);\n const filename = u.pathname.split(\"/\").filter(Boolean).pop() ?? u.pathname;\n return `${u.hostname}/${filename}`;\n } catch {\n // bukan valid URL, potong saja jika terlalu panjang\n return url.length > 80 ? url.slice(0, 80) + \"...\" : url;\n }\n }\n\n // Helper: build dedup key\n private buildErrorKey(type: string, message: string): string {\n return `${type}::${message}`;\n }\n\n // Helper: should skip resource error\n private shouldSkipResourceError(src: string, tagName: string): boolean {\n // Skip IMG — tidak sebabkan blank page\n if (tagName === \"IMG\") return true;\n\n // Skip base64 URL\n if (src.startsWith(\"data:\")) return true;\n\n // Skip known 3rd party trackers\n const THIRD_PARTY = [\n \"omguk.com\",\n \"track.\",\n \"doubleclick\",\n \"adservice\",\n \"googletagmanager\",\n \"google-analytics\",\n \"hotjar\",\n \"facebook.net\",\n \"analytics\",\n \"magnolia\",\n \"gtm.js\",\n ];\n if (THIRD_PARTY.some((p) => src.includes(p))) return true;\n\n return false;\n }\n\n private shouldSkipGlobalError(message: string): boolean {\n const NOISE = [\n \"CORS\",\n \"cross-origin\",\n \"ERR_FAILED\",\n \"Failed to load resource\",\n \"NetworkError\",\n \"ResizeObserver loop\",\n \"Script error\",\n \"ServiceWorker\",\n \"sw.js\",\n \"dev-sw.js\",\n \"Warning:\",\n ];\n const THIRD_PARTY = [\n \"magnolia\",\n \"gtm.js\",\n \"analytics\",\n \"googletagmanager\",\n \"facebook\",\n \"hotjar\",\n \"omguk.com\",\n \"track.\",\n \"doubleclick\",\n \"adservice\",\n ];\n return NOISE.some((p) => message.includes(p)) || THIRD_PARTY.some((p) => message.includes(p));\n }\n\n initGlobalCapture(): void {\n const isLocalhost = window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n if (isLocalhost) return;\n\n window.onerror = (message, source, lineno, colno) => {\n const msg = typeof message === \"string\" ? message : \"Unknown error\";\n if (this.shouldSkipGlobalError(msg)) return false;\n\n const trimmedSource = source ? this.trimUrl(source) : \"unknown source\";\n const finalMessage = `${msg} at ${trimmedSource}:${lineno ?? 0}`;\n const key = this.buildErrorKey(\"onerror\", finalMessage);\n if (this.loggedErrorKeys.has(key)) return false;\n this.loggedErrorKeys.add(key);\n\n void this.error({\n fnName: \"window.onerror\",\n messageError: new Error(finalMessage),\n payload: { source: trimmedSource, lineno, colno },\n });\n\n return false;\n };\n\n // unhandledrejection\n window.addEventListener(\"unhandledrejection\", (event) => {\n const msg =\n event.reason instanceof Error\n ? event.reason.message\n : String(event.reason ?? \"Unhandled Promise rejection\");\n\n if (this.shouldSkipGlobalError(msg)) return;\n\n const key = this.buildErrorKey(\"unhandledrejection\", msg);\n if (this.loggedErrorKeys.has(key)) return;\n this.loggedErrorKeys.add(key);\n\n void this.error({\n fnName: \"window.unhandledrejection\",\n messageError: new Error(msg),\n });\n });\n\n // console.error override\n const originalConsoleError = console.error.bind(console);\n console.error = (...args: unknown[]) => {\n originalConsoleError(...args);\n\n const msg = args.map((a) => (a instanceof Error ? a.message : String(a))).join(\" \");\n\n if (this.shouldSkipGlobalError(msg)) return;\n\n const trimmedMsg = msg.length > 150 ? msg.slice(0, 150) + \"...\" : msg;\n const key = this.buildErrorKey(\"console.error\", trimmedMsg);\n if (this.loggedErrorKeys.has(key)) return;\n this.loggedErrorKeys.add(key);\n\n const originalError = args.find((a): a is Error => a instanceof Error);\n\n const errorToSend = originalError ?? new Error(trimmedMsg);\n\n void this.error({\n fnName: \"console.error\",\n messageError: errorToSend,\n });\n };\n\n // resource load error (CSS / JS / font)\n window.addEventListener(\n \"error\",\n (event) => {\n const target = event.target;\n if (!(target instanceof HTMLElement)) return;\n if (target === (window as unknown)) return;\n\n const tagName = target.tagName.toUpperCase();\n\n const src =\n tagName === \"LINK\"\n ? (target as HTMLLinkElement).href\n : tagName === \"SCRIPT\"\n ? (target as HTMLScriptElement).src\n : \"\";\n\n if (this.shouldSkipResourceError(src, tagName)) return;\n\n // Deteksi dari ekstensi file, bukan tagName\n const ext = src.split(\"?\")[0].split(\".\").pop()?.toLowerCase();\n const resourceType =\n ext === \"css\"\n ? \"CSS\"\n : ext === \"js\"\n ? \"JS\"\n : ext === \"woff\" || ext === \"woff2\" || ext === \"ttf\"\n ? \"FONT\"\n : tagName;\n\n const trimmedSrc = this.trimUrl(src);\n const finalMessage = `Failed to load ${resourceType}: ${trimmedSrc}`;\n const key = this.buildErrorKey(\"resource\", finalMessage);\n if (this.loggedErrorKeys.has(key)) return;\n this.loggedErrorKeys.add(key);\n\n void this.error({\n fnName: \"resource.onerror\",\n messageError: new Error(finalMessage),\n payload: { tagName, src: trimmedSrc },\n });\n },\n true,\n );\n }\n\n private sanitizePayload(obj: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) => {\n if (value === undefined) return [key, null];\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n return [key, this.sanitizePayload(value as Record<string, unknown>)];\n }\n return [key, value];\n }),\n );\n }\n\n private sanitizeForFirestore(obj: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) => {\n if (value === undefined) return [key, null];\n if (typeof value === \"function\") return [key, \"[function]\"];\n if (value instanceof Date) return [key, value.toISOString()];\n if (Array.isArray(value)) {\n return [\n key,\n value.map((item) => {\n if (item instanceof Date) return item.toISOString();\n if (typeof item === \"function\") return \"[function]\"; // ← tambah\n if (item !== null && typeof item === \"object\") {\n return this.sanitizeForFirestore(item as Record<string, unknown>);\n }\n return item ?? null;\n }),\n ];\n }\n if (value !== null && typeof value === \"object\") {\n return [key, this.sanitizeForFirestore(value as Record<string, unknown>)];\n }\n return [key, value];\n }),\n );\n }\n\n // interceptFetch — auto breadcrumb, ke Firestore\n interceptFetch() {\n if (typeof window === \"undefined\") return;\n\n const originalFetch = window.fetch;\n\n const skipPatterns = [\n \"google.com\",\n \"googletagmanager.com\",\n \"googleapis.com\",\n \"facebook.com\",\n \"hotjar.com\",\n \"analytics\",\n \"gtm\",\n \"ccm/collect\",\n \"on.aws\",\n \"/locales/\",\n \"/sw.js\",\n \"/manifest.json\",\n \"/sourcemaps/\",\n \"magnolia\",\n ];\n\n const shouldSkipUrl = (url: string): boolean => {\n return skipPatterns.some((pattern) => url.includes(pattern));\n };\n\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\n // Skip third-party domains\n if (shouldSkipUrl(url)) {\n return originalFetch(input, init);\n }\n\n const method = init?.method ?? \"GET\";\n const startTime = Date.now();\n\n try {\n const response = await originalFetch(input, init);\n const duration = Date.now() - startTime;\n\n this.addBreadcrumb(\"fetch\", `${method} ${url}`, {\n status: response.status,\n duration: `${duration}ms`,\n });\n\n if (response.status >= 400) {\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} ${response.status}`);\n\n if (!this.loggedErrorKeys.has(dedupKey)) {\n this.loggedErrorKeys.add(dedupKey);\n\n let responseBody: unknown = null;\n try {\n const cloned = response.clone();\n const contentType = cloned.headers.get(\"content-type\") ?? \"\";\n responseBody = contentType.includes(\"application/json\")\n ? await cloned.json()\n : await cloned.text();\n } catch {\n responseBody = \"[unable to read response body]\";\n }\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n\n if (!isLocalhost) {\n this.error({\n fnName: \"interceptFetch\",\n messageError: new Error(`HTTP ${response.status} ${response.statusText} — ${url}`),\n endpointName: url,\n payload: {\n method,\n status: response.status,\n statusText: response.statusText,\n duration: `${duration}ms`,\n responseBody,\n },\n });\n }\n }\n }\n\n return response;\n } catch (networkError) {\n const duration = Date.now() - startTime;\n const error = networkError instanceof Error ? networkError : new Error(String(networkError));\n\n this.addBreadcrumb(\"fetch\", `${method} ${url} — NETWORK ERROR`, {\n duration: `${duration}ms`,\n error: error.message,\n });\n\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} NETWORK_ERROR`);\n\n if (!this.loggedErrorKeys.has(dedupKey)) {\n this.loggedErrorKeys.add(dedupKey);\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n if (!isLocalhost) {\n this.error({\n fnName: \"interceptFetch\",\n messageError: error,\n endpointName: url,\n payload: { method, duration: `${duration}ms` },\n });\n }\n }\n\n throw networkError;\n }\n };\n\n console.info(\"[ga-logger] fetch intercepted\");\n }\n\n // interceptXHR — auto breadcrumb + error capture untuk axios/XHR-based request\n interceptXHR(): void {\n if (typeof window === \"undefined\") return;\n\n const skipPatterns = [\n \"google.com\",\n \"googletagmanager.com\",\n \"googleapis.com\",\n \"facebook.com\",\n \"hotjar.com\",\n \"analytics\",\n \"ccm/collect\",\n \"on.aws\",\n \"/locales/\",\n \"/sw.js\",\n \"/manifest.json\",\n \"/sourcemaps/\",\n \"magnolia\",\n ];\n\n const shouldSkipUrl = (url: string): boolean => skipPatterns.some((p) => url.includes(p));\n\n const originalOpen = XMLHttpRequest.prototype.open;\n const originalSend = XMLHttpRequest.prototype.send;\n const self = this;\n\n type GaXHR = XMLHttpRequest & {\n _gaMethod?: string;\n _gaUrl?: string;\n _gaStartTime?: number;\n };\n\n XMLHttpRequest.prototype.open = function (this: GaXHR, method: string, url: string | URL, ...rest: unknown[]) {\n this._gaMethod = method;\n this._gaUrl = typeof url === \"string\" ? url : url.href;\n // @ts-expect-error - spreading native arguments\n return originalOpen.call(this, method, url, ...rest);\n };\n\n XMLHttpRequest.prototype.send = function (this: GaXHR, ...args: unknown[]) {\n const url = this._gaUrl ?? \"\";\n const method = this._gaMethod ?? \"GET\";\n\n if (shouldSkipUrl(url)) {\n // @ts-expect-error - spreading native arguments\n return originalSend.call(this, ...args);\n }\n\n this._gaStartTime = Date.now();\n\n this.addEventListener(\"loadend\", () => {\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\n const status = this.status;\n\n self.addBreadcrumb(\"fetch\", `${method} ${url}`, {\n status,\n duration: `${duration}ms`,\n });\n\n if (status >= 400) {\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} ${status}`);\n if (self.loggedErrorKeys.has(dedupKey)) return;\n self.loggedErrorKeys.add(dedupKey);\n\n let responseBody: unknown = this.responseText;\n try {\n responseBody = JSON.parse(this.responseText);\n } catch {\n // keep as raw text\n }\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n\n if (!isLocalhost) {\n void self.error({\n fnName: \"interceptXHR\",\n messageError: new Error(`HTTP ${status} ${this.statusText} — ${url}`),\n endpointName: url,\n payload: {\n method,\n status,\n statusText: this.statusText,\n duration: `${duration}ms`,\n responseBody,\n },\n });\n }\n }\n });\n\n this.addEventListener(\"error\", () => {\n // network-level failure (status selalu 0)\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} NETWORK_ERROR`);\n if (self.loggedErrorKeys.has(dedupKey)) return;\n self.loggedErrorKeys.add(dedupKey);\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n\n if (!isLocalhost) {\n void self.error({\n fnName: \"interceptXHR\",\n messageError: new Error(`Network error — ${method} ${url}`),\n endpointName: url,\n payload: { method, duration: `${duration}ms` },\n });\n }\n });\n\n // @ts-expect-error - spreading native arguments\n return originalSend.call(this, ...args);\n };\n\n console.info(\"[ga-logger] XHR intercepted\");\n }\n\n interceptClick(): void {\n if (typeof window === \"undefined\") return;\n\n if ((window as any).__ga_click_intercepted) return;\n (window as any).__ga_click_intercepted = true;\n this.initDomActivityObserver();\n\n document.addEventListener(\n \"click\",\n (event: MouseEvent) => {\n const raw = event.target as HTMLElement;\n if (!raw) return;\n\n const target = (raw.closest('button, a, [role=\"button\"], [data-log], input') as HTMLElement) ?? raw;\n\n const innerText = sanitizeClickText(target.innerText?.trim().slice(0, 50) ?? \"\");\n const id = target.id ?? \"\";\n const tagName = target.tagName?.toLowerCase() ?? \"\";\n\n const isInteractive =\n [\"button\", \"a\", \"input\"].includes(tagName) ||\n target.hasAttribute(\"data-log\") ||\n target.getAttribute(\"role\") === \"button\";\n\n if (!innerText && !id && !isInteractive) return;\n\n const message = innerText || id || `(no label: ${tagName || \"unknown\"})`;\n\n // Skip internal React/Router identifiers\n if (CLICK_SKIP_PATTERNS.some((p) => p.test(message))) return;\n\n this.addBreadcrumb(\"click\", message, {\n tagName,\n ...(id ? { id } : {}),\n ...(innerText ? { text: innerText } : {}),\n });\n\n // ── Dead click detection ──\n const clickTime = Date.now();\n const domActivityAtClick = this.lastDomActivity;\n\n if ([\"button\", \"a\", \"input\"].includes(tagName)) {\n setTimeout(() => {\n if (this.lastDomActivity <= domActivityAtClick) {\n const reason = \"no DOM/scroll activity within 7s\";\n\n this.addBreadcrumb(\"click\", `Dead click: ${message}`, {\n tagName,\n ...(id ? { id } : {}),\n reason,\n });\n\n void this.warn({\n fnName: \"interceptClick.deadClick\",\n messageError: new Error(`Dead click: ${message}`),\n payload: { tagName, ...(id ? { id } : {}), reason },\n excludeBreadcrumbTypes: [\"fetch\"],\n });\n }\n }, 7000);\n }\n\n // ── Rage click detection ──\n const historyKey = `${tagName}::${message}`;\n const timestamps = (this.clickHistory.get(historyKey) ?? []).filter((t) => clickTime - t < 7000);\n timestamps.push(clickTime);\n this.clickHistory.set(historyKey, timestamps);\n\n const lastFired = this.rageClickFired.get(historyKey) ?? 0;\n if (timestamps.length >= 3 && clickTime - lastFired > 7000) {\n this.rageClickFired.set(historyKey, clickTime);\n\n const possibleReason = this.recentDomChanges.length\n ? this.recentDomChanges.slice(-1)[0]\n : \"no visible UI feedback detected\";\n\n this.addBreadcrumb(\"click\", `Loop click: ${message}`, {\n tagName,\n clickCount: timestamps.length,\n windowMs: 7000,\n possibleReason,\n });\n\n void this.warn({\n fnName: \"interceptClick.rageClick\",\n messageError: new Error(`Loop click: ${message}`),\n payload: { tagName, clickCount: timestamps.length, windowMs: 7000, possibleReason },\n excludeBreadcrumbTypes: [\"fetch\"],\n });\n }\n },\n true,\n );\n }\n\n captureAction<T>(params: LogParams, fn: () => T | Promise<T>): Promise<T | undefined> {\n return Promise.resolve()\n .then(() => fn())\n .catch((error: unknown) => {\n const err = error instanceof Error ? error : new Error(String(error));\n this.send(\"error\", params.fnName, params.payload, err, params.endpointName);\n return undefined;\n });\n }\n\n withLogger<T extends (...args: any[]) => any>(\n fnName: string,\n fn: T,\n options?: {\n endpointName?: string;\n payload?: Record<string, unknown>;\n },\n ): T {\n return ((...args: Parameters<T>): ReturnType<T> | undefined => {\n try {\n const result = fn(...args);\n\n // Handle async function\n if (result instanceof Promise) {\n return result.catch((error: unknown) => {\n const err = error instanceof Error ? error : new Error(String(error));\n this.error({\n fnName,\n messageError: err,\n endpointName: options?.endpointName,\n payload: options?.payload,\n });\n return undefined;\n }) as ReturnType<T>;\n }\n\n return result;\n } catch (error: unknown) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.error({\n fnName,\n messageError: err,\n endpointName: options?.endpointName,\n payload: options?.payload,\n });\n return undefined;\n }\n }) as T;\n }\n\n private async send(\n level: LogLevel,\n message: string,\n payload: Record<string, unknown> = {},\n error?: Error,\n endpoint?: string,\n excludeBreadcrumbTypes?: BreadcrumbType[],\n ) {\n if (!this.initialized || !this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n\n if (this.config.env === \"localhost\") {\n console.error(`[ga-localhost][DEV] ${message}`, { payload, error });\n return;\n }\n\n const filteredBreadcrumbs = excludeBreadcrumbTypes?.length\n ? this.breadcrumbs.filter((b) => !excludeBreadcrumbTypes.includes(b.type))\n : this.breadcrumbs;\n\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n message,\n env: this.config.env,\n userId: this.config.userId ?? null,\n sessionId,\n page: typeof window !== \"undefined\" ? window.location.pathname : \"\",\n stackTrace: error?.stack ?? null,\n payload: this.sanitizePayload(payload),\n repo: this.config.repo,\n endpoint: endpoint ?? null,\n breadcrumbs: [...filteredBreadcrumbs],\n ...getEnvironmentPayload(),\n };\n\n try {\n const db = getDb();\n const safeEntry = this.sanitizeForFirestore(entry as unknown as Record<string, unknown>);\n await addDoc(collection(db, \"logs\"), {\n ...safeEntry,\n timestamp: Timestamp.fromDate(entry.timestamp),\n });\n } catch (e) {\n console.error(\"[ga-logger] failed to send log:\", e);\n }\n }\n\n info(params: LogParams) {\n const { fnName, messageError, payload, endpointName } = params;\n return this.send(\"info\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\n }\n\n warn(params: LogParams) {\n const { fnName, messageError, payload, endpointName, excludeBreadcrumbTypes } = params;\n return this.send(\n \"warn\",\n messageError?.message ?? fnName,\n { ...payload, fnName },\n messageError,\n endpointName,\n excludeBreadcrumbTypes,\n );\n }\n\n error(params: LogParams) {\n const { fnName, messageError, payload, endpointName } = params;\n return this.send(\"error\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\n }\n}\n\nexport const logger = new Logger();","import { initializeApp, getApps, getApp } from \"firebase/app\";\nimport { getFirestore } from \"firebase/firestore\";\nimport type { Firestore } from \"firebase/firestore\";\nimport type { LoggerConfig } from \"../types\";\n\nconst APP_NAME = \"ga-logger\";\nlet db: Firestore | null = null;\n\nexport const initFirebase = (config: LoggerConfig) => {\n // Cek apakah named instance \"ga-logger\" sudah ada\n const existingApp = getApps().find((app) => app.name === APP_NAME);\n\n const app =\n existingApp ??\n initializeApp(\n {\n apiKey: config.apiKey,\n authDomain: config.authDomain,\n projectId: config.projectId,\n storageBucket: config.storageBucket,\n messagingSenderId: config.messagingSenderId,\n appId: config.appId,\n },\n APP_NAME,\n );\n\n db = getFirestore(app);\n return db;\n};\n\nexport const getDb = (): Firestore => {\n if (!db) throw new Error(\"[ga-logger] Firebase not initialized. Call logger.init() first.\");\n return db;\n};\n","interface EnvironmentPayload {\n userAgent: string;\n deviceType: \"Mobile\" | \"Tablet\" | \"Desktop\";\n screenWidth: number;\n screenHeight: number;\n}\n\nlet cachedEnvironment: EnvironmentPayload | null = null;\n\nfunction getDeviceType(width: number): \"Mobile\" | \"Tablet\" | \"Desktop\" {\n if (width < 768) return \"Mobile\";\n if (width <= 1024) return \"Tablet\";\n return \"Desktop\";\n}\n\nexport function initEnvironment(): void {\n if (cachedEnvironment) return;\n\n const width = window.innerWidth;\n const height = window.innerHeight;\n\n cachedEnvironment = {\n userAgent: navigator.userAgent,\n deviceType: getDeviceType(width),\n screenWidth: width,\n screenHeight: height,\n };\n}\n\nexport function getEnvironmentPayload(): EnvironmentPayload {\n if (!cachedEnvironment) initEnvironment();\n return cachedEnvironment!;\n}","import { 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 { collection, addDoc, Timestamp } from \"firebase/firestore\";\nimport { initFirebase, getDb } from \"../config/firebase\";\nimport type { Breadcrumb, BreadcrumbType, LogEntry, LogLevel, LogParams, LoggerConfig } from \"../types\";\nimport { initEnvironment } from \"./environment\";\nimport { getEnvironmentPayload } from \"./environment\";\n\nconst sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\nconst MAX_BREADCRUMBS = 50;\n\nconst CLICK_SKIP_PATTERNS = [/^:r[a-z0-9]+:$/, /^\\[object/, /^#/];\n\n// Sanitize sebelum masuk Firestore\nconst sanitizeClickText = (text: string): string => {\n return text\n .replace(/\\b[\\w.-]+@[\\w.-]+\\.\\w+\\b/g, \"[email]\")\n .replace(/\\b\\d{10,}\\b/g, \"[number]\")\n .replace(/Rp[\\s]?\\d[\\d.,]*/gi, \"[amount]\")\n .trim();\n};\n\nclass Logger {\n private config: LoggerConfig | null = null;\n private initialized = false;\n private breadcrumbs: Breadcrumb[] = [];\n\n private lastDomActivity = Date.now();\n private clickHistory = new Map<string, number[]>();\n private rageClickFired = new Map<string, number>();\n private domObserverInitialized = false;\n\n private recentDomChanges: string[] = [];\n\n private isElementDisabled(target: HTMLElement): boolean {\n return (target as HTMLButtonElement).disabled === true || target.getAttribute(\"aria-disabled\") === \"true\";\n }\n\n // Helper: cek apakah ada field invalid di form terdekat dari target\n private hasInvalidFieldNearby(target: HTMLElement): boolean {\n const form = target.closest(\"form\");\n if (!form) return false;\n return form.querySelector('[aria-invalid=\"true\"]') !== null;\n }\n\n private initDomActivityObserver(): void {\n if (this.domObserverInitialized) return;\n this.domObserverInitialized = true;\n\n const markActivity = (addedText?: string) => {\n this.lastDomActivity = Date.now();\n if (addedText) {\n this.recentDomChanges.push(addedText);\n setTimeout(() => {\n this.recentDomChanges = this.recentDomChanges.filter((t) => t !== addedText);\n }, 1000);\n }\n };\n\n const observer = new MutationObserver((mutations) => {\n markActivity();\n for (const m of mutations) {\n if (m.type === \"childList\") {\n m.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const text = (node as HTMLElement).innerText?.trim();\n if (text && text.length < 200) markActivity(text);\n }\n });\n } else if (m.type === \"characterData\") {\n const parent = m.target.parentElement;\n const text = parent?.innerText?.trim();\n if (text && text.length < 200) markActivity(text);\n }\n }\n });\n\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true });\n window.addEventListener(\"scroll\", () => markActivity(), { passive: true, capture: true });\n }\n\n init(config: LoggerConfig) {\n this.config = config;\n initFirebase(config);\n initEnvironment();\n this.initialized = true;\n console.info(\"[ga-logger] initialized —\", config.env);\n }\n setUserId(userId: string | null) {\n if (!this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n this.config.userId = userId;\n }\n\n initAnonymousUser(): void {\n if (!this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n\n const STORAGE_KEY = \"ga_anon_id\";\n\n try {\n let anonId = localStorage.getItem(STORAGE_KEY);\n\n if (!anonId) {\n anonId =\n typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\n ? crypto.randomUUID()\n : \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n\n localStorage.setItem(STORAGE_KEY, anonId);\n }\n\n const shortId = anonId.slice(-8);\n this.setUserId(`Anonymous #${shortId}`);\n } catch (e) {\n console.warn(\"[ga-logger] initAnonymousUser failed:\", e);\n this.setUserId(\"Anonymous\");\n }\n }\n\n // Add breadcrumb ke in-memory array\n addBreadcrumb(type: BreadcrumbType, message: string, data?: Record<string, unknown>) {\n const breadcrumb: Breadcrumb = {\n timestamp: new Date(),\n type,\n message,\n data,\n };\n\n this.breadcrumbs.push(breadcrumb);\n\n // Jaga memory — max 50 breadcrumbs, buang yang paling lama\n if (this.breadcrumbs.length > MAX_BREADCRUMBS) {\n this.breadcrumbs.shift();\n }\n }\n\n // Global error capture — hanya aktif di non-development\n private loggedErrorKeys = new Set<string>();\n\n // Helper: trim URL jadi domain/filename\n private trimUrl(url: string): string {\n try {\n const u = new URL(url);\n const filename = u.pathname.split(\"/\").filter(Boolean).pop() ?? u.pathname;\n return `${u.hostname}/${filename}`;\n } catch {\n // bukan valid URL, potong saja jika terlalu panjang\n return url.length > 80 ? url.slice(0, 80) + \"...\" : url;\n }\n }\n\n // Helper: build dedup key\n private buildErrorKey(type: string, message: string): string {\n return `${type}::${message}`;\n }\n\n // Helper: should skip resource error\n private shouldSkipResourceError(src: string, tagName: string): boolean {\n // Skip IMG — tidak sebabkan blank page\n if (tagName === \"IMG\") return true;\n\n // Skip base64 URL\n if (src.startsWith(\"data:\")) return true;\n\n // Skip known 3rd party trackers\n const THIRD_PARTY = [\n \"omguk.com\",\n \"track.\",\n \"doubleclick\",\n \"adservice\",\n \"googletagmanager\",\n \"google-analytics\",\n \"hotjar\",\n \"facebook.net\",\n \"analytics\",\n \"magnolia\",\n \"gtm.js\",\n ];\n if (THIRD_PARTY.some((p) => src.includes(p))) return true;\n\n return false;\n }\n\n private shouldSkipGlobalError(message: string): boolean {\n const NOISE = [\n \"CORS\",\n \"cross-origin\",\n \"ERR_FAILED\",\n \"Failed to load resource\",\n \"NetworkError\",\n \"ResizeObserver loop\",\n \"Script error\",\n \"ServiceWorker\",\n \"sw.js\",\n \"dev-sw.js\",\n \"Warning:\",\n ];\n const THIRD_PARTY = [\n \"magnolia\",\n \"gtm.js\",\n \"analytics\",\n \"googletagmanager\",\n \"facebook\",\n \"hotjar\",\n \"omguk.com\",\n \"track.\",\n \"doubleclick\",\n \"adservice\",\n ];\n return NOISE.some((p) => message.includes(p)) || THIRD_PARTY.some((p) => message.includes(p));\n }\n\n initGlobalCapture(): void {\n const isLocalhost = window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n if (isLocalhost) return;\n\n window.onerror = (message, source, lineno, colno) => {\n const msg = typeof message === \"string\" ? message : \"Unknown error\";\n if (this.shouldSkipGlobalError(msg)) return false;\n\n const trimmedSource = source ? this.trimUrl(source) : \"unknown source\";\n const finalMessage = `${msg} at ${trimmedSource}:${lineno ?? 0}`;\n const key = this.buildErrorKey(\"onerror\", finalMessage);\n if (this.loggedErrorKeys.has(key)) return false;\n this.loggedErrorKeys.add(key);\n\n void this.error({\n fnName: \"window.onerror\",\n messageError: new Error(finalMessage),\n payload: { source: trimmedSource, lineno, colno },\n });\n\n return false;\n };\n\n // unhandledrejection\n window.addEventListener(\"unhandledrejection\", (event) => {\n const msg =\n event.reason instanceof Error\n ? event.reason.message\n : String(event.reason ?? \"Unhandled Promise rejection\");\n\n if (this.shouldSkipGlobalError(msg)) return;\n\n const key = this.buildErrorKey(\"unhandledrejection\", msg);\n if (this.loggedErrorKeys.has(key)) return;\n this.loggedErrorKeys.add(key);\n\n void this.error({\n fnName: \"window.unhandledrejection\",\n messageError: new Error(msg),\n });\n });\n\n // console.error override\n const originalConsoleError = console.error.bind(console);\n console.error = (...args: unknown[]) => {\n originalConsoleError(...args);\n\n const msg = args.map((a) => (a instanceof Error ? a.message : String(a))).join(\" \");\n\n if (this.shouldSkipGlobalError(msg)) return;\n\n const trimmedMsg = msg.length > 150 ? msg.slice(0, 150) + \"...\" : msg;\n const key = this.buildErrorKey(\"console.error\", trimmedMsg);\n if (this.loggedErrorKeys.has(key)) return;\n this.loggedErrorKeys.add(key);\n\n const originalError = args.find((a): a is Error => a instanceof Error);\n\n const errorToSend = originalError ?? new Error(trimmedMsg);\n\n void this.error({\n fnName: \"console.error\",\n messageError: errorToSend,\n });\n };\n\n // resource load error (CSS / JS / font)\n window.addEventListener(\n \"error\",\n (event) => {\n const target = event.target;\n if (!(target instanceof HTMLElement)) return;\n if (target === (window as unknown)) return;\n\n const tagName = target.tagName.toUpperCase();\n\n const src =\n tagName === \"LINK\"\n ? (target as HTMLLinkElement).href\n : tagName === \"SCRIPT\"\n ? (target as HTMLScriptElement).src\n : \"\";\n\n if (this.shouldSkipResourceError(src, tagName)) return;\n\n // Deteksi dari ekstensi file, bukan tagName\n const ext = src.split(\"?\")[0].split(\".\").pop()?.toLowerCase();\n const resourceType =\n ext === \"css\"\n ? \"CSS\"\n : ext === \"js\"\n ? \"JS\"\n : ext === \"woff\" || ext === \"woff2\" || ext === \"ttf\"\n ? \"FONT\"\n : tagName;\n\n const trimmedSrc = this.trimUrl(src);\n const finalMessage = `Failed to load ${resourceType}: ${trimmedSrc}`;\n const key = this.buildErrorKey(\"resource\", finalMessage);\n if (this.loggedErrorKeys.has(key)) return;\n this.loggedErrorKeys.add(key);\n\n void this.error({\n fnName: \"resource.onerror\",\n messageError: new Error(finalMessage),\n payload: { tagName, src: trimmedSrc },\n });\n },\n true,\n );\n }\n\n private sanitizePayload(obj: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) => {\n if (value === undefined) return [key, null];\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n return [key, this.sanitizePayload(value as Record<string, unknown>)];\n }\n return [key, value];\n }),\n );\n }\n\n private sanitizeForFirestore(obj: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) => {\n if (value === undefined) return [key, null];\n if (typeof value === \"function\") return [key, \"[function]\"];\n if (value instanceof Date) return [key, value.toISOString()];\n if (Array.isArray(value)) {\n return [\n key,\n value.map((item) => {\n if (item instanceof Date) return item.toISOString();\n if (typeof item === \"function\") return \"[function]\"; // ← tambah\n if (item !== null && typeof item === \"object\") {\n return this.sanitizeForFirestore(item as Record<string, unknown>);\n }\n return item ?? null;\n }),\n ];\n }\n if (value !== null && typeof value === \"object\") {\n return [key, this.sanitizeForFirestore(value as Record<string, unknown>)];\n }\n return [key, value];\n }),\n );\n }\n\n // interceptFetch — auto breadcrumb, ke Firestore\n interceptFetch() {\n if (typeof window === \"undefined\") return;\n\n const originalFetch = window.fetch;\n\n const skipPatterns = [\n \"google.com\",\n \"googletagmanager.com\",\n \"googleapis.com\",\n \"facebook.com\",\n \"hotjar.com\",\n \"analytics\",\n \"gtm\",\n \"ccm/collect\",\n \"on.aws\",\n \"/locales/\",\n \"/sw.js\",\n \"/manifest.json\",\n \"/sourcemaps/\",\n \"magnolia\",\n ];\n\n const shouldSkipUrl = (url: string): boolean => {\n return skipPatterns.some((pattern) => url.includes(pattern));\n };\n\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\n // Skip third-party domains\n if (shouldSkipUrl(url)) {\n return originalFetch(input, init);\n }\n\n const method = init?.method ?? \"GET\";\n const startTime = Date.now();\n\n try {\n const response = await originalFetch(input, init);\n const duration = Date.now() - startTime;\n\n this.addBreadcrumb(\"fetch\", `${method} ${url}`, {\n status: response.status,\n duration: `${duration}ms`,\n });\n\n if (response.status >= 400) {\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} ${response.status}`);\n\n if (!this.loggedErrorKeys.has(dedupKey)) {\n this.loggedErrorKeys.add(dedupKey);\n\n let responseBody: unknown = null;\n try {\n const cloned = response.clone();\n const contentType = cloned.headers.get(\"content-type\") ?? \"\";\n responseBody = contentType.includes(\"application/json\")\n ? await cloned.json()\n : await cloned.text();\n } catch {\n responseBody = \"[unable to read response body]\";\n }\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n\n if (!isLocalhost) {\n this.error({\n fnName: \"interceptFetch\",\n messageError: new Error(`HTTP ${response.status} ${response.statusText} — ${url}`),\n endpointName: url,\n payload: {\n method,\n status: response.status,\n statusText: response.statusText,\n duration: `${duration}ms`,\n responseBody,\n },\n });\n }\n }\n }\n\n return response;\n } catch (networkError) {\n const duration = Date.now() - startTime;\n const error = networkError instanceof Error ? networkError : new Error(String(networkError));\n\n this.addBreadcrumb(\"fetch\", `${method} ${url} — NETWORK ERROR`, {\n duration: `${duration}ms`,\n error: error.message,\n });\n\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} NETWORK_ERROR`);\n\n if (!this.loggedErrorKeys.has(dedupKey)) {\n this.loggedErrorKeys.add(dedupKey);\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n if (!isLocalhost) {\n this.error({\n fnName: \"interceptFetch\",\n messageError: error,\n endpointName: url,\n payload: { method, duration: `${duration}ms` },\n });\n }\n }\n\n throw networkError;\n }\n };\n\n console.info(\"[ga-logger] fetch intercepted\");\n }\n\n // interceptXHR — auto breadcrumb + error capture untuk axios/XHR-based request\n interceptXHR(): void {\n if (typeof window === \"undefined\") return;\n\n const skipPatterns = [\n \"google.com\",\n \"googletagmanager.com\",\n \"googleapis.com\",\n \"facebook.com\",\n \"hotjar.com\",\n \"analytics\",\n \"ccm/collect\",\n \"on.aws\",\n \"/locales/\",\n \"/sw.js\",\n \"/manifest.json\",\n \"/sourcemaps/\",\n \"magnolia\",\n ];\n\n const shouldSkipUrl = (url: string): boolean => skipPatterns.some((p) => url.includes(p));\n\n const originalOpen = XMLHttpRequest.prototype.open;\n const originalSend = XMLHttpRequest.prototype.send;\n const self = this;\n\n type GaXHR = XMLHttpRequest & {\n _gaMethod?: string;\n _gaUrl?: string;\n _gaStartTime?: number;\n };\n\n XMLHttpRequest.prototype.open = function (this: GaXHR, method: string, url: string | URL, ...rest: unknown[]) {\n this._gaMethod = method;\n this._gaUrl = typeof url === \"string\" ? url : url.href;\n // @ts-expect-error - spreading native arguments\n return originalOpen.call(this, method, url, ...rest);\n };\n\n XMLHttpRequest.prototype.send = function (this: GaXHR, ...args: unknown[]) {\n const url = this._gaUrl ?? \"\";\n const method = this._gaMethod ?? \"GET\";\n\n if (shouldSkipUrl(url)) {\n // @ts-expect-error - spreading native arguments\n return originalSend.call(this, ...args);\n }\n\n this._gaStartTime = Date.now();\n\n this.addEventListener(\"loadend\", () => {\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\n const status = this.status;\n\n self.addBreadcrumb(\"fetch\", `${method} ${url}`, {\n status,\n duration: `${duration}ms`,\n });\n\n if (status >= 400) {\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} ${status}`);\n if (self.loggedErrorKeys.has(dedupKey)) return;\n self.loggedErrorKeys.add(dedupKey);\n\n let responseBody: unknown = this.responseText;\n try {\n responseBody = JSON.parse(this.responseText);\n } catch {\n // keep as raw text\n }\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n\n if (!isLocalhost) {\n void self.error({\n fnName: \"interceptXHR\",\n messageError: new Error(`HTTP ${status} ${this.statusText} — ${url}`),\n endpointName: url,\n payload: {\n method,\n status,\n statusText: this.statusText,\n duration: `${duration}ms`,\n responseBody,\n },\n });\n }\n }\n });\n\n this.addEventListener(\"error\", () => {\n // network-level failure (status selalu 0)\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} NETWORK_ERROR`);\n if (self.loggedErrorKeys.has(dedupKey)) return;\n self.loggedErrorKeys.add(dedupKey);\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n\n if (!isLocalhost) {\n void self.error({\n fnName: \"interceptXHR\",\n messageError: new Error(`Network error — ${method} ${url}`),\n endpointName: url,\n payload: { method, duration: `${duration}ms` },\n });\n }\n });\n\n // @ts-expect-error - spreading native arguments\n return originalSend.call(this, ...args);\n };\n\n console.info(\"[ga-logger] XHR intercepted\");\n }\n\n interceptClick(): void {\n if (typeof window === \"undefined\") return;\n\n if ((window as any).__ga_click_intercepted) return;\n (window as any).__ga_click_intercepted = true;\n this.initDomActivityObserver();\n\n document.addEventListener(\n \"click\",\n (event: MouseEvent) => {\n const raw = event.target as HTMLElement;\n if (!raw) return;\n\n const target = (raw.closest('button, a, [role=\"button\"], [data-log], input') as HTMLElement) ?? raw;\n\n const innerText = sanitizeClickText(target.innerText?.trim().slice(0, 50) ?? \"\");\n const id = target.id ?? \"\";\n const tagName = target.tagName?.toLowerCase() ?? \"\";\n\n const isInteractive =\n [\"button\", \"a\", \"input\"].includes(tagName) ||\n target.hasAttribute(\"data-log\") ||\n target.getAttribute(\"role\") === \"button\";\n\n if (!innerText && !id && !isInteractive) return;\n\n const message = innerText || id || `(no label: ${tagName || \"unknown\"})`;\n\n // Skip internal React/Router identifiers\n if (CLICK_SKIP_PATTERNS.some((p) => p.test(message))) return;\n\n this.addBreadcrumb(\"click\", message, {\n tagName,\n ...(id ? { id } : {}),\n ...(innerText ? { text: innerText } : {}),\n });\n\n const disabledAtClick = this.isElementDisabled(target);\n const invalidFieldAtClick = this.hasInvalidFieldNearby(target);\n // ── Dead click detection ──\n const clickTime = Date.now();\n const domActivityAtClick = this.lastDomActivity;\n const DEAD_CLICK_DELAY_MS = 4000;\n const RAGE_CLICK_WINDOW_MS = 2000;\n const RAGE_CLICK_COOLDOWN_MS = 5000;\n \n\n if ([\"button\", \"a\", \"input\"].includes(tagName)) {\n setTimeout(() => {\n if (this.lastDomActivity <= domActivityAtClick) {\n const recentFetch = this.breadcrumbs\n .filter(\n (b) =>\n b.type === \"fetch\" &&\n new Date(b.timestamp).getTime() >= clickTime &&\n new Date(b.timestamp).getTime() <= clickTime + DEAD_CLICK_DELAY_MS,\n )\n .slice(-1)[0];\n\n const reason = disabledAtClick\n ? \"button is disabled\"\n : recentFetch\n ? `recent request: ${recentFetch.message}`\n : invalidFieldAtClick\n ? \"validation error detected nearby\"\n : `no DOM/scroll activity within ${DEAD_CLICK_DELAY_MS / 1000}s`;\n\n this.addBreadcrumb(\"click\", `Dead click: ${message}`, {\n tagName,\n ...(id ? { id } : {}),\n reason,\n });\n\n void this.warn({\n fnName: \"interceptClick.deadClick\",\n messageError: new Error(`Dead click: ${message}`),\n payload: { tagName, ...(id ? { id } : {}), reason },\n excludeBreadcrumbTypes: [\"fetch\"],\n breadcrumbWindowMs: 10000,\n });\n }\n }, DEAD_CLICK_DELAY_MS);\n }\n\n // ── Rage click detection 3x klik ──\n const historyKey = `${tagName}::${message}`;\n const timestamps = (this.clickHistory.get(historyKey) ?? []).filter(\n (t) => clickTime - t < RAGE_CLICK_WINDOW_MS,\n );\n timestamps.push(clickTime);\n this.clickHistory.set(historyKey, timestamps);\n\n const lastFired = this.rageClickFired.get(historyKey) ?? 0;\n if (timestamps.length >= 3 && clickTime - lastFired > RAGE_CLICK_COOLDOWN_MS) {\n this.rageClickFired.set(historyKey, clickTime);\n\n const recentFetch = this.breadcrumbs\n .filter(\n (b) =>\n b.type === \"fetch\" &&\n clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS,\n )\n .slice(-1)[0];\n\n const possibleReason = disabledAtClick\n ? \"button is disabled\"\n : recentFetch\n ? `recent request: ${recentFetch.message}`\n : invalidFieldAtClick\n ? \"validation error detected nearby\"\n : this.recentDomChanges.length\n ? this.recentDomChanges.slice(-1)[0]\n : \"no visible UI feedback detected\";\n\n this.addBreadcrumb(\"click\", `Loop click: ${message}`, {\n tagName,\n clickCount: timestamps.length,\n windowMs: RAGE_CLICK_WINDOW_MS,\n possibleReason,\n });\n\n void this.warn({\n fnName: \"interceptClick.rageClick\",\n messageError: new Error(`Loop click: ${message}`),\n payload: {\n tagName,\n clickCount: timestamps.length,\n windowMs: RAGE_CLICK_WINDOW_MS,\n possibleReason,\n },\n excludeBreadcrumbTypes: [\"fetch\"],\n breadcrumbWindowMs: 10000,\n });\n }\n },\n true,\n );\n }\n\n captureAction<T>(params: LogParams, fn: () => T | Promise<T>): Promise<T | undefined> {\n return Promise.resolve()\n .then(() => fn())\n .catch((error: unknown) => {\n const err = error instanceof Error ? error : new Error(String(error));\n this.send(\"error\", params.fnName, params.payload, err, params.endpointName);\n return undefined;\n });\n }\n\n withLogger<T extends (...args: any[]) => any>(\n fnName: string,\n fn: T,\n options?: {\n endpointName?: string;\n payload?: Record<string, unknown>;\n },\n ): T {\n return ((...args: Parameters<T>): ReturnType<T> | undefined => {\n try {\n const result = fn(...args);\n if (result instanceof Promise) {\n return result.catch((error: unknown) => {\n const err = error instanceof Error ? error : new Error(String(error));\n this.error({\n fnName,\n messageError: err,\n endpointName: options?.endpointName,\n payload: options?.payload,\n });\n return undefined;\n }) as ReturnType<T>;\n }\n\n return result;\n } catch (error: unknown) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.error({\n fnName,\n messageError: err,\n endpointName: options?.endpointName,\n payload: options?.payload,\n });\n return undefined;\n }\n }) as T;\n }\n\n private async send(\n level: LogLevel,\n message: string,\n payload: Record<string, unknown> = {},\n error?: Error,\n endpoint?: string,\n excludeBreadcrumbTypes?: BreadcrumbType[],\n breadcrumbWindowMs?: number,\n ) {\n if (!this.initialized || !this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n\n if (this.config.env === \"localhost\") {\n console.error(`[ga-localhost][DEV] ${message}`, { payload, error });\n return;\n }\n\n const now = Date.now();\n\n let filteredBreadcrumbs = excludeBreadcrumbTypes?.length\n ? this.breadcrumbs.filter((b) => !excludeBreadcrumbTypes.includes(b.type))\n : this.breadcrumbs;\n\n if (breadcrumbWindowMs) {\n filteredBreadcrumbs = filteredBreadcrumbs.filter(\n (b) => now - new Date(b.timestamp).getTime() <= breadcrumbWindowMs,\n );\n }\n\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n message,\n env: this.config.env,\n userId: this.config.userId ?? null,\n sessionId,\n page: typeof window !== \"undefined\" ? window.location.pathname : \"\",\n stackTrace: error?.stack ?? null,\n payload: this.sanitizePayload(payload),\n repo: this.config.repo,\n endpoint: endpoint ?? null,\n breadcrumbs: [...filteredBreadcrumbs],\n ...getEnvironmentPayload(),\n };\n\n try {\n const db = getDb();\n const safeEntry = this.sanitizeForFirestore(entry as unknown as Record<string, unknown>);\n await addDoc(collection(db, \"logs\"), {\n ...safeEntry,\n timestamp: Timestamp.fromDate(entry.timestamp),\n });\n } catch (e) {\n console.error(\"[ga-logger] failed to send log:\", e);\n }\n }\n\n info(params: LogParams) {\n const { fnName, messageError, payload, endpointName } = params;\n return this.send(\"info\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\n }\n\n warn(params: LogParams) {\n const { fnName, messageError, payload, endpointName, excludeBreadcrumbTypes, breadcrumbWindowMs } = params;\n return this.send(\n \"warn\",\n messageError?.message ?? fnName,\n { ...payload, fnName },\n messageError,\n endpointName,\n excludeBreadcrumbTypes,\n breadcrumbWindowMs,\n );\n }\n\n error(params: LogParams) {\n const { fnName, messageError, payload, endpointName } = params;\n return this.send(\"error\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\n }\n}\n\nexport const logger = new Logger();\n\n\n\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"]}
|
package/dist/index.mjs
CHANGED
|
@@ -70,6 +70,15 @@ var Logger = class {
|
|
|
70
70
|
// Global error capture — hanya aktif di non-development
|
|
71
71
|
this.loggedErrorKeys = /* @__PURE__ */ new Set();
|
|
72
72
|
}
|
|
73
|
+
isElementDisabled(target) {
|
|
74
|
+
return target.disabled === true || target.getAttribute("aria-disabled") === "true";
|
|
75
|
+
}
|
|
76
|
+
// Helper: cek apakah ada field invalid di form terdekat dari target
|
|
77
|
+
hasInvalidFieldNearby(target) {
|
|
78
|
+
const form = target.closest("form");
|
|
79
|
+
if (!form) return false;
|
|
80
|
+
return form.querySelector('[aria-invalid="true"]') !== null;
|
|
81
|
+
}
|
|
73
82
|
initDomActivityObserver() {
|
|
74
83
|
if (this.domObserverInitialized) return;
|
|
75
84
|
this.domObserverInitialized = true;
|
|
@@ -85,15 +94,21 @@ var Logger = class {
|
|
|
85
94
|
const observer = new MutationObserver((mutations) => {
|
|
86
95
|
markActivity();
|
|
87
96
|
for (const m of mutations) {
|
|
88
|
-
m.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
if (m.type === "childList") {
|
|
98
|
+
m.addedNodes.forEach((node) => {
|
|
99
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
100
|
+
const text = node.innerText?.trim();
|
|
101
|
+
if (text && text.length < 200) markActivity(text);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
} else if (m.type === "characterData") {
|
|
105
|
+
const parent = m.target.parentElement;
|
|
106
|
+
const text = parent?.innerText?.trim();
|
|
107
|
+
if (text && text.length < 200) markActivity(text);
|
|
108
|
+
}
|
|
94
109
|
}
|
|
95
110
|
});
|
|
96
|
-
observer.observe(document.body, { childList: true, subtree: true, attributes: true });
|
|
111
|
+
observer.observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true });
|
|
97
112
|
window.addEventListener("scroll", () => markActivity(), { passive: true, capture: true });
|
|
98
113
|
}
|
|
99
114
|
init(config) {
|
|
@@ -516,12 +531,20 @@ var Logger = class {
|
|
|
516
531
|
...id ? { id } : {},
|
|
517
532
|
...innerText ? { text: innerText } : {}
|
|
518
533
|
});
|
|
534
|
+
const disabledAtClick = this.isElementDisabled(target);
|
|
535
|
+
const invalidFieldAtClick = this.hasInvalidFieldNearby(target);
|
|
519
536
|
const clickTime = Date.now();
|
|
520
537
|
const domActivityAtClick = this.lastDomActivity;
|
|
538
|
+
const DEAD_CLICK_DELAY_MS = 4e3;
|
|
539
|
+
const RAGE_CLICK_WINDOW_MS = 2e3;
|
|
540
|
+
const RAGE_CLICK_COOLDOWN_MS = 5e3;
|
|
521
541
|
if (["button", "a", "input"].includes(tagName)) {
|
|
522
542
|
setTimeout(() => {
|
|
523
543
|
if (this.lastDomActivity <= domActivityAtClick) {
|
|
524
|
-
const
|
|
544
|
+
const recentFetch = this.breadcrumbs.filter(
|
|
545
|
+
(b) => b.type === "fetch" && new Date(b.timestamp).getTime() >= clickTime && new Date(b.timestamp).getTime() <= clickTime + DEAD_CLICK_DELAY_MS
|
|
546
|
+
).slice(-1)[0];
|
|
547
|
+
const reason = disabledAtClick ? "button is disabled" : recentFetch ? `recent request: ${recentFetch.message}` : invalidFieldAtClick ? "validation error detected nearby" : `no DOM/scroll activity within ${DEAD_CLICK_DELAY_MS / 1e3}s`;
|
|
525
548
|
this.addBreadcrumb("click", `Dead click: ${message}`, {
|
|
526
549
|
tagName,
|
|
527
550
|
...id ? { id } : {},
|
|
@@ -531,30 +554,42 @@ var Logger = class {
|
|
|
531
554
|
fnName: "interceptClick.deadClick",
|
|
532
555
|
messageError: new Error(`Dead click: ${message}`),
|
|
533
556
|
payload: { tagName, ...id ? { id } : {}, reason },
|
|
534
|
-
excludeBreadcrumbTypes: ["fetch"]
|
|
557
|
+
excludeBreadcrumbTypes: ["fetch"],
|
|
558
|
+
breadcrumbWindowMs: 1e4
|
|
535
559
|
});
|
|
536
560
|
}
|
|
537
|
-
},
|
|
561
|
+
}, DEAD_CLICK_DELAY_MS);
|
|
538
562
|
}
|
|
539
563
|
const historyKey = `${tagName}::${message}`;
|
|
540
|
-
const timestamps = (this.clickHistory.get(historyKey) ?? []).filter(
|
|
564
|
+
const timestamps = (this.clickHistory.get(historyKey) ?? []).filter(
|
|
565
|
+
(t) => clickTime - t < RAGE_CLICK_WINDOW_MS
|
|
566
|
+
);
|
|
541
567
|
timestamps.push(clickTime);
|
|
542
568
|
this.clickHistory.set(historyKey, timestamps);
|
|
543
569
|
const lastFired = this.rageClickFired.get(historyKey) ?? 0;
|
|
544
|
-
if (timestamps.length >= 3 && clickTime - lastFired >
|
|
570
|
+
if (timestamps.length >= 3 && clickTime - lastFired > RAGE_CLICK_COOLDOWN_MS) {
|
|
545
571
|
this.rageClickFired.set(historyKey, clickTime);
|
|
546
|
-
const
|
|
572
|
+
const recentFetch = this.breadcrumbs.filter(
|
|
573
|
+
(b) => b.type === "fetch" && clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS
|
|
574
|
+
).slice(-1)[0];
|
|
575
|
+
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";
|
|
547
576
|
this.addBreadcrumb("click", `Loop click: ${message}`, {
|
|
548
577
|
tagName,
|
|
549
578
|
clickCount: timestamps.length,
|
|
550
|
-
windowMs:
|
|
579
|
+
windowMs: RAGE_CLICK_WINDOW_MS,
|
|
551
580
|
possibleReason
|
|
552
581
|
});
|
|
553
582
|
void this.warn({
|
|
554
583
|
fnName: "interceptClick.rageClick",
|
|
555
584
|
messageError: new Error(`Loop click: ${message}`),
|
|
556
|
-
payload: {
|
|
557
|
-
|
|
585
|
+
payload: {
|
|
586
|
+
tagName,
|
|
587
|
+
clickCount: timestamps.length,
|
|
588
|
+
windowMs: RAGE_CLICK_WINDOW_MS,
|
|
589
|
+
possibleReason
|
|
590
|
+
},
|
|
591
|
+
excludeBreadcrumbTypes: ["fetch"],
|
|
592
|
+
breadcrumbWindowMs: 1e4
|
|
558
593
|
});
|
|
559
594
|
}
|
|
560
595
|
},
|
|
@@ -597,7 +632,7 @@ var Logger = class {
|
|
|
597
632
|
}
|
|
598
633
|
});
|
|
599
634
|
}
|
|
600
|
-
async send(level, message, payload = {}, error, endpoint, excludeBreadcrumbTypes) {
|
|
635
|
+
async send(level, message, payload = {}, error, endpoint, excludeBreadcrumbTypes, breadcrumbWindowMs) {
|
|
601
636
|
if (!this.initialized || !this.config) {
|
|
602
637
|
console.warn("[ga-logger] not initialized. Call logger.init() first.");
|
|
603
638
|
return;
|
|
@@ -606,7 +641,13 @@ var Logger = class {
|
|
|
606
641
|
console.error(`[ga-localhost][DEV] ${message}`, { payload, error });
|
|
607
642
|
return;
|
|
608
643
|
}
|
|
609
|
-
const
|
|
644
|
+
const now = Date.now();
|
|
645
|
+
let filteredBreadcrumbs = excludeBreadcrumbTypes?.length ? this.breadcrumbs.filter((b) => !excludeBreadcrumbTypes.includes(b.type)) : this.breadcrumbs;
|
|
646
|
+
if (breadcrumbWindowMs) {
|
|
647
|
+
filteredBreadcrumbs = filteredBreadcrumbs.filter(
|
|
648
|
+
(b) => now - new Date(b.timestamp).getTime() <= breadcrumbWindowMs
|
|
649
|
+
);
|
|
650
|
+
}
|
|
610
651
|
const entry = {
|
|
611
652
|
timestamp: /* @__PURE__ */ new Date(),
|
|
612
653
|
level,
|
|
@@ -638,14 +679,15 @@ var Logger = class {
|
|
|
638
679
|
return this.send("info", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);
|
|
639
680
|
}
|
|
640
681
|
warn(params) {
|
|
641
|
-
const { fnName, messageError, payload, endpointName, excludeBreadcrumbTypes } = params;
|
|
682
|
+
const { fnName, messageError, payload, endpointName, excludeBreadcrumbTypes, breadcrumbWindowMs } = params;
|
|
642
683
|
return this.send(
|
|
643
684
|
"warn",
|
|
644
685
|
messageError?.message ?? fnName,
|
|
645
686
|
{ ...payload, fnName },
|
|
646
687
|
messageError,
|
|
647
688
|
endpointName,
|
|
648
|
-
excludeBreadcrumbTypes
|
|
689
|
+
excludeBreadcrumbTypes,
|
|
690
|
+
breadcrumbWindowMs
|
|
649
691
|
);
|
|
650
692
|
}
|
|
651
693
|
error(params) {
|
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 { collection, addDoc, Timestamp } from \"firebase/firestore\";\nimport { initFirebase, getDb } from \"../config/firebase\";\nimport type { Breadcrumb, BreadcrumbType, LogEntry, LogLevel, LogParams, LoggerConfig } from \"../types\";\nimport { initEnvironment } from \"./environment\";\nimport { getEnvironmentPayload } from \"./environment\";\n\nconst sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\nconst MAX_BREADCRUMBS = 50;\n\nconst CLICK_SKIP_PATTERNS = [/^:r[a-z0-9]+:$/, /^\\[object/, /^#/];\n\n// Sanitize PII sebelum masuk Firestore\nconst sanitizeClickText = (text: string): string => {\n return text\n .replace(/\\b[\\w.-]+@[\\w.-]+\\.\\w+\\b/g, \"[email]\")\n .replace(/\\b\\d{10,}\\b/g, \"[number]\")\n .replace(/Rp[\\s]?\\d[\\d.,]*/gi, \"[amount]\")\n .trim();\n};\n\nclass Logger {\n private config: LoggerConfig | null = null;\n private initialized = false;\n private breadcrumbs: Breadcrumb[] = [];\n\n private lastDomActivity = Date.now();\n private clickHistory = new Map<string, number[]>();\n private rageClickFired = new Map<string, number>();\n private domObserverInitialized = false;\n\n \n private recentDomChanges: string[] = [];\n\n private initDomActivityObserver(): void {\n if (this.domObserverInitialized) return;\n this.domObserverInitialized = true;\n\n const markActivity = (addedText?: string) => {\n this.lastDomActivity = Date.now();\n if (addedText) {\n this.recentDomChanges.push(addedText);\n // keep only last few seconds worth\n setTimeout(() => {\n this.recentDomChanges = this.recentDomChanges.filter((t) => t !== addedText);\n }, 1000); // ⚠️ perlu konfirmasi durasi\n }\n };\n\n const observer = new MutationObserver((mutations) => {\n markActivity();\n for (const m of mutations) {\n m.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const text = (node as HTMLElement).innerText?.trim();\n if (text && text.length < 200) markActivity(text);\n }\n });\n }\n });\n\n observer.observe(document.body, { childList: true, subtree: true, attributes: true });\n window.addEventListener(\"scroll\", () => markActivity(), { passive: true, capture: true });\n }\n\n init(config: LoggerConfig) {\n this.config = config;\n initFirebase(config);\n initEnvironment();\n this.initialized = true;\n console.info(\"[ga-logger] initialized —\", config.env);\n }\n setUserId(userId: string | null) {\n if (!this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n this.config.userId = userId;\n }\n\n initAnonymousUser(): void {\n if (!this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n\n const STORAGE_KEY = \"ga_anon_id\";\n\n try {\n let anonId = localStorage.getItem(STORAGE_KEY);\n\n if (!anonId) {\n anonId =\n typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\n ? crypto.randomUUID()\n : \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n\n localStorage.setItem(STORAGE_KEY, anonId);\n }\n\n const shortId = anonId.slice(-8);\n this.setUserId(`Anonymous #${shortId}`);\n } catch (e) {\n // localStorage blocked (private mode / HTTP restriction)\n console.warn(\"[ga-logger] initAnonymousUser failed:\", e);\n this.setUserId(\"Anonymous\");\n }\n }\n\n // Add breadcrumb ke in-memory array\n addBreadcrumb(type: BreadcrumbType, message: string, data?: Record<string, unknown>) {\n const breadcrumb: Breadcrumb = {\n timestamp: new Date(),\n type,\n message,\n data,\n };\n\n this.breadcrumbs.push(breadcrumb);\n\n // Jaga memory — max 50 breadcrumbs, buang yang paling lama\n if (this.breadcrumbs.length > MAX_BREADCRUMBS) {\n this.breadcrumbs.shift();\n }\n }\n\n // Global error capture — hanya aktif di non-development\n private loggedErrorKeys = new Set<string>();\n\n // Helper: trim URL jadi domain/filename\n private trimUrl(url: string): string {\n try {\n const u = new URL(url);\n const filename = u.pathname.split(\"/\").filter(Boolean).pop() ?? u.pathname;\n return `${u.hostname}/${filename}`;\n } catch {\n // bukan valid URL, potong saja jika terlalu panjang\n return url.length > 80 ? url.slice(0, 80) + \"...\" : url;\n }\n }\n\n // Helper: build dedup key\n private buildErrorKey(type: string, message: string): string {\n return `${type}::${message}`;\n }\n\n // Helper: should skip resource error\n private shouldSkipResourceError(src: string, tagName: string): boolean {\n // Skip IMG — tidak sebabkan blank page\n if (tagName === \"IMG\") return true;\n\n // Skip base64 URL\n if (src.startsWith(\"data:\")) return true;\n\n // Skip known 3rd party trackers\n const THIRD_PARTY = [\n \"omguk.com\",\n \"track.\",\n \"doubleclick\",\n \"adservice\",\n \"googletagmanager\",\n \"google-analytics\",\n \"hotjar\",\n \"facebook.net\",\n \"analytics\",\n \"magnolia\",\n \"gtm.js\",\n ];\n if (THIRD_PARTY.some((p) => src.includes(p))) return true;\n\n return false;\n }\n\n private shouldSkipGlobalError(message: string): boolean {\n const NOISE = [\n \"CORS\",\n \"cross-origin\",\n \"ERR_FAILED\",\n \"Failed to load resource\",\n \"NetworkError\",\n \"ResizeObserver loop\",\n \"Script error\",\n \"ServiceWorker\",\n \"sw.js\",\n \"dev-sw.js\",\n \"Warning:\",\n ];\n const THIRD_PARTY = [\n \"magnolia\",\n \"gtm.js\",\n \"analytics\",\n \"googletagmanager\",\n \"facebook\",\n \"hotjar\",\n \"omguk.com\",\n \"track.\",\n \"doubleclick\",\n \"adservice\",\n ];\n return NOISE.some((p) => message.includes(p)) || THIRD_PARTY.some((p) => message.includes(p));\n }\n\n initGlobalCapture(): void {\n const isLocalhost = window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n if (isLocalhost) return;\n\n window.onerror = (message, source, lineno, colno) => {\n const msg = typeof message === \"string\" ? message : \"Unknown error\";\n if (this.shouldSkipGlobalError(msg)) return false;\n\n const trimmedSource = source ? this.trimUrl(source) : \"unknown source\";\n const finalMessage = `${msg} at ${trimmedSource}:${lineno ?? 0}`;\n const key = this.buildErrorKey(\"onerror\", finalMessage);\n if (this.loggedErrorKeys.has(key)) return false;\n this.loggedErrorKeys.add(key);\n\n void this.error({\n fnName: \"window.onerror\",\n messageError: new Error(finalMessage),\n payload: { source: trimmedSource, lineno, colno },\n });\n\n return false;\n };\n\n // unhandledrejection\n window.addEventListener(\"unhandledrejection\", (event) => {\n const msg =\n event.reason instanceof Error\n ? event.reason.message\n : String(event.reason ?? \"Unhandled Promise rejection\");\n\n if (this.shouldSkipGlobalError(msg)) return;\n\n const key = this.buildErrorKey(\"unhandledrejection\", msg);\n if (this.loggedErrorKeys.has(key)) return;\n this.loggedErrorKeys.add(key);\n\n void this.error({\n fnName: \"window.unhandledrejection\",\n messageError: new Error(msg),\n });\n });\n\n // console.error override\n const originalConsoleError = console.error.bind(console);\n console.error = (...args: unknown[]) => {\n originalConsoleError(...args);\n\n const msg = args.map((a) => (a instanceof Error ? a.message : String(a))).join(\" \");\n\n if (this.shouldSkipGlobalError(msg)) return;\n\n const trimmedMsg = msg.length > 150 ? msg.slice(0, 150) + \"...\" : msg;\n const key = this.buildErrorKey(\"console.error\", trimmedMsg);\n if (this.loggedErrorKeys.has(key)) return;\n this.loggedErrorKeys.add(key);\n\n const originalError = args.find((a): a is Error => a instanceof Error);\n\n const errorToSend = originalError ?? new Error(trimmedMsg);\n\n void this.error({\n fnName: \"console.error\",\n messageError: errorToSend,\n });\n };\n\n // resource load error (CSS / JS / font)\n window.addEventListener(\n \"error\",\n (event) => {\n const target = event.target;\n if (!(target instanceof HTMLElement)) return;\n if (target === (window as unknown)) return;\n\n const tagName = target.tagName.toUpperCase();\n\n const src =\n tagName === \"LINK\"\n ? (target as HTMLLinkElement).href\n : tagName === \"SCRIPT\"\n ? (target as HTMLScriptElement).src\n : \"\";\n\n if (this.shouldSkipResourceError(src, tagName)) return;\n\n // Deteksi dari ekstensi file, bukan tagName\n const ext = src.split(\"?\")[0].split(\".\").pop()?.toLowerCase();\n const resourceType =\n ext === \"css\"\n ? \"CSS\"\n : ext === \"js\"\n ? \"JS\"\n : ext === \"woff\" || ext === \"woff2\" || ext === \"ttf\"\n ? \"FONT\"\n : tagName;\n\n const trimmedSrc = this.trimUrl(src);\n const finalMessage = `Failed to load ${resourceType}: ${trimmedSrc}`;\n const key = this.buildErrorKey(\"resource\", finalMessage);\n if (this.loggedErrorKeys.has(key)) return;\n this.loggedErrorKeys.add(key);\n\n void this.error({\n fnName: \"resource.onerror\",\n messageError: new Error(finalMessage),\n payload: { tagName, src: trimmedSrc },\n });\n },\n true,\n );\n }\n\n private sanitizePayload(obj: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) => {\n if (value === undefined) return [key, null];\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n return [key, this.sanitizePayload(value as Record<string, unknown>)];\n }\n return [key, value];\n }),\n );\n }\n\n private sanitizeForFirestore(obj: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) => {\n if (value === undefined) return [key, null];\n if (typeof value === \"function\") return [key, \"[function]\"];\n if (value instanceof Date) return [key, value.toISOString()];\n if (Array.isArray(value)) {\n return [\n key,\n value.map((item) => {\n if (item instanceof Date) return item.toISOString();\n if (typeof item === \"function\") return \"[function]\"; // ← tambah\n if (item !== null && typeof item === \"object\") {\n return this.sanitizeForFirestore(item as Record<string, unknown>);\n }\n return item ?? null;\n }),\n ];\n }\n if (value !== null && typeof value === \"object\") {\n return [key, this.sanitizeForFirestore(value as Record<string, unknown>)];\n }\n return [key, value];\n }),\n );\n }\n\n // interceptFetch — auto breadcrumb, ke Firestore\n interceptFetch() {\n if (typeof window === \"undefined\") return;\n\n const originalFetch = window.fetch;\n\n const skipPatterns = [\n \"google.com\",\n \"googletagmanager.com\",\n \"googleapis.com\",\n \"facebook.com\",\n \"hotjar.com\",\n \"analytics\",\n \"gtm\",\n \"ccm/collect\",\n \"on.aws\",\n \"/locales/\",\n \"/sw.js\",\n \"/manifest.json\",\n \"/sourcemaps/\",\n \"magnolia\",\n ];\n\n const shouldSkipUrl = (url: string): boolean => {\n return skipPatterns.some((pattern) => url.includes(pattern));\n };\n\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\n // Skip third-party domains\n if (shouldSkipUrl(url)) {\n return originalFetch(input, init);\n }\n\n const method = init?.method ?? \"GET\";\n const startTime = Date.now();\n\n try {\n const response = await originalFetch(input, init);\n const duration = Date.now() - startTime;\n\n this.addBreadcrumb(\"fetch\", `${method} ${url}`, {\n status: response.status,\n duration: `${duration}ms`,\n });\n\n if (response.status >= 400) {\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} ${response.status}`);\n\n if (!this.loggedErrorKeys.has(dedupKey)) {\n this.loggedErrorKeys.add(dedupKey);\n\n let responseBody: unknown = null;\n try {\n const cloned = response.clone();\n const contentType = cloned.headers.get(\"content-type\") ?? \"\";\n responseBody = contentType.includes(\"application/json\")\n ? await cloned.json()\n : await cloned.text();\n } catch {\n responseBody = \"[unable to read response body]\";\n }\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n\n if (!isLocalhost) {\n this.error({\n fnName: \"interceptFetch\",\n messageError: new Error(`HTTP ${response.status} ${response.statusText} — ${url}`),\n endpointName: url,\n payload: {\n method,\n status: response.status,\n statusText: response.statusText,\n duration: `${duration}ms`,\n responseBody,\n },\n });\n }\n }\n }\n\n return response;\n } catch (networkError) {\n const duration = Date.now() - startTime;\n const error = networkError instanceof Error ? networkError : new Error(String(networkError));\n\n this.addBreadcrumb(\"fetch\", `${method} ${url} — NETWORK ERROR`, {\n duration: `${duration}ms`,\n error: error.message,\n });\n\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} NETWORK_ERROR`);\n\n if (!this.loggedErrorKeys.has(dedupKey)) {\n this.loggedErrorKeys.add(dedupKey);\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n if (!isLocalhost) {\n this.error({\n fnName: \"interceptFetch\",\n messageError: error,\n endpointName: url,\n payload: { method, duration: `${duration}ms` },\n });\n }\n }\n\n throw networkError;\n }\n };\n\n console.info(\"[ga-logger] fetch intercepted\");\n }\n\n // interceptXHR — auto breadcrumb + error capture untuk axios/XHR-based request\n interceptXHR(): void {\n if (typeof window === \"undefined\") return;\n\n const skipPatterns = [\n \"google.com\",\n \"googletagmanager.com\",\n \"googleapis.com\",\n \"facebook.com\",\n \"hotjar.com\",\n \"analytics\",\n \"ccm/collect\",\n \"on.aws\",\n \"/locales/\",\n \"/sw.js\",\n \"/manifest.json\",\n \"/sourcemaps/\",\n \"magnolia\",\n ];\n\n const shouldSkipUrl = (url: string): boolean => skipPatterns.some((p) => url.includes(p));\n\n const originalOpen = XMLHttpRequest.prototype.open;\n const originalSend = XMLHttpRequest.prototype.send;\n const self = this;\n\n type GaXHR = XMLHttpRequest & {\n _gaMethod?: string;\n _gaUrl?: string;\n _gaStartTime?: number;\n };\n\n XMLHttpRequest.prototype.open = function (this: GaXHR, method: string, url: string | URL, ...rest: unknown[]) {\n this._gaMethod = method;\n this._gaUrl = typeof url === \"string\" ? url : url.href;\n // @ts-expect-error - spreading native arguments\n return originalOpen.call(this, method, url, ...rest);\n };\n\n XMLHttpRequest.prototype.send = function (this: GaXHR, ...args: unknown[]) {\n const url = this._gaUrl ?? \"\";\n const method = this._gaMethod ?? \"GET\";\n\n if (shouldSkipUrl(url)) {\n // @ts-expect-error - spreading native arguments\n return originalSend.call(this, ...args);\n }\n\n this._gaStartTime = Date.now();\n\n this.addEventListener(\"loadend\", () => {\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\n const status = this.status;\n\n self.addBreadcrumb(\"fetch\", `${method} ${url}`, {\n status,\n duration: `${duration}ms`,\n });\n\n if (status >= 400) {\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} ${status}`);\n if (self.loggedErrorKeys.has(dedupKey)) return;\n self.loggedErrorKeys.add(dedupKey);\n\n let responseBody: unknown = this.responseText;\n try {\n responseBody = JSON.parse(this.responseText);\n } catch {\n // keep as raw text\n }\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n\n if (!isLocalhost) {\n void self.error({\n fnName: \"interceptXHR\",\n messageError: new Error(`HTTP ${status} ${this.statusText} — ${url}`),\n endpointName: url,\n payload: {\n method,\n status,\n statusText: this.statusText,\n duration: `${duration}ms`,\n responseBody,\n },\n });\n }\n }\n });\n\n this.addEventListener(\"error\", () => {\n // network-level failure (status selalu 0)\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} NETWORK_ERROR`);\n if (self.loggedErrorKeys.has(dedupKey)) return;\n self.loggedErrorKeys.add(dedupKey);\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n\n if (!isLocalhost) {\n void self.error({\n fnName: \"interceptXHR\",\n messageError: new Error(`Network error — ${method} ${url}`),\n endpointName: url,\n payload: { method, duration: `${duration}ms` },\n });\n }\n });\n\n // @ts-expect-error - spreading native arguments\n return originalSend.call(this, ...args);\n };\n\n console.info(\"[ga-logger] XHR intercepted\");\n }\n\n interceptClick(): void {\n if (typeof window === \"undefined\") return;\n\n if ((window as any).__ga_click_intercepted) return;\n (window as any).__ga_click_intercepted = true;\n this.initDomActivityObserver();\n\n document.addEventListener(\n \"click\",\n (event: MouseEvent) => {\n const raw = event.target as HTMLElement;\n if (!raw) return;\n\n const target = (raw.closest('button, a, [role=\"button\"], [data-log], input') as HTMLElement) ?? raw;\n\n const innerText = sanitizeClickText(target.innerText?.trim().slice(0, 50) ?? \"\");\n const id = target.id ?? \"\";\n const tagName = target.tagName?.toLowerCase() ?? \"\";\n\n const isInteractive =\n [\"button\", \"a\", \"input\"].includes(tagName) ||\n target.hasAttribute(\"data-log\") ||\n target.getAttribute(\"role\") === \"button\";\n\n if (!innerText && !id && !isInteractive) return;\n\n const message = innerText || id || `(no label: ${tagName || \"unknown\"})`;\n\n // Skip internal React/Router identifiers\n if (CLICK_SKIP_PATTERNS.some((p) => p.test(message))) return;\n\n this.addBreadcrumb(\"click\", message, {\n tagName,\n ...(id ? { id } : {}),\n ...(innerText ? { text: innerText } : {}),\n });\n\n // ── Dead click detection ──\n const clickTime = Date.now();\n const domActivityAtClick = this.lastDomActivity;\n\n if ([\"button\", \"a\", \"input\"].includes(tagName)) {\n setTimeout(() => {\n if (this.lastDomActivity <= domActivityAtClick) {\n const reason = \"no DOM/scroll activity within 7s\";\n\n this.addBreadcrumb(\"click\", `Dead click: ${message}`, {\n tagName,\n ...(id ? { id } : {}),\n reason,\n });\n\n void this.warn({\n fnName: \"interceptClick.deadClick\",\n messageError: new Error(`Dead click: ${message}`),\n payload: { tagName, ...(id ? { id } : {}), reason },\n excludeBreadcrumbTypes: [\"fetch\"],\n });\n }\n }, 7000);\n }\n\n // ── Rage click detection ──\n const historyKey = `${tagName}::${message}`;\n const timestamps = (this.clickHistory.get(historyKey) ?? []).filter((t) => clickTime - t < 7000);\n timestamps.push(clickTime);\n this.clickHistory.set(historyKey, timestamps);\n\n const lastFired = this.rageClickFired.get(historyKey) ?? 0;\n if (timestamps.length >= 3 && clickTime - lastFired > 7000) {\n this.rageClickFired.set(historyKey, clickTime);\n\n const possibleReason = this.recentDomChanges.length\n ? this.recentDomChanges.slice(-1)[0]\n : \"no visible UI feedback detected\";\n\n this.addBreadcrumb(\"click\", `Loop click: ${message}`, {\n tagName,\n clickCount: timestamps.length,\n windowMs: 7000,\n possibleReason,\n });\n\n void this.warn({\n fnName: \"interceptClick.rageClick\",\n messageError: new Error(`Loop click: ${message}`),\n payload: { tagName, clickCount: timestamps.length, windowMs: 7000, possibleReason },\n excludeBreadcrumbTypes: [\"fetch\"],\n });\n }\n },\n true,\n );\n }\n\n captureAction<T>(params: LogParams, fn: () => T | Promise<T>): Promise<T | undefined> {\n return Promise.resolve()\n .then(() => fn())\n .catch((error: unknown) => {\n const err = error instanceof Error ? error : new Error(String(error));\n this.send(\"error\", params.fnName, params.payload, err, params.endpointName);\n return undefined;\n });\n }\n\n withLogger<T extends (...args: any[]) => any>(\n fnName: string,\n fn: T,\n options?: {\n endpointName?: string;\n payload?: Record<string, unknown>;\n },\n ): T {\n return ((...args: Parameters<T>): ReturnType<T> | undefined => {\n try {\n const result = fn(...args);\n\n // Handle async function\n if (result instanceof Promise) {\n return result.catch((error: unknown) => {\n const err = error instanceof Error ? error : new Error(String(error));\n this.error({\n fnName,\n messageError: err,\n endpointName: options?.endpointName,\n payload: options?.payload,\n });\n return undefined;\n }) as ReturnType<T>;\n }\n\n return result;\n } catch (error: unknown) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.error({\n fnName,\n messageError: err,\n endpointName: options?.endpointName,\n payload: options?.payload,\n });\n return undefined;\n }\n }) as T;\n }\n\n private async send(\n level: LogLevel,\n message: string,\n payload: Record<string, unknown> = {},\n error?: Error,\n endpoint?: string,\n excludeBreadcrumbTypes?: BreadcrumbType[],\n ) {\n if (!this.initialized || !this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n\n if (this.config.env === \"localhost\") {\n console.error(`[ga-localhost][DEV] ${message}`, { payload, error });\n return;\n }\n\n const filteredBreadcrumbs = excludeBreadcrumbTypes?.length\n ? this.breadcrumbs.filter((b) => !excludeBreadcrumbTypes.includes(b.type))\n : this.breadcrumbs;\n\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n message,\n env: this.config.env,\n userId: this.config.userId ?? null,\n sessionId,\n page: typeof window !== \"undefined\" ? window.location.pathname : \"\",\n stackTrace: error?.stack ?? null,\n payload: this.sanitizePayload(payload),\n repo: this.config.repo,\n endpoint: endpoint ?? null,\n breadcrumbs: [...filteredBreadcrumbs],\n ...getEnvironmentPayload(),\n };\n\n try {\n const db = getDb();\n const safeEntry = this.sanitizeForFirestore(entry as unknown as Record<string, unknown>);\n await addDoc(collection(db, \"logs\"), {\n ...safeEntry,\n timestamp: Timestamp.fromDate(entry.timestamp),\n });\n } catch (e) {\n console.error(\"[ga-logger] failed to send log:\", e);\n }\n }\n\n info(params: LogParams) {\n const { fnName, messageError, payload, endpointName } = params;\n return this.send(\"info\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\n }\n\n warn(params: LogParams) {\n const { fnName, messageError, payload, endpointName, excludeBreadcrumbTypes } = params;\n return this.send(\n \"warn\",\n messageError?.message ?? fnName,\n { ...payload, fnName },\n messageError,\n endpointName,\n excludeBreadcrumbTypes,\n );\n }\n\n error(params: LogParams) {\n const { fnName, messageError, payload, endpointName } = params;\n return this.send(\"error\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\n }\n}\n\nexport const logger = new Logger();","import { initializeApp, getApps, getApp } from \"firebase/app\";\nimport { getFirestore } from \"firebase/firestore\";\nimport type { Firestore } from \"firebase/firestore\";\nimport type { LoggerConfig } from \"../types\";\n\nconst APP_NAME = \"ga-logger\";\nlet db: Firestore | null = null;\n\nexport const initFirebase = (config: LoggerConfig) => {\n // Cek apakah named instance \"ga-logger\" sudah ada\n const existingApp = getApps().find((app) => app.name === APP_NAME);\n\n const app =\n existingApp ??\n initializeApp(\n {\n apiKey: config.apiKey,\n authDomain: config.authDomain,\n projectId: config.projectId,\n storageBucket: config.storageBucket,\n messagingSenderId: config.messagingSenderId,\n appId: config.appId,\n },\n APP_NAME,\n );\n\n db = getFirestore(app);\n return db;\n};\n\nexport const getDb = (): Firestore => {\n if (!db) throw new Error(\"[ga-logger] Firebase not initialized. Call logger.init() first.\");\n return db;\n};\n","interface EnvironmentPayload {\n userAgent: string;\n deviceType: \"Mobile\" | \"Tablet\" | \"Desktop\";\n screenWidth: number;\n screenHeight: number;\n}\n\nlet cachedEnvironment: EnvironmentPayload | null = null;\n\nfunction getDeviceType(width: number): \"Mobile\" | \"Tablet\" | \"Desktop\" {\n if (width < 768) return \"Mobile\";\n if (width <= 1024) return \"Tablet\";\n return \"Desktop\";\n}\n\nexport function initEnvironment(): void {\n if (cachedEnvironment) return;\n\n const width = window.innerWidth;\n const height = window.innerHeight;\n\n cachedEnvironment = {\n userAgent: navigator.userAgent,\n deviceType: getDeviceType(width),\n screenWidth: width,\n screenHeight: height,\n };\n}\n\nexport function getEnvironmentPayload(): EnvironmentPayload {\n if (!cachedEnvironment) initEnvironment();\n return cachedEnvironment!;\n}","import { 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 { collection, addDoc, Timestamp } from \"firebase/firestore\";\nimport { initFirebase, getDb } from \"../config/firebase\";\nimport type { Breadcrumb, BreadcrumbType, LogEntry, LogLevel, LogParams, LoggerConfig } from \"../types\";\nimport { initEnvironment } from \"./environment\";\nimport { getEnvironmentPayload } from \"./environment\";\n\nconst sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\nconst MAX_BREADCRUMBS = 50;\n\nconst CLICK_SKIP_PATTERNS = [/^:r[a-z0-9]+:$/, /^\\[object/, /^#/];\n\n// Sanitize sebelum masuk Firestore\nconst sanitizeClickText = (text: string): string => {\n return text\n .replace(/\\b[\\w.-]+@[\\w.-]+\\.\\w+\\b/g, \"[email]\")\n .replace(/\\b\\d{10,}\\b/g, \"[number]\")\n .replace(/Rp[\\s]?\\d[\\d.,]*/gi, \"[amount]\")\n .trim();\n};\n\nclass Logger {\n private config: LoggerConfig | null = null;\n private initialized = false;\n private breadcrumbs: Breadcrumb[] = [];\n\n private lastDomActivity = Date.now();\n private clickHistory = new Map<string, number[]>();\n private rageClickFired = new Map<string, number>();\n private domObserverInitialized = false;\n\n private recentDomChanges: string[] = [];\n\n private isElementDisabled(target: HTMLElement): boolean {\n return (target as HTMLButtonElement).disabled === true || target.getAttribute(\"aria-disabled\") === \"true\";\n }\n\n // Helper: cek apakah ada field invalid di form terdekat dari target\n private hasInvalidFieldNearby(target: HTMLElement): boolean {\n const form = target.closest(\"form\");\n if (!form) return false;\n return form.querySelector('[aria-invalid=\"true\"]') !== null;\n }\n\n private initDomActivityObserver(): void {\n if (this.domObserverInitialized) return;\n this.domObserverInitialized = true;\n\n const markActivity = (addedText?: string) => {\n this.lastDomActivity = Date.now();\n if (addedText) {\n this.recentDomChanges.push(addedText);\n setTimeout(() => {\n this.recentDomChanges = this.recentDomChanges.filter((t) => t !== addedText);\n }, 1000);\n }\n };\n\n const observer = new MutationObserver((mutations) => {\n markActivity();\n for (const m of mutations) {\n if (m.type === \"childList\") {\n m.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const text = (node as HTMLElement).innerText?.trim();\n if (text && text.length < 200) markActivity(text);\n }\n });\n } else if (m.type === \"characterData\") {\n const parent = m.target.parentElement;\n const text = parent?.innerText?.trim();\n if (text && text.length < 200) markActivity(text);\n }\n }\n });\n\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true });\n window.addEventListener(\"scroll\", () => markActivity(), { passive: true, capture: true });\n }\n\n init(config: LoggerConfig) {\n this.config = config;\n initFirebase(config);\n initEnvironment();\n this.initialized = true;\n console.info(\"[ga-logger] initialized —\", config.env);\n }\n setUserId(userId: string | null) {\n if (!this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n this.config.userId = userId;\n }\n\n initAnonymousUser(): void {\n if (!this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n\n const STORAGE_KEY = \"ga_anon_id\";\n\n try {\n let anonId = localStorage.getItem(STORAGE_KEY);\n\n if (!anonId) {\n anonId =\n typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\n ? crypto.randomUUID()\n : \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n\n localStorage.setItem(STORAGE_KEY, anonId);\n }\n\n const shortId = anonId.slice(-8);\n this.setUserId(`Anonymous #${shortId}`);\n } catch (e) {\n console.warn(\"[ga-logger] initAnonymousUser failed:\", e);\n this.setUserId(\"Anonymous\");\n }\n }\n\n // Add breadcrumb ke in-memory array\n addBreadcrumb(type: BreadcrumbType, message: string, data?: Record<string, unknown>) {\n const breadcrumb: Breadcrumb = {\n timestamp: new Date(),\n type,\n message,\n data,\n };\n\n this.breadcrumbs.push(breadcrumb);\n\n // Jaga memory — max 50 breadcrumbs, buang yang paling lama\n if (this.breadcrumbs.length > MAX_BREADCRUMBS) {\n this.breadcrumbs.shift();\n }\n }\n\n // Global error capture — hanya aktif di non-development\n private loggedErrorKeys = new Set<string>();\n\n // Helper: trim URL jadi domain/filename\n private trimUrl(url: string): string {\n try {\n const u = new URL(url);\n const filename = u.pathname.split(\"/\").filter(Boolean).pop() ?? u.pathname;\n return `${u.hostname}/${filename}`;\n } catch {\n // bukan valid URL, potong saja jika terlalu panjang\n return url.length > 80 ? url.slice(0, 80) + \"...\" : url;\n }\n }\n\n // Helper: build dedup key\n private buildErrorKey(type: string, message: string): string {\n return `${type}::${message}`;\n }\n\n // Helper: should skip resource error\n private shouldSkipResourceError(src: string, tagName: string): boolean {\n // Skip IMG — tidak sebabkan blank page\n if (tagName === \"IMG\") return true;\n\n // Skip base64 URL\n if (src.startsWith(\"data:\")) return true;\n\n // Skip known 3rd party trackers\n const THIRD_PARTY = [\n \"omguk.com\",\n \"track.\",\n \"doubleclick\",\n \"adservice\",\n \"googletagmanager\",\n \"google-analytics\",\n \"hotjar\",\n \"facebook.net\",\n \"analytics\",\n \"magnolia\",\n \"gtm.js\",\n ];\n if (THIRD_PARTY.some((p) => src.includes(p))) return true;\n\n return false;\n }\n\n private shouldSkipGlobalError(message: string): boolean {\n const NOISE = [\n \"CORS\",\n \"cross-origin\",\n \"ERR_FAILED\",\n \"Failed to load resource\",\n \"NetworkError\",\n \"ResizeObserver loop\",\n \"Script error\",\n \"ServiceWorker\",\n \"sw.js\",\n \"dev-sw.js\",\n \"Warning:\",\n ];\n const THIRD_PARTY = [\n \"magnolia\",\n \"gtm.js\",\n \"analytics\",\n \"googletagmanager\",\n \"facebook\",\n \"hotjar\",\n \"omguk.com\",\n \"track.\",\n \"doubleclick\",\n \"adservice\",\n ];\n return NOISE.some((p) => message.includes(p)) || THIRD_PARTY.some((p) => message.includes(p));\n }\n\n initGlobalCapture(): void {\n const isLocalhost = window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n if (isLocalhost) return;\n\n window.onerror = (message, source, lineno, colno) => {\n const msg = typeof message === \"string\" ? message : \"Unknown error\";\n if (this.shouldSkipGlobalError(msg)) return false;\n\n const trimmedSource = source ? this.trimUrl(source) : \"unknown source\";\n const finalMessage = `${msg} at ${trimmedSource}:${lineno ?? 0}`;\n const key = this.buildErrorKey(\"onerror\", finalMessage);\n if (this.loggedErrorKeys.has(key)) return false;\n this.loggedErrorKeys.add(key);\n\n void this.error({\n fnName: \"window.onerror\",\n messageError: new Error(finalMessage),\n payload: { source: trimmedSource, lineno, colno },\n });\n\n return false;\n };\n\n // unhandledrejection\n window.addEventListener(\"unhandledrejection\", (event) => {\n const msg =\n event.reason instanceof Error\n ? event.reason.message\n : String(event.reason ?? \"Unhandled Promise rejection\");\n\n if (this.shouldSkipGlobalError(msg)) return;\n\n const key = this.buildErrorKey(\"unhandledrejection\", msg);\n if (this.loggedErrorKeys.has(key)) return;\n this.loggedErrorKeys.add(key);\n\n void this.error({\n fnName: \"window.unhandledrejection\",\n messageError: new Error(msg),\n });\n });\n\n // console.error override\n const originalConsoleError = console.error.bind(console);\n console.error = (...args: unknown[]) => {\n originalConsoleError(...args);\n\n const msg = args.map((a) => (a instanceof Error ? a.message : String(a))).join(\" \");\n\n if (this.shouldSkipGlobalError(msg)) return;\n\n const trimmedMsg = msg.length > 150 ? msg.slice(0, 150) + \"...\" : msg;\n const key = this.buildErrorKey(\"console.error\", trimmedMsg);\n if (this.loggedErrorKeys.has(key)) return;\n this.loggedErrorKeys.add(key);\n\n const originalError = args.find((a): a is Error => a instanceof Error);\n\n const errorToSend = originalError ?? new Error(trimmedMsg);\n\n void this.error({\n fnName: \"console.error\",\n messageError: errorToSend,\n });\n };\n\n // resource load error (CSS / JS / font)\n window.addEventListener(\n \"error\",\n (event) => {\n const target = event.target;\n if (!(target instanceof HTMLElement)) return;\n if (target === (window as unknown)) return;\n\n const tagName = target.tagName.toUpperCase();\n\n const src =\n tagName === \"LINK\"\n ? (target as HTMLLinkElement).href\n : tagName === \"SCRIPT\"\n ? (target as HTMLScriptElement).src\n : \"\";\n\n if (this.shouldSkipResourceError(src, tagName)) return;\n\n // Deteksi dari ekstensi file, bukan tagName\n const ext = src.split(\"?\")[0].split(\".\").pop()?.toLowerCase();\n const resourceType =\n ext === \"css\"\n ? \"CSS\"\n : ext === \"js\"\n ? \"JS\"\n : ext === \"woff\" || ext === \"woff2\" || ext === \"ttf\"\n ? \"FONT\"\n : tagName;\n\n const trimmedSrc = this.trimUrl(src);\n const finalMessage = `Failed to load ${resourceType}: ${trimmedSrc}`;\n const key = this.buildErrorKey(\"resource\", finalMessage);\n if (this.loggedErrorKeys.has(key)) return;\n this.loggedErrorKeys.add(key);\n\n void this.error({\n fnName: \"resource.onerror\",\n messageError: new Error(finalMessage),\n payload: { tagName, src: trimmedSrc },\n });\n },\n true,\n );\n }\n\n private sanitizePayload(obj: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) => {\n if (value === undefined) return [key, null];\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n return [key, this.sanitizePayload(value as Record<string, unknown>)];\n }\n return [key, value];\n }),\n );\n }\n\n private sanitizeForFirestore(obj: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) => {\n if (value === undefined) return [key, null];\n if (typeof value === \"function\") return [key, \"[function]\"];\n if (value instanceof Date) return [key, value.toISOString()];\n if (Array.isArray(value)) {\n return [\n key,\n value.map((item) => {\n if (item instanceof Date) return item.toISOString();\n if (typeof item === \"function\") return \"[function]\"; // ← tambah\n if (item !== null && typeof item === \"object\") {\n return this.sanitizeForFirestore(item as Record<string, unknown>);\n }\n return item ?? null;\n }),\n ];\n }\n if (value !== null && typeof value === \"object\") {\n return [key, this.sanitizeForFirestore(value as Record<string, unknown>)];\n }\n return [key, value];\n }),\n );\n }\n\n // interceptFetch — auto breadcrumb, ke Firestore\n interceptFetch() {\n if (typeof window === \"undefined\") return;\n\n const originalFetch = window.fetch;\n\n const skipPatterns = [\n \"google.com\",\n \"googletagmanager.com\",\n \"googleapis.com\",\n \"facebook.com\",\n \"hotjar.com\",\n \"analytics\",\n \"gtm\",\n \"ccm/collect\",\n \"on.aws\",\n \"/locales/\",\n \"/sw.js\",\n \"/manifest.json\",\n \"/sourcemaps/\",\n \"magnolia\",\n ];\n\n const shouldSkipUrl = (url: string): boolean => {\n return skipPatterns.some((pattern) => url.includes(pattern));\n };\n\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\n // Skip third-party domains\n if (shouldSkipUrl(url)) {\n return originalFetch(input, init);\n }\n\n const method = init?.method ?? \"GET\";\n const startTime = Date.now();\n\n try {\n const response = await originalFetch(input, init);\n const duration = Date.now() - startTime;\n\n this.addBreadcrumb(\"fetch\", `${method} ${url}`, {\n status: response.status,\n duration: `${duration}ms`,\n });\n\n if (response.status >= 400) {\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} ${response.status}`);\n\n if (!this.loggedErrorKeys.has(dedupKey)) {\n this.loggedErrorKeys.add(dedupKey);\n\n let responseBody: unknown = null;\n try {\n const cloned = response.clone();\n const contentType = cloned.headers.get(\"content-type\") ?? \"\";\n responseBody = contentType.includes(\"application/json\")\n ? await cloned.json()\n : await cloned.text();\n } catch {\n responseBody = \"[unable to read response body]\";\n }\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n\n if (!isLocalhost) {\n this.error({\n fnName: \"interceptFetch\",\n messageError: new Error(`HTTP ${response.status} ${response.statusText} — ${url}`),\n endpointName: url,\n payload: {\n method,\n status: response.status,\n statusText: response.statusText,\n duration: `${duration}ms`,\n responseBody,\n },\n });\n }\n }\n }\n\n return response;\n } catch (networkError) {\n const duration = Date.now() - startTime;\n const error = networkError instanceof Error ? networkError : new Error(String(networkError));\n\n this.addBreadcrumb(\"fetch\", `${method} ${url} — NETWORK ERROR`, {\n duration: `${duration}ms`,\n error: error.message,\n });\n\n const dedupKey = this.buildErrorKey(\"interceptFetch\", `${method} ${url} NETWORK_ERROR`);\n\n if (!this.loggedErrorKeys.has(dedupKey)) {\n this.loggedErrorKeys.add(dedupKey);\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n if (!isLocalhost) {\n this.error({\n fnName: \"interceptFetch\",\n messageError: error,\n endpointName: url,\n payload: { method, duration: `${duration}ms` },\n });\n }\n }\n\n throw networkError;\n }\n };\n\n console.info(\"[ga-logger] fetch intercepted\");\n }\n\n // interceptXHR — auto breadcrumb + error capture untuk axios/XHR-based request\n interceptXHR(): void {\n if (typeof window === \"undefined\") return;\n\n const skipPatterns = [\n \"google.com\",\n \"googletagmanager.com\",\n \"googleapis.com\",\n \"facebook.com\",\n \"hotjar.com\",\n \"analytics\",\n \"ccm/collect\",\n \"on.aws\",\n \"/locales/\",\n \"/sw.js\",\n \"/manifest.json\",\n \"/sourcemaps/\",\n \"magnolia\",\n ];\n\n const shouldSkipUrl = (url: string): boolean => skipPatterns.some((p) => url.includes(p));\n\n const originalOpen = XMLHttpRequest.prototype.open;\n const originalSend = XMLHttpRequest.prototype.send;\n const self = this;\n\n type GaXHR = XMLHttpRequest & {\n _gaMethod?: string;\n _gaUrl?: string;\n _gaStartTime?: number;\n };\n\n XMLHttpRequest.prototype.open = function (this: GaXHR, method: string, url: string | URL, ...rest: unknown[]) {\n this._gaMethod = method;\n this._gaUrl = typeof url === \"string\" ? url : url.href;\n // @ts-expect-error - spreading native arguments\n return originalOpen.call(this, method, url, ...rest);\n };\n\n XMLHttpRequest.prototype.send = function (this: GaXHR, ...args: unknown[]) {\n const url = this._gaUrl ?? \"\";\n const method = this._gaMethod ?? \"GET\";\n\n if (shouldSkipUrl(url)) {\n // @ts-expect-error - spreading native arguments\n return originalSend.call(this, ...args);\n }\n\n this._gaStartTime = Date.now();\n\n this.addEventListener(\"loadend\", () => {\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\n const status = this.status;\n\n self.addBreadcrumb(\"fetch\", `${method} ${url}`, {\n status,\n duration: `${duration}ms`,\n });\n\n if (status >= 400) {\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} ${status}`);\n if (self.loggedErrorKeys.has(dedupKey)) return;\n self.loggedErrorKeys.add(dedupKey);\n\n let responseBody: unknown = this.responseText;\n try {\n responseBody = JSON.parse(this.responseText);\n } catch {\n // keep as raw text\n }\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n\n if (!isLocalhost) {\n void self.error({\n fnName: \"interceptXHR\",\n messageError: new Error(`HTTP ${status} ${this.statusText} — ${url}`),\n endpointName: url,\n payload: {\n method,\n status,\n statusText: this.statusText,\n duration: `${duration}ms`,\n responseBody,\n },\n });\n }\n }\n });\n\n this.addEventListener(\"error\", () => {\n // network-level failure (status selalu 0)\n const duration = Date.now() - (this._gaStartTime ?? Date.now());\n const dedupKey = self.buildErrorKey(\"interceptXHR\", `${method} ${url} NETWORK_ERROR`);\n if (self.loggedErrorKeys.has(dedupKey)) return;\n self.loggedErrorKeys.add(dedupKey);\n\n const isLocalhost =\n window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\";\n\n if (!isLocalhost) {\n void self.error({\n fnName: \"interceptXHR\",\n messageError: new Error(`Network error — ${method} ${url}`),\n endpointName: url,\n payload: { method, duration: `${duration}ms` },\n });\n }\n });\n\n // @ts-expect-error - spreading native arguments\n return originalSend.call(this, ...args);\n };\n\n console.info(\"[ga-logger] XHR intercepted\");\n }\n\n interceptClick(): void {\n if (typeof window === \"undefined\") return;\n\n if ((window as any).__ga_click_intercepted) return;\n (window as any).__ga_click_intercepted = true;\n this.initDomActivityObserver();\n\n document.addEventListener(\n \"click\",\n (event: MouseEvent) => {\n const raw = event.target as HTMLElement;\n if (!raw) return;\n\n const target = (raw.closest('button, a, [role=\"button\"], [data-log], input') as HTMLElement) ?? raw;\n\n const innerText = sanitizeClickText(target.innerText?.trim().slice(0, 50) ?? \"\");\n const id = target.id ?? \"\";\n const tagName = target.tagName?.toLowerCase() ?? \"\";\n\n const isInteractive =\n [\"button\", \"a\", \"input\"].includes(tagName) ||\n target.hasAttribute(\"data-log\") ||\n target.getAttribute(\"role\") === \"button\";\n\n if (!innerText && !id && !isInteractive) return;\n\n const message = innerText || id || `(no label: ${tagName || \"unknown\"})`;\n\n // Skip internal React/Router identifiers\n if (CLICK_SKIP_PATTERNS.some((p) => p.test(message))) return;\n\n this.addBreadcrumb(\"click\", message, {\n tagName,\n ...(id ? { id } : {}),\n ...(innerText ? { text: innerText } : {}),\n });\n\n const disabledAtClick = this.isElementDisabled(target);\n const invalidFieldAtClick = this.hasInvalidFieldNearby(target);\n // ── Dead click detection ──\n const clickTime = Date.now();\n const domActivityAtClick = this.lastDomActivity;\n const DEAD_CLICK_DELAY_MS = 4000;\n const RAGE_CLICK_WINDOW_MS = 2000;\n const RAGE_CLICK_COOLDOWN_MS = 5000;\n \n\n if ([\"button\", \"a\", \"input\"].includes(tagName)) {\n setTimeout(() => {\n if (this.lastDomActivity <= domActivityAtClick) {\n const recentFetch = this.breadcrumbs\n .filter(\n (b) =>\n b.type === \"fetch\" &&\n new Date(b.timestamp).getTime() >= clickTime &&\n new Date(b.timestamp).getTime() <= clickTime + DEAD_CLICK_DELAY_MS,\n )\n .slice(-1)[0];\n\n const reason = disabledAtClick\n ? \"button is disabled\"\n : recentFetch\n ? `recent request: ${recentFetch.message}`\n : invalidFieldAtClick\n ? \"validation error detected nearby\"\n : `no DOM/scroll activity within ${DEAD_CLICK_DELAY_MS / 1000}s`;\n\n this.addBreadcrumb(\"click\", `Dead click: ${message}`, {\n tagName,\n ...(id ? { id } : {}),\n reason,\n });\n\n void this.warn({\n fnName: \"interceptClick.deadClick\",\n messageError: new Error(`Dead click: ${message}`),\n payload: { tagName, ...(id ? { id } : {}), reason },\n excludeBreadcrumbTypes: [\"fetch\"],\n breadcrumbWindowMs: 10000,\n });\n }\n }, DEAD_CLICK_DELAY_MS);\n }\n\n // ── Rage click detection 3x klik ──\n const historyKey = `${tagName}::${message}`;\n const timestamps = (this.clickHistory.get(historyKey) ?? []).filter(\n (t) => clickTime - t < RAGE_CLICK_WINDOW_MS,\n );\n timestamps.push(clickTime);\n this.clickHistory.set(historyKey, timestamps);\n\n const lastFired = this.rageClickFired.get(historyKey) ?? 0;\n if (timestamps.length >= 3 && clickTime - lastFired > RAGE_CLICK_COOLDOWN_MS) {\n this.rageClickFired.set(historyKey, clickTime);\n\n const recentFetch = this.breadcrumbs\n .filter(\n (b) =>\n b.type === \"fetch\" &&\n clickTime - new Date(b.timestamp).getTime() <= RAGE_CLICK_WINDOW_MS,\n )\n .slice(-1)[0];\n\n const possibleReason = disabledAtClick\n ? \"button is disabled\"\n : recentFetch\n ? `recent request: ${recentFetch.message}`\n : invalidFieldAtClick\n ? \"validation error detected nearby\"\n : this.recentDomChanges.length\n ? this.recentDomChanges.slice(-1)[0]\n : \"no visible UI feedback detected\";\n\n this.addBreadcrumb(\"click\", `Loop click: ${message}`, {\n tagName,\n clickCount: timestamps.length,\n windowMs: RAGE_CLICK_WINDOW_MS,\n possibleReason,\n });\n\n void this.warn({\n fnName: \"interceptClick.rageClick\",\n messageError: new Error(`Loop click: ${message}`),\n payload: {\n tagName,\n clickCount: timestamps.length,\n windowMs: RAGE_CLICK_WINDOW_MS,\n possibleReason,\n },\n excludeBreadcrumbTypes: [\"fetch\"],\n breadcrumbWindowMs: 10000,\n });\n }\n },\n true,\n );\n }\n\n captureAction<T>(params: LogParams, fn: () => T | Promise<T>): Promise<T | undefined> {\n return Promise.resolve()\n .then(() => fn())\n .catch((error: unknown) => {\n const err = error instanceof Error ? error : new Error(String(error));\n this.send(\"error\", params.fnName, params.payload, err, params.endpointName);\n return undefined;\n });\n }\n\n withLogger<T extends (...args: any[]) => any>(\n fnName: string,\n fn: T,\n options?: {\n endpointName?: string;\n payload?: Record<string, unknown>;\n },\n ): T {\n return ((...args: Parameters<T>): ReturnType<T> | undefined => {\n try {\n const result = fn(...args);\n if (result instanceof Promise) {\n return result.catch((error: unknown) => {\n const err = error instanceof Error ? error : new Error(String(error));\n this.error({\n fnName,\n messageError: err,\n endpointName: options?.endpointName,\n payload: options?.payload,\n });\n return undefined;\n }) as ReturnType<T>;\n }\n\n return result;\n } catch (error: unknown) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.error({\n fnName,\n messageError: err,\n endpointName: options?.endpointName,\n payload: options?.payload,\n });\n return undefined;\n }\n }) as T;\n }\n\n private async send(\n level: LogLevel,\n message: string,\n payload: Record<string, unknown> = {},\n error?: Error,\n endpoint?: string,\n excludeBreadcrumbTypes?: BreadcrumbType[],\n breadcrumbWindowMs?: number,\n ) {\n if (!this.initialized || !this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n\n if (this.config.env === \"localhost\") {\n console.error(`[ga-localhost][DEV] ${message}`, { payload, error });\n return;\n }\n\n const now = Date.now();\n\n let filteredBreadcrumbs = excludeBreadcrumbTypes?.length\n ? this.breadcrumbs.filter((b) => !excludeBreadcrumbTypes.includes(b.type))\n : this.breadcrumbs;\n\n if (breadcrumbWindowMs) {\n filteredBreadcrumbs = filteredBreadcrumbs.filter(\n (b) => now - new Date(b.timestamp).getTime() <= breadcrumbWindowMs,\n );\n }\n\n const entry: LogEntry = {\n timestamp: new Date(),\n level,\n message,\n env: this.config.env,\n userId: this.config.userId ?? null,\n sessionId,\n page: typeof window !== \"undefined\" ? window.location.pathname : \"\",\n stackTrace: error?.stack ?? null,\n payload: this.sanitizePayload(payload),\n repo: this.config.repo,\n endpoint: endpoint ?? null,\n breadcrumbs: [...filteredBreadcrumbs],\n ...getEnvironmentPayload(),\n };\n\n try {\n const db = getDb();\n const safeEntry = this.sanitizeForFirestore(entry as unknown as Record<string, unknown>);\n await addDoc(collection(db, \"logs\"), {\n ...safeEntry,\n timestamp: Timestamp.fromDate(entry.timestamp),\n });\n } catch (e) {\n console.error(\"[ga-logger] failed to send log:\", e);\n }\n }\n\n info(params: LogParams) {\n const { fnName, messageError, payload, endpointName } = params;\n return this.send(\"info\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\n }\n\n warn(params: LogParams) {\n const { fnName, messageError, payload, endpointName, excludeBreadcrumbTypes, breadcrumbWindowMs } = params;\n return this.send(\n \"warn\",\n messageError?.message ?? fnName,\n { ...payload, fnName },\n messageError,\n endpointName,\n excludeBreadcrumbTypes,\n breadcrumbWindowMs,\n );\n }\n\n error(params: LogParams) {\n const { fnName, messageError, payload, endpointName } = params;\n return this.send(\"error\", messageError?.message ?? fnName, { ...payload, fnName }, messageError, endpointName);\n }\n}\n\nexport const logger = new Logger();\n\n\n\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"]}
|