All files / src/core/strategies SessionStorageStrategy.ts

97.84% Statements 91/93
100% Branches 32/32
100% Functions 19/19
97.84% Lines 91/93

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 1191x       1x 88x 88x   88x 88x 88x 88x   88x 100x 100x   88x 98x 14x 14x 14x 14x 14x 98x   88x 88x   88x     88x 88x     88x 89x 89x   88x 43x 43x   88x 87x 87x 87x   88x 67x 67x   88x 128x 128x   88x 16x 16x   88x 33x 33x   88x 2x 2x   88x 17x 17x   88x 17x 17x   88x 35x   19x 35x 6x 6x 6x 6x 6x 35x 13x 13x 13x 13x 13x 13x 19x 35x   88x 97x 97x   88x 7x   4x 4x 4x 4x 4x 4x 4x 4x 7x   88x 87x 87x 88x  
import type { DataModel, StorageBase, ValueType } from "../types";
import { StorageEngine } from "../types";
 
/** @ignore */
export class SessionStorageStrategy implements StorageBase {
	private prefixKey: string;
	private memoryCache: Map<string, DataModel<ValueType>> = new Map();
 
	constructor(prefixKey = "HybridWebCache") {
		this.prefixKey = `${prefixKey.trim()}::`;
		this.loadMemoryCache(); // Load existing data into memory cache on initialization
	}
 
	private formattedKey(key: string): string {
		return `${this.prefixKey}${key}`;
	}
 
	private _forEachStorage(callback: (originalKey: string, value: string | null) => void): void {
		for (let i = 0; i < sessionStorage.length; i++) {
			const key = sessionStorage.key(i);
			if (key?.startsWith(this.prefixKey)) {
				callback(key.replace(this.prefixKey, ""), sessionStorage.getItem(key));
			}
		}
	}
 
	private loadMemoryCache(): void {
		this.memoryCache.clear(); // Clear existing cache before loading
 
		this._forEachStorage((key, value) => {
			const data: DataModel<ValueType> = JSON.parse(value ?? "{}");
			this.memoryCache.set(key, data);
		});
	}
 
	/** @internal */
	async init(): Promise<void> {
		return Promise.resolve();
	}
 
	set<T extends ValueType>(key: string, data: DataModel<T>): Promise<void> {
		return Promise.resolve(this.setSync(key, data));
	}
 
	setSync<T extends ValueType>(key: string, data: DataModel<T>): void {
		sessionStorage.setItem(this.formattedKey(key), JSON.stringify(data));
		this.memoryCache.set(key, data);
	}
 
	get<T extends ValueType>(key: string): Promise<DataModel<T> | undefined> {
		return Promise.resolve(this.getSync(key));
	}
 
	getSync<T extends ValueType>(key: string): DataModel<T> | undefined {
		return this.memoryCache.get(key) as DataModel<T>;
	}
 
	getAll<T extends ValueType>(): Promise<Map<string, DataModel<T>> | null> {
		return Promise.resolve(this.getAllSync<T>());
	}
 
	getAllSync<T extends ValueType>(): Map<string, DataModel<T>> | null {
		return this.memoryCache.size > 0 ? (this.memoryCache as Map<string, DataModel<T>>) : null;
	}
 
	has(key: string): Promise<boolean> {
		return Promise.resolve(this.hasSync(key));
	}
 
	hasSync(key: string): boolean {
		return this.memoryCache.has(key);
	}
 
	unset(key?: string): Promise<boolean> {
		return Promise.resolve(this.unsetSync(key));
	}
 
	unsetSync(key?: string): boolean {
		if (this.memoryCache.size === 0) return false;
 
		let result = false;
		if (!key) {
			const keysToRemove: string[] = [];
			this._forEachStorage((originalKey, _value) => keysToRemove.push(originalKey));
			keysToRemove.forEach((k) => sessionStorage.removeItem(k));
			this.memoryCache.clear();
			result = true;
		} else {
			if (this.hasSync(key)) {
				const fKey = this.formattedKey(key);
				sessionStorage.removeItem(fKey);
				result = this.memoryCache.delete(key);
			}
		}
		return result;
	}
 
	get length(): number {
		return this.memoryCache.size;
	}
 
	get bytes(): number {
		if (this.memoryCache.size === 0) return 0;
 
		let totalBytes = 0;
		this._forEachStorage((key, value) => {
			totalBytes += new TextEncoder().encode(key).length;
			if (value) {
				totalBytes += new TextEncoder().encode(value).length;
			}
		});
		return totalBytes;
	}
 
	get type(): StorageEngine {
		return StorageEngine.SessionStorage;
	}
}