Created
April 1, 2023 17:18
-
-
Save AndrewPrifer/40ca687a69a8691f12abb93dcbf4a889 to your computer and use it in GitHub Desktop.
Rolling storage wrapping a Storage object
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * A storage class that will remove the oldest items when the storage size is exceeded. | |
| */ | |
| export class RollingStorage implements Storage { | |
| private baseKey: string; | |
| private maxSize: number; | |
| private storage: Storage; | |
| /** | |
| * @param baseKey Base key to use for storage. Will be prefixed to all keys. | |
| * @param maxSize Maximum size of storage in bytes. | |
| * @param storage Storage to use. Defaults to localStorage. | |
| */ | |
| constructor( | |
| baseKey: string, | |
| maxSize: number, | |
| storage: Storage = localStorage | |
| ) { | |
| this.baseKey = baseKey; | |
| this.maxSize = maxSize; | |
| this.storage = storage; | |
| if (!this.storage.getItem(this.getIndexKey())) { | |
| this.storage.setItem( | |
| this.getIndexKey(), | |
| JSON.stringify({ keys: [], size: 0 }) | |
| ); | |
| } | |
| } | |
| private getIndexKey(): string { | |
| return `${this.baseKey}_index`; | |
| } | |
| private getIndex(): { keys: string[]; size: number } { | |
| const indexString = this.storage.getItem(this.getIndexKey()); | |
| return indexString ? JSON.parse(indexString) : { keys: [], size: 0 }; | |
| } | |
| private setIndex(index: { keys: string[]; size: number }): void { | |
| this.storage.setItem(this.getIndexKey(), JSON.stringify(index)); | |
| } | |
| private getStringByteSize(str: string): number { | |
| return new Blob([str]).size; | |
| } | |
| private prefixedKey(key: string): string { | |
| return `${this.baseKey}_${key}`; | |
| } | |
| public setItem(key: string, value: any): void { | |
| const index = this.getIndex(); | |
| const currentIndex = index.keys.indexOf(key); | |
| const newValueString = JSON.stringify(value); | |
| const newSize = this.getStringByteSize(newValueString); | |
| if (currentIndex === -1) { | |
| index.keys.push(key); | |
| while (index.size + newSize > this.maxSize) { | |
| const removedKey = index.keys.shift(); | |
| if (removedKey) { | |
| const removedValueString = this.storage.getItem( | |
| this.prefixedKey(removedKey) | |
| ); | |
| this.storage.removeItem(this.prefixedKey(removedKey)); | |
| if (removedValueString) { | |
| index.size -= this.getStringByteSize(removedValueString); | |
| } | |
| } | |
| } | |
| } else { | |
| const oldValueString = this.storage.getItem(this.prefixedKey(key)); | |
| if (oldValueString) { | |
| index.size -= this.getStringByteSize(oldValueString); | |
| } | |
| } | |
| this.storage.setItem(this.prefixedKey(key), newValueString); | |
| index.size += newSize; | |
| this.setIndex(index); | |
| } | |
| public getItem(key: string): any | null { | |
| const itemString = this.storage.getItem(this.prefixedKey(key)); | |
| return itemString ? JSON.parse(itemString) : null; | |
| } | |
| public removeItem(key: string): void { | |
| const index = this.getIndex(); | |
| const currentIndex = index.keys.indexOf(key); | |
| if (currentIndex !== -1) { | |
| index.keys.splice(currentIndex, 1); | |
| const itemString = this.storage.getItem(this.prefixedKey(key)); | |
| if (itemString) { | |
| index.size -= this.getStringByteSize(itemString); | |
| } | |
| this.storage.removeItem(this.prefixedKey(key)); | |
| this.setIndex(index); | |
| } | |
| } | |
| public clear(): void { | |
| const index = this.getIndex(); | |
| for (const key of index.keys) { | |
| this.storage.removeItem(this.prefixedKey(key)); | |
| } | |
| this.setIndex({ keys: [], size: 0 }); | |
| } | |
| public get length(): number { | |
| return this.getIndex().keys.length; | |
| } | |
| public key(index: number): string | null { | |
| const keys = this.getIndex().keys; | |
| return index >= 0 && index < keys.length ? keys[index] : null; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment