"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
    return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LazyLoadedSessionRecording = exports.SESSION_RECORDING_BATCH_KEY = exports.RECORDING_BUFFER_TIMEOUT = exports.RECORDING_MAX_EVENT_SIZE = exports.RECORDING_REMOTE_CONFIG_TTL_MS = exports.RECORDING_IDLE_THRESHOLD_MS = exports.splitBuffer = exports.SEVEN_MEGABYTES = void 0;
var rrweb_types_1 = require("../types/rrweb-types");
var config_1 = require("./config");
var triggerMatching_1 = require("./triggerMatching");
var sessionrecording_utils_1 = require("./sessionrecording-utils");
var sessionrecording_utils_2 = require("./sessionrecording-utils");
Object.defineProperty(exports, "SEVEN_MEGABYTES", { enumerable: true, get: function () { return sessionrecording_utils_2.SEVEN_MEGABYTES; } });
Object.defineProperty(exports, "splitBuffer", { enumerable: true, get: function () { return sessionrecording_utils_2.splitBuffer; } });
var fflate_1 = require("fflate");
var globals_1 = require("../../../utils/globals");
var utils_1 = require("../../../utils");
var mutation_throttler_1 = require("./mutation-throttler");
var logger_1 = require("../../../utils/logger");
var core_1 = require("@posthog/core");
var constants_1 = require("../../../constants");
var request_utils_1 = require("../../../utils/request-utils");
var config_2 = __importDefault(require("../../../config"));
var flushed_size_tracker_1 = require("./flushed-size-tracker");
var recording_strategies_1 = require("./recording-strategies");
var BASE_ENDPOINT = '/s/';
var DEFAULT_CANVAS_QUALITY = 0.4;
var DEFAULT_CANVAS_FPS = 4;
var MAX_CANVAS_FPS = 12;
var MAX_CANVAS_QUALITY = 1;
var TWO_SECONDS = 2000;
var ONE_KB = 1024;
var ONE_MINUTE = 1000 * 60;
var FIVE_MINUTES = ONE_MINUTE * 5;
var ONE_HOUR = ONE_MINUTE * 60;
/**
 * Extracts the network_timing value from a capturePerformance config.
 * Returns `true`/`false` if explicitly set, or `undefined` if not specified.
 */
function networkTimingFromConfig(config) {
    return (0, core_1.isObject)(config) ? config.network_timing : config;
}
exports.RECORDING_IDLE_THRESHOLD_MS = FIVE_MINUTES;
exports.RECORDING_REMOTE_CONFIG_TTL_MS = ONE_HOUR;
exports.RECORDING_MAX_EVENT_SIZE = ONE_KB * ONE_KB * 0.9; // ~1mb (with some wiggle room)
exports.RECORDING_BUFFER_TIMEOUT = 2000; // 2 seconds
exports.SESSION_RECORDING_BATCH_KEY = 'recordings';
var LOGGER_PREFIX = '[SessionRecording]';
var logger = (0, logger_1.createLogger)(LOGGER_PREFIX);
var ACTIVE_SOURCES = [
    rrweb_types_1.IncrementalSource.MouseMove,
    rrweb_types_1.IncrementalSource.MouseInteraction,
    rrweb_types_1.IncrementalSource.Scroll,
    rrweb_types_1.IncrementalSource.ViewportResize,
    rrweb_types_1.IncrementalSource.Input,
    rrweb_types_1.IncrementalSource.TouchMove,
    rrweb_types_1.IncrementalSource.MediaInteraction,
    rrweb_types_1.IncrementalSource.Drag,
];
var newQueuedEvent = function (rrwebMethod) { return ({
    rrwebMethod: rrwebMethod,
    enqueuedAt: Date.now(),
    attempt: 1,
}); };
function getRRWeb() {
    var _a;
    return (_a = globals_1.assignableWindow === null || globals_1.assignableWindow === void 0 ? void 0 : globals_1.assignableWindow.__PosthogExtensions__) === null || _a === void 0 ? void 0 : _a.rrweb;
}
function getRRWebRecord() {
    var _a;
    return (_a = getRRWeb()) === null || _a === void 0 ? void 0 : _a.record;
}
function gzipStringToString(serializedData) {
    return (0, fflate_1.strFromU8)((0, fflate_1.gzipSync)((0, fflate_1.strToU8)(serializedData)), true);
}
function gzipToString(data) {
    return gzipStringToString(JSON.stringify(data));
}
function gzipToStringAsync(data) {
    return __awaiter(this, void 0, void 0, function () {
        var serializedData, compressed, _a, _b;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    serializedData = JSON.stringify(data);
                    return [4 /*yield*/, (0, core_1.gzipCompress)(serializedData, config_2.default.DEBUG, { rethrow: true })];
                case 1:
                    compressed = _c.sent();
                    _a = fflate_1.strFromU8;
                    _b = Uint8Array.bind;
                    return [4 /*yield*/, compressed.arrayBuffer()];
                case 2: return [2 /*return*/, _a.apply(void 0, [new (_b.apply(Uint8Array, [void 0, _c.sent()]))(), true])];
            }
        });
    });
}
var _gzippedEmptyArray;
var _gzippedEmptyArrayPromise;
var _isNativeAsyncSessionRecordingGzipSupported = typeof globalThis !== 'undefined' && (0, core_1.isGzipSupported)();
var _nativeAsyncSessionRecordingGzipDisabled = false;
function gzipField(data) {
    if ((0, core_1.isArray)(data) && data.length === 0) {
        _gzippedEmptyArray = _gzippedEmptyArray !== null && _gzippedEmptyArray !== void 0 ? _gzippedEmptyArray : gzipToString([]);
        return _gzippedEmptyArray;
    }
    return gzipToString(data);
}
function gzipFieldAsync(data) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            if ((0, core_1.isArray)(data) && data.length === 0) {
                if (_gzippedEmptyArray) {
                    return [2 /*return*/, _gzippedEmptyArray];
                }
                _gzippedEmptyArrayPromise =
                    _gzippedEmptyArrayPromise !== null && _gzippedEmptyArrayPromise !== void 0 ? _gzippedEmptyArrayPromise : gzipToStringAsync([])
                        .then(function (compressed) {
                        _gzippedEmptyArray = compressed;
                        return compressed;
                    })
                        .catch(function (error) {
                        _gzippedEmptyArray = undefined;
                        _gzippedEmptyArrayPromise = undefined;
                        throw error;
                    });
                return [2 /*return*/, _gzippedEmptyArrayPromise];
            }
            return [2 /*return*/, gzipToStringAsync(data)];
        });
    });
}
function shouldCompressEvent(event) {
    return (event.type === rrweb_types_1.EventType.FullSnapshot ||
        (event.type === rrweb_types_1.EventType.IncrementalSnapshot &&
            (event.data.source === rrweb_types_1.IncrementalSource.Mutation ||
                event.data.source === rrweb_types_1.IncrementalSource.StyleSheetRule)));
}
function shouldUseNativeAsyncSessionRecordingGzip(event) {
    return (_isNativeAsyncSessionRecordingGzipSupported &&
        !_nativeAsyncSessionRecordingGzipDisabled &&
        shouldCompressEvent(event));
}
function compressedResult(event) {
    return { event: event, size: (0, sessionrecording_utils_1.estimateCompressedEventSize)(event) };
}
function buildCompressedFullSnapshotEvent(event, data) {
    return __assign(__assign({}, event), { data: data, cv: '2024-10' });
}
function buildCompressedIncrementalEvent(event, fields) {
    return __assign(__assign({}, event), { cv: '2024-10', data: __assign(__assign({}, event.data), fields) });
}
/**
 * rrweb's packer takes an event and returns a string or the reverse on `unpack`.
 * but we want to be able to inspect metadata during ingestion.
 * and don't want to compress the entire event,
 * so we have a custom packer that only compresses part of some events
 *
 * returns the compressed event and its estimated JSON size,
 * avoiding a redundant JSON.stringify for size estimation
 */
function compressEventSync(event) {
    try {
        if (event.type === rrweb_types_1.EventType.FullSnapshot) {
            return compressedResult(buildCompressedFullSnapshotEvent(event, gzipToString(event.data)));
        }
        if (event.type === rrweb_types_1.EventType.IncrementalSnapshot && event.data.source === rrweb_types_1.IncrementalSource.Mutation) {
            return compressedResult(buildCompressedIncrementalEvent(event, {
                texts: gzipField(event.data.texts),
                attributes: gzipField(event.data.attributes),
                removes: gzipField(event.data.removes),
                adds: gzipField(event.data.adds),
            }));
        }
        if (event.type === rrweb_types_1.EventType.IncrementalSnapshot && event.data.source === rrweb_types_1.IncrementalSource.StyleSheetRule) {
            return compressedResult(buildCompressedIncrementalEvent(event, {
                adds: event.data.adds ? gzipToString(event.data.adds) : undefined,
                removes: event.data.removes ? gzipToString(event.data.removes) : undefined,
            }));
        }
    }
    catch (e) {
        logger.error('could not compress event - will use uncompressed event', e);
    }
    return { event: event, size: (0, sessionrecording_utils_1.estimateSize)(event) };
}
function compressEventAsync(event) {
    return __awaiter(this, void 0, void 0, function () {
        var _a, _b, _c, _d, texts, attributes, removes, adds, _e, adds, removes, e_1;
        return __generator(this, function (_f) {
            switch (_f.label) {
                case 0:
                    _f.trys.push([0, 7, , 8]);
                    if (!(event.type === rrweb_types_1.EventType.FullSnapshot)) return [3 /*break*/, 2];
                    _a = compressedResult;
                    _b = buildCompressedFullSnapshotEvent;
                    _c = [event];
                    return [4 /*yield*/, gzipToStringAsync(event.data)];
                case 1: return [2 /*return*/, _a.apply(void 0, [_b.apply(void 0, _c.concat([_f.sent()]))])];
                case 2:
                    if (!(event.type === rrweb_types_1.EventType.IncrementalSnapshot && event.data.source === rrweb_types_1.IncrementalSource.Mutation)) return [3 /*break*/, 4];
                    return [4 /*yield*/, Promise.all([
                            gzipFieldAsync(event.data.texts),
                            gzipFieldAsync(event.data.attributes),
                            gzipFieldAsync(event.data.removes),
                            gzipFieldAsync(event.data.adds),
                        ])];
                case 3:
                    _d = __read.apply(void 0, [_f.sent(), 4]), texts = _d[0], attributes = _d[1], removes = _d[2], adds = _d[3];
                    return [2 /*return*/, compressedResult(buildCompressedIncrementalEvent(event, { texts: texts, attributes: attributes, removes: removes, adds: adds }))];
                case 4:
                    if (!(event.type === rrweb_types_1.EventType.IncrementalSnapshot && event.data.source === rrweb_types_1.IncrementalSource.StyleSheetRule)) return [3 /*break*/, 6];
                    return [4 /*yield*/, Promise.all([
                            event.data.adds ? gzipToStringAsync(event.data.adds) : undefined,
                            event.data.removes ? gzipToStringAsync(event.data.removes) : undefined,
                        ])];
                case 5:
                    _e = __read.apply(void 0, [_f.sent(), 2]), adds = _e[0], removes = _e[1];
                    return [2 /*return*/, compressedResult(buildCompressedIncrementalEvent(event, { adds: adds, removes: removes }))];
                case 6: return [3 /*break*/, 8];
                case 7:
                    e_1 = _f.sent();
                    if ((0, core_1.isNativeAsyncGzipError)(e_1)) {
                        _nativeAsyncSessionRecordingGzipDisabled = true;
                    }
                    logger.error('could not compress event asynchronously - trying synchronous compression', e_1);
                    return [2 /*return*/, compressEventSync(event)];
                case 8: return [2 /*return*/, { event: event, size: (0, sessionrecording_utils_1.estimateSize)(event) }];
            }
        });
    });
}
function isCustomEvent(e, tag) {
    return e.type === rrweb_types_1.EventType.Custom && e.data.tag === tag;
}
function isSessionIdleEvent(e) {
    return isCustomEvent(e, 'sessionIdle');
}
function isSessionEndingEvent(e) {
    return isCustomEvent(e, '$session_ending');
}
function getSessionEndingPayload(e) {
    return isSessionEndingEvent(e) ? e.data.payload : null;
}
function isSessionStartingEvent(e) {
    return isCustomEvent(e, '$session_starting');
}
function getSessionStartingPayload(e) {
    return isSessionStartingEvent(e) ? e.data.payload : null;
}
function isAllowedWhenIdle(e) {
    return isSessionIdleEvent(e) || isSessionEndingEvent(e) || isSessionStartingEvent(e);
}
/** When we put the recording into a paused state, we add a custom event.
 *  However, in the paused state, events are dropped and never make it to the buffer,
 *  so we need to manually let this one through */
function isRecordingPausedEvent(e) {
    return e.type === rrweb_types_1.EventType.Custom && e.data.tag === 'recording paused';
}
var LazyLoadedSessionRecording = /** @class */ (function () {
    function LazyLoadedSessionRecording(_instance) {
        var _this = this;
        this._instance = _instance;
        this._endpoint = BASE_ENDPOINT;
        /**
         * Util to help developers working on this feature manually override
         */
        this._forceAllowLocalhostNetworkCapture = false;
        this._stopRrweb = undefined;
        this._lastActivityTimestamp = Date.now();
        this._isActivatingTrigger = false;
        /**
         * and a queue - that contains rrweb events that we want to send to rrweb, but rrweb wasn't able to accept them yet
         */
        this._queuedRRWebEvents = [];
        this._isIdle = 'unknown';
        this._rrwebError = false;
        this._rrwebStartAttempted = false;
        this._maxDepthExceeded = false;
        this._fullSnapshotTimestamps = [];
        this._pendingCompressionEvents = [];
        this._queuedCompressionEvents = 0;
        this._compressionQueueGeneration = 0;
        this._isStoppingAfterCompression = false;
        this._removePageViewCaptureHook = undefined;
        this._removeEventTriggerCaptureHook = undefined;
        this._onSessionIdListener = undefined;
        this._onSessionIdleResetForcedListener = undefined;
        this._forceIdleSessionIdListener = undefined;
        this._onSessionIdCallback = function (sessionId, windowId, changeReason) {
            var _a, _b, _c, _d, _e;
            if (!changeReason)
                return;
            // Skip if session hasn't actually changed (callback might fire multiple times)
            if (sessionId === _this._sessionId && windowId === _this._windowId) {
                return;
            }
            var wasLikelyReset = changeReason.noSessionId;
            var shouldLinkSessions = !wasLikelyReset && (changeReason.activityTimeout || changeReason.sessionPastMaximumLength);
            // Capture old IDs before start() updates them
            var oldSessionId = _this._sessionId;
            var oldWindowId = _this._windowId;
            if (shouldLinkSessions) {
                _this._tryAddCustomEvent('$session_ending', {
                    currentSessionId: oldSessionId,
                    currentWindowId: oldWindowId,
                    nextSessionId: sessionId,
                    nextWindowId: windowId,
                    changeReason: changeReason,
                    // we'll need to correct the time of this if it's captured when idle
                    // so we don't extend reported session time with a debug event
                    lastActivityTimestamp: _this._lastActivityTimestamp,
                    flushed_size: (_a = _this._flushedSizeTracker) === null || _a === void 0 ? void 0 : _a.currentTrackedSize,
                });
            }
            // reset flushed size tracker after capturing the ending event
            if (_this._flushedSizeTracker) {
                _this._flushedSizeTracker.reset();
            }
            // Reset first full snapshot timestamp for the new session
            (_b = _this._instance.persistence) === null || _b === void 0 ? void 0 : _b.unregister(constants_1.SESSION_RECORDING_FIRST_FULL_SNAPSHOT_TIMESTAMP);
            _this._maxDepthExceeded = false;
            (_d = (_c = getRRWeb()) === null || _c === void 0 ? void 0 : _c.resetMaxDepthState) === null || _d === void 0 ? void 0 : _d.call(_c);
            _this._tryAddCustomEvent('$session_id_change', { sessionId: sessionId, windowId: windowId, changeReason: changeReason });
            _this._clearConditionalRecordingPersistence();
            // When rrweb isn't running _updateWindowAndSessionIds can't drive the restart,
            // so we restart here. Otherwise it handles the restart after this callback returns.
            if (_this._isIdle === true || !_this.isStarted) {
                _this._isIdle = 'unknown';
                _this.stop();
                _this.start('session_id_changed');
            }
            if (shouldLinkSessions) {
                _this._tryAddCustomEvent('$session_starting', {
                    previousSessionId: oldSessionId,
                    previousWindowId: oldWindowId,
                    nextSessionId: sessionId,
                    nextWindowId: windowId,
                    changeReason: changeReason,
                    // we'll need to correct the time of this if it's captured when idle
                    // so we don't extend reported session time with a debug event
                    lastActivityTimestamp: _this._lastActivityTimestamp,
                });
            }
            // always re-decide for the new session — guarding on the persisted config here would skip
            // V2 trigger groups and post-reset() sessions, leaving them undecided (= ACTIVE, unsampled)
            (_e = _this._strategy) === null || _e === void 0 ? void 0 : _e.makeSamplingDecisions(sessionId);
        };
        this._hasPassedMinimumDuration = function () {
            var _a;
            var persistedSessionId = (_a = _this._instance.persistence) === null || _a === void 0 ? void 0 : _a.props[constants_1.SESSION_RECORDING_PAST_MINIMUM_DURATION];
            return persistedSessionId === _this._sessionId;
        };
        this._getBufferDuration = function () {
            var _a, _b;
            if (_this._buffer.data.length === 0) {
                return null;
            }
            var firstTimestamp = (_a = _this._buffer.data[0]) === null || _a === void 0 ? void 0 : _a.timestamp;
            var lastTimestamp = (_b = _this._buffer.data[_this._buffer.data.length - 1]) === null || _b === void 0 ? void 0 : _b.timestamp;
            if (!(0, core_1.isNumber)(firstTimestamp) || !(0, core_1.isNumber)(lastTimestamp)) {
                return null;
            }
            return lastTimestamp - firstTimestamp;
        };
        this._isBelowMinimumDuration = function () {
            var _a;
            var _b, _c, _d;
            var minimumDuration = _this._minimumDuration;
            if (!(0, core_1.isNumber)(minimumDuration)) {
                return false;
            }
            var strictMode = (_c = (_b = _this._instance.config.session_recording) === null || _b === void 0 ? void 0 : _b.strictMinimumDuration) !== null && _c !== void 0 ? _c : false;
            if (!strictMode) {
                var sessionDuration = _this._sessionDuration;
                var isPositiveSessionDuration = (0, core_1.isNumber)(sessionDuration) && sessionDuration >= 0;
                return isPositiveSessionDuration && sessionDuration < minimumDuration;
            }
            if (_this._hasPassedMinimumDuration()) {
                return false;
            }
            var bufferDuration = _this._getBufferDuration();
            if ((0, core_1.isNull)(bufferDuration)) {
                return true;
            }
            if (bufferDuration >= minimumDuration) {
                (_d = _this._instance.persistence) === null || _d === void 0 ? void 0 : _d.register((_a = {},
                    _a[constants_1.SESSION_RECORDING_PAST_MINIMUM_DURATION] = _this._sessionId,
                    _a));
                return false;
            }
            return true;
        };
        this._onBeforeUnload = function () {
            // If still buffering (waiting for triggers), discard the buffer
            if (_this.status === triggerMatching_1.BUFFERING) {
                _this._clearBuffer();
                return;
            }
            // beforeunload cannot wait for async CompressionStream work. Synchronously
            // compress any queued events so sendBeacon can include them in this flush.
            _this._drainCompressionQueueSync();
            _this._flushBuffer();
        };
        this._onOffline = function () {
            _this._tryAddCustomEvent('browser offline', {});
        };
        this._onOnline = function () {
            _this._tryAddCustomEvent('browser online', {});
        };
        this._onVisibilityChange = function () {
            if (globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.visibilityState) {
                var label = 'window ' + globals_1.document.visibilityState;
                _this._tryAddCustomEvent(label, {});
            }
        };
        // we know there's a sessionManager, so don't need to start without a session id
        var _a = this._sessionManager.checkAndGetSessionAndWindowId(), sessionId = _a.sessionId, windowId = _a.windowId;
        this._sessionId = sessionId;
        this._windowId = windowId;
        this._linkedFlagMatching = new triggerMatching_1.LinkedFlagMatching(this._instance);
        this._urlTriggerMatching = new triggerMatching_1.URLTriggerMatching(this._instance);
        this._eventTriggerMatching = new triggerMatching_1.EventTriggerMatching(this._instance);
        this._buffer = this._clearBuffer();
        if (this._sessionIdleThresholdMilliseconds >= this._sessionManager.sessionTimeoutMs) {
            logger.warn("session_idle_threshold_ms (".concat(this._sessionIdleThresholdMilliseconds, ") is greater than the session timeout (").concat(this._sessionManager.sessionTimeoutMs, "). Session will never be detected as idle"));
        }
        this._flushedSizeTracker = new flushed_size_tracker_1.FlushedSizeTracker(this._instance);
    }
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "sessionId", {
        get: function () {
            return this._sessionId;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "_sessionManager", {
        get: function () {
            if (!this._instance.sessionManager) {
                throw new Error(LOGGER_PREFIX + ' must be started with a valid sessionManager.');
            }
            return this._instance.sessionManager;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "_sessionIdleThresholdMilliseconds", {
        get: function () {
            return this._instance.config.session_recording.session_idle_threshold_ms || exports.RECORDING_IDLE_THRESHOLD_MS;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "_isSampled", {
        get: function () {
            var currentValue = this._instance.get_property(constants_1.SESSION_RECORDING_IS_SAMPLED);
            // anything but this session's own tagged decision decodes to null, forcing a fresh decision
            return (0, recording_strategies_1.decodeSamplingDecision)(currentValue, this.sessionId);
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "_sampleRate", {
        get: function () {
            var _a;
            var rate = (_a = this._remoteConfig) === null || _a === void 0 ? void 0 : _a.sampleRate;
            return (0, core_1.isNumber)(rate) ? rate : null;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "_minimumDuration", {
        get: function () {
            var _a, _b;
            return (_b = (_a = this._strategy) === null || _a === void 0 ? void 0 : _a.getMinimumDuration(this.sessionId)) !== null && _b !== void 0 ? _b : null;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "_masking", {
        get: function () {
            var _a, _b, _c, _d, _e, _f, _g;
            var masking_server_side = (_a = this._remoteConfig) === null || _a === void 0 ? void 0 : _a.masking;
            var masking_client_side = {
                maskAllInputs: (_b = this._instance.config.session_recording) === null || _b === void 0 ? void 0 : _b.maskAllInputs,
                maskTextSelector: (_c = this._instance.config.session_recording) === null || _c === void 0 ? void 0 : _c.maskTextSelector,
                blockSelector: (_d = this._instance.config.session_recording) === null || _d === void 0 ? void 0 : _d.blockSelector,
            };
            var maskAllInputs = (_e = masking_client_side === null || masking_client_side === void 0 ? void 0 : masking_client_side.maskAllInputs) !== null && _e !== void 0 ? _e : masking_server_side === null || masking_server_side === void 0 ? void 0 : masking_server_side.maskAllInputs;
            var maskTextSelector = (_f = masking_client_side === null || masking_client_side === void 0 ? void 0 : masking_client_side.maskTextSelector) !== null && _f !== void 0 ? _f : masking_server_side === null || masking_server_side === void 0 ? void 0 : masking_server_side.maskTextSelector;
            var blockSelector = (_g = masking_client_side === null || masking_client_side === void 0 ? void 0 : masking_client_side.blockSelector) !== null && _g !== void 0 ? _g : masking_server_side === null || masking_server_side === void 0 ? void 0 : masking_server_side.blockSelector;
            return !(0, core_1.isUndefined)(maskAllInputs) || !(0, core_1.isUndefined)(maskTextSelector) || !(0, core_1.isUndefined)(blockSelector)
                ? {
                    maskAllInputs: maskAllInputs !== null && maskAllInputs !== void 0 ? maskAllInputs : true,
                    maskTextSelector: maskTextSelector,
                    blockSelector: blockSelector,
                }
                : undefined;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "_canvasRecording", {
        get: function () {
            var _a, _b, _c, _d, _e, _f, _g;
            var canvasRecording_client_side = this._instance.config.session_recording.captureCanvas;
            var canvasRecording_server_side = (_a = this._remoteConfig) === null || _a === void 0 ? void 0 : _a.canvasRecording;
            var enabled = (_c = (_b = canvasRecording_client_side === null || canvasRecording_client_side === void 0 ? void 0 : canvasRecording_client_side.recordCanvas) !== null && _b !== void 0 ? _b : canvasRecording_server_side === null || canvasRecording_server_side === void 0 ? void 0 : canvasRecording_server_side.enabled) !== null && _c !== void 0 ? _c : false;
            var fps = (_e = (_d = canvasRecording_client_side === null || canvasRecording_client_side === void 0 ? void 0 : canvasRecording_client_side.canvasFps) !== null && _d !== void 0 ? _d : canvasRecording_server_side === null || canvasRecording_server_side === void 0 ? void 0 : canvasRecording_server_side.fps) !== null && _e !== void 0 ? _e : DEFAULT_CANVAS_FPS;
            var quality = (_g = (_f = canvasRecording_client_side === null || canvasRecording_client_side === void 0 ? void 0 : canvasRecording_client_side.canvasQuality) !== null && _f !== void 0 ? _f : canvasRecording_server_side === null || canvasRecording_server_side === void 0 ? void 0 : canvasRecording_server_side.quality) !== null && _g !== void 0 ? _g : DEFAULT_CANVAS_QUALITY;
            if (typeof quality === 'string') {
                var parsed = parseFloat(quality);
                quality = isNaN(parsed) ? 0.4 : parsed;
            }
            return {
                enabled: enabled,
                fps: (0, core_1.clampToRange)(fps, 0, MAX_CANVAS_FPS, (0, logger_1.createLogger)('canvas recording fps'), DEFAULT_CANVAS_FPS),
                quality: (0, core_1.clampToRange)(quality, 0, MAX_CANVAS_QUALITY, (0, logger_1.createLogger)('canvas recording quality'), DEFAULT_CANVAS_QUALITY),
            };
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "_isConsoleLogCaptureEnabled", {
        get: function () {
            var _a;
            var enabled_server_side = !!((_a = this._remoteConfig) === null || _a === void 0 ? void 0 : _a.consoleLogRecordingEnabled);
            var enabled_client_side = this._instance.config.enable_recording_console_log;
            return enabled_client_side !== null && enabled_client_side !== void 0 ? enabled_client_side : enabled_server_side;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "_networkPayloadCapture", {
        // network payload capture config has three parts
        // each can be configured server side or client side
        get: function () {
            var _a, _b, _c;
            var networkPayloadCapture_server_side = (_a = this._remoteConfig) === null || _a === void 0 ? void 0 : _a.networkPayloadCapture;
            var networkPayloadCapture_client_side = {
                recordHeaders: (_b = this._instance.config.session_recording) === null || _b === void 0 ? void 0 : _b.recordHeaders,
                recordBody: (_c = this._instance.config.session_recording) === null || _c === void 0 ? void 0 : _c.recordBody,
            };
            var headersEnabled = (networkPayloadCapture_client_side === null || networkPayloadCapture_client_side === void 0 ? void 0 : networkPayloadCapture_client_side.recordHeaders) || (networkPayloadCapture_server_side === null || networkPayloadCapture_server_side === void 0 ? void 0 : networkPayloadCapture_server_side.recordHeaders);
            var bodyEnabled = (networkPayloadCapture_client_side === null || networkPayloadCapture_client_side === void 0 ? void 0 : networkPayloadCapture_client_side.recordBody) || (networkPayloadCapture_server_side === null || networkPayloadCapture_server_side === void 0 ? void 0 : networkPayloadCapture_server_side.recordBody);
            var clientNetworkTiming = networkTimingFromConfig(this._instance.config.capture_performance);
            var serverNetworkTiming = networkTimingFromConfig(networkPayloadCapture_server_side === null || networkPayloadCapture_server_side === void 0 ? void 0 : networkPayloadCapture_server_side.capturePerformance);
            var networkTimingEnabled = !!((0, core_1.isBoolean)(clientNetworkTiming) ? clientNetworkTiming : serverNetworkTiming);
            return headersEnabled || bodyEnabled || networkTimingEnabled
                ? { recordHeaders: headersEnabled, recordBody: bodyEnabled, recordPerformance: networkTimingEnabled }
                : undefined;
        },
        enumerable: false,
        configurable: true
    });
    LazyLoadedSessionRecording.prototype._gatherRRWebPlugins = function () {
        var _a, _b, _c, _d;
        var plugins = [];
        var recordConsolePlugin = (_b = (_a = globals_1.assignableWindow.__PosthogExtensions__) === null || _a === void 0 ? void 0 : _a.rrwebPlugins) === null || _b === void 0 ? void 0 : _b.getRecordConsolePlugin;
        if (recordConsolePlugin && this._isConsoleLogCaptureEnabled) {
            plugins.push(recordConsolePlugin());
        }
        var networkPlugin = (_d = (_c = globals_1.assignableWindow.__PosthogExtensions__) === null || _c === void 0 ? void 0 : _c.rrwebPlugins) === null || _d === void 0 ? void 0 : _d.getRecordNetworkPlugin;
        if (!!this._networkPayloadCapture && (0, core_1.isFunction)(networkPlugin)) {
            var canRecordNetwork = !(0, request_utils_1.isLocalhost)() || this._forceAllowLocalhostNetworkCapture;
            if (canRecordNetwork) {
                plugins.push(networkPlugin((0, config_1.buildNetworkRequestOptions)(this._instance.config, this._networkPayloadCapture)));
            }
            else {
                logger.info('NetworkCapture not started because we are on localhost.');
            }
        }
        return plugins;
    };
    LazyLoadedSessionRecording.prototype._maskUrl = function (url) {
        var _a;
        var userSessionRecordingOptions = this._instance.config.session_recording;
        // userSessionRecordingOptions.maskNetworkRequestFn is deprecated, fallback to it
        if (userSessionRecordingOptions.maskCapturedNetworkRequestFn) {
            var result = userSessionRecordingOptions.maskCapturedNetworkRequestFn({
                name: url,
            });
            // CapturedNetworkRequest uses 'name' for URL, but also check 'url' for compatibility
            return (_a = result === null || result === void 0 ? void 0 : result.name) !== null && _a !== void 0 ? _a : result === null || result === void 0 ? void 0 : result.url;
        }
        if (userSessionRecordingOptions.maskNetworkRequestFn) {
            var result = userSessionRecordingOptions.maskNetworkRequestFn({
                url: url,
            });
            return result === null || result === void 0 ? void 0 : result.url;
        }
        return url;
    };
    LazyLoadedSessionRecording.prototype._tryRRWebMethod = function (queuedRRWebEvent) {
        try {
            queuedRRWebEvent.rrwebMethod();
            return true;
        }
        catch (e) {
            // Sometimes a race can occur where the recorder is not fully started yet
            if (this._queuedRRWebEvents.length < 10) {
                this._queuedRRWebEvents.push({
                    enqueuedAt: queuedRRWebEvent.enqueuedAt || Date.now(),
                    attempt: queuedRRWebEvent.attempt + 1,
                    rrwebMethod: queuedRRWebEvent.rrwebMethod,
                });
            }
            else {
                logger.warn('could not emit queued rrweb event.', e, queuedRRWebEvent);
            }
            return false;
        }
    };
    LazyLoadedSessionRecording.prototype._tryAddCustomEvent = function (tag, payload) {
        return this._tryRRWebMethod(newQueuedEvent(function () { return getRRWebRecord().addCustomEvent(tag, payload); }));
    };
    LazyLoadedSessionRecording.prototype._pageViewFallBack = function () {
        try {
            if (this._instance.config.capture_pageview || !globals_1.window) {
                return;
            }
            // Strip hash parameters from URL since they often aren't helpful
            // Use URL constructor for proper parsing to handle edge cases
            // recording doesn't run in IE11, so we don't need compat here
            // eslint-disable-next-line compat/compat
            var url = new URL(globals_1.window.location.href);
            var hrefWithoutHash = url.origin + url.pathname + url.search;
            var currentUrl = this._maskUrl(hrefWithoutHash);
            if (this._lastHref !== currentUrl) {
                this._lastHref = currentUrl;
                this._tryAddCustomEvent('$url_changed', { href: currentUrl });
            }
        }
        catch (_a) {
            // If URL processing fails, don't capture anything
        }
    };
    LazyLoadedSessionRecording.prototype._processQueuedEvents = function () {
        var _this = this;
        if (this._queuedRRWebEvents.length) {
            // if rrweb isn't ready to accept events earlier, then we queued them up.
            // now that `emit` has been called rrweb should be ready to accept them.
            // so, before we process this event, we try our queued events _once_ each
            // we don't want to risk queuing more things and never exiting this loop!
            // if they fail here, they'll be pushed into a new queue
            // and tried on the next loop.
            // there is a risk of this queue growing in an uncontrolled manner.
            // so its length is limited elsewhere
            // for now this is to help us ensure we can capture events that happen
            // and try to identify more about when it is failing
            var itemsToProcess = __spreadArray([], __read(this._queuedRRWebEvents), false);
            this._queuedRRWebEvents = [];
            itemsToProcess.forEach(function (queuedRRWebEvent) {
                if (Date.now() - queuedRRWebEvent.enqueuedAt <= TWO_SECONDS) {
                    _this._tryRRWebMethod(queuedRRWebEvent);
                }
            });
        }
    };
    LazyLoadedSessionRecording.prototype._tryTakeFullSnapshot = function () {
        return this._tryRRWebMethod(newQueuedEvent(function () { return getRRWebRecord().takeFullSnapshot(); }));
    };
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "_fullSnapshotIntervalMillis", {
        get: function () {
            var _a, _b, _c;
            if (((_a = this._strategy) === null || _a === void 0 ? void 0 : _a.hasPendingTriggers(this.sessionId)) && !['sampled', 'active'].includes(this.status)) {
                return ONE_MINUTE;
            }
            return (_c = (_b = this._instance.config.session_recording) === null || _b === void 0 ? void 0 : _b.full_snapshot_interval_millis) !== null && _c !== void 0 ? _c : FIVE_MINUTES;
        },
        enumerable: false,
        configurable: true
    });
    LazyLoadedSessionRecording.prototype._scheduleFullSnapshot = function () {
        var _this = this;
        if (this._fullSnapshotTimer) {
            clearInterval(this._fullSnapshotTimer);
        }
        // we don't schedule snapshots while idle
        if (this._isIdle === true) {
            return;
        }
        var interval = this._fullSnapshotIntervalMillis;
        if (!interval) {
            return;
        }
        this._fullSnapshotTimer = setInterval(function () {
            _this._tryTakeFullSnapshot();
        }, interval);
    };
    LazyLoadedSessionRecording.prototype._pauseRecording = function () {
        // we check _urlBlocked not status, since more than one thing can affect status
        if (this._urlTriggerMatching.urlBlocked) {
            return;
        }
        // we can't flush the buffer here since someone might be starting on a blocked page.
        // and we need to be sure that we don't record that page,
        // so we might not get the below custom event, but events will report the paused status.
        // which will allow debugging of sessions that start on blocked pages
        this._urlTriggerMatching.urlBlocked = true;
        // Clear the snapshot timer since we don't want new snapshots while paused
        clearInterval(this._fullSnapshotTimer);
        logger.info('recording paused due to URL blocker');
        this._tryAddCustomEvent('recording paused', { reason: 'url blocker' });
    };
    LazyLoadedSessionRecording.prototype._resumeRecording = function () {
        // we check _urlBlocked not status, since more than one thing can affect status
        if (!this._urlTriggerMatching.urlBlocked) {
            return;
        }
        this._urlTriggerMatching.urlBlocked = false;
        this._tryTakeFullSnapshot();
        this._scheduleFullSnapshot();
        this._tryAddCustomEvent('recording resumed', { reason: 'left blocked url' });
        logger.info('recording resumed');
    };
    LazyLoadedSessionRecording.prototype._activateTrigger = function (triggerType, matchDetail) {
        var _a, _b;
        var _c, _d, _e;
        // V1 only: V2 uses per-group activation and never calls this method
        // Prevent re-entry: if we're already activating a trigger, skip to avoid infinite recursion
        // This can happen when _reportStarted emits custom events that match the trigger condition
        if (this._isActivatingTrigger) {
            return;
        }
        if (!((_c = this._strategy) === null || _c === void 0 ? void 0 : _c.hasPendingTriggers(this.sessionId))) {
            return;
        }
        this._isActivatingTrigger = true;
        try {
            // V1: Write trigger activation to persistence
            // (V2 handles this per-group via TriggerGroupMatching.activateTrigger)
            var persistenceKey = triggerType === 'url'
                ? constants_1.SESSION_RECORDING_URL_TRIGGER_ACTIVATED_SESSION
                : constants_1.SESSION_RECORDING_EVENT_TRIGGER_ACTIVATED_SESSION;
            (_d = this._instance.persistence) === null || _d === void 0 ? void 0 : _d.register((_a = {},
                _a[persistenceKey] = this.sessionId,
                _a));
            (_e = this._strategy) === null || _e === void 0 ? void 0 : _e.updateActiveTriggers(this.sessionId);
            this._flushBuffer();
            this._reportStarted((triggerType + '_trigger_matched'), (_b = {},
                _b[triggerType === 'url' ? 'matchedUrl' : 'matchedEvent'] = matchDetail,
                _b));
        }
        finally {
            this._isActivatingTrigger = false;
        }
    };
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "isStarted", {
        get: function () {
            return !!this._stopRrweb;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "_remoteConfig", {
        get: function () {
            var _a, _b;
            var persistedConfig = this._instance.get_property(constants_1.SESSION_RECORDING_REMOTE_CONFIG);
            if (!persistedConfig) {
                return undefined;
            }
            var parsedConfig;
            try {
                parsedConfig = (0, core_1.isObject)(persistedConfig) ? persistedConfig : JSON.parse(persistedConfig);
            }
            catch (e) {
                // Do not unregister here: the SDK only registers structured configs, and this read path should
                // ignore corrupt legacy/external values without mutating persistence.
                logger.warn('persisted remote config for session recording is invalid and will be ignored', e);
                return undefined;
            }
            // Only check TTL if recording hasn't started yet
            // Once started, trust the config until a hard page load
            if (!this.isStarted) {
                // default to now so that configs persisted by older SDK versions
                // (which never set cache_timestamp) are treated as fresh
                var cacheTimestamp = (_a = parsedConfig.cache_timestamp) !== null && _a !== void 0 ? _a : Date.now();
                if (Date.now() - cacheTimestamp > exports.RECORDING_REMOTE_CONFIG_TTL_MS) {
                    logger.info('persisted remote config for session recording is stale and will be ignored', {
                        cacheTimestamp: cacheTimestamp,
                        persistedConfig: persistedConfig,
                    });
                    (_b = this._instance.persistence) === null || _b === void 0 ? void 0 : _b.unregister(constants_1.SESSION_RECORDING_REMOTE_CONFIG);
                    return undefined;
                }
            }
            return parsedConfig;
        },
        enumerable: false,
        configurable: true
    });
    LazyLoadedSessionRecording.prototype._checkOverride = function (key, overrideFunction, clearOverride) {
        var overrideFlag = this._instance.get_property(key);
        if (overrideFlag) {
            overrideFunction();
            // Clean up the override flag after applying it
            clearOverride();
        }
    };
    LazyLoadedSessionRecording.prototype.start = function (startReason) {
        var _this = this;
        var _a, _b;
        var config = this._remoteConfig;
        if (!config) {
            logger.info('remote config must be stored in persistence before recording can start');
            return;
        }
        // We want to ensure the sessionManager is reset if necessary on loading the recorder
        var _c = this._sessionManager.checkAndGetSessionAndWindowId(), sessionId = _c.sessionId, windowId = _c.windowId;
        this._sessionId = sessionId;
        this._windowId = windowId;
        // Reset first full snapshot tracking for the new session
        (_a = this._instance.persistence) === null || _a === void 0 ? void 0 : _a.unregister(constants_1.SESSION_RECORDING_FIRST_FULL_SNAPSHOT_TIMESTAMP);
        if (config === null || config === void 0 ? void 0 : config.endpoint) {
            this._endpoint = config === null || config === void 0 ? void 0 : config.endpoint;
        }
        // Initialize the appropriate strategy based on config version
        var isV2 = (config === null || config === void 0 ? void 0 : config.version) === 2 && (config === null || config === void 0 ? void 0 : config.triggerGroups) && config.triggerGroups.length > 0;
        if (isV2) {
            this._strategy = new recording_strategies_1.V2TriggerGroupStrategy(this._instance, this._urlTriggerMatching, this._reportStarted.bind(this), this._tryAddCustomEvent.bind(this));
        }
        else {
            this._strategy = new recording_strategies_1.V1RecordingStrategy(this._instance, this._urlTriggerMatching, this._eventTriggerMatching, this._linkedFlagMatching, this._reportStarted.bind(this), this._tryTakeFullSnapshot.bind(this));
        }
        // Let the strategy configure itself
        this._strategy.onRemoteConfig(config);
        // Setup event trigger listeners via strategy
        (_b = this._removeEventTriggerCaptureHook) === null || _b === void 0 ? void 0 : _b.call(this);
        this._removeEventTriggerCaptureHook = this._strategy.setupEventTriggerListeners(this._instance.on.bind(this._instance, 'eventCaptured'), this.sessionId, function (triggerType, matchDetail) { return _this._activateTrigger(triggerType, matchDetail); });
        this._checkOverride(constants_1.SESSION_RECORDING_OVERRIDE_SAMPLING, function () {
            _this.overrideSampling();
        }, function () { var _a; return (_a = _this._instance.persistence) === null || _a === void 0 ? void 0 : _a.unregister(constants_1.SESSION_RECORDING_OVERRIDE_SAMPLING); });
        this._checkOverride(constants_1.SESSION_RECORDING_OVERRIDE_LINKED_FLAG, function () {
            _this.overrideLinkedFlag();
        }, function () { var _a; return (_a = _this._instance.persistence) === null || _a === void 0 ? void 0 : _a.unregister(constants_1.SESSION_RECORDING_OVERRIDE_LINKED_FLAG); });
        this._checkOverride(constants_1.SESSION_RECORDING_OVERRIDE_EVENT_TRIGGER, function () {
            _this.overrideTrigger('event');
        }, function () { var _a; return (_a = _this._instance.persistence) === null || _a === void 0 ? void 0 : _a.unregister(constants_1.SESSION_RECORDING_OVERRIDE_EVENT_TRIGGER); });
        this._checkOverride(constants_1.SESSION_RECORDING_OVERRIDE_URL_TRIGGER, function () {
            _this.overrideTrigger('url');
        }, function () { var _a; return (_a = _this._instance.persistence) === null || _a === void 0 ? void 0 : _a.unregister(constants_1.SESSION_RECORDING_OVERRIDE_URL_TRIGGER); });
        // Let strategy make sampling decisions
        this._strategy.makeSamplingDecisions(this.sessionId);
        this._startRecorder();
        if (this._rrwebError) {
            return;
        }
        // calling addEventListener multiple times is safe and will not add duplicates
        (0, utils_1.addEventListener)(globals_1.window, 'beforeunload', this._onBeforeUnload);
        (0, utils_1.addEventListener)(globals_1.window, 'offline', this._onOffline);
        (0, utils_1.addEventListener)(globals_1.window, 'online', this._onOnline);
        (0, utils_1.addEventListener)(globals_1.window, 'visibilitychange', this._onVisibilityChange);
        if (!this._onSessionIdListener) {
            this._onSessionIdListener = this._sessionManager.onSessionId(this._onSessionIdCallback);
        }
        if (!this._onSessionIdleResetForcedListener) {
            this._onSessionIdleResetForcedListener = this._sessionManager.on('forcedIdleReset', function () {
                // a session was forced to reset due to idle timeout and lack of activity
                _this._clearConditionalRecordingPersistence();
                _this._isIdle = 'unknown';
                _this.stop();
                // then we want a session id listener to restart the recording when a new session starts
                _this._forceIdleSessionIdListener = _this._sessionManager.onSessionId(function (sessionId, windowId, changeReason) {
                    var _a;
                    // this should first unregister itself
                    (_a = _this._forceIdleSessionIdListener) === null || _a === void 0 ? void 0 : _a.call(_this);
                    _this._forceIdleSessionIdListener = undefined;
                    _this._onSessionIdCallback(sessionId, windowId, changeReason);
                });
            });
        }
        if ((0, core_1.isNullish)(this._removePageViewCaptureHook)) {
            // :TRICKY: rrweb does not capture navigation within SPA-s, so hook into our $pageview events to get access to all events.
            //   Dropping the initial event is fine (it's always captured by rrweb).
            this._removePageViewCaptureHook = this._instance.on('eventCaptured', function (event) {
                // If anything could go wrong here,
                // it has the potential to block the main loop,
                // so we catch all errors.
                try {
                    if (event.event === '$pageview') {
                        var href = (event === null || event === void 0 ? void 0 : event.properties.$current_url) ? _this._maskUrl(event === null || event === void 0 ? void 0 : event.properties.$current_url) : '';
                        if (!href) {
                            return;
                        }
                        _this._tryAddCustomEvent('$pageview', { href: href });
                    }
                }
                catch (e) {
                    logger.error('Could not add $pageview to rrweb session', e);
                }
            });
        }
        if (this.status === triggerMatching_1.ACTIVE) {
            this._reportStarted(startReason || 'recording_initialized');
        }
    };
    LazyLoadedSessionRecording.prototype._teardown = function () {
        var _a, _b, _c, _d, _e, _f;
        globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.removeEventListener('beforeunload', this._onBeforeUnload);
        globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.removeEventListener('offline', this._onOffline);
        globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.removeEventListener('online', this._onOnline);
        globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.removeEventListener('visibilitychange', this._onVisibilityChange);
        clearInterval(this._fullSnapshotTimer);
        this._clearFlushBufferTimer();
        (_a = this._removePageViewCaptureHook) === null || _a === void 0 ? void 0 : _a.call(this);
        this._removePageViewCaptureHook = undefined;
        (_b = this._removeEventTriggerCaptureHook) === null || _b === void 0 ? void 0 : _b.call(this);
        this._removeEventTriggerCaptureHook = undefined;
        (_c = this._onSessionIdListener) === null || _c === void 0 ? void 0 : _c.call(this);
        this._onSessionIdListener = undefined;
        (_d = this._onSessionIdleResetForcedListener) === null || _d === void 0 ? void 0 : _d.call(this);
        this._onSessionIdleResetForcedListener = undefined;
        (_e = this._forceIdleSessionIdListener) === null || _e === void 0 ? void 0 : _e.call(this);
        this._forceIdleSessionIdListener = undefined;
        (_f = this._strategy) === null || _f === void 0 ? void 0 : _f.stop();
        this._stopRecordingProducers();
        // Invalidate any in-flight async compression work so it does not capture events
        // after stop()/discard() has cleared the buffer or after a future restart.
        this._compressionQueueGeneration += 1;
        this._pendingCompressionEvents = [];
        this._queuedCompressionEvents = 0;
        this._compressionQueue = undefined;
        this._isStoppingAfterCompression = false;
    };
    LazyLoadedSessionRecording.prototype._stopRecordingProducers = function () {
        var _a, _b;
        (_a = this._mutationThrottler) === null || _a === void 0 ? void 0 : _a.stop();
        // Clear any queued rrweb events to prevent memory leaks from closures
        this._queuedRRWebEvents = [];
        (_b = this._stopRrweb) === null || _b === void 0 ? void 0 : _b.call(this);
        this._stopRrweb = undefined;
    };
    LazyLoadedSessionRecording.prototype._stopAfterCompressionQueueDrains = function () {
        var _this = this;
        if (!this._compressionQueue || this._queuedCompressionEvents === 0) {
            return false;
        }
        if (this._isStoppingAfterCompression) {
            return true;
        }
        this._isStoppingAfterCompression = true;
        var generation = this._compressionQueueGeneration;
        this._clearFlushBufferTimer();
        // Stop rrweb synchronously so it cannot keep producing events while we wait
        // for the compression queue to drain and flush the already queued events.
        this._stopRecordingProducers();
        this._compressionQueue
            .catch(function () { return undefined; })
            .then(function () {
            if (generation !== _this._compressionQueueGeneration) {
                return;
            }
            _this._isStoppingAfterCompression = false;
            _this._flushBuffer();
            _this._clearBuffer();
            _this._teardown();
            logger.info('stopped');
        })
            .catch(function () {
            // Keep stop() best-effort. Compression errors are handled per event,
            // but never let an unexpected queue failure block teardown.
            _this._isStoppingAfterCompression = false;
            _this._teardown();
            logger.info('stopped');
        });
        return true;
    };
    LazyLoadedSessionRecording.prototype.stop = function () {
        if (this._stopAfterCompressionQueueDrains()) {
            return;
        }
        this._flushBuffer();
        this._clearBuffer();
        this._teardown();
        logger.info('stopped');
    };
    LazyLoadedSessionRecording.prototype.discard = function () {
        this._clearBuffer();
        this._teardown();
        logger.info('discarded');
    };
    LazyLoadedSessionRecording.prototype._captureProcessedEvent = function (event, eventToSend, size, targetSessionId, targetWindowId) {
        var _a, _b;
        var properties = {
            $snapshot_bytes: size,
            $snapshot_data: eventToSend,
            $session_id: targetSessionId,
            $window_id: targetWindowId,
        };
        if (event.type === rrweb_types_1.EventType.FullSnapshot && ((_b = (_a = getRRWeb()) === null || _a === void 0 ? void 0 : _a.wasMaxDepthReached) === null || _b === void 0 ? void 0 : _b.call(_a))) {
            this._maxDepthExceeded = true;
        }
        if (this.status === triggerMatching_1.DISABLED) {
            this._clearBuffer();
            return;
        }
        this._captureSnapshotBuffered(properties);
    };
    LazyLoadedSessionRecording.prototype._finishQueuedCompressionEvent = function (queuedEvent) {
        if (queuedEvent.counted && queuedEvent.generation === this._compressionQueueGeneration) {
            this._queuedCompressionEvents = Math.max(0, this._queuedCompressionEvents - 1);
        }
        queuedEvent.counted = false;
        this._pendingCompressionEvents = this._pendingCompressionEvents.filter(function (x) { return x !== queuedEvent; });
    };
    LazyLoadedSessionRecording.prototype._captureQueuedCompressionEvent = function (queuedEvent, eventToSend, size) {
        if (queuedEvent.processed || queuedEvent.generation !== this._compressionQueueGeneration) {
            return;
        }
        queuedEvent.processed = true;
        this._captureProcessedEvent(queuedEvent.event, eventToSend, size, queuedEvent.targetSessionId, queuedEvent.targetWindowId);
    };
    LazyLoadedSessionRecording.prototype._processQueuedCompressionEventSync = function (queuedEvent) {
        try {
            var _a = queuedEvent.compressionEnabled
                ? compressEventSync(queuedEvent.event)
                : { event: queuedEvent.event, size: (0, sessionrecording_utils_1.estimateSize)(queuedEvent.event) }, eventToSend = _a.event, size = _a.size;
            this._captureQueuedCompressionEvent(queuedEvent, eventToSend, size);
        }
        finally {
            this._finishQueuedCompressionEvent(queuedEvent);
        }
    };
    LazyLoadedSessionRecording.prototype._drainCompressionQueueSync = function () {
        var _this = this;
        var queuedEvents = __spreadArray([], __read(this._pendingCompressionEvents), false);
        queuedEvents.forEach(function (queuedEvent) {
            _this._processQueuedCompressionEventSync(queuedEvent);
        });
    };
    LazyLoadedSessionRecording.prototype._enqueueCompression = function (event, compressionEnabled, targetSessionId, targetWindowId) {
        var _this = this;
        var queuedEvent = {
            event: event,
            compressionEnabled: compressionEnabled,
            targetSessionId: targetSessionId,
            targetWindowId: targetWindowId,
            generation: this._compressionQueueGeneration,
            processed: false,
            counted: true,
        };
        this._pendingCompressionEvents.push(queuedEvent);
        this._queuedCompressionEvents += 1;
        var processEvent = function () { return __awaiter(_this, void 0, void 0, function () {
            var _a, eventToSend, size, _b, _c;
            return __generator(this, function (_d) {
                switch (_d.label) {
                    case 0:
                        _d.trys.push([0, , 6, 7]);
                        if (queuedEvent.processed) {
                            return [2 /*return*/];
                        }
                        if (!compressionEnabled) return [3 /*break*/, 4];
                        if (!shouldUseNativeAsyncSessionRecordingGzip(event)) return [3 /*break*/, 2];
                        return [4 /*yield*/, compressEventAsync(event)];
                    case 1:
                        _c = _d.sent();
                        return [3 /*break*/, 3];
                    case 2:
                        _c = compressEventSync(event);
                        _d.label = 3;
                    case 3:
                        _b = _c;
                        return [3 /*break*/, 5];
                    case 4:
                        _b = { event: event, size: (0, sessionrecording_utils_1.estimateSize)(event) };
                        _d.label = 5;
                    case 5:
                        _a = _b, eventToSend = _a.event, size = _a.size;
                        this._captureQueuedCompressionEvent(queuedEvent, eventToSend, size);
                        return [3 /*break*/, 7];
                    case 6:
                        this._finishQueuedCompressionEvent(queuedEvent);
                        return [7 /*endfinally*/];
                    case 7: return [2 /*return*/];
                }
            });
        }); };
        this._compressionQueue = this._compressionQueue
            ? this._compressionQueue.catch(function () { return undefined; }).then(processEvent)
            : processEvent();
    };
    LazyLoadedSessionRecording.prototype.onRRwebEmit = function (rawEvent) {
        var _a;
        var _this = this;
        var _b, _c, _d, _e, _f, _g, _h, _j, _k;
        // late event after sessionManager teardown (e.g. cookieless opt-out) — _sessionManager would throw
        if (!this._instance.sessionManager) {
            return;
        }
        this._processQueuedEvents();
        if (!rawEvent || !(0, core_1.isObject)(rawEvent)) {
            return;
        }
        if (rawEvent.type === rrweb_types_1.EventType.Meta) {
            var href = this._maskUrl(rawEvent.data.href);
            this._lastHref = href;
            if (!href) {
                return;
            }
            rawEvent.data.href = href;
        }
        else {
            this._pageViewFallBack();
        }
        // Check if the URL matches any trigger patterns - delegate to strategy
        (_b = this._strategy) === null || _b === void 0 ? void 0 : _b.checkUrlTriggers(this.sessionId, function () { return _this._pauseRecording(); }, function () { return _this._resumeRecording(); }, function (triggerType, matchDetail) { return _this._activateTrigger(triggerType, matchDetail); });
        // always have to check if the URL is blocked really early,
        // or you risk getting stuck in a loop
        if (this._urlTriggerMatching.urlBlocked && !isRecordingPausedEvent(rawEvent)) {
            return;
        }
        // we're processing a full snapshot, so we should reset the timer
        if (rawEvent.type === rrweb_types_1.EventType.FullSnapshot) {
            this._scheduleFullSnapshot();
            // Full snapshots reset rrweb's node IDs, so clear any logged node tracking
            (_c = this._mutationThrottler) === null || _c === void 0 ? void 0 : _c.reset();
            // Track the timestamp of the first full snapshot for this session
            // This helps us detect session rotation issues where incremental snapshots
            // are sent before the full snapshot
            (_d = this._instance.persistence) === null || _d === void 0 ? void 0 : _d.register_once((_a = {},
                _a[constants_1.SESSION_RECORDING_FIRST_FULL_SNAPSHOT_TIMESTAMP] = rawEvent.timestamp,
                _a), undefined);
        }
        // Clear the buffer if waiting for a trigger and only keep data from after the current full snapshot
        if (rawEvent.type === rrweb_types_1.EventType.FullSnapshot && ((_e = this._strategy) === null || _e === void 0 ? void 0 : _e.hasPendingTriggers(this.sessionId))) {
            this._clearBufferBeforeMostRecentMeta();
        }
        var throttledEvent = this._mutationThrottler ? this._mutationThrottler.throttleMutations(rawEvent) : rawEvent;
        if (!throttledEvent) {
            return;
        }
        // TODO: Re-add ensureMaxMessageSize once we are confident in it
        var event = (0, sessionrecording_utils_1.truncateLargeConsoleLogs)(throttledEvent);
        // Session lifecycle events ($session_ending, $session_starting) carry their target session ID
        // in the payload. We must extract this BEFORE _updateWindowAndSessionIds runs, because that
        // method triggers checkAndGetSessionAndWindowId() which would update this._sessionId.
        // This is critical for $session_ending which must go to the OLD session, not the new one,
        // and for $session_starting which must go to the NEW session.
        var sessionEndingPayload = getSessionEndingPayload(event);
        var sessionStartingPayload = getSessionStartingPayload(event);
        if (sessionEndingPayload || sessionStartingPayload) {
            // Adjust timestamp from payload to avoid artificially extending session duration
            var payload = (sessionEndingPayload !== null && sessionEndingPayload !== void 0 ? sessionEndingPayload : sessionStartingPayload);
            if (payload === null || payload === void 0 ? void 0 : payload.lastActivityTimestamp) {
                event.timestamp = payload.lastActivityTimestamp;
            }
        }
        else {
            this._updateWindowAndSessionIds(event);
        }
        if (rawEvent.type === rrweb_types_1.EventType.FullSnapshot) {
            this._fullSnapshotTimestamps.push([this._sessionId, rawEvent.timestamp]);
            if (this._fullSnapshotTimestamps.length > 6) {
                this._fullSnapshotTimestamps = this._fullSnapshotTimestamps.slice(-6);
            }
        }
        // Route lifecycle events using their payload IDs:
        // - $session_ending uses currentSessionId (the old session it's ending)
        // - $session_starting uses nextSessionId (the new session it's starting)
        // - All other events use the current session ID
        var targetSessionId = (_g = (_f = sessionEndingPayload === null || sessionEndingPayload === void 0 ? void 0 : sessionEndingPayload.currentSessionId) !== null && _f !== void 0 ? _f : sessionStartingPayload === null || sessionStartingPayload === void 0 ? void 0 : sessionStartingPayload.nextSessionId) !== null && _g !== void 0 ? _g : this._sessionId;
        var targetWindowId = (_j = (_h = sessionEndingPayload === null || sessionEndingPayload === void 0 ? void 0 : sessionEndingPayload.currentWindowId) !== null && _h !== void 0 ? _h : sessionStartingPayload === null || sessionStartingPayload === void 0 ? void 0 : sessionStartingPayload.nextWindowId) !== null && _j !== void 0 ? _j : this._windowId;
        // When in an idle state we keep recording but don't capture the events,
        // we don't want to return early if idle is 'unknown'
        if (this._isIdle === true && !isAllowedWhenIdle(event)) {
            return;
        }
        if (isSessionIdleEvent(event)) {
            // session idle events have a timestamp when rrweb sees them
            // which can artificially lengthen a session
            // we know when we detected it based on the payload and can correct the timestamp
            var payload = event.data.payload;
            if (payload) {
                var lastActivity = payload.lastActivityTimestamp;
                var threshold = payload.threshold;
                event.timestamp = lastActivity + threshold;
            }
        }
        var compressionEnabled = (_k = this._instance.config.session_recording.compress_events) !== null && _k !== void 0 ? _k : true;
        if (this._queuedCompressionEvents > 0 ||
            (compressionEnabled && shouldUseNativeAsyncSessionRecordingGzip(event))) {
            this._enqueueCompression(event, compressionEnabled, targetSessionId, targetWindowId);
            return;
        }
        var _l = compressionEnabled
            ? compressEventSync(event)
            : { event: event, size: (0, sessionrecording_utils_1.estimateSize)(event) }, eventToSend = _l.event, size = _l.size;
        this._captureProcessedEvent(event, eventToSend, size, targetSessionId, targetWindowId);
    };
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "status", {
        get: function () {
            if (!this._strategy) {
                return triggerMatching_1.DISABLED;
            }
            var context = {
                instance: this._instance,
                sessionId: this.sessionId,
                isSampled: this._isSampled,
                rrwebError: this._rrwebError,
                urlTriggerMatching: this._urlTriggerMatching,
                eventTriggerMatching: this._eventTriggerMatching,
                linkedFlagMatching: this._linkedFlagMatching,
                remoteConfig: this._remoteConfig,
            };
            return this._strategy.getStatus(context);
        },
        enumerable: false,
        configurable: true
    });
    LazyLoadedSessionRecording.prototype.log = function (message, level) {
        var _a;
        if (level === void 0) { level = 'log'; }
        (_a = this._instance.sessionRecording) === null || _a === void 0 ? void 0 : _a.onRRwebEmit({
            type: 6,
            data: {
                plugin: 'rrweb/console@1',
                payload: {
                    level: level,
                    trace: [],
                    // Even though it is a string, we stringify it as that's what rrweb expects
                    payload: [JSON.stringify(message)],
                },
            },
            timestamp: Date.now(),
        });
    };
    LazyLoadedSessionRecording.prototype.overrideLinkedFlag = function () {
        this._linkedFlagMatching.linkedFlagSeen = true;
        this._tryTakeFullSnapshot();
        this._reportStarted('linked_flag_overridden');
    };
    /**
     * this ignores the sampling config and (if other conditions are met) causes capture to start
     *
     * It is not usual to call this directly,
     * instead call `posthog.startSessionRecording({sampling: true})`
     * */
    LazyLoadedSessionRecording.prototype.overrideSampling = function () {
        var _a;
        var _b;
        (_b = this._instance.persistence) === null || _b === void 0 ? void 0 : _b.register((_a = {},
            // short-circuits the `makeSamplingDecision` function in the session recording module
            _a[constants_1.SESSION_RECORDING_IS_SAMPLED] = this.sessionId,
            _a[constants_1.SESSION_RECORDING_SAMPLE_RATE] = null,
            _a));
        this._tryTakeFullSnapshot();
        this._reportStarted('sampling_overridden');
    };
    /**
     * this ignores the URL/Event trigger config and (if other conditions are met) causes capture to start
     *
     * It is not usual to call this directly,
     * instead call `posthog.startSessionRecording({trigger: 'url' | 'event'})`
     * */
    LazyLoadedSessionRecording.prototype.overrideTrigger = function (triggerType) {
        this._activateTrigger(triggerType);
    };
    LazyLoadedSessionRecording.prototype._clearFlushBufferTimer = function () {
        if (this._flushBufferTimer) {
            clearTimeout(this._flushBufferTimer);
            this._flushBufferTimer = undefined;
        }
    };
    LazyLoadedSessionRecording.prototype._flushBuffer = function () {
        var _this = this;
        var _a, _b;
        this._clearFlushBufferTimer();
        // never flush while a sampling decision is missing (e.g. wiped by posthog.reset()) — an
        // undecided session reads as ACTIVE and would leak a batch it then decides not to record
        (_a = this._strategy) === null || _a === void 0 ? void 0 : _a.ensureSamplingDecision(this.sessionId);
        var isBelowMinimumDuration = this._isBelowMinimumDuration();
        if (this.status === triggerMatching_1.BUFFERING || this.status === triggerMatching_1.PAUSED || this.status === triggerMatching_1.DISABLED || isBelowMinimumDuration) {
            this._flushBufferTimer = setTimeout(function () {
                _this._flushBuffer();
            }, exports.RECORDING_BUFFER_TIMEOUT);
            return this._buffer;
        }
        if (this._buffer.data.length > 0) {
            var snapshotEvents = (0, sessionrecording_utils_1.splitBuffer)(this._buffer);
            snapshotEvents.forEach(function (snapshotBuffer) {
                var _a;
                (_a = _this._flushedSizeTracker) === null || _a === void 0 ? void 0 : _a.trackSize(snapshotBuffer.size);
                _this._captureSnapshot({
                    $snapshot_bytes: snapshotBuffer.size,
                    $snapshot_data: snapshotBuffer.data,
                    $session_id: snapshotBuffer.sessionId,
                    $window_id: snapshotBuffer.windowId,
                    $lib: config_2.default.LIB_NAME,
                    $lib_version: config_2.default.LIB_VERSION,
                });
            });
            // Notify strategy that initial flush is complete (performance optimization)
            (_b = this._strategy) === null || _b === void 0 ? void 0 : _b.onFlushComplete();
        }
        // buffer is empty, we clear it in case the session id has changed
        return this._clearBuffer();
    };
    LazyLoadedSessionRecording.prototype._captureSnapshotBuffered = function (properties) {
        var _this = this;
        var _a;
        var additionalBytes = 2 + (((_a = this._buffer) === null || _a === void 0 ? void 0 : _a.data.length) || 0); // 2 bytes for the array brackets and 1 byte for each comma
        // Extract target session ID from properties to ensure we flush when session changes
        // This is critical for lifecycle events ($session_ending, $session_starting) which may
        // have different target session IDs than this._sessionId
        var targetSessionId = properties.$session_id;
        if (!this._isIdle && // we never want to flush when idle
            (this._buffer.size + properties.$snapshot_bytes + additionalBytes > exports.RECORDING_MAX_EVENT_SIZE ||
                this._buffer.sessionId !== targetSessionId)) {
            this._buffer = this._flushBuffer();
            // After flushing, update buffer to use the new target session/window IDs
            this._buffer.sessionId = targetSessionId;
            this._buffer.windowId = properties.$window_id;
        }
        this._buffer.size += properties.$snapshot_bytes;
        this._buffer.data.push(properties.$snapshot_data);
        this._buffer.sizes.push(properties.$snapshot_bytes);
        if (!this._flushBufferTimer && !this._isIdle) {
            this._flushBufferTimer = setTimeout(function () {
                _this._flushBuffer();
            }, exports.RECORDING_BUFFER_TIMEOUT);
        }
    };
    LazyLoadedSessionRecording.prototype._captureSnapshot = function (properties) {
        // :TRICKY: Make sure we batch these requests, use a custom endpoint and don't truncate the strings.
        this._instance.capture('$snapshot', properties, {
            _url: this._instance.requestRouter.endpointFor('api', this._endpoint),
            _noTruncate: true,
            _batchKey: exports.SESSION_RECORDING_BATCH_KEY,
            skip_client_rate_limiting: true,
        });
    };
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "_sessionDuration", {
        get: function () {
            var _a, _b;
            var mostRecentSnapshot = (_a = this._buffer) === null || _a === void 0 ? void 0 : _a.data[((_b = this._buffer) === null || _b === void 0 ? void 0 : _b.data.length) - 1];
            var sessionStartTimestamp = this._sessionManager.checkAndGetSessionAndWindowId(true).sessionStartTimestamp;
            return mostRecentSnapshot ? mostRecentSnapshot.timestamp - sessionStartTimestamp : null;
        },
        enumerable: false,
        configurable: true
    });
    LazyLoadedSessionRecording.prototype._clearBufferBeforeMostRecentMeta = function () {
        if (!this._buffer || this._buffer.data.length === 0) {
            return this._clearBuffer();
        }
        // Find the last meta event index by iterating backwards
        var lastMetaIndex = -1;
        for (var i = this._buffer.data.length - 1; i >= 0; i--) {
            if (this._buffer.data[i].type === rrweb_types_1.EventType.Meta) {
                lastMetaIndex = i;
                break;
            }
        }
        if (lastMetaIndex >= 0) {
            this._buffer.data = this._buffer.data.slice(lastMetaIndex);
            this._buffer.sizes = this._buffer.sizes.slice(lastMetaIndex);
            this._buffer.size = this._buffer.sizes.reduce(function (a, b) { return a + b; }, 0);
            return this._buffer;
        }
        else {
            return this._clearBuffer();
        }
    };
    LazyLoadedSessionRecording.prototype._clearBuffer = function () {
        this._buffer = {
            size: 0,
            data: [],
            sizes: [],
            sessionId: this._sessionId,
            windowId: this._windowId,
        };
        return this._buffer;
    };
    LazyLoadedSessionRecording.prototype._reportStarted = function (startReason, tagPayload) {
        var _a;
        this._instance.register_for_session((_a = {},
            _a[constants_1.SESSION_RECORDING_START_REASON] = startReason,
            _a));
        logger.info(startReason.replace('_', ' '), tagPayload);
        if (startReason !== 'session_id_changed') {
            this._tryAddCustomEvent('$recording_started', __assign({ reason: startReason }, tagPayload));
        }
    };
    LazyLoadedSessionRecording.prototype._isInteractiveEvent = function (event) {
        var _a;
        return (event.type === sessionrecording_utils_1.INCREMENTAL_SNAPSHOT_EVENT_TYPE &&
            ACTIVE_SOURCES.indexOf((_a = event.data) === null || _a === void 0 ? void 0 : _a.source) !== -1);
    };
    LazyLoadedSessionRecording.prototype._updateWindowAndSessionIds = function (event) {
        // Some recording events are triggered by non-user events (e.g. "X minutes ago" text updating on the screen).
        // We don't want to extend the session or trigger a new session in these cases. These events are designated by event
        // type -> incremental update, and source -> mutation.
        var isUserInteraction = this._isInteractiveEvent(event);
        if (!isUserInteraction && !this._isIdle) {
            // We check if the lastActivityTimestamp is old enough to go idle
            var timeSinceLastActivity = event.timestamp - this._lastActivityTimestamp;
            if (timeSinceLastActivity > this._sessionIdleThresholdMilliseconds) {
                // we mark as idle right away,
                // or else we get multiple idle events
                // if there are lots of non-user activity events being emitted
                this._isIdle = true;
                // don't take full snapshots while idle
                clearInterval(this._fullSnapshotTimer);
                this._tryAddCustomEvent('sessionIdle', {
                    eventTimestamp: event.timestamp,
                    lastActivityTimestamp: this._lastActivityTimestamp,
                    threshold: this._sessionIdleThresholdMilliseconds,
                    bufferLength: this._buffer.data.length,
                    bufferSize: this._buffer.size,
                });
                // proactively flush the buffer in case the session is idle for a long time
                this._flushBuffer();
            }
        }
        var returningFromIdle = false;
        if (isUserInteraction) {
            this._lastActivityTimestamp = event.timestamp;
            if (this._isIdle) {
                var idleWasUnknown = this._isIdle === 'unknown';
                // Remove the idle state
                this._isIdle = false;
                // if the idle state was unknown, we don't want to add an event, since we're just in bootup
                // whereas if it was true, we know we've been idle for a while, and we can mark ourselves as returning from idle
                if (!idleWasUnknown) {
                    this._tryAddCustomEvent('sessionNoLongerIdle', {
                        reason: 'user activity',
                        type: event.type,
                    });
                    returningFromIdle = true;
                }
            }
        }
        if (this._isIdle) {
            return;
        }
        // We only want to extend the session if it is an interactive event.
        var _a = this._sessionManager.checkAndGetSessionAndWindowId(!isUserInteraction, event.timestamp), windowId = _a.windowId, sessionId = _a.sessionId;
        var sessionIdChanged = this._sessionId !== sessionId;
        var windowIdChanged = this._windowId !== windowId;
        this._windowId = windowId;
        this._sessionId = sessionId;
        if (sessionIdChanged || windowIdChanged) {
            this.stop();
            this.start('session_id_changed');
        }
        else if (returningFromIdle) {
            this._scheduleFullSnapshot();
        }
    };
    LazyLoadedSessionRecording.prototype._clearConditionalRecordingPersistence = function () {
        var _a;
        (_a = this._strategy) === null || _a === void 0 ? void 0 : _a.clearConditionalRecordingPersistence();
    };
    Object.defineProperty(LazyLoadedSessionRecording.prototype, "sdkDebugProperties", {
        get: function () {
            var _a;
            var _b;
            var sessionStartTimestamp = this._sessionManager.checkAndGetSessionAndWindowId(true).sessionStartTimestamp;
            return _a = {
                    $recording_status: this.status,
                    $sdk_debug_replay_internal_buffer_length: this._buffer.data.length,
                    $sdk_debug_replay_internal_buffer_size: this._buffer.size,
                    $sdk_debug_current_session_duration: this._sessionDuration,
                    $sdk_debug_session_start: sessionStartTimestamp,
                    $sdk_debug_replay_flushed_size: (_b = this._flushedSizeTracker) === null || _b === void 0 ? void 0 : _b.currentTrackedSize,
                    $sdk_debug_replay_full_snapshots: this._fullSnapshotTimestamps,
                    $snapshot_max_depth_exceeded: this._maxDepthExceeded,
                    $sdk_debug_replay_rrweb_error: this._rrwebError
                },
                _a[constants_1.SDK_DEBUG_REPLAY_RRWEB_ATTACHED] = !!this._stopRrweb,
                _a[constants_1.SDK_DEBUG_REPLAY_RRWEB_START_ATTEMPTED] = this._rrwebStartAttempted,
                _a;
        },
        enumerable: false,
        configurable: true
    });
    LazyLoadedSessionRecording.prototype._startRecorder = function () {
        var e_2, _a;
        var _this = this;
        var _b, _c, _d, _e;
        if (this._stopRrweb) {
            return;
        }
        this._rrwebStartAttempted = true;
        // rrweb config info: https://github.com/rrweb-io/rrweb/blob/7d5d0033258d6c29599fb08412202d9a2c7b9413/src/record/index.ts#L28
        var sessionRecordingOptions = {
            // a limited set of the rrweb config options that we expose to our users.
            // see https://github.com/rrweb-io/rrweb/blob/master/guide.md
            blockClass: 'ph-no-capture',
            blockSelector: undefined,
            ignoreClass: 'ph-ignore-input',
            maskTextClass: 'ph-mask',
            maskTextSelector: undefined,
            maskTextFn: undefined,
            maskAllInputs: true,
            maskInputOptions: { password: true },
            maskInputFn: undefined,
            slimDOMOptions: {},
            collectFonts: false,
            inlineStylesheet: true,
            recordCrossOriginIframes: false,
        };
        // only allows user to set our allowlisted options
        var userSessionRecordingOptions = this._instance.config.session_recording;
        try {
            for (var _f = __values(Object.entries(userSessionRecordingOptions || {})), _g = _f.next(); !_g.done; _g = _f.next()) {
                var _h = __read(_g.value, 2), key = _h[0], value = _h[1];
                if (key in sessionRecordingOptions) {
                    if (key === 'maskInputOptions') {
                        // ensure password config is set if not included
                        sessionRecordingOptions.maskInputOptions = __assign({ password: true }, value);
                    }
                    else {
                        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                        // @ts-ignore
                        sessionRecordingOptions[key] = value;
                    }
                }
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (_g && !_g.done && (_a = _f.return)) _a.call(_f);
            }
            finally { if (e_2) throw e_2.error; }
        }
        if (this._canvasRecording && this._canvasRecording.enabled) {
            sessionRecordingOptions.recordCanvas = true;
            sessionRecordingOptions.sampling = { canvas: this._canvasRecording.fps };
            sessionRecordingOptions.dataURLOptions = { type: 'image/webp', quality: this._canvasRecording.quality };
        }
        if (this._masking) {
            sessionRecordingOptions.maskAllInputs = (_b = this._masking.maskAllInputs) !== null && _b !== void 0 ? _b : true;
            sessionRecordingOptions.maskTextSelector = (_c = this._masking.maskTextSelector) !== null && _c !== void 0 ? _c : undefined;
            sessionRecordingOptions.blockSelector = (_d = this._masking.blockSelector) !== null && _d !== void 0 ? _d : undefined;
        }
        var rrwebRecord = getRRWebRecord();
        if (!rrwebRecord) {
            logger.error('_startRecorder was called but rrwebRecord is not available. This indicates something has gone wrong.');
            return;
        }
        this._mutationThrottler =
            (_e = this._mutationThrottler) !== null && _e !== void 0 ? _e : new mutation_throttler_1.MutationThrottler(rrwebRecord, {
                refillRate: this._instance.config.session_recording.__mutationThrottlerRefillRate,
                bucketSize: this._instance.config.session_recording.__mutationThrottlerBucketSize,
                onBlockedNode: function (id, node) {
                    var message = "Too many mutations on node '".concat(id, "'. Rate limiting. This could be due to SVG animations or something similar");
                    logger.info(message, {
                        node: node,
                    });
                    _this.log(LOGGER_PREFIX + ' ' + message, 'warn');
                },
            });
        var activePlugins = this._gatherRRWebPlugins();
        this._stopRrweb = rrwebRecord(__assign({ emit: function (event) {
                _this.onRRwebEmit(event);
            }, plugins: activePlugins }, sessionRecordingOptions));
        if (!this._stopRrweb) {
            this._rrwebError = true;
            logger.error('rrweb failed to start - Loss of recording data is possible. Check the browser console for rrweb errors.');
            return;
        }
        this._rrwebError = false;
        // We reset the last activity timestamp, resetting the idle timer
        this._lastActivityTimestamp = Date.now();
        // stay unknown if we're not sure if we're idle or not
        this._isIdle = (0, core_1.isBoolean)(this._isIdle) ? this._isIdle : 'unknown';
        this.tryAddCustomEvent('$remote_config_received', this._remoteConfig);
        this._tryAddCustomEvent('$session_options', {
            sessionRecordingOptions: sessionRecordingOptions,
            activePlugins: activePlugins.map(function (p) { return p === null || p === void 0 ? void 0 : p.name; }),
        });
        this._tryAddCustomEvent('$posthog_config', {
            config: this._instance.config,
        });
    };
    LazyLoadedSessionRecording.prototype.tryAddCustomEvent = function (tag, payload) {
        return this._tryAddCustomEvent(tag, payload);
    };
    return LazyLoadedSessionRecording;
}());
exports.LazyLoadedSessionRecording = LazyLoadedSessionRecording;
//# sourceMappingURL=lazy-loaded-session-recorder.js.map