@embrace-io/web-sdk
Version:
611 lines (610 loc) • 26.4 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const require_utils_clampNumber = require("../../utils/clampNumber.cjs");
const require_utils_generateUUID = require("../../utils/generateUUID.cjs");
const require_utils_getIncrementedCount = require("../../utils/getIncrementedCount.cjs");
const require_constants_attributes = require("../../constants/attributes.cjs");
const require_utils_getVisibilityState = require("../../utils/getVisibilityState.cjs");
const require_utils_throttle = require("../../utils/throttle.cjs");
const require_managers_EmbraceTraceManager_EmbraceExtendedSpan = require("../EmbraceTraceManager/EmbraceExtendedSpan.cjs");
const require_managers_EmbraceUserSessionManager_constants = require("./constants.cjs");
const require_managers_EmbraceUserSessionManager_utils_activity = require("./utils/activity.cjs");
const require_managers_EmbraceUserSessionManager_utils_state = require("./utils/state.cjs");
let _opentelemetry_api = require("@opentelemetry/api");
let _opentelemetry_semantic_conventions_incubating = require("@opentelemetry/semantic-conventions/incubating");
//#region src/managers/EmbraceUserSessionManager/EmbraceUserSessionManager.ts
/**
* Parts are engagement-gated (visible AND focused), so only one part can
* be active at a time. Engagement exclusivity lets us treat storage as
* a plain shared row read on engagement and written on changes.
*/
var EmbraceUserSessionManager = class {
_state = null;
_previousUserSessionId = null;
_maxDurationTimeout = null;
_permanentProperties = {};
_hasStoredState = false;
_activeSessionPartId = null;
_currentSessionPartNumber = null;
_sessionPartSpan = null;
_activeSessionPartCounts = null;
_coldStart = true;
_nextSessionPartCounts = {};
_sdkStartupDuration = 0;
_sessionPartStartedListeners = [];
_sessionPartEndedListeners = [];
_tracer;
_diag;
_perf;
_storage;
_visibilityDoc;
_limitManager;
_dynamicConfigManager;
_target;
_activityEvents;
_onActivityThrottled;
_sessionPartInactivityTimer = null;
constructor({ dynamicConfigManager, diag: diagParam, perf, visibilityDoc, storage, limitManager, target = window, activityThrottleMs = require_managers_EmbraceUserSessionManager_constants.DEFAULT_ACTIVITY_THROTTLE_MS, activityEvents = require_managers_EmbraceUserSessionManager_constants.DEFAULT_ACTIVITY_EVENTS }) {
this._diag = diagParam ?? _opentelemetry_api.diag.createComponentLogger({ namespace: "EmbraceUserSessionManager" });
this._perf = perf;
this._visibilityDoc = visibilityDoc;
this._storage = storage;
this._limitManager = limitManager;
this._dynamicConfigManager = dynamicConfigManager;
this._permanentProperties = require_managers_EmbraceUserSessionManager_utils_state.readPermanentProperties(this._storage, this._diag);
this._tracer = _opentelemetry_api.trace.getTracer("embrace-web-sdk-session-parts");
this._target = target;
this._activityEvents = activityEvents;
this._onActivityThrottled = require_utils_throttle.throttle(this._onActivity, activityThrottleMs);
}
/**
* Reads the user-session durations from remote config, clamps each to its
* allowed range, and enforces inactivity <= max duration. Called once per
* user-session creation; the resolved values are frozen into that session's
* state.
*/
_resolveUserSessionDurations() {
const config = this._dynamicConfigManager.getConfig();
const userSessionMaxDurationSeconds = require_utils_clampNumber.clampNumber({
diag: this._diag,
value: config.userSessionMaxDurationSeconds,
defaultValue: require_managers_EmbraceUserSessionManager_constants.DEFAULT_USER_SESSION_MAX_DURATION_SECONDS,
min: require_managers_EmbraceUserSessionManager_constants.MIN_USER_SESSION_MAX_DURATION_SECONDS,
max: require_managers_EmbraceUserSessionManager_constants.MAX_USER_SESSION_MAX_DURATION_SECONDS
});
const userSessionInactivityTimeoutSeconds = require_utils_clampNumber.clampNumber({
diag: this._diag,
value: config.userSessionInactivityTimeoutSeconds,
defaultValue: require_managers_EmbraceUserSessionManager_constants.DEFAULT_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS,
min: 30,
max: require_managers_EmbraceUserSessionManager_constants.MAX_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS
});
const userSessionForegroundInactivityTimeoutSeconds = require_utils_clampNumber.clampNumber({
diag: this._diag,
value: config.userSessionForegroundInactivityTimeoutSeconds,
defaultValue: require_managers_EmbraceUserSessionManager_constants.DEFAULT_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS,
min: 30,
max: require_managers_EmbraceUserSessionManager_constants.MAX_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS
});
const inactivityWithinMax = userSessionInactivityTimeoutSeconds <= userSessionMaxDurationSeconds;
const foregroundWithinMax = userSessionForegroundInactivityTimeoutSeconds <= userSessionMaxDurationSeconds;
if (!inactivityWithinMax) this._diag.warn(`userSessionInactivityTimeoutSeconds (${userSessionInactivityTimeoutSeconds.toString()}s) exceeds userSessionMaxDurationSeconds (${userSessionMaxDurationSeconds.toString()}s); falling back to default inactivity timeout (${require_managers_EmbraceUserSessionManager_constants.DEFAULT_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS.toString()}s).`);
if (!foregroundWithinMax) this._diag.warn(`userSessionForegroundInactivityTimeoutSeconds (${userSessionForegroundInactivityTimeoutSeconds.toString()}s) exceeds userSessionMaxDurationSeconds (${userSessionMaxDurationSeconds.toString()}s); falling back to default foreground inactivity timeout (${require_managers_EmbraceUserSessionManager_constants.DEFAULT_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS.toString()}s).`);
return {
userSessionMaxDurationSeconds,
userSessionInactivityTimeoutSeconds: inactivityWithinMax ? userSessionInactivityTimeoutSeconds : require_managers_EmbraceUserSessionManager_constants.DEFAULT_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS,
userSessionForegroundInactivityTimeoutSeconds: foregroundWithinMax ? userSessionForegroundInactivityTimeoutSeconds : require_managers_EmbraceUserSessionManager_constants.DEFAULT_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS
};
}
setTracerProvider(tracerProvider) {
this._tracer = tracerProvider.getTracer("embrace-web-sdk-session-parts");
require_managers_EmbraceUserSessionManager_utils_activity.addActivityListeners({
target: this._target,
visibilityDoc: this._visibilityDoc,
activityEvents: this._activityEvents,
onActivity: this._onActivityThrottled,
onVisibilityChange: this._onVisibilityChange,
onFocus: this._onFocus,
onBlur: this._onBlur
});
const { state, created } = this._loadOrCreateUserSessionState(this._perf.getNowMillis());
this._state = state;
if (created) this._storeState();
}
recordSDKStartupDuration(duration) {
this._sdkStartupDuration = Math.ceil(duration);
}
getUserSessionId() {
return this._state?.userSessionId ?? null;
}
getPreviousUserSessionId() {
return this._state?.previousUserSessionId ?? this._previousUserSessionId;
}
getUserSessionStartTime() {
return this._state?.userSessionStartTs ?? null;
}
getUserSessionAttributes() {
if (!this._state) return null;
const previousUserSessionId = this.getPreviousUserSessionId() ?? "";
return {
[require_constants_attributes.KEY_EMB_USER_SESSION_ID]: this._state.userSessionId,
[_opentelemetry_semantic_conventions_incubating.ATTR_SESSION_ID]: this._state.userSessionId,
[require_constants_attributes.KEY_EMB_USER_SESSION_PREVIOUS_ID]: previousUserSessionId,
[_opentelemetry_semantic_conventions_incubating.ATTR_SESSION_PREVIOUS_ID]: previousUserSessionId,
[require_constants_attributes.KEY_EMB_USER_SESSION_NUMBER]: this._state.userSessionNumber,
[require_constants_attributes.KEY_EMB_USER_SESSION_PART_INDEX]: this._state.userSessionPartIndex,
[require_constants_attributes.KEY_EMB_SESSION_PART_NUMBER]: this._currentSessionPartNumber ?? 0,
[require_constants_attributes.KEY_EMB_USER_SESSION_START_TS]: this._state.userSessionStartTs,
[require_constants_attributes.KEY_EMB_USER_SESSION_MAX_DURATION_SECONDS]: this._state.userSessionMaxDurationSeconds,
[require_constants_attributes.KEY_EMB_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS]: this._state.userSessionInactivityTimeoutSeconds,
[require_constants_attributes.KEY_EMB_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS]: this._state.userSessionForegroundInactivityTimeoutSeconds
};
}
endUserSession() {
if (!this._state) {
this._diag.debug("Trying to end user session, but there is no active session. This is a no-op.");
return;
}
const now = this._perf.getNowMillis();
const lastEndRaw = this._storage.getItem(require_managers_EmbraceUserSessionManager_constants.EMBRACE_LAST_END_USER_SESSION_TS_KEY);
const lastEndTs = lastEndRaw === null ? NaN : Number(lastEndRaw);
if (Number.isFinite(lastEndTs) && now - lastEndTs < 5e3) {
this._diag.warn("endUserSession called within cooldown period, ignoring.");
return;
}
this._rolloverUserSession("manual");
this._storage.setItem(require_managers_EmbraceUserSessionManager_constants.EMBRACE_LAST_END_USER_SESSION_TS_KEY, String(now));
}
getSessionPartId() {
return this._activeSessionPartId;
}
getSessionPartSpan() {
return this._sessionPartSpan;
}
startSessionPartInternal({ reason, timestamp }) {
if (this._sessionPartSpan) {
this._diag.warn(`startSessionPartInternal called while a part is active (reason: ${reason}); ignoring`);
return;
}
if (require_utils_getVisibilityState.getVisibilityState(this._visibilityDoc) === "background" || !this._visibilityDoc.hasFocus()) {
this._diag.debug(`skipping session part start (reason: ${reason}) because the tab is not engaged`);
return;
}
const activeSessionPartId = require_utils_generateUUID.generateUUID();
const previouslyRecordedCounts = this._nextSessionPartCounts;
this._beginUserSessionForPartStart();
const attributes = {
...this.getUserSessionAttributes(),
[require_constants_attributes.KEY_EMB_TYPE]: "ux.session_part",
[require_constants_attributes.KEY_EMB_STATE]: "foreground",
[require_constants_attributes.KEY_EMB_SESSION_PART_ID]: activeSessionPartId,
[require_constants_attributes.KEY_EMB_SESSION_PART_START_REASON]: reason,
[require_constants_attributes.KEY_EMB_COLD_START]: this._coldStart,
...previouslyRecordedCounts
};
this._activeSessionPartId = activeSessionPartId;
let span;
try {
span = new require_managers_EmbraceTraceManager_EmbraceExtendedSpan.EmbraceExtendedSpan(this._tracer.startSpan(require_managers_EmbraceUserSessionManager_constants.SESSION_PART_SPAN_NAME, {
attributes,
startTime: timestamp
}));
} catch (error) {
this._activeSessionPartId = null;
this._diag.error("Error starting session part span", error);
return;
}
this._activeSessionPartCounts = {};
this._nextSessionPartCounts = {};
this._sessionPartSpan = span;
this._startSessionPartInactivityTimer();
this._fireListeners(this._sessionPartStartedListeners, "started", { reason });
}
endSessionPartInternal({ reason, userSessionEndReason, timestamp }) {
if (!this._sessionPartSpan) {
this._diag.debug("trying to end a session part, but there is no session part in progress. This is a no-op.");
return;
}
this._clearSessionPartInactivityTimer();
const isFinalSessionPart = require_managers_EmbraceUserSessionManager_constants.FINAL_SESSION_PART_END_REASONS.has(reason);
const now = this._perf.getNowMillis();
const partEndTs = timestamp ?? now;
const span = this._sessionPartSpan;
try {
const endAttrs = {
[require_constants_attributes.KEY_EMB_SESSION_PART_END_REASON]: reason,
...this._activeSessionPartCounts,
...this._limitManager.getDiagnosticCounts(),
[require_constants_attributes.KEY_EMB_SDK_STARTUP_DURATION]: this._sdkStartupDuration
};
if (this._coldStart) endAttrs[require_constants_attributes.KEY_EMB_PAGE_LOAD] = this._visibilityDoc.readyState === "complete";
if (isFinalSessionPart) {
endAttrs[require_constants_attributes.KEY_EMB_IS_FINAL_SESSION_PART] = 1;
if (userSessionEndReason) endAttrs[require_constants_attributes.KEY_EMB_USER_SESSION_TERMINATION_REASON] = userSessionEndReason;
}
const propertyAttrs = {};
for (const [bareKey, value] of Object.entries(this.getSessionPartProperties())) propertyAttrs[require_constants_attributes.KEY_PREFIX_EMB_PROPERTIES + bareKey] = value;
try {
span.setAttributes({
...propertyAttrs,
...endAttrs
});
} catch (error) {
this._diag.warn("Error setting end attributes on session part span; ending span without them", error);
}
} finally {
this._fireListeners(this._sessionPartEndedListeners, "ended", void 0);
try {
span.end(partEndTs);
} catch (error) {
this._diag.warn("Error ending session part span", error);
}
this._sessionPartSpan = null;
this._coldStart = false;
this._activeSessionPartId = null;
this._activeSessionPartCounts = null;
this._currentSessionPartNumber = null;
this._limitManager.reset();
}
if (isFinalSessionPart) {
this._previousUserSessionId = this._state?.userSessionId ?? null;
this._state = null;
this._storage.removeItem(require_managers_EmbraceUserSessionManager_constants.EMBRACE_USER_SESSION_STATE_KEY);
this._clearMaxDurationTimer();
} else this._continueUserSessionAfterPartEnd(now);
}
addBreadcrumb(name) {
if (!this._sessionPartSpan) {
this._diag.debug("trying to add breadcrumb, but there is no session part in progress. This is a no-op.");
return;
}
const limitedBreadcrumb = this._limitManager.limitBreadcrumb(name);
if (limitedBreadcrumb === "dropped") return;
this._sessionPartSpan.addEvent("emb-breadcrumb", { message: limitedBreadcrumb.name }, this._perf.getNowMillis());
}
addProperty(propertyKey, value, options) {
const limitedSessionProperty = this._limitManager.limitUserSessionProperty(propertyKey, value);
if (limitedSessionProperty === "dropped") return;
const bareKey = limitedSessionProperty.key;
const bareValue = limitedSessionProperty.value;
if (options?.lifespan === "permanent") {
const candidate = {
...this._permanentProperties,
[bareKey]: bareValue
};
if (!require_managers_EmbraceUserSessionManager_utils_state.storePermanentProperties(this._storage, candidate)) {
this._diag.warn("Storage unavailable; rejecting permanent property write.");
return;
}
this._permanentProperties = candidate;
this._removeFromUserSessionProperties(bareKey);
return;
}
const { state } = this._loadOrCreateUserSessionState(this._perf.getNowMillis());
this._state = {
...state,
userSessionProperties: {
...state.userSessionProperties,
[bareKey]: bareValue
}
};
if (!this._storeState()) {
this._state = state;
this._diag.warn("Storage unavailable; rejecting user-session property write.");
}
}
removeProperty(propertyKey) {
const bareKey = this._limitManager.truncateString("session_property_key", propertyKey);
this._removeFromPermanentProperties(bareKey);
this._removeFromUserSessionProperties(bareKey);
}
/**
* Bare-keyed view of properties for the active part. User-session-scoped
* entries shadow permanent ones, matching the cross-scope flip rule
* enforced in `addProperty`. Callers apply the wire-format prefix at
* stamp time.
*/
getSessionPartProperties() {
return {
...this._permanentProperties,
...this._state?.userSessionProperties ?? {}
};
}
incrSessionPartCountForKey(key) {
if (!this._sessionPartSpan || !this._activeSessionPartCounts) {
this._diag.debug("trying to increment a count for the active session part, but there is no session part in progress. This is a no-op.");
return;
}
this._activeSessionPartCounts[key] = (this._activeSessionPartCounts[key] || 0) + 1;
}
incrNextSessionPartCountForKey(key) {
this._nextSessionPartCounts[key] = (this._nextSessionPartCounts[key] || 0) + 1;
}
addSessionPartStartedListener(listener) {
return this._addListener(this._sessionPartStartedListeners, listener);
}
addSessionPartEndedListener(listener) {
return this._addListener(this._sessionPartEndedListeners, listener);
}
getSessionId() {
return this.getUserSessionId();
}
getPreviousSessionId() {
return null;
}
getSessionStartTime() {
return this.getUserSessionStartTime();
}
endSessionSpan() {
this.endUserSession();
}
startSessionSpan() {}
getSessionSpan() {
return this.getSessionPartSpan();
}
currentSessionAsReadableSpan() {
return null;
}
addSessionStartedListener(_listener) {
return () => {};
}
addSessionEndedListener(_listener) {
return () => {};
}
/**
* Returns the persisted user-session state, rotating to a fresh one if
* the persisted row is missing or expired. Arms the max-duration timer
* when needed. The caller is responsible for assigning the result to
* `_state` and for persisting (the helper does not write to storage).
*/
_loadOrCreateUserSessionState(now) {
let state = require_managers_EmbraceUserSessionManager_utils_state.readUserSessionState(this._storage, this._diag);
if (!state && !this._hasStoredState && this._state) state = this._state;
let created = false;
if (!state || require_managers_EmbraceUserSessionManager_utils_state.isUserSessionExpired(state, now)) {
if (state) this._previousUserSessionId = state.userSessionId;
const userSessionNumber = require_utils_getIncrementedCount.getIncrementedCount(this._storage, require_managers_EmbraceUserSessionManager_constants.EMBRACE_USER_SESSION_NUMBER_KEY, this._diag);
if (!this._coldStart) this._dynamicConfigManager.refreshRemoteConfig();
const { userSessionMaxDurationSeconds, userSessionInactivityTimeoutSeconds, userSessionForegroundInactivityTimeoutSeconds } = this._resolveUserSessionDurations();
state = require_managers_EmbraceUserSessionManager_utils_state.createUserSessionState({
now,
previousUserSessionId: this._previousUserSessionId,
userSessionMaxDurationSeconds,
userSessionInactivityTimeoutSeconds,
userSessionForegroundInactivityTimeoutSeconds,
userSessionNumber
});
created = true;
}
if (created || this._maxDurationTimeout === null) this._setupMaxDurationTimer(state, now);
return {
state,
created
};
}
/**
* Bumps the part counter and arms the max-duration timer. Relies on
* `_loadOrCreateUserSessionState` to provide a non-null, non-expired state,
* so `getUserSessionAttributes()` is guaranteed non-null after this.
*/
_beginUserSessionForPartStart() {
const now = this._perf.getNowMillis();
const { state } = this._loadOrCreateUserSessionState(now);
this._currentSessionPartNumber = require_utils_getIncrementedCount.getIncrementedCount(this._storage, require_managers_EmbraceUserSessionManager_constants.EMBRACE_SESSION_PART_NUMBER_KEY, this._diag);
this._state = {
...state,
userSessionPartIndex: state.userSessionPartIndex + 1,
inactivityDeadlineTs: null
};
this._storeState();
}
/**
* Records the inactivity deadline on the user-session row so the next
* part start can detect lazy expiry. The max-duration timer stays valid
* because userSessionMaxEndTs doesn't change between parts.
*/
_continueUserSessionAfterPartEnd(now) {
if (!this._state) {
this._clearMaxDurationTimer();
return;
}
if (this._hasStoredState && this._storage.getItem("embrace_user_session_state") === null) {
this._previousUserSessionId = this._state.userSessionId;
this._state = null;
this._clearMaxDurationTimer();
return;
}
this._state = {
...this._state,
inactivityDeadlineTs: now + this._state.userSessionInactivityTimeoutSeconds * 1e3
};
this._storeState();
}
/**
* Ends the active part as final and starts a fresh part for the next
* user session. State clearing for the engaged path is handled by
* endSessionPartInternal's isFinal branch. When no part is active
* (tab disengaged), the same state-clearing is performed inline so
* the next engagement creates a fresh user session rather than
* resuming the just-ended one.
*/
_rolloverUserSession(userSessionEndReason) {
if (this._sessionPartSpan) {
const boundaryTimestamp = this._perf.getNowMillis();
this.endSessionPartInternal({
reason: "user_session_ended",
userSessionEndReason,
timestamp: boundaryTimestamp
});
this.startSessionPartInternal({
reason: "user_session_rollover",
timestamp: boundaryTimestamp
});
return;
}
this._previousUserSessionId = this._state?.userSessionId ?? null;
this._state = null;
this._storage.removeItem(require_managers_EmbraceUserSessionManager_constants.EMBRACE_USER_SESSION_STATE_KEY);
this._clearMaxDurationTimer();
}
/**
* Ends the active part and starts a new one within the same user session.
* The user session is preserved while the part counters advance, so the
* caller's `endReason`/`startReason` must both be non-final. Both spans share
* one boundary timestamp so they tile without a gap; the caller may supply it
* (for example the timestamp of the triggering event), defaulting to now. A
* no-op when no part is active: there is nothing to roll over and the next
* engagement starts one.
*/
rolloverSessionPartInternal({ endReason, startReason, timestamp }) {
if (!this._sessionPartSpan) return;
const boundaryTimestamp = timestamp ?? this._perf.getNowMillis();
this.endSessionPartInternal({
reason: endReason,
timestamp: boundaryTimestamp
});
this.startSessionPartInternal({
reason: startReason,
timestamp: boundaryTimestamp
});
}
_setupMaxDurationTimer(state, now) {
this._clearMaxDurationTimer();
const remaining = state.userSessionMaxEndTs - now;
if (remaining <= 0) return;
this._maxDurationTimeout = setTimeout(() => {
this._maxDurationTimeout = null;
this._rolloverUserSession("max_duration_reached");
}, remaining);
}
/**
* @internal Would be private but exposed for tests to simulate a page reload
*/
_clearMaxDurationTimer() {
if (this._maxDurationTimeout !== null) {
clearTimeout(this._maxDurationTimeout);
this._maxDurationTimeout = null;
}
}
/**
* Detaches browser-activity listeners and clears the part-inactivity
* timer. Intended for SDK teardown and tests; in production the manager
* lives for the page's lifetime.
*/
_shutdown() {
require_managers_EmbraceUserSessionManager_utils_activity.removeActivityListeners({
target: this._target,
visibilityDoc: this._visibilityDoc,
activityEvents: this._activityEvents,
onActivity: this._onActivityThrottled,
onVisibilityChange: this._onVisibilityChange,
onFocus: this._onFocus,
onBlur: this._onBlur
});
this._clearSessionPartInactivityTimer();
this._clearMaxDurationTimer();
}
_onActivity = () => {
try {
if (!require_managers_EmbraceUserSessionManager_utils_activity.isTabEngaged(this._visibilityDoc)) return;
if (this._activeSessionPartId === null) {
this.startSessionPartInternal({ reason: "web_activity" });
return;
}
this._startSessionPartInactivityTimer();
} catch (e) {
this._diag.warn("Error handling activity event", e);
}
};
_onVisibilityChange = () => {
this._handleEngagementTransition("visibilitychange");
};
_onFocus = () => {
this._handleEngagementTransition("focus");
};
_onBlur = () => {
this._handleEngagementTransition("blur");
};
_handleEngagementTransition(source) {
try {
const engaged = require_managers_EmbraceUserSessionManager_utils_activity.isTabEngaged(this._visibilityDoc);
const active = this._activeSessionPartId !== null;
if (!engaged && active) {
this._diag.debug(`tab disengaged via ${source}; ending current part`);
this.endSessionPartInternal({ reason: "background" });
return;
}
if (engaged && !active) {
this._diag.debug(`tab engaged via ${source}; starting new part`);
this.startSessionPartInternal({ reason: "foreground" });
return;
}
if (engaged && active) this._startSessionPartInactivityTimer();
} catch (e) {
this._diag.warn("Error handling engagement change", e);
}
}
_onSessionPartInactivity = () => {
this._sessionPartInactivityTimer = null;
try {
if (this._activeSessionPartId === null) return;
this._diag.debug("inactivity timer fired; ending current part and user session");
this.endSessionPartInternal({
reason: "web_foreground_inactivity",
userSessionEndReason: "inactivity"
});
} catch (e) {
this._diag.warn("Error handling inactivity timer", e);
}
};
_startSessionPartInactivityTimer() {
this._clearSessionPartInactivityTimer();
if (!this._state) return;
this._sessionPartInactivityTimer = setTimeout(this._onSessionPartInactivity, this._state.userSessionForegroundInactivityTimeoutSeconds * 1e3);
}
_clearSessionPartInactivityTimer() {
if (this._sessionPartInactivityTimer !== null) {
clearTimeout(this._sessionPartInactivityTimer);
this._sessionPartInactivityTimer = null;
}
}
_addListener(list, listener) {
list.push(listener);
return () => {
const i = list.indexOf(listener);
if (i !== -1) list.splice(i, 1);
};
}
_fireListeners(list, kind, arg) {
for (const listener of list) try {
listener(arg);
} catch (error) {
this._diag.warn(`Error while executing session part ${kind} listener`, error);
}
}
_storeState() {
if (!this._state) return false;
const stored = this._storage.setItem(require_managers_EmbraceUserSessionManager_constants.EMBRACE_USER_SESSION_STATE_KEY, JSON.stringify(this._state));
if (stored) this._hasStoredState = true;
return stored;
}
_removeFromPermanentProperties(bareKey) {
if (!(bareKey in this._permanentProperties)) return;
const pruned = { ...this._permanentProperties };
delete pruned[bareKey];
this._permanentProperties = pruned;
require_managers_EmbraceUserSessionManager_utils_state.storePermanentProperties(this._storage, pruned);
}
_removeFromUserSessionProperties(bareKey) {
if (!this._state || !(bareKey in this._state.userSessionProperties)) return;
const pruned = { ...this._state.userSessionProperties };
delete pruned[bareKey];
this._state = {
...this._state,
userSessionProperties: pruned
};
this._storeState();
}
};
//#endregion
exports.EmbraceUserSessionManager = EmbraceUserSessionManager;
//# sourceMappingURL=EmbraceUserSessionManager.cjs.map