@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
|
@@ -0,0 +1,1104 @@
|
|
|
1
|
+
# BugSpotter SDK - Framework Integration Guide
|
|
2
|
+
|
|
3
|
+
Complete guide for integrating BugSpotter SDK into React, Vue, Angular, and other JavaScript frameworks.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [React Integration](#react-integration)
|
|
8
|
+
- [Vue Integration](#vue-integration)
|
|
9
|
+
- [Angular Integration](#angular-integration)
|
|
10
|
+
- [Next.js Integration](#nextjs-integration)
|
|
11
|
+
- [Nuxt Integration](#nuxt-integration)
|
|
12
|
+
- [Svelte Integration](#svelte-integration)
|
|
13
|
+
- [Vanilla JavaScript](#vanilla-javascript)
|
|
14
|
+
- [TypeScript Support](#typescript-support)
|
|
15
|
+
- [Best Practices](#best-practices)
|
|
16
|
+
- [Troubleshooting](#troubleshooting)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## React Integration
|
|
21
|
+
|
|
22
|
+
### Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Install the SDK
|
|
26
|
+
npm install @bugspotter/sdk
|
|
27
|
+
# or
|
|
28
|
+
yarn add @bugspotter/sdk
|
|
29
|
+
# or
|
|
30
|
+
pnpm add @bugspotter/sdk
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Basic Setup with Hook
|
|
34
|
+
|
|
35
|
+
Create a custom hook for BugSpotter:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// hooks/useBugSpotter.ts
|
|
39
|
+
import { useEffect, useRef } from 'react';
|
|
40
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
41
|
+
|
|
42
|
+
export function useBugSpotter() {
|
|
43
|
+
const bugSpotterRef = useRef<any>(null);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
// Initialize BugSpotter on mount
|
|
47
|
+
bugSpotterRef.current = BugSpotter.init({
|
|
48
|
+
apiKey: process.env.REACT_APP_BUGSPOTTER_API_KEY,
|
|
49
|
+
endpoint: process.env.REACT_APP_BUGSPOTTER_ENDPOINT,
|
|
50
|
+
showWidget: true,
|
|
51
|
+
widgetOptions: {
|
|
52
|
+
position: 'bottom-right',
|
|
53
|
+
icon: '🐛',
|
|
54
|
+
},
|
|
55
|
+
sanitize: {
|
|
56
|
+
enabled: true,
|
|
57
|
+
patterns: ['email', 'phone', 'creditcard'],
|
|
58
|
+
},
|
|
59
|
+
replay: {
|
|
60
|
+
enabled: true,
|
|
61
|
+
duration: 30,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Cleanup on unmount
|
|
66
|
+
return () => {
|
|
67
|
+
if (bugSpotterRef.current) {
|
|
68
|
+
bugSpotterRef.current.destroy();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
return bugSpotterRef.current;
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Usage in App Component
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// App.tsx
|
|
81
|
+
import React from 'react';
|
|
82
|
+
import { useBugSpotter } from './hooks/useBugSpotter';
|
|
83
|
+
|
|
84
|
+
function App() {
|
|
85
|
+
const bugSpotter = useBugSpotter();
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div className="App">
|
|
89
|
+
<h1>My Application</h1>
|
|
90
|
+
{/* Your app content */}
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export default App;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Context Provider Pattern
|
|
99
|
+
|
|
100
|
+
For more control, create a context:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// context/BugSpotterContext.tsx
|
|
104
|
+
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
105
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
106
|
+
|
|
107
|
+
interface BugSpotterContextType {
|
|
108
|
+
bugSpotter: any | null;
|
|
109
|
+
reportBug: () => Promise<void>;
|
|
110
|
+
isInitialized: boolean;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const BugSpotterContext = createContext<BugSpotterContextType>({
|
|
114
|
+
bugSpotter: null,
|
|
115
|
+
reportBug: async () => {},
|
|
116
|
+
isInitialized: false,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
export function BugSpotterProvider({ children }: { children: React.ReactNode }) {
|
|
120
|
+
const [bugSpotter, setBugSpotter] = useState<any>(null);
|
|
121
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
122
|
+
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
const instance = BugSpotter.init({
|
|
125
|
+
apiKey: process.env.REACT_APP_BUGSPOTTER_API_KEY,
|
|
126
|
+
endpoint: process.env.REACT_APP_BUGSPOTTER_ENDPOINT,
|
|
127
|
+
showWidget: true,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
setBugSpotter(instance);
|
|
131
|
+
setIsInitialized(true);
|
|
132
|
+
|
|
133
|
+
return () => {
|
|
134
|
+
instance.destroy();
|
|
135
|
+
};
|
|
136
|
+
}, []);
|
|
137
|
+
|
|
138
|
+
const reportBug = async () => {
|
|
139
|
+
if (bugSpotter) {
|
|
140
|
+
const report = await bugSpotter.capture();
|
|
141
|
+
console.log('Bug report captured:', report);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<BugSpotterContext.Provider value={{ bugSpotter, reportBug, isInitialized }}>
|
|
147
|
+
{children}
|
|
148
|
+
</BugSpotterContext.Provider>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function useBugSpotterContext() {
|
|
153
|
+
return useContext(BugSpotterContext);
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
// index.tsx
|
|
159
|
+
import { BugSpotterProvider } from './context/BugSpotterContext';
|
|
160
|
+
|
|
161
|
+
ReactDOM.render(
|
|
162
|
+
<BugSpotterProvider>
|
|
163
|
+
<App />
|
|
164
|
+
</BugSpotterProvider>,
|
|
165
|
+
document.getElementById('root')
|
|
166
|
+
);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Manual Bug Reporting Component
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// components/BugReportButton.tsx
|
|
173
|
+
import React from 'react';
|
|
174
|
+
import { useBugSpotterContext } from '../context/BugSpotterContext';
|
|
175
|
+
|
|
176
|
+
export function BugReportButton() {
|
|
177
|
+
const { reportBug, isInitialized } = useBugSpotterContext();
|
|
178
|
+
|
|
179
|
+
const handleClick = async () => {
|
|
180
|
+
try {
|
|
181
|
+
await reportBug();
|
|
182
|
+
alert('Bug report submitted!');
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.error('Failed to submit bug report:', error);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
if (!isInitialized) return null;
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<button onClick={handleClick} className="bug-report-btn">
|
|
192
|
+
Report Bug
|
|
193
|
+
</button>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Error Boundary Integration
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
// components/ErrorBoundary.tsx
|
|
202
|
+
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
|
203
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
204
|
+
|
|
205
|
+
interface Props {
|
|
206
|
+
children: ReactNode;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
interface State {
|
|
210
|
+
hasError: boolean;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
class ErrorBoundary extends Component<Props, State> {
|
|
214
|
+
public state: State = {
|
|
215
|
+
hasError: false,
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
private bugSpotter: any;
|
|
219
|
+
|
|
220
|
+
constructor(props: Props) {
|
|
221
|
+
super(props);
|
|
222
|
+
this.bugSpotter = BugSpotter.init({
|
|
223
|
+
apiKey: process.env.REACT_APP_BUGSPOTTER_API_KEY,
|
|
224
|
+
endpoint: process.env.REACT_APP_BUGSPOTTER_ENDPOINT,
|
|
225
|
+
showWidget: false, // Hide widget in error boundary
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
public static getDerivedStateFromError(): State {
|
|
230
|
+
return { hasError: true };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
public async componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
234
|
+
console.error('Uncaught error:', error, errorInfo);
|
|
235
|
+
|
|
236
|
+
// Automatically capture and report the error
|
|
237
|
+
try {
|
|
238
|
+
const report = await this.bugSpotter.capture();
|
|
239
|
+
// Send report with error details
|
|
240
|
+
console.log('Error report captured:', report);
|
|
241
|
+
} catch (captureError) {
|
|
242
|
+
console.error('Failed to capture error report:', captureError);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
public render() {
|
|
247
|
+
if (this.state.hasError) {
|
|
248
|
+
return (
|
|
249
|
+
<div className="error-fallback">
|
|
250
|
+
<h1>Something went wrong</h1>
|
|
251
|
+
<p>The error has been automatically reported.</p>
|
|
252
|
+
</div>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return this.props.children;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export default ErrorBoundary;
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Vue Integration
|
|
266
|
+
|
|
267
|
+
### Vue 3 Composition API
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
// composables/useBugSpotter.ts
|
|
271
|
+
import { onMounted, onUnmounted, ref } from 'vue';
|
|
272
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
273
|
+
|
|
274
|
+
export function useBugSpotter() {
|
|
275
|
+
const bugSpotter = ref<any>(null);
|
|
276
|
+
const isInitialized = ref(false);
|
|
277
|
+
|
|
278
|
+
onMounted(() => {
|
|
279
|
+
bugSpotter.value = BugSpotter.init({
|
|
280
|
+
apiKey: import.meta.env.VITE_BUGSPOTTER_API_KEY,
|
|
281
|
+
endpoint: import.meta.env.VITE_BUGSPOTTER_ENDPOINT,
|
|
282
|
+
showWidget: true,
|
|
283
|
+
widgetOptions: {
|
|
284
|
+
position: 'bottom-right',
|
|
285
|
+
icon: '🐛',
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
isInitialized.value = true;
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
onUnmounted(() => {
|
|
292
|
+
if (bugSpotter.value) {
|
|
293
|
+
bugSpotter.value.destroy();
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const reportBug = async () => {
|
|
298
|
+
if (bugSpotter.value) {
|
|
299
|
+
return await bugSpotter.value.capture();
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
bugSpotter,
|
|
305
|
+
isInitialized,
|
|
306
|
+
reportBug,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Usage in Component
|
|
312
|
+
|
|
313
|
+
```vue
|
|
314
|
+
<!-- App.vue -->
|
|
315
|
+
<script setup lang="ts">
|
|
316
|
+
import { useBugSpotter } from './composables/useBugSpotter';
|
|
317
|
+
|
|
318
|
+
const { isInitialized, reportBug } = useBugSpotter();
|
|
319
|
+
|
|
320
|
+
const handleReportBug = async () => {
|
|
321
|
+
try {
|
|
322
|
+
const report = await reportBug();
|
|
323
|
+
console.log('Bug reported:', report);
|
|
324
|
+
} catch (error) {
|
|
325
|
+
console.error('Failed to report bug:', error);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
</script>
|
|
329
|
+
|
|
330
|
+
<template>
|
|
331
|
+
<div id="app">
|
|
332
|
+
<h1>My Vue Application</h1>
|
|
333
|
+
<button v-if="isInitialized" @click="handleReportBug">Report Bug</button>
|
|
334
|
+
</div>
|
|
335
|
+
</template>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Vue 3 Plugin
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
// plugins/bugspotter.ts
|
|
342
|
+
import type { App } from 'vue';
|
|
343
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
344
|
+
|
|
345
|
+
export default {
|
|
346
|
+
install: (app: App, options: any) => {
|
|
347
|
+
const bugSpotter = BugSpotter.init({
|
|
348
|
+
apiKey: options.apiKey,
|
|
349
|
+
endpoint: options.endpoint,
|
|
350
|
+
showWidget: true,
|
|
351
|
+
...options,
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// Make available globally
|
|
355
|
+
app.config.globalProperties.$bugSpotter = bugSpotter;
|
|
356
|
+
|
|
357
|
+
// Provide/inject pattern
|
|
358
|
+
app.provide('bugSpotter', bugSpotter);
|
|
359
|
+
|
|
360
|
+
// Global error handler
|
|
361
|
+
app.config.errorHandler = async (err, instance, info) => {
|
|
362
|
+
console.error('Global error:', err, info);
|
|
363
|
+
try {
|
|
364
|
+
await bugSpotter.capture();
|
|
365
|
+
} catch (captureError) {
|
|
366
|
+
console.error('Failed to capture error:', captureError);
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
// main.ts
|
|
375
|
+
import { createApp } from 'vue';
|
|
376
|
+
import App from './App.vue';
|
|
377
|
+
import BugSpotterPlugin from './plugins/bugspotter';
|
|
378
|
+
|
|
379
|
+
const app = createApp(App);
|
|
380
|
+
|
|
381
|
+
app.use(BugSpotterPlugin, {
|
|
382
|
+
apiKey: import.meta.env.VITE_BUGSPOTTER_API_KEY,
|
|
383
|
+
endpoint: import.meta.env.VITE_BUGSPOTTER_ENDPOINT,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
app.mount('#app');
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Vue 2 (Options API)
|
|
390
|
+
|
|
391
|
+
```javascript
|
|
392
|
+
// main.js
|
|
393
|
+
import Vue from 'vue';
|
|
394
|
+
import App from './App.vue';
|
|
395
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
396
|
+
|
|
397
|
+
// Initialize BugSpotter
|
|
398
|
+
const bugSpotter = BugSpotter.init({
|
|
399
|
+
apiKey: process.env.VUE_APP_BUGSPOTTER_API_KEY,
|
|
400
|
+
endpoint: process.env.VUE_APP_BUGSPOTTER_ENDPOINT,
|
|
401
|
+
showWidget: true,
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// Make available globally
|
|
405
|
+
Vue.prototype.$bugSpotter = bugSpotter;
|
|
406
|
+
|
|
407
|
+
// Global error handler
|
|
408
|
+
Vue.config.errorHandler = async (err, vm, info) => {
|
|
409
|
+
console.error('Global error:', err, info);
|
|
410
|
+
try {
|
|
411
|
+
await bugSpotter.capture();
|
|
412
|
+
} catch (captureError) {
|
|
413
|
+
console.error('Failed to capture error:', captureError);
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
new Vue({
|
|
418
|
+
render: (h) => h(App),
|
|
419
|
+
}).$mount('#app');
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Angular Integration
|
|
425
|
+
|
|
426
|
+
### Service Setup
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
// services/bugspotter.service.ts
|
|
430
|
+
import { Injectable, OnDestroy } from '@angular/core';
|
|
431
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
432
|
+
import { environment } from '../environments/environment';
|
|
433
|
+
|
|
434
|
+
@Injectable({
|
|
435
|
+
providedIn: 'root',
|
|
436
|
+
})
|
|
437
|
+
export class BugSpotterService implements OnDestroy {
|
|
438
|
+
private bugSpotter: any;
|
|
439
|
+
private initialized = false;
|
|
440
|
+
|
|
441
|
+
constructor() {
|
|
442
|
+
this.initialize();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
private initialize(): void {
|
|
446
|
+
this.bugSpotter = BugSpotter.init({
|
|
447
|
+
apiKey: environment.bugspotterApiKey,
|
|
448
|
+
endpoint: environment.bugspotterEndpoint,
|
|
449
|
+
showWidget: true,
|
|
450
|
+
widgetOptions: {
|
|
451
|
+
position: 'bottom-right',
|
|
452
|
+
icon: '🐛',
|
|
453
|
+
},
|
|
454
|
+
sanitize: {
|
|
455
|
+
enabled: true,
|
|
456
|
+
patterns: ['email', 'phone', 'creditcard'],
|
|
457
|
+
},
|
|
458
|
+
});
|
|
459
|
+
this.initialized = true;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
async capture(): Promise<any> {
|
|
463
|
+
if (!this.initialized) {
|
|
464
|
+
throw new Error('BugSpotter not initialized');
|
|
465
|
+
}
|
|
466
|
+
return await this.bugSpotter.capture();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
getConfig() {
|
|
470
|
+
return this.bugSpotter?.getConfig();
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
ngOnDestroy(): void {
|
|
474
|
+
if (this.bugSpotter) {
|
|
475
|
+
this.bugSpotter.destroy();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Environment Configuration
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
// environments/environment.ts
|
|
485
|
+
export const environment = {
|
|
486
|
+
production: false,
|
|
487
|
+
bugspotterApiKey: 'bgs_your_dev_api_key',
|
|
488
|
+
bugspotterEndpoint: 'http://localhost:3000',
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// environments/environment.prod.ts
|
|
492
|
+
export const environment = {
|
|
493
|
+
production: true,
|
|
494
|
+
bugspotterApiKey: 'bgs_your_prod_api_key',
|
|
495
|
+
bugspotterEndpoint: 'https://api.bugspotter.com',
|
|
496
|
+
};
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Global Error Handler
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
// app.module.ts
|
|
503
|
+
import { ErrorHandler, NgModule } from '@angular/core';
|
|
504
|
+
import { BugSpotterService } from './services/bugspotter.service';
|
|
505
|
+
|
|
506
|
+
class GlobalErrorHandler implements ErrorHandler {
|
|
507
|
+
constructor(private bugSpotter: BugSpotterService) {}
|
|
508
|
+
|
|
509
|
+
async handleError(error: Error): Promise<void> {
|
|
510
|
+
console.error('Global error:', error);
|
|
511
|
+
try {
|
|
512
|
+
await this.bugSpotter.capture();
|
|
513
|
+
} catch (captureError) {
|
|
514
|
+
console.error('Failed to capture error:', captureError);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
@NgModule({
|
|
520
|
+
providers: [
|
|
521
|
+
{
|
|
522
|
+
provide: ErrorHandler,
|
|
523
|
+
useClass: GlobalErrorHandler,
|
|
524
|
+
deps: [BugSpotterService],
|
|
525
|
+
},
|
|
526
|
+
],
|
|
527
|
+
})
|
|
528
|
+
export class AppModule {}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Component Usage
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
// components/bug-report-button.component.ts
|
|
535
|
+
import { Component } from '@angular/core';
|
|
536
|
+
import { BugSpotterService } from '../services/bugspotter.service';
|
|
537
|
+
|
|
538
|
+
@Component({
|
|
539
|
+
selector: 'app-bug-report-button',
|
|
540
|
+
template: ` <button (click)="reportBug()" class="bug-report-btn">Report Bug</button> `,
|
|
541
|
+
})
|
|
542
|
+
export class BugReportButtonComponent {
|
|
543
|
+
constructor(private bugSpotter: BugSpotterService) {}
|
|
544
|
+
|
|
545
|
+
async reportBug(): Promise<void> {
|
|
546
|
+
try {
|
|
547
|
+
const report = await this.bugSpotter.capture();
|
|
548
|
+
console.log('Bug report captured:', report);
|
|
549
|
+
alert('Bug report submitted!');
|
|
550
|
+
} catch (error) {
|
|
551
|
+
console.error('Failed to submit bug report:', error);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
## Next.js Integration
|
|
560
|
+
|
|
561
|
+
### App Router (Next.js 13+)
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
// app/providers.tsx
|
|
565
|
+
'use client';
|
|
566
|
+
|
|
567
|
+
import { useEffect, useRef } from 'react';
|
|
568
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
569
|
+
|
|
570
|
+
export function BugSpotterProvider({ children }: { children: React.ReactNode }) {
|
|
571
|
+
const bugSpotterRef = useRef<any>(null);
|
|
572
|
+
|
|
573
|
+
useEffect(() => {
|
|
574
|
+
// Only initialize on client side
|
|
575
|
+
if (typeof window !== 'undefined' && !bugSpotterRef.current) {
|
|
576
|
+
bugSpotterRef.current = BugSpotter.init({
|
|
577
|
+
apiKey: process.env.NEXT_PUBLIC_BUGSPOTTER_API_KEY,
|
|
578
|
+
endpoint: process.env.NEXT_PUBLIC_BUGSPOTTER_ENDPOINT,
|
|
579
|
+
showWidget: true,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return () => {
|
|
584
|
+
if (bugSpotterRef.current) {
|
|
585
|
+
bugSpotterRef.current.destroy();
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
}, []);
|
|
589
|
+
|
|
590
|
+
return <>{children}</>;
|
|
591
|
+
}
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
```typescript
|
|
595
|
+
// app/layout.tsx
|
|
596
|
+
import { BugSpotterProvider } from './providers';
|
|
597
|
+
|
|
598
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
599
|
+
return (
|
|
600
|
+
<html lang="en">
|
|
601
|
+
<body>
|
|
602
|
+
<BugSpotterProvider>{children}</BugSpotterProvider>
|
|
603
|
+
</body>
|
|
604
|
+
</html>
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### Pages Router (Next.js 12 and below)
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
// pages/_app.tsx
|
|
613
|
+
import type { AppProps } from 'next/app';
|
|
614
|
+
import { useEffect } from 'react';
|
|
615
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
616
|
+
|
|
617
|
+
function MyApp({ Component, pageProps }: AppProps) {
|
|
618
|
+
useEffect(() => {
|
|
619
|
+
// Initialize BugSpotter on client side only
|
|
620
|
+
const bugSpotter = BugSpotter.init({
|
|
621
|
+
apiKey: process.env.NEXT_PUBLIC_BUGSPOTTER_API_KEY,
|
|
622
|
+
endpoint: process.env.NEXT_PUBLIC_BUGSPOTTER_ENDPOINT,
|
|
623
|
+
showWidget: true,
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
return () => {
|
|
627
|
+
bugSpotter.destroy();
|
|
628
|
+
};
|
|
629
|
+
}, []);
|
|
630
|
+
|
|
631
|
+
return <Component {...pageProps} />;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export default MyApp;
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### Environment Variables
|
|
638
|
+
|
|
639
|
+
```bash
|
|
640
|
+
# .env.local
|
|
641
|
+
NEXT_PUBLIC_BUGSPOTTER_API_KEY=bgs_your_api_key
|
|
642
|
+
NEXT_PUBLIC_BUGSPOTTER_ENDPOINT=https://api.bugspotter.com
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
## Nuxt Integration
|
|
648
|
+
|
|
649
|
+
### Nuxt 3
|
|
650
|
+
|
|
651
|
+
```typescript
|
|
652
|
+
// plugins/bugspotter.client.ts
|
|
653
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
654
|
+
|
|
655
|
+
export default defineNuxtPlugin(() => {
|
|
656
|
+
const config = useRuntimeConfig();
|
|
657
|
+
|
|
658
|
+
const bugSpotter = BugSpotter.init({
|
|
659
|
+
apiKey: config.public.bugspotterApiKey,
|
|
660
|
+
endpoint: config.public.bugspotterEndpoint,
|
|
661
|
+
showWidget: true,
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
return {
|
|
665
|
+
provide: {
|
|
666
|
+
bugSpotter,
|
|
667
|
+
},
|
|
668
|
+
};
|
|
669
|
+
});
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
```typescript
|
|
673
|
+
// nuxt.config.ts
|
|
674
|
+
export default defineNuxtConfig({
|
|
675
|
+
runtimeConfig: {
|
|
676
|
+
public: {
|
|
677
|
+
bugspotterApiKey: process.env.NUXT_PUBLIC_BUGSPOTTER_API_KEY,
|
|
678
|
+
bugspotterEndpoint: process.env.NUXT_PUBLIC_BUGSPOTTER_ENDPOINT,
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
});
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
### Usage in Component
|
|
685
|
+
|
|
686
|
+
```vue
|
|
687
|
+
<script setup lang="ts">
|
|
688
|
+
const { $bugSpotter } = useNuxtApp();
|
|
689
|
+
|
|
690
|
+
const reportBug = async () => {
|
|
691
|
+
const report = await $bugSpotter.capture();
|
|
692
|
+
console.log('Bug reported:', report);
|
|
693
|
+
};
|
|
694
|
+
</script>
|
|
695
|
+
|
|
696
|
+
<template>
|
|
697
|
+
<div>
|
|
698
|
+
<button @click="reportBug">Report Bug</button>
|
|
699
|
+
</div>
|
|
700
|
+
</template>
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
---
|
|
704
|
+
|
|
705
|
+
## Svelte Integration
|
|
706
|
+
|
|
707
|
+
### SvelteKit
|
|
708
|
+
|
|
709
|
+
```typescript
|
|
710
|
+
// src/hooks.client.ts
|
|
711
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
712
|
+
import { browser } from '$app/environment';
|
|
713
|
+
import { PUBLIC_BUGSPOTTER_API_KEY, PUBLIC_BUGSPOTTER_ENDPOINT } from '$env/static/public';
|
|
714
|
+
|
|
715
|
+
if (browser) {
|
|
716
|
+
BugSpotter.init({
|
|
717
|
+
apiKey: PUBLIC_BUGSPOTTER_API_KEY,
|
|
718
|
+
endpoint: PUBLIC_BUGSPOTTER_ENDPOINT,
|
|
719
|
+
showWidget: true,
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
### Svelte Store
|
|
725
|
+
|
|
726
|
+
```typescript
|
|
727
|
+
// stores/bugspotter.ts
|
|
728
|
+
import { writable } from 'svelte/store';
|
|
729
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
730
|
+
import { browser } from '$app/environment';
|
|
731
|
+
|
|
732
|
+
function createBugSpotterStore() {
|
|
733
|
+
const { subscribe, set } = writable<any>(null);
|
|
734
|
+
|
|
735
|
+
if (browser) {
|
|
736
|
+
const instance = BugSpotter.init({
|
|
737
|
+
apiKey: import.meta.env.VITE_BUGSPOTTER_API_KEY,
|
|
738
|
+
endpoint: import.meta.env.VITE_BUGSPOTTER_ENDPOINT,
|
|
739
|
+
showWidget: true,
|
|
740
|
+
});
|
|
741
|
+
set(instance);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
return {
|
|
745
|
+
subscribe,
|
|
746
|
+
capture: async () => {
|
|
747
|
+
let instance: any;
|
|
748
|
+
subscribe((value) => (instance = value))();
|
|
749
|
+
if (instance) {
|
|
750
|
+
return await instance.capture();
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export const bugSpotter = createBugSpotterStore();
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
---
|
|
760
|
+
|
|
761
|
+
## Vanilla JavaScript
|
|
762
|
+
|
|
763
|
+
### Script Tag (CDN)
|
|
764
|
+
|
|
765
|
+
```html
|
|
766
|
+
<!DOCTYPE html>
|
|
767
|
+
<html lang="en">
|
|
768
|
+
<head>
|
|
769
|
+
<meta charset="UTF-8" />
|
|
770
|
+
<title>My App</title>
|
|
771
|
+
</head>
|
|
772
|
+
<body>
|
|
773
|
+
<h1>My Application</h1>
|
|
774
|
+
<button id="report-bug">Report Bug</button>
|
|
775
|
+
|
|
776
|
+
<!-- Load BugSpotter SDK from CDN -->
|
|
777
|
+
<script src="https://cdn.bugspotter.io/sdk/bugspotter-latest.min.js"></script>
|
|
778
|
+
<script>
|
|
779
|
+
// Initialize BugSpotter
|
|
780
|
+
var bugSpotter = BugSpotter.init({
|
|
781
|
+
apiKey: 'bgs_your_api_key',
|
|
782
|
+
endpoint: 'https://api.bugspotter.com',
|
|
783
|
+
showWidget: true,
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
// Manual bug reporting
|
|
787
|
+
document.getElementById('report-bug').addEventListener('click', async function () {
|
|
788
|
+
try {
|
|
789
|
+
var report = await bugSpotter.capture();
|
|
790
|
+
console.log('Bug reported:', report);
|
|
791
|
+
alert('Bug report submitted!');
|
|
792
|
+
} catch (error) {
|
|
793
|
+
console.error('Failed to report bug:', error);
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
</script>
|
|
797
|
+
</body>
|
|
798
|
+
</html>
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
### ES Modules
|
|
802
|
+
|
|
803
|
+
```javascript
|
|
804
|
+
// app.js
|
|
805
|
+
import BugSpotter from '@bugspotter/sdk';
|
|
806
|
+
|
|
807
|
+
// Initialize
|
|
808
|
+
const bugSpotter = BugSpotter.init({
|
|
809
|
+
apiKey: 'bgs_your_api_key',
|
|
810
|
+
endpoint: 'https://api.bugspotter.com',
|
|
811
|
+
showWidget: true,
|
|
812
|
+
widgetOptions: {
|
|
813
|
+
position: 'bottom-right',
|
|
814
|
+
icon: '🐛',
|
|
815
|
+
},
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
// Manual reporting
|
|
819
|
+
document.getElementById('report-bug').addEventListener('click', async () => {
|
|
820
|
+
const report = await bugSpotter.capture();
|
|
821
|
+
console.log('Report captured:', report);
|
|
822
|
+
});
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
---
|
|
826
|
+
|
|
827
|
+
## TypeScript Support
|
|
828
|
+
|
|
829
|
+
### Type Definitions
|
|
830
|
+
|
|
831
|
+
```typescript
|
|
832
|
+
// types/bugspotter.d.ts
|
|
833
|
+
declare module '@bugspotter/sdk' {
|
|
834
|
+
export interface BugSpotterConfig {
|
|
835
|
+
apiKey?: string;
|
|
836
|
+
endpoint?: string;
|
|
837
|
+
showWidget?: boolean;
|
|
838
|
+
widgetOptions?: {
|
|
839
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
840
|
+
icon?: string;
|
|
841
|
+
backgroundColor?: string;
|
|
842
|
+
size?: number;
|
|
843
|
+
};
|
|
844
|
+
replay?: {
|
|
845
|
+
enabled?: boolean;
|
|
846
|
+
duration?: number;
|
|
847
|
+
sampling?: {
|
|
848
|
+
mousemove?: number;
|
|
849
|
+
scroll?: number;
|
|
850
|
+
};
|
|
851
|
+
};
|
|
852
|
+
sanitize?: {
|
|
853
|
+
enabled?: boolean;
|
|
854
|
+
patterns?: string[];
|
|
855
|
+
customPatterns?: Array<{ name: string; regex: RegExp }>;
|
|
856
|
+
excludeSelectors?: string[];
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
export interface BugReport {
|
|
861
|
+
screenshot: string;
|
|
862
|
+
console: ConsoleLog[];
|
|
863
|
+
network: NetworkRequest[];
|
|
864
|
+
metadata: BrowserMetadata;
|
|
865
|
+
replay: any[];
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
export interface ConsoleLog {
|
|
869
|
+
level: string;
|
|
870
|
+
message: string;
|
|
871
|
+
timestamp: number;
|
|
872
|
+
stack?: string;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
export interface NetworkRequest {
|
|
876
|
+
url: string;
|
|
877
|
+
method: string;
|
|
878
|
+
status: number;
|
|
879
|
+
duration: number;
|
|
880
|
+
timestamp: number;
|
|
881
|
+
error?: string;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
export interface BrowserMetadata {
|
|
885
|
+
userAgent: string;
|
|
886
|
+
viewport: { width: number; height: number };
|
|
887
|
+
browser: string;
|
|
888
|
+
os: string;
|
|
889
|
+
url: string;
|
|
890
|
+
timestamp: number;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
export interface BugSpotterInstance {
|
|
894
|
+
capture(): Promise<BugReport>;
|
|
895
|
+
getConfig(): Readonly<BugSpotterConfig>;
|
|
896
|
+
destroy(): void;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
export default class BugSpotter {
|
|
900
|
+
static init(config: BugSpotterConfig): BugSpotterInstance;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
### Usage with Types
|
|
906
|
+
|
|
907
|
+
```typescript
|
|
908
|
+
import BugSpotter, { BugSpotterConfig, BugReport } from '@bugspotter/sdk';
|
|
909
|
+
|
|
910
|
+
const config: BugSpotterConfig = {
|
|
911
|
+
apiKey: process.env.BUGSPOTTER_API_KEY,
|
|
912
|
+
endpoint: process.env.BUGSPOTTER_ENDPOINT,
|
|
913
|
+
showWidget: true,
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
const bugSpotter = BugSpotter.init(config);
|
|
917
|
+
|
|
918
|
+
async function captureReport(): Promise<BugReport> {
|
|
919
|
+
return await bugSpotter.capture();
|
|
920
|
+
}
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
---
|
|
924
|
+
|
|
925
|
+
## Best Practices
|
|
926
|
+
|
|
927
|
+
### 1. Environment-Based Configuration
|
|
928
|
+
|
|
929
|
+
Use different configurations for development and production:
|
|
930
|
+
|
|
931
|
+
```typescript
|
|
932
|
+
const config = {
|
|
933
|
+
apiKey:
|
|
934
|
+
process.env.NODE_ENV === 'production'
|
|
935
|
+
? process.env.PROD_BUGSPOTTER_API_KEY
|
|
936
|
+
: process.env.DEV_BUGSPOTTER_API_KEY,
|
|
937
|
+
endpoint:
|
|
938
|
+
process.env.NODE_ENV === 'production' ? 'https://api.bugspotter.com' : 'http://localhost:3000',
|
|
939
|
+
showWidget: process.env.NODE_ENV === 'development', // Only in dev
|
|
940
|
+
};
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
### 2. Lazy Loading
|
|
944
|
+
|
|
945
|
+
Load BugSpotter only when needed to reduce initial bundle size:
|
|
946
|
+
|
|
947
|
+
```typescript
|
|
948
|
+
// Lazy load BugSpotter
|
|
949
|
+
const loadBugSpotter = async () => {
|
|
950
|
+
const BugSpotter = await import('@bugspotter/sdk');
|
|
951
|
+
return BugSpotter.default.init({
|
|
952
|
+
apiKey: process.env.BUGSPOTTER_API_KEY,
|
|
953
|
+
endpoint: process.env.BUGSPOTTER_ENDPOINT,
|
|
954
|
+
});
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
// Use when needed
|
|
958
|
+
button.addEventListener('click', async () => {
|
|
959
|
+
const bugSpotter = await loadBugSpotter();
|
|
960
|
+
await bugSpotter.capture();
|
|
961
|
+
});
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
### 3. Error Handling
|
|
965
|
+
|
|
966
|
+
Always wrap BugSpotter calls in try-catch:
|
|
967
|
+
|
|
968
|
+
```typescript
|
|
969
|
+
async function reportBug() {
|
|
970
|
+
try {
|
|
971
|
+
const report = await bugSpotter.capture();
|
|
972
|
+
console.log('Report captured:', report);
|
|
973
|
+
} catch (error) {
|
|
974
|
+
console.error('Failed to capture report:', error);
|
|
975
|
+
// Fallback: Show user a message or use alternative error reporting
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
### 4. PII Sanitization
|
|
981
|
+
|
|
982
|
+
Enable PII sanitization in production:
|
|
983
|
+
|
|
984
|
+
```typescript
|
|
985
|
+
BugSpotter.init({
|
|
986
|
+
sanitize: {
|
|
987
|
+
enabled: true,
|
|
988
|
+
patterns: ['email', 'phone', 'creditcard', 'ssn'],
|
|
989
|
+
excludeSelectors: ['.public-data'],
|
|
990
|
+
},
|
|
991
|
+
});
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
### 5. Performance Optimization
|
|
995
|
+
|
|
996
|
+
Configure session replay sampling for better performance:
|
|
997
|
+
|
|
998
|
+
```typescript
|
|
999
|
+
BugSpotter.init({
|
|
1000
|
+
replay: {
|
|
1001
|
+
enabled: true,
|
|
1002
|
+
duration: 15, // Shorter buffer for less memory usage
|
|
1003
|
+
sampling: {
|
|
1004
|
+
mousemove: 100, // Higher = less CPU usage
|
|
1005
|
+
scroll: 200,
|
|
1006
|
+
},
|
|
1007
|
+
},
|
|
1008
|
+
});
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
### 6. Testing
|
|
1012
|
+
|
|
1013
|
+
Mock BugSpotter in tests:
|
|
1014
|
+
|
|
1015
|
+
```typescript
|
|
1016
|
+
// __mocks__/@bugspotter/sdk.ts
|
|
1017
|
+
export default {
|
|
1018
|
+
init: jest.fn(() => ({
|
|
1019
|
+
capture: jest.fn(() => Promise.resolve({})),
|
|
1020
|
+
destroy: jest.fn(),
|
|
1021
|
+
getConfig: jest.fn(() => ({})),
|
|
1022
|
+
})),
|
|
1023
|
+
};
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
---
|
|
1027
|
+
|
|
1028
|
+
## Troubleshooting
|
|
1029
|
+
|
|
1030
|
+
### Widget Not Appearing
|
|
1031
|
+
|
|
1032
|
+
**Issue**: BugSpotter widget doesn't show up.
|
|
1033
|
+
|
|
1034
|
+
**Solutions**:
|
|
1035
|
+
|
|
1036
|
+
- Ensure `showWidget: true` in config
|
|
1037
|
+
- Check CSS z-index conflicts
|
|
1038
|
+
- Verify BugSpotter is initialized after DOM is ready
|
|
1039
|
+
- Check browser console for errors
|
|
1040
|
+
|
|
1041
|
+
### CSP (Content Security Policy) Errors
|
|
1042
|
+
|
|
1043
|
+
**Issue**: Content Security Policy blocks BugSpotter.
|
|
1044
|
+
|
|
1045
|
+
**Solution**: Add to your CSP headers:
|
|
1046
|
+
|
|
1047
|
+
```
|
|
1048
|
+
img-src 'self' data: blob:;
|
|
1049
|
+
script-src 'self';
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
### Memory Issues
|
|
1053
|
+
|
|
1054
|
+
**Issue**: High memory usage with session replay.
|
|
1055
|
+
|
|
1056
|
+
**Solutions**:
|
|
1057
|
+
|
|
1058
|
+
- Reduce replay duration: `replay: { duration: 15 }`
|
|
1059
|
+
- Increase sampling intervals: `sampling: { mousemove: 100 }`
|
|
1060
|
+
- Disable replay if not needed: `replay: { enabled: false }`
|
|
1061
|
+
|
|
1062
|
+
### Build Errors
|
|
1063
|
+
|
|
1064
|
+
**Issue**: Module not found or build failures.
|
|
1065
|
+
|
|
1066
|
+
**Solutions**:
|
|
1067
|
+
|
|
1068
|
+
- Ensure package is installed: `npm install @bugspotter/sdk`
|
|
1069
|
+
- Check import path: `import BugSpotter from '@bugspotter/sdk'`
|
|
1070
|
+
- Clear node_modules and reinstall: `rm -rf node_modules && npm install`
|
|
1071
|
+
|
|
1072
|
+
### SSR/SSG Issues (Next.js, Nuxt)
|
|
1073
|
+
|
|
1074
|
+
**Issue**: "window is not defined" or similar SSR errors.
|
|
1075
|
+
|
|
1076
|
+
**Solutions**:
|
|
1077
|
+
|
|
1078
|
+
- Use `.client` suffix for plugins (Nuxt)
|
|
1079
|
+
- Check `typeof window !== 'undefined'` before initializing
|
|
1080
|
+
- Use `useEffect` or `onMounted` hooks
|
|
1081
|
+
- Mark components as client-only
|
|
1082
|
+
|
|
1083
|
+
### TypeScript Errors
|
|
1084
|
+
|
|
1085
|
+
**Issue**: Type errors or missing types.
|
|
1086
|
+
|
|
1087
|
+
**Solutions**:
|
|
1088
|
+
|
|
1089
|
+
- Add type definitions file (see TypeScript Support section)
|
|
1090
|
+
- Use `@ts-ignore` as temporary fix
|
|
1091
|
+
- Check if types are exported from package
|
|
1092
|
+
|
|
1093
|
+
---
|
|
1094
|
+
|
|
1095
|
+
## Additional Resources
|
|
1096
|
+
|
|
1097
|
+
- [SDK README](../README.md) - Complete SDK documentation
|
|
1098
|
+
- [Session Replay Guide](./SESSION_REPLAY.md) - Session replay configuration
|
|
1099
|
+
- [API Documentation](../../../API_DOCUMENTATION.md) - Backend API reference
|
|
1100
|
+
- [Demo App](../../../apps/demo/README.md) - Working example
|
|
1101
|
+
|
|
1102
|
+
---
|
|
1103
|
+
|
|
1104
|
+
**Need Help?** Open an issue on [GitHub](https://github.com/apexbridge-tech/bugspotter) or contact support.
|