@sailfish-ai/recorder 1.7.20 → 1.7.21
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.js +189 -78
- 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/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -16,19 +16,24 @@ const DOMAINS_TO_NOT_PROPAGATE_HEADER_TO_DEFAULT = [
|
|
|
16
16
|
"*.googleapis.com",
|
|
17
17
|
"*.amazonaws.com", // Exclude AWS S3
|
|
18
18
|
"*.smooch.io", // Exclude smooch-related requests
|
|
19
|
-
|
|
19
|
+
// Exclude zendesk-related requests
|
|
20
|
+
"*.zendesk.com",
|
|
21
|
+
"*.zdassets.com",
|
|
20
22
|
];
|
|
21
23
|
const BAD_HTTP_STATUS = [
|
|
22
24
|
400, // BAD REQUEST
|
|
23
25
|
403, // FORBIDDEN
|
|
24
26
|
];
|
|
25
27
|
const CORS_KEYWORD = "CORS";
|
|
28
|
+
const STORAGE_VERSION = 1;
|
|
26
29
|
const DYNAMIC_PASSED_HOSTS_KEY = "dynamicPassedHosts";
|
|
27
30
|
const DYNAMIC_EXCLUDED_HOSTS_KEY = "dynamicExcludedHosts";
|
|
28
31
|
const SF_API_KEY_FOR_UPDATE = "sailfishApiKey";
|
|
29
32
|
const SF_BACKEND_API = "sailfishBackendApi";
|
|
30
33
|
const INCLUDE = "include";
|
|
31
34
|
const SAME_ORIGIN = "same-origin";
|
|
35
|
+
const ALLOWED_HEADERS_HEADER = "access-control-allow-headers";
|
|
36
|
+
const OPTIONS = "OPTIONS";
|
|
32
37
|
/**
|
|
33
38
|
* Notify the backend of the updated dynamicExcludedHosts
|
|
34
39
|
*/
|
|
@@ -42,54 +47,106 @@ function updateExcludedHostsStorageAndBackend(dynamicExcludedHosts) {
|
|
|
42
47
|
}
|
|
43
48
|
const dynamicExcludedHosts = new Set();
|
|
44
49
|
const dynamicPassedHosts = new Set();
|
|
45
|
-
// Load
|
|
50
|
+
// 2️⃣ Load & evict old entries (>7 days) + version check for Excluded
|
|
46
51
|
(() => {
|
|
47
52
|
const stored = localStorage.getItem(DYNAMIC_EXCLUDED_HOSTS_KEY);
|
|
48
|
-
if (stored)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
console.log("Failed to parse dynamicExcludedHosts from storage", e);
|
|
53
|
+
if (!stored)
|
|
54
|
+
return;
|
|
55
|
+
try {
|
|
56
|
+
const wrapper = JSON.parse(stored);
|
|
57
|
+
// if it's from an old version, drop it
|
|
58
|
+
if (wrapper.version !== STORAGE_VERSION) {
|
|
55
59
|
localStorage.removeItem(DYNAMIC_EXCLUDED_HOSTS_KEY);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
const valid = {};
|
|
64
|
+
for (const [host, ts] of Object.entries(wrapper.entries)) {
|
|
65
|
+
if (now - ts < 7 * 24 * 60 * 60 * 1000) {
|
|
66
|
+
dynamicExcludedHosts.add(host);
|
|
67
|
+
valid[host] = ts;
|
|
68
|
+
}
|
|
56
69
|
}
|
|
70
|
+
localStorage.setItem(DYNAMIC_EXCLUDED_HOSTS_KEY, JSON.stringify({ version: STORAGE_VERSION, entries: valid }));
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
if (DEBUG)
|
|
74
|
+
console.warn("Failed to parse dynamicExcludedHosts:", e);
|
|
75
|
+
localStorage.removeItem(DYNAMIC_EXCLUDED_HOSTS_KEY);
|
|
57
76
|
}
|
|
58
77
|
})();
|
|
59
|
-
// Load
|
|
78
|
+
// 3️⃣ Load & evict old entries (>7 days) + version check for Passed
|
|
60
79
|
(() => {
|
|
61
80
|
const stored = localStorage.getItem(DYNAMIC_PASSED_HOSTS_KEY);
|
|
62
|
-
if (stored)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (DEBUG)
|
|
68
|
-
console.log("Failed to parse dynamicPassedHosts from storage", e);
|
|
81
|
+
if (!stored)
|
|
82
|
+
return;
|
|
83
|
+
try {
|
|
84
|
+
const wrapper = JSON.parse(stored);
|
|
85
|
+
if (wrapper.version !== STORAGE_VERSION) {
|
|
69
86
|
localStorage.removeItem(DYNAMIC_PASSED_HOSTS_KEY);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const now = Date.now();
|
|
90
|
+
const valid = {};
|
|
91
|
+
for (const [host, ts] of Object.entries(wrapper.entries)) {
|
|
92
|
+
if (now - ts < 7 * 24 * 60 * 60 * 1000) {
|
|
93
|
+
dynamicPassedHosts.add(host);
|
|
94
|
+
valid[host] = ts;
|
|
95
|
+
}
|
|
70
96
|
}
|
|
97
|
+
localStorage.setItem(DYNAMIC_PASSED_HOSTS_KEY, JSON.stringify({ version: STORAGE_VERSION, entries: valid }));
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
if (DEBUG)
|
|
101
|
+
console.warn("Failed to parse dynamicPassedHosts:", e);
|
|
102
|
+
localStorage.removeItem(DYNAMIC_PASSED_HOSTS_KEY);
|
|
71
103
|
}
|
|
72
104
|
})();
|
|
73
105
|
// Override add() to persist updates to localStorage
|
|
74
106
|
const originalExcludedAdd = dynamicExcludedHosts.add;
|
|
75
107
|
dynamicExcludedHosts.add = (host) => {
|
|
76
108
|
const cleanedHost = host?.trim();
|
|
77
|
-
if (!cleanedHost) {
|
|
109
|
+
if (!cleanedHost || dynamicExcludedHosts.has(cleanedHost)) {
|
|
78
110
|
return dynamicExcludedHosts;
|
|
79
111
|
}
|
|
80
112
|
originalExcludedAdd.call(dynamicExcludedHosts, cleanedHost);
|
|
81
|
-
|
|
113
|
+
try {
|
|
114
|
+
// read existing map or start fresh
|
|
115
|
+
const stored = localStorage.getItem(DYNAMIC_EXCLUDED_HOSTS_KEY);
|
|
116
|
+
const obj = stored ? JSON.parse(stored) : {};
|
|
117
|
+
obj[cleanedHost] = Date.now();
|
|
118
|
+
localStorage.setItem(DYNAMIC_EXCLUDED_HOSTS_KEY, JSON.stringify(obj));
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
if (DEBUG)
|
|
122
|
+
console.warn("Persist dynamicExcludedHosts failed:", e);
|
|
123
|
+
}
|
|
82
124
|
updateExcludedHostsStorageAndBackend(dynamicExcludedHosts);
|
|
83
125
|
return dynamicExcludedHosts;
|
|
84
126
|
};
|
|
85
127
|
const originalPassedAdd = dynamicPassedHosts.add;
|
|
128
|
+
// === UPDATED: override to store timestamp on add (passed hosts) ===
|
|
86
129
|
dynamicPassedHosts.add = (host) => {
|
|
87
130
|
const cleanedHost = host?.trim();
|
|
88
131
|
if (!cleanedHost) {
|
|
89
132
|
return dynamicPassedHosts;
|
|
90
133
|
}
|
|
134
|
+
// If we already have it, just return
|
|
135
|
+
if (dynamicPassedHosts.has(cleanedHost)) {
|
|
136
|
+
return dynamicPassedHosts;
|
|
137
|
+
}
|
|
91
138
|
originalPassedAdd.call(dynamicPassedHosts, cleanedHost);
|
|
92
|
-
|
|
139
|
+
// Persist a host->timestamp map
|
|
140
|
+
try {
|
|
141
|
+
const stored = localStorage.getItem(DYNAMIC_PASSED_HOSTS_KEY);
|
|
142
|
+
const obj = stored ? JSON.parse(stored) : {};
|
|
143
|
+
obj[cleanedHost] = Date.now();
|
|
144
|
+
localStorage.setItem(DYNAMIC_PASSED_HOSTS_KEY, JSON.stringify(obj));
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
if (DEBUG)
|
|
148
|
+
console.warn("Persist dynamicPassedHosts failed:", e);
|
|
149
|
+
}
|
|
93
150
|
return dynamicPassedHosts;
|
|
94
151
|
};
|
|
95
152
|
const ActionType = {
|
|
@@ -197,7 +254,10 @@ function getOrSetUserDeviceUuid() {
|
|
|
197
254
|
let userDeviceUuid = localStorage.getItem("sailfishUserDeviceUuid");
|
|
198
255
|
if (!userDeviceUuid) {
|
|
199
256
|
userDeviceUuid = uuidv4();
|
|
200
|
-
|
|
257
|
+
try {
|
|
258
|
+
localStorage.setItem("sailfishUserDeviceUuid", userDeviceUuid);
|
|
259
|
+
}
|
|
260
|
+
catch { }
|
|
201
261
|
}
|
|
202
262
|
return userDeviceUuid;
|
|
203
263
|
}
|
|
@@ -315,16 +375,12 @@ function shouldSkipHeadersPropagation(url, domainsToNotPropagateHeadersTo = [])
|
|
|
315
375
|
function performOptionsPreflightForXHR(url, init, xSf3RidHeaderValue, domain) {
|
|
316
376
|
return new Promise((resolve) => {
|
|
317
377
|
const xhr = new XMLHttpRequest();
|
|
318
|
-
xhr.open(
|
|
319
|
-
// Mirror credentials
|
|
378
|
+
xhr.open(OPTIONS, url, true);
|
|
320
379
|
xhr.withCredentials = init.credentials === INCLUDE;
|
|
321
|
-
//
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
? Object.entries(init.headers)
|
|
326
|
-
: init.headers || {};
|
|
327
|
-
const customHeaders = Object.keys(rawHeaders)
|
|
380
|
+
// Normalize headers into a standard Headers instance
|
|
381
|
+
const headerEntries = new Headers(init.headers || {});
|
|
382
|
+
const rawKeys = Array.from(headerEntries.keys());
|
|
383
|
+
const customHeaders = rawKeys
|
|
328
384
|
.map((h) => h.toLowerCase())
|
|
329
385
|
.filter((h) => ![
|
|
330
386
|
"accept",
|
|
@@ -335,11 +391,25 @@ function performOptionsPreflightForXHR(url, init, xSf3RidHeaderValue, domain) {
|
|
|
335
391
|
if (customHeaders.length) {
|
|
336
392
|
xhr.setRequestHeader("Access-Control-Request-Headers", customHeaders.join(","));
|
|
337
393
|
}
|
|
338
|
-
//
|
|
339
|
-
|
|
394
|
+
// Set the CORS preflight method header
|
|
395
|
+
xhr.setRequestHeader("Access-Control-Request-Method", (init.method || "GET").toUpperCase());
|
|
396
|
+
// Correctly add the tracing header to the XHR instance
|
|
397
|
+
xhr.setRequestHeader(xSf3RidHeader, xSf3RidHeaderValue);
|
|
340
398
|
xhr.onload = () => {
|
|
341
399
|
if (xhr.status >= 200 && xhr.status < 300) {
|
|
342
|
-
|
|
400
|
+
const allowed = xhr.getResponseHeader(ALLOWED_HEADERS_HEADER);
|
|
401
|
+
if (allowed &&
|
|
402
|
+
allowed
|
|
403
|
+
.split(",")
|
|
404
|
+
.map((h) => h.trim().toLowerCase())
|
|
405
|
+
.includes(xSf3RidHeader.toLowerCase())) {
|
|
406
|
+
resolve(ActionType.PROPAGATE);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
DEBUG &&
|
|
410
|
+
console.log(`[XHR Interceptor] Header ${xSf3RidHeader} not allowed by preflight for ${domain}`);
|
|
411
|
+
resolve(ActionType.IGNORE);
|
|
412
|
+
}
|
|
343
413
|
}
|
|
344
414
|
else {
|
|
345
415
|
DEBUG &&
|
|
@@ -401,19 +471,26 @@ function setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo = []) {
|
|
|
401
471
|
console.warn(`Could not set X-Sf3-Rid header for ${url}`, e);
|
|
402
472
|
}
|
|
403
473
|
}
|
|
404
|
-
// On CORS or network error during send, add
|
|
405
|
-
this.addEventListener("error", () => {
|
|
474
|
+
// On CORS or network error during send, log it and add to excluded-hosts
|
|
475
|
+
this.addEventListener("error", (evt) => {
|
|
476
|
+
console.error(`[XHR Interceptor] Network error for ${domain} (${url}):`, evt, `status=${this.status}`, `statusText=${this.statusText}`);
|
|
406
477
|
dynamicExcludedHosts.add(domain);
|
|
407
478
|
}, { once: true });
|
|
408
|
-
// On
|
|
479
|
+
// On load, log non-2xx statuses (including CORS-blocked status=0) and track passes
|
|
409
480
|
this.addEventListener("load", () => {
|
|
410
481
|
if (this.status === 0) {
|
|
411
|
-
|
|
482
|
+
DEBUG &&
|
|
483
|
+
console.error(`[XHR Interceptor] CORS blocked (status=0) for ${domain} (${url})`, `statusText=${this.statusText}`);
|
|
412
484
|
dynamicExcludedHosts.add(domain);
|
|
413
485
|
}
|
|
414
|
-
if (this.status >= 200 && this.status < 300) {
|
|
486
|
+
else if (this.status >= 200 && this.status < 300) {
|
|
415
487
|
dynamicPassedHosts.add(domain);
|
|
416
488
|
}
|
|
489
|
+
else {
|
|
490
|
+
DEBUG &&
|
|
491
|
+
console.error(`[XHR Interceptor] HTTP error ${this.status} ${this.statusText} for ${domain} (${url})`);
|
|
492
|
+
dynamicExcludedHosts.add(domain);
|
|
493
|
+
}
|
|
417
494
|
}, { once: true });
|
|
418
495
|
return originalSend.apply(this, args);
|
|
419
496
|
};
|
|
@@ -447,37 +524,44 @@ function setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo = []) {
|
|
|
447
524
|
}
|
|
448
525
|
/**
|
|
449
526
|
* Performs an OPTIONS preflight check to decide header propagation.
|
|
450
|
-
* Returns
|
|
527
|
+
* Returns PROPAGATE if both header & method are allowed, IGNORE if disallowed
|
|
528
|
+
* or on a CORS error, null if OPTIONS returned non-2xx.
|
|
451
529
|
*/
|
|
452
530
|
async function performOptionsPreflight(target, thisArg, url, init, sessionId, domain) {
|
|
453
531
|
try {
|
|
454
532
|
const headers = new Headers(init.headers || {});
|
|
455
533
|
headers.set(xSf3RidHeader, sessionId);
|
|
456
|
-
const
|
|
457
|
-
method:
|
|
534
|
+
const opts = {
|
|
535
|
+
method: OPTIONS,
|
|
458
536
|
headers,
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
537
|
+
mode: "cors",
|
|
538
|
+
credentials: init.credentials || SAME_ORIGIN,
|
|
539
|
+
};
|
|
540
|
+
const response = await target.call(thisArg, url, opts);
|
|
541
|
+
if (!response.ok) {
|
|
464
542
|
DEBUG &&
|
|
465
543
|
console.log(`[Fetch Interceptor] OPTIONS returned status ${response.status} for ${domain}`);
|
|
466
544
|
return null;
|
|
467
545
|
}
|
|
546
|
+
// 1️⃣ Check that our header is allowed
|
|
547
|
+
const allowedHeaders = response.headers
|
|
548
|
+
.get(ALLOWED_HEADERS_HEADER)
|
|
549
|
+
?.split(",")
|
|
550
|
+
.map((h) => h.trim().toLowerCase()) || [];
|
|
551
|
+
if (!allowedHeaders.includes(xSf3RidHeader.toLowerCase())) {
|
|
552
|
+
DEBUG &&
|
|
553
|
+
console.log(`[Fetch Interceptor] Header ${xSf3RidHeader} not allowed by preflight for ${domain}`);
|
|
554
|
+
return ActionType.IGNORE;
|
|
555
|
+
}
|
|
556
|
+
return ActionType.PROPAGATE;
|
|
468
557
|
}
|
|
469
558
|
catch (error) {
|
|
470
|
-
|
|
471
|
-
|
|
559
|
+
if (error instanceof TypeError &&
|
|
560
|
+
error.message.toLowerCase().includes(CORS_KEYWORD.toLowerCase())) {
|
|
472
561
|
DEBUG &&
|
|
473
562
|
console.log(`[Fetch Interceptor] Preflight OPTIONS CORS error for ${domain}:`, error);
|
|
474
563
|
return ActionType.IGNORE;
|
|
475
564
|
}
|
|
476
|
-
// Other failures also ignored as some APIs or reverse proxies (e.g. NGINX) don’t route
|
|
477
|
-
// or handle OPTIONS requests, leading to:
|
|
478
|
-
// * 404 Not Found
|
|
479
|
-
// * 405 Method Not Allowed
|
|
480
|
-
// * 500 Internal Server Error
|
|
481
565
|
DEBUG &&
|
|
482
566
|
console.log(`[Fetch Interceptor] Preflight OPTIONS failed for ${domain}:`, error);
|
|
483
567
|
return null;
|
|
@@ -487,7 +571,7 @@ async function performOptionsPreflight(target, thisArg, url, init, sessionId, do
|
|
|
487
571
|
function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
488
572
|
const originalFetch = window.fetch;
|
|
489
573
|
const sessionId = getOrSetSessionId();
|
|
490
|
-
const
|
|
574
|
+
const cachedDomains = new Map();
|
|
491
575
|
window.fetch = new Proxy(originalFetch, {
|
|
492
576
|
apply: async (target, thisArg, args) => {
|
|
493
577
|
let input = args[0];
|
|
@@ -508,8 +592,8 @@ function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
|
508
592
|
// Determine the target domain
|
|
509
593
|
const domain = new URL(url, window.location.href).hostname;
|
|
510
594
|
// Use cached decision if available
|
|
511
|
-
if (
|
|
512
|
-
const decision =
|
|
595
|
+
if (cachedDomains.has(domain)) {
|
|
596
|
+
const decision = cachedDomains.get(domain);
|
|
513
597
|
if (decision === ActionType.IGNORE) {
|
|
514
598
|
return target.apply(thisArg, args);
|
|
515
599
|
}
|
|
@@ -519,7 +603,7 @@ function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
|
519
603
|
}
|
|
520
604
|
// Check exclusion domains and cache 'ignore'
|
|
521
605
|
if (shouldSkipHeadersPropagation(url, domainsToNotPropagateHeadersTo)) {
|
|
522
|
-
|
|
606
|
+
cachedDomains.set(domain, ActionType.IGNORE);
|
|
523
607
|
return target.apply(thisArg, args);
|
|
524
608
|
}
|
|
525
609
|
let decision = ActionType.PROPAGATE;
|
|
@@ -533,7 +617,7 @@ function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
|
533
617
|
dynamicExcludedHosts.add(domain);
|
|
534
618
|
}
|
|
535
619
|
}
|
|
536
|
-
|
|
620
|
+
cachedDomains.set(domain, decision);
|
|
537
621
|
if (decision === ActionType.PROPAGATE) {
|
|
538
622
|
return injectHeaderWrapper(target, thisArg, args, input, init, sessionId, url);
|
|
539
623
|
}
|
|
@@ -595,8 +679,8 @@ function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
|
595
679
|
const errorMessage = error.message || "Fetch request failed";
|
|
596
680
|
// Treat fetch errors (likely CORS failures) as ignore
|
|
597
681
|
// Since some APIs or reverse proxies (such as NGINX) do not route or handle OPTIONS requests, CORS may occur while the request is being made.
|
|
598
|
-
if (error instanceof TypeError
|
|
599
|
-
error?.message?.includes(CORS_KEYWORD)) {
|
|
682
|
+
if (error instanceof TypeError &&
|
|
683
|
+
error?.message?.toLowerCase().includes(CORS_KEYWORD.toLowerCase())) {
|
|
600
684
|
DEBUG &&
|
|
601
685
|
console.log(`[Fetch Interceptor] CORS error for ${domain}:`, error);
|
|
602
686
|
dynamicExcludedHosts.add(domain);
|
|
@@ -654,23 +738,44 @@ function setupFetchInterceptor(domainsToNotPropagateHeadersTo = []) {
|
|
|
654
738
|
async function retryWithoutPropagateHeaders(target, thisArg, args, url) {
|
|
655
739
|
const domain = new URL(url).hostname;
|
|
656
740
|
try {
|
|
657
|
-
//
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
741
|
+
// **Fix:** Properly await and clone the request without the tracing header
|
|
742
|
+
let input = args[0];
|
|
743
|
+
let init = args[1] || {};
|
|
744
|
+
if (typeof input === "string" || input instanceof URL) {
|
|
745
|
+
const retryInit = { ...init };
|
|
746
|
+
const retryHeaders = new Headers(init.headers || {});
|
|
747
|
+
retryHeaders.delete(xSf3RidHeader);
|
|
748
|
+
retryInit.headers = retryHeaders;
|
|
749
|
+
const response = await target.call(thisArg, input, retryInit);
|
|
750
|
+
if (response.ok || !BAD_HTTP_STATUS.includes(response.status)) {
|
|
751
|
+
dynamicExcludedHosts.add(domain);
|
|
752
|
+
cachedDomains.set(domain, ActionType.IGNORE);
|
|
753
|
+
DEBUG &&
|
|
754
|
+
console.info(`Retried request to ${url} without ${xSf3RidHeader} succeeded. Added "${domain}" to exclusion list.`);
|
|
755
|
+
}
|
|
756
|
+
return response;
|
|
757
|
+
}
|
|
758
|
+
else if (input instanceof Request) {
|
|
759
|
+
const original = input;
|
|
760
|
+
const cloned = original.clone();
|
|
761
|
+
const retryHeaders = new Headers(cloned.headers);
|
|
762
|
+
retryHeaders.delete(xSf3RidHeader);
|
|
763
|
+
const retryReq = new Request(cloned, { headers: retryHeaders });
|
|
764
|
+
const response = await target.call(thisArg, retryReq, init);
|
|
765
|
+
if (response.ok || !BAD_HTTP_STATUS.includes(response.status)) {
|
|
766
|
+
dynamicExcludedHosts.add(domain);
|
|
767
|
+
cachedDomains.set(domain, ActionType.IGNORE);
|
|
768
|
+
DEBUG &&
|
|
769
|
+
console.info(`Retried request to ${url} without ${xSf3RidHeader} succeeded. Added "${domain}" to exclusion list.`);
|
|
770
|
+
}
|
|
771
|
+
return response;
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
// Fallback
|
|
775
|
+
return target.apply(thisArg, args);
|
|
668
776
|
}
|
|
669
|
-
// Return the response from the retry attempt (successful or not)
|
|
670
|
-
return response;
|
|
671
777
|
}
|
|
672
778
|
catch (retryError) {
|
|
673
|
-
// Propagate the failure (no domain added to exclude lists since retry failed)
|
|
674
779
|
DEBUG &&
|
|
675
780
|
console.log(`Retry without ${xSf3RidHeader} for ${url} also failed:`, retryError);
|
|
676
781
|
throw retryError;
|
|
@@ -689,13 +794,19 @@ export async function startRecording({ apiKey, backendApi = "https://api-service
|
|
|
689
794
|
host?.trim() && originalExcludedAdd.call(dynamicExcludedHosts, host);
|
|
690
795
|
});
|
|
691
796
|
// Persist updated excluded hosts to localStorage
|
|
692
|
-
|
|
797
|
+
try {
|
|
798
|
+
localStorage.setItem(DYNAMIC_EXCLUDED_HOSTS_KEY, JSON.stringify(Array.from(dynamicExcludedHosts)));
|
|
799
|
+
}
|
|
800
|
+
catch { }
|
|
693
801
|
// Add provided domainsToPropagateHeaderTo to dynamicPassedHosts
|
|
694
802
|
domainsToPropagateHeaderTo.forEach((host) => {
|
|
695
803
|
originalPassedAdd.call(dynamicPassedHosts, host);
|
|
696
804
|
});
|
|
697
805
|
// Persist updated included hosts to localStorage
|
|
698
|
-
|
|
806
|
+
try {
|
|
807
|
+
localStorage.setItem(DYNAMIC_PASSED_HOSTS_KEY, JSON.stringify(Array.from(dynamicPassedHosts)));
|
|
808
|
+
}
|
|
809
|
+
catch { }
|
|
699
810
|
// Non-blocking GraphQL request to send the domains if provided
|
|
700
811
|
if (dynamicExcludedHosts.size > 0) {
|
|
701
812
|
sendDomainsToNotPropagateHeaderTo(apiKey, [...dynamicExcludedHosts, ...DOMAINS_TO_NOT_PROPAGATE_HEADER_TO_DEFAULT], backendApi).catch((error) => console.error("Failed to send domains to not propagate header to:", error));
|
|
@@ -703,8 +814,8 @@ export async function startRecording({ apiKey, backendApi = "https://api-service
|
|
|
703
814
|
sessionStorage.setItem(SF_API_KEY_FOR_UPDATE, apiKey);
|
|
704
815
|
sessionStorage.setItem(SF_BACKEND_API, backendApi);
|
|
705
816
|
// Setup interceptors with custom ignore and propagate domains
|
|
706
|
-
setupXMLHttpRequestInterceptor(
|
|
707
|
-
setupFetchInterceptor(
|
|
817
|
+
setupXMLHttpRequestInterceptor(domainsToNotPropagateHeaderTo);
|
|
818
|
+
setupFetchInterceptor(domainsToNotPropagateHeaderTo);
|
|
708
819
|
gatherAndCacheDeviceInfo();
|
|
709
820
|
try {
|
|
710
821
|
const captureSettingsResponse = await fetchCaptureSettings(apiKey, backendApi);
|