@bbearai/core 0.5.0 → 0.5.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
@@ -252,6 +252,8 @@ interface TestAssignment {
252
252
  group?: TestGroup;
253
253
  /** Required app role for testing (e.g., Owner, Manager) */
254
254
  role?: ProjectRole;
255
+ /** Platforms this test applies to (e.g., ['ios', 'web']) */
256
+ platforms?: string[];
255
257
  };
256
258
  status: 'pending' | 'in_progress' | 'passed' | 'failed' | 'blocked' | 'skipped';
257
259
  /** Reason for skipping (when status is 'skipped') */
@@ -881,6 +883,14 @@ declare class BugBearClient {
881
883
  error?: string;
882
884
  durationSeconds?: number;
883
885
  }>;
886
+ /**
887
+ * Reopen a completed assignment — sets it back to in_progress with a fresh timer.
888
+ * Clears completed_at and duration_seconds so it can be re-evaluated.
889
+ */
890
+ reopenAssignment(assignmentId: string): Promise<{
891
+ success: boolean;
892
+ error?: string;
893
+ }>;
884
894
  /**
885
895
  * Skip a test assignment with a required reason
886
896
  * Marks the assignment as 'skipped' and records why it was skipped
package/dist/index.d.ts CHANGED
@@ -252,6 +252,8 @@ interface TestAssignment {
252
252
  group?: TestGroup;
253
253
  /** Required app role for testing (e.g., Owner, Manager) */
254
254
  role?: ProjectRole;
255
+ /** Platforms this test applies to (e.g., ['ios', 'web']) */
256
+ platforms?: string[];
255
257
  };
256
258
  status: 'pending' | 'in_progress' | 'passed' | 'failed' | 'blocked' | 'skipped';
257
259
  /** Reason for skipping (when status is 'skipped') */
@@ -881,6 +883,14 @@ declare class BugBearClient {
881
883
  error?: string;
882
884
  durationSeconds?: number;
883
885
  }>;
886
+ /**
887
+ * Reopen a completed assignment — sets it back to in_progress with a fresh timer.
888
+ * Clears completed_at and duration_seconds so it can be re-evaluated.
889
+ */
890
+ reopenAssignment(assignmentId: string): Promise<{
891
+ success: boolean;
892
+ error?: string;
893
+ }>;
884
894
  /**
885
895
  * Skip a test assignment with a required reason
886
896
  * Marks the assignment as 'skipped' and records why it was skipped
package/dist/index.js CHANGED
@@ -661,8 +661,8 @@ var BugBearClient = class {
661
661
  return { success: false, error: rateLimit.error };
662
662
  }
663
663
  if (!userInfo) {
664
- console.error("BugBear: No user info available, cannot submit report");
665
- return { success: false, error: "User not authenticated" };
664
+ console.error("BugBear: No user info available, cannot submit report. Ensure your BugBear config provides a getCurrentUser() callback that returns { id, email }.");
665
+ return { success: false, error: "Unable to identify user. Check that your app passes a getCurrentUser callback to BugBear config." };
666
666
  }
667
667
  const testerInfo = await this.getTesterInfo();
668
668
  fullReport = {
@@ -725,10 +725,11 @@ var BugBearClient = class {
725
725
  const pageSize = Math.min(options?.pageSize ?? 100, 100);
726
726
  const from = (options?.page ?? 0) * pageSize;
727
727
  const to = from + pageSize - 1;
728
- const { data, error } = await this.supabase.from("test_assignments").select(`
728
+ const selectFields = `
729
729
  id,
730
730
  status,
731
731
  started_at,
732
+ completed_at,
732
733
  skip_reason,
733
734
  is_verification,
734
735
  original_report_id,
@@ -763,20 +764,23 @@ var BugBearClient = class {
763
764
  color,
764
765
  description,
765
766
  login_hint
766
- )
767
+ ),
768
+ platforms
767
769
  )
768
- `).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).range(from, to);
769
- if (error) {
770
- console.error("BugBear: Failed to fetch assignments", formatPgError(error));
770
+ `;
771
+ const [pendingResult, completedResult] = await Promise.all([
772
+ this.supabase.from("test_assignments").select(selectFields).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).range(from, to),
773
+ this.supabase.from("test_assignments").select(selectFields).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["passed", "failed", "skipped", "blocked"]).order("completed_at", { ascending: false }).limit(100)
774
+ ]);
775
+ if (pendingResult.error) {
776
+ console.error("BugBear: Failed to fetch assignments", formatPgError(pendingResult.error));
771
777
  return [];
772
778
  }
773
- const mapped = (data || []).filter((item) => {
774
- if (!item.test_case) {
775
- console.warn("BugBear: Assignment returned without test_case", { id: item.id });
776
- return false;
777
- }
778
- return true;
779
- }).map((item) => ({
779
+ const allData = [
780
+ ...pendingResult.data || [],
781
+ ...completedResult.data || []
782
+ ];
783
+ const mapItem = (item) => ({
780
784
  id: item.id,
781
785
  status: item.status,
782
786
  startedAt: item.started_at,
@@ -814,12 +818,24 @@ var BugBearClient = class {
814
818
  color: item.test_case.role.color,
815
819
  description: item.test_case.role.description,
816
820
  loginHint: item.test_case.role.login_hint
817
- } : void 0
821
+ } : void 0,
822
+ platforms: item.test_case.platforms || void 0
818
823
  }
819
- }));
824
+ });
825
+ const mapped = allData.filter((item) => {
826
+ if (!item.test_case) {
827
+ console.warn("BugBear: Assignment returned without test_case", { id: item.id });
828
+ return false;
829
+ }
830
+ return true;
831
+ }).map(mapItem);
820
832
  mapped.sort((a, b) => {
821
833
  if (a.isVerification && !b.isVerification) return -1;
822
834
  if (!a.isVerification && b.isVerification) return 1;
835
+ const aActive = a.status === "pending" || a.status === "in_progress";
836
+ const bActive = b.status === "pending" || b.status === "in_progress";
837
+ if (aActive && !bActive) return -1;
838
+ if (!aActive && bActive) return 1;
823
839
  return 0;
824
840
  });
825
841
  return mapped;
@@ -984,6 +1000,36 @@ var BugBearClient = class {
984
1000
  async failAssignment(assignmentId) {
985
1001
  return this.updateAssignmentStatus(assignmentId, "failed");
986
1002
  }
1003
+ /**
1004
+ * Reopen a completed assignment — sets it back to in_progress with a fresh timer.
1005
+ * Clears completed_at and duration_seconds so it can be re-evaluated.
1006
+ */
1007
+ async reopenAssignment(assignmentId) {
1008
+ try {
1009
+ const { data: current, error: fetchError } = await this.supabase.from("test_assignments").select("status").eq("id", assignmentId).single();
1010
+ if (fetchError || !current) {
1011
+ return { success: false, error: "Assignment not found" };
1012
+ }
1013
+ if (current.status === "pending" || current.status === "in_progress") {
1014
+ return { success: true };
1015
+ }
1016
+ const { error } = await this.supabase.from("test_assignments").update({
1017
+ status: "in_progress",
1018
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
1019
+ completed_at: null,
1020
+ duration_seconds: null,
1021
+ skip_reason: null
1022
+ }).eq("id", assignmentId).eq("status", current.status);
1023
+ if (error) {
1024
+ console.error("BugBear: Failed to reopen assignment", error);
1025
+ return { success: false, error: error.message };
1026
+ }
1027
+ return { success: true };
1028
+ } catch (err) {
1029
+ const message = err instanceof Error ? err.message : "Unknown error";
1030
+ return { success: false, error: message };
1031
+ }
1032
+ }
987
1033
  /**
988
1034
  * Skip a test assignment with a required reason
989
1035
  * Marks the assignment as 'skipped' and records why it was skipped
package/dist/index.mjs CHANGED
@@ -627,8 +627,8 @@ var BugBearClient = class {
627
627
  return { success: false, error: rateLimit.error };
628
628
  }
629
629
  if (!userInfo) {
630
- console.error("BugBear: No user info available, cannot submit report");
631
- return { success: false, error: "User not authenticated" };
630
+ console.error("BugBear: No user info available, cannot submit report. Ensure your BugBear config provides a getCurrentUser() callback that returns { id, email }.");
631
+ return { success: false, error: "Unable to identify user. Check that your app passes a getCurrentUser callback to BugBear config." };
632
632
  }
633
633
  const testerInfo = await this.getTesterInfo();
634
634
  fullReport = {
@@ -691,10 +691,11 @@ var BugBearClient = class {
691
691
  const pageSize = Math.min(options?.pageSize ?? 100, 100);
692
692
  const from = (options?.page ?? 0) * pageSize;
693
693
  const to = from + pageSize - 1;
694
- const { data, error } = await this.supabase.from("test_assignments").select(`
694
+ const selectFields = `
695
695
  id,
696
696
  status,
697
697
  started_at,
698
+ completed_at,
698
699
  skip_reason,
699
700
  is_verification,
700
701
  original_report_id,
@@ -729,20 +730,23 @@ var BugBearClient = class {
729
730
  color,
730
731
  description,
731
732
  login_hint
732
- )
733
+ ),
734
+ platforms
733
735
  )
734
- `).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).range(from, to);
735
- if (error) {
736
- console.error("BugBear: Failed to fetch assignments", formatPgError(error));
736
+ `;
737
+ const [pendingResult, completedResult] = await Promise.all([
738
+ this.supabase.from("test_assignments").select(selectFields).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).range(from, to),
739
+ this.supabase.from("test_assignments").select(selectFields).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["passed", "failed", "skipped", "blocked"]).order("completed_at", { ascending: false }).limit(100)
740
+ ]);
741
+ if (pendingResult.error) {
742
+ console.error("BugBear: Failed to fetch assignments", formatPgError(pendingResult.error));
737
743
  return [];
738
744
  }
739
- const mapped = (data || []).filter((item) => {
740
- if (!item.test_case) {
741
- console.warn("BugBear: Assignment returned without test_case", { id: item.id });
742
- return false;
743
- }
744
- return true;
745
- }).map((item) => ({
745
+ const allData = [
746
+ ...pendingResult.data || [],
747
+ ...completedResult.data || []
748
+ ];
749
+ const mapItem = (item) => ({
746
750
  id: item.id,
747
751
  status: item.status,
748
752
  startedAt: item.started_at,
@@ -780,12 +784,24 @@ var BugBearClient = class {
780
784
  color: item.test_case.role.color,
781
785
  description: item.test_case.role.description,
782
786
  loginHint: item.test_case.role.login_hint
783
- } : void 0
787
+ } : void 0,
788
+ platforms: item.test_case.platforms || void 0
784
789
  }
785
- }));
790
+ });
791
+ const mapped = allData.filter((item) => {
792
+ if (!item.test_case) {
793
+ console.warn("BugBear: Assignment returned without test_case", { id: item.id });
794
+ return false;
795
+ }
796
+ return true;
797
+ }).map(mapItem);
786
798
  mapped.sort((a, b) => {
787
799
  if (a.isVerification && !b.isVerification) return -1;
788
800
  if (!a.isVerification && b.isVerification) return 1;
801
+ const aActive = a.status === "pending" || a.status === "in_progress";
802
+ const bActive = b.status === "pending" || b.status === "in_progress";
803
+ if (aActive && !bActive) return -1;
804
+ if (!aActive && bActive) return 1;
789
805
  return 0;
790
806
  });
791
807
  return mapped;
@@ -950,6 +966,36 @@ var BugBearClient = class {
950
966
  async failAssignment(assignmentId) {
951
967
  return this.updateAssignmentStatus(assignmentId, "failed");
952
968
  }
969
+ /**
970
+ * Reopen a completed assignment — sets it back to in_progress with a fresh timer.
971
+ * Clears completed_at and duration_seconds so it can be re-evaluated.
972
+ */
973
+ async reopenAssignment(assignmentId) {
974
+ try {
975
+ const { data: current, error: fetchError } = await this.supabase.from("test_assignments").select("status").eq("id", assignmentId).single();
976
+ if (fetchError || !current) {
977
+ return { success: false, error: "Assignment not found" };
978
+ }
979
+ if (current.status === "pending" || current.status === "in_progress") {
980
+ return { success: true };
981
+ }
982
+ const { error } = await this.supabase.from("test_assignments").update({
983
+ status: "in_progress",
984
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
985
+ completed_at: null,
986
+ duration_seconds: null,
987
+ skip_reason: null
988
+ }).eq("id", assignmentId).eq("status", current.status);
989
+ if (error) {
990
+ console.error("BugBear: Failed to reopen assignment", error);
991
+ return { success: false, error: error.message };
992
+ }
993
+ return { success: true };
994
+ } catch (err) {
995
+ const message = err instanceof Error ? err.message : "Unknown error";
996
+ return { success: false, error: message };
997
+ }
998
+ }
953
999
  /**
954
1000
  * Skip a test assignment with a required reason
955
1001
  * Marks the assignment as 'skipped' and records why it was skipped
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbearai/core",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Core utilities and types for BugBear QA platform",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",