@bbearai/core 0.2.10 → 0.2.12
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/dist/index.d.mts +35 -4
- package/dist/index.d.ts +35 -4
- package/dist/index.js +268 -169
- package/dist/index.mjs +268 -169
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -39,6 +39,8 @@ interface NetworkRequest {
|
|
|
39
39
|
duration?: number;
|
|
40
40
|
timestamp: string;
|
|
41
41
|
error?: string;
|
|
42
|
+
/** Truncated response body for failed requests (4xx/5xx) */
|
|
43
|
+
responseBody?: string;
|
|
42
44
|
}
|
|
43
45
|
/** Enhanced bug context for detailed debugging */
|
|
44
46
|
interface EnhancedBugContext {
|
|
@@ -46,6 +48,8 @@ interface EnhancedBugContext {
|
|
|
46
48
|
consoleLogs?: ConsoleLogEntry[];
|
|
47
49
|
/** Recent network requests (last 20) */
|
|
48
50
|
networkRequests?: NetworkRequest[];
|
|
51
|
+
/** Auto-captured navigation history (last 20 route changes) */
|
|
52
|
+
navigationHistory?: string[];
|
|
49
53
|
/** Current Redux/state snapshot (serialized) */
|
|
50
54
|
stateSnapshot?: Record<string, unknown>;
|
|
51
55
|
/** Performance metrics */
|
|
@@ -116,6 +120,8 @@ interface HostUserInfo {
|
|
|
116
120
|
id: string;
|
|
117
121
|
/** User's email address */
|
|
118
122
|
email: string;
|
|
123
|
+
/** User's display name (optional, used for reporter identity) */
|
|
124
|
+
name?: string;
|
|
119
125
|
}
|
|
120
126
|
interface BugBearConfig {
|
|
121
127
|
/** Your BugBear project ID */
|
|
@@ -600,13 +606,20 @@ declare class BugBearClient {
|
|
|
600
606
|
private navigationHistory;
|
|
601
607
|
constructor(config: BugBearConfig);
|
|
602
608
|
/**
|
|
603
|
-
* Track navigation for context
|
|
609
|
+
* Track navigation for context.
|
|
610
|
+
* Also forwards to contextCapture for auto-tracked navigation.
|
|
604
611
|
*/
|
|
605
612
|
trackNavigation(route: string): void;
|
|
606
613
|
/**
|
|
607
|
-
* Get current navigation history
|
|
614
|
+
* Get current navigation history.
|
|
615
|
+
* Priority: config callback > manual tracking > auto-captured (pushState/popstate)
|
|
608
616
|
*/
|
|
609
617
|
getNavigationHistory(): string[];
|
|
618
|
+
/**
|
|
619
|
+
* Get current app context.
|
|
620
|
+
* Uses config.getAppContext() callback if provided, otherwise builds from available data.
|
|
621
|
+
*/
|
|
622
|
+
getAppContext(): AppContext;
|
|
610
623
|
/**
|
|
611
624
|
* Get current user info from host app or BugBear's own auth
|
|
612
625
|
*/
|
|
@@ -890,16 +903,21 @@ declare function createBugBear(config: BugBearConfig): BugBearClient;
|
|
|
890
903
|
*/
|
|
891
904
|
|
|
892
905
|
/**
|
|
893
|
-
* Context capture singleton that captures console logs
|
|
906
|
+
* Context capture singleton that captures console logs, network requests,
|
|
907
|
+
* and navigation history automatically.
|
|
894
908
|
*/
|
|
895
909
|
declare class ContextCaptureManager {
|
|
896
910
|
private consoleLogs;
|
|
897
911
|
private networkRequests;
|
|
912
|
+
private navigationHistory;
|
|
898
913
|
private originalConsole;
|
|
899
914
|
private originalFetch?;
|
|
915
|
+
private originalPushState?;
|
|
916
|
+
private originalReplaceState?;
|
|
917
|
+
private popstateHandler?;
|
|
900
918
|
private isCapturing;
|
|
901
919
|
/**
|
|
902
|
-
* Start capturing console logs
|
|
920
|
+
* Start capturing console logs, network requests, and navigation
|
|
903
921
|
*/
|
|
904
922
|
startCapture(): void;
|
|
905
923
|
/**
|
|
@@ -910,6 +928,18 @@ declare class ContextCaptureManager {
|
|
|
910
928
|
* Get captured context for a bug report
|
|
911
929
|
*/
|
|
912
930
|
getEnhancedContext(): EnhancedBugContext;
|
|
931
|
+
/**
|
|
932
|
+
* Get the auto-captured navigation history
|
|
933
|
+
*/
|
|
934
|
+
getNavigationHistory(): string[];
|
|
935
|
+
/**
|
|
936
|
+
* Get the current route (last entry in navigation history, or window.location)
|
|
937
|
+
*/
|
|
938
|
+
getCurrentRoute(): string;
|
|
939
|
+
/**
|
|
940
|
+
* Manually track a navigation event (for React Native or custom routing)
|
|
941
|
+
*/
|
|
942
|
+
trackNavigation(route: string): void;
|
|
913
943
|
/**
|
|
914
944
|
* Clear captured data
|
|
915
945
|
*/
|
|
@@ -924,6 +954,7 @@ declare class ContextCaptureManager {
|
|
|
924
954
|
addNetworkRequest(request: NetworkRequest): void;
|
|
925
955
|
private captureConsole;
|
|
926
956
|
private captureFetch;
|
|
957
|
+
private captureNavigation;
|
|
927
958
|
private getPerformanceMetrics;
|
|
928
959
|
private getEnvironmentInfo;
|
|
929
960
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ interface NetworkRequest {
|
|
|
39
39
|
duration?: number;
|
|
40
40
|
timestamp: string;
|
|
41
41
|
error?: string;
|
|
42
|
+
/** Truncated response body for failed requests (4xx/5xx) */
|
|
43
|
+
responseBody?: string;
|
|
42
44
|
}
|
|
43
45
|
/** Enhanced bug context for detailed debugging */
|
|
44
46
|
interface EnhancedBugContext {
|
|
@@ -46,6 +48,8 @@ interface EnhancedBugContext {
|
|
|
46
48
|
consoleLogs?: ConsoleLogEntry[];
|
|
47
49
|
/** Recent network requests (last 20) */
|
|
48
50
|
networkRequests?: NetworkRequest[];
|
|
51
|
+
/** Auto-captured navigation history (last 20 route changes) */
|
|
52
|
+
navigationHistory?: string[];
|
|
49
53
|
/** Current Redux/state snapshot (serialized) */
|
|
50
54
|
stateSnapshot?: Record<string, unknown>;
|
|
51
55
|
/** Performance metrics */
|
|
@@ -116,6 +120,8 @@ interface HostUserInfo {
|
|
|
116
120
|
id: string;
|
|
117
121
|
/** User's email address */
|
|
118
122
|
email: string;
|
|
123
|
+
/** User's display name (optional, used for reporter identity) */
|
|
124
|
+
name?: string;
|
|
119
125
|
}
|
|
120
126
|
interface BugBearConfig {
|
|
121
127
|
/** Your BugBear project ID */
|
|
@@ -600,13 +606,20 @@ declare class BugBearClient {
|
|
|
600
606
|
private navigationHistory;
|
|
601
607
|
constructor(config: BugBearConfig);
|
|
602
608
|
/**
|
|
603
|
-
* Track navigation for context
|
|
609
|
+
* Track navigation for context.
|
|
610
|
+
* Also forwards to contextCapture for auto-tracked navigation.
|
|
604
611
|
*/
|
|
605
612
|
trackNavigation(route: string): void;
|
|
606
613
|
/**
|
|
607
|
-
* Get current navigation history
|
|
614
|
+
* Get current navigation history.
|
|
615
|
+
* Priority: config callback > manual tracking > auto-captured (pushState/popstate)
|
|
608
616
|
*/
|
|
609
617
|
getNavigationHistory(): string[];
|
|
618
|
+
/**
|
|
619
|
+
* Get current app context.
|
|
620
|
+
* Uses config.getAppContext() callback if provided, otherwise builds from available data.
|
|
621
|
+
*/
|
|
622
|
+
getAppContext(): AppContext;
|
|
610
623
|
/**
|
|
611
624
|
* Get current user info from host app or BugBear's own auth
|
|
612
625
|
*/
|
|
@@ -890,16 +903,21 @@ declare function createBugBear(config: BugBearConfig): BugBearClient;
|
|
|
890
903
|
*/
|
|
891
904
|
|
|
892
905
|
/**
|
|
893
|
-
* Context capture singleton that captures console logs
|
|
906
|
+
* Context capture singleton that captures console logs, network requests,
|
|
907
|
+
* and navigation history automatically.
|
|
894
908
|
*/
|
|
895
909
|
declare class ContextCaptureManager {
|
|
896
910
|
private consoleLogs;
|
|
897
911
|
private networkRequests;
|
|
912
|
+
private navigationHistory;
|
|
898
913
|
private originalConsole;
|
|
899
914
|
private originalFetch?;
|
|
915
|
+
private originalPushState?;
|
|
916
|
+
private originalReplaceState?;
|
|
917
|
+
private popstateHandler?;
|
|
900
918
|
private isCapturing;
|
|
901
919
|
/**
|
|
902
|
-
* Start capturing console logs
|
|
920
|
+
* Start capturing console logs, network requests, and navigation
|
|
903
921
|
*/
|
|
904
922
|
startCapture(): void;
|
|
905
923
|
/**
|
|
@@ -910,6 +928,18 @@ declare class ContextCaptureManager {
|
|
|
910
928
|
* Get captured context for a bug report
|
|
911
929
|
*/
|
|
912
930
|
getEnhancedContext(): EnhancedBugContext;
|
|
931
|
+
/**
|
|
932
|
+
* Get the auto-captured navigation history
|
|
933
|
+
*/
|
|
934
|
+
getNavigationHistory(): string[];
|
|
935
|
+
/**
|
|
936
|
+
* Get the current route (last entry in navigation history, or window.location)
|
|
937
|
+
*/
|
|
938
|
+
getCurrentRoute(): string;
|
|
939
|
+
/**
|
|
940
|
+
* Manually track a navigation event (for React Native or custom routing)
|
|
941
|
+
*/
|
|
942
|
+
trackNavigation(route: string): void;
|
|
913
943
|
/**
|
|
914
944
|
* Clear captured data
|
|
915
945
|
*/
|
|
@@ -924,6 +954,7 @@ declare class ContextCaptureManager {
|
|
|
924
954
|
addNetworkRequest(request: NetworkRequest): void;
|
|
925
955
|
private captureConsole;
|
|
926
956
|
private captureFetch;
|
|
957
|
+
private captureNavigation;
|
|
927
958
|
private getPerformanceMetrics;
|
|
928
959
|
private getEnvironmentInfo;
|
|
929
960
|
}
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,250 @@ module.exports = __toCommonJS(index_exports);
|
|
|
29
29
|
|
|
30
30
|
// src/client.ts
|
|
31
31
|
var import_supabase_js = require("@supabase/supabase-js");
|
|
32
|
+
|
|
33
|
+
// src/capture.ts
|
|
34
|
+
var MAX_CONSOLE_LOGS = 50;
|
|
35
|
+
var MAX_NETWORK_REQUESTS = 20;
|
|
36
|
+
var MAX_NAVIGATION_HISTORY = 20;
|
|
37
|
+
var MAX_RESPONSE_BODY_LENGTH = 500;
|
|
38
|
+
var ContextCaptureManager = class {
|
|
39
|
+
constructor() {
|
|
40
|
+
this.consoleLogs = [];
|
|
41
|
+
this.networkRequests = [];
|
|
42
|
+
this.navigationHistory = [];
|
|
43
|
+
this.originalConsole = {};
|
|
44
|
+
this.isCapturing = false;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Start capturing console logs, network requests, and navigation
|
|
48
|
+
*/
|
|
49
|
+
startCapture() {
|
|
50
|
+
if (this.isCapturing) return;
|
|
51
|
+
this.isCapturing = true;
|
|
52
|
+
this.captureConsole();
|
|
53
|
+
this.captureFetch();
|
|
54
|
+
this.captureNavigation();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Stop capturing and restore original functions
|
|
58
|
+
*/
|
|
59
|
+
stopCapture() {
|
|
60
|
+
if (!this.isCapturing) return;
|
|
61
|
+
this.isCapturing = false;
|
|
62
|
+
if (this.originalConsole.log) console.log = this.originalConsole.log;
|
|
63
|
+
if (this.originalConsole.warn) console.warn = this.originalConsole.warn;
|
|
64
|
+
if (this.originalConsole.error) console.error = this.originalConsole.error;
|
|
65
|
+
if (this.originalConsole.info) console.info = this.originalConsole.info;
|
|
66
|
+
if (this.originalFetch && typeof window !== "undefined") {
|
|
67
|
+
window.fetch = this.originalFetch;
|
|
68
|
+
}
|
|
69
|
+
if (typeof window !== "undefined" && typeof history !== "undefined") {
|
|
70
|
+
if (this.originalPushState) {
|
|
71
|
+
history.pushState = this.originalPushState;
|
|
72
|
+
}
|
|
73
|
+
if (this.originalReplaceState) {
|
|
74
|
+
history.replaceState = this.originalReplaceState;
|
|
75
|
+
}
|
|
76
|
+
if (this.popstateHandler) {
|
|
77
|
+
window.removeEventListener("popstate", this.popstateHandler);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get captured context for a bug report
|
|
83
|
+
*/
|
|
84
|
+
getEnhancedContext() {
|
|
85
|
+
return {
|
|
86
|
+
consoleLogs: [...this.consoleLogs],
|
|
87
|
+
networkRequests: [...this.networkRequests],
|
|
88
|
+
navigationHistory: [...this.navigationHistory],
|
|
89
|
+
performanceMetrics: this.getPerformanceMetrics(),
|
|
90
|
+
environment: this.getEnvironmentInfo()
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get the auto-captured navigation history
|
|
95
|
+
*/
|
|
96
|
+
getNavigationHistory() {
|
|
97
|
+
return [...this.navigationHistory];
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get the current route (last entry in navigation history, or window.location)
|
|
101
|
+
*/
|
|
102
|
+
getCurrentRoute() {
|
|
103
|
+
if (this.navigationHistory.length > 0) {
|
|
104
|
+
return this.navigationHistory[this.navigationHistory.length - 1];
|
|
105
|
+
}
|
|
106
|
+
if (typeof window !== "undefined") {
|
|
107
|
+
return window.location.pathname;
|
|
108
|
+
}
|
|
109
|
+
return "unknown";
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Manually track a navigation event (for React Native or custom routing)
|
|
113
|
+
*/
|
|
114
|
+
trackNavigation(route) {
|
|
115
|
+
const last = this.navigationHistory[this.navigationHistory.length - 1];
|
|
116
|
+
if (route === last) return;
|
|
117
|
+
this.navigationHistory.push(route);
|
|
118
|
+
if (this.navigationHistory.length > MAX_NAVIGATION_HISTORY) {
|
|
119
|
+
this.navigationHistory.shift();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Clear captured data
|
|
124
|
+
*/
|
|
125
|
+
clear() {
|
|
126
|
+
this.consoleLogs = [];
|
|
127
|
+
this.networkRequests = [];
|
|
128
|
+
this.navigationHistory = [];
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Add a log entry manually (for custom logging)
|
|
132
|
+
*/
|
|
133
|
+
addLog(level, message, args) {
|
|
134
|
+
this.consoleLogs.push({
|
|
135
|
+
level,
|
|
136
|
+
message,
|
|
137
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
138
|
+
args
|
|
139
|
+
});
|
|
140
|
+
if (this.consoleLogs.length > MAX_CONSOLE_LOGS) {
|
|
141
|
+
this.consoleLogs = this.consoleLogs.slice(-MAX_CONSOLE_LOGS);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Add a network request manually
|
|
146
|
+
*/
|
|
147
|
+
addNetworkRequest(request) {
|
|
148
|
+
this.networkRequests.push(request);
|
|
149
|
+
if (this.networkRequests.length > MAX_NETWORK_REQUESTS) {
|
|
150
|
+
this.networkRequests = this.networkRequests.slice(-MAX_NETWORK_REQUESTS);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
captureConsole() {
|
|
154
|
+
if (typeof console === "undefined") return;
|
|
155
|
+
const levels = ["log", "warn", "error", "info"];
|
|
156
|
+
levels.forEach((level) => {
|
|
157
|
+
this.originalConsole[level] = console[level];
|
|
158
|
+
console[level] = (...args) => {
|
|
159
|
+
this.originalConsole[level]?.apply(console, args);
|
|
160
|
+
try {
|
|
161
|
+
const message = args.map((arg) => {
|
|
162
|
+
if (typeof arg === "string") return arg;
|
|
163
|
+
try {
|
|
164
|
+
return JSON.stringify(arg);
|
|
165
|
+
} catch {
|
|
166
|
+
return String(arg);
|
|
167
|
+
}
|
|
168
|
+
}).join(" ");
|
|
169
|
+
this.addLog(level, message.slice(0, 500));
|
|
170
|
+
} catch {
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
captureFetch() {
|
|
176
|
+
if (typeof window === "undefined" || typeof fetch === "undefined") return;
|
|
177
|
+
this.originalFetch = window.fetch;
|
|
178
|
+
const self = this;
|
|
179
|
+
window.fetch = async function(input, init) {
|
|
180
|
+
const startTime = Date.now();
|
|
181
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
182
|
+
const method = init?.method || "GET";
|
|
183
|
+
try {
|
|
184
|
+
const response = await self.originalFetch.call(window, input, init);
|
|
185
|
+
const requestEntry = {
|
|
186
|
+
method,
|
|
187
|
+
url: url.slice(0, 200),
|
|
188
|
+
// Limit URL length
|
|
189
|
+
status: response.status,
|
|
190
|
+
duration: Date.now() - startTime,
|
|
191
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
192
|
+
};
|
|
193
|
+
if (response.status >= 400) {
|
|
194
|
+
try {
|
|
195
|
+
const cloned = response.clone();
|
|
196
|
+
const body = await cloned.text();
|
|
197
|
+
if (body) {
|
|
198
|
+
requestEntry.responseBody = body.slice(0, MAX_RESPONSE_BODY_LENGTH);
|
|
199
|
+
}
|
|
200
|
+
} catch {
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
self.addNetworkRequest(requestEntry);
|
|
204
|
+
return response;
|
|
205
|
+
} catch (error) {
|
|
206
|
+
self.addNetworkRequest({
|
|
207
|
+
method,
|
|
208
|
+
url: url.slice(0, 200),
|
|
209
|
+
duration: Date.now() - startTime,
|
|
210
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
211
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
212
|
+
});
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
captureNavigation() {
|
|
218
|
+
if (typeof window === "undefined" || typeof history === "undefined") return;
|
|
219
|
+
this.trackNavigation(window.location.pathname);
|
|
220
|
+
const self = this;
|
|
221
|
+
this.originalPushState = history.pushState;
|
|
222
|
+
history.pushState = function(...args) {
|
|
223
|
+
self.originalPushState.apply(history, args);
|
|
224
|
+
self.trackNavigation(window.location.pathname);
|
|
225
|
+
};
|
|
226
|
+
this.originalReplaceState = history.replaceState;
|
|
227
|
+
history.replaceState = function(...args) {
|
|
228
|
+
self.originalReplaceState.apply(history, args);
|
|
229
|
+
self.trackNavigation(window.location.pathname);
|
|
230
|
+
};
|
|
231
|
+
this.popstateHandler = () => {
|
|
232
|
+
self.trackNavigation(window.location.pathname);
|
|
233
|
+
};
|
|
234
|
+
window.addEventListener("popstate", this.popstateHandler);
|
|
235
|
+
}
|
|
236
|
+
getPerformanceMetrics() {
|
|
237
|
+
if (typeof window === "undefined" || typeof performance === "undefined") return void 0;
|
|
238
|
+
const metrics = {};
|
|
239
|
+
try {
|
|
240
|
+
const navigation = performance.getEntriesByType("navigation")[0];
|
|
241
|
+
if (navigation) {
|
|
242
|
+
metrics.pageLoadTime = Math.round(navigation.loadEventEnd - navigation.startTime);
|
|
243
|
+
}
|
|
244
|
+
} catch {
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
const memory = performance.memory;
|
|
248
|
+
if (memory) {
|
|
249
|
+
metrics.memoryUsage = Math.round(memory.usedJSHeapSize / 1024 / 1024);
|
|
250
|
+
}
|
|
251
|
+
} catch {
|
|
252
|
+
}
|
|
253
|
+
return Object.keys(metrics).length > 0 ? metrics : void 0;
|
|
254
|
+
}
|
|
255
|
+
getEnvironmentInfo() {
|
|
256
|
+
if (typeof window === "undefined" || typeof navigator === "undefined") return void 0;
|
|
257
|
+
return {
|
|
258
|
+
language: navigator.language,
|
|
259
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
260
|
+
cookiesEnabled: navigator.cookieEnabled,
|
|
261
|
+
localStorage: typeof localStorage !== "undefined",
|
|
262
|
+
online: navigator.onLine
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
var contextCapture = new ContextCaptureManager();
|
|
267
|
+
function captureError(error, errorInfo) {
|
|
268
|
+
return {
|
|
269
|
+
errorMessage: error.message,
|
|
270
|
+
errorStack: error.stack,
|
|
271
|
+
componentStack: errorInfo?.componentStack
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/client.ts
|
|
32
276
|
var DEFAULT_SUPABASE_URL = "https://kyxgzjnqgvapvlnvqawz.supabase.co";
|
|
33
277
|
var getEnvVar = (key) => {
|
|
34
278
|
try {
|
|
@@ -50,22 +294,40 @@ var BugBearClient = class {
|
|
|
50
294
|
);
|
|
51
295
|
}
|
|
52
296
|
/**
|
|
53
|
-
* Track navigation for context
|
|
297
|
+
* Track navigation for context.
|
|
298
|
+
* Also forwards to contextCapture for auto-tracked navigation.
|
|
54
299
|
*/
|
|
55
300
|
trackNavigation(route) {
|
|
56
301
|
this.navigationHistory.push(route);
|
|
57
302
|
if (this.navigationHistory.length > 10) {
|
|
58
303
|
this.navigationHistory.shift();
|
|
59
304
|
}
|
|
305
|
+
contextCapture.trackNavigation(route);
|
|
60
306
|
}
|
|
61
307
|
/**
|
|
62
|
-
* Get current navigation history
|
|
308
|
+
* Get current navigation history.
|
|
309
|
+
* Priority: config callback > manual tracking > auto-captured (pushState/popstate)
|
|
63
310
|
*/
|
|
64
311
|
getNavigationHistory() {
|
|
65
312
|
if (this.config.getNavigationHistory) {
|
|
66
313
|
return this.config.getNavigationHistory();
|
|
67
314
|
}
|
|
68
|
-
|
|
315
|
+
if (this.navigationHistory.length > 0) {
|
|
316
|
+
return [...this.navigationHistory];
|
|
317
|
+
}
|
|
318
|
+
return contextCapture.getNavigationHistory();
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Get current app context.
|
|
322
|
+
* Uses config.getAppContext() callback if provided, otherwise builds from available data.
|
|
323
|
+
*/
|
|
324
|
+
getAppContext() {
|
|
325
|
+
if (this.config.getAppContext) {
|
|
326
|
+
return this.config.getAppContext();
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
currentRoute: contextCapture.getCurrentRoute()
|
|
330
|
+
};
|
|
69
331
|
}
|
|
70
332
|
/**
|
|
71
333
|
* Get current user info from host app or BugBear's own auth
|
|
@@ -105,6 +367,8 @@ var BugBearClient = class {
|
|
|
105
367
|
project_id: this.config.projectId,
|
|
106
368
|
reporter_id: userInfo.id,
|
|
107
369
|
// User ID from host app (required)
|
|
370
|
+
reporter_name: testerInfo?.name || userInfo.name || null,
|
|
371
|
+
reporter_email: userInfo.email || null,
|
|
108
372
|
tester_id: testerInfo?.id || null,
|
|
109
373
|
// Tester record ID (optional)
|
|
110
374
|
report_type: report.type,
|
|
@@ -118,6 +382,7 @@ var BugBearClient = class {
|
|
|
118
382
|
app_context: report.appContext,
|
|
119
383
|
device_info: report.deviceInfo || this.getDeviceInfo(),
|
|
120
384
|
navigation_history: this.getNavigationHistory(),
|
|
385
|
+
enhanced_context: report.enhancedContext || contextCapture.getEnhancedContext(),
|
|
121
386
|
assignment_id: report.assignmentId,
|
|
122
387
|
test_case_id: report.testCaseId
|
|
123
388
|
};
|
|
@@ -1351,172 +1616,6 @@ var BugBearClient = class {
|
|
|
1351
1616
|
function createBugBear(config) {
|
|
1352
1617
|
return new BugBearClient(config);
|
|
1353
1618
|
}
|
|
1354
|
-
|
|
1355
|
-
// src/capture.ts
|
|
1356
|
-
var MAX_CONSOLE_LOGS = 50;
|
|
1357
|
-
var MAX_NETWORK_REQUESTS = 20;
|
|
1358
|
-
var ContextCaptureManager = class {
|
|
1359
|
-
constructor() {
|
|
1360
|
-
this.consoleLogs = [];
|
|
1361
|
-
this.networkRequests = [];
|
|
1362
|
-
this.originalConsole = {};
|
|
1363
|
-
this.isCapturing = false;
|
|
1364
|
-
}
|
|
1365
|
-
/**
|
|
1366
|
-
* Start capturing console logs and network requests
|
|
1367
|
-
*/
|
|
1368
|
-
startCapture() {
|
|
1369
|
-
if (this.isCapturing) return;
|
|
1370
|
-
this.isCapturing = true;
|
|
1371
|
-
this.captureConsole();
|
|
1372
|
-
this.captureFetch();
|
|
1373
|
-
}
|
|
1374
|
-
/**
|
|
1375
|
-
* Stop capturing and restore original functions
|
|
1376
|
-
*/
|
|
1377
|
-
stopCapture() {
|
|
1378
|
-
if (!this.isCapturing) return;
|
|
1379
|
-
this.isCapturing = false;
|
|
1380
|
-
if (this.originalConsole.log) console.log = this.originalConsole.log;
|
|
1381
|
-
if (this.originalConsole.warn) console.warn = this.originalConsole.warn;
|
|
1382
|
-
if (this.originalConsole.error) console.error = this.originalConsole.error;
|
|
1383
|
-
if (this.originalConsole.info) console.info = this.originalConsole.info;
|
|
1384
|
-
if (this.originalFetch && typeof window !== "undefined") {
|
|
1385
|
-
window.fetch = this.originalFetch;
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
/**
|
|
1389
|
-
* Get captured context for a bug report
|
|
1390
|
-
*/
|
|
1391
|
-
getEnhancedContext() {
|
|
1392
|
-
return {
|
|
1393
|
-
consoleLogs: [...this.consoleLogs],
|
|
1394
|
-
networkRequests: [...this.networkRequests],
|
|
1395
|
-
performanceMetrics: this.getPerformanceMetrics(),
|
|
1396
|
-
environment: this.getEnvironmentInfo()
|
|
1397
|
-
};
|
|
1398
|
-
}
|
|
1399
|
-
/**
|
|
1400
|
-
* Clear captured data
|
|
1401
|
-
*/
|
|
1402
|
-
clear() {
|
|
1403
|
-
this.consoleLogs = [];
|
|
1404
|
-
this.networkRequests = [];
|
|
1405
|
-
}
|
|
1406
|
-
/**
|
|
1407
|
-
* Add a log entry manually (for custom logging)
|
|
1408
|
-
*/
|
|
1409
|
-
addLog(level, message, args) {
|
|
1410
|
-
this.consoleLogs.push({
|
|
1411
|
-
level,
|
|
1412
|
-
message,
|
|
1413
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1414
|
-
args
|
|
1415
|
-
});
|
|
1416
|
-
if (this.consoleLogs.length > MAX_CONSOLE_LOGS) {
|
|
1417
|
-
this.consoleLogs = this.consoleLogs.slice(-MAX_CONSOLE_LOGS);
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
/**
|
|
1421
|
-
* Add a network request manually
|
|
1422
|
-
*/
|
|
1423
|
-
addNetworkRequest(request) {
|
|
1424
|
-
this.networkRequests.push(request);
|
|
1425
|
-
if (this.networkRequests.length > MAX_NETWORK_REQUESTS) {
|
|
1426
|
-
this.networkRequests = this.networkRequests.slice(-MAX_NETWORK_REQUESTS);
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
captureConsole() {
|
|
1430
|
-
if (typeof console === "undefined") return;
|
|
1431
|
-
const levels = ["log", "warn", "error", "info"];
|
|
1432
|
-
levels.forEach((level) => {
|
|
1433
|
-
this.originalConsole[level] = console[level];
|
|
1434
|
-
console[level] = (...args) => {
|
|
1435
|
-
this.originalConsole[level]?.apply(console, args);
|
|
1436
|
-
try {
|
|
1437
|
-
const message = args.map((arg) => {
|
|
1438
|
-
if (typeof arg === "string") return arg;
|
|
1439
|
-
try {
|
|
1440
|
-
return JSON.stringify(arg);
|
|
1441
|
-
} catch {
|
|
1442
|
-
return String(arg);
|
|
1443
|
-
}
|
|
1444
|
-
}).join(" ");
|
|
1445
|
-
this.addLog(level, message.slice(0, 500));
|
|
1446
|
-
} catch {
|
|
1447
|
-
}
|
|
1448
|
-
};
|
|
1449
|
-
});
|
|
1450
|
-
}
|
|
1451
|
-
captureFetch() {
|
|
1452
|
-
if (typeof window === "undefined" || typeof fetch === "undefined") return;
|
|
1453
|
-
this.originalFetch = window.fetch;
|
|
1454
|
-
const self = this;
|
|
1455
|
-
window.fetch = async function(input, init) {
|
|
1456
|
-
const startTime = Date.now();
|
|
1457
|
-
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
1458
|
-
const method = init?.method || "GET";
|
|
1459
|
-
try {
|
|
1460
|
-
const response = await self.originalFetch.call(window, input, init);
|
|
1461
|
-
self.addNetworkRequest({
|
|
1462
|
-
method,
|
|
1463
|
-
url: url.slice(0, 200),
|
|
1464
|
-
// Limit URL length
|
|
1465
|
-
status: response.status,
|
|
1466
|
-
duration: Date.now() - startTime,
|
|
1467
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1468
|
-
});
|
|
1469
|
-
return response;
|
|
1470
|
-
} catch (error) {
|
|
1471
|
-
self.addNetworkRequest({
|
|
1472
|
-
method,
|
|
1473
|
-
url: url.slice(0, 200),
|
|
1474
|
-
duration: Date.now() - startTime,
|
|
1475
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1476
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1477
|
-
});
|
|
1478
|
-
throw error;
|
|
1479
|
-
}
|
|
1480
|
-
};
|
|
1481
|
-
}
|
|
1482
|
-
getPerformanceMetrics() {
|
|
1483
|
-
if (typeof window === "undefined" || typeof performance === "undefined") return void 0;
|
|
1484
|
-
const metrics = {};
|
|
1485
|
-
try {
|
|
1486
|
-
const navigation = performance.getEntriesByType("navigation")[0];
|
|
1487
|
-
if (navigation) {
|
|
1488
|
-
metrics.pageLoadTime = Math.round(navigation.loadEventEnd - navigation.startTime);
|
|
1489
|
-
}
|
|
1490
|
-
} catch {
|
|
1491
|
-
}
|
|
1492
|
-
try {
|
|
1493
|
-
const memory = performance.memory;
|
|
1494
|
-
if (memory) {
|
|
1495
|
-
metrics.memoryUsage = Math.round(memory.usedJSHeapSize / 1024 / 1024);
|
|
1496
|
-
}
|
|
1497
|
-
} catch {
|
|
1498
|
-
}
|
|
1499
|
-
return Object.keys(metrics).length > 0 ? metrics : void 0;
|
|
1500
|
-
}
|
|
1501
|
-
getEnvironmentInfo() {
|
|
1502
|
-
if (typeof window === "undefined" || typeof navigator === "undefined") return void 0;
|
|
1503
|
-
return {
|
|
1504
|
-
language: navigator.language,
|
|
1505
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
1506
|
-
cookiesEnabled: navigator.cookieEnabled,
|
|
1507
|
-
localStorage: typeof localStorage !== "undefined",
|
|
1508
|
-
online: navigator.onLine
|
|
1509
|
-
};
|
|
1510
|
-
}
|
|
1511
|
-
};
|
|
1512
|
-
var contextCapture = new ContextCaptureManager();
|
|
1513
|
-
function captureError(error, errorInfo) {
|
|
1514
|
-
return {
|
|
1515
|
-
errorMessage: error.message,
|
|
1516
|
-
errorStack: error.stack,
|
|
1517
|
-
componentStack: errorInfo?.componentStack
|
|
1518
|
-
};
|
|
1519
|
-
}
|
|
1520
1619
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1521
1620
|
0 && (module.exports = {
|
|
1522
1621
|
BugBearClient,
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,249 @@
|
|
|
1
1
|
// src/client.ts
|
|
2
2
|
import { createClient } from "@supabase/supabase-js";
|
|
3
|
+
|
|
4
|
+
// src/capture.ts
|
|
5
|
+
var MAX_CONSOLE_LOGS = 50;
|
|
6
|
+
var MAX_NETWORK_REQUESTS = 20;
|
|
7
|
+
var MAX_NAVIGATION_HISTORY = 20;
|
|
8
|
+
var MAX_RESPONSE_BODY_LENGTH = 500;
|
|
9
|
+
var ContextCaptureManager = class {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.consoleLogs = [];
|
|
12
|
+
this.networkRequests = [];
|
|
13
|
+
this.navigationHistory = [];
|
|
14
|
+
this.originalConsole = {};
|
|
15
|
+
this.isCapturing = false;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Start capturing console logs, network requests, and navigation
|
|
19
|
+
*/
|
|
20
|
+
startCapture() {
|
|
21
|
+
if (this.isCapturing) return;
|
|
22
|
+
this.isCapturing = true;
|
|
23
|
+
this.captureConsole();
|
|
24
|
+
this.captureFetch();
|
|
25
|
+
this.captureNavigation();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Stop capturing and restore original functions
|
|
29
|
+
*/
|
|
30
|
+
stopCapture() {
|
|
31
|
+
if (!this.isCapturing) return;
|
|
32
|
+
this.isCapturing = false;
|
|
33
|
+
if (this.originalConsole.log) console.log = this.originalConsole.log;
|
|
34
|
+
if (this.originalConsole.warn) console.warn = this.originalConsole.warn;
|
|
35
|
+
if (this.originalConsole.error) console.error = this.originalConsole.error;
|
|
36
|
+
if (this.originalConsole.info) console.info = this.originalConsole.info;
|
|
37
|
+
if (this.originalFetch && typeof window !== "undefined") {
|
|
38
|
+
window.fetch = this.originalFetch;
|
|
39
|
+
}
|
|
40
|
+
if (typeof window !== "undefined" && typeof history !== "undefined") {
|
|
41
|
+
if (this.originalPushState) {
|
|
42
|
+
history.pushState = this.originalPushState;
|
|
43
|
+
}
|
|
44
|
+
if (this.originalReplaceState) {
|
|
45
|
+
history.replaceState = this.originalReplaceState;
|
|
46
|
+
}
|
|
47
|
+
if (this.popstateHandler) {
|
|
48
|
+
window.removeEventListener("popstate", this.popstateHandler);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get captured context for a bug report
|
|
54
|
+
*/
|
|
55
|
+
getEnhancedContext() {
|
|
56
|
+
return {
|
|
57
|
+
consoleLogs: [...this.consoleLogs],
|
|
58
|
+
networkRequests: [...this.networkRequests],
|
|
59
|
+
navigationHistory: [...this.navigationHistory],
|
|
60
|
+
performanceMetrics: this.getPerformanceMetrics(),
|
|
61
|
+
environment: this.getEnvironmentInfo()
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get the auto-captured navigation history
|
|
66
|
+
*/
|
|
67
|
+
getNavigationHistory() {
|
|
68
|
+
return [...this.navigationHistory];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get the current route (last entry in navigation history, or window.location)
|
|
72
|
+
*/
|
|
73
|
+
getCurrentRoute() {
|
|
74
|
+
if (this.navigationHistory.length > 0) {
|
|
75
|
+
return this.navigationHistory[this.navigationHistory.length - 1];
|
|
76
|
+
}
|
|
77
|
+
if (typeof window !== "undefined") {
|
|
78
|
+
return window.location.pathname;
|
|
79
|
+
}
|
|
80
|
+
return "unknown";
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Manually track a navigation event (for React Native or custom routing)
|
|
84
|
+
*/
|
|
85
|
+
trackNavigation(route) {
|
|
86
|
+
const last = this.navigationHistory[this.navigationHistory.length - 1];
|
|
87
|
+
if (route === last) return;
|
|
88
|
+
this.navigationHistory.push(route);
|
|
89
|
+
if (this.navigationHistory.length > MAX_NAVIGATION_HISTORY) {
|
|
90
|
+
this.navigationHistory.shift();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Clear captured data
|
|
95
|
+
*/
|
|
96
|
+
clear() {
|
|
97
|
+
this.consoleLogs = [];
|
|
98
|
+
this.networkRequests = [];
|
|
99
|
+
this.navigationHistory = [];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Add a log entry manually (for custom logging)
|
|
103
|
+
*/
|
|
104
|
+
addLog(level, message, args) {
|
|
105
|
+
this.consoleLogs.push({
|
|
106
|
+
level,
|
|
107
|
+
message,
|
|
108
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
109
|
+
args
|
|
110
|
+
});
|
|
111
|
+
if (this.consoleLogs.length > MAX_CONSOLE_LOGS) {
|
|
112
|
+
this.consoleLogs = this.consoleLogs.slice(-MAX_CONSOLE_LOGS);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Add a network request manually
|
|
117
|
+
*/
|
|
118
|
+
addNetworkRequest(request) {
|
|
119
|
+
this.networkRequests.push(request);
|
|
120
|
+
if (this.networkRequests.length > MAX_NETWORK_REQUESTS) {
|
|
121
|
+
this.networkRequests = this.networkRequests.slice(-MAX_NETWORK_REQUESTS);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
captureConsole() {
|
|
125
|
+
if (typeof console === "undefined") return;
|
|
126
|
+
const levels = ["log", "warn", "error", "info"];
|
|
127
|
+
levels.forEach((level) => {
|
|
128
|
+
this.originalConsole[level] = console[level];
|
|
129
|
+
console[level] = (...args) => {
|
|
130
|
+
this.originalConsole[level]?.apply(console, args);
|
|
131
|
+
try {
|
|
132
|
+
const message = args.map((arg) => {
|
|
133
|
+
if (typeof arg === "string") return arg;
|
|
134
|
+
try {
|
|
135
|
+
return JSON.stringify(arg);
|
|
136
|
+
} catch {
|
|
137
|
+
return String(arg);
|
|
138
|
+
}
|
|
139
|
+
}).join(" ");
|
|
140
|
+
this.addLog(level, message.slice(0, 500));
|
|
141
|
+
} catch {
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
captureFetch() {
|
|
147
|
+
if (typeof window === "undefined" || typeof fetch === "undefined") return;
|
|
148
|
+
this.originalFetch = window.fetch;
|
|
149
|
+
const self = this;
|
|
150
|
+
window.fetch = async function(input, init) {
|
|
151
|
+
const startTime = Date.now();
|
|
152
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
153
|
+
const method = init?.method || "GET";
|
|
154
|
+
try {
|
|
155
|
+
const response = await self.originalFetch.call(window, input, init);
|
|
156
|
+
const requestEntry = {
|
|
157
|
+
method,
|
|
158
|
+
url: url.slice(0, 200),
|
|
159
|
+
// Limit URL length
|
|
160
|
+
status: response.status,
|
|
161
|
+
duration: Date.now() - startTime,
|
|
162
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
163
|
+
};
|
|
164
|
+
if (response.status >= 400) {
|
|
165
|
+
try {
|
|
166
|
+
const cloned = response.clone();
|
|
167
|
+
const body = await cloned.text();
|
|
168
|
+
if (body) {
|
|
169
|
+
requestEntry.responseBody = body.slice(0, MAX_RESPONSE_BODY_LENGTH);
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
self.addNetworkRequest(requestEntry);
|
|
175
|
+
return response;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
self.addNetworkRequest({
|
|
178
|
+
method,
|
|
179
|
+
url: url.slice(0, 200),
|
|
180
|
+
duration: Date.now() - startTime,
|
|
181
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
182
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
183
|
+
});
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
captureNavigation() {
|
|
189
|
+
if (typeof window === "undefined" || typeof history === "undefined") return;
|
|
190
|
+
this.trackNavigation(window.location.pathname);
|
|
191
|
+
const self = this;
|
|
192
|
+
this.originalPushState = history.pushState;
|
|
193
|
+
history.pushState = function(...args) {
|
|
194
|
+
self.originalPushState.apply(history, args);
|
|
195
|
+
self.trackNavigation(window.location.pathname);
|
|
196
|
+
};
|
|
197
|
+
this.originalReplaceState = history.replaceState;
|
|
198
|
+
history.replaceState = function(...args) {
|
|
199
|
+
self.originalReplaceState.apply(history, args);
|
|
200
|
+
self.trackNavigation(window.location.pathname);
|
|
201
|
+
};
|
|
202
|
+
this.popstateHandler = () => {
|
|
203
|
+
self.trackNavigation(window.location.pathname);
|
|
204
|
+
};
|
|
205
|
+
window.addEventListener("popstate", this.popstateHandler);
|
|
206
|
+
}
|
|
207
|
+
getPerformanceMetrics() {
|
|
208
|
+
if (typeof window === "undefined" || typeof performance === "undefined") return void 0;
|
|
209
|
+
const metrics = {};
|
|
210
|
+
try {
|
|
211
|
+
const navigation = performance.getEntriesByType("navigation")[0];
|
|
212
|
+
if (navigation) {
|
|
213
|
+
metrics.pageLoadTime = Math.round(navigation.loadEventEnd - navigation.startTime);
|
|
214
|
+
}
|
|
215
|
+
} catch {
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
const memory = performance.memory;
|
|
219
|
+
if (memory) {
|
|
220
|
+
metrics.memoryUsage = Math.round(memory.usedJSHeapSize / 1024 / 1024);
|
|
221
|
+
}
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
224
|
+
return Object.keys(metrics).length > 0 ? metrics : void 0;
|
|
225
|
+
}
|
|
226
|
+
getEnvironmentInfo() {
|
|
227
|
+
if (typeof window === "undefined" || typeof navigator === "undefined") return void 0;
|
|
228
|
+
return {
|
|
229
|
+
language: navigator.language,
|
|
230
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
231
|
+
cookiesEnabled: navigator.cookieEnabled,
|
|
232
|
+
localStorage: typeof localStorage !== "undefined",
|
|
233
|
+
online: navigator.onLine
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
var contextCapture = new ContextCaptureManager();
|
|
238
|
+
function captureError(error, errorInfo) {
|
|
239
|
+
return {
|
|
240
|
+
errorMessage: error.message,
|
|
241
|
+
errorStack: error.stack,
|
|
242
|
+
componentStack: errorInfo?.componentStack
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/client.ts
|
|
3
247
|
var DEFAULT_SUPABASE_URL = "https://kyxgzjnqgvapvlnvqawz.supabase.co";
|
|
4
248
|
var getEnvVar = (key) => {
|
|
5
249
|
try {
|
|
@@ -21,22 +265,40 @@ var BugBearClient = class {
|
|
|
21
265
|
);
|
|
22
266
|
}
|
|
23
267
|
/**
|
|
24
|
-
* Track navigation for context
|
|
268
|
+
* Track navigation for context.
|
|
269
|
+
* Also forwards to contextCapture for auto-tracked navigation.
|
|
25
270
|
*/
|
|
26
271
|
trackNavigation(route) {
|
|
27
272
|
this.navigationHistory.push(route);
|
|
28
273
|
if (this.navigationHistory.length > 10) {
|
|
29
274
|
this.navigationHistory.shift();
|
|
30
275
|
}
|
|
276
|
+
contextCapture.trackNavigation(route);
|
|
31
277
|
}
|
|
32
278
|
/**
|
|
33
|
-
* Get current navigation history
|
|
279
|
+
* Get current navigation history.
|
|
280
|
+
* Priority: config callback > manual tracking > auto-captured (pushState/popstate)
|
|
34
281
|
*/
|
|
35
282
|
getNavigationHistory() {
|
|
36
283
|
if (this.config.getNavigationHistory) {
|
|
37
284
|
return this.config.getNavigationHistory();
|
|
38
285
|
}
|
|
39
|
-
|
|
286
|
+
if (this.navigationHistory.length > 0) {
|
|
287
|
+
return [...this.navigationHistory];
|
|
288
|
+
}
|
|
289
|
+
return contextCapture.getNavigationHistory();
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Get current app context.
|
|
293
|
+
* Uses config.getAppContext() callback if provided, otherwise builds from available data.
|
|
294
|
+
*/
|
|
295
|
+
getAppContext() {
|
|
296
|
+
if (this.config.getAppContext) {
|
|
297
|
+
return this.config.getAppContext();
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
currentRoute: contextCapture.getCurrentRoute()
|
|
301
|
+
};
|
|
40
302
|
}
|
|
41
303
|
/**
|
|
42
304
|
* Get current user info from host app or BugBear's own auth
|
|
@@ -76,6 +338,8 @@ var BugBearClient = class {
|
|
|
76
338
|
project_id: this.config.projectId,
|
|
77
339
|
reporter_id: userInfo.id,
|
|
78
340
|
// User ID from host app (required)
|
|
341
|
+
reporter_name: testerInfo?.name || userInfo.name || null,
|
|
342
|
+
reporter_email: userInfo.email || null,
|
|
79
343
|
tester_id: testerInfo?.id || null,
|
|
80
344
|
// Tester record ID (optional)
|
|
81
345
|
report_type: report.type,
|
|
@@ -89,6 +353,7 @@ var BugBearClient = class {
|
|
|
89
353
|
app_context: report.appContext,
|
|
90
354
|
device_info: report.deviceInfo || this.getDeviceInfo(),
|
|
91
355
|
navigation_history: this.getNavigationHistory(),
|
|
356
|
+
enhanced_context: report.enhancedContext || contextCapture.getEnhancedContext(),
|
|
92
357
|
assignment_id: report.assignmentId,
|
|
93
358
|
test_case_id: report.testCaseId
|
|
94
359
|
};
|
|
@@ -1322,172 +1587,6 @@ var BugBearClient = class {
|
|
|
1322
1587
|
function createBugBear(config) {
|
|
1323
1588
|
return new BugBearClient(config);
|
|
1324
1589
|
}
|
|
1325
|
-
|
|
1326
|
-
// src/capture.ts
|
|
1327
|
-
var MAX_CONSOLE_LOGS = 50;
|
|
1328
|
-
var MAX_NETWORK_REQUESTS = 20;
|
|
1329
|
-
var ContextCaptureManager = class {
|
|
1330
|
-
constructor() {
|
|
1331
|
-
this.consoleLogs = [];
|
|
1332
|
-
this.networkRequests = [];
|
|
1333
|
-
this.originalConsole = {};
|
|
1334
|
-
this.isCapturing = false;
|
|
1335
|
-
}
|
|
1336
|
-
/**
|
|
1337
|
-
* Start capturing console logs and network requests
|
|
1338
|
-
*/
|
|
1339
|
-
startCapture() {
|
|
1340
|
-
if (this.isCapturing) return;
|
|
1341
|
-
this.isCapturing = true;
|
|
1342
|
-
this.captureConsole();
|
|
1343
|
-
this.captureFetch();
|
|
1344
|
-
}
|
|
1345
|
-
/**
|
|
1346
|
-
* Stop capturing and restore original functions
|
|
1347
|
-
*/
|
|
1348
|
-
stopCapture() {
|
|
1349
|
-
if (!this.isCapturing) return;
|
|
1350
|
-
this.isCapturing = false;
|
|
1351
|
-
if (this.originalConsole.log) console.log = this.originalConsole.log;
|
|
1352
|
-
if (this.originalConsole.warn) console.warn = this.originalConsole.warn;
|
|
1353
|
-
if (this.originalConsole.error) console.error = this.originalConsole.error;
|
|
1354
|
-
if (this.originalConsole.info) console.info = this.originalConsole.info;
|
|
1355
|
-
if (this.originalFetch && typeof window !== "undefined") {
|
|
1356
|
-
window.fetch = this.originalFetch;
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
/**
|
|
1360
|
-
* Get captured context for a bug report
|
|
1361
|
-
*/
|
|
1362
|
-
getEnhancedContext() {
|
|
1363
|
-
return {
|
|
1364
|
-
consoleLogs: [...this.consoleLogs],
|
|
1365
|
-
networkRequests: [...this.networkRequests],
|
|
1366
|
-
performanceMetrics: this.getPerformanceMetrics(),
|
|
1367
|
-
environment: this.getEnvironmentInfo()
|
|
1368
|
-
};
|
|
1369
|
-
}
|
|
1370
|
-
/**
|
|
1371
|
-
* Clear captured data
|
|
1372
|
-
*/
|
|
1373
|
-
clear() {
|
|
1374
|
-
this.consoleLogs = [];
|
|
1375
|
-
this.networkRequests = [];
|
|
1376
|
-
}
|
|
1377
|
-
/**
|
|
1378
|
-
* Add a log entry manually (for custom logging)
|
|
1379
|
-
*/
|
|
1380
|
-
addLog(level, message, args) {
|
|
1381
|
-
this.consoleLogs.push({
|
|
1382
|
-
level,
|
|
1383
|
-
message,
|
|
1384
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1385
|
-
args
|
|
1386
|
-
});
|
|
1387
|
-
if (this.consoleLogs.length > MAX_CONSOLE_LOGS) {
|
|
1388
|
-
this.consoleLogs = this.consoleLogs.slice(-MAX_CONSOLE_LOGS);
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
/**
|
|
1392
|
-
* Add a network request manually
|
|
1393
|
-
*/
|
|
1394
|
-
addNetworkRequest(request) {
|
|
1395
|
-
this.networkRequests.push(request);
|
|
1396
|
-
if (this.networkRequests.length > MAX_NETWORK_REQUESTS) {
|
|
1397
|
-
this.networkRequests = this.networkRequests.slice(-MAX_NETWORK_REQUESTS);
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
captureConsole() {
|
|
1401
|
-
if (typeof console === "undefined") return;
|
|
1402
|
-
const levels = ["log", "warn", "error", "info"];
|
|
1403
|
-
levels.forEach((level) => {
|
|
1404
|
-
this.originalConsole[level] = console[level];
|
|
1405
|
-
console[level] = (...args) => {
|
|
1406
|
-
this.originalConsole[level]?.apply(console, args);
|
|
1407
|
-
try {
|
|
1408
|
-
const message = args.map((arg) => {
|
|
1409
|
-
if (typeof arg === "string") return arg;
|
|
1410
|
-
try {
|
|
1411
|
-
return JSON.stringify(arg);
|
|
1412
|
-
} catch {
|
|
1413
|
-
return String(arg);
|
|
1414
|
-
}
|
|
1415
|
-
}).join(" ");
|
|
1416
|
-
this.addLog(level, message.slice(0, 500));
|
|
1417
|
-
} catch {
|
|
1418
|
-
}
|
|
1419
|
-
};
|
|
1420
|
-
});
|
|
1421
|
-
}
|
|
1422
|
-
captureFetch() {
|
|
1423
|
-
if (typeof window === "undefined" || typeof fetch === "undefined") return;
|
|
1424
|
-
this.originalFetch = window.fetch;
|
|
1425
|
-
const self = this;
|
|
1426
|
-
window.fetch = async function(input, init) {
|
|
1427
|
-
const startTime = Date.now();
|
|
1428
|
-
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
1429
|
-
const method = init?.method || "GET";
|
|
1430
|
-
try {
|
|
1431
|
-
const response = await self.originalFetch.call(window, input, init);
|
|
1432
|
-
self.addNetworkRequest({
|
|
1433
|
-
method,
|
|
1434
|
-
url: url.slice(0, 200),
|
|
1435
|
-
// Limit URL length
|
|
1436
|
-
status: response.status,
|
|
1437
|
-
duration: Date.now() - startTime,
|
|
1438
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1439
|
-
});
|
|
1440
|
-
return response;
|
|
1441
|
-
} catch (error) {
|
|
1442
|
-
self.addNetworkRequest({
|
|
1443
|
-
method,
|
|
1444
|
-
url: url.slice(0, 200),
|
|
1445
|
-
duration: Date.now() - startTime,
|
|
1446
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1447
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
1448
|
-
});
|
|
1449
|
-
throw error;
|
|
1450
|
-
}
|
|
1451
|
-
};
|
|
1452
|
-
}
|
|
1453
|
-
getPerformanceMetrics() {
|
|
1454
|
-
if (typeof window === "undefined" || typeof performance === "undefined") return void 0;
|
|
1455
|
-
const metrics = {};
|
|
1456
|
-
try {
|
|
1457
|
-
const navigation = performance.getEntriesByType("navigation")[0];
|
|
1458
|
-
if (navigation) {
|
|
1459
|
-
metrics.pageLoadTime = Math.round(navigation.loadEventEnd - navigation.startTime);
|
|
1460
|
-
}
|
|
1461
|
-
} catch {
|
|
1462
|
-
}
|
|
1463
|
-
try {
|
|
1464
|
-
const memory = performance.memory;
|
|
1465
|
-
if (memory) {
|
|
1466
|
-
metrics.memoryUsage = Math.round(memory.usedJSHeapSize / 1024 / 1024);
|
|
1467
|
-
}
|
|
1468
|
-
} catch {
|
|
1469
|
-
}
|
|
1470
|
-
return Object.keys(metrics).length > 0 ? metrics : void 0;
|
|
1471
|
-
}
|
|
1472
|
-
getEnvironmentInfo() {
|
|
1473
|
-
if (typeof window === "undefined" || typeof navigator === "undefined") return void 0;
|
|
1474
|
-
return {
|
|
1475
|
-
language: navigator.language,
|
|
1476
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
1477
|
-
cookiesEnabled: navigator.cookieEnabled,
|
|
1478
|
-
localStorage: typeof localStorage !== "undefined",
|
|
1479
|
-
online: navigator.onLine
|
|
1480
|
-
};
|
|
1481
|
-
}
|
|
1482
|
-
};
|
|
1483
|
-
var contextCapture = new ContextCaptureManager();
|
|
1484
|
-
function captureError(error, errorInfo) {
|
|
1485
|
-
return {
|
|
1486
|
-
errorMessage: error.message,
|
|
1487
|
-
errorStack: error.stack,
|
|
1488
|
-
componentStack: errorInfo?.componentStack
|
|
1489
|
-
};
|
|
1490
|
-
}
|
|
1491
1590
|
export {
|
|
1492
1591
|
BugBearClient,
|
|
1493
1592
|
captureError,
|