@rithuu/bugblitz-sdk 1.0.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.
@@ -0,0 +1,338 @@
1
+ 'use strict';
2
+
3
+ const BugBlitz = {
4
+ apiKey: null,
5
+ projectId: null,
6
+ errors: [],
7
+ consoleErrors: [],
8
+ networkErrors: [],
9
+
10
+ init(config) {
11
+ if (!config.apiKey) {
12
+ console.error("BugBlitz: apiKey is required");
13
+ return;
14
+ }
15
+
16
+ if (!config.triggerElement) {
17
+ console.error("BugBlitz: triggerElement is required");
18
+ return;
19
+ }
20
+
21
+ this.apiKey = config.apiKey;
22
+
23
+ console.log("BugBlitz initialized with key:", this.apiKey);
24
+
25
+ this.captureErrors();
26
+ this.interceptNetworkErrors();
27
+
28
+ this.attachTrigger(config.triggerElement);
29
+ },
30
+
31
+ attachTrigger(selector) {
32
+ const element = document.querySelector(selector);
33
+
34
+ if (!element) {
35
+ console.error(`BugBlitz: No element found for selector "${selector}"`);
36
+ return;
37
+ }
38
+
39
+ element.addEventListener("click", () => {
40
+ this.openPopup();
41
+ });
42
+
43
+ console.log("BugBlitz: trigger attached to", selector);
44
+ },
45
+
46
+
47
+ openPopup() {
48
+ // Prevent multiple popups
49
+ if (document.getElementById("bugblitz-popup")) return;
50
+
51
+ const browserInfo = this.collectBrowserInfo();
52
+ console.log("BugBlitz: browser info collected", browserInfo);
53
+
54
+ // Overlay (dark background)
55
+ const overlay = document.createElement("div");
56
+ overlay.id = "bugblitz-overlay";
57
+ overlay.style.cssText = `
58
+ position: fixed;
59
+ top: 0; left: 0;
60
+ width: 100%; height: 100%;
61
+ background: rgba(0,0,0,0.5);
62
+ z-index: 999998;
63
+ `;
64
+
65
+ // Popup box
66
+ const popup = document.createElement("div");
67
+ popup.id = "bugblitz-popup";
68
+ popup.style.cssText = `
69
+ position: fixed;
70
+ top: 50%; left: 50%;
71
+ transform: translate(-50%, -50%);
72
+ background: white;
73
+ padding: 24px;
74
+ border-radius: 12px;
75
+ z-index: 999999;
76
+ width: 400px;
77
+ font-family: sans-serif;
78
+ box-shadow: 0 4px 24px rgba(0,0,0,0.2);
79
+ `;
80
+
81
+ // Title
82
+ const title = document.createElement("h3");
83
+ title.innerText = "Report a Bug 🐛";
84
+ title.style.cssText = `
85
+ margin: 0 0 16px 0;
86
+ font-size: 18px;
87
+ color: #111;
88
+ `;
89
+
90
+ // Textarea
91
+ const textarea = document.createElement("textarea");
92
+ textarea.id = "bugblitz-description";
93
+ textarea.placeholder = "Describe the bug...";
94
+ textarea.style.cssText = `
95
+ width: 100%;
96
+ height: 120px;
97
+ padding: 10px;
98
+ border: 1px solid #ddd;
99
+ border-radius: 8px;
100
+ font-size: 14px;
101
+ resize: none;
102
+ box-sizing: border-box;
103
+ `;
104
+
105
+ // Submit button
106
+ const submitBtn = document.createElement("button");
107
+ submitBtn.innerText = "Submit Report";
108
+ submitBtn.style.cssText = `
109
+ margin-top: 12px;
110
+ width: 100%;
111
+ padding: 10px;
112
+ background: #6c47ff;
113
+ color: white;
114
+ border: none;
115
+ border-radius: 8px;
116
+ font-size: 14px;
117
+ cursor: pointer;
118
+ `;
119
+
120
+ // Close button
121
+ const closeBtn = document.createElement("button");
122
+ closeBtn.innerText = "✕";
123
+ closeBtn.style.cssText = `
124
+ position: absolute;
125
+ top: 12px; right: 16px;
126
+ background: none;
127
+ border: none;
128
+ font-size: 18px;
129
+ cursor: pointer;
130
+ color: #888;
131
+ `;
132
+
133
+ // Close logic
134
+ closeBtn.addEventListener("click", () => this.closePopup());
135
+ overlay.addEventListener("click", () => this.closePopup());
136
+
137
+ // Submit logic
138
+ submitBtn.addEventListener("click", async () => {
139
+ const description = document.getElementById("bugblitz-description").value;
140
+
141
+ if (!description.trim()) {
142
+ alert("Please describe the bug before submitting.");
143
+ return;
144
+ }
145
+
146
+ submitBtn.innerText = "Capturing screenshot..";
147
+ submitBtn.disabled = true;
148
+
149
+ const screenshot = await this.captureScreenshot();
150
+
151
+ submitBtn.innerText = "Sending...";
152
+
153
+ // const payload = {
154
+ // apiKey: this.apiKey,
155
+ // description,
156
+ // browserInfo: this.collectBrowserInfo(),
157
+ // errors: this.errors,
158
+ // };
159
+
160
+ const formData = new FormData();
161
+ formData.append("apiKey", this.apiKey);
162
+ formData.append("description", description);
163
+ formData.append("browserInfo", JSON.stringify(this.collectBrowserInfo()));
164
+ formData.append("errors", JSON.stringify(this.consoleErrors));
165
+ formData.append("networkErrors", JSON.stringify(this.networkErrors));
166
+ if (screenshot) {
167
+ formData.append("screenshot", screenshot, "screenshot.png");
168
+ }
169
+
170
+
171
+ try {
172
+ const response = await fetch("https://bugblitz-backend.onrender.com/api/bugs", {
173
+ method: "POST",
174
+ body: formData,
175
+ });
176
+
177
+ if (!response.ok) {
178
+ throw new Error("Server error");
179
+ }
180
+
181
+ submitBtn.innerText = "Submitted! ✓";
182
+ setTimeout(() => this.closePopup(), 1500);
183
+
184
+ } catch (err) {
185
+ console.error("BugBlitz: failed to send report", err);
186
+ submitBtn.innerText = "Failed. Try again.";
187
+ submitBtn.disabled = false;
188
+ }
189
+ });
190
+
191
+ // Assemble popup
192
+ popup.style.position = "fixed";
193
+ popup.appendChild(closeBtn);
194
+ popup.appendChild(title);
195
+ popup.appendChild(textarea);
196
+ popup.appendChild(submitBtn);
197
+
198
+ document.body.appendChild(overlay);
199
+ document.body.appendChild(popup);
200
+ },
201
+
202
+ closePopup() {
203
+ document.getElementById("bugblitz-popup")?.remove();
204
+ document.getElementById("bugblitz-overlay")?.remove();
205
+ },
206
+
207
+ collectBrowserInfo() {
208
+ return {
209
+ userAgent: navigator.userAgent,
210
+ url: window.location.href,
211
+ screenWidth: screen.width,
212
+ screenHeight: screen.height,
213
+ platform: navigator.platform,
214
+ timestamp: new Date().toISOString(),
215
+ };
216
+ },
217
+
218
+ captureErrors() {
219
+ // Capture JS errors
220
+ window.onerror = (message, source, line, column, error) => {
221
+ const entry = {
222
+ type: "error",
223
+ message,
224
+ source,
225
+ line,
226
+ column,
227
+ stack: error?.stack,
228
+ timestamp: new Date().toISOString(),
229
+ };
230
+
231
+ this.consoleErrors.push(entry);
232
+ this.errors.push(entry);
233
+ };
234
+
235
+ // Capture unhandled promise rejections
236
+ window.onunhandledrejection = (event) => {
237
+ const entry = {
238
+ type: "unhandledrejection",
239
+ message: event.reason?.message || String(event.reason),
240
+ stack: event.reason?.stack,
241
+ timestamp: new Date().toISOString(),
242
+ };
243
+
244
+ this.consoleErrors.push(entry);
245
+ this.errors.push(entry);
246
+ };
247
+ },
248
+
249
+ interceptNetworkErrors() {
250
+ if (typeof window === "undefined" || typeof window.fetch !== "function") {
251
+ return;
252
+ }
253
+
254
+ const originalFetch = window.fetch.bind(window);
255
+
256
+ window.fetch = async (...args) => {
257
+ const [resource, init] = args;
258
+ const url = typeof resource === "string" ? resource : resource?.url || "";
259
+ const method = init?.method || "GET";
260
+
261
+ try {
262
+ const response = await originalFetch(...args);
263
+
264
+ if (!response.ok) {
265
+ this.networkErrors.push({
266
+ type: "network",
267
+ url,
268
+ method,
269
+ status: response.status,
270
+ statusText: response.statusText,
271
+ timestamp: new Date().toISOString(),
272
+ });
273
+ }
274
+
275
+ return response;
276
+ } catch (error) {
277
+ this.networkErrors.push({
278
+ type: "network",
279
+ url,
280
+ method,
281
+ status: null,
282
+ statusText: error?.message || "Network request failed",
283
+ timestamp: new Date().toISOString(),
284
+ });
285
+
286
+ throw error;
287
+ }
288
+ };
289
+ },
290
+
291
+ loadHtml2Canvas() {
292
+ return new Promise((resolve, reject) => {
293
+ if (window.html2canvas) {
294
+ resolve(window.html2canvas);
295
+ return;
296
+ }
297
+
298
+ const script = document.createElement("script");
299
+ script.src = "https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js";
300
+
301
+ script.onload = () => resolve(window.html2canvas);
302
+ script.onerror = () => reject(new Error("BugBlitz: failed to load html2canvas"));
303
+
304
+ document.head.appendChild(script);
305
+ });
306
+ },
307
+
308
+ async captureScreenshot() {
309
+ try {
310
+ const html2canvas = await this.loadHtml2Canvas();
311
+
312
+ const popup = document.getElementById("bugblitz-popup");
313
+ const overlay = document.getElementById("bugblitz-overlay");
314
+ if (popup) popup.style.display = "none";
315
+ if (overlay) overlay.style.display = "none";
316
+
317
+ const canvas = await html2canvas(document.body);
318
+
319
+ if (popup) popup.style.display = "block";
320
+ if (overlay) overlay.style.display = "block";
321
+
322
+ return new Promise((resolve) => {
323
+ canvas.toBlob((blob) => resolve(blob), "image/png");
324
+ });
325
+
326
+ } catch (err) {
327
+ console.error("BugBlitz: screenshot failed", err);
328
+ return null;
329
+ }
330
+ },
331
+
332
+ };
333
+
334
+ if (typeof window !== "undefined") {
335
+ window.BugBlitz = BugBlitz;
336
+ }
337
+
338
+ module.exports = BugBlitz;
@@ -0,0 +1,336 @@
1
+ const BugBlitz = {
2
+ apiKey: null,
3
+ projectId: null,
4
+ errors: [],
5
+ consoleErrors: [],
6
+ networkErrors: [],
7
+
8
+ init(config) {
9
+ if (!config.apiKey) {
10
+ console.error("BugBlitz: apiKey is required");
11
+ return;
12
+ }
13
+
14
+ if (!config.triggerElement) {
15
+ console.error("BugBlitz: triggerElement is required");
16
+ return;
17
+ }
18
+
19
+ this.apiKey = config.apiKey;
20
+
21
+ console.log("BugBlitz initialized with key:", this.apiKey);
22
+
23
+ this.captureErrors();
24
+ this.interceptNetworkErrors();
25
+
26
+ this.attachTrigger(config.triggerElement);
27
+ },
28
+
29
+ attachTrigger(selector) {
30
+ const element = document.querySelector(selector);
31
+
32
+ if (!element) {
33
+ console.error(`BugBlitz: No element found for selector "${selector}"`);
34
+ return;
35
+ }
36
+
37
+ element.addEventListener("click", () => {
38
+ this.openPopup();
39
+ });
40
+
41
+ console.log("BugBlitz: trigger attached to", selector);
42
+ },
43
+
44
+
45
+ openPopup() {
46
+ // Prevent multiple popups
47
+ if (document.getElementById("bugblitz-popup")) return;
48
+
49
+ const browserInfo = this.collectBrowserInfo();
50
+ console.log("BugBlitz: browser info collected", browserInfo);
51
+
52
+ // Overlay (dark background)
53
+ const overlay = document.createElement("div");
54
+ overlay.id = "bugblitz-overlay";
55
+ overlay.style.cssText = `
56
+ position: fixed;
57
+ top: 0; left: 0;
58
+ width: 100%; height: 100%;
59
+ background: rgba(0,0,0,0.5);
60
+ z-index: 999998;
61
+ `;
62
+
63
+ // Popup box
64
+ const popup = document.createElement("div");
65
+ popup.id = "bugblitz-popup";
66
+ popup.style.cssText = `
67
+ position: fixed;
68
+ top: 50%; left: 50%;
69
+ transform: translate(-50%, -50%);
70
+ background: white;
71
+ padding: 24px;
72
+ border-radius: 12px;
73
+ z-index: 999999;
74
+ width: 400px;
75
+ font-family: sans-serif;
76
+ box-shadow: 0 4px 24px rgba(0,0,0,0.2);
77
+ `;
78
+
79
+ // Title
80
+ const title = document.createElement("h3");
81
+ title.innerText = "Report a Bug 🐛";
82
+ title.style.cssText = `
83
+ margin: 0 0 16px 0;
84
+ font-size: 18px;
85
+ color: #111;
86
+ `;
87
+
88
+ // Textarea
89
+ const textarea = document.createElement("textarea");
90
+ textarea.id = "bugblitz-description";
91
+ textarea.placeholder = "Describe the bug...";
92
+ textarea.style.cssText = `
93
+ width: 100%;
94
+ height: 120px;
95
+ padding: 10px;
96
+ border: 1px solid #ddd;
97
+ border-radius: 8px;
98
+ font-size: 14px;
99
+ resize: none;
100
+ box-sizing: border-box;
101
+ `;
102
+
103
+ // Submit button
104
+ const submitBtn = document.createElement("button");
105
+ submitBtn.innerText = "Submit Report";
106
+ submitBtn.style.cssText = `
107
+ margin-top: 12px;
108
+ width: 100%;
109
+ padding: 10px;
110
+ background: #6c47ff;
111
+ color: white;
112
+ border: none;
113
+ border-radius: 8px;
114
+ font-size: 14px;
115
+ cursor: pointer;
116
+ `;
117
+
118
+ // Close button
119
+ const closeBtn = document.createElement("button");
120
+ closeBtn.innerText = "✕";
121
+ closeBtn.style.cssText = `
122
+ position: absolute;
123
+ top: 12px; right: 16px;
124
+ background: none;
125
+ border: none;
126
+ font-size: 18px;
127
+ cursor: pointer;
128
+ color: #888;
129
+ `;
130
+
131
+ // Close logic
132
+ closeBtn.addEventListener("click", () => this.closePopup());
133
+ overlay.addEventListener("click", () => this.closePopup());
134
+
135
+ // Submit logic
136
+ submitBtn.addEventListener("click", async () => {
137
+ const description = document.getElementById("bugblitz-description").value;
138
+
139
+ if (!description.trim()) {
140
+ alert("Please describe the bug before submitting.");
141
+ return;
142
+ }
143
+
144
+ submitBtn.innerText = "Capturing screenshot..";
145
+ submitBtn.disabled = true;
146
+
147
+ const screenshot = await this.captureScreenshot();
148
+
149
+ submitBtn.innerText = "Sending...";
150
+
151
+ // const payload = {
152
+ // apiKey: this.apiKey,
153
+ // description,
154
+ // browserInfo: this.collectBrowserInfo(),
155
+ // errors: this.errors,
156
+ // };
157
+
158
+ const formData = new FormData();
159
+ formData.append("apiKey", this.apiKey);
160
+ formData.append("description", description);
161
+ formData.append("browserInfo", JSON.stringify(this.collectBrowserInfo()));
162
+ formData.append("errors", JSON.stringify(this.consoleErrors));
163
+ formData.append("networkErrors", JSON.stringify(this.networkErrors));
164
+ if (screenshot) {
165
+ formData.append("screenshot", screenshot, "screenshot.png");
166
+ }
167
+
168
+
169
+ try {
170
+ const response = await fetch("https://bugblitz-backend.onrender.com/api/bugs", {
171
+ method: "POST",
172
+ body: formData,
173
+ });
174
+
175
+ if (!response.ok) {
176
+ throw new Error("Server error");
177
+ }
178
+
179
+ submitBtn.innerText = "Submitted! ✓";
180
+ setTimeout(() => this.closePopup(), 1500);
181
+
182
+ } catch (err) {
183
+ console.error("BugBlitz: failed to send report", err);
184
+ submitBtn.innerText = "Failed. Try again.";
185
+ submitBtn.disabled = false;
186
+ }
187
+ });
188
+
189
+ // Assemble popup
190
+ popup.style.position = "fixed";
191
+ popup.appendChild(closeBtn);
192
+ popup.appendChild(title);
193
+ popup.appendChild(textarea);
194
+ popup.appendChild(submitBtn);
195
+
196
+ document.body.appendChild(overlay);
197
+ document.body.appendChild(popup);
198
+ },
199
+
200
+ closePopup() {
201
+ document.getElementById("bugblitz-popup")?.remove();
202
+ document.getElementById("bugblitz-overlay")?.remove();
203
+ },
204
+
205
+ collectBrowserInfo() {
206
+ return {
207
+ userAgent: navigator.userAgent,
208
+ url: window.location.href,
209
+ screenWidth: screen.width,
210
+ screenHeight: screen.height,
211
+ platform: navigator.platform,
212
+ timestamp: new Date().toISOString(),
213
+ };
214
+ },
215
+
216
+ captureErrors() {
217
+ // Capture JS errors
218
+ window.onerror = (message, source, line, column, error) => {
219
+ const entry = {
220
+ type: "error",
221
+ message,
222
+ source,
223
+ line,
224
+ column,
225
+ stack: error?.stack,
226
+ timestamp: new Date().toISOString(),
227
+ };
228
+
229
+ this.consoleErrors.push(entry);
230
+ this.errors.push(entry);
231
+ };
232
+
233
+ // Capture unhandled promise rejections
234
+ window.onunhandledrejection = (event) => {
235
+ const entry = {
236
+ type: "unhandledrejection",
237
+ message: event.reason?.message || String(event.reason),
238
+ stack: event.reason?.stack,
239
+ timestamp: new Date().toISOString(),
240
+ };
241
+
242
+ this.consoleErrors.push(entry);
243
+ this.errors.push(entry);
244
+ };
245
+ },
246
+
247
+ interceptNetworkErrors() {
248
+ if (typeof window === "undefined" || typeof window.fetch !== "function") {
249
+ return;
250
+ }
251
+
252
+ const originalFetch = window.fetch.bind(window);
253
+
254
+ window.fetch = async (...args) => {
255
+ const [resource, init] = args;
256
+ const url = typeof resource === "string" ? resource : resource?.url || "";
257
+ const method = init?.method || "GET";
258
+
259
+ try {
260
+ const response = await originalFetch(...args);
261
+
262
+ if (!response.ok) {
263
+ this.networkErrors.push({
264
+ type: "network",
265
+ url,
266
+ method,
267
+ status: response.status,
268
+ statusText: response.statusText,
269
+ timestamp: new Date().toISOString(),
270
+ });
271
+ }
272
+
273
+ return response;
274
+ } catch (error) {
275
+ this.networkErrors.push({
276
+ type: "network",
277
+ url,
278
+ method,
279
+ status: null,
280
+ statusText: error?.message || "Network request failed",
281
+ timestamp: new Date().toISOString(),
282
+ });
283
+
284
+ throw error;
285
+ }
286
+ };
287
+ },
288
+
289
+ loadHtml2Canvas() {
290
+ return new Promise((resolve, reject) => {
291
+ if (window.html2canvas) {
292
+ resolve(window.html2canvas);
293
+ return;
294
+ }
295
+
296
+ const script = document.createElement("script");
297
+ script.src = "https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js";
298
+
299
+ script.onload = () => resolve(window.html2canvas);
300
+ script.onerror = () => reject(new Error("BugBlitz: failed to load html2canvas"));
301
+
302
+ document.head.appendChild(script);
303
+ });
304
+ },
305
+
306
+ async captureScreenshot() {
307
+ try {
308
+ const html2canvas = await this.loadHtml2Canvas();
309
+
310
+ const popup = document.getElementById("bugblitz-popup");
311
+ const overlay = document.getElementById("bugblitz-overlay");
312
+ if (popup) popup.style.display = "none";
313
+ if (overlay) overlay.style.display = "none";
314
+
315
+ const canvas = await html2canvas(document.body);
316
+
317
+ if (popup) popup.style.display = "block";
318
+ if (overlay) overlay.style.display = "block";
319
+
320
+ return new Promise((resolve) => {
321
+ canvas.toBlob((blob) => resolve(blob), "image/png");
322
+ });
323
+
324
+ } catch (err) {
325
+ console.error("BugBlitz: screenshot failed", err);
326
+ return null;
327
+ }
328
+ },
329
+
330
+ };
331
+
332
+ if (typeof window !== "undefined") {
333
+ window.BugBlitz = BugBlitz;
334
+ }
335
+
336
+ export { BugBlitz as default };
@@ -0,0 +1,341 @@
1
+ var BugBlitz = (function () {
2
+ 'use strict';
3
+
4
+ const BugBlitz = {
5
+ apiKey: null,
6
+ projectId: null,
7
+ errors: [],
8
+ consoleErrors: [],
9
+ networkErrors: [],
10
+
11
+ init(config) {
12
+ if (!config.apiKey) {
13
+ console.error("BugBlitz: apiKey is required");
14
+ return;
15
+ }
16
+
17
+ if (!config.triggerElement) {
18
+ console.error("BugBlitz: triggerElement is required");
19
+ return;
20
+ }
21
+
22
+ this.apiKey = config.apiKey;
23
+
24
+ console.log("BugBlitz initialized with key:", this.apiKey);
25
+
26
+ this.captureErrors();
27
+ this.interceptNetworkErrors();
28
+
29
+ this.attachTrigger(config.triggerElement);
30
+ },
31
+
32
+ attachTrigger(selector) {
33
+ const element = document.querySelector(selector);
34
+
35
+ if (!element) {
36
+ console.error(`BugBlitz: No element found for selector "${selector}"`);
37
+ return;
38
+ }
39
+
40
+ element.addEventListener("click", () => {
41
+ this.openPopup();
42
+ });
43
+
44
+ console.log("BugBlitz: trigger attached to", selector);
45
+ },
46
+
47
+
48
+ openPopup() {
49
+ // Prevent multiple popups
50
+ if (document.getElementById("bugblitz-popup")) return;
51
+
52
+ const browserInfo = this.collectBrowserInfo();
53
+ console.log("BugBlitz: browser info collected", browserInfo);
54
+
55
+ // Overlay (dark background)
56
+ const overlay = document.createElement("div");
57
+ overlay.id = "bugblitz-overlay";
58
+ overlay.style.cssText = `
59
+ position: fixed;
60
+ top: 0; left: 0;
61
+ width: 100%; height: 100%;
62
+ background: rgba(0,0,0,0.5);
63
+ z-index: 999998;
64
+ `;
65
+
66
+ // Popup box
67
+ const popup = document.createElement("div");
68
+ popup.id = "bugblitz-popup";
69
+ popup.style.cssText = `
70
+ position: fixed;
71
+ top: 50%; left: 50%;
72
+ transform: translate(-50%, -50%);
73
+ background: white;
74
+ padding: 24px;
75
+ border-radius: 12px;
76
+ z-index: 999999;
77
+ width: 400px;
78
+ font-family: sans-serif;
79
+ box-shadow: 0 4px 24px rgba(0,0,0,0.2);
80
+ `;
81
+
82
+ // Title
83
+ const title = document.createElement("h3");
84
+ title.innerText = "Report a Bug 🐛";
85
+ title.style.cssText = `
86
+ margin: 0 0 16px 0;
87
+ font-size: 18px;
88
+ color: #111;
89
+ `;
90
+
91
+ // Textarea
92
+ const textarea = document.createElement("textarea");
93
+ textarea.id = "bugblitz-description";
94
+ textarea.placeholder = "Describe the bug...";
95
+ textarea.style.cssText = `
96
+ width: 100%;
97
+ height: 120px;
98
+ padding: 10px;
99
+ border: 1px solid #ddd;
100
+ border-radius: 8px;
101
+ font-size: 14px;
102
+ resize: none;
103
+ box-sizing: border-box;
104
+ `;
105
+
106
+ // Submit button
107
+ const submitBtn = document.createElement("button");
108
+ submitBtn.innerText = "Submit Report";
109
+ submitBtn.style.cssText = `
110
+ margin-top: 12px;
111
+ width: 100%;
112
+ padding: 10px;
113
+ background: #6c47ff;
114
+ color: white;
115
+ border: none;
116
+ border-radius: 8px;
117
+ font-size: 14px;
118
+ cursor: pointer;
119
+ `;
120
+
121
+ // Close button
122
+ const closeBtn = document.createElement("button");
123
+ closeBtn.innerText = "✕";
124
+ closeBtn.style.cssText = `
125
+ position: absolute;
126
+ top: 12px; right: 16px;
127
+ background: none;
128
+ border: none;
129
+ font-size: 18px;
130
+ cursor: pointer;
131
+ color: #888;
132
+ `;
133
+
134
+ // Close logic
135
+ closeBtn.addEventListener("click", () => this.closePopup());
136
+ overlay.addEventListener("click", () => this.closePopup());
137
+
138
+ // Submit logic
139
+ submitBtn.addEventListener("click", async () => {
140
+ const description = document.getElementById("bugblitz-description").value;
141
+
142
+ if (!description.trim()) {
143
+ alert("Please describe the bug before submitting.");
144
+ return;
145
+ }
146
+
147
+ submitBtn.innerText = "Capturing screenshot..";
148
+ submitBtn.disabled = true;
149
+
150
+ const screenshot = await this.captureScreenshot();
151
+
152
+ submitBtn.innerText = "Sending...";
153
+
154
+ // const payload = {
155
+ // apiKey: this.apiKey,
156
+ // description,
157
+ // browserInfo: this.collectBrowserInfo(),
158
+ // errors: this.errors,
159
+ // };
160
+
161
+ const formData = new FormData();
162
+ formData.append("apiKey", this.apiKey);
163
+ formData.append("description", description);
164
+ formData.append("browserInfo", JSON.stringify(this.collectBrowserInfo()));
165
+ formData.append("errors", JSON.stringify(this.consoleErrors));
166
+ formData.append("networkErrors", JSON.stringify(this.networkErrors));
167
+ if (screenshot) {
168
+ formData.append("screenshot", screenshot, "screenshot.png");
169
+ }
170
+
171
+
172
+ try {
173
+ const response = await fetch("https://bugblitz-backend.onrender.com/api/bugs", {
174
+ method: "POST",
175
+ body: formData,
176
+ });
177
+
178
+ if (!response.ok) {
179
+ throw new Error("Server error");
180
+ }
181
+
182
+ submitBtn.innerText = "Submitted! ✓";
183
+ setTimeout(() => this.closePopup(), 1500);
184
+
185
+ } catch (err) {
186
+ console.error("BugBlitz: failed to send report", err);
187
+ submitBtn.innerText = "Failed. Try again.";
188
+ submitBtn.disabled = false;
189
+ }
190
+ });
191
+
192
+ // Assemble popup
193
+ popup.style.position = "fixed";
194
+ popup.appendChild(closeBtn);
195
+ popup.appendChild(title);
196
+ popup.appendChild(textarea);
197
+ popup.appendChild(submitBtn);
198
+
199
+ document.body.appendChild(overlay);
200
+ document.body.appendChild(popup);
201
+ },
202
+
203
+ closePopup() {
204
+ document.getElementById("bugblitz-popup")?.remove();
205
+ document.getElementById("bugblitz-overlay")?.remove();
206
+ },
207
+
208
+ collectBrowserInfo() {
209
+ return {
210
+ userAgent: navigator.userAgent,
211
+ url: window.location.href,
212
+ screenWidth: screen.width,
213
+ screenHeight: screen.height,
214
+ platform: navigator.platform,
215
+ timestamp: new Date().toISOString(),
216
+ };
217
+ },
218
+
219
+ captureErrors() {
220
+ // Capture JS errors
221
+ window.onerror = (message, source, line, column, error) => {
222
+ const entry = {
223
+ type: "error",
224
+ message,
225
+ source,
226
+ line,
227
+ column,
228
+ stack: error?.stack,
229
+ timestamp: new Date().toISOString(),
230
+ };
231
+
232
+ this.consoleErrors.push(entry);
233
+ this.errors.push(entry);
234
+ };
235
+
236
+ // Capture unhandled promise rejections
237
+ window.onunhandledrejection = (event) => {
238
+ const entry = {
239
+ type: "unhandledrejection",
240
+ message: event.reason?.message || String(event.reason),
241
+ stack: event.reason?.stack,
242
+ timestamp: new Date().toISOString(),
243
+ };
244
+
245
+ this.consoleErrors.push(entry);
246
+ this.errors.push(entry);
247
+ };
248
+ },
249
+
250
+ interceptNetworkErrors() {
251
+ if (typeof window === "undefined" || typeof window.fetch !== "function") {
252
+ return;
253
+ }
254
+
255
+ const originalFetch = window.fetch.bind(window);
256
+
257
+ window.fetch = async (...args) => {
258
+ const [resource, init] = args;
259
+ const url = typeof resource === "string" ? resource : resource?.url || "";
260
+ const method = init?.method || "GET";
261
+
262
+ try {
263
+ const response = await originalFetch(...args);
264
+
265
+ if (!response.ok) {
266
+ this.networkErrors.push({
267
+ type: "network",
268
+ url,
269
+ method,
270
+ status: response.status,
271
+ statusText: response.statusText,
272
+ timestamp: new Date().toISOString(),
273
+ });
274
+ }
275
+
276
+ return response;
277
+ } catch (error) {
278
+ this.networkErrors.push({
279
+ type: "network",
280
+ url,
281
+ method,
282
+ status: null,
283
+ statusText: error?.message || "Network request failed",
284
+ timestamp: new Date().toISOString(),
285
+ });
286
+
287
+ throw error;
288
+ }
289
+ };
290
+ },
291
+
292
+ loadHtml2Canvas() {
293
+ return new Promise((resolve, reject) => {
294
+ if (window.html2canvas) {
295
+ resolve(window.html2canvas);
296
+ return;
297
+ }
298
+
299
+ const script = document.createElement("script");
300
+ script.src = "https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js";
301
+
302
+ script.onload = () => resolve(window.html2canvas);
303
+ script.onerror = () => reject(new Error("BugBlitz: failed to load html2canvas"));
304
+
305
+ document.head.appendChild(script);
306
+ });
307
+ },
308
+
309
+ async captureScreenshot() {
310
+ try {
311
+ const html2canvas = await this.loadHtml2Canvas();
312
+
313
+ const popup = document.getElementById("bugblitz-popup");
314
+ const overlay = document.getElementById("bugblitz-overlay");
315
+ if (popup) popup.style.display = "none";
316
+ if (overlay) overlay.style.display = "none";
317
+
318
+ const canvas = await html2canvas(document.body);
319
+
320
+ if (popup) popup.style.display = "block";
321
+ if (overlay) overlay.style.display = "block";
322
+
323
+ return new Promise((resolve) => {
324
+ canvas.toBlob((blob) => resolve(blob), "image/png");
325
+ });
326
+
327
+ } catch (err) {
328
+ console.error("BugBlitz: screenshot failed", err);
329
+ return null;
330
+ }
331
+ },
332
+
333
+ };
334
+
335
+ if (typeof window !== "undefined") {
336
+ window.BugBlitz = BugBlitz;
337
+ }
338
+
339
+ return BugBlitz;
340
+
341
+ })();
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+
2
+
3
+ {
4
+ "name": "@rithuu/bugblitz-sdk",
5
+ "version": "1.0.0",
6
+ "description": "Embeddable bug-reporting SDK for any website",
7
+ "type": "module",
8
+ "main": "dist/bugblitz.cjs.js",
9
+ "module": "dist/bugblitz.esm.js",
10
+ "unpkg": "dist/bugblitz.js",
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "build": "rollup -c",
16
+ "test": "echo \"Error: no test specified\" && exit 1"
17
+ },
18
+ "keywords": [
19
+ "bug-reporting",
20
+ "sdk",
21
+ "feedback",
22
+ "widget"
23
+ ],
24
+ "author": "",
25
+ "license": "ISC",
26
+ "devDependencies": {
27
+ "rollup": "^4.62.2"
28
+ }
29
+ }