quick-bug-reporter-react 1.3.1 → 1.4.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.cjs CHANGED
@@ -882,42 +882,100 @@ var CloudIntegration = class {
882
882
  throw new Error("CloudIntegration: projectKey is required.");
883
883
  }
884
884
  this.projectKey = options.projectKey;
885
- this.endpoint = options.endpoint ?? "/api/ingest";
885
+ this.endpoint = options.ingestUrl ?? options.endpoint ?? "/api/ingest";
886
+ this.appVersion = options.appVersion;
887
+ this.environment = options.environment;
886
888
  this.fetchFn = options.fetchImpl ?? fetch.bind(globalThis);
887
889
  }
888
890
  async submit(payload, onProgress = noop3) {
889
891
  onProgress("Preparing report\u2026");
890
- const ua = payload.userAgent || navigator.userAgent;
892
+ const ua = payload.userAgent || getRuntimeUserAgent();
891
893
  const browserName = parseBrowserName(ua);
894
+ const browserVersion = parseBrowserVersion(ua);
892
895
  const osName = parseOsName(ua);
893
- const body = {
894
- project_key: this.projectKey,
895
- title: payload.title,
896
- provider: "cloud",
897
- capture_mode: payload.captureMode,
898
- has_screenshot: Boolean(payload.screenshotBlob),
899
- has_video: Boolean(payload.videoBlob),
900
- has_network_logs: payload.networkLogs.length > 0,
901
- has_console_logs: payload.consoleLogs.length > 0,
902
- js_error_count: payload.jsErrors.length,
903
- user_agent: ua,
904
- browser_name: browserName,
905
- os_name: osName,
906
- device_type: getDeviceType(),
907
- screen_resolution: getScreenResolution(),
908
- viewport: getViewport(),
909
- color_scheme: payload.metadata.colorScheme !== "unknown" ? payload.metadata.colorScheme : null,
910
- locale: payload.metadata.locale,
911
- timezone: payload.metadata.timezone,
912
- connection_type: payload.metadata.connection?.effectiveType ?? null,
913
- page_url: payload.pageUrl,
914
- environment: getEnvironment()
915
- };
896
+ const osVersion = parseOsVersion(ua, osName);
897
+ const fd = new FormData();
898
+ fd.set("project_key", this.projectKey);
899
+ fd.set("title", payload.title);
900
+ fd.set("description", payload.description || "");
901
+ fd.set("provider", "cloud");
902
+ fd.set("capture_mode", payload.captureMode);
903
+ fd.set("has_screenshot", String(Boolean(payload.screenshotBlob)));
904
+ fd.set("has_video", String(Boolean(payload.videoBlob)));
905
+ fd.set("has_network_logs", String(payload.networkLogs.length > 0));
906
+ fd.set("has_console_logs", String(payload.consoleLogs.length > 0));
907
+ fd.set("js_error_count", String(payload.jsErrors.length));
908
+ fd.set("user_agent", ua);
909
+ fd.set("browser_name", browserName);
910
+ fd.set("browser_version", browserVersion ?? "");
911
+ fd.set("os_name", osName);
912
+ fd.set("os_version", osVersion ?? "");
913
+ fd.set("device_type", getDeviceType());
914
+ fd.set("screen_resolution", getScreenResolution());
915
+ fd.set("viewport", getViewport());
916
+ fd.set("color_scheme", payload.metadata.colorScheme !== "unknown" ? payload.metadata.colorScheme : "");
917
+ fd.set("locale", payload.metadata.locale ?? "");
918
+ fd.set("timezone", payload.metadata.timezone ?? "");
919
+ fd.set("connection_type", payload.metadata.connection?.effectiveType ?? "");
920
+ fd.set("page_url", payload.pageUrl || "");
921
+ fd.set("environment", this.environment ?? getEnvironment());
922
+ fd.set("app_version", this.appVersion ?? "");
923
+ fd.set("platform", payload.metadata.platform ?? "");
924
+ fd.set("duration_ms", String(payload.elapsedMs));
925
+ fd.set("stopped_at", payload.stoppedAt || "");
926
+ if (payload.metadata.mobile) {
927
+ fd.set("platform", payload.metadata.mobile.platform);
928
+ fd.set("device_model", payload.metadata.mobile.deviceModel ?? "");
929
+ fd.set("device_brand", payload.metadata.mobile.deviceBrand ?? "");
930
+ fd.set("os_version", payload.metadata.mobile.osVersion ?? "");
931
+ fd.set("app_build_number", payload.metadata.mobile.appBuildNumber ?? "");
932
+ fd.set("is_emulator", String(payload.metadata.mobile.isEmulator));
933
+ fd.set(
934
+ "battery_level",
935
+ payload.metadata.mobile.batteryLevel == null ? "" : String(payload.metadata.mobile.batteryLevel)
936
+ );
937
+ fd.set(
938
+ "free_storage_mb",
939
+ payload.metadata.mobile.freeStorageMb == null ? "" : String(payload.metadata.mobile.freeStorageMb)
940
+ );
941
+ fd.set("invocation_method", payload.metadata.mobile.invocationMethod);
942
+ }
943
+ if (payload.screenshotBlob) {
944
+ fd.append("screenshot", payload.screenshotBlob, "bug-screenshot.png");
945
+ }
946
+ if (payload.videoBlob) {
947
+ fd.append("video", payload.videoBlob, "bug-recording.webm");
948
+ }
949
+ fd.append(
950
+ "network_logs",
951
+ new Blob([formatNetworkLogs(payload.networkLogs)], { type: "text/plain" }),
952
+ "network-logs.txt"
953
+ );
954
+ const parts = [];
955
+ if (payload.jsErrors.length > 0) {
956
+ parts.push("=== JavaScript Errors ===\n" + formatJsErrors(payload.jsErrors));
957
+ } else {
958
+ parts.push("=== JavaScript Errors ===\nNo JavaScript errors captured.");
959
+ }
960
+ if (payload.consoleLogs.length > 0) {
961
+ parts.push("=== Console Output ===\n" + formatConsoleLogs(payload.consoleLogs));
962
+ } else {
963
+ parts.push("=== Console Output ===\nNo console output captured.");
964
+ }
965
+ fd.append(
966
+ "console_logs",
967
+ new Blob([parts.join("\n\n")], { type: "text/plain" }),
968
+ "console-logs.txt"
969
+ );
970
+ fd.append(
971
+ "metadata",
972
+ new Blob([JSON.stringify(payload.metadata, null, 2)], { type: "application/json" }),
973
+ "client-metadata.json"
974
+ );
916
975
  onProgress("Sending report\u2026");
917
976
  const response = await this.fetchFn(this.endpoint, {
918
977
  method: "POST",
919
- headers: { "Content-Type": "application/json" },
920
- body: JSON.stringify(body)
978
+ body: fd
921
979
  });
922
980
  if (!response.ok) {
923
981
  const errorBody = await response.json().catch(() => ({}));
@@ -926,12 +984,22 @@ var CloudIntegration = class {
926
984
  }
927
985
  const result = await response.json();
928
986
  onProgress("Report submitted.");
987
+ const fwd = result.forwarding;
988
+ const externalKey = fwd?.key;
989
+ const externalUrl = fwd?.url;
990
+ const warnings = [];
991
+ if (result.forwarding_status === "queued") {
992
+ warnings.push("Tracker forwarding is running in the background.");
993
+ }
994
+ if (fwd?.error) {
995
+ warnings.push(`Forwarding: ${fwd.error}`);
996
+ }
929
997
  return {
930
998
  provider: "cloud",
931
999
  issueId: result.id,
932
- issueKey: `QB-${result.id.slice(0, 8)}`,
933
- issueUrl: null,
934
- warnings: []
1000
+ issueKey: externalKey || `QB-${result.id.slice(0, 8)}`,
1001
+ issueUrl: externalUrl || null,
1002
+ warnings
935
1003
  };
936
1004
  }
937
1005
  };
@@ -943,14 +1011,50 @@ function parseBrowserName(ua) {
943
1011
  if (ua.includes("Safari/") && !ua.includes("Chrome/")) return "Safari";
944
1012
  return "Unknown";
945
1013
  }
1014
+ function parseBrowserVersion(ua) {
1015
+ return matchVersion(ua, /Edg\/([\d.]+)/) || matchVersion(ua, /OPR\/([\d.]+)/) || matchVersion(ua, /Opera\/([\d.]+)/) || matchVersion(ua, /Chrome\/([\d.]+)/) || matchVersion(ua, /Firefox\/([\d.]+)/) || matchVersion(ua, /Version\/([\d.]+).*Safari/);
1016
+ }
1017
+ function getRuntimeUserAgent() {
1018
+ if (typeof navigator !== "undefined" && typeof navigator.userAgent === "string") {
1019
+ return navigator.userAgent;
1020
+ }
1021
+ return "";
1022
+ }
946
1023
  function parseOsName(ua) {
947
1024
  if (ua.includes("Windows")) return "Windows";
948
- if (ua.includes("Mac OS")) return "macOS";
949
- if (ua.includes("Linux")) return "Linux";
950
1025
  if (ua.includes("Android")) return "Android";
951
1026
  if (ua.includes("iPhone") || ua.includes("iPad")) return "iOS";
1027
+ if (ua.includes("Mac OS")) return "macOS";
1028
+ if (ua.includes("Linux")) return "Linux";
952
1029
  return "Unknown";
953
1030
  }
1031
+ function parseOsVersion(ua, osName) {
1032
+ if (osName === "Windows") {
1033
+ const nt = matchVersion(ua, /Windows NT ([\d.]+)/);
1034
+ if (!nt) return null;
1035
+ if (nt.startsWith("10.0")) return "10/11";
1036
+ if (nt.startsWith("6.3")) return "8.1";
1037
+ if (nt.startsWith("6.2")) return "8";
1038
+ if (nt.startsWith("6.1")) return "7";
1039
+ return nt;
1040
+ }
1041
+ if (osName === "macOS") {
1042
+ const mac = matchVersion(ua, /Mac OS X ([0-9_]+)/);
1043
+ return mac ? mac.replace(/_/g, ".") : null;
1044
+ }
1045
+ if (osName === "Android") {
1046
+ return matchVersion(ua, /Android ([\d.]+)/);
1047
+ }
1048
+ if (osName === "iOS") {
1049
+ const ios = matchVersion(ua, /OS ([0-9_]+) like Mac OS X/);
1050
+ return ios ? ios.replace(/_/g, ".") : null;
1051
+ }
1052
+ return null;
1053
+ }
1054
+ function matchVersion(ua, pattern) {
1055
+ const match = ua.match(pattern);
1056
+ return match?.[1] ?? null;
1057
+ }
954
1058
  function getDeviceType() {
955
1059
  if (typeof window === "undefined") return "unknown";
956
1060
  const w = window.innerWidth;
@@ -1429,6 +1533,7 @@ var BugSession = class {
1429
1533
  this.autoStopTimeout = null;
1430
1534
  this.stopInFlight = null;
1431
1535
  this.lastArtifacts = null;
1536
+ this.screenshotLogsPendingSubmit = false;
1432
1537
  this.maxDurationMs = options.maxDurationMs ?? DEFAULT_MAX_RECORDING_MS;
1433
1538
  this.screenRecorder = options.screenRecorder ?? new ScreenRecorder();
1434
1539
  this.screenshotCapturer = options.screenshotCapturer ?? new ScreenshotCapturer();
@@ -1439,9 +1544,13 @@ var BugSession = class {
1439
1544
  if (this.recording) {
1440
1545
  return;
1441
1546
  }
1547
+ if (this.networkLogger.isRecording()) {
1548
+ this.networkLogger.stop();
1549
+ }
1442
1550
  this.clearAutoStopTimer();
1443
1551
  this.networkLogger.clear();
1444
1552
  this.lastArtifacts = null;
1553
+ this.screenshotLogsPendingSubmit = false;
1445
1554
  this.networkLogger.start();
1446
1555
  try {
1447
1556
  await this.screenRecorder.start({
@@ -1464,14 +1573,18 @@ var BugSession = class {
1464
1573
  if (this.recording) {
1465
1574
  await this.stop("manual");
1466
1575
  }
1576
+ if (this.networkLogger.isRecording()) {
1577
+ this.networkLogger.stop();
1578
+ }
1467
1579
  this.clearAutoStopTimer();
1468
1580
  this.networkLogger.clear();
1469
1581
  this.lastArtifacts = null;
1582
+ this.screenshotLogsPendingSubmit = false;
1470
1583
  const startedAtMs = Date.now();
1471
1584
  this.networkLogger.start();
1472
1585
  try {
1473
1586
  const screenshotBlob = region ? await this.screenshotCapturer.captureRegion(region) : await this.screenshotCapturer.capture();
1474
- const networkLogs = this.networkLogger.stop();
1587
+ const networkLogs = this.networkLogger.getLogs();
1475
1588
  const stoppedAtMs = Date.now();
1476
1589
  const artifacts = {
1477
1590
  videoBlob: null,
@@ -1484,10 +1597,12 @@ var BugSession = class {
1484
1597
  stopReason: "manual"
1485
1598
  };
1486
1599
  this.lastArtifacts = artifacts;
1600
+ this.screenshotLogsPendingSubmit = true;
1487
1601
  return artifacts;
1488
1602
  } catch (error) {
1489
1603
  this.networkLogger.stop();
1490
1604
  this.networkLogger.clear();
1605
+ this.screenshotLogsPendingSubmit = false;
1491
1606
  throw error;
1492
1607
  }
1493
1608
  }
@@ -1521,17 +1636,32 @@ var BugSession = class {
1521
1636
  getLastCaptureMode() {
1522
1637
  return this.lastArtifacts?.captureMode ?? null;
1523
1638
  }
1639
+ finalizeNetworkLogsForSubmit(captureMode) {
1640
+ if (captureMode === "screenshot" && this.screenshotLogsPendingSubmit && this.networkLogger.isRecording()) {
1641
+ const logs = this.networkLogger.stop();
1642
+ this.screenshotLogsPendingSubmit = false;
1643
+ return logs;
1644
+ }
1645
+ return this.networkLogger.getLogs();
1646
+ }
1524
1647
  resetArtifacts() {
1525
1648
  this.lastArtifacts = null;
1526
1649
  this.screenRecorder.clearLastBlob();
1650
+ if (this.networkLogger.isRecording()) {
1651
+ this.networkLogger.stop();
1652
+ }
1527
1653
  this.networkLogger.clear();
1654
+ this.screenshotLogsPendingSubmit = false;
1528
1655
  }
1529
1656
  async dispose() {
1530
1657
  await this.stop("manual");
1531
1658
  this.clearAutoStopTimer();
1532
1659
  this.screenRecorder.dispose();
1533
- this.networkLogger.stop();
1660
+ if (this.networkLogger.isRecording()) {
1661
+ this.networkLogger.stop();
1662
+ }
1534
1663
  this.networkLogger.clear();
1664
+ this.screenshotLogsPendingSubmit = false;
1535
1665
  }
1536
1666
  async stopInternal(reason) {
1537
1667
  this.clearAutoStopTimer();
@@ -1552,6 +1682,7 @@ var BugSession = class {
1552
1682
  stopReason: reason
1553
1683
  };
1554
1684
  this.lastArtifacts = artifacts;
1685
+ this.screenshotLogsPendingSubmit = false;
1555
1686
  return artifacts;
1556
1687
  }
1557
1688
  async handleForcedStop(reason) {
@@ -1704,7 +1835,7 @@ var BugReporter = class {
1704
1835
  description: normalizedDescription,
1705
1836
  videoBlob: artifacts.videoBlob,
1706
1837
  screenshotBlob: options.screenshotBlob ?? artifacts.screenshotBlob,
1707
- networkLogs: artifacts.networkLogs,
1838
+ networkLogs: this.session.finalizeNetworkLogsForSubmit(artifacts.captureMode),
1708
1839
  consoleLogs: options.consoleLogs ?? [],
1709
1840
  jsErrors: options.jsErrors ?? [],
1710
1841
  captureMode: artifacts.captureMode,
@@ -2274,7 +2405,11 @@ function BugReporterProvider({
2274
2405
  jsErrors,
2275
2406
  onProgress: setSubmissionProgress
2276
2407
  });
2277
- setSuccess(`Submitted to ${getProviderLabel(result.provider)} (${result.issueKey}).`);
2408
+ if (result.provider === "cloud" && !result.issueUrl) {
2409
+ setSuccess("Report received by QuickBugs Cloud. Tracker forwarding is running in the background.");
2410
+ } else {
2411
+ setSuccess(`Submitted to ${getProviderLabel(result.provider)} (${result.issueKey}).`);
2412
+ }
2278
2413
  clearDraft();
2279
2414
  setIsOpen(false);
2280
2415
  return result;