UNPKG

@ngx-pwa/local-storage

Version:

Efficient local storage module for Angular: simple API based on native localStorage API, but internally stored via the asynchronous IndexedDB API for performance, and wrapped in RxJS observables to be homogeneous with other Angular modules.

199 lines (198 loc) 8.91 kB
import { Observable, OperatorFunction, ReplaySubject } from 'rxjs'; import { JSONSchema, JSONSchemaBoolean, JSONSchemaInteger, JSONSchemaNumber, JSONSchemaString, JSONSchemaArrayOf } from '../validation/json-schema'; import { JSONValidator } from '../validation/json-validator'; import { LocalDatabase } from '../databases/local-database'; export declare class StorageMap { protected database: LocalDatabase; protected jsonValidator: JSONValidator; protected LSPrefix: string; protected notifiers: Map<string, ReplaySubject<unknown>>; /** * Constructor params are provided by Angular (but can also be passed manually in tests) * @param database Storage to use * @param jsonValidator Validator service * @param LSPrefix Prefix for `localStorage` keys to avoid collision for multiple apps on the same subdomain or for interoperability */ constructor(database: LocalDatabase, jsonValidator?: JSONValidator, LSPrefix?: string); /** * **Number of items** in storage, wrapped in an `Observable`. * * @example * this.storageMap.size.subscribe((size) => { * console.log(size); * }); */ get size(): Observable<number>; /** * Tells you which storage engine is used. *Only useful for interoperability.* * Note that due to some browsers issues in some special contexts * (Firefox private mode and Safari cross-origin iframes), * **this information may be wrong at initialization,** * as the storage could fallback from `indexedDB` to `localStorage` * only after a first read or write operation. * @returns Storage engine used * * @see {@link https://github.com/cyrilletuzi/angular-async-local-storage/blob/master/docs/INTEROPERABILITY.md} * * @example * if (this.storageMap.backingEngine === 'indexedDB') {} */ get backingEngine(): 'indexedDB' | 'localStorage' | 'memory' | 'unknown'; /** * Info about `indexedDB` database. *Only useful for interoperability.* * @returns `indexedDB` database name, store name and database version. * **Values will be empty if the storage is not `indexedDB`,** * **so it should be used after an engine check**. * * @see {@link https://github.com/cyrilletuzi/angular-async-local-storage/blob/master/docs/INTEROPERABILITY.md} * * @example * if (this.storageMap.backingEngine === 'indexedDB') { * const { database, store, version } = this.storageMap.backingStore; * } */ get backingStore(): { database: string; store: string; version: number; }; /** * Info about `localStorage` fallback storage. *Only useful for interoperability.* * @returns `localStorage` prefix. * **Values will be empty if the storage is not `localStorage`,** * **so it should be used after an engine check**. * * @see {@link https://github.com/cyrilletuzi/angular-async-local-storage/blob/master/docs/INTEROPERABILITY.md} * * @example * if (this.storageMap.backingEngine === 'localStorage') { * const { prefix } = this.storageMap.fallbackBackingStore; * } */ get fallbackBackingStore(): { prefix: string; }; /** * Get an item value in storage. * The signature has many overloads due to validation, **please refer to the documentation.** * @see {@link https://github.com/cyrilletuzi/angular-async-local-storage/blob/master/docs/VALIDATION.md} * @param key The item's key * @param schema Optional JSON schema to validate the data * @returns The item's value if the key exists, `undefined` otherwise, wrapped in a RxJS `Observable` * * @example * this.storageMap.get('key', { type: 'string' }).subscribe((result) => { * result; // string or undefined * }); * * @example * interface User { * firstName: string; * lastName?: string; * } * * const schema = { * type: 'object', * properties: { * firstName: { type: 'string' }, * lastName: { type: 'string' }, * }, * required: ['firstName'] * }; * * this.storageMap.get<User>('user', schema).subscribe((user) => { * if (user) { * user.firstName; * } * }); */ get<T = string>(key: string, schema: JSONSchemaString): Observable<string | undefined>; get<T = number>(key: string, schema: JSONSchemaInteger | JSONSchemaNumber): Observable<number | undefined>; get<T = boolean>(key: string, schema: JSONSchemaBoolean): Observable<boolean | undefined>; get<T = string[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaString>): Observable<string[] | undefined>; get<T = number[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaInteger | JSONSchemaNumber>): Observable<number[] | undefined>; get<T = boolean[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaBoolean>): Observable<boolean[] | undefined>; get<T = unknown>(key: string, schema: JSONSchema): Observable<T | undefined>; get<T = unknown>(key: string, schema?: JSONSchema): Observable<unknown>; /** * Set an item in storage. * Note that setting `null` or `undefined` will remove the item to avoid some browsers issues. * @param key The item's key * @param data The item's value * @param schema Optional JSON schema to validate the data * @returns A RxJS `Observable` to wait the end of the operation * * @example * this.storageMap.set('key', 'value').subscribe(() => {}); */ set(key: string, data: unknown, schema?: JSONSchema): Observable<undefined>; /** * Delete an item in storage * @param key The item's key * @returns A RxJS `Observable` to wait the end of the operation * * @example * this.storageMap.delete('key').subscribe(() => {}); */ delete(key: string): Observable<undefined>; /** * Delete all items in storage * @returns A RxJS `Observable` to wait the end of the operation * * @example * this.storageMap.clear().subscribe(() => {}); */ clear(): Observable<undefined>; /** * Get all keys stored in storage. Note **this is an *iterating* `Observable`**: * * if there is no key, the `next` callback will not be invoked, * * if you need to wait the whole operation to end, be sure to act in the `complete` callback, * as this `Observable` can emit several values and so will invoke the `next` callback several times. * @returns A list of the keys wrapped in a RxJS `Observable` * * @example * this.storageMap.keys().subscribe({ * next: (key) => { console.log(key); }, * complete: () => { console.log('Done'); }, * }); */ keys(): Observable<string>; /** * Tells if a key exists in storage * @returns A RxJS `Observable` telling if the key exists * * @example * this.storageMap.has('key').subscribe((hasKey) => { * if (hasKey) {} * }); */ has(key: string): Observable<boolean>; /** * Watch an item value in storage. * **Note only changes done via this lib will be watched**, external changes in storage can't be detected. * The signature has many overloads due to validation, **please refer to the documentation.** * @see https://github.com/cyrilletuzi/angular-async-local-storage/blob/master/docs/VALIDATION.md * @param key The item's key to watch * @param schema Optional JSON schema to validate the initial value * @returns An infinite `Observable` giving the current value */ watch<T = string>(key: string, schema: JSONSchemaString): Observable<string | undefined>; watch<T = number>(key: string, schema: JSONSchemaInteger | JSONSchemaNumber): Observable<number | undefined>; watch<T = boolean>(key: string, schema: JSONSchemaBoolean): Observable<boolean | undefined>; watch<T = string[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaString>): Observable<string[] | undefined>; watch<T = number[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaInteger | JSONSchemaNumber>): Observable<number[] | undefined>; watch<T = boolean[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaBoolean>): Observable<boolean[] | undefined>; watch<T = unknown>(key: string, schema: JSONSchema): Observable<T | undefined>; watch<T = unknown>(key: string, schema?: JSONSchema): Observable<unknown>; /** * Notify when a value changes * @param key The item's key * @param data The new value */ protected notify(key: string, value: unknown): void; /** * RxJS operator to catch if `indexedDB` is broken * @param operationCallback Callback with the operation to redo */ protected catchIDBBroken<T>(operationCallback: () => Observable<T>): OperatorFunction<T, T>; }