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 +201 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -1
- package/dist/index.d.ts +37 -1
- package/dist/index.js +200 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
985
|
-
|
|
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;
|