posthog-js
Version:
Posthog-js allows you to automatically capture usage and send events to PostHog.
516 lines • 29.5 kB
JavaScript
"use strict";
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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SessionIdManager = exports.SESSION_COOKIE_SYNC_INTERVAL_MS = exports.ACTIVITY_TIMESTAMP_PERSIST_GRANULARITY_MS = exports.MIN_SESSION_IDLE_TIMEOUT_SECONDS = exports.MAX_SESSION_IDLE_TIMEOUT_SECONDS = exports.DEFAULT_SESSION_IDLE_TIMEOUT_SECONDS = void 0;
var constants_1 = require("./constants");
var storage_1 = require("./storage");
var uuidv7_1 = require("@posthog/browser-common/utils/uuidv7");
var globals_1 = require("@posthog/browser-common/utils/globals");
var logger_1 = require("@posthog/browser-common/utils/logger");
var core_1 = require("@posthog/core");
var general_utils_1 = require("@posthog/browser-common/utils/general-utils");
var simple_event_emitter_1 = require("@posthog/browser-common/utils/simple-event-emitter");
var logger = (0, logger_1.createLogger)('[SessionId]');
exports.DEFAULT_SESSION_IDLE_TIMEOUT_SECONDS = 30 * 60; // 30 minutes
exports.MAX_SESSION_IDLE_TIMEOUT_SECONDS = 10 * 60 * 60; // 10 hours
exports.MIN_SESSION_IDLE_TIMEOUT_SECONDS = 60; // 1 minute
var SESSION_LENGTH_LIMIT_MILLISECONDS = 24 * 3600 * 1000; // 24 hours
var BOOTSTRAP_SESSION_CLOCK_SKEW_TOLERANCE_MILLISECONDS = 60 * 1000; // 1 minute
var parseBootstrapSessionId = function (sessionID, timestamp) {
if (timestamp === void 0) { timestamp = new Date().getTime(); }
try {
var sessionStartTimestamp = (0, uuidv7_1.uuid7ToTimestampMs)(sessionID);
if (sessionStartTimestamp > timestamp + BOOTSTRAP_SESSION_CLOCK_SKEW_TOLERANCE_MILLISECONDS) {
logger.error('Bootstrap sessionID cannot be in the future');
return undefined;
}
return sessionStartTimestamp;
}
catch (e) {
logger.error('Invalid sessionID in bootstrap', e);
return undefined;
}
};
// Must stay well under MIN_SESSION_IDLE_TIMEOUT_SECONDS so idle detection on
// other tabs cannot fire on stale persisted data (pinned by a unit test).
// Tradeoff: sibling tabs only observe activity once it has been persisted —
// in-memory ticks within this window are invisible across tabs.
exports.ACTIVITY_TIMESTAMP_PERSIST_GRANULARITY_MS = 5000;
// Session checks run for every accepted replay event. Ordinary captures force a
// reconciliation before reaching the session manager; direct/replay checks are
// bounded so they do not synchronously scan document.cookie for every event.
exports.SESSION_COOKIE_SYNC_INTERVAL_MS = 1000;
var SessionIdManager = /** @class */ (function () {
function SessionIdManager(instance, sessionIdGenerator, windowIdGenerator) {
var _this = this;
var _a;
this._lastPersistedActivityTimestamp = null;
this._lastCookieSyncTimestamp = null;
this._sessionIdChangedHandlers = [];
this._beforeUnloadListener = undefined;
// Flipped to true by `destroy()` so a setTimeout callback that is
// already queued can't re-arm itself onto a torn-down instance.
this._destroyed = false;
this._eventEmitter = new simple_event_emitter_1.SimpleEventEmitter();
this._sessionHasBeenIdleTooLong = function (timestamp, lastActivityTimestamp) {
if (!(0, core_1.isPositiveNumber)(timestamp) || !(0, core_1.isPositiveNumber)(lastActivityTimestamp)) {
return false;
}
return Math.abs(timestamp - lastActivityTimestamp) > _this.sessionTimeoutMs;
};
if (!instance.persistence) {
throw new Error('SessionIdManager requires a PostHogPersistence instance');
}
if (instance.config.cookieless_mode === constants_1.COOKIELESS_ALWAYS) {
throw new Error('SessionIdManager cannot be used with cookieless_mode="always"');
}
this._config = instance.config;
this._persistence = instance.persistence;
this._windowId = undefined;
this._sessionId = undefined;
this._sessionStartTimestamp = null;
this._pendingBootstrapSession = undefined;
this._sessionActivityTimestamp = null;
this._sessionIdGenerator = sessionIdGenerator || uuidv7_1.uuidv7;
this._windowIdGenerator = windowIdGenerator || uuidv7_1.uuidv7;
var persistenceName = this._config['persistence_name'] || this._config['token'];
var desiredTimeout = this._config['session_idle_timeout_seconds'] || exports.DEFAULT_SESSION_IDLE_TIMEOUT_SECONDS;
this._sessionTimeoutMs =
(0, core_1.clampToRange)(desiredTimeout, exports.MIN_SESSION_IDLE_TIMEOUT_SECONDS, exports.MAX_SESSION_IDLE_TIMEOUT_SECONDS, logger.createLogger('session_idle_timeout_seconds'), exports.DEFAULT_SESSION_IDLE_TIMEOUT_SECONDS) * 1000;
instance.register({ $configured_session_timeout_ms: this._sessionTimeoutMs });
this._resetIdleTimer();
this._window_id_storage_key = 'ph_' + persistenceName + '_window_id';
this._primary_window_exists_storage_key = 'ph_' + persistenceName + '_primary_window_exists';
// primary_window_exists is set when the DOM has been loaded and is cleared on unload
// if it exists here it means there was no unload which suggests this window is opened as a tab duplication, window.open, etc.
if (this._canUseSessionStorage()) {
var lastWindowId = storage_1.sessionStore._parse(this._window_id_storage_key);
var primaryWindowExists = storage_1.sessionStore._parse(this._primary_window_exists_storage_key);
if (lastWindowId && !primaryWindowExists) {
// Persist window from previous storage state
this._windowId = lastWindowId;
}
else {
// Wipe any reference to previous window id
storage_1.sessionStore._remove(this._window_id_storage_key);
}
// Flag this session as having a primary window
storage_1.sessionStore._set(this._primary_window_exists_storage_key, true);
}
if ((_a = this._config.bootstrap) === null || _a === void 0 ? void 0 : _a.sessionID) {
this.setBootstrapSessionId(this._config.bootstrap.sessionID);
}
this._listenToReloadWindow();
}
SessionIdManager.prototype.on = function (event, handler) {
return this._eventEmitter.on(event, handler);
};
Object.defineProperty(SessionIdManager.prototype, "sessionTimeoutMs", {
get: function () {
return this._sessionTimeoutMs;
},
enumerable: false,
configurable: true
});
SessionIdManager.prototype.onSessionId = function (callback) {
var _this = this;
// KLUDGE: when running in tests the handlers array was always undefined
// it's yucky but safe to set it here so that it's always definitely available
if ((0, core_1.isUndefined)(this._sessionIdChangedHandlers)) {
this._sessionIdChangedHandlers = [];
}
this._sessionIdChangedHandlers.push(callback);
if (this._sessionId) {
callback(this._sessionId, this._windowId);
}
return function () {
_this._sessionIdChangedHandlers = _this._sessionIdChangedHandlers.filter(function (h) { return h !== callback; });
};
};
SessionIdManager.prototype._canUseSessionStorage = function () {
// We only want to use sessionStorage if persistence is enabled and not memory storage
return this._config.persistence !== 'memory' && !this._persistence._disabled && storage_1.sessionStore._is_supported();
};
// Note: this tries to store the windowId in sessionStorage. SessionStorage is unique to the current window/tab,
// and persists page loads/reloads. So it's uniquely suited for storing the windowId. This function also respects
// when persistence is disabled (by user config) and when sessionStorage is not supported (it *should* be supported on all browsers),
// and in that case, it falls back to memory (which sadly, won't persist page loads)
SessionIdManager.prototype._setWindowId = function (windowId) {
if (windowId !== this._windowId) {
this._windowId = windowId;
if (this._canUseSessionStorage()) {
storage_1.sessionStore._set(this._window_id_storage_key, windowId);
}
}
};
SessionIdManager.prototype._getWindowId = function () {
if (this._windowId) {
return this._windowId;
}
if (this._canUseSessionStorage()) {
return storage_1.sessionStore._parse(this._window_id_storage_key);
}
// New window id will be generated
return null;
};
SessionIdManager.prototype._isActivityChangeBelowGranularity = function (newActivityTimestamp) {
var lastPersisted = this._lastPersistedActivityTimestamp;
if ((0, core_1.isNull)(lastPersisted) || (0, core_1.isNull)(newActivityTimestamp)) {
return false;
}
// Math.abs guards against clock skew (NTP, devtools time travel) — a
// backwards jump would otherwise slip past the gate.
return Math.abs(newActivityTimestamp - lastPersisted) < exports.ACTIVITY_TIMESTAMP_PERSIST_GRANULARITY_MS;
};
// Note: 'this.persistence.register' can be disabled in the config.
// In that case, this works by storing sessionId and the timestamp in memory.
SessionIdManager.prototype._setSessionId = function (sessionId, sessionActivityTimestamp, sessionStartTimestamp) {
var _a;
var idChanged = sessionId !== this._sessionId;
var startChanged = sessionStartTimestamp !== this._sessionStartTimestamp;
var activityChanged = sessionActivityTimestamp !== this._sessionActivityTimestamp;
var isActivityOnlyChange = !idChanged && !startChanged;
// In-memory state always tracks the freshest values, even when the
// write below is throttled, so in-process reads stay accurate.
this._sessionStartTimestamp = sessionStartTimestamp;
this._sessionActivityTimestamp = sessionActivityTimestamp;
this._sessionId = sessionId;
if (isActivityOnlyChange && !activityChanged) {
return;
}
if (isActivityOnlyChange && this._isActivityChangeBelowGranularity(sessionActivityTimestamp)) {
return;
}
this._lastPersistedActivityTimestamp = sessionActivityTimestamp;
this._persistence.register((_a = {},
_a[constants_1.SESSION_ID] = [sessionActivityTimestamp, sessionId, sessionStartTimestamp],
_a));
};
// Gates the debounce-specific cross-tab storage mechanics. The cross-tab
// refresh on the idle path, the idle-timer re-arm, sibling-session
// adoption, and `onSessionId` emission all run for all callers — only the
// per-key SESSION_ID refresh (vs the legacy whole-blob flush()+load(),
// which can only clobber a sibling when there is a pending debounced
// write) is gated on debounce being enabled. The dated default
// (>= 2026-05-30) enables debounce, so the per-key path ships with it;
// debounce-disabled callers keep the safe legacy refresh.
SessionIdManager.prototype._useCrossTabRefreshHardening = function () {
var _a;
var debounce = (_a = this._config) === null || _a === void 0 ? void 0 : _a.persistence_save_debounce_ms;
return (0, core_1.isPositiveNumber)(debounce) && debounce > 0;
};
// Refresh this tab's SESSION_ID view from storage to pick up a sibling
// tab's writes. With debounce enabled, pull only the SESSION_ID slot — a
// whole-blob flush() would clobber the sibling's SESSION_ID write before
// we read it. With debounce disabled, flush() is a no-op (no pending
// timer) so the legacy whole-blob load() is safe and picks up sibling
// writes.
SessionIdManager.prototype._refreshSessionIdFromStorage = function () {
if (this._useCrossTabRefreshHardening()) {
this._persistence.refreshKey(constants_1.SESSION_ID);
}
else {
this._persistence.flush();
this._persistence.load();
}
};
// Called from destroy / beforeunload so a tab close inside the throttle
// window doesn't leave the persisted activity timestamp stale.
SessionIdManager.prototype._flushPendingActivityTimestamp = function () {
var _a;
var _b;
if ((0, core_1.isNull)(this._sessionActivityTimestamp) ||
this._sessionActivityTimestamp === this._lastPersistedActivityTimestamp) {
return;
}
// A sibling tab may have rotated the session — refresh and skip the
// write if storage no longer matches our cached id/start, otherwise
// we would clobber the sibling's new session with stale values.
this._refreshSessionIdFromStorage();
var _c = __read(this._getSessionId(), 3), persistedSessionId = _c[1], persistedStart = _c[2];
if (persistedSessionId !== this._sessionId || persistedStart !== this._sessionStartTimestamp) {
return;
}
this._lastPersistedActivityTimestamp = this._sessionActivityTimestamp;
this._persistence.register((_a = {},
_a[constants_1.SESSION_ID] = [this._sessionActivityTimestamp, (_b = this._sessionId) !== null && _b !== void 0 ? _b : null, this._sessionStartTimestamp],
_a));
// Force the write past the debounce — destroy / unload paths cannot
// wait for a deferred timer that will never fire.
this._persistence.flush();
};
// `max` because either view can be ahead: the throttle holds in-memory
// ahead of persisted, and a sibling tab can hold persisted ahead of a
// frozen in-memory.
SessionIdManager.prototype._freshestActivityTimestamp = function () {
var _a = __read(this._getSessionId(), 1), persistedActivity = _a[0];
var persisted = (0, core_1.isPositiveNumber)(persistedActivity) ? persistedActivity : 0;
var inMemory = (0, core_1.isPositiveNumber)(this._sessionActivityTimestamp) ? this._sessionActivityTimestamp : 0;
return Math.max(persisted, inMemory);
};
// PostHogPersistence does not listen for `storage` events, so this tab's
// cached props lag sibling tabs' writes. Refresh before deciding idle so a
// sibling keeping the session alive isn't seen as idle here.
SessionIdManager.prototype._isSessionIdleAfterCrossTabRefresh = function (timestamp) {
this._refreshSessionIdFromStorage();
return this._sessionHasBeenIdleTooLong(timestamp, this._freshestActivityTimestamp());
};
SessionIdManager.prototype._getSessionId = function () {
// Always read from persistence - it's the source of truth
// The in-memory cache could become stale (e.g., in a frozen tab scenario where
// time passes but the cache isn't updated)
var sessionIdInfo = this._persistence.props[constants_1.SESSION_ID];
if ((0, core_1.isArray)(sessionIdInfo) && sessionIdInfo.length === 2) {
// Storage does not yet have a session start time. Add the last activity timestamp as the start time
sessionIdInfo.push(sessionIdInfo[0]);
}
return sessionIdInfo || [0, null, 0];
};
// Resets the session id by setting it to null. On the subsequent call to checkAndGetSessionAndWindowId,
// new ids will be generated. Also clears the idle timer so a stale fire
// cannot rotate the freshly-cleared session.
SessionIdManager.prototype.resetSessionId = function () {
this._lastPersistedActivityTimestamp = null;
this._pendingBootstrapSession = undefined;
clearTimeout(this._enforceIdleTimeout);
this._enforceIdleTimeout = undefined;
this._setSessionId(null, null, null);
};
// Reset defers bootstrap assignment until the next session check so rotation listeners
// observe the same event-driven lifecycle as a normal reset, rather than starting a replay immediately.
SessionIdManager.prototype.setBootstrapSessionId = function (sessionID, deferUntilNextSession) {
if (deferUntilNextSession === void 0) { deferUntilNextSession = false; }
var sessionStartTimestamp = parseBootstrapSessionId(sessionID);
if ((0, core_1.isUndefined)(sessionStartTimestamp)) {
return false;
}
if (deferUntilNextSession) {
this._pendingBootstrapSession = { sessionId: sessionID, sessionStartTimestamp: sessionStartTimestamp };
}
else {
this._setSessionId(sessionID, new Date().getTime(), sessionStartTimestamp);
}
return true;
};
/**
* Cleans up resources used by SessionIdManager.
* Should be called when the SessionIdManager is no longer needed to prevent memory leaks.
*/
SessionIdManager.prototype.destroy = function () {
this._destroyed = true;
this._flushPendingActivityTimestamp();
// Clear the idle timeout timer
clearTimeout(this._enforceIdleTimeout);
this._enforceIdleTimeout = undefined;
// Remove the beforeunload event listener
if (this._beforeUnloadListener && globals_1.window) {
globals_1.window.removeEventListener(constants_1.DOM_EVENT_BEFOREUNLOAD, this._beforeUnloadListener, { capture: false });
this._beforeUnloadListener = undefined;
}
// Clear session id changed handlers
this._sessionIdChangedHandlers = [];
};
/*
* Listens to window unloads and removes the primaryWindowExists key from sessionStorage.
* Reloaded or fresh tabs created after a DOM unloads (reloading the same tab) WILL NOT have this primaryWindowExists flag in session storage.
* Cloned sessions (new tab, tab duplication, window.open(), ...) WILL have this primaryWindowExists flag in their copied session storage.
* We conditionally check the primaryWindowExists value in the constructor to decide if the window id in the last session storage should be carried over.
*/
SessionIdManager.prototype._listenToReloadWindow = function () {
var _this = this;
this._beforeUnloadListener = function () {
_this._flushPendingActivityTimestamp();
if (_this._canUseSessionStorage()) {
storage_1.sessionStore._remove(_this._primary_window_exists_storage_key);
}
};
(0, general_utils_1.addEventListener)(globals_1.window, constants_1.DOM_EVENT_BEFOREUNLOAD, this._beforeUnloadListener, { capture: false });
};
/*
* This function returns the current sessionId and windowId. It should be used to
* access these values over directly calling `._sessionId` or `._windowId`.
* In addition to returning the sessionId and windowId, this function also manages cycling the
* sessionId and windowId when appropriate by doing the following:
*
* 1. If the sessionId or windowId is not set, it will generate a new one and store it.
* 2. If the readOnly param is set to false, it will:
* a. Check if it has been > SESSION_CHANGE_THRESHOLD since the last call with this flag set.
* If so, it will generate a new sessionId and store it.
* b. Update the timestamp stored with the sessionId to ensure the current session is extended
* for the appropriate amount of time.
*
* @param {boolean} readOnly (optional) Defaults to False. Should be set to True when the call to the function should not extend or cycle the session (e.g. being called for non-user generated events)
* @param {Number} timestamp (optional) Defaults to the current time. The timestamp to be stored with the sessionId (used when determining if a new sessionId should be generated)
*/
SessionIdManager.prototype.checkAndGetSessionAndWindowId = function (readOnly, _timestamp, persistenceAlreadySynced) {
var _a;
var _b, _c;
if (readOnly === void 0) { readOnly = false; }
if (_timestamp === void 0) { _timestamp = null; }
if (persistenceAlreadySynced === void 0) { persistenceAlreadySynced = false; }
if (this._config.cookieless_mode === constants_1.COOKIELESS_ALWAYS) {
throw new Error('checkAndGetSessionAndWindowId should not be called with cookieless_mode="always"');
}
var timestamp = _timestamp || new Date().getTime();
var managedSessionIdBeforeCookieSync = this._sessionId;
if (persistenceAlreadySynced) {
this._lastCookieSyncTimestamp = timestamp;
}
else if ((0, core_1.isNull)(this._lastCookieSyncTimestamp) ||
timestamp < this._lastCookieSyncTimestamp ||
timestamp - this._lastCookieSyncTimestamp >= exports.SESSION_COOKIE_SYNC_INTERVAL_MS) {
(_c = (_b = this._persistence).syncCookieProperties) === null || _c === void 0 ? void 0 : _c.call(_b);
this._lastCookieSyncTimestamp = timestamp;
}
var _d = __read(this._getSessionId(), 3), sessionId = _d[1], startTimestamp = _d[2];
var cookieSessionAdoption = !(0, core_1.isUndefined)(managedSessionIdBeforeCookieSync) && sessionId !== managedSessionIdBeforeCookieSync;
var lastActivityTimestamp = this._freshestActivityTimestamp();
var windowId = this._getWindowId();
var pendingBootstrapSession = this._pendingBootstrapSession;
var pendingBootstrapSessionPastMaximumLength = !!pendingBootstrapSession &&
(pendingBootstrapSession.sessionStartTimestamp >
timestamp + BOOTSTRAP_SESSION_CLOCK_SKEW_TOLERANCE_MILLISECONDS ||
timestamp - pendingBootstrapSession.sessionStartTimestamp > SESSION_LENGTH_LIMIT_MILLISECONDS);
var sessionPastMaximumLength = pendingBootstrapSession
? pendingBootstrapSessionPastMaximumLength
: (0, core_1.isPositiveNumber)(startTimestamp) &&
Math.abs(timestamp - startTimestamp) > SESSION_LENGTH_LIMIT_MILLISECONDS;
var valuesChanged = false;
var crossTabAdoption = cookieSessionAdoption;
var noSessionId = !sessionId || !!pendingBootstrapSession;
var preRefreshSessionId = sessionId;
var activityTimeout = !noSessionId && !readOnly && this._sessionHasBeenIdleTooLong(timestamp, lastActivityTimestamp);
if (activityTimeout) {
// Re-check against the freshest cross-tab view: a sibling tab
// may have kept the session alive.
activityTimeout = this._isSessionIdleAfterCrossTabRefresh(timestamp);
if (!activityTimeout) {
// The rare, timing-dependent case this path exists for: a
// sibling kept the session alive, so we avoid a spurious
// rotation. Logged because it is otherwise invisible in the
// wild and hard to reproduce.
logger.info('cross-tab refresh kept the session alive', { sessionId: sessionId });
}
// The cross-tab refresh may have observed a sibling tab's
// session rotation. Re-sample sessionId / startTimestamp so we
// don't clobber the sibling's new session with our stale id when
// we write below. (`_flushPendingActivityTimestamp` re-reads the
// same way on the unload path, but bails instead of adopting.)
;
_a = __read(this._getSessionId(), 3), sessionId = _a[1], startTimestamp = _a[2];
// The sibling may have reset the session to null: a stale
// `noSessionId` here would return and persist a null id.
// https://github.com/PostHog/posthog-js/issues/5036
// Only when the refresh cleared the timeout, so an idle rotation
// keeps `activityTimeout` as its sole reason.
if (!activityTimeout) {
noSessionId = !sessionId || !!pendingBootstrapSession;
}
}
if (noSessionId || activityTimeout || sessionPastMaximumLength) {
crossTabAdoption = false;
var usePendingBootstrapSession = pendingBootstrapSession && !pendingBootstrapSessionPastMaximumLength;
sessionId = usePendingBootstrapSession ? pendingBootstrapSession.sessionId : this._sessionIdGenerator();
windowId = this._windowIdGenerator();
logger.info('new session ID assigned', {
sessionId: sessionId,
windowId: windowId,
bootstrapped: !!usePendingBootstrapSession,
changeReason: { noSessionId: noSessionId, activityTimeout: activityTimeout, sessionPastMaximumLength: sessionPastMaximumLength },
});
startTimestamp = usePendingBootstrapSession ? pendingBootstrapSession.sessionStartTimestamp : timestamp;
this._pendingBootstrapSession = undefined;
valuesChanged = true;
}
else {
if (!windowId) {
windowId = this._windowIdGenerator();
valuesChanged = true;
}
// Not gated on debounce: the refresh + re-sample above run for all
// callers, so adoption can happen for all callers — and a session id
// change that handlers don't hear about leaves consumers (page-view
// state, a stopped recorder, session-scoped props) on the old
// session. If we adopted, we must notify.
crossTabAdoption = crossTabAdoption || sessionId !== preRefreshSessionId;
if (crossTabAdoption) {
// We took a sibling tab's session id (keeping our own window
// id). Fire handlers so downstream consumers (session
// recorder, session-scoped props, $pageview) follow the new
// session.
logger.info('adopted cross-tab session id', { sessionId: sessionId, windowId: windowId });
valuesChanged = true;
}
}
var noActivityTimestamp = !(0, core_1.isPositiveNumber)(lastActivityTimestamp);
var shouldPreserveActivityTimestamp = !noActivityTimestamp && readOnly && !sessionPastMaximumLength;
var newActivityTimestamp = shouldPreserveActivityTimestamp ? lastActivityTimestamp : timestamp;
var noStartTimestamp = !(0, core_1.isPositiveNumber)(startTimestamp);
var sessionStartTimestamp = noStartTimestamp ? new Date().getTime() : startTimestamp;
this._setWindowId(windowId);
this._setSessionId(sessionId, newActivityTimestamp, sessionStartTimestamp);
if (!readOnly) {
this._resetIdleTimer();
}
var changeReason = { noSessionId: noSessionId, activityTimeout: activityTimeout, sessionPastMaximumLength: sessionPastMaximumLength, crossTabAdoption: crossTabAdoption };
if (valuesChanged) {
this._sessionIdChangedHandlers.forEach(function (handler) { return handler(sessionId, windowId, changeReason); });
}
return {
sessionId: sessionId,
windowId: windowId,
sessionStartTimestamp: sessionStartTimestamp,
changeReason: valuesChanged ? changeReason : undefined,
lastActivityTimestamp: lastActivityTimestamp,
};
};
SessionIdManager.prototype._resetIdleTimer = function () {
var _this = this;
if (this._destroyed) {
// The instance has been torn down. Don't schedule any more
// work — a setTimeout callback queued before destroy could
// otherwise re-arm onto a dead manager and fire after teardown.
return;
}
clearTimeout(this._enforceIdleTimeout);
this._enforceIdleTimeout = setTimeout(function () {
if (_this._destroyed) {
return;
}
if (_this._isSessionIdleAfterCrossTabRefresh(new Date().getTime())) {
var idleSessionId = _this._sessionId;
_this.resetSessionId();
_this._eventEmitter.emit('forcedIdleReset', { idleSessionId: idleSessionId });
}
else {
// A sibling tab kept the session alive — re-arm so we
// re-check after another timeout window. This is unbounded by
// design: a session with at least one active tab re-arms
// indefinitely until every tab goes idle (or is destroyed —
// guarded by the check above). We deliberately do NOT adopt a
// sibling's rotated id here; adoption is scoped to the event
// path (checkAndGetSessionAndWindowId), and this tab picks up
// any rotation on its next event.
_this._resetIdleTimer();
}
}, this.sessionTimeoutMs * 1.1);
};
return SessionIdManager;
}());
exports.SessionIdManager = SessionIdManager;
//# sourceMappingURL=sessionid.js.map