@bugspotter/sdk
Version:
Professional bug reporting SDK with screenshots, session replay, and automatic error capture for web applications
640 lines (478 loc) โข 15.2 kB
Markdown
# @bugspotter/sdk
> Core SDK for capturing and reporting bugs
The BugSpotter SDK provides a comprehensive solution for capturing bug reports in web applications, including screenshots, console logs, network requests, and browser metadata.
## ๐ฆ Installation
### NPM Package
```bash
# npm
npm install @bugspotter/sdk
# yarn
yarn add @bugspotter/sdk
# pnpm
pnpm add @bugspotter/sdk
```
### CDN
```html
<!-- BugSpotter CDN (versioned - recommended for production) -->
<script src="https://cdn.bugspotter.io/sdk/bugspotter-0.1.0.min.js"></script>
<!-- Latest version (for development only) -->
<script src="https://cdn.bugspotter.io/sdk/bugspotter-latest.min.js"></script>
<!-- Or unpkg -->
<script src="https://unpkg.com/@bugspotter/sdk@latest/dist/bugspotter.min.js"></script>
```
๐ **See [CDN Usage Guide](./docs/CDN.md) for detailed CDN integration, SRI hashes, and troubleshooting.**
### From Source
```bash
# Clone and build from source
git clone https://github.com/apexbridge-tech/bugspotter.git
cd bugspotter/packages/sdk
pnpm install
pnpm build
```
The built SDK will be available at `dist/bugspotter.min.js` (~99 KB minified with session replay).
## ๐ Quick Start
### Basic Usage
**ES Modules (React, Vue, Angular, etc.)**
```javascript
import BugSpotter from '@bugspotter/sdk';
// Initialize with auto-widget
const bugSpotter = BugSpotter.init({
apiKey: 'bgs_your_api_key',
endpoint: 'https://api.bugspotter.com',
showWidget: true,
});
```
**CommonJS (Node.js)**
```javascript
const BugSpotter = require('@bugspotter/sdk');
const bugSpotter = BugSpotter.init({
apiKey: 'bgs_your_api_key',
endpoint: 'https://api.bugspotter.com',
showWidget: true,
});
```
**UMD (Browser script tag)**
```html
<script src="https://cdn.bugspotter.io/sdk/bugspotter-latest.min.js"></script>
<script>
// Initialize with auto-widget
const bugSpotter = BugSpotter.init({
apiKey: 'bgs_your_api_key',
endpoint: 'https://api.bugspotter.com',
showWidget: true,
});
</script>
```
### Direct File Uploads (Presigned URLs)
For better performance with large files, use `DirectUploader` to upload files directly to storage:
```javascript
import { DirectUploader, compressReplayEvents } from '@bugspotter/sdk';
// 1. Create bug report (metadata only)
const response = await fetch('https://api.example.com/api/v1/reports', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'bgs_your_api_key',
},
body: JSON.stringify({
project_id: 'project-uuid',
title: 'Bug title',
description: 'Bug description',
}),
});
const { id: bugId } = await response.json();
// 2. Initialize DirectUploader
const uploader = new DirectUploader({
apiEndpoint: 'https://api.example.com',
apiKey: 'bgs_your_api_key',
projectId: 'project-uuid',
bugId,
});
// 3. Upload screenshot with progress tracking
const screenshotBlob = await fetch(screenshotDataUrl).then((r) => r.blob());
await uploader.uploadScreenshot(screenshotBlob, (progress) => {
console.log(`Upload: ${progress.percentage}%`);
});
// 4. Compress and upload replay
const compressedReplay = await compressReplayEvents(replayEvents);
await uploader.uploadReplay(compressedReplay);
console.log('All uploads complete!');
```
**Benefits:**
- 97% memory reduction (3.33MB โ 100KB API payload)
- 3x faster uploads (direct to storage)
- Progress tracking for large files
- Automatic compression for replays
### Manual Capture
```javascript
// Initialize without widget
const bugSpotter = BugSpotter.init({
apiKey: 'your-api-key',
endpoint: 'https://api.example.com/bugs',
showWidget: false,
});
// Capture bug report manually
async function reportBug() {
const report = await bugSpotter.capture();
console.log('Captured:', report);
// report contains: screenshot, console, network, metadata
}
```
## ๐จ Using the Widget
### Automatic Widget
```javascript
// Widget appears automatically with showWidget: true
const bugSpotter = BugSpotter.init({
apiKey: 'demo-key',
endpoint: 'http://localhost:4000/api/bugs',
showWidget: true,
widgetOptions: {
position: 'bottom-right',
icon: 'โก',
backgroundColor: '#1a365d',
size: 48,
},
});
```
### Custom Widget
```javascript
// Create custom floating button
const button = new BugSpotter.FloatingButton({
position: 'bottom-right',
icon: '๐',
backgroundColor: '#ff4444',
size: 56,
offset: { x: 24, y: 24 },
style: {
boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
border: '2px solid white',
},
});
// Handle click
button.onClick(async () => {
const report = await bugSpotter.capture();
const modal = new BugSpotter.BugReportModal({
onSubmit: async (data) => {
// data.title, data.description
const response = await fetch('https://api.example.com/bugs', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
...data,
report,
}),
});
if (!response.ok) {
throw new Error('Submission failed');
}
},
});
modal.show(report.screenshot);
});
// Control button
button.show();
button.hide();
button.setIcon('โ ๏ธ');
button.setBackgroundColor('#00ff00');
```
## ๐ PII Sanitization
Automatic detection and masking of sensitive data before submission.
**Built-in patterns:** Email, phone, credit card, SSN, Kazakhstan IIN, IP address
```javascript
BugSpotter.init({
sanitize: {
enabled: true, // Default
patterns: ['email', 'phone', 'creditcard'],
customPatterns: [{ name: 'api-key', regex: /API[-_]KEY:\s*[\w-]{20,}/gi }],
excludeSelectors: ['.public-email'],
},
});
```
**Performance:** <10ms overhead, supports Cyrillic text
## ๏ฟฝ๐ API Reference
### BugSpotter Class
#### `BugSpotter.init(config)`
Initialize the SDK.
**Parameters:**
```typescript
interface BugSpotterConfig {
apiKey?: string; // API key for authentication
endpoint?: string; // Backend API URL
showWidget?: boolean; // Auto-show widget (default: true)
widgetOptions?: FloatingButtonOptions;
replay?: {
// Session replay configuration
enabled?: boolean; // Enable replay (default: true)
duration?: number; // Buffer duration in seconds (default: 15)
sampling?: {
mousemove?: number; // Mousemove throttle in ms (default: 50)
scroll?: number; // Scroll throttle in ms (default: 100)
};
};
sanitize?: {
// PII sanitization configuration
enabled?: boolean; // Enable PII sanitization (default: true)
patterns?: Array<
// PII patterns to detect
'email' | 'phone' | 'creditcard' | 'ssn' | 'iin' | 'ip' | 'custom'
>;
customPatterns?: Array<{
// Custom regex patterns
name: string; // Pattern name for [REDACTED-NAME]
regex: RegExp; // Detection regex
}>;
excludeSelectors?: string[]; // CSS selectors to exclude from sanitization
};
}
```
**Returns:** `BugSpotter` instance
#### `bugSpotter.capture()`
Capture current bug report data.
**Returns:** `Promise<BugReport>`
```typescript
interface BugReport {
screenshot: string; // Base64 PNG data URL
console: ConsoleLog[]; // Array of console entries
network: NetworkRequest[]; // Array of network requests
metadata: BrowserMetadata; // Browser/system info
replay: eventWithTime[]; // Session replay events (rrweb format)
}
interface ConsoleLog {
level: string; // 'log', 'warn', 'error', 'info', 'debug'
message: string; // Formatted message
timestamp: number; // Unix timestamp
stack?: string; // Error stack trace (for errors)
}
interface NetworkRequest {
url: string; // Request URL
method: string; // HTTP method
status: number; // HTTP status code
duration: number; // Request duration in ms
timestamp: number; // Unix timestamp
error?: string; // Error message if failed
}
interface BrowserMetadata {
userAgent: string;
viewport: { width: number; height: number };
browser: string; // Detected browser name
os: string; // Detected OS
url: string; // Current page URL
timestamp: number; // Capture timestamp
}
```
#### `bugSpotter.getConfig()`
Get current configuration.
**Returns:** `Readonly<BugSpotterConfig>`
#### `bugSpotter.destroy()`
Clean up and destroy the SDK instance.
### FloatingButton Class
#### Constructor
```typescript
new FloatingButton(options?: FloatingButtonOptions)
interface FloatingButtonOptions {
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
icon?: string; // Emoji or text
backgroundColor?: string; // CSS color
size?: number; // Size in pixels
offset?: { x: number; y: number };
style?: Record<string, string>; // Additional CSS
}
```
#### Methods
- `button.onClick(handler: () => void | Promise<void>)` - Set click handler
- `button.show()` - Show the button
- `button.hide()` - Hide the button
- `button.setIcon(icon: string)` - Change icon
- `button.setBackgroundColor(color: string)` - Change color
- `button.destroy()` - Remove button from DOM
### BugReportModal Class
#### Constructor
```typescript
new BugReportModal(options: BugReportModalOptions)
interface BugReportModalOptions {
onSubmit: (data: BugReportData) => void | Promise<void>;
onClose?: () => void;
}
interface BugReportData {
title: string;
description: string;
}
```
#### Methods
- `modal.show(screenshot: string)` - Display modal with screenshot
- `modal.close()` - Close the modal
- `modal.destroy()` - Remove modal from DOM
**Features:**
- Form validation (title and description required)
- Loading state during async submission
- Error handling with user feedback
- Escape key to close
- Click X button to close
- Cannot close by clicking outside (prevents data loss)
### DirectUploader Class
Direct client-to-storage uploads using presigned URLs (97% memory reduction, 3x faster).
#### Constructor
```typescript
new DirectUploader(config: DirectUploadConfig)
interface DirectUploadConfig {
apiEndpoint: string; // Backend API URL
apiKey: string; // bgs_... API key
projectId: string; // Project UUID
bugId: string; // Bug report UUID
}
```
#### Methods
**`uploadScreenshot(file, onProgress?)`**
Upload screenshot directly to storage.
```typescript
const screenshotBlob = await fetch(dataUrl).then((r) => r.blob());
const result = await uploader.uploadScreenshot(screenshotBlob, (progress) => {
console.log(`Screenshot: ${progress.loaded}/${progress.total} (${progress.percentage}%)`);
});
// result: { success: true, storageKey: "screenshots/..." }
```
**`uploadReplay(compressedBlob, onProgress?)`**
Upload compressed replay data to storage.
```typescript
const compressed = await compressReplayEvents(events);
const result = await uploader.uploadReplay(compressed);
```
**`uploadAttachment(file, onProgress?)`**
Upload attachment file to storage.
```typescript
const file = document.querySelector('input[type="file"]').files[0];
const result = await uploader.uploadAttachment(file);
```
#### Upload Progress
```typescript
interface UploadProgress {
loaded: number; // Bytes uploaded
total: number; // Total bytes
percentage: number; // 0-100
}
type UploadProgressCallback = (progress: UploadProgress) => void;
```
#### Upload Result
```typescript
interface UploadResult {
success: boolean;
storageKey?: string; // Storage location (if successful)
error?: string; // Error message (if failed)
}
```
### Compression Utilities
Compress replay events before uploading (2MB JSON โ ~200KB gzip).
**`compressReplayEvents(events)`**
```typescript
import { compressReplayEvents } from '@bugspotter/sdk';
const events = [
/* rrweb events */
];
const compressed = await compressReplayEvents(events);
console.log(`Compressed: ${(compressed.size / 1024).toFixed(2)} KB`);
```
Uses native `CompressionStream` API (Chrome 80+, Firefox 113+, Safari 16.4+).
**`estimateCompressedReplaySize(events)`**
Estimate compressed size without actually compressing.
```typescript
const estimatedSize = estimateCompressedReplaySize(events);
console.log(`Estimated: ${(estimatedSize / 1024).toFixed(2)} KB`);
```
**`isWithinSizeLimit(blob, limitMB)`**
Check if blob is within size limit.
```typescript
if (!isWithinSizeLimit(compressed, 10)) {
console.warn('File exceeds 10MB limit');
}
```
## ๐ Capture Modules
The SDK automatically captures:
- **๐ธ Screenshots** - CSP-safe full page capture (~500ms)
- **๐ฅ Session Replay** - Last 15-30s of user interactions (rrweb)
- **๐ Console** - All log levels with stack traces
- **๐ Network** - fetch/XHR timing and responses
- **๐ป Metadata** - Browser, OS, viewport, URL
See [Session Replay Documentation](docs/SESSION_REPLAY.md) for detailed configuration.
### Screenshot Capture
```typescript
import { ScreenshotCapture } from '@bugspotter/sdk';
const screenshotCapture = new ScreenshotCapture();
const screenshot = await screenshotCapture.capture();
// Returns: Base64 PNG data URL or 'SCREENSHOT_FAILED'
```
**Features:**
- CSP-safe using `html-to-image`
- Full page capture
- Automatic error handling
- ~500ms average capture time
### Console Capture
```typescript
import { ConsoleCapture } from '@bugspotter/sdk';
const consoleCapture = new ConsoleCapture();
const logs = consoleCapture.getLogs();
```
**Features:**
- Captures: log, warn, error, info, debug
- Stack traces for errors
- Timestamps for all entries
- Object stringification
- Circular reference handling
- Configurable max logs (default: 100)
### Network Capture
```typescript
import { NetworkCapture } from '@bugspotter/sdk';
const networkCapture = new NetworkCapture();
const requests = networkCapture.getRequests();
```
**Features:**
- Captures fetch() and XMLHttpRequest
- Request/response timing
- HTTP status codes
- Error tracking
- Singleton pattern (one instance per page)
### Metadata Capture
```typescript
import { MetadataCapture } from '@bugspotter/sdk';
const metadataCapture = new MetadataCapture();
const metadata = metadataCapture.capture();
```
**Features:**
- Browser detection (Chrome, Firefox, Safari, Edge, etc.)
- OS detection (Windows, macOS, Linux, iOS, Android)
- Viewport dimensions
- User agent string
- Current URL
- Timestamp
## ๐งช Testing
```bash
pnpm test # All tests
pnpm test --watch # Watch mode
pnpm test --coverage # Coverage report
```
**345 tests** passing (unit + E2E + Playwright)
## ๏ฟฝ๏ธ Building
```bash
pnpm run dev # Development with watch
pnpm run build # Production build
```
Output: `dist/bugspotter.min.js` (~99 KB)
## ๐ Performance
- **Bundle**: ~99 KB minified
- **Load**: < 100ms
- **Memory**: < 15 MB (30s replay buffer)
- **Screenshot**: ~500ms
- **PII sanitization**: <10ms
## ๐ Security
- CSP-safe (no eval, no inline scripts)
- Automatic PII detection and masking
- Input validation
- HTTPS recommended
## ๐ค Contributing
See the main [CONTRIBUTING.md](../../CONTRIBUTING.md) guide.
## ๐ License
MIT License - see [LICENSE](../../LICENSE)
---
Part of the [BugSpotter](../../README.md) project