@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 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 and network requests
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 and network requests
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 and network requests
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 and network requests
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
- return [...this.navigationHistory];
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
- return [...this.navigationHistory];
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbearai/core",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "description": "Core utilities and types for BugBear QA platform",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",