import { EncryptionUtils } from '@repo/utils'; import type { IStorageService } from './storage.interface'; export interface StorageOptions { encryptedKeys?: Set; plainTextKeys?: Set; } /** * Enterprise-grade localStorage wrapper with optional AES encryption. */ export class LocalStorageService implements IStorageService { private readonly encryption: EncryptionUtils; private readonly encryptedKeys: Set; private readonly plainTextKeys: Set; constructor(options?: StorageOptions, encryptionUtils?: EncryptionUtils) { this.encryption = encryptionUtils ?? EncryptionUtils.getInstance(); this.encryptedKeys = options?.encryptedKeys ?? new Set(); this.plainTextKeys = options?.plainTextKeys ?? new Set(); } private validateKey(key: TKey): void { if (!this.encryptedKeys.has(key) && !this.plainTextKeys.has(key)) { throw new Error(`[Storage Engine] Security Exception: Key '${key}' is not registered and cannot be accessed.`); } } private shouldEncrypt(key: TKey): boolean { return this.encryptedKeys.has(key); } async setItem(key: TKey, value: T): Promise { this.validateKey(key); const serialized = JSON.stringify(value); if (this.shouldEncrypt(key)) { const encrypted = this.encryption.encrypt(serialized); localStorage.setItem(key as string, encrypted); } else { localStorage.setItem(key as string, serialized); } } async getItem(key: TKey): Promise { this.validateKey(key); const raw = localStorage.getItem(key as string); if (raw === null) return null; try { if (this.shouldEncrypt(key)) { const decrypted = this.encryption.decrypt(raw); if (!decrypted) return null; return JSON.parse(decrypted) as T; } return JSON.parse(raw) as T; } catch { console.warn(`[core-storage] Failed to parse key "${key}". Removing corrupt entry.`); localStorage.removeItem(key as string); return null; } } async removeItem(key: TKey): Promise { this.validateKey(key); localStorage.removeItem(key as string); } async clear(): Promise { localStorage.clear(); } async hasItem(key: TKey): Promise { return localStorage.getItem(key as string) !== null; } async keys(): Promise { const result: TKey[] = []; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key !== null) result.push(key as TKey); } return result; } } export function createLocalStorage( options?: StorageOptions ): IStorageService { return new LocalStorageService(options); }