rusty-replay 1.0.8 → 1.0.10

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
@@ -19,9 +19,6 @@ var __spreadValues = (a, b) => {
19
19
  }
20
20
  return a;
21
21
  };
22
- var __esm = (fn, res) => function __init() {
23
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
24
- };
25
22
  var __export = (target, all) => {
26
23
  for (var name in all)
27
24
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -44,12 +41,20 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
44
41
  ));
45
42
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
46
43
 
47
- // ../../node_modules/.pnpm/tsup@8.4.0_jiti@2.4.2_postcss@8.5.3_typescript@5.8.3/node_modules/tsup/assets/cjs_shims.js
48
- var init_cjs_shims = __esm({
49
- "../../node_modules/.pnpm/tsup@8.4.0_jiti@2.4.2_postcss@8.5.3_typescript@5.8.3/node_modules/tsup/assets/cjs_shims.js"() {
50
- "use strict";
51
- }
44
+ // src/index.ts
45
+ var index_exports = {};
46
+ __export(index_exports, {
47
+ ErrorBatcher: () => ErrorBatcher,
48
+ captureException: () => captureException,
49
+ decompressFromBase64: () => decompressFromBase64,
50
+ getBrowserInfo: () => getBrowserInfo,
51
+ getEnvironment: () => getEnvironment,
52
+ getRecordedEvents: () => getRecordedEvents,
53
+ init: () => init,
54
+ setupGlobalErrorHandler: () => setupGlobalErrorHandler,
55
+ startRecording: () => startRecording
52
56
  });
57
+ module.exports = __toCommonJS(index_exports);
53
58
 
54
59
  // src/environment.ts
55
60
  function getBrowserInfo() {
@@ -72,94 +77,83 @@ function getBrowserInfo() {
72
77
  }
73
78
  function getEnvironment() {
74
79
  if (process.env.NODE_ENV === "development") return "development";
75
- if (process.env.NEXT_PUBLIC_VERCEL_ENV === "preview") return "staging";
76
80
  return "production";
77
81
  }
78
- var init_environment = __esm({
79
- "src/environment.ts"() {
80
- "use strict";
81
- init_cjs_shims();
82
- }
83
- });
84
82
 
85
83
  // src/error-batcher.ts
86
- var import_axios, ErrorBatcher;
87
- var init_error_batcher = __esm({
88
- "src/error-batcher.ts"() {
89
- "use strict";
90
- init_cjs_shims();
91
- import_axios = __toESM(require("axios"), 1);
92
- ErrorBatcher = class {
93
- constructor(opts) {
94
- this.opts = opts;
95
- this.queue = [];
96
- this.isFlushing = false;
97
- var _a;
98
- this.apiKey = opts.apiKey;
99
- const interval = (_a = opts.flushIntervalMs) != null ? _a : 3e3;
100
- this.flushTimer = window.setInterval(() => this.flush(), interval);
101
- window.addEventListener("beforeunload", () => this.flushOnUnload());
102
- }
103
- getApiKey() {
104
- return this.apiKey;
105
- }
106
- capture(evt) {
107
- var _a;
108
- const id = this.makeId();
109
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
110
- const record2 = __spreadValues({ id, timestamp }, evt);
111
- if (this.queue.length >= ((_a = this.opts.maxBufferSize) != null ? _a : 64)) {
112
- this.queue.shift();
113
- }
114
- this.queue.push(record2);
115
- return id;
116
- }
117
- async flush() {
118
- if (this.isFlushing || this.queue.length === 0) return;
119
- this.isFlushing = true;
120
- const batch = this.queue.splice(0, this.queue.length);
121
- try {
122
- await import_axios.default.post(
123
- this.opts.endpoint,
124
- { events: batch },
125
- {
126
- maxBodyLength: 1e3 * 1024 * 1024,
127
- // 10MB
128
- maxContentLength: 1e3 * 1024 * 1024,
129
- // 10MB
130
- timeout: 3e4,
131
- headers: {
132
- "Content-Type": "application/json",
133
- Authorization: `Bearer ${this.opts.apiKey}`
134
- }
135
- }
136
- );
137
- } catch (e) {
138
- this.queue.unshift(...batch);
139
- } finally {
140
- this.isFlushing = false;
84
+ var import_axios = __toESM(require("axios"), 1);
85
+ var ErrorBatcher = class {
86
+ constructor(opts) {
87
+ this.opts = opts;
88
+ this.queue = [];
89
+ this.isFlushing = false;
90
+ var _a;
91
+ this.apiKey = opts.apiKey;
92
+ const interval = (_a = opts.flushIntervalMs) != null ? _a : 3e4;
93
+ this.flushTimer = window.setInterval(() => this.flush(), interval);
94
+ window.addEventListener("beforeunload", () => this.flushOnUnload());
95
+ }
96
+ getApiKey() {
97
+ return this.apiKey;
98
+ }
99
+ capture(evt) {
100
+ var _a;
101
+ const id = this.makeId();
102
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
103
+ const record2 = __spreadValues({ id, timestamp }, evt);
104
+ if (this.queue.length >= ((_a = this.opts.maxBufferSize) != null ? _a : 64)) {
105
+ this.queue.shift();
106
+ }
107
+ this.queue.push(record2);
108
+ return id;
109
+ }
110
+ async flush() {
111
+ if (this.isFlushing || this.queue.length === 0) return;
112
+ this.isFlushing = true;
113
+ const batch = this.queue.splice(0, this.queue.length);
114
+ try {
115
+ await import_axios.default.post(
116
+ this.opts.endpoint,
117
+ { events: batch },
118
+ {
119
+ maxBodyLength: 1e3 * 1024 * 1024,
120
+ // 10MB
121
+ maxContentLength: 1e3 * 1024 * 1024,
122
+ // 10MB
123
+ timeout: 3e4,
124
+ headers: {
125
+ "Content-Type": "application/json"
126
+ }
141
127
  }
142
- }
143
- flushOnUnload() {
144
- if (!navigator.sendBeacon || this.queue.length === 0) return;
145
- const payload = JSON.stringify({ events: this.queue });
146
- navigator.sendBeacon(this.opts.endpoint, payload);
147
- }
148
- makeId() {
149
- return "xxxx-xxxx-4xxx-yxxx".replace(/[xy]/g, (c) => {
150
- const r = Math.random() * 16 | 0;
151
- const v = c === "x" ? r : r & 3 | 8;
152
- return v.toString(16);
153
- });
154
- }
155
- destroy() {
156
- clearInterval(this.flushTimer);
157
- }
158
- };
128
+ );
129
+ } catch (e) {
130
+ this.queue.unshift(...batch);
131
+ } finally {
132
+ this.isFlushing = false;
133
+ }
159
134
  }
160
- });
135
+ flushOnUnload() {
136
+ if (!navigator.sendBeacon || this.queue.length === 0) return;
137
+ const payload = JSON.stringify({ events: this.queue });
138
+ navigator.sendBeacon(this.opts.endpoint, payload);
139
+ }
140
+ makeId() {
141
+ return "xxxx-xxxx-4xxx-yxxx".replace(/[xy]/g, (c) => {
142
+ const r = Math.random() * 16 | 0;
143
+ const v = c === "x" ? r : r & 3 | 8;
144
+ return v.toString(16);
145
+ });
146
+ }
147
+ destroy() {
148
+ clearInterval(this.flushTimer);
149
+ }
150
+ };
161
151
 
162
152
  // src/recorder.ts
153
+ var import_rrweb = require("rrweb");
154
+ var events = [];
155
+ var MAX_EVENTS = 1e3;
156
+ var stopFn = void 0;
163
157
  function startRecording() {
164
158
  events = [];
165
159
  stopFn == null ? void 0 : stopFn();
@@ -174,9 +168,22 @@ function startRecording() {
174
168
  // checkoutEveryNms: 1000, // 1초마다 체크아웃
175
169
  checkoutEveryNms: 15e3,
176
170
  // 15초마다 한 번
177
- checkoutEveryNth: 100
171
+ checkoutEveryNth: 100,
178
172
  // 100개 이벤트마다 한 번
179
- // packFn: pack,
173
+ maskAllInputs: true,
174
+ sampling: {
175
+ mouseInteraction: {
176
+ MouseUp: false,
177
+ MouseDown: false,
178
+ Click: false,
179
+ ContextMenu: false,
180
+ DblClick: false,
181
+ Focus: false,
182
+ Blur: false,
183
+ TouchStart: false,
184
+ TouchEnd: false
185
+ }
186
+ }
180
187
  });
181
188
  }
182
189
  function getRecordedEvents(beforeErrorSec = 10, errorTime = Date.now(), source = events) {
@@ -199,50 +206,23 @@ function clearEvents() {
199
206
  function getCurrentEvents() {
200
207
  return events.slice();
201
208
  }
202
- var import_rrweb, events, MAX_EVENTS, stopFn;
203
- var init_recorder = __esm({
204
- "src/recorder.ts"() {
205
- "use strict";
206
- init_cjs_shims();
207
- import_rrweb = require("rrweb");
208
- events = [];
209
- MAX_EVENTS = 1e3;
210
- stopFn = void 0;
211
- }
212
- });
213
209
 
214
- // src/handler.ts
215
- var handler_exports = {};
216
- __export(handler_exports, {
217
- setupGlobalErrorHandler: () => setupGlobalErrorHandler
218
- });
219
- function setupGlobalErrorHandler() {
220
- if (window.__errorHandlerSetup) return;
221
- const origOnError = window.onerror;
222
- window.onerror = function thisWindowOnError(message, source, lineno, colno, error) {
223
- origOnError == null ? void 0 : origOnError.call(this, message, source, lineno, colno, error);
224
- captureException(
225
- error != null ? error : new Error(typeof message === "string" ? message : "Unknown error")
226
- );
227
- return false;
228
- };
229
- const origOnUnhandledRejection = window.onunhandledrejection;
230
- window.onunhandledrejection = function thisWindowOnRejection(event) {
231
- origOnUnhandledRejection == null ? void 0 : origOnUnhandledRejection.call(this, event);
232
- const err = event.reason instanceof Error ? event.reason : new Error(JSON.stringify(event.reason));
233
- captureException(err);
234
- };
235
- window.__errorHandlerSetup = true;
210
+ // src/utils.ts
211
+ var import_fflate = require("fflate");
212
+ function compressToBase64(obj) {
213
+ const json = JSON.stringify(obj);
214
+ const compressed = (0, import_fflate.zlibSync)(new TextEncoder().encode(json));
215
+ return btoa(String.fromCharCode(...compressed));
216
+ }
217
+ function decompressFromBase64(base64) {
218
+ const binary = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
219
+ const json = new TextDecoder().decode((0, import_fflate.unzlibSync)(binary));
220
+ return JSON.parse(json);
236
221
  }
237
- var init_handler = __esm({
238
- "src/handler.ts"() {
239
- "use strict";
240
- init_cjs_shims();
241
- init_reporter();
242
- }
243
- });
244
222
 
245
223
  // src/reporter.ts
224
+ var batcher;
225
+ var globalOpts = { beforeErrorSec: 30 };
246
226
  function init(options) {
247
227
  var _a;
248
228
  globalOpts.beforeErrorSec = (_a = options.beforeErrorSec) != null ? _a : 10;
@@ -253,10 +233,6 @@ function init(options) {
253
233
  maxBufferSize: options.maxBufferSize
254
234
  });
255
235
  if (typeof window !== "undefined") {
256
- const start = () => {
257
- startRecording();
258
- Promise.resolve().then(() => (init_handler(), handler_exports)).then((mod) => mod.setupGlobalErrorHandler());
259
- };
260
236
  if (document.readyState === "complete") {
261
237
  requestAnimationFrame(() => startRecording());
262
238
  } else {
@@ -270,17 +246,18 @@ function captureException(error, additionalInfo, userId) {
270
246
  var _a, _b;
271
247
  const errorTime = Date.now();
272
248
  const eventsSnapshot = getCurrentEvents();
273
- const replay = getRecordedEvents(
249
+ const rawReplay = getRecordedEvents(
274
250
  globalOpts.beforeErrorSec,
275
251
  errorTime,
276
252
  eventsSnapshot
277
253
  );
278
254
  clearEvents();
279
255
  const { browser, os, userAgent } = getBrowserInfo();
256
+ const compressedReplay = compressToBase64(rawReplay);
280
257
  return batcher.capture({
281
258
  message: (_a = error.message) != null ? _a : "",
282
259
  stacktrace: (_b = error.stack) != null ? _b : "",
283
- replay,
260
+ replay: compressedReplay,
284
261
  environment: getEnvironment(),
285
262
  browser,
286
263
  os,
@@ -291,41 +268,31 @@ function captureException(error, additionalInfo, userId) {
291
268
  apiKey: batcher.getApiKey()
292
269
  });
293
270
  }
294
- var batcher, globalOpts;
295
- var init_reporter = __esm({
296
- "src/reporter.ts"() {
297
- "use strict";
298
- init_cjs_shims();
299
- init_environment();
300
- init_error_batcher();
301
- init_recorder();
302
- globalOpts = { beforeErrorSec: 30 };
303
- }
304
- });
305
271
 
306
- // src/index.ts
307
- var index_exports = {};
308
- __export(index_exports, {
309
- ErrorBatcher: () => ErrorBatcher,
310
- captureException: () => captureException,
311
- getBrowserInfo: () => getBrowserInfo,
312
- getEnvironment: () => getEnvironment,
313
- getRecordedEvents: () => getRecordedEvents,
314
- init: () => init,
315
- setupGlobalErrorHandler: () => setupGlobalErrorHandler,
316
- startRecording: () => startRecording
317
- });
318
- module.exports = __toCommonJS(index_exports);
319
- init_cjs_shims();
320
- init_reporter();
321
- init_handler();
322
- init_recorder();
323
- init_environment();
324
- init_error_batcher();
272
+ // src/handler.ts
273
+ function setupGlobalErrorHandler() {
274
+ if (window.__errorHandlerSetup) return;
275
+ const origOnError = window.onerror;
276
+ window.onerror = function thisWindowOnError(message, source, lineno, colno, error) {
277
+ origOnError == null ? void 0 : origOnError.call(this, message, source, lineno, colno, error);
278
+ captureException(
279
+ error != null ? error : new Error(typeof message === "string" ? message : "Unknown error")
280
+ );
281
+ return false;
282
+ };
283
+ const origOnUnhandledRejection = window.onunhandledrejection;
284
+ window.onunhandledrejection = function thisWindowOnRejection(event) {
285
+ origOnUnhandledRejection == null ? void 0 : origOnUnhandledRejection.call(this, event);
286
+ const err = event.reason instanceof Error ? event.reason : new Error(JSON.stringify(event.reason));
287
+ captureException(err);
288
+ };
289
+ window.__errorHandlerSetup = true;
290
+ }
325
291
  // Annotate the CommonJS export names for ESM import in node:
326
292
  0 && (module.exports = {
327
293
  ErrorBatcher,
328
294
  captureException,
295
+ decompressFromBase64,
329
296
  getBrowserInfo,
330
297
  getEnvironment,
331
298
  getRecordedEvents,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../node_modules/.pnpm/tsup@8.4.0_jiti@2.4.2_postcss@8.5.3_typescript@5.8.3/node_modules/tsup/assets/cjs_shims.js","../src/environment.ts","../src/error-batcher.ts","../src/recorder.ts","../src/handler.ts","../src/reporter.ts","../src/index.ts"],"sourcesContent":["// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () =>\n typeof document === 'undefined'\n ? new URL(`file:${__filename}`).href\n : (document.currentScript && document.currentScript.src) ||\n new URL('main.js', document.baseURI).href\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\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 if (process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview') return 'staging';\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 ?? 3000;\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 Authorization: `Bearer ${this.opts.apiKey}`,\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';\nimport { pack } from '@rrweb/packer';\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 // packFn: pack,\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\n// export 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 fullSnapshots = source.filter((e) => e.type === 2);\n// const lastSnapshot = fullSnapshots.reverse().find(\n// (e) => e.timestamp <= sliced[0]?.timestamp\n// );\n// // const lastSnapshot = fullSnapshots\n// // .reverse()\n// // .find((e) => e.timestamp <= (sliced[0]?.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 { 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 { getBrowserInfo, getEnvironment } from './environment';\nimport { ErrorBatcher } from './error-batcher';\nimport {\n startRecording,\n getRecordedEvents,\n getCurrentEvents,\n clearEvents,\n} from './recorder';\n\nexport interface BatchedEvent {\n id: string;\n timestamp: string;\n message: string;\n stacktrace: string;\n replay: any[];\n environment: string;\n browser: string;\n os: string;\n userAgent: string;\n userId?: number;\n additionalInfo?: Record<string, any>;\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 const start = () => {\n startRecording(); // DOM 렌더링 후에 시작되도록 지연 실행\n import('./handler.js').then((mod) => mod.setupGlobalErrorHandler());\n };\n\n if (document.readyState === 'complete') {\n requestAnimationFrame(() => startRecording());\n } else {\n window.addEventListener('load', () => {\n requestAnimationFrame(() => startRecording());\n });\n }\n\n // if ('requestIdleCallback' in window) {\n // window.requestIdleCallback(start);\n // } else {\n // setTimeout(start, 100);\n // }\n }\n}\n\nexport function captureException(\n error: Error,\n additionalInfo?: Record<string, any>,\n userId?: number\n): string {\n const errorTime = Date.now();\n const eventsSnapshot = getCurrentEvents();\n const replay = getRecordedEvents(\n globalOpts.beforeErrorSec,\n errorTime,\n eventsSnapshot\n );\n\n clearEvents(); // 다음 에러 기록을 위해 초기화\n\n const { browser, os, userAgent } = getBrowserInfo();\n\n return batcher.capture({\n message: error.message ?? '',\n stacktrace: error.stack ?? '',\n replay,\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","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 } from './reporter';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,SAAS,iBAAiB;AAC/B,QAAM,KAAK,UAAU;AACrB,MAAI,UAAU,WACZ,KAAK;AAEP,MAAI,GAAG,SAAS,SAAS,EAAG,WAAU;AAAA,WAC7B,GAAG,SAAS,gBAAgB,EAAG,WAAU;AAAA,WACzC,GAAG,SAAS,OAAO,KAAK,GAAG,SAAS,KAAK,EAAG,WAAU;AAAA,WACtD,GAAG,SAAS,SAAS,EAAG,WAAU;AAAA,WAClC,GAAG,SAAS,MAAM,EAAG,WAAU;AAAA,WAC/B,GAAG,SAAS,KAAK,EAAG,WAAU;AAAA,WAC9B,GAAG,SAAS,QAAQ,EAAG,WAAU;AAAA,WACjC,GAAG,SAAS,QAAQ,EAAG,WAAU;AAE1C,MAAI,GAAG,SAAS,SAAS,EAAG,MAAK;AAAA,WACxB,GAAG,SAAS,KAAK,EAAG,MAAK;AAAA,WACzB,GAAG,SAAS,OAAO,EAAG,MAAK;AAAA,WAC3B,GAAG,SAAS,SAAS,EAAG,MAAK;AAAA,WAC7B,GAAG,SAAS,UAAU,EAAG,MAAK;AAEvC,SAAO,EAAE,SAAS,IAAI,WAAW,GAAG;AACtC;AAEO,SAAS,iBAA2D;AACzE,MAAI,QAAQ,IAAI,aAAa,cAAe,QAAO;AACnD,MAAI,QAAQ,IAAI,2BAA2B,UAAW,QAAO;AAC7D,SAAO;AACT;AA3BA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAGa;AAHb;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAGX,IAAM,eAAN,MAAmB;AAAA,MAMxB,YAAoB,MAAsB;AAAtB;AALpB,aAAQ,QAAwB,CAAC;AACjC,aAAQ,aAAa;AALvB;AAUI,aAAK,SAAS,KAAK;AACnB,cAAM,YAAW,UAAK,oBAAL,YAAwB;AACzC,aAAK,aAAa,OAAO,YAAY,MAAM,KAAK,MAAM,GAAG,QAAQ;AACjE,eAAO,iBAAiB,gBAAgB,MAAM,KAAK,cAAc,CAAC;AAAA,MACpE;AAAA,MAEO,YAAoB;AACzB,eAAO,KAAK;AAAA,MACd;AAAA,MAEO,QAAQ,KAAqD;AApBtE;AAqBI,cAAM,KAAK,KAAK,OAAO;AACvB,cAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,cAAMA,UAAuB,iBAAE,IAAI,aAAc;AAEjD,YAAI,KAAK,MAAM,YAAW,UAAK,KAAK,kBAAV,YAA2B,KAAK;AACxD,eAAK,MAAM,MAAM;AAAA,QACnB;AACA,aAAK,MAAM,KAAKA,OAAM;AACtB,eAAO;AAAA,MACT;AAAA,MAEA,MAAc,QAAQ;AACpB,YAAI,KAAK,cAAc,KAAK,MAAM,WAAW,EAAG;AAChD,aAAK,aAAa;AAElB,cAAM,QAAQ,KAAK,MAAM,OAAO,GAAG,KAAK,MAAM,MAAM;AACpD,YAAI;AACF,gBAAM,aAAAC,QAAM;AAAA,YACV,KAAK,KAAK;AAAA,YACV,EAAE,QAAQ,MAAM;AAAA,YAChB;AAAA,cACE,eAAe,MAAO,OAAO;AAAA;AAAA,cAC7B,kBAAkB,MAAO,OAAO;AAAA;AAAA,cAChC,SAAS;AAAA,cACT,SAAS;AAAA,gBACP,gBAAgB;AAAA,gBAChB,eAAe,UAAU,KAAK,KAAK,MAAM;AAAA,cAC3C;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAQ;AACN,eAAK,MAAM,QAAQ,GAAG,KAAK;AAAA,QAC7B,UAAE;AACA,eAAK,aAAa;AAAA,QACpB;AAAA,MACF;AAAA,MAEQ,gBAAgB;AACtB,YAAI,CAAC,UAAU,cAAc,KAAK,MAAM,WAAW,EAAG;AACtD,cAAM,UAAU,KAAK,UAAU,EAAE,QAAQ,KAAK,MAAM,CAAC;AACrD,kBAAU,WAAW,KAAK,KAAK,UAAU,OAAO;AAAA,MAClD;AAAA,MAEQ,SAAS;AACf,eAAO,sBAAsB,QAAQ,SAAS,CAAC,MAAM;AACnD,gBAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,gBAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,iBAAO,EAAE,SAAS,EAAE;AAAA,QACtB,CAAC;AAAA,MACH;AAAA,MAEO,UAAU;AACf,sBAAc,KAAK,UAAU;AAAA,MAC/B;AAAA,IACF;AAAA;AAAA;;;ACnEO,SAAS,iBAAiB;AAC/B,WAAS,CAAC;AACV;AACA,eAAS,qBAAO;AAAA,IACd,KAAK,OAAO;AACV,UAAI,MAAM,SAAS,EAAG,SAAQ,IAAI,4CAA6B,KAAK;AAEpE,aAAO,KAAK,KAAK;AACjB,UAAI,OAAO,SAAS,YAAY;AAC9B,iBAAS,OAAO,MAAM,CAAC,UAAU;AAAA,MACnC;AAAA,IACF;AAAA;AAAA,IAEA,kBAAkB;AAAA;AAAA,IAClB,kBAAkB;AAAA;AAAA;AAAA,EAEpB,CAAC;AACH;AAEO,SAAS,kBACd,iBAAiB,IACjB,YAAY,KAAK,IAAI,GACrB,SAAS,QACQ;AACjB,QAAM,SAAS,OAAO;AAAA,IACpB,CAAC,MAAM,YAAY,EAAE,YAAY,iBAAiB;AAAA,EACpD;AAEA,QAAM,qBAAqB,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC5D,QAAM,eAAe,CAAC,GAAG,kBAAkB,EACxC,QAAQ,EACR,KAAK,CAAC,MAAM,EAAE,aAAa,SAAS;AAEvC,MAAI,gBAAgB,CAAC,OAAO,SAAS,YAAY,GAAG;AAClD,WAAO,CAAC,cAAc,GAAG,MAAM;AAAA,EACjC;AAEA,MAAI,CAAC,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG;AACrC,YAAQ,KAAK,sIAA4C;AAAA,EAC3D;AAEA,SAAO;AACT;AA8BO,SAAS,cAAc;AAC5B,WAAS,CAAC;AACZ;AAEO,SAAS,mBAAoC;AAClD,SAAO,OAAO,MAAM;AACtB;AAtFA,IACA,cAGI,QACE,YACF;AANJ;AAAA;AAAA;AAAA;AACA,mBAAuB;AAGvB,IAAI,SAA0B,CAAC;AAC/B,IAAM,aAAa;AACnB,IAAI,SAAsC;AAAA;AAAA;;;ACN1C;AAAA;AAAA;AAAA;AAEO,SAAS,0BAA0B;AACxC,MAAK,OAAe,oBAAqB;AAEzC,QAAM,cAAc,OAAO;AAC3B,SAAO,UAAU,SAAS,kBAExB,SACA,QACA,QACA,OACA,OACS;AACT,+CAAa,KAAK,MAAM,SAAS,QAAQ,QAAQ,OAAO;AACxD;AAAA,MACE,wBACE,IAAI,MAAM,OAAO,YAAY,WAAW,UAAU,eAAe;AAAA,IACrE;AACA,WAAO;AAAA,EACT;AAEA,QAAM,2BAA2B,OAAO;AACxC,SAAO,uBAAuB,SAAS,sBAErC,OACK;AACL,yEAA0B,KAAK,MAAM;AACrC,UAAM,MACJ,MAAM,kBAAkB,QACpB,MAAM,SACN,IAAI,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC;AAC5C,qBAAiB,GAAG;AAAA,EACtB;AAEA,EAAC,OAAe,sBAAsB;AACxC;AApCA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC2CO,SAAS,KAAK,SAAsB;AA3C3C;AA4CE,aAAW,kBAAiB,aAAQ,mBAAR,YAA0B;AAEtD,YAAU,IAAI,aAAa;AAAA,IACzB,UAAU,QAAQ;AAAA,IAClB,QAAQ,QAAQ;AAAA,IAChB,iBAAiB,QAAQ;AAAA,IACzB,eAAe,QAAQ;AAAA,EACzB,CAAC;AAED,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,QAAQ,MAAM;AAClB,qBAAe;AACf,sEAAuB,KAAK,CAAC,QAAQ,IAAI,wBAAwB,CAAC;AAAA,IACpE;AAEA,QAAI,SAAS,eAAe,YAAY;AACtC,4BAAsB,MAAM,eAAe,CAAC;AAAA,IAC9C,OAAO;AACL,aAAO,iBAAiB,QAAQ,MAAM;AACpC,8BAAsB,MAAM,eAAe,CAAC;AAAA,MAC9C,CAAC;AAAA,IACH;AAAA,EAOF;AACF;AAEO,SAAS,iBACd,OACA,gBACA,QACQ;AA/EV;AAgFE,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,iBAAiB,iBAAiB;AACxC,QAAM,SAAS;AAAA,IACb,WAAW;AAAA,IACX;AAAA,IACA;AAAA,EACF;AAEA,cAAY;AAEZ,QAAM,EAAE,SAAS,IAAI,UAAU,IAAI,eAAe;AAElD,SAAO,QAAQ,QAAQ;AAAA,IACrB,UAAS,WAAM,YAAN,YAAiB;AAAA,IAC1B,aAAY,WAAM,UAAN,YAAe;AAAA,IAC3B;AAAA,IACA,aAAa,eAAe;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,QAAQ,QAAQ,UAAU;AAAA,EAC5B,CAAC;AACH;AAzGA,IAwCI,SACA;AAzCJ;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAuCA,IAAI,aAAyC,EAAE,gBAAgB,GAAG;AAAA;AAAA;;;ACzClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;","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"],"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"]}
package/dist/index.d.cts CHANGED
@@ -1,17 +1,33 @@
1
1
  import { eventWithTime } from '@rrweb/types';
2
2
 
3
+ interface AdditionalInfo {
4
+ pageUrl: string;
5
+ request: {
6
+ url: string;
7
+ method: string;
8
+ headers: Record<string, string>;
9
+ };
10
+ response: {
11
+ data: {
12
+ message: string;
13
+ errorCode: string;
14
+ };
15
+ status: number;
16
+ statusText: string;
17
+ };
18
+ }
3
19
  interface BatchedEvent {
4
20
  id: string;
5
21
  timestamp: string;
6
22
  message: string;
7
23
  stacktrace: string;
8
- replay: any[];
24
+ replay: string | null;
9
25
  environment: string;
10
26
  browser: string;
11
27
  os: string;
12
28
  userAgent: string;
13
29
  userId?: number;
14
- additionalInfo?: Record<string, any>;
30
+ additionalInfo?: Partial<AdditionalInfo>;
15
31
  appVersion: string;
16
32
  apiKey: string;
17
33
  }
@@ -29,7 +45,7 @@ interface InitOptions {
29
45
  beforeErrorSec?: number;
30
46
  }
31
47
  declare function init(options: InitOptions): void;
32
- declare function captureException(error: Error, additionalInfo?: Record<string, any>, userId?: number): string;
48
+ declare function captureException(error: Error, additionalInfo?: Partial<AdditionalInfo>, userId?: number): string;
33
49
 
34
50
  declare function setupGlobalErrorHandler(): void;
35
51
 
@@ -58,4 +74,6 @@ declare class ErrorBatcher {
58
74
  destroy(): void;
59
75
  }
60
76
 
61
- export { type BatchedEvent, type BatcherOptions, ErrorBatcher, type InitOptions, captureException, getBrowserInfo, getEnvironment, getRecordedEvents, init, setupGlobalErrorHandler, startRecording };
77
+ declare function decompressFromBase64(base64: string): any[];
78
+
79
+ export { type AdditionalInfo, type BatchedEvent, type BatcherOptions, ErrorBatcher, type InitOptions, captureException, decompressFromBase64, getBrowserInfo, getEnvironment, getRecordedEvents, init, setupGlobalErrorHandler, startRecording };
package/dist/index.d.ts CHANGED
@@ -1,17 +1,33 @@
1
1
  import { eventWithTime } from '@rrweb/types';
2
2
 
3
+ interface AdditionalInfo {
4
+ pageUrl: string;
5
+ request: {
6
+ url: string;
7
+ method: string;
8
+ headers: Record<string, string>;
9
+ };
10
+ response: {
11
+ data: {
12
+ message: string;
13
+ errorCode: string;
14
+ };
15
+ status: number;
16
+ statusText: string;
17
+ };
18
+ }
3
19
  interface BatchedEvent {
4
20
  id: string;
5
21
  timestamp: string;
6
22
  message: string;
7
23
  stacktrace: string;
8
- replay: any[];
24
+ replay: string | null;
9
25
  environment: string;
10
26
  browser: string;
11
27
  os: string;
12
28
  userAgent: string;
13
29
  userId?: number;
14
- additionalInfo?: Record<string, any>;
30
+ additionalInfo?: Partial<AdditionalInfo>;
15
31
  appVersion: string;
16
32
  apiKey: string;
17
33
  }
@@ -29,7 +45,7 @@ interface InitOptions {
29
45
  beforeErrorSec?: number;
30
46
  }
31
47
  declare function init(options: InitOptions): void;
32
- declare function captureException(error: Error, additionalInfo?: Record<string, any>, userId?: number): string;
48
+ declare function captureException(error: Error, additionalInfo?: Partial<AdditionalInfo>, userId?: number): string;
33
49
 
34
50
  declare function setupGlobalErrorHandler(): void;
35
51
 
@@ -58,4 +74,6 @@ declare class ErrorBatcher {
58
74
  destroy(): void;
59
75
  }
60
76
 
61
- export { type BatchedEvent, type BatcherOptions, ErrorBatcher, type InitOptions, captureException, getBrowserInfo, getEnvironment, getRecordedEvents, init, setupGlobalErrorHandler, startRecording };
77
+ declare function decompressFromBase64(base64: string): any[];
78
+
79
+ export { type AdditionalInfo, type BatchedEvent, type BatcherOptions, ErrorBatcher, type InitOptions, captureException, decompressFromBase64, getBrowserInfo, getEnvironment, getRecordedEvents, init, setupGlobalErrorHandler, startRecording };
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  var __defProp = Object.defineProperty;
2
- var __getOwnPropNames = Object.getOwnPropertyNames;
3
2
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
4
3
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
4
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -15,20 +14,6 @@ var __spreadValues = (a, b) => {
15
14
  }
16
15
  return a;
17
16
  };
18
- var __esm = (fn, res) => function __init() {
19
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
20
- };
21
- var __export = (target, all) => {
22
- for (var name in all)
23
- __defProp(target, name, { get: all[name], enumerable: true });
24
- };
25
-
26
- // ../../node_modules/.pnpm/tsup@8.4.0_jiti@2.4.2_postcss@8.5.3_typescript@5.8.3/node_modules/tsup/assets/esm_shims.js
27
- var init_esm_shims = __esm({
28
- "../../node_modules/.pnpm/tsup@8.4.0_jiti@2.4.2_postcss@8.5.3_typescript@5.8.3/node_modules/tsup/assets/esm_shims.js"() {
29
- "use strict";
30
- }
31
- });
32
17
 
33
18
  // src/environment.ts
34
19
  function getBrowserInfo() {
@@ -51,95 +36,83 @@ function getBrowserInfo() {
51
36
  }
52
37
  function getEnvironment() {
53
38
  if (process.env.NODE_ENV === "development") return "development";
54
- if (process.env.NEXT_PUBLIC_VERCEL_ENV === "preview") return "staging";
55
39
  return "production";
56
40
  }
57
- var init_environment = __esm({
58
- "src/environment.ts"() {
59
- "use strict";
60
- init_esm_shims();
61
- }
62
- });
63
41
 
64
42
  // src/error-batcher.ts
65
43
  import axios from "axios";
66
- var ErrorBatcher;
67
- var init_error_batcher = __esm({
68
- "src/error-batcher.ts"() {
69
- "use strict";
70
- init_esm_shims();
71
- ErrorBatcher = class {
72
- constructor(opts) {
73
- this.opts = opts;
74
- this.queue = [];
75
- this.isFlushing = false;
76
- var _a;
77
- this.apiKey = opts.apiKey;
78
- const interval = (_a = opts.flushIntervalMs) != null ? _a : 3e3;
79
- this.flushTimer = window.setInterval(() => this.flush(), interval);
80
- window.addEventListener("beforeunload", () => this.flushOnUnload());
81
- }
82
- getApiKey() {
83
- return this.apiKey;
84
- }
85
- capture(evt) {
86
- var _a;
87
- const id = this.makeId();
88
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
89
- const record2 = __spreadValues({ id, timestamp }, evt);
90
- if (this.queue.length >= ((_a = this.opts.maxBufferSize) != null ? _a : 64)) {
91
- this.queue.shift();
92
- }
93
- this.queue.push(record2);
94
- return id;
95
- }
96
- async flush() {
97
- if (this.isFlushing || this.queue.length === 0) return;
98
- this.isFlushing = true;
99
- const batch = this.queue.splice(0, this.queue.length);
100
- try {
101
- await axios.post(
102
- this.opts.endpoint,
103
- { events: batch },
104
- {
105
- maxBodyLength: 1e3 * 1024 * 1024,
106
- // 10MB
107
- maxContentLength: 1e3 * 1024 * 1024,
108
- // 10MB
109
- timeout: 3e4,
110
- headers: {
111
- "Content-Type": "application/json",
112
- Authorization: `Bearer ${this.opts.apiKey}`
113
- }
114
- }
115
- );
116
- } catch (e) {
117
- this.queue.unshift(...batch);
118
- } finally {
119
- this.isFlushing = false;
44
+ var ErrorBatcher = class {
45
+ constructor(opts) {
46
+ this.opts = opts;
47
+ this.queue = [];
48
+ this.isFlushing = false;
49
+ var _a;
50
+ this.apiKey = opts.apiKey;
51
+ const interval = (_a = opts.flushIntervalMs) != null ? _a : 3e4;
52
+ this.flushTimer = window.setInterval(() => this.flush(), interval);
53
+ window.addEventListener("beforeunload", () => this.flushOnUnload());
54
+ }
55
+ getApiKey() {
56
+ return this.apiKey;
57
+ }
58
+ capture(evt) {
59
+ var _a;
60
+ const id = this.makeId();
61
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
62
+ const record2 = __spreadValues({ id, timestamp }, evt);
63
+ if (this.queue.length >= ((_a = this.opts.maxBufferSize) != null ? _a : 64)) {
64
+ this.queue.shift();
65
+ }
66
+ this.queue.push(record2);
67
+ return id;
68
+ }
69
+ async flush() {
70
+ if (this.isFlushing || this.queue.length === 0) return;
71
+ this.isFlushing = true;
72
+ const batch = this.queue.splice(0, this.queue.length);
73
+ try {
74
+ await axios.post(
75
+ this.opts.endpoint,
76
+ { events: batch },
77
+ {
78
+ maxBodyLength: 1e3 * 1024 * 1024,
79
+ // 10MB
80
+ maxContentLength: 1e3 * 1024 * 1024,
81
+ // 10MB
82
+ timeout: 3e4,
83
+ headers: {
84
+ "Content-Type": "application/json"
85
+ }
120
86
  }
121
- }
122
- flushOnUnload() {
123
- if (!navigator.sendBeacon || this.queue.length === 0) return;
124
- const payload = JSON.stringify({ events: this.queue });
125
- navigator.sendBeacon(this.opts.endpoint, payload);
126
- }
127
- makeId() {
128
- return "xxxx-xxxx-4xxx-yxxx".replace(/[xy]/g, (c) => {
129
- const r = Math.random() * 16 | 0;
130
- const v = c === "x" ? r : r & 3 | 8;
131
- return v.toString(16);
132
- });
133
- }
134
- destroy() {
135
- clearInterval(this.flushTimer);
136
- }
137
- };
87
+ );
88
+ } catch (e) {
89
+ this.queue.unshift(...batch);
90
+ } finally {
91
+ this.isFlushing = false;
92
+ }
93
+ }
94
+ flushOnUnload() {
95
+ if (!navigator.sendBeacon || this.queue.length === 0) return;
96
+ const payload = JSON.stringify({ events: this.queue });
97
+ navigator.sendBeacon(this.opts.endpoint, payload);
98
+ }
99
+ makeId() {
100
+ return "xxxx-xxxx-4xxx-yxxx".replace(/[xy]/g, (c) => {
101
+ const r = Math.random() * 16 | 0;
102
+ const v = c === "x" ? r : r & 3 | 8;
103
+ return v.toString(16);
104
+ });
105
+ }
106
+ destroy() {
107
+ clearInterval(this.flushTimer);
138
108
  }
139
- });
109
+ };
140
110
 
141
111
  // src/recorder.ts
142
112
  import { record } from "rrweb";
113
+ var events = [];
114
+ var MAX_EVENTS = 1e3;
115
+ var stopFn = void 0;
143
116
  function startRecording() {
144
117
  events = [];
145
118
  stopFn == null ? void 0 : stopFn();
@@ -154,9 +127,22 @@ function startRecording() {
154
127
  // checkoutEveryNms: 1000, // 1초마다 체크아웃
155
128
  checkoutEveryNms: 15e3,
156
129
  // 15초마다 한 번
157
- checkoutEveryNth: 100
130
+ checkoutEveryNth: 100,
158
131
  // 100개 이벤트마다 한 번
159
- // packFn: pack,
132
+ maskAllInputs: true,
133
+ sampling: {
134
+ mouseInteraction: {
135
+ MouseUp: false,
136
+ MouseDown: false,
137
+ Click: false,
138
+ ContextMenu: false,
139
+ DblClick: false,
140
+ Focus: false,
141
+ Blur: false,
142
+ TouchStart: false,
143
+ TouchEnd: false
144
+ }
145
+ }
160
146
  });
161
147
  }
162
148
  function getRecordedEvents(beforeErrorSec = 10, errorTime = Date.now(), source = events) {
@@ -179,49 +165,23 @@ function clearEvents() {
179
165
  function getCurrentEvents() {
180
166
  return events.slice();
181
167
  }
182
- var events, MAX_EVENTS, stopFn;
183
- var init_recorder = __esm({
184
- "src/recorder.ts"() {
185
- "use strict";
186
- init_esm_shims();
187
- events = [];
188
- MAX_EVENTS = 1e3;
189
- stopFn = void 0;
190
- }
191
- });
192
168
 
193
- // src/handler.ts
194
- var handler_exports = {};
195
- __export(handler_exports, {
196
- setupGlobalErrorHandler: () => setupGlobalErrorHandler
197
- });
198
- function setupGlobalErrorHandler() {
199
- if (window.__errorHandlerSetup) return;
200
- const origOnError = window.onerror;
201
- window.onerror = function thisWindowOnError(message, source, lineno, colno, error) {
202
- origOnError == null ? void 0 : origOnError.call(this, message, source, lineno, colno, error);
203
- captureException(
204
- error != null ? error : new Error(typeof message === "string" ? message : "Unknown error")
205
- );
206
- return false;
207
- };
208
- const origOnUnhandledRejection = window.onunhandledrejection;
209
- window.onunhandledrejection = function thisWindowOnRejection(event) {
210
- origOnUnhandledRejection == null ? void 0 : origOnUnhandledRejection.call(this, event);
211
- const err = event.reason instanceof Error ? event.reason : new Error(JSON.stringify(event.reason));
212
- captureException(err);
213
- };
214
- window.__errorHandlerSetup = true;
169
+ // src/utils.ts
170
+ import { zlibSync, unzlibSync } from "fflate";
171
+ function compressToBase64(obj) {
172
+ const json = JSON.stringify(obj);
173
+ const compressed = zlibSync(new TextEncoder().encode(json));
174
+ return btoa(String.fromCharCode(...compressed));
175
+ }
176
+ function decompressFromBase64(base64) {
177
+ const binary = Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
178
+ const json = new TextDecoder().decode(unzlibSync(binary));
179
+ return JSON.parse(json);
215
180
  }
216
- var init_handler = __esm({
217
- "src/handler.ts"() {
218
- "use strict";
219
- init_esm_shims();
220
- init_reporter();
221
- }
222
- });
223
181
 
224
182
  // src/reporter.ts
183
+ var batcher;
184
+ var globalOpts = { beforeErrorSec: 30 };
225
185
  function init(options) {
226
186
  var _a;
227
187
  globalOpts.beforeErrorSec = (_a = options.beforeErrorSec) != null ? _a : 10;
@@ -232,10 +192,6 @@ function init(options) {
232
192
  maxBufferSize: options.maxBufferSize
233
193
  });
234
194
  if (typeof window !== "undefined") {
235
- const start = () => {
236
- startRecording();
237
- Promise.resolve().then(() => (init_handler(), handler_exports)).then((mod) => mod.setupGlobalErrorHandler());
238
- };
239
195
  if (document.readyState === "complete") {
240
196
  requestAnimationFrame(() => startRecording());
241
197
  } else {
@@ -249,17 +205,18 @@ function captureException(error, additionalInfo, userId) {
249
205
  var _a, _b;
250
206
  const errorTime = Date.now();
251
207
  const eventsSnapshot = getCurrentEvents();
252
- const replay = getRecordedEvents(
208
+ const rawReplay = getRecordedEvents(
253
209
  globalOpts.beforeErrorSec,
254
210
  errorTime,
255
211
  eventsSnapshot
256
212
  );
257
213
  clearEvents();
258
214
  const { browser, os, userAgent } = getBrowserInfo();
215
+ const compressedReplay = compressToBase64(rawReplay);
259
216
  return batcher.capture({
260
217
  message: (_a = error.message) != null ? _a : "",
261
218
  stacktrace: (_b = error.stack) != null ? _b : "",
262
- replay,
219
+ replay: compressedReplay,
263
220
  environment: getEnvironment(),
264
221
  browser,
265
222
  os,
@@ -270,28 +227,30 @@ function captureException(error, additionalInfo, userId) {
270
227
  apiKey: batcher.getApiKey()
271
228
  });
272
229
  }
273
- var batcher, globalOpts;
274
- var init_reporter = __esm({
275
- "src/reporter.ts"() {
276
- "use strict";
277
- init_esm_shims();
278
- init_environment();
279
- init_error_batcher();
280
- init_recorder();
281
- globalOpts = { beforeErrorSec: 30 };
282
- }
283
- });
284
230
 
285
- // src/index.ts
286
- init_esm_shims();
287
- init_reporter();
288
- init_handler();
289
- init_recorder();
290
- init_environment();
291
- init_error_batcher();
231
+ // src/handler.ts
232
+ function setupGlobalErrorHandler() {
233
+ if (window.__errorHandlerSetup) return;
234
+ const origOnError = window.onerror;
235
+ window.onerror = function thisWindowOnError(message, source, lineno, colno, error) {
236
+ origOnError == null ? void 0 : origOnError.call(this, message, source, lineno, colno, error);
237
+ captureException(
238
+ error != null ? error : new Error(typeof message === "string" ? message : "Unknown error")
239
+ );
240
+ return false;
241
+ };
242
+ const origOnUnhandledRejection = window.onunhandledrejection;
243
+ window.onunhandledrejection = function thisWindowOnRejection(event) {
244
+ origOnUnhandledRejection == null ? void 0 : origOnUnhandledRejection.call(this, event);
245
+ const err = event.reason instanceof Error ? event.reason : new Error(JSON.stringify(event.reason));
246
+ captureException(err);
247
+ };
248
+ window.__errorHandlerSetup = true;
249
+ }
292
250
  export {
293
251
  ErrorBatcher,
294
252
  captureException,
253
+ decompressFromBase64,
295
254
  getBrowserInfo,
296
255
  getEnvironment,
297
256
  getRecordedEvents,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../node_modules/.pnpm/tsup@8.4.0_jiti@2.4.2_postcss@8.5.3_typescript@5.8.3/node_modules/tsup/assets/esm_shims.js","../src/environment.ts","../src/error-batcher.ts","../src/recorder.ts","../src/handler.ts","../src/reporter.ts","../src/index.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport { fileURLToPath } from 'url'\nimport path from 'path'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\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 if (process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview') return 'staging';\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 ?? 3000;\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 Authorization: `Bearer ${this.opts.apiKey}`,\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';\nimport { pack } from '@rrweb/packer';\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 // packFn: pack,\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\n// export 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 fullSnapshots = source.filter((e) => e.type === 2);\n// const lastSnapshot = fullSnapshots.reverse().find(\n// (e) => e.timestamp <= sliced[0]?.timestamp\n// );\n// // const lastSnapshot = fullSnapshots\n// // .reverse()\n// // .find((e) => e.timestamp <= (sliced[0]?.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 { 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 { getBrowserInfo, getEnvironment } from './environment';\nimport { ErrorBatcher } from './error-batcher';\nimport {\n startRecording,\n getRecordedEvents,\n getCurrentEvents,\n clearEvents,\n} from './recorder';\n\nexport interface BatchedEvent {\n id: string;\n timestamp: string;\n message: string;\n stacktrace: string;\n replay: any[];\n environment: string;\n browser: string;\n os: string;\n userAgent: string;\n userId?: number;\n additionalInfo?: Record<string, any>;\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 const start = () => {\n startRecording(); // DOM 렌더링 후에 시작되도록 지연 실행\n import('./handler.js').then((mod) => mod.setupGlobalErrorHandler());\n };\n\n if (document.readyState === 'complete') {\n requestAnimationFrame(() => startRecording());\n } else {\n window.addEventListener('load', () => {\n requestAnimationFrame(() => startRecording());\n });\n }\n\n // if ('requestIdleCallback' in window) {\n // window.requestIdleCallback(start);\n // } else {\n // setTimeout(start, 100);\n // }\n }\n}\n\nexport function captureException(\n error: Error,\n additionalInfo?: Record<string, any>,\n userId?: number\n): string {\n const errorTime = Date.now();\n const eventsSnapshot = getCurrentEvents();\n const replay = getRecordedEvents(\n globalOpts.beforeErrorSec,\n errorTime,\n eventsSnapshot\n );\n\n clearEvents(); // 다음 에러 기록을 위해 초기화\n\n const { browser, os, userAgent } = getBrowserInfo();\n\n return batcher.capture({\n message: error.message ?? '',\n stacktrace: error.stack ?? '',\n replay,\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","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 } from './reporter';\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"],"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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rusty-replay",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -22,6 +22,7 @@
22
22
  "dependencies": {
23
23
  "@rrweb/packer": "2.0.0-alpha.18",
24
24
  "axios": "^1.8.4",
25
+ "fflate": "^0.8.2",
25
26
  "rrweb-snapshot": "2.0.0-alpha.4"
26
27
  },
27
28
  "devDependencies": {