@aws-amplify/core
Version:
Core category of aws-amplify
116 lines (113 loc) • 4.7 kB
JavaScript
import { ConsoleLogger } from '../Logger/ConsoleLogger.mjs';
import { PlatformNotSupportedError } from '../errors/PlatformNotSupportedError.mjs';
import '../errors/errorHelpers.mjs';
import '../utils/getClientInfo/getClientInfo.mjs';
import { isBrowser } from '../utils/isBrowser.mjs';
import '../utils/retry/retry.mjs';
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
const logger = new ConsoleLogger('KeyValueStorage');
/**
* @internal
*/
class KeyValueStorage {
constructor(storage) {
this.storage = storage;
}
/**
* This is used to set a specific item in storage
* @param {string} key - the key for the item
* @param {object} value - the value
* @returns {string} value that was set
*/
async setItem(key, value) {
if (!this.storage)
throw new PlatformNotSupportedError();
this.storage.setItem(key, value);
}
/**
* This is used to get a specific key from storage
* @param {string} key - the key for the item
* This is used to clear the storage
* @returns {string} the data item
*/
async getItem(key) {
if (!this.storage)
throw new PlatformNotSupportedError();
return this.storage.getItem(key);
}
/**
* This is used to remove an item from storage
* @param {string} key - the key being set
* @returns {string} value - value that was deleted
*/
async removeItem(key) {
if (!this.storage)
throw new PlatformNotSupportedError();
this.storage.removeItem(key);
}
/**
* This is used to clear the storage
* @returns {string} nothing
*/
async clear() {
if (!this.storage)
throw new PlatformNotSupportedError();
this.storage.clear();
}
/**
* This is used to allow listening for changes
* @param {function} listener - the function called on storage change
* @returns {function} an unsubscribe function that removes the listener. The
* underlying `window` 'storage' event listener is attached lazily on the
* first subscription and detached once the last listener unsubscribes. In
* non-browser (SSR/native) environments nothing is attached and the returned
* unsubscribe function is a safe no-op.
*/
addListener(listener) {
const listeners = (this.listeners ?? (this.listeners = new Set()));
listeners.add(listener);
// Lazily attach the cross-tab 'storage' listener on the first subscription.
if (isBrowser() && listeners.size === 1) {
// Create the handler lazily (and only in a browser) so instances that
// never subscribe keep no function-valued own property. The same
// reference is reused for detach on last-unsubscribe.
const storageListener = (e) => {
// Only react to events for this instance's backing store. Without
// this guard a localStorage-backed instance and a
// sessionStorage-backed instance would react to each other's
// 'storage' events, since both share the single window listener.
if (e.storageArea !== this.storage) {
return;
}
this.listeners?.forEach(l => {
// A listener may be async and reject; isolate each call so one
// rejecting listener neither aborts the others nor surfaces as an
// unhandled promise rejection.
Promise.resolve(l({
key: e.key,
oldValue: e.oldValue,
newValue: e.newValue,
})).catch(error => {
logger.error('Error in storage event listener', error);
});
});
};
this.storageListener = storageListener;
window.addEventListener('storage', storageListener, false);
}
return () => {
listeners.delete(listener);
const { storageListener } = this;
// Detach once the last subscriber unsubscribes — real teardown, not
// just pruning the set. Guarding on the stored handler keeps repeated
// unsubscribe calls idempotent.
if (isBrowser() && listeners.size === 0 && storageListener) {
window.removeEventListener('storage', storageListener, false);
this.storageListener = undefined;
}
};
}
}
export { KeyValueStorage };
//# sourceMappingURL=KeyValueStorage.mjs.map