import type { AppPageDescriptor } from '../../store/appStore/types'; import { isPreviewRuntime } from './previewRuntime'; export const CLIENT_ID_STORAGE_KEY = 'work-app.visitor.client-id'; function readStorageValue(storage: Storage | null | undefined, key: string) { try { return storage?.getItem(key)?.trim() ?? ''; } catch { return ''; } } function writeStorageValue(storage: Storage | null | undefined, key: string, value: string) { try { if (value) { storage?.setItem(key, value); } else { storage?.removeItem(key); } } catch { // Ignore storage failures in restricted preview runtimes. } } function removeStorageValue(storage: Storage | null | undefined, key: string) { try { storage?.removeItem(key); } catch { // Ignore storage failures in restricted preview runtimes. } } function getClientStorage() { if (typeof window === 'undefined') { return null; } if (isPreviewRuntime()) { return { key: CLIENT_ID_STORAGE_KEY, primaryStorage: window.localStorage, legacyStorage: window.sessionStorage, }; } return { key: CLIENT_ID_STORAGE_KEY, primaryStorage: window.localStorage, legacyStorage: null, }; } function generateFallbackClientId() { return `client-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`; } function generateClientId() { if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { return crypto.randomUUID(); } return generateFallbackClientId(); } export function getClientId() { const storageConfig = getClientStorage(); if (!storageConfig) { return ''; } const savedClientId = readStorageValue(storageConfig.primaryStorage, storageConfig.key); if (savedClientId) { return savedClientId; } const legacyClientId = readStorageValue(storageConfig.legacyStorage, storageConfig.key); if (legacyClientId) { writeStorageValue(storageConfig.primaryStorage, storageConfig.key, legacyClientId); removeStorageValue(storageConfig.legacyStorage, storageConfig.key); return legacyClientId; } return ''; } export function clearClientId() { const storageConfig = getClientStorage(); if (!storageConfig) { return; } removeStorageValue(storageConfig.primaryStorage, storageConfig.key); removeStorageValue(storageConfig.legacyStorage, storageConfig.key); } export function getOrCreateClientId() { const existingClientId = getClientId(); if (existingClientId) { return existingClientId; } const storageConfig = getClientStorage(); if (!storageConfig) { return ''; } const nextClientId = generateClientId(); writeStorageValue(storageConfig.primaryStorage, storageConfig.key, nextClientId); removeStorageValue(storageConfig.legacyStorage, storageConfig.key); return nextClientId; } export function appendClientIdHeader(headersInit?: HeadersInit) { const headers = new Headers(headersInit); const clientId = getOrCreateClientId(); if (clientId && !headers.has('X-Client-Id')) { headers.set('X-Client-Id', clientId); } if (typeof window !== 'undefined') { const { origin, hostname } = window.location; if (origin && !headers.has('X-App-Origin')) { headers.set('X-App-Origin', origin); } if (hostname && !headers.has('X-App-Domain')) { headers.set('X-App-Domain', hostname); } } return headers; } export function buildTrackedPageUrl(page: AppPageDescriptor) { if (typeof window === 'undefined') { return ''; } const url = new URL(window.location.href); if (page.topMenu === 'plans') { url.searchParams.set('topMenu', 'plans'); url.searchParams.set('planFilter', page.section); url.searchParams.delete('planSection'); } else { url.searchParams.set('topMenu', page.topMenu); url.searchParams.delete('planSection'); url.searchParams.delete('planFilter'); } if (page.topMenu === 'docs') { url.searchParams.set('docsSection', page.section); } else { url.searchParams.delete('docsSection'); } if (page.topMenu === 'apis') { url.searchParams.set('apiSection', page.section); } else { url.searchParams.delete('apiSection'); } if (page.topMenu === 'chat') { url.searchParams.set('chatSection', page.section); } else { url.searchParams.delete('chatSection'); } if (page.topMenu === 'play') { url.searchParams.set('playSection', page.section); } else { url.searchParams.delete('playSection'); } return url.toString(); }