@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.
659 lines (649 loc) • 22.7 kB
TypeScript
import * as i0 from '@angular/core';
import { EnvironmentProviders, ModuleWithProviders } from '@angular/core';
import { Observable } from 'rxjs';
/**
* Exception message when a value cannot be serialized for `localStorage`
*/
declare const SERIALIZATION_ERROR = "The storage is currently localStorage,\nwhere data must be serialized, and the provided data can't be serialized.";
/**
* Exception throwned when a value cannot be serialized for `localStorage`
*/
declare class SerializationError extends Error {
message: string;
}
/**
* Allows to add a prefix before `localStorage` keys.
*
* *Use only* for interoperability with other APIs or to avoid collision for multiple applications on the same subdomain.
*
* **WARNING: do not change this option in an application already deployed in production, as previously stored data would be lost.**
*
* @example
* export const appConfig: ApplicationConfig = {
* providers: [provideLocalStoragePrefix('custom_')]
* };
*/
declare function provideLocalStoragePrefix(prefix: string): EnvironmentProviders;
/**
* Allows to change the name used for `indexedDB` database.
*
* *Use only* for interoperability with other APIs or to avoid collision for multiple applications on the same subdomain.
*
* **WARNING: do not change this option in an application already deployed in production, as previously stored data would be lost.**
*
* @example
* export const appConfig: ApplicationConfig = {
* providers: [provideIndexedDBDataBaseName('custom')]
* };
*/
declare function provideIndexedDBDataBaseName(name: string): EnvironmentProviders;
/**
* Allows to change the database version used for `indexedDB` database.
* Must be an unsigned **integer**.
*
* **Use with caution as the creation of the store depends on the version.**
*
* *Use only* for interoperability with other APIs or to avoid collision for multiple applications on the same subdomain.
*
* **WARNING: do not change this option in an applicattion already deployed in production, as previously stored data would be lost.**
*
* @example
* export const appConfig: ApplicationConfig = {
* providers: [provideIndexedDBDataBaseVersion(2)]
* };
*/
declare function provideIndexedDBDataBaseVersion(version: number): EnvironmentProviders;
/**
* Allows to change the name used for `indexedDB` object store.
*
* *Use only* for interoperability with other APIs.
*
* **WARNING: do not change this option in an application already deployed in production, as previously stored data would be lost.**
*
* @example
* export const appConfig: ApplicationConfig = {
* providers: [provideIndexedDBStoreName('custom')]
* };
*/
declare function provideIndexedDBStoreName(name: string): EnvironmentProviders;
/**
* This interface is only here for backward compatibility, **do not add it by yourself**
*
* @ignore
*/
interface StorageConfig {
/**
* @deprecated Use `provideLocalStoragePrefix()` method instead
*/
LSPrefix?: string;
/**
* @deprecated Use `provideIndexedDBDataBaseName()` method instead
*/
IDBDBName?: string;
/**
* @deprecated Use `provideIndexedDBStoreName()` method instead
*/
IDBStoreName?: string;
/**
* @deprecated Use `provideIndexedDBDataBaseVersion()` method instead
*/
IDBDBVersion?: number;
/**
* Allows interoperability with native `indexedDB` and other storage libs,
* by changing how values are stored in `indexedDB` database.
*
* Defaults to `true`. Change to `false` for backward compatiblity in existing applications.
*
* **DO NOT CHANGE THIS BEHAVIOR ONCE IN PRODUCTION**, as it would break with existing data.
*/
IDBNoWrap?: boolean;
}
/**
* This module is only here for backward compatibility, **do not add it by yourself**
*
* @ignore
*/
declare class StorageModule {
/**
* Only useful to provide options, otherwise it does nothing.
*
* **Must be used at initialization, ie. in `AppModule`, and must not be loaded again in another module.**
*/
static forRoot(config: StorageConfig): ModuleWithProviders<StorageModule>;
static ɵfac: i0.ɵɵFactoryDeclaration<StorageModule, never>;
static ɵmod: i0.ɵɵNgModuleDeclaration<StorageModule, never, never, never>;
static ɵinj: i0.ɵɵInjectorDeclaration<StorageModule>;
}
/**
* Exception message when a value is not valid against the JSON schema
*/
declare const VALIDATION_ERROR = "Data stored is not valid against the provided JSON schema.\nCheck your JSON schema, otherwise it means data has been corrupted.";
/**
* Exception throwned when a value is not valid against the JSON schema
*/
declare class ValidationError extends Error {
message: string;
}
declare abstract class LocalDatabase {
abstract readonly size: Observable<number>;
abstract get(key: string): Observable<unknown>;
abstract set(key: string, data: unknown): Observable<undefined>;
abstract delete(key: string): Observable<undefined>;
abstract clear(): Observable<undefined>;
abstract keys(): Observable<string>;
abstract has(key: string): Observable<boolean>;
static ɵfac: i0.ɵɵFactoryDeclaration<LocalDatabase, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<LocalDatabase>;
}
/**
* JSON Schema to describe a boolean value.
*/
interface JSONSchemaBoolean {
/**
* Type for a boolean value.
*/
type: "boolean";
/**
* Checks if a value is strictly equal to this.
*/
const?: boolean;
}
/**
* JSON Schema to describe a number value.
*/
interface JSONSchemaNumber {
/**
* Type for a numeric value.
*/
type: "number";
/**
* Checks if a value is strictly equal to this.
*/
const?: number;
/**
* Checks if a value is strictly equal to one of the value of enum.
*/
enum?: readonly number[];
/**
* Check if a number is a multiple of x.
* Must be strictly greater than 0.
*/
multipleOf?: number;
/**
* Check if a number is lower or equal than this maximum.
*/
maximum?: number;
/**
* Check if a number is strictly lower than this maximum.
*/
exclusiveMaximum?: number;
/**
* Check if a number is greater or equal than this minimum.
*/
minimum?: number;
/**
* Check if a number is strictly greater than this minimum.
*/
exclusiveMinimum?: number;
}
/**
* JSON Schema to describe an integer value.
*/
interface JSONSchemaInteger {
/**
* Type for an integer value.
*/
type: "integer";
/**
* Checks if a value is strictly equal to this.
*/
const?: number;
/**
* Checks if a value is strictly equal to one of the value of enum.
*/
enum?: readonly number[];
/**
* Check if a number is a multiple of x.
* Must be strictly greater than 0.
*/
multipleOf?: number;
/**
* Check if a number is lower or equal than this maximum.
*/
maximum?: number;
/**
* Check if a number is strictly lower than this maximum.
*/
exclusiveMaximum?: number;
/**
* Check if a number is greater or equal than this minimum.
*/
minimum?: number;
/**
* Check if a number is strictly greater than this minimum.
*/
exclusiveMinimum?: number;
}
/**
* JSON Schema to describe a string value.
*/
interface JSONSchemaString {
/**
* Type for a string value.
*/
type: "string";
/**
* Checks if a value is strictly equal to this.
*/
const?: string;
/**
* Checks if a value is strictly equal to one of the value of enum.
*/
enum?: readonly string[];
/**
* Maxium length for a string.
* Must be a non-negative integer.
*/
maxLength?: number;
/**
* Minimum length for a string.
* Must be a non-negative integer.
*/
minLength?: number;
/**
* Pattern to match for a string.
* Must be a valid regular expression, *without* the `/` delimiters.
*/
pattern?: string;
}
/**
* JSON schema to describe an array of values.
*/
interface JSONSchemaArray {
/**
* Type for an array of values.
*/
type: "array";
/**
* Schema for the values of the array.
*/
items: JSONSchema;
/**
* Check if an array length is lower or equal to this value.
* Must be a non negative integer.
*/
maxItems?: number;
/**
* Check if an array length is greater or equal to this value.
* Must be a non negative integer.
*/
minItems?: number;
/**
* Check if an array only have unique values.
*/
uniqueItems?: boolean;
}
/**
* JSON Schema to describe an array of primitive values:
* - array of booleans: `JSONSchemaArrayOf<JSONSchemaBoolean>`,
* - array of numbers: `JSONSchemaArrayOf<JSONSchemaNumber>`,
* - array of integers: `JSONSchemaArrayOf<JSONSchemaInteger>`,
* - array of strings: `JSONSchemaArrayOf<JSONSchemaString>`.
*/
interface JSONSchemaArrayOf<T extends JSONSchemaBoolean | JSONSchemaNumber | JSONSchemaInteger | JSONSchemaString> {
/**
* Type for an array of values.
*/
type: "array";
/**
* Schema for the values of an array.
*/
items: T;
/**
* Check if an array length is lower or equal to this value.
* Must be a non negative integer.
*/
maxItems?: number;
/**
* Check if an array length is greater or equal to this value.
* Must be a non negative integer.
*/
minItems?: number;
/**
* Check if an array only have unique values.
*/
uniqueItems?: boolean;
}
/**
* JSON schema to describe a tuple.
*/
interface JSONSchemaTuple {
/**
* Type for an array of values.
*/
type: "array";
/**
* Schema for the values of the tuple.
*/
items?: readonly JSONSchema[];
/**
* Check if an array length is lower or equal to this value.
* Must be a non negative integer.
*/
maxItems?: number;
/**
* Check if an array length is greater or equal to this value.
* Must be a non negative integer.
*/
minItems?: number;
/**
* Check if an array only have unique values.
*/
uniqueItems?: boolean;
}
/**
* JSON schema to describe an object.
*/
interface JSONSchemaObject {
/**
* Type for an object.
*/
type: "object";
/**
* List of properties of the object and their associated JSON schemas.
*/
properties: Record<string, JSONSchema>;
/**
* Array of names of the required properties for an object.
* Properties set as required should be present in `properties` too.
*/
required?: readonly string[];
}
/**
* Subset of the JSON Schema standard.
*
* Types are enforced to validate everything: each value **must** have a `type`.
*
* @see {@link https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/VALIDATION.md}
*
* @example
* const schema = { type: 'string' } satisfies JSONSchema;
*
* @example
* const schema = { type: 'number' } satisfies JSONSchema;
*
* @example
* const schema = { type: 'integer' } satisfies JSONSchema;
*
* @example
* const schema = { type: 'boolean' } satisfies JSONSchema;
*
* @example
* const schema = {
* type: 'array',
* items: { type: 'string' },
* } satisfies JSONSchema;
*
* @example
* const schema = {
* type: 'object',
* properties: {
* firstName: { type: 'string' },
* lastName: { type: 'string' },
* },
* required: ['firstName'],
* } satisfies JSONSchema;
*/
type JSONSchema = JSONSchemaString | JSONSchemaNumber | JSONSchemaInteger | JSONSchemaBoolean | JSONSchemaArray | JSONSchemaTuple | JSONSchemaObject;
declare class StorageMap {
#private;
/**
* Constructor params are provided by Angular (but can also be passed manually in tests)
* @param database Storage to use
*/
constructor(database: LocalDatabase);
/**
* **Number of items** in storage, wrapped in an Observable.
*
* Note you do *not* need to unsubscribe (it is a self-completing 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
* (like 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/main/docs/INTEROPERABILITY.md}
*
* @example
* if (this.storageMap.backingEngine === 'indexedDB') {}
*/
get backingEngine(): "indexedDB" | "localStorage" | "memory" | "unknown";
/**
* Information 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/main/docs/INTEROPERABILITY.md}
*
* @example
* if (this.storageMap.backingEngine === 'indexedDB') {
* const { database, store, version } = this.storageMap.backingStore;
* }
*/
get backingStore(): {
database: string;
store: string;
version: number;
};
/**
* Information 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/main/docs/INTEROPERABILITY.md}
*
* @example
* if (this.storageMap.backingEngine === 'localStorage') {
* const { prefix } = this.storageMap.fallbackBackingStore;
* }
*/
get fallbackBackingStore(): {
prefix: string;
};
/**
* Get an item value in storage.
*
* Note that:
* * not finding an item is not an error, it succeeds but returns `undefined`,
* * you do *not* need to unsubscribe (it is a self-completing Observable),
* * you will only get *one* value: the Observable is here for asynchrony but is *not* meant to emit again when the stored data is changed. If you need to watch the value, see the `watch` method.
*
* Do not forget it is client-side storage: **always check the data**, as it could have been forged.
* @see {@link https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/VALIDATION.md}
*
* @param key The item's key
* @param schema Optional but recommended 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'],
* } satisfies JSONSchema;
*
* this.storageMap.get<User>('user', schema).subscribe((user) => {
* if (user) {
* user.firstName;
* }
* });
*/
get(key: string): Observable<unknown>;
get<T extends string = string>(key: string, schema: JSONSchemaString): Observable<T | undefined>;
get<T extends number = number>(key: string, schema: JSONSchemaInteger | JSONSchemaNumber): Observable<T | undefined>;
get<T extends boolean = boolean>(key: string, schema: JSONSchemaBoolean): Observable<T | undefined>;
get<T extends readonly string[] = string[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaString>): Observable<T | undefined>;
get<T extends readonly number[] = number[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaInteger | JSONSchemaNumber>): Observable<T | undefined>;
get<T extends readonly boolean[] = boolean[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaBoolean>): Observable<T | undefined>;
get<T>(key: string, schema: JSONSchema): Observable<T | undefined>;
/**
* Store an item in storage.
*
* Note that:
* * you *do* need to subscribe, even if you do not have something specific to do after writing in storage, otherwise nothing happens (because it is how RxJS Observables work),
* * but you do *not* need to unsubscribe (it is a self-completing Observable),
* * setting `null` or `undefined` will remove the item to avoid some browsers issues,
* * you should stick to serializable JSON data, meaning primitive types, arrays and literal objects. Date, Map, Set, Blob and other special structures can cause issues in some scenarios.
* @see {@link https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/SERIALIZATION.md}
*
* @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.
*
* Note that:
* * you *do* need to subscribe, even if you do not have something specific to do after deleting, otherwise nothing happens (because it is how RxJS Observables work),
* * but you do *not* need to unsubscribe (it is a self-completing Observable).
*
* @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.
*
* Note that:
* * you *do* need to subscribe, even if you do not have something specific to do after clearing, otherwise nothing happens (because it is how RxJS Observables work),
* * but you do *not* need to unsubscribe (it is a self-completing Observable).
*
* @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,
* * you do *not* need to unsubscribe (it is a self-completing Observable).
*
* @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.
*
* Note you do *not* need to unsubscribe (it is a self-completing Observable).
*
* @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 that:
* * it is an infinite Observable, do not forget to unsubscribe,
* * only changes done via this library will be watched, external changes in storage cannot 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/main/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
*
* @example
* Component()
* export class MyComponent implements OnInit, OnDestroy {
*
* private storageSubscription?: Subscription;
*
* ngOnInit(): void {
* this.storageSubscription = this.storageMap.watch('key', { type: 'string' }).subscribe((result) => {
* result; // string or undefined
* });
* }
*
* ngOnDestroy(): void {
* this.storageSubscription?.unsubscribe();
* }
*
* }
*/
watch(key: string): Observable<unknown>;
watch<T extends string = string>(key: string, schema: JSONSchemaString): Observable<T | undefined>;
watch<T extends number = number>(key: string, schema: JSONSchemaInteger | JSONSchemaNumber): Observable<T | undefined>;
watch<T extends boolean = boolean>(key: string, schema: JSONSchemaBoolean): Observable<T | undefined>;
watch<T extends readonly string[] = string[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaString>): Observable<T | undefined>;
watch<T extends readonly number[] = number[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaInteger | JSONSchemaNumber>): Observable<T | undefined>;
watch<T extends readonly boolean[] = boolean[]>(key: string, schema: JSONSchemaArrayOf<JSONSchemaBoolean>): Observable<T | undefined>;
watch<T>(key: string, schema: JSONSchema): Observable<T | undefined>;
/**
* THIS METHOD IS FOR INTERNAL PURPOSE ONLY AND MUST NOT BE USED,
* IT CAN BE REMOVED AT ANY TIME AND MESSING WITH IT CAN CAUSE ISSUES
* @private
* @ignore
*/
private ɵinternalGetDatabase;
static ɵfac: i0.ɵɵFactoryDeclaration<StorageMap, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<StorageMap>;
}
export { SERIALIZATION_ERROR, SerializationError, StorageMap, StorageModule, VALIDATION_ERROR, ValidationError, provideIndexedDBDataBaseName, provideIndexedDBDataBaseVersion, provideIndexedDBStoreName, provideLocalStoragePrefix };
export type { JSONSchema, StorageConfig };