UNPKG

@apptus/esales-api

Version:

Library for making requests to Elevate 4 API v3

123 lines (105 loc) 3.99 kB
import { assert, assertString, once, isString } from './util/mod.ts'; import type { Session, SessionMetadata } from './config.ts'; /** Helps retrieve and update session metadata and persist it in LocalStorage. */ export interface LocalStorageSession extends Session { /** * Method for retrieving session information synchronously * from `LocalStorage`. Returns a `SessionMetadata` object that * contains `customerKey` and `sessionKey`. * * @example * ```ts * declare const session: LocalStorageSession; * * const { customerKey, sessionKey } = session(); * ``` */ (): SessionMetadata; /** * Generates new customer- & sessionKeys via `crypto.randomUUID()` and persists them. * Suitable to use e.g. when a visitor logs out. */ reset(): void; /** * Updates the `customerKey` to the provided value and persists it. * Suitable to use e.g. when a visitor is identified by signing in to their account. */ updateCustomerKey(key: string): void; } const __storage: Storage = /* @__PURE__ */ (() => globalThis.localStorage)(); /** @internal exported for testing */ export const __sessionMetadataCache = new Map<string, SessionMetadata>(); /** * Create a `Session` compatible object for retrieving and mutating session metadata. * This method is only intended to be used in Browsers. * * Storage is backed by LocalStorage, where reads/writes will happen. Reads are cached * in memory, and invalidated by `storage` events on Window. This method can be called * multiple times, but shares Window listener between them. * * If session information does not exist, customer- & sessionKeys will be generated * automatically via `crypto.randomUUID()`. * * @param storageKey - The key that will be used to store session metadata. Defaults to `'voyado.session'`. * * @example * ```ts * import { elevate, localStorageBackedSession } from '@apptus/esales-api'; * * const api = elevate({ * // ... * session: localStorageBackedSession() * }); * ``` * @example * ```ts * const session = localStorageBackedSession(); * * // Set a specific customerKey (e.g. on visitor sign-in) * session.updateCustomerKey(user.id); * * // Generate new customer/sessionKeys (e.g. on visitor signout) * session.reset(); * ``` */ export function localStorageBackedSession(storageKey = 'voyado.session'): LocalStorageSession { enableCachePruning(); const fetcher = () => read(storageKey); fetcher.updateCustomerKey = (customerKey: string) => update(storageKey, { ...fetcher(), customerKey }); fetcher.reset = () => update(storageKey, generateSession()); return fetcher; } const enableCachePruning = /* @__PURE__ */ once(() => { globalThis.addEventListener('storage', ({ key, storageArea }: StorageEvent) => { if (key && storageArea === __storage) __sessionMetadataCache.delete(key); }); }); function generateSession(): SessionMetadata { return { customerKey: crypto.randomUUID(), sessionKey: crypto.randomUUID() }; } function read(key: string): SessionMetadata { // Update cached value by reading from LocalStorage if needed if (!__sessionMetadataCache.has(key)) { const strData = __storage.getItem(key); const session = generateSession(); try { assertString(strData); const d: unknown = JSON.parse(strData); assert(d && typeof d === 'object'); if ('customerKey' in d && isString(d.customerKey)) session.customerKey = d.customerKey; if ('sessionKey' in d && isString(d.sessionKey)) session.sessionKey = d.sessionKey; update(key, session, strData); } catch { update(key, session); } } return __sessionMetadataCache.get(key)!; } function update(key: string, data: SessionMetadata, prevData = '') { updateCache(key, data); const currData = JSON.stringify(data); if (prevData !== currData) __storage.setItem(key, currData); } function updateCache(key: string, data: SessionMetadata) { __sessionMetadataCache.set(key, data); }