@bbearai/core 0.6.0 → 0.7.1

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,6 +232,81 @@ 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;
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', 'light', or 'auto' (follows system preference, default). */
305
+ type WidgetColorScheme = 'dark' | 'light' | 'auto';
306
+ /** Server-side widget configuration set by the project admin in the dashboard. */
307
+ interface WidgetConfig {
308
+ /** Admin-forced color scheme. When set, overrides the developer's config and user's OS preference. */
309
+ colorScheme?: WidgetColorScheme;
224
310
  }
225
311
  interface BugBearTheme {
226
312
  /** Primary brand color */
@@ -231,6 +317,8 @@ interface BugBearTheme {
231
317
  textColor?: string;
232
318
  /** Border radius */
233
319
  borderRadius?: number;
320
+ /** Color scheme for the widget. Defaults to 'auto' (follows OS preference). */
321
+ colorScheme?: WidgetColorScheme;
234
322
  }
235
323
  type TestTemplate = 'steps' | 'checklist' | 'rubric' | 'freeform';
236
324
  type RubricMode = 'pass_fail' | 'rating';
@@ -365,6 +453,8 @@ interface TesterInfo {
365
453
  platforms: string[];
366
454
  assignedTests: number;
367
455
  completedTests: number;
456
+ /** Role: 'tester' for QA testers, 'feedback' for feedback-only users */
457
+ role?: 'tester' | 'feedback';
368
458
  }
369
459
  interface TesterProfileUpdate {
370
460
  name?: string;
@@ -791,6 +881,155 @@ declare class OfflineQueue {
791
881
  /** Heuristic: does this error string look like a connectivity failure? */
792
882
  declare function isNetworkError(error?: string | null): boolean;
793
883
 
884
+ interface MonitorDeps {
885
+ submitReport: (report: Record<string, unknown>) => Promise<{
886
+ success: boolean;
887
+ reportId?: string;
888
+ error?: string;
889
+ queued?: boolean;
890
+ }>;
891
+ getCurrentRoute: () => string;
892
+ getEnhancedContext: () => EnhancedBugContext;
893
+ getDeviceInfo: () => DeviceInfo;
894
+ }
895
+ declare class ErrorMonitor {
896
+ private deps;
897
+ private dedup;
898
+ private sessionReportCount;
899
+ private config;
900
+ private pendingSentryData;
901
+ constructor(config: MonitoringConfig, deps: MonitorDeps);
902
+ handleEvent(event: MonitoringEvent): Promise<void>;
903
+ enrichWithSentry(data: {
904
+ sentryEventId?: string;
905
+ sentryBreadcrumbs?: unknown[];
906
+ }): void;
907
+ private getSeverity;
908
+ private generateTitle;
909
+ destroy(): void;
910
+ }
911
+
912
+ /**
913
+ * Generate a stable fingerprint for deduplicating monitoring events.
914
+ * Same source + message + route = same fingerprint.
915
+ */
916
+ declare function generateFingerprint(source: string, message: string, route: string): string;
917
+ /**
918
+ * Sliding-window deduplication.
919
+ * Tracks fingerprints and suppresses duplicates within a configurable time window.
920
+ */
921
+ declare class DedupWindow {
922
+ private windowMs;
923
+ private seen;
924
+ constructor(windowMs: number);
925
+ /** Returns true if this fingerprint should be reported (not a recent duplicate). */
926
+ shouldReport(fingerprint: string): boolean;
927
+ /** Remove expired entries to prevent memory leaks in long sessions. */
928
+ private cleanup;
929
+ }
930
+
931
+ /**
932
+ * Redact sensitive query parameters from URLs.
933
+ * Returns the URL with sensitive values replaced by [REDACTED].
934
+ */
935
+ declare function scrubUrl(url: string): string;
936
+
937
+ interface RageClickEvent {
938
+ clickCount: number;
939
+ targetSelector: string;
940
+ x: number;
941
+ y: number;
942
+ }
943
+ /**
944
+ * Detects rage clicks — 3+ clicks on the same target within 2 seconds.
945
+ * Platform-agnostic: web passes DOM click data, RN passes touch data.
946
+ */
947
+ declare class RageClickDetector {
948
+ private onRageClick;
949
+ private clicks;
950
+ private readonly threshold;
951
+ private readonly windowMs;
952
+ private destroyed;
953
+ constructor(onRageClick: (event: RageClickEvent) => void);
954
+ recordClick(x: number, y: number, targetSelector: string): void;
955
+ destroy(): void;
956
+ }
957
+
958
+ type EventCallback = (event: MonitoringEvent) => void;
959
+ declare class WebCrashHandler {
960
+ private onEvent;
961
+ private prevOnError;
962
+ private rejectionHandler;
963
+ constructor(onEvent: EventCallback);
964
+ start(): void;
965
+ destroy(): void;
966
+ }
967
+ declare class WebApiFailureHandler {
968
+ private onEvent;
969
+ private originalFetch;
970
+ constructor(onEvent: EventCallback);
971
+ start(): void;
972
+ destroy(): void;
973
+ }
974
+ declare class WebRageClickHandler {
975
+ private onEvent;
976
+ private detector;
977
+ private clickHandler;
978
+ constructor(onEvent: EventCallback);
979
+ start(): void;
980
+ destroy(): void;
981
+ }
982
+
983
+ /**
984
+ * React Native crash handler.
985
+ * Hooks into RN's global ErrorUtils to capture unhandled JS errors.
986
+ * Chains with the existing handler so RN's default error display (red box) still works.
987
+ */
988
+ declare class RNCrashHandler {
989
+ private onEvent;
990
+ private getCurrentRoute;
991
+ private originalHandler;
992
+ private started;
993
+ constructor(onEvent: (event: MonitoringEvent) => void, getCurrentRoute: () => string);
994
+ start(): void;
995
+ destroy(): void;
996
+ }
997
+ /**
998
+ * React Native API failure handler.
999
+ * Monkey-patches globalThis.fetch to intercept 4xx/5xx responses.
1000
+ * RN uses the same fetch API as the web, so this is identical in pattern.
1001
+ */
1002
+ declare class RNApiFailureHandler {
1003
+ private onEvent;
1004
+ private getCurrentRoute;
1005
+ private originalFetch;
1006
+ private started;
1007
+ constructor(onEvent: (event: MonitoringEvent) => void, getCurrentRoute: () => string);
1008
+ start(): void;
1009
+ destroy(): void;
1010
+ }
1011
+ /**
1012
+ * React Native rage click handler.
1013
+ * Unlike web, RN doesn't have DOM click events. Instead, exposes a `recordTouch`
1014
+ * method that the BugBearProvider calls from `onTouchEnd` events on the wrapper View.
1015
+ * Internally delegates to RageClickDetector using viewId as the target selector.
1016
+ */
1017
+ declare class RNRageClickHandler {
1018
+ private onEvent;
1019
+ private getCurrentRoute;
1020
+ private detector;
1021
+ constructor(onEvent: (event: MonitoringEvent) => void, getCurrentRoute: () => string);
1022
+ start(): void;
1023
+ /**
1024
+ * Record a touch event. Call this from the BugBearProvider's onTouchEnd handler.
1025
+ * @param x - Touch x coordinate
1026
+ * @param y - Touch y coordinate
1027
+ * @param viewId - Identifier for the touched view (e.g., testID, accessibilityLabel, or nativeID)
1028
+ */
1029
+ recordTouch(x: number, y: number, viewId: string): void;
1030
+ destroy(): void;
1031
+ }
1032
+
794
1033
  /**
795
1034
  * BugBear Client
796
1035
  * Handles communication with the BugBear backend
@@ -805,6 +1044,8 @@ declare class BugBearClient {
805
1044
  private _queue;
806
1045
  /** Active Realtime channel references for cleanup. */
807
1046
  private realtimeChannels;
1047
+ /** Error monitor instance — created when config.monitoring is present. */
1048
+ monitor: ErrorMonitor | null;
808
1049
  /**
809
1050
  * Resolves when the client is ready to make requests.
810
1051
  * For the explicit config path (supabaseUrl + supabaseAnonKey), this is immediate.
@@ -829,6 +1070,8 @@ declare class BugBearClient {
829
1070
  private applyResolvedConfig;
830
1071
  /** Initialize offline queue if configured. Shared by both init paths. */
831
1072
  private initOfflineQueue;
1073
+ /** Initialize error monitoring if configured. Shared by both init paths. */
1074
+ private initMonitoring;
832
1075
  /** Read cached config from localStorage if available and not expired. */
833
1076
  private readConfigCache;
834
1077
  /** Write resolved config to localStorage cache. */
@@ -1047,10 +1290,35 @@ declare class BugBearClient {
1047
1290
  */
1048
1291
  isQAEnabled(): Promise<boolean>;
1049
1292
  /**
1050
- * Check if the widget should be visible
1051
- * (QA mode enabled AND current user is a tester)
1293
+ * Fetch server-side widget config set by the project admin.
1294
+ * Returns settings like color scheme override. Returns empty config on error.
1295
+ */
1296
+ getWidgetConfig(): Promise<WidgetConfig>;
1297
+ /**
1298
+ * Check if feedback mode is enabled for this project.
1299
+ * This is a master switch that admins can toggle in the dashboard.
1300
+ */
1301
+ isFeedbackEnabled(): Promise<boolean>;
1302
+ /**
1303
+ * Check if the widget should be visible.
1304
+ * Behavior depends on the configured mode:
1305
+ * - 'qa': QA enabled AND user is a registered tester
1306
+ * - 'feedback': Feedback enabled AND user is authenticated
1307
+ * - 'auto': QA tester (if QA enabled) OR authenticated user (if feedback enabled)
1052
1308
  */
1053
1309
  shouldShowWidget(): Promise<boolean>;
1310
+ /**
1311
+ * Resolve the effective widget mode for the current user.
1312
+ * - 'qa' or 'feedback' config → returned as-is
1313
+ * - 'auto' → QA tester with QA enabled → 'qa', feedback enabled → 'feedback', else 'qa'
1314
+ */
1315
+ getEffectiveMode(): Promise<'qa' | 'feedback'>;
1316
+ /**
1317
+ * Auto-provision a feedback user record in the testers table.
1318
+ * Called during initialization when mode is 'feedback' or 'auto' for non-testers.
1319
+ * Idempotent — safe to call multiple times. Returns the tester info for the user.
1320
+ */
1321
+ ensureFeedbackUser(): Promise<TesterInfo | null>;
1054
1322
  /**
1055
1323
  * Upload a screenshot (web - uses File/Blob)
1056
1324
  */
@@ -1290,4 +1558,4 @@ declare function captureError(error: Error, errorInfo?: {
1290
1558
  componentStack?: string;
1291
1559
  };
1292
1560
 
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 };
1561
+ 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, type WidgetConfig, 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,6 +232,81 @@ 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;
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', 'light', or 'auto' (follows system preference, default). */
305
+ type WidgetColorScheme = 'dark' | 'light' | 'auto';
306
+ /** Server-side widget configuration set by the project admin in the dashboard. */
307
+ interface WidgetConfig {
308
+ /** Admin-forced color scheme. When set, overrides the developer's config and user's OS preference. */
309
+ colorScheme?: WidgetColorScheme;
224
310
  }
225
311
  interface BugBearTheme {
226
312
  /** Primary brand color */
@@ -231,6 +317,8 @@ interface BugBearTheme {
231
317
  textColor?: string;
232
318
  /** Border radius */
233
319
  borderRadius?: number;
320
+ /** Color scheme for the widget. Defaults to 'auto' (follows OS preference). */
321
+ colorScheme?: WidgetColorScheme;
234
322
  }
235
323
  type TestTemplate = 'steps' | 'checklist' | 'rubric' | 'freeform';
236
324
  type RubricMode = 'pass_fail' | 'rating';
@@ -365,6 +453,8 @@ interface TesterInfo {
365
453
  platforms: string[];
366
454
  assignedTests: number;
367
455
  completedTests: number;
456
+ /** Role: 'tester' for QA testers, 'feedback' for feedback-only users */
457
+ role?: 'tester' | 'feedback';
368
458
  }
369
459
  interface TesterProfileUpdate {
370
460
  name?: string;
@@ -791,6 +881,155 @@ declare class OfflineQueue {
791
881
  /** Heuristic: does this error string look like a connectivity failure? */
792
882
  declare function isNetworkError(error?: string | null): boolean;
793
883
 
884
+ interface MonitorDeps {
885
+ submitReport: (report: Record<string, unknown>) => Promise<{
886
+ success: boolean;
887
+ reportId?: string;
888
+ error?: string;
889
+ queued?: boolean;
890
+ }>;
891
+ getCurrentRoute: () => string;
892
+ getEnhancedContext: () => EnhancedBugContext;
893
+ getDeviceInfo: () => DeviceInfo;
894
+ }
895
+ declare class ErrorMonitor {
896
+ private deps;
897
+ private dedup;
898
+ private sessionReportCount;
899
+ private config;
900
+ private pendingSentryData;
901
+ constructor(config: MonitoringConfig, deps: MonitorDeps);
902
+ handleEvent(event: MonitoringEvent): Promise<void>;
903
+ enrichWithSentry(data: {
904
+ sentryEventId?: string;
905
+ sentryBreadcrumbs?: unknown[];
906
+ }): void;
907
+ private getSeverity;
908
+ private generateTitle;
909
+ destroy(): void;
910
+ }
911
+
912
+ /**
913
+ * Generate a stable fingerprint for deduplicating monitoring events.
914
+ * Same source + message + route = same fingerprint.
915
+ */
916
+ declare function generateFingerprint(source: string, message: string, route: string): string;
917
+ /**
918
+ * Sliding-window deduplication.
919
+ * Tracks fingerprints and suppresses duplicates within a configurable time window.
920
+ */
921
+ declare class DedupWindow {
922
+ private windowMs;
923
+ private seen;
924
+ constructor(windowMs: number);
925
+ /** Returns true if this fingerprint should be reported (not a recent duplicate). */
926
+ shouldReport(fingerprint: string): boolean;
927
+ /** Remove expired entries to prevent memory leaks in long sessions. */
928
+ private cleanup;
929
+ }
930
+
931
+ /**
932
+ * Redact sensitive query parameters from URLs.
933
+ * Returns the URL with sensitive values replaced by [REDACTED].
934
+ */
935
+ declare function scrubUrl(url: string): string;
936
+
937
+ interface RageClickEvent {
938
+ clickCount: number;
939
+ targetSelector: string;
940
+ x: number;
941
+ y: number;
942
+ }
943
+ /**
944
+ * Detects rage clicks — 3+ clicks on the same target within 2 seconds.
945
+ * Platform-agnostic: web passes DOM click data, RN passes touch data.
946
+ */
947
+ declare class RageClickDetector {
948
+ private onRageClick;
949
+ private clicks;
950
+ private readonly threshold;
951
+ private readonly windowMs;
952
+ private destroyed;
953
+ constructor(onRageClick: (event: RageClickEvent) => void);
954
+ recordClick(x: number, y: number, targetSelector: string): void;
955
+ destroy(): void;
956
+ }
957
+
958
+ type EventCallback = (event: MonitoringEvent) => void;
959
+ declare class WebCrashHandler {
960
+ private onEvent;
961
+ private prevOnError;
962
+ private rejectionHandler;
963
+ constructor(onEvent: EventCallback);
964
+ start(): void;
965
+ destroy(): void;
966
+ }
967
+ declare class WebApiFailureHandler {
968
+ private onEvent;
969
+ private originalFetch;
970
+ constructor(onEvent: EventCallback);
971
+ start(): void;
972
+ destroy(): void;
973
+ }
974
+ declare class WebRageClickHandler {
975
+ private onEvent;
976
+ private detector;
977
+ private clickHandler;
978
+ constructor(onEvent: EventCallback);
979
+ start(): void;
980
+ destroy(): void;
981
+ }
982
+
983
+ /**
984
+ * React Native crash handler.
985
+ * Hooks into RN's global ErrorUtils to capture unhandled JS errors.
986
+ * Chains with the existing handler so RN's default error display (red box) still works.
987
+ */
988
+ declare class RNCrashHandler {
989
+ private onEvent;
990
+ private getCurrentRoute;
991
+ private originalHandler;
992
+ private started;
993
+ constructor(onEvent: (event: MonitoringEvent) => void, getCurrentRoute: () => string);
994
+ start(): void;
995
+ destroy(): void;
996
+ }
997
+ /**
998
+ * React Native API failure handler.
999
+ * Monkey-patches globalThis.fetch to intercept 4xx/5xx responses.
1000
+ * RN uses the same fetch API as the web, so this is identical in pattern.
1001
+ */
1002
+ declare class RNApiFailureHandler {
1003
+ private onEvent;
1004
+ private getCurrentRoute;
1005
+ private originalFetch;
1006
+ private started;
1007
+ constructor(onEvent: (event: MonitoringEvent) => void, getCurrentRoute: () => string);
1008
+ start(): void;
1009
+ destroy(): void;
1010
+ }
1011
+ /**
1012
+ * React Native rage click handler.
1013
+ * Unlike web, RN doesn't have DOM click events. Instead, exposes a `recordTouch`
1014
+ * method that the BugBearProvider calls from `onTouchEnd` events on the wrapper View.
1015
+ * Internally delegates to RageClickDetector using viewId as the target selector.
1016
+ */
1017
+ declare class RNRageClickHandler {
1018
+ private onEvent;
1019
+ private getCurrentRoute;
1020
+ private detector;
1021
+ constructor(onEvent: (event: MonitoringEvent) => void, getCurrentRoute: () => string);
1022
+ start(): void;
1023
+ /**
1024
+ * Record a touch event. Call this from the BugBearProvider's onTouchEnd handler.
1025
+ * @param x - Touch x coordinate
1026
+ * @param y - Touch y coordinate
1027
+ * @param viewId - Identifier for the touched view (e.g., testID, accessibilityLabel, or nativeID)
1028
+ */
1029
+ recordTouch(x: number, y: number, viewId: string): void;
1030
+ destroy(): void;
1031
+ }
1032
+
794
1033
  /**
795
1034
  * BugBear Client
796
1035
  * Handles communication with the BugBear backend
@@ -805,6 +1044,8 @@ declare class BugBearClient {
805
1044
  private _queue;
806
1045
  /** Active Realtime channel references for cleanup. */
807
1046
  private realtimeChannels;
1047
+ /** Error monitor instance — created when config.monitoring is present. */
1048
+ monitor: ErrorMonitor | null;
808
1049
  /**
809
1050
  * Resolves when the client is ready to make requests.
810
1051
  * For the explicit config path (supabaseUrl + supabaseAnonKey), this is immediate.
@@ -829,6 +1070,8 @@ declare class BugBearClient {
829
1070
  private applyResolvedConfig;
830
1071
  /** Initialize offline queue if configured. Shared by both init paths. */
831
1072
  private initOfflineQueue;
1073
+ /** Initialize error monitoring if configured. Shared by both init paths. */
1074
+ private initMonitoring;
832
1075
  /** Read cached config from localStorage if available and not expired. */
833
1076
  private readConfigCache;
834
1077
  /** Write resolved config to localStorage cache. */
@@ -1047,10 +1290,35 @@ declare class BugBearClient {
1047
1290
  */
1048
1291
  isQAEnabled(): Promise<boolean>;
1049
1292
  /**
1050
- * Check if the widget should be visible
1051
- * (QA mode enabled AND current user is a tester)
1293
+ * Fetch server-side widget config set by the project admin.
1294
+ * Returns settings like color scheme override. Returns empty config on error.
1295
+ */
1296
+ getWidgetConfig(): Promise<WidgetConfig>;
1297
+ /**
1298
+ * Check if feedback mode is enabled for this project.
1299
+ * This is a master switch that admins can toggle in the dashboard.
1300
+ */
1301
+ isFeedbackEnabled(): Promise<boolean>;
1302
+ /**
1303
+ * Check if the widget should be visible.
1304
+ * Behavior depends on the configured mode:
1305
+ * - 'qa': QA enabled AND user is a registered tester
1306
+ * - 'feedback': Feedback enabled AND user is authenticated
1307
+ * - 'auto': QA tester (if QA enabled) OR authenticated user (if feedback enabled)
1052
1308
  */
1053
1309
  shouldShowWidget(): Promise<boolean>;
1310
+ /**
1311
+ * Resolve the effective widget mode for the current user.
1312
+ * - 'qa' or 'feedback' config → returned as-is
1313
+ * - 'auto' → QA tester with QA enabled → 'qa', feedback enabled → 'feedback', else 'qa'
1314
+ */
1315
+ getEffectiveMode(): Promise<'qa' | 'feedback'>;
1316
+ /**
1317
+ * Auto-provision a feedback user record in the testers table.
1318
+ * Called during initialization when mode is 'feedback' or 'auto' for non-testers.
1319
+ * Idempotent — safe to call multiple times. Returns the tester info for the user.
1320
+ */
1321
+ ensureFeedbackUser(): Promise<TesterInfo | null>;
1054
1322
  /**
1055
1323
  * Upload a screenshot (web - uses File/Blob)
1056
1324
  */
@@ -1290,4 +1558,4 @@ declare function captureError(error: Error, errorInfo?: {
1290
1558
  componentStack?: string;
1291
1559
  };
1292
1560
 
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 };
1561
+ 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, type WidgetConfig, captureError, contextCapture, createBugBear, generateFingerprint, isBugCategory, isNetworkError, scrubUrl };