rusty-replay 1.0.10 β†’ 1.0.11

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.
Files changed (2) hide show
  1. package/README.md +164 -22
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,26 +1,168 @@
1
+ # πŸ¦€ rusty-replay
2
+
3
+ μ›Ή ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ λ°œμƒν•œ 였λ₯˜λ₯Ό μˆ˜μ§‘ν•˜κ³ , 직전 μ‚¬μš©μž ν™œλ™μ„ λ¦¬ν”Œλ ˆμ΄ ν˜•νƒœλ‘œ ν•¨κ»˜ μ „μ†‘ν•˜λŠ” κ²½λŸ‰ 였λ₯˜ 좔적 λ„κ΅¬μž…λ‹ˆλ‹€.
4
+
5
+ `rrweb` 기반의 μ‚¬μš©μž 행동 λ¦¬ν”Œλ ˆμ΄ κΈ°λŠ₯κ³Ό `axios` μ—λŸ¬ μžλ™ μ „μ†‘κΉŒμ§€ μ§€μ›ν•©λ‹ˆλ‹€.
6
+
7
+ ## πŸ“¦ μ„€μΉ˜
8
+
9
+ ```bash
10
+ npm install rusty-replay
11
+ ```
12
+
13
+ ---
14
+
15
+ ## βš™οΈ μ΄ˆκΈ°ν™”
16
+
17
+ Next.jsμ—μ„œ `rusty-replay`λŠ” 일반적으둜 `app/providers.tsx` λ˜λŠ” `layout.tsx`μ—μ„œ μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€.
18
+
19
+ ```ts
20
+ import { init } from 'rusty-replay';
21
+
22
+ init({
23
+ endpoint: 'https://your-api.com/batch-events',
24
+ apiKey: 'YOUR_PUBLIC_API_KEY',
25
+ flushIntervalMs: 10000, // 버퍼가 μ°° λ•ŒκΉŒμ§€ μ΅œλŒ€ λŒ€κΈ° μ‹œκ°„ (ms)
26
+ maxBufferSize: 2000000, // 전솑 μ „ μ΅œλŒ€ 버퍼 μ‚¬μ΄μ¦ˆ (bytes)
27
+ beforeErrorSec: 10, // μ—λŸ¬ λ°œμƒ μ „ λͺ‡ μ΄ˆκ°„μ˜ 이벀트λ₯Ό λ¦¬ν”Œλ ˆμ΄λ‘œ 남길지
28
+ });
29
+ ```
30
+
31
+ ---
32
+
33
+ ## 🧠 κΈ€λ‘œλ²Œ μ—λŸ¬ μžλ™ 캑처
34
+
35
+ ```
36
+ ts
37
+ λ³΅μ‚¬νŽΈμ§‘
38
+ import { setupGlobalErrorHandler } from 'rusty-replay';
39
+
40
+ setupGlobalErrorHandler();
41
+
42
+ ```
43
+
44
+ - `window.onerror`
45
+ - `window.onunhandledrejection`
46
+ 을 μžλ™ κ°μ§€ν•˜μ—¬ 였λ₯˜λ₯Ό μ„œλ²„λ‘œ μ „μ†‘ν•©λ‹ˆλ‹€.
47
+
48
+ ---
49
+
50
+ ## πŸ”§ Axios μ—λŸ¬ μžλ™ 전솑
51
+
52
+ ```ts
53
+ import axios from 'axios';
54
+ import { captureException, AdditionalInfo } from 'rusty-replay';
55
+
56
+ axios.interceptors.response.use(
57
+ (res) => res,
58
+ (error) => {
59
+ if (axios.isAxiosError(error)) {
60
+ const additionalInfo: Partial<AdditionalInfo> = {
61
+ pageUrl: window.location.href,
62
+ request: {
63
+ url: error.config?.url ?? '',
64
+ method: error.config?.method ?? '',
65
+ headers: error.config?.headers ?? {},
66
+ },
67
+ response: {
68
+ status: error.response?.status ?? 0,
69
+ statusText: error.response?.statusText ?? '',
70
+ data: {
71
+ message: error.response?.data?.message ?? '',
72
+ errorCode: error.response?.data?.errorCode ?? '',
73
+ },
74
+ },
75
+ };
76
+
77
+ captureException(
78
+ error instanceof Error ? error : new Error('API μš”μ²­ μ‹€νŒ¨'),
79
+ additionalInfo
80
+ );
81
+ }
82
+
83
+ return Promise.reject(error);
84
+ }
85
+ );
86
+ ```
87
+
88
+ ---
89
+
90
+ ## πŸ–₯️ React Error Boundary 톡합
91
+
1
92
  ```tsx
2
- // layout.tsx or RootLayout.tsx
3
- useEffect(() => {
4
- init({
5
- endpoint: 'http://localhost:8080/batch-errors', // endpoint
6
- apiKey: 'proj_c08cd1b02c9449c9a0d41f084ee92db9', // project id
7
- flushIntervalMs: 5000, // 5μ΄ˆλ§ˆλ‹€ ν•œλ²ˆμ”© 전솑
8
- maxBufferSize: 200, // 큐에 μ΅œλŒ€ 200κ±΄κΉŒμ§€
9
- beforeErrorSec: 30, // μ—λŸ¬ μ „ 30초 λ¦¬ν”Œλ ˆμ΄ 이벀트 포함
10
- });
11
- }, []);
12
-
13
- <button
14
- onClick={() => {
15
- captureException(new Error('μ˜λ„μ μœΌλ‘œ λ°œμƒμ‹œν‚¨ μˆ˜λ™ μ—λŸ¬'), {
16
- additionalInfo: {
17
- context: 'μˆ˜λ™ μ—λŸ¬ λ¦¬ν¬νŒ… ν…ŒμŠ€νŠΈ',
18
- button: 'μˆ˜λ™ μ—λŸ¬ λ²„νŠΌ',
19
- },
20
- });
93
+ import { ErrorBoundary } from 'react-error-boundary';
94
+ import { captureException } from 'rusty-replay';
95
+
96
+ <ErrorBoundaryFallbackComponent={() => <div>였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.</div>}
97
+ onError={(error, info) => {
98
+ captureException(error);
21
99
  }}
22
- className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
23
100
  >
24
- μˆ˜λ™ μ—λŸ¬ λ°œμƒ 및 리포트
25
- </button>;
101
+ <App />
102
+ </ErrorBoundary>
103
+
104
+ ```
105
+
106
+ ---
107
+
108
+ ## πŸ” λ¦¬ν”Œλ ˆμ΄ μž¬μƒ (rrweb-player)
109
+
110
+ ```tsx
111
+ import { decompressFromBase64 } from 'rusty-replay';
112
+ import 'rrweb-player/dist/style.css';
113
+
114
+ const events = decompressFromBase64(error.replay);
115
+
116
+ new Player({
117
+ target: document.getElementById('player')!,
118
+ props: {
119
+ events,
120
+ width: 1000,
121
+ height: 600,
122
+ autoPlay: false,
123
+ showController: true,
124
+ skipInactive: true,
125
+ },
126
+ });
127
+ ```
128
+
129
+ ---
130
+
131
+ ## πŸ“‹ μ„œλ²„λ‘œ μ „μ†‘λ˜λŠ” 데이터 Payload
132
+
133
+ ```ts
134
+ {
135
+ message: 'Uncaught TypeError: ...',
136
+ stacktrace: 'TypeError: ...',
137
+ replay: 'compressedBase64Data',
138
+ environment: 'production',
139
+ browser: 'Chrome 123.0',
140
+ os: 'macOS 14',
141
+ userAgent: '...',
142
+ appVersion: '1.0.0',
143
+ apiKey: 'YOUR_API_KEY',
144
+ additionalInfo: {
145
+ request: {...},
146
+ response: {...},
147
+ pageUrl: 'https://your.site/path'
148
+ },
149
+ userId: 123
150
+ }
151
+
26
152
  ```
153
+
154
+ ---
155
+
156
+ ## πŸ“Ž API μ°Έκ³ 
157
+
158
+ ### `init(options: InitOptions)`
159
+
160
+ μ˜΅μ…˜ μ„€λͺ…:
161
+
162
+ | μ˜΅μ…˜ | νƒ€μž… | μ„€λͺ… |
163
+ | ----------------- | ------ | -------------------------------------- |
164
+ | `endpoint` | string | μ—λŸ¬ μˆ˜μ§‘ μ„œλ²„μ˜ μ—”λ“œν¬μΈνŠΈ |
165
+ | `apiKey` | string | ν”„λ‘œμ νŠΈ μ‹λ³„μš© API Key |
166
+ | `flushIntervalMs` | number | μ—λŸ¬ 전솑 간격 (κΈ°λ³Έ: 10초) |
167
+ | `maxBufferSize` | number | μ΅œλŒ€ 전솑 버퍼 크기 |
168
+ | `beforeErrorSec` | number | λ¦¬ν”Œλ ˆμ΄ μˆ˜μ§‘ ꡬ간 (κΈ°λ³Έ: 10초 μ „κΉŒμ§€) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rusty-replay",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",