@bugspotter/sdk 2.0.5 → 2.1.0
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/CONTRIBUTING.md +3 -3
- package/README.md +112 -27
- package/dist/bugspotter.min.js +1 -1
- package/dist/bugspotter.min.js.map +1 -1
- package/dist/collectors/dom.d.ts +4 -0
- package/dist/collectors/dom.js +13 -14
- package/dist/core/capture-manager.d.ts +2 -0
- package/dist/core/capture-manager.js +3 -1
- package/dist/index.d.ts +26 -3
- package/dist/index.esm.js +87 -32
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +45 -6
- package/dist/utils/sanitize.js +25 -10
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/CDN.md +3 -3
- package/docs/FRAMEWORK_INTEGRATION.md +1 -1
- package/package.json +4 -4
- package/release_notes.md +1 -1
package/dist/index.js
CHANGED
|
@@ -91,13 +91,20 @@ async function fetchReplaySettings(endpoint, apiKey) {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
class BugSpotter {
|
|
94
|
-
constructor(config) {
|
|
94
|
+
constructor(config, sampled = true) {
|
|
95
95
|
var _a, _b, _c, _d, _e, _f;
|
|
96
96
|
// Validate deduplication configuration if provided
|
|
97
97
|
if (config.deduplication) {
|
|
98
98
|
(0, config_validator_1.validateDeduplicationConfig)(config.deduplication);
|
|
99
99
|
}
|
|
100
100
|
this.config = config;
|
|
101
|
+
this._sampled = sampled;
|
|
102
|
+
this.bugReporter = new bug_reporter_1.BugReporter(config);
|
|
103
|
+
// If not sampled, skip all capture initialization — true zero overhead
|
|
104
|
+
// No console/network interception, no DOM recording, no widget
|
|
105
|
+
if (!sampled) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
101
108
|
// Initialize sanitizer (enabled by default)
|
|
102
109
|
const sanitizeEnabled = (_b = (_a = config.sanitize) === null || _a === void 0 ? void 0 : _a.enabled) !== null && _b !== void 0 ? _b : true;
|
|
103
110
|
if (sanitizeEnabled) {
|
|
@@ -110,8 +117,6 @@ class BugSpotter {
|
|
|
110
117
|
}
|
|
111
118
|
// Initialize capture manager
|
|
112
119
|
this.captureManager = new capture_manager_1.CaptureManager(Object.assign(Object.assign({ sanitizer: this.sanitizer }, (config.endpoint && { apiEndpoint: (0, url_helpers_1.getApiBaseUrl)(config.endpoint) })), { replay: config.replay }));
|
|
113
|
-
// Initialize bug reporter
|
|
114
|
-
this.bugReporter = new bug_reporter_1.BugReporter(config);
|
|
115
120
|
// Initialize widget (enabled by default)
|
|
116
121
|
const widgetEnabled = (_f = config.showWidget) !== null && _f !== void 0 ? _f : true;
|
|
117
122
|
if (widgetEnabled) {
|
|
@@ -150,6 +155,19 @@ class BugSpotter {
|
|
|
150
155
|
*/
|
|
151
156
|
static async createInstance(config) {
|
|
152
157
|
var _a, _b;
|
|
158
|
+
// Check sampling rate — if this session is not sampled, disable all capture
|
|
159
|
+
if (config.sampleRate !== undefined) {
|
|
160
|
+
if (typeof config.sampleRate !== 'number' ||
|
|
161
|
+
!Number.isFinite(config.sampleRate) ||
|
|
162
|
+
config.sampleRate < 0 ||
|
|
163
|
+
config.sampleRate > 1) {
|
|
164
|
+
throw new Error('sampleRate must be a finite number between 0 and 1');
|
|
165
|
+
}
|
|
166
|
+
if (Math.random() >= config.sampleRate) {
|
|
167
|
+
// Create a lightweight no-op instance — zero overhead (no console/network interception)
|
|
168
|
+
return new BugSpotter(config, /* sampled */ false);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
153
171
|
// Fetch replay quality settings from backend if replay is enabled
|
|
154
172
|
let backendSettings = null;
|
|
155
173
|
const replayEnabled = (_b = (_a = config.replay) === null || _a === void 0 ? void 0 : _a.enabled) !== null && _b !== void 0 ? _b : true;
|
|
@@ -177,7 +195,28 @@ class BugSpotter {
|
|
|
177
195
|
* Note: Screenshot is captured for modal preview only (_screenshotPreview)
|
|
178
196
|
* File uploads use presigned URLs returned from the backend
|
|
179
197
|
*/
|
|
198
|
+
/** Whether this session was sampled for capture */
|
|
199
|
+
get isSampled() {
|
|
200
|
+
return this._sampled;
|
|
201
|
+
}
|
|
180
202
|
async capture() {
|
|
203
|
+
if (!this.captureManager) {
|
|
204
|
+
// Unsampled session — return minimal valid report
|
|
205
|
+
return {
|
|
206
|
+
console: [],
|
|
207
|
+
network: [],
|
|
208
|
+
metadata: {
|
|
209
|
+
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : '',
|
|
210
|
+
url: typeof window !== 'undefined' ? window.location.href : '',
|
|
211
|
+
timestamp: Date.now(),
|
|
212
|
+
viewport: typeof window !== 'undefined'
|
|
213
|
+
? { width: window.innerWidth, height: window.innerHeight }
|
|
214
|
+
: { width: 0, height: 0 },
|
|
215
|
+
browser: 'unknown',
|
|
216
|
+
os: 'unknown',
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
181
220
|
return await this.captureManager.captureAll();
|
|
182
221
|
}
|
|
183
222
|
async handleBugReport() {
|
|
@@ -216,9 +255,9 @@ class BugSpotter {
|
|
|
216
255
|
return Object.assign({}, this.config);
|
|
217
256
|
}
|
|
218
257
|
destroy() {
|
|
219
|
-
var _a;
|
|
220
|
-
this.captureManager.destroy();
|
|
221
|
-
(
|
|
258
|
+
var _a, _b;
|
|
259
|
+
(_a = this.captureManager) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
260
|
+
(_b = this.widget) === null || _b === void 0 ? void 0 : _b.destroy();
|
|
222
261
|
this.bugReporter.destroy();
|
|
223
262
|
BugSpotter.instance = undefined;
|
|
224
263
|
BugSpotter.initPromise = undefined;
|
package/dist/utils/sanitize.js
CHANGED
|
@@ -80,6 +80,7 @@ class StringSanitizer {
|
|
|
80
80
|
class ValueSanitizer {
|
|
81
81
|
constructor(stringSanitizer) {
|
|
82
82
|
this.stringSanitizer = stringSanitizer;
|
|
83
|
+
this.seen = new WeakSet();
|
|
83
84
|
}
|
|
84
85
|
sanitize(value) {
|
|
85
86
|
// Handle null/undefined
|
|
@@ -90,26 +91,40 @@ class ValueSanitizer {
|
|
|
90
91
|
if (typeof value === 'string') {
|
|
91
92
|
return this.stringSanitizer.sanitize(value);
|
|
92
93
|
}
|
|
93
|
-
// Handle arrays
|
|
94
|
+
// Handle arrays (with circular reference protection)
|
|
94
95
|
if (Array.isArray(value)) {
|
|
95
|
-
|
|
96
|
-
return
|
|
97
|
-
|
|
96
|
+
if (this.seen.has(value))
|
|
97
|
+
return '[Circular]';
|
|
98
|
+
this.seen.add(value);
|
|
99
|
+
try {
|
|
100
|
+
return value.map((item) => this.sanitize(item));
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
this.seen.delete(value);
|
|
104
|
+
}
|
|
98
105
|
}
|
|
99
|
-
// Handle objects
|
|
106
|
+
// Handle objects (with circular reference protection)
|
|
100
107
|
if (typeof value === 'object') {
|
|
108
|
+
if (this.seen.has(value))
|
|
109
|
+
return '[Circular]';
|
|
101
110
|
return this.sanitizeObject(value);
|
|
102
111
|
}
|
|
103
112
|
// Return primitives as-is
|
|
104
113
|
return value;
|
|
105
114
|
}
|
|
106
115
|
sanitizeObject(obj) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
116
|
+
this.seen.add(obj);
|
|
117
|
+
try {
|
|
118
|
+
const sanitized = {};
|
|
119
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
120
|
+
const sanitizedKey = this.stringSanitizer.sanitize(key);
|
|
121
|
+
sanitized[sanitizedKey] = this.sanitize(val);
|
|
122
|
+
}
|
|
123
|
+
return sanitized;
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
this.seen.delete(obj);
|
|
111
127
|
}
|
|
112
|
-
return sanitized;
|
|
113
128
|
}
|
|
114
129
|
}
|
|
115
130
|
/**
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/docs/CDN.md
CHANGED
|
@@ -140,7 +140,7 @@ If our primary CDN is unavailable, you can also use:
|
|
|
140
140
|
Check available versions:
|
|
141
141
|
|
|
142
142
|
- [npm package page](https://www.npmjs.com/package/@bugspotter/sdk?activeTab=versions)
|
|
143
|
-
- [GitHub releases](https://github.com/
|
|
143
|
+
- [GitHub releases](https://github.com/apex-bridge/bugspotter-sdk/releases)
|
|
144
144
|
|
|
145
145
|
## ⚙️ Configuration
|
|
146
146
|
|
|
@@ -208,6 +208,6 @@ If updates aren't showing:
|
|
|
208
208
|
|
|
209
209
|
## 🆘 Support
|
|
210
210
|
|
|
211
|
-
- **Issues:** [GitHub Issues](https://github.com/
|
|
212
|
-
- **Discussions:** [GitHub Discussions](https://github.com/
|
|
211
|
+
- **Issues:** [GitHub Issues](https://github.com/apex-bridge/bugspotter-sdk/issues)
|
|
212
|
+
- **Discussions:** [GitHub Discussions](https://github.com/apex-bridge/bugspotter-sdk/discussions)
|
|
213
213
|
- **Security:** security@bugspotter.io
|
|
@@ -1101,4 +1101,4 @@ script-src 'self';
|
|
|
1101
1101
|
|
|
1102
1102
|
---
|
|
1103
1103
|
|
|
1104
|
-
**Need Help?** Open an issue on [GitHub](https://github.com/
|
|
1104
|
+
**Need Help?** Open an issue on [GitHub](https://github.com/apex-bridge/bugspotter-sdk) or contact support.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bugspotter/sdk",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Professional bug reporting SDK with screenshots, session replay, and automatic error capture for web applications",
|
|
5
5
|
"packageManager": "pnpm@9.15.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
"license": "MIT",
|
|
57
57
|
"repository": {
|
|
58
58
|
"type": "git",
|
|
59
|
-
"url": "https://github.com/
|
|
59
|
+
"url": "https://github.com/apex-bridge/bugspotter-sdk.git"
|
|
60
60
|
},
|
|
61
61
|
"bugs": {
|
|
62
|
-
"url": "https://github.com/
|
|
62
|
+
"url": "https://github.com/apex-bridge/bugspotter-sdk/issues"
|
|
63
63
|
},
|
|
64
|
-
"homepage": "https://github.com/
|
|
64
|
+
"homepage": "https://github.com/apex-bridge/bugspotter-sdk#readme",
|
|
65
65
|
"engines": {
|
|
66
66
|
"node": ">=16.0.0"
|
|
67
67
|
},
|
package/release_notes.md
CHANGED