quick-bug-reporter-react 1.3.2 → 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.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { NetworkLogger, BugSessionArtifacts, RecordingStopReason, ReportCaptureMode, BugReporterIntegration, BugClientMetadata, ConsoleLogEntry, CapturedJsError, SubmitProgressCallback, BugSubmitResult, BugTrackerProvider, BugReporterIntegrations, ScreenshotHighlightRegion } from '@quick-bug-reporter/core';
1
+ import { NetworkLogger, BugSessionArtifacts, RecordingStopReason, ReportCaptureMode, NetworkLogEntry, BugReporterIntegration, BugClientMetadata, ConsoleLogEntry, CapturedJsError, SubmitProgressCallback, BugSubmitResult, BugTrackerProvider, BugReporterIntegrations, ScreenshotHighlightRegion } from '@quick-bug-reporter/core';
2
2
  export { BugClientMetadata, BugReportPayload, BugReporterIntegration, BugReporterIntegrations, BugSessionArtifacts, BugSubmitResult, BugTrackerProvider, CapturedJsError, CloudIntegration, CloudIntegrationOptions, ConsoleCapture, ConsoleLogEntry, DEFAULT_MAX_RECORDING_MS, JiraIntegration, JiraIntegrationOptions, LinearIntegration, LinearIntegrationOptions, NetworkLogEntry, NetworkLogger, RecordingStopReason, ReportCaptureMode, ScreenshotHighlightRegion, SubmitProgressCallback, formatConsoleLogs, formatJsErrors, formatNetworkLogs, toErrorMessage } from '@quick-bug-reporter/core';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode } from 'react';
@@ -62,6 +62,7 @@ declare class BugSession {
62
62
  private autoStopTimeout;
63
63
  private stopInFlight;
64
64
  private lastArtifacts;
65
+ private screenshotLogsPendingSubmit;
65
66
  constructor(options?: BugSessionOptions);
66
67
  start(): Promise<void>;
67
68
  captureScreenshot(region?: CaptureRegion): Promise<BugSessionArtifacts>;
@@ -71,6 +72,7 @@ declare class BugSession {
71
72
  getMaxDurationMs(): number;
72
73
  getLastArtifacts(): BugSessionArtifacts | null;
73
74
  getLastCaptureMode(): ReportCaptureMode | null;
75
+ finalizeNetworkLogsForSubmit(captureMode: ReportCaptureMode): NetworkLogEntry[];
74
76
  resetArtifacts(): void;
75
77
  dispose(): Promise<void>;
76
78
  private stopInternal;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { NetworkLogger, BugSessionArtifacts, RecordingStopReason, ReportCaptureMode, BugReporterIntegration, BugClientMetadata, ConsoleLogEntry, CapturedJsError, SubmitProgressCallback, BugSubmitResult, BugTrackerProvider, BugReporterIntegrations, ScreenshotHighlightRegion } from '@quick-bug-reporter/core';
1
+ import { NetworkLogger, BugSessionArtifacts, RecordingStopReason, ReportCaptureMode, NetworkLogEntry, BugReporterIntegration, BugClientMetadata, ConsoleLogEntry, CapturedJsError, SubmitProgressCallback, BugSubmitResult, BugTrackerProvider, BugReporterIntegrations, ScreenshotHighlightRegion } from '@quick-bug-reporter/core';
2
2
  export { BugClientMetadata, BugReportPayload, BugReporterIntegration, BugReporterIntegrations, BugSessionArtifacts, BugSubmitResult, BugTrackerProvider, CapturedJsError, CloudIntegration, CloudIntegrationOptions, ConsoleCapture, ConsoleLogEntry, DEFAULT_MAX_RECORDING_MS, JiraIntegration, JiraIntegrationOptions, LinearIntegration, LinearIntegrationOptions, NetworkLogEntry, NetworkLogger, RecordingStopReason, ReportCaptureMode, ScreenshotHighlightRegion, SubmitProgressCallback, formatConsoleLogs, formatJsErrors, formatNetworkLogs, toErrorMessage } from '@quick-bug-reporter/core';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { ReactNode } from 'react';
@@ -62,6 +62,7 @@ declare class BugSession {
62
62
  private autoStopTimeout;
63
63
  private stopInFlight;
64
64
  private lastArtifacts;
65
+ private screenshotLogsPendingSubmit;
65
66
  constructor(options?: BugSessionOptions);
66
67
  start(): Promise<void>;
67
68
  captureScreenshot(region?: CaptureRegion): Promise<BugSessionArtifacts>;
@@ -71,6 +72,7 @@ declare class BugSession {
71
72
  getMaxDurationMs(): number;
72
73
  getLastArtifacts(): BugSessionArtifacts | null;
73
74
  getLastCaptureMode(): ReportCaptureMode | null;
75
+ finalizeNetworkLogsForSubmit(captureMode: ReportCaptureMode): NetworkLogEntry[];
74
76
  resetArtifacts(): void;
75
77
  dispose(): Promise<void>;
76
78
  private stopInternal;
package/dist/index.js CHANGED
@@ -876,42 +876,100 @@ var CloudIntegration = class {
876
876
  throw new Error("CloudIntegration: projectKey is required.");
877
877
  }
878
878
  this.projectKey = options.projectKey;
879
- this.endpoint = options.endpoint ?? "/api/ingest";
879
+ this.endpoint = options.ingestUrl ?? options.endpoint ?? "/api/ingest";
880
+ this.appVersion = options.appVersion;
881
+ this.environment = options.environment;
880
882
  this.fetchFn = options.fetchImpl ?? fetch.bind(globalThis);
881
883
  }
882
884
  async submit(payload, onProgress = noop3) {
883
885
  onProgress("Preparing report\u2026");
884
- const ua = payload.userAgent || navigator.userAgent;
886
+ const ua = payload.userAgent || getRuntimeUserAgent();
885
887
  const browserName = parseBrowserName(ua);
888
+ const browserVersion = parseBrowserVersion(ua);
886
889
  const osName = parseOsName(ua);
887
- const body = {
888
- project_key: this.projectKey,
889
- title: payload.title,
890
- provider: "cloud",
891
- capture_mode: payload.captureMode,
892
- has_screenshot: Boolean(payload.screenshotBlob),
893
- has_video: Boolean(payload.videoBlob),
894
- has_network_logs: payload.networkLogs.length > 0,
895
- has_console_logs: payload.consoleLogs.length > 0,
896
- js_error_count: payload.jsErrors.length,
897
- user_agent: ua,
898
- browser_name: browserName,
899
- os_name: osName,
900
- device_type: getDeviceType(),
901
- screen_resolution: getScreenResolution(),
902
- viewport: getViewport(),
903
- color_scheme: payload.metadata.colorScheme !== "unknown" ? payload.metadata.colorScheme : null,
904
- locale: payload.metadata.locale,
905
- timezone: payload.metadata.timezone,
906
- connection_type: payload.metadata.connection?.effectiveType ?? null,
907
- page_url: payload.pageUrl,
908
- environment: getEnvironment()
909
- };
890
+ const osVersion = parseOsVersion(ua, osName);
891
+ const fd = new FormData();
892
+ fd.set("project_key", this.projectKey);
893
+ fd.set("title", payload.title);
894
+ fd.set("description", payload.description || "");
895
+ fd.set("provider", "cloud");
896
+ fd.set("capture_mode", payload.captureMode);
897
+ fd.set("has_screenshot", String(Boolean(payload.screenshotBlob)));
898
+ fd.set("has_video", String(Boolean(payload.videoBlob)));
899
+ fd.set("has_network_logs", String(payload.networkLogs.length > 0));
900
+ fd.set("has_console_logs", String(payload.consoleLogs.length > 0));
901
+ fd.set("js_error_count", String(payload.jsErrors.length));
902
+ fd.set("user_agent", ua);
903
+ fd.set("browser_name", browserName);
904
+ fd.set("browser_version", browserVersion ?? "");
905
+ fd.set("os_name", osName);
906
+ fd.set("os_version", osVersion ?? "");
907
+ fd.set("device_type", getDeviceType());
908
+ fd.set("screen_resolution", getScreenResolution());
909
+ fd.set("viewport", getViewport());
910
+ fd.set("color_scheme", payload.metadata.colorScheme !== "unknown" ? payload.metadata.colorScheme : "");
911
+ fd.set("locale", payload.metadata.locale ?? "");
912
+ fd.set("timezone", payload.metadata.timezone ?? "");
913
+ fd.set("connection_type", payload.metadata.connection?.effectiveType ?? "");
914
+ fd.set("page_url", payload.pageUrl || "");
915
+ fd.set("environment", this.environment ?? getEnvironment());
916
+ fd.set("app_version", this.appVersion ?? "");
917
+ fd.set("platform", payload.metadata.platform ?? "");
918
+ fd.set("duration_ms", String(payload.elapsedMs));
919
+ fd.set("stopped_at", payload.stoppedAt || "");
920
+ if (payload.metadata.mobile) {
921
+ fd.set("platform", payload.metadata.mobile.platform);
922
+ fd.set("device_model", payload.metadata.mobile.deviceModel ?? "");
923
+ fd.set("device_brand", payload.metadata.mobile.deviceBrand ?? "");
924
+ fd.set("os_version", payload.metadata.mobile.osVersion ?? "");
925
+ fd.set("app_build_number", payload.metadata.mobile.appBuildNumber ?? "");
926
+ fd.set("is_emulator", String(payload.metadata.mobile.isEmulator));
927
+ fd.set(
928
+ "battery_level",
929
+ payload.metadata.mobile.batteryLevel == null ? "" : String(payload.metadata.mobile.batteryLevel)
930
+ );
931
+ fd.set(
932
+ "free_storage_mb",
933
+ payload.metadata.mobile.freeStorageMb == null ? "" : String(payload.metadata.mobile.freeStorageMb)
934
+ );
935
+ fd.set("invocation_method", payload.metadata.mobile.invocationMethod);
936
+ }
937
+ if (payload.screenshotBlob) {
938
+ fd.append("screenshot", payload.screenshotBlob, "bug-screenshot.png");
939
+ }
940
+ if (payload.videoBlob) {
941
+ fd.append("video", payload.videoBlob, "bug-recording.webm");
942
+ }
943
+ fd.append(
944
+ "network_logs",
945
+ new Blob([formatNetworkLogs(payload.networkLogs)], { type: "text/plain" }),
946
+ "network-logs.txt"
947
+ );
948
+ const parts = [];
949
+ if (payload.jsErrors.length > 0) {
950
+ parts.push("=== JavaScript Errors ===\n" + formatJsErrors(payload.jsErrors));
951
+ } else {
952
+ parts.push("=== JavaScript Errors ===\nNo JavaScript errors captured.");
953
+ }
954
+ if (payload.consoleLogs.length > 0) {
955
+ parts.push("=== Console Output ===\n" + formatConsoleLogs(payload.consoleLogs));
956
+ } else {
957
+ parts.push("=== Console Output ===\nNo console output captured.");
958
+ }
959
+ fd.append(
960
+ "console_logs",
961
+ new Blob([parts.join("\n\n")], { type: "text/plain" }),
962
+ "console-logs.txt"
963
+ );
964
+ fd.append(
965
+ "metadata",
966
+ new Blob([JSON.stringify(payload.metadata, null, 2)], { type: "application/json" }),
967
+ "client-metadata.json"
968
+ );
910
969
  onProgress("Sending report\u2026");
911
970
  const response = await this.fetchFn(this.endpoint, {
912
971
  method: "POST",
913
- headers: { "Content-Type": "application/json" },
914
- body: JSON.stringify(body)
972
+ body: fd
915
973
  });
916
974
  if (!response.ok) {
917
975
  const errorBody = await response.json().catch(() => ({}));
@@ -920,12 +978,22 @@ var CloudIntegration = class {
920
978
  }
921
979
  const result = await response.json();
922
980
  onProgress("Report submitted.");
981
+ const fwd = result.forwarding;
982
+ const externalKey = fwd?.key;
983
+ const externalUrl = fwd?.url;
984
+ const warnings = [];
985
+ if (result.forwarding_status === "queued") {
986
+ warnings.push("Tracker forwarding is running in the background.");
987
+ }
988
+ if (fwd?.error) {
989
+ warnings.push(`Forwarding: ${fwd.error}`);
990
+ }
923
991
  return {
924
992
  provider: "cloud",
925
993
  issueId: result.id,
926
- issueKey: `QB-${result.id.slice(0, 8)}`,
927
- issueUrl: null,
928
- warnings: []
994
+ issueKey: externalKey || `QB-${result.id.slice(0, 8)}`,
995
+ issueUrl: externalUrl || null,
996
+ warnings
929
997
  };
930
998
  }
931
999
  };
@@ -937,14 +1005,50 @@ function parseBrowserName(ua) {
937
1005
  if (ua.includes("Safari/") && !ua.includes("Chrome/")) return "Safari";
938
1006
  return "Unknown";
939
1007
  }
1008
+ function parseBrowserVersion(ua) {
1009
+ 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/);
1010
+ }
1011
+ function getRuntimeUserAgent() {
1012
+ if (typeof navigator !== "undefined" && typeof navigator.userAgent === "string") {
1013
+ return navigator.userAgent;
1014
+ }
1015
+ return "";
1016
+ }
940
1017
  function parseOsName(ua) {
941
1018
  if (ua.includes("Windows")) return "Windows";
942
- if (ua.includes("Mac OS")) return "macOS";
943
- if (ua.includes("Linux")) return "Linux";
944
1019
  if (ua.includes("Android")) return "Android";
945
1020
  if (ua.includes("iPhone") || ua.includes("iPad")) return "iOS";
1021
+ if (ua.includes("Mac OS")) return "macOS";
1022
+ if (ua.includes("Linux")) return "Linux";
946
1023
  return "Unknown";
947
1024
  }
1025
+ function parseOsVersion(ua, osName) {
1026
+ if (osName === "Windows") {
1027
+ const nt = matchVersion(ua, /Windows NT ([\d.]+)/);
1028
+ if (!nt) return null;
1029
+ if (nt.startsWith("10.0")) return "10/11";
1030
+ if (nt.startsWith("6.3")) return "8.1";
1031
+ if (nt.startsWith("6.2")) return "8";
1032
+ if (nt.startsWith("6.1")) return "7";
1033
+ return nt;
1034
+ }
1035
+ if (osName === "macOS") {
1036
+ const mac = matchVersion(ua, /Mac OS X ([0-9_]+)/);
1037
+ return mac ? mac.replace(/_/g, ".") : null;
1038
+ }
1039
+ if (osName === "Android") {
1040
+ return matchVersion(ua, /Android ([\d.]+)/);
1041
+ }
1042
+ if (osName === "iOS") {
1043
+ const ios = matchVersion(ua, /OS ([0-9_]+) like Mac OS X/);
1044
+ return ios ? ios.replace(/_/g, ".") : null;
1045
+ }
1046
+ return null;
1047
+ }
1048
+ function matchVersion(ua, pattern) {
1049
+ const match = ua.match(pattern);
1050
+ return match?.[1] ?? null;
1051
+ }
948
1052
  function getDeviceType() {
949
1053
  if (typeof window === "undefined") return "unknown";
950
1054
  const w = window.innerWidth;
@@ -1423,6 +1527,7 @@ var BugSession = class {
1423
1527
  this.autoStopTimeout = null;
1424
1528
  this.stopInFlight = null;
1425
1529
  this.lastArtifacts = null;
1530
+ this.screenshotLogsPendingSubmit = false;
1426
1531
  this.maxDurationMs = options.maxDurationMs ?? DEFAULT_MAX_RECORDING_MS;
1427
1532
  this.screenRecorder = options.screenRecorder ?? new ScreenRecorder();
1428
1533
  this.screenshotCapturer = options.screenshotCapturer ?? new ScreenshotCapturer();
@@ -1433,9 +1538,13 @@ var BugSession = class {
1433
1538
  if (this.recording) {
1434
1539
  return;
1435
1540
  }
1541
+ if (this.networkLogger.isRecording()) {
1542
+ this.networkLogger.stop();
1543
+ }
1436
1544
  this.clearAutoStopTimer();
1437
1545
  this.networkLogger.clear();
1438
1546
  this.lastArtifacts = null;
1547
+ this.screenshotLogsPendingSubmit = false;
1439
1548
  this.networkLogger.start();
1440
1549
  try {
1441
1550
  await this.screenRecorder.start({
@@ -1458,14 +1567,18 @@ var BugSession = class {
1458
1567
  if (this.recording) {
1459
1568
  await this.stop("manual");
1460
1569
  }
1570
+ if (this.networkLogger.isRecording()) {
1571
+ this.networkLogger.stop();
1572
+ }
1461
1573
  this.clearAutoStopTimer();
1462
1574
  this.networkLogger.clear();
1463
1575
  this.lastArtifacts = null;
1576
+ this.screenshotLogsPendingSubmit = false;
1464
1577
  const startedAtMs = Date.now();
1465
1578
  this.networkLogger.start();
1466
1579
  try {
1467
1580
  const screenshotBlob = region ? await this.screenshotCapturer.captureRegion(region) : await this.screenshotCapturer.capture();
1468
- const networkLogs = this.networkLogger.stop();
1581
+ const networkLogs = this.networkLogger.getLogs();
1469
1582
  const stoppedAtMs = Date.now();
1470
1583
  const artifacts = {
1471
1584
  videoBlob: null,
@@ -1478,10 +1591,12 @@ var BugSession = class {
1478
1591
  stopReason: "manual"
1479
1592
  };
1480
1593
  this.lastArtifacts = artifacts;
1594
+ this.screenshotLogsPendingSubmit = true;
1481
1595
  return artifacts;
1482
1596
  } catch (error) {
1483
1597
  this.networkLogger.stop();
1484
1598
  this.networkLogger.clear();
1599
+ this.screenshotLogsPendingSubmit = false;
1485
1600
  throw error;
1486
1601
  }
1487
1602
  }
@@ -1515,17 +1630,32 @@ var BugSession = class {
1515
1630
  getLastCaptureMode() {
1516
1631
  return this.lastArtifacts?.captureMode ?? null;
1517
1632
  }
1633
+ finalizeNetworkLogsForSubmit(captureMode) {
1634
+ if (captureMode === "screenshot" && this.screenshotLogsPendingSubmit && this.networkLogger.isRecording()) {
1635
+ const logs = this.networkLogger.stop();
1636
+ this.screenshotLogsPendingSubmit = false;
1637
+ return logs;
1638
+ }
1639
+ return this.networkLogger.getLogs();
1640
+ }
1518
1641
  resetArtifacts() {
1519
1642
  this.lastArtifacts = null;
1520
1643
  this.screenRecorder.clearLastBlob();
1644
+ if (this.networkLogger.isRecording()) {
1645
+ this.networkLogger.stop();
1646
+ }
1521
1647
  this.networkLogger.clear();
1648
+ this.screenshotLogsPendingSubmit = false;
1522
1649
  }
1523
1650
  async dispose() {
1524
1651
  await this.stop("manual");
1525
1652
  this.clearAutoStopTimer();
1526
1653
  this.screenRecorder.dispose();
1527
- this.networkLogger.stop();
1654
+ if (this.networkLogger.isRecording()) {
1655
+ this.networkLogger.stop();
1656
+ }
1528
1657
  this.networkLogger.clear();
1658
+ this.screenshotLogsPendingSubmit = false;
1529
1659
  }
1530
1660
  async stopInternal(reason) {
1531
1661
  this.clearAutoStopTimer();
@@ -1546,6 +1676,7 @@ var BugSession = class {
1546
1676
  stopReason: reason
1547
1677
  };
1548
1678
  this.lastArtifacts = artifacts;
1679
+ this.screenshotLogsPendingSubmit = false;
1549
1680
  return artifacts;
1550
1681
  }
1551
1682
  async handleForcedStop(reason) {
@@ -1698,7 +1829,7 @@ var BugReporter = class {
1698
1829
  description: normalizedDescription,
1699
1830
  videoBlob: artifacts.videoBlob,
1700
1831
  screenshotBlob: options.screenshotBlob ?? artifacts.screenshotBlob,
1701
- networkLogs: artifacts.networkLogs,
1832
+ networkLogs: this.session.finalizeNetworkLogsForSubmit(artifacts.captureMode),
1702
1833
  consoleLogs: options.consoleLogs ?? [],
1703
1834
  jsErrors: options.jsErrors ?? [],
1704
1835
  captureMode: artifacts.captureMode,
@@ -2268,7 +2399,11 @@ function BugReporterProvider({
2268
2399
  jsErrors,
2269
2400
  onProgress: setSubmissionProgress
2270
2401
  });
2271
- setSuccess(`Submitted to ${getProviderLabel(result.provider)} (${result.issueKey}).`);
2402
+ if (result.provider === "cloud" && !result.issueUrl) {
2403
+ setSuccess("Report received by QuickBugs Cloud. Tracker forwarding is running in the background.");
2404
+ } else {
2405
+ setSuccess(`Submitted to ${getProviderLabel(result.provider)} (${result.issueKey}).`);
2406
+ }
2272
2407
  clearDraft();
2273
2408
  setIsOpen(false);
2274
2409
  return result;