@bbearai/core 0.2.11 → 0.2.13
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/README.md +95 -8
- package/dist/index.d.mts +53 -5
- package/dist/index.d.ts +53 -5
- package/dist/index.js +124 -9
- package/dist/index.mjs +124 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Core client and utilities for BugBear QA platform.
|
|
4
4
|
|
|
5
|
-
> **Note:** Most users should install `@bbearai/react` instead, which
|
|
5
|
+
> **Note:** Most users should install `@bbearai/react` or `@bbearai/react-native` instead, which include this package automatically.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -20,6 +20,7 @@ const bugbear = createBugBear({
|
|
|
20
20
|
getCurrentUser: async () => ({
|
|
21
21
|
id: 'user-123',
|
|
22
22
|
email: 'user@example.com',
|
|
23
|
+
name: 'Jane Doe', // Optional, used for reporter identity
|
|
23
24
|
}),
|
|
24
25
|
});
|
|
25
26
|
|
|
@@ -28,6 +29,7 @@ await bugbear.submitReport({
|
|
|
28
29
|
type: 'bug',
|
|
29
30
|
description: 'Button does not work',
|
|
30
31
|
severity: 'high',
|
|
32
|
+
appContext: { currentRoute: '/dashboard' },
|
|
31
33
|
});
|
|
32
34
|
|
|
33
35
|
// Check if user is a tester
|
|
@@ -37,13 +39,84 @@ const isTester = await bugbear.isTester();
|
|
|
37
39
|
const assignments = await bugbear.getAssignedTests();
|
|
38
40
|
```
|
|
39
41
|
|
|
40
|
-
##
|
|
42
|
+
## Configuration
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
```typescript
|
|
45
|
+
interface BugBearConfig {
|
|
46
|
+
// Required
|
|
47
|
+
projectId: string;
|
|
48
|
+
getCurrentUser: () => Promise<{ id: string; email: string; name?: string } | null>;
|
|
49
|
+
|
|
50
|
+
// Optional — context
|
|
51
|
+
getAppContext?: () => AppContext; // Rich app context (userRole, etc.)
|
|
52
|
+
getNavigationHistory?: () => string[]; // Custom navigation tracking
|
|
53
|
+
|
|
54
|
+
// Optional — callbacks
|
|
55
|
+
onReportSubmitted?: (report: BugBearReport) => void;
|
|
56
|
+
onNavigate?: (route: string) => void; // Deep linking from test cases
|
|
57
|
+
|
|
58
|
+
// Optional — self-hosted
|
|
59
|
+
supabaseUrl?: string;
|
|
60
|
+
supabaseAnonKey?: string;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### AppContext
|
|
65
|
+
|
|
66
|
+
Provide `getAppContext` to attach rich context to every bug report. This helps the developer fixing the bug understand the user's state:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const bugbear = createBugBear({
|
|
70
|
+
projectId: 'your-project-id',
|
|
71
|
+
getCurrentUser: async () => ({ id: user.id, email: user.email }),
|
|
72
|
+
getAppContext: () => ({
|
|
73
|
+
currentRoute: window.location.pathname,
|
|
74
|
+
userRole: currentUser?.role, // e.g. 'owner', 'manager', 'guest'
|
|
75
|
+
propertyId: currentProperty?.id, // App-specific context
|
|
76
|
+
custom: { // Any additional context
|
|
77
|
+
theme: 'dark',
|
|
78
|
+
locale: 'en-US',
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Automatic Context Capture
|
|
85
|
+
|
|
86
|
+
BugBear automatically captures debugging context when initialized via `BugBearProvider` (React or React Native). No manual setup required.
|
|
43
87
|
|
|
44
|
-
|
|
88
|
+
**What's captured automatically:**
|
|
45
89
|
|
|
46
|
-
|
|
90
|
+
| Data | Details |
|
|
91
|
+
|------|---------|
|
|
92
|
+
| **Console logs** | Last 50 `console.log/warn/error/info` calls |
|
|
93
|
+
| **Network requests** | Last 20 `fetch()` calls with method, URL, status, duration |
|
|
94
|
+
| **Failed response bodies** | First 500 chars of response body for 4xx/5xx errors |
|
|
95
|
+
| **Navigation history** | Last 20 route changes (via `pushState`/`replaceState`/`popstate`) |
|
|
96
|
+
| **Performance metrics** | Page load time, memory usage (Chrome) |
|
|
97
|
+
| **Environment** | Language, timezone, online status, cookies, localStorage |
|
|
98
|
+
|
|
99
|
+
This data is attached to every bug report as `enhanced_context` and is available to the MCP server's `get_report_context` tool.
|
|
100
|
+
|
|
101
|
+
### Manual Capture (framework authors)
|
|
102
|
+
|
|
103
|
+
If you're not using `BugBearProvider`, start capture manually:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { contextCapture } from '@bbearai/core';
|
|
107
|
+
|
|
108
|
+
// Start capturing (call once at app init)
|
|
109
|
+
contextCapture.startCapture();
|
|
110
|
+
|
|
111
|
+
// For React Native: manually track screen changes
|
|
112
|
+
contextCapture.trackNavigation('/home');
|
|
113
|
+
contextCapture.trackNavigation('/settings');
|
|
114
|
+
|
|
115
|
+
// Get captured data (automatically included in submitReport)
|
|
116
|
+
const context = contextCapture.getEnhancedContext();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Client Methods
|
|
47
120
|
|
|
48
121
|
| Method | Description |
|
|
49
122
|
|--------|-------------|
|
|
@@ -53,7 +126,9 @@ Creates a BugBear client instance.
|
|
|
53
126
|
| `shouldShowWidget()` | Check if widget should be visible (QA enabled + is tester) |
|
|
54
127
|
| `getTesterInfo()` | Get current tester's info |
|
|
55
128
|
| `getAssignedTests()` | Get test cases assigned to current tester |
|
|
56
|
-
| `
|
|
129
|
+
| `getAppContext()` | Get current app context (uses config callback or auto-captured) |
|
|
130
|
+
| `getNavigationHistory()` | Get navigation history (config callback > manual > auto-captured) |
|
|
131
|
+
| `trackNavigation(route)` | Manually track a route change |
|
|
57
132
|
| `uploadScreenshot(file)` | Upload a screenshot |
|
|
58
133
|
|
|
59
134
|
## Types
|
|
@@ -66,7 +141,15 @@ interface BugBearReport {
|
|
|
66
141
|
severity?: 'critical' | 'high' | 'medium' | 'low';
|
|
67
142
|
screenshots?: string[];
|
|
68
143
|
appContext?: AppContext;
|
|
69
|
-
|
|
144
|
+
enhancedContext?: EnhancedBugContext;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
interface AppContext {
|
|
148
|
+
currentRoute: string;
|
|
149
|
+
screenName?: string;
|
|
150
|
+
userRole?: string;
|
|
151
|
+
propertyId?: string;
|
|
152
|
+
custom?: Record<string, unknown>;
|
|
70
153
|
}
|
|
71
154
|
|
|
72
155
|
interface TesterInfo {
|
|
@@ -96,7 +179,11 @@ interface TestAssignment {
|
|
|
96
179
|
If you're building a BugBear integration for a different framework (Vue, Svelte, etc.), use this package as the foundation:
|
|
97
180
|
|
|
98
181
|
```typescript
|
|
99
|
-
import { BugBearClient, createBugBear, BugBearConfig } from '@bbearai/core';
|
|
182
|
+
import { BugBearClient, createBugBear, contextCapture, BugBearConfig } from '@bbearai/core';
|
|
183
|
+
|
|
184
|
+
// Start context capture at app init
|
|
185
|
+
contextCapture.startCapture();
|
|
100
186
|
|
|
101
187
|
// Create your framework-specific wrapper around the client
|
|
188
|
+
const client = createBugBear(config);
|
|
102
189
|
```
|
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 */
|
|
@@ -602,13 +606,20 @@ declare class BugBearClient {
|
|
|
602
606
|
private navigationHistory;
|
|
603
607
|
constructor(config: BugBearConfig);
|
|
604
608
|
/**
|
|
605
|
-
* Track navigation for context
|
|
609
|
+
* Track navigation for context.
|
|
610
|
+
* Also forwards to contextCapture for auto-tracked navigation.
|
|
606
611
|
*/
|
|
607
612
|
trackNavigation(route: string): void;
|
|
608
613
|
/**
|
|
609
|
-
* Get current navigation history
|
|
614
|
+
* Get current navigation history.
|
|
615
|
+
* Priority: config callback > manual tracking > auto-captured (pushState/popstate)
|
|
610
616
|
*/
|
|
611
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;
|
|
612
623
|
/**
|
|
613
624
|
* Get current user info from host app or BugBear's own auth
|
|
614
625
|
*/
|
|
@@ -651,11 +662,30 @@ declare class BugBearClient {
|
|
|
651
662
|
error?: string;
|
|
652
663
|
durationSeconds?: number;
|
|
653
664
|
}>;
|
|
665
|
+
/**
|
|
666
|
+
* Pass a test assignment — convenience wrapper around updateAssignmentStatus
|
|
667
|
+
*/
|
|
668
|
+
passAssignment(assignmentId: string): Promise<{
|
|
669
|
+
success: boolean;
|
|
670
|
+
error?: string;
|
|
671
|
+
durationSeconds?: number;
|
|
672
|
+
}>;
|
|
673
|
+
/**
|
|
674
|
+
* Fail a test assignment — convenience wrapper around updateAssignmentStatus
|
|
675
|
+
*/
|
|
676
|
+
failAssignment(assignmentId: string): Promise<{
|
|
677
|
+
success: boolean;
|
|
678
|
+
error?: string;
|
|
679
|
+
durationSeconds?: number;
|
|
680
|
+
}>;
|
|
654
681
|
/**
|
|
655
682
|
* Skip a test assignment with a required reason
|
|
656
683
|
* Marks the assignment as 'skipped' and records why it was skipped
|
|
657
684
|
*/
|
|
658
|
-
skipAssignment(assignmentId: string, reason: SkipReason
|
|
685
|
+
skipAssignment(assignmentId: string, reason: SkipReason | {
|
|
686
|
+
reason: SkipReason;
|
|
687
|
+
notes?: string;
|
|
688
|
+
}, notes?: string): Promise<{
|
|
659
689
|
success: boolean;
|
|
660
690
|
error?: string;
|
|
661
691
|
}>;
|
|
@@ -892,16 +922,21 @@ declare function createBugBear(config: BugBearConfig): BugBearClient;
|
|
|
892
922
|
*/
|
|
893
923
|
|
|
894
924
|
/**
|
|
895
|
-
* Context capture singleton that captures console logs
|
|
925
|
+
* Context capture singleton that captures console logs, network requests,
|
|
926
|
+
* and navigation history automatically.
|
|
896
927
|
*/
|
|
897
928
|
declare class ContextCaptureManager {
|
|
898
929
|
private consoleLogs;
|
|
899
930
|
private networkRequests;
|
|
931
|
+
private navigationHistory;
|
|
900
932
|
private originalConsole;
|
|
901
933
|
private originalFetch?;
|
|
934
|
+
private originalPushState?;
|
|
935
|
+
private originalReplaceState?;
|
|
936
|
+
private popstateHandler?;
|
|
902
937
|
private isCapturing;
|
|
903
938
|
/**
|
|
904
|
-
* Start capturing console logs
|
|
939
|
+
* Start capturing console logs, network requests, and navigation
|
|
905
940
|
*/
|
|
906
941
|
startCapture(): void;
|
|
907
942
|
/**
|
|
@@ -912,6 +947,18 @@ declare class ContextCaptureManager {
|
|
|
912
947
|
* Get captured context for a bug report
|
|
913
948
|
*/
|
|
914
949
|
getEnhancedContext(): EnhancedBugContext;
|
|
950
|
+
/**
|
|
951
|
+
* Get the auto-captured navigation history
|
|
952
|
+
*/
|
|
953
|
+
getNavigationHistory(): string[];
|
|
954
|
+
/**
|
|
955
|
+
* Get the current route (last entry in navigation history, or window.location)
|
|
956
|
+
*/
|
|
957
|
+
getCurrentRoute(): string;
|
|
958
|
+
/**
|
|
959
|
+
* Manually track a navigation event (for React Native or custom routing)
|
|
960
|
+
*/
|
|
961
|
+
trackNavigation(route: string): void;
|
|
915
962
|
/**
|
|
916
963
|
* Clear captured data
|
|
917
964
|
*/
|
|
@@ -926,6 +973,7 @@ declare class ContextCaptureManager {
|
|
|
926
973
|
addNetworkRequest(request: NetworkRequest): void;
|
|
927
974
|
private captureConsole;
|
|
928
975
|
private captureFetch;
|
|
976
|
+
private captureNavigation;
|
|
929
977
|
private getPerformanceMetrics;
|
|
930
978
|
private getEnvironmentInfo;
|
|
931
979
|
}
|
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 */
|
|
@@ -602,13 +606,20 @@ declare class BugBearClient {
|
|
|
602
606
|
private navigationHistory;
|
|
603
607
|
constructor(config: BugBearConfig);
|
|
604
608
|
/**
|
|
605
|
-
* Track navigation for context
|
|
609
|
+
* Track navigation for context.
|
|
610
|
+
* Also forwards to contextCapture for auto-tracked navigation.
|
|
606
611
|
*/
|
|
607
612
|
trackNavigation(route: string): void;
|
|
608
613
|
/**
|
|
609
|
-
* Get current navigation history
|
|
614
|
+
* Get current navigation history.
|
|
615
|
+
* Priority: config callback > manual tracking > auto-captured (pushState/popstate)
|
|
610
616
|
*/
|
|
611
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;
|
|
612
623
|
/**
|
|
613
624
|
* Get current user info from host app or BugBear's own auth
|
|
614
625
|
*/
|
|
@@ -651,11 +662,30 @@ declare class BugBearClient {
|
|
|
651
662
|
error?: string;
|
|
652
663
|
durationSeconds?: number;
|
|
653
664
|
}>;
|
|
665
|
+
/**
|
|
666
|
+
* Pass a test assignment — convenience wrapper around updateAssignmentStatus
|
|
667
|
+
*/
|
|
668
|
+
passAssignment(assignmentId: string): Promise<{
|
|
669
|
+
success: boolean;
|
|
670
|
+
error?: string;
|
|
671
|
+
durationSeconds?: number;
|
|
672
|
+
}>;
|
|
673
|
+
/**
|
|
674
|
+
* Fail a test assignment — convenience wrapper around updateAssignmentStatus
|
|
675
|
+
*/
|
|
676
|
+
failAssignment(assignmentId: string): Promise<{
|
|
677
|
+
success: boolean;
|
|
678
|
+
error?: string;
|
|
679
|
+
durationSeconds?: number;
|
|
680
|
+
}>;
|
|
654
681
|
/**
|
|
655
682
|
* Skip a test assignment with a required reason
|
|
656
683
|
* Marks the assignment as 'skipped' and records why it was skipped
|
|
657
684
|
*/
|
|
658
|
-
skipAssignment(assignmentId: string, reason: SkipReason
|
|
685
|
+
skipAssignment(assignmentId: string, reason: SkipReason | {
|
|
686
|
+
reason: SkipReason;
|
|
687
|
+
notes?: string;
|
|
688
|
+
}, notes?: string): Promise<{
|
|
659
689
|
success: boolean;
|
|
660
690
|
error?: string;
|
|
661
691
|
}>;
|
|
@@ -892,16 +922,21 @@ declare function createBugBear(config: BugBearConfig): BugBearClient;
|
|
|
892
922
|
*/
|
|
893
923
|
|
|
894
924
|
/**
|
|
895
|
-
* Context capture singleton that captures console logs
|
|
925
|
+
* Context capture singleton that captures console logs, network requests,
|
|
926
|
+
* and navigation history automatically.
|
|
896
927
|
*/
|
|
897
928
|
declare class ContextCaptureManager {
|
|
898
929
|
private consoleLogs;
|
|
899
930
|
private networkRequests;
|
|
931
|
+
private navigationHistory;
|
|
900
932
|
private originalConsole;
|
|
901
933
|
private originalFetch?;
|
|
934
|
+
private originalPushState?;
|
|
935
|
+
private originalReplaceState?;
|
|
936
|
+
private popstateHandler?;
|
|
902
937
|
private isCapturing;
|
|
903
938
|
/**
|
|
904
|
-
* Start capturing console logs
|
|
939
|
+
* Start capturing console logs, network requests, and navigation
|
|
905
940
|
*/
|
|
906
941
|
startCapture(): void;
|
|
907
942
|
/**
|
|
@@ -912,6 +947,18 @@ declare class ContextCaptureManager {
|
|
|
912
947
|
* Get captured context for a bug report
|
|
913
948
|
*/
|
|
914
949
|
getEnhancedContext(): EnhancedBugContext;
|
|
950
|
+
/**
|
|
951
|
+
* Get the auto-captured navigation history
|
|
952
|
+
*/
|
|
953
|
+
getNavigationHistory(): string[];
|
|
954
|
+
/**
|
|
955
|
+
* Get the current route (last entry in navigation history, or window.location)
|
|
956
|
+
*/
|
|
957
|
+
getCurrentRoute(): string;
|
|
958
|
+
/**
|
|
959
|
+
* Manually track a navigation event (for React Native or custom routing)
|
|
960
|
+
*/
|
|
961
|
+
trackNavigation(route: string): void;
|
|
915
962
|
/**
|
|
916
963
|
* Clear captured data
|
|
917
964
|
*/
|
|
@@ -926,6 +973,7 @@ declare class ContextCaptureManager {
|
|
|
926
973
|
addNetworkRequest(request: NetworkRequest): void;
|
|
927
974
|
private captureConsole;
|
|
928
975
|
private captureFetch;
|
|
976
|
+
private captureNavigation;
|
|
929
977
|
private getPerformanceMetrics;
|
|
930
978
|
private getEnvironmentInfo;
|
|
931
979
|
}
|
package/dist/index.js
CHANGED
|
@@ -33,21 +33,25 @@ var import_supabase_js = require("@supabase/supabase-js");
|
|
|
33
33
|
// src/capture.ts
|
|
34
34
|
var MAX_CONSOLE_LOGS = 50;
|
|
35
35
|
var MAX_NETWORK_REQUESTS = 20;
|
|
36
|
+
var MAX_NAVIGATION_HISTORY = 20;
|
|
37
|
+
var MAX_RESPONSE_BODY_LENGTH = 500;
|
|
36
38
|
var ContextCaptureManager = class {
|
|
37
39
|
constructor() {
|
|
38
40
|
this.consoleLogs = [];
|
|
39
41
|
this.networkRequests = [];
|
|
42
|
+
this.navigationHistory = [];
|
|
40
43
|
this.originalConsole = {};
|
|
41
44
|
this.isCapturing = false;
|
|
42
45
|
}
|
|
43
46
|
/**
|
|
44
|
-
* Start capturing console logs
|
|
47
|
+
* Start capturing console logs, network requests, and navigation
|
|
45
48
|
*/
|
|
46
49
|
startCapture() {
|
|
47
50
|
if (this.isCapturing) return;
|
|
48
51
|
this.isCapturing = true;
|
|
49
52
|
this.captureConsole();
|
|
50
53
|
this.captureFetch();
|
|
54
|
+
this.captureNavigation();
|
|
51
55
|
}
|
|
52
56
|
/**
|
|
53
57
|
* Stop capturing and restore original functions
|
|
@@ -62,6 +66,17 @@ var ContextCaptureManager = class {
|
|
|
62
66
|
if (this.originalFetch && typeof window !== "undefined") {
|
|
63
67
|
window.fetch = this.originalFetch;
|
|
64
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
|
+
}
|
|
65
80
|
}
|
|
66
81
|
/**
|
|
67
82
|
* Get captured context for a bug report
|
|
@@ -70,16 +85,47 @@ var ContextCaptureManager = class {
|
|
|
70
85
|
return {
|
|
71
86
|
consoleLogs: [...this.consoleLogs],
|
|
72
87
|
networkRequests: [...this.networkRequests],
|
|
88
|
+
navigationHistory: [...this.navigationHistory],
|
|
73
89
|
performanceMetrics: this.getPerformanceMetrics(),
|
|
74
90
|
environment: this.getEnvironmentInfo()
|
|
75
91
|
};
|
|
76
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
|
+
}
|
|
77
122
|
/**
|
|
78
123
|
* Clear captured data
|
|
79
124
|
*/
|
|
80
125
|
clear() {
|
|
81
126
|
this.consoleLogs = [];
|
|
82
127
|
this.networkRequests = [];
|
|
128
|
+
this.navigationHistory = [];
|
|
83
129
|
}
|
|
84
130
|
/**
|
|
85
131
|
* Add a log entry manually (for custom logging)
|
|
@@ -136,14 +182,25 @@ var ContextCaptureManager = class {
|
|
|
136
182
|
const method = init?.method || "GET";
|
|
137
183
|
try {
|
|
138
184
|
const response = await self.originalFetch.call(window, input, init);
|
|
139
|
-
|
|
185
|
+
const requestEntry = {
|
|
140
186
|
method,
|
|
141
187
|
url: url.slice(0, 200),
|
|
142
188
|
// Limit URL length
|
|
143
189
|
status: response.status,
|
|
144
190
|
duration: Date.now() - startTime,
|
|
145
191
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
146
|
-
}
|
|
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);
|
|
147
204
|
return response;
|
|
148
205
|
} catch (error) {
|
|
149
206
|
self.addNetworkRequest({
|
|
@@ -157,6 +214,25 @@ var ContextCaptureManager = class {
|
|
|
157
214
|
}
|
|
158
215
|
};
|
|
159
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
|
+
}
|
|
160
236
|
getPerformanceMetrics() {
|
|
161
237
|
if (typeof window === "undefined" || typeof performance === "undefined") return void 0;
|
|
162
238
|
const metrics = {};
|
|
@@ -218,22 +294,40 @@ var BugBearClient = class {
|
|
|
218
294
|
);
|
|
219
295
|
}
|
|
220
296
|
/**
|
|
221
|
-
* Track navigation for context
|
|
297
|
+
* Track navigation for context.
|
|
298
|
+
* Also forwards to contextCapture for auto-tracked navigation.
|
|
222
299
|
*/
|
|
223
300
|
trackNavigation(route) {
|
|
224
301
|
this.navigationHistory.push(route);
|
|
225
302
|
if (this.navigationHistory.length > 10) {
|
|
226
303
|
this.navigationHistory.shift();
|
|
227
304
|
}
|
|
305
|
+
contextCapture.trackNavigation(route);
|
|
228
306
|
}
|
|
229
307
|
/**
|
|
230
|
-
* Get current navigation history
|
|
308
|
+
* Get current navigation history.
|
|
309
|
+
* Priority: config callback > manual tracking > auto-captured (pushState/popstate)
|
|
231
310
|
*/
|
|
232
311
|
getNavigationHistory() {
|
|
233
312
|
if (this.config.getNavigationHistory) {
|
|
234
313
|
return this.config.getNavigationHistory();
|
|
235
314
|
}
|
|
236
|
-
|
|
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
|
+
};
|
|
237
331
|
}
|
|
238
332
|
/**
|
|
239
333
|
* Get current user info from host app or BugBear's own auth
|
|
@@ -536,19 +630,40 @@ var BugBearClient = class {
|
|
|
536
630
|
return { success: false, error: message };
|
|
537
631
|
}
|
|
538
632
|
}
|
|
633
|
+
/**
|
|
634
|
+
* Pass a test assignment — convenience wrapper around updateAssignmentStatus
|
|
635
|
+
*/
|
|
636
|
+
async passAssignment(assignmentId) {
|
|
637
|
+
return this.updateAssignmentStatus(assignmentId, "passed");
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Fail a test assignment — convenience wrapper around updateAssignmentStatus
|
|
641
|
+
*/
|
|
642
|
+
async failAssignment(assignmentId) {
|
|
643
|
+
return this.updateAssignmentStatus(assignmentId, "failed");
|
|
644
|
+
}
|
|
539
645
|
/**
|
|
540
646
|
* Skip a test assignment with a required reason
|
|
541
647
|
* Marks the assignment as 'skipped' and records why it was skipped
|
|
542
648
|
*/
|
|
543
649
|
async skipAssignment(assignmentId, reason, notes) {
|
|
650
|
+
let actualReason;
|
|
651
|
+
let actualNotes;
|
|
652
|
+
if (typeof reason === "object") {
|
|
653
|
+
actualReason = reason.reason;
|
|
654
|
+
actualNotes = reason.notes;
|
|
655
|
+
} else {
|
|
656
|
+
actualReason = reason;
|
|
657
|
+
actualNotes = notes;
|
|
658
|
+
}
|
|
544
659
|
try {
|
|
545
660
|
const updateData = {
|
|
546
661
|
status: "skipped",
|
|
547
|
-
skip_reason:
|
|
662
|
+
skip_reason: actualReason,
|
|
548
663
|
completed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
549
664
|
};
|
|
550
|
-
if (
|
|
551
|
-
updateData.notes =
|
|
665
|
+
if (actualNotes) {
|
|
666
|
+
updateData.notes = actualNotes;
|
|
552
667
|
}
|
|
553
668
|
const { error } = await this.supabase.from("test_assignments").update(updateData).eq("id", assignmentId);
|
|
554
669
|
if (error) {
|
package/dist/index.mjs
CHANGED
|
@@ -4,21 +4,25 @@ import { createClient } from "@supabase/supabase-js";
|
|
|
4
4
|
// src/capture.ts
|
|
5
5
|
var MAX_CONSOLE_LOGS = 50;
|
|
6
6
|
var MAX_NETWORK_REQUESTS = 20;
|
|
7
|
+
var MAX_NAVIGATION_HISTORY = 20;
|
|
8
|
+
var MAX_RESPONSE_BODY_LENGTH = 500;
|
|
7
9
|
var ContextCaptureManager = class {
|
|
8
10
|
constructor() {
|
|
9
11
|
this.consoleLogs = [];
|
|
10
12
|
this.networkRequests = [];
|
|
13
|
+
this.navigationHistory = [];
|
|
11
14
|
this.originalConsole = {};
|
|
12
15
|
this.isCapturing = false;
|
|
13
16
|
}
|
|
14
17
|
/**
|
|
15
|
-
* Start capturing console logs
|
|
18
|
+
* Start capturing console logs, network requests, and navigation
|
|
16
19
|
*/
|
|
17
20
|
startCapture() {
|
|
18
21
|
if (this.isCapturing) return;
|
|
19
22
|
this.isCapturing = true;
|
|
20
23
|
this.captureConsole();
|
|
21
24
|
this.captureFetch();
|
|
25
|
+
this.captureNavigation();
|
|
22
26
|
}
|
|
23
27
|
/**
|
|
24
28
|
* Stop capturing and restore original functions
|
|
@@ -33,6 +37,17 @@ var ContextCaptureManager = class {
|
|
|
33
37
|
if (this.originalFetch && typeof window !== "undefined") {
|
|
34
38
|
window.fetch = this.originalFetch;
|
|
35
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
|
+
}
|
|
36
51
|
}
|
|
37
52
|
/**
|
|
38
53
|
* Get captured context for a bug report
|
|
@@ -41,16 +56,47 @@ var ContextCaptureManager = class {
|
|
|
41
56
|
return {
|
|
42
57
|
consoleLogs: [...this.consoleLogs],
|
|
43
58
|
networkRequests: [...this.networkRequests],
|
|
59
|
+
navigationHistory: [...this.navigationHistory],
|
|
44
60
|
performanceMetrics: this.getPerformanceMetrics(),
|
|
45
61
|
environment: this.getEnvironmentInfo()
|
|
46
62
|
};
|
|
47
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
|
+
}
|
|
48
93
|
/**
|
|
49
94
|
* Clear captured data
|
|
50
95
|
*/
|
|
51
96
|
clear() {
|
|
52
97
|
this.consoleLogs = [];
|
|
53
98
|
this.networkRequests = [];
|
|
99
|
+
this.navigationHistory = [];
|
|
54
100
|
}
|
|
55
101
|
/**
|
|
56
102
|
* Add a log entry manually (for custom logging)
|
|
@@ -107,14 +153,25 @@ var ContextCaptureManager = class {
|
|
|
107
153
|
const method = init?.method || "GET";
|
|
108
154
|
try {
|
|
109
155
|
const response = await self.originalFetch.call(window, input, init);
|
|
110
|
-
|
|
156
|
+
const requestEntry = {
|
|
111
157
|
method,
|
|
112
158
|
url: url.slice(0, 200),
|
|
113
159
|
// Limit URL length
|
|
114
160
|
status: response.status,
|
|
115
161
|
duration: Date.now() - startTime,
|
|
116
162
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
117
|
-
}
|
|
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);
|
|
118
175
|
return response;
|
|
119
176
|
} catch (error) {
|
|
120
177
|
self.addNetworkRequest({
|
|
@@ -128,6 +185,25 @@ var ContextCaptureManager = class {
|
|
|
128
185
|
}
|
|
129
186
|
};
|
|
130
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
|
+
}
|
|
131
207
|
getPerformanceMetrics() {
|
|
132
208
|
if (typeof window === "undefined" || typeof performance === "undefined") return void 0;
|
|
133
209
|
const metrics = {};
|
|
@@ -189,22 +265,40 @@ var BugBearClient = class {
|
|
|
189
265
|
);
|
|
190
266
|
}
|
|
191
267
|
/**
|
|
192
|
-
* Track navigation for context
|
|
268
|
+
* Track navigation for context.
|
|
269
|
+
* Also forwards to contextCapture for auto-tracked navigation.
|
|
193
270
|
*/
|
|
194
271
|
trackNavigation(route) {
|
|
195
272
|
this.navigationHistory.push(route);
|
|
196
273
|
if (this.navigationHistory.length > 10) {
|
|
197
274
|
this.navigationHistory.shift();
|
|
198
275
|
}
|
|
276
|
+
contextCapture.trackNavigation(route);
|
|
199
277
|
}
|
|
200
278
|
/**
|
|
201
|
-
* Get current navigation history
|
|
279
|
+
* Get current navigation history.
|
|
280
|
+
* Priority: config callback > manual tracking > auto-captured (pushState/popstate)
|
|
202
281
|
*/
|
|
203
282
|
getNavigationHistory() {
|
|
204
283
|
if (this.config.getNavigationHistory) {
|
|
205
284
|
return this.config.getNavigationHistory();
|
|
206
285
|
}
|
|
207
|
-
|
|
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
|
+
};
|
|
208
302
|
}
|
|
209
303
|
/**
|
|
210
304
|
* Get current user info from host app or BugBear's own auth
|
|
@@ -507,19 +601,40 @@ var BugBearClient = class {
|
|
|
507
601
|
return { success: false, error: message };
|
|
508
602
|
}
|
|
509
603
|
}
|
|
604
|
+
/**
|
|
605
|
+
* Pass a test assignment — convenience wrapper around updateAssignmentStatus
|
|
606
|
+
*/
|
|
607
|
+
async passAssignment(assignmentId) {
|
|
608
|
+
return this.updateAssignmentStatus(assignmentId, "passed");
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Fail a test assignment — convenience wrapper around updateAssignmentStatus
|
|
612
|
+
*/
|
|
613
|
+
async failAssignment(assignmentId) {
|
|
614
|
+
return this.updateAssignmentStatus(assignmentId, "failed");
|
|
615
|
+
}
|
|
510
616
|
/**
|
|
511
617
|
* Skip a test assignment with a required reason
|
|
512
618
|
* Marks the assignment as 'skipped' and records why it was skipped
|
|
513
619
|
*/
|
|
514
620
|
async skipAssignment(assignmentId, reason, notes) {
|
|
621
|
+
let actualReason;
|
|
622
|
+
let actualNotes;
|
|
623
|
+
if (typeof reason === "object") {
|
|
624
|
+
actualReason = reason.reason;
|
|
625
|
+
actualNotes = reason.notes;
|
|
626
|
+
} else {
|
|
627
|
+
actualReason = reason;
|
|
628
|
+
actualNotes = notes;
|
|
629
|
+
}
|
|
515
630
|
try {
|
|
516
631
|
const updateData = {
|
|
517
632
|
status: "skipped",
|
|
518
|
-
skip_reason:
|
|
633
|
+
skip_reason: actualReason,
|
|
519
634
|
completed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
520
635
|
};
|
|
521
|
-
if (
|
|
522
|
-
updateData.notes =
|
|
636
|
+
if (actualNotes) {
|
|
637
|
+
updateData.notes = actualNotes;
|
|
523
638
|
}
|
|
524
639
|
const { error } = await this.supabase.from("test_assignments").update(updateData).eq("id", assignmentId);
|
|
525
640
|
if (error) {
|