{"version":3,"file":"useSecureStorage.mjs","sources":["../../../../src/lib/security/hooks/useSecureStorage.ts"],"sourcesContent":["/**\n * @fileoverview useSecureStorage Hook\n * @module @/lib/security/hooks/useSecureStorage\n *\n * React hook for encrypted storage access with automatic\n * state synchronization and error handling.\n *\n * @author Harbor Security Team\n * @version 1.0.0\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type { SecureStorageSetOptions, UseSecureStorageResult } from '../types';\nimport { getSecureStorage, type SecureStorage } from '../secure-storage';\n\n/**\n * Options for the useSecureStorage hook\n */\nexport interface UseSecureStorageOptions {\n  /** Custom SecureStorage instance (defaults to global instance) */\n  storage?: SecureStorage;\n  /** Initial value before first load */\n  initialValue?: unknown;\n  /** Whether to sync with other tabs via storage events */\n  syncAcrossTabs?: boolean;\n  /** Auto-refresh interval in milliseconds */\n  refreshInterval?: number;\n}\n\n/**\n * Hook for accessing encrypted secure storage\n *\n * Provides a React-friendly interface for reading and writing\n * encrypted data with automatic state management.\n *\n * @param key - The storage key\n * @param options - Hook options\n * @returns Secure storage operations and state\n *\n * @example\n * ```tsx\n * function SecureDataComponent() {\n *   const {\n *     value,\n *     isLoading,\n *     error,\n *     setValue,\n *     removeValue,\n *     refresh,\n *   } = useSecureStorage<UserPreferences>('user-preferences');\n *\n *   if (isLoading) return <Loading />;\n *   if (error) return <Error message={error.message} />;\n *\n *   return (\n *     <div>\n *       <p>Theme: {value?.theme ?? 'default'}</p>\n *       <button onClick={() => setValue({ theme: 'dark' })}>\n *         Set Dark Theme\n *       </button>\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useSecureStorage<T>(\n  key: string,\n  options: UseSecureStorageOptions = {}\n): UseSecureStorageResult<T> {\n  const { storage, initialValue, syncAcrossTabs = false, refreshInterval } = options;\n\n  // State\n  const [value, setValueState] = useState<T | null>((initialValue as T | undefined) ?? null);\n  const [isLoading, setIsLoading] = useState(true);\n  const [error, setError] = useState<Error | null>(null);\n\n  // Refs for cleanup\n  const mountedRef = useRef(true);\n  const refreshIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n  // Get storage instance\n  const getStorage = useCallback((): SecureStorage => {\n    return storage ?? getSecureStorage();\n  }, [storage]);\n\n  // Load value from storage\n  const loadValue = useCallback(async (): Promise<void> => {\n    try {\n      setIsLoading(true);\n      setError(null);\n\n      const storageInstance = getStorage();\n      const result = await storageInstance.getItem<T>(key);\n\n      if (!mountedRef.current) return;\n\n      if (result.success) {\n        setValueState(result.data ?? null);\n      } else {\n        // Item not found is not an error\n        if (result.error !== 'Item not found') {\n          setError(new Error(result.error));\n        }\n        setValueState(null);\n      }\n    } catch (err) {\n      if (!mountedRef.current) return;\n      setError(err instanceof Error ? err : new Error('Failed to load value'));\n    } finally {\n      if (mountedRef.current) {\n        setIsLoading(false);\n      }\n    }\n  }, [key, getStorage]);\n\n  // Set value in storage\n  const setValue = useCallback(\n    async (newValue: T, setOptions?: SecureStorageSetOptions): Promise<void> => {\n      try {\n        setError(null);\n\n        const storageInstance = getStorage();\n        const result = await storageInstance.setItem(key, newValue, setOptions);\n\n        if (!mountedRef.current) return;\n\n        if (result.success) {\n          setValueState(newValue);\n        } else {\n          throw new Error(result.error);\n        }\n      } catch (err) {\n        if (!mountedRef.current) return;\n        const error = err instanceof Error ? err : new Error('Failed to set value');\n        setError(error);\n        throw error;\n      }\n    },\n    [key, getStorage]\n  );\n\n  // Remove value from storage\n  const removeValue = useCallback(async (): Promise<void> => {\n    try {\n      setError(null);\n\n      const storageInstance = getStorage();\n      const result = await storageInstance.removeItem(key);\n\n      if (!mountedRef.current) return;\n\n      if (result.success) {\n        setValueState(null);\n      } else {\n        throw new Error(result.error);\n      }\n    } catch (err) {\n      if (!mountedRef.current) return;\n      const error = err instanceof Error ? err : new Error('Failed to remove value');\n      setError(error);\n      throw error;\n    }\n  }, [key, getStorage]);\n\n  // Refresh value from storage\n  const refresh = useCallback(async (): Promise<void> => {\n    await loadValue();\n  }, [loadValue]);\n\n  // Initial load\n  useEffect(() => {\n    mountedRef.current = true;\n    void loadValue();\n\n    return () => {\n      mountedRef.current = false;\n    };\n  }, [loadValue]);\n\n  // Set up refresh interval\n  useEffect(() => {\n    if (refreshInterval != null && refreshInterval > 0) {\n      refreshIntervalRef.current = setInterval(() => {\n        void loadValue();\n      }, refreshInterval);\n\n      return () => {\n        if (refreshIntervalRef.current != null) {\n          clearInterval(refreshIntervalRef.current);\n          refreshIntervalRef.current = null;\n        }\n      };\n    }\n    return undefined;\n  }, [refreshInterval, loadValue]);\n\n  // Sync across tabs via storage events\n  useEffect(() => {\n    if (!syncAcrossTabs || typeof window === 'undefined') {\n      return;\n    }\n\n    const handleStorageChange = (event: StorageEvent): void => {\n      // Check if the changed key matches our key\n      // Note: Secure storage uses a prefix, so we need to check for that\n      if (event.key?.includes(key) === true) {\n        void loadValue();\n      }\n    };\n\n    window.addEventListener('storage', handleStorageChange);\n\n    return () => {\n      window.removeEventListener('storage', handleStorageChange);\n    };\n  }, [syncAcrossTabs, key, loadValue]);\n\n  return {\n    value,\n    isLoading,\n    error,\n    setValue,\n    removeValue,\n    refresh,\n  };\n}\n\n/**\n * Hook for accessing secure storage with TTL\n * Convenience wrapper with automatic expiration\n *\n * @param key - The storage key\n * @param ttl - Time-to-live in milliseconds\n * @param options - Hook options (excluding refreshInterval which is set automatically)\n * @returns Secure storage operations and state with TTL enforcement\n */\nexport function useSecureStorageWithTTL<T>(\n  key: string,\n  ttl: number,\n  options: Omit<UseSecureStorageOptions, 'refreshInterval'> = {}\n): UseSecureStorageResult<T> {\n  const result = useSecureStorage<T>(key, {\n    ...options,\n    // Refresh at half the TTL to catch expirations\n    refreshInterval: ttl / 2,\n  });\n\n  // Store setValue in a ref to avoid dependency instability\n  const setValueRef = useRef(result.setValue);\n  useEffect(() => {\n    setValueRef.current = result.setValue;\n  }, [result.setValue]);\n\n  // Wrapper to always include TTL\n  const setValueWithTTL = useCallback(\n    async (newValue: T, setOptions?: SecureStorageSetOptions): Promise<void> => {\n      await setValueRef.current(newValue, { ...setOptions, ttl });\n    },\n    [ttl]\n  );\n\n  return {\n    ...result,\n    setValue: setValueWithTTL,\n  };\n}\n"],"names":["useSecureStorage","key","options","storage","initialValue","syncAcrossTabs","refreshInterval","value","setValueState","useState","isLoading","setIsLoading","error","setError","mountedRef","useRef","refreshIntervalRef","getStorage","useCallback","getSecureStorage","loadValue","result","err","setValue","newValue","setOptions","removeValue","refresh","useEffect","handleStorageChange","event","useSecureStorageWithTTL","ttl","setValueRef","setValueWithTTL"],"mappings":"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"}