@embrace-io/web-sdk
Version:
74 lines (73 loc) • 3.56 kB
JavaScript
import { generateUUID } from "../../../utils/generateUUID.js";
import { EMBRACE_PERMANENT_PROPERTIES_KEY, EMBRACE_USER_SESSION_STATE_KEY } from "../constants.js";
//#region src/managers/EmbraceUserSessionManager/utils/state.ts
/**
* True when the stored state is no longer trustworthy: the device clock
* jumped backwards past the recorded start, the max-duration deadline has
* passed, or an inactivity deadline (set when the last part ended) has
* passed. Inactivity only applies between parts; the deadline is null
* while a part is active.
*/
const isUserSessionExpired = (state, now) => now < state.userSessionStartTs || now >= state.userSessionMaxEndTs || state.inactivityDeadlineTs !== null && now >= state.inactivityDeadlineTs;
const createUserSessionState = ({ now, previousUserSessionId, userSessionMaxDurationSeconds, userSessionInactivityTimeoutSeconds, userSessionForegroundInactivityTimeoutSeconds, userSessionNumber }) => ({
schemaVersion: 1,
userSessionId: generateUUID(),
previousUserSessionId,
userSessionStartTs: now,
userSessionMaxEndTs: now + userSessionMaxDurationSeconds * 1e3,
userSessionNumber,
userSessionPartIndex: 0,
userSessionMaxDurationSeconds,
userSessionInactivityTimeoutSeconds,
userSessionForegroundInactivityTimeoutSeconds,
inactivityDeadlineTs: null,
userSessionProperties: {}
});
/**
* Treats the schemaVersion stamp as the contract. Every field is written
* by createUserSessionState (and userSessionProperties is validated at
* addProperty time), so a matching version means the blob came from this
* code path and we trust the rest of its shape.
*/
const isValidStoredUserSessionState = (state) => state !== null && typeof state === "object" && state.schemaVersion === 1;
const readUserSessionState = (storage, diag) => {
const raw = storage.getItem(EMBRACE_USER_SESSION_STATE_KEY);
if (!raw) return null;
try {
const parsed = JSON.parse(raw);
if (!isValidStoredUserSessionState(parsed)) throw new Error("stored user session state failed structural validation");
return parsed;
} catch (e) {
diag.error("User session state in storage is corrupt; discarding it", e);
storage.removeItem(EMBRACE_USER_SESSION_STATE_KEY);
return null;
}
};
const readPermanentProperties = (storage, diag) => {
const raw = storage.getItem(EMBRACE_PERMANENT_PROPERTIES_KEY);
if (!raw) return {};
try {
const parsed = JSON.parse(raw);
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("permanent properties blob is not a plain object");
const validated = {};
for (const [key, value] of Object.entries(parsed)) if (typeof value === "string") validated[key] = value;
return validated;
} catch (e) {
diag.error("Permanent properties in storage are corrupt; discarding", e);
storage.removeItem(EMBRACE_PERMANENT_PROPERTIES_KEY);
return {};
}
};
/**
* Empty blobs are removed rather than written so storage stays tidy when
* the last property is cleared. Returns false on a failed write; callers
* gate cross-scope flips on this so a failed permanent write doesn't
* strip the previously-visible user-session-scoped value.
*/
const storePermanentProperties = (storage, properties) => {
if (Object.keys(properties).length === 0) return storage.removeItem(EMBRACE_PERMANENT_PROPERTIES_KEY);
return storage.setItem(EMBRACE_PERMANENT_PROPERTIES_KEY, JSON.stringify(properties));
};
//#endregion
export { createUserSessionState, isUserSessionExpired, readPermanentProperties, readUserSessionState, storePermanentProperties };
//# sourceMappingURL=state.js.map