- Edge 5.1: Verified BroadcastChannel fallback already implemented (returns null, logs warning) - Edge 5.2: Created safe storage utility for localStorage/sessionStorage - safeLocalStorage and safeSessionStorage automatically fall back to in-memory storage - Handles private browsing mode and quota exceeded errors gracefully - Tests availability before use - Logs warnings when fallback is used - Provides utility functions to check support - Improves reliability in all browser environments
159 lines
4.1 KiB
TypeScript
159 lines
4.1 KiB
TypeScript
/**
|
|
* Edge 5.2: Safe Storage Utility
|
|
* Provides safe wrappers for localStorage and sessionStorage with fallback support
|
|
* for private browsing mode and unsupported browsers.
|
|
*
|
|
* In private browsing mode, localStorage may throw errors when accessed.
|
|
* This utility handles those errors gracefully by falling back to in-memory storage.
|
|
*/
|
|
|
|
import { logger } from './logger';
|
|
|
|
/**
|
|
* In-memory storage fallback for when localStorage/sessionStorage is unavailable
|
|
*/
|
|
class MemoryStorage implements Storage {
|
|
private data: Map<string, string> = new Map();
|
|
|
|
get length(): number {
|
|
return this.data.size;
|
|
}
|
|
|
|
clear(): void {
|
|
this.data.clear();
|
|
}
|
|
|
|
getItem(key: string): string | null {
|
|
return this.data.get(key) || null;
|
|
}
|
|
|
|
key(index: number): string | null {
|
|
const keys = Array.from(this.data.keys());
|
|
return keys[index] || null;
|
|
}
|
|
|
|
removeItem(key: string): void {
|
|
this.data.delete(key);
|
|
}
|
|
|
|
setItem(key: string, value: string): void {
|
|
this.data.set(key, value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Edge 5.2: Test if localStorage is available and working
|
|
* Returns true if localStorage can be used, false otherwise
|
|
*/
|
|
function isLocalStorageAvailable(): boolean {
|
|
if (typeof window === 'undefined') {
|
|
return false; // SSR environment
|
|
}
|
|
|
|
try {
|
|
const testKey = '__veza_storage_test__';
|
|
localStorage.setItem(testKey, 'test');
|
|
localStorage.removeItem(testKey);
|
|
return true;
|
|
} catch {
|
|
// localStorage is not available (private browsing, quota exceeded, etc.)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Edge 5.2: Test if sessionStorage is available and working
|
|
* Returns true if sessionStorage can be used, false otherwise
|
|
*/
|
|
function isSessionStorageAvailable(): boolean {
|
|
if (typeof window === 'undefined') {
|
|
return false; // SSR environment
|
|
}
|
|
|
|
try {
|
|
const testKey = '__veza_session_storage_test__';
|
|
sessionStorage.setItem(testKey, 'test');
|
|
sessionStorage.removeItem(testKey);
|
|
return true;
|
|
} catch {
|
|
// sessionStorage is not available (private browsing, quota exceeded, etc.)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Create fallback storage instances
|
|
const memoryLocalStorage = new MemoryStorage();
|
|
const memorySessionStorage = new MemoryStorage();
|
|
|
|
// Determine which storage to use
|
|
const localStorageAvailable = isLocalStorageAvailable();
|
|
const sessionStorageAvailable = isSessionStorageAvailable();
|
|
|
|
// Log warnings if fallback is being used
|
|
if (typeof window !== 'undefined' && !localStorageAvailable) {
|
|
logger.warn(
|
|
'[SafeStorage] localStorage not available, using in-memory fallback',
|
|
{
|
|
reason: 'private_browsing_or_quota_exceeded',
|
|
},
|
|
);
|
|
}
|
|
|
|
if (typeof window !== 'undefined' && !sessionStorageAvailable) {
|
|
logger.warn(
|
|
'[SafeStorage] sessionStorage not available, using in-memory fallback',
|
|
{
|
|
reason: 'private_browsing_or_quota_exceeded',
|
|
},
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Edge 5.2: Safe localStorage wrapper
|
|
* Automatically falls back to in-memory storage if localStorage is unavailable
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* import { safeLocalStorage } from '@/utils/safeStorage';
|
|
*
|
|
* // Use like regular localStorage
|
|
* safeLocalStorage.setItem('key', 'value');
|
|
* const value = safeLocalStorage.getItem('key');
|
|
* safeLocalStorage.removeItem('key');
|
|
* ```
|
|
*/
|
|
export const safeLocalStorage: Storage = localStorageAvailable
|
|
? window.localStorage
|
|
: memoryLocalStorage;
|
|
|
|
/**
|
|
* Edge 5.2: Safe sessionStorage wrapper
|
|
* Automatically falls back to in-memory storage if sessionStorage is unavailable
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* import { safeSessionStorage } from '@/utils/safeStorage';
|
|
*
|
|
* // Use like regular sessionStorage
|
|
* safeSessionStorage.setItem('key', 'value');
|
|
* const value = safeSessionStorage.getItem('key');
|
|
* safeSessionStorage.removeItem('key');
|
|
* ```
|
|
*/
|
|
export const safeSessionStorage: Storage = sessionStorageAvailable
|
|
? window.sessionStorage
|
|
: memorySessionStorage;
|
|
|
|
/**
|
|
* Edge 5.2: Check if localStorage is available (not using fallback)
|
|
*/
|
|
export function isLocalStorageSupported(): boolean {
|
|
return localStorageAvailable;
|
|
}
|
|
|
|
/**
|
|
* Edge 5.2: Check if sessionStorage is available (not using fallback)
|
|
*/
|
|
export function isSessionStorageSupported(): boolean {
|
|
return sessionStorageAvailable;
|
|
}
|