{"version":3,"file":"sessionSlice.mjs","sources":["../../../../src/lib/state/slices/sessionSlice.ts"],"sourcesContent":["/**\n * @file Session Slice\n * @description Client-side session state management with DevTools integration\n *\n * IMPORTANT: This slice manages client-side session state only.\n * Authentication tokens and sensitive credentials should NEVER be stored here.\n * Use secure HTTP-only cookies or dedicated auth solutions for authentication.\n */\n\nimport { createSlice } from '../core/createSlice';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Session state interface\n */\nexport interface SessionState {\n  // Session identification\n  sessionId: string | null;\n  sessionStartedAt: number | null;\n  sessionExpiresAt: number | null;\n\n  // Activity tracking\n  lastActivity: number | null;\n  isSessionActive: boolean;\n  activityTimeoutMs: number;\n\n  // Navigation history (for back navigation, analytics)\n  navigationHistory: string[];\n  maxHistoryLength: number;\n\n  // Session metadata\n  deviceId: string | null;\n  browserTabId: string | null;\n}\n\n/**\n * Session actions interface\n */\nexport interface SessionActions {\n  // Session Lifecycle Actions\n  /** Initialize a new session with ID and optional configuration */\n  initSession: (sessionId: string, options?: { expiresInMs?: number; deviceId?: string }) => void;\n  /** Update last activity timestamp (call on user interaction) */\n  updateActivity: () => void;\n  /** End the current session and clear session data */\n  endSession: () => void;\n  /** Check if session has expired (returns true if expired) */\n  checkSessionExpiry: () => boolean;\n  /** Extend session expiry by additional milliseconds */\n  extendSession: (additionalMs: number) => void;\n\n  // Navigation History Actions\n  /** Add a path to navigation history (deduplicates consecutive entries) */\n  addToHistory: (path: string) => void;\n  /** Remove last occurrence of a path from history */\n  removeFromHistory: (path: string) => void;\n  /** Clear all navigation history */\n  clearHistory: () => void;\n  /** Get the last visited path or null if history is empty */\n  getLastVisitedPath: () => string | null;\n  /** Go back in history, returns the previous path or null */\n  goBack: () => string | null;\n\n  // Configuration Actions\n  /** Set activity timeout duration in milliseconds */\n  setActivityTimeout: (timeoutMs: number) => void;\n  /** Set maximum navigation history length */\n  setMaxHistoryLength: (length: number) => void;\n\n  // Browser Tab Management Actions\n  /** Set the browser tab identifier for cross-tab awareness */\n  setBrowserTabId: (tabId: string) => void;\n}\n\n// ============================================================================\n// Initial State\n// ============================================================================\n\nconst initialSessionState: SessionState = {\n  // Session identification\n  sessionId: null,\n  sessionStartedAt: null,\n  sessionExpiresAt: null,\n\n  // Activity tracking\n  lastActivity: null,\n  isSessionActive: false,\n  activityTimeoutMs: 30 * 60 * 1000, // 30 minutes default\n\n  // Navigation history\n  navigationHistory: [],\n  maxHistoryLength: 50,\n\n  // Session metadata\n  deviceId: null,\n  browserTabId: null,\n};\n\n// ============================================================================\n// Slice Definition\n// ============================================================================\n\n/**\n * Session slice using createSlice factory for automatic DevTools action naming\n *\n * All actions are automatically prefixed with \"session/\" in DevTools, e.g.:\n * - session/initSession\n * - session/updateActivity\n * - session/endSession\n */\nexport const sessionSlice = createSlice<\n  SessionState,\n  SessionActions & Record<string, (...args: never[]) => unknown>\n>({\n  name: 'session',\n  initialState: initialSessionState,\n  actions: (set, get) => ({\n    // ========================================================================\n    // Session Lifecycle\n    // ========================================================================\n\n    initSession: (sessionId, options = {}) => {\n      const now = Date.now();\n      const { expiresInMs, deviceId } = options;\n\n      set((state) => {\n        state.sessionId = sessionId;\n        state.sessionStartedAt = now;\n        state.lastActivity = now;\n        state.isSessionActive = true;\n        state.sessionExpiresAt = expiresInMs !== undefined ? now + expiresInMs : null;\n        if (deviceId !== undefined && deviceId !== '') {\n          state.deviceId = deviceId;\n        }\n      }, 'initSession');\n    },\n\n    updateActivity: () => {\n      set((state) => {\n        state.lastActivity = Date.now();\n      }, 'updateActivity');\n    },\n\n    endSession: () => {\n      set((state) => {\n        state.sessionId = null;\n        state.sessionStartedAt = null;\n        state.sessionExpiresAt = null;\n        state.lastActivity = null;\n        state.isSessionActive = false;\n        state.navigationHistory = [];\n        // Keep deviceId and browserTabId for continuity\n      }, 'endSession');\n    },\n\n    checkSessionExpiry: () => {\n      const state = get();\n      const now = Date.now();\n\n      // Check explicit session expiry\n      if (state.sessionExpiresAt !== null && now > state.sessionExpiresAt) {\n        return true;\n      }\n\n      // Check activity timeout\n      return state.lastActivity !== null && now - state.lastActivity > state.activityTimeoutMs;\n    },\n\n    extendSession: (additionalMs) => {\n      set((state) => {\n        if (state.sessionExpiresAt !== null) {\n          state.sessionExpiresAt = state.sessionExpiresAt + additionalMs;\n        } else {\n          state.sessionExpiresAt = Date.now() + additionalMs;\n        }\n        state.lastActivity = Date.now();\n      }, 'extendSession');\n    },\n\n    // ========================================================================\n    // Navigation History\n    // ========================================================================\n\n    addToHistory: (path) => {\n      set((state) => {\n        // Avoid duplicate consecutive entries\n        const lastEntry = state.navigationHistory[state.navigationHistory.length - 1];\n        if (lastEntry !== path) {\n          state.navigationHistory.push(path);\n\n          // Trim if over max length\n          if (state.navigationHistory.length > state.maxHistoryLength) {\n            state.navigationHistory = state.navigationHistory.slice(-state.maxHistoryLength);\n          }\n        }\n      }, 'addToHistory');\n    },\n\n    removeFromHistory: (path) => {\n      set((state) => {\n        const index = state.navigationHistory.lastIndexOf(path);\n        if (index !== -1) {\n          state.navigationHistory.splice(index, 1);\n        }\n      }, 'removeFromHistory');\n    },\n\n    clearHistory: () => {\n      set((state) => {\n        state.navigationHistory = [];\n      }, 'clearHistory');\n    },\n\n    getLastVisitedPath: () => {\n      const { navigationHistory } = get();\n      return navigationHistory[navigationHistory.length - 1] ?? null;\n    },\n\n    goBack: () => {\n      const { navigationHistory } = get();\n      if (navigationHistory.length < 2) {\n        return null;\n      }\n\n      // Remove current page\n      set((state) => {\n        state.navigationHistory.pop();\n      }, 'goBack');\n\n      // Return previous page\n      return navigationHistory[navigationHistory.length - 2] ?? null;\n    },\n\n    // ========================================================================\n    // Configuration\n    // ========================================================================\n\n    setActivityTimeout: (timeoutMs) => {\n      set((state) => {\n        state.activityTimeoutMs = timeoutMs;\n      }, 'setActivityTimeout');\n    },\n\n    setMaxHistoryLength: (length) => {\n      set((state) => {\n        state.maxHistoryLength = length;\n        // Trim existing history if needed\n        if (state.navigationHistory.length > length) {\n          state.navigationHistory = state.navigationHistory.slice(-length);\n        }\n      }, 'setMaxHistoryLength');\n    },\n\n    // ========================================================================\n    // Browser Tab Management\n    // ========================================================================\n\n    setBrowserTabId: (tabId) => {\n      set((state) => {\n        state.browserTabId = tabId;\n      }, 'setBrowserTabId');\n    },\n  }),\n});\n\n// ============================================================================\n// Exported Type\n// ============================================================================\n\nexport type SessionSlice = SessionState & SessionActions;\n"],"names":["initialSessionState","sessionSlice","createSlice","set","get","sessionId","options","now","expiresInMs","deviceId","state","additionalMs","path","index","navigationHistory","timeoutMs","length","tabId"],"mappings":"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"}