k-l-lambda's picture
updated
502af73
/**
* Frontend Storage Utility
*
* Provides a centralized API for storing and retrieving data in browser storage.
* Supports both sessionStorage and localStorage with type-safe operations.
*
* Features:
* - Type-safe storage operations
* - Automatic JSON serialization/deserialization
* - Error handling with fallback values
* - Namespaced keys to avoid collisions
* - Support for both session and local storage
*/
/**
* Storage type enum
*/
export enum StorageType {
SESSION = "session",
LOCAL = "local"
}
/**
* Storage keys enum - Add new keys here
*/
export enum StorageKey {
// Game state
GAME_STATE = "trigoGameState",
// AI settings
AI_PLAYER_COLOR = "trigoAIPlayerColor",
// User preferences (examples for future use)
// THEME = "trigoTheme",
// LANGUAGE = "trigoLanguage",
// BOARD_SHAPE = "trigoBoardShape",
// CAMERA_POSITION = "trigoCameraPosition",
}
/**
* Storage configuration
*/
interface StorageConfig {
type: StorageType;
prefix?: string; // Optional namespace prefix for all keys
}
/**
* Default configuration
*/
const DEFAULT_CONFIG: StorageConfig = {
type: StorageType.SESSION,
prefix: "" // No prefix by default, keys already have 'trigo' prefix
};
/**
* Get the appropriate storage instance
*/
function getStorage(type: StorageType): Storage {
return type === StorageType.SESSION ? sessionStorage : localStorage;
}
/**
* Build full storage key with optional prefix
*/
function buildKey(key: StorageKey, prefix?: string): string {
return prefix ? `${prefix}${key}` : key;
}
/**
* Storage Manager Class
*/
export class StorageManager {
private config: StorageConfig;
constructor(config: Partial<StorageConfig> = {}) {
this.config = { ...DEFAULT_CONFIG, ...config };
}
/**
* Get storage instance based on configuration
*/
private getStorageInstance(): Storage {
return getStorage(this.config.type);
}
/**
* Set a value in storage
* @param key - Storage key
* @param value - Value to store (will be JSON stringified)
* @returns true if successful, false otherwise
*/
set<T>(key: StorageKey, value: T): boolean {
try {
const storage = this.getStorageInstance();
const fullKey = buildKey(key, this.config.prefix);
const serialized = JSON.stringify(value);
storage.setItem(fullKey, serialized);
return true;
} catch (error) {
console.warn(`[Storage] Failed to set ${key}:`, error);
return false;
}
}
/**
* Get a value from storage
* @param key - Storage key
* @param defaultValue - Default value if key doesn't exist or parsing fails
* @returns Stored value or default value
*/
get<T>(key: StorageKey, defaultValue: T): T {
try {
const storage = this.getStorageInstance();
const fullKey = buildKey(key, this.config.prefix);
const item = storage.getItem(fullKey);
if (item === null) {
return defaultValue;
}
return JSON.parse(item) as T;
} catch (error) {
console.warn(`[Storage] Failed to get ${key}:`, error);
return defaultValue;
}
}
/**
* Get a string value directly (no JSON parsing)
* Useful for simple string values
*/
getString(key: StorageKey, defaultValue: string = ""): string {
try {
const storage = this.getStorageInstance();
const fullKey = buildKey(key, this.config.prefix);
return storage.getItem(fullKey) ?? defaultValue;
} catch (error) {
console.warn(`[Storage] Failed to get string ${key}:`, error);
return defaultValue;
}
}
/**
* Set a string value directly (no JSON stringification)
* Useful for simple string values
*/
setString(key: StorageKey, value: string): boolean {
try {
const storage = this.getStorageInstance();
const fullKey = buildKey(key, this.config.prefix);
storage.setItem(fullKey, value);
return true;
} catch (error) {
console.warn(`[Storage] Failed to set string ${key}:`, error);
return false;
}
}
/**
* Remove a value from storage
* @param key - Storage key
* @returns true if successful, false otherwise
*/
remove(key: StorageKey): boolean {
try {
const storage = this.getStorageInstance();
const fullKey = buildKey(key, this.config.prefix);
storage.removeItem(fullKey);
return true;
} catch (error) {
console.warn(`[Storage] Failed to remove ${key}:`, error);
return false;
}
}
/**
* Check if a key exists in storage
*/
has(key: StorageKey): boolean {
try {
const storage = this.getStorageInstance();
const fullKey = buildKey(key, this.config.prefix);
return storage.getItem(fullKey) !== null;
} catch (error) {
console.warn(`[Storage] Failed to check ${key}:`, error);
return false;
}
}
/**
* Clear all items from storage
* WARNING: This clears ALL items in the storage, not just trigo-related ones
*/
clear(): boolean {
try {
const storage = this.getStorageInstance();
storage.clear();
return true;
} catch (error) {
console.warn("[Storage] Failed to clear storage:", error);
return false;
}
}
/**
* Get the current storage type
*/
getStorageType(): StorageType {
return this.config.type;
}
/**
* Switch storage type
*/
setStorageType(type: StorageType): void {
this.config.type = type;
}
}
/**
* Default storage manager instance (sessionStorage)
*/
export const storage = new StorageManager();
/**
* Local storage manager instance
* Note: Named localStorageManager to avoid conflict with built-in localStorage
*/
export const localStorageManager = new StorageManager({ type: StorageType.LOCAL });
/**
* Example helper functions for future use:
*
* export function saveTheme(theme: "light" | "dark"): boolean {
* return localStorageManager.setString(StorageKey.THEME, theme);
* }
*
* export function loadTheme(): "light" | "dark" {
* const stored = localStorageManager.getString(StorageKey.THEME);
* return stored === "dark" ? "dark" : "light";
* }
*
* export function saveBoardShape(shape: { x: number; y: number; z: number }): boolean {
* return storage.set(StorageKey.BOARD_SHAPE, shape);
* }
*
* export function loadBoardShape(): { x: number; y: number; z: number } | null {
* return storage.get(StorageKey.BOARD_SHAPE, null);
* }
*/