UNPKG

ng7-storage

Version:

An Angular service for browser session storage management with optional base64 encryption/decryption.

1 lines 53.6 kB
{"version":3,"file":"ng7-storage.mjs","sources":["../../../projects/ng-storage/src/lib/ng-storage.model.ts","../../../projects/ng-storage/src/lib/ng-storage.module.ts","../../../projects/ng-storage/src/lib/utils.ts","../../../projects/ng-storage/src/lib/ng-storage.service.ts","../../../projects/ng-storage/src/ng7-storage.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport type StorageType = 'localStorage' | 'sessionStorage';\n\nexport interface StorageItem<T = any> {\n value: T;\n timestamp: number;\n expiry?: number;\n encrypted?: boolean;\n}\n\nexport interface StorageConfig {\n prefix?: string;\n defaultTTL?: number; // in minutes\n enableLogging?: boolean;\n caseSensitive?: boolean;\n storageType?: StorageType;\n}\n\nexport interface NamedStorageConfig {\n [name: string]: StorageConfig;\n}\n\nexport interface StorageFlags {\n autoCleanup?: boolean;\n strictMode?: boolean;\n enableMetrics?: boolean;\n}\n\nexport interface StorageStats {\n totalItems: number;\n totalSize: number; // in bytes\n availableSpace: number;\n items: Array<{\n key: string;\n size: number;\n timestamp: number;\n hasExpiry: boolean;\n }>;\n}\n\nexport interface StorageChangeEvent<T = any> {\n key: string;\n oldValue: T | null;\n newValue: T | null;\n action: 'set' | 'remove' | 'clear' | 'expire';\n timestamp: number;\n}\n\n// Injection Tokens\nexport const STORAGE_OPTIONS = new InjectionToken<StorageConfig>(\n 'StorageOptions'\n);\nexport const STORAGE_NAMED_OPTIONS = new InjectionToken<NamedStorageConfig>(\n 'StorageNamedOptions'\n);\nexport const STORAGE_FLAGS = new InjectionToken<StorageFlags>('StorageFlags');\n\n// Default Configuration\nexport const DEFAULT_STORAGE_CONFIG: Required<StorageConfig> = {\n prefix: 'ng-storage',\n defaultTTL: 0,\n enableLogging: false,\n caseSensitive: false,\n storageType: 'sessionStorage',\n};\n\nexport const DEFAULT_STORAGE_FLAGS: Required<StorageFlags> = {\n autoCleanup: true,\n strictMode: false,\n enableMetrics: false,\n};\n","import { Provider } from '@angular/core';\nimport {\n DEFAULT_STORAGE_FLAGS,\n NamedStorageConfig,\n STORAGE_FLAGS,\n STORAGE_NAMED_OPTIONS,\n STORAGE_OPTIONS,\n StorageConfig,\n StorageFlags,\n} from './ng-storage.model';\nimport { NgStorageService } from './ng-storage.service';\n\nexport function provideNgStorage<T = any>(\n optionsFactory: () => StorageConfig,\n flags: StorageFlags = {}\n): Provider[] {\n return [\n NgStorageService,\n {\n provide: STORAGE_OPTIONS,\n useFactory: optionsFactory,\n },\n {\n provide: STORAGE_FLAGS,\n useValue: { ...DEFAULT_STORAGE_FLAGS, ...flags },\n },\n ];\n}\n\n/**\n * Provides multiple named NgStorageService instances\n */\nexport function provideNamedNgStorage(\n optionsFactory: () => NamedStorageConfig,\n flags: StorageFlags = {}\n): Provider[] {\n return [\n {\n provide: STORAGE_NAMED_OPTIONS,\n useFactory: optionsFactory,\n },\n {\n provide: STORAGE_FLAGS,\n useValue: { ...DEFAULT_STORAGE_FLAGS, ...flags },\n },\n ];\n}\n","export class CryptoUtils {\n private static readonly ALGORITHM = 'AES-GCM';\n private static readonly KEY_LENGTH = 256;\n private static readonly IV_LENGTH = 12; // 96 bits for GCM\n private static readonly TAG_LENGTH = 128; // 128 bits authentication tag\n\n private static encryptionKey: CryptoKey | null = null;\n private static keyPromise: Promise<CryptoKey> | null = null;\n\n /**\n * Derives a consistent key from a password using PBKDF2\n */\n private static async deriveKey(\n password: string,\n salt: Uint8Array\n ): Promise<CryptoKey> {\n const encoder = new TextEncoder();\n const keyMaterial = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(password),\n { name: 'PBKDF2' },\n false,\n ['deriveKey']\n );\n\n return crypto.subtle.deriveKey(\n {\n name: 'PBKDF2',\n salt: salt,\n iterations: 100000, // OWASP recommended minimum\n hash: 'SHA-256',\n },\n keyMaterial,\n {\n name: this.ALGORITHM,\n length: this.KEY_LENGTH,\n },\n false,\n ['encrypt', 'decrypt']\n );\n }\n\n /**\n * Gets or creates the encryption key\n */\n private static async getEncryptionKey(): Promise<CryptoKey> {\n if (this.encryptionKey) {\n return this.encryptionKey;\n }\n\n if (this.keyPromise) {\n return this.keyPromise;\n }\n\n this.keyPromise = this.createEncryptionKey();\n this.encryptionKey = await this.keyPromise;\n return this.encryptionKey;\n }\n\n /**\n * Creates a new encryption key or derives from stored salt\n */\n private static async createEncryptionKey(): Promise<CryptoKey> {\n const keyIdentifier = '__ng_storage_key_salt__';\n let salt: Uint8Array;\n\n // Try to get existing salt from localStorage\n try {\n const existingSalt = localStorage.getItem(keyIdentifier);\n if (existingSalt) {\n salt = new Uint8Array(JSON.parse(existingSalt));\n } else {\n // Generate new salt\n salt = crypto.getRandomValues(new Uint8Array(32));\n localStorage.setItem(keyIdentifier, JSON.stringify(Array.from(salt)));\n }\n } catch {\n // Fallback to generated salt if localStorage is not available\n salt = crypto.getRandomValues(new Uint8Array(32));\n }\n\n // Use a default password - in production, you might want to make this configurable\n const password = 'ng-storage-default-key-2024';\n\n return this.deriveKey(password, salt);\n }\n\n /**\n * Encrypts data using AES-GCM\n */\n static async encrypt(data: string): Promise<string> {\n try {\n const key = await this.getEncryptionKey();\n const encoder = new TextEncoder();\n const dataBuffer = encoder.encode(data);\n\n // Generate random IV\n const iv = crypto.getRandomValues(new Uint8Array(this.IV_LENGTH));\n\n // Encrypt the data\n const encryptedBuffer = await crypto.subtle.encrypt(\n {\n name: this.ALGORITHM,\n iv: iv,\n tagLength: this.TAG_LENGTH,\n },\n key,\n dataBuffer\n );\n\n // Combine IV and encrypted data\n const encryptedArray = new Uint8Array(encryptedBuffer);\n const combined = new Uint8Array(iv.length + encryptedArray.length);\n combined.set(iv);\n combined.set(encryptedArray, iv.length);\n\n // Convert to base64 for storage\n return btoa(String.fromCharCode(...combined));\n } catch (error) {\n throw new Error(\n `Encryption failed: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n\n /**\n * Decrypts data using AES-GCM\n */\n static async decrypt(encryptedData: string): Promise<string> {\n try {\n const key = await this.getEncryptionKey();\n\n // Convert from base64\n const combined = new Uint8Array(\n atob(encryptedData)\n .split('')\n .map((char) => char.charCodeAt(0))\n );\n\n // Extract IV and encrypted data\n const iv = combined.slice(0, this.IV_LENGTH);\n const encryptedBuffer = combined.slice(this.IV_LENGTH);\n\n // Decrypt the data\n const decryptedBuffer = await crypto.subtle.decrypt(\n {\n name: this.ALGORITHM,\n iv: iv,\n tagLength: this.TAG_LENGTH,\n },\n key,\n encryptedBuffer\n );\n\n // Convert back to string\n const decoder = new TextDecoder();\n return decoder.decode(decryptedBuffer);\n } catch (error) {\n throw new Error(\n `Decryption failed: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n\n /**\n * Checks if Web Crypto API is available\n */\n static isSupported(): boolean {\n return (\n typeof crypto !== 'undefined' &&\n typeof crypto.subtle !== 'undefined' &&\n typeof crypto.subtle.encrypt === 'function'\n );\n }\n\n /**\n * Clears the cached encryption key (useful for testing or key rotation)\n */\n static clearKey(): void {\n this.encryptionKey = null;\n this.keyPromise = null;\n }\n}\n","import {\n Injectable,\n signal,\n computed,\n Inject,\n Optional,\n Provider,\n} from '@angular/core';\nimport {\n BehaviorSubject,\n Observable,\n Subject,\n filter,\n map,\n distinctUntilChanged,\n} from 'rxjs';\nimport {\n DEFAULT_STORAGE_CONFIG,\n DEFAULT_STORAGE_FLAGS,\n STORAGE_FLAGS,\n STORAGE_OPTIONS,\n StorageChangeEvent,\n StorageConfig,\n StorageFlags,\n StorageItem,\n StorageStats,\n StorageType,\n} from './ng-storage.model';\nimport { provideNgStorage } from './ng-storage.module';\nimport { CryptoUtils } from './utils';\n\n/**\n * Simple provider for basic configuration\n */\nexport function provideNgStorageConfig(\n config: StorageConfig,\n flags: StorageFlags = {}\n): Provider[] {\n return provideNgStorage(() => config, flags);\n}\n\n@Injectable({\n providedIn: 'root',\n})\nexport class NgStorageService {\n private readonly config: Required<StorageConfig>;\n private readonly flags: Required<StorageFlags>;\n private readonly storage: Storage;\n private readonly storageTypeName: string;\n private readonly supportedMessage: string;\n private readonly isSupported: boolean;\n private readonly isCryptoSupported: boolean;\n\n // Reactive state management\n private readonly _storageData = signal<Record<string, any>>({});\n private readonly _changeSubject = new Subject<StorageChangeEvent>();\n private readonly _keyChangeSubjects = new Map<string, BehaviorSubject<any>>();\n\n // Public reactive properties\n public readonly storageData = this._storageData.asReadonly();\n public readonly changes$ = this._changeSubject.asObservable();\n\n // Computed stats\n public readonly stats = computed(() => {\n const data = this._storageData();\n return {\n itemCount: Object.keys(data).length,\n isEmpty: Object.keys(data).length === 0,\n keys: Object.keys(data),\n };\n });\n\n constructor(\n @Optional() @Inject(STORAGE_OPTIONS) options?: StorageConfig,\n @Optional() @Inject(STORAGE_FLAGS) flags?: StorageFlags\n ) {\n // Merge configurations\n this.config = {\n ...DEFAULT_STORAGE_CONFIG,\n ...options,\n };\n\n this.flags = {\n ...DEFAULT_STORAGE_FLAGS,\n ...flags,\n };\n\n // Set storage type and messages\n this.storageTypeName =\n this.config.storageType === 'localStorage'\n ? 'local storage'\n : 'session storage';\n this.supportedMessage = `Your browser doesn't support ${this.storageTypeName}. Please update your browser.`;\n\n // Get the appropriate storage instance\n this.storage = this.getStorageInstance();\n\n // Check if storage is supported\n this.isSupported = this.checkStorageSupport();\n this.isCryptoSupported = CryptoUtils.isSupported();\n\n if (!this.isSupported) {\n console.error(this.supportedMessage);\n if (this.flags.strictMode) {\n throw new Error(this.supportedMessage);\n }\n }\n\n if (!this.isCryptoSupported) {\n console.warn(\n '[NgStorageService] Web Crypto API not supported. Encryption will be disabled.'\n );\n }\n\n // Initialize reactive state\n this.initializeReactiveState();\n\n // Clean expired items on initialization\n if (this.flags.autoCleanup) {\n this.cleanupExpiredItems();\n }\n\n // Set up periodic cleanup\n if (typeof window !== 'undefined' && this.flags.autoCleanup) {\n setInterval(() => this.cleanupExpiredItems(), 5 * 60 * 1000); // Every 5 minutes\n }\n }\n\n /**\n * Gets the appropriate storage instance based on configuration\n */\n private getStorageInstance(): Storage {\n if (typeof window === 'undefined') {\n throw new Error('Storage is not available in server-side rendering');\n }\n\n switch (this.config.storageType) {\n case 'localStorage':\n return window.localStorage;\n case 'sessionStorage':\n return window.sessionStorage;\n default:\n throw new Error(`Unsupported storage type: ${this.config.storageType}`);\n }\n }\n\n /**\n * Checks if the configured storage is supported and available\n */\n private checkStorageSupport(): boolean {\n try {\n if (typeof window === 'undefined') {\n return false;\n }\n\n const testKey = '__ng_storage_test__';\n this.storage.setItem(testKey, 'test');\n this.storage.removeItem(testKey);\n return true;\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Initializes reactive state from existing storage\n */\n private async initializeReactiveState(): Promise<void> {\n try {\n const data: Record<string, any> = {};\n const prefix = `${this.config.prefix}:`;\n\n for (let i = 0; i < this.storage.length; i++) {\n const key = this.storage.key(i);\n if (key?.startsWith(prefix)) {\n const originalKey = key.substring(prefix.length);\n try {\n const value = await this.getDataInternal(originalKey);\n if (value !== null) {\n data[originalKey] = value;\n }\n } catch (error) {\n this.log(\n 'Failed to load key during initialization',\n originalKey,\n error\n );\n }\n }\n }\n\n this._storageData.set(data);\n } catch (error) {\n this.log('Failed to initialize reactive state', '', error);\n }\n }\n\n /**\n * Generates a storage key with prefix\n */\n private generateKey(key: string): string {\n if (!key || typeof key !== 'string') {\n throw new Error('Storage key must be a non-empty string');\n }\n\n const processedKey = this.config.caseSensitive ? key : key.toLowerCase();\n return `${this.config.prefix}:${processedKey}`;\n }\n\n /**\n * Logs debug information if logging is enabled\n */\n private log(action: string, key: string, data?: any): void {\n if (this.config.enableLogging) {\n console.log(`[NgStorageService] ${action}:`, { key, data });\n }\n }\n\n /**\n * Encrypts data using AES-GCM encryption\n */\n private async encrypt(data: string): Promise<string> {\n if (!this.isCryptoSupported) {\n this.log(\n 'Crypto not supported, falling back to base64 encoding',\n '',\n null\n );\n return btoa(encodeURIComponent(data));\n }\n\n try {\n return await CryptoUtils.encrypt(data);\n } catch (error) {\n this.log('Encryption failed, falling back to base64 encoding', '', error);\n return btoa(encodeURIComponent(data));\n }\n }\n\n /**\n * Decrypts data using AES-GCM decryption\n */\n private async decrypt(encryptedData: string): Promise<string> {\n if (!this.isCryptoSupported) {\n try {\n return decodeURIComponent(atob(encryptedData));\n } catch (error) {\n this.log('Base64 decoding failed', '', error);\n throw new Error('Failed to decode data');\n }\n }\n\n try {\n return await CryptoUtils.decrypt(encryptedData);\n } catch (error) {\n // Fallback to base64 decoding for backward compatibility\n try {\n return decodeURIComponent(atob(encryptedData));\n } catch (fallbackError) {\n this.log('Both decryption methods failed', '', {\n error,\n fallbackError,\n });\n throw new Error('Failed to decrypt data');\n }\n }\n }\n\n /**\n * Calculates the expiry timestamp\n */\n private calculateExpiry(ttlMinutes?: number): number | undefined {\n const ttl = ttlMinutes ?? this.config.defaultTTL;\n return ttl > 0 ? Date.now() + ttl * 60 * 1000 : undefined;\n }\n\n /**\n * Checks if an item has expired\n */\n private isExpired(item: StorageItem): boolean {\n return item.expiry ? Date.now() > item.expiry : false;\n }\n\n /**\n * Emits change event\n */\n private emitChange<T>(\n key: string,\n oldValue: T | null,\n newValue: T | null,\n action: StorageChangeEvent['action']\n ): void {\n const event: StorageChangeEvent<T> = {\n key,\n oldValue,\n newValue,\n action,\n timestamp: Date.now(),\n };\n\n this._changeSubject.next(event);\n\n // Update key-specific subject\n const keySubject = this._keyChangeSubjects.get(key);\n if (keySubject) {\n keySubject.next(newValue);\n }\n\n this.log('Change emitted', key, event);\n }\n\n /**\n * Updates reactive state\n */\n private updateReactiveState(\n key: string,\n value: any,\n action: StorageChangeEvent['action']\n ): void {\n const currentData = this._storageData();\n const oldValue = currentData[key] ?? null;\n\n if (action === 'remove' || action === 'clear' || action === 'expire') {\n const newData = { ...currentData };\n delete newData[key];\n this._storageData.set(newData);\n this.emitChange(key, oldValue, null, action);\n } else {\n this._storageData.update((data) => ({ ...data, [key]: value }));\n this.emitChange(key, oldValue, value, action);\n }\n }\n\n /**\n * Internal get data method without reactive updates\n */\n private async getDataInternal<T = any>(\n key: string,\n options: { decrypt?: boolean; defaultValue?: T } = {}\n ): Promise<T | null> {\n try {\n const storageKey = this.generateKey(key);\n const { decrypt = false, defaultValue = null } = options;\n\n const rawData = this.storage.getItem(storageKey);\n\n if (!rawData) {\n return defaultValue;\n }\n\n let parsedData: string;\n\n if (decrypt) {\n parsedData = await this.decrypt(rawData);\n } else {\n parsedData = rawData;\n }\n\n const item: StorageItem<T> = JSON.parse(parsedData);\n\n // Check if item has expired\n if (this.isExpired(item)) {\n this.storage.removeItem(storageKey);\n return defaultValue;\n }\n\n return item.value;\n } catch (error) {\n this.log('Get data failed', key, error);\n return options.defaultValue ?? null;\n }\n }\n\n /**\n * Removes expired items from storage\n */\n private async cleanupExpiredItems(): Promise<void> {\n try {\n const keysToRemove: string[] = [];\n const currentData = this._storageData();\n\n for (let i = 0; i < this.storage.length; i++) {\n const key = this.storage.key(i);\n if (key?.startsWith(`${this.config.prefix}:`)) {\n try {\n const rawData = this.storage.getItem(key);\n if (rawData) {\n let parsedData = rawData;\n\n // Try to detect if encrypted\n try {\n JSON.parse(parsedData);\n } catch {\n try {\n parsedData = await this.decrypt(rawData);\n } catch {\n keysToRemove.push(key);\n continue;\n }\n }\n\n const item: StorageItem = JSON.parse(parsedData);\n if (this.isExpired(item)) {\n keysToRemove.push(key);\n }\n }\n } catch (error) {\n keysToRemove.push(key);\n }\n }\n }\n\n keysToRemove.forEach((storageKey) => {\n const originalKey = storageKey.substring(\n `${this.config.prefix}:`.length\n );\n this.storage.removeItem(storageKey);\n\n if (currentData[originalKey] !== undefined) {\n this.updateReactiveState(originalKey, null, 'expire');\n }\n\n this.log('Removed expired item', originalKey);\n });\n } catch (error) {\n this.log('Cleanup failed', '', error);\n }\n }\n\n /**\n * Stores data in storage with optional encryption and TTL\n */\n async setData<T = any>(\n key: string,\n value: T,\n options: {\n encrypt?: boolean;\n ttlMinutes?: number;\n } = {}\n ): Promise<boolean> {\n try {\n if (!this.isSupported) {\n throw new Error(this.supportedMessage);\n }\n\n const storageKey = this.generateKey(key);\n const { encrypt = false, ttlMinutes } = options;\n\n const item: StorageItem<T> = {\n value,\n timestamp: Date.now(),\n expiry: this.calculateExpiry(ttlMinutes),\n encrypted: encrypt,\n };\n\n let serializedData = JSON.stringify(item);\n\n if (encrypt) {\n serializedData = await this.encrypt(serializedData);\n }\n\n this.storage.setItem(storageKey, serializedData);\n this.updateReactiveState(key, value, 'set');\n this.log('Data stored', key, { value, options });\n\n return true;\n } catch (error) {\n this.log('Set data failed', key, error);\n if (error instanceof Error && error.name === 'QuotaExceededError') {\n throw new Error('Storage quota exceeded. Try clearing some data.');\n }\n throw error;\n }\n }\n\n /**\n * Retrieves data from storage with automatic decryption and expiry check\n */\n async getData<T = any>(\n key: string,\n options: {\n decrypt?: boolean;\n defaultValue?: T;\n } = {}\n ): Promise<T | null> {\n // Try to get from reactive state first\n const reactiveValue = this._storageData()[key];\n if (reactiveValue !== undefined) {\n return reactiveValue;\n }\n\n // Fallback to storage\n const value = await this.getDataInternal<T>(key, options);\n\n // Update reactive state if value found\n if (value !== null) {\n this._storageData.update((data) => ({ ...data, [key]: value }));\n }\n\n return value;\n }\n\n /**\n * Creates a signal for a specific key\n */\n async createSignal<T = any>(key: string, defaultValue?: T) {\n const initialValue = (await this.getData<T>(key)) ?? defaultValue ?? null;\n const keySignal = signal<T | null>(initialValue);\n\n // Subscribe to changes for this key\n this.watch<T>(key).subscribe((value) => {\n keySignal.set(value);\n });\n\n return keySignal.asReadonly();\n }\n\n /**\n * Watch changes for a specific key\n */\n watch<T = any>(key: string): Observable<T | null> {\n if (!this._keyChangeSubjects.has(key)) {\n // Initialize with current value asynchronously\n this.getData<T>(key).then((currentValue) => {\n if (!this._keyChangeSubjects.has(key)) {\n this._keyChangeSubjects.set(\n key,\n new BehaviorSubject<T | null>(currentValue)\n );\n }\n });\n\n // Create with null initially\n this._keyChangeSubjects.set(key, new BehaviorSubject<T | null>(null));\n }\n\n return this._keyChangeSubjects\n .get(key)!\n .asObservable()\n .pipe(distinctUntilChanged());\n }\n\n /**\n * Watch all changes\n */\n watchAll(): Observable<StorageChangeEvent> {\n return this.changes$;\n }\n\n /**\n * Watch changes for multiple keys\n */\n watchKeys<T = any>(\n keys: string[]\n ): Observable<{ key: string; value: T | null }> {\n return this.changes$.pipe(\n filter((event) => keys.includes(event.key)),\n map((event) => ({ key: event.key, value: event.newValue as T | null }))\n );\n }\n\n /**\n * Watch changes by pattern (simple glob-like matching)\n */\n watchPattern<T = any>(\n pattern: string\n ): Observable<{ key: string; value: T | null }> {\n const regex = new RegExp(pattern.replace(/\\*/g, '.*'));\n return this.changes$.pipe(\n filter((event) => regex.test(event.key)),\n map((event) => ({ key: event.key, value: event.newValue as T | null }))\n );\n }\n\n /**\n * Removes data associated with the specified key\n */\n removeData(key: string): boolean {\n try {\n if (!this.isSupported) {\n throw new Error(this.supportedMessage);\n }\n\n const storageKey = this.generateKey(key);\n this.storage.removeItem(storageKey);\n this.updateReactiveState(key, null, 'remove');\n this.log('Data removed', key);\n return true;\n } catch (error) {\n this.log('Remove data failed', key, error);\n return false;\n }\n }\n\n /**\n * Removes multiple items at once\n */\n removeMultiple(keys: string[]): { success: string[]; failed: string[] } {\n const result = { success: [] as string[], failed: [] as string[] };\n\n keys.forEach((key) => {\n if (this.removeData(key)) {\n result.success.push(key);\n } else {\n result.failed.push(key);\n }\n });\n\n return result;\n }\n\n /**\n * Clears all storage data with the current prefix\n */\n removeAll(): boolean {\n try {\n if (!this.isSupported) {\n throw new Error(this.supportedMessage);\n }\n\n const keysToRemove: string[] = [];\n const currentData = this._storageData();\n\n for (let i = 0; i < this.storage.length; i++) {\n const key = this.storage.key(i);\n if (key?.startsWith(`${this.config.prefix}:`)) {\n keysToRemove.push(key);\n }\n }\n\n keysToRemove.forEach((key) => this.storage.removeItem(key));\n\n // Clear reactive state and emit events\n Object.keys(currentData).forEach((key) => {\n this.updateReactiveState(key, null, 'clear');\n });\n\n this.log('All data cleared', '', { removedCount: keysToRemove.length });\n\n return true;\n } catch (error) {\n this.log('Clear all failed', '', error);\n return false;\n }\n }\n\n /**\n * Checks if a key exists in storage\n */\n async hasKey(key: string): Promise<boolean> {\n // Check reactive state first\n if (this._storageData()[key] !== undefined) {\n return true;\n }\n\n // Check storage\n try {\n if (!this.isSupported) {\n return false;\n }\n\n const value = await this.getDataInternal(key);\n const exists = value !== null;\n\n // Update reactive state if found\n if (exists) {\n this._storageData.update((data) => ({ ...data, [key]: value }));\n }\n\n return exists;\n } catch (error) {\n this.log('Has key check failed', key, error);\n return false;\n }\n }\n\n /**\n * Gets all keys managed by this service\n */\n getKeys(): string[] {\n return Object.keys(this._storageData());\n }\n\n /**\n * Gets storage statistics\n */\n async getStorageStats(): Promise<StorageStats> {\n const stats: StorageStats = {\n totalItems: 0,\n totalSize: 0,\n availableSpace: 0,\n items: [],\n };\n\n try {\n if (!this.isSupported) {\n return stats;\n }\n\n const prefix = `${this.config.prefix}:`;\n\n for (let i = 0; i < this.storage.length; i++) {\n const key = this.storage.key(i);\n if (key?.startsWith(prefix)) {\n const data = this.storage.getItem(key);\n if (data) {\n const size = new Blob([data]).size;\n stats.totalSize += size;\n stats.totalItems++;\n\n try {\n let parsedData = data;\n try {\n JSON.parse(parsedData);\n } catch {\n parsedData = await this.decrypt(data);\n }\n\n const item: StorageItem = JSON.parse(parsedData);\n\n stats.items.push({\n key: key.substring(prefix.length),\n size,\n timestamp: item.timestamp,\n hasExpiry: Boolean(item.expiry),\n });\n } catch {\n stats.items.push({\n key: key.substring(prefix.length),\n size,\n timestamp: 0,\n hasExpiry: false,\n });\n }\n }\n }\n }\n\n const estimatedLimit = 5 * 1024 * 1024; // 5MB\n stats.availableSpace = Math.max(0, estimatedLimit - stats.totalSize);\n } catch (error) {\n this.log('Get storage stats failed', '', error);\n }\n\n return stats;\n }\n\n /**\n * Updates existing data if key exists\n */\n async updateData<T = any>(\n key: string,\n updateFn: (currentValue: T | null) => T,\n options: {\n encrypt?: boolean;\n ttlMinutes?: number;\n } = {}\n ): Promise<boolean> {\n try {\n const currentValue = await this.getData<T>(key, {\n decrypt: options.encrypt,\n });\n const newValue = updateFn(currentValue);\n return await this.setData(key, newValue, options);\n } catch (error) {\n this.log('Update data failed', key, error);\n return false;\n }\n }\n\n /**\n * Sets data only if key doesn't exist\n */\n async setIfNotExists<T = any>(\n key: string,\n value: T,\n options: {\n encrypt?: boolean;\n ttlMinutes?: number;\n } = {}\n ): Promise<boolean> {\n if (!(await this.hasKey(key))) {\n return await this.setData(key, value, options);\n }\n return false;\n }\n\n /**\n * Gets storage configuration\n */\n getConfig(): Required<StorageConfig> {\n return { ...this.config };\n }\n\n /**\n * Gets the current storage type\n */\n getStorageType(): StorageType {\n return this.config.storageType;\n }\n\n /**\n * Switches storage type (creates a new instance)\n */\n static withStorageType(\n config: Partial<StorageConfig> & { storageType: StorageType }\n ): NgStorageService {\n return new NgStorageService(config);\n }\n\n /**\n * Creates a localStorage instance\n */\n static localStorage(\n config: Partial<Omit<StorageConfig, 'storageType'>> = {}\n ): NgStorageService {\n return new NgStorageService({ ...config, storageType: 'localStorage' });\n }\n\n /**\n * Creates a sessionStorage instance\n */\n static sessionStorage(\n config: Partial<Omit<StorageConfig, 'storageType'>> = {}\n ): NgStorageService {\n return new NgStorageService({ ...config, storageType: 'sessionStorage' });\n }\n\n /**\n * Checks if storage is supported\n */\n isStorageSupported(): boolean {\n return this.isSupported;\n }\n\n /**\n * Checks if encryption is supported\n */\n isEncryptionSupported(): boolean {\n return this.isCryptoSupported;\n }\n\n /**\n * Forces cleanup of expired items\n */\n async cleanup(): Promise<number> {\n const initialStats = await this.getStorageStats();\n await this.cleanupExpiredItems();\n const finalStats = await this.getStorageStats();\n\n const removedCount = initialStats.totalItems - finalStats.totalItems;\n this.log('Manual cleanup completed', '', { removedCount });\n\n return removedCount;\n }\n\n /**\n * Clears the encryption key (useful for key rotation)\n */\n clearEncryptionKey(): void {\n CryptoUtils.clearKey();\n this.log('Encryption key cleared', '');\n }\n\n /**\n * Destroys all subscriptions and cleanup\n */\n destroy(): void {\n this._changeSubject.complete();\n this._keyChangeSubjects.forEach((subject) => subject.complete());\n this._keyChangeSubjects.clear();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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