@bbearai/core 0.5.4 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2,6 +2,15 @@
2
2
  * BugBear Core Types
3
3
  */
4
4
  type ReportType = 'bug' | 'feedback' | 'suggestion' | 'test_pass' | 'test_fail';
5
+ /**
6
+ * Widget operating mode:
7
+ * - 'qa': Full QA widget. Only visible to registered testers when QA is enabled.
8
+ * - 'feedback': Feedback-only widget. Visible to ALL authenticated users.
9
+ * Hides QA flows (tests, sessions). Shows: bug reporting, feedback, messaging, issues.
10
+ * - 'auto': Auto-detect based on user role. Registered testers → QA mode.
11
+ * Authenticated non-testers → feedback mode (auto-provisioned).
12
+ */
13
+ type BugBearMode = 'qa' | 'feedback' | 'auto';
5
14
  type Severity = 'critical' | 'high' | 'medium' | 'low';
6
15
  /** Valid bug categories - single source of truth */
7
16
  declare const BUG_CATEGORIES: readonly ["ui_ux", "functional", "crash", "security", "other"];
@@ -121,6 +130,8 @@ interface BugBearReport {
121
130
  testCaseId?: string;
122
131
  /** Enhanced debugging context */
123
132
  enhancedContext?: EnhancedBugContext;
133
+ /** How this report was created. Defaults to 'manual' for widget-submitted reports. */
134
+ reportSource?: ReportSource;
124
135
  }
125
136
  /** User info from the host application */
126
137
  interface HostUserInfo {
@@ -132,12 +143,39 @@ interface HostUserInfo {
132
143
  name?: string;
133
144
  }
134
145
  interface BugBearConfig {
135
- /** Your BugBear project ID */
136
- projectId: string;
137
- /** Supabase URL for the BugBear backend */
138
- supabaseUrl: string;
139
- /** Supabase anon key for the BugBear backend */
140
- supabaseAnonKey: string;
146
+ /**
147
+ * BugBear API key (recommended).
148
+ * When provided, the SDK resolves projectId, supabaseUrl, and supabaseAnonKey
149
+ * automatically from the BugBear API. This is the simplest config path —
150
+ * only one env var needed.
151
+ *
152
+ * Get yours at https://app.bugbear.ai/settings/projects
153
+ */
154
+ apiKey?: string;
155
+ /**
156
+ * Base URL for the BugBear dashboard API.
157
+ * Defaults to 'https://app.bugbear.ai'. Only change this for
158
+ * self-hosted or local development environments.
159
+ */
160
+ apiBaseUrl?: string;
161
+ /**
162
+ * Your BugBear project ID.
163
+ * Required when using the explicit config path (without apiKey).
164
+ * Automatically resolved when apiKey is provided.
165
+ */
166
+ projectId?: string;
167
+ /**
168
+ * Supabase URL for the BugBear backend.
169
+ * Required when using the explicit config path (without apiKey).
170
+ * Automatically resolved when apiKey is provided.
171
+ */
172
+ supabaseUrl?: string;
173
+ /**
174
+ * Supabase anon key for the BugBear backend.
175
+ * Required when using the explicit config path (without apiKey).
176
+ * Automatically resolved when apiKey is provided.
177
+ */
178
+ supabaseAnonKey?: string;
141
179
  /** Enable voice input */
142
180
  enableVoice?: boolean;
143
181
  /** Enable screenshot capture */
@@ -194,7 +232,77 @@ interface BugBearConfig {
194
232
  realtime?: {
195
233
  enabled: boolean;
196
234
  };
235
+ /**
236
+ * Widget operating mode. Controls which features are visible.
237
+ * - 'qa' (default): Full QA widget for registered testers.
238
+ * - 'feedback': Feedback-only mode for all authenticated users.
239
+ * - 'auto': Auto-detect — testers get QA, everyone else gets feedback.
240
+ */
241
+ mode?: BugBearMode;
242
+ /**
243
+ * Enable automatic error monitoring.
244
+ * When configured, the SDK auto-detects crashes, API failures, and rage clicks
245
+ * and submits them as reports with report_source tagging.
246
+ */
247
+ monitoring?: MonitoringConfig;
197
248
  }
249
+ /**
250
+ * Report source — distinguishes auto-detected reports from manual ones.
251
+ */
252
+ type ReportSource = 'manual' | 'auto_crash' | 'auto_api' | 'auto_rage_click' | 'sentry_sync';
253
+ /**
254
+ * Configuration for automatic error monitoring.
255
+ * When enabled, the SDK auto-detects production errors and submits them as reports.
256
+ */
257
+ interface MonitoringConfig {
258
+ /** Capture unhandled errors and promise rejections (default: false) */
259
+ crashes?: boolean;
260
+ /** Capture HTTP 4xx/5xx responses from fetch (default: false) */
261
+ apiFailures?: boolean;
262
+ /** Capture rapid repeated clicks with no UI response (default: false) */
263
+ rageClicks?: boolean;
264
+ /** Fraction of events to report: 0–1 (default: 1.0) */
265
+ sampleRate?: number;
266
+ /** Max auto-reports per session (default: 10) */
267
+ maxReportsPerSession?: number;
268
+ /** Window in ms for deduplicating same-fingerprint events (default: 300000 = 5 min) */
269
+ dedupWindowMs?: number;
270
+ /** Only report events at or above this severity (default: 'low') */
271
+ severityThreshold?: Severity;
272
+ /** Called before submitting — return false to suppress this event */
273
+ beforeCapture?: (event: MonitoringEvent) => boolean;
274
+ /** Strip auth tokens, passwords, keys from captured URLs (default: true) */
275
+ scrubUrls?: boolean;
276
+ }
277
+ /**
278
+ * A captured monitoring event before it becomes a report.
279
+ * Passed to beforeCapture for filtering.
280
+ */
281
+ interface MonitoringEvent {
282
+ source: 'crash' | 'api_failure' | 'rage_click';
283
+ fingerprint: string;
284
+ message: string;
285
+ route: string;
286
+ timestamp: number;
287
+ /** The original error (crashes only) */
288
+ error?: Error;
289
+ /** Request URL (API failures only) */
290
+ requestUrl?: string;
291
+ /** HTTP method (API failures only) */
292
+ requestMethod?: string;
293
+ /** HTTP status code (API failures only) */
294
+ statusCode?: number;
295
+ /** Number of rapid clicks detected (rage clicks only) */
296
+ clickCount?: number;
297
+ /** CSS selector of the clicked element (rage clicks only) */
298
+ targetSelector?: string;
299
+ /** Sentry event ID — populated by @bbearai/sentry adapter */
300
+ sentryEventId?: string;
301
+ /** Sentry breadcrumbs — populated by @bbearai/sentry adapter */
302
+ sentryBreadcrumbs?: unknown[];
303
+ }
304
+ /** Widget color scheme: 'dark' (default), 'light', or 'auto' (follows system preference). */
305
+ type WidgetColorScheme = 'dark' | 'light' | 'auto';
198
306
  interface BugBearTheme {
199
307
  /** Primary brand color */
200
308
  primaryColor?: string;
@@ -204,6 +312,8 @@ interface BugBearTheme {
204
312
  textColor?: string;
205
313
  /** Border radius */
206
314
  borderRadius?: number;
315
+ /** Color scheme for the widget. Defaults to 'dark'. */
316
+ colorScheme?: WidgetColorScheme;
207
317
  }
208
318
  type TestTemplate = 'steps' | 'checklist' | 'rubric' | 'freeform';
209
319
  type RubricMode = 'pass_fail' | 'rating';
@@ -338,6 +448,8 @@ interface TesterInfo {
338
448
  platforms: string[];
339
449
  assignedTests: number;
340
450
  completedTests: number;
451
+ /** Role: 'tester' for QA testers, 'feedback' for feedback-only users */
452
+ role?: 'tester' | 'feedback';
341
453
  }
342
454
  interface TesterProfileUpdate {
343
455
  name?: string;
@@ -764,6 +876,155 @@ declare class OfflineQueue {
764
876
  /** Heuristic: does this error string look like a connectivity failure? */
765
877
  declare function isNetworkError(error?: string | null): boolean;
766
878
 
879
+ interface MonitorDeps {
880
+ submitReport: (report: Record<string, unknown>) => Promise<{
881
+ success: boolean;
882
+ reportId?: string;
883
+ error?: string;
884
+ queued?: boolean;
885
+ }>;
886
+ getCurrentRoute: () => string;
887
+ getEnhancedContext: () => EnhancedBugContext;
888
+ getDeviceInfo: () => DeviceInfo;
889
+ }
890
+ declare class ErrorMonitor {
891
+ private deps;
892
+ private dedup;
893
+ private sessionReportCount;
894
+ private config;
895
+ private pendingSentryData;
896
+ constructor(config: MonitoringConfig, deps: MonitorDeps);
897
+ handleEvent(event: MonitoringEvent): Promise<void>;
898
+ enrichWithSentry(data: {
899
+ sentryEventId?: string;
900
+ sentryBreadcrumbs?: unknown[];
901
+ }): void;
902
+ private getSeverity;
903
+ private generateTitle;
904
+ destroy(): void;
905
+ }
906
+
907
+ /**
908
+ * Generate a stable fingerprint for deduplicating monitoring events.
909
+ * Same source + message + route = same fingerprint.
910
+ */
911
+ declare function generateFingerprint(source: string, message: string, route: string): string;
912
+ /**
913
+ * Sliding-window deduplication.
914
+ * Tracks fingerprints and suppresses duplicates within a configurable time window.
915
+ */
916
+ declare class DedupWindow {
917
+ private windowMs;
918
+ private seen;
919
+ constructor(windowMs: number);
920
+ /** Returns true if this fingerprint should be reported (not a recent duplicate). */
921
+ shouldReport(fingerprint: string): boolean;
922
+ /** Remove expired entries to prevent memory leaks in long sessions. */
923
+ private cleanup;
924
+ }
925
+
926
+ /**
927
+ * Redact sensitive query parameters from URLs.
928
+ * Returns the URL with sensitive values replaced by [REDACTED].
929
+ */
930
+ declare function scrubUrl(url: string): string;
931
+
932
+ interface RageClickEvent {
933
+ clickCount: number;
934
+ targetSelector: string;
935
+ x: number;
936
+ y: number;
937
+ }
938
+ /**
939
+ * Detects rage clicks — 3+ clicks on the same target within 2 seconds.
940
+ * Platform-agnostic: web passes DOM click data, RN passes touch data.
941
+ */
942
+ declare class RageClickDetector {
943
+ private onRageClick;
944
+ private clicks;
945
+ private readonly threshold;
946
+ private readonly windowMs;
947
+ private destroyed;
948
+ constructor(onRageClick: (event: RageClickEvent) => void);
949
+ recordClick(x: number, y: number, targetSelector: string): void;
950
+ destroy(): void;
951
+ }
952
+
953
+ type EventCallback = (event: MonitoringEvent) => void;
954
+ declare class WebCrashHandler {
955
+ private onEvent;
956
+ private prevOnError;
957
+ private rejectionHandler;
958
+ constructor(onEvent: EventCallback);
959
+ start(): void;
960
+ destroy(): void;
961
+ }
962
+ declare class WebApiFailureHandler {
963
+ private onEvent;
964
+ private originalFetch;
965
+ constructor(onEvent: EventCallback);
966
+ start(): void;
967
+ destroy(): void;
968
+ }
969
+ declare class WebRageClickHandler {
970
+ private onEvent;
971
+ private detector;
972
+ private clickHandler;
973
+ constructor(onEvent: EventCallback);
974
+ start(): void;
975
+ destroy(): void;
976
+ }
977
+
978
+ /**
979
+ * React Native crash handler.
980
+ * Hooks into RN's global ErrorUtils to capture unhandled JS errors.
981
+ * Chains with the existing handler so RN's default error display (red box) still works.
982
+ */
983
+ declare class RNCrashHandler {
984
+ private onEvent;
985
+ private getCurrentRoute;
986
+ private originalHandler;
987
+ private started;
988
+ constructor(onEvent: (event: MonitoringEvent) => void, getCurrentRoute: () => string);
989
+ start(): void;
990
+ destroy(): void;
991
+ }
992
+ /**
993
+ * React Native API failure handler.
994
+ * Monkey-patches globalThis.fetch to intercept 4xx/5xx responses.
995
+ * RN uses the same fetch API as the web, so this is identical in pattern.
996
+ */
997
+ declare class RNApiFailureHandler {
998
+ private onEvent;
999
+ private getCurrentRoute;
1000
+ private originalFetch;
1001
+ private started;
1002
+ constructor(onEvent: (event: MonitoringEvent) => void, getCurrentRoute: () => string);
1003
+ start(): void;
1004
+ destroy(): void;
1005
+ }
1006
+ /**
1007
+ * React Native rage click handler.
1008
+ * Unlike web, RN doesn't have DOM click events. Instead, exposes a `recordTouch`
1009
+ * method that the BugBearProvider calls from `onTouchEnd` events on the wrapper View.
1010
+ * Internally delegates to RageClickDetector using viewId as the target selector.
1011
+ */
1012
+ declare class RNRageClickHandler {
1013
+ private onEvent;
1014
+ private getCurrentRoute;
1015
+ private detector;
1016
+ constructor(onEvent: (event: MonitoringEvent) => void, getCurrentRoute: () => string);
1017
+ start(): void;
1018
+ /**
1019
+ * Record a touch event. Call this from the BugBearProvider's onTouchEnd handler.
1020
+ * @param x - Touch x coordinate
1021
+ * @param y - Touch y coordinate
1022
+ * @param viewId - Identifier for the touched view (e.g., testID, accessibilityLabel, or nativeID)
1023
+ */
1024
+ recordTouch(x: number, y: number, viewId: string): void;
1025
+ destroy(): void;
1026
+ }
1027
+
767
1028
  /**
768
1029
  * BugBear Client
769
1030
  * Handles communication with the BugBear backend
@@ -778,7 +1039,42 @@ declare class BugBearClient {
778
1039
  private _queue;
779
1040
  /** Active Realtime channel references for cleanup. */
780
1041
  private realtimeChannels;
1042
+ /** Error monitor instance — created when config.monitoring is present. */
1043
+ monitor: ErrorMonitor | null;
1044
+ /**
1045
+ * Resolves when the client is ready to make requests.
1046
+ * For the explicit config path (supabaseUrl + supabaseAnonKey), this is immediate.
1047
+ * For the apiKey path, this resolves after credentials are fetched from /api/v1/config.
1048
+ */
1049
+ private pendingInit;
1050
+ /** Whether the client has been successfully initialized. */
1051
+ private initialized;
1052
+ /** Initialization error, if any. */
1053
+ private initError;
781
1054
  constructor(config: BugBearConfig);
1055
+ /** Whether the client is ready for requests. */
1056
+ get isReady(): boolean;
1057
+ /** Wait until the client is ready. Throws if initialization failed. */
1058
+ ready(): Promise<void>;
1059
+ /**
1060
+ * Resolve Supabase credentials from a BugBear API key.
1061
+ * Checks localStorage cache first, falls back to /api/v1/config.
1062
+ */
1063
+ private resolveFromApiKey;
1064
+ /** Apply resolved credentials and create the Supabase client. */
1065
+ private applyResolvedConfig;
1066
+ /** Initialize offline queue if configured. Shared by both init paths. */
1067
+ private initOfflineQueue;
1068
+ /** Initialize error monitoring if configured. Shared by both init paths. */
1069
+ private initMonitoring;
1070
+ /** Read cached config from localStorage if available and not expired. */
1071
+ private readConfigCache;
1072
+ /** Write resolved config to localStorage cache. */
1073
+ private writeConfigCache;
1074
+ /** Simple string hash for cache keys — avoids storing raw API keys. */
1075
+ private hashKey;
1076
+ /** Ensure the client is initialized before making requests. */
1077
+ private ensureReady;
782
1078
  /**
783
1079
  * Access the offline queue (if enabled).
784
1080
  * Use this to check queue.count, subscribe to changes, or trigger flush.
@@ -989,10 +1285,25 @@ declare class BugBearClient {
989
1285
  */
990
1286
  isQAEnabled(): Promise<boolean>;
991
1287
  /**
992
- * Check if the widget should be visible
993
- * (QA mode enabled AND current user is a tester)
1288
+ * Check if the widget should be visible.
1289
+ * Behavior depends on the configured mode:
1290
+ * - 'qa': QA enabled AND user is a registered tester
1291
+ * - 'feedback': Any authenticated user
1292
+ * - 'auto': Either QA tester OR authenticated non-tester
994
1293
  */
995
1294
  shouldShowWidget(): Promise<boolean>;
1295
+ /**
1296
+ * Resolve the effective widget mode for the current user.
1297
+ * - 'qa' or 'feedback' config → returned as-is
1298
+ * - 'auto' → checks if user is a QA tester (role='tester') → 'qa', otherwise 'feedback'
1299
+ */
1300
+ getEffectiveMode(): Promise<'qa' | 'feedback'>;
1301
+ /**
1302
+ * Auto-provision a feedback user record in the testers table.
1303
+ * Called during initialization when mode is 'feedback' or 'auto' for non-testers.
1304
+ * Idempotent — safe to call multiple times. Returns the tester info for the user.
1305
+ */
1306
+ ensureFeedbackUser(): Promise<TesterInfo | null>;
996
1307
  /**
997
1308
  * Upload a screenshot (web - uses File/Blob)
998
1309
  */
@@ -1173,6 +1484,7 @@ declare class ContextCaptureManager {
1173
1484
  private navigationHistory;
1174
1485
  private originalConsole;
1175
1486
  private originalFetch?;
1487
+ private fetchHost;
1176
1488
  private originalPushState?;
1177
1489
  private originalReplaceState?;
1178
1490
  private popstateHandler?;
@@ -1231,4 +1543,4 @@ declare function captureError(error: Error, errorInfo?: {
1231
1543
  componentStack?: string;
1232
1544
  };
1233
1545
 
1234
- export { type AddFindingOptions, type AppContext, BUG_CATEGORIES, BugBearClient, type BugBearConfig, type BugBearReport, type BugBearTheme, type BugCategory, type ChecklistItem, type ChecklistResult, type ConsoleLogEntry, type CoverageGap, type CoverageMatrixCell, type CoverageMatrixRow, type DeployChecklist, type DeviceInfo, type EndSessionOptions, type EnhancedBugContext, type FindingSeverity, type FindingType, type HostUserInfo, type IssueCategory, type IssueCounts, LocalStorageAdapter, type MessageSenderType, type NetworkRequest, OfflineQueue, type OfflineQueueConfig, type PriorityFactors, type ProjectRole, type QAFinding, type QAHealthMetrics, type QAHealthScore, type QASession, type QASessionStatus, type QATrack, type QueueItem, type QueueItemType, type RegressionEvent, type ReportStatus, type ReportType, type RoutePriority, type RouteTestStats, type RubricMode, type RubricResult, type Severity, type SkipReason, type StartSessionOptions, type StorageAdapter, type SubmitFeedbackOptions, type TestAssignment, type TestFeedback, type TestGroup, type TestResult, type TestStep, type TestTemplate, type TesterInfo, type TesterIssue, type TesterMessage, type TesterProfileUpdate, type TesterThread, type ThreadPriority, type ThreadType, captureError, contextCapture, createBugBear, isBugCategory, isNetworkError };
1546
+ export { type AddFindingOptions, type AppContext, BUG_CATEGORIES, BugBearClient, type BugBearConfig, type BugBearMode, type BugBearReport, type BugBearTheme, type BugCategory, type ChecklistItem, type ChecklistResult, type ConsoleLogEntry, type CoverageGap, type CoverageMatrixCell, type CoverageMatrixRow, DedupWindow, type DeployChecklist, type DeviceInfo, type EndSessionOptions, type EnhancedBugContext, ErrorMonitor, type FindingSeverity, type FindingType, type HostUserInfo, type IssueCategory, type IssueCounts, LocalStorageAdapter, type MessageSenderType, type MonitorDeps, type MonitoringConfig, type MonitoringEvent, type NetworkRequest, OfflineQueue, type OfflineQueueConfig, type PriorityFactors, type ProjectRole, type QAFinding, type QAHealthMetrics, type QAHealthScore, type QASession, type QASessionStatus, type QATrack, type QueueItem, type QueueItemType, RNApiFailureHandler, RNCrashHandler, RNRageClickHandler, RageClickDetector, type RageClickEvent, type RegressionEvent, type ReportSource, type ReportStatus, type ReportType, type RoutePriority, type RouteTestStats, type RubricMode, type RubricResult, type Severity, type SkipReason, type StartSessionOptions, type StorageAdapter, type SubmitFeedbackOptions, type TestAssignment, type TestFeedback, type TestGroup, type TestResult, type TestStep, type TestTemplate, type TesterInfo, type TesterIssue, type TesterMessage, type TesterProfileUpdate, type TesterThread, type ThreadPriority, type ThreadType, WebApiFailureHandler, WebCrashHandler, WebRageClickHandler, type WidgetColorScheme, captureError, contextCapture, createBugBear, generateFingerprint, isBugCategory, isNetworkError, scrubUrl };