rusty-replay 1.0.12 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -51,6 +51,7 @@ __export(index_exports, {
51
51
  getEnvironment: () => getEnvironment,
52
52
  getRecordedEvents: () => getRecordedEvents,
53
53
  init: () => init,
54
+ initOtel: () => initOtel,
54
55
  setupGlobalErrorHandler: () => setupGlobalErrorHandler,
55
56
  startRecording: () => startRecording
56
57
  });
@@ -76,7 +77,7 @@ function getBrowserInfo() {
76
77
  return { browser, os, userAgent: ua };
77
78
  }
78
79
  function getEnvironment() {
79
- if (process.env.NODE_ENV === "development") return "development";
80
+ if (true) return "development";
80
81
  return "production";
81
82
  }
82
83
 
@@ -288,16 +289,70 @@ function setupGlobalErrorHandler() {
288
289
  };
289
290
  window.__errorHandlerSetup = true;
290
291
  }
291
- // Annotate the CommonJS export names for ESM import in node:
292
- 0 && (module.exports = {
293
- ErrorBatcher,
294
- captureException,
295
- decompressFromBase64,
296
- getBrowserInfo,
297
- getEnvironment,
298
- getRecordedEvents,
299
- init,
300
- setupGlobalErrorHandler,
301
- startRecording
302
- });
292
+
293
+ // src/front-end-tracer.ts
294
+ var import_core = require("@opentelemetry/core");
295
+ var import_sdk_trace_web = require("@opentelemetry/sdk-trace-web");
296
+ var import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
297
+ var import_resources = require("@opentelemetry/resources");
298
+ var import_semantic_conventions = require("@opentelemetry/semantic-conventions");
299
+ var import_exporter_trace_otlp_proto = require("@opentelemetry/exporter-trace-otlp-proto");
300
+ var initOtel = async (config = {}) => {
301
+ var _a, _b, _c, _d;
302
+ const finalConfig = {
303
+ serviceName: (_a = config.serviceName) != null ? _a : "replay",
304
+ endpoint: (_b = config.endpoint) != null ? _b : "http://localhost:8081/traces",
305
+ isSyntheticRequest: (_c = config.isSyntheticRequest) != null ? _c : false,
306
+ scheduledDelayMillis: (_d = config.scheduledDelayMillis) != null ? _d : 500,
307
+ customHeaders: __spreadValues({
308
+ "Content-Type": "application/x-protobuf"
309
+ }, config.customHeaders)
310
+ };
311
+ const { ZoneContextManager } = await import("@opentelemetry/context-zone");
312
+ let resource = (0, import_resources.resourceFromAttributes)({
313
+ [import_semantic_conventions.ATTR_SERVICE_NAME]: finalConfig.serviceName
314
+ });
315
+ if (finalConfig.isSyntheticRequest) {
316
+ resource = resource.merge(
317
+ (0, import_resources.resourceFromAttributes)({ "app.synthetic_request": "true" })
318
+ );
319
+ }
320
+ resource = resource.merge((0, import_resources.detectResources)({ detectors: [import_resources.osDetector] }));
321
+ const spanProcessor = new import_sdk_trace_base.BatchSpanProcessor(
322
+ new import_exporter_trace_otlp_proto.OTLPTraceExporter({
323
+ url: finalConfig.endpoint,
324
+ headers: finalConfig.customHeaders
325
+ }),
326
+ { scheduledDelayMillis: finalConfig.scheduledDelayMillis }
327
+ );
328
+ const provider = new import_sdk_trace_web.WebTracerProvider({
329
+ resource,
330
+ spanProcessors: [spanProcessor]
331
+ });
332
+ provider.register({
333
+ contextManager: new ZoneContextManager(),
334
+ propagator: new import_core.W3CTraceContextPropagator()
335
+ });
336
+ if (typeof window !== "undefined") {
337
+ const backendOrigin = new URL(finalConfig.endpoint).origin;
338
+ const [{ FetchInstrumentation }, { XMLHttpRequestInstrumentation }] = await Promise.all([
339
+ import("@opentelemetry/instrumentation-fetch"),
340
+ import("@opentelemetry/instrumentation-xml-http-request")
341
+ ]);
342
+ const fetchInst = new FetchInstrumentation({
343
+ propagateTraceHeaderCorsUrls: [backendOrigin],
344
+ clearTimingResources: true
345
+ });
346
+ const xhrInst = new XMLHttpRequestInstrumentation({
347
+ propagateTraceHeaderCorsUrls: [backendOrigin],
348
+ clearTimingResources: true
349
+ });
350
+ fetchInst.setTracerProvider(provider);
351
+ xhrInst.setTracerProvider(provider);
352
+ fetchInst.enable();
353
+ xhrInst.enable();
354
+ }
355
+ console.log("OpenTelemetry initialized successfully");
356
+ return provider;
357
+ };
303
358
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/environment.ts","../src/error-batcher.ts","../src/recorder.ts","../src/utils.ts","../src/reporter.ts","../src/handler.ts"],"sourcesContent":["export * from './reporter';\nexport { setupGlobalErrorHandler } from './handler';\nexport { startRecording, getRecordedEvents } from './recorder';\nexport { getBrowserInfo, getEnvironment } from './environment';\nexport { ErrorBatcher } from './error-batcher';\nexport type { BatchedEvent, InitOptions, AdditionalInfo } from './reporter';\nexport { decompressFromBase64 } from './utils';\n","export function getBrowserInfo() {\n const ua = navigator.userAgent;\n let browser = 'unknown',\n os = 'unknown';\n\n if (ua.includes('Firefox')) browser = 'Firefox';\n else if (ua.includes('SamsungBrowser')) browser = 'Samsung Browser';\n else if (ua.includes('Opera') || ua.includes('OPR')) browser = 'Opera';\n else if (ua.includes('Trident')) browser = 'IE';\n else if (ua.includes('Edge')) browser = 'Edge (Legacy)';\n else if (ua.includes('Edg')) browser = 'Edge';\n else if (ua.includes('Chrome')) browser = 'Chrome';\n else if (ua.includes('Safari')) browser = 'Safari';\n\n if (ua.includes('Windows')) os = 'Windows';\n else if (ua.includes('Mac')) os = 'macOS';\n else if (ua.includes('Linux')) os = 'Linux';\n else if (ua.includes('Android')) os = 'Android';\n else if (ua.includes('like Mac')) os = 'iOS';\n\n return { browser, os, userAgent: ua };\n}\n\nexport function getEnvironment(): 'development' | 'staging' | 'production' {\n if (process.env.NODE_ENV === 'development') return 'development';\n return 'production';\n}\n","import axios from 'axios';\nimport type { BatcherOptions, BatchedEvent } from './reporter';\n\nexport class ErrorBatcher {\n private queue: BatchedEvent[] = [];\n private isFlushing = false;\n private flushTimer: number;\n private readonly apiKey: string;\n\n constructor(private opts: BatcherOptions) {\n this.apiKey = opts.apiKey;\n const interval = opts.flushIntervalMs ?? 30000;\n this.flushTimer = window.setInterval(() => this.flush(), interval);\n window.addEventListener('beforeunload', () => this.flushOnUnload());\n }\n\n public getApiKey(): string {\n return this.apiKey;\n }\n\n public capture(evt: Omit<BatchedEvent, 'id' | 'timestamp'>): string {\n const id = this.makeId();\n const timestamp = new Date().toISOString();\n const record: BatchedEvent = { id, timestamp, ...evt };\n\n if (this.queue.length >= (this.opts.maxBufferSize ?? 64)) {\n this.queue.shift();\n }\n this.queue.push(record);\n return id;\n }\n\n private async flush() {\n if (this.isFlushing || this.queue.length === 0) return;\n this.isFlushing = true;\n\n const batch = this.queue.splice(0, this.queue.length);\n try {\n await axios.post(\n this.opts.endpoint,\n { events: batch },\n {\n maxBodyLength: 1000 * 1024 * 1024, // 10MB\n maxContentLength: 1000 * 1024 * 1024, // 10MB\n timeout: 30000,\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n );\n } catch {\n this.queue.unshift(...batch);\n } finally {\n this.isFlushing = false;\n }\n }\n\n private flushOnUnload() {\n if (!navigator.sendBeacon || this.queue.length === 0) return;\n const payload = JSON.stringify({ events: this.queue });\n navigator.sendBeacon(this.opts.endpoint, payload);\n }\n\n private makeId() {\n return 'xxxx-xxxx-4xxx-yxxx'.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\n public destroy() {\n clearInterval(this.flushTimer);\n }\n}\n","import type { eventWithTime, listenerHandler } from '@rrweb/types';\nimport { record } from 'rrweb';\n\nlet events: eventWithTime[] = [];\nconst MAX_EVENTS = 1000;\nlet stopFn: listenerHandler | undefined = undefined;\n\nexport function startRecording() {\n events = [];\n stopFn?.();\n stopFn = record({\n emit(event) {\n if (event.type === 2) console.log('[rrweb] FullSnapshot 기록됨:', event);\n\n events.push(event);\n if (events.length > MAX_EVENTS) {\n events = events.slice(-MAX_EVENTS);\n }\n },\n // checkoutEveryNms: 1000, // 1초마다 체크아웃\n checkoutEveryNms: 15000, // 15초마다 한 번\n checkoutEveryNth: 100, // 100개 이벤트마다 한 번\n maskAllInputs: true,\n sampling: {\n mouseInteraction: {\n MouseUp: false,\n MouseDown: false,\n Click: false,\n ContextMenu: false,\n DblClick: false,\n Focus: false,\n Blur: false,\n TouchStart: false,\n TouchEnd: false,\n },\n },\n });\n}\n\nexport function getRecordedEvents(\n beforeErrorSec = 10,\n errorTime = Date.now(),\n source = events\n): eventWithTime[] {\n const sliced = source.filter(\n (e) => errorTime - e.timestamp < beforeErrorSec * 1000\n );\n\n const snapshotCandidates = source.filter((e) => e.type === 2);\n const lastSnapshot = [...snapshotCandidates]\n .reverse()\n .find((e) => e.timestamp <= errorTime);\n\n if (lastSnapshot && !sliced.includes(lastSnapshot)) {\n return [lastSnapshot, ...sliced];\n }\n\n if (!sliced.some((e) => e.type === 2)) {\n console.warn('⚠️ Snapshot 없이 잘린 replay입니다. 복원 불가능할 수 있음.');\n }\n\n return sliced;\n}\n\nexport function clearEvents() {\n events = [];\n}\n\nexport function getCurrentEvents(): eventWithTime[] {\n return events.slice();\n}\n","import { zlibSync, unzlibSync } from 'fflate';\n\nexport function compressToBase64(obj: any): string {\n const json = JSON.stringify(obj);\n const compressed = zlibSync(new TextEncoder().encode(json));\n return btoa(String.fromCharCode(...compressed));\n}\n\nexport function decompressFromBase64(base64: string): any[] {\n const binary = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));\n const json = new TextDecoder().decode(unzlibSync(binary));\n return JSON.parse(json);\n}\n","import { getBrowserInfo, getEnvironment } from './environment';\nimport { ErrorBatcher } from './error-batcher';\nimport {\n startRecording,\n getRecordedEvents,\n getCurrentEvents,\n clearEvents,\n} from './recorder';\nimport { compressToBase64 } from './utils';\n\nexport interface AdditionalInfo {\n pageUrl: string;\n request: {\n url: string;\n method: string;\n headers: Record<string, string>;\n };\n response: {\n data: {\n message: string;\n errorCode: string;\n };\n status: number;\n statusText: string;\n };\n}\n\nexport interface BatchedEvent {\n id: string;\n timestamp: string;\n message: string;\n stacktrace: string;\n replay: string | null;\n environment: string;\n browser: string;\n os: string;\n userAgent: string;\n userId?: number;\n additionalInfo?: Partial<AdditionalInfo>;\n appVersion: string;\n apiKey: string;\n}\n\nexport interface BatcherOptions {\n endpoint: string;\n apiKey: string;\n flushIntervalMs?: number;\n maxBufferSize?: number;\n}\n\nexport interface InitOptions {\n endpoint: string;\n apiKey: string;\n flushIntervalMs?: number;\n maxBufferSize?: number;\n beforeErrorSec?: number;\n}\n\nlet batcher: ErrorBatcher;\nlet globalOpts: { beforeErrorSec: number } = { beforeErrorSec: 30 };\n\nexport function init(options: InitOptions) {\n globalOpts.beforeErrorSec = options.beforeErrorSec ?? 10;\n\n batcher = new ErrorBatcher({\n endpoint: options.endpoint,\n apiKey: options.apiKey,\n flushIntervalMs: options.flushIntervalMs,\n maxBufferSize: options.maxBufferSize,\n });\n\n if (typeof window !== 'undefined') {\n if (document.readyState === 'complete') {\n requestAnimationFrame(() => startRecording());\n } else {\n window.addEventListener('load', () => {\n requestAnimationFrame(() => startRecording());\n });\n }\n }\n}\n\nexport function captureException(\n error: Error,\n additionalInfo?: Partial<AdditionalInfo>,\n userId?: number\n): string {\n const errorTime = Date.now();\n const eventsSnapshot = getCurrentEvents();\n const rawReplay = getRecordedEvents(\n globalOpts.beforeErrorSec,\n errorTime,\n eventsSnapshot\n );\n\n clearEvents();\n\n const { browser, os, userAgent } = getBrowserInfo();\n\n const compressedReplay = compressToBase64(rawReplay);\n\n return batcher.capture({\n message: error.message ?? '',\n stacktrace: error.stack ?? '',\n replay: compressedReplay as any,\n environment: getEnvironment(),\n browser,\n os,\n userAgent,\n userId,\n additionalInfo,\n appVersion: '1.0.0',\n apiKey: batcher.getApiKey(),\n });\n}\n","import { captureException } from './reporter';\n\nexport function setupGlobalErrorHandler() {\n if ((window as any).__errorHandlerSetup) return;\n\n const origOnError = window.onerror;\n window.onerror = function thisWindowOnError(\n this: Window & WindowEventHandlers,\n message: string | Event,\n source?: string,\n lineno?: number,\n colno?: number,\n error?: Error\n ): boolean {\n origOnError?.call(this, message, source, lineno, colno, error);\n captureException(\n error ??\n new Error(typeof message === 'string' ? message : 'Unknown error')\n );\n return false;\n };\n\n const origOnUnhandledRejection = window.onunhandledrejection;\n window.onunhandledrejection = function thisWindowOnRejection(\n this: Window & WindowEventHandlers,\n event: PromiseRejectionEvent\n ): any {\n origOnUnhandledRejection?.call(this, event);\n const err =\n event.reason instanceof Error\n ? event.reason\n : new Error(JSON.stringify(event.reason));\n captureException(err);\n } as typeof window.onunhandledrejection;\n\n (window as any).__errorHandlerSetup = true;\n}\n"],"mappings":"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","names":["record","axios"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/environment.ts","../src/error-batcher.ts","../src/recorder.ts","../src/utils.ts","../src/reporter.ts","../src/handler.ts","../src/front-end-tracer.ts"],"sourcesContent":["export * from './reporter';\nexport { setupGlobalErrorHandler } from './handler';\nexport { startRecording, getRecordedEvents } from './recorder';\nexport { getBrowserInfo, getEnvironment } from './environment';\nexport { ErrorBatcher } from './error-batcher';\nexport type { BatchedEvent, InitOptions, AdditionalInfo } from './reporter';\nexport { decompressFromBase64 } from './utils';\nexport { initOtel } from './front-end-tracer';\n","export function getBrowserInfo() {\n const ua = navigator.userAgent;\n let browser = 'unknown',\n os = 'unknown';\n\n if (ua.includes('Firefox')) browser = 'Firefox';\n else if (ua.includes('SamsungBrowser')) browser = 'Samsung Browser';\n else if (ua.includes('Opera') || ua.includes('OPR')) browser = 'Opera';\n else if (ua.includes('Trident')) browser = 'IE';\n else if (ua.includes('Edge')) browser = 'Edge (Legacy)';\n else if (ua.includes('Edg')) browser = 'Edge';\n else if (ua.includes('Chrome')) browser = 'Chrome';\n else if (ua.includes('Safari')) browser = 'Safari';\n\n if (ua.includes('Windows')) os = 'Windows';\n else if (ua.includes('Mac')) os = 'macOS';\n else if (ua.includes('Linux')) os = 'Linux';\n else if (ua.includes('Android')) os = 'Android';\n else if (ua.includes('like Mac')) os = 'iOS';\n\n return { browser, os, userAgent: ua };\n}\n\nexport function getEnvironment(): 'development' | 'staging' | 'production' {\n if (process.env.NODE_ENV === 'development') return 'development';\n return 'production';\n}\n","import axios from 'axios';\nimport type { BatcherOptions, BatchedEvent } from './reporter';\n\nexport class ErrorBatcher {\n private queue: BatchedEvent[] = [];\n private isFlushing = false;\n private flushTimer: number;\n private readonly apiKey: string;\n\n constructor(private opts: BatcherOptions) {\n this.apiKey = opts.apiKey;\n const interval = opts.flushIntervalMs ?? 30000;\n this.flushTimer = window.setInterval(() => this.flush(), interval);\n window.addEventListener('beforeunload', () => this.flushOnUnload());\n }\n\n public getApiKey(): string {\n return this.apiKey;\n }\n\n public capture(evt: Omit<BatchedEvent, 'id' | 'timestamp'>): string {\n const id = this.makeId();\n const timestamp = new Date().toISOString();\n const record: BatchedEvent = { id, timestamp, ...evt };\n\n if (this.queue.length >= (this.opts.maxBufferSize ?? 64)) {\n this.queue.shift();\n }\n this.queue.push(record);\n return id;\n }\n\n private async flush() {\n if (this.isFlushing || this.queue.length === 0) return;\n this.isFlushing = true;\n\n const batch = this.queue.splice(0, this.queue.length);\n try {\n await axios.post(\n this.opts.endpoint,\n { events: batch },\n {\n maxBodyLength: 1000 * 1024 * 1024, // 10MB\n maxContentLength: 1000 * 1024 * 1024, // 10MB\n timeout: 30000,\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n );\n } catch {\n this.queue.unshift(...batch);\n } finally {\n this.isFlushing = false;\n }\n }\n\n private flushOnUnload() {\n if (!navigator.sendBeacon || this.queue.length === 0) return;\n const payload = JSON.stringify({ events: this.queue });\n navigator.sendBeacon(this.opts.endpoint, payload);\n }\n\n private makeId() {\n return 'xxxx-xxxx-4xxx-yxxx'.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\n public destroy() {\n clearInterval(this.flushTimer);\n }\n}\n","import type { eventWithTime, listenerHandler } from '@rrweb/types';\nimport { record } from 'rrweb';\n\nlet events: eventWithTime[] = [];\nconst MAX_EVENTS = 1000;\nlet stopFn: listenerHandler | undefined = undefined;\n\nexport function startRecording() {\n events = [];\n stopFn?.();\n stopFn = record({\n emit(event) {\n if (event.type === 2) console.log('[rrweb] FullSnapshot 기록됨:', event);\n\n events.push(event);\n if (events.length > MAX_EVENTS) {\n events = events.slice(-MAX_EVENTS);\n }\n },\n // checkoutEveryNms: 1000, // 1초마다 체크아웃\n checkoutEveryNms: 15000, // 15초마다 한 번\n checkoutEveryNth: 100, // 100개 이벤트마다 한 번\n maskAllInputs: true,\n sampling: {\n mouseInteraction: {\n MouseUp: false,\n MouseDown: false,\n Click: false,\n ContextMenu: false,\n DblClick: false,\n Focus: false,\n Blur: false,\n TouchStart: false,\n TouchEnd: false,\n },\n },\n });\n}\n\nexport function getRecordedEvents(\n beforeErrorSec = 10,\n errorTime = Date.now(),\n source = events\n): eventWithTime[] {\n const sliced = source.filter(\n (e) => errorTime - e.timestamp < beforeErrorSec * 1000\n );\n\n const snapshotCandidates = source.filter((e) => e.type === 2);\n const lastSnapshot = [...snapshotCandidates]\n .reverse()\n .find((e) => e.timestamp <= errorTime);\n\n if (lastSnapshot && !sliced.includes(lastSnapshot)) {\n return [lastSnapshot, ...sliced];\n }\n\n if (!sliced.some((e) => e.type === 2)) {\n console.warn('⚠️ Snapshot 없이 잘린 replay입니다. 복원 불가능할 수 있음.');\n }\n\n return sliced;\n}\n\nexport function clearEvents() {\n events = [];\n}\n\nexport function getCurrentEvents(): eventWithTime[] {\n return events.slice();\n}\n","import { zlibSync, unzlibSync } from 'fflate';\n\nexport function compressToBase64(obj: any): string {\n const json = JSON.stringify(obj);\n const compressed = zlibSync(new TextEncoder().encode(json));\n return btoa(String.fromCharCode(...compressed));\n}\n\nexport function decompressFromBase64(base64: string): any[] {\n const binary = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));\n const json = new TextDecoder().decode(unzlibSync(binary));\n return JSON.parse(json);\n}\n","import { getBrowserInfo, getEnvironment } from './environment';\nimport { ErrorBatcher } from './error-batcher';\nimport {\n startRecording,\n getRecordedEvents,\n getCurrentEvents,\n clearEvents,\n} from './recorder';\nimport { compressToBase64 } from './utils';\n\nexport interface AdditionalInfo {\n pageUrl: string;\n request: {\n url: string;\n method: string;\n headers: Record<string, string>;\n };\n response: {\n data: {\n message: string;\n errorCode: string;\n };\n status: number;\n statusText: string;\n };\n}\n\nexport interface BatchedEvent {\n id: string;\n timestamp: string;\n message: string;\n stacktrace: string;\n replay: string | null;\n environment: string;\n browser: string;\n os: string;\n userAgent: string;\n userId?: number;\n additionalInfo?: Partial<AdditionalInfo>;\n appVersion: string;\n apiKey: string;\n}\n\nexport interface BatcherOptions {\n endpoint: string;\n apiKey: string;\n flushIntervalMs?: number;\n maxBufferSize?: number;\n}\n\nexport interface InitOptions {\n endpoint: string;\n apiKey: string;\n flushIntervalMs?: number;\n maxBufferSize?: number;\n beforeErrorSec?: number;\n}\n\nlet batcher: ErrorBatcher;\nlet globalOpts: { beforeErrorSec: number } = { beforeErrorSec: 30 };\n\nexport function init(options: InitOptions) {\n globalOpts.beforeErrorSec = options.beforeErrorSec ?? 10;\n\n batcher = new ErrorBatcher({\n endpoint: options.endpoint,\n apiKey: options.apiKey,\n flushIntervalMs: options.flushIntervalMs,\n maxBufferSize: options.maxBufferSize,\n });\n\n if (typeof window !== 'undefined') {\n if (document.readyState === 'complete') {\n requestAnimationFrame(() => startRecording());\n } else {\n window.addEventListener('load', () => {\n requestAnimationFrame(() => startRecording());\n });\n }\n }\n}\n\nexport function captureException(\n error: Error,\n additionalInfo?: Partial<AdditionalInfo>,\n userId?: number\n): string {\n const errorTime = Date.now();\n const eventsSnapshot = getCurrentEvents();\n const rawReplay = getRecordedEvents(\n globalOpts.beforeErrorSec,\n errorTime,\n eventsSnapshot\n );\n\n clearEvents();\n\n const { browser, os, userAgent } = getBrowserInfo();\n\n const compressedReplay = compressToBase64(rawReplay);\n\n return batcher.capture({\n message: error.message ?? '',\n stacktrace: error.stack ?? '',\n replay: compressedReplay as any,\n environment: getEnvironment(),\n browser,\n os,\n userAgent,\n userId,\n additionalInfo,\n appVersion: '1.0.0',\n apiKey: batcher.getApiKey(),\n });\n}\n","import { captureException } from './reporter';\n\nexport function setupGlobalErrorHandler() {\n if ((window as any).__errorHandlerSetup) return;\n\n const origOnError = window.onerror;\n window.onerror = function thisWindowOnError(\n this: Window & WindowEventHandlers,\n message: string | Event,\n source?: string,\n lineno?: number,\n colno?: number,\n error?: Error\n ): boolean {\n origOnError?.call(this, message, source, lineno, colno, error);\n captureException(\n error ??\n new Error(typeof message === 'string' ? message : 'Unknown error')\n );\n return false;\n };\n\n const origOnUnhandledRejection = window.onunhandledrejection;\n window.onunhandledrejection = function thisWindowOnRejection(\n this: Window & WindowEventHandlers,\n event: PromiseRejectionEvent\n ): any {\n origOnUnhandledRejection?.call(this, event);\n const err =\n event.reason instanceof Error\n ? event.reason\n : new Error(JSON.stringify(event.reason));\n captureException(err);\n } as typeof window.onunhandledrejection;\n\n (window as any).__errorHandlerSetup = true;\n}\n","import { W3CTraceContextPropagator } from '@opentelemetry/core';\nimport { WebTracerProvider } from '@opentelemetry/sdk-trace-web';\nimport { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport {\n resourceFromAttributes,\n osDetector,\n detectResources,\n} from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';\n\nexport interface OtelConfig {\n serviceName?: string;\n endpoint?: string;\n isSyntheticRequest?: boolean;\n scheduledDelayMillis?: number;\n customHeaders?: Record<string, string>;\n}\n\nexport const initOtel = async (config: OtelConfig = {}) => {\n const finalConfig = {\n serviceName: config.serviceName ?? 'replay',\n endpoint: config.endpoint ?? 'http://localhost:8081/traces',\n isSyntheticRequest: config.isSyntheticRequest ?? false,\n scheduledDelayMillis: config.scheduledDelayMillis ?? 500,\n customHeaders: {\n 'Content-Type': 'application/x-protobuf',\n ...config.customHeaders,\n },\n };\n\n const { ZoneContextManager } = await import('@opentelemetry/context-zone');\n\n let resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: finalConfig.serviceName,\n });\n if (finalConfig.isSyntheticRequest) {\n resource = resource.merge(\n resourceFromAttributes({ 'app.synthetic_request': 'true' })\n );\n }\n resource = resource.merge(detectResources({ detectors: [osDetector] }));\n\n const spanProcessor = new BatchSpanProcessor(\n new OTLPTraceExporter({\n url: finalConfig.endpoint,\n headers: finalConfig.customHeaders,\n }),\n { scheduledDelayMillis: finalConfig.scheduledDelayMillis }\n );\n const provider = new WebTracerProvider({\n resource,\n spanProcessors: [spanProcessor],\n });\n provider.register({\n contextManager: new ZoneContextManager(),\n propagator: new W3CTraceContextPropagator(),\n });\n\n if (typeof window !== 'undefined') {\n const backendOrigin = new URL(finalConfig.endpoint).origin;\n\n const [{ FetchInstrumentation }, { XMLHttpRequestInstrumentation }] =\n await Promise.all([\n import('@opentelemetry/instrumentation-fetch'),\n import('@opentelemetry/instrumentation-xml-http-request'),\n ]);\n\n const fetchInst = new FetchInstrumentation({\n propagateTraceHeaderCorsUrls: [backendOrigin],\n clearTimingResources: true,\n });\n const xhrInst = new XMLHttpRequestInstrumentation({\n propagateTraceHeaderCorsUrls: [backendOrigin],\n clearTimingResources: true,\n });\n\n fetchInst.setTracerProvider(provider);\n xhrInst.setTracerProvider(provider);\n fetchInst.enable();\n xhrInst.enable();\n }\n\n console.log('OpenTelemetry initialized successfully');\n return provider;\n};\n"],"mappings":"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","names":["record","axios"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { eventWithTime } from '@rrweb/types';
2
+ import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
2
3
 
3
4
  interface AdditionalInfo {
4
5
  pageUrl: string;
@@ -76,4 +77,13 @@ declare class ErrorBatcher {
76
77
 
77
78
  declare function decompressFromBase64(base64: string): any[];
78
79
 
79
- export { type AdditionalInfo, type BatchedEvent, type BatcherOptions, ErrorBatcher, type InitOptions, captureException, decompressFromBase64, getBrowserInfo, getEnvironment, getRecordedEvents, init, setupGlobalErrorHandler, startRecording };
80
+ interface OtelConfig {
81
+ serviceName?: string;
82
+ endpoint?: string;
83
+ isSyntheticRequest?: boolean;
84
+ scheduledDelayMillis?: number;
85
+ customHeaders?: Record<string, string>;
86
+ }
87
+ declare const initOtel: (config?: OtelConfig) => Promise<WebTracerProvider>;
88
+
89
+ export { type AdditionalInfo, type BatchedEvent, type BatcherOptions, ErrorBatcher, type InitOptions, captureException, decompressFromBase64, getBrowserInfo, getEnvironment, getRecordedEvents, init, initOtel, setupGlobalErrorHandler, startRecording };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { eventWithTime } from '@rrweb/types';
2
+ import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
2
3
 
3
4
  interface AdditionalInfo {
4
5
  pageUrl: string;
@@ -76,4 +77,13 @@ declare class ErrorBatcher {
76
77
 
77
78
  declare function decompressFromBase64(base64: string): any[];
78
79
 
79
- export { type AdditionalInfo, type BatchedEvent, type BatcherOptions, ErrorBatcher, type InitOptions, captureException, decompressFromBase64, getBrowserInfo, getEnvironment, getRecordedEvents, init, setupGlobalErrorHandler, startRecording };
80
+ interface OtelConfig {
81
+ serviceName?: string;
82
+ endpoint?: string;
83
+ isSyntheticRequest?: boolean;
84
+ scheduledDelayMillis?: number;
85
+ customHeaders?: Record<string, string>;
86
+ }
87
+ declare const initOtel: (config?: OtelConfig) => Promise<WebTracerProvider>;
88
+
89
+ export { type AdditionalInfo, type BatchedEvent, type BatcherOptions, ErrorBatcher, type InitOptions, captureException, decompressFromBase64, getBrowserInfo, getEnvironment, getRecordedEvents, init, initOtel, setupGlobalErrorHandler, startRecording };
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ function getBrowserInfo() {
35
35
  return { browser, os, userAgent: ua };
36
36
  }
37
37
  function getEnvironment() {
38
- if (process.env.NODE_ENV === "development") return "development";
38
+ if (true) return "development";
39
39
  return "production";
40
40
  }
41
41
 
@@ -247,6 +247,76 @@ function setupGlobalErrorHandler() {
247
247
  };
248
248
  window.__errorHandlerSetup = true;
249
249
  }
250
+
251
+ // src/front-end-tracer.ts
252
+ import { W3CTraceContextPropagator } from "@opentelemetry/core";
253
+ import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
254
+ import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
255
+ import {
256
+ resourceFromAttributes,
257
+ osDetector,
258
+ detectResources
259
+ } from "@opentelemetry/resources";
260
+ import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
261
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
262
+ var initOtel = async (config = {}) => {
263
+ var _a, _b, _c, _d;
264
+ const finalConfig = {
265
+ serviceName: (_a = config.serviceName) != null ? _a : "replay",
266
+ endpoint: (_b = config.endpoint) != null ? _b : "http://localhost:8081/traces",
267
+ isSyntheticRequest: (_c = config.isSyntheticRequest) != null ? _c : false,
268
+ scheduledDelayMillis: (_d = config.scheduledDelayMillis) != null ? _d : 500,
269
+ customHeaders: __spreadValues({
270
+ "Content-Type": "application/x-protobuf"
271
+ }, config.customHeaders)
272
+ };
273
+ const { ZoneContextManager } = await import("@opentelemetry/context-zone");
274
+ let resource = resourceFromAttributes({
275
+ [ATTR_SERVICE_NAME]: finalConfig.serviceName
276
+ });
277
+ if (finalConfig.isSyntheticRequest) {
278
+ resource = resource.merge(
279
+ resourceFromAttributes({ "app.synthetic_request": "true" })
280
+ );
281
+ }
282
+ resource = resource.merge(detectResources({ detectors: [osDetector] }));
283
+ const spanProcessor = new BatchSpanProcessor(
284
+ new OTLPTraceExporter({
285
+ url: finalConfig.endpoint,
286
+ headers: finalConfig.customHeaders
287
+ }),
288
+ { scheduledDelayMillis: finalConfig.scheduledDelayMillis }
289
+ );
290
+ const provider = new WebTracerProvider({
291
+ resource,
292
+ spanProcessors: [spanProcessor]
293
+ });
294
+ provider.register({
295
+ contextManager: new ZoneContextManager(),
296
+ propagator: new W3CTraceContextPropagator()
297
+ });
298
+ if (typeof window !== "undefined") {
299
+ const backendOrigin = new URL(finalConfig.endpoint).origin;
300
+ const [{ FetchInstrumentation }, { XMLHttpRequestInstrumentation }] = await Promise.all([
301
+ import("@opentelemetry/instrumentation-fetch"),
302
+ import("@opentelemetry/instrumentation-xml-http-request")
303
+ ]);
304
+ const fetchInst = new FetchInstrumentation({
305
+ propagateTraceHeaderCorsUrls: [backendOrigin],
306
+ clearTimingResources: true
307
+ });
308
+ const xhrInst = new XMLHttpRequestInstrumentation({
309
+ propagateTraceHeaderCorsUrls: [backendOrigin],
310
+ clearTimingResources: true
311
+ });
312
+ fetchInst.setTracerProvider(provider);
313
+ xhrInst.setTracerProvider(provider);
314
+ fetchInst.enable();
315
+ xhrInst.enable();
316
+ }
317
+ console.log("OpenTelemetry initialized successfully");
318
+ return provider;
319
+ };
250
320
  export {
251
321
  ErrorBatcher,
252
322
  captureException,
@@ -255,6 +325,7 @@ export {
255
325
  getEnvironment,
256
326
  getRecordedEvents,
257
327
  init,
328
+ initOtel,
258
329
  setupGlobalErrorHandler,
259
330
  startRecording
260
331
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/environment.ts","../src/error-batcher.ts","../src/recorder.ts","../src/utils.ts","../src/reporter.ts","../src/handler.ts"],"sourcesContent":["export function getBrowserInfo() {\n const ua = navigator.userAgent;\n let browser = 'unknown',\n os = 'unknown';\n\n if (ua.includes('Firefox')) browser = 'Firefox';\n else if (ua.includes('SamsungBrowser')) browser = 'Samsung Browser';\n else if (ua.includes('Opera') || ua.includes('OPR')) browser = 'Opera';\n else if (ua.includes('Trident')) browser = 'IE';\n else if (ua.includes('Edge')) browser = 'Edge (Legacy)';\n else if (ua.includes('Edg')) browser = 'Edge';\n else if (ua.includes('Chrome')) browser = 'Chrome';\n else if (ua.includes('Safari')) browser = 'Safari';\n\n if (ua.includes('Windows')) os = 'Windows';\n else if (ua.includes('Mac')) os = 'macOS';\n else if (ua.includes('Linux')) os = 'Linux';\n else if (ua.includes('Android')) os = 'Android';\n else if (ua.includes('like Mac')) os = 'iOS';\n\n return { browser, os, userAgent: ua };\n}\n\nexport function getEnvironment(): 'development' | 'staging' | 'production' {\n if (process.env.NODE_ENV === 'development') return 'development';\n return 'production';\n}\n","import axios from 'axios';\nimport type { BatcherOptions, BatchedEvent } from './reporter';\n\nexport class ErrorBatcher {\n private queue: BatchedEvent[] = [];\n private isFlushing = false;\n private flushTimer: number;\n private readonly apiKey: string;\n\n constructor(private opts: BatcherOptions) {\n this.apiKey = opts.apiKey;\n const interval = opts.flushIntervalMs ?? 30000;\n this.flushTimer = window.setInterval(() => this.flush(), interval);\n window.addEventListener('beforeunload', () => this.flushOnUnload());\n }\n\n public getApiKey(): string {\n return this.apiKey;\n }\n\n public capture(evt: Omit<BatchedEvent, 'id' | 'timestamp'>): string {\n const id = this.makeId();\n const timestamp = new Date().toISOString();\n const record: BatchedEvent = { id, timestamp, ...evt };\n\n if (this.queue.length >= (this.opts.maxBufferSize ?? 64)) {\n this.queue.shift();\n }\n this.queue.push(record);\n return id;\n }\n\n private async flush() {\n if (this.isFlushing || this.queue.length === 0) return;\n this.isFlushing = true;\n\n const batch = this.queue.splice(0, this.queue.length);\n try {\n await axios.post(\n this.opts.endpoint,\n { events: batch },\n {\n maxBodyLength: 1000 * 1024 * 1024, // 10MB\n maxContentLength: 1000 * 1024 * 1024, // 10MB\n timeout: 30000,\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n );\n } catch {\n this.queue.unshift(...batch);\n } finally {\n this.isFlushing = false;\n }\n }\n\n private flushOnUnload() {\n if (!navigator.sendBeacon || this.queue.length === 0) return;\n const payload = JSON.stringify({ events: this.queue });\n navigator.sendBeacon(this.opts.endpoint, payload);\n }\n\n private makeId() {\n return 'xxxx-xxxx-4xxx-yxxx'.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\n public destroy() {\n clearInterval(this.flushTimer);\n }\n}\n","import type { eventWithTime, listenerHandler } from '@rrweb/types';\nimport { record } from 'rrweb';\n\nlet events: eventWithTime[] = [];\nconst MAX_EVENTS = 1000;\nlet stopFn: listenerHandler | undefined = undefined;\n\nexport function startRecording() {\n events = [];\n stopFn?.();\n stopFn = record({\n emit(event) {\n if (event.type === 2) console.log('[rrweb] FullSnapshot 기록됨:', event);\n\n events.push(event);\n if (events.length > MAX_EVENTS) {\n events = events.slice(-MAX_EVENTS);\n }\n },\n // checkoutEveryNms: 1000, // 1초마다 체크아웃\n checkoutEveryNms: 15000, // 15초마다 한 번\n checkoutEveryNth: 100, // 100개 이벤트마다 한 번\n maskAllInputs: true,\n sampling: {\n mouseInteraction: {\n MouseUp: false,\n MouseDown: false,\n Click: false,\n ContextMenu: false,\n DblClick: false,\n Focus: false,\n Blur: false,\n TouchStart: false,\n TouchEnd: false,\n },\n },\n });\n}\n\nexport function getRecordedEvents(\n beforeErrorSec = 10,\n errorTime = Date.now(),\n source = events\n): eventWithTime[] {\n const sliced = source.filter(\n (e) => errorTime - e.timestamp < beforeErrorSec * 1000\n );\n\n const snapshotCandidates = source.filter((e) => e.type === 2);\n const lastSnapshot = [...snapshotCandidates]\n .reverse()\n .find((e) => e.timestamp <= errorTime);\n\n if (lastSnapshot && !sliced.includes(lastSnapshot)) {\n return [lastSnapshot, ...sliced];\n }\n\n if (!sliced.some((e) => e.type === 2)) {\n console.warn('⚠️ Snapshot 없이 잘린 replay입니다. 복원 불가능할 수 있음.');\n }\n\n return sliced;\n}\n\nexport function clearEvents() {\n events = [];\n}\n\nexport function getCurrentEvents(): eventWithTime[] {\n return events.slice();\n}\n","import { zlibSync, unzlibSync } from 'fflate';\n\nexport function compressToBase64(obj: any): string {\n const json = JSON.stringify(obj);\n const compressed = zlibSync(new TextEncoder().encode(json));\n return btoa(String.fromCharCode(...compressed));\n}\n\nexport function decompressFromBase64(base64: string): any[] {\n const binary = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));\n const json = new TextDecoder().decode(unzlibSync(binary));\n return JSON.parse(json);\n}\n","import { getBrowserInfo, getEnvironment } from './environment';\nimport { ErrorBatcher } from './error-batcher';\nimport {\n startRecording,\n getRecordedEvents,\n getCurrentEvents,\n clearEvents,\n} from './recorder';\nimport { compressToBase64 } from './utils';\n\nexport interface AdditionalInfo {\n pageUrl: string;\n request: {\n url: string;\n method: string;\n headers: Record<string, string>;\n };\n response: {\n data: {\n message: string;\n errorCode: string;\n };\n status: number;\n statusText: string;\n };\n}\n\nexport interface BatchedEvent {\n id: string;\n timestamp: string;\n message: string;\n stacktrace: string;\n replay: string | null;\n environment: string;\n browser: string;\n os: string;\n userAgent: string;\n userId?: number;\n additionalInfo?: Partial<AdditionalInfo>;\n appVersion: string;\n apiKey: string;\n}\n\nexport interface BatcherOptions {\n endpoint: string;\n apiKey: string;\n flushIntervalMs?: number;\n maxBufferSize?: number;\n}\n\nexport interface InitOptions {\n endpoint: string;\n apiKey: string;\n flushIntervalMs?: number;\n maxBufferSize?: number;\n beforeErrorSec?: number;\n}\n\nlet batcher: ErrorBatcher;\nlet globalOpts: { beforeErrorSec: number } = { beforeErrorSec: 30 };\n\nexport function init(options: InitOptions) {\n globalOpts.beforeErrorSec = options.beforeErrorSec ?? 10;\n\n batcher = new ErrorBatcher({\n endpoint: options.endpoint,\n apiKey: options.apiKey,\n flushIntervalMs: options.flushIntervalMs,\n maxBufferSize: options.maxBufferSize,\n });\n\n if (typeof window !== 'undefined') {\n if (document.readyState === 'complete') {\n requestAnimationFrame(() => startRecording());\n } else {\n window.addEventListener('load', () => {\n requestAnimationFrame(() => startRecording());\n });\n }\n }\n}\n\nexport function captureException(\n error: Error,\n additionalInfo?: Partial<AdditionalInfo>,\n userId?: number\n): string {\n const errorTime = Date.now();\n const eventsSnapshot = getCurrentEvents();\n const rawReplay = getRecordedEvents(\n globalOpts.beforeErrorSec,\n errorTime,\n eventsSnapshot\n );\n\n clearEvents();\n\n const { browser, os, userAgent } = getBrowserInfo();\n\n const compressedReplay = compressToBase64(rawReplay);\n\n return batcher.capture({\n message: error.message ?? '',\n stacktrace: error.stack ?? '',\n replay: compressedReplay as any,\n environment: getEnvironment(),\n browser,\n os,\n userAgent,\n userId,\n additionalInfo,\n appVersion: '1.0.0',\n apiKey: batcher.getApiKey(),\n });\n}\n","import { captureException } from './reporter';\n\nexport function setupGlobalErrorHandler() {\n if ((window as any).__errorHandlerSetup) return;\n\n const origOnError = window.onerror;\n window.onerror = function thisWindowOnError(\n this: Window & WindowEventHandlers,\n message: string | Event,\n source?: string,\n lineno?: number,\n colno?: number,\n error?: Error\n ): boolean {\n origOnError?.call(this, message, source, lineno, colno, error);\n captureException(\n error ??\n new Error(typeof message === 'string' ? message : 'Unknown error')\n );\n return false;\n };\n\n const origOnUnhandledRejection = window.onunhandledrejection;\n window.onunhandledrejection = function thisWindowOnRejection(\n this: Window & WindowEventHandlers,\n event: PromiseRejectionEvent\n ): any {\n origOnUnhandledRejection?.call(this, event);\n const err =\n event.reason instanceof Error\n ? event.reason\n : new Error(JSON.stringify(event.reason));\n captureException(err);\n } as typeof window.onunhandledrejection;\n\n (window as any).__errorHandlerSetup = true;\n}\n"],"mappings":"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","names":["record"]}
1
+ {"version":3,"sources":["../src/environment.ts","../src/error-batcher.ts","../src/recorder.ts","../src/utils.ts","../src/reporter.ts","../src/handler.ts","../src/front-end-tracer.ts"],"sourcesContent":["export function getBrowserInfo() {\n const ua = navigator.userAgent;\n let browser = 'unknown',\n os = 'unknown';\n\n if (ua.includes('Firefox')) browser = 'Firefox';\n else if (ua.includes('SamsungBrowser')) browser = 'Samsung Browser';\n else if (ua.includes('Opera') || ua.includes('OPR')) browser = 'Opera';\n else if (ua.includes('Trident')) browser = 'IE';\n else if (ua.includes('Edge')) browser = 'Edge (Legacy)';\n else if (ua.includes('Edg')) browser = 'Edge';\n else if (ua.includes('Chrome')) browser = 'Chrome';\n else if (ua.includes('Safari')) browser = 'Safari';\n\n if (ua.includes('Windows')) os = 'Windows';\n else if (ua.includes('Mac')) os = 'macOS';\n else if (ua.includes('Linux')) os = 'Linux';\n else if (ua.includes('Android')) os = 'Android';\n else if (ua.includes('like Mac')) os = 'iOS';\n\n return { browser, os, userAgent: ua };\n}\n\nexport function getEnvironment(): 'development' | 'staging' | 'production' {\n if (process.env.NODE_ENV === 'development') return 'development';\n return 'production';\n}\n","import axios from 'axios';\nimport type { BatcherOptions, BatchedEvent } from './reporter';\n\nexport class ErrorBatcher {\n private queue: BatchedEvent[] = [];\n private isFlushing = false;\n private flushTimer: number;\n private readonly apiKey: string;\n\n constructor(private opts: BatcherOptions) {\n this.apiKey = opts.apiKey;\n const interval = opts.flushIntervalMs ?? 30000;\n this.flushTimer = window.setInterval(() => this.flush(), interval);\n window.addEventListener('beforeunload', () => this.flushOnUnload());\n }\n\n public getApiKey(): string {\n return this.apiKey;\n }\n\n public capture(evt: Omit<BatchedEvent, 'id' | 'timestamp'>): string {\n const id = this.makeId();\n const timestamp = new Date().toISOString();\n const record: BatchedEvent = { id, timestamp, ...evt };\n\n if (this.queue.length >= (this.opts.maxBufferSize ?? 64)) {\n this.queue.shift();\n }\n this.queue.push(record);\n return id;\n }\n\n private async flush() {\n if (this.isFlushing || this.queue.length === 0) return;\n this.isFlushing = true;\n\n const batch = this.queue.splice(0, this.queue.length);\n try {\n await axios.post(\n this.opts.endpoint,\n { events: batch },\n {\n maxBodyLength: 1000 * 1024 * 1024, // 10MB\n maxContentLength: 1000 * 1024 * 1024, // 10MB\n timeout: 30000,\n headers: {\n 'Content-Type': 'application/json',\n },\n }\n );\n } catch {\n this.queue.unshift(...batch);\n } finally {\n this.isFlushing = false;\n }\n }\n\n private flushOnUnload() {\n if (!navigator.sendBeacon || this.queue.length === 0) return;\n const payload = JSON.stringify({ events: this.queue });\n navigator.sendBeacon(this.opts.endpoint, payload);\n }\n\n private makeId() {\n return 'xxxx-xxxx-4xxx-yxxx'.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\n public destroy() {\n clearInterval(this.flushTimer);\n }\n}\n","import type { eventWithTime, listenerHandler } from '@rrweb/types';\nimport { record } from 'rrweb';\n\nlet events: eventWithTime[] = [];\nconst MAX_EVENTS = 1000;\nlet stopFn: listenerHandler | undefined = undefined;\n\nexport function startRecording() {\n events = [];\n stopFn?.();\n stopFn = record({\n emit(event) {\n if (event.type === 2) console.log('[rrweb] FullSnapshot 기록됨:', event);\n\n events.push(event);\n if (events.length > MAX_EVENTS) {\n events = events.slice(-MAX_EVENTS);\n }\n },\n // checkoutEveryNms: 1000, // 1초마다 체크아웃\n checkoutEveryNms: 15000, // 15초마다 한 번\n checkoutEveryNth: 100, // 100개 이벤트마다 한 번\n maskAllInputs: true,\n sampling: {\n mouseInteraction: {\n MouseUp: false,\n MouseDown: false,\n Click: false,\n ContextMenu: false,\n DblClick: false,\n Focus: false,\n Blur: false,\n TouchStart: false,\n TouchEnd: false,\n },\n },\n });\n}\n\nexport function getRecordedEvents(\n beforeErrorSec = 10,\n errorTime = Date.now(),\n source = events\n): eventWithTime[] {\n const sliced = source.filter(\n (e) => errorTime - e.timestamp < beforeErrorSec * 1000\n );\n\n const snapshotCandidates = source.filter((e) => e.type === 2);\n const lastSnapshot = [...snapshotCandidates]\n .reverse()\n .find((e) => e.timestamp <= errorTime);\n\n if (lastSnapshot && !sliced.includes(lastSnapshot)) {\n return [lastSnapshot, ...sliced];\n }\n\n if (!sliced.some((e) => e.type === 2)) {\n console.warn('⚠️ Snapshot 없이 잘린 replay입니다. 복원 불가능할 수 있음.');\n }\n\n return sliced;\n}\n\nexport function clearEvents() {\n events = [];\n}\n\nexport function getCurrentEvents(): eventWithTime[] {\n return events.slice();\n}\n","import { zlibSync, unzlibSync } from 'fflate';\n\nexport function compressToBase64(obj: any): string {\n const json = JSON.stringify(obj);\n const compressed = zlibSync(new TextEncoder().encode(json));\n return btoa(String.fromCharCode(...compressed));\n}\n\nexport function decompressFromBase64(base64: string): any[] {\n const binary = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));\n const json = new TextDecoder().decode(unzlibSync(binary));\n return JSON.parse(json);\n}\n","import { getBrowserInfo, getEnvironment } from './environment';\nimport { ErrorBatcher } from './error-batcher';\nimport {\n startRecording,\n getRecordedEvents,\n getCurrentEvents,\n clearEvents,\n} from './recorder';\nimport { compressToBase64 } from './utils';\n\nexport interface AdditionalInfo {\n pageUrl: string;\n request: {\n url: string;\n method: string;\n headers: Record<string, string>;\n };\n response: {\n data: {\n message: string;\n errorCode: string;\n };\n status: number;\n statusText: string;\n };\n}\n\nexport interface BatchedEvent {\n id: string;\n timestamp: string;\n message: string;\n stacktrace: string;\n replay: string | null;\n environment: string;\n browser: string;\n os: string;\n userAgent: string;\n userId?: number;\n additionalInfo?: Partial<AdditionalInfo>;\n appVersion: string;\n apiKey: string;\n}\n\nexport interface BatcherOptions {\n endpoint: string;\n apiKey: string;\n flushIntervalMs?: number;\n maxBufferSize?: number;\n}\n\nexport interface InitOptions {\n endpoint: string;\n apiKey: string;\n flushIntervalMs?: number;\n maxBufferSize?: number;\n beforeErrorSec?: number;\n}\n\nlet batcher: ErrorBatcher;\nlet globalOpts: { beforeErrorSec: number } = { beforeErrorSec: 30 };\n\nexport function init(options: InitOptions) {\n globalOpts.beforeErrorSec = options.beforeErrorSec ?? 10;\n\n batcher = new ErrorBatcher({\n endpoint: options.endpoint,\n apiKey: options.apiKey,\n flushIntervalMs: options.flushIntervalMs,\n maxBufferSize: options.maxBufferSize,\n });\n\n if (typeof window !== 'undefined') {\n if (document.readyState === 'complete') {\n requestAnimationFrame(() => startRecording());\n } else {\n window.addEventListener('load', () => {\n requestAnimationFrame(() => startRecording());\n });\n }\n }\n}\n\nexport function captureException(\n error: Error,\n additionalInfo?: Partial<AdditionalInfo>,\n userId?: number\n): string {\n const errorTime = Date.now();\n const eventsSnapshot = getCurrentEvents();\n const rawReplay = getRecordedEvents(\n globalOpts.beforeErrorSec,\n errorTime,\n eventsSnapshot\n );\n\n clearEvents();\n\n const { browser, os, userAgent } = getBrowserInfo();\n\n const compressedReplay = compressToBase64(rawReplay);\n\n return batcher.capture({\n message: error.message ?? '',\n stacktrace: error.stack ?? '',\n replay: compressedReplay as any,\n environment: getEnvironment(),\n browser,\n os,\n userAgent,\n userId,\n additionalInfo,\n appVersion: '1.0.0',\n apiKey: batcher.getApiKey(),\n });\n}\n","import { captureException } from './reporter';\n\nexport function setupGlobalErrorHandler() {\n if ((window as any).__errorHandlerSetup) return;\n\n const origOnError = window.onerror;\n window.onerror = function thisWindowOnError(\n this: Window & WindowEventHandlers,\n message: string | Event,\n source?: string,\n lineno?: number,\n colno?: number,\n error?: Error\n ): boolean {\n origOnError?.call(this, message, source, lineno, colno, error);\n captureException(\n error ??\n new Error(typeof message === 'string' ? message : 'Unknown error')\n );\n return false;\n };\n\n const origOnUnhandledRejection = window.onunhandledrejection;\n window.onunhandledrejection = function thisWindowOnRejection(\n this: Window & WindowEventHandlers,\n event: PromiseRejectionEvent\n ): any {\n origOnUnhandledRejection?.call(this, event);\n const err =\n event.reason instanceof Error\n ? event.reason\n : new Error(JSON.stringify(event.reason));\n captureException(err);\n } as typeof window.onunhandledrejection;\n\n (window as any).__errorHandlerSetup = true;\n}\n","import { W3CTraceContextPropagator } from '@opentelemetry/core';\nimport { WebTracerProvider } from '@opentelemetry/sdk-trace-web';\nimport { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport {\n resourceFromAttributes,\n osDetector,\n detectResources,\n} from '@opentelemetry/resources';\nimport { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';\n\nexport interface OtelConfig {\n serviceName?: string;\n endpoint?: string;\n isSyntheticRequest?: boolean;\n scheduledDelayMillis?: number;\n customHeaders?: Record<string, string>;\n}\n\nexport const initOtel = async (config: OtelConfig = {}) => {\n const finalConfig = {\n serviceName: config.serviceName ?? 'replay',\n endpoint: config.endpoint ?? 'http://localhost:8081/traces',\n isSyntheticRequest: config.isSyntheticRequest ?? false,\n scheduledDelayMillis: config.scheduledDelayMillis ?? 500,\n customHeaders: {\n 'Content-Type': 'application/x-protobuf',\n ...config.customHeaders,\n },\n };\n\n const { ZoneContextManager } = await import('@opentelemetry/context-zone');\n\n let resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: finalConfig.serviceName,\n });\n if (finalConfig.isSyntheticRequest) {\n resource = resource.merge(\n resourceFromAttributes({ 'app.synthetic_request': 'true' })\n );\n }\n resource = resource.merge(detectResources({ detectors: [osDetector] }));\n\n const spanProcessor = new BatchSpanProcessor(\n new OTLPTraceExporter({\n url: finalConfig.endpoint,\n headers: finalConfig.customHeaders,\n }),\n { scheduledDelayMillis: finalConfig.scheduledDelayMillis }\n );\n const provider = new WebTracerProvider({\n resource,\n spanProcessors: [spanProcessor],\n });\n provider.register({\n contextManager: new ZoneContextManager(),\n propagator: new W3CTraceContextPropagator(),\n });\n\n if (typeof window !== 'undefined') {\n const backendOrigin = new URL(finalConfig.endpoint).origin;\n\n const [{ FetchInstrumentation }, { XMLHttpRequestInstrumentation }] =\n await Promise.all([\n import('@opentelemetry/instrumentation-fetch'),\n import('@opentelemetry/instrumentation-xml-http-request'),\n ]);\n\n const fetchInst = new FetchInstrumentation({\n propagateTraceHeaderCorsUrls: [backendOrigin],\n clearTimingResources: true,\n });\n const xhrInst = new XMLHttpRequestInstrumentation({\n propagateTraceHeaderCorsUrls: [backendOrigin],\n clearTimingResources: true,\n });\n\n fetchInst.setTracerProvider(provider);\n xhrInst.setTracerProvider(provider);\n fetchInst.enable();\n xhrInst.enable();\n }\n\n console.log('OpenTelemetry initialized successfully');\n return provider;\n};\n"],"mappings":"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","names":["record"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rusty-replay",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "description": "Lightweight error tracking and replay system for React apps using rrweb and Rust-powered backend integration.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -20,6 +20,11 @@
20
20
  "cli",
21
21
  "monitoring"
22
22
  ],
23
+ "browser": {
24
+ "require-in-the-middle": false,
25
+ "import-in-the-middle": false,
26
+ "@opentelemetry/instrumentation": false
27
+ },
23
28
  "author": "Jaeha Lee <wogkdkrm112@gmail.com>",
24
29
  "license": "MIT",
25
30
  "repository": {
@@ -34,9 +39,21 @@
34
39
  "@rrweb/types": "^2.0.0-alpha.4",
35
40
  "react": "^16.8 || ^17 || ^18 || ^19",
36
41
  "rrweb": "^2.0.0-alpha.4",
37
- "rrweb-player": "^1.0.0-alpha.4"
42
+ "rrweb-player": "^1.0.0-alpha.4",
43
+ "import-in-the-middle": "^1.13.1",
44
+ "require-in-the-middle": "^7.5.2"
38
45
  },
39
46
  "dependencies": {
47
+ "@opentelemetry/context-zone": "^2.0.0",
48
+ "@opentelemetry/core": "^2.0.0",
49
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.200.0",
50
+ "@opentelemetry/instrumentation": "^0.200.0",
51
+ "@opentelemetry/instrumentation-fetch": "^0.200.0",
52
+ "@opentelemetry/instrumentation-xml-http-request": "^0.200.0",
53
+ "@opentelemetry/resources": "^2.0.0",
54
+ "@opentelemetry/sdk-trace-base": "^2.0.0",
55
+ "@opentelemetry/sdk-trace-web": "^2.0.0",
56
+ "@opentelemetry/semantic-conventions": "^1.32.0",
40
57
  "@rrweb/packer": "2.0.0-alpha.18",
41
58
  "axios": "^1.8.4",
42
59
  "fflate": "^0.8.2",