UNPKG

posthog-js

Version:

Posthog-js allows you to automatically capture usage and send events to PostHog.

1,720 lines • 90.1 kB
"use strict";
/* eslint camelcase: "off" */
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 __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.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PostHogPersistence = void 0;
var general_utils_1 = require("@posthog/browser-common/utils/general-utils");
var storage_1 = require("./storage");
var globals_1 = require("@posthog/browser-common/utils/globals");
var constants_1 = require("./constants");
var persistence_key_policy_1 = require("./persistence-key-policy");
// The "freshness" key each split group stamps when its server payload is
// (re)loaded. A group entry carrying an older timestamp than the main blob is a
// stale orphan (a gate-off / older-SDK tab wrote a fresher payload back to main)
// and must not win on load. Groups without an entry here have no freshness
// signal, so the group entry wins by default (the migrated-forward home).
var VOLATILE_FINGERPRINT_PLACEHOLDER = '__volatile__';
// Both freshness keys are volatile (their on-disk value lags the last content
// change). That is safe: when only the stamp moved, both the group entry and the
// main blob hold identical content, so whichever side wins still produces the
// same values. A mixed-fleet race at most causes one extra /flags cycle.
var GROUP_FRESHNESS_KEY = {
    flags: constants_1.PERSISTENCE_FEATURE_FLAG_EVALUATED_AT,
    surveys: constants_1.SURVEYS_LOADED_AT,
};
var core_1 = require("@posthog/core");
var event_utils_1 = require("@posthog/browser-common/utils/event-utils");
var logger_1 = require("@posthog/browser-common/utils/logger");
var core_2 = require("@posthog/core");
var CASE_INSENSITIVE_PERSISTENCE_TYPES = [
    'cookie',
    'localstorage',
    'localstorage+cookie',
    'sessionstorage',
    'memory',
];
var getCookieIdentityChangePendingName = function (name) { return "".concat(name, "_cookie_identity_change_pending"); };
var parseName = function (config) {
    var token = '';
    if (config['token']) {
        token = config['token'].replace(/\+/g, 'PL').replace(/\//g, 'SL').replace(/=/g, 'EQ');
    }
    if (config['persistence_name']) {
        return 'ph_' + config['persistence_name'];
    }
    else {
        return 'ph_' + token + '_posthog';
    }
};
// Fingerprint slot for the main persistence entry. Group entries (`flags`)
// use their group name as the slot. See `_writeEntry`.
var MAIN_STORAGE_SLOT = 'main';
// Feature flag evaluation state is shared by every same-origin tab. Keep these
// keys synchronized so a stale tab cannot overwrite an enrollment or cached
// flag update when it next writes the persistence blob.
var CROSS_TAB_FEATURE_FLAG_KEYS = [
    constants_1.ENABLED_FEATURE_FLAGS,
    constants_1.PERSISTENCE_ACTIVE_FEATURE_FLAGS,
    constants_1.PERSISTENCE_FEATURE_FLAG_DETAILS,
    constants_1.PERSISTENCE_FEATURE_FLAG_PAYLOADS,
    constants_1.PERSISTENCE_FEATURE_FLAG_REQUEST_ID,
    constants_1.PERSISTENCE_FEATURE_FLAG_EVALUATED_AT,
    constants_1.PERSISTENCE_MINIMAL_FLAG_CALLED_EVENTS,
    constants_1.STORED_PERSON_PROPERTIES_KEY,
];
var isCrossTabFeatureFlagKey = function (key) {
    return CROSS_TAB_FEATURE_FLAG_KEYS.indexOf(key) !== -1;
};
var isStorageValueEqual = function (left, right) {
    try {
        return JSON.stringify(left) === JSON.stringify(right);
    }
    catch (_a) {
        return left === right;
    }
};
var parseStorageValue = function (value) {
    if (!value) {
        return {};
    }
    var parsed = JSON.parse(value);
    return (0, core_2.isObject)(parsed) ? parsed : {};
};
var isArrayContentsEqual = function (arr1, arr2) {
    if (arr1.length !== arr2.length) {
        return false;
    }
    var sortedArr1 = __spreadArray([], __read(arr1), false).sort();
    var sortedArr2 = __spreadArray([], __read(arr2), false).sort();
    return sortedArr1.every(function (item, index) { return item === sortedArr2[index]; });
};
var clearStaleEventProperties = function (props, cookieProperties) {
    (0, general_utils_1.each)(props, function (_value, key) {
        var policy = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key);
        if ((!policy || policy.exposure === 'event') && !Object.prototype.hasOwnProperty.call(cookieProperties, key)) {
            delete props[key];
        }
    });
};
/**
 * PostHog Persistence Object
 * @constructor
 */
var PostHogPersistence = /** @class */ (function () {
    /**
     * @param {PostHogConfig} config initial PostHog configuration
     * @param {boolean=} isDisabled should persistence be disabled (e.g. because of consent management)
     */
    function PostHogPersistence(config, isDisabled, ownsSplitStorage) {
        if (ownsSplitStorage === void 0) { ownsSplitStorage = true; }
        var _this = this;
        // Per-storage-entry write bookkeeping, keyed by slot (`main` plus each group
        // name): no-op-rejection fingerprint, per-group dirty flag, and on-disk
        // materialization. Reset wholesale on remove()/clear() so a save after
        // remove always lands. See `SlotWriteState`.
        this._slotState = {};
        // Whether the resolved storage backend can host the split (localStorage /
        // localStorage+cookie). Set by `_buildStorage`.
        this._splitStorageEligible = false;
        // Whether flag config is stored in their own entries this session:
        // backend-eligible AND `split_storage` enabled.
        // Re-resolved on every `update_config` (backend rebuild or a runtime flag flip).
        this._splitStorage = false;
        // Identity changes can be adopted by background persistence writes before
        // capture runs. Keep the transition pending until core performs the flag and
        // person-cache side effects associated with the new identity.
        this._cookieIdentityChangePending = false;
        // A local reset or storage migration owns the next cookie snapshot. Ignore
        // sibling writes until the complete replacement has been published.
        this._cookieSyncSuppressed = false;
        // Nested feature-flag entries changed locally and waiting for a durable
        // write. Sibling updates to other flags can still be merged while pending.
        this._pendingCrossTabFeatureFlagChanges = new Map();
        this._storageMigrationInProgress = false;
        this._localIdentityChangePending = false;
        this._crossTabFeatureFlagIdentityMismatch = false;
        this._crossTabFeatureFlagHandlers = new Set();
        this._config = config;
        this._ownsSplitStorage = ownsSplitStorage;
        this.props = {};
        this._campaign_params_url = undefined;
        this._name = parseName(config);
        this._storage = this._buildStorage(config);
        this._splitStorage = this._resolveSplitStorage(config);
        this.load();
        // Preserve only values for which load() selected a fresher source than
        // the current storage entry. Ordinary loaded values remain mergeable.
        this._markLoadedCrossTabFeatureFlagChangesPending();
        if (config.debug) {
            logger_1.logger.info('Persistence loaded', config['persistence'], __assign({}, this.props));
        }
        this.update_config(config, config, isDisabled);
        this.save();
        // Install unload flush listeners unconditionally. They are a no-op
        // when no debounced write is pending (see `flush()`), so it is safe
        // to install even when `persistence_save_debounce_ms` is 0 at
        // construction. Crucially this also handles `posthog.set_config({
        // persistence_save_debounce_ms: 250 })` enabling debounce later —
        // we'd otherwise miss the listener install and lose pending writes
        // on close.
        if (globals_1.window) {
            var flush = function () { return _this.flush(); };
            (0, general_utils_1.addEventListener)(globals_1.window, 'beforeunload', flush, { capture: false });
            (0, general_utils_1.addEventListener)(globals_1.window, 'pagehide', flush, { capture: false });
            this._onStorage = function (event) {
                if (!_this._splitStorageEligible ||
                    (event.storageArea && event.storageArea !== (globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.localStorage)) ||
                    !event.key) {
                    return;
                }
                if (event.key === _this._name) {
                    _this._syncCrossTabFeatureFlagProperties(event.key, MAIN_STORAGE_SLOT);
                    return;
                }
                if (_this._splitStorage) {
                    var group = persistence_key_policy_1.PERSISTENCE_STORAGE_GROUPS.find(function (group) { return event.key === _this._groupEntryName(group); });
                    if (group) {
                        _this._syncCrossTabFeatureFlagProperties(event.key, group);
                    }
                }
            };
            (0, general_utils_1.addEventListener)(globals_1.window, 'storage', this._onStorage);
        }
    }
    PostHogPersistence.prototype.markCrossTabFeatureFlagChanges = function (changes) {
        var _this = this;
        Object.entries(changes).forEach(function (_a) {
            var _b = __read(_a, 2), key = _b[0], properties = _b[1];
            var existingPendingChanges = _this._pendingCrossTabFeatureFlagChanges.get(key);
            if (!isCrossTabFeatureFlagKey(key) || existingPendingChanges === true) {
                return;
            }
            if (properties === true) {
                _this._setCrossTabFeatureFlagChangesPending(key, true);
                return;
            }
            var pendingChanges = new Set(existingPendingChanges || []);
            properties.forEach(function (property) { return pendingChanges.add(property); });
            if (pendingChanges.size) {
                _this._setCrossTabFeatureFlagChangesPending(key, pendingChanges);
            }
        });
    };
    PostHogPersistence.prototype.onCrossTabFeatureFlagChange = function (handler) {
        var _this = this;
        this._crossTabFeatureFlagHandlers.add(handler);
        return function () { return _this._crossTabFeatureFlagHandlers.delete(handler); };
    };
    PostHogPersistence.prototype.destroy = function () {
        if (this._onStorage && globals_1.window) {
            globals_1.window.removeEventListener('storage', this._onStorage);
            this._onStorage = undefined;
        }
        this._crossTabFeatureFlagHandlers.clear();
    };
    PostHogPersistence.prototype._syncCrossTabFeatureFlagProperties = function (storageKey, slot, notify) {
        if (notify === void 0) { notify = true; }
        if (this._disabled) {
            return false;
        }
        var nextEntry;
        var storageValue;
        try {
            // A queued storage event can be older than the value currently on disk.
            // Always reconcile against the latest snapshot instead of event.newValue.
            storageValue = storage_1.localStore._get(storageKey);
            if ((0, core_1.isNull)(storageValue)) {
                var state = this._slotWriteState(slot);
                state.storageValue = null;
                if (slot !== MAIN_STORAGE_SLOT) {
                    state.persisted = false;
                }
                return false;
            }
            nextEntry = parseStorageValue(storageValue);
        }
        catch (_a) {
            return false;
        }
        var identityEntry = slot === MAIN_STORAGE_SLOT ? nextEntry : storage_1.localStore._parse(this._name);
        if (identityEntry && this._hasCrossTabFeatureFlagIdentityMismatch(identityEntry)) {
            return false;
        }
        var changed = this._mergeCrossTabFeatureFlagProperties(nextEntry, slot, notify);
        if (slot !== MAIN_STORAGE_SLOT ||
            !this._splitStorage ||
            isStorageValueEqual(this._partitionProps().main, nextEntry)) {
            this._rememberCrossTabStorageFingerprint(nextEntry, slot, true, storageValue);
        }
        return changed;
    };
    PostHogPersistence.prototype._hasCrossTabFeatureFlagIdentityMismatch = function (entry) {
        var localDistinctId = this.props[constants_1.DISTINCT_ID];
        var persistedDistinctId = entry[constants_1.DISTINCT_ID];
        return (!(0, core_1.isUndefined)(localDistinctId) &&
            !(0, core_1.isUndefined)(persistedDistinctId) &&
            localDistinctId !== persistedDistinctId);
    };
    PostHogPersistence.prototype._rememberCrossTabStorageFingerprint = function (entry, slot, materialized, storageValue) {
        var state = this._slotWriteState(slot);
        state.storageValue = storageValue;
        if (slot !== MAIN_STORAGE_SLOT) {
            state.persisted = materialized;
        }
        try {
            state.fingerprint = this._entryFingerprint(entry, slot);
        }
        catch (_a) {
            state.fingerprint = undefined;
        }
    };
    PostHogPersistence.prototype._mergeCrossTabFeatureFlagProperties = function (nextEntry, slot, notify) {
        var _this = this;
        var changed = false;
        CROSS_TAB_FEATURE_FLAG_KEYS.forEach(function (prop) {
            var _a;
            var group = (_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(prop)) === null || _a === void 0 ? void 0 : _a.storageGroup;
            if ((slot === MAIN_STORAGE_SLOT && _this._splitStorage && group) ||
                (slot !== MAIN_STORAGE_SLOT && group !== slot)) {
                return;
            }
            var hasNextValue = prop in nextEntry;
            var nextValue = _this._mergePendingCrossTabFeatureFlagChanges(prop, hasNextValue ? nextEntry[prop] : undefined);
            var keepKey = _this._pendingCrossTabFeatureFlagChanges.has(prop) ? prop in _this.props : hasNextValue;
            if (keepKey === prop in _this.props && isStorageValueEqual(nextValue, _this.props[prop])) {
                return;
            }
            if (keepKey) {
                _this._setProp(prop, nextValue, false);
            }
            else {
                _this._deleteProp(prop, false);
            }
            changed = true;
        });
        if (changed && notify) {
            this._crossTabFeatureFlagHandlers.forEach(function (handler) { return handler(); });
        }
        return changed;
    };
    PostHogPersistence.prototype._reconcileCrossTabFeatureFlagPropertiesBeforeWrite = function () {
        var _this = this;
        // localStorage has no atomic read/merge/write operation. This reconciliation
        // closes normal event and debounce windows, but two tabs that flush at the
        // same instant can still race and leave the last complete blob on disk.
        // Solving that requires a separate cross-tab locking or versioning protocol.
        if (!this._splitStorageEligible) {
            return false;
        }
        try {
            this._crossTabFeatureFlagIdentityMismatch = false;
            var mainValue = storage_1.localStore._get(this._name);
            var mainState = this._slotWriteState(MAIN_STORAGE_SLOT);
            var mainEntry_1;
            var mainChanged_1 = false;
            var changed_1 = false;
            if (mainValue !== mainState.storageValue) {
                if ((0, core_1.isNull)(mainValue)) {
                    // Removing persistence disables or resets durable state; it is
                    // not an authoritative empty feature-flag evaluation.
                    mainState.storageValue = null;
                }
                else {
                    mainEntry_1 = parseStorageValue(mainValue);
                    this._crossTabFeatureFlagIdentityMismatch =
                        !this._localIdentityChangePending && this._hasCrossTabFeatureFlagIdentityMismatch(mainEntry_1);
                    if (this._crossTabFeatureFlagIdentityMismatch) {
                        return false;
                    }
                    changed_1 = this._mergeCrossTabFeatureFlagProperties(mainEntry_1, MAIN_STORAGE_SLOT, false);
                    if (!this._splitStorage || isStorageValueEqual(this._partitionProps().main, mainEntry_1)) {
                        this._rememberCrossTabStorageFingerprint(mainEntry_1, MAIN_STORAGE_SLOT, true, mainValue);
                    }
                    else {
                        mainState.storageValue = mainValue;
                    }
                    mainChanged_1 = true;
                }
            }
            if (this._splitStorage) {
                persistence_key_policy_1.PERSISTENCE_STORAGE_GROUPS.forEach(function (group) {
                    var groupValue = storage_1.localStore._get(_this._groupEntryName(group));
                    var groupState = _this._slotWriteState(group);
                    var mainFallback = mainEntry_1 || {};
                    var mainCarriesGroup = mainChanged_1 &&
                        CROSS_TAB_FEATURE_FLAG_KEYS.some(function (key) { var _a; return ((_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key)) === null || _a === void 0 ? void 0 : _a.storageGroup) === group && key in mainFallback; });
                    if (groupValue === groupState.storageValue && !mainCarriesGroup) {
                        return;
                    }
                    if ((0, core_1.isNull)(groupValue)) {
                        groupState.storageValue = null;
                        groupState.persisted = false;
                        // Before the first split write, grouped keys can still live
                        // in the main blob. Use them as the migration fallback.
                        if (mainCarriesGroup) {
                            changed_1 = _this._mergeCrossTabFeatureFlagProperties(mainFallback, group, false) || changed_1;
                        }
                        return;
                    }
                    var groupEntry = parseStorageValue(groupValue);
                    changed_1 = _this._mergeCrossTabFeatureFlagProperties(groupEntry, group, false) || changed_1;
                    _this._rememberCrossTabStorageFingerprint(groupEntry, group, true, groupValue);
                });
            }
            return changed_1;
        }
        catch (_a) {
            return false;
        }
    };
    PostHogPersistence.prototype._setCrossTabFeatureFlagChangesPending = function (key, changes) {
        this._pendingCrossTabFeatureFlagChanges.set(key, changes);
        var policy = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key);
        if (!(policy === null || policy === void 0 ? void 0 : policy.volatile)) {
            var group = this._splitStorage ? policy === null || policy === void 0 ? void 0 : policy.storageGroup : undefined;
            this._slotWriteState(group || MAIN_STORAGE_SLOT).fingerprint = undefined;
            this._markGroupDirty(key);
        }
    };
    PostHogPersistence.prototype._markAllCrossTabFeatureFlagChangesPending = function () {
        var _this = this;
        // remove() either resets slot bookkeeping or deliberately retains group
        // fingerprints. Do not invalidate retained groups just for the transaction.
        CROSS_TAB_FEATURE_FLAG_KEYS.forEach(function (key) { return _this._pendingCrossTabFeatureFlagChanges.set(key, true); });
    };
    PostHogPersistence.prototype._markLoadedCrossTabFeatureFlagChangesPending = function () {
        var _this = this;
        if (!this._splitStorageEligible) {
            return;
        }
        try {
            var mainValue = storage_1.localStore._get(this._name);
            var mainEntry_2 = parseStorageValue(mainValue);
            this._slotWriteState(MAIN_STORAGE_SLOT).storageValue = mainValue;
            CROSS_TAB_FEATURE_FLAG_KEYS.forEach(function (key) {
                var _a;
                var group = _this._splitStorage ? (_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key)) === null || _a === void 0 ? void 0 : _a.storageGroup : undefined;
                var groupValue = group ? storage_1.localStore._get(_this._groupEntryName(group)) : null;
                if (group) {
                    var groupState = _this._slotWriteState(group);
                    groupState.storageValue = groupValue;
                    groupState.persisted = !(0, core_1.isNull)(groupValue);
                }
                var storedEntry = group && !(0, core_1.isNull)(groupValue) ? parseStorageValue(groupValue) : mainEntry_2;
                if (key in _this.props) {
                    _this._markPendingCrossTabFeatureFlagChanges(key, storedEntry[key], _this.props[key]);
                }
                else if (key in storedEntry) {
                    _this._setCrossTabFeatureFlagChangesPending(key, true);
                }
            });
        }
        catch (_a) { }
    };
    PostHogPersistence.prototype._mergePendingCrossTabFeatureFlagChanges = function (key, nextValue) {
        var pendingChanges = this._pendingCrossTabFeatureFlagChanges.get(key);
        if (!pendingChanges) {
            return nextValue;
        }
        if (pendingChanges === true) {
            return this.props[key];
        }
        if (key === constants_1.PERSISTENCE_ACTIVE_FEATURE_FLAGS) {
            var merged_1 = new Set((0, core_1.isArray)(nextValue) ? nextValue : []);
            var local_1 = new Set((0, core_1.isArray)(this.props[key]) ? this.props[key] : []);
            pendingChanges.forEach(function (flag) { return (local_1.has(flag) ? merged_1.add(flag) : merged_1.delete(flag)); });
            return Array.from(merged_1);
        }
        var merged = (0, core_2.isObject)(nextValue) ? __assign({}, nextValue) : {};
        var local = (0, core_2.isObject)(this.props[key]) ? this.props[key] : {};
        pendingChanges.forEach(function (property) {
            if (property in local) {
                merged[property] = local[property];
            }
            else {
                delete merged[property];
            }
        });
        return merged;
    };
    PostHogPersistence.prototype._markPendingCrossTabFeatureFlagChanges = function (key, previousValue, nextValue) {
        var _a;
        if (!isCrossTabFeatureFlagKey(key)) {
            return;
        }
        var existingPendingChanges = this._pendingCrossTabFeatureFlagChanges.get(key);
        if (existingPendingChanges === true) {
            return;
        }
        // Recompute against the latest durable value instead of accumulating
        // mutations. A local false -> true -> false sequence is no longer pending
        // when storage still contains false, so a later sibling update can win.
        var durableValue = previousValue;
        if (this._splitStorageEligible) {
            try {
                var group = this._splitStorage ? (_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key)) === null || _a === void 0 ? void 0 : _a.storageGroup : undefined;
                var storageKey = group ? this._groupEntryName(group) : this._name;
                durableValue = parseStorageValue(storage_1.localStore._get(storageKey))[key];
            }
            catch (_b) { }
        }
        var pendingChanges = new Set(existingPendingChanges || []);
        if (key === constants_1.PERSISTENCE_ACTIVE_FEATURE_FLAGS) {
            if ((!(0, core_1.isUndefined)(previousValue) && !(0, core_1.isArray)(previousValue)) ||
                (!(0, core_1.isUndefined)(durableValue) && !(0, core_1.isArray)(durableValue)) ||
                !(0, core_1.isArray)(nextValue)) {
                this._setCrossTabFeatureFlagChangesPending(key, true);
                return;
            }
            var previous_1 = new Set(previousValue || []);
            var durable_1 = new Set(durableValue || []);
            var next_1 = new Set(nextValue);
            new Set(__spreadArray(__spreadArray([], __read(previous_1), false), __read(next_1), false)).forEach(function (flag) {
                if (previous_1.has(flag) !== next_1.has(flag)) {
                    pendingChanges.add(flag);
                }
            });
            pendingChanges.forEach(function (flag) {
                if (durable_1.has(flag) === next_1.has(flag)) {
                    pendingChanges.delete(flag);
                }
            });
        }
        else if ((0, core_2.isObject)(nextValue)) {
            if ((!(0, core_1.isUndefined)(previousValue) && !(0, core_2.isObject)(previousValue)) ||
                ((0, core_1.isUndefined)(previousValue) && (0, core_2.isEmptyObject)(nextValue) && (0, core_1.isUndefined)(durableValue))) {
                this._setCrossTabFeatureFlagChangesPending(key, true);
                return;
            }
            var previous_2 = (0, core_2.isObject)(previousValue) ? previousValue : {};
            var durable_2 = (0, core_2.isObject)(durableValue) ? durableValue : {};
            new Set(__spreadArray(__spreadArray([], __read(Object.keys(previous_2)), false), __read(Object.keys(nextValue)), false)).forEach(function (property) {
                if (property in previous_2 !== property in nextValue ||
                    !isStorageValueEqual(previous_2[property], nextValue[property])) {
                    pendingChanges.add(property);
                }
            });
            pendingChanges.forEach(function (property) {
                if (property in durable_2 === property in nextValue &&
                    isStorageValueEqual(durable_2[property], nextValue[property])) {
                    pendingChanges.delete(property);
                }
            });
        }
        else {
            if (!isStorageValueEqual(durableValue, nextValue)) {
                this._setCrossTabFeatureFlagChangesPending(key, true);
            }
            else {
                this._pendingCrossTabFeatureFlagChanges.delete(key);
            }
            return;
        }
        if (pendingChanges.size) {
            this._setCrossTabFeatureFlagChangesPending(key, pendingChanges);
        }
        else {
            this._pendingCrossTabFeatureFlagChanges.delete(key);
        }
    };
    PostHogPersistence.prototype._saveDebounceMs = function () {
        var _a;
        var value = (_a = this._config) === null || _a === void 0 ? void 0 : _a.persistence_save_debounce_ms;
        return (0, core_1.isNumber)(value) && value > 0 ? value : 0;
    };
    PostHogPersistence.prototype._rememberCurrentCookieProperties = function (props) {
        if (!this._config.cookieWinsOnConflict || this._config.persistence.toLowerCase() !== 'localstorage+cookie') {
            return;
        }
        if (props) {
            // Remember exactly what this write attempted instead of rereading the
            // shared cookie. A sibling can update the cookie immediately after our
            // write; recording that later value here would hide an unseen update.
            try {
                var cookieProperties = (0, storage_1.getCookiePersistedProperties)(props, this._config.cookie_persisted_properties || []);
                var customCookieProperties = this._config.cookie_persisted_properties || [];
                var metadata = (0, storage_1.getCookiePersistedPropertiesMetadata)(cookieProperties, customCookieProperties);
                var expectedFingerprint = JSON.stringify(cookieProperties) + '|' + JSON.stringify(metadata);
                // `localStorage+cookie._set` reports localStorage success even if
                // its best-effort cookie mirror fails. Advance the observed
                // fingerprint only when the shared cookie actually contains this
                // exact snapshot. A different value may be either the old cookie
                // or a sibling write, and must remain eligible for reconciliation.
                var currentCookieValue = storage_1.cookieStore._get(this._name) || undefined;
                if (currentCookieValue &&
                    (0, storage_1.getCookiePropertiesFingerprint)(this._name, currentCookieValue) === expectedFingerprint) {
                    this._lastSeenCookiePropertiesFingerprint = expectedFingerprint;
                    this._lastSeenMainCookieValue = currentCookieValue;
                }
            }
            catch (_a) { }
            return;
        }
        try {
            var cookieValue = storage_1.cookieStore._get(this._name) || undefined;
            this._lastSeenCookiePropertiesFingerprint = cookieValue
                ? (0, storage_1.getCookiePropertiesFingerprint)(this._name, cookieValue)
                : undefined;
            this._lastSeenMainCookieValue = cookieValue;
        }
        catch (_b) { }
    };
    /**
     * Adopt shared-cookie changes made by a sibling subdomain without replacing
     * localStorage-only or pending in-memory properties. Returns true when a new
     * non-empty cookie snapshot was observed.
     */
    PostHogPersistence.prototype.syncCookieProperties = function () {
        return this._syncCookieProperties(this._config);
    };
    PostHogPersistence.prototype._syncCookieProperties = function (config, ignoreDisabled) {
        var _this = this;
        if (ignoreDisabled === void 0) { ignoreDisabled = false; }
        if ((this._disabled && !ignoreDisabled) ||
            this._cookieSyncSuppressed ||
            !config.cookieWinsOnConflict ||
            config.persistence.toLowerCase() !== 'localstorage+cookie') {
            return false;
        }
        var cookieValue;
        try {
            cookieValue = storage_1.cookieStore._get(this._name) || undefined;
        }
        catch (_a) { }
        if (!cookieValue || cookieValue === this._lastSeenMainCookieValue) {
            // Ignore sidecar-only changes. Metadata is meaningful only together
            // with a new main snapshot, and treating it as a sibling identity
            // update could roll back a local write when the main mirror failed.
            return false;
        }
        var cookieFingerprint = (0, storage_1.getCookiePropertiesFingerprint)(this._name, cookieValue);
        var cookieProperties;
        try {
            cookieProperties = (0, storage_1.getSafeCookieProperties)(JSON.parse(cookieValue));
        }
        catch (_b) {
            return false;
        }
        // If the main cookie changed while its metadata was read, retry on the
        // next synchronization rather than applying the old values while marking
        // the new snapshot as observed.
        if ((storage_1.cookieStore._get(this._name) || undefined) !== cookieValue) {
            return false;
        }
        this._lastSeenCookiePropertiesFingerprint = cookieFingerprint;
        this._lastSeenMainCookieValue = cookieValue;
        var metadataState = (0, storage_1.getCookiePersistedPropertiesMetadataState)(this._name, cookieValue);
        var authoritativeCookieProperties = __spreadArray(__spreadArray([], __read(storage_1.COOKIE_PERSISTED_PROPERTIES), false), __read(metadataState.properties), false);
        var invalidCookieProperties = {};
        Object.keys(cookieProperties).forEach(function (key) {
            var value = cookieProperties[key];
            if ((0, core_1.isUndefined)(value) ||
                (0, core_1.isNull)(value) ||
                value === '' ||
                (key === constants_1.USER_STATE && value !== constants_1.USER_STATE_ANONYMOUS && value !== constants_1.USER_STATE_IDENTIFIED)) {
                invalidCookieProperties[key] = true;
                delete cookieProperties[key];
            }
        });
        if ((0, core_2.isEmptyObject)(cookieProperties)) {
            return false;
        }
        var hasValidCookieIdentity = constants_1.DISTINCT_ID in cookieProperties ||
            cookieProperties[constants_1.USER_STATE] === constants_1.USER_STATE_ANONYMOUS ||
            cookieProperties[constants_1.USER_STATE] === constants_1.USER_STATE_IDENTIFIED;
        var previousProps = this.props;
        var previousDistinctId = previousProps[constants_1.DISTINCT_ID];
        var previousUserState = previousProps[constants_1.USER_STATE];
        var nextProps = (0, general_utils_1.extend)({}, previousProps);
        var cookiePersistedProperties = __spreadArray(__spreadArray([], __read(storage_1.COOKIE_PERSISTED_PROPERTIES), false), __read((config.cookie_persisted_properties || [])), false);
        cookiePersistedProperties.forEach(function (key) {
            if (authoritativeCookieProperties.indexOf(key) !== -1 &&
                !(key in cookieProperties) &&
                !invalidCookieProperties[key] &&
                (hasValidCookieIdentity || (key !== constants_1.DISTINCT_ID && key !== constants_1.USER_STATE))) {
                var localValue = nextProps[key];
                var legacyFalsyBuiltIn = !metadataState.isValid &&
                    storage_1.COOKIE_PERSISTED_PROPERTIES.indexOf(key) !== -1 &&
                    (localValue === false || localValue === 0);
                if (!legacyFalsyBuiltIn) {
                    delete nextProps[key];
                }
            }
        });
        this.props = (0, general_utils_1.extend)(nextProps, cookieProperties);
        CROSS_TAB_FEATURE_FLAG_KEYS.forEach(function (key) {
            var hadPreviousValue = key in previousProps;
            var hasNextValue = key in _this.props;
            if (hadPreviousValue !== hasNextValue || !isStorageValueEqual(previousProps[key], _this.props[key])) {
                if (hasNextValue) {
                    _this._markPendingCrossTabFeatureFlagChanges(key, previousProps[key], _this.props[key]);
                }
                else {
                    _this._setCrossTabFeatureFlagChangesPending(key, true);
                }
            }
        });
        // Older writers can omit $user_state entirely. Once that authoritative
        // omission removes a prior identified state, represent it explicitly as
        // anonymous so reset cleanup (including $groups) runs consistently.
        if (hasValidCookieIdentity && !(constants_1.USER_STATE in cookieProperties) && !(constants_1.USER_STATE in this.props)) {
            this._setProp(constants_1.USER_STATE, constants_1.USER_STATE_ANONYMOUS);
        }
        var nextDistinctId = this.props[constants_1.DISTINCT_ID];
        var nextUserState = this.props[constants_1.USER_STATE];
        if (hasValidCookieIdentity && (nextDistinctId !== previousDistinctId || nextUserState !== previousUserState)) {
            this._localIdentityChangePending = true;
            this._cookieIdentityChangePending = true;
            storage_1.sessionStore._set(getCookieIdentityChangePendingName(this._name), true);
            this._deleteProp(constants_1.STORED_PERSON_PROPERTIES_KEY);
            this._deleteProp(constants_1.PERSISTENCE_ACTIVE_FEATURE_FLAGS);
            this._deleteProp(constants_1.ENABLED_FEATURE_FLAGS);
            this._deleteProp(constants_1.PERSISTENCE_FEATURE_FLAG_DETAILS);
            this._deleteProp(constants_1.PERSISTENCE_FEATURE_FLAG_PAYLOADS);
            this._deleteProp(constants_1.PERSISTENCE_FEATURE_FLAG_REQUEST_ID);
            this._deleteProp(constants_1.PERSISTENCE_FEATURE_FLAG_EVALUATED_AT);
            this._deleteProp(constants_1.PERSISTENCE_FEATURE_FLAG_ERRORS);
            this._deleteProp(constants_1.FLAG_CALL_REPORTED);
            var siblingReset = nextUserState === constants_1.USER_STATE_ANONYMOUS &&
                (previousUserState === constants_1.USER_STATE_IDENTIFIED ||
                    cookieProperties[constants_1.USER_STATE] === constants_1.USER_STATE_ANONYMOUS ||
                    (!(0, core_1.isUndefined)(previousDistinctId) && nextDistinctId !== previousDistinctId));
            if (siblingReset) {
                // reset() clears event-visible persistence. Mirror that for a
                // sibling reset without discarding hidden SDK state or values
                // that the new shared-cookie snapshot explicitly carries.
                clearStaleEventProperties(this.props, cookieProperties);
                this._deleteProp(constants_1.STORED_GROUP_PROPERTIES_KEY);
            }
            // `$user_id` is localStorage-only, but it is bound to the current
            // identity. Never carry the previous logged-in user across a sibling
            // identify/reset adopted from the shared cookie.
            if (nextUserState === constants_1.USER_STATE_IDENTIFIED) {
                this.props.$user_id = nextDistinctId;
            }
            else {
                delete this.props.$user_id;
            }
            this._deleteProp(constants_1.ALIAS_ID_KEY);
        }
        return true;
    };
    PostHogPersistence.prototype.consumeCookieIdentityChange = function () {
        var pendingName = getCookieIdentityChangePendingName(this._name);
        var changed = this._cookieIdentityChangePending || !!storage_1.sessionStore._get(pendingName);
        this._cookieIdentityChangePending = false;
        if (changed) {
            storage_1.sessionStore._remove(pendingName);
        }
        return changed;
    };
    PostHogPersistence.prototype._beginCookieSyncSuppression = function (ignoreDisabled) {
        if (ignoreDisabled === void 0) { ignoreDisabled = false; }
        if (!this._cookieSyncSuppressed &&
            (!this._disabled || ignoreDisabled) &&
            this._config.cookieWinsOnConflict &&
            this._config.persistence.toLowerCase() === 'localstorage+cookie') {
            this._cookieSyncSuppressed = true;
            return true;
        }
        return false;
    };
    PostHogPersistence.prototype._publishSuppressedCookieSnapshot = function () {
        if (!this._cookieSyncSuppressed) {
            return;
        }
        if (!(0, core_1.isUndefined)(this._pendingSaveTimer)) {
            clearTimeout(this._pendingSaveTimer);
            this._pendingSaveTimer = undefined;
        }
        // Force the complete local snapshot through without reconciling a
        // stale sibling write observed during this authoritative transaction.
        delete this._slotState[MAIN_STORAGE_SLOT];
        this._writeNow(true);
    };
    PostHogPersistence.prototype._endCookieSyncSuppression = function (publish) {
        if (publish === void 0) { publish = true; }
        if (!this._cookieSyncSuppressed) {
            return;
        }
        try {
            if (publish) {
                this._publishSuppressedCookieSnapshot();
            }
            else if (!(0, core_1.isUndefined)(this._pendingSaveTimer)) {
                // A failed identify/reset may have scheduled saves before it
                // threw. Do not let one run after suppression is released and
                // publish the incomplete transaction asynchronously.
                clearTimeout(this._pendingSaveTimer);
                this._pendingSaveTimer = undefined;
            }
        }
        finally {
            this._cookieSyncSuppressed = false;
        }
    };
    /**
     * Returns whether persistence is disabled. Only available in SDKs > 1.257.1. Do not use on extensions, otherwise
     * it'll break backwards compatibility for any version before 1.257.1.
     */
    PostHogPersistence.prototype.isDisabled = function () {
        return !!this._disabled;
    };
    PostHogPersistence.prototype._buildStorage = function (config) {
        if (CASE_INSENSITIVE_PERSISTENCE_TYPES.indexOf(config['persistence'].toLowerCase()) === -1) {
            logger_1.logger.critical('Unknown persistence type ' + config['persistence'] + '; falling back to localStorage+cookie');
            config['persistence'] = 'localStorage+cookie';
        }
        // Create this before hand to avoid creating it multiple times
        // Creating it inside each individual condition below is too complicated and will break backwards compatibility
        // so create it once for this specific config and use it if necessary
        var localPlusCookieStore = (0, storage_1.createLocalPlusCookieStore)(config['cookie_persisted_properties'] || [], config.cookieWinsOnConflict);
        var store;
        // The flag split is only meaningful on a localStorage-backed
        // store: it is the one that broadcasts large cross-tab `storage` events.
        // cookie can't hold the cluster, memory/sessionStorage don't broadcast.
        var splitEligible = false;
        // We handle storage type in a case-insensitive way for backwards compatibility
        var storage_type = config['persistence'].toLowerCase();
        if (storage_type === 'localstorage' && storage_1.localStore._is_supported()) {
            store = storage_1.localStore;
            splitEligible = true;
        }
        else if (storage_type === 'localstorage+cookie' && localPlusCookieStore._is_supported()) {
            store = localPlusCookieStore;
            splitEligible = true;
        }
        else if (storage_type === 'sessionstorage' && storage_1.sessionStore._is_supported()) {
            store = storage_1.sessionStore;
        }
        else if (storage_type === 'memory') {
            store = storage_1.memoryStore;
        }
        else if (storage_type === 'cookie' && storage_1.cookieStore._is_supported()) {
            store = storage_1.cookieStore;
        }
        else if (localPlusCookieStore._is_supported()) {
            // selected storage type wasn't supported, fallback to 'localstorage+cookie' if possible
            store = localPlusCookieStore;
            splitEligible = true;
        }
        else if (storage_1.cookieStore._is_supported()) {
            store = storage_1.cookieStore;
        }
        else {
            // Neither web storage nor cookies are available -- e.g. a page served from a
            // `data:` URL, where Chrome disables both. Falling back to cookieStore here left
            // every read and write silently failing, so degrade to in-memory persistence.
            store = storage_1.memoryStore;
        }
        this._splitStorageEligible = splitEligible;
        return store;
    };
    PostHogPersistence.prototype._groupEntryName = function (group) {
        return "".concat(this._name, "__").concat(group);
    };
    // The split is on only when the resolved backend can host it (localStorage /
    // localStorage+cookie, set by `_buildStorage` into `_splitStorageEligible`)
    // AND the config opts in. Resolved here so the constructor and the runtime
    // `update_config` toggle can never disagree about whether the split is active.
    PostHogPersistence.prototype._resolveSplitStorage = function (config) {
        return this._splitStorageEligible && !!config['split_storage'];
    };
    PostHogPersistence.prototype.properties = function () {
        var p = {};
        (0, general_utils_1.each)(this.props, function (v, k) {
            var _a;
            var policy = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(k);
            if (!policy || policy.exposure === 'event') {
                if ((_a = policy === null || policy === void 0 ? void 0 : policy.shouldSkipFromEventProperties) === null || _a === void 0 ? void 0 : _a.call(policy, v)) {
                    return;
                }
                // Unknown keys are treated as user-defined super properties and remain event-visible.
                p[k] = v;
            }
        });
        return p;
    };
    /**
     * Reload persisted properties from storage.
     *
     * @param allowDisabled - Read while this instance is disabled when shared consent changed in another tab
     * and the in-memory disabled state has not yet been reconciled.
     */
    PostHogPersistence.prototype.load = function (allowDisabled) {
        var _this = this;
        var _a, _b;
        if (allowDisabled === void 0) { allowDisabled = false; }
        if (this._disabled && !allowDisabled) {
            return;
        }
        var reconcileCookieIdentity = this._config.cookieWinsOnConflict && this._config.persistence.toLowerCase() === 'localstorage+cookie';
        var localEntryBeforeMerge = reconcileCookieIdentity ? storage_1.localStore._parse(this._name) : null;
        var cookieEntryBeforeMerge = {};
        if (reconcileCookieIdentity) {
            try {
                cookieEntryBeforeMerge = (0, storage_1.getSafeCookieProperties)(storage_1.cookieStore._parse(this._name));
                (0, general_utils_1.each)(cookieEntryBeforeMerge, function (value, key) {
                    if ((0, core_1.isUndefined)(value) || (0, core_1.isNull)(value) || value === '') {
                        delete cookieEntryBeforeMerge[key];
                    }
                });
            }
            catch (_c) { }
        }
        var entry = this._storage._parse(this._name);
        if (entry) {
            this.props = (0, general_utils_1.extend)({}, entry);
        }
        if (this._splitStorage) {
            this._loadGroupEntries();
        }
        if (reconcileCookieIdentity && entry) {
            var previousDistinctId = localEntryBeforeMerge === null || localEntryBeforeMerge === void 0 ? void 0 : localEntryBeforeMerge[constants_1.DISTINCT_ID];
            var previousUserState = (_a = localEntryBeforeMerge === null || localEntryBeforeMerge === void 0 ? void 0 : localEntryBeforeMerge[constants_1.USER_STATE]) !== null && _a !== void 0 ? _a : constants_1.USER_STATE_ANONYMOUS;
            var nextDistinctId = entry[constants_1.DISTINCT_ID];
            var nextUserState = (_b = entry[constants_1.USER_STATE]) !== null && _b !== void 0 ? _b : constants_1.USER_STATE_ANONYMOUS;
            if (nextDistinctId !== previousDistinctId || nextUserState !== previousUserState) {
                this._cookieIdentityChangePending = true;
                storage_1.sessionStore._set(getCookieIdentityChangePendingName(this._name), true);
                var nextProps_1 = (0, general_utils_1.extend)({}, this.props);
                storage_1.COOKIE_IDENTITY_BOUND_LOCAL_PROPERTIES.forEach(function (key) { return delete nextProps_1[key]; });
                var siblingReset = nextUserState === constants_1.USER_STATE_ANONYMOUS &&
                    (previousUserState === constants_1.USER_STATE_IDENTIFIED ||
                        cookieEntryBeforeMerge[constants_1.USER_STATE] === constants_1.USER_STATE_ANONYMOUS ||
                        (!(0, core_1.isUndefined)(previousDistinctId) && nextDistinctId !== previousDistinctId));
                if (siblingReset) {
                    clearStaleEventProperties(nextProps_1, cookieEntryBeforeMerge);
                    delete nextProps_1[constants_1.STORED_GROUP_PROPERTIES_KEY];
                }
                this.props = nextProps_1;
                // Split entries are loaded after the main blob, so remove the
                // previous identity's grouped flag state from both memory and its
                // localStorage slot before a future load can restore it again.
                var affectedGroups_1 = new Set();
                storage_1.COOKIE_IDENTITY_BOUND_LOCAL_PROPERTIES.forEach(function (key) {
                    var _a;
                    var group = (_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key)) === null || _a === void 0 ? void 0 : _a.storageGroup;
                    if (group) {
                        affectedGroups_1.add(group);
                    }
                });
                affectedGroups_1.forEach(function (group) {
                    var groupProps = {};
                    (0, general_utils_1.each)(_this.props, function (value, key) {
                        var _a;
                        if (((_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key)) === null || _a === void 0 ? void 0 : _a.storageGroup) === group) {
                            groupProps[key] = value;
                        }
                    });
                    if ((0, core_2.isEmptyObject)(groupProps)) {
                        storage_1.localStore._remove(_this._groupEntryName(group));
                        _this._slotState[group] = {};
                    }
                    else if (storage_1.localStore._set(_this._groupEntryName(group), groupProps)) {
                        _this._slotState[group] = {
                            persisted: true,
                            fingerprint: _this._entryFingerprint(groupProps, group),
                        };
                    }
                });
            }
        }
        // A write can durably adopt the new main-cookie identity before core has
        // cleared the separate session persistence. Keep that cleanup pending
        // across reloads in this browser tab.
        if (storage_1.sessionStore._get(getCookieIdentityChangePendingName(this._name))) {
            this._cookieIdentityChangePending = true;
        }
        // `_parse()` may have read a different cookie than a reread here if a
        // sibling writes concurrently. Let the first synchronization compare the
        // current shared cookie with the loaded props instead of marking an
        // arbitrary later snapshot as observed.
    };
    // Merge each group entry over `props`, which already holds the main blob.
    // On a first upgrade the main blob may still carry the old flag
    // values; a present group entry wins, so we resolve
    // `props[key] = group[key] ?? main[key]` in a single pass (the "check the
    // old key once" migration). The first `save()` then strips the keys from
    // the main blob. Group entries are localStorage-only (read via `localStore`
    // directly, never the cookie or the localPlusCookie re-write-on-parse path).
    PostHogPersistence.prototype._loadGroupEntries = function () {
        var e_1, _a;
        try {
            for (var PERSISTENCE_STORAGE_GROUPS_1 = __values(persistence_key_policy_1.PERSISTENCE_STORAGE_GROUPS), PERSISTENCE_STORAGE_GROUPS_1_1 = PERSISTENCE_STORAGE_GROUPS_1.next(); !PERSISTENCE_STORAGE_GROUPS_1_1.done; PERSISTENCE_STORAGE_GROUPS_1_1 = PERSISTENCE_STORAGE_GROUPS_1.next()) {
                var group = PERSISTENCE_STORAGE_GROUPS_1_1.value;
                // `localStore._parse` returns `{}` (not null) for a missing key, so
                // gate on real content: an empty/absent entry is not "persisted" and
                // must not be tracked, or `_writeNowSplit` would re-create it as `{}`.
                var groupEntry = storage_1.localStore._parse(this._groupEntryName(group));
                if (groupEntry && !(0, core_2.isEmptyObject)(groupEntry)) {
                    var state = this._slotWriteState(group);
                    state.persisted = true;
                    // Seed the no-op fingerprint with the snapshot we just read, so the
                    // first frequent main-blob save at startup (before fresh flags
                    // return from the network) recognises an unchanged flag entry and
                    // neither re-serializes nor re-broadcasts it to every open tab.
                    // Only safe when the main blob carries no key for this group: a
                    // leftover (partial migration, or a stale tab that wrote a flag key
                    // back to main) makes the partitioned payload differ from what is on
                    // disk, so the entry must still be written. Leaving it unseeded then
                    // lets the first save's fingerprint check write the merged payload
                    // through — completing the migration / healing the orphan. The
                    // `_writeEntry` group fast-path skips on `!dirty && fingerprint set`,
                    // which would otherwise short-circuit that write before the
                    // fingerprint is even compared.
                    if (!this._mainCarriesGroupKey(group)) {
                        state.fingerprint = this._entryFingerprint(groupEntry, group);
                    }
                    // The group entry is normally the migrated-forward home and wins
                    // over the main blob. The exception: a group that stamps a
                    // freshness timestamp (flags: $feature_flag_evaluated_at) lets us
                    // detect when a gate-off / older-SDK tab wrote a fresher payload
                    // back into the main blob — then we keep the main blob and let the
                    // next save heal the group entry. With no timestamp on either side
                    // (migration leftover) the group wins.
                    if (!this._groupEntryIsStale(group, groupEntry)) {
                        (0, general_utils_1.extend)(this.props, groupEntry);
                    }
                }
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (PERSISTENCE_STORAGE_GROUPS_1_1 && !PERSISTENCE_STORAGE_GROUPS_1_1.done && (_a = PERSISTENCE_STORAGE_GROUPS_1.return)) _a.call(PERSISTENCE_STORAGE_GROUPS_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
    };
    // True when the already-loaded main blob still holds a key belonging to this
    // group — a migration leftover the next save must fold into the group entry.
    // Checked before the group entry is merged in, so it sees only the main blob's
    // own keys (sibling groups carry a different storageGroup and never match).
    PostHogPersistence.prototype._mainCarriesGroupKey = function (group) {
        return Object.keys(this.props).some(function (key) { var _a; return ((_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key)) === null || _a === void 0 ? void 0 : _a.storageGroup) === group; });
    };
    PostHogPersistence.prototype._groupEntryIsStale = function (group, groupEntry) {
        var freshnessKey = GROUP_FRESHNESS_KEY[group];
        if (!freshnessKey) {
            return false;
        }
        var groupLoadedAt = groupEntry[freshnessKey];
        var mainLoadedAt = this.props[freshnessKey];
        return (0, core_1.isNumber)(groupLoadedAt) && (0, core_1.isNumber)(mainLoadedAt) && mainLoadedAt > groupLoadedAt;
    };
    /**
     * Refresh a single key from on-disk storage into `this.props` without
     * touching the rest. Used by `SessionIdManager` on the cross-tab idle
     * path so we can pick up a sibling tab's SESSION_ID write without
     * either:
     *  - flushing our own (potentially stale) whole-props blob to storage
     *    via `flush()`, which would clobber the sibling's write, or
     *  - replacing all of `props` via `load()`, which would discard any
     *    in-memory writes that haven't yet been debounced to storage.
     */
    PostHogPersistence.prototype.refreshKey = function (prop) {
        var _a;
        if (this._disabled) {
            return;
        }
        var group = this._splitStorage ? (_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(prop)) === null || _a === void 0 ? void 0 : _a.storageGroup : undefined;
        var entry = group ? storage_1.localStore._parse(this._groupEntryName(group)) : this._storage._parse(this._name);
        if (entry && prop in entry) {
            this._setProp(prop, entry[prop]);
            return;
        }
        // A grouped key that has not migrated yet still lives in the main blob;
        // check there once before concluding a sibling removed it.
        if (group) {
            var mainEntry = this._storage._parse(this._name);
            if (mainEntry && prop in mainEntry) {
                this._setProp(prop, mainEntry[prop]);
                return;
            }
        }
        this._deleteProp(prop);
    };
    /**
     * NOTE: Saving frequently causes issues with Recordings and Consent Management Platform (CMP) tools which
     * observe cookie changes, and modify their UI, often causing infinite loops.
     * As such callers of this should ideally check that the data has changed beforehand
     */
    PostHogPersistence.prototype.save = function () {
        var _this = this;
        if (this._disabled) {
            return;
        }
        var debounce = this._saveDebounceMs();
        if (debounce <= 0) {
            this._writeNow();
            return;
        }
        // Coalesce: if a flush is already scheduled, the latest `props`
        // will be picked up when the timer fires. Otherwise schedule one.
        if (!(0, core_1.isUndefined)(this._pendingSaveTimer)) {
            return;
        }
        this._pendingSaveTimer = setTimeout(function () {
            _this._pendingSaveTimer = undefined;
            _this._writeNow();
        }, debounce);
    };
    /**
     * Force any pending debounced save to land in storage immediately.
     * No-op when there is no pending timer — crucially, this means the
     * `beforeunload` / `pagehide` listeners installed in the constructor
     * cannot accidentally resurrect a storage entry that `remove()` or
     * `clear()` just deleted. Without this guard, the listener would
     * call `_writeNow()` and write the in-memory `props` (now `{}`) back
     * to storage, breaking `posthog.reset()` / opt-out flows.
     */
    PostHogPersistence.prototype.flush = function () {
        if ((0, core_1.isUndefined)(this._pendingSaveTimer)) {
            return;
        }
        clearTimeout(this._pendingSaveTimer);
        this._pendingSaveTimer = undefined;
        this._writeNow();
    };
    PostHogPersistence.prototype._writeNow = function (forceSuppressedSnapshot) {
        if (forceSuppressedSnapshot === void 0) { forceSuppressedSnapshot = false; }
        if (this._disabled || (this._cookieSyncSuppressed && !forceSuppressedSnapshot)) {
            return;
        }
        // Reconcile immediately before writing as well as before capture. This
        // closes the debounce window where a sibling identify/reset could
        // otherwise be overwritten by this tab's pending stale whole-blob save.
        // A transaction ending suppression owns its complete snapshot and must
        // not adopt a sibling write that arrived while it was in progress.
        if (!forceSuppressedSnapshot) {
            this.syncCookieProperties();
        }
        var shouldReconcileCrossTabProperties = !forceSuppressedSnapshot && !this._storageMigrationInProgress;
        if (!shouldReconcileCrossTabProperties) {
            this._crossTabFeatureFlagIdentityMismatch = false;
        }
        var crossTabPropertiesChanged = shouldReconcileCrossTabProperties
            ? this._reconcileCrossTabFeatureFlagPropertiesBeforeWrite()
            : false;
        if (this._crossTabFeatureFlagIdentityMismatch) {
            if (this._config.debug) {
                logger_1.logger.warn('skipping persistence write because storage belongs to a different distinct ID');
            }
            return;
        }
        if (this._splitStorage) {
            this._writeNowSplit();
            if (crossTabPropertiesChanged) {
                this._crossTabFeatureFlagHandlers.forEach(function (handler) { return handler(); });
            }
            return;
        }
        var writeResult = this._writeEntry(this._storage, this._name, this.props, MAIN_STORAGE_SLOT);
        if (writeResult === 'written') {
            this._rememberCurrentCookieProperties(this.props);
        }
        if (writeResult !== 'failed') {
            this._pendingCrossTabFeatureFlagChanges.clear();
            this._localIdentityChangePending = false;
        }
        if (crossTabPropertiesChanged) {
            this._crossTabFeatureFlagHandlers.forEach(function (handler) { return handler(); });
        }
    };
    // Partition `props` by storage group and write each entry independently:
    // the main blob without the grouped keys (stripping them completes the
    // migration), plus one entry per group holding only its keys. Per-entry
    // fingerprints mean a main-blob change does not rewrite the rarely-changing
    // flag entries, and vice-versa — which is the whole bandwidth win.
    // Group entries go to `localStore` directly so they never hit the 4 KB
    // cookie or the localPlusCookie re-write-on-parse path. INVARIANT: keep group
    // entries on `localStore` — `_entryFingerprint` omits the cookie options from
    // group fingerprints precisely because cookies can never carry a group entry;
    // routing one to a cookie store would make a cookie-option change silently
    // skip a needed rewrite.
    PostHogPersistence.prototype._writeNowSplit = function () {
        var e_2, _a;
        var _this = this;
        var _b;
        var _c = this._partitionProps(), main = _c.main, groups = _c.groups;
        var mainWriteResult = this._writeEntry(this._storage, this._name, main, MAIN_STORAGE_SLOT);
        if (mainWriteResult === 'written') {
            this._rememberCurrentCookieProperties(main);
        }
        if (mainWriteResult !== 'failed') {
            this._localIdentityChangePending = false;
            CROSS_TAB_FEATURE_FLAG_KEYS.forEach(function (key) {
                var _a;
                if (!((_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key)) === null || _a === void 0 ? void 0 : _a.storageGroup)) {
                    _this._pendingCrossTabFeatureFlagChanges.delete(key);
                }
            });
        }
        var _loop_1 = function (group) {
            var groupProps = groups[group];
            // Don't materialize an entry just to hold `{}`: skip a group that is
            // empty and has never been persisted. Once a group has held content
            // we keep writing it (even when empty) so a later clear actually lands.
            if ((0, core_2.isEmptyObject)(groupProps) && !((_b = this_1._slotState[group]) === null || _b === void 0 ? void 0 : _b.persisted)) {
                CROSS_TAB_FEATURE_FLAG_KEYS.forEach(function (key) {
                    var _a;
                    if (((_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key)) === null || _a === void 0 ? void 0 : _a.storageGroup) === group) {
                        _this._pendingCrossTabFeatureFlagChanges.delete(key);
                    }
                });
                return "continue";
            }
            // `_writeEntry` marks the slot `persisted` (on `_slotState`) only
            // after a confirmed-successful `_set`, so a failed (e.g. quota) write
            // does not falsely mark the group as materialized on disk.
            var groupWriteResult = this_1._writeEntry(storage_1.localStore, this_1._groupEntryName(group), groupProps, group);
            if (groupWriteResult !== 'failed') {
                CROSS_TAB_FEATURE_FLAG_KEYS.forEach(function (key) {
                    var policy = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key);
                    if ((policy === null || policy === void 0 ? void 0 : policy.storageGroup) === group && (groupWriteResult === 'written' || !policy.volatile)) {
                        _this._pendingCrossTabFeatureFlagChanges.delete(key);
                    }
                });
            }
        };
        var this_1 = this;
        try {
            for (var PERSISTENCE_STORAGE_GROUPS_2 = __values(persistence_key_policy_1.PERSISTENCE_STORAGE_GROUPS), PERSISTENCE_STORAGE_GROUPS_2_1 = PERSISTENCE_STORAGE_GROUPS_2.next(); !PERSISTENCE_STORAGE_GROUPS_2_1.done; PERSISTENCE_STORAGE_GROUPS_2_1 = PERSISTENCE_STORAGE_GROUPS_2.next()) {
                var group = PERSISTENCE_STORAGE_GROUPS_2_1.value;
                _loop_1(group);
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (PERSISTENCE_STORAGE_GROUPS_2_1 && !PERSISTENCE_STORAGE_GROUPS_2_1.done && (_a = PERSISTENCE_STORAGE_GROUPS_2.return)) _a.call(PERSISTENCE_STORAGE_GROUPS_2);
            }
            finally { if (e_2) throw e_2.error; }
        }
    };
    PostHogPersistence.prototype._partitionProps = function () {
        var main = {};
        var groups = { flags: {}, surveys: {} };
        (0, general_utils_1.each)(this.props, function (value, key) {
            var _a;
            var group = (_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key)) === null || _a === void 0 ? void 0 : _a.storageGroup;
            if (group) {
                groups[group][key] = value;
            }
            else {
                main[key] = value;
            }
        });
        return { main: main, groups: groups };
    };
    // The no-op-rejection snapshot for an entry. The main entry can live in a
    // cookie, so its fingerprint also covers the cookie options (expire_days,
    // cross_subdomain, secure): a `set_config({ cookie_expiration })` must force
    // a rewrite even when props are unchanged, otherwise the cookie keeps its old
    // `Expires` header until some other prop changes. Group entries are
    // localStorage-only — cookie options never reach them, so excluding those
    // keeps a group fingerprint a pure function of its payload. That lets `load()`
    // seed it before the cookie options are even resolved, and keeps it stable
    // across the cookie-option setters that run during construction, so an
    // unchanged flag entry is neither re-serialized nor re-broadcast.
    PostHogPersistence.prototype._entryFingerprint = function (props, slot) {
        if (slot === MAIN_STORAGE_SLOT) {
            return JSON.stringify(props) + '|' + this._expire_days + '|' + this._cross_subdomain + '|' + this._secure;
        }
        // Volatile keys count by presence only: a write triggered by a real
        // content change records a fingerprint that stays valid while the
        // volatile values keep moving between writes, but adding or deleting a
        // volatile key still changes the fingerprint so the entry writes through.
        var stable = {};
        (0, general_utils_1.each)(props, function (value, key) {
            var _a;
            stable[key] = ((_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(key)) === null || _a === void 0 ? void 0 : _a.volatile) ? VOLATILE_FINGERPRINT_PLACEHOLDER : value;
        });
        return JSON.stringify(stable);
    };
    // No-op rejection: skip the write when nothing that affects this entry has
    // changed since the last successful write. Callers spam `save()` after every
    // property change, and many of those changes leave the storage payload
    // unchanged. Writing identical bytes to localStorage still fires a cross-tab
    // `storage` event where Chrome allocates the payload buffer in mojo IPC even
    // though no listener reacts.
    //
    // JSON.stringify can throw on BigInt / circular refs. We let the
    // underlying storage layer keep its existing try/catch behaviour
    // (log and drop) by falling through on serialization errors.
    PostHogPersistence.prototype._writeEntry = function (storage, name, props, slot) {
        var state = this._slotWriteState(slot);
        // Fast path for group slots (localStorage-only): when nothing in the
        // group changed since its last successful write, skip the JSON.stringify
        // of the large flag payload entirely. The main slot is excluded —
        // it is small, changes on nearly every write, and carries cookie options
        // in its fingerprint, so it always serializes.
        if (slot !== MAIN_STORAGE_SLOT && !state.dirty && !(0, core_1.isUndefined)(state.fingerprint)) {
            return 'skipped';
        }
        var fingerprint;
        try {
            fingerprint = this._entryFingerprint(props, slot);
            if (fingerprint === state.fingerprint) {
                state.dirty = false;
                return 'skipped';
            }
        }
        catch (_a) {
            // serialization failed (BigInt / circular ref); fall through to
            // storage._set, which handles the error itself, but don't cache an
            // un-fingerprinted write.
            fingerprint = undefined;
        }
        // Record the fingerprint (and clear the dirty flag, mark persisted) only
        // after a confirmed-successful durable write: localStorage / sessionStorage
        // swallow quota errors, so caching ahead of a failed write would skip
        // every future retry and silently lose the entry.
        if (storage._set(name, props, this._expire_days, this._cross_subdomain, this._secure, this._config.debug)) {
            state.dirty = false;
            if (slot !== MAIN_STORAGE_SLOT) {
                // The group entry has now actually landed on disk — only here is
                // it correct to record it as persisted (gates the empty-entry
                // skip in `_writeNowSplit`).
                state.persisted = true;
            }
            if (!(0, core_1.isUndefined)(fingerprint)) {
                state.fingerprint = fingerprint;
            }
            if (this._splitStorageEligible) {
                state.storageValue = storage_1.localStore._get(name);
            }
            return 'written';
        }
        else if (this._config.debug) {
            // The durable write did not land (e.g. localStorage quota). The slot
            // stays dirty / un-fingerprinted so the next save retries it; surface
            // it under debug so a repeated failure on a group entry — which would
            // otherwise silently strand the flag cache — is visible.
            logger_1.logger.warn("failed to persist storage entry \"".concat(name, "\"; will retry on next save"));
        }
        return 'failed';
    };
    // `keepGroupEntries` is set by the cookie-option setters (set_secure /
    // set_cross_subdomain). A cookie-scope change has to clear the cookie-backed
    // main entry, but the group entries are localStorage-only and entirely
    // scope-independent, so deleting and rewriting them would be the exact
    // per-page-load flag-blob churn the split exists to remove (these setters fire
    // once each on every construction, transitioning the in-memory option from
    // undefined to its configured value). Opt-out / reset (set_disabled / clear)
    // pass nothing and wipe everything.
    //
    // INVARIANT for `keepGroupEntries: true`: the caller must not also mutate
    // `props`. We keep the on-disk group entries AND their retained fingerprint;
    // that is only safe while `props` still matches what is on disk. The cookie
    // setters satisfy this (they touch only `_secure` / `_cross_subdomain`). A
    // future caller that clears or rewrites `props` while keeping the entries
    // would leave the retained fingerprint describing stale on-disk content and
    // skip the corrective write.
    PostHogPersistence.prototype.remove = function (_a) {
        var e_3, _b;
        var _c = _a === void 0 ? {} : _a, _d = _c.keepGroupEntries, keepGroupEntries = _d === void 0 ? false : _d;
        // Any write following this removal owns its complete feature-flag
        // snapshot and must not adopt the entry that was just removed.
        this._markAllCrossTabFeatureFlagChangesPending();
        // Cancel any pending debounced write — the storage entry is going
        // away so there is nothing useful to flush.
        if (!(0, core_1.isUndefined)(this._pendingSaveTimer)) {
            clearTimeout(this._pendingSaveTimer);
            this._pendingSaveTimer = undefined;
        }
        // remove both domain and subdomain cookies
        this._storage._remove(this._name, false);
        this._storage._remove(this._name, true);
        // Wipe the group entries too — even when the split is currently off — so
        // a default flip-flop or version downgrade cannot strand an orphaned
        // flag entry that would leak across users on reset()/opt-out.
        // Only the owning instance does this: the sessionStorage sibling
        // posthog-core spins up shares this instance's storage name, so it must
        // not delete the localStorage owner's entries. localStorage-only.
        if (!keepGroupEntries && this._ownsSplitStorage) {
            try {
                for (var PERSISTENCE_STORAGE_GROUPS_3 = __values(persistence_key_policy_1.PERSISTENCE_STORAGE_GROUPS), PERSISTENCE_STORAGE_GROUPS_3_1 = PERSISTENCE_STORAGE_GROUPS_3.next(); !PERSISTENCE_STORAGE_GROUPS_3_1.done; PERSISTENCE_STORAGE_GROUPS_3_1 = PERSISTENCE_STORAGE_GROUPS_3.next()) {
                    var group = PERSISTENCE_STORAGE_GROUPS_3_1.value;
                    storage_1.localStore._remove(this._groupEntryName(group));
                }
            }
            catch (e_3_1) { e_3 = { error: e_3_1 }; }
            finally {
                try {
                    if (PERSISTENCE_STORAGE_GROUPS_3_1 && !PERSISTENCE_STORAGE_GROUPS_3_1.done && (_b = PERSISTENCE_STORAGE_GROUPS_3.return)) _b.call(PERSISTENCE_STORAGE_GROUPS_3);
                }
                finally { if (e_3) throw e_3.error; }
            }
        }
        // The main entry is gone, so its bookkeeping must reset for the next
        // save to write through. When the group entries are kept, so is their
        // fingerprint/persisted state — that is what lets the following save
        // recognise them as unchanged and skip the rewrite.
        if (keepGroupEntries) {
            delete this._slotState[MAIN_STORAGE_SLOT];
        }
        else {
            this._slotState = {};
        }
        this._lastSeenCookiePropertiesFingerprint = undefined;
        this._lastSeenMainCookieValue = undefined;
    };
    // removes the storage entry and deletes all loaded data
    // forced name for tests
    PostHogPersistence.prototype.clear = function () {
        this.remove();
        this.props = {};
    };
    /**
     * @param {Object} props
     * @param {*=} default_value
     * @param {number=} days
     */
    PostHogPersistence.prototype.register_once = function (props, default_value, days) {
        var _this = this;
        if ((0, core_2.isObject)(props)) {
            // Explicit local mutations are newer than any unobserved sibling
            // snapshot. Reconcile first so the caller's values win afterward.
            this.syncCookieProperties();
            if ((0, core_1.isUndefined)(default_value)) {
                default_value = 'None';
            }
            this._expire_days = (0, core_1.isUndefined)(days) ? this._default_expiry : days;
            var hasChanges_1 = false;
            (0, general_utils_1.each)(props, function (val, prop) {
                if (!_this.props.hasOwnProperty(prop) || _this.props[prop] === default_value) {
                    _this._setProp(prop, val);
                    hasChanges_1 = true;
                }
            });
            if (hasChanges_1) {
                this.save();
                return true;
            }
        }
        return false;
    };
    /**
     * @param {Object} props
     * @param {number=} days
     */
    PostHogPersistence.prototype.register = function (props, days) {
        var _this = this;
        if ((0, core_2.isObject)(props)) {
            this.syncCookieProperties();
            this._expire_days = (0, core_1.isUndefined)(days) ? this._default_expiry : days;
            var hasChanges_2 = false;
            (0, general_utils_1.each)(props, function (val, prop) {
                if (props.hasOwnProperty(prop) && (_this.props[prop] !== val || (0, core_2.isObject)(val) || (0, core_1.isArray)(val))) {
                    _this._setProp(prop, val);
                    hasChanges_2 = true;
                }
            });
            if (hasChanges_2) {
                this.save();
                return true;
            }
        }
        return false;
    };
    PostHogPersistence.prototype.unregister = function (propOrProps) {
        var e_4, _a;
        this.syncCookieProperties();
        var props = typeof propOrProps === 'string' ? [propOrProps] : propOrProps;
        var hasChanges = false;
        try {
            for (var props_1 = __values(props), props_1_1 = props_1.next(); !props_1_1.done; props_1_1 = props_1.next()) {
                var prop = props_1_1.value;
                if (prop in this.props) {
                    this._deleteProp(prop);
                    hasChanges = true;
                }
            }
        }
        catch (e_4_1) { e_4 = { error: e_4_1 }; }
        finally {
            try {
                if (props_1_1 && !props_1_1.done && (_a = props_1.return)) _a.call(props_1);
            }
            finally { if (e_4) throw e_4.error; }
        }
        if (hasChanges) {
            this.save();
        }
    };
    PostHogPersistence.prototype.update_campaign_params = function () {
        var currentUrl = globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.URL;
        if (currentUrl === this._campaign_params_url) {
            return;
        }
        var campaignParams = (0, event_utils_1.getCampaignParams)(this._config.custom_campaign_params, this._config.mask_personal_data_properties, this._config.custom_personal_data_properties);
        // only save campaign params if there were any
        if (!(0, core_2.isEmptyObject)((0, general_utils_1.stripEmptyProperties)(campaignParams))) {
            this.register(campaignParams);
        }
        this._campaign_params_url = currentUrl;
    };
    PostHogPersistence.prototype.update_search_keyword = function () {
        this.register((0, event_utils_1.getSearchInfo)());
    };
    PostHogPersistence.prototype.update_referrer_info = function () {
        this.register_once((0, event_utils_1.getReferrerInfo)(), undefined);
    };
    PostHogPersistence.prototype.set_initial_person_info = function () {
        var _a;
        if (this.props[constants_1.INITIAL_CAMPAIGN_PARAMS] || this.props[constants_1.INITIAL_REFERRER_INFO]) {
            // the user has initial properties stored the previous way, don't save them again
            return;
        }
        this.register_once((_a = {},
            _a[constants_1.INITIAL_PERSON_INFO] = (0, event_utils_1.getPersonInfo)(this._config.mask_personal_data_properties, this._config.custom_personal_data_properties, this._config.disable_capture_url_hashes),
            _a), undefined);
    };
    PostHogPersistence.prototype.get_initial_props = function () {
        var _this = this;
        var p = {};
        // this section isn't written to anymore, but we should keep reading from it for backwards compatibility
        // for a while
        (0, general_utils_1.each)([constants_1.INITIAL_REFERRER_INFO, constants_1.INITIAL_CAMPAIGN_PARAMS], function (key) {
            var initialReferrerInfo = _this.props[key];
            if (initialReferrerInfo) {
                (0, general_utils_1.each)(initialReferrerInfo, function (v, k) {
                    p['$initial_' + (0, core_2.stripLeadingDollar)(k)] = v;
                });
            }
        });
        var initialPersonInfo = this.props[constants_1.INITIAL_PERSON_INFO];
        if (initialPersonInfo) {
            var initialPersonProps = (0, event_utils_1.getInitialPersonPropsFromInfo)(initialPersonInfo, this._config.disable_capture_url_hashes);
            (0, general_utils_1.extend)(p, initialPersonProps);
        }
        return p;
    };
    // safely fills the passed in object with stored properties,
    // does not override any properties defined in both
    // returns the passed in object
    PostHogPersistence.prototype.safe_merge = function (props) {
        (0, general_utils_1.each)(this.props, function (val, prop) {
            if (!(prop in props)) {
                props[prop] = val;
            }
        });
        return props;
    };
    PostHogPersistence.prototype.update_config = function (config, oldConfig, isDisabled) {
        var persistenceModeChanged = config.persistence !== oldConfig.persistence;
        var cookiePersistedPropertiesChanged = !isArrayContentsEqual(config.cookie_persisted_properties || [], oldConfig.cookie_persisted_properties || []);
        var persistenceChanged = persistenceModeChanged || cookiePersistedPropertiesChanged;
        var cookiePrecedenceChanged = config.cookieWinsOnConflict !== oldConfig.cookieWinsOnConflict;
        var disabled = config['disable_persistence'] || !!isDisabled;
        var reEnabling = !!this._disabled && !disabled;
        // Reconcile through the old routing before replacing it. PostHog mutates
        // its config object before calling this method, so use the old snapshot
        // explicitly rather than relying on this._config. A configuration change
        // that re-enables persistence may read the shared cookie before writes resume.
        if (!disabled) {
            this._syncCookieProperties(oldConfig, reEnabling);
        }
        this._config = config;
        // A newly enabled precedence policy or cookie-backed key set must apply
        // to the current cookie even if the old-policy sync just fingerprinted it.
        if (!disabled && (persistenceModeChanged || cookiePersistedPropertiesChanged || cookiePrecedenceChanged)) {
            this._lastSeenCookiePropertiesFingerprint = undefined;
            this._lastSeenMainCookieValue = undefined;
            // Newly added cookie-backed keys are still local-only in the current
            // cookie. Keep the old key set authoritative until migration writes
            // the first snapshot under the new configuration.
            this._syncCookieProperties(__assign(__assign({}, config), { cookie_persisted_properties: oldConfig.cookie_persisted_properties }), reEnabling);
        }
        // `_buildStorage` re-resolves both the backend and `_splitStorageEligible`,
        // so on a persistence change build the new store first, then derive the
        // split flag from the fresh eligibility. The new backend may no longer be
        // split-eligible (e.g. localStorage -> memory).
        var newStore = persistenceChanged || cookiePrecedenceChanged ? this._buildStorage(config) : this._storage;
        var wantSplit = this._resolveSplitStorage(config);
        var storageMigration = persistenceChanged || wantSplit !== this._splitStorage;
        var cookieOptionsChanged = config['cross_subdomain_cookie'] !== this._cross_subdomain || config['secure_cookie'] !== this._secure;
        var cookieSyncSuppressionStarted = !disabled && (storageMigration || cookieOptionsChanged) && this._beginCookieSyncSuppression(reEnabling);
        // _buildStorage has already resolved the new backend eligibility, while
        // this._storage still points at the old backend until migration clears it.
        // Keep that authoritative migration snapshot intact until the swap.
        this._storageMigrationInProgress = storageMigration;
        try {
            this._default_expiry = this._expire_days = config['cookie_expiration'];
            this.set_disabled(disabled);
            this.set_cross_subdomain(config['cross_subdomain_cookie']);
            this.set_secure(config['secure_cookie']);
            // Migrate when the backend changed or the split routing flipped at
            // runtime, e.g. set_config({ split_storage: true }). Either way we
            // clear the old layout and re-save in the new routing.
            if (storageMigration) {
                var props = this.props;
                this.clear();
                this._storage = newStore;
                this._splitStorage = wantSplit;
                this.props = props;
                this.save();
            }
            else if (cookiePrecedenceChanged) {
                this._storage = newStore;
                if (!disabled) {
                    // Persist the reconciled snapshot immediately. In particular,
                    // when precedence is disabled, a later reload must not let the
                    // stale localStorage value become authoritative again.
                    delete this._slotState[MAIN_STORAGE_SLOT];
                    this._writeNow();
                }
            }
        }
        finally {
            this._storageMigrationInProgress = false;
            if (cookieSyncSuppressionStarted) {
                // Cookie option and storage migrations clear the shared cookie.
                // Restore one complete authoritative snapshot before another
                // subdomain can initialize.
                this._endCookieSyncSuppression();
            }
        }
    };
    PostHogPersistence.prototype.set_disabled = function (disabled) {
        this._disabled = disabled;
        if (this._disabled) {
            this.remove();
        }
        else {
            this.save();
        }
    };
    PostHogPersistence.prototype.set_cross_subdomain = function (cross_subdomain) {
        if (cross_subdomain !== this._cross_subdomain) {
            this._cross_subdomain = cross_subdomain;
            this.remove({ keepGroupEntries: true });
            this.save();
        }
    };
    PostHogPersistence.prototype.set_secure = function (secure) {
        if (secure !== this._secure) {
            this._secure = secure;
            this.remove({ keepGroupEntries: true });
            this.save();
        }
    };
    PostHogPersistence.prototype.set_event_timer = function (event_name, timestamp) {
        var timers = this.props[constants_1.EVENT_TIMERS_KEY] || {};
        timers[event_name] = timestamp;
        this._setProp(constants_1.EVENT_TIMERS_KEY, timers);
        this.save();
    };
    PostHogPersistence.prototype.remove_event_timer = function (event_name) {
        var timers = this.props[constants_1.EVENT_TIMERS_KEY] || {};
        var timestamp = timers[event_name];
        if (!(0, core_1.isUndefined)(timestamp)) {
            delete timers[event_name];
            this._setProp(constants_1.EVENT_TIMERS_KEY, timers);
            this.save();
        }
        return timestamp;
    };
    PostHogPersistence.prototype.get_property = function (prop) {
        return this.props[prop];
    };
    PostHogPersistence.prototype.set_property = function (prop, to) {
        this._setProp(prop, to);
        this.save();
    };
    PostHogPersistence.prototype._setProp = function (prop, to, trackLocalChange) {
        var _a;
        if (trackLocalChange === void 0) { trackLocalChange = true; }
        var previousValue = this.props[prop];
        this.props[prop] = to;
        if (!trackLocalChange) {
            return;
        }
        if ((prop === constants_1.DISTINCT_ID || prop === constants_1.USER_STATE) && previousValue !== to) {
            this._localIdentityChangePending = true;
        }
        this._markPendingCrossTabFeatureFlagChanges(prop, previousValue, to);
        // A volatile value change never dirties its group — it changes on every
        // remote load and would otherwise force a rewrite of the large entry per
        // load. Deletions still dirty (see _deleteProp): presence is part of the
        // fingerprint, the moving value is not.
        if (!((_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(prop)) === null || _a === void 0 ? void 0 : _a.volatile)) {
            this._markGroupDirty(prop);
        }
    };
    PostHogPersistence.prototype._deleteProp = function (prop, trackLocalChange) {
        if (trackLocalChange === void 0) { trackLocalChange = true; }
        delete this.props[prop];
        if (!trackLocalChange) {
            return;
        }
        if (isCrossTabFeatureFlagKey(prop)) {
            this._setCrossTabFeatureFlagChangesPending(prop, true);
        }
        this._markGroupDirty(prop);
    };
    // Mark the prop's storage group dirty so its entry is re-serialized on the
    // next write. Props with no group live in the main blob, which always writes.
    PostHogPersistence.prototype._markGroupDirty = function (prop) {
        var _a;
        var group = (_a = (0, persistence_key_policy_1.getPersistenceKeyPolicy)(prop)) === null || _a === void 0 ? void 0 : _a.storageGroup;
        if (group) {
            this._slotWriteState(group).dirty = true;
        }
    };
    // The write-bookkeeping record for a slot, created on first access.
    PostHogPersistence.prototype._slotWriteState = function (slot) {
        return this._slotState[slot] || (this._slotState[slot] = {});
    };
    return PostHogPersistence;
}());
exports.PostHogPersistence = PostHogPersistence;
//# sourceMappingURL=posthog-persistence.js.map