hybrid-webcache
Version:
Hybrid WebCache - A library that combines `localStorage`, `IndexedDB`, `SessionStorage` and `Memory` to provide a high-performance hybrid cache with multi-instance synchronization support.
731 lines (730 loc) • 23.8 kB
TypeScript
import type { DataGetModel, KeyPath, Options, RecordType, TTL, ValueType } from "./types";
import { StorageEngine } from "./types";
/**
* Represents a hybrid web cache that supports both asynchronous and synchronous
* operations for storing, retrieving, and managing key-value pairs with optional
* time-to-live (TTL) settings.
*
* The cache can automatically remove expired entries
* and supports various storage engines.
*
* Provides methods for setting, getting,
* checking existence, and unsetting values, as well as resetting the cache with
* new data. Includes utility functions for converting TTL and calculating storage
* size.
* @author Heliomar Marques
* @example
*
* Basic Usage with Default Options (storage: Auto, ttl: 1 hour)
* ```ts
* import { HybridWebCache, StorageEngine } from 'hybrid-webcache';
*
* const cache = new HybridWebCache();
*
* await cache.set('sessionToken', 'abc123');
* const tokenData = await cache.get<string>('sessionToken');
* console.log(`Token: ${tokenData?.value}`); // Output: Token: abc123
* console.log(`Is Expired: ${tokenData?.isExpired}`); // Output: Is Expired: false
* ```
* @example
* Creating an instance with custom options (e.g., IndexedDB, 10-minute TTL)
* ```ts
* import { HybridWebCache, StorageEngine } from 'hybrid-webcache';
*
* // Note: For IndexedDB, remember to call .init() if you plan to use synchronous methods
* const indexedDBCache = new HybridWebCache('myAppCache', {
* storage: StorageEngine.IndexedDB,
* ttl: { minutes: 10 },
* removeExpired: true,
* });
*
* await indexedDBCache.init(); // Initialize IndexedDB to load memory cache for sync operations
* //Setting and Getting Nested Data
* await indexedDBCache.set('user.profile.firstName', 'John', { hours: 1 });
* indexedDBCache.setSync('user.profile.lastName', 'Doe'); // Uses instance's default TTL (10 minutes)
* indexedDBCache.setSync(['user', 'profile', 'age'], 30); // Array KeyPath
*
* const userData = await indexedDBCache.get('user.profile');
* console.log(userData?.value); // Output: { firstName: 'John', lastName: 'Doe', age: 30 }
* const firstNameData = indexedDBCache.getSync('user.profile.firstName');
* console.log(firstNameData?.value); // Output: John
*
* // Checking for Key Existence
* const hasUser = await indexedDBCache.has('user.profile.firstName');
* console.log(`Has user first name: ${hasUser}`); // Output: Has user first name: true
*
* const hasNonExistentKey = indexedDBCache.hasSync('non.existent.key');
* console.log(`Has non-existent key: ${hasNonExistentKey}`); // Output: Has non-existent key: false
*
* // Unsetting Data (Partial and Full)
* const complexObject = {
* theme: 'dark',
* settings: {
* language: 'en-US',
* notifications: { email: true, sms: false }
* }
* };
* await indexedDBCache.set('appConfig', complexObject);
*
* // Unset a nested property
* await indexedDBCache.unset('appConfig.settings.notifications.sms');
* const updatedAppConfig = await indexedDBCache.get('appConfig');
* console.log(updatedAppConfig?.value);
* // Output: { theme: 'dark', settings: { language: 'en-US', notifications: { email: true } } }
*
* // Unset an array element (sets to null)
* indexedDBCache.unsetSync('appConfig.items[1]');
* const updatedItems = indexedDBCache.getSync('appConfig.items');
* console.log(updatedItems?.value); // Output: ['apple', null, 'orange']
*
* // Unset the entire 'appConfig' key
* await indexedDBCache.unset('appConfig');
* const appConfigAfterUnset = await indexedDBCache.get('appConfig');
* console.log(appConfigAfterUnset); // Output: undefined
*
* // Retrieving All Data
* await indexedDBCache.set('product1', { id: 1, name: 'Laptop' });
* await indexedDBCache.set('product2', { id: 2, name: 'Mouse' });
*
* const allItemsMap = await indexedDBCache.getAll();
* console.log(allItemsMap);
* /* Output:
* Map(2) {
* 'product1' => { value: { id: 1, name: 'Laptop' }, expiresAt: ..., isExpired: false },
* 'product2' => { value: { id: 2, name: 'Mouse' }, expiresAt: ..., isExpired: false }
* }
* *\/
*
* const allItemsJson = indexedDBCache.getJsonSync();
* console.log(allItemsJson);
* /* Output:
* { product1: { id: 1, name: 'Laptop' },
* product2: { id: 2, name: 'Mouse' }
* } *\/
*
* // Resetting the Cache
* await indexedDBCache.resetWith({
* user: { id: 'user123', status: 'active' },
* app: { version: '1.0.0' }
* }, { minutes: 5 }); // New TTL for reset
*
* const resetData = await indexedDBCache.getJson();
* console.log(resetData);
* /* Output:
* {
* user: { id: 'user123', status: 'active' },
* app: { version: '1.0.0' }
* } *\/
*
* // Getting Cache Info
* const cacheInfo = indexedDBCache.info;
* console.log(cacheInfo);
* /* Output:
* {
* dataBase: 'myAppCache',
* size: 'XXb', // e.g., '120b'
* options: {
* ttl: 300000, // 5 minutes in ms
* removeExpired: true,
* storage: 2 // StorageEngine.IndexedDB
* }
* } *\/
* ```
*
* @category Core
*/
export declare class HybridWebCache {
/**
* Basename
* @private
*/
private baseName;
/**
* The options for the Cache.
* @type {@link Options}
* @private
*/
private options;
/** @ignore */
private storageBase;
/**
* Constructor for Hybrid WebCache.
*
* To reset the cache, use [`resetWith()`|`resetWithSync()`].
*
* _**Note:**_ For `StorageType.IndexedDB`, remember to call .init() if you plan to use synchronous methods
*
* @param {string} [baseName='HybridWebCache'] - The base name of the cache.
* @param {Partial<Options>} options
* @default
* ```ts
* //options
* {
* ttl: { seconds: 0, minutes: 0, hours: 1, days: 0 },
* removeExpired: true,
* storage: StorageType.Auto
* }
* ```
*/
constructor(baseName?: string, options?: Partial<Options>);
private determineStorageEngine;
private createKey;
private prepareDataSet;
/**
* Initializes the memory cache
*
* This method is only necessary to use the synchronous functions of the IndexedDB strategy.
*
* @return A promise that resolves when the local storage is initialized.
*
* @example
*
* ```ts
* const cache = new HybridWebCache("CacheDB", {storage: StorageEngine.IndexedDB});
* await cache.init();
* ```
*
* @category Init Method
*/
init(): Promise<void>;
/**
* Sets the value for a given keyPath in the storage engine.
*
* If the keyPath already exists, its value is updated with the provided
* value. If the keyPath does not exist, a new entry is created with the
* provided TTL.
*
* @template {@link ValueType} T - The type of the value being stored.
* @param {@link KeyPath} keyPath - The keyPath to be stored.
* @param {@link ValueType} value - The value to be stored.
* @param {@link TTL} ttl - Optional TTL settings for the stored value. Defaults to
* the instance's configured TTL.
*
* @example
*
* Change the value at `color.name` to `sapphire`.
* ```ts
* // Given:
* {
* "color": {
* "name": "cerulean",
* "code": {
* "rgb": [0, 179, 230],
* "hex": "#003BE6"
* }
* }
* }
*
* const cache = new HybridWebCache();
* await cache.set('color.name', 'sapphire');
* ```
* @example
*
* Set the value of `color.hue` to `bluish`.
* ```ts
* const cache = new HybridWebCache();
* await cache.set(['color', 'hue'], 'bluish);
* ```
* @example
*
* Change the value of `color.code`.
* ```ts
* const cache = new HybridWebCache();
* await cache.set('color.code', { rgb: [16, 31, 134], hex: '#101F86' });
* ```
*
* @category Set Methods
*/
set<T extends ValueType>(keyPath: KeyPath, value: T, ttl?: Partial<TTL>): Promise<void>;
/**
* Synchronous version of set.
*
* @template {@link ValueType} T - The type of the value being stored.
* @param {@link KeyPath} keyPath - The keyPath to be stored.
* @param {@link ValueType} value - The value to be stored.
* @param {@link TTL} ttl - Optional TTL settings for the stored value. Defaults to
* the instance's configured TTL.
*
* @example
*
* Change the value at `color.name` to `sapphire`.
* ```ts
* // Given:
* {
* "color": {
* "name": "cerulean",
* "code": {
* "rgb": [0, 179, 230],
* "hex": "#003BE6"
* }
* }
* }
* cache.setSync('color.name', 'sapphire');
* ```
* @example
*
* Set the value of `color.hue` to `bluish`.
* ```ts
* cache.setSync(['color', 'hue'], 'bluish);
* ```
* @example
*
* Change the value of `color.code`.
* ```ts
* cache.setSync('color.code', { rgb: [16, 31, 134], hex: '#101F86' });
* ```
*
* @category Set Methods
*/
setSync<T extends ValueType>(keyPath: KeyPath, value: T, ttl?: Partial<TTL>): void;
/**
* Retrieves the value associated with the specified keyPath from the storage engine.
*
* If the value is found, it returns an object containing the value, expiration time,
* and expiration status. If the value is expired and the `removeExpired` flag is set
* to true, the expired value is removed from storage and `undefined` is returned.
*
* @template T - The type of the value being retrieved.
* @param {@link KeyPath} keyPath - The path to the key whose value should be retrieved.
* @param {boolean} removeExpired - A flag indicating whether to remove the key if its value
* is expired. Defaults to the instance's configured setting.
* @returns A promise that resolves to an object containing the value and its metadata,
* or `undefined` if the value does not exist or is expired and removed.
*
* @example
*
* Get the value at `color.name`.
* ```ts
* // Given:
* {
* "color": {
* "name": "cerulean",
* "code": {
* "rgb": [0, 179, 230],
* "hex": "#003BE6"
* }
* }
* }
* const cache = new HybridWebCache();
* const value = await cache.get('color.name');
* // => "cerulean"
* ```
* @example
*
* Get the value at `color.code.hex`.
* ```ts
* const hex = await cache.get('color.color.hex');
* // => "#003BE6"
* ```
* @example
*
* Get the value at `color.hue`.
* ```ts
* const value = await cache.get(['color', 'hue']);
* // => undefined
* ```
* @example
*
* Get the value at `color.code.rgb[1]`.
* ```ts
* const value = await cache.get('color.code.rgb[1]');
* // => 179
* ```
*
* @category Get Methods
*/
get<T extends ValueType>(keyPath: KeyPath, removeExpired?: boolean): Promise<DataGetModel<T> | undefined>;
/**
* Synchronous version of get.
*
* Retrieves the value associated with the specified keyPath from the storage engine.
*
* If the value is found, it returns an object containing the value, expiration time,
* and expiration status. If the value is expired and the `removeExpired` flag is set
* to true, the expired value is removed from storage and `undefined` is returned.
*
* @template T - The type of the value being retrieved.
* @param {@link KeyPath} keyPath - The path to the key whose value should be retrieved.
* @param {boolean} removeExpired - A flag indicating whether to remove the key if its value
* is expired. Defaults to the instance's configured setting.
* @returns An object containing the value and its metadata, or `undefined` if the
* value does not exist or is expired and removed.
* @example
*
* Get the value at `color.name`.
* ```ts
* // Given:
* {
* "color": {
* "name": "cerulean",
* "code": {
* "rgb": [0, 179, 230],
* "hex": "#003BE6"
* }
* }
* }
*
* const value = cache.getSync('color.name');
* // => "cerulean"
* ```
* @example
*
* Get the value at `color.code.hex`.
* ```ts
* const hex = cache.getSync('color.color.hex');
* // => "#003BE6"
* ```
* @example
*
* Get the value at `color.hue`.
* ```ts
* const value = cache.getSync(['color', 'hue']);
* // => undefined
* ```
* @example
*
* Get the value at `color.code.rgb[1]`.
* ```ts
* const value = cache.getSync('color.code.rgb[1]');
* // => 179
* ```
*
* @category Get Methods
*/
getSync<T extends ValueType>(keyPath: KeyPath, removeExpired?: boolean): DataGetModel<T> | undefined;
/**
* Retrieves all key-value pairs from the storage engine.
*
* If the `removeExpired` flag is set to true, expired values are removed from storage
* before being returned.
*
* @param {boolean} removeExpired - A flag indicating whether to remove expired values from storage.
* Defaults to the instance's configured setting.
* @returns A map of key-value pairs, where each value is an object containing the value,
* expiration time, and expiration status. If no values are found or if all values
* are expired and removed, `null` is returned.
*
* @category Get Methods
*/
getAll<T extends ValueType>(removeExpired?: boolean): Promise<Map<string, DataGetModel<T>> | null>;
/**
* Synchronously retrieves all items from storage as a map of key-value pairs,
* where each value is an object containing the value, expiration time, and expiration status.
*
* If the `removeExpired` flag is set to true, expired values are removed from storage
* before being returned.
*
* @param {boolean} removeExpired - A flag indicating whether to remove expired values from storage.
* Defaults to the instance's configured setting.
* @returns A map of key-value pairs, where each value is an object containing the value,
* expiration time, and expiration status. If no values are found or if all values
* are expired and removed, `null` is returned.
*
* @category Get Methods
*/
getAllSync<T extends ValueType>(removeExpired?: boolean): Map<string, DataGetModel<T>> | null;
/**
* Asynchronously retrieves all key-value pairs from the storage as a JSON object.
*
* If the `removeExpired` flag is set to true, expired values are removed from storage
* before being included in the result.
*
* @param {boolean} removeExpired - A flag indicating whether to remove expired values from storage.
* Defaults to the instance's configured setting.
* @returns A promise that resolves to a JSON object containing all key-value pairs.
* If no items are found or all items are expired and removed, `null` is returned.
*
* @category Get Methods
*/
getJson(removeExpired?: boolean): Promise<Record<string, ValueType> | null>;
/**
* Synchronously retrieves all key-value pairs from the storage as a JSON object.
*
* If the `removeExpired` flag is set to true, expired values are removed from storage
* before being included in the result.
*
* @param {boolean} removeExpired - A flag indicating whether to remove expired values from storage.
* Defaults to the instance's configured setting.
* @returns A JSON object containing all key-value pairs. If no items are found or all
* items are expired and removed, `null` is returned.
*
* @category Get Methods
*/
getJsonSync(removeExpired?: boolean): Record<string, ValueType> | null;
/**
* Checks if the given key path exists.
*
* _For sync method, use_ [`hasSync()`].
*
* @param {@link KeyPath} keyPath The key path to check.
* @returns A promise which resolves to `true` if the `keyPath` exists, else `false`.
* @example
*
* Check if the value at `color.name` exists.
* ```ts
* // Given:
* {
* "color": {
* "name": "cerulean",
* "code": {
* "rgb": [0, 179, 230],
* "hex": "#003BE6"
* }
* }
* }
*
* const exists = await cache.has('color.name');
* // => true
* ```
* @example
*
* Check if the value at `color.hue` exists.
* ```ts
* const exists = await cache.has(['color', 'hue']);
* // => false
* ```
* @example
*
* Check if the value at `color.code.rgb[1]` exists.
* ```ts
* const exists = await cache.has(color.code.rgb[1]);
* // => true
* ```
*
* @category Has Methods
*/
has(keyPath: KeyPath): Promise<boolean>;
/**
* Checks if the given key path exists.
*
* _For async method, use_ [`has()`].
*
* @param {@link KeyPath} keyPath The key path to check.
* @returns `true` if the `keyPath` exists, else `false`.
* @example
*
* Check if the value at `color.name` exists.
* ```ts
* // Given:
* {
* "color": {
* "name": "cerulean",
* "code": {
* "rgb": [0, 179, 230],
* "hex": "#003BE6"
* }
* }
* }
*
* const exists = cache.hasSync('color.name');
* // => true
* ```
* @example
*
* Check if the value at `color.hue` exists.
* ```ts
* const exists = cache.hasSync(['color', 'hue']);
* // => false
* ```
* @example
*
* Check if the value at `color.code.rgb[1]` exists.
* ```ts
* const exists = cache.hasSync(color.code.rgb[1]);
* // => true
* ```
*
* @category Has Methods
*/
hasSync(keyPath: KeyPath): boolean;
/**
* Unsets all key values.
*
* _For sync method, use_ [`unsetSync()`].
*
* @returns A promise which resolves when the key values have been unset.
* @example
*
* Unsets all key values.
* ```ts
* await cache.unset();
* await cache.getAll();
* // => undefined
* ```
*
* @category Unset Methods
*/
unset(): Promise<boolean>;
/**
* Unsets the property at the given key path.
*
* _For sync method, use_ [`unsetSync()`].
*
* @param {@link KeyPath} keyPath The key path of the property.
* @returns A promise which resolves when the setting has been unset.
* @example
*
* Unset the property `color.name`.
* ```ts
* // Given:
* {
* "color": {
* "name": "cerulean",
* "code": {
* "rgb": [0, 179, 230],
* "hex": "#003BE6"
* }
* }
* }
*
* await cache.unset('color.name');
* await cache.get('color.name');
* // => undefined
* ```
* @example
*
* Unset the property `color.code.rgba[1]`.
* ```ts
* await cache.unset('color.code.rgba[1]');
* await cache.get('color.code.rgb');
* // => [0, null, 230]
* ```
*
* @category Unset Methods
*/
unset(keyPath: KeyPath): Promise<boolean>;
/**
* Unsets all key values.
*
* _For async method, use_ [`unset()`].
*
* @example
*
* Unsets all key values.
* ```ts
* cache.unsetSync();
* ```
*
* @category Unset Methods
*/
unsetSync(): boolean;
/**
* Unsets the property at the given key path.
*
* _For async method, use_ [`unset()`].
*
* @param {@link KeyPath} keyPath The key path of the property.
* @example
*
* Unset the property `color.name`.
* ```ts
* // Given:
* {
* "color": {
* "name": "cerulean",
* "code": {
* "rgb": [0, 179, 230],
* "hex": "#003BE6"
* }
* }
* }
*
* cache.unsetSync('color.name');
* cache.getSync('color.name');
* // => undefined
* ```
* @example
*
* Unset the property `color.code.rgba[1]`.
* ```ts
* cache.unsetSync('color.code.rgba[1]');
* cache.getSync('color.code.rgb');
* // => [0, null, 230]
* ```
*
* @category Unset Methods
*/
unsetSync(keyPath: KeyPath): boolean;
/**
* Resets the storage with the provided key-value pairs and optional TTL.
*
* This method first clears all existing entries in the storage engine.
* It then iterates over the provided key-value pairs, setting each one
* in the storage with the specified TTL. If no TTL is provided, the
* default TTL from the options is used.
*
* @template T - The type of values being stored.
* @param {@link KeyPath} keyValues - An object containing key-value pairs to be stored.
* @param {@link TTL} ttl - Optional TTL settings for the stored values. Defaults to
* the instance's configured TTL.
* @returns A promise that resolves when all key-value pairs have been
* set in the storage.
*
* @category Reset Data Methods
*/
resetWith<T extends ValueType>(keyValues: RecordType<T>, ttl?: Partial<TTL>): Promise<void>;
/**
* Resets the storage with the provided key-value pairs and optional TTL.
*
* This method first clears all existing entries in the storage engine.
* It then iterates over the provided key-value pairs, setting each one
* in the storage with the specified TTL. If no TTL is provided, the
* default TTL from the options is used.
*
* @template T - The type of values being stored.
* @param {@link KeyPath} keyValues - An object containing key-value pairs to be stored.
* @param {@link TTL} ttl - Optional TTL settings for the stored values. Defaults to
* the instance's configured TTL.
*
* @category Reset Data Methods
*/
resetWithSync<T extends ValueType>(keyValues: RecordType<T>, ttl?: Partial<TTL>): void;
/**
* Retrieves the number of items currently stored in the cache.
*
* @returns The count of items in the storage. *
* @category Auxiliary Methods
*/
get length(): number;
/**
* Retrieves the total number of bytes used by the cache in the storage.
*
* @returns The total bytes used by the cache.
* @category Auxiliary Methods
*/
get bytes(): number;
/**
* Provides information about the current cache.
*
* @returns An object containing:
* - `dataBase`: The name of the database used by the cache.
* - `size`: The calculated storage size in bytes represented as a string.
* - `options`: The current cache options including TTL converted to milliseconds.
*
* ```ts
* {
* dataBase: 'myAppCache',
* size: 'XXb', // e.g., '120b'
* options: {
* ttl: 300000, // 5 minutes in ms
* removeExpired: true,
* storage: 2 // StorageEngine.IndexedDB
* }
* }
* ```
*
* @category Auxiliary Methods
*/
get info(): {
dataBase: string;
size: string;
options: Options;
};
/**
* Returns the type of storage engine used by the cache.
*
* @returns The type of storage engine used by the cache.
* @category Auxiliary Methods
*/
get storageType(): StorageEngine;
}