@sailfish-ai/recorder 1.8.18 → 1.8.19
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/recorder.cjs +82 -29
- package/dist/recorder.js +84 -31
- package/dist/recorder.js.br +0 -0
- package/dist/recorder.js.gz +0 -0
- package/dist/recorder.umd.cjs +82 -29
- package/dist/recording.js +14 -1
- package/dist/sourceLocation.js +175 -0
- package/dist/types/sourceLocation.d.ts +17 -0
- package/package.json +1 -1
package/dist/recorder.cjs
CHANGED
|
@@ -465,7 +465,7 @@ function initializeWebSocket(e, a, u, m2) {
|
|
|
465
465
|
const a2 = document.createElement("a");
|
|
466
466
|
return a2.href = e2, `${a2.hostname}${a2.port ? `:${a2.port}` : ""}`;
|
|
467
467
|
})(e);
|
|
468
|
-
let b2 = `${"https:" === new URL(e).protocol ? "wss" : "ws"}://${w2}/ws/notify/?apiKey=${a}&sessionId=${u}&sender=JS%2FTS&version=1.8.
|
|
468
|
+
let b2 = `${"https:" === new URL(e).protocol ? "wss" : "ws"}://${w2}/ws/notify/?apiKey=${a}&sessionId=${u}&sender=JS%2FTS&version=1.8.19`;
|
|
469
469
|
m2 && (b2 += `&envValue=${encodeURIComponent(m2)}`);
|
|
470
470
|
return oe = new U(b2, [], { connectionTimeout: 3e4 }), oe.addEventListener("open", () => {
|
|
471
471
|
ne && (console.log("[Sailfish] WebSocket connection opened"), console.log("[Sailfish] Function span tracking state: " + (ce ? "ENABLED" : "DISABLED"))), (async () => {
|
|
@@ -7622,6 +7622,58 @@ function R(e, a, u) {
|
|
|
7622
7622
|
}
|
|
7623
7623
|
}
|
|
7624
7624
|
var hd = ((e) => (e[e.DomContentLoaded = 0] = "DomContentLoaded", e[e.Load = 1] = "Load", e[e.FullSnapshot = 2] = "FullSnapshot", e[e.IncrementalSnapshot = 3] = "IncrementalSnapshot", e[e.Meta = 4] = "Meta", e[e.Custom = 5] = "Custom", e[e.Plugin = 6] = "Plugin", e[e.Device = 24] = "Device", e[e.SailfishCustom = 25] = "SailfishCustom", e))(hd || {});
|
|
7625
|
+
const fd = ["/node_modules/", "/@sailfish-ai/", "/@sailfish-rrweb/", "/dist/", "/webpack/", "/vite/", "/__vite", "/react-dom/", "/react/", "/scheduler/", "/<", "/chrome-extension://", "/extensions/"];
|
|
7626
|
+
function shouldSkipFrame(e) {
|
|
7627
|
+
return fd.some((a) => e.includes(a));
|
|
7628
|
+
}
|
|
7629
|
+
function normalizeFilePath(e) {
|
|
7630
|
+
let a = e;
|
|
7631
|
+
if (a.startsWith("file://") && (a = a.substring(7)), a.startsWith("webpack-internal:///") && (a = a.substring(20)), a.startsWith("webpack:///") && (a = a.substring(11)), a.startsWith("/@fs/") && (a = a.substring(5)), a.startsWith("http://") || a.startsWith("https://")) try {
|
|
7632
|
+
a = new URL(a).pathname || a, "/" === a && (a = "index.html");
|
|
7633
|
+
} catch {
|
|
7634
|
+
}
|
|
7635
|
+
return a;
|
|
7636
|
+
}
|
|
7637
|
+
function getCallerLocationFromTrace(e, a = 0) {
|
|
7638
|
+
if (!Array.isArray(e) || 0 === e.length) return [null, null];
|
|
7639
|
+
const u = (function parseRrwebTraceFrames(e2) {
|
|
7640
|
+
const a2 = [];
|
|
7641
|
+
for (const u2 of e2) {
|
|
7642
|
+
if (!u2) continue;
|
|
7643
|
+
const e3 = u2.startsWith("at ") ? u2.slice(3) : u2;
|
|
7644
|
+
let m2 = /^(.*?)\s+\((.+?):(\d+):(\d+)\)$/.exec(e3);
|
|
7645
|
+
m2 ? a2.push({ functionName: m2[1] || "<anonymous>", file: m2[2], line: parseInt(m2[3], 10), column: parseInt(m2[4], 10) }) : (m2 = /^(.+?):(\d+):(\d+)$/.exec(e3), m2 && a2.push({ functionName: "<anonymous>", file: m2[1], line: parseInt(m2[2], 10), column: parseInt(m2[3], 10) }));
|
|
7646
|
+
}
|
|
7647
|
+
return a2;
|
|
7648
|
+
})(e);
|
|
7649
|
+
for (let e2 = a; e2 < Math.min(u.length, a + 20); e2++) {
|
|
7650
|
+
const a2 = u[e2];
|
|
7651
|
+
if (!(a2 == null ? void 0 : a2.file)) continue;
|
|
7652
|
+
const m2 = normalizeFilePath(a2.file);
|
|
7653
|
+
if (!shouldSkipFrame(m2)) return [m2, a2.line];
|
|
7654
|
+
}
|
|
7655
|
+
return [null, null];
|
|
7656
|
+
}
|
|
7657
|
+
function getCallerLocation(e = 0) {
|
|
7658
|
+
const a = new Error().stack;
|
|
7659
|
+
if (!a) return [null, null];
|
|
7660
|
+
const u = (function parseV8Stack(e2) {
|
|
7661
|
+
if (!e2) return [];
|
|
7662
|
+
const a2 = e2.split("\n").slice(1), u2 = [];
|
|
7663
|
+
for (const e3 of a2) {
|
|
7664
|
+
let a3 = /at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/.exec(e3);
|
|
7665
|
+
a3 ? u2.push({ functionName: a3[1], file: a3[2], line: parseInt(a3[3], 10), column: parseInt(a3[4], 10) }) : (a3 = /at\s+(.+?):(\d+):(\d+)/.exec(e3), a3 && u2.push({ functionName: "<anonymous>", file: a3[1], line: parseInt(a3[2], 10), column: parseInt(a3[3], 10) }));
|
|
7666
|
+
}
|
|
7667
|
+
return u2;
|
|
7668
|
+
})(a), m2 = 1 + e;
|
|
7669
|
+
for (let e2 = m2; e2 < Math.min(u.length, m2 + 20); e2++) {
|
|
7670
|
+
const a2 = u[e2];
|
|
7671
|
+
if (!(a2 == null ? void 0 : a2.file)) continue;
|
|
7672
|
+
const m3 = normalizeFilePath(a2.file);
|
|
7673
|
+
if (!shouldSkipFrame(m3)) return [m3, a2.line];
|
|
7674
|
+
}
|
|
7675
|
+
return [null, null];
|
|
7676
|
+
}
|
|
7625
7677
|
function suppressConsoleLogsDuringCall(e) {
|
|
7626
7678
|
const a = console.log, u = console.warn, m2 = console.error;
|
|
7627
7679
|
console.log = () => {
|
|
@@ -7634,7 +7686,7 @@ function suppressConsoleLogsDuringCall(e) {
|
|
|
7634
7686
|
console.log = a, console.warn = u, console.error = m2;
|
|
7635
7687
|
}
|
|
7636
7688
|
}
|
|
7637
|
-
const
|
|
7689
|
+
const md = "zendesk_chat", gd = "Zendesk";
|
|
7638
7690
|
function zE_safe(...e) {
|
|
7639
7691
|
try {
|
|
7640
7692
|
if ((function hasZendesk() {
|
|
@@ -7673,7 +7725,8 @@ function initializeDomContentEvents(e) {
|
|
|
7673
7725
|
function initializeConsolePlugin(e, a) {
|
|
7674
7726
|
const { name: u, observer: m2 } = /* @__PURE__ */ ((e2) => ({ name: "@sailfish-rrweb/rrweb/console@1", observer: R, options: e2 }))(e);
|
|
7675
7727
|
m2((e2) => {
|
|
7676
|
-
|
|
7728
|
+
const m3 = e2, [w2, b2] = getCallerLocationFromTrace(m3 == null ? void 0 : m3.trace, 0), [x2, C2] = getCallerLocation(2), I2 = w2 ?? x2, E2 = b2 ?? C2, _2 = { ...m3, sourceFile: I2, sourceLine: E2 };
|
|
7729
|
+
sendEvent({ type: hd.Plugin, timestamp: Date.now(), data: { plugin: u, payload: _2 }, sessionId: a, ...getUrlAndStoredUuids() });
|
|
7677
7730
|
}, window, e);
|
|
7678
7731
|
}
|
|
7679
7732
|
async function initializeRecording(e, a, u, m2, w2) {
|
|
@@ -7693,11 +7746,11 @@ async function initializeRecording(e, a, u, m2, w2) {
|
|
|
7693
7746
|
zE_safe("messenger:set", "conversationTags", [`sailfish-session-${m2}`]);
|
|
7694
7747
|
});
|
|
7695
7748
|
const handleWidgetOpen = () => {
|
|
7696
|
-
ae.addSailfishEvent(hd.SailfishCustom, { action: "customer support chat opened", element_id:
|
|
7749
|
+
ae.addSailfishEvent(hd.SailfishCustom, { action: "customer support chat opened", element_id: md, provider: gd });
|
|
7697
7750
|
}, handleWidgetClose = () => {
|
|
7698
|
-
ae.addSailfishEvent(hd.SailfishCustom, { action: "customer support chat closed", element_id:
|
|
7751
|
+
ae.addSailfishEvent(hd.SailfishCustom, { action: "customer support chat closed", element_id: md, provider: gd });
|
|
7699
7752
|
}, handleUnreadMessages = (e2) => {
|
|
7700
|
-
ae.addSailfishEvent(hd.SailfishCustom, { action: "zendesk unreadmessages", element_id:
|
|
7753
|
+
ae.addSailfishEvent(hd.SailfishCustom, { action: "zendesk unreadmessages", element_id: md, provider: gd });
|
|
7701
7754
|
};
|
|
7702
7755
|
suppressConsoleLogsDuringCall(() => {
|
|
7703
7756
|
zE_safe("messenger:on", "open", handleWidgetOpen), zE_safe("messenger:on", "close", handleWidgetClose), zE_safe("messenger:on", "unreadMessages", handleUnreadMessages);
|
|
@@ -7708,8 +7761,8 @@ async function initializeRecording(e, a, u, m2, w2) {
|
|
|
7708
7761
|
}
|
|
7709
7762
|
return b2;
|
|
7710
7763
|
}
|
|
7711
|
-
let
|
|
7712
|
-
const
|
|
7764
|
+
let yd = null, wd = null;
|
|
7765
|
+
const bd = readDebugFlag(), Sd = ["t.co", "*.twitter.com", "*.gravatar.com", "*.googleapis.com", "*.amazonaws.com", "*.smooch.io", "*.zendesk.com", "*.zdassets.com"], vd = [400, 403], kd = "CORS", xd = { recordCanvas: false, recordCrossOriginIframes: false, collectFonts: false, inlineImages: false, recordPassword: false, recordRealName: true, recordCreditCardInfo: false, recordSsn: false, recordDob: false, sampling: {} }, Cd = { level: ["info", "log", "warn", "error"], lengthThreshold: 1e4, stringifyOptions: { stringLengthLimit: 1e3, numOfKeysLimit: 20, depthOfLimit: 4 }, logger: "console" };
|
|
7713
7766
|
function trackDomainChangesOnce() {
|
|
7714
7767
|
const e = window.__sailfish_recorder || (window.__sailfish_recorder = {});
|
|
7715
7768
|
if (e.routeWatcherIntervalId) return;
|
|
@@ -7776,7 +7829,7 @@ function shouldSkipHeadersPropagation(e, a = []) {
|
|
|
7776
7829
|
return true;
|
|
7777
7830
|
}
|
|
7778
7831
|
for (const e2 of O) if (u.pathname.toLowerCase().endsWith(e2)) return true;
|
|
7779
|
-
return !!matchUrlWithWildcard(e, [...
|
|
7832
|
+
return !!matchUrlWithWildcard(e, [...Sd, ...a]);
|
|
7780
7833
|
}
|
|
7781
7834
|
function setupFetchInterceptor(e = []) {
|
|
7782
7835
|
const a = window.fetch, u = getOrSetSessionId();
|
|
@@ -7811,7 +7864,7 @@ function setupFetchInterceptor(e = []) {
|
|
|
7811
7864
|
D2[e3] = a4;
|
|
7812
7865
|
}) : D2 = { ...w3.headers }), F2 = w3.body;
|
|
7813
7866
|
} catch (e3) {
|
|
7814
|
-
|
|
7867
|
+
bd && console.warn("[Sailfish] Failed to capture request data:", e3);
|
|
7815
7868
|
}
|
|
7816
7869
|
delete D2[b];
|
|
7817
7870
|
const $2 = (_a2 = getFuncSpanHeader()) == null ? void 0 : _a2.name;
|
|
@@ -7821,16 +7874,16 @@ function setupFetchInterceptor(e = []) {
|
|
|
7821
7874
|
const I4 = getFuncSpanHeader();
|
|
7822
7875
|
if (u3 instanceof Request) {
|
|
7823
7876
|
const E3 = u3.clone(), _3 = new Headers(E3.headers);
|
|
7824
|
-
_3.set(b, `${w4}/${x4}/${C4}`), I4 && (_3.set(I4.name, I4.value),
|
|
7877
|
+
_3.set(b, `${w4}/${x4}/${C4}`), I4 && (_3.set(I4.name, I4.value), bd && console.log("[Sailfish] Added funcspan header to HTTP Request:", { url: u3.url, header: I4.name }));
|
|
7825
7878
|
const O3 = new Request(E3, { headers: _3 });
|
|
7826
7879
|
return await e3.call(a4, O3, m4);
|
|
7827
7880
|
}
|
|
7828
7881
|
{
|
|
7829
7882
|
const E3 = { ...m4 }, _3 = new Headers(m4.headers || {});
|
|
7830
|
-
return _3.set(b, `${w4}/${x4}/${C4}`), I4 && (_3.set(I4.name, I4.value),
|
|
7883
|
+
return _3.set(b, `${w4}/${x4}/${C4}`), I4 && (_3.set(I4.name, I4.value), bd && console.log("[Sailfish] Added funcspan header to HTTP fetch:", { url: "string" == typeof u3 ? u3 : u3.href, header: I4.name })), E3.headers = _3, await e3.call(a4, u3, E3);
|
|
7831
7884
|
}
|
|
7832
7885
|
})(e2, a3, m3, w3, x3, E2.page_visit_uuid, I3), B2 = false;
|
|
7833
|
-
|
|
7886
|
+
vd.includes($3.status) && (bd && console.log("Perform retry as status was fail:", $3), I3 = v4(), $3 = await (async function retryWithoutPropagateHeaders(e3, a4, u3, m4) {
|
|
7834
7887
|
try {
|
|
7835
7888
|
let m5 = u3[0], w4 = u3[1] || {};
|
|
7836
7889
|
if ("string" == typeof m5 || m5 instanceof URL) {
|
|
@@ -7846,7 +7899,7 @@ function setupFetchInterceptor(e = []) {
|
|
|
7846
7899
|
}
|
|
7847
7900
|
return e3.apply(a4, u3);
|
|
7848
7901
|
} catch (e4) {
|
|
7849
|
-
throw
|
|
7902
|
+
throw bd && console.log(`Retry without ${b} for ${m4} also failed:`, e4), e4;
|
|
7850
7903
|
}
|
|
7851
7904
|
})(e2, a3, u2, C3), B2 = true);
|
|
7852
7905
|
const U2 = Date.now(), j2 = $3.status, z2 = $3.ok, q2 = z2 ? "" : `Request Error: ${$3.statusText}`;
|
|
@@ -7855,7 +7908,7 @@ function setupFetchInterceptor(e = []) {
|
|
|
7855
7908
|
const e3 = $3.clone();
|
|
7856
7909
|
V2 = await e3.text();
|
|
7857
7910
|
} catch (e3) {
|
|
7858
|
-
|
|
7911
|
+
bd && console.warn("[Sailfish] Failed to capture response data:", e3), V2 = null;
|
|
7859
7912
|
}
|
|
7860
7913
|
let H2 = null;
|
|
7861
7914
|
try {
|
|
@@ -7863,12 +7916,12 @@ function setupFetchInterceptor(e = []) {
|
|
|
7863
7916
|
H2[a4] = e3;
|
|
7864
7917
|
});
|
|
7865
7918
|
} catch (e3) {
|
|
7866
|
-
|
|
7919
|
+
bd && console.warn("[Sailfish] Failed to capture response headers:", e3), H2 = null;
|
|
7867
7920
|
}
|
|
7868
7921
|
return sendEvent({ type: 27, timestamp: U2, sessionId: x3, data: { request_id: I3, session_id: x3, timestamp_start: O2, timestamp_end: U2, response_code: j2, success: z2, error: q2, method: _2, url: C3, retry_without_trace_id: B2, request_headers: D2, request_body: F2, response_headers: H2, response_body: V2 }, ...E2 }), $3;
|
|
7869
7922
|
} catch (m4) {
|
|
7870
7923
|
const w4 = Date.now(), b2 = false, $3 = ((_b = m4.response) == null ? void 0 : _b.status) || 500, B2 = m4.message || "Fetch request failed";
|
|
7871
|
-
if (m4 instanceof TypeError && ((_c2 = m4 == null ? void 0 : m4.message) == null ? void 0 : _c2.toLowerCase().includes(
|
|
7924
|
+
if (m4 instanceof TypeError && ((_c2 = m4 == null ? void 0 : m4.message) == null ? void 0 : _c2.toLowerCase().includes(kd.toLowerCase()))) return e2.apply(a3, u2);
|
|
7872
7925
|
throw sendEvent({ type: 27, timestamp: w4, sessionId: x3, data: { request_id: I3, session_id: x3, timestamp_start: O2, timestamp_end: w4, response_code: $3, success: b2, error: B2, method: _2, url: C3, request_headers: D2, request_body: F2, response_body: null }, ...E2 }), m4;
|
|
7873
7926
|
}
|
|
7874
7927
|
})(a2, m2, w2, C2, I2, u, x2);
|
|
@@ -7902,7 +7955,7 @@ async function startRecording({ apiKey: e, backendApi: a = "https://api-service.
|
|
|
7902
7955
|
})(), D2 = getOrSetSessionId(), $2 = window.__sailfish_recorder || (window.__sailfish_recorder = {});
|
|
7903
7956
|
if ($2.sessionId = D2, $2.apiKey = e, $2.backendApi = a, $2.serviceAdditionalMetadata = I2, $2.initialized && $2.sessionId === D2 && $2.ws && 1 === $2.ws.readyState) trackDomainChangesOnce();
|
|
7904
7957
|
else {
|
|
7905
|
-
$2.domEventsInit || (initializeDomContentEvents(D2), $2.domEventsInit = true), $2.consoleInit || (initializeConsolePlugin(
|
|
7958
|
+
$2.domEventsInit || (initializeDomContentEvents(D2), $2.domEventsInit = true), $2.consoleInit || (initializeConsolePlugin(Cd, D2), $2.consoleInit = true), $2.errorInit || (!(function initializeErrorInterceptor() {
|
|
7906
7959
|
window.addEventListener("error", (e2) => {
|
|
7907
7960
|
captureError(e2.error || e2.message);
|
|
7908
7961
|
}), window.addEventListener("unhandledrejection", (e2) => {
|
|
@@ -7912,10 +7965,10 @@ async function startRecording({ apiKey: e, backendApi: a = "https://api-service.
|
|
|
7912
7965
|
X && (sessionStorage.setItem("sailfishApiKey", e2), sessionStorage.setItem("sailfishBackendApi", a2));
|
|
7913
7966
|
})({ apiKey: e, backendApi: a }), trackDomainChangesOnce(), sessionStorage.setItem("sailfishApiKey", e), sessionStorage.setItem("sailfishBackendApi", a), !isFunctionSpanTrackingEnabled() || $2.ws && 1 === $2.ws.readyState || fetchFunctionSpanTrackingEnabled(e, a).then((e2) => {
|
|
7914
7967
|
var _a3;
|
|
7915
|
-
((_a3 = e2.data) == null ? void 0 : _a3.isFunctionSpanTrackingEnabledFromApiKey) ?? false ?
|
|
7968
|
+
((_a3 = e2.data) == null ? void 0 : _a3.isFunctionSpanTrackingEnabledFromApiKey) ?? false ? bd && console.log("[Sailfish] Function span tracking state validated with backend: ACTIVE") : (clearStaleFuncSpanState(), bd && console.log("[Sailfish] Cleared stale function span tracking state - backend validation shows tracking is not active"));
|
|
7916
7969
|
}).catch((e2) => {
|
|
7917
|
-
|
|
7918
|
-
}), $2.sentDoNotPropagateOnce || (sendDomainsToNotPropagateHeaderTo(e, [...m2, ...
|
|
7970
|
+
bd && console.warn("[Sailfish] Failed to validate function span tracking status with backend:", e2);
|
|
7971
|
+
}), $2.sentDoNotPropagateOnce || (sendDomainsToNotPropagateHeaderTo(e, [...m2, ...Sd], a).catch((e2) => console.error("Failed to send domains to not propagate header to:", e2)), $2.sentDoNotPropagateOnce = true), $2.xhrPatched || (!(function setupXMLHttpRequestInterceptor(e2 = []) {
|
|
7919
7972
|
const a2 = XMLHttpRequest.prototype.open, u2 = XMLHttpRequest.prototype.send, m3 = XMLHttpRequest.prototype.setRequestHeader, w3 = getOrSetSessionId();
|
|
7920
7973
|
XMLHttpRequest.prototype.setRequestHeader = function(e3, a3) {
|
|
7921
7974
|
return this._capturedRequestHeaders || (this._capturedRequestHeaders = {}), this._capturedRequestHeaders[e3] = a3, m3.call(this, e3, a3);
|
|
@@ -7933,9 +7986,9 @@ async function startRecording({ apiKey: e, backendApi: a = "https://api-service.
|
|
|
7933
7986
|
}
|
|
7934
7987
|
const E3 = getFuncSpanHeader();
|
|
7935
7988
|
if (E3) try {
|
|
7936
|
-
this.setRequestHeader(E3.name, E3.value),
|
|
7989
|
+
this.setRequestHeader(E3.name, E3.value), bd && console.log("[Sailfish] Added funcspan header to XMLHttpRequest:", { url: m4, header: E3.name });
|
|
7937
7990
|
} catch (e3) {
|
|
7938
|
-
|
|
7991
|
+
bd && console.warn(`[Sailfish] Could not set funcspan header for ${m4}`, e3);
|
|
7939
7992
|
}
|
|
7940
7993
|
const _3 = Date.now();
|
|
7941
7994
|
let O3 = false;
|
|
@@ -7963,7 +8016,7 @@ async function startRecording({ apiKey: e, backendApi: a = "https://api-service.
|
|
|
7963
8016
|
2 === a5.length && (u3[a5[0]] = a5[1]);
|
|
7964
8017
|
});
|
|
7965
8018
|
} catch (e4) {
|
|
7966
|
-
|
|
8019
|
+
bd && console.warn("[Sailfish] Failed to capture XHR response headers:", e4), u3 = null;
|
|
7967
8020
|
}
|
|
7968
8021
|
if (e3 >= 200 && e3 < 300) emitFinished(true, e3, "", a4, u3);
|
|
7969
8022
|
else {
|
|
@@ -7979,7 +8032,7 @@ async function startRecording({ apiKey: e, backendApi: a = "https://api-service.
|
|
|
7979
8032
|
sendMessage({ type: "deviceInfo", data: { deviceInfo: { language: navigator.language, userAgent: navigator.userAgent } } });
|
|
7980
8033
|
})();
|
|
7981
8034
|
try {
|
|
7982
|
-
const u2 = await fetchCaptureSettings(e, a), m3 = ((_a2 = u2.data) == null ? void 0 : _a2.captureSettingsFromApiKey) ||
|
|
8035
|
+
const u2 = await fetchCaptureSettings(e, a), m3 = ((_a2 = u2.data) == null ? void 0 : _a2.captureSettingsFromApiKey) || xd;
|
|
7983
8036
|
if ($2.ws && 1 === $2.ws.readyState) return;
|
|
7984
8037
|
const b2 = withAppUrlMetadata(I2), C3 = await startRecordingSession(e, D2, a, _2, O2, F2, E2, "JS/TS", b2);
|
|
7985
8038
|
if ((_b = C3.data) == null ? void 0 : _b.startRecordingSession) {
|
|
@@ -8015,7 +8068,7 @@ H && (!(function sendUserDeviceUuid() {
|
|
|
8015
8068
|
const e = document.visibilityState, a = Date.now();
|
|
8016
8069
|
"visible" === e && getOrSetSessionId();
|
|
8017
8070
|
try {
|
|
8018
|
-
sendMessage({ type: "visibilityChange", data: { state: e, url: window.location.href.split("?")[0], timestamp: a, ...getUrlAndStoredUuids() } }),
|
|
8071
|
+
sendMessage({ type: "visibilityChange", data: { state: e, url: window.location.href.split("?")[0], timestamp: a, ...getUrlAndStoredUuids() } }), bd && console.log(`[Sailfish] Tab became ${e}, sent visibility change event`);
|
|
8019
8072
|
} catch (e2) {
|
|
8020
8073
|
console.warn("[Sailfish] Failed to send visibility change event:", e2);
|
|
8021
8074
|
}
|
|
@@ -8023,12 +8076,12 @@ H && (!(function sendUserDeviceUuid() {
|
|
|
8023
8076
|
}), H && window.addEventListener("beforeunload", () => {
|
|
8024
8077
|
clearPageVisitDataFromSessionStorage();
|
|
8025
8078
|
});
|
|
8026
|
-
exports.DEFAULT_CAPTURE_SETTINGS =
|
|
8079
|
+
exports.DEFAULT_CAPTURE_SETTINGS = xd, exports.DEFAULT_CONSOLE_RECORDING_SETTINGS = Cd, exports.STORAGE_VERSION = 1, exports.addOrUpdateMetadata = function addOrUpdateMetadata(e) {
|
|
8027
8080
|
const a = { type: "addOrUpdateMetadata", metadata: e };
|
|
8028
|
-
|
|
8081
|
+
wd && JSON.stringify(wd) === JSON.stringify(e) || (wd = e, sendMessage(a));
|
|
8029
8082
|
}, exports.buildBatches = buildBatches, exports.clearStaleFuncSpanState = clearStaleFuncSpanState, exports.createTriageAndIssueFromRecorder = createTriageAndIssueFromRecorder, exports.createTriageFromRecorder = createTriageFromRecorder, exports.disableFunctionSpanTracking = disableFunctionSpanTracking, exports.enableFunctionSpanTracking = enableFunctionSpanTracking, exports.eventSize = eventSize, exports.fetchCaptureSettings = fetchCaptureSettings, exports.fetchEngineeringTicketPlatformIntegrations = fetchEngineeringTicketPlatformIntegrations, exports.fetchFunctionSpanTrackingEnabled = fetchFunctionSpanTrackingEnabled, exports.flushBufferedEvents = flushBufferedEvents, exports.getFuncSpanHeader = getFuncSpanHeader, exports.getOrSetSessionId = getOrSetSessionId, exports.getUrlAndStoredUuids = getUrlAndStoredUuids, exports.identify = function identify(e, a = {}, u = false) {
|
|
8030
8083
|
const m2 = { type: "identify", userId: e, traits: a };
|
|
8031
|
-
|
|
8084
|
+
yd && yd.userId === e && JSON.stringify(yd.traits) === JSON.stringify(a) || (yd = { userId: e, traits: a, overwrite: u }, sendMessage(m2));
|
|
8032
8085
|
}, exports.initRecorder = async (e) => {
|
|
8033
8086
|
if ("undefined" == typeof window) return;
|
|
8034
8087
|
const a = window.__sailfish_recorder || (window.__sailfish_recorder = {}), u = getOrSetSessionId();
|
package/dist/recorder.js
CHANGED
|
@@ -465,7 +465,7 @@ function initializeWebSocket(e, a, u, m2) {
|
|
|
465
465
|
const a2 = document.createElement("a");
|
|
466
466
|
return a2.href = e2, `${a2.hostname}${a2.port ? `:${a2.port}` : ""}`;
|
|
467
467
|
})(e);
|
|
468
|
-
let b2 = `${"https:" === new URL(e).protocol ? "wss" : "ws"}://${w2}/ws/notify/?apiKey=${a}&sessionId=${u}&sender=JS%2FTS&version=1.8.
|
|
468
|
+
let b2 = `${"https:" === new URL(e).protocol ? "wss" : "ws"}://${w2}/ws/notify/?apiKey=${a}&sessionId=${u}&sender=JS%2FTS&version=1.8.19`;
|
|
469
469
|
m2 && (b2 += `&envValue=${encodeURIComponent(m2)}`);
|
|
470
470
|
return se = new U(b2, [], { connectionTimeout: 3e4 }), se.addEventListener("open", () => {
|
|
471
471
|
re && (console.log("[Sailfish] WebSocket connection opened"), console.log("[Sailfish] Function span tracking state: " + (de ? "ENABLED" : "DISABLED"))), (async () => {
|
|
@@ -7625,6 +7625,58 @@ function R(e, a, u) {
|
|
|
7625
7625
|
}
|
|
7626
7626
|
}
|
|
7627
7627
|
var fd = ((e) => (e[e.DomContentLoaded = 0] = "DomContentLoaded", e[e.Load = 1] = "Load", e[e.FullSnapshot = 2] = "FullSnapshot", e[e.IncrementalSnapshot = 3] = "IncrementalSnapshot", e[e.Meta = 4] = "Meta", e[e.Custom = 5] = "Custom", e[e.Plugin = 6] = "Plugin", e[e.Device = 24] = "Device", e[e.SailfishCustom = 25] = "SailfishCustom", e))(fd || {});
|
|
7628
|
+
const md = ["/node_modules/", "/@sailfish-ai/", "/@sailfish-rrweb/", "/dist/", "/webpack/", "/vite/", "/__vite", "/react-dom/", "/react/", "/scheduler/", "/<", "/chrome-extension://", "/extensions/"];
|
|
7629
|
+
function shouldSkipFrame(e) {
|
|
7630
|
+
return md.some((a) => e.includes(a));
|
|
7631
|
+
}
|
|
7632
|
+
function normalizeFilePath(e) {
|
|
7633
|
+
let a = e;
|
|
7634
|
+
if (a.startsWith("file://") && (a = a.substring(7)), a.startsWith("webpack-internal:///") && (a = a.substring(20)), a.startsWith("webpack:///") && (a = a.substring(11)), a.startsWith("/@fs/") && (a = a.substring(5)), a.startsWith("http://") || a.startsWith("https://")) try {
|
|
7635
|
+
a = new URL(a).pathname || a, "/" === a && (a = "index.html");
|
|
7636
|
+
} catch {
|
|
7637
|
+
}
|
|
7638
|
+
return a;
|
|
7639
|
+
}
|
|
7640
|
+
function getCallerLocationFromTrace(e, a = 0) {
|
|
7641
|
+
if (!Array.isArray(e) || 0 === e.length) return [null, null];
|
|
7642
|
+
const u = (function parseRrwebTraceFrames(e2) {
|
|
7643
|
+
const a2 = [];
|
|
7644
|
+
for (const u2 of e2) {
|
|
7645
|
+
if (!u2) continue;
|
|
7646
|
+
const e3 = u2.startsWith("at ") ? u2.slice(3) : u2;
|
|
7647
|
+
let m2 = /^(.*?)\s+\((.+?):(\d+):(\d+)\)$/.exec(e3);
|
|
7648
|
+
m2 ? a2.push({ functionName: m2[1] || "<anonymous>", file: m2[2], line: parseInt(m2[3], 10), column: parseInt(m2[4], 10) }) : (m2 = /^(.+?):(\d+):(\d+)$/.exec(e3), m2 && a2.push({ functionName: "<anonymous>", file: m2[1], line: parseInt(m2[2], 10), column: parseInt(m2[3], 10) }));
|
|
7649
|
+
}
|
|
7650
|
+
return a2;
|
|
7651
|
+
})(e);
|
|
7652
|
+
for (let e2 = a; e2 < Math.min(u.length, a + 20); e2++) {
|
|
7653
|
+
const a2 = u[e2];
|
|
7654
|
+
if (!(a2 == null ? void 0 : a2.file)) continue;
|
|
7655
|
+
const m2 = normalizeFilePath(a2.file);
|
|
7656
|
+
if (!shouldSkipFrame(m2)) return [m2, a2.line];
|
|
7657
|
+
}
|
|
7658
|
+
return [null, null];
|
|
7659
|
+
}
|
|
7660
|
+
function getCallerLocation(e = 0) {
|
|
7661
|
+
const a = new Error().stack;
|
|
7662
|
+
if (!a) return [null, null];
|
|
7663
|
+
const u = (function parseV8Stack(e2) {
|
|
7664
|
+
if (!e2) return [];
|
|
7665
|
+
const a2 = e2.split("\n").slice(1), u2 = [];
|
|
7666
|
+
for (const e3 of a2) {
|
|
7667
|
+
let a3 = /at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/.exec(e3);
|
|
7668
|
+
a3 ? u2.push({ functionName: a3[1], file: a3[2], line: parseInt(a3[3], 10), column: parseInt(a3[4], 10) }) : (a3 = /at\s+(.+?):(\d+):(\d+)/.exec(e3), a3 && u2.push({ functionName: "<anonymous>", file: a3[1], line: parseInt(a3[2], 10), column: parseInt(a3[3], 10) }));
|
|
7669
|
+
}
|
|
7670
|
+
return u2;
|
|
7671
|
+
})(a), m2 = 1 + e;
|
|
7672
|
+
for (let e2 = m2; e2 < Math.min(u.length, m2 + 20); e2++) {
|
|
7673
|
+
const a2 = u[e2];
|
|
7674
|
+
if (!(a2 == null ? void 0 : a2.file)) continue;
|
|
7675
|
+
const m3 = normalizeFilePath(a2.file);
|
|
7676
|
+
if (!shouldSkipFrame(m3)) return [m3, a2.line];
|
|
7677
|
+
}
|
|
7678
|
+
return [null, null];
|
|
7679
|
+
}
|
|
7628
7680
|
function suppressConsoleLogsDuringCall(e) {
|
|
7629
7681
|
const a = console.log, u = console.warn, m2 = console.error;
|
|
7630
7682
|
console.log = () => {
|
|
@@ -7637,7 +7689,7 @@ function suppressConsoleLogsDuringCall(e) {
|
|
|
7637
7689
|
console.log = a, console.warn = u, console.error = m2;
|
|
7638
7690
|
}
|
|
7639
7691
|
}
|
|
7640
|
-
const
|
|
7692
|
+
const gd = "zendesk_chat", yd = "Zendesk";
|
|
7641
7693
|
function zE_safe(...e) {
|
|
7642
7694
|
try {
|
|
7643
7695
|
if ((function hasZendesk() {
|
|
@@ -7676,7 +7728,8 @@ function initializeDomContentEvents(e) {
|
|
|
7676
7728
|
function initializeConsolePlugin(e, a) {
|
|
7677
7729
|
const { name: u, observer: m2 } = /* @__PURE__ */ ((e2) => ({ name: "@sailfish-rrweb/rrweb/console@1", observer: R, options: e2 }))(e);
|
|
7678
7730
|
m2((e2) => {
|
|
7679
|
-
|
|
7731
|
+
const m3 = e2, [w2, b2] = getCallerLocationFromTrace(m3 == null ? void 0 : m3.trace, 0), [C2, x2] = getCallerLocation(2), I2 = w2 ?? C2, E2 = b2 ?? x2, _2 = { ...m3, sourceFile: I2, sourceLine: E2 };
|
|
7732
|
+
sendEvent({ type: fd.Plugin, timestamp: Date.now(), data: { plugin: u, payload: _2 }, sessionId: a, ...getUrlAndStoredUuids() });
|
|
7680
7733
|
}, window, e);
|
|
7681
7734
|
}
|
|
7682
7735
|
async function initializeRecording(e, a, u, m2, w2) {
|
|
@@ -7696,11 +7749,11 @@ async function initializeRecording(e, a, u, m2, w2) {
|
|
|
7696
7749
|
zE_safe("messenger:set", "conversationTags", [`sailfish-session-${m2}`]);
|
|
7697
7750
|
});
|
|
7698
7751
|
const handleWidgetOpen = () => {
|
|
7699
|
-
ae.addSailfishEvent(fd.SailfishCustom, { action: "customer support chat opened", element_id:
|
|
7752
|
+
ae.addSailfishEvent(fd.SailfishCustom, { action: "customer support chat opened", element_id: gd, provider: yd });
|
|
7700
7753
|
}, handleWidgetClose = () => {
|
|
7701
|
-
ae.addSailfishEvent(fd.SailfishCustom, { action: "customer support chat closed", element_id:
|
|
7754
|
+
ae.addSailfishEvent(fd.SailfishCustom, { action: "customer support chat closed", element_id: gd, provider: yd });
|
|
7702
7755
|
}, handleUnreadMessages = (e2) => {
|
|
7703
|
-
ae.addSailfishEvent(fd.SailfishCustom, { action: "zendesk unreadmessages", element_id:
|
|
7756
|
+
ae.addSailfishEvent(fd.SailfishCustom, { action: "zendesk unreadmessages", element_id: gd, provider: yd });
|
|
7704
7757
|
};
|
|
7705
7758
|
suppressConsoleLogsDuringCall(() => {
|
|
7706
7759
|
zE_safe("messenger:on", "open", handleWidgetOpen), zE_safe("messenger:on", "close", handleWidgetClose), zE_safe("messenger:on", "unreadMessages", handleUnreadMessages);
|
|
@@ -7711,19 +7764,19 @@ async function initializeRecording(e, a, u, m2, w2) {
|
|
|
7711
7764
|
}
|
|
7712
7765
|
return b2;
|
|
7713
7766
|
}
|
|
7714
|
-
let
|
|
7767
|
+
let wd = null, bd = null;
|
|
7715
7768
|
function identify(e, a = {}, u = false) {
|
|
7716
7769
|
const m2 = { type: "identify", userId: e, traits: a };
|
|
7717
|
-
|
|
7770
|
+
wd && wd.userId === e && JSON.stringify(wd.traits) === JSON.stringify(a) || (wd = { userId: e, traits: a, overwrite: u }, sendMessage(m2));
|
|
7718
7771
|
}
|
|
7719
7772
|
function addOrUpdateMetadata(e) {
|
|
7720
7773
|
const a = { type: "addOrUpdateMetadata", metadata: e };
|
|
7721
|
-
|
|
7774
|
+
bd && JSON.stringify(bd) === JSON.stringify(e) || (bd = e, sendMessage(a));
|
|
7722
7775
|
}
|
|
7723
7776
|
function trackingEvent(e) {
|
|
7724
7777
|
sendMessage({ type: "trackingEvent", trackingData: e, timestamp: ne() });
|
|
7725
7778
|
}
|
|
7726
|
-
const
|
|
7779
|
+
const Sd = readDebugFlag(), vd = ["t.co", "*.twitter.com", "*.gravatar.com", "*.googleapis.com", "*.amazonaws.com", "*.smooch.io", "*.zendesk.com", "*.zdassets.com"], kd = [400, 403], Cd = "CORS", xd = 1, Id = { recordCanvas: false, recordCrossOriginIframes: false, collectFonts: false, inlineImages: false, recordPassword: false, recordRealName: true, recordCreditCardInfo: false, recordSsn: false, recordDob: false, sampling: {} }, Md = { level: ["info", "log", "warn", "error"], lengthThreshold: 1e4, stringifyOptions: { stringLengthLimit: 1e3, numOfKeysLimit: 20, depthOfLimit: 4 }, logger: "console" };
|
|
7727
7780
|
function trackDomainChangesOnce() {
|
|
7728
7781
|
const e = window.__sailfish_recorder || (window.__sailfish_recorder = {});
|
|
7729
7782
|
if (e.routeWatcherIntervalId) return;
|
|
@@ -7790,7 +7843,7 @@ function shouldSkipHeadersPropagation(e, a = []) {
|
|
|
7790
7843
|
return true;
|
|
7791
7844
|
}
|
|
7792
7845
|
for (const e2 of O) if (u.pathname.toLowerCase().endsWith(e2)) return true;
|
|
7793
|
-
return !!matchUrlWithWildcard(e, [...
|
|
7846
|
+
return !!matchUrlWithWildcard(e, [...vd, ...a]);
|
|
7794
7847
|
}
|
|
7795
7848
|
function setupFetchInterceptor(e = []) {
|
|
7796
7849
|
const a = window.fetch, u = getOrSetSessionId();
|
|
@@ -7825,7 +7878,7 @@ function setupFetchInterceptor(e = []) {
|
|
|
7825
7878
|
D2[e3] = a4;
|
|
7826
7879
|
}) : D2 = { ...w3.headers }), F2 = w3.body;
|
|
7827
7880
|
} catch (e3) {
|
|
7828
|
-
|
|
7881
|
+
Sd && console.warn("[Sailfish] Failed to capture request data:", e3);
|
|
7829
7882
|
}
|
|
7830
7883
|
delete D2[b];
|
|
7831
7884
|
const $2 = (_a2 = getFuncSpanHeader()) == null ? void 0 : _a2.name;
|
|
@@ -7835,16 +7888,16 @@ function setupFetchInterceptor(e = []) {
|
|
|
7835
7888
|
const I4 = getFuncSpanHeader();
|
|
7836
7889
|
if (u3 instanceof Request) {
|
|
7837
7890
|
const E3 = u3.clone(), _3 = new Headers(E3.headers);
|
|
7838
|
-
_3.set(b, `${w4}/${C4}/${x4}`), I4 && (_3.set(I4.name, I4.value),
|
|
7891
|
+
_3.set(b, `${w4}/${C4}/${x4}`), I4 && (_3.set(I4.name, I4.value), Sd && console.log("[Sailfish] Added funcspan header to HTTP Request:", { url: u3.url, header: I4.name }));
|
|
7839
7892
|
const O3 = new Request(E3, { headers: _3 });
|
|
7840
7893
|
return await e3.call(a4, O3, m4);
|
|
7841
7894
|
}
|
|
7842
7895
|
{
|
|
7843
7896
|
const E3 = { ...m4 }, _3 = new Headers(m4.headers || {});
|
|
7844
|
-
return _3.set(b, `${w4}/${C4}/${x4}`), I4 && (_3.set(I4.name, I4.value),
|
|
7897
|
+
return _3.set(b, `${w4}/${C4}/${x4}`), I4 && (_3.set(I4.name, I4.value), Sd && console.log("[Sailfish] Added funcspan header to HTTP fetch:", { url: "string" == typeof u3 ? u3 : u3.href, header: I4.name })), E3.headers = _3, await e3.call(a4, u3, E3);
|
|
7845
7898
|
}
|
|
7846
7899
|
})(e2, a3, m3, w3, C3, E2.page_visit_uuid, I3), B2 = false;
|
|
7847
|
-
|
|
7900
|
+
kd.includes($3.status) && (Sd && console.log("Perform retry as status was fail:", $3), I3 = v4(), $3 = await (async function retryWithoutPropagateHeaders(e3, a4, u3, m4) {
|
|
7848
7901
|
try {
|
|
7849
7902
|
let m5 = u3[0], w4 = u3[1] || {};
|
|
7850
7903
|
if ("string" == typeof m5 || m5 instanceof URL) {
|
|
@@ -7860,7 +7913,7 @@ function setupFetchInterceptor(e = []) {
|
|
|
7860
7913
|
}
|
|
7861
7914
|
return e3.apply(a4, u3);
|
|
7862
7915
|
} catch (e4) {
|
|
7863
|
-
throw
|
|
7916
|
+
throw Sd && console.log(`Retry without ${b} for ${m4} also failed:`, e4), e4;
|
|
7864
7917
|
}
|
|
7865
7918
|
})(e2, a3, u2, x3), B2 = true);
|
|
7866
7919
|
const U2 = Date.now(), j2 = $3.status, z2 = $3.ok, q2 = z2 ? "" : `Request Error: ${$3.statusText}`;
|
|
@@ -7869,7 +7922,7 @@ function setupFetchInterceptor(e = []) {
|
|
|
7869
7922
|
const e3 = $3.clone();
|
|
7870
7923
|
V2 = await e3.text();
|
|
7871
7924
|
} catch (e3) {
|
|
7872
|
-
|
|
7925
|
+
Sd && console.warn("[Sailfish] Failed to capture response data:", e3), V2 = null;
|
|
7873
7926
|
}
|
|
7874
7927
|
let H2 = null;
|
|
7875
7928
|
try {
|
|
@@ -7877,12 +7930,12 @@ function setupFetchInterceptor(e = []) {
|
|
|
7877
7930
|
H2[a4] = e3;
|
|
7878
7931
|
});
|
|
7879
7932
|
} catch (e3) {
|
|
7880
|
-
|
|
7933
|
+
Sd && console.warn("[Sailfish] Failed to capture response headers:", e3), H2 = null;
|
|
7881
7934
|
}
|
|
7882
7935
|
return sendEvent({ type: 27, timestamp: U2, sessionId: C3, data: { request_id: I3, session_id: C3, timestamp_start: O2, timestamp_end: U2, response_code: j2, success: z2, error: q2, method: _2, url: x3, retry_without_trace_id: B2, request_headers: D2, request_body: F2, response_headers: H2, response_body: V2 }, ...E2 }), $3;
|
|
7883
7936
|
} catch (m4) {
|
|
7884
7937
|
const w4 = Date.now(), b2 = false, $3 = ((_b = m4.response) == null ? void 0 : _b.status) || 500, B2 = m4.message || "Fetch request failed";
|
|
7885
|
-
if (m4 instanceof TypeError && ((_c2 = m4 == null ? void 0 : m4.message) == null ? void 0 : _c2.toLowerCase().includes(
|
|
7938
|
+
if (m4 instanceof TypeError && ((_c2 = m4 == null ? void 0 : m4.message) == null ? void 0 : _c2.toLowerCase().includes(Cd.toLowerCase()))) return e2.apply(a3, u2);
|
|
7886
7939
|
throw sendEvent({ type: 27, timestamp: w4, sessionId: C3, data: { request_id: I3, session_id: C3, timestamp_start: O2, timestamp_end: w4, response_code: $3, success: b2, error: B2, method: _2, url: x3, request_headers: D2, request_body: F2, response_body: null }, ...E2 }), m4;
|
|
7887
7940
|
}
|
|
7888
7941
|
})(a2, m2, w2, x2, I2, u, C2);
|
|
@@ -7916,7 +7969,7 @@ async function startRecording({ apiKey: e, backendApi: a = "https://api-service.
|
|
|
7916
7969
|
})(), D2 = getOrSetSessionId(), $2 = window.__sailfish_recorder || (window.__sailfish_recorder = {});
|
|
7917
7970
|
if ($2.sessionId = D2, $2.apiKey = e, $2.backendApi = a, $2.serviceAdditionalMetadata = I2, $2.initialized && $2.sessionId === D2 && $2.ws && 1 === $2.ws.readyState) trackDomainChangesOnce();
|
|
7918
7971
|
else {
|
|
7919
|
-
$2.domEventsInit || (initializeDomContentEvents(D2), $2.domEventsInit = true), $2.consoleInit || (initializeConsolePlugin(
|
|
7972
|
+
$2.domEventsInit || (initializeDomContentEvents(D2), $2.domEventsInit = true), $2.consoleInit || (initializeConsolePlugin(Md, D2), $2.consoleInit = true), $2.errorInit || (!(function initializeErrorInterceptor() {
|
|
7920
7973
|
window.addEventListener("error", (e2) => {
|
|
7921
7974
|
captureError(e2.error || e2.message);
|
|
7922
7975
|
}), window.addEventListener("unhandledrejection", (e2) => {
|
|
@@ -7926,10 +7979,10 @@ async function startRecording({ apiKey: e, backendApi: a = "https://api-service.
|
|
|
7926
7979
|
X && (sessionStorage.setItem("sailfishApiKey", e2), sessionStorage.setItem("sailfishBackendApi", a2));
|
|
7927
7980
|
})({ apiKey: e, backendApi: a }), trackDomainChangesOnce(), sessionStorage.setItem("sailfishApiKey", e), sessionStorage.setItem("sailfishBackendApi", a), !isFunctionSpanTrackingEnabled() || $2.ws && 1 === $2.ws.readyState || fetchFunctionSpanTrackingEnabled(e, a).then((e2) => {
|
|
7928
7981
|
var _a3;
|
|
7929
|
-
((_a3 = e2.data) == null ? void 0 : _a3.isFunctionSpanTrackingEnabledFromApiKey) ?? false ?
|
|
7982
|
+
((_a3 = e2.data) == null ? void 0 : _a3.isFunctionSpanTrackingEnabledFromApiKey) ?? false ? Sd && console.log("[Sailfish] Function span tracking state validated with backend: ACTIVE") : (clearStaleFuncSpanState(), Sd && console.log("[Sailfish] Cleared stale function span tracking state - backend validation shows tracking is not active"));
|
|
7930
7983
|
}).catch((e2) => {
|
|
7931
|
-
|
|
7932
|
-
}), $2.sentDoNotPropagateOnce || (sendDomainsToNotPropagateHeaderTo(e, [...m2, ...
|
|
7984
|
+
Sd && console.warn("[Sailfish] Failed to validate function span tracking status with backend:", e2);
|
|
7985
|
+
}), $2.sentDoNotPropagateOnce || (sendDomainsToNotPropagateHeaderTo(e, [...m2, ...vd], a).catch((e2) => console.error("Failed to send domains to not propagate header to:", e2)), $2.sentDoNotPropagateOnce = true), $2.xhrPatched || (!(function setupXMLHttpRequestInterceptor(e2 = []) {
|
|
7933
7986
|
const a2 = XMLHttpRequest.prototype.open, u2 = XMLHttpRequest.prototype.send, m3 = XMLHttpRequest.prototype.setRequestHeader, w3 = getOrSetSessionId();
|
|
7934
7987
|
XMLHttpRequest.prototype.setRequestHeader = function(e3, a3) {
|
|
7935
7988
|
return this._capturedRequestHeaders || (this._capturedRequestHeaders = {}), this._capturedRequestHeaders[e3] = a3, m3.call(this, e3, a3);
|
|
@@ -7947,9 +8000,9 @@ async function startRecording({ apiKey: e, backendApi: a = "https://api-service.
|
|
|
7947
8000
|
}
|
|
7948
8001
|
const E3 = getFuncSpanHeader();
|
|
7949
8002
|
if (E3) try {
|
|
7950
|
-
this.setRequestHeader(E3.name, E3.value),
|
|
8003
|
+
this.setRequestHeader(E3.name, E3.value), Sd && console.log("[Sailfish] Added funcspan header to XMLHttpRequest:", { url: m4, header: E3.name });
|
|
7951
8004
|
} catch (e3) {
|
|
7952
|
-
|
|
8005
|
+
Sd && console.warn(`[Sailfish] Could not set funcspan header for ${m4}`, e3);
|
|
7953
8006
|
}
|
|
7954
8007
|
const _3 = Date.now();
|
|
7955
8008
|
let O3 = false;
|
|
@@ -7977,7 +8030,7 @@ async function startRecording({ apiKey: e, backendApi: a = "https://api-service.
|
|
|
7977
8030
|
2 === a5.length && (u3[a5[0]] = a5[1]);
|
|
7978
8031
|
});
|
|
7979
8032
|
} catch (e4) {
|
|
7980
|
-
|
|
8033
|
+
Sd && console.warn("[Sailfish] Failed to capture XHR response headers:", e4), u3 = null;
|
|
7981
8034
|
}
|
|
7982
8035
|
if (e3 >= 200 && e3 < 300) emitFinished(true, e3, "", a4, u3);
|
|
7983
8036
|
else {
|
|
@@ -7993,7 +8046,7 @@ async function startRecording({ apiKey: e, backendApi: a = "https://api-service.
|
|
|
7993
8046
|
sendMessage({ type: "deviceInfo", data: { deviceInfo: { language: navigator.language, userAgent: navigator.userAgent } } });
|
|
7994
8047
|
})();
|
|
7995
8048
|
try {
|
|
7996
|
-
const u2 = await fetchCaptureSettings(e, a), m3 = ((_a2 = u2.data) == null ? void 0 : _a2.captureSettingsFromApiKey) ||
|
|
8049
|
+
const u2 = await fetchCaptureSettings(e, a), m3 = ((_a2 = u2.data) == null ? void 0 : _a2.captureSettingsFromApiKey) || Id;
|
|
7997
8050
|
if ($2.ws && 1 === $2.ws.readyState) return;
|
|
7998
8051
|
const b2 = withAppUrlMetadata(I2), x3 = await startRecordingSession(e, D2, a, _2, O2, F2, E2, "JS/TS", b2);
|
|
7999
8052
|
if ((_b = x3.data) == null ? void 0 : _b.startRecordingSession) {
|
|
@@ -8029,7 +8082,7 @@ H && (!(function sendUserDeviceUuid() {
|
|
|
8029
8082
|
const e = document.visibilityState, a = Date.now();
|
|
8030
8083
|
"visible" === e && getOrSetSessionId();
|
|
8031
8084
|
try {
|
|
8032
|
-
sendMessage({ type: "visibilityChange", data: { state: e, url: window.location.href.split("?")[0], timestamp: a, ...getUrlAndStoredUuids() } }),
|
|
8085
|
+
sendMessage({ type: "visibilityChange", data: { state: e, url: window.location.href.split("?")[0], timestamp: a, ...getUrlAndStoredUuids() } }), Sd && console.log(`[Sailfish] Tab became ${e}, sent visibility change event`);
|
|
8033
8086
|
} catch (e2) {
|
|
8034
8087
|
console.warn("[Sailfish] Failed to send visibility change event:", e2);
|
|
8035
8088
|
}
|
|
@@ -8056,9 +8109,9 @@ const initRecorder = async (e) => {
|
|
|
8056
8109
|
})), a.initPromise);
|
|
8057
8110
|
};
|
|
8058
8111
|
export {
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8112
|
+
Id as DEFAULT_CAPTURE_SETTINGS,
|
|
8113
|
+
Md as DEFAULT_CONSOLE_RECORDING_SETTINGS,
|
|
8114
|
+
xd as STORAGE_VERSION,
|
|
8062
8115
|
addOrUpdateMetadata,
|
|
8063
8116
|
buildBatches,
|
|
8064
8117
|
clearStaleFuncSpanState,
|
package/dist/recorder.js.br
CHANGED
|
Binary file
|
package/dist/recorder.js.gz
CHANGED
|
Binary file
|
package/dist/recorder.umd.cjs
CHANGED
|
@@ -467,7 +467,7 @@
|
|
|
467
467
|
const a3 = document.createElement("a");
|
|
468
468
|
return a3.href = e3, `${a3.hostname}${a3.port ? `:${a3.port}` : ""}`;
|
|
469
469
|
})(e2);
|
|
470
|
-
let b2 = `${"https:" === new URL(e2).protocol ? "wss" : "ws"}://${w2}/ws/notify/?apiKey=${a2}&sessionId=${u2}&sender=JS%2FTS&version=1.8.
|
|
470
|
+
let b2 = `${"https:" === new URL(e2).protocol ? "wss" : "ws"}://${w2}/ws/notify/?apiKey=${a2}&sessionId=${u2}&sender=JS%2FTS&version=1.8.19`;
|
|
471
471
|
m2 && (b2 += `&envValue=${encodeURIComponent(m2)}`);
|
|
472
472
|
return se = new j(b2, [], { connectionTimeout: 3e4 }), se.addEventListener("open", () => {
|
|
473
473
|
re && (console.log("[Sailfish] WebSocket connection opened"), console.log("[Sailfish] Function span tracking state: " + (de ? "ENABLED" : "DISABLED"))), (async () => {
|
|
@@ -7624,6 +7624,58 @@
|
|
|
7624
7624
|
}
|
|
7625
7625
|
}
|
|
7626
7626
|
var fd = ((e2) => (e2[e2.DomContentLoaded = 0] = "DomContentLoaded", e2[e2.Load = 1] = "Load", e2[e2.FullSnapshot = 2] = "FullSnapshot", e2[e2.IncrementalSnapshot = 3] = "IncrementalSnapshot", e2[e2.Meta = 4] = "Meta", e2[e2.Custom = 5] = "Custom", e2[e2.Plugin = 6] = "Plugin", e2[e2.Device = 24] = "Device", e2[e2.SailfishCustom = 25] = "SailfishCustom", e2))(fd || {});
|
|
7627
|
+
const md = ["/node_modules/", "/@sailfish-ai/", "/@sailfish-rrweb/", "/dist/", "/webpack/", "/vite/", "/__vite", "/react-dom/", "/react/", "/scheduler/", "/<", "/chrome-extension://", "/extensions/"];
|
|
7628
|
+
function shouldSkipFrame(e2) {
|
|
7629
|
+
return md.some((a2) => e2.includes(a2));
|
|
7630
|
+
}
|
|
7631
|
+
function normalizeFilePath(e2) {
|
|
7632
|
+
let a2 = e2;
|
|
7633
|
+
if (a2.startsWith("file://") && (a2 = a2.substring(7)), a2.startsWith("webpack-internal:///") && (a2 = a2.substring(20)), a2.startsWith("webpack:///") && (a2 = a2.substring(11)), a2.startsWith("/@fs/") && (a2 = a2.substring(5)), a2.startsWith("http://") || a2.startsWith("https://")) try {
|
|
7634
|
+
a2 = new URL(a2).pathname || a2, "/" === a2 && (a2 = "index.html");
|
|
7635
|
+
} catch {
|
|
7636
|
+
}
|
|
7637
|
+
return a2;
|
|
7638
|
+
}
|
|
7639
|
+
function getCallerLocationFromTrace(e2, a2 = 0) {
|
|
7640
|
+
if (!Array.isArray(e2) || 0 === e2.length) return [null, null];
|
|
7641
|
+
const u2 = (function parseRrwebTraceFrames(e3) {
|
|
7642
|
+
const a3 = [];
|
|
7643
|
+
for (const u3 of e3) {
|
|
7644
|
+
if (!u3) continue;
|
|
7645
|
+
const e4 = u3.startsWith("at ") ? u3.slice(3) : u3;
|
|
7646
|
+
let m2 = /^(.*?)\s+\((.+?):(\d+):(\d+)\)$/.exec(e4);
|
|
7647
|
+
m2 ? a3.push({ functionName: m2[1] || "<anonymous>", file: m2[2], line: parseInt(m2[3], 10), column: parseInt(m2[4], 10) }) : (m2 = /^(.+?):(\d+):(\d+)$/.exec(e4), m2 && a3.push({ functionName: "<anonymous>", file: m2[1], line: parseInt(m2[2], 10), column: parseInt(m2[3], 10) }));
|
|
7648
|
+
}
|
|
7649
|
+
return a3;
|
|
7650
|
+
})(e2);
|
|
7651
|
+
for (let e3 = a2; e3 < Math.min(u2.length, a2 + 20); e3++) {
|
|
7652
|
+
const a3 = u2[e3];
|
|
7653
|
+
if (!(a3 == null ? void 0 : a3.file)) continue;
|
|
7654
|
+
const m2 = normalizeFilePath(a3.file);
|
|
7655
|
+
if (!shouldSkipFrame(m2)) return [m2, a3.line];
|
|
7656
|
+
}
|
|
7657
|
+
return [null, null];
|
|
7658
|
+
}
|
|
7659
|
+
function getCallerLocation(e2 = 0) {
|
|
7660
|
+
const a2 = new Error().stack;
|
|
7661
|
+
if (!a2) return [null, null];
|
|
7662
|
+
const u2 = (function parseV8Stack(e3) {
|
|
7663
|
+
if (!e3) return [];
|
|
7664
|
+
const a3 = e3.split("\n").slice(1), u3 = [];
|
|
7665
|
+
for (const e4 of a3) {
|
|
7666
|
+
let a4 = /at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/.exec(e4);
|
|
7667
|
+
a4 ? u3.push({ functionName: a4[1], file: a4[2], line: parseInt(a4[3], 10), column: parseInt(a4[4], 10) }) : (a4 = /at\s+(.+?):(\d+):(\d+)/.exec(e4), a4 && u3.push({ functionName: "<anonymous>", file: a4[1], line: parseInt(a4[2], 10), column: parseInt(a4[3], 10) }));
|
|
7668
|
+
}
|
|
7669
|
+
return u3;
|
|
7670
|
+
})(a2), m2 = 1 + e2;
|
|
7671
|
+
for (let e3 = m2; e3 < Math.min(u2.length, m2 + 20); e3++) {
|
|
7672
|
+
const a3 = u2[e3];
|
|
7673
|
+
if (!(a3 == null ? void 0 : a3.file)) continue;
|
|
7674
|
+
const m3 = normalizeFilePath(a3.file);
|
|
7675
|
+
if (!shouldSkipFrame(m3)) return [m3, a3.line];
|
|
7676
|
+
}
|
|
7677
|
+
return [null, null];
|
|
7678
|
+
}
|
|
7627
7679
|
function suppressConsoleLogsDuringCall(e2) {
|
|
7628
7680
|
const a2 = console.log, u2 = console.warn, m2 = console.error;
|
|
7629
7681
|
console.log = () => {
|
|
@@ -7636,7 +7688,7 @@
|
|
|
7636
7688
|
console.log = a2, console.warn = u2, console.error = m2;
|
|
7637
7689
|
}
|
|
7638
7690
|
}
|
|
7639
|
-
const
|
|
7691
|
+
const gd = "zendesk_chat", yd = "Zendesk";
|
|
7640
7692
|
function zE_safe(...e2) {
|
|
7641
7693
|
try {
|
|
7642
7694
|
if ((function hasZendesk() {
|
|
@@ -7675,7 +7727,8 @@
|
|
|
7675
7727
|
function initializeConsolePlugin(e2, a2) {
|
|
7676
7728
|
const { name: u2, observer: m2 } = /* @__PURE__ */ ((e3) => ({ name: "@sailfish-rrweb/rrweb/console@1", observer: R, options: e3 }))(e2);
|
|
7677
7729
|
m2((e3) => {
|
|
7678
|
-
|
|
7730
|
+
const m3 = e3, [w2, b2] = getCallerLocationFromTrace(m3 == null ? void 0 : m3.trace, 0), [C2, x2] = getCallerLocation(2), I2 = w2 ?? C2, E2 = b2 ?? x2, _2 = { ...m3, sourceFile: I2, sourceLine: E2 };
|
|
7731
|
+
sendEvent({ type: fd.Plugin, timestamp: Date.now(), data: { plugin: u2, payload: _2 }, sessionId: a2, ...getUrlAndStoredUuids() });
|
|
7679
7732
|
}, window, e2);
|
|
7680
7733
|
}
|
|
7681
7734
|
async function initializeRecording(e2, a2, u2, m2, w2) {
|
|
@@ -7695,11 +7748,11 @@
|
|
|
7695
7748
|
zE_safe("messenger:set", "conversationTags", [`sailfish-session-${m2}`]);
|
|
7696
7749
|
});
|
|
7697
7750
|
const handleWidgetOpen = () => {
|
|
7698
|
-
ae.addSailfishEvent(fd.SailfishCustom, { action: "customer support chat opened", element_id:
|
|
7751
|
+
ae.addSailfishEvent(fd.SailfishCustom, { action: "customer support chat opened", element_id: gd, provider: yd });
|
|
7699
7752
|
}, handleWidgetClose = () => {
|
|
7700
|
-
ae.addSailfishEvent(fd.SailfishCustom, { action: "customer support chat closed", element_id:
|
|
7753
|
+
ae.addSailfishEvent(fd.SailfishCustom, { action: "customer support chat closed", element_id: gd, provider: yd });
|
|
7701
7754
|
}, handleUnreadMessages = (e3) => {
|
|
7702
|
-
ae.addSailfishEvent(fd.SailfishCustom, { action: "zendesk unreadmessages", element_id:
|
|
7755
|
+
ae.addSailfishEvent(fd.SailfishCustom, { action: "zendesk unreadmessages", element_id: gd, provider: yd });
|
|
7703
7756
|
};
|
|
7704
7757
|
suppressConsoleLogsDuringCall(() => {
|
|
7705
7758
|
zE_safe("messenger:on", "open", handleWidgetOpen), zE_safe("messenger:on", "close", handleWidgetClose), zE_safe("messenger:on", "unreadMessages", handleUnreadMessages);
|
|
@@ -7710,8 +7763,8 @@
|
|
|
7710
7763
|
}
|
|
7711
7764
|
return b2;
|
|
7712
7765
|
}
|
|
7713
|
-
let
|
|
7714
|
-
const
|
|
7766
|
+
let wd = null, bd = null;
|
|
7767
|
+
const Sd = readDebugFlag(), vd = ["t.co", "*.twitter.com", "*.gravatar.com", "*.googleapis.com", "*.amazonaws.com", "*.smooch.io", "*.zendesk.com", "*.zdassets.com"], kd = [400, 403], Cd = "CORS", xd = { recordCanvas: false, recordCrossOriginIframes: false, collectFonts: false, inlineImages: false, recordPassword: false, recordRealName: true, recordCreditCardInfo: false, recordSsn: false, recordDob: false, sampling: {} }, Id = { level: ["info", "log", "warn", "error"], lengthThreshold: 1e4, stringifyOptions: { stringLengthLimit: 1e3, numOfKeysLimit: 20, depthOfLimit: 4 }, logger: "console" };
|
|
7715
7768
|
function trackDomainChangesOnce() {
|
|
7716
7769
|
const e2 = window.__sailfish_recorder || (window.__sailfish_recorder = {});
|
|
7717
7770
|
if (e2.routeWatcherIntervalId) return;
|
|
@@ -7778,7 +7831,7 @@
|
|
|
7778
7831
|
return true;
|
|
7779
7832
|
}
|
|
7780
7833
|
for (const e3 of F) if (u2.pathname.toLowerCase().endsWith(e3)) return true;
|
|
7781
|
-
return !!matchUrlWithWildcard(e2, [...
|
|
7834
|
+
return !!matchUrlWithWildcard(e2, [...vd, ...a2]);
|
|
7782
7835
|
}
|
|
7783
7836
|
function setupFetchInterceptor(e2 = []) {
|
|
7784
7837
|
const a2 = window.fetch, u2 = getOrSetSessionId();
|
|
@@ -7813,7 +7866,7 @@
|
|
|
7813
7866
|
D2[e4] = a5;
|
|
7814
7867
|
}) : D2 = { ...w3.headers }), F2 = w3.body;
|
|
7815
7868
|
} catch (e4) {
|
|
7816
|
-
|
|
7869
|
+
Sd && console.warn("[Sailfish] Failed to capture request data:", e4);
|
|
7817
7870
|
}
|
|
7818
7871
|
delete D2[C];
|
|
7819
7872
|
const $2 = (_a2 = getFuncSpanHeader()) == null ? void 0 : _a2.name;
|
|
@@ -7823,16 +7876,16 @@
|
|
|
7823
7876
|
const I4 = getFuncSpanHeader();
|
|
7824
7877
|
if (u4 instanceof Request) {
|
|
7825
7878
|
const E3 = u4.clone(), _3 = new Headers(E3.headers);
|
|
7826
|
-
_3.set(C, `${w4}/${b4}/${x4}`), I4 && (_3.set(I4.name, I4.value),
|
|
7879
|
+
_3.set(C, `${w4}/${b4}/${x4}`), I4 && (_3.set(I4.name, I4.value), Sd && console.log("[Sailfish] Added funcspan header to HTTP Request:", { url: u4.url, header: I4.name }));
|
|
7827
7880
|
const O3 = new Request(E3, { headers: _3 });
|
|
7828
7881
|
return await e4.call(a5, O3, m4);
|
|
7829
7882
|
}
|
|
7830
7883
|
{
|
|
7831
7884
|
const E3 = { ...m4 }, _3 = new Headers(m4.headers || {});
|
|
7832
|
-
return _3.set(C, `${w4}/${b4}/${x4}`), I4 && (_3.set(I4.name, I4.value),
|
|
7885
|
+
return _3.set(C, `${w4}/${b4}/${x4}`), I4 && (_3.set(I4.name, I4.value), Sd && console.log("[Sailfish] Added funcspan header to HTTP fetch:", { url: "string" == typeof u4 ? u4 : u4.href, header: I4.name })), E3.headers = _3, await e4.call(a5, u4, E3);
|
|
7833
7886
|
}
|
|
7834
7887
|
})(e3, a4, m3, w3, b3, E2.page_visit_uuid, I3), B2 = false;
|
|
7835
|
-
|
|
7888
|
+
kd.includes($3.status) && (Sd && console.log("Perform retry as status was fail:", $3), I3 = v4(), $3 = await (async function retryWithoutPropagateHeaders(e4, a5, u4, m4) {
|
|
7836
7889
|
try {
|
|
7837
7890
|
let m5 = u4[0], w4 = u4[1] || {};
|
|
7838
7891
|
if ("string" == typeof m5 || m5 instanceof URL) {
|
|
@@ -7848,7 +7901,7 @@
|
|
|
7848
7901
|
}
|
|
7849
7902
|
return e4.apply(a5, u4);
|
|
7850
7903
|
} catch (e5) {
|
|
7851
|
-
throw
|
|
7904
|
+
throw Sd && console.log(`Retry without ${C} for ${m4} also failed:`, e5), e5;
|
|
7852
7905
|
}
|
|
7853
7906
|
})(e3, a4, u3, x3), B2 = true);
|
|
7854
7907
|
const U2 = Date.now(), j2 = $3.status, z2 = $3.ok, q2 = z2 ? "" : `Request Error: ${$3.statusText}`;
|
|
@@ -7857,7 +7910,7 @@
|
|
|
7857
7910
|
const e4 = $3.clone();
|
|
7858
7911
|
V2 = await e4.text();
|
|
7859
7912
|
} catch (e4) {
|
|
7860
|
-
|
|
7913
|
+
Sd && console.warn("[Sailfish] Failed to capture response data:", e4), V2 = null;
|
|
7861
7914
|
}
|
|
7862
7915
|
let H2 = null;
|
|
7863
7916
|
try {
|
|
@@ -7865,12 +7918,12 @@
|
|
|
7865
7918
|
H2[a5] = e4;
|
|
7866
7919
|
});
|
|
7867
7920
|
} catch (e4) {
|
|
7868
|
-
|
|
7921
|
+
Sd && console.warn("[Sailfish] Failed to capture response headers:", e4), H2 = null;
|
|
7869
7922
|
}
|
|
7870
7923
|
return sendEvent({ type: 27, timestamp: U2, sessionId: b3, data: { request_id: I3, session_id: b3, timestamp_start: O2, timestamp_end: U2, response_code: j2, success: z2, error: q2, method: _2, url: x3, retry_without_trace_id: B2, request_headers: D2, request_body: F2, response_headers: H2, response_body: V2 }, ...E2 }), $3;
|
|
7871
7924
|
} catch (m4) {
|
|
7872
7925
|
const w4 = Date.now(), C2 = false, $3 = ((_b = m4.response) == null ? void 0 : _b.status) || 500, B2 = m4.message || "Fetch request failed";
|
|
7873
|
-
if (m4 instanceof TypeError && ((_c2 = m4 == null ? void 0 : m4.message) == null ? void 0 : _c2.toLowerCase().includes(
|
|
7926
|
+
if (m4 instanceof TypeError && ((_c2 = m4 == null ? void 0 : m4.message) == null ? void 0 : _c2.toLowerCase().includes(Cd.toLowerCase()))) return e3.apply(a4, u3);
|
|
7874
7927
|
throw sendEvent({ type: 27, timestamp: w4, sessionId: b3, data: { request_id: I3, session_id: b3, timestamp_start: O2, timestamp_end: w4, response_code: $3, success: C2, error: B2, method: _2, url: x3, request_headers: D2, request_body: F2, response_body: null }, ...E2 }), m4;
|
|
7875
7928
|
}
|
|
7876
7929
|
})(a3, m2, w2, x2, I2, u2, b2);
|
|
@@ -7904,7 +7957,7 @@
|
|
|
7904
7957
|
})(), D2 = getOrSetSessionId(), $2 = window.__sailfish_recorder || (window.__sailfish_recorder = {});
|
|
7905
7958
|
if ($2.sessionId = D2, $2.apiKey = e2, $2.backendApi = a2, $2.serviceAdditionalMetadata = I2, $2.initialized && $2.sessionId === D2 && $2.ws && 1 === $2.ws.readyState) trackDomainChangesOnce();
|
|
7906
7959
|
else {
|
|
7907
|
-
$2.domEventsInit || (initializeDomContentEvents(D2), $2.domEventsInit = true), $2.consoleInit || (initializeConsolePlugin(
|
|
7960
|
+
$2.domEventsInit || (initializeDomContentEvents(D2), $2.domEventsInit = true), $2.consoleInit || (initializeConsolePlugin(Id, D2), $2.consoleInit = true), $2.errorInit || (!(function initializeErrorInterceptor() {
|
|
7908
7961
|
window.addEventListener("error", (e3) => {
|
|
7909
7962
|
captureError(e3.error || e3.message);
|
|
7910
7963
|
}), window.addEventListener("unhandledrejection", (e3) => {
|
|
@@ -7914,10 +7967,10 @@
|
|
|
7914
7967
|
Q && (sessionStorage.setItem("sailfishApiKey", e3), sessionStorage.setItem("sailfishBackendApi", a3));
|
|
7915
7968
|
})({ apiKey: e2, backendApi: a2 }), trackDomainChangesOnce(), sessionStorage.setItem("sailfishApiKey", e2), sessionStorage.setItem("sailfishBackendApi", a2), !isFunctionSpanTrackingEnabled() || $2.ws && 1 === $2.ws.readyState || fetchFunctionSpanTrackingEnabled(e2, a2).then((e3) => {
|
|
7916
7969
|
var _a3;
|
|
7917
|
-
((_a3 = e3.data) == null ? void 0 : _a3.isFunctionSpanTrackingEnabledFromApiKey) ?? false ?
|
|
7970
|
+
((_a3 = e3.data) == null ? void 0 : _a3.isFunctionSpanTrackingEnabledFromApiKey) ?? false ? Sd && console.log("[Sailfish] Function span tracking state validated with backend: ACTIVE") : (clearStaleFuncSpanState(), Sd && console.log("[Sailfish] Cleared stale function span tracking state - backend validation shows tracking is not active"));
|
|
7918
7971
|
}).catch((e3) => {
|
|
7919
|
-
|
|
7920
|
-
}), $2.sentDoNotPropagateOnce || (sendDomainsToNotPropagateHeaderTo(e2, [...m2, ...
|
|
7972
|
+
Sd && console.warn("[Sailfish] Failed to validate function span tracking status with backend:", e3);
|
|
7973
|
+
}), $2.sentDoNotPropagateOnce || (sendDomainsToNotPropagateHeaderTo(e2, [...m2, ...vd], a2).catch((e3) => console.error("Failed to send domains to not propagate header to:", e3)), $2.sentDoNotPropagateOnce = true), $2.xhrPatched || (!(function setupXMLHttpRequestInterceptor(e3 = []) {
|
|
7921
7974
|
const a3 = XMLHttpRequest.prototype.open, u3 = XMLHttpRequest.prototype.send, m3 = XMLHttpRequest.prototype.setRequestHeader, w3 = getOrSetSessionId();
|
|
7922
7975
|
XMLHttpRequest.prototype.setRequestHeader = function(e4, a4) {
|
|
7923
7976
|
return this._capturedRequestHeaders || (this._capturedRequestHeaders = {}), this._capturedRequestHeaders[e4] = a4, m3.call(this, e4, a4);
|
|
@@ -7935,9 +7988,9 @@
|
|
|
7935
7988
|
}
|
|
7936
7989
|
const E3 = getFuncSpanHeader();
|
|
7937
7990
|
if (E3) try {
|
|
7938
|
-
this.setRequestHeader(E3.name, E3.value),
|
|
7991
|
+
this.setRequestHeader(E3.name, E3.value), Sd && console.log("[Sailfish] Added funcspan header to XMLHttpRequest:", { url: m4, header: E3.name });
|
|
7939
7992
|
} catch (e4) {
|
|
7940
|
-
|
|
7993
|
+
Sd && console.warn(`[Sailfish] Could not set funcspan header for ${m4}`, e4);
|
|
7941
7994
|
}
|
|
7942
7995
|
const _3 = Date.now();
|
|
7943
7996
|
let O3 = false;
|
|
@@ -7965,7 +8018,7 @@
|
|
|
7965
8018
|
2 === a6.length && (u4[a6[0]] = a6[1]);
|
|
7966
8019
|
});
|
|
7967
8020
|
} catch (e5) {
|
|
7968
|
-
|
|
8021
|
+
Sd && console.warn("[Sailfish] Failed to capture XHR response headers:", e5), u4 = null;
|
|
7969
8022
|
}
|
|
7970
8023
|
if (e4 >= 200 && e4 < 300) emitFinished(true, e4, "", a5, u4);
|
|
7971
8024
|
else {
|
|
@@ -7981,7 +8034,7 @@
|
|
|
7981
8034
|
sendMessage({ type: "deviceInfo", data: { deviceInfo: { language: navigator.language, userAgent: navigator.userAgent } } });
|
|
7982
8035
|
})();
|
|
7983
8036
|
try {
|
|
7984
|
-
const u3 = await fetchCaptureSettings(e2, a2), m3 = ((_a2 = u3.data) == null ? void 0 : _a2.captureSettingsFromApiKey) ||
|
|
8037
|
+
const u3 = await fetchCaptureSettings(e2, a2), m3 = ((_a2 = u3.data) == null ? void 0 : _a2.captureSettingsFromApiKey) || xd;
|
|
7985
8038
|
if ($2.ws && 1 === $2.ws.readyState) return;
|
|
7986
8039
|
const C2 = withAppUrlMetadata(I2), x3 = await startRecordingSession(e2, D2, a2, _2, O2, F2, E2, "JS/TS", C2);
|
|
7987
8040
|
if ((_b = x3.data) == null ? void 0 : _b.startRecordingSession) {
|
|
@@ -8017,7 +8070,7 @@
|
|
|
8017
8070
|
const e2 = document.visibilityState, a2 = Date.now();
|
|
8018
8071
|
"visible" === e2 && getOrSetSessionId();
|
|
8019
8072
|
try {
|
|
8020
|
-
sendMessage({ type: "visibilityChange", data: { state: e2, url: window.location.href.split("?")[0], timestamp: a2, ...getUrlAndStoredUuids() } }),
|
|
8073
|
+
sendMessage({ type: "visibilityChange", data: { state: e2, url: window.location.href.split("?")[0], timestamp: a2, ...getUrlAndStoredUuids() } }), Sd && console.log(`[Sailfish] Tab became ${e2}, sent visibility change event`);
|
|
8021
8074
|
} catch (e3) {
|
|
8022
8075
|
console.warn("[Sailfish] Failed to send visibility change event:", e3);
|
|
8023
8076
|
}
|
|
@@ -8025,12 +8078,12 @@
|
|
|
8025
8078
|
}), J && window.addEventListener("beforeunload", () => {
|
|
8026
8079
|
clearPageVisitDataFromSessionStorage();
|
|
8027
8080
|
});
|
|
8028
|
-
e.DEFAULT_CAPTURE_SETTINGS =
|
|
8081
|
+
e.DEFAULT_CAPTURE_SETTINGS = xd, e.DEFAULT_CONSOLE_RECORDING_SETTINGS = Id, e.STORAGE_VERSION = 1, e.addOrUpdateMetadata = function addOrUpdateMetadata(e2) {
|
|
8029
8082
|
const a2 = { type: "addOrUpdateMetadata", metadata: e2 };
|
|
8030
|
-
|
|
8083
|
+
bd && JSON.stringify(bd) === JSON.stringify(e2) || (bd = e2, sendMessage(a2));
|
|
8031
8084
|
}, e.buildBatches = buildBatches, e.clearStaleFuncSpanState = clearStaleFuncSpanState, e.createTriageAndIssueFromRecorder = createTriageAndIssueFromRecorder, e.createTriageFromRecorder = createTriageFromRecorder, e.disableFunctionSpanTracking = disableFunctionSpanTracking, e.enableFunctionSpanTracking = enableFunctionSpanTracking, e.eventSize = eventSize, e.fetchCaptureSettings = fetchCaptureSettings, e.fetchEngineeringTicketPlatformIntegrations = fetchEngineeringTicketPlatformIntegrations, e.fetchFunctionSpanTrackingEnabled = fetchFunctionSpanTrackingEnabled, e.flushBufferedEvents = flushBufferedEvents, e.getFuncSpanHeader = getFuncSpanHeader, e.getOrSetSessionId = getOrSetSessionId, e.getUrlAndStoredUuids = getUrlAndStoredUuids, e.identify = function identify(e2, a2 = {}, u2 = false) {
|
|
8032
8085
|
const m2 = { type: "identify", userId: e2, traits: a2 };
|
|
8033
|
-
|
|
8086
|
+
wd && wd.userId === e2 && JSON.stringify(wd.traits) === JSON.stringify(a2) || (wd = { userId: e2, traits: a2, overwrite: u2 }, sendMessage(m2));
|
|
8034
8087
|
}, e.initRecorder = async (e2) => {
|
|
8035
8088
|
if ("undefined" == typeof window) return;
|
|
8036
8089
|
const a2 = window.__sailfish_recorder || (window.__sailfish_recorder = {}), u2 = getOrSetSessionId();
|
package/dist/recording.js
CHANGED
|
@@ -3,6 +3,7 @@ import { getRecordConsolePlugin, } from "@sailfish-rrweb/rrweb-plugin-console-re
|
|
|
3
3
|
// import { NetworkRecordOptions } from "@sailfish-rrweb/rrweb-plugin-network-record";
|
|
4
4
|
import { EventType } from "@sailfish-rrweb/types";
|
|
5
5
|
import { Complete, DomContentEventId, DomContentSource, Loading, } from "./constants";
|
|
6
|
+
import { getCallerLocation, getCallerLocationFromTrace, } from "./sourceLocation";
|
|
6
7
|
import suppressConsoleLogsDuringCall from "./suppressConsoleLogsDuringCall";
|
|
7
8
|
import { initializeWebSocket, sendEvent } from "./websocket";
|
|
8
9
|
const MASK_CLASS = "sailfishSanitize";
|
|
@@ -140,12 +141,24 @@ export function initializeDomContentEvents(sessionId) {
|
|
|
140
141
|
export function initializeConsolePlugin(consoleRecordSettings, sessionId) {
|
|
141
142
|
const { name, observer } = getRecordConsolePlugin(consoleRecordSettings);
|
|
142
143
|
observer((payload) => {
|
|
144
|
+
const anyPayload = payload;
|
|
145
|
+
// rrweb console record plugin includes `trace` for logs (stack frames). :contentReference[oaicite:1]{index=1}
|
|
146
|
+
const [sourceFileFromTrace, sourceLineFromTrace] = getCallerLocationFromTrace(anyPayload?.trace, 0);
|
|
147
|
+
// Fallback (less accurate when inside wrappers)
|
|
148
|
+
const [sourceFileFallback, sourceLineFallback] = getCallerLocation(2);
|
|
149
|
+
const sourceFile = sourceFileFromTrace ?? sourceFileFallback;
|
|
150
|
+
const sourceLine = sourceLineFromTrace ?? sourceLineFallback;
|
|
151
|
+
const enhancedPayload = {
|
|
152
|
+
...anyPayload,
|
|
153
|
+
sourceFile,
|
|
154
|
+
sourceLine,
|
|
155
|
+
};
|
|
143
156
|
const eventData = {
|
|
144
157
|
type: EventType.Plugin,
|
|
145
158
|
timestamp: Date.now(),
|
|
146
159
|
data: {
|
|
147
160
|
plugin: name,
|
|
148
|
-
payload,
|
|
161
|
+
payload: enhancedPayload, // Use enhanced payload
|
|
149
162
|
},
|
|
150
163
|
sessionId: sessionId,
|
|
151
164
|
...getUrlAndStoredUuids(),
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-compatible source location capture for console logs.
|
|
3
|
+
*
|
|
4
|
+
* Prefer using rrweb's captured "trace" (stack) when available:
|
|
5
|
+
* - It is captured at the moment console.X was invoked (best chance to get true callsite)
|
|
6
|
+
* - It is often already source-mapped by the browser (webpack-internal:///src/..., etc.)
|
|
7
|
+
*/
|
|
8
|
+
const SKIP_PATTERNS = [
|
|
9
|
+
"/node_modules/",
|
|
10
|
+
"/@sailfish-ai/",
|
|
11
|
+
"/@sailfish-rrweb/",
|
|
12
|
+
"/dist/",
|
|
13
|
+
"/webpack/",
|
|
14
|
+
"/vite/",
|
|
15
|
+
"/__vite",
|
|
16
|
+
"/react-dom/",
|
|
17
|
+
"/react/",
|
|
18
|
+
"/scheduler/",
|
|
19
|
+
"/<",
|
|
20
|
+
"/chrome-extension://",
|
|
21
|
+
"/extensions/",
|
|
22
|
+
];
|
|
23
|
+
const MAX_FRAME_WALK = 20;
|
|
24
|
+
function shouldSkipFrame(file) {
|
|
25
|
+
return SKIP_PATTERNS.some((pattern) => file.includes(pattern));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Normalize file path for browser environment.
|
|
29
|
+
*/
|
|
30
|
+
function normalizeFilePath(rawPath) {
|
|
31
|
+
let path = rawPath;
|
|
32
|
+
// Some stacks include "eval at ..." or "blob:" — keep but normalize what we can.
|
|
33
|
+
// file:// URLs (local dev)
|
|
34
|
+
if (path.startsWith("file://")) {
|
|
35
|
+
path = path.substring("file://".length);
|
|
36
|
+
}
|
|
37
|
+
// webpack internal urls
|
|
38
|
+
if (path.startsWith("webpack-internal:///")) {
|
|
39
|
+
path = path.substring("webpack-internal:///".length);
|
|
40
|
+
}
|
|
41
|
+
if (path.startsWith("webpack:///")) {
|
|
42
|
+
path = path.substring("webpack:///".length);
|
|
43
|
+
}
|
|
44
|
+
// Vite sometimes uses "/@fs/..." to represent absolute paths
|
|
45
|
+
if (path.startsWith("/@fs/")) {
|
|
46
|
+
path = path.substring("/@fs/".length);
|
|
47
|
+
}
|
|
48
|
+
// http(s) URLs -> pathname
|
|
49
|
+
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
50
|
+
try {
|
|
51
|
+
const url = new URL(path);
|
|
52
|
+
path = url.pathname || path;
|
|
53
|
+
if (path === "/")
|
|
54
|
+
path = "index.html";
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// ignore
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return path;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parse V8 stack lines (Error.stack format).
|
|
64
|
+
*/
|
|
65
|
+
function parseV8Stack(stack) {
|
|
66
|
+
if (!stack)
|
|
67
|
+
return [];
|
|
68
|
+
const stackLines = stack.split("\n").slice(1);
|
|
69
|
+
const frames = [];
|
|
70
|
+
for (const line of stackLines) {
|
|
71
|
+
let match = /at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/.exec(line);
|
|
72
|
+
if (match) {
|
|
73
|
+
frames.push({
|
|
74
|
+
functionName: match[1],
|
|
75
|
+
file: match[2],
|
|
76
|
+
line: parseInt(match[3], 10),
|
|
77
|
+
column: parseInt(match[4], 10),
|
|
78
|
+
});
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
match = /at\s+(.+?):(\d+):(\d+)/.exec(line);
|
|
82
|
+
if (match) {
|
|
83
|
+
frames.push({
|
|
84
|
+
functionName: "<anonymous>",
|
|
85
|
+
file: match[1],
|
|
86
|
+
line: parseInt(match[2], 10),
|
|
87
|
+
column: parseInt(match[3], 10),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return frames;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* rrweb "trace" entries are typically stack-frame-like strings.
|
|
95
|
+
* They may look like:
|
|
96
|
+
* - "myFn (http://localhost:5173/src/App.tsx:42:17)"
|
|
97
|
+
* - "http://localhost:5173/src/App.tsx:42:17"
|
|
98
|
+
* - "myFn (webpack-internal:///./src/App.tsx:42:17)"
|
|
99
|
+
*
|
|
100
|
+
* We'll parse these into StackFrame and then apply your skip rules.
|
|
101
|
+
*/
|
|
102
|
+
function parseRrwebTraceFrames(trace) {
|
|
103
|
+
const frames = [];
|
|
104
|
+
for (const entry of trace) {
|
|
105
|
+
if (!entry)
|
|
106
|
+
continue;
|
|
107
|
+
// Trim leading "at " if present
|
|
108
|
+
const line = entry.startsWith("at ") ? entry.slice(3) : entry;
|
|
109
|
+
// "fn (file:line:col)"
|
|
110
|
+
let match = /^(.*?)\s+\((.+?):(\d+):(\d+)\)$/.exec(line);
|
|
111
|
+
if (match) {
|
|
112
|
+
frames.push({
|
|
113
|
+
functionName: match[1] || "<anonymous>",
|
|
114
|
+
file: match[2],
|
|
115
|
+
line: parseInt(match[3], 10),
|
|
116
|
+
column: parseInt(match[4], 10),
|
|
117
|
+
});
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
// "file:line:col"
|
|
121
|
+
match = /^(.+?):(\d+):(\d+)$/.exec(line);
|
|
122
|
+
if (match) {
|
|
123
|
+
frames.push({
|
|
124
|
+
functionName: "<anonymous>",
|
|
125
|
+
file: match[1],
|
|
126
|
+
line: parseInt(match[2], 10),
|
|
127
|
+
column: parseInt(match[3], 10),
|
|
128
|
+
});
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
// If it doesn't match, ignore it (some environments add noise)
|
|
132
|
+
}
|
|
133
|
+
return frames;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Prefer rrweb's trace when provided.
|
|
137
|
+
* Returns first "user" frame after filtering.
|
|
138
|
+
*/
|
|
139
|
+
export function getCallerLocationFromTrace(trace, skipFrames = 0) {
|
|
140
|
+
if (!Array.isArray(trace) || trace.length === 0)
|
|
141
|
+
return [null, null];
|
|
142
|
+
const frames = parseRrwebTraceFrames(trace);
|
|
143
|
+
for (let i = skipFrames; i < Math.min(frames.length, skipFrames + MAX_FRAME_WALK); i++) {
|
|
144
|
+
const frame = frames[i];
|
|
145
|
+
if (!frame?.file)
|
|
146
|
+
continue;
|
|
147
|
+
const normalized = normalizeFilePath(frame.file);
|
|
148
|
+
if (shouldSkipFrame(normalized))
|
|
149
|
+
continue;
|
|
150
|
+
return [normalized, frame.line];
|
|
151
|
+
}
|
|
152
|
+
return [null, null];
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Fallback: compute location from current stack.
|
|
156
|
+
* This is less reliable when called inside wrappers.
|
|
157
|
+
*/
|
|
158
|
+
export function getCallerLocation(skipFrames = 0) {
|
|
159
|
+
const err = new Error();
|
|
160
|
+
const stack = err.stack;
|
|
161
|
+
if (!stack)
|
|
162
|
+
return [null, null];
|
|
163
|
+
const frames = parseV8Stack(stack);
|
|
164
|
+
const frameOffset = 1 + skipFrames;
|
|
165
|
+
for (let i = frameOffset; i < Math.min(frames.length, frameOffset + MAX_FRAME_WALK); i++) {
|
|
166
|
+
const frame = frames[i];
|
|
167
|
+
if (!frame?.file)
|
|
168
|
+
continue;
|
|
169
|
+
const normalized = normalizeFilePath(frame.file);
|
|
170
|
+
if (shouldSkipFrame(normalized))
|
|
171
|
+
continue;
|
|
172
|
+
return [normalized, frame.line];
|
|
173
|
+
}
|
|
174
|
+
return [null, null];
|
|
175
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-compatible source location capture for console logs.
|
|
3
|
+
*
|
|
4
|
+
* Prefer using rrweb's captured "trace" (stack) when available:
|
|
5
|
+
* - It is captured at the moment console.X was invoked (best chance to get true callsite)
|
|
6
|
+
* - It is often already source-mapped by the browser (webpack-internal:///src/..., etc.)
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Prefer rrweb's trace when provided.
|
|
10
|
+
* Returns first "user" frame after filtering.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getCallerLocationFromTrace(trace: unknown, skipFrames?: number): [string | null, number | null];
|
|
13
|
+
/**
|
|
14
|
+
* Fallback: compute location from current stack.
|
|
15
|
+
* This is less reliable when called inside wrappers.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getCallerLocation(skipFrames?: number): [string | null, number | null];
|