@sailfish-ai/recorder 1.7.42 → 1.7.47
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/constants.js +2 -0
- package/dist/exponentialBackoff.js +1 -0
- package/dist/inAppReportIssueModal.js +106 -43
- package/dist/index.js +4 -64
- package/dist/sailfish-recorder.cjs.js +1 -1
- package/dist/sailfish-recorder.cjs.js.br +0 -0
- package/dist/sailfish-recorder.cjs.js.gz +0 -0
- package/dist/sailfish-recorder.es.js +1 -1
- package/dist/sailfish-recorder.es.js.br +0 -0
- package/dist/sailfish-recorder.es.js.gz +0 -0
- package/dist/sailfish-recorder.umd.js +1 -1
- package/dist/sailfish-recorder.umd.js.br +0 -0
- package/dist/sailfish-recorder.umd.js.gz +0 -0
- package/dist/types/inAppReportIssueModal.d.ts +24 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/websocket.js +64 -35
- package/package.json +2 -2
package/dist/constants.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const DomContentEventId = 24;
|
|
2
2
|
export const NetworkRequestEventId = 27;
|
|
3
3
|
export const xSf3RidHeader = "X-Sf3-Rid";
|
|
4
|
+
// Values for DomContentLoadedEvent
|
|
4
5
|
export const DomContentSource = {
|
|
5
6
|
loading: 0,
|
|
6
7
|
contentLoaded: 1,
|
|
@@ -8,6 +9,7 @@ export const DomContentSource = {
|
|
|
8
9
|
beforeunload: 3,
|
|
9
10
|
unload: 4,
|
|
10
11
|
};
|
|
12
|
+
// Values for NetworkRequestEvent
|
|
11
13
|
export const Loading = "loading";
|
|
12
14
|
export const Complete = "complete";
|
|
13
15
|
export const STATIC_EXTENSIONS = [
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import { createTriageFromRecorder } from "./graphql";
|
|
2
|
+
// TODO - enable configuration by keyboard typing in UI
|
|
3
|
+
const DEFAULT_SHORTCUTS = {
|
|
4
|
+
enabled: false,
|
|
5
|
+
openModalExistingMode: { key: "e", requireCmdCtrl: false },
|
|
6
|
+
openModalCaptureNewMode: { key: "n", requireCmdCtrl: false },
|
|
7
|
+
closeModal: { key: "escape", requireCmdCtrl: false },
|
|
8
|
+
submitReport: { key: "enter", requireCmdCtrl: true },
|
|
9
|
+
startRecording: { key: "r", requireCmdCtrl: false },
|
|
10
|
+
stopRecording: { key: "escape", requireCmdCtrl: true },
|
|
11
|
+
};
|
|
12
|
+
export const ReportIssueContext = {
|
|
13
|
+
shortcuts: { ...DEFAULT_SHORTCUTS },
|
|
14
|
+
resolveSessionId: null,
|
|
15
|
+
apiKey: null,
|
|
16
|
+
backendApi: null,
|
|
17
|
+
triageBaseUrl: "https://app.sailfishqa.com",
|
|
18
|
+
};
|
|
2
19
|
let modalEl = null;
|
|
3
20
|
let currentState = {
|
|
4
21
|
mode: "lookback",
|
|
@@ -9,12 +26,6 @@ let recordingStartTime = null;
|
|
|
9
26
|
let recordingEndTime = null;
|
|
10
27
|
let timerInterval = null;
|
|
11
28
|
let isRecording = false;
|
|
12
|
-
export const ReportIssueContext = {
|
|
13
|
-
resolveSessionId: null,
|
|
14
|
-
apiKey: null,
|
|
15
|
-
backendApi: null,
|
|
16
|
-
triageBaseUrl: "https://app.sailfishqa.com",
|
|
17
|
-
};
|
|
18
29
|
function isMacPlatform() {
|
|
19
30
|
// Newer API (Chrome, Edge, Opera)
|
|
20
31
|
const uaData = navigator.userAgentData;
|
|
@@ -33,62 +44,118 @@ function isTypingInInput() {
|
|
|
33
44
|
el instanceof HTMLTextAreaElement ||
|
|
34
45
|
(el instanceof HTMLElement && el.isContentEditable));
|
|
35
46
|
}
|
|
47
|
+
function formatShortcutKeyLabel(key) {
|
|
48
|
+
const map = {
|
|
49
|
+
escape: "esc",
|
|
50
|
+
};
|
|
51
|
+
return (map[key.toLowerCase()] || key).toUpperCase();
|
|
52
|
+
}
|
|
53
|
+
function getShortcutLabel(config) {
|
|
54
|
+
const parts = [];
|
|
55
|
+
if (config.requireCmdCtrl) {
|
|
56
|
+
parts.push(getShortcutKeyCmdCtrlLabel());
|
|
57
|
+
}
|
|
58
|
+
parts.push(formatShortcutKeyLabel(config.key));
|
|
59
|
+
return parts
|
|
60
|
+
.map((p) => `<span style="background: #F1F5F9; border:1px solid #cbd5e1; border-radius: 4px; padding: 0 4px; font-weight: 500; font-size: 12px; color: #94A3B8; line-height: 16px;">${p}</span>`)
|
|
61
|
+
.join(config.requireCmdCtrl ? " + " : "");
|
|
62
|
+
}
|
|
63
|
+
function mergeShortcutsConfig(userConfig) {
|
|
64
|
+
const merged = { ...DEFAULT_SHORTCUTS };
|
|
65
|
+
if (!userConfig)
|
|
66
|
+
return merged;
|
|
67
|
+
// Handle boolean key separately
|
|
68
|
+
if (typeof userConfig.enabled === "boolean") {
|
|
69
|
+
merged.enabled = userConfig.enabled;
|
|
70
|
+
}
|
|
71
|
+
// Handle object keys explicitly
|
|
72
|
+
const objectKeys = [
|
|
73
|
+
"openModalExistingMode",
|
|
74
|
+
"openModalCaptureNewMode",
|
|
75
|
+
"closeModal",
|
|
76
|
+
"submitReport",
|
|
77
|
+
"startRecording",
|
|
78
|
+
"stopRecording",
|
|
79
|
+
];
|
|
80
|
+
for (const k of objectKeys) {
|
|
81
|
+
const val = userConfig[k];
|
|
82
|
+
if (val && typeof val === "object") {
|
|
83
|
+
merged[k] = { ...merged[k], ...val };
|
|
84
|
+
merged[k].key = merged[k].key.toLowerCase();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return merged;
|
|
88
|
+
}
|
|
89
|
+
function getShortcutLabelFromContext(shortcutKey) {
|
|
90
|
+
const config = ReportIssueContext.shortcuts[shortcutKey];
|
|
91
|
+
return getShortcutLabel(config);
|
|
92
|
+
}
|
|
36
93
|
export function setupIssueReporting(options) {
|
|
37
94
|
ReportIssueContext.apiKey = options.apiKey;
|
|
38
95
|
ReportIssueContext.backendApi = options.backendApi;
|
|
39
96
|
ReportIssueContext.resolveSessionId = options.getSessionId;
|
|
40
|
-
|
|
97
|
+
ReportIssueContext.shortcuts = mergeShortcutsConfig(options.shortcuts);
|
|
98
|
+
const { shortcuts } = ReportIssueContext;
|
|
41
99
|
window.addEventListener("keydown", (e) => {
|
|
42
100
|
const typingInInput = isTypingInInput();
|
|
43
101
|
const key = e.key.toLowerCase();
|
|
44
102
|
const isCmdOrCtrl = e.metaKey || e.ctrlKey;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
e.preventDefault();
|
|
54
|
-
injectModalHTML("startnow");
|
|
55
|
-
return;
|
|
103
|
+
const modalOpen = !!document.getElementById("sf-report-issue-modal");
|
|
104
|
+
const enableModeShortcuts = !typingInInput && (shortcuts.enabled || modalOpen);
|
|
105
|
+
const shortcutUsed = (shortcutKey) => key === shortcuts[shortcutKey].key &&
|
|
106
|
+
isCmdOrCtrl === shortcuts[shortcutKey].requireCmdCtrl;
|
|
107
|
+
const openModal = modalOpen
|
|
108
|
+
? (mode) => {
|
|
109
|
+
setActiveTab(mode);
|
|
110
|
+
updateModeSpecificUI(mode);
|
|
56
111
|
}
|
|
112
|
+
: injectModalHTML;
|
|
113
|
+
// --- Open modal OR switch mode ---
|
|
114
|
+
if (enableModeShortcuts && shortcutUsed("openModalExistingMode")) {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
openModal("lookback");
|
|
117
|
+
return;
|
|
57
118
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
119
|
+
if (enableModeShortcuts && shortcutUsed("openModalCaptureNewMode")) {
|
|
120
|
+
e.preventDefault();
|
|
121
|
+
openModal("startnow");
|
|
61
122
|
return;
|
|
62
|
-
|
|
63
|
-
|
|
123
|
+
}
|
|
124
|
+
// --- Close modal ---
|
|
125
|
+
if (modalOpen && !isRecording && shortcutUsed("closeModal")) {
|
|
64
126
|
e.preventDefault();
|
|
65
127
|
closeModal();
|
|
66
128
|
return;
|
|
67
129
|
}
|
|
68
|
-
//
|
|
69
|
-
if (
|
|
130
|
+
// --- Submit report ---
|
|
131
|
+
if (modalOpen && shortcutUsed("submitReport")) {
|
|
70
132
|
const submitBtn = document.getElementById("sf-issue-submit-btn");
|
|
71
133
|
if (submitBtn && !submitBtn.disabled) {
|
|
72
134
|
e.preventDefault();
|
|
73
135
|
submitBtn.click();
|
|
74
136
|
}
|
|
137
|
+
return;
|
|
75
138
|
}
|
|
76
|
-
//
|
|
77
|
-
if (
|
|
139
|
+
// --- Stop recording ---
|
|
140
|
+
if (isRecording &&
|
|
141
|
+
key === shortcuts.stopRecording.key &&
|
|
142
|
+
isCmdOrCtrl === shortcuts.stopRecording.requireCmdCtrl) {
|
|
78
143
|
e.preventDefault();
|
|
79
144
|
stopRecording();
|
|
145
|
+
return;
|
|
80
146
|
}
|
|
81
|
-
//
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
key ===
|
|
85
|
-
|
|
86
|
-
|
|
147
|
+
// --- Start recording ---
|
|
148
|
+
if (modalOpen &&
|
|
149
|
+
currentState.mode === "startnow" &&
|
|
150
|
+
key === shortcuts.startRecording.key &&
|
|
151
|
+
isCmdOrCtrl === shortcuts.startRecording.requireCmdCtrl &&
|
|
152
|
+
!typingInInput) {
|
|
87
153
|
const recordBtn = document.getElementById("sf-start-recording-btn");
|
|
88
154
|
if (recordBtn) {
|
|
89
155
|
e.preventDefault();
|
|
90
156
|
recordBtn.click();
|
|
91
157
|
}
|
|
158
|
+
return;
|
|
92
159
|
}
|
|
93
160
|
});
|
|
94
161
|
}
|
|
@@ -159,11 +226,11 @@ function injectModalHTML(initialMode = "lookback") {
|
|
|
159
226
|
<div id="sf-issue-tabs" style="display:flex; gap:4px; margin-bottom:16px; background:#f1f5f9; padding:6px; border-radius:6px; width: fit-content;">
|
|
160
227
|
<button id="sf-tab-lookback" data-mode="lookback" class="sf-issue-tab ${!isStartNow ? "active" : ""}"
|
|
161
228
|
style="padding:6px 12px; border:none; background:${!isStartNow ? "white" : "transparent"}; color: ${!isStartNow ? "#0F172A" : "#64748B"}; border-radius:4px; font-size:14px; cursor:pointer; font-weight:500; height:32px; display: flex; align-items: center; gap: 8px;">
|
|
162
|
-
Existing
|
|
229
|
+
Existing ${getShortcutLabelFromContext("openModalExistingMode")}
|
|
163
230
|
</button>
|
|
164
231
|
<button id="sf-tab-startnow" data-mode="startnow" class="sf-issue-tab ${isStartNow ? "active" : ""}"
|
|
165
232
|
style="padding:6px 12px; border:none; background:${isStartNow ? "white" : "transparent"}; color: ${isStartNow ? "#0F172A" : "#64748B"}; border-radius:4px; font-size:14px; cursor:pointer; font-weight:500; height:32px; display: flex; align-items: center; gap: 8px;">
|
|
166
|
-
Capture new
|
|
233
|
+
Capture new ${getShortcutLabelFromContext("openModalCaptureNewMode")}
|
|
167
234
|
</button>
|
|
168
235
|
</div>
|
|
169
236
|
|
|
@@ -189,7 +256,7 @@ function injectModalHTML(initialMode = "lookback") {
|
|
|
189
256
|
<textarea id="sf-issue-description" placeholder="Add description here"
|
|
190
257
|
style="width:100%; height:80px; padding:8px 12px; font-size:14px;
|
|
191
258
|
border:1px solid #cbd5e1; border-radius:6px; margin-bottom:20px;
|
|
192
|
-
resize:none; outline:none;"
|
|
259
|
+
resize:none; outline:none;">${currentState.description}</textarea>
|
|
193
260
|
|
|
194
261
|
<div id="sf-lookback-container" style="display:${isStartNow ? "none" : "block"}; margin-bottom:20px;">
|
|
195
262
|
<label for="sf-lookback-minutes" style="display:block; font-size:14px; font-weight:500; margin-bottom:6px;">
|
|
@@ -261,7 +328,7 @@ function injectModalHTML(initialMode = "lookback") {
|
|
|
261
328
|
<div style="width: 14px; height: 14px; background: #fc5555; border-radius: 50%; border: 1px solid #991b1b;"></div>
|
|
262
329
|
</div>
|
|
263
330
|
<span>Start Recording</span>
|
|
264
|
-
|
|
331
|
+
${getShortcutLabelFromContext("startRecording")}
|
|
265
332
|
</button>
|
|
266
333
|
</div>
|
|
267
334
|
|
|
@@ -270,9 +337,7 @@ function injectModalHTML(initialMode = "lookback") {
|
|
|
270
337
|
border-radius:6px; font-size:14px; line-height: 24px; font-weight:500;
|
|
271
338
|
cursor:${isStartNow ? "not-allowed" : "pointer"}; opacity:${isStartNow ? "0.4" : "1"}; margin-bottom:1px" ${isStartNow ? "disabled" : ""}>
|
|
272
339
|
Report <span style="margin-left: 8px; display: inline-flex; align-items: center; gap: 4px; color: #94A3B8; font-size: 12px; line-height:16px;">
|
|
273
|
-
|
|
274
|
-
+
|
|
275
|
-
<span style="background: #F1F5F9; border-radius: 4px; padding: 0 4px; font-weight: 500;">ENTER</span>
|
|
340
|
+
${getShortcutLabelFromContext("submitReport")}
|
|
276
341
|
</span>
|
|
277
342
|
</button>
|
|
278
343
|
</div>
|
|
@@ -487,9 +552,7 @@ function showFloatingTimer() {
|
|
|
487
552
|
<div style="display:flex; align-items:center; gap:4px; margin-left:auto; font-size:14px; color:#0F172A;">
|
|
488
553
|
<span style="color: #71717A;">stop</span>
|
|
489
554
|
<span style="display: inline-flex; align-items: center; gap: 4px; color: #94A3B8; font-size: 12px; line-height:16px;">
|
|
490
|
-
|
|
491
|
-
+
|
|
492
|
-
<span style="background: #F1F5F9; border-radius: 4px; border:1px solid #cbd5e1; padding: 0 4px; font-weight: 500;">ESC</span>
|
|
555
|
+
${getShortcutLabelFromContext("stopRecording")}
|
|
493
556
|
</span>
|
|
494
557
|
</div>
|
|
495
558
|
`;
|
package/dist/index.js
CHANGED
|
@@ -323,6 +323,7 @@ function trackDomainChanges() {
|
|
|
323
323
|
timestamp,
|
|
324
324
|
page_visit_uuid: pageVisitUUID,
|
|
325
325
|
prev_page_visit_uuid: prevPageVisitUUID,
|
|
326
|
+
session_id: getOrSetSessionId(),
|
|
326
327
|
},
|
|
327
328
|
});
|
|
328
329
|
}
|
|
@@ -535,11 +536,6 @@ function shouldSkipHeadersPropagation(url, domainsToNotPropagateHeaderTo = []) {
|
|
|
535
536
|
if (matchUrlWithWildcard(url, combinedPatterns)) {
|
|
536
537
|
return true;
|
|
537
538
|
}
|
|
538
|
-
// 3️⃣ DYNAMIC-FAILURE EXCLUSION (exact host)
|
|
539
|
-
const hostname = urlObj.hostname;
|
|
540
|
-
if (dynamicExcludedHosts.has(hostname)) {
|
|
541
|
-
return true;
|
|
542
|
-
}
|
|
543
539
|
return false;
|
|
544
540
|
}
|
|
545
541
|
// Updated XMLHttpRequest interceptor with domain exclusion
|
|
@@ -572,10 +568,6 @@ function setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo = []) {
|
|
|
572
568
|
if (!url) {
|
|
573
569
|
return originalSend.apply(this, args);
|
|
574
570
|
}
|
|
575
|
-
// parse host+path for exclusion
|
|
576
|
-
const urlObj = new URL(url, window.location.href);
|
|
577
|
-
const domain = urlObj.hostname;
|
|
578
|
-
const hostPath = urlObj.pathname === "/" ? domain : `${domain}${urlObj.pathname}`;
|
|
579
571
|
// 1️⃣ Skip injection for excluded domains/paths
|
|
580
572
|
if (shouldSkipHeadersPropagation(url, domainsToNotPropagateHeaderTo)) {
|
|
581
573
|
return originalSend.apply(this, args);
|
|
@@ -616,15 +608,6 @@ function setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo = []) {
|
|
|
616
608
|
},
|
|
617
609
|
...getUrlAndStoredUuids(),
|
|
618
610
|
});
|
|
619
|
-
// 5️⃣ Update dynamic sets
|
|
620
|
-
if (success) {
|
|
621
|
-
// once any route passes, skip preflight on entire domain
|
|
622
|
-
dynamicPassedHosts.add(domain);
|
|
623
|
-
}
|
|
624
|
-
else {
|
|
625
|
-
// only exclude the specific failing path
|
|
626
|
-
dynamicExcludedHosts.add(hostPath);
|
|
627
|
-
}
|
|
628
611
|
};
|
|
629
612
|
// 6️⃣ On successful load
|
|
630
613
|
this.addEventListener("load", () => {
|
|
@@ -654,7 +637,6 @@ function setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo = []) {
|
|
|
654
637
|
function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
655
638
|
const originalFetch = window.fetch;
|
|
656
639
|
const sessionId = getOrSetSessionId();
|
|
657
|
-
const cachedDomains = new Map();
|
|
658
640
|
window.fetch = new Proxy(originalFetch, {
|
|
659
641
|
apply: async (target, thisArg, args) => {
|
|
660
642
|
let input = args[0];
|
|
@@ -688,9 +670,6 @@ function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
|
688
670
|
if (!sessionId) {
|
|
689
671
|
return target.apply(thisArg, args);
|
|
690
672
|
}
|
|
691
|
-
const urlObj = new URL(url, window.location.href);
|
|
692
|
-
const domain = urlObj.hostname;
|
|
693
|
-
const hostPath = urlObj.pathname === "/" ? domain : `${domain}${urlObj.pathname}`;
|
|
694
673
|
const networkUUID = uuidv4();
|
|
695
674
|
const urlAndStoredUuids = getUrlAndStoredUuids();
|
|
696
675
|
const method = init.method || "GET";
|
|
@@ -708,13 +687,6 @@ function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
|
708
687
|
const status = response.status;
|
|
709
688
|
const success = response.ok;
|
|
710
689
|
const error = success ? "" : `Request Error: ${response.statusText}`;
|
|
711
|
-
// 5️⃣ Update dynamic sets
|
|
712
|
-
if (success) {
|
|
713
|
-
dynamicPassedHosts.add(domain);
|
|
714
|
-
}
|
|
715
|
-
else {
|
|
716
|
-
dynamicExcludedHosts.add(hostPath);
|
|
717
|
-
}
|
|
718
690
|
// Emit 'networkRequestFinished' event
|
|
719
691
|
const eventData = {
|
|
720
692
|
type: NetworkRequestEventId,
|
|
@@ -730,6 +702,7 @@ function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
|
730
702
|
error,
|
|
731
703
|
method,
|
|
732
704
|
url,
|
|
705
|
+
retry_without_trace_id: isRetry,
|
|
733
706
|
},
|
|
734
707
|
...urlAndStoredUuids,
|
|
735
708
|
};
|
|
@@ -744,11 +717,8 @@ function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
|
744
717
|
if (error instanceof TypeError &&
|
|
745
718
|
error?.message?.toLowerCase().includes(CORS_KEYWORD.toLowerCase())) {
|
|
746
719
|
// CORS/network failure: exclude just this path
|
|
747
|
-
dynamicExcludedHosts.add(hostPath);
|
|
748
720
|
return target.apply(thisArg, args);
|
|
749
721
|
}
|
|
750
|
-
// On other errors, treat as “passed” so we don’t re-preflight
|
|
751
|
-
dynamicPassedHosts.add(domain);
|
|
752
722
|
const eventData = {
|
|
753
723
|
type: NetworkRequestEventId,
|
|
754
724
|
timestamp: endTime,
|
|
@@ -793,7 +763,6 @@ function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
|
793
763
|
}
|
|
794
764
|
// Helper to retry a fetch without the X-Sf3-Rid header if the initial attempt fails due to that header
|
|
795
765
|
async function retryWithoutPropagateHeaders(target, thisArg, args, url) {
|
|
796
|
-
const domain = new URL(url).hostname;
|
|
797
766
|
try {
|
|
798
767
|
// **Fix:** Properly await and clone the request without the tracing header
|
|
799
768
|
let input = args[0];
|
|
@@ -804,12 +773,6 @@ function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
|
804
773
|
retryHeaders.delete(xSf3RidHeader);
|
|
805
774
|
retryInit.headers = retryHeaders;
|
|
806
775
|
const response = await target.call(thisArg, input, retryInit);
|
|
807
|
-
if (response.ok || !BAD_HTTP_STATUS.includes(response.status)) {
|
|
808
|
-
dynamicExcludedHosts.add(domain);
|
|
809
|
-
cachedDomains.set(domain, ActionType.IGNORE);
|
|
810
|
-
DEBUG &&
|
|
811
|
-
console.info(`Retried request to ${url} without ${xSf3RidHeader} succeeded. Added "${domain}" to exclusion list.`);
|
|
812
|
-
}
|
|
813
776
|
return response;
|
|
814
777
|
}
|
|
815
778
|
else if (input instanceof Request) {
|
|
@@ -819,12 +782,6 @@ function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
|
819
782
|
retryHeaders.delete(xSf3RidHeader);
|
|
820
783
|
const retryReq = new Request(cloned, { headers: retryHeaders });
|
|
821
784
|
const response = await target.call(thisArg, retryReq, init);
|
|
822
|
-
if (response.ok || !BAD_HTTP_STATUS.includes(response.status)) {
|
|
823
|
-
dynamicExcludedHosts.add(domain);
|
|
824
|
-
cachedDomains.set(domain, ActionType.IGNORE);
|
|
825
|
-
DEBUG &&
|
|
826
|
-
console.info(`Retried request to ${url} without ${xSf3RidHeader} succeeded. Added "${domain}" to exclusion list.`);
|
|
827
|
-
}
|
|
828
785
|
return response;
|
|
829
786
|
}
|
|
830
787
|
else {
|
|
@@ -897,24 +854,6 @@ export async function startRecording({ apiKey, backendApi = "https://api-service
|
|
|
897
854
|
initializeConsolePlugin(DEFAULT_CONSOLE_RECORDING_SETTINGS, sessionId);
|
|
898
855
|
storeCredentialsAndConnection({ apiKey, backendApi });
|
|
899
856
|
trackDomainChanges();
|
|
900
|
-
// ─── Merge stored excludes + passed-in excludes ───
|
|
901
|
-
const initialExcludes = new Set(dynamicExcludedHosts);
|
|
902
|
-
domainsToNotPropagateHeaderTo.forEach((host) => {
|
|
903
|
-
if (host?.trim()) {
|
|
904
|
-
dynamicExcludedHosts.add(host.trim());
|
|
905
|
-
}
|
|
906
|
-
});
|
|
907
|
-
const newlyExcluded = Array.from(dynamicExcludedHosts).filter((h) => !initialExcludes.has(h));
|
|
908
|
-
if (newlyExcluded.length) {
|
|
909
|
-
// single notify of the full updated list
|
|
910
|
-
updateExcludedHostsStorageAndBackend(dynamicExcludedHosts);
|
|
911
|
-
}
|
|
912
|
-
// ─── Merge passed hosts ───
|
|
913
|
-
domainsToPropagateHeaderTo.forEach((host) => {
|
|
914
|
-
if (host?.trim()) {
|
|
915
|
-
dynamicPassedHosts.add(host.trim());
|
|
916
|
-
}
|
|
917
|
-
});
|
|
918
857
|
sessionStorage.setItem(SF_API_KEY_FOR_UPDATE, apiKey);
|
|
919
858
|
sessionStorage.setItem(SF_BACKEND_API, backendApi);
|
|
920
859
|
// Setup interceptors with custom ignore and propagate domains
|
|
@@ -942,6 +881,7 @@ export async function startRecording({ apiKey, backendApi = "https://api-service
|
|
|
942
881
|
}
|
|
943
882
|
}
|
|
944
883
|
export const initRecorder = async (options) => {
|
|
884
|
+
console.log("Initializing Sailfish Recorder with options:", options);
|
|
945
885
|
// Only run on the client (browser) environment
|
|
946
886
|
if (typeof window === "undefined") {
|
|
947
887
|
return;
|
|
@@ -952,7 +892,7 @@ export const initRecorder = async (options) => {
|
|
|
952
892
|
apiKey: options.apiKey,
|
|
953
893
|
backendApi: options.backendApi ?? "https://api-service.sailfishqa.com",
|
|
954
894
|
getSessionId: () => getOrSetSessionId(),
|
|
955
|
-
|
|
895
|
+
shortcuts: options.reportIssueShortcuts,
|
|
956
896
|
});
|
|
957
897
|
});
|
|
958
898
|
};
|