rusty-replay 1.0.0
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 +302 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +62 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +267 -0
- package/dist/index.js.map +1 -0
- package/package.json +30 -0
package/dist/index.cjs
ADDED
@@ -0,0 +1,302 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __create = Object.create;
|
3
|
+
var __defProp = Object.defineProperty;
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
6
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
9
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
10
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
11
|
+
var __spreadValues = (a, b) => {
|
12
|
+
for (var prop in b || (b = {}))
|
13
|
+
if (__hasOwnProp.call(b, prop))
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
15
|
+
if (__getOwnPropSymbols)
|
16
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
17
|
+
if (__propIsEnum.call(b, prop))
|
18
|
+
__defNormalProp(a, prop, b[prop]);
|
19
|
+
}
|
20
|
+
return a;
|
21
|
+
};
|
22
|
+
var __esm = (fn, res) => function __init() {
|
23
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
24
|
+
};
|
25
|
+
var __export = (target, all) => {
|
26
|
+
for (var name in all)
|
27
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
28
|
+
};
|
29
|
+
var __copyProps = (to, from, except, desc) => {
|
30
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
31
|
+
for (let key of __getOwnPropNames(from))
|
32
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
33
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
34
|
+
}
|
35
|
+
return to;
|
36
|
+
};
|
37
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
38
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
39
|
+
// file that has been converted to a CommonJS file using a Babel-
|
40
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
41
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
42
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
43
|
+
mod
|
44
|
+
));
|
45
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
46
|
+
|
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
|
+
}
|
52
|
+
});
|
53
|
+
|
54
|
+
// src/recorder.ts
|
55
|
+
function startRecording() {
|
56
|
+
events = [];
|
57
|
+
(0, import_rrweb.record)({
|
58
|
+
emit(event) {
|
59
|
+
events.push(event);
|
60
|
+
if (events.length > MAX_EVENTS) {
|
61
|
+
events = events.slice(-MAX_EVENTS);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
});
|
65
|
+
}
|
66
|
+
function getRecordedEvents(beforeErrorSec = 30) {
|
67
|
+
const now = Date.now();
|
68
|
+
return events.filter((e) => now - e.timestamp < beforeErrorSec * 1e3);
|
69
|
+
}
|
70
|
+
var import_rrweb, events, MAX_EVENTS;
|
71
|
+
var init_recorder = __esm({
|
72
|
+
"src/recorder.ts"() {
|
73
|
+
"use strict";
|
74
|
+
init_cjs_shims();
|
75
|
+
import_rrweb = require("rrweb");
|
76
|
+
events = [];
|
77
|
+
MAX_EVENTS = 1e3;
|
78
|
+
}
|
79
|
+
});
|
80
|
+
|
81
|
+
// src/environment.ts
|
82
|
+
function getBrowserInfo() {
|
83
|
+
const ua = navigator.userAgent;
|
84
|
+
let browser = "unknown", os = "unknown";
|
85
|
+
if (ua.includes("Firefox")) browser = "Firefox";
|
86
|
+
else if (ua.includes("SamsungBrowser")) browser = "Samsung Browser";
|
87
|
+
else if (ua.includes("Opera") || ua.includes("OPR")) browser = "Opera";
|
88
|
+
else if (ua.includes("Trident")) browser = "IE";
|
89
|
+
else if (ua.includes("Edge")) browser = "Edge (Legacy)";
|
90
|
+
else if (ua.includes("Edg")) browser = "Edge";
|
91
|
+
else if (ua.includes("Chrome")) browser = "Chrome";
|
92
|
+
else if (ua.includes("Safari")) browser = "Safari";
|
93
|
+
if (ua.includes("Windows")) os = "Windows";
|
94
|
+
else if (ua.includes("Mac")) os = "macOS";
|
95
|
+
else if (ua.includes("Linux")) os = "Linux";
|
96
|
+
else if (ua.includes("Android")) os = "Android";
|
97
|
+
else if (ua.includes("like Mac")) os = "iOS";
|
98
|
+
return { browser, os, userAgent: ua };
|
99
|
+
}
|
100
|
+
function getEnvironment() {
|
101
|
+
if (process.env.NODE_ENV === "development") return "development";
|
102
|
+
if (process.env.NEXT_PUBLIC_VERCEL_ENV === "preview") return "staging";
|
103
|
+
return "production";
|
104
|
+
}
|
105
|
+
var init_environment = __esm({
|
106
|
+
"src/environment.ts"() {
|
107
|
+
"use strict";
|
108
|
+
init_cjs_shims();
|
109
|
+
}
|
110
|
+
});
|
111
|
+
|
112
|
+
// src/error-batcher.ts
|
113
|
+
var import_axios, ErrorBatcher;
|
114
|
+
var init_error_batcher = __esm({
|
115
|
+
"src/error-batcher.ts"() {
|
116
|
+
"use strict";
|
117
|
+
init_cjs_shims();
|
118
|
+
import_axios = __toESM(require("axios"), 1);
|
119
|
+
ErrorBatcher = class {
|
120
|
+
constructor(opts) {
|
121
|
+
this.opts = opts;
|
122
|
+
this.queue = [];
|
123
|
+
this.isFlushing = false;
|
124
|
+
var _a;
|
125
|
+
const interval = (_a = opts.flushIntervalMs) != null ? _a : 3e3;
|
126
|
+
this.flushTimer = window.setInterval(() => this.flush(), interval);
|
127
|
+
window.addEventListener("beforeunload", () => this.flushOnUnload());
|
128
|
+
}
|
129
|
+
capture(evt) {
|
130
|
+
var _a;
|
131
|
+
const id = this.makeId();
|
132
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
133
|
+
const record2 = __spreadValues({ id, timestamp }, evt);
|
134
|
+
if (this.queue.length >= ((_a = this.opts.maxBufferSize) != null ? _a : 64)) {
|
135
|
+
this.queue.shift();
|
136
|
+
}
|
137
|
+
this.queue.push(record2);
|
138
|
+
return id;
|
139
|
+
}
|
140
|
+
async flush() {
|
141
|
+
if (this.isFlushing || this.queue.length === 0) return;
|
142
|
+
this.isFlushing = true;
|
143
|
+
const batch = this.queue.splice(0, this.queue.length);
|
144
|
+
try {
|
145
|
+
await import_axios.default.post(
|
146
|
+
this.opts.endpoint,
|
147
|
+
{ events: batch },
|
148
|
+
{
|
149
|
+
headers: {
|
150
|
+
"Content-Type": "application/json",
|
151
|
+
Authorization: `Bearer ${this.opts.apiKey}`
|
152
|
+
}
|
153
|
+
}
|
154
|
+
);
|
155
|
+
} catch (e) {
|
156
|
+
this.queue.unshift(...batch);
|
157
|
+
} finally {
|
158
|
+
this.isFlushing = false;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
flushOnUnload() {
|
162
|
+
if (!navigator.sendBeacon || this.queue.length === 0) return;
|
163
|
+
const payload = JSON.stringify({ events: this.queue });
|
164
|
+
navigator.sendBeacon(this.opts.endpoint, payload);
|
165
|
+
}
|
166
|
+
makeId() {
|
167
|
+
return "xxxx-xxxx-4xxx-yxxx".replace(/[xy]/g, (c) => {
|
168
|
+
const r = Math.random() * 16 | 0;
|
169
|
+
const v = c === "x" ? r : r & 3 | 8;
|
170
|
+
return v.toString(16);
|
171
|
+
});
|
172
|
+
}
|
173
|
+
destroy() {
|
174
|
+
clearInterval(this.flushTimer);
|
175
|
+
}
|
176
|
+
};
|
177
|
+
}
|
178
|
+
});
|
179
|
+
|
180
|
+
// src/handler.ts
|
181
|
+
var handler_exports = {};
|
182
|
+
__export(handler_exports, {
|
183
|
+
setupGlobalErrorHandler: () => setupGlobalErrorHandler
|
184
|
+
});
|
185
|
+
function setupGlobalErrorHandler() {
|
186
|
+
if (window.__errorHandlerSetup) return;
|
187
|
+
const origOnError = window.onerror;
|
188
|
+
window.onerror = function thisWindowOnError(message, source, lineno, colno, error) {
|
189
|
+
origOnError == null ? void 0 : origOnError.call(this, message, source, lineno, colno, error);
|
190
|
+
captureException(
|
191
|
+
error != null ? error : new Error(typeof message === "string" ? message : "Unknown error")
|
192
|
+
);
|
193
|
+
return false;
|
194
|
+
};
|
195
|
+
const origOnUnhandledRejection = window.onunhandledrejection;
|
196
|
+
window.onunhandledrejection = function thisWindowOnRejection(event) {
|
197
|
+
origOnUnhandledRejection == null ? void 0 : origOnUnhandledRejection.call(this, event);
|
198
|
+
const err = event.reason instanceof Error ? event.reason : new Error(JSON.stringify(event.reason));
|
199
|
+
captureException(err);
|
200
|
+
};
|
201
|
+
window.__errorHandlerSetup = true;
|
202
|
+
}
|
203
|
+
var init_handler = __esm({
|
204
|
+
"src/handler.ts"() {
|
205
|
+
"use strict";
|
206
|
+
init_cjs_shims();
|
207
|
+
init_reporter();
|
208
|
+
}
|
209
|
+
});
|
210
|
+
|
211
|
+
// src/reporter.ts
|
212
|
+
function init(options) {
|
213
|
+
var _a;
|
214
|
+
startRecording();
|
215
|
+
globalOpts.beforeErrorSec = (_a = options.beforeErrorSec) != null ? _a : 30;
|
216
|
+
batcher = new ErrorBatcher({
|
217
|
+
endpoint: options.endpoint,
|
218
|
+
apiKey: options.apiKey,
|
219
|
+
flushIntervalMs: options.flushIntervalMs,
|
220
|
+
maxBufferSize: options.maxBufferSize
|
221
|
+
});
|
222
|
+
Promise.resolve().then(() => (init_handler(), handler_exports)).then((mod) => mod.setupGlobalErrorHandler());
|
223
|
+
}
|
224
|
+
function captureException(error, additionalInfo, userId) {
|
225
|
+
var _a;
|
226
|
+
const { browser, os, userAgent } = getBrowserInfo();
|
227
|
+
const replay = getRecordedEvents(globalOpts.beforeErrorSec);
|
228
|
+
return batcher.capture({
|
229
|
+
message: error.message,
|
230
|
+
stack: (_a = error.stack) != null ? _a : "",
|
231
|
+
replay,
|
232
|
+
environment: getEnvironment(),
|
233
|
+
browser,
|
234
|
+
os,
|
235
|
+
userAgent,
|
236
|
+
userId,
|
237
|
+
additionalInfo
|
238
|
+
});
|
239
|
+
}
|
240
|
+
function wrap(fn, info) {
|
241
|
+
return (...args) => {
|
242
|
+
try {
|
243
|
+
return fn(...args);
|
244
|
+
} catch (err) {
|
245
|
+
if (err instanceof Error) {
|
246
|
+
captureException(err, info == null ? void 0 : info.additionalInfo, info == null ? void 0 : info.userId);
|
247
|
+
} else {
|
248
|
+
captureException(
|
249
|
+
new Error(String(err)),
|
250
|
+
info == null ? void 0 : info.additionalInfo,
|
251
|
+
info == null ? void 0 : info.userId
|
252
|
+
);
|
253
|
+
}
|
254
|
+
throw err;
|
255
|
+
}
|
256
|
+
};
|
257
|
+
}
|
258
|
+
var batcher, globalOpts;
|
259
|
+
var init_reporter = __esm({
|
260
|
+
"src/reporter.ts"() {
|
261
|
+
"use strict";
|
262
|
+
init_cjs_shims();
|
263
|
+
init_recorder();
|
264
|
+
init_environment();
|
265
|
+
init_error_batcher();
|
266
|
+
globalOpts = { beforeErrorSec: 30 };
|
267
|
+
}
|
268
|
+
});
|
269
|
+
|
270
|
+
// src/index.ts
|
271
|
+
var index_exports = {};
|
272
|
+
__export(index_exports, {
|
273
|
+
ErrorBatcher: () => ErrorBatcher,
|
274
|
+
captureException: () => captureException,
|
275
|
+
getBrowserInfo: () => getBrowserInfo,
|
276
|
+
getEnvironment: () => getEnvironment,
|
277
|
+
getRecordedEvents: () => getRecordedEvents,
|
278
|
+
init: () => init,
|
279
|
+
setupGlobalErrorHandler: () => setupGlobalErrorHandler,
|
280
|
+
startRecording: () => startRecording,
|
281
|
+
wrap: () => wrap
|
282
|
+
});
|
283
|
+
module.exports = __toCommonJS(index_exports);
|
284
|
+
init_cjs_shims();
|
285
|
+
init_reporter();
|
286
|
+
init_handler();
|
287
|
+
init_recorder();
|
288
|
+
init_environment();
|
289
|
+
init_error_batcher();
|
290
|
+
// Annotate the CommonJS export names for ESM import in node:
|
291
|
+
0 && (module.exports = {
|
292
|
+
ErrorBatcher,
|
293
|
+
captureException,
|
294
|
+
getBrowserInfo,
|
295
|
+
getEnvironment,
|
296
|
+
getRecordedEvents,
|
297
|
+
init,
|
298
|
+
setupGlobalErrorHandler,
|
299
|
+
startRecording,
|
300
|
+
wrap
|
301
|
+
});
|
302
|
+
//# sourceMappingURL=index.cjs.map
|
@@ -0,0 +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/recorder.ts","../src/environment.ts","../src/error-batcher.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","import type { eventWithTime } from '@rrweb/types';\nimport { record } from 'rrweb';\n\nlet events: eventWithTime[] = [];\nconst MAX_EVENTS = 1000;\n\nexport function startRecording() {\n events = [];\n record({\n emit(event) {\n events.push(event);\n if (events.length > MAX_EVENTS) {\n events = events.slice(-MAX_EVENTS);\n }\n },\n });\n}\n\nexport function getRecordedEvents(beforeErrorSec = 30): eventWithTime[] {\n const now = Date.now();\n return events.filter((e) => now - e.timestamp < beforeErrorSec * 1000);\n}\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\n constructor(private opts: BatcherOptions) {\n const interval = opts.flushIntervalMs ?? 3000;\n this.flushTimer = window.setInterval(() => this.flush(), interval);\n window.addEventListener('beforeunload', () => this.flushOnUnload());\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 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 { 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 { startRecording, getRecordedEvents } from './recorder';\nimport { getBrowserInfo, getEnvironment } from './environment';\nimport { ErrorBatcher } from './error-batcher';\n\nexport interface BatchedEvent {\n id: string;\n timestamp: string;\n message: string;\n stack: 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}\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 startRecording();\n globalOpts.beforeErrorSec = options.beforeErrorSec ?? 30;\n batcher = new ErrorBatcher({\n endpoint: options.endpoint,\n apiKey: options.apiKey,\n flushIntervalMs: options.flushIntervalMs,\n maxBufferSize: options.maxBufferSize,\n });\n import('./handler.js').then((mod) => mod.setupGlobalErrorHandler());\n}\n\nexport function captureException(\n error: Error,\n additionalInfo?: Record<string, any>,\n userId?: number\n): string {\n const { browser, os, userAgent } = getBrowserInfo();\n const replay = getRecordedEvents(globalOpts.beforeErrorSec);\n return batcher.capture({\n message: error.message,\n stack: error.stack ?? '',\n replay,\n environment: getEnvironment(),\n browser,\n os,\n userAgent,\n userId,\n additionalInfo,\n });\n}\n\n/** 기존 함수 래핑 */\nexport function wrap<T extends (...args: any[]) => any>(\n fn: T,\n info?: { additionalInfo?: Record<string, any>; userId?: number }\n): (...args: Parameters<T>) => ReturnType<T> {\n return (...args) => {\n try {\n return fn(...args);\n } catch (err) {\n if (err instanceof Error) {\n captureException(err, info?.additionalInfo, info?.userId);\n } else {\n captureException(\n new Error(String(err)),\n info?.additionalInfo,\n info?.userId\n );\n }\n throw err;\n }\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","axios"]}
|
package/dist/index.d.cts
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
import { eventWithTime } from '@rrweb/types';
|
2
|
+
|
3
|
+
interface BatchedEvent {
|
4
|
+
id: string;
|
5
|
+
timestamp: string;
|
6
|
+
message: string;
|
7
|
+
stack: string;
|
8
|
+
replay: any[];
|
9
|
+
environment: string;
|
10
|
+
browser: string;
|
11
|
+
os: string;
|
12
|
+
userAgent: string;
|
13
|
+
userId?: number;
|
14
|
+
additionalInfo?: Record<string, any>;
|
15
|
+
}
|
16
|
+
interface BatcherOptions {
|
17
|
+
endpoint: string;
|
18
|
+
apiKey: string;
|
19
|
+
flushIntervalMs?: number;
|
20
|
+
maxBufferSize?: number;
|
21
|
+
}
|
22
|
+
interface InitOptions {
|
23
|
+
endpoint: string;
|
24
|
+
apiKey: string;
|
25
|
+
flushIntervalMs?: number;
|
26
|
+
maxBufferSize?: number;
|
27
|
+
beforeErrorSec?: number;
|
28
|
+
}
|
29
|
+
declare function init(options: InitOptions): void;
|
30
|
+
declare function captureException(error: Error, additionalInfo?: Record<string, any>, userId?: number): string;
|
31
|
+
/** 기존 함수 래핑 */
|
32
|
+
declare function wrap<T extends (...args: any[]) => any>(fn: T, info?: {
|
33
|
+
additionalInfo?: Record<string, any>;
|
34
|
+
userId?: number;
|
35
|
+
}): (...args: Parameters<T>) => ReturnType<T>;
|
36
|
+
|
37
|
+
declare function setupGlobalErrorHandler(): void;
|
38
|
+
|
39
|
+
declare function startRecording(): void;
|
40
|
+
declare function getRecordedEvents(beforeErrorSec?: number): eventWithTime[];
|
41
|
+
|
42
|
+
declare function getBrowserInfo(): {
|
43
|
+
browser: string;
|
44
|
+
os: string;
|
45
|
+
userAgent: string;
|
46
|
+
};
|
47
|
+
declare function getEnvironment(): 'development' | 'staging' | 'production';
|
48
|
+
|
49
|
+
declare class ErrorBatcher {
|
50
|
+
private opts;
|
51
|
+
private queue;
|
52
|
+
private isFlushing;
|
53
|
+
private flushTimer;
|
54
|
+
constructor(opts: BatcherOptions);
|
55
|
+
capture(evt: Omit<BatchedEvent, 'id' | 'timestamp'>): string;
|
56
|
+
private flush;
|
57
|
+
private flushOnUnload;
|
58
|
+
private makeId;
|
59
|
+
destroy(): void;
|
60
|
+
}
|
61
|
+
|
62
|
+
export { type BatchedEvent, type BatcherOptions, ErrorBatcher, type InitOptions, captureException, getBrowserInfo, getEnvironment, getRecordedEvents, init, setupGlobalErrorHandler, startRecording, wrap };
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
import { eventWithTime } from '@rrweb/types';
|
2
|
+
|
3
|
+
interface BatchedEvent {
|
4
|
+
id: string;
|
5
|
+
timestamp: string;
|
6
|
+
message: string;
|
7
|
+
stack: string;
|
8
|
+
replay: any[];
|
9
|
+
environment: string;
|
10
|
+
browser: string;
|
11
|
+
os: string;
|
12
|
+
userAgent: string;
|
13
|
+
userId?: number;
|
14
|
+
additionalInfo?: Record<string, any>;
|
15
|
+
}
|
16
|
+
interface BatcherOptions {
|
17
|
+
endpoint: string;
|
18
|
+
apiKey: string;
|
19
|
+
flushIntervalMs?: number;
|
20
|
+
maxBufferSize?: number;
|
21
|
+
}
|
22
|
+
interface InitOptions {
|
23
|
+
endpoint: string;
|
24
|
+
apiKey: string;
|
25
|
+
flushIntervalMs?: number;
|
26
|
+
maxBufferSize?: number;
|
27
|
+
beforeErrorSec?: number;
|
28
|
+
}
|
29
|
+
declare function init(options: InitOptions): void;
|
30
|
+
declare function captureException(error: Error, additionalInfo?: Record<string, any>, userId?: number): string;
|
31
|
+
/** 기존 함수 래핑 */
|
32
|
+
declare function wrap<T extends (...args: any[]) => any>(fn: T, info?: {
|
33
|
+
additionalInfo?: Record<string, any>;
|
34
|
+
userId?: number;
|
35
|
+
}): (...args: Parameters<T>) => ReturnType<T>;
|
36
|
+
|
37
|
+
declare function setupGlobalErrorHandler(): void;
|
38
|
+
|
39
|
+
declare function startRecording(): void;
|
40
|
+
declare function getRecordedEvents(beforeErrorSec?: number): eventWithTime[];
|
41
|
+
|
42
|
+
declare function getBrowserInfo(): {
|
43
|
+
browser: string;
|
44
|
+
os: string;
|
45
|
+
userAgent: string;
|
46
|
+
};
|
47
|
+
declare function getEnvironment(): 'development' | 'staging' | 'production';
|
48
|
+
|
49
|
+
declare class ErrorBatcher {
|
50
|
+
private opts;
|
51
|
+
private queue;
|
52
|
+
private isFlushing;
|
53
|
+
private flushTimer;
|
54
|
+
constructor(opts: BatcherOptions);
|
55
|
+
capture(evt: Omit<BatchedEvent, 'id' | 'timestamp'>): string;
|
56
|
+
private flush;
|
57
|
+
private flushOnUnload;
|
58
|
+
private makeId;
|
59
|
+
destroy(): void;
|
60
|
+
}
|
61
|
+
|
62
|
+
export { type BatchedEvent, type BatcherOptions, ErrorBatcher, type InitOptions, captureException, getBrowserInfo, getEnvironment, getRecordedEvents, init, setupGlobalErrorHandler, startRecording, wrap };
|
package/dist/index.js
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
var __defProp = Object.defineProperty;
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
3
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
5
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
6
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
7
|
+
var __spreadValues = (a, b) => {
|
8
|
+
for (var prop in b || (b = {}))
|
9
|
+
if (__hasOwnProp.call(b, prop))
|
10
|
+
__defNormalProp(a, prop, b[prop]);
|
11
|
+
if (__getOwnPropSymbols)
|
12
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
13
|
+
if (__propIsEnum.call(b, prop))
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
15
|
+
}
|
16
|
+
return a;
|
17
|
+
};
|
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
|
+
|
33
|
+
// src/recorder.ts
|
34
|
+
import { record } from "rrweb";
|
35
|
+
function startRecording() {
|
36
|
+
events = [];
|
37
|
+
record({
|
38
|
+
emit(event) {
|
39
|
+
events.push(event);
|
40
|
+
if (events.length > MAX_EVENTS) {
|
41
|
+
events = events.slice(-MAX_EVENTS);
|
42
|
+
}
|
43
|
+
}
|
44
|
+
});
|
45
|
+
}
|
46
|
+
function getRecordedEvents(beforeErrorSec = 30) {
|
47
|
+
const now = Date.now();
|
48
|
+
return events.filter((e) => now - e.timestamp < beforeErrorSec * 1e3);
|
49
|
+
}
|
50
|
+
var events, MAX_EVENTS;
|
51
|
+
var init_recorder = __esm({
|
52
|
+
"src/recorder.ts"() {
|
53
|
+
"use strict";
|
54
|
+
init_esm_shims();
|
55
|
+
events = [];
|
56
|
+
MAX_EVENTS = 1e3;
|
57
|
+
}
|
58
|
+
});
|
59
|
+
|
60
|
+
// src/environment.ts
|
61
|
+
function getBrowserInfo() {
|
62
|
+
const ua = navigator.userAgent;
|
63
|
+
let browser = "unknown", os = "unknown";
|
64
|
+
if (ua.includes("Firefox")) browser = "Firefox";
|
65
|
+
else if (ua.includes("SamsungBrowser")) browser = "Samsung Browser";
|
66
|
+
else if (ua.includes("Opera") || ua.includes("OPR")) browser = "Opera";
|
67
|
+
else if (ua.includes("Trident")) browser = "IE";
|
68
|
+
else if (ua.includes("Edge")) browser = "Edge (Legacy)";
|
69
|
+
else if (ua.includes("Edg")) browser = "Edge";
|
70
|
+
else if (ua.includes("Chrome")) browser = "Chrome";
|
71
|
+
else if (ua.includes("Safari")) browser = "Safari";
|
72
|
+
if (ua.includes("Windows")) os = "Windows";
|
73
|
+
else if (ua.includes("Mac")) os = "macOS";
|
74
|
+
else if (ua.includes("Linux")) os = "Linux";
|
75
|
+
else if (ua.includes("Android")) os = "Android";
|
76
|
+
else if (ua.includes("like Mac")) os = "iOS";
|
77
|
+
return { browser, os, userAgent: ua };
|
78
|
+
}
|
79
|
+
function getEnvironment() {
|
80
|
+
if (process.env.NODE_ENV === "development") return "development";
|
81
|
+
if (process.env.NEXT_PUBLIC_VERCEL_ENV === "preview") return "staging";
|
82
|
+
return "production";
|
83
|
+
}
|
84
|
+
var init_environment = __esm({
|
85
|
+
"src/environment.ts"() {
|
86
|
+
"use strict";
|
87
|
+
init_esm_shims();
|
88
|
+
}
|
89
|
+
});
|
90
|
+
|
91
|
+
// src/error-batcher.ts
|
92
|
+
import axios from "axios";
|
93
|
+
var ErrorBatcher;
|
94
|
+
var init_error_batcher = __esm({
|
95
|
+
"src/error-batcher.ts"() {
|
96
|
+
"use strict";
|
97
|
+
init_esm_shims();
|
98
|
+
ErrorBatcher = class {
|
99
|
+
constructor(opts) {
|
100
|
+
this.opts = opts;
|
101
|
+
this.queue = [];
|
102
|
+
this.isFlushing = false;
|
103
|
+
var _a;
|
104
|
+
const interval = (_a = opts.flushIntervalMs) != null ? _a : 3e3;
|
105
|
+
this.flushTimer = window.setInterval(() => this.flush(), interval);
|
106
|
+
window.addEventListener("beforeunload", () => this.flushOnUnload());
|
107
|
+
}
|
108
|
+
capture(evt) {
|
109
|
+
var _a;
|
110
|
+
const id = this.makeId();
|
111
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
112
|
+
const record2 = __spreadValues({ id, timestamp }, evt);
|
113
|
+
if (this.queue.length >= ((_a = this.opts.maxBufferSize) != null ? _a : 64)) {
|
114
|
+
this.queue.shift();
|
115
|
+
}
|
116
|
+
this.queue.push(record2);
|
117
|
+
return id;
|
118
|
+
}
|
119
|
+
async flush() {
|
120
|
+
if (this.isFlushing || this.queue.length === 0) return;
|
121
|
+
this.isFlushing = true;
|
122
|
+
const batch = this.queue.splice(0, this.queue.length);
|
123
|
+
try {
|
124
|
+
await axios.post(
|
125
|
+
this.opts.endpoint,
|
126
|
+
{ events: batch },
|
127
|
+
{
|
128
|
+
headers: {
|
129
|
+
"Content-Type": "application/json",
|
130
|
+
Authorization: `Bearer ${this.opts.apiKey}`
|
131
|
+
}
|
132
|
+
}
|
133
|
+
);
|
134
|
+
} catch (e) {
|
135
|
+
this.queue.unshift(...batch);
|
136
|
+
} finally {
|
137
|
+
this.isFlushing = false;
|
138
|
+
}
|
139
|
+
}
|
140
|
+
flushOnUnload() {
|
141
|
+
if (!navigator.sendBeacon || this.queue.length === 0) return;
|
142
|
+
const payload = JSON.stringify({ events: this.queue });
|
143
|
+
navigator.sendBeacon(this.opts.endpoint, payload);
|
144
|
+
}
|
145
|
+
makeId() {
|
146
|
+
return "xxxx-xxxx-4xxx-yxxx".replace(/[xy]/g, (c) => {
|
147
|
+
const r = Math.random() * 16 | 0;
|
148
|
+
const v = c === "x" ? r : r & 3 | 8;
|
149
|
+
return v.toString(16);
|
150
|
+
});
|
151
|
+
}
|
152
|
+
destroy() {
|
153
|
+
clearInterval(this.flushTimer);
|
154
|
+
}
|
155
|
+
};
|
156
|
+
}
|
157
|
+
});
|
158
|
+
|
159
|
+
// src/handler.ts
|
160
|
+
var handler_exports = {};
|
161
|
+
__export(handler_exports, {
|
162
|
+
setupGlobalErrorHandler: () => setupGlobalErrorHandler
|
163
|
+
});
|
164
|
+
function setupGlobalErrorHandler() {
|
165
|
+
if (window.__errorHandlerSetup) return;
|
166
|
+
const origOnError = window.onerror;
|
167
|
+
window.onerror = function thisWindowOnError(message, source, lineno, colno, error) {
|
168
|
+
origOnError == null ? void 0 : origOnError.call(this, message, source, lineno, colno, error);
|
169
|
+
captureException(
|
170
|
+
error != null ? error : new Error(typeof message === "string" ? message : "Unknown error")
|
171
|
+
);
|
172
|
+
return false;
|
173
|
+
};
|
174
|
+
const origOnUnhandledRejection = window.onunhandledrejection;
|
175
|
+
window.onunhandledrejection = function thisWindowOnRejection(event) {
|
176
|
+
origOnUnhandledRejection == null ? void 0 : origOnUnhandledRejection.call(this, event);
|
177
|
+
const err = event.reason instanceof Error ? event.reason : new Error(JSON.stringify(event.reason));
|
178
|
+
captureException(err);
|
179
|
+
};
|
180
|
+
window.__errorHandlerSetup = true;
|
181
|
+
}
|
182
|
+
var init_handler = __esm({
|
183
|
+
"src/handler.ts"() {
|
184
|
+
"use strict";
|
185
|
+
init_esm_shims();
|
186
|
+
init_reporter();
|
187
|
+
}
|
188
|
+
});
|
189
|
+
|
190
|
+
// src/reporter.ts
|
191
|
+
function init(options) {
|
192
|
+
var _a;
|
193
|
+
startRecording();
|
194
|
+
globalOpts.beforeErrorSec = (_a = options.beforeErrorSec) != null ? _a : 30;
|
195
|
+
batcher = new ErrorBatcher({
|
196
|
+
endpoint: options.endpoint,
|
197
|
+
apiKey: options.apiKey,
|
198
|
+
flushIntervalMs: options.flushIntervalMs,
|
199
|
+
maxBufferSize: options.maxBufferSize
|
200
|
+
});
|
201
|
+
Promise.resolve().then(() => (init_handler(), handler_exports)).then((mod) => mod.setupGlobalErrorHandler());
|
202
|
+
}
|
203
|
+
function captureException(error, additionalInfo, userId) {
|
204
|
+
var _a;
|
205
|
+
const { browser, os, userAgent } = getBrowserInfo();
|
206
|
+
const replay = getRecordedEvents(globalOpts.beforeErrorSec);
|
207
|
+
return batcher.capture({
|
208
|
+
message: error.message,
|
209
|
+
stack: (_a = error.stack) != null ? _a : "",
|
210
|
+
replay,
|
211
|
+
environment: getEnvironment(),
|
212
|
+
browser,
|
213
|
+
os,
|
214
|
+
userAgent,
|
215
|
+
userId,
|
216
|
+
additionalInfo
|
217
|
+
});
|
218
|
+
}
|
219
|
+
function wrap(fn, info) {
|
220
|
+
return (...args) => {
|
221
|
+
try {
|
222
|
+
return fn(...args);
|
223
|
+
} catch (err) {
|
224
|
+
if (err instanceof Error) {
|
225
|
+
captureException(err, info == null ? void 0 : info.additionalInfo, info == null ? void 0 : info.userId);
|
226
|
+
} else {
|
227
|
+
captureException(
|
228
|
+
new Error(String(err)),
|
229
|
+
info == null ? void 0 : info.additionalInfo,
|
230
|
+
info == null ? void 0 : info.userId
|
231
|
+
);
|
232
|
+
}
|
233
|
+
throw err;
|
234
|
+
}
|
235
|
+
};
|
236
|
+
}
|
237
|
+
var batcher, globalOpts;
|
238
|
+
var init_reporter = __esm({
|
239
|
+
"src/reporter.ts"() {
|
240
|
+
"use strict";
|
241
|
+
init_esm_shims();
|
242
|
+
init_recorder();
|
243
|
+
init_environment();
|
244
|
+
init_error_batcher();
|
245
|
+
globalOpts = { beforeErrorSec: 30 };
|
246
|
+
}
|
247
|
+
});
|
248
|
+
|
249
|
+
// src/index.ts
|
250
|
+
init_esm_shims();
|
251
|
+
init_reporter();
|
252
|
+
init_handler();
|
253
|
+
init_recorder();
|
254
|
+
init_environment();
|
255
|
+
init_error_batcher();
|
256
|
+
export {
|
257
|
+
ErrorBatcher,
|
258
|
+
captureException,
|
259
|
+
getBrowserInfo,
|
260
|
+
getEnvironment,
|
261
|
+
getRecordedEvents,
|
262
|
+
init,
|
263
|
+
setupGlobalErrorHandler,
|
264
|
+
startRecording,
|
265
|
+
wrap
|
266
|
+
};
|
267
|
+
//# sourceMappingURL=index.js.map
|
@@ -0,0 +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/recorder.ts","../src/environment.ts","../src/error-batcher.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","import type { eventWithTime } from '@rrweb/types';\nimport { record } from 'rrweb';\n\nlet events: eventWithTime[] = [];\nconst MAX_EVENTS = 1000;\n\nexport function startRecording() {\n events = [];\n record({\n emit(event) {\n events.push(event);\n if (events.length > MAX_EVENTS) {\n events = events.slice(-MAX_EVENTS);\n }\n },\n });\n}\n\nexport function getRecordedEvents(beforeErrorSec = 30): eventWithTime[] {\n const now = Date.now();\n return events.filter((e) => now - e.timestamp < beforeErrorSec * 1000);\n}\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\n constructor(private opts: BatcherOptions) {\n const interval = opts.flushIntervalMs ?? 3000;\n this.flushTimer = window.setInterval(() => this.flush(), interval);\n window.addEventListener('beforeunload', () => this.flushOnUnload());\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 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 { 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 { startRecording, getRecordedEvents } from './recorder';\nimport { getBrowserInfo, getEnvironment } from './environment';\nimport { ErrorBatcher } from './error-batcher';\n\nexport interface BatchedEvent {\n id: string;\n timestamp: string;\n message: string;\n stack: 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}\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 startRecording();\n globalOpts.beforeErrorSec = options.beforeErrorSec ?? 30;\n batcher = new ErrorBatcher({\n endpoint: options.endpoint,\n apiKey: options.apiKey,\n flushIntervalMs: options.flushIntervalMs,\n maxBufferSize: options.maxBufferSize,\n });\n import('./handler.js').then((mod) => mod.setupGlobalErrorHandler());\n}\n\nexport function captureException(\n error: Error,\n additionalInfo?: Record<string, any>,\n userId?: number\n): string {\n const { browser, os, userAgent } = getBrowserInfo();\n const replay = getRecordedEvents(globalOpts.beforeErrorSec);\n return batcher.capture({\n message: error.message,\n stack: error.stack ?? '',\n replay,\n environment: getEnvironment(),\n browser,\n os,\n userAgent,\n userId,\n additionalInfo,\n });\n}\n\n/** 기존 함수 래핑 */\nexport function wrap<T extends (...args: any[]) => any>(\n fn: T,\n info?: { additionalInfo?: Record<string, any>; userId?: number }\n): (...args: Parameters<T>) => ReturnType<T> {\n return (...args) => {\n try {\n return fn(...args);\n } catch (err) {\n if (err instanceof Error) {\n captureException(err, info?.additionalInfo, info?.userId);\n } else {\n captureException(\n new Error(String(err)),\n info?.additionalInfo,\n info?.userId\n );\n }\n throw err;\n }\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"]}
|
package/package.json
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
{
|
2
|
+
"name": "rusty-replay",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "",
|
5
|
+
"main": "dist/index.cjs.js",
|
6
|
+
"type": "module",
|
7
|
+
"module": "dist/index.esm.js",
|
8
|
+
"types": "dist/index.d.ts",
|
9
|
+
"files": [
|
10
|
+
"dist"
|
11
|
+
],
|
12
|
+
"scripts": {
|
13
|
+
"build": "tsup",
|
14
|
+
"prepublishOnly": "npm run build"
|
15
|
+
},
|
16
|
+
"peerDependencies": {
|
17
|
+
"@rrweb/types": "^2.0.0-alpha.4",
|
18
|
+
"react": "^16.8 || ^17 || ^18 || ^19",
|
19
|
+
"rrweb": "^2.0.0-alpha.4",
|
20
|
+
"rrweb-player": "^1.0.0-alpha.4"
|
21
|
+
},
|
22
|
+
"dependencies": {
|
23
|
+
"axios": "^1.8.4"
|
24
|
+
},
|
25
|
+
"devDependencies": {
|
26
|
+
"@types/node": "^20",
|
27
|
+
"tsup": "^8.4.0",
|
28
|
+
"typescript": "^5.8.3"
|
29
|
+
}
|
30
|
+
}
|