quick-bug-reporter-react 1.0.6 → 1.1.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.cjs CHANGED
@@ -555,6 +555,31 @@ var ScreenshotCapturer = class {
555
555
 
556
556
  // src/core/types.ts
557
557
  var DEFAULT_MAX_RECORDING_MS = 2 * 60 * 1e3;
558
+ function formatConsoleLogs(logs) {
559
+ if (logs.length === 0) {
560
+ return "No console output captured.";
561
+ }
562
+ return logs.map((entry) => {
563
+ const tag = entry.level.toUpperCase().padEnd(5);
564
+ const args = entry.args.join(" ");
565
+ return `[${entry.timestamp}] ${tag} ${args}`;
566
+ }).join("\n");
567
+ }
568
+ function formatJsErrors(errors) {
569
+ if (errors.length === 0) {
570
+ return "No JavaScript errors captured.";
571
+ }
572
+ return errors.map((entry) => {
573
+ const lines = [`[${entry.timestamp}] ${entry.type}: ${entry.message}`];
574
+ if (entry.source) {
575
+ lines.push(` at ${entry.source}${entry.lineno ? `:${entry.lineno}` : ""}${entry.colno ? `:${entry.colno}` : ""}`);
576
+ }
577
+ if (entry.stack) {
578
+ lines.push(entry.stack.split("\n").map((l) => ` ${l}`).join("\n"));
579
+ }
580
+ return lines.join("\n");
581
+ }).join("\n\n");
582
+ }
558
583
  function formatNetworkLogs(logs) {
559
584
  if (logs.length === 0) {
560
585
  return "No network requests captured.";
@@ -859,6 +884,8 @@ var BugReporter = class {
859
884
  videoBlob: artifacts.videoBlob,
860
885
  screenshotBlob: options.screenshotBlob ?? artifacts.screenshotBlob,
861
886
  networkLogs: artifacts.networkLogs,
887
+ consoleLogs: options.consoleLogs ?? [],
888
+ jsErrors: options.jsErrors ?? [],
862
889
  captureMode: artifacts.captureMode,
863
890
  pageUrl: typeof window !== "undefined" ? window.location.href : "",
864
891
  userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "",
@@ -898,6 +925,124 @@ var BugReporter = class {
898
925
  }
899
926
  };
900
927
 
928
+ // src/core/ConsoleCapture.ts
929
+ var MAX_CONSOLE_ENTRIES = 200;
930
+ var MAX_ERROR_ENTRIES = 50;
931
+ var MAX_ARG_LENGTH = 1e3;
932
+ function serializeArg(arg) {
933
+ if (typeof arg === "string") {
934
+ return arg.length > MAX_ARG_LENGTH ? arg.slice(0, MAX_ARG_LENGTH) + "\u2026" : arg;
935
+ }
936
+ try {
937
+ const json = JSON.stringify(arg);
938
+ return json.length > MAX_ARG_LENGTH ? json.slice(0, MAX_ARG_LENGTH) + "\u2026" : json;
939
+ } catch {
940
+ return String(arg);
941
+ }
942
+ }
943
+ var ConsoleCapture = class {
944
+ constructor() {
945
+ this.consoleLogs = [];
946
+ this.errors = [];
947
+ this.originals = {};
948
+ this.errorHandler = null;
949
+ this.rejectionHandler = null;
950
+ this.active = false;
951
+ }
952
+ start() {
953
+ if (this.active || typeof window === "undefined") {
954
+ return;
955
+ }
956
+ this.active = true;
957
+ this.consoleLogs = [];
958
+ this.errors = [];
959
+ const levels = ["log", "info", "warn", "error"];
960
+ for (const level of levels) {
961
+ const original = console[level];
962
+ this.originals[level] = original;
963
+ const capture = this;
964
+ console[level] = (...args) => {
965
+ try {
966
+ capture.consoleLogs.push({
967
+ level,
968
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
969
+ args: args.map(serializeArg)
970
+ });
971
+ if (capture.consoleLogs.length > MAX_CONSOLE_ENTRIES) {
972
+ capture.consoleLogs.shift();
973
+ }
974
+ } catch {
975
+ }
976
+ original.apply(console, args);
977
+ };
978
+ }
979
+ this.errorHandler = (event) => {
980
+ try {
981
+ this.errors.push({
982
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
983
+ message: event.message || "Unknown error",
984
+ source: event.filename || void 0,
985
+ lineno: event.lineno || void 0,
986
+ colno: event.colno || void 0,
987
+ stack: event.error?.stack || void 0,
988
+ type: "error"
989
+ });
990
+ if (this.errors.length > MAX_ERROR_ENTRIES) {
991
+ this.errors.shift();
992
+ }
993
+ } catch {
994
+ }
995
+ };
996
+ window.addEventListener("error", this.errorHandler);
997
+ this.rejectionHandler = (event) => {
998
+ try {
999
+ const reason = event.reason;
1000
+ this.errors.push({
1001
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1002
+ message: reason instanceof Error ? reason.message : String(reason),
1003
+ stack: reason instanceof Error ? reason.stack || void 0 : void 0,
1004
+ type: "unhandledrejection"
1005
+ });
1006
+ if (this.errors.length > MAX_ERROR_ENTRIES) {
1007
+ this.errors.shift();
1008
+ }
1009
+ } catch {
1010
+ }
1011
+ };
1012
+ window.addEventListener("unhandledrejection", this.rejectionHandler);
1013
+ }
1014
+ snapshot() {
1015
+ return {
1016
+ consoleLogs: [...this.consoleLogs],
1017
+ jsErrors: [...this.errors]
1018
+ };
1019
+ }
1020
+ stop() {
1021
+ if (!this.active) {
1022
+ return;
1023
+ }
1024
+ for (const [level, original] of Object.entries(this.originals)) {
1025
+ if (original) {
1026
+ console[level] = original;
1027
+ }
1028
+ }
1029
+ this.originals = {};
1030
+ if (this.errorHandler) {
1031
+ window.removeEventListener("error", this.errorHandler);
1032
+ this.errorHandler = null;
1033
+ }
1034
+ if (this.rejectionHandler) {
1035
+ window.removeEventListener("unhandledrejection", this.rejectionHandler);
1036
+ this.rejectionHandler = null;
1037
+ }
1038
+ this.active = false;
1039
+ }
1040
+ clear() {
1041
+ this.consoleLogs = [];
1042
+ this.errors = [];
1043
+ }
1044
+ };
1045
+
901
1046
  // src/integrations/linear.ts
902
1047
  var DEFAULT_GRAPHQL_ENDPOINT = "https://api.linear.app/graphql";
903
1048
  var noop = () => {
@@ -979,12 +1124,22 @@ var LinearIntegration = class {
979
1124
  const description = buildCleanDescription(payload, { screenshotUrl, recordingUrl });
980
1125
  const issue = await this.createIssue(payload.title, description);
981
1126
  progress("Attaching logs\u2026");
1127
+ const comments = [];
982
1128
  const logsComment = "### Network Logs\n```text\n" + formatNetworkLogs(payload.networkLogs) + "\n```";
1129
+ comments.push(this.addComment(issue.id, logsComment));
1130
+ if (payload.jsErrors.length > 0 || payload.consoleLogs.length > 0) {
1131
+ const parts = [];
1132
+ if (payload.jsErrors.length > 0) {
1133
+ parts.push("### JavaScript Errors\n```text\n" + formatJsErrors(payload.jsErrors) + "\n```");
1134
+ }
1135
+ if (payload.consoleLogs.length > 0) {
1136
+ parts.push("### Console Output\n```text\n" + formatConsoleLogs(payload.consoleLogs) + "\n```");
1137
+ }
1138
+ comments.push(this.addComment(issue.id, parts.join("\n\n")));
1139
+ }
983
1140
  const metadataComment = "### Client Metadata\n```json\n" + JSON.stringify(payload.metadata, null, 2) + "\n```";
984
- await Promise.all([
985
- this.addComment(issue.id, logsComment),
986
- this.addComment(issue.id, metadataComment)
987
- ]);
1141
+ comments.push(this.addComment(issue.id, metadataComment));
1142
+ await Promise.all(comments);
988
1143
  progress("Done!");
989
1144
  return {
990
1145
  provider: this.provider,
@@ -1317,6 +1472,17 @@ var JiraIntegration = class {
1317
1472
  }
1318
1473
  const logsBlob = new Blob([formatNetworkLogs(payload.networkLogs)], { type: "text/plain" });
1319
1474
  uploads.push(this.uploadAttachment(issue.key, logsBlob, "network-logs.txt", "text/plain"));
1475
+ if (payload.consoleLogs.length > 0 || payload.jsErrors.length > 0) {
1476
+ const consoleParts = [];
1477
+ if (payload.jsErrors.length > 0) {
1478
+ consoleParts.push("=== JavaScript Errors ===\n" + formatJsErrors(payload.jsErrors));
1479
+ }
1480
+ if (payload.consoleLogs.length > 0) {
1481
+ consoleParts.push("=== Console Output ===\n" + formatConsoleLogs(payload.consoleLogs));
1482
+ }
1483
+ const consoleBlob = new Blob([consoleParts.join("\n\n")], { type: "text/plain" });
1484
+ uploads.push(this.uploadAttachment(issue.key, consoleBlob, "console-logs.txt", "text/plain"));
1485
+ }
1320
1486
  const metadataBlob = new Blob([JSON.stringify(payload.metadata, null, 2)], { type: "application/json" });
1321
1487
  uploads.push(this.uploadAttachment(issue.key, metadataBlob, "client-metadata.json", "application/json"));
1322
1488
  await Promise.all(uploads);
@@ -1617,6 +1783,16 @@ function BugReporterProvider({
1617
1783
  imageHeight: 0
1618
1784
  });
1619
1785
  const reporterRef = react.useRef(null);
1786
+ const consoleCaptureRef = react.useRef(null);
1787
+ react.useEffect(() => {
1788
+ const capture = new ConsoleCapture();
1789
+ capture.start();
1790
+ consoleCaptureRef.current = capture;
1791
+ return () => {
1792
+ capture.stop();
1793
+ consoleCaptureRef.current = null;
1794
+ };
1795
+ }, []);
1620
1796
  const availableProviders = react.useMemo(() => {
1621
1797
  return ["linear", "jira"].filter((provider) => Boolean(integrations[provider]));
1622
1798
  }, [integrations]);
@@ -1740,6 +1916,18 @@ function BugReporterProvider({
1740
1916
  window.clearInterval(interval);
1741
1917
  };
1742
1918
  }, [isRecording]);
1919
+ react.useEffect(() => {
1920
+ if (!isRecording) {
1921
+ return;
1922
+ }
1923
+ const onBeforeUnload = (event) => {
1924
+ event.preventDefault();
1925
+ };
1926
+ window.addEventListener("beforeunload", onBeforeUnload);
1927
+ return () => {
1928
+ window.removeEventListener("beforeunload", onBeforeUnload);
1929
+ };
1930
+ }, [isRecording]);
1743
1931
  react.useEffect(() => {
1744
1932
  return () => {
1745
1933
  setScreenshotPreviewUrl((current) => {
@@ -1963,10 +2151,16 @@ function BugReporterProvider({
1963
2151
  highlights: screenshotAnnotation.highlights
1964
2152
  } : void 0
1965
2153
  };
2154
+ const { consoleLogs, jsErrors } = consoleCaptureRef.current?.snapshot() ?? {
2155
+ consoleLogs: [],
2156
+ jsErrors: []
2157
+ };
1966
2158
  try {
1967
2159
  const result = await reporter.submit(title, description, {
1968
2160
  screenshotBlob: screenshotBlobForSubmit,
1969
2161
  metadata,
2162
+ consoleLogs,
2163
+ jsErrors,
1970
2164
  onProgress: setSubmissionProgress
1971
2165
  });
1972
2166
  setSuccess(`Submitted to ${getProviderLabel(result.provider)} (${result.issueKey}).`);
@@ -3040,6 +3234,7 @@ exports.BugReporter = BugReporter;
3040
3234
  exports.BugReporterModal = BugReporterModal;
3041
3235
  exports.BugReporterProvider = BugReporterProvider;
3042
3236
  exports.BugSession = BugSession;
3237
+ exports.ConsoleCapture = ConsoleCapture;
3043
3238
  exports.DEFAULT_MAX_RECORDING_MS = DEFAULT_MAX_RECORDING_MS;
3044
3239
  exports.FloatingBugButton = FloatingBugButton;
3045
3240
  exports.JiraIntegration = JiraIntegration;
@@ -3048,6 +3243,8 @@ exports.NetworkLogger = NetworkLogger;
3048
3243
  exports.ScreenRecorder = ScreenRecorder;
3049
3244
  exports.ScreenshotCapturer = ScreenshotCapturer;
3050
3245
  exports.collectClientEnvironmentMetadata = collectClientEnvironmentMetadata;
3246
+ exports.formatConsoleLogs = formatConsoleLogs;
3247
+ exports.formatJsErrors = formatJsErrors;
3051
3248
  exports.formatNetworkLogs = formatNetworkLogs;
3052
3249
  exports.toErrorMessage = toErrorMessage;
3053
3250
  exports.useBugReporter = useBugReporter;