@ga-components/logger 1.0.13 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -157,12 +157,14 @@ var Logger = class {
157
157
  return Object.fromEntries(
158
158
  Object.entries(obj).map(([key, value]) => {
159
159
  if (value === void 0) return [key, null];
160
+ if (typeof value === "function") return [key, "[function]"];
160
161
  if (value instanceof Date) return [key, value.toISOString()];
161
162
  if (Array.isArray(value)) {
162
163
  return [
163
164
  key,
164
165
  value.map((item) => {
165
166
  if (item instanceof Date) return item.toISOString();
167
+ if (typeof item === "function") return "[function]";
166
168
  if (item !== null && typeof item === "object") {
167
169
  return this.sanitizeForFirestore(item);
168
170
  }
@@ -177,24 +179,39 @@ var Logger = class {
177
179
  })
178
180
  );
179
181
  }
180
- // NEW: interceptFetch — auto breadcrumb, skip 4xx/5xx ke Firestore
182
+ // NEW: interceptFetch — auto breadcrumb, skip 4xx/5xx ke Firestore
181
183
  interceptFetch() {
182
184
  if (typeof window === "undefined") return;
183
185
  const originalFetch = window.fetch;
186
+ const skipDomains = [
187
+ "google.com",
188
+ "googletagmanager.com",
189
+ "googleapis.com",
190
+ "facebook.com",
191
+ "hotjar.com",
192
+ "analytics",
193
+ "gtm",
194
+ "ccm/collect"
195
+ ];
196
+ const shouldSkipUrl = (url) => {
197
+ return skipDomains.some((domain) => url.includes(domain));
198
+ };
184
199
  window.fetch = async (input, init) => {
185
200
  const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
201
+ if (shouldSkipUrl(url)) {
202
+ return originalFetch(input, init);
203
+ }
186
204
  const method = init?.method ?? "GET";
187
205
  const startTime = Date.now();
188
206
  try {
189
207
  const response = await originalFetch(input, init);
190
208
  const duration = Date.now() - startTime;
191
- const status = response.status;
192
209
  this.addBreadcrumb("fetch", `${method} ${url}`, {
193
- status,
210
+ status: response.status,
194
211
  duration: `${duration}ms`
195
212
  });
196
- if (status >= 400) {
197
- console.warn(`[ga-logger] HTTP ${status} skipped \u2014 Kibana handles this`, url);
213
+ if (response.status >= 400) {
214
+ console.warn(`[ga-logger] HTTP ${response.status} skipped \u2014 Kibana handles this`, url);
198
215
  }
199
216
  return response;
200
217
  } catch (networkError) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/logger.ts","../src/config/firebase.ts","../src/components/LoggerErrorBoundary.tsx"],"sourcesContent":["export { logger } from \"./core/logger\";\nexport { LoggerErrorBoundary } from \"./components/LoggerErrorBoundary\"; // ← baru\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\";\n\nconst sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\nconst MAX_BREADCRUMBS = 50;\n\nclass Logger {\n private config: LoggerConfig | null = null;\n private initialized = false;\n private breadcrumbs: Breadcrumb[] = [];\n\n init(config: LoggerConfig) {\n this.config = config;\n initFirebase(config);\n this.initialized = true;\n console.info(\"[ga-logger] initialized —\", config.env);\n }\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 // ✅ NEW: 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 // ✅ NEW: Global error capture — hanya aktif di non-development\n initGlobalCapture() {\n if (!this.config || this.config.env === \"DEV\") {\n console.info(\"[ga-logger] global capture skipped — DEV environment\");\n return;\n }\n\n // Capture JS runtime error\n window.onerror = (message, source, lineno, colno, error) => {\n if (this.shouldSkipGlobalError(message?.toString(), source)) return false;\n\n this.addBreadcrumb(\"error\", `window.onerror: ${message}`, {\n source,\n lineno,\n colno,\n });\n\n this.error({\n fnName: \"window.onerror\",\n messageError: error ?? new Error(String(message)),\n payload: { source, lineno, colno },\n });\n\n return false;\n };\n\n // Capture unhandled Promise rejection\n window.addEventListener(\"unhandledrejection\", (event) => {\n const message = event.reason?.message ?? String(event.reason);\n\n if (this.shouldSkipGlobalError(message)) return;\n\n const error = event.reason instanceof Error ? event.reason : new Error(message);\n\n this.addBreadcrumb(\"error\", `unhandledrejection: ${message}`);\n\n this.error({\n fnName: \"window.unhandledrejection\",\n messageError: error,\n });\n });\n\n console.info(\"[ga-logger] global capture active\");\n }\n\n // ✅ NEW: Smart filter — skip noise dari CORS, network, third-party\n private shouldSkipGlobalError(message?: string, source?: string): boolean {\n if (!message) return true;\n\n const noisePatterns = [\n \"CORS\",\n \"cross-origin\",\n \"ERR_FAILED\",\n \"Failed to load resource\",\n \"NetworkError\",\n \"ResizeObserver loop\",\n \"Script error\",\n ];\n\n const isNoise = noisePatterns.some((pattern) => message.toLowerCase().includes(pattern.toLowerCase()));\n if (isNoise) return true;\n\n // Skip error dari third-party source\n if (source) {\n const thirdPartyPatterns = [\"magnolia\", \"gtm.js\", \"analytics\", \"googletagmanager\", \"facebook\", \"hotjar\"];\n const isThirdParty = thirdPartyPatterns.some((pattern) => source.includes(pattern));\n if (isThirdParty) return true;\n }\n\n return false;\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 // ← tambah: Date object harus convert ke ISO string\n if (value instanceof Date) return [key, value.toISOString()];\n\n if (Array.isArray(value)) {\n return [\n key,\n value.map((item) => {\n if (item instanceof Date) return item.toISOString();\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 // ✅ NEW: interceptFetch — auto breadcrumb, skip 4xx/5xx ke Firestore\n interceptFetch() {\n if (typeof window === \"undefined\") return;\n\n const originalFetch = window.fetch;\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 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 const status = response.status;\n\n // Selalu add breadcrumb untuk semua fetch\n this.addBreadcrumb(\"fetch\", `${method} ${url}`, {\n status,\n duration: `${duration}ms`,\n });\n\n // 4xx/5xx → skip logger.error, Kibana sudah handle\n if (status >= 400) {\n console.warn(`[ga-logger] HTTP ${status} skipped — Kibana handles this`, url);\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 // Network failure → Kibana tidak tahu, kita log!\n this.addBreadcrumb(\"fetch\", `${method} ${url} — NETWORK ERROR`, {\n duration: `${duration}ms`,\n error: error.message,\n });\n\n // Hanya log jika bukan DEV\n if (this.config?.env !== \"DEV\") {\n this.error({\n fnName: \"interceptFetch\",\n messageError: error,\n endpointName: url,\n payload: { method, duration: `${duration}ms` },\n });\n }\n\n throw networkError;\n }\n };\n\n console.info(\"[ga-logger] fetch intercepted\");\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 private async send(\n level: LogLevel,\n message: string,\n payload: Record<string, unknown> = {},\n error?: Error,\n endpoint?: string,\n ) {\n if (!this.initialized || !this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n\n // DEV → console only, skip Firestore\n if (this.config.env === \"DEV\") {\n console.error(`[ga-logger][DEV] ${message}`, { payload, error });\n return;\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: [...this.breadcrumbs], // ← snapshot saat error terjadi\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, payload, endpointName } = params;\n return this.send(\"info\", fnName, payload, undefined, endpointName);\n }\n\n warn(params: LogParams) {\n const { fnName, payload, endpointName } = params;\n return this.send(\"warn\", fnName, payload, undefined, endpointName);\n }\n\n error(params: LogParams) {\n const { fnName, messageError, payload, endpointName } = params;\n return this.send(\"error\", fnName, payload, messageError, endpointName);\n }\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","import { Component, type ErrorInfo, type ReactNode } from \"react\";\nimport { logger } from \"../core/logger\";\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode; // ← optional custom UI saat crash\n}\n\ninterface State {\n hasError: boolean;\n}\n\nexport class LoggerErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(): State {\n return { hasError: true };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n logger.addBreadcrumb(\"error\", `React crash: ${error.message}`, {\n componentStack: info.componentStack ?? undefined,\n });\n\n logger.error({\n fnName: \"LoggerErrorBoundary\",\n messageError: error,\n payload: {\n componentStack: info.componentStack ?? undefined,\n },\n });\n }\n\n render() {\n if (this.state.hasError) {\n return this.props.fallback ?? (\n <div style={{ padding: \"1rem\", color: \"red\" }}>\n Something went wrong.\n </div>\n );\n }\n\n return this.props.children;\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,oBAA8C;;;ACA9C,iBAA+C;AAC/C,uBAA6B;AAI7B,IAAM,WAAW;AACjB,IAAI,KAAuB;AAEpB,IAAM,eAAe,CAAC,WAAyB;AAElD,QAAM,kBAAc,oBAAQ,EAAE,KAAK,CAACC,SAAQA,KAAI,SAAS,QAAQ;AAEjE,QAAM,MACF,mBACA;AAAA,IACI;AAAA,MACI,QAAQ,OAAO;AAAA,MACf,YAAY,OAAO;AAAA,MACnB,WAAW,OAAO;AAAA,MAClB,eAAe,OAAO;AAAA,MACtB,mBAAmB,OAAO;AAAA,MAC1B,OAAO,OAAO;AAAA,IAClB;AAAA,IACA;AAAA,EACJ;AAEJ,WAAK,+BAAa,GAAG;AACrB,SAAO;AACX;AAEO,IAAM,QAAQ,MAAiB;AAClC,MAAI,CAAC,GAAI,OAAM,IAAI,MAAM,iEAAiE;AAC1F,SAAO;AACX;;;AD7BA,IAAM,YAAY,QAAQ,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC9E,IAAM,kBAAkB;AAExB,IAAM,SAAN,MAAa;AAAA,EAAb;AACI,SAAQ,SAA8B;AACtC,SAAQ,cAAc;AACtB,SAAQ,cAA4B,CAAC;AAAA;AAAA,EAErC,KAAK,QAAsB;AACvB,SAAK,SAAS;AACd,iBAAa,MAAM;AACnB,SAAK,cAAc;AACnB,YAAQ,KAAK,kCAA6B,OAAO,GAAG;AAAA,EACxD;AAAA,EAEA,UAAU,QAAuB;AAC7B,QAAI,CAAC,KAAK,QAAQ;AACd,cAAQ,KAAK,wDAAwD;AACrE;AAAA,IACJ;AACA,SAAK,OAAO,SAAS;AAAA,EACzB;AAAA;AAAA,EAGA,cAAc,MAAsB,SAAiB,MAAgC;AACjF,UAAM,aAAyB;AAAA,MAC3B,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,SAAK,YAAY,KAAK,UAAU;AAGhC,QAAI,KAAK,YAAY,SAAS,iBAAiB;AAC3C,WAAK,YAAY,MAAM;AAAA,IAC3B;AAAA,EACJ;AAAA;AAAA,EAGA,oBAAoB;AAChB,QAAI,CAAC,KAAK,UAAU,KAAK,OAAO,QAAQ,OAAO;AAC3C,cAAQ,KAAK,2DAAsD;AACnE;AAAA,IACJ;AAGA,WAAO,UAAU,CAAC,SAAS,QAAQ,QAAQ,OAAO,UAAU;AACxD,UAAI,KAAK,sBAAsB,SAAS,SAAS,GAAG,MAAM,EAAG,QAAO;AAEpE,WAAK,cAAc,SAAS,mBAAmB,OAAO,IAAI;AAAA,QACtD;AAAA,QACA;AAAA,QACA;AAAA,MACJ,CAAC;AAED,WAAK,MAAM;AAAA,QACP,QAAQ;AAAA,QACR,cAAc,SAAS,IAAI,MAAM,OAAO,OAAO,CAAC;AAAA,QAChD,SAAS,EAAE,QAAQ,QAAQ,MAAM;AAAA,MACrC,CAAC;AAED,aAAO;AAAA,IACX;AAGA,WAAO,iBAAiB,sBAAsB,CAAC,UAAU;AACrD,YAAM,UAAU,MAAM,QAAQ,WAAW,OAAO,MAAM,MAAM;AAE5D,UAAI,KAAK,sBAAsB,OAAO,EAAG;AAEzC,YAAM,QAAQ,MAAM,kBAAkB,QAAQ,MAAM,SAAS,IAAI,MAAM,OAAO;AAE9E,WAAK,cAAc,SAAS,uBAAuB,OAAO,EAAE;AAE5D,WAAK,MAAM;AAAA,QACP,QAAQ;AAAA,QACR,cAAc;AAAA,MAClB,CAAC;AAAA,IACL,CAAC;AAED,YAAQ,KAAK,mCAAmC;AAAA,EACpD;AAAA;AAAA,EAGQ,sBAAsB,SAAkB,QAA0B;AACtE,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,gBAAgB;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,UAAM,UAAU,cAAc,KAAK,CAAC,YAAY,QAAQ,YAAY,EAAE,SAAS,QAAQ,YAAY,CAAC,CAAC;AACrG,QAAI,QAAS,QAAO;AAGpB,QAAI,QAAQ;AACR,YAAM,qBAAqB,CAAC,YAAY,UAAU,aAAa,oBAAoB,YAAY,QAAQ;AACvG,YAAM,eAAe,mBAAmB,KAAK,CAAC,YAAY,OAAO,SAAS,OAAO,CAAC;AAClF,UAAI,aAAc,QAAO;AAAA,IAC7B;AAEA,WAAO;AAAA,EACX;AAAA,EAEQ,gBAAgB,KAAuD;AAC3E,WAAO,OAAO;AAAA,MACV,OAAO,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACtC,YAAI,UAAU,OAAW,QAAO,CAAC,KAAK,IAAI;AAC1C,YAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACtE,iBAAO,CAAC,KAAK,KAAK,gBAAgB,KAAgC,CAAC;AAAA,QACvE;AACA,eAAO,CAAC,KAAK,KAAK;AAAA,MACtB,CAAC;AAAA,IACL;AAAA,EACJ;AAAA,EAEQ,qBAAqB,KAAuD;AAChF,WAAO,OAAO;AAAA,MACV,OAAO,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACtC,YAAI,UAAU,OAAW,QAAO,CAAC,KAAK,IAAI;AAE1C,YAAI,iBAAiB,KAAM,QAAO,CAAC,KAAK,MAAM,YAAY,CAAC;AAE3D,YAAI,MAAM,QAAQ,KAAK,GAAG;AACtB,iBAAO;AAAA,YACH;AAAA,YACA,MAAM,IAAI,CAAC,SAAS;AAChB,kBAAI,gBAAgB,KAAM,QAAO,KAAK,YAAY;AAClD,kBAAI,SAAS,QAAQ,OAAO,SAAS,UAAU;AAC3C,uBAAO,KAAK,qBAAqB,IAA+B;AAAA,cACpE;AACA,qBAAO,QAAQ;AAAA,YACnB,CAAC;AAAA,UACL;AAAA,QACJ;AACA,YAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC7C,iBAAO,CAAC,KAAK,KAAK,qBAAqB,KAAgC,CAAC;AAAA,QAC5E;AACA,eAAO,CAAC,KAAK,KAAK;AAAA,MACtB,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA,EAGA,iBAAiB;AACb,QAAI,OAAO,WAAW,YAAa;AAEnC,UAAM,gBAAgB,OAAO;AAE7B,WAAO,QAAQ,OAAO,OAA0B,SAA0C;AACtF,YAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,OAAO,MAAM;AAE1F,YAAM,SAAS,MAAM,UAAU;AAC/B,YAAM,YAAY,KAAK,IAAI;AAE3B,UAAI;AACA,cAAM,WAAW,MAAM,cAAc,OAAO,IAAI;AAChD,cAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,cAAM,SAAS,SAAS;AAGxB,aAAK,cAAc,SAAS,GAAG,MAAM,IAAI,GAAG,IAAI;AAAA,UAC5C;AAAA,UACA,UAAU,GAAG,QAAQ;AAAA,QACzB,CAAC;AAGD,YAAI,UAAU,KAAK;AACf,kBAAQ,KAAK,oBAAoB,MAAM,uCAAkC,GAAG;AAAA,QAChF;AAEA,eAAO;AAAA,MACX,SAAS,cAAc;AACnB,cAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,cAAM,QAAQ,wBAAwB,QAAQ,eAAe,IAAI,MAAM,OAAO,YAAY,CAAC;AAG3F,aAAK,cAAc,SAAS,GAAG,MAAM,IAAI,GAAG,yBAAoB;AAAA,UAC5D,UAAU,GAAG,QAAQ;AAAA,UACrB,OAAO,MAAM;AAAA,QACjB,CAAC;AAGD,YAAI,KAAK,QAAQ,QAAQ,OAAO;AAC5B,eAAK,MAAM;AAAA,YACP,QAAQ;AAAA,YACR,cAAc;AAAA,YACd,cAAc;AAAA,YACd,SAAS,EAAE,QAAQ,UAAU,GAAG,QAAQ,KAAK;AAAA,UACjD,CAAC;AAAA,QACL;AAEA,cAAM;AAAA,MACV;AAAA,IACJ;AAEA,YAAQ,KAAK,+BAA+B;AAAA,EAChD;AAAA,EAEA,cAAiB,QAAmB,IAAkD;AAClF,WAAO,QAAQ,QAAQ,EAClB,KAAK,MAAM,GAAG,CAAC,EACf,MAAM,CAAC,UAAmB;AACvB,YAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,WAAK,KAAK,SAAS,OAAO,QAAQ,OAAO,SAAS,KAAK,OAAO,YAAY;AAC1E,aAAO;AAAA,IACX,CAAC;AAAA,EACT;AAAA,EAEA,MAAc,KACV,OACA,SACA,UAAmC,CAAC,GACpC,OACA,UACF;AACE,QAAI,CAAC,KAAK,eAAe,CAAC,KAAK,QAAQ;AACnC,cAAQ,KAAK,wDAAwD;AACrE;AAAA,IACJ;AAGA,QAAI,KAAK,OAAO,QAAQ,OAAO;AAC3B,cAAQ,MAAM,oBAAoB,OAAO,IAAI,EAAE,SAAS,MAAM,CAAC;AAC/D;AAAA,IACJ;AAEA,UAAM,QAAkB;AAAA,MACpB,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA,KAAK,KAAK,OAAO;AAAA,MACjB,QAAQ,KAAK,OAAO,UAAU;AAAA,MAC9B;AAAA,MACA,MAAM,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAAA,MACjE,YAAY,OAAO,SAAS;AAAA,MAC5B,SAAS,KAAK,gBAAgB,OAAO;AAAA,MACrC,MAAM,KAAK,OAAO;AAAA,MAClB,UAAU,YAAY;AAAA,MACtB,aAAa,CAAC,GAAG,KAAK,WAAW;AAAA;AAAA,IACrC;AAEA,QAAI;AACA,YAAMC,MAAK,MAAM;AACjB,YAAM,YAAY,KAAK,qBAAqB,KAA2C;AACvF,gBAAM,8BAAO,8BAAWA,KAAI,MAAM,GAAG;AAAA,QACjC,GAAG;AAAA,QACH,WAAW,4BAAU,SAAS,MAAM,SAAS;AAAA,MACjD,CAAC;AAAA,IACL,SAAS,GAAG;AACR,cAAQ,MAAM,mCAAmC,CAAC;AAAA,IACtD;AAAA,EACJ;AAAA,EAEA,KAAK,QAAmB;AACpB,UAAM,EAAE,QAAQ,SAAS,aAAa,IAAI;AAC1C,WAAO,KAAK,KAAK,QAAQ,QAAQ,SAAS,QAAW,YAAY;AAAA,EACrE;AAAA,EAEA,KAAK,QAAmB;AACpB,UAAM,EAAE,QAAQ,SAAS,aAAa,IAAI;AAC1C,WAAO,KAAK,KAAK,QAAQ,QAAQ,SAAS,QAAW,YAAY;AAAA,EACrE;AAAA,EAEA,MAAM,QAAmB;AACrB,UAAM,EAAE,QAAQ,cAAc,SAAS,aAAa,IAAI;AACxD,WAAO,KAAK,KAAK,SAAS,QAAQ,SAAS,cAAc,YAAY;AAAA,EACzE;AACJ;AAGO,IAAM,SAAS,IAAI,OAAO;;;AE3RjC,mBAA0D;AAuC1C;AA3BT,IAAM,sBAAN,cAAkC,uBAAwB;AAAA,EAC7D,YAAY,OAAc;AACtB,UAAM,KAAK;AACX,SAAK,QAAQ,EAAE,UAAU,MAAM;AAAA,EACnC;AAAA,EAEA,OAAO,2BAAkC;AACrC,WAAO,EAAE,UAAU,KAAK;AAAA,EAC5B;AAAA,EAEA,kBAAkB,OAAc,MAAiB;AAC7C,WAAO,cAAc,SAAS,gBAAgB,MAAM,OAAO,IAAI;AAAA,MAC3D,gBAAgB,KAAK,kBAAkB;AAAA,IAC3C,CAAC;AAED,WAAO,MAAM;AAAA,MACT,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,SAAS;AAAA,QACL,gBAAgB,KAAK,kBAAkB;AAAA,MAC3C;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EAEA,SAAS;AACL,QAAI,KAAK,MAAM,UAAU;AACrB,aAAO,KAAK,MAAM,YACd,4CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,OAAO,MAAM,GAAG,mCAE/C;AAAA,IAER;AAEA,WAAO,KAAK,MAAM;AAAA,EACtB;AACJ;","names":["import_firestore","app","db"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core/logger.ts","../src/config/firebase.ts","../src/components/LoggerErrorBoundary.tsx"],"sourcesContent":["export { logger } from \"./core/logger\";\nexport { LoggerErrorBoundary } from \"./components/LoggerErrorBoundary\"; // ← baru\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\";\n\nconst sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\nconst MAX_BREADCRUMBS = 50;\n\nclass Logger {\n private config: LoggerConfig | null = null;\n private initialized = false;\n private breadcrumbs: Breadcrumb[] = [];\n\n init(config: LoggerConfig) {\n this.config = config;\n initFirebase(config);\n this.initialized = true;\n console.info(\"[ga-logger] initialized —\", config.env);\n }\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 // ✅ NEW: 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 // ✅ NEW: Global error capture — hanya aktif di non-development\n initGlobalCapture() {\n if (!this.config || this.config.env === \"DEV\") {\n console.info(\"[ga-logger] global capture skipped — DEV environment\");\n return;\n }\n\n // Capture JS runtime error\n window.onerror = (message, source, lineno, colno, error) => {\n if (this.shouldSkipGlobalError(message?.toString(), source)) return false;\n\n this.addBreadcrumb(\"error\", `window.onerror: ${message}`, {\n source,\n lineno,\n colno,\n });\n\n this.error({\n fnName: \"window.onerror\",\n messageError: error ?? new Error(String(message)),\n payload: { source, lineno, colno },\n });\n\n return false;\n };\n\n // Capture unhandled Promise rejection\n window.addEventListener(\"unhandledrejection\", (event) => {\n const message = event.reason?.message ?? String(event.reason);\n\n if (this.shouldSkipGlobalError(message)) return;\n\n const error = event.reason instanceof Error ? event.reason : new Error(message);\n\n this.addBreadcrumb(\"error\", `unhandledrejection: ${message}`);\n\n this.error({\n fnName: \"window.unhandledrejection\",\n messageError: error,\n });\n });\n\n console.info(\"[ga-logger] global capture active\");\n }\n\n // ✅ NEW: Smart filter — skip noise dari CORS, network, third-party\n private shouldSkipGlobalError(message?: string, source?: string): boolean {\n if (!message) return true;\n\n const noisePatterns = [\n \"CORS\",\n \"cross-origin\",\n \"ERR_FAILED\",\n \"Failed to load resource\",\n \"NetworkError\",\n \"ResizeObserver loop\",\n \"Script error\",\n ];\n\n const isNoise = noisePatterns.some((pattern) => message.toLowerCase().includes(pattern.toLowerCase()));\n if (isNoise) return true;\n\n // Skip error dari third-party source\n if (source) {\n const thirdPartyPatterns = [\"magnolia\", \"gtm.js\", \"analytics\", \"googletagmanager\", \"facebook\", \"hotjar\"];\n const isThirdParty = thirdPartyPatterns.some((pattern) => source.includes(pattern));\n if (isThirdParty) return true;\n }\n\n return false;\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 // NEW: interceptFetch — auto breadcrumb, skip 4xx/5xx ke Firestore\n interceptFetch() {\n if (typeof window === \"undefined\") return;\n\n const originalFetch = window.fetch;\n\n // Domain yang di-skip dari breadcrumb\n const skipDomains = [\n \"google.com\",\n \"googletagmanager.com\",\n \"googleapis.com\",\n \"facebook.com\",\n \"hotjar.com\",\n \"analytics\",\n \"gtm\",\n \"ccm/collect\",\n ];\n\n const shouldSkipUrl = (url: string): boolean => {\n return skipDomains.some((domain) => url.includes(domain));\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 console.warn(`[ga-logger] HTTP ${response.status} skipped — Kibana handles this`, url);\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 if (this.config?.env !== \"DEV\") {\n this.error({\n fnName: \"interceptFetch\",\n messageError: error,\n endpointName: url,\n payload: { method, duration: `${duration}ms` },\n });\n }\n\n throw networkError;\n }\n };\n\n console.info(\"[ga-logger] fetch intercepted\");\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 private async send(\n level: LogLevel,\n message: string,\n payload: Record<string, unknown> = {},\n error?: Error,\n endpoint?: string,\n ) {\n if (!this.initialized || !this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n\n // DEV → console only, skip Firestore\n if (this.config.env === \"DEV\") {\n console.error(`[ga-logger][DEV] ${message}`, { payload, error });\n return;\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: [...this.breadcrumbs], // ← snapshot saat error terjadi\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, payload, endpointName } = params;\n return this.send(\"info\", fnName, payload, undefined, endpointName);\n }\n\n warn(params: LogParams) {\n const { fnName, payload, endpointName } = params;\n return this.send(\"warn\", fnName, payload, undefined, endpointName);\n }\n\n error(params: LogParams) {\n const { fnName, messageError, payload, endpointName } = params;\n return this.send(\"error\", fnName, payload, messageError, endpointName);\n }\n}\n\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","import { Component, type ErrorInfo, type ReactNode } from \"react\";\nimport { logger } from \"../core/logger\";\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode; // ← optional custom UI saat crash\n}\n\ninterface State {\n hasError: boolean;\n}\n\nexport class LoggerErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(): State {\n return { hasError: true };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n logger.addBreadcrumb(\"error\", `React crash: ${error.message}`, {\n componentStack: info.componentStack ?? undefined,\n });\n\n logger.error({\n fnName: \"LoggerErrorBoundary\",\n messageError: error,\n payload: {\n componentStack: info.componentStack ?? undefined,\n },\n });\n }\n\n render() {\n if (this.state.hasError) {\n return this.props.fallback ?? (\n <div style={{ padding: \"1rem\", color: \"red\" }}>\n Something went wrong.\n </div>\n );\n }\n\n return this.props.children;\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,oBAA8C;;;ACA9C,iBAA+C;AAC/C,uBAA6B;AAI7B,IAAM,WAAW;AACjB,IAAI,KAAuB;AAEpB,IAAM,eAAe,CAAC,WAAyB;AAElD,QAAM,kBAAc,oBAAQ,EAAE,KAAK,CAACC,SAAQA,KAAI,SAAS,QAAQ;AAEjE,QAAM,MACF,mBACA;AAAA,IACI;AAAA,MACI,QAAQ,OAAO;AAAA,MACf,YAAY,OAAO;AAAA,MACnB,WAAW,OAAO;AAAA,MAClB,eAAe,OAAO;AAAA,MACtB,mBAAmB,OAAO;AAAA,MAC1B,OAAO,OAAO;AAAA,IAClB;AAAA,IACA;AAAA,EACJ;AAEJ,WAAK,+BAAa,GAAG;AACrB,SAAO;AACX;AAEO,IAAM,QAAQ,MAAiB;AAClC,MAAI,CAAC,GAAI,OAAM,IAAI,MAAM,iEAAiE;AAC1F,SAAO;AACX;;;AD7BA,IAAM,YAAY,QAAQ,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC9E,IAAM,kBAAkB;AAExB,IAAM,SAAN,MAAa;AAAA,EAAb;AACI,SAAQ,SAA8B;AACtC,SAAQ,cAAc;AACtB,SAAQ,cAA4B,CAAC;AAAA;AAAA,EAErC,KAAK,QAAsB;AACvB,SAAK,SAAS;AACd,iBAAa,MAAM;AACnB,SAAK,cAAc;AACnB,YAAQ,KAAK,kCAA6B,OAAO,GAAG;AAAA,EACxD;AAAA,EAEA,UAAU,QAAuB;AAC7B,QAAI,CAAC,KAAK,QAAQ;AACd,cAAQ,KAAK,wDAAwD;AACrE;AAAA,IACJ;AACA,SAAK,OAAO,SAAS;AAAA,EACzB;AAAA;AAAA,EAGA,cAAc,MAAsB,SAAiB,MAAgC;AACjF,UAAM,aAAyB;AAAA,MAC3B,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,SAAK,YAAY,KAAK,UAAU;AAGhC,QAAI,KAAK,YAAY,SAAS,iBAAiB;AAC3C,WAAK,YAAY,MAAM;AAAA,IAC3B;AAAA,EACJ;AAAA;AAAA,EAGA,oBAAoB;AAChB,QAAI,CAAC,KAAK,UAAU,KAAK,OAAO,QAAQ,OAAO;AAC3C,cAAQ,KAAK,2DAAsD;AACnE;AAAA,IACJ;AAGA,WAAO,UAAU,CAAC,SAAS,QAAQ,QAAQ,OAAO,UAAU;AACxD,UAAI,KAAK,sBAAsB,SAAS,SAAS,GAAG,MAAM,EAAG,QAAO;AAEpE,WAAK,cAAc,SAAS,mBAAmB,OAAO,IAAI;AAAA,QACtD;AAAA,QACA;AAAA,QACA;AAAA,MACJ,CAAC;AAED,WAAK,MAAM;AAAA,QACP,QAAQ;AAAA,QACR,cAAc,SAAS,IAAI,MAAM,OAAO,OAAO,CAAC;AAAA,QAChD,SAAS,EAAE,QAAQ,QAAQ,MAAM;AAAA,MACrC,CAAC;AAED,aAAO;AAAA,IACX;AAGA,WAAO,iBAAiB,sBAAsB,CAAC,UAAU;AACrD,YAAM,UAAU,MAAM,QAAQ,WAAW,OAAO,MAAM,MAAM;AAE5D,UAAI,KAAK,sBAAsB,OAAO,EAAG;AAEzC,YAAM,QAAQ,MAAM,kBAAkB,QAAQ,MAAM,SAAS,IAAI,MAAM,OAAO;AAE9E,WAAK,cAAc,SAAS,uBAAuB,OAAO,EAAE;AAE5D,WAAK,MAAM;AAAA,QACP,QAAQ;AAAA,QACR,cAAc;AAAA,MAClB,CAAC;AAAA,IACL,CAAC;AAED,YAAQ,KAAK,mCAAmC;AAAA,EACpD;AAAA;AAAA,EAGQ,sBAAsB,SAAkB,QAA0B;AACtE,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,gBAAgB;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,UAAM,UAAU,cAAc,KAAK,CAAC,YAAY,QAAQ,YAAY,EAAE,SAAS,QAAQ,YAAY,CAAC,CAAC;AACrG,QAAI,QAAS,QAAO;AAGpB,QAAI,QAAQ;AACR,YAAM,qBAAqB,CAAC,YAAY,UAAU,aAAa,oBAAoB,YAAY,QAAQ;AACvG,YAAM,eAAe,mBAAmB,KAAK,CAAC,YAAY,OAAO,SAAS,OAAO,CAAC;AAClF,UAAI,aAAc,QAAO;AAAA,IAC7B;AAEA,WAAO;AAAA,EACX;AAAA,EAEQ,gBAAgB,KAAuD;AAC3E,WAAO,OAAO;AAAA,MACV,OAAO,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACtC,YAAI,UAAU,OAAW,QAAO,CAAC,KAAK,IAAI;AAC1C,YAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACtE,iBAAO,CAAC,KAAK,KAAK,gBAAgB,KAAgC,CAAC;AAAA,QACvE;AACA,eAAO,CAAC,KAAK,KAAK;AAAA,MACtB,CAAC;AAAA,IACL;AAAA,EACJ;AAAA,EAEQ,qBAAqB,KAAuD;AAChF,WAAO,OAAO;AAAA,MACV,OAAO,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACtC,YAAI,UAAU,OAAW,QAAO,CAAC,KAAK,IAAI;AAC1C,YAAI,OAAO,UAAU,WAAY,QAAO,CAAC,KAAK,YAAY;AAC1D,YAAI,iBAAiB,KAAM,QAAO,CAAC,KAAK,MAAM,YAAY,CAAC;AAC3D,YAAI,MAAM,QAAQ,KAAK,GAAG;AACtB,iBAAO;AAAA,YACH;AAAA,YACA,MAAM,IAAI,CAAC,SAAS;AAChB,kBAAI,gBAAgB,KAAM,QAAO,KAAK,YAAY;AAClD,kBAAI,OAAO,SAAS,WAAY,QAAO;AACvC,kBAAI,SAAS,QAAQ,OAAO,SAAS,UAAU;AAC3C,uBAAO,KAAK,qBAAqB,IAA+B;AAAA,cACpE;AACA,qBAAO,QAAQ;AAAA,YACnB,CAAC;AAAA,UACL;AAAA,QACJ;AACA,YAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC7C,iBAAO,CAAC,KAAK,KAAK,qBAAqB,KAAgC,CAAC;AAAA,QAC5E;AACA,eAAO,CAAC,KAAK,KAAK;AAAA,MACtB,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA,EAGA,iBAAiB;AACb,QAAI,OAAO,WAAW,YAAa;AAEnC,UAAM,gBAAgB,OAAO;AAG7B,UAAM,cAAc;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAEA,UAAM,gBAAgB,CAAC,QAAyB;AAC5C,aAAO,YAAY,KAAK,CAAC,WAAW,IAAI,SAAS,MAAM,CAAC;AAAA,IAC5D;AAEA,WAAO,QAAQ,OAAO,OAA0B,SAA0C;AACtF,YAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,OAAO,MAAM;AAG1F,UAAI,cAAc,GAAG,GAAG;AACpB,eAAO,cAAc,OAAO,IAAI;AAAA,MACpC;AAEA,YAAM,SAAS,MAAM,UAAU;AAC/B,YAAM,YAAY,KAAK,IAAI;AAE3B,UAAI;AACA,cAAM,WAAW,MAAM,cAAc,OAAO,IAAI;AAChD,cAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,aAAK,cAAc,SAAS,GAAG,MAAM,IAAI,GAAG,IAAI;AAAA,UAC5C,QAAQ,SAAS;AAAA,UACjB,UAAU,GAAG,QAAQ;AAAA,QACzB,CAAC;AAED,YAAI,SAAS,UAAU,KAAK;AACxB,kBAAQ,KAAK,oBAAoB,SAAS,MAAM,uCAAkC,GAAG;AAAA,QACzF;AAEA,eAAO;AAAA,MACX,SAAS,cAAc;AACnB,cAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,cAAM,QAAQ,wBAAwB,QAAQ,eAAe,IAAI,MAAM,OAAO,YAAY,CAAC;AAE3F,aAAK,cAAc,SAAS,GAAG,MAAM,IAAI,GAAG,yBAAoB;AAAA,UAC5D,UAAU,GAAG,QAAQ;AAAA,UACrB,OAAO,MAAM;AAAA,QACjB,CAAC;AAED,YAAI,KAAK,QAAQ,QAAQ,OAAO;AAC5B,eAAK,MAAM;AAAA,YACP,QAAQ;AAAA,YACR,cAAc;AAAA,YACd,cAAc;AAAA,YACd,SAAS,EAAE,QAAQ,UAAU,GAAG,QAAQ,KAAK;AAAA,UACjD,CAAC;AAAA,QACL;AAEA,cAAM;AAAA,MACV;AAAA,IACJ;AAEA,YAAQ,KAAK,+BAA+B;AAAA,EAChD;AAAA,EAEA,cAAiB,QAAmB,IAAkD;AAClF,WAAO,QAAQ,QAAQ,EAClB,KAAK,MAAM,GAAG,CAAC,EACf,MAAM,CAAC,UAAmB;AACvB,YAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,WAAK,KAAK,SAAS,OAAO,QAAQ,OAAO,SAAS,KAAK,OAAO,YAAY;AAC1E,aAAO;AAAA,IACX,CAAC;AAAA,EACT;AAAA,EAEA,MAAc,KACV,OACA,SACA,UAAmC,CAAC,GACpC,OACA,UACF;AACE,QAAI,CAAC,KAAK,eAAe,CAAC,KAAK,QAAQ;AACnC,cAAQ,KAAK,wDAAwD;AACrE;AAAA,IACJ;AAGA,QAAI,KAAK,OAAO,QAAQ,OAAO;AAC3B,cAAQ,MAAM,oBAAoB,OAAO,IAAI,EAAE,SAAS,MAAM,CAAC;AAC/D;AAAA,IACJ;AAEA,UAAM,QAAkB;AAAA,MACpB,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA,KAAK,KAAK,OAAO;AAAA,MACjB,QAAQ,KAAK,OAAO,UAAU;AAAA,MAC9B;AAAA,MACA,MAAM,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAAA,MACjE,YAAY,OAAO,SAAS;AAAA,MAC5B,SAAS,KAAK,gBAAgB,OAAO;AAAA,MACrC,MAAM,KAAK,OAAO;AAAA,MAClB,UAAU,YAAY;AAAA,MACtB,aAAa,CAAC,GAAG,KAAK,WAAW;AAAA;AAAA,IACrC;AAEA,QAAI;AACA,YAAMC,MAAK,MAAM;AACjB,YAAM,YAAY,KAAK,qBAAqB,KAA2C;AACvF,gBAAM,8BAAO,8BAAWA,KAAI,MAAM,GAAG;AAAA,QACjC,GAAG;AAAA,QACH,WAAW,4BAAU,SAAS,MAAM,SAAS;AAAA,MACjD,CAAC;AAAA,IACL,SAAS,GAAG;AACR,cAAQ,MAAM,mCAAmC,CAAC;AAAA,IACtD;AAAA,EACJ;AAAA,EAEA,KAAK,QAAmB;AACpB,UAAM,EAAE,QAAQ,SAAS,aAAa,IAAI;AAC1C,WAAO,KAAK,KAAK,QAAQ,QAAQ,SAAS,QAAW,YAAY;AAAA,EACrE;AAAA,EAEA,KAAK,QAAmB;AACpB,UAAM,EAAE,QAAQ,SAAS,aAAa,IAAI;AAC1C,WAAO,KAAK,KAAK,QAAQ,QAAQ,SAAS,QAAW,YAAY;AAAA,EACrE;AAAA,EAEA,MAAM,QAAmB;AACrB,UAAM,EAAE,QAAQ,cAAc,SAAS,aAAa,IAAI;AACxD,WAAO,KAAK,KAAK,SAAS,QAAQ,SAAS,cAAc,YAAY;AAAA,EACzE;AACJ;AAKO,IAAM,SAAS,IAAI,OAAO;;;AE7SjC,mBAA0D;AAuC1C;AA3BT,IAAM,sBAAN,cAAkC,uBAAwB;AAAA,EAC7D,YAAY,OAAc;AACtB,UAAM,KAAK;AACX,SAAK,QAAQ,EAAE,UAAU,MAAM;AAAA,EACnC;AAAA,EAEA,OAAO,2BAAkC;AACrC,WAAO,EAAE,UAAU,KAAK;AAAA,EAC5B;AAAA,EAEA,kBAAkB,OAAc,MAAiB;AAC7C,WAAO,cAAc,SAAS,gBAAgB,MAAM,OAAO,IAAI;AAAA,MAC3D,gBAAgB,KAAK,kBAAkB;AAAA,IAC3C,CAAC;AAED,WAAO,MAAM;AAAA,MACT,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,SAAS;AAAA,QACL,gBAAgB,KAAK,kBAAkB;AAAA,MAC3C;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EAEA,SAAS;AACL,QAAI,KAAK,MAAM,UAAU;AACrB,aAAO,KAAK,MAAM,YACd,4CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,OAAO,MAAM,GAAG,mCAE/C;AAAA,IAER;AAEA,WAAO,KAAK,MAAM;AAAA,EACtB;AACJ;","names":["import_firestore","app","db"]}
package/dist/index.mjs CHANGED
@@ -130,12 +130,14 @@ var Logger = class {
130
130
  return Object.fromEntries(
131
131
  Object.entries(obj).map(([key, value]) => {
132
132
  if (value === void 0) return [key, null];
133
+ if (typeof value === "function") return [key, "[function]"];
133
134
  if (value instanceof Date) return [key, value.toISOString()];
134
135
  if (Array.isArray(value)) {
135
136
  return [
136
137
  key,
137
138
  value.map((item) => {
138
139
  if (item instanceof Date) return item.toISOString();
140
+ if (typeof item === "function") return "[function]";
139
141
  if (item !== null && typeof item === "object") {
140
142
  return this.sanitizeForFirestore(item);
141
143
  }
@@ -150,24 +152,39 @@ var Logger = class {
150
152
  })
151
153
  );
152
154
  }
153
- // NEW: interceptFetch — auto breadcrumb, skip 4xx/5xx ke Firestore
155
+ // NEW: interceptFetch — auto breadcrumb, skip 4xx/5xx ke Firestore
154
156
  interceptFetch() {
155
157
  if (typeof window === "undefined") return;
156
158
  const originalFetch = window.fetch;
159
+ const skipDomains = [
160
+ "google.com",
161
+ "googletagmanager.com",
162
+ "googleapis.com",
163
+ "facebook.com",
164
+ "hotjar.com",
165
+ "analytics",
166
+ "gtm",
167
+ "ccm/collect"
168
+ ];
169
+ const shouldSkipUrl = (url) => {
170
+ return skipDomains.some((domain) => url.includes(domain));
171
+ };
157
172
  window.fetch = async (input, init) => {
158
173
  const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
174
+ if (shouldSkipUrl(url)) {
175
+ return originalFetch(input, init);
176
+ }
159
177
  const method = init?.method ?? "GET";
160
178
  const startTime = Date.now();
161
179
  try {
162
180
  const response = await originalFetch(input, init);
163
181
  const duration = Date.now() - startTime;
164
- const status = response.status;
165
182
  this.addBreadcrumb("fetch", `${method} ${url}`, {
166
- status,
183
+ status: response.status,
167
184
  duration: `${duration}ms`
168
185
  });
169
- if (status >= 400) {
170
- console.warn(`[ga-logger] HTTP ${status} skipped \u2014 Kibana handles this`, url);
186
+ if (response.status >= 400) {
187
+ console.warn(`[ga-logger] HTTP ${response.status} skipped \u2014 Kibana handles this`, url);
171
188
  }
172
189
  return response;
173
190
  } catch (networkError) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/logger.ts","../src/config/firebase.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\";\n\nconst sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\nconst MAX_BREADCRUMBS = 50;\n\nclass Logger {\n private config: LoggerConfig | null = null;\n private initialized = false;\n private breadcrumbs: Breadcrumb[] = [];\n\n init(config: LoggerConfig) {\n this.config = config;\n initFirebase(config);\n this.initialized = true;\n console.info(\"[ga-logger] initialized —\", config.env);\n }\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 // ✅ NEW: 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 // ✅ NEW: Global error capture — hanya aktif di non-development\n initGlobalCapture() {\n if (!this.config || this.config.env === \"DEV\") {\n console.info(\"[ga-logger] global capture skipped — DEV environment\");\n return;\n }\n\n // Capture JS runtime error\n window.onerror = (message, source, lineno, colno, error) => {\n if (this.shouldSkipGlobalError(message?.toString(), source)) return false;\n\n this.addBreadcrumb(\"error\", `window.onerror: ${message}`, {\n source,\n lineno,\n colno,\n });\n\n this.error({\n fnName: \"window.onerror\",\n messageError: error ?? new Error(String(message)),\n payload: { source, lineno, colno },\n });\n\n return false;\n };\n\n // Capture unhandled Promise rejection\n window.addEventListener(\"unhandledrejection\", (event) => {\n const message = event.reason?.message ?? String(event.reason);\n\n if (this.shouldSkipGlobalError(message)) return;\n\n const error = event.reason instanceof Error ? event.reason : new Error(message);\n\n this.addBreadcrumb(\"error\", `unhandledrejection: ${message}`);\n\n this.error({\n fnName: \"window.unhandledrejection\",\n messageError: error,\n });\n });\n\n console.info(\"[ga-logger] global capture active\");\n }\n\n // ✅ NEW: Smart filter — skip noise dari CORS, network, third-party\n private shouldSkipGlobalError(message?: string, source?: string): boolean {\n if (!message) return true;\n\n const noisePatterns = [\n \"CORS\",\n \"cross-origin\",\n \"ERR_FAILED\",\n \"Failed to load resource\",\n \"NetworkError\",\n \"ResizeObserver loop\",\n \"Script error\",\n ];\n\n const isNoise = noisePatterns.some((pattern) => message.toLowerCase().includes(pattern.toLowerCase()));\n if (isNoise) return true;\n\n // Skip error dari third-party source\n if (source) {\n const thirdPartyPatterns = [\"magnolia\", \"gtm.js\", \"analytics\", \"googletagmanager\", \"facebook\", \"hotjar\"];\n const isThirdParty = thirdPartyPatterns.some((pattern) => source.includes(pattern));\n if (isThirdParty) return true;\n }\n\n return false;\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 // ← tambah: Date object harus convert ke ISO string\n if (value instanceof Date) return [key, value.toISOString()];\n\n if (Array.isArray(value)) {\n return [\n key,\n value.map((item) => {\n if (item instanceof Date) return item.toISOString();\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 // ✅ NEW: interceptFetch — auto breadcrumb, skip 4xx/5xx ke Firestore\n interceptFetch() {\n if (typeof window === \"undefined\") return;\n\n const originalFetch = window.fetch;\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 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 const status = response.status;\n\n // Selalu add breadcrumb untuk semua fetch\n this.addBreadcrumb(\"fetch\", `${method} ${url}`, {\n status,\n duration: `${duration}ms`,\n });\n\n // 4xx/5xx → skip logger.error, Kibana sudah handle\n if (status >= 400) {\n console.warn(`[ga-logger] HTTP ${status} skipped — Kibana handles this`, url);\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 // Network failure → Kibana tidak tahu, kita log!\n this.addBreadcrumb(\"fetch\", `${method} ${url} — NETWORK ERROR`, {\n duration: `${duration}ms`,\n error: error.message,\n });\n\n // Hanya log jika bukan DEV\n if (this.config?.env !== \"DEV\") {\n this.error({\n fnName: \"interceptFetch\",\n messageError: error,\n endpointName: url,\n payload: { method, duration: `${duration}ms` },\n });\n }\n\n throw networkError;\n }\n };\n\n console.info(\"[ga-logger] fetch intercepted\");\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 private async send(\n level: LogLevel,\n message: string,\n payload: Record<string, unknown> = {},\n error?: Error,\n endpoint?: string,\n ) {\n if (!this.initialized || !this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n\n // DEV → console only, skip Firestore\n if (this.config.env === \"DEV\") {\n console.error(`[ga-logger][DEV] ${message}`, { payload, error });\n return;\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: [...this.breadcrumbs], // ← snapshot saat error terjadi\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, payload, endpointName } = params;\n return this.send(\"info\", fnName, payload, undefined, endpointName);\n }\n\n warn(params: LogParams) {\n const { fnName, payload, endpointName } = params;\n return this.send(\"warn\", fnName, payload, undefined, endpointName);\n }\n\n error(params: LogParams) {\n const { fnName, messageError, payload, endpointName } = params;\n return this.send(\"error\", fnName, payload, messageError, endpointName);\n }\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","import { Component, type ErrorInfo, type ReactNode } from \"react\";\nimport { logger } from \"../core/logger\";\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode; // ← optional custom UI saat crash\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/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\";\n\nconst sessionId = `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\nconst MAX_BREADCRUMBS = 50;\n\nclass Logger {\n private config: LoggerConfig | null = null;\n private initialized = false;\n private breadcrumbs: Breadcrumb[] = [];\n\n init(config: LoggerConfig) {\n this.config = config;\n initFirebase(config);\n this.initialized = true;\n console.info(\"[ga-logger] initialized —\", config.env);\n }\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 // ✅ NEW: 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 // ✅ NEW: Global error capture — hanya aktif di non-development\n initGlobalCapture() {\n if (!this.config || this.config.env === \"DEV\") {\n console.info(\"[ga-logger] global capture skipped — DEV environment\");\n return;\n }\n\n // Capture JS runtime error\n window.onerror = (message, source, lineno, colno, error) => {\n if (this.shouldSkipGlobalError(message?.toString(), source)) return false;\n\n this.addBreadcrumb(\"error\", `window.onerror: ${message}`, {\n source,\n lineno,\n colno,\n });\n\n this.error({\n fnName: \"window.onerror\",\n messageError: error ?? new Error(String(message)),\n payload: { source, lineno, colno },\n });\n\n return false;\n };\n\n // Capture unhandled Promise rejection\n window.addEventListener(\"unhandledrejection\", (event) => {\n const message = event.reason?.message ?? String(event.reason);\n\n if (this.shouldSkipGlobalError(message)) return;\n\n const error = event.reason instanceof Error ? event.reason : new Error(message);\n\n this.addBreadcrumb(\"error\", `unhandledrejection: ${message}`);\n\n this.error({\n fnName: \"window.unhandledrejection\",\n messageError: error,\n });\n });\n\n console.info(\"[ga-logger] global capture active\");\n }\n\n // ✅ NEW: Smart filter — skip noise dari CORS, network, third-party\n private shouldSkipGlobalError(message?: string, source?: string): boolean {\n if (!message) return true;\n\n const noisePatterns = [\n \"CORS\",\n \"cross-origin\",\n \"ERR_FAILED\",\n \"Failed to load resource\",\n \"NetworkError\",\n \"ResizeObserver loop\",\n \"Script error\",\n ];\n\n const isNoise = noisePatterns.some((pattern) => message.toLowerCase().includes(pattern.toLowerCase()));\n if (isNoise) return true;\n\n // Skip error dari third-party source\n if (source) {\n const thirdPartyPatterns = [\"magnolia\", \"gtm.js\", \"analytics\", \"googletagmanager\", \"facebook\", \"hotjar\"];\n const isThirdParty = thirdPartyPatterns.some((pattern) => source.includes(pattern));\n if (isThirdParty) return true;\n }\n\n return false;\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 // NEW: interceptFetch — auto breadcrumb, skip 4xx/5xx ke Firestore\n interceptFetch() {\n if (typeof window === \"undefined\") return;\n\n const originalFetch = window.fetch;\n\n // Domain yang di-skip dari breadcrumb\n const skipDomains = [\n \"google.com\",\n \"googletagmanager.com\",\n \"googleapis.com\",\n \"facebook.com\",\n \"hotjar.com\",\n \"analytics\",\n \"gtm\",\n \"ccm/collect\",\n ];\n\n const shouldSkipUrl = (url: string): boolean => {\n return skipDomains.some((domain) => url.includes(domain));\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 console.warn(`[ga-logger] HTTP ${response.status} skipped — Kibana handles this`, url);\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 if (this.config?.env !== \"DEV\") {\n this.error({\n fnName: \"interceptFetch\",\n messageError: error,\n endpointName: url,\n payload: { method, duration: `${duration}ms` },\n });\n }\n\n throw networkError;\n }\n };\n\n console.info(\"[ga-logger] fetch intercepted\");\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 private async send(\n level: LogLevel,\n message: string,\n payload: Record<string, unknown> = {},\n error?: Error,\n endpoint?: string,\n ) {\n if (!this.initialized || !this.config) {\n console.warn(\"[ga-logger] not initialized. Call logger.init() first.\");\n return;\n }\n\n // DEV → console only, skip Firestore\n if (this.config.env === \"DEV\") {\n console.error(`[ga-logger][DEV] ${message}`, { payload, error });\n return;\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: [...this.breadcrumbs], // ← snapshot saat error terjadi\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, payload, endpointName } = params;\n return this.send(\"info\", fnName, payload, undefined, endpointName);\n }\n\n warn(params: LogParams) {\n const { fnName, payload, endpointName } = params;\n return this.send(\"warn\", fnName, payload, undefined, endpointName);\n }\n\n error(params: LogParams) {\n const { fnName, messageError, payload, endpointName } = params;\n return this.send(\"error\", fnName, payload, messageError, endpointName);\n }\n}\n\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","import { Component, type ErrorInfo, type ReactNode } from \"react\";\nimport { logger } from \"../core/logger\";\n\ninterface Props {\n children: ReactNode;\n fallback?: ReactNode; // ← optional custom UI saat crash\n}\n\ninterface State {\n hasError: boolean;\n}\n\nexport class LoggerErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(): State {\n return { hasError: true };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n logger.addBreadcrumb(\"error\", `React crash: ${error.message}`, {\n componentStack: info.componentStack ?? undefined,\n });\n\n logger.error({\n fnName: \"LoggerErrorBoundary\",\n messageError: error,\n payload: {\n componentStack: info.componentStack ?? undefined,\n },\n });\n }\n\n render() {\n if (this.state.hasError) {\n return this.props.fallback ?? (\n <div style={{ padding: \"1rem\", color: \"red\" }}>\n Something went wrong.\n </div>\n );\n }\n\n return this.props.children;\n }\n}"],"mappings":"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","names":["app","db"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ga-components/logger",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",