@cedx/webstorage
Version:
Service for interacting with the Web Storage.
181 lines (180 loc) • 5.72 kB
JavaScript
import { StorageEvent } from "./StorageEvent.js";
/**
* Provides access to the [Web Storage](https://developer.mozilla.org/docs/Web/API/Web_Storage_API).
*/
export class Storage extends EventTarget {
/**
* The underlying data store.
*/
#backend;
/**
* A string prefixed to every key so that it is unique globally in the whole storage.
*/
#keyPrefix;
/**
* Creates a new storage service.
* @param backend The underlying data store.
* @param options An object providing values to initialize this instance.
*/
constructor(backend, options = {}) {
super();
this.#backend = backend;
this.#keyPrefix = options.keyPrefix ?? "";
if (options.listenToGlobalEvents)
addEventListener("storage", this);
}
/**
* The keys of this storage.
*/
get keys() {
const keys = Array.from(Array(this.#backend.length), (_, index) => this.#backend.key(index));
return new Set(this.#keyPrefix ? keys.filter(key => key.startsWith(this.#keyPrefix)).map(key => key.slice(this.#keyPrefix.length)) : keys);
}
/**
* The number of entries in this storage.
*/
get length() {
return this.#keyPrefix ? this.keys.size : this.#backend.length;
}
/**
* Creates a new local storage service.
* @param options An object providing values to initialize the service.
* @returns The newly created service.
*/
static local(options = {}) {
return new this(localStorage, options);
}
/**
* Creates a new session storage service.
* @param options An object providing values to initialize the service.
* @returns The newly created service.
*/
static session(options = {}) {
return new this(sessionStorage, options);
}
/**
* Releases any resources associated with this object.
*/
[Symbol.dispose]() {
return this.destroy();
}
/**
* Returns a new iterator that allows iterating the entries of this storage.
* @returns An iterator for the entries of this storage.
*/
*[Symbol.iterator]() {
for (const key of this.keys)
yield [key, this.get(key)];
}
/**
* Removes all entries from this storage.
*/
clear() {
if (this.#keyPrefix)
for (const key of this.keys)
this.delete(key);
else {
this.#backend.clear();
this.dispatchEvent(new StorageEvent(null));
}
}
/**
* Removes the value associated with the specified key.
* @param key The storage key.
* @returns The value associated with the key before it was removed.
*/
delete(key) {
const oldValue = this.get(key);
this.#backend.removeItem(this.#buildKey(key));
this.dispatchEvent(new StorageEvent(key, oldValue));
return oldValue;
}
/**
* Cancels the subscription to the global storage events.
*/
destroy() {
removeEventListener("storage", this);
}
/**
* Gets the value associated to the specified key.
* @param key The storage key.
* @returns The storage value, or `null` if the key does not exist.
*/
get(key) {
return this.#backend.getItem(this.#buildKey(key));
}
/**
* Gets the deserialized value associated with the specified key.
* @param key The storage key.
* @returns The storage value, or `null` if the key does not exist or the value cannot be deserialized.
*/
getObject(key) {
try {
return JSON.parse(this.get(key) ?? "");
}
catch {
return null;
}
}
/**
* Handles the events.
* @param event The dispatched event.
*/
handleEvent(event) {
if (event.storageArea == this.#backend && (!event.key || event.key.startsWith(this.#keyPrefix)))
this.dispatchEvent(new StorageEvent(event.key?.slice(this.#keyPrefix.length) ?? null, event.oldValue, event.newValue));
}
/**
* Gets a value indicating whether this storage contains the specified key.
* @param key The storage key.
* @returns `true` if this storage contains the specified key, otherwise `false`.
*/
has(key) {
return this.get(key) != null;
}
/**
* Registers a function that will be invoked whenever the `change` event is triggered.
* @param listener The event handler to register.
* @returns This instance.
*/
onChange(listener) {
this.addEventListener(StorageEvent.type, listener);
return this;
}
/**
* Associates a given value with the specified key.
* @param key The storage key.
* @param value The storage value.
* @returns This instance.
*/
set(key, value) {
const oldValue = this.get(key);
this.#backend.setItem(this.#buildKey(key), value);
this.dispatchEvent(new StorageEvent(key, oldValue, value));
return this;
}
/**
* Serializes and associates a given `value` with the specified `key`.
* @param key The storage key.
* @param value The storage value.
* @returns This instance.
*/
setObject(key, value) {
return this.set(key, JSON.stringify(value));
}
/**
* Returns a JSON representation of this object.
* @returns The JSON representation of this object.
*/
toJSON() {
return Array.from(this);
}
/**
* Builds a normalized storage key from the given key.
* @param key The original key.
* @returns The normalized storage key.
*/
#buildKey(key) {
return `${this.#keyPrefix}${key}`;
}
}