UNPKG

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
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(