@bugspotter/sdk 0.1.0-alpha.1
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/CHANGELOG.md +69 -0
- package/LICENSE +21 -0
- package/README.md +639 -0
- package/dist/bugspotter.min.js +2 -0
- package/dist/bugspotter.min.js.LICENSE.txt +14 -0
- package/dist/capture/base-capture.d.ts +34 -0
- package/dist/capture/base-capture.js +23 -0
- package/dist/capture/capture-lifecycle.d.ts +24 -0
- package/dist/capture/capture-lifecycle.js +2 -0
- package/dist/capture/console.d.ts +29 -0
- package/dist/capture/console.js +107 -0
- package/dist/capture/metadata.d.ts +21 -0
- package/dist/capture/metadata.js +76 -0
- package/dist/capture/network.d.ts +32 -0
- package/dist/capture/network.js +135 -0
- package/dist/capture/screenshot.d.ts +19 -0
- package/dist/capture/screenshot.js +52 -0
- package/dist/collectors/dom.d.ts +67 -0
- package/dist/collectors/dom.js +164 -0
- package/dist/collectors/index.d.ts +2 -0
- package/dist/collectors/index.js +5 -0
- package/dist/core/buffer.d.ts +50 -0
- package/dist/core/buffer.js +88 -0
- package/dist/core/circular-buffer.d.ts +42 -0
- package/dist/core/circular-buffer.js +77 -0
- package/dist/core/compress.d.ts +49 -0
- package/dist/core/compress.js +245 -0
- package/dist/core/offline-queue.d.ts +76 -0
- package/dist/core/offline-queue.js +301 -0
- package/dist/core/transport.d.ts +73 -0
- package/dist/core/transport.js +352 -0
- package/dist/core/upload-helpers.d.ts +32 -0
- package/dist/core/upload-helpers.js +79 -0
- package/dist/core/uploader.d.ts +70 -0
- package/dist/core/uploader.js +185 -0
- package/dist/index.d.ts +140 -0
- package/dist/index.esm.js +205 -0
- package/dist/index.js +244 -0
- package/dist/utils/logger.d.ts +28 -0
- package/dist/utils/logger.js +84 -0
- package/dist/utils/sanitize-patterns.d.ts +103 -0
- package/dist/utils/sanitize-patterns.js +282 -0
- package/dist/utils/sanitize.d.ts +73 -0
- package/dist/utils/sanitize.js +254 -0
- package/dist/widget/button.d.ts +33 -0
- package/dist/widget/button.js +143 -0
- package/dist/widget/components/dom-element-cache.d.ts +62 -0
- package/dist/widget/components/dom-element-cache.js +105 -0
- package/dist/widget/components/form-validator.d.ts +66 -0
- package/dist/widget/components/form-validator.js +115 -0
- package/dist/widget/components/pii-detection-display.d.ts +64 -0
- package/dist/widget/components/pii-detection-display.js +142 -0
- package/dist/widget/components/redaction-canvas.d.ts +95 -0
- package/dist/widget/components/redaction-canvas.js +230 -0
- package/dist/widget/components/screenshot-processor.d.ts +44 -0
- package/dist/widget/components/screenshot-processor.js +191 -0
- package/dist/widget/components/style-manager.d.ts +37 -0
- package/dist/widget/components/style-manager.js +296 -0
- package/dist/widget/components/template-manager.d.ts +66 -0
- package/dist/widget/components/template-manager.js +198 -0
- package/dist/widget/modal.d.ts +62 -0
- package/dist/widget/modal.js +299 -0
- package/docs/CDN.md +213 -0
- package/docs/FRAMEWORK_INTEGRATION.md +1104 -0
- package/docs/PUBLISHING.md +550 -0
- package/docs/SESSION_REPLAY.md +381 -0
- package/package.json +90 -0
package/README.md
ADDED
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
# @bugspotter/sdk
|
|
2
|
+
|
|
3
|
+
> Core SDK for capturing and reporting bugs
|
|
4
|
+
|
|
5
|
+
The BugSpotter SDK provides a comprehensive solution for capturing bug reports in web applications, including screenshots, console logs, network requests, and browser metadata.
|
|
6
|
+
|
|
7
|
+
## ๐ฆ Installation
|
|
8
|
+
|
|
9
|
+
### NPM Package
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# npm
|
|
13
|
+
npm install @bugspotter/sdk
|
|
14
|
+
|
|
15
|
+
# yarn
|
|
16
|
+
yarn add @bugspotter/sdk
|
|
17
|
+
|
|
18
|
+
# pnpm
|
|
19
|
+
pnpm add @bugspotter/sdk
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### CDN
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<!-- BugSpotter CDN (versioned - recommended for production) -->
|
|
26
|
+
<script src="https://cdn.bugspotter.io/sdk/bugspotter-0.1.0.min.js"></script>
|
|
27
|
+
|
|
28
|
+
<!-- Latest version (for development only) -->
|
|
29
|
+
<script src="https://cdn.bugspotter.io/sdk/bugspotter-latest.min.js"></script>
|
|
30
|
+
|
|
31
|
+
<!-- Or unpkg -->
|
|
32
|
+
<script src="https://unpkg.com/@bugspotter/sdk@latest/dist/bugspotter.min.js"></script>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
๐ **See [CDN Usage Guide](./docs/CDN.md) for detailed CDN integration, SRI hashes, and troubleshooting.**
|
|
36
|
+
|
|
37
|
+
### From Source
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Clone and build from source
|
|
41
|
+
git clone https://github.com/apexbridge-tech/bugspotter.git
|
|
42
|
+
cd bugspotter/packages/sdk
|
|
43
|
+
pnpm install
|
|
44
|
+
pnpm build
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The built SDK will be available at `dist/bugspotter.min.js` (~99 KB minified with session replay).
|
|
48
|
+
|
|
49
|
+
## ๐ Quick Start
|
|
50
|
+
|
|
51
|
+
### Basic Usage
|
|
52
|
+
|
|
53
|
+
**ES Modules (React, Vue, Angular, etc.)**
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
57
|
+
|
|
58
|
+
// Initialize with auto-widget
|
|
59
|
+
const bugSpotter = BugSpotter.init({
|
|
60
|
+
apiKey: 'bgs_your_api_key',
|
|
61
|
+
endpoint: 'https://api.bugspotter.com',
|
|
62
|
+
showWidget: true,
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**CommonJS (Node.js)**
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
const BugSpotter = require('@bugspotter/sdk');
|
|
70
|
+
|
|
71
|
+
const bugSpotter = BugSpotter.init({
|
|
72
|
+
apiKey: 'bgs_your_api_key',
|
|
73
|
+
endpoint: 'https://api.bugspotter.com',
|
|
74
|
+
showWidget: true,
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**UMD (Browser script tag)**
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<script src="https://cdn.bugspotter.io/sdk/bugspotter-latest.min.js"></script>
|
|
82
|
+
<script>
|
|
83
|
+
// Initialize with auto-widget
|
|
84
|
+
const bugSpotter = BugSpotter.init({
|
|
85
|
+
apiKey: 'bgs_your_api_key',
|
|
86
|
+
endpoint: 'https://api.bugspotter.com',
|
|
87
|
+
showWidget: true,
|
|
88
|
+
});
|
|
89
|
+
</script>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Direct File Uploads (Presigned URLs)
|
|
93
|
+
|
|
94
|
+
For better performance with large files, use `DirectUploader` to upload files directly to storage:
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
import { DirectUploader, compressReplayEvents } from '@bugspotter/sdk';
|
|
98
|
+
|
|
99
|
+
// 1. Create bug report (metadata only)
|
|
100
|
+
const response = await fetch('https://api.example.com/api/v1/reports', {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
headers: {
|
|
103
|
+
'Content-Type': 'application/json',
|
|
104
|
+
'x-api-key': 'bgs_your_api_key',
|
|
105
|
+
},
|
|
106
|
+
body: JSON.stringify({
|
|
107
|
+
project_id: 'project-uuid',
|
|
108
|
+
title: 'Bug title',
|
|
109
|
+
description: 'Bug description',
|
|
110
|
+
}),
|
|
111
|
+
});
|
|
112
|
+
const { id: bugId } = await response.json();
|
|
113
|
+
|
|
114
|
+
// 2. Initialize DirectUploader
|
|
115
|
+
const uploader = new DirectUploader({
|
|
116
|
+
apiEndpoint: 'https://api.example.com',
|
|
117
|
+
apiKey: 'bgs_your_api_key',
|
|
118
|
+
projectId: 'project-uuid',
|
|
119
|
+
bugId,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// 3. Upload screenshot with progress tracking
|
|
123
|
+
const screenshotBlob = await fetch(screenshotDataUrl).then((r) => r.blob());
|
|
124
|
+
await uploader.uploadScreenshot(screenshotBlob, (progress) => {
|
|
125
|
+
console.log(`Upload: ${progress.percentage}%`);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// 4. Compress and upload replay
|
|
129
|
+
const compressedReplay = await compressReplayEvents(replayEvents);
|
|
130
|
+
await uploader.uploadReplay(compressedReplay);
|
|
131
|
+
|
|
132
|
+
console.log('All uploads complete!');
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Benefits:**
|
|
136
|
+
|
|
137
|
+
- 97% memory reduction (3.33MB โ 100KB API payload)
|
|
138
|
+
- 3x faster uploads (direct to storage)
|
|
139
|
+
- Progress tracking for large files
|
|
140
|
+
- Automatic compression for replays
|
|
141
|
+
|
|
142
|
+
### Manual Capture
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
// Initialize without widget
|
|
146
|
+
const bugSpotter = BugSpotter.init({
|
|
147
|
+
apiKey: 'your-api-key',
|
|
148
|
+
endpoint: 'https://api.example.com/bugs',
|
|
149
|
+
showWidget: false,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Capture bug report manually
|
|
153
|
+
async function reportBug() {
|
|
154
|
+
const report = await bugSpotter.capture();
|
|
155
|
+
console.log('Captured:', report);
|
|
156
|
+
// report contains: screenshot, console, network, metadata
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## ๐จ Using the Widget
|
|
161
|
+
|
|
162
|
+
### Automatic Widget
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
// Widget appears automatically with showWidget: true
|
|
166
|
+
const bugSpotter = BugSpotter.init({
|
|
167
|
+
apiKey: 'demo-key',
|
|
168
|
+
endpoint: 'http://localhost:4000/api/bugs',
|
|
169
|
+
showWidget: true,
|
|
170
|
+
widgetOptions: {
|
|
171
|
+
position: 'bottom-right',
|
|
172
|
+
icon: 'โก',
|
|
173
|
+
backgroundColor: '#1a365d',
|
|
174
|
+
size: 48,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Custom Widget
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
// Create custom floating button
|
|
183
|
+
const button = new BugSpotter.FloatingButton({
|
|
184
|
+
position: 'bottom-right',
|
|
185
|
+
icon: '๐',
|
|
186
|
+
backgroundColor: '#ff4444',
|
|
187
|
+
size: 56,
|
|
188
|
+
offset: { x: 24, y: 24 },
|
|
189
|
+
style: {
|
|
190
|
+
boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
|
|
191
|
+
border: '2px solid white',
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Handle click
|
|
196
|
+
button.onClick(async () => {
|
|
197
|
+
const report = await bugSpotter.capture();
|
|
198
|
+
|
|
199
|
+
const modal = new BugSpotter.BugReportModal({
|
|
200
|
+
onSubmit: async (data) => {
|
|
201
|
+
// data.title, data.description
|
|
202
|
+
const response = await fetch('https://api.example.com/bugs', {
|
|
203
|
+
method: 'POST',
|
|
204
|
+
headers: {
|
|
205
|
+
'Content-Type': 'application/json',
|
|
206
|
+
Authorization: `Bearer ${apiKey}`,
|
|
207
|
+
},
|
|
208
|
+
body: JSON.stringify({
|
|
209
|
+
...data,
|
|
210
|
+
report,
|
|
211
|
+
}),
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
if (!response.ok) {
|
|
215
|
+
throw new Error('Submission failed');
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
modal.show(report.screenshot);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Control button
|
|
224
|
+
button.show();
|
|
225
|
+
button.hide();
|
|
226
|
+
button.setIcon('โ ๏ธ');
|
|
227
|
+
button.setBackgroundColor('#00ff00');
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## ๐ PII Sanitization
|
|
231
|
+
|
|
232
|
+
Automatic detection and masking of sensitive data before submission.
|
|
233
|
+
|
|
234
|
+
**Built-in patterns:** Email, phone, credit card, SSN, Kazakhstan IIN, IP address
|
|
235
|
+
|
|
236
|
+
```javascript
|
|
237
|
+
BugSpotter.init({
|
|
238
|
+
sanitize: {
|
|
239
|
+
enabled: true, // Default
|
|
240
|
+
patterns: ['email', 'phone', 'creditcard'],
|
|
241
|
+
customPatterns: [{ name: 'api-key', regex: /API[-_]KEY:\s*[\w-]{20,}/gi }],
|
|
242
|
+
excludeSelectors: ['.public-email'],
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Performance:** <10ms overhead, supports Cyrillic text
|
|
248
|
+
|
|
249
|
+
## ๏ฟฝ๐ API Reference
|
|
250
|
+
|
|
251
|
+
### BugSpotter Class
|
|
252
|
+
|
|
253
|
+
#### `BugSpotter.init(config)`
|
|
254
|
+
|
|
255
|
+
Initialize the SDK.
|
|
256
|
+
|
|
257
|
+
**Parameters:**
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
interface BugSpotterConfig {
|
|
261
|
+
apiKey?: string; // API key for authentication
|
|
262
|
+
endpoint?: string; // Backend API URL
|
|
263
|
+
showWidget?: boolean; // Auto-show widget (default: true)
|
|
264
|
+
widgetOptions?: FloatingButtonOptions;
|
|
265
|
+
replay?: {
|
|
266
|
+
// Session replay configuration
|
|
267
|
+
enabled?: boolean; // Enable replay (default: true)
|
|
268
|
+
duration?: number; // Buffer duration in seconds (default: 15)
|
|
269
|
+
sampling?: {
|
|
270
|
+
mousemove?: number; // Mousemove throttle in ms (default: 50)
|
|
271
|
+
scroll?: number; // Scroll throttle in ms (default: 100)
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
sanitize?: {
|
|
275
|
+
// PII sanitization configuration
|
|
276
|
+
enabled?: boolean; // Enable PII sanitization (default: true)
|
|
277
|
+
patterns?: Array<
|
|
278
|
+
// PII patterns to detect
|
|
279
|
+
'email' | 'phone' | 'creditcard' | 'ssn' | 'iin' | 'ip' | 'custom'
|
|
280
|
+
>;
|
|
281
|
+
customPatterns?: Array<{
|
|
282
|
+
// Custom regex patterns
|
|
283
|
+
name: string; // Pattern name for [REDACTED-NAME]
|
|
284
|
+
regex: RegExp; // Detection regex
|
|
285
|
+
}>;
|
|
286
|
+
excludeSelectors?: string[]; // CSS selectors to exclude from sanitization
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Returns:** `BugSpotter` instance
|
|
292
|
+
|
|
293
|
+
#### `bugSpotter.capture()`
|
|
294
|
+
|
|
295
|
+
Capture current bug report data.
|
|
296
|
+
|
|
297
|
+
**Returns:** `Promise<BugReport>`
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
interface BugReport {
|
|
301
|
+
screenshot: string; // Base64 PNG data URL
|
|
302
|
+
console: ConsoleLog[]; // Array of console entries
|
|
303
|
+
network: NetworkRequest[]; // Array of network requests
|
|
304
|
+
metadata: BrowserMetadata; // Browser/system info
|
|
305
|
+
replay: eventWithTime[]; // Session replay events (rrweb format)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
interface ConsoleLog {
|
|
309
|
+
level: string; // 'log', 'warn', 'error', 'info', 'debug'
|
|
310
|
+
message: string; // Formatted message
|
|
311
|
+
timestamp: number; // Unix timestamp
|
|
312
|
+
stack?: string; // Error stack trace (for errors)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
interface NetworkRequest {
|
|
316
|
+
url: string; // Request URL
|
|
317
|
+
method: string; // HTTP method
|
|
318
|
+
status: number; // HTTP status code
|
|
319
|
+
duration: number; // Request duration in ms
|
|
320
|
+
timestamp: number; // Unix timestamp
|
|
321
|
+
error?: string; // Error message if failed
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
interface BrowserMetadata {
|
|
325
|
+
userAgent: string;
|
|
326
|
+
viewport: { width: number; height: number };
|
|
327
|
+
browser: string; // Detected browser name
|
|
328
|
+
os: string; // Detected OS
|
|
329
|
+
url: string; // Current page URL
|
|
330
|
+
timestamp: number; // Capture timestamp
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
#### `bugSpotter.getConfig()`
|
|
335
|
+
|
|
336
|
+
Get current configuration.
|
|
337
|
+
|
|
338
|
+
**Returns:** `Readonly<BugSpotterConfig>`
|
|
339
|
+
|
|
340
|
+
#### `bugSpotter.destroy()`
|
|
341
|
+
|
|
342
|
+
Clean up and destroy the SDK instance.
|
|
343
|
+
|
|
344
|
+
### FloatingButton Class
|
|
345
|
+
|
|
346
|
+
#### Constructor
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
new FloatingButton(options?: FloatingButtonOptions)
|
|
350
|
+
|
|
351
|
+
interface FloatingButtonOptions {
|
|
352
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
353
|
+
icon?: string; // Emoji or text
|
|
354
|
+
backgroundColor?: string; // CSS color
|
|
355
|
+
size?: number; // Size in pixels
|
|
356
|
+
offset?: { x: number; y: number };
|
|
357
|
+
style?: Record<string, string>; // Additional CSS
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
#### Methods
|
|
362
|
+
|
|
363
|
+
- `button.onClick(handler: () => void | Promise<void>)` - Set click handler
|
|
364
|
+
- `button.show()` - Show the button
|
|
365
|
+
- `button.hide()` - Hide the button
|
|
366
|
+
- `button.setIcon(icon: string)` - Change icon
|
|
367
|
+
- `button.setBackgroundColor(color: string)` - Change color
|
|
368
|
+
- `button.destroy()` - Remove button from DOM
|
|
369
|
+
|
|
370
|
+
### BugReportModal Class
|
|
371
|
+
|
|
372
|
+
#### Constructor
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
new BugReportModal(options: BugReportModalOptions)
|
|
376
|
+
|
|
377
|
+
interface BugReportModalOptions {
|
|
378
|
+
onSubmit: (data: BugReportData) => void | Promise<void>;
|
|
379
|
+
onClose?: () => void;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
interface BugReportData {
|
|
383
|
+
title: string;
|
|
384
|
+
description: string;
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
#### Methods
|
|
389
|
+
|
|
390
|
+
- `modal.show(screenshot: string)` - Display modal with screenshot
|
|
391
|
+
- `modal.close()` - Close the modal
|
|
392
|
+
- `modal.destroy()` - Remove modal from DOM
|
|
393
|
+
|
|
394
|
+
**Features:**
|
|
395
|
+
|
|
396
|
+
- Form validation (title and description required)
|
|
397
|
+
- Loading state during async submission
|
|
398
|
+
- Error handling with user feedback
|
|
399
|
+
- Escape key to close
|
|
400
|
+
- Click X button to close
|
|
401
|
+
- Cannot close by clicking outside (prevents data loss)
|
|
402
|
+
|
|
403
|
+
### DirectUploader Class
|
|
404
|
+
|
|
405
|
+
Direct client-to-storage uploads using presigned URLs (97% memory reduction, 3x faster).
|
|
406
|
+
|
|
407
|
+
#### Constructor
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
new DirectUploader(config: DirectUploadConfig)
|
|
411
|
+
|
|
412
|
+
interface DirectUploadConfig {
|
|
413
|
+
apiEndpoint: string; // Backend API URL
|
|
414
|
+
apiKey: string; // bgs_... API key
|
|
415
|
+
projectId: string; // Project UUID
|
|
416
|
+
bugId: string; // Bug report UUID
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
#### Methods
|
|
421
|
+
|
|
422
|
+
**`uploadScreenshot(file, onProgress?)`**
|
|
423
|
+
|
|
424
|
+
Upload screenshot directly to storage.
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
const screenshotBlob = await fetch(dataUrl).then((r) => r.blob());
|
|
428
|
+
|
|
429
|
+
const result = await uploader.uploadScreenshot(screenshotBlob, (progress) => {
|
|
430
|
+
console.log(`Screenshot: ${progress.loaded}/${progress.total} (${progress.percentage}%)`);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// result: { success: true, storageKey: "screenshots/..." }
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**`uploadReplay(compressedBlob, onProgress?)`**
|
|
437
|
+
|
|
438
|
+
Upload compressed replay data to storage.
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
const compressed = await compressReplayEvents(events);
|
|
442
|
+
const result = await uploader.uploadReplay(compressed);
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
**`uploadAttachment(file, onProgress?)`**
|
|
446
|
+
|
|
447
|
+
Upload attachment file to storage.
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
const file = document.querySelector('input[type="file"]').files[0];
|
|
451
|
+
const result = await uploader.uploadAttachment(file);
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
#### Upload Progress
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
interface UploadProgress {
|
|
458
|
+
loaded: number; // Bytes uploaded
|
|
459
|
+
total: number; // Total bytes
|
|
460
|
+
percentage: number; // 0-100
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
type UploadProgressCallback = (progress: UploadProgress) => void;
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
#### Upload Result
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
interface UploadResult {
|
|
470
|
+
success: boolean;
|
|
471
|
+
storageKey?: string; // Storage location (if successful)
|
|
472
|
+
error?: string; // Error message (if failed)
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Compression Utilities
|
|
477
|
+
|
|
478
|
+
Compress replay events before uploading (2MB JSON โ ~200KB gzip).
|
|
479
|
+
|
|
480
|
+
**`compressReplayEvents(events)`**
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
import { compressReplayEvents } from '@bugspotter/sdk';
|
|
484
|
+
|
|
485
|
+
const events = [
|
|
486
|
+
/* rrweb events */
|
|
487
|
+
];
|
|
488
|
+
const compressed = await compressReplayEvents(events);
|
|
489
|
+
console.log(`Compressed: ${(compressed.size / 1024).toFixed(2)} KB`);
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
Uses native `CompressionStream` API (Chrome 80+, Firefox 113+, Safari 16.4+).
|
|
493
|
+
|
|
494
|
+
**`estimateCompressedReplaySize(events)`**
|
|
495
|
+
|
|
496
|
+
Estimate compressed size without actually compressing.
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
const estimatedSize = estimateCompressedReplaySize(events);
|
|
500
|
+
console.log(`Estimated: ${(estimatedSize / 1024).toFixed(2)} KB`);
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**`isWithinSizeLimit(blob, limitMB)`**
|
|
504
|
+
|
|
505
|
+
Check if blob is within size limit.
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
if (!isWithinSizeLimit(compressed, 10)) {
|
|
509
|
+
console.warn('File exceeds 10MB limit');
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
## ๐ Capture Modules
|
|
514
|
+
|
|
515
|
+
The SDK automatically captures:
|
|
516
|
+
|
|
517
|
+
- **๐ธ Screenshots** - CSP-safe full page capture (~500ms)
|
|
518
|
+
- **๐ฅ Session Replay** - Last 15-30s of user interactions (rrweb)
|
|
519
|
+
- **๐ Console** - All log levels with stack traces
|
|
520
|
+
- **๐ Network** - fetch/XHR timing and responses
|
|
521
|
+
- **๐ป Metadata** - Browser, OS, viewport, URL
|
|
522
|
+
|
|
523
|
+
See [Session Replay Documentation](docs/SESSION_REPLAY.md) for detailed configuration.
|
|
524
|
+
|
|
525
|
+
### Screenshot Capture
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
import { ScreenshotCapture } from '@bugspotter/sdk';
|
|
529
|
+
|
|
530
|
+
const screenshotCapture = new ScreenshotCapture();
|
|
531
|
+
const screenshot = await screenshotCapture.capture();
|
|
532
|
+
// Returns: Base64 PNG data URL or 'SCREENSHOT_FAILED'
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**Features:**
|
|
536
|
+
|
|
537
|
+
- CSP-safe using `html-to-image`
|
|
538
|
+
- Full page capture
|
|
539
|
+
- Automatic error handling
|
|
540
|
+
- ~500ms average capture time
|
|
541
|
+
|
|
542
|
+
### Console Capture
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
import { ConsoleCapture } from '@bugspotter/sdk';
|
|
546
|
+
|
|
547
|
+
const consoleCapture = new ConsoleCapture();
|
|
548
|
+
const logs = consoleCapture.getLogs();
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
**Features:**
|
|
552
|
+
|
|
553
|
+
- Captures: log, warn, error, info, debug
|
|
554
|
+
- Stack traces for errors
|
|
555
|
+
- Timestamps for all entries
|
|
556
|
+
- Object stringification
|
|
557
|
+
- Circular reference handling
|
|
558
|
+
- Configurable max logs (default: 100)
|
|
559
|
+
|
|
560
|
+
### Network Capture
|
|
561
|
+
|
|
562
|
+
```typescript
|
|
563
|
+
import { NetworkCapture } from '@bugspotter/sdk';
|
|
564
|
+
|
|
565
|
+
const networkCapture = new NetworkCapture();
|
|
566
|
+
const requests = networkCapture.getRequests();
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
**Features:**
|
|
570
|
+
|
|
571
|
+
- Captures fetch() and XMLHttpRequest
|
|
572
|
+
- Request/response timing
|
|
573
|
+
- HTTP status codes
|
|
574
|
+
- Error tracking
|
|
575
|
+
- Singleton pattern (one instance per page)
|
|
576
|
+
|
|
577
|
+
### Metadata Capture
|
|
578
|
+
|
|
579
|
+
```typescript
|
|
580
|
+
import { MetadataCapture } from '@bugspotter/sdk';
|
|
581
|
+
|
|
582
|
+
const metadataCapture = new MetadataCapture();
|
|
583
|
+
const metadata = metadataCapture.capture();
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
**Features:**
|
|
587
|
+
|
|
588
|
+
- Browser detection (Chrome, Firefox, Safari, Edge, etc.)
|
|
589
|
+
- OS detection (Windows, macOS, Linux, iOS, Android)
|
|
590
|
+
- Viewport dimensions
|
|
591
|
+
- User agent string
|
|
592
|
+
- Current URL
|
|
593
|
+
- Timestamp
|
|
594
|
+
|
|
595
|
+
## ๐งช Testing
|
|
596
|
+
|
|
597
|
+
```bash
|
|
598
|
+
pnpm test # All tests
|
|
599
|
+
pnpm test --watch # Watch mode
|
|
600
|
+
pnpm test --coverage # Coverage report
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
**345 tests** passing (unit + E2E + Playwright)
|
|
604
|
+
|
|
605
|
+
## ๏ฟฝ๏ธ Building
|
|
606
|
+
|
|
607
|
+
```bash
|
|
608
|
+
pnpm run dev # Development with watch
|
|
609
|
+
pnpm run build # Production build
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
Output: `dist/bugspotter.min.js` (~99 KB)
|
|
613
|
+
|
|
614
|
+
## ๐ Performance
|
|
615
|
+
|
|
616
|
+
- **Bundle**: ~99 KB minified
|
|
617
|
+
- **Load**: < 100ms
|
|
618
|
+
- **Memory**: < 15 MB (30s replay buffer)
|
|
619
|
+
- **Screenshot**: ~500ms
|
|
620
|
+
- **PII sanitization**: <10ms
|
|
621
|
+
|
|
622
|
+
## ๐ Security
|
|
623
|
+
|
|
624
|
+
- CSP-safe (no eval, no inline scripts)
|
|
625
|
+
- Automatic PII detection and masking
|
|
626
|
+
- Input validation
|
|
627
|
+
- HTTPS recommended
|
|
628
|
+
|
|
629
|
+
## ๐ค Contributing
|
|
630
|
+
|
|
631
|
+
See the main [CONTRIBUTING.md](../../CONTRIBUTING.md) guide.
|
|
632
|
+
|
|
633
|
+
## ๐ License
|
|
634
|
+
|
|
635
|
+
MIT License - see [LICENSE](../../LICENSE)
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
Part of the [BugSpotter](../../README.md) project
|