@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.
Files changed (67) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/LICENSE +21 -0
  3. package/README.md +639 -0
  4. package/dist/bugspotter.min.js +2 -0
  5. package/dist/bugspotter.min.js.LICENSE.txt +14 -0
  6. package/dist/capture/base-capture.d.ts +34 -0
  7. package/dist/capture/base-capture.js +23 -0
  8. package/dist/capture/capture-lifecycle.d.ts +24 -0
  9. package/dist/capture/capture-lifecycle.js +2 -0
  10. package/dist/capture/console.d.ts +29 -0
  11. package/dist/capture/console.js +107 -0
  12. package/dist/capture/metadata.d.ts +21 -0
  13. package/dist/capture/metadata.js +76 -0
  14. package/dist/capture/network.d.ts +32 -0
  15. package/dist/capture/network.js +135 -0
  16. package/dist/capture/screenshot.d.ts +19 -0
  17. package/dist/capture/screenshot.js +52 -0
  18. package/dist/collectors/dom.d.ts +67 -0
  19. package/dist/collectors/dom.js +164 -0
  20. package/dist/collectors/index.d.ts +2 -0
  21. package/dist/collectors/index.js +5 -0
  22. package/dist/core/buffer.d.ts +50 -0
  23. package/dist/core/buffer.js +88 -0
  24. package/dist/core/circular-buffer.d.ts +42 -0
  25. package/dist/core/circular-buffer.js +77 -0
  26. package/dist/core/compress.d.ts +49 -0
  27. package/dist/core/compress.js +245 -0
  28. package/dist/core/offline-queue.d.ts +76 -0
  29. package/dist/core/offline-queue.js +301 -0
  30. package/dist/core/transport.d.ts +73 -0
  31. package/dist/core/transport.js +352 -0
  32. package/dist/core/upload-helpers.d.ts +32 -0
  33. package/dist/core/upload-helpers.js +79 -0
  34. package/dist/core/uploader.d.ts +70 -0
  35. package/dist/core/uploader.js +185 -0
  36. package/dist/index.d.ts +140 -0
  37. package/dist/index.esm.js +205 -0
  38. package/dist/index.js +244 -0
  39. package/dist/utils/logger.d.ts +28 -0
  40. package/dist/utils/logger.js +84 -0
  41. package/dist/utils/sanitize-patterns.d.ts +103 -0
  42. package/dist/utils/sanitize-patterns.js +282 -0
  43. package/dist/utils/sanitize.d.ts +73 -0
  44. package/dist/utils/sanitize.js +254 -0
  45. package/dist/widget/button.d.ts +33 -0
  46. package/dist/widget/button.js +143 -0
  47. package/dist/widget/components/dom-element-cache.d.ts +62 -0
  48. package/dist/widget/components/dom-element-cache.js +105 -0
  49. package/dist/widget/components/form-validator.d.ts +66 -0
  50. package/dist/widget/components/form-validator.js +115 -0
  51. package/dist/widget/components/pii-detection-display.d.ts +64 -0
  52. package/dist/widget/components/pii-detection-display.js +142 -0
  53. package/dist/widget/components/redaction-canvas.d.ts +95 -0
  54. package/dist/widget/components/redaction-canvas.js +230 -0
  55. package/dist/widget/components/screenshot-processor.d.ts +44 -0
  56. package/dist/widget/components/screenshot-processor.js +191 -0
  57. package/dist/widget/components/style-manager.d.ts +37 -0
  58. package/dist/widget/components/style-manager.js +296 -0
  59. package/dist/widget/components/template-manager.d.ts +66 -0
  60. package/dist/widget/components/template-manager.js +198 -0
  61. package/dist/widget/modal.d.ts +62 -0
  62. package/dist/widget/modal.js +299 -0
  63. package/docs/CDN.md +213 -0
  64. package/docs/FRAMEWORK_INTEGRATION.md +1104 -0
  65. package/docs/PUBLISHING.md +550 -0
  66. package/docs/SESSION_REPLAY.md +381 -0
  67. package/package.json +90 -0
@@ -0,0 +1,299 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BugReportModal = void 0;
4
+ const style_manager_1 = require("./components/style-manager");
5
+ const template_manager_1 = require("./components/template-manager");
6
+ const dom_element_cache_1 = require("./components/dom-element-cache");
7
+ const form_validator_1 = require("./components/form-validator");
8
+ const pii_detection_display_1 = require("./components/pii-detection-display");
9
+ const redaction_canvas_1 = require("./components/redaction-canvas");
10
+ const screenshot_processor_1 = require("./components/screenshot-processor");
11
+ const sanitize_1 = require("../utils/sanitize");
12
+ const logger_1 = require("../utils/logger");
13
+ const logger = (0, logger_1.getLogger)();
14
+ /**
15
+ * BugReportModal
16
+ *
17
+ * Refactored to follow SOLID principles
18
+ * Acts as a lightweight coordinator for specialized components
19
+ */
20
+ class BugReportModal {
21
+ constructor(options) {
22
+ this.redactionCanvas = null;
23
+ // State
24
+ this.originalScreenshot = '';
25
+ this.piiDetections = [];
26
+ this.options = options;
27
+ this.container = document.createElement('div');
28
+ this.shadow = this.container.attachShadow({ mode: 'open' });
29
+ // Initialize components
30
+ this.styleManager = new style_manager_1.StyleManager();
31
+ this.templateManager = new template_manager_1.TemplateManager();
32
+ this.domCache = new dom_element_cache_1.DOMElementCache();
33
+ this.validator = new form_validator_1.FormValidator();
34
+ this.piiDisplay = new pii_detection_display_1.PIIDetectionDisplay();
35
+ this.screenshotProcessor = new screenshot_processor_1.ScreenshotProcessor();
36
+ // Bind event handler
37
+ this.handleEscapeKey = this.onEscapeKey.bind(this);
38
+ }
39
+ /**
40
+ * Show the modal with optional screenshot
41
+ */
42
+ show(screenshotDataUrl) {
43
+ if (screenshotDataUrl) {
44
+ this.originalScreenshot = screenshotDataUrl;
45
+ }
46
+ // Generate and inject HTML (includes inline styles in shadow DOM)
47
+ this.shadow.innerHTML = `
48
+ <style>
49
+ ${this.styleManager.generateStyles()}
50
+ </style>
51
+ ${this.templateManager.generateModalHTML(this.originalScreenshot)}
52
+ `;
53
+ // Cache DOM elements
54
+ this.domCache.initialize(this.shadow);
55
+ // Initialize error display states
56
+ const elements = this.domCache.get();
57
+ elements.titleError.style.display = 'none';
58
+ elements.descriptionError.style.display = 'none';
59
+ // Setup components
60
+ this.setupRedactionCanvas();
61
+ this.attachEventListeners();
62
+ // Add to DOM
63
+ document.body.appendChild(this.container);
64
+ // Focus first input
65
+ elements.titleInput.focus();
66
+ }
67
+ /**
68
+ * Close and cleanup the modal
69
+ */
70
+ close() {
71
+ // Remove keyboard listener
72
+ document.removeEventListener('keydown', this.handleEscapeKey);
73
+ // Cleanup components
74
+ if (this.redactionCanvas) {
75
+ this.redactionCanvas.destroy();
76
+ this.redactionCanvas = null;
77
+ }
78
+ // Remove from DOM
79
+ if (this.container.parentNode) {
80
+ this.container.parentNode.removeChild(this.container);
81
+ }
82
+ // Clear cache
83
+ this.domCache.clear();
84
+ // Call onClose callback
85
+ if (this.options.onClose) {
86
+ this.options.onClose();
87
+ }
88
+ }
89
+ /**
90
+ * Destroy the modal (alias for close)
91
+ */
92
+ destroy() {
93
+ this.close();
94
+ }
95
+ // Private helper methods
96
+ setupRedactionCanvas() {
97
+ const elements = this.domCache.get();
98
+ if (!elements.redactionCanvas || !elements.screenshotImg) {
99
+ return;
100
+ }
101
+ try {
102
+ this.redactionCanvas = new redaction_canvas_1.RedactionCanvas(elements.redactionCanvas);
103
+ this.redactionCanvas.initializeCanvas(elements.screenshotImg);
104
+ }
105
+ catch (error) {
106
+ // Canvas 2D context not available (e.g., in test environment)
107
+ // Redaction features will be disabled
108
+ (0, logger_1.getLogger)().warn('Canvas redaction not available:', error);
109
+ this.redactionCanvas = null;
110
+ }
111
+ }
112
+ attachEventListeners() {
113
+ const elements = this.domCache.get();
114
+ // Close button
115
+ elements.closeButton.addEventListener('click', () => {
116
+ return this.close();
117
+ });
118
+ // Escape key to close
119
+ document.addEventListener('keydown', this.handleEscapeKey);
120
+ // Note: Overlay click does NOT close modal (improved UX to prevent accidental data loss)
121
+ // Form submission
122
+ elements.form.addEventListener('submit', (e) => {
123
+ return this.handleSubmit(e);
124
+ });
125
+ // Submit button click (manually trigger form submit for test compatibility)
126
+ elements.submitButton.addEventListener('click', () => {
127
+ const submitEvent = new Event('submit', { bubbles: true, cancelable: true });
128
+ elements.form.dispatchEvent(submitEvent);
129
+ });
130
+ // Cancel button
131
+ elements.cancelButton.addEventListener('click', () => {
132
+ return this.close();
133
+ });
134
+ // Real-time validation
135
+ elements.titleInput.addEventListener('input', () => {
136
+ return this.validateField('title');
137
+ });
138
+ elements.descriptionTextarea.addEventListener('input', () => {
139
+ this.validateField('description');
140
+ this.checkForPII();
141
+ });
142
+ // Redaction controls
143
+ if (elements.redactButton && this.redactionCanvas) {
144
+ elements.redactButton.addEventListener('click', () => {
145
+ return this.toggleRedactionMode();
146
+ });
147
+ }
148
+ if (elements.clearButton && this.redactionCanvas) {
149
+ elements.clearButton.addEventListener('click', () => {
150
+ return this.clearRedactions();
151
+ });
152
+ }
153
+ // PII confirmation
154
+ elements.piiConfirmCheckbox.addEventListener('change', () => {
155
+ return this.updateSubmitButton();
156
+ });
157
+ }
158
+ validateField(fieldName) {
159
+ const elements = this.domCache.get();
160
+ const value = fieldName === 'title' ? elements.titleInput.value : elements.descriptionTextarea.value;
161
+ const error = this.validator.validateField(fieldName, value);
162
+ const errorElement = fieldName === 'title' ? elements.titleError : elements.descriptionError;
163
+ if (error) {
164
+ errorElement.textContent = error;
165
+ errorElement.style.display = 'block';
166
+ }
167
+ else {
168
+ errorElement.textContent = '';
169
+ errorElement.style.display = 'none';
170
+ }
171
+ }
172
+ checkForPII() {
173
+ const elements = this.domCache.get();
174
+ const text = `${elements.titleInput.value} ${elements.descriptionTextarea.value}`;
175
+ // Create temporary sanitizer to detect PII
176
+ const sanitizer = (0, sanitize_1.createSanitizer)({ enabled: true });
177
+ const detections = sanitizer.detectPII(text);
178
+ // Convert to PIIDetection array
179
+ this.piiDetections = Array.from(detections.entries()).map(([type, count]) => {
180
+ return {
181
+ type,
182
+ count,
183
+ };
184
+ });
185
+ if (this.piiDetections.length > 0) {
186
+ elements.piiSection.style.display = 'block';
187
+ this.piiDisplay.render(this.piiDetections, elements.piiContent);
188
+ }
189
+ else {
190
+ elements.piiSection.style.display = 'none';
191
+ elements.piiConfirmCheckbox.checked = false;
192
+ }
193
+ this.updateSubmitButton();
194
+ }
195
+ updateSubmitButton() {
196
+ const elements = this.domCache.get();
197
+ const hasPII = this.piiDetections.length > 0;
198
+ const piiConfirmed = elements.piiConfirmCheckbox.checked;
199
+ elements.submitButton.disabled = hasPII && !piiConfirmed;
200
+ }
201
+ toggleRedactionMode() {
202
+ const elements = this.domCache.get();
203
+ if (!this.redactionCanvas || !elements.redactButton) {
204
+ return;
205
+ }
206
+ const isActive = this.redactionCanvas.toggleRedactionMode();
207
+ if (isActive) {
208
+ elements.redactButton.classList.add('active');
209
+ elements.redactButton.textContent = '✓ Redacting...';
210
+ }
211
+ else {
212
+ elements.redactButton.classList.remove('active');
213
+ elements.redactButton.textContent = '✏️ Redact Area';
214
+ }
215
+ }
216
+ clearRedactions() {
217
+ if (this.redactionCanvas) {
218
+ this.redactionCanvas.clearRedactions();
219
+ }
220
+ }
221
+ async handleSubmit(e) {
222
+ e.preventDefault();
223
+ const elements = this.domCache.get();
224
+ const formData = {
225
+ title: elements.titleInput.value,
226
+ description: elements.descriptionTextarea.value,
227
+ piiDetected: this.piiDetections.length > 0,
228
+ piiConfirmed: elements.piiConfirmCheckbox.checked,
229
+ };
230
+ const validation = this.validator.validate(formData);
231
+ if (!validation.isValid) {
232
+ // Display errors
233
+ if (validation.errors.title) {
234
+ elements.titleError.textContent = validation.errors.title;
235
+ elements.titleError.style.display = 'block';
236
+ }
237
+ else {
238
+ elements.titleError.textContent = '';
239
+ elements.titleError.style.display = 'none';
240
+ }
241
+ if (validation.errors.description) {
242
+ elements.descriptionError.textContent = validation.errors.description;
243
+ elements.descriptionError.style.display = 'block';
244
+ }
245
+ else {
246
+ elements.descriptionError.textContent = '';
247
+ elements.descriptionError.style.display = 'none';
248
+ }
249
+ if (validation.errors.piiConfirmation) {
250
+ alert(validation.errors.piiConfirmation);
251
+ }
252
+ return;
253
+ }
254
+ // Clear any previous error messages on successful validation
255
+ elements.titleError.style.display = 'none';
256
+ elements.descriptionError.style.display = 'none';
257
+ // Prepare screenshot with redactions
258
+ let finalScreenshot = this.originalScreenshot;
259
+ if (this.redactionCanvas && this.redactionCanvas.getRedactions().length > 0) {
260
+ try {
261
+ finalScreenshot = await this.screenshotProcessor.mergeRedactions(this.originalScreenshot, this.redactionCanvas.getCanvas());
262
+ }
263
+ catch (mergeError) {
264
+ logger.error('Failed to merge redactions:', mergeError);
265
+ finalScreenshot = this.originalScreenshot;
266
+ }
267
+ }
268
+ // Update original screenshot for submission
269
+ this.originalScreenshot = finalScreenshot;
270
+ // Submit
271
+ const bugReportData = {
272
+ title: formData.title.trim(),
273
+ description: formData.description.trim(),
274
+ };
275
+ try {
276
+ await this.options.onSubmit(bugReportData);
277
+ this.close();
278
+ }
279
+ catch (error) {
280
+ (0, logger_1.getLogger)().error('Error submitting bug report:', error);
281
+ alert('Failed to submit bug report. Please try again.');
282
+ }
283
+ }
284
+ /**
285
+ * Get the final screenshot (with redactions applied)
286
+ */
287
+ getScreenshot() {
288
+ return this.originalScreenshot;
289
+ }
290
+ /**
291
+ * Handle Escape key press to close modal
292
+ */
293
+ onEscapeKey(e) {
294
+ if (e.key === 'Escape') {
295
+ this.close();
296
+ }
297
+ }
298
+ }
299
+ exports.BugReportModal = BugReportModal;
package/docs/CDN.md ADDED
@@ -0,0 +1,213 @@
1
+ # Using BugSpotter SDK via CDN
2
+
3
+ The BugSpotter SDK is available via CDN for easy integration without npm installation.
4
+
5
+ ## 🚀 Quick Start
6
+
7
+ Add the SDK to your HTML file:
8
+
9
+ ```html
10
+ <script src="https://cdn.bugspotter.io/sdk/bugspotter-latest.min.js"></script>
11
+ <script>
12
+ const bugSpotter = window.BugSpotter.init({
13
+ apiKey: 'bgs_your_api_key',
14
+ endpoint: 'https://api.bugspotter.io',
15
+ });
16
+ </script>
17
+ ```
18
+
19
+ ## 📦 CDN URLs
20
+
21
+ ### Production (Versioned)
22
+
23
+ **Recommended for production use** - immutable, cached for 1 year:
24
+
25
+ ```html
26
+ <!-- Specific version (replace X.Y.Z with actual version) -->
27
+ <script src="https://cdn.bugspotter.io/sdk/bugspotter-X.Y.Z.min.js"></script>
28
+ ```
29
+
30
+ **Example:**
31
+
32
+ ```html
33
+ <script src="https://cdn.bugspotter.io/sdk/bugspotter-0.1.0.min.js"></script>
34
+ ```
35
+
36
+ ### Development (Latest)
37
+
38
+ **For development/testing only** - auto-updates to latest version:
39
+
40
+ ```html
41
+ <script src="https://cdn.bugspotter.io/sdk/bugspotter-latest.min.js"></script>
42
+ ```
43
+
44
+ ⚠️ **Warning:** The `latest` version updates automatically. Use versioned URLs in production to prevent breaking changes.
45
+
46
+ ## 🔒 Subresource Integrity (SRI)
47
+
48
+ For enhanced security, use SRI hashes to verify file integrity:
49
+
50
+ ```html
51
+ <script
52
+ src="https://cdn.bugspotter.io/sdk/bugspotter-0.1.0.min.js"
53
+ integrity="sha384-..."
54
+ crossorigin="anonymous"
55
+ ></script>
56
+ ```
57
+
58
+ To generate SRI hash for a specific version:
59
+
60
+ ```bash
61
+ curl https://cdn.bugspotter.io/sdk/bugspotter-0.1.0.min.js | openssl dgst -sha384 -binary | openssl base64 -A
62
+ ```
63
+
64
+ ## 📝 Complete Example
65
+
66
+ ```html
67
+ <!DOCTYPE html>
68
+ <html lang="en">
69
+ <head>
70
+ <meta charset="UTF-8" />
71
+ <title>BugSpotter Example</title>
72
+ </head>
73
+ <body>
74
+ <h1>My Application</h1>
75
+ <button id="trigger-error">Trigger Test Error</button>
76
+
77
+ <!-- Load BugSpotter SDK -->
78
+ <script src="https://cdn.bugspotter.io/sdk/bugspotter-0.1.0.min.js"></script>
79
+
80
+ <script>
81
+ // Initialize BugSpotter
82
+ const bugSpotter = window.BugSpotter.init({
83
+ apiKey: 'bgs_your_api_key',
84
+ endpoint: 'https://api.bugspotter.io',
85
+ sessionReplay: true,
86
+ captureConsole: true,
87
+ captureNetwork: true,
88
+ });
89
+
90
+ // Test error reporting
91
+ document.getElementById('trigger-error').addEventListener('click', () => {
92
+ try {
93
+ throw new Error('Test error from CDN example');
94
+ } catch (error) {
95
+ console.error('Caught error:', error);
96
+ }
97
+ });
98
+ </script>
99
+ </body>
100
+ </html>
101
+ ```
102
+
103
+ ## 🌐 Alternative CDN Options
104
+
105
+ If our primary CDN is unavailable, you can also use:
106
+
107
+ ### unpkg
108
+
109
+ ```html
110
+ <!-- Auto-resolves to browser field (recommended) -->
111
+ <script src="https://unpkg.com/@bugspotter/sdk@latest"></script>
112
+
113
+ <!-- Or explicit path with specific version -->
114
+ <script src="https://unpkg.com/@bugspotter/sdk@0.1.0/dist/bugspotter.min.js"></script>
115
+ ```
116
+
117
+ ### jsDelivr
118
+
119
+ ```html
120
+ <!-- Auto-resolves to browser field (recommended) -->
121
+ <script src="https://cdn.jsdelivr.net/npm/@bugspotter/sdk@latest"></script>
122
+
123
+ <!-- Or explicit path with specific version -->
124
+ <script src="https://cdn.jsdelivr.net/npm/@bugspotter/sdk@0.1.0/dist/bugspotter.min.js"></script>
125
+ ```
126
+
127
+ ## 🆚 CDN vs npm
128
+
129
+ | Feature | CDN | npm |
130
+ | ------------------- | ----------------------- | ------------------------------- |
131
+ | **Setup** | Add `<script>` tag | `npm install @bugspotter/sdk` |
132
+ | **Bundle Size** | ~99KB (gzipped: ~35KB) | Tree-shakable, optimized |
133
+ | **Updates** | Change version in URL | `npm update` |
134
+ | **TypeScript** | No types | Full TypeScript support |
135
+ | **Best For** | Quick prototypes, demos | Production apps, React/Vue/etc. |
136
+ | **Browser Support** | ES2017+ (95% coverage) | Configurable via bundler |
137
+
138
+ ## 📊 Version History
139
+
140
+ Check available versions:
141
+
142
+ - [npm package page](https://www.npmjs.com/package/@bugspotter/sdk?activeTab=versions)
143
+ - [GitHub releases](https://github.com/apexbridge-tech/bugspotter/releases)
144
+
145
+ ## ⚙️ Configuration
146
+
147
+ All configuration options from npm package work with CDN:
148
+
149
+ ```javascript
150
+ window.BugSpotter.init({
151
+ apiKey: 'bgs_your_api_key',
152
+ endpoint: 'https://api.bugspotter.io',
153
+
154
+ // Session replay
155
+ sessionReplay: true,
156
+
157
+ // Data capture
158
+ captureConsole: true,
159
+ captureNetwork: true,
160
+ captureScreenshot: true,
161
+
162
+ // Privacy
163
+ sanitizePII: true,
164
+
165
+ // Performance
166
+ sampleRate: 1.0, // 100% of sessions
167
+
168
+ // Custom data
169
+ metadata: {
170
+ environment: 'production',
171
+ version: '1.0.0',
172
+ },
173
+ });
174
+ ```
175
+
176
+ See [Configuration Guide](./README.md#configuration) for all options.
177
+
178
+ ## 🔧 Troubleshooting
179
+
180
+ ### Script Not Loading
181
+
182
+ 1. **Check browser console** for CORS or loading errors
183
+ 2. **Verify URL** - ensure version exists
184
+ 3. **Try alternative CDN** (unpkg or jsDelivr)
185
+
186
+ ### Global Not Found
187
+
188
+ If `window.BugSpotter` is undefined:
189
+
190
+ 1. **Script loaded?** Check Network tab in DevTools
191
+ 2. **Correct order?** SDK must load before initialization
192
+ 3. **No conflicts?** Another script may override `window.BugSpotter`
193
+
194
+ ### Old Version Cached
195
+
196
+ If updates aren't showing:
197
+
198
+ 1. **Hard refresh:** Ctrl+Shift+R (Cmd+Shift+R on Mac)
199
+ 2. **Use versioned URL** in production (not `latest`)
200
+ 3. **Check cache headers** in Network tab
201
+
202
+ ## 📚 Next Steps
203
+
204
+ - [Full SDK Documentation](./README.md)
205
+ - [Framework Integration](./FRAMEWORK_INTEGRATION.md)
206
+ - [Session Replay Guide](./SESSION_REPLAY.md)
207
+ - [API Reference](../../README.md#api-reference)
208
+
209
+ ## 🆘 Support
210
+
211
+ - **Issues:** [GitHub Issues](https://github.com/apexbridge-tech/bugspotter/issues)
212
+ - **Discussions:** [GitHub Discussions](https://github.com/apexbridge-tech/bugspotter/discussions)
213
+ - **Security:** security@bugspotter.io