The current session's $session_id (a UUIDv7).
await getSessionId(); // => "018f4f3e-1a2b-7c3d-8e4f-5a6b7c8d9e0f"
export async function getSessionId(): Promise<string> {
const current = await readStoredSessionId();
if (current && Date.now() - current.createdAt < SESSION_MAX_AGE_MS) {
return current.id;
}
sessionWriteChain = sessionWriteChain.then(async () => {
// Re-check inside the chain: a concurrent call in this same context may
// have already minted a fresh id while this one waited its turn.
const latest = await readStoredSessionId();
if (latest && Date.now() - latest.createdAt < SESSION_MAX_AGE_MS) {
return latest.id;
}
const fresh: StoredSessionId = { id: generateUuidV7(), createdAt: Date.now() };
try {
await cstorage.session.set({ [CACHE.ANALYTICS_SESSION_ID]: fresh });
} catch {
// Best-effort: a missed persist just means the next call regenerates.
}
return fresh.id;
});
return sessionWriteChain;
}
Reads (or creates) the current
$session_id, mirroringerrorBuffer.ts's read/write pattern: stored inchrome.storage.session(dropped when the browser closes), with a fresh UUIDv7 minted when absent or older thanSESSION_MAX_AGE_MS. PostHog requires$session_idto be a UUIDv7, not a plain random UUID — see generateUuidV7.Deliberately does not implement PostHog's normal 30-minute-idle session rotation — only the browser-close and
SESSION_MAX_AGE_MSboundaries above — a simplification appropriate for ChemPal's short, bursty usage pattern.Known, accepted limitation:
sessionWriteChainonly serializes writes within this JS context, not across the popup, options page, and service worker, each of which holds its own chain. If two contexts cold-start at the same instant with nothing stored yet, both may briefly generate and use different ids for a single event before converging on the next read. Narrow, low-stakes, and intentionally not fixed with achrome.runtime.sendMessage-based single-source-of-truth alternative.