@bugspotter/sdk 0.1.0-alpha.2 → 0.1.0-alpha.3

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 CHANGED
@@ -51,6 +51,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
51
51
  - 345 tests (unit + E2E + Playwright)
52
52
  - Full test coverage for core functionality
53
53
 
54
+ ## [0.1.0-alpha.3] - 2025-11-11
55
+
56
+ ### Changed
57
+
58
+ - **BREAKING**: Updated S3 upload implementation to use `fetch()` API instead of `XMLHttpRequest`
59
+ - More modern and efficient file upload mechanism
60
+ - Better compatibility with modern browser environments
61
+ - Improved error handling with AbortController
62
+
63
+ ### Fixed
64
+
65
+ - Fixed timeout memory leak in file upload handler by ensuring timeout is cleared in both success and error paths
66
+ - Fixed all integration and E2E tests to properly handle fetch-based S3 uploads
67
+
68
+ ### Improved
69
+
70
+ - Added comprehensive JSDoc documentation for `UPLOAD_TIMEOUT_MS` constant
71
+ - Refactored test mocks to match actual API structure with `presignedUrls` object
72
+ - Centralized test fixtures to eliminate code duplication (created `test-images.ts`)
73
+ - All 454 SDK tests now passing with 100% pass rate
74
+
54
75
  ## [Unreleased]
55
76
 
56
77
  ### Planned Features
package/README.md CHANGED
@@ -57,8 +57,12 @@ import BugSpotter from '@bugspotter/sdk';
57
57
 
58
58
  // Initialize with auto-widget
59
59
  const bugSpotter = BugSpotter.init({
60
- apiKey: 'bgs_your_api_key',
61
- endpoint: 'https://api.bugspotter.com',
60
+ endpoint: 'https://api.bugspotter.com/api/v1/reports',
61
+ auth: {
62
+ type: 'api-key',
63
+ apiKey: 'bgs_your_api_key',
64
+ projectId: 'your-project-uuid',
65
+ },
62
66
  showWidget: true,
63
67
  });
64
68
  ```
@@ -69,8 +73,12 @@ const bugSpotter = BugSpotter.init({
69
73
  const BugSpotter = require('@bugspotter/sdk');
70
74
 
71
75
  const bugSpotter = BugSpotter.init({
72
- apiKey: 'bgs_your_api_key',
73
- endpoint: 'https://api.bugspotter.com',
76
+ endpoint: 'https://api.bugspotter.com/api/v1/reports',
77
+ auth: {
78
+ type: 'api-key',
79
+ apiKey: 'bgs_your_api_key',
80
+ projectId: 'your-project-uuid',
81
+ },
74
82
  showWidget: true,
75
83
  });
76
84
  ```
@@ -82,70 +90,62 @@ const bugSpotter = BugSpotter.init({
82
90
  <script>
83
91
  // Initialize with auto-widget
84
92
  const bugSpotter = BugSpotter.init({
85
- apiKey: 'bgs_your_api_key',
86
- endpoint: 'https://api.bugspotter.com',
93
+ endpoint: 'https://api.bugspotter.com/api/v1/reports',
94
+ auth: {
95
+ type: 'api-key',
96
+ apiKey: 'bgs_your_api_key',
97
+ projectId: 'your-project-uuid',
98
+ },
87
99
  showWidget: true,
88
100
  });
89
101
  </script>
90
102
  ```
91
103
 
92
- ### Direct File Uploads (Presigned URLs)
104
+ ### How It Works
93
105
 
94
- For better performance with large files, use `DirectUploader` to upload files directly to storage:
106
+ The SDK automatically uses an **optimized presigned URL upload flow** (40% fewer HTTP requests):
95
107
 
96
108
  ```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
- });
109
+ import BugSpotter from '@bugspotter/sdk';
121
110
 
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}%`);
111
+ // 1. Initialize SDK with required auth
112
+ const bugSpotter = BugSpotter.init({
113
+ endpoint: 'https://api.bugspotter.com/api/v1/reports',
114
+ auth: {
115
+ type: 'api-key',
116
+ apiKey: 'bgs_your_api_key',
117
+ projectId: 'your-project-uuid', // Required for file uploads
118
+ },
119
+ showWidget: true,
126
120
  });
127
121
 
128
- // 4. Compress and upload replay
129
- const compressedReplay = await compressReplayEvents(replayEvents);
130
- await uploader.uploadReplay(compressedReplay);
131
-
132
- console.log('All uploads complete!');
122
+ // 2. Submit a bug report (SDK handles everything automatically)
123
+ // Optimized flow (3 HTTP requests instead of 5):
124
+ // - Step 1: POST /api/v1/reports with hasScreenshot/hasReplay flags
125
+ // → Returns bug ID + presigned URLs for screenshot & replay
126
+ // - Step 2: PUT to S3 presigned URLs (parallel uploads)
127
+ // - Step 3: POST /api/v1/reports/{id}/confirm-upload (confirm each file)
133
128
  ```
134
129
 
135
130
  **Benefits:**
136
131
 
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
132
+ - **40% fewer HTTP requests** - 3 requests vs 5 in legacy flow
133
+ - Files upload directly to S3 (faster, no API bottleneck)
134
+ - Automatic compression for replay events
135
+ - Concurrent uploads (screenshot + replay in parallel)
136
+ - Reduced server load
141
137
 
142
138
  ### Manual Capture
143
139
 
144
140
  ```javascript
145
141
  // Initialize without widget
146
142
  const bugSpotter = BugSpotter.init({
147
- apiKey: 'your-api-key',
148
- endpoint: 'https://api.example.com/bugs',
143
+ endpoint: 'https://api.bugspotter.com/api/v1/reports',
144
+ auth: {
145
+ type: 'api-key',
146
+ apiKey: 'bgs_your_api_key',
147
+ projectId: 'your-project-uuid',
148
+ },
149
149
  showWidget: false,
150
150
  });
151
151
 
@@ -164,8 +164,12 @@ async function reportBug() {
164
164
  ```javascript
165
165
  // Widget appears automatically with showWidget: true
166
166
  const bugSpotter = BugSpotter.init({
167
- apiKey: 'demo-key',
168
- endpoint: 'http://localhost:4000/api/bugs',
167
+ endpoint: 'https://api.bugspotter.com/api/v1/reports',
168
+ auth: {
169
+ type: 'api-key',
170
+ apiKey: 'bgs_your_api_key',
171
+ projectId: 'your-project-uuid',
172
+ },
169
173
  showWidget: true,
170
174
  widgetOptions: {
171
175
  position: 'bottom-right',
@@ -179,17 +183,35 @@ const bugSpotter = BugSpotter.init({
179
183
  ### Custom Widget
180
184
 
181
185
  ```javascript
182
- // Create custom floating button
186
+ // Default professional SVG icon (recommended)
183
187
  const button = new BugSpotter.FloatingButton({
188
+ position: 'bottom-right',
189
+ // icon: 'svg' is default - professional bug icon
190
+ backgroundColor: '#2563eb', // Professional blue (default)
191
+ tooltip: 'Report an Issue',
192
+ });
193
+
194
+ // Custom emoji/text icon
195
+ const button2 = new BugSpotter.FloatingButton({
184
196
  position: 'bottom-right',
185
197
  icon: '🐛',
186
198
  backgroundColor: '#ff4444',
187
199
  size: 56,
188
200
  offset: { x: 24, y: 24 },
189
- style: {
190
- boxShadow: '0 2px 8px rgba(0,0,0,0.2)',
191
- border: '2px solid white',
192
- },
201
+ });
202
+
203
+ // Custom SVG icon
204
+ const button3 = new BugSpotter.FloatingButton({
205
+ position: 'bottom-left',
206
+ customSvg: `
207
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
208
+ <path d="M12 2L2 7l10 5 10-5-10-5z"/>
209
+ <path d="M2 17l10 5 10-5M2 12l10 5 10-5"/>
210
+ </svg>
211
+ `,
212
+ tooltip: 'Custom Report Button',
213
+ backgroundColor: '#1a365d',
214
+ size: 48,
193
215
  });
194
216
 
195
217
  // Handle click
@@ -217,7 +239,7 @@ button.onClick(async () => {
217
239
  },
218
240
  });
219
241
 
220
- modal.show(report.screenshot);
242
+ modal.show(report._screenshotPreview || '');
221
243
  });
222
244
 
223
245
  // Control button
@@ -258,8 +280,13 @@ Initialize the SDK.
258
280
 
259
281
  ```typescript
260
282
  interface BugSpotterConfig {
261
- apiKey?: string; // API key for authentication
262
- endpoint?: string; // Backend API URL
283
+ endpoint: string; // Required: Backend API URL
284
+ auth: {
285
+ // Required: Authentication configuration
286
+ type: 'api-key';
287
+ apiKey: string; // API key (bgs_...)
288
+ projectId: string; // Project UUID (required for file uploads)
289
+ };
263
290
  showWidget?: boolean; // Auto-show widget (default: true)
264
291
  widgetOptions?: FloatingButtonOptions;
265
292
  replay?: {
@@ -298,11 +325,13 @@ Capture current bug report data.
298
325
 
299
326
  ```typescript
300
327
  interface BugReport {
301
- screenshot: string; // Base64 PNG data URL
328
+ screenshotKey?: string; // Storage key after presigned URL upload
302
329
  console: ConsoleLog[]; // Array of console entries
303
330
  network: NetworkRequest[]; // Array of network requests
304
331
  metadata: BrowserMetadata; // Browser/system info
305
- replay: eventWithTime[]; // Session replay events (rrweb format)
332
+ replay?: eventWithTime[]; // Session replay events (rrweb format)
333
+ replayKey?: string; // Storage key after presigned URL upload
334
+ _screenshotPreview?: string; // Internal: screenshot preview for modal (not sent to API)
306
335
  }
307
336
 
308
337
  interface ConsoleLog {
@@ -350,10 +379,12 @@ new FloatingButton(options?: FloatingButtonOptions)
350
379
 
351
380
  interface FloatingButtonOptions {
352
381
  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
382
+ icon?: string; // 'svg' for default bug icon, or custom emoji/text
383
+ customSvg?: string; // Custom SVG icon (overrides icon if provided)
384
+ backgroundColor?: string; // CSS color (default: '#2563eb' professional blue)
385
+ size?: number; // Size in pixels (default: 56)
356
386
  offset?: { x: number; y: number };
387
+ tooltip?: string; // Custom tooltip text (default: 'Report an Issue')
357
388
  style?: Record<string, string>; // Additional CSS
358
389
  }
359
390
  ```