@sailfish-ai/recorder 1.7.35 → 1.7.41
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/eventStore.js +89 -31
- package/dist/inAppReportIssueModal.js +45 -21
- package/dist/index.js +128 -92
- package/dist/notifyEventStore.js +73 -19
- 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/segmentHelpers.js +150 -0
- package/dist/sendSailfishMessages.js +10 -0
- package/dist/types/inAppReportIssueModal.d.ts +6 -0
- package/dist/types/segmentHelpers.d.ts +10 -0
- package/dist/types/sendSailfishMessages.d.ts +1 -0
- package/dist/types/utils.d.ts +2 -0
- package/dist/utils.js +7 -0
- package/package.json +1 -1
package/dist/eventStore.js
CHANGED
|
@@ -1,42 +1,100 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
// lib/safe-idb.ts
|
|
2
|
+
// SSR/Edge-safe IndexedDB helpers with zero top-level access to `indexedDB`.
|
|
3
|
+
// On the server (or any env without IDB), functions are no-ops / return safe defaults.
|
|
4
|
+
const DB_NAME = "leapsEventDB";
|
|
5
|
+
const STORE_NAME = "recordingEvents";
|
|
6
|
+
const DB_VERSION = 1;
|
|
7
|
+
// Cached open promise; never created on SSR
|
|
8
|
+
let _dbPromise = null;
|
|
9
|
+
// Narrow, safe feature check (no throws in weird runtimes)
|
|
10
|
+
function hasIndexedDB() {
|
|
11
|
+
return typeof globalThis !== "undefined" && !!globalThis.indexedDB;
|
|
12
|
+
}
|
|
13
|
+
// Open the DB lazily and only on the client.
|
|
14
|
+
function openDb() {
|
|
15
|
+
if (!hasIndexedDB())
|
|
16
|
+
return Promise.resolve(null);
|
|
17
|
+
if (_dbPromise)
|
|
18
|
+
return _dbPromise;
|
|
19
|
+
_dbPromise = new Promise((resolve) => {
|
|
20
|
+
try {
|
|
21
|
+
const req = globalThis.indexedDB.open(DB_NAME, DB_VERSION);
|
|
22
|
+
req.onupgradeneeded = () => {
|
|
23
|
+
const db = req.result;
|
|
24
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
25
|
+
db.createObjectStore(STORE_NAME, {
|
|
26
|
+
keyPath: "id",
|
|
27
|
+
autoIncrement: true,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
req.onsuccess = () => resolve(req.result);
|
|
32
|
+
req.onerror = () => resolve(null); // fail closed → treat as unavailable
|
|
33
|
+
req.onblocked = () => {
|
|
34
|
+
// Best effort: resolve null to avoid hanging builds if something odd blocks upgrades
|
|
35
|
+
resolve(null);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Some exotic environments can still throw on open()
|
|
40
|
+
resolve(null);
|
|
8
41
|
}
|
|
9
|
-
}
|
|
10
|
-
|
|
42
|
+
});
|
|
43
|
+
return _dbPromise;
|
|
44
|
+
}
|
|
45
|
+
function withStore(mode, fn) {
|
|
46
|
+
return openDb().then((db) => {
|
|
47
|
+
if (!db)
|
|
48
|
+
return null;
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
try {
|
|
51
|
+
const tx = db.transaction(STORE_NAME, mode);
|
|
52
|
+
const store = tx.objectStore(STORE_NAME);
|
|
53
|
+
Promise.resolve(fn(store))
|
|
54
|
+
.then((result) => {
|
|
55
|
+
tx.oncomplete = () => resolve(result);
|
|
56
|
+
tx.onerror = () => resolve(null);
|
|
57
|
+
// If fn() already did async work, ensure we complete; otherwise completing will call resolve above
|
|
58
|
+
})
|
|
59
|
+
.catch(() => resolve(null));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
resolve(null);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// ── Public API (SSR-safe) ──────────────────────────────────────────────────────
|
|
11
68
|
export async function saveEventToIDB(event) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
await tx.done;
|
|
69
|
+
await withStore("readwrite", (store) => {
|
|
70
|
+
store.add({ timestamp: Date.now(), data: event });
|
|
71
|
+
});
|
|
16
72
|
}
|
|
17
73
|
export async function saveEventsToIDB(events) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
await tx.done;
|
|
74
|
+
await withStore("readwrite", async (store) => {
|
|
75
|
+
for (const event of events) {
|
|
76
|
+
store.add({ timestamp: Date.now(), data: event });
|
|
77
|
+
}
|
|
78
|
+
});
|
|
24
79
|
}
|
|
25
80
|
export async function getAllIndexedEvents() {
|
|
26
|
-
const
|
|
27
|
-
|
|
81
|
+
const result = await withStore("readonly", (store) => {
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
const req = store.getAll();
|
|
84
|
+
req.onsuccess = () => resolve(req.result);
|
|
85
|
+
req.onerror = () => resolve([]);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
return result ?? []; // SSR/Edge → []
|
|
28
89
|
}
|
|
29
90
|
export async function deleteEventById(id) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
await tx.done;
|
|
91
|
+
await withStore("readwrite", (store) => {
|
|
92
|
+
store.delete(id);
|
|
93
|
+
});
|
|
34
94
|
}
|
|
35
95
|
export async function deleteEventsByIds(ids) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
await tx.done;
|
|
96
|
+
await withStore("readwrite", async (store) => {
|
|
97
|
+
for (const id of ids)
|
|
98
|
+
store.delete(id);
|
|
99
|
+
});
|
|
42
100
|
}
|
|
@@ -9,10 +9,12 @@ let recordingStartTime = null;
|
|
|
9
9
|
let recordingEndTime = null;
|
|
10
10
|
let timerInterval = null;
|
|
11
11
|
let isRecording = false;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
export const ReportIssueContext = {
|
|
13
|
+
resolveSessionId: null,
|
|
14
|
+
apiKey: null,
|
|
15
|
+
backendApi: null,
|
|
16
|
+
triageBaseUrl: "https://app.sailfishqa.com",
|
|
17
|
+
};
|
|
16
18
|
function isMacPlatform() {
|
|
17
19
|
// Newer API (Chrome, Edge, Opera)
|
|
18
20
|
const uaData = navigator.userAgentData;
|
|
@@ -32,9 +34,9 @@ function isTypingInInput() {
|
|
|
32
34
|
(el instanceof HTMLElement && el.isContentEditable));
|
|
33
35
|
}
|
|
34
36
|
export function setupIssueReporting(options) {
|
|
35
|
-
apiKey = options.apiKey;
|
|
36
|
-
backendApi = options.backendApi;
|
|
37
|
-
resolveSessionId = options.getSessionId;
|
|
37
|
+
ReportIssueContext.apiKey = options.apiKey;
|
|
38
|
+
ReportIssueContext.backendApi = options.backendApi;
|
|
39
|
+
ReportIssueContext.resolveSessionId = options.getSessionId;
|
|
38
40
|
// Attach keyboard shortcuts
|
|
39
41
|
window.addEventListener("keydown", (e) => {
|
|
40
42
|
const typingInInput = isTypingInInput();
|
|
@@ -91,10 +93,10 @@ export function setupIssueReporting(options) {
|
|
|
91
93
|
});
|
|
92
94
|
}
|
|
93
95
|
function getSessionIdSafely() {
|
|
94
|
-
if (!resolveSessionId) {
|
|
96
|
+
if (!ReportIssueContext.resolveSessionId) {
|
|
95
97
|
throw new Error("getSessionId not defined");
|
|
96
98
|
}
|
|
97
|
-
return resolveSessionId();
|
|
99
|
+
return ReportIssueContext.resolveSessionId();
|
|
98
100
|
}
|
|
99
101
|
export function openReportIssueModal(customBaseUrl) {
|
|
100
102
|
if (isRecording) {
|
|
@@ -105,7 +107,8 @@ export function openReportIssueModal(customBaseUrl) {
|
|
|
105
107
|
if (modalEl)
|
|
106
108
|
document.body.appendChild(modalEl);
|
|
107
109
|
// Store custom base URL globally for reuse
|
|
108
|
-
triageBaseUrl =
|
|
110
|
+
ReportIssueContext.triageBaseUrl =
|
|
111
|
+
customBaseUrl ?? "https://app.sailfishqa.com";
|
|
109
112
|
}
|
|
110
113
|
function closeModal() {
|
|
111
114
|
// ✅ Explicitly blur the focused element
|
|
@@ -266,7 +269,7 @@ function injectModalHTML(initialMode = "lookback") {
|
|
|
266
269
|
style="background: #295DBF; color:white; border:none; padding:8px 16px;
|
|
267
270
|
border-radius:6px; font-size:14px; line-height: 24px; font-weight:500;
|
|
268
271
|
cursor:${isStartNow ? "not-allowed" : "pointer"}; opacity:${isStartNow ? "0.4" : "1"}; margin-bottom:1px" ${isStartNow ? "disabled" : ""}>
|
|
269
|
-
|
|
272
|
+
Report <span style="margin-left: 8px; display: inline-flex; align-items: center; gap: 4px; color: #94A3B8; font-size: 12px; line-height:16px;">
|
|
270
273
|
<span style="background: #F1F5F9; border-radius: 4px; padding: 0 4px; font-weight: 500;">${getShortcutKeyCmdCtrlLabel()}</span>
|
|
271
274
|
+
|
|
272
275
|
<span style="background: #F1F5F9; border-radius: 4px; padding: 0 4px; font-weight: 500;">ENTER</span>
|
|
@@ -561,7 +564,7 @@ function reopenModalAfterStop() {
|
|
|
561
564
|
async function createTriage(startTimestamp, endTimestamp, description) {
|
|
562
565
|
try {
|
|
563
566
|
showTriageStatusModal(true);
|
|
564
|
-
const response = await createTriageFromRecorder(apiKey, backendApi, getSessionIdSafely(), startTimestamp, endTimestamp, description);
|
|
567
|
+
const response = await createTriageFromRecorder(ReportIssueContext.apiKey, ReportIssueContext.backendApi, getSessionIdSafely(), startTimestamp, endTimestamp, description);
|
|
565
568
|
const triageId = response?.data?.createTriageFromRecorder?.id;
|
|
566
569
|
if (triageId) {
|
|
567
570
|
showTriageStatusModal(false, triageId);
|
|
@@ -578,10 +581,20 @@ async function createTriage(startTimestamp, endTimestamp, description) {
|
|
|
578
581
|
}
|
|
579
582
|
function showTriageStatusModal(isLoading, triageId) {
|
|
580
583
|
removeExistingModals();
|
|
581
|
-
const triageUrl = triageId
|
|
584
|
+
const triageUrl = triageId
|
|
585
|
+
? `${ReportIssueContext.triageBaseUrl}/triage/${triageId}?from=inAppReportIssue`
|
|
586
|
+
: "";
|
|
582
587
|
const container = document.createElement("div");
|
|
583
588
|
container.id = "sf-triage-status-modal";
|
|
584
|
-
|
|
589
|
+
Object.assign(container.style, {
|
|
590
|
+
position: "fixed",
|
|
591
|
+
inset: "0",
|
|
592
|
+
zIndex: "9998",
|
|
593
|
+
display: "flex",
|
|
594
|
+
alignItems: "center",
|
|
595
|
+
justifyContent: "center",
|
|
596
|
+
});
|
|
597
|
+
const statusTitle = isLoading ? "Reporting Issue..." : "Issue reported!";
|
|
585
598
|
const statusSubtitle = isLoading
|
|
586
599
|
? `<p style="font-size:14px; color:#64748b; line-height:20px;">This may take ~10 seconds</p>`
|
|
587
600
|
: "";
|
|
@@ -601,10 +614,10 @@ function showTriageStatusModal(isLoading, triageId) {
|
|
|
601
614
|
: "";
|
|
602
615
|
container.innerHTML = `
|
|
603
616
|
<div style="position:fixed; inset:0; background:rgba(0,0,0,0.4); z-index:9998;"></div>
|
|
604
|
-
<div
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
617
|
+
<div id="sf-triage-card"
|
|
618
|
+
style="position:relative; background:#fff; padding:24px; border-radius:12px; width:300px; max-width:90%;
|
|
619
|
+
font-family:sans-serif; box-shadow:0 4px 20px rgba(0,0,0,0.15);
|
|
620
|
+
z-index:9999; opacity:1; transition:opacity 300ms ease;">
|
|
608
621
|
<div style="position:absolute; top:24px; right:48px;">${copiedStatus}</div>
|
|
609
622
|
|
|
610
623
|
<button id="sf-triage-modal-close"
|
|
@@ -615,7 +628,7 @@ function showTriageStatusModal(isLoading, triageId) {
|
|
|
615
628
|
</svg>
|
|
616
629
|
</button>
|
|
617
630
|
|
|
618
|
-
<h2 style="font-size:18px; font-weight:600; margin-bottom:${isLoading ? 8 : 40}px; line-height:
|
|
631
|
+
<h2 style="font-size:18px; font-weight:600; margin-bottom:${isLoading ? 8 : 40}px; line-height:28px;">${statusTitle}</h2>
|
|
619
632
|
${statusSubtitle}
|
|
620
633
|
${spinner}
|
|
621
634
|
|
|
@@ -628,7 +641,7 @@ function showTriageStatusModal(isLoading, triageId) {
|
|
|
628
641
|
<button id="sf-view-triage-btn"
|
|
629
642
|
style="display:flex; align-items:center; gap:8px; background:#295dbf; border:none;
|
|
630
643
|
padding:8px 16px; border-radius:6px; font-size:14px; color:white; cursor:pointer;">
|
|
631
|
-
View
|
|
644
|
+
View
|
|
632
645
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
633
646
|
<path d="M12 8.66667V12.6667C12 13.0203 11.8595 13.3594 11.6095 13.6095C11.3594 13.8595 11.0203 14 10.6667 14H3.33333C2.97971 14 2.64057 13.8595 2.39052 13.6095C2.14048 13.3594 2 13.0203 2 12.6667V5.33333C2 4.97971 2.14048 4.64057 2.39052 4.39052C2.64057 4.14048 2.97971 4 3.33333 4H7.33333" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
|
634
647
|
<path d="M10 2H14V6" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
|
@@ -642,10 +655,12 @@ function showTriageStatusModal(isLoading, triageId) {
|
|
|
642
655
|
</style>
|
|
643
656
|
`;
|
|
644
657
|
document.body.appendChild(container);
|
|
658
|
+
const card = container.querySelector("#sf-triage-card");
|
|
659
|
+
// Close handler with fade only on card
|
|
645
660
|
document
|
|
646
661
|
.getElementById("sf-triage-modal-close")
|
|
647
662
|
?.addEventListener("click", () => {
|
|
648
|
-
container
|
|
663
|
+
fadeCardAndRemove(container, card, 300);
|
|
649
664
|
});
|
|
650
665
|
const copyBtn = document.getElementById("sf-copy-triage-link");
|
|
651
666
|
const viewBtn = document.getElementById("sf-view-triage-btn");
|
|
@@ -672,8 +687,17 @@ function showTriageStatusModal(isLoading, triageId) {
|
|
|
672
687
|
window.open(triageUrl, "_blank");
|
|
673
688
|
}
|
|
674
689
|
});
|
|
690
|
+
// Auto-hide after success
|
|
691
|
+
setTimeout(() => fadeCardAndRemove(container, card, 300), 10000);
|
|
675
692
|
}
|
|
676
693
|
}
|
|
694
|
+
function fadeCardAndRemove(container, card, durationMs = 300) {
|
|
695
|
+
card.style.opacity = "0"; // fade only the card
|
|
696
|
+
card.addEventListener("transitionend", () => container.remove(), {
|
|
697
|
+
once: true,
|
|
698
|
+
});
|
|
699
|
+
setTimeout(() => container.remove(), durationMs + 100); // fallback
|
|
700
|
+
}
|
|
677
701
|
function removeExistingModals() {
|
|
678
702
|
document.getElementById("sf-report-issue-modal")?.remove();
|
|
679
703
|
document.getElementById("sf-triage-status-modal")?.remove();
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
const DEBUG = import.meta.env.VITE_DEBUG ? import.meta.env.VITE_DEBUG : false;
|
|
2
|
+
// ── SSR guards ─────────────────────────────────────────
|
|
3
|
+
const HAS_WINDOW = typeof globalThis !== "undefined" &&
|
|
4
|
+
typeof globalThis.window !== "undefined";
|
|
5
|
+
const HAS_DOCUMENT = typeof globalThis !== "undefined" &&
|
|
6
|
+
typeof globalThis.document !== "undefined";
|
|
7
|
+
const HAS_LOCAL_STORAGE = typeof globalThis !== "undefined" && "localStorage" in globalThis;
|
|
8
|
+
const HAS_SESSION_STORAGE = typeof globalThis !== "undefined" && "sessionStorage" in globalThis;
|
|
2
9
|
// import { NetworkRecordOptions } from "@sailfish-rrweb/rrweb-plugin-network-record";
|
|
3
10
|
import { v4 as uuidv4 } from "uuid";
|
|
4
11
|
import { NetworkRequestEventId, STATIC_EXTENSIONS, xSf3RidHeader, } from "./constants";
|
|
@@ -57,16 +64,18 @@ dynamicExcludedHosts.add = (host) => {
|
|
|
57
64
|
}
|
|
58
65
|
// 4. Persist wrapper to localStorage
|
|
59
66
|
try {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
if (HAS_LOCAL_STORAGE) {
|
|
68
|
+
// TODO decouple into two logical pieces
|
|
69
|
+
const wrapper = {
|
|
70
|
+
version: STORAGE_VERSION,
|
|
71
|
+
entries: {},
|
|
72
|
+
};
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
for (const p of dynamicExcludedHosts) {
|
|
75
|
+
wrapper.entries[p] = now;
|
|
76
|
+
}
|
|
77
|
+
localStorage.setItem(DYNAMIC_EXCLUDED_HOSTS_KEY, JSON.stringify(wrapper));
|
|
68
78
|
}
|
|
69
|
-
localStorage.setItem(DYNAMIC_EXCLUDED_HOSTS_KEY, JSON.stringify(wrapper));
|
|
70
79
|
}
|
|
71
80
|
catch (e) {
|
|
72
81
|
if (DEBUG)
|
|
@@ -85,35 +94,37 @@ dynamicPassedHosts.add = (host) => {
|
|
|
85
94
|
}
|
|
86
95
|
// 1. Persist wrapper to localStorage (update timestamp unconditionally)
|
|
87
96
|
try {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
else {
|
|
95
|
-
const parsed = JSON.parse(raw);
|
|
96
|
-
if (Array.isArray(parsed)) {
|
|
97
|
-
// migrate old array → wrapper
|
|
98
|
-
wrapper = {
|
|
99
|
-
version: STORAGE_VERSION,
|
|
100
|
-
entries: Object.fromEntries(parsed.map((h) => [h, Date.now()])),
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
else if (parsed &&
|
|
104
|
-
typeof parsed === "object" &&
|
|
105
|
-
"entries" in parsed &&
|
|
106
|
-
typeof parsed.entries === "object") {
|
|
107
|
-
wrapper = parsed;
|
|
97
|
+
if (HAS_LOCAL_STORAGE) {
|
|
98
|
+
// TODO decouple into two logical pieces
|
|
99
|
+
const raw = localStorage.getItem(DYNAMIC_PASSED_HOSTS_KEY);
|
|
100
|
+
let wrapper;
|
|
101
|
+
if (!raw) {
|
|
102
|
+
wrapper = { version: STORAGE_VERSION, entries: {} };
|
|
108
103
|
}
|
|
109
104
|
else {
|
|
110
|
-
|
|
105
|
+
const parsed = JSON.parse(raw);
|
|
106
|
+
if (Array.isArray(parsed)) {
|
|
107
|
+
// migrate old array → wrapper
|
|
108
|
+
wrapper = {
|
|
109
|
+
version: STORAGE_VERSION,
|
|
110
|
+
entries: Object.fromEntries(parsed.map((h) => [h, Date.now()])),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
else if (parsed &&
|
|
114
|
+
typeof parsed === "object" &&
|
|
115
|
+
"entries" in parsed &&
|
|
116
|
+
typeof parsed.entries === "object") {
|
|
117
|
+
wrapper = parsed;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
wrapper = { version: STORAGE_VERSION, entries: {} };
|
|
121
|
+
}
|
|
111
122
|
}
|
|
123
|
+
// always set/update the timestamp
|
|
124
|
+
wrapper.entries[cleaned] = Date.now();
|
|
125
|
+
wrapper.version = STORAGE_VERSION;
|
|
126
|
+
localStorage.setItem(DYNAMIC_PASSED_HOSTS_KEY, JSON.stringify(wrapper));
|
|
112
127
|
}
|
|
113
|
-
// always set/update the timestamp
|
|
114
|
-
wrapper.entries[cleaned] = Date.now();
|
|
115
|
-
wrapper.version = STORAGE_VERSION;
|
|
116
|
-
localStorage.setItem(DYNAMIC_PASSED_HOSTS_KEY, JSON.stringify(wrapper));
|
|
117
128
|
}
|
|
118
129
|
catch (e) {
|
|
119
130
|
if (DEBUG)
|
|
@@ -129,6 +140,8 @@ dynamicPassedHosts.add = (host) => {
|
|
|
129
140
|
* Notify the backend of the updated dynamicExcludedHosts
|
|
130
141
|
*/
|
|
131
142
|
function updateExcludedHostsStorageAndBackend(dynamicExcludedHosts) {
|
|
143
|
+
if (!HAS_SESSION_STORAGE)
|
|
144
|
+
return;
|
|
132
145
|
const apiKeyForUpdate = sessionStorage.getItem(SF_API_KEY_FOR_UPDATE) || "";
|
|
133
146
|
const apiForUpdate = sessionStorage.getItem(SF_BACKEND_API) || "";
|
|
134
147
|
if (!apiForUpdate) {
|
|
@@ -190,60 +203,64 @@ export function consolidateDynamicExclusions(hostPathSet, threshold = 3) {
|
|
|
190
203
|
return newSet;
|
|
191
204
|
}
|
|
192
205
|
// 2️⃣ Load & evict old entries (>7 days) + version check for Excluded
|
|
193
|
-
(
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
const wrapper = JSON.parse(stored);
|
|
199
|
-
// if it's from an old version, drop it
|
|
200
|
-
if (wrapper.version !== STORAGE_VERSION) {
|
|
201
|
-
localStorage.removeItem(DYNAMIC_EXCLUDED_HOSTS_KEY);
|
|
206
|
+
if (HAS_LOCAL_STORAGE) {
|
|
207
|
+
(() => {
|
|
208
|
+
const stored = localStorage.getItem(DYNAMIC_EXCLUDED_HOSTS_KEY);
|
|
209
|
+
if (!stored)
|
|
202
210
|
return;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
valid[host] = ts;
|
|
211
|
+
try {
|
|
212
|
+
const wrapper = JSON.parse(stored);
|
|
213
|
+
// if it's from an old version, drop it
|
|
214
|
+
if (wrapper.version !== STORAGE_VERSION) {
|
|
215
|
+
localStorage.removeItem(DYNAMIC_EXCLUDED_HOSTS_KEY);
|
|
216
|
+
return;
|
|
210
217
|
}
|
|
218
|
+
const now = Date.now();
|
|
219
|
+
const valid = {};
|
|
220
|
+
for (const [host, ts] of Object.entries(wrapper.entries)) {
|
|
221
|
+
if (now - ts < 7 * 24 * 60 * 60 * 1000) {
|
|
222
|
+
dynamicExcludedHosts.add(host);
|
|
223
|
+
valid[host] = ts;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
localStorage.setItem(DYNAMIC_EXCLUDED_HOSTS_KEY, JSON.stringify({ version: STORAGE_VERSION, entries: valid }));
|
|
211
227
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
})();
|
|
228
|
+
catch (e) {
|
|
229
|
+
if (DEBUG)
|
|
230
|
+
console.warn("Failed to parse dynamicExcludedHosts:", e);
|
|
231
|
+
localStorage.removeItem(DYNAMIC_EXCLUDED_HOSTS_KEY);
|
|
232
|
+
}
|
|
233
|
+
})();
|
|
234
|
+
}
|
|
220
235
|
// 3️⃣ Load & evict old entries (>7 days) + version check for Passed
|
|
221
|
-
(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
try {
|
|
226
|
-
const wrapper = JSON.parse(stored);
|
|
227
|
-
if (wrapper.version !== STORAGE_VERSION) {
|
|
228
|
-
localStorage.removeItem(DYNAMIC_PASSED_HOSTS_KEY);
|
|
236
|
+
if (HAS_LOCAL_STORAGE) {
|
|
237
|
+
(() => {
|
|
238
|
+
const stored = localStorage.getItem(DYNAMIC_PASSED_HOSTS_KEY);
|
|
239
|
+
if (!stored)
|
|
229
240
|
return;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
241
|
+
try {
|
|
242
|
+
const wrapper = JSON.parse(stored);
|
|
243
|
+
if (wrapper.version !== STORAGE_VERSION) {
|
|
244
|
+
localStorage.removeItem(DYNAMIC_PASSED_HOSTS_KEY);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const now = Date.now();
|
|
248
|
+
const valid = {};
|
|
249
|
+
for (const [host, ts] of Object.entries(wrapper.entries)) {
|
|
250
|
+
if (now - ts < 7 * 24 * 60 * 60 * 1000) {
|
|
251
|
+
dynamicPassedHosts.add(host);
|
|
252
|
+
valid[host] = ts;
|
|
253
|
+
}
|
|
237
254
|
}
|
|
255
|
+
localStorage.setItem(DYNAMIC_PASSED_HOSTS_KEY, JSON.stringify({ version: STORAGE_VERSION, entries: valid }));
|
|
238
256
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
})();
|
|
257
|
+
catch (e) {
|
|
258
|
+
if (DEBUG)
|
|
259
|
+
console.warn("Failed to parse dynamicPassedHosts:", e);
|
|
260
|
+
localStorage.removeItem(DYNAMIC_PASSED_HOSTS_KEY);
|
|
261
|
+
}
|
|
262
|
+
})();
|
|
263
|
+
}
|
|
247
264
|
const ActionType = {
|
|
248
265
|
PROPAGATE: "propagate",
|
|
249
266
|
IGNORE: "ignore",
|
|
@@ -266,7 +283,7 @@ export const DEFAULT_CONSOLE_RECORDING_SETTINGS = {
|
|
|
266
283
|
stringifyOptions: {
|
|
267
284
|
stringLengthLimit: 1000,
|
|
268
285
|
numOfKeysLimit: 20,
|
|
269
|
-
depthOfLimit:
|
|
286
|
+
depthOfLimit: 4,
|
|
270
287
|
},
|
|
271
288
|
logger: "console",
|
|
272
289
|
};
|
|
@@ -342,15 +359,25 @@ function sendTimeZone() {
|
|
|
342
359
|
sendMessage(message);
|
|
343
360
|
}
|
|
344
361
|
// Send standard information like userDeviceUuid and timeZone
|
|
345
|
-
|
|
346
|
-
|
|
362
|
+
if (HAS_WINDOW) {
|
|
363
|
+
sendUserDeviceUuid();
|
|
364
|
+
sendTimeZone();
|
|
365
|
+
}
|
|
347
366
|
// Function to get or set the device & program UUID in localStorage
|
|
348
367
|
function getOrSetUserDeviceUuid() {
|
|
349
|
-
let userDeviceUuid =
|
|
368
|
+
let userDeviceUuid = null;
|
|
369
|
+
if (HAS_LOCAL_STORAGE) {
|
|
370
|
+
try {
|
|
371
|
+
userDeviceUuid = localStorage.getItem("sailfishUserDeviceUuid");
|
|
372
|
+
}
|
|
373
|
+
catch { }
|
|
374
|
+
}
|
|
350
375
|
if (!userDeviceUuid) {
|
|
351
376
|
userDeviceUuid = uuidv4();
|
|
352
377
|
try {
|
|
353
|
-
|
|
378
|
+
if (HAS_LOCAL_STORAGE) {
|
|
379
|
+
localStorage.setItem("sailfishUserDeviceUuid", userDeviceUuid);
|
|
380
|
+
}
|
|
354
381
|
}
|
|
355
382
|
catch { }
|
|
356
383
|
}
|
|
@@ -359,6 +386,8 @@ function getOrSetUserDeviceUuid() {
|
|
|
359
386
|
// Storing the sailfishSessionId in window.name, as window.name retains its value after a page refresh
|
|
360
387
|
// but resets when a new tab (including a duplicated tab) is opened.
|
|
361
388
|
function getOrSetSessionId() {
|
|
389
|
+
if (!HAS_WINDOW)
|
|
390
|
+
return uuidv4();
|
|
362
391
|
if (!window.name) {
|
|
363
392
|
window.name = uuidv4();
|
|
364
393
|
}
|
|
@@ -371,17 +400,24 @@ function handleVisibilityChange() {
|
|
|
371
400
|
}
|
|
372
401
|
}
|
|
373
402
|
function clearPageVisitUuid() {
|
|
403
|
+
if (!HAS_SESSION_STORAGE)
|
|
404
|
+
return;
|
|
374
405
|
sessionStorage.removeItem("pageVisitUUID");
|
|
375
406
|
sessionStorage.removeItem("prevPageVisitUUID");
|
|
376
407
|
}
|
|
377
408
|
// Initialize event listeners for visibility change and page unload
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
409
|
+
if (HAS_DOCUMENT) {
|
|
410
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
411
|
+
}
|
|
412
|
+
if (HAS_WINDOW) {
|
|
413
|
+
window.addEventListener("beforeunload", () => {
|
|
414
|
+
window.name = "";
|
|
415
|
+
clearPageVisitUuid();
|
|
416
|
+
});
|
|
417
|
+
}
|
|
384
418
|
function storeCredentialsAndConnection({ apiKey, backendApi, }) {
|
|
419
|
+
if (!HAS_SESSION_STORAGE)
|
|
420
|
+
return;
|
|
385
421
|
sessionStorage.setItem("sailfishApiKey", apiKey);
|
|
386
422
|
sessionStorage.setItem("sailfishBackendApi", backendApi);
|
|
387
423
|
}
|