highlight.run
Version:
Open source, fullstack monitoring. Capture frontend errors, record server side logs, and visualize what broke with session replay.
1,176 lines (1,175 loc) • 64.3 kB
JavaScript
var Be = Object.defineProperty, Me = Object.defineProperties;
var Fe = Object.getOwnPropertyDescriptors;
var F = Object.getOwnPropertySymbols;
var he = Object.prototype.hasOwnProperty, ge = Object.prototype.propertyIsEnumerable;
var K = (n, e, t) => e in n ? Be(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t, w = (n, e) => {
for (var t in e || (e = {}))
he.call(e, t) && K(n, t, e[t]);
if (F)
for (var t of F(e))
ge.call(e, t) && K(n, t, e[t]);
return n;
}, y = (n, e) => Me(n, Fe(e));
var x = (n, e) => {
var t = {};
for (var s in n)
he.call(n, s) && e.indexOf(s) < 0 && (t[s] = n[s]);
if (n != null && F)
for (var s of F(n))
e.indexOf(s) < 0 && ge.call(n, s) && (t[s] = n[s]);
return t;
};
var a = (n, e, t) => K(n, typeof e != "symbol" ? e + "" : e, t);
var _ = (n, e, t) => new Promise((s, i) => {
var o = (d) => {
try {
l(t.next(d));
} catch (c) {
i(c);
}
}, r = (d) => {
try {
l(t.throw(d));
} catch (c) {
i(c);
}
}, l = (d) => d.done ? s(d.value) : Promise.resolve(d.value).then(o, r);
l((t = t.apply(n, e)).next());
});
import { W as $e, M as C, r as q, I as ue, a as me, S as Ve, C as Ae, F as Ke, b as xe, P as qe, c as fe, g as Ge, p as je, d as Je, e as Qe, f as Ye, s as Xe, h as Ze } from "./record-BP2p3lFo.js";
import { R as Yt } from "./record-BP2p3lFo.js";
import { s as V, G as N, g as M, S as L, r as pe, a as G, b as et, c as tt, d as j, e as J, f as U, F as we, V as st, P as it, h as nt, H as Q, M as rt, i as ot, L as Ce, j as Le, p as at, k as Se, l as ye, m as ct, n as lt, o as dt, q as ht, t as gt, u as ut, v as mt, w as ft, x as pt } from "./common-Cqt7tQ3r.js";
import { X as wt, F as St, A as yt, C as bt, E as kt, s as Ee, D as vt, a as _t, m as be, d as Ne, b as ke, c as ve, r as Oe, e as Rt, p as Dt, f as It, g as Ct, M as D, h as E, W as Lt, N as Et, P as Nt, J as Ot, i as $, j as Tt, k as Pt, l as Ht, n as _e, o as Re } from "./observe-CoFuWblN.js";
import { O as Zt } from "./observe-CoFuWblN.js";
import { L as Ut } from "./buffer-DIFxvF2Q.js";
import { LDObserve as ts } from "./LDObserve.js";
import { LDRecord as is } from "./LDRecord.js";
const Wt = (n, e, t) => {
const s = window._highlightWebSocketRequestCallback;
window._highlightWebSocketRequestCallback = n;
const i = window._highlightWebSocketEventCallback;
return window._highlightWebSocketEventCallback = (o) => {
const h = o, { message: r, size: l } = h, d = x(h, ["message", "size"]), g = t.some(
(m) => o.name.toLowerCase().includes(m)
) ? d : o;
e(g);
}, () => {
window._highlightWebSocketRequestCallback = s, window._highlightWebSocketEventCallback = i;
};
}, zt = ({
xhrCallback: n,
fetchCallback: e,
webSocketRequestCallback: t,
webSocketEventCallback: s,
disableWebSocketRecording: i,
bodyKeysToRedact: o,
highlightEndpoints: r,
tracingOrigins: l,
urlBlocklist: d,
bodyKeysToRecord: c
}) => {
const g = wt(
n,
r,
l,
d,
o,
c
), h = St(
e,
r,
l,
d,
o,
c
), m = i ? () => {
} : Wt(
t,
s,
d
);
return () => {
g(), h(), m();
};
};
class I {
constructor(e) {
a(this, "disableConsoleRecording");
a(this, "reportConsoleErrors");
a(this, "enablePromisePatch");
a(this, "consoleMethodsToRecord");
a(this, "listeners");
a(this, "errors");
a(this, "messages");
// The properties below were added in 4.0.0 (Feb 2022), and are patched in by client via setupNetworkListeners()
a(this, "options");
a(this, "hasNetworkRecording", !0);
a(this, "disableNetworkRecording");
a(this, "enableRecordingNetworkContents");
a(this, "xhrNetworkContents");
a(this, "fetchNetworkContents");
a(this, "disableRecordingWebSocketContents");
a(this, "webSocketNetworkContents");
a(this, "webSocketEventContents");
a(this, "tracingOrigins");
a(this, "networkHeadersToRedact");
a(this, "networkBodyKeysToRedact");
a(this, "networkBodyKeysToRecord");
a(this, "networkHeaderKeysToRecord");
a(this, "lastNetworkRequestTimestamp");
a(this, "urlBlocklist");
a(this, "highlightEndpoints");
a(this, "requestResponseSanitizer");
var t, s;
this.options = e, this.disableConsoleRecording = !!e.disableConsoleRecording, this.reportConsoleErrors = (t = e.reportConsoleErrors) != null ? t : !1, this.enablePromisePatch = (s = e.enablePromisePatch) != null ? s : !1, this.consoleMethodsToRecord = e.consoleMethodsToRecord || [
...yt
], this.listeners = [], this.errors = [], this.messages = [], this.lastNetworkRequestTimestamp = 0;
}
isListening() {
return this.listeners.length > 0;
}
startListening() {
if (this.isListening()) return;
const e = this;
this.disableConsoleRecording || this.listeners.push(
bt(
(t) => {
var s, i, o;
if (this.reportConsoleErrors && (t.type === "Error" || t.type === "error") && t.value && t.trace) {
const r = V(t.value);
if (ke.includes(r) || ve.some(
(l) => r.includes(l)
))
return;
e.errors.push({
event: r,
type: "console.error",
url: window.location.href,
source: (s = t.trace[0]) != null && s.fileName ? t.trace[0].fileName : "",
lineNumber: (i = t.trace[0]) != null && i.lineNumber ? t.trace[0].lineNumber : 0,
columnNumber: (o = t.trace[0]) != null && o.columnNumber ? t.trace[0].columnNumber : 0,
stackTrace: t.trace,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
id: Oe()
});
}
e.messages.push(t);
},
y(w({}, Ne), {
level: this.consoleMethodsToRecord
})
)
), this.listeners.push(
kt(
(t) => {
ke.includes(t.event) || ve.some(
(s) => t.event.includes(s)
) || e.errors.push(t);
},
{ enablePromisePatch: this.enablePromisePatch }
)
), this.listeners.push(Ee), I.setupNetworkListener(this, this.options);
}
stopListening() {
this.listeners.forEach((e) => e()), this.listeners = [];
}
// We define this as a static method because versions earlier than 4.0.0 (Feb 2022) don't have this code.
// For those versions, calling this from client will monkey-patch the network listeners onto the old FirstLoadListener object.
static setupNetworkListener(e, t) {
var o, r, l, d, c, g, h, m, f, R, k, O;
const s = (t == null ? void 0 : t.backendUrl) || void 0 || "https://pub.highlight.io", i = t.otlpEndpoint || "https://otel.highlight.io";
e.highlightEndpoints = [
s,
`${i}/v1/traces`,
`${i}/v1/logs`,
`${i}/v1/metrics`
], e.xhrNetworkContents = [], e.fetchNetworkContents = [], e.webSocketNetworkContents = [], e.webSocketEventContents = [], e.networkHeadersToRedact = [], e.urlBlocklist = [], e.tracingOrigins = t.tracingOrigins || [], (t == null ? void 0 : t.disableNetworkRecording) !== void 0 ? (e.disableNetworkRecording = t == null ? void 0 : t.disableNetworkRecording, e.enableRecordingNetworkContents = !1, e.disableRecordingWebSocketContents = !0, e.networkHeadersToRedact = [], e.networkBodyKeysToRedact = [], e.urlBlocklist = [], e.networkBodyKeysToRecord = []) : typeof (t == null ? void 0 : t.networkRecording) == "boolean" ? (e.disableNetworkRecording = !t.networkRecording, e.enableRecordingNetworkContents = !1, e.disableRecordingWebSocketContents = !0, e.networkHeadersToRedact = [], e.networkBodyKeysToRedact = [], e.urlBlocklist = []) : (((o = t.networkRecording) == null ? void 0 : o.enabled) !== void 0 ? e.disableNetworkRecording = !t.networkRecording.enabled : e.disableNetworkRecording = !1, e.enableRecordingNetworkContents = ((r = t.networkRecording) == null ? void 0 : r.recordHeadersAndBody) || !1, e.disableRecordingWebSocketContents = ((l = t.networkRecording) == null ? void 0 : l.disableWebSocketEventRecordings) || !1, e.networkHeadersToRedact = ((c = (d = t.networkRecording) == null ? void 0 : d.networkHeadersToRedact) == null ? void 0 : c.map(
(S) => S.toLowerCase()
)) || [], e.networkBodyKeysToRedact = ((h = (g = t.networkRecording) == null ? void 0 : g.networkBodyKeysToRedact) == null ? void 0 : h.map(
(S) => S.toLowerCase()
)) || [], e.urlBlocklist = ((f = (m = t.networkRecording) == null ? void 0 : m.urlBlocklist) == null ? void 0 : f.map(
(S) => S.toLowerCase()
)) || [], e.urlBlocklist = [
...e.urlBlocklist,
...vt
], e.requestResponseSanitizer = (R = t.networkRecording) == null ? void 0 : R.requestResponseSanitizer, e.networkHeaderKeysToRecord = (k = t.networkRecording) == null ? void 0 : k.headerKeysToRecord, e.networkHeaderKeysToRecord && (e.networkHeadersToRedact = [], e.networkHeaderKeysToRecord = e.networkHeaderKeysToRecord.map(
(S) => S.toLocaleLowerCase()
)), e.networkBodyKeysToRecord = (O = t.networkRecording) == null ? void 0 : O.bodyKeysToRecord, e.networkBodyKeysToRecord && (e.networkBodyKeysToRedact = [], e.networkBodyKeysToRecord = e.networkBodyKeysToRecord.map(
(S) => S.toLocaleLowerCase()
))), !e.disableNetworkRecording && e.enableRecordingNetworkContents && e.listeners.push(
zt({
xhrCallback: (S) => {
e.xhrNetworkContents.push(S);
},
fetchCallback: (S) => {
e.fetchNetworkContents.push(S);
},
webSocketRequestCallback: (S) => {
e.webSocketNetworkContents && e.webSocketNetworkContents.push(S);
},
webSocketEventCallback: (S) => {
e.webSocketEventContents.push(S);
},
disableWebSocketRecording: e.disableRecordingWebSocketContents,
bodyKeysToRedact: e.networkBodyKeysToRedact,
highlightEndpoints: e.highlightEndpoints,
tracingOrigins: e.tracingOrigins,
urlBlocklist: e.urlBlocklist,
bodyKeysToRecord: e.networkBodyKeysToRecord
})
);
}
static getRecordedNetworkResources(e, t) {
var o, r;
let s = [], i = [];
if (!e.disableNetworkRecording) {
const l = ((o = window == null ? void 0 : window.performance) == null ? void 0 : o.timeOrigin) || 0;
s = performance.getEntriesByType(
"resource"
);
const d = (t - l) * 2;
if (s = s.filter((c) => c.responseEnd < e.lastNetworkRequestTimestamp ? !1 : _t(
c.name,
e.highlightEndpoints
)).map((c) => y(w({}, c.toJSON()), {
offsetStartTime: c.startTime - d,
offsetResponseEnd: c.responseEnd - d,
offsetFetchStart: c.fetchStart - d
})), e.lastNetworkRequestTimestamp = ((r = s.at(-1)) == null ? void 0 : r.responseEnd) || e.lastNetworkRequestTimestamp, e.enableRecordingNetworkContents) {
const c = {
headersToRedact: e.networkHeadersToRedact,
headersToRecord: e.networkHeaderKeysToRecord,
requestResponseSanitizer: e.requestResponseSanitizer
};
s = be(
s,
e.xhrNetworkContents,
"xmlhttprequest",
c
), s = be(
s,
e.fetchNetworkContents,
"fetch",
c
);
}
}
return e.disableRecordingWebSocketContents || (i = e.webSocketNetworkContents || []), [...s, ...i];
}
static getRecordedWebSocketEvents(e) {
let t = [];
return !e.disableNetworkRecording && !e.disableRecordingWebSocketContents && (t = e.webSocketEventContents), t;
}
static clearRecordedNetworkResources(e) {
e.disableNetworkRecording || (e.xhrNetworkContents = [], e.fetchNetworkContents = [], e.webSocketNetworkContents = [], e.webSocketEventContents = [], performance.clearResourceTimings());
}
}
const W = (n, e) => {
console.warn(`Highlight Warning: (${n}): `, { output: e });
};
class X {
constructor(e, t) {
a(this, "options");
/** Determines if the client is running on a Highlight property (e.g. frontend). */
a(this, "isRunningOnHighlight");
/** Verbose project ID that is exposed to users. Legacy users may still be using ints. */
a(this, "organizationID");
a(this, "graphqlSDK");
a(this, "events");
a(this, "sessionData");
a(this, "ready");
a(this, "manualStopped");
a(this, "state");
a(this, "logger");
a(this, "enableSegmentIntegration");
a(this, "privacySetting");
a(this, "enableCanvasRecording");
a(this, "enablePerformanceRecording");
a(this, "samplingStrategy");
a(this, "inlineImages");
a(this, "inlineVideos");
a(this, "inlineStylesheet");
a(this, "debugOptions");
a(this, "listeners");
a(this, "firstloadVersion");
a(this, "environment");
a(this, "sessionShortcut");
/** The end-user's app version. This isn't Highlight's version. */
a(this, "appVersion");
a(this, "serviceName");
a(this, "_worker");
a(this, "_optionsInternal");
a(this, "_backendUrl");
a(this, "_recordingStartTime");
a(this, "_isOnLocalHost");
a(this, "_onToggleFeedbackFormVisibility");
a(this, "_firstLoadListeners");
a(this, "_isCrossOriginIframe");
a(this, "_eventBytesSinceSnapshot");
a(this, "_lastSnapshotTime");
a(this, "_lastVisibilityChangeTime");
a(this, "pushPayloadTimerId");
a(this, "hasSessionUnloaded");
a(this, "hasPushedData");
a(this, "reloaded");
a(this, "_hasPreviouslyInitialized");
a(this, "_recordStop");
a(this, "_gauges", /* @__PURE__ */ new Map());
a(this, "_counters", /* @__PURE__ */ new Map());
a(this, "_histograms", /* @__PURE__ */ new Map());
a(this, "_up_down_counters", /* @__PURE__ */ new Map());
a(this, "_integrations", []);
var i, o, r, l, d;
e.sessionSecureID || (e.sessionSecureID = N()), this.options = e, typeof ((i = this.options) == null ? void 0 : i.debug) == "boolean" ? this.debugOptions = this.options.debug ? { clientInteractions: !0 } : {} : this.debugOptions = (r = (o = this.options) == null ? void 0 : o.debug) != null ? r : {}, this.logger = new Ut(this.debugOptions.clientInteractions), e.storageMode && (this.logger.log(
`initializing in ${e.storageMode} session mode`
), ct(e.storageMode)), Le(!!(e != null && e.sessionCookie)), this._worker = new $e(), this._worker.onmessage = (c) => {
var g, h, m;
((g = c.data.response) == null ? void 0 : g.type) === C.AsyncEvents ? (this._eventBytesSinceSnapshot += c.data.response.eventsSize, this.logger.log(
`Web worker sent payloadID ${c.data.response.id} size ${c.data.response.eventsSize} bytes, compression ratio ${c.data.response.eventsSize / c.data.response.compressedSize}.
Total since snapshot: ${(this._eventBytesSinceSnapshot / 1e6).toFixed(1)}MB`
)) : ((h = c.data.response) == null ? void 0 : h.type) === C.CustomEvent ? this.addCustomEvent(
c.data.response.tag,
c.data.response.payload
) : ((m = c.data.response) == null ? void 0 : m.type) === C.Stop && (W(
"Stopping recording due to worker failure",
c.data.response
), this.stopRecording(!1));
};
let s = M();
if (this.reloaded = !1, !((l = this.sessionData) != null && l.sessionSecureID) && (s != null && s.sessionSecureID))
this.sessionData = s, this.options.sessionSecureID = s.sessionSecureID, this.reloaded = !0, this.logger.log(
`Tab reloaded, continuing previous session: ${this.sessionData.sessionSecureID}`
);
else {
for (const c of Object.values(L))
pe(c);
this.sessionData = {
sessionSecureID: this.options.sessionSecureID,
projectID: 0,
sessionStartTime: Date.now()
};
}
this._hasPreviouslyInitialized = !1, this._firstLoadListeners = t || new I(this.options);
try {
window.parent.document && (this._isCrossOriginIframe = !1);
} catch (c) {
this._isCrossOriginIframe = (d = this.options.recordCrossOriginIframe) != null ? d : !0;
}
this._initMembers(this.options);
}
static create(e) {
return new X(e);
}
// Start a new session
_reset(s) {
return _(this, arguments, function* ({
forceNew: e,
sessionKey: t
}) {
this.pushPayloadTimerId && (clearTimeout(this.pushPayloadTimerId), this.pushPayloadTimerId = void 0);
let i, o;
if (!e)
try {
i = G(L.USER_IDENTIFIER);
const r = G(
L.USER_OBJECT
);
r && (o = JSON.parse(r));
} catch (r) {
}
for (const r of Object.values(L))
pe(r);
this.sessionData.sessionSecureID = t ? N(`${this.organizationID}-${t}`) : N(), this.sessionData.sessionKey = t, this.sessionData.sessionStartTime = Date.now(), this.options.sessionSecureID = this.sessionData.sessionSecureID, this.stopRecording(), this._firstLoadListeners = new I(this.options), yield this.initialize(), i && o && this.identify(i, o);
});
}
_initMembers(e) {
var o, r, l, d, c, g, h, m, f, R;
this.sessionShortcut = !1, this._recordingStartTime = 0, this._isOnLocalHost = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname === "", this.ready = !1, this.state = "NotRecording", this.manualStopped = !1, this.enableSegmentIntegration = !!e.enableSegmentIntegration, this.privacySetting = (o = e.privacySetting) != null ? o : "default", this.enableCanvasRecording = (r = e.enableCanvasRecording) != null ? r : !1, this.enablePerformanceRecording = (l = e.enablePerformanceRecording) != null ? l : !0, this.inlineImages = (d = e.inlineImages) != null ? d : this._isOnLocalHost, this.inlineVideos = (c = e.inlineVideos) != null ? c : this._isOnLocalHost, this.inlineStylesheet = (g = e.inlineStylesheet) != null ? g : this._isOnLocalHost, this.samplingStrategy = w({
canvasFactor: 0.5,
canvasMaxSnapshotDimension: 360,
canvasClearWebGLBuffer: !0,
dataUrlOptions: et()
}, (h = e.samplingStrategy) != null ? h : {
canvas: 2
}), this._backendUrl = (m = e == null ? void 0 : e.backendUrl) != null ? m : "https://pub.highlight.io", this._backendUrl[0] === "/" && (this._backendUrl = new URL(this._backendUrl, document.baseURI).href);
const t = new dt(`${this._backendUrl}`, {
headers: {}
});
this.graphqlSDK = tt(t, lt()), this.environment = (f = e.environment) != null ? f : "production", this.appVersion = e.appVersion, this.serviceName = (R = e.serviceName) != null ? R : "", typeof e.organizationID == "string" ? this.organizationID = e.organizationID : this.organizationID = e.organizationID.toString(), this.isRunningOnHighlight = this.organizationID === "1" || this.organizationID === "1jdkoe52", this.firstloadVersion = e.firstloadVersion || "unknown", this.sessionShortcut = e.sessionShortcut || !1, this._onToggleFeedbackFormVisibility = () => {
};
const k = e, { firstloadVersion: s } = k, i = x(k, ["firstloadVersion"]);
this._optionsInternal = i, this.listeners = [], this.events = [], this.hasSessionUnloaded = !1, this.hasPushedData = !1, window.Intercom && window.Intercom("onShow", () => {
window.Intercom("update", {
highlightSessionURL: this.getCurrentSessionURLWithTimestamp()
}), this.addProperties({ event: "Intercom onShow" });
}), this._eventBytesSinceSnapshot = 0, this._lastSnapshotTime = (/* @__PURE__ */ new Date()).getTime(), this._lastVisibilityChangeTime = (/* @__PURE__ */ new Date()).getTime();
}
identify(e, t = {}, s) {
if (!e || e === "") {
console.warn(
"Highlight's identify() call was passed an empty identifier.",
{ user_identifier: e, user_object: t }
);
return;
}
this.sessionData.userIdentifier = e.toString(), this.sessionData.userObject = t, j(
L.USER_IDENTIFIER,
e.toString()
), j(L.USER_OBJECT, JSON.stringify(t)), this._worker.postMessage({
message: {
type: C.Identify,
userIdentifier: e,
userObject: t,
source: s
}
});
for (const i of this._integrations)
i.identify(
this.sessionData.sessionSecureID,
e,
t,
s
);
}
log(e, t, s) {
this._firstLoadListeners.messages.push(
Rt(t, Ne, e, s)
);
}
pushCustomError(e, t) {
return this.consumeCustomError(new Error(e), void 0, t);
}
consumeCustomError(e, t, s) {
let i = {};
if (s)
try {
i = w(w({}, JSON.parse(s)), i);
} catch (o) {
}
return this.consumeError(e, {
message: t,
payload: i
});
}
consumeError(e, {
message: t,
payload: s,
source: i,
type: o
}) {
var c, g, h, m;
e.cause && (s = y(w({}, s), { "exception.cause": e.cause }));
let r = t ? t + ":" + e.message : e.message;
o === "React.ErrorBoundary" && (r = "ErrorBoundary: " + r);
const l = Dt(e), d = {
event: r,
type: o != null ? o : "custom",
url: window.location.href,
source: i != null ? i : "",
lineNumber: (c = l[0]) != null && c.lineNumber ? (g = l[0]) == null ? void 0 : g.lineNumber : 0,
columnNumber: (h = l[0]) != null && h.columnNumber ? (m = l[0]) == null ? void 0 : m.columnNumber : 0,
stackTrace: l,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
payload: JSON.stringify(s),
id: Oe()
};
this._firstLoadListeners.errors.push(d);
for (const f of this._integrations)
f.error(this.sessionData.sessionSecureID, d);
}
addProperties(e = {}, t) {
const s = w({}, e);
Object.entries(s).forEach(([i, o]) => {
try {
structuredClone(o);
} catch (r) {
delete s[i];
}
}), this._worker.postMessage({
message: {
type: C.Properties,
propertiesObject: s,
propertyType: t
}
});
for (const i of this._integrations)
i.track(this.sessionData.sessionSecureID, w({
sessionSecureID: this.sessionData.sessionSecureID,
propertyType: t
}, e));
}
initialize(e) {
return _(this, null, function* () {
var t, s, i, o, r, l, d, c, g, h, m, f, R, k, O, S, Z, ee, te, se, ie, ne, re, oe, ae;
if (navigator != null && navigator.webdriver && !window.Cypress || (t = navigator == null ? void 0 : navigator.userAgent) != null && t.includes("Googlebot") || (s = navigator == null ? void 0 : navigator.userAgent) != null && s.includes("AdsBot")) {
(i = this._firstLoadListeners) == null || i.stopListening();
return;
}
try {
if (e != null && e.forceNew) {
yield this._reset(e);
return;
}
if (e != null && e.sessionKey && (e == null ? void 0 : e.sessionKey) !== this.sessionData.sessionKey) {
yield this._reset(y(w({}, e), { forceNew: !0 }));
return;
}
const T = new Ct();
It(
{
backendUrl: (r = (o = this.options) == null ? void 0 : o.backendUrl) != null ? r : "https://pub.highlight.io",
otlpEndpoint: (d = (l = this.options) == null ? void 0 : l.otlpEndpoint) != null ? d : "https://otel.highlight.io",
projectId: (c = this.options) == null ? void 0 : c.organizationID,
sessionSecureId: (g = this.options) == null ? void 0 : g.sessionSecureID,
environment: (m = (h = this.options) == null ? void 0 : h.environment) != null ? m : "production",
networkRecordingOptions: typeof ((f = this.options) == null ? void 0 : f.networkRecording) == "object" ? this.options.networkRecording : void 0,
tracingOrigins: (R = this.options) == null ? void 0 : R.tracingOrigins,
serviceName: (O = (k = this.options) == null ? void 0 : k.serviceName) != null ? O : "highlight-browser",
instrumentations: (Z = (S = this.options) == null ? void 0 : S.otel) == null ? void 0 : Z.instrumentations,
getIntegrations: () => [...this._integrations],
productAnalyticsEvents: this._productAnalyticsEvents()
},
T
), this._gauges.clear(), this._counters.clear(), this._histograms.clear(), this._up_down_counters.clear(), this.logger.log(
"Initializing...",
e,
this.sessionData,
this.options
), this.sessionData = (ee = M(this.sessionData.sessionSecureID)) != null ? ee : this.sessionData, (te = this.sessionData) != null && te.sessionStartTime ? this._recordingStartTime = (se = this.sessionData) == null ? void 0 : se.sessionStartTime : (this._recordingStartTime = (/* @__PURE__ */ new Date()).getTime(), this.sessionData.sessionStartTime = this._recordingStartTime), J(""), U(this.sessionData);
let P = G(ye.CLIENT_ID);
P || (P = N(), j(ye.CLIENT_ID, P));
let H;
this.options.disableSessionRecording || this.options.disableNetworkRecording !== void 0 || typeof this.options.networkRecording == "boolean" ? H = !1 : H = ((ie = this.options.networkRecording) == null ? void 0 : ie.recordHeadersAndBody) || !1;
let ce = [];
if (typeof this.options.networkRecording == "object" && ((ne = this.options.networkRecording.destinationDomains) != null && ne.length) && (ce = this.options.networkRecording.destinationDomains), this._isCrossOriginIframe)
yield this._setupCrossOriginIframe();
else {
const v = yield this.graphqlSDK.initializeSession({
organization_verbose_id: this.organizationID,
enable_strict_privacy: this.privacySetting === "strict",
privacy_setting: this.privacySetting,
enable_recording_network_contents: H,
clientVersion: this.firstloadVersion,
firstloadVersion: this.firstloadVersion,
clientConfig: JSON.stringify(this._optionsInternal),
environment: this.environment,
id: P,
appVersion: this.appVersion,
serviceName: this.serviceName,
session_secure_id: this.sessionData.sessionSecureID,
session_key: this.sessionData.sessionKey,
client_id: P,
network_recording_domains: ce,
disable_session_recording: this.options.disableSessionRecording
});
if (v.initializeSession.secure_id !== this.sessionData.sessionSecureID && this.logger.log(
`Unexpected secure id returned by initializeSession: ${v.initializeSession.secure_id}, expected ${this.sessionData.sessionSecureID}`
), this.sessionData.sessionSecureID = v.initializeSession.secure_id, this.sessionData.projectID = parseInt(
((re = v == null ? void 0 : v.initializeSession) == null ? void 0 : re.project_id) || "0"
), T.setConfig(v.initializeSession.sampling), !this.sessionData.projectID || !this.sessionData.sessionSecureID) {
console.error(
"Failed to initialize Highlight; an error occurred on our end.",
this.sessionData
);
return;
}
}
this.logger.log(
`Loaded Highlight
Remote: ${this._backendUrl}
Project ID: ${this.sessionData.projectID}
SessionSecureID: ${this.sessionData.sessionSecureID}`
), this.options.sessionSecureID = this.sessionData.sessionSecureID, this._worker.postMessage({
message: {
type: C.Initialize,
sessionSecureID: this.sessionData.sessionSecureID,
backend: this._backendUrl,
debug: !!this.debugOptions.clientInteractions,
recordingStartTime: this._recordingStartTime
}
});
for (const v of this._integrations)
v.init(this.sessionData.sessionSecureID);
if (this.sessionData.userIdentifier && this.identify(
this.sessionData.userIdentifier,
this.sessionData.userObject
), this._firstLoadListeners.isListening() ? this._firstLoadListeners.hasNetworkRecording || I.setupNetworkListener(
this._firstLoadListeners,
this.options
) : this._firstLoadListeners.startListening(), this.pushPayloadTimerId && (clearTimeout(this.pushPayloadTimerId), this.pushPayloadTimerId = void 0), this._isCrossOriginIframe || (this.pushPayloadTimerId = setTimeout(() => {
this._save();
}, we)), this.options.disableSessionRecording) {
this.logger.log(
"Highlight is NOT RECORDING a session replay per H.init setting."
), this.ready = !0, this.state = "Recording", this.manualStopped = !1;
return;
}
const { getDeviceDetails: le } = Tt();
le && this.recordGauge({
name: E.DeviceMemory,
value: le().deviceMemory,
category: D.Device,
group: window.location.href
});
const de = (v, ze) => {
ze && this.logger.log("received isCheckout emit", { event: v }), this.events.push(v);
};
de.bind(this);
const He = !!this._recordStop;
this._recordStop && (this._recordStop(), this._recordStop = void 0);
const [Ue, We] = Ye(
this.privacySetting,
this.options.maskAllInputs,
this.options.maskInputOptions
);
this._recordStop = q({
ignoreClass: (oe = this.options.ignoreClass) != null ? oe : "highlight-ignore",
ignoreSelector: this.options.ignoreSelector,
blockClass: (ae = this.options.blockClass) != null ? ae : "highlight-block",
blockSelector: this.options.blockSelector,
emit: de,
recordCrossOriginIframes: this.options.recordCrossOriginIframe,
privacySetting: this.privacySetting,
maskAllInputs: Ue,
maskInputOptions: We,
maskTextClass: this.options.maskTextClass,
maskTextSelector: this.options.maskTextSelector,
recordCanvas: this.enableCanvasRecording,
sampling: {
canvas: {
fps: this.samplingStrategy.canvas,
fpsManual: this.samplingStrategy.canvasManualSnapshot,
resizeFactor: this.samplingStrategy.canvasFactor,
clearWebGLBuffer: this.samplingStrategy.canvasClearWebGLBuffer,
initialSnapshotDelay: this.samplingStrategy.canvasInitialSnapshotDelay,
dataURLOptions: this.samplingStrategy.dataUrlOptions,
maxSnapshotDimension: this.samplingStrategy.canvasMaxSnapshotDimension
}
},
keepIframeSrcFn: (v) => !this.options.recordCrossOriginIframe,
inlineImages: this.inlineImages,
inlineVideos: this.inlineVideos,
collectFonts: this.inlineImages,
inlineStylesheet: this.inlineStylesheet,
plugins: [Qe()],
logger: typeof this.options.debug == "boolean" && this.options.debug || typeof this.options.debug == "object" && this.options.debug.domRecording ? {
debug: this.logger.log,
warn: W
} : void 0
}), He || this.options.recordCrossOriginIframe && this._setupCrossOriginIframeParent(), document.referrer && (window && document.referrer.includes(window.location.origin) || (this.addCustomEvent("Referrer", document.referrer), this.addProperties(
{ referrer: document.referrer },
{ type: "session" }
))), this.sessionData.sessionKey && this.addProperties(
{
sessionKey: this.sessionData.sessionKey
},
{ type: "session" }
), this._setupWindowListeners(), this.ready = !0, this.state = "Recording", this.manualStopped = !1;
} catch (T) {
this._isOnLocalHost && (console.error(T), W("initializeSession", T));
}
});
}
_productAnalyticsEvents() {
var s;
const e = (s = this.options) == null ? void 0 : s.productAnalytics;
if (e === !1)
return {};
const t = {
clicks: !0,
pageViews: !0,
trackEvents: !0
};
if (e === void 0 || e === !0)
return t;
for (const i of Object.keys(e))
e[i] === !1 && (t[i] = !1);
return t;
}
_visibilityHandler(e) {
return _(this, null, function* () {
if (this.manualStopped) {
this.logger.log("Ignoring visibility event due to manual stop.");
return;
}
(/* @__PURE__ */ new Date()).getTime() - this._lastVisibilityChangeTime < st || (this._lastVisibilityChangeTime = (/* @__PURE__ */ new Date()).getTime(), this.logger.log(`Detected window ${e ? "hidden" : "visible"}.`), e ? (this.addCustomEvent("TabHidden", !0), this.options.disableBackgroundRecording && this.stopRecording()) : (this.options.disableBackgroundRecording && (yield this.initialize()), this.addCustomEvent("TabHidden", !1)));
});
}
_setupCrossOriginIframe() {
return _(this, null, function* () {
this.logger.log("highlight in cross-origin iframe is waiting "), yield new Promise((e) => {
const t = (s) => {
if (s.data.highlight === ue) {
const i = s.data;
this.logger.log("highlight got window message ", i), this.sessionData.projectID = i.projectID, this.sessionData.sessionSecureID = i.sessionSecureID, window.parent.postMessage(
{
highlight: me
},
"*"
), window.removeEventListener("message", t), e();
}
};
window.addEventListener("message", t);
});
});
}
_setupCrossOriginIframeParent() {
this.logger.log(
"highlight setting up cross origin iframe parent notification"
), setInterval(() => {
window.document.querySelectorAll("iframe").forEach((e) => {
var t;
(t = e.contentWindow) == null || t.postMessage(
{
highlight: ue,
projectID: this.sessionData.projectID,
sessionSecureID: this.sessionData.sessionSecureID
},
"*"
);
});
}, we), window.addEventListener("message", (e) => {
e.data.highlight === me && this.logger.log(
"highlight got response from initialized iframe"
);
});
}
_setupWindowListeners() {
var s;
try {
const i = this;
this.enableSegmentIntegration && this.listeners.push(
Ve((r) => {
if (r.type === "track") {
const l = {};
l["segment-event"] = r.event, i.addProperties(l, {
type: "track",
source: "segment"
});
} else if (r.type === "identify") {
const l = r.userId.replace(
/^"(.*)"$/,
"$1"
);
i.identify(
l,
r.traits,
"segment"
);
}
})
), this.listeners.push(
it((r) => {
this.reloaded ? (this.addCustomEvent("Reload", r), this.reloaded = !1, i.addProperties(
{ reload: !0 },
{ type: "session" }
)) : this.addCustomEvent("Navigate", r);
})
), this.listeners.push(
nt(
(r) => {
this.addCustomEvent("Viewport", r), this.submitViewportMetrics(r);
}
)
), this.listeners.push(
Ae((r, l) => {
let d = null, c = null;
if (l && l.target) {
const g = l.target;
d = ht(g), c = g.textContent, c && c.length > 2e3 && (c = c.substring(0, 2e3));
}
this.addCustomEvent("Click", {
clickTarget: r,
clickTextContent: c,
clickSelector: d
});
})
), this.listeners.push(
Ke((r) => {
r && this.addCustomEvent("Focus", r);
})
), this.listeners.push(
Lt((r) => {
const { name: l, value: d } = r, c = [], g = (h, m) => {
m && c.push({ name: h, value: m });
};
switch (r.name) {
case "LCP": {
const h = r.attribution;
g("web_vital.element", h.element), g(
"web_vital.attribution.url",
h.url ? Pt(h.url) : void 0
);
break;
}
case "CLS": {
const h = r.attribution;
g("web_vital.element", h.largestShiftTarget), g("web_vital.load_state", h.loadState);
break;
}
case "INP": {
const h = r.attribution;
g("web_vital.element", h.eventTarget), g("web_vital.event_type", h.eventType), g("web_vital.load_state", h.loadState);
break;
}
case "FID": {
const h = r.attribution;
g("web_vital.element", h.eventTarget), g("web_vital.event_type", h.eventType);
break;
}
case "FCP": {
const h = r.attribution;
g("web_vital.load_state", h.loadState);
break;
}
}
this.recordGauge({
name: l,
value: d,
group: window.location.href,
category: D.WebVital,
tags: c.length ? c : void 0
});
})
), this.listeners.push(
Et(
(r) => {
const l = [];
r.saveData !== void 0 && l.push({
name: "saveData",
value: r.saveData.toString()
}), r.effectiveType !== void 0 && l.push({
name: "effectiveType",
value: r.effectiveType.toString()
}), r.type !== void 0 && l.push({
name: "type",
value: r.type.toString()
}), Object.entries(r).forEach(([d, c]) => {
gt(c) && this.recordGauge({
name: d,
value: c,
category: D.Performance,
group: window.location.href,
tags: l
});
});
},
this._recordingStartTime
)
), this.sessionShortcut && xe(this.sessionShortcut, () => {
window.open(
this.getCurrentSessionURLWithTimestamp(),
"_blank"
);
}), this.enablePerformanceRecording && (this.listeners.push(
Nt((r) => {
this.addCustomEvent("Performance", V(r)), Object.entries(r).filter(([l]) => l !== "relativeTimestamp").forEach(
([l, d]) => d && this.recordGauge({
name: l,
value: d,
category: D.Performance,
group: window.location.href
})
);
}, this._recordingStartTime)
), this.listeners.push(
Ot((r) => {
this.addCustomEvent("Jank", V(r)), this.recordGauge({
name: "Jank",
value: r.jankAmount,
category: D.WebVital,
group: r.querySelector
});
}, this._recordingStartTime)
)), this._hasPreviouslyInitialized || ((s = window.electron) != null && s.ipcRenderer ? (window.electron.ipcRenderer.on(
"highlight.run",
({ visible: r }) => {
this._visibilityHandler(!r);
}
), this.logger.log("Set up Electron highlight.run events.")) : (qe(
(r) => this._visibilityHandler(r)
), this.logger.log("Set up document visibility listener.")), this._hasPreviouslyInitialized = !0);
const o = () => {
this.hasSessionUnloaded = !0, this.pushPayloadTimerId && (clearTimeout(this.pushPayloadTimerId), this.pushPayloadTimerId = void 0);
};
window.addEventListener("beforeunload", o), this.listeners.push(
() => window.removeEventListener("beforeunload", o)
);
} catch (i) {
this._isOnLocalHost && (console.error(i), W("initializeSession _setupWindowListeners", i));
}
const e = () => {
this.addCustomEvent("Page Unload", ""), J(this.sessionData.sessionSecureID), U(this.sessionData);
};
if (window.addEventListener("beforeunload", e), this.listeners.push(
() => window.removeEventListener("beforeunload", e)
), navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/iPhone/i)) {
const i = () => {
this.addCustomEvent("Page Unload", ""), J(this.sessionData.sessionSecureID), U(this.sessionData);
};
window.addEventListener("pagehide", i), this.listeners.push(
() => window.removeEventListener("beforeunload", i)
);
}
}
submitViewportMetrics({
height: e,
width: t,
availHeight: s,
availWidth: i
}) {
this.recordGauge({
name: E.ViewportHeight,
value: e,
category: D.Device,
group: window.location.href
}), this.recordGauge({
name: E.ViewportWidth,
value: t,
category: D.Device,
group: window.location.href
}), this.recordGauge({
name: E.ScreenHeight,
value: s,
category: D.Device,
group: window.location.href
}), this.recordGauge({
name: E.ScreenWidth,
value: i,
category: D.Device,
group: window.location.href
}), this.recordGauge({
name: E.ViewportArea,
value: e * t,
category: D.Device,
group: window.location.href
});
}
recordGauge(e) {
var s, i;
let t = this._gauges.get(e.name);
if (!t) {
if (t = (s = $()) == null ? void 0 : s.createGauge(e.name), !t) return;
this._gauges.set(e.name, t);
}
t.record(e.value, y(w({}, (i = e.tags) == null ? void 0 : i.reduce((o, r) => y(w({}, o), { [r.name]: r.value }), {})), {
group: e.group,
category: e.category,
"highlight.session_id": this.sessionData.sessionSecureID
}));
for (const o of this._integrations)
o.recordGauge(this.sessionData.sessionSecureID, e);
}
recordCount(e) {
var s, i;
let t = this._counters.get(e.name);
if (!t) {
if (t = (s = $()) == null ? void 0 : s.createCounter(e.name), !t) return;
this._counters.set(e.name, t);
}
t.add(e.value, y(w({}, (i = e.tags) == null ? void 0 : i.reduce((o, r) => y(w({}, o), { [r.name]: r.value }), {})), {
group: e.group,
category: e.category,
"highlight.session_id": this.sessionData.sessionSecureID
}));
}
recordIncr(e) {
this.recordCount(y(w({}, e), { value: 1 }));
}
recordHistogram(e) {
var s, i;
let t = this._histograms.get(e.name);
if (!t) {
if (t = (s = $()) == null ? void 0 : s.createHistogram(e.name), !t) return;
this._histograms.set(e.name, t);
}
t.record(e.value, y(w({}, (i = e.tags) == null ? void 0 : i.reduce((o, r) => y(w({}, o), { [r.name]: r.value }), {})), {
group: e.group,
category: e.category,
"highlight.session_id": this.sessionData.sessionSecureID
}));
}
recordUpDownCounter(e) {
var s, i;
let t = this._up_down_counters.get(e.name);
if (!t) {
if (t = (s = $()) == null ? void 0 : s.createUpDownCounter(e.name), !t) return;
this._up_down_counters.set(e.name, t);
}
t.add(e.value, y(w({}, (i = e.tags) == null ? void 0 : i.reduce((o, r) => y(w({}, o), { [r.name]: r.value }), {})), {
group: e.group,
category: e.category,
"highlight.session_id": this.sessionData.sessionSecureID
}));
}
/**
* Stops Highlight from recording.
* @param manual The end user requested to stop recording.
*/
stopRecording(e) {
this.manualStopped = !!e, this.manualStopped && this.addCustomEvent(
"Stop",
"H.stop() was called which stops Highlight from recording."
), this.state = "NotRecording", e && this._recordStop && (this._recordStop(), this._recordStop = void 0), this.listeners.forEach((t) => t()), this.listeners = [], Ee();
}
getCurrentSessionTimestamp() {
return this._recordingStartTime;
}
/**
* Returns the current timestamp for the current session.
*/
getCurrentSessionURLWithTimestamp() {
const e = (/* @__PURE__ */ new Date()).getTime(), { projectID: t, sessionSecureID: s } = this.sessionData, i = (e - this._recordingStartTime) / 1e3;
return `https://${Q}/${t}/sessions/${s}?ts=${i}`;
}
getCurrentSessionURL() {
const e = this.sessionData.projectID, t = this.sessionData.sessionSecureID;
return e && t ? `https://${Q}/${e}/sessions/${t}` : null;
}
snapshot(e) {
return _(this, null, function* () {
yield q.snapshotCanvas(e);
});
}
addSessionFeedback({
timestamp: e,
verbatim: t,
user_email: s,
user_name: i
}) {
var o;
this._worker.postMessage({
message: {
type: C.Feedback,
verbatim: t,
timestamp: e,
userName: i || this.sessionData.userIdentifier,
userEmail: s || ((o = this.sessionData.userObject) == null ? void 0 : o.name)
}
});
}
// Reset the events array and push to a backend.
_save() {
return _(this, null, function* () {
var e;
try {
this.state === "Recording" && this.listeners && !this.sessionData.sessionKey && this.sessionData.sessionStartTime && Date.now() - this.sessionData.sessionStartTime > rt && (this.logger.log("Resetting session", {
start: this.sessionData.sessionStartTime
}), yield this._reset({}));
let t;
((e = this.options) == null ? void 0 : e.sendMode) === "local" && (t = (s) => _(this, null, function* () {
let i = new Blob(
[
JSON.stringify({
query: at(ut),
variables: s
})
],
{
type: "application/json"
}
);
return yield window.fetch(`${this._backendUrl}`, {
method: "POST",
body: i
}), 0;
})), yield this._sendPayload({ sendFn: t }), this.hasPushedData = !0, this.sessionData.lastPushTime = Date.now(), U(this.sessionData);
} catch (t) {
this._isOnLocalHost && (console.error(t), W("_save", t));
}
this.state === "Recording" && (this.pushPayloadTimerId && (clearTimeout(this.pushPayloadTimerId), this.pushPayloadTimerId = void 0), this.pushPayloadTimerId = setTimeout(() => {
this._save();
}, ot));
});
}
/**
* This proxy should be used instead of rrweb's native addCustomEvent.
* The proxy makes sure recording has started before emitting a custom event.
*/
addCustomEvent(e, t) {
if (this.state === "NotRecording") {
let s;
const i = () => {
clearInterval(s), this.state === "Recording" && this.events.length > 0 ? fe(e, t) : s = setTimeout(i, 500);
};
s = setTimeout(i, 500);
} else this.state === "Recording" && (this.events.length > 0 || this.hasPushedData) && fe(e, t);
}
_sendPayload(t) {
return _(this, arguments, function* ({
sendFn: e
}) {
const s = I.getRecordedNetworkResources(
this._firstLoadListeners,
this._recordingStartTime
), i = I.getRecordedWebSocketEvents(
this._firstLoadListeners
), o = [...this.events], r = [...this._firstLoadListeners.messages], l = [...this._firstLoadListeners.errors], { bytes: d, time: c } = this.enableCanvasRecording ? Se.canvas : Se.normal;
this._eventBytesSinceSnapshot >= d && (/* @__PURE__ */ new Date()).getTime() - this._lastSnapshotTime >= c && this.takeFullSnapshot(), this.logger.log(
`Sending: ${o.length} events, ${r.length} messages, ${s.length} network resources, ${l.length} errors
To: ${this._backendUrl}
Org: ${this.organizationID}
SessionSecureID: ${this.sessionData.sessionSecureID}`
);
const g = (/* @__PURE__ */ new Date()).getTime(), h = Ge();
if (e) {
const m = {
session_secure_id: this.sessionData.sessionSecureID,
payload_id: g.toString(),
events: { events: o },
messages: V({ messages: r }),
resources: JSON.stringify({ resources: s }),
web_socket_events: JSON.stringify({ webSocketEvents: i }),
errors: l,
is_beacon: !1,
has_session_unloaded: this.hasSessionUnloaded,
highlight_logs: h || void 0
}, { compressedBase64: f } = yield je(m);
yield e({
session_secure_id: this.sessionData.sessionSecureID,
payload_id: (/* @__PURE__ */ new Date()).getTime().toString(),
data: f
});
} else
this._worker.postMessage({
message: {
type: C.AsyncEvents,
id: g,
events: o,
messages: r,
errors: l,
resourcesString: JSON.stringify({ resources: s }),
webSocketEventsString: JSON.stringify({
webSocketEvents: i
}),
hasSessionUnloaded: this.hasSessionUnloaded,
highlightLogs: h
}
});
U(this.sessionData), I.clearRecordedNetworkResources(
this._firstLoadListeners
), this.events = this.events.slice(o.length), this._firstLoadListeners.messages = this._firstLoadListeners.messages.slice(r.length), this._firstLoadListeners.errors = this._firstLoadListeners.errors.slice(
l.length
), Je(h);
});
}
takeFullSnapshot() {
if (!this._recordStop) {
this.logger.log("skipping full snapshot as rrweb is not running");
return;
}
this.logger.log("taking full snapshot", {
bytesSinceSnapshot: this._eventBytesSinceSnapshot,
lastSnapshotTime: this._lastSnapshotTime
}), q.takeFullSnapshot(), this._eventBytesSinceSnapshot = 0, this._lastSnapshotTime = (/* @__PURE__ */ new Date()).getTime();
}
registerLD(e) {
this._integrations.length || this._integrations.push(new Ce(e));
}
}
const Bt = () => {
var n, e;
typeof chrome != "undefined" && ((n = chrome == null ? void 0 : chrome.runtime) != null && n.onMessage) && ((e = chrome == null ? void 0 : chrome.runtime) == null || e.onMessage.addListener(
(t, s, i) => {
const o = t.action;
switch (console.log(