@bbearai/core 0.4.0 → 0.4.2

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
@@ -610,6 +610,37 @@ interface AddFindingOptions {
610
610
  deviceInfo?: DeviceInfo;
611
611
  appContext?: AppContext;
612
612
  }
613
+ /** Category for filtering tester issues in the widget */
614
+ type IssueCategory = 'open' | 'done' | 'reopened';
615
+ /** Issue counts for each category (HomeScreen cards) */
616
+ interface IssueCounts {
617
+ open: number;
618
+ done: number;
619
+ reopened: number;
620
+ }
621
+ /** A report as seen by the tester in the widget */
622
+ interface TesterIssue {
623
+ id: string;
624
+ title: string;
625
+ description: string;
626
+ reportType: ReportType;
627
+ severity: Severity | null;
628
+ status: ReportStatus;
629
+ screenshotUrls: string[];
630
+ /** Route where the issue was observed (from app_context) */
631
+ route?: string;
632
+ reporterName?: string;
633
+ createdAt: string;
634
+ updatedAt: string;
635
+ /** Who verified/retested this issue (for done issues) */
636
+ verifiedByName?: string;
637
+ /** When verification passed (for done issues) */
638
+ verifiedAt?: string;
639
+ /** Original bug report ID (for reopened/test_fail issues) */
640
+ originalBugId?: string;
641
+ /** Original bug title (for reopened/test_fail issues) */
642
+ originalBugTitle?: string;
643
+ }
613
644
 
614
645
  /**
615
646
  * BugBear Client
@@ -620,6 +651,7 @@ declare class BugBearClient {
620
651
  private supabase;
621
652
  private config;
622
653
  private navigationHistory;
654
+ private reportSubmitInFlight;
623
655
  constructor(config: BugBearConfig);
624
656
  /**
625
657
  * Track navigation for context.
@@ -739,6 +771,21 @@ declare class BugBearClient {
739
771
  skipped: number;
740
772
  total: number;
741
773
  } | null>;
774
+ /**
775
+ * Get issue counts for the tester (Open, Done, Reopened)
776
+ * Used by the widget HomeScreen cards
777
+ */
778
+ getIssueCounts(): Promise<{
779
+ open: number;
780
+ done: number;
781
+ reopened: number;
782
+ }>;
783
+ /**
784
+ * Get issues for the tester by category.
785
+ * Returns enriched data: done issues include verification proof,
786
+ * reopened issues include original bug context.
787
+ */
788
+ getIssues(category: 'open' | 'done' | 'reopened'): Promise<TesterIssue[]>;
742
789
  /**
743
790
  * Basic email format validation (defense in depth)
744
791
  */
@@ -1018,4 +1065,4 @@ declare function captureError(error: Error, errorInfo?: {
1018
1065
  componentStack?: string;
1019
1066
  };
1020
1067
 
1021
- export { type AddFindingOptions, type AppContext, BugBearClient, type BugBearConfig, type BugBearReport, type BugBearTheme, 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 MessageSenderType, type NetworkRequest, type PriorityFactors, type ProjectRole, type QAFinding, type QAHealthMetrics, type QAHealthScore, type QASession, type QASessionStatus, type QATrack, type RegressionEvent, type ReportStatus, type ReportType, type RoutePriority, type RouteTestStats, type RubricMode, type RubricResult, type Severity, type SkipReason, type StartSessionOptions, type SubmitFeedbackOptions, type TestAssignment, type TestFeedback, type TestGroup, type TestResult, type TestStep, type TestTemplate, type TesterInfo, type TesterMessage, type TesterProfileUpdate, type TesterThread, type ThreadPriority, type ThreadType, captureError, contextCapture, createBugBear };
1068
+ export { type AddFindingOptions, type AppContext, BugBearClient, type BugBearConfig, type BugBearReport, type BugBearTheme, 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, type MessageSenderType, type NetworkRequest, type PriorityFactors, type ProjectRole, type QAFinding, type QAHealthMetrics, type QAHealthScore, type QASession, type QASessionStatus, type QATrack, type RegressionEvent, type ReportStatus, type ReportType, type RoutePriority, type RouteTestStats, type RubricMode, type RubricResult, type Severity, type SkipReason, type StartSessionOptions, 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 };
package/dist/index.d.ts CHANGED
@@ -610,6 +610,37 @@ interface AddFindingOptions {
610
610
  deviceInfo?: DeviceInfo;
611
611
  appContext?: AppContext;
612
612
  }
613
+ /** Category for filtering tester issues in the widget */
614
+ type IssueCategory = 'open' | 'done' | 'reopened';
615
+ /** Issue counts for each category (HomeScreen cards) */
616
+ interface IssueCounts {
617
+ open: number;
618
+ done: number;
619
+ reopened: number;
620
+ }
621
+ /** A report as seen by the tester in the widget */
622
+ interface TesterIssue {
623
+ id: string;
624
+ title: string;
625
+ description: string;
626
+ reportType: ReportType;
627
+ severity: Severity | null;
628
+ status: ReportStatus;
629
+ screenshotUrls: string[];
630
+ /** Route where the issue was observed (from app_context) */
631
+ route?: string;
632
+ reporterName?: string;
633
+ createdAt: string;
634
+ updatedAt: string;
635
+ /** Who verified/retested this issue (for done issues) */
636
+ verifiedByName?: string;
637
+ /** When verification passed (for done issues) */
638
+ verifiedAt?: string;
639
+ /** Original bug report ID (for reopened/test_fail issues) */
640
+ originalBugId?: string;
641
+ /** Original bug title (for reopened/test_fail issues) */
642
+ originalBugTitle?: string;
643
+ }
613
644
 
614
645
  /**
615
646
  * BugBear Client
@@ -620,6 +651,7 @@ declare class BugBearClient {
620
651
  private supabase;
621
652
  private config;
622
653
  private navigationHistory;
654
+ private reportSubmitInFlight;
623
655
  constructor(config: BugBearConfig);
624
656
  /**
625
657
  * Track navigation for context.
@@ -739,6 +771,21 @@ declare class BugBearClient {
739
771
  skipped: number;
740
772
  total: number;
741
773
  } | null>;
774
+ /**
775
+ * Get issue counts for the tester (Open, Done, Reopened)
776
+ * Used by the widget HomeScreen cards
777
+ */
778
+ getIssueCounts(): Promise<{
779
+ open: number;
780
+ done: number;
781
+ reopened: number;
782
+ }>;
783
+ /**
784
+ * Get issues for the tester by category.
785
+ * Returns enriched data: done issues include verification proof,
786
+ * reopened issues include original bug context.
787
+ */
788
+ getIssues(category: 'open' | 'done' | 'reopened'): Promise<TesterIssue[]>;
742
789
  /**
743
790
  * Basic email format validation (defense in depth)
744
791
  */
@@ -1018,4 +1065,4 @@ declare function captureError(error: Error, errorInfo?: {
1018
1065
  componentStack?: string;
1019
1066
  };
1020
1067
 
1021
- export { type AddFindingOptions, type AppContext, BugBearClient, type BugBearConfig, type BugBearReport, type BugBearTheme, 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 MessageSenderType, type NetworkRequest, type PriorityFactors, type ProjectRole, type QAFinding, type QAHealthMetrics, type QAHealthScore, type QASession, type QASessionStatus, type QATrack, type RegressionEvent, type ReportStatus, type ReportType, type RoutePriority, type RouteTestStats, type RubricMode, type RubricResult, type Severity, type SkipReason, type StartSessionOptions, type SubmitFeedbackOptions, type TestAssignment, type TestFeedback, type TestGroup, type TestResult, type TestStep, type TestTemplate, type TesterInfo, type TesterMessage, type TesterProfileUpdate, type TesterThread, type ThreadPriority, type ThreadType, captureError, contextCapture, createBugBear };
1068
+ export { type AddFindingOptions, type AppContext, BugBearClient, type BugBearConfig, type BugBearReport, type BugBearTheme, 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, type MessageSenderType, type NetworkRequest, type PriorityFactors, type ProjectRole, type QAFinding, type QAHealthMetrics, type QAHealthScore, type QASession, type QASessionStatus, type QATrack, type RegressionEvent, type ReportStatus, type ReportType, type RoutePriority, type RouteTestStats, type RubricMode, type RubricResult, type Severity, type SkipReason, type StartSessionOptions, 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 };
package/dist/index.js CHANGED
@@ -292,6 +292,7 @@ var HOSTED_BUGBEAR_ANON_KEY = getEnvVar("BUGBEAR_ANON_KEY") || getEnvVar("NEXT_P
292
292
  var BugBearClient = class {
293
293
  constructor(config) {
294
294
  this.navigationHistory = [];
295
+ this.reportSubmitInFlight = false;
295
296
  this.config = config;
296
297
  this.supabase = (0, import_supabase_js.createClient)(
297
298
  config.supabaseUrl || DEFAULT_SUPABASE_URL,
@@ -352,6 +353,10 @@ var BugBearClient = class {
352
353
  * Submit a report
353
354
  */
354
355
  async submitReport(report) {
356
+ if (this.reportSubmitInFlight) {
357
+ return { success: false, error: "A report is already being submitted" };
358
+ }
359
+ this.reportSubmitInFlight = true;
355
360
  try {
356
361
  const validationError = this.validateReport(report);
357
362
  if (validationError) {
@@ -403,6 +408,8 @@ var BugBearClient = class {
403
408
  } catch (err) {
404
409
  const message = err instanceof Error ? err.message : "Unknown error";
405
410
  return { success: false, error: message };
411
+ } finally {
412
+ this.reportSubmitInFlight = false;
406
413
  }
407
414
  }
408
415
  /**
@@ -879,6 +886,72 @@ var BugBearClient = class {
879
886
  return null;
880
887
  }
881
888
  }
889
+ /**
890
+ * Get issue counts for the tester (Open, Done, Reopened)
891
+ * Used by the widget HomeScreen cards
892
+ */
893
+ async getIssueCounts() {
894
+ try {
895
+ const testerInfo = await this.getTesterInfo();
896
+ if (!testerInfo) return { open: 0, done: 0, reopened: 0 };
897
+ const { data, error } = await this.supabase.rpc("get_tester_issue_counts", {
898
+ p_project_id: this.config.projectId,
899
+ p_tester_id: testerInfo.id
900
+ });
901
+ if (error) {
902
+ console.error("BugBear: Failed to fetch issue counts", formatPgError(error));
903
+ return { open: 0, done: 0, reopened: 0 };
904
+ }
905
+ return {
906
+ open: data?.open ?? 0,
907
+ done: data?.done ?? 0,
908
+ reopened: data?.reopened ?? 0
909
+ };
910
+ } catch (err) {
911
+ console.error("BugBear: Error fetching issue counts", err);
912
+ return { open: 0, done: 0, reopened: 0 };
913
+ }
914
+ }
915
+ /**
916
+ * Get issues for the tester by category.
917
+ * Returns enriched data: done issues include verification proof,
918
+ * reopened issues include original bug context.
919
+ */
920
+ async getIssues(category) {
921
+ try {
922
+ const testerInfo = await this.getTesterInfo();
923
+ if (!testerInfo) return [];
924
+ const { data, error } = await this.supabase.rpc("get_tester_issues", {
925
+ p_project_id: this.config.projectId,
926
+ p_tester_id: testerInfo.id,
927
+ p_category: category
928
+ });
929
+ if (error) {
930
+ console.error("BugBear: Failed to fetch issues", formatPgError(error));
931
+ return [];
932
+ }
933
+ return (data || []).map((row) => ({
934
+ id: row.id,
935
+ title: row.title || "Untitled",
936
+ description: row.description,
937
+ reportType: row.report_type,
938
+ severity: row.severity || null,
939
+ status: row.status,
940
+ screenshotUrls: row.screenshot_urls || [],
941
+ route: row.app_context?.currentRoute || void 0,
942
+ reporterName: row.reporter_name || void 0,
943
+ createdAt: row.created_at,
944
+ updatedAt: row.updated_at,
945
+ verifiedByName: row.verified_by_name || void 0,
946
+ verifiedAt: row.verified_at || void 0,
947
+ originalBugId: row.original_bug_id || void 0,
948
+ originalBugTitle: row.original_bug_title || void 0
949
+ }));
950
+ } catch (err) {
951
+ console.error("BugBear: Error fetching issues", err);
952
+ return [];
953
+ }
954
+ }
882
955
  /**
883
956
  * Basic email format validation (defense in depth)
884
957
  */
@@ -911,8 +984,8 @@ var BugBearClient = class {
911
984
  return "Maximum 10 screenshots allowed";
912
985
  }
913
986
  for (const url of report.screenshots) {
914
- if (typeof url !== "string" || url.length > 2e3) {
915
- return "Invalid screenshot URL";
987
+ if (typeof url !== "string" || url.length > 2e3 || !/^https?:\/\//i.test(url)) {
988
+ return "Invalid screenshot URL (must be an HTTP/HTTPS URL)";
916
989
  }
917
990
  }
918
991
  }
@@ -1301,7 +1374,10 @@ var BugBearClient = class {
1301
1374
  content
1302
1375
  };
1303
1376
  if (attachments && attachments.length > 0) {
1304
- insertData.attachments = attachments;
1377
+ const safeAttachments = attachments.filter((a) => /^https?:\/\//i.test(a.url));
1378
+ if (safeAttachments.length > 0) {
1379
+ insertData.attachments = safeAttachments;
1380
+ }
1305
1381
  }
1306
1382
  const { error } = await this.supabase.from("discussion_messages").insert(insertData);
1307
1383
  if (error) {
package/dist/index.mjs CHANGED
@@ -263,6 +263,7 @@ var HOSTED_BUGBEAR_ANON_KEY = getEnvVar("BUGBEAR_ANON_KEY") || getEnvVar("NEXT_P
263
263
  var BugBearClient = class {
264
264
  constructor(config) {
265
265
  this.navigationHistory = [];
266
+ this.reportSubmitInFlight = false;
266
267
  this.config = config;
267
268
  this.supabase = createClient(
268
269
  config.supabaseUrl || DEFAULT_SUPABASE_URL,
@@ -323,6 +324,10 @@ var BugBearClient = class {
323
324
  * Submit a report
324
325
  */
325
326
  async submitReport(report) {
327
+ if (this.reportSubmitInFlight) {
328
+ return { success: false, error: "A report is already being submitted" };
329
+ }
330
+ this.reportSubmitInFlight = true;
326
331
  try {
327
332
  const validationError = this.validateReport(report);
328
333
  if (validationError) {
@@ -374,6 +379,8 @@ var BugBearClient = class {
374
379
  } catch (err) {
375
380
  const message = err instanceof Error ? err.message : "Unknown error";
376
381
  return { success: false, error: message };
382
+ } finally {
383
+ this.reportSubmitInFlight = false;
377
384
  }
378
385
  }
379
386
  /**
@@ -850,6 +857,72 @@ var BugBearClient = class {
850
857
  return null;
851
858
  }
852
859
  }
860
+ /**
861
+ * Get issue counts for the tester (Open, Done, Reopened)
862
+ * Used by the widget HomeScreen cards
863
+ */
864
+ async getIssueCounts() {
865
+ try {
866
+ const testerInfo = await this.getTesterInfo();
867
+ if (!testerInfo) return { open: 0, done: 0, reopened: 0 };
868
+ const { data, error } = await this.supabase.rpc("get_tester_issue_counts", {
869
+ p_project_id: this.config.projectId,
870
+ p_tester_id: testerInfo.id
871
+ });
872
+ if (error) {
873
+ console.error("BugBear: Failed to fetch issue counts", formatPgError(error));
874
+ return { open: 0, done: 0, reopened: 0 };
875
+ }
876
+ return {
877
+ open: data?.open ?? 0,
878
+ done: data?.done ?? 0,
879
+ reopened: data?.reopened ?? 0
880
+ };
881
+ } catch (err) {
882
+ console.error("BugBear: Error fetching issue counts", err);
883
+ return { open: 0, done: 0, reopened: 0 };
884
+ }
885
+ }
886
+ /**
887
+ * Get issues for the tester by category.
888
+ * Returns enriched data: done issues include verification proof,
889
+ * reopened issues include original bug context.
890
+ */
891
+ async getIssues(category) {
892
+ try {
893
+ const testerInfo = await this.getTesterInfo();
894
+ if (!testerInfo) return [];
895
+ const { data, error } = await this.supabase.rpc("get_tester_issues", {
896
+ p_project_id: this.config.projectId,
897
+ p_tester_id: testerInfo.id,
898
+ p_category: category
899
+ });
900
+ if (error) {
901
+ console.error("BugBear: Failed to fetch issues", formatPgError(error));
902
+ return [];
903
+ }
904
+ return (data || []).map((row) => ({
905
+ id: row.id,
906
+ title: row.title || "Untitled",
907
+ description: row.description,
908
+ reportType: row.report_type,
909
+ severity: row.severity || null,
910
+ status: row.status,
911
+ screenshotUrls: row.screenshot_urls || [],
912
+ route: row.app_context?.currentRoute || void 0,
913
+ reporterName: row.reporter_name || void 0,
914
+ createdAt: row.created_at,
915
+ updatedAt: row.updated_at,
916
+ verifiedByName: row.verified_by_name || void 0,
917
+ verifiedAt: row.verified_at || void 0,
918
+ originalBugId: row.original_bug_id || void 0,
919
+ originalBugTitle: row.original_bug_title || void 0
920
+ }));
921
+ } catch (err) {
922
+ console.error("BugBear: Error fetching issues", err);
923
+ return [];
924
+ }
925
+ }
853
926
  /**
854
927
  * Basic email format validation (defense in depth)
855
928
  */
@@ -882,8 +955,8 @@ var BugBearClient = class {
882
955
  return "Maximum 10 screenshots allowed";
883
956
  }
884
957
  for (const url of report.screenshots) {
885
- if (typeof url !== "string" || url.length > 2e3) {
886
- return "Invalid screenshot URL";
958
+ if (typeof url !== "string" || url.length > 2e3 || !/^https?:\/\//i.test(url)) {
959
+ return "Invalid screenshot URL (must be an HTTP/HTTPS URL)";
887
960
  }
888
961
  }
889
962
  }
@@ -1272,7 +1345,10 @@ var BugBearClient = class {
1272
1345
  content
1273
1346
  };
1274
1347
  if (attachments && attachments.length > 0) {
1275
- insertData.attachments = attachments;
1348
+ const safeAttachments = attachments.filter((a) => /^https?:\/\//i.test(a.url));
1349
+ if (safeAttachments.length > 0) {
1350
+ insertData.attachments = safeAttachments;
1351
+ }
1276
1352
  }
1277
1353
  const { error } = await this.supabase.from("discussion_messages").insert(insertData);
1278
1354
  if (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbearai/core",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Core utilities and types for BugBear QA platform",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",