@febkosq8/local-save
Version:
Lightweight wrapper around IndexedDB for secure and structured client-side data storage.
202 lines (200 loc) • 7.96 kB
TypeScript
declare class LocalSave {
dbName: DBName;
encryptionKey?: EncryptionKey;
categories: Category[];
expiryThreshold: number;
clearOnDecryptError: boolean;
printLogs: boolean;
constructor(config?: Config);
/**
* Opens a connection to the IndexedDB database.
* It handles the database versioning and ensures that the required object stores are created if they do not exist.
*
* @internal
*
* @param version - The version of the database to open. Optional.
*
* @returns A promise that resolves to the opened 'IDBDatabase' instance.
*/
private openDB;
/**
* Retrieves an object store from the IndexedDB database.
* It handles the transaction mode and ensures that the requested object store is returned.
*
* If the object store does not exist in the database and the category is valid, it will create a new version of the database with the object store.
*
* @internal
*
* @param category - The name of the object store to retrieve.
* @param mode - The mode for the transaction (default is "readonly").
*
* @returns A promise that resolves to the requested object store.
*
* @throws {LocalSaveError} Will throw an error if the object store does not exist in the database and the category is invalid
*/
private getStore;
/**
* Retrieves the encryption key as a CryptoKey object.
*
* @internal
* @returns A promise that resolves to a CryptoKey object.
*
* @throws {LocalSaveEncryptionKeyError} If the encryption key is not configured.
* @throws {LocalSaveEncryptionKeyError} If the encryption key length is not 16, 24, or 32 characters.
*/
private getEncryptKey;
/**
* Encrypts the provided data using AES-GCM encryption with the help of SubtleCrypto API.
* Refer to https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt for more technical details.
*
* Generates a random 12-byte IV for each encryption.
* Base64 encodes the IV and the encrypted data and returns the result as a string.
*
* If no encryption key is configured, it returns the data as is.
*
* @internal
*
* @param data The data to be encrypted. Should be an instance of DBItem.
*
* @returns A promise that resolves to the encrypted data as a base64 encoded string.
*
* @throws {LocalSaveEncryptionKeyError} If the encryption key is not configured.
* @throws {LocalSaveError} If the encryption process fails.
*/
private encryptData;
/**
* Decrypts the provided data using the configured encryption key.
* If no encryption key is configured, it returns the data as is.
*
* @param encryptedBase64Data The data to decrypt, as a string.
*
* @returns The decrypted data as an object.
*
* @throws {LocalSaveEncryptionKeyError} If the encryption key is not configured.
* @throws {LocalSaveError} If the decryption process fails.
*/
decryptData(encryptedBase64Data: string): Promise<DBItem>;
/**
* Stores data in the specified category with the given item key.
* If encryption key is configured, the data is encrypted first before being stored.
*
* @param category The category under which the data should be stored.
* @param itemKey The key to identify the stored data.
* @param data The data to be stored.
*
* @returns A promise that resolves to `true` if the operation was successful.
*
* @throws {LocalSaveError} Will reject the promise if an error occurs during the saving process.
*/
set(category: Category, itemKey: string, data: unknown): Promise<true>;
/**
* Retrieves an item from the specified category in the IndexedDB.
* If the item is not found, the promise resolves to 'null'.
* If an encryption key is configured, the data is decrypted before being returned.
*
* @param category The category from which to retrieve the item.
* @param itemKey The key of the item to retrieve.
*
* @returns A promise that resolves to the retrieved item or null if not found.
*
* @throws {LocalSaveError} Will reject the promise if an error occurs while decrypting the data. Depending on the 'clearOnDecryptError' configuration, all data for the category can be cleared.
* @throws {LocalSaveError} Will reject the promise if an error occurs during the retrieval process.
*/
get(category: Category, itemKey: string): Promise<DBItem | null>;
/**
* Removes an entry from the specified category and the specific itemKey in the IndexedDB store.
*
* @param category The category from which the item should be removed.
* @param itemKey The key of the item to be removed.
*
* @returns A promise that resolves to `true` if the operation was successful.
*
* @throws {LocalSaveError} Will reject the promise if an error occurs during the removal process.
*/
remove(category: Category, itemKey: string): Promise<true>;
/**
* Clears all entries in the specified category.
*
* @param category - The category to clear.
*
* @returns A promise that resolves to `true` if the operation was successful.
*
* @throws {LocalSaveError} Will reject the promise if an error occurs during the clearing process.
*/
clear(category: Category): Promise<true>;
/**
* Expires data older than the specified number of days.
*
* This method iterates through all categories and removes items that have a timestamp
* older than the specified number of days from the current date.
*
* @param {number} [days=this.expiryThreshold] The number of days to use as the threshold for expiring data.
* Defaults to expiryThreshold from config if not provided.
*
* @returns A promise that resolves to `true` if the operation was successful.
*
* @throws {LocalSaveError} - Throws an error if there is an issue accessing the store or removing items.
*/
expire(days?: number): Promise<true>;
/**
* Asynchronously destroys the database by deleting it from IndexedDB.
*
* @returns A promise that resolves to `true` if the operation was successful.
*
* @throws {LocalSaveError} Will reject the promise if an error occurs during the deletion process.
*/
destroy(): Promise<true>;
}
type DBName = string;
type EncryptionKey = string;
type Category = string;
interface DBItem {
timestamp: number;
data: unknown;
}
type DBItemEncryptedBase64 = string;
interface Config {
/**
* The name of the database to use for local save
*
* @default "LocalSave"
*/
dbName?: DBName;
/**
* The key to use for encrypting and decrypting data
* Not providing this will store data in plain text
* Should be a string without spaces of length 16, 24, or 32 characters
*
* @default undefined
*/
encryptionKey?: EncryptionKey;
/**
* The categories to use for storing data
* You can use these to separate different types of data
* No spaces are allowed in the key
*
* @default ["userData"]
*/
categories?: Category[];
/**
* The number of days to use as the threshold for expiring data
*
* @default '30' days
*/
expiryThreshold?: number;
/**
* Whether to clear all data for a category if an error occurs while decrypting data
* Most likely reason of error is due to an incorrect encryption key
*
* @default true
*/
clearOnDecryptError?: boolean;
/**
* Whether to print logs
* Includes debug and errors logs
*
* @default false
*/
printLogs?: boolean;
}
export { type Category, type Config, type DBItem, type DBItemEncryptedBase64, type DBName, type EncryptionKey, LocalSave as default };