@bbearai/core 0.6.0 → 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.mts 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 {
@@ -221,7 +232,77 @@ interface BugBearConfig {
221
232
  realtime?: {
222
233
  enabled: boolean;
223
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;
224
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';
225
306
  interface BugBearTheme {
226
307
  /** Primary brand color */
227
308
  primaryColor?: string;
@@ -231,6 +312,8 @@ interface BugBearTheme {
231
312
  textColor?: string;
232
313
  /** Border radius */
233
314
  borderRadius?: number;
315
+ /** Color scheme for the widget. Defaults to 'dark'. */
316
+ colorScheme?: WidgetColorScheme;
234
317
  }
235
318
  type TestTemplate = 'steps' | 'checklist' | 'rubric' | 'freeform';
236
319
  type RubricMode = 'pass_fail' | 'rating';
@@ -365,6 +448,8 @@ interface TesterInfo {
365
448
  platforms: string[];
366
449
  assignedTests: number;
367
450
  completedTests: number;
451
+ /** Role: 'tester' for QA testers, 'feedback' for feedback-only users */
452
+ role?: 'tester' | 'feedback';
368
453
  }
369
454
  interface TesterProfileUpdate {
370
455
  name?: string;
@@ -791,6 +876,155 @@ declare class OfflineQueue {
791
876
  /** Heuristic: does this error string look like a connectivity failure? */
792
877
  declare function isNetworkError(error?: string | null): boolean;
793
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
+
794
1028
  /**
795
1029
  * BugBear Client
796
1030
  * Handles communication with the BugBear backend
@@ -805,6 +1039,8 @@ declare class BugBearClient {
805
1039
  private _queue;
806
1040
  /** Active Realtime channel references for cleanup. */
807
1041
  private realtimeChannels;
1042
+ /** Error monitor instance — created when config.monitoring is present. */
1043
+ monitor: ErrorMonitor | null;
808
1044
  /**
809
1045
  * Resolves when the client is ready to make requests.
810
1046
  * For the explicit config path (supabaseUrl + supabaseAnonKey), this is immediate.
@@ -829,6 +1065,8 @@ declare class BugBearClient {
829
1065
  private applyResolvedConfig;
830
1066
  /** Initialize offline queue if configured. Shared by both init paths. */
831
1067
  private initOfflineQueue;
1068
+ /** Initialize error monitoring if configured. Shared by both init paths. */
1069
+ private initMonitoring;
832
1070
  /** Read cached config from localStorage if available and not expired. */
833
1071
  private readConfigCache;
834
1072
  /** Write resolved config to localStorage cache. */
@@ -1047,10 +1285,25 @@ declare class BugBearClient {
1047
1285
  */
1048
1286
  isQAEnabled(): Promise<boolean>;
1049
1287
  /**
1050
- * Check if the widget should be visible
1051
- * (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
1052
1293
  */
1053
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>;
1054
1307
  /**
1055
1308
  * Upload a screenshot (web - uses File/Blob)
1056
1309
  */
@@ -1290,4 +1543,4 @@ declare function captureError(error: Error, errorInfo?: {
1290
1543
  componentStack?: string;
1291
1544
  };
1292
1545
 
1293
- 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 };
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 {
@@ -221,7 +232,77 @@ interface BugBearConfig {
221
232
  realtime?: {
222
233
  enabled: boolean;
223
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;
224
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';
225
306
  interface BugBearTheme {
226
307
  /** Primary brand color */
227
308
  primaryColor?: string;
@@ -231,6 +312,8 @@ interface BugBearTheme {
231
312
  textColor?: string;
232
313
  /** Border radius */
233
314
  borderRadius?: number;
315
+ /** Color scheme for the widget. Defaults to 'dark'. */
316
+ colorScheme?: WidgetColorScheme;
234
317
  }
235
318
  type TestTemplate = 'steps' | 'checklist' | 'rubric' | 'freeform';
236
319
  type RubricMode = 'pass_fail' | 'rating';
@@ -365,6 +448,8 @@ interface TesterInfo {
365
448
  platforms: string[];
366
449
  assignedTests: number;
367
450
  completedTests: number;
451
+ /** Role: 'tester' for QA testers, 'feedback' for feedback-only users */
452
+ role?: 'tester' | 'feedback';
368
453
  }
369
454
  interface TesterProfileUpdate {
370
455
  name?: string;
@@ -791,6 +876,155 @@ declare class OfflineQueue {
791
876
  /** Heuristic: does this error string look like a connectivity failure? */
792
877
  declare function isNetworkError(error?: string | null): boolean;
793
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
+
794
1028
  /**
795
1029
  * BugBear Client
796
1030
  * Handles communication with the BugBear backend
@@ -805,6 +1039,8 @@ declare class BugBearClient {
805
1039
  private _queue;
806
1040
  /** Active Realtime channel references for cleanup. */
807
1041
  private realtimeChannels;
1042
+ /** Error monitor instance — created when config.monitoring is present. */
1043
+ monitor: ErrorMonitor | null;
808
1044
  /**
809
1045
  * Resolves when the client is ready to make requests.
810
1046
  * For the explicit config path (supabaseUrl + supabaseAnonKey), this is immediate.
@@ -829,6 +1065,8 @@ declare class BugBearClient {
829
1065
  private applyResolvedConfig;
830
1066
  /** Initialize offline queue if configured. Shared by both init paths. */
831
1067
  private initOfflineQueue;
1068
+ /** Initialize error monitoring if configured. Shared by both init paths. */
1069
+ private initMonitoring;
832
1070
  /** Read cached config from localStorage if available and not expired. */
833
1071
  private readConfigCache;
834
1072
  /** Write resolved config to localStorage cache. */
@@ -1047,10 +1285,25 @@ declare class BugBearClient {
1047
1285
  */
1048
1286
  isQAEnabled(): Promise<boolean>;
1049
1287
  /**
1050
- * Check if the widget should be visible
1051
- * (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
1052
1293
  */
1053
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>;
1054
1307
  /**
1055
1308
  * Upload a screenshot (web - uses File/Blob)
1056
1309
  */
@@ -1290,4 +1543,4 @@ declare function captureError(error: Error, errorInfo?: {
1290
1543
  componentStack?: string;
1291
1544
  };
1292
1545
 
1293
- 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 };