@superutils/rx 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.min.js +2 -2
- package/dist/browser/index.min.js.map +1 -1
- package/dist/index.cjs +154 -267
- package/dist/index.d.cts +366 -234
- package/dist/index.d.ts +366 -234
- package/dist/index.js +151 -264
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { TimeoutOptions, IPromisE_Timeout } from '@superutils/promise';
|
|
2
2
|
import { Subscribable, BehaviorSubject, Subject, Subscription } from 'rxjs';
|
|
3
3
|
export { BehaviorSubject, Subject, Subscribable, Subscription, Unsubscribable, isObservable, skip } from 'rxjs';
|
|
4
|
-
import { DeferredOptions, ThrottleOptions, DebounceOptions, DropFirst, filter,
|
|
4
|
+
import { DeferredOptions, ThrottleOptions, DebounceOptions, ValueOrPromise, DropFirst, filter, FindOptions, find, search, sort, SortOptions, TypedMap } from '@superutils/core';
|
|
5
|
+
export { TypedMap, objToMap } from '@superutils/core';
|
|
5
6
|
|
|
6
7
|
interface SubjectLike<T = unknown> {
|
|
7
8
|
next: (value: T) => void;
|
|
@@ -177,20 +178,31 @@ declare enum OnErrorType {
|
|
|
177
178
|
}
|
|
178
179
|
/** Storage type with only properties that are used by `DataStorage` */
|
|
179
180
|
type StorageCompact = Pick<Storage, 'getItem' | 'setItem'>;
|
|
180
|
-
type StorageFilter<K, V, AsArray extends boolean = false> = (...args: DropFirst<Parameters<typeof filter<K, V, AsArray>>>) => ReturnType<typeof filter<K, V>>;
|
|
181
|
-
type StorageFind<K, V, AsArray extends boolean = false> = (predicateOrOptions: Parameters<StorageFilter<K, V, AsArray>>[0] | Parameters<StorageSearch<K, V>>[0]) => V | undefined;
|
|
182
|
-
type StorageMap<K, V, T = unknown> = (callback: (value: V, key: K, data: [K, V][], index: number) => T) => T[];
|
|
183
|
-
type StorageOnChangeFn<K, V, CD extends boolean = false> = (this: IDataStorage<K, V, CD>, data: Map<K, V>) => ValueOrPromise<void | Map<K, V>>;
|
|
184
|
-
type StorageOnErrorFn<K, V, CD extends boolean = false> = (this: IDataStorage<K, V, CD>, err: unknown, type: OnErrorType) => ValueOrPromise<void>;
|
|
185
181
|
/** Initial options provided through the constructor */
|
|
186
182
|
type StorageOptions<Key, Value, CacheDisabled extends boolean = false> = {
|
|
187
|
-
/**
|
|
183
|
+
/**
|
|
184
|
+
* An optional `Map` used to seed the storage if no persistent data is found for the instance.
|
|
185
|
+
*
|
|
186
|
+
* **Data Precedence:**
|
|
187
|
+
* Persistent data associated with the instance's specific `name` takes priority. This value is
|
|
188
|
+
* only utilized if the storage entry for that `name` does not exist (e.g., first-time use).
|
|
189
|
+
*
|
|
190
|
+
* **Initialization Behavior:**
|
|
191
|
+
* - If provided and non-empty, the instance initializes immediately during construction.
|
|
192
|
+
* - Otherwise, initialization is lazy, occurring upon an explicit `init()` call or the first read/write operation.
|
|
193
|
+
*
|
|
194
|
+
* **Type Inference:**
|
|
195
|
+
* When provided, it enables automatic inference of the `Key` and `Value` generic types.
|
|
196
|
+
* If omitted, these default to `unknown` and `object` respectively, unless explicitly defined.
|
|
197
|
+
*
|
|
198
|
+
* @default undefined
|
|
199
|
+
*/
|
|
188
200
|
initialValue?: Map<Key, Value>;
|
|
189
201
|
} & Pick<Partial<IDataStorage<Key, Value, CacheDisabled>>, 'cacheDisabled' | 'onChange' | 'onError' | 'parse' | 'spaces' | 'storage' | 'stringify'> & (CacheDisabled extends false ? Pick<Partial<IDataStorage<Key, Value, CacheDisabled>>, 'delay' | 'delayOptions'> : {
|
|
190
202
|
delay?: never;
|
|
191
203
|
delayOptions?: never;
|
|
192
204
|
});
|
|
193
|
-
type StorageParseFn<
|
|
205
|
+
type StorageParseFn<ResultMap, ThisArg> = (this: ThisArg, text: string | null | undefined) => ResultMap | void;
|
|
194
206
|
type StorageSearch<K, V, MatchExact extends boolean = false, AsMap extends boolean = true> = (...args: DropFirst<Parameters<typeof search<K, V, MatchExact, AsMap>>>) => ReturnType<typeof search<K, V, MatchExact, AsMap>>;
|
|
195
207
|
type StorageSort<K, V> = (...args: StorageSortByComparator<K, V> | StorageSortByPropertyName<V> | StorageSortByKey) => Map<K, V>;
|
|
196
208
|
type StorageSortByComparator<K, V> = [
|
|
@@ -205,7 +217,7 @@ type StorageSortByPropertyName<V> = [
|
|
|
205
217
|
type StorageSortOptions = SortOptions & {
|
|
206
218
|
save?: boolean;
|
|
207
219
|
};
|
|
208
|
-
type
|
|
220
|
+
type StorageStringify<Data, ThisArg> = (this: ThisArg, data: Data) => string | undefined | void;
|
|
209
221
|
type StorageToJSON<K, V> = (replacer?: null | ((key: K, value: V) => unknown), spacing?: string | number, data?: Map<K, V>) => string;
|
|
210
222
|
interface IDataStorage<Key, Value, CacheDisabled extends boolean = false> {
|
|
211
223
|
/** Disable in-memory cache and only directly read/write from storage (local storage or JSON fle) */
|
|
@@ -230,7 +242,46 @@ interface IDataStorage<Key, Value, CacheDisabled extends boolean = false> {
|
|
|
230
242
|
*
|
|
231
243
|
* Default: `''`
|
|
232
244
|
*/
|
|
233
|
-
readonly name?: string;
|
|
245
|
+
readonly name?: string | null;
|
|
246
|
+
/**
|
|
247
|
+
* A callback function executed whenever a data change occurs within the storage.
|
|
248
|
+
*
|
|
249
|
+
* This hook allows for reactive side-effects. If the callback throws an error or returns a
|
|
250
|
+
* rejected Promise, the exception is caught gracefully and redirected to the {@link onError}
|
|
251
|
+
* callback with the type {@link OnErrorType.onChange}.
|
|
252
|
+
*
|
|
253
|
+
* Note: Execution of this callback is managed by internal subscriptions and will stop
|
|
254
|
+
* firing once {@link unsubscribe} is called.
|
|
255
|
+
*/
|
|
256
|
+
onChange?: (this: IDataStorage<Key, Value, CacheDisabled>, data: Map<Key, Value>) => ValueOrPromise<void | Map<Key, Value>>;
|
|
257
|
+
/**
|
|
258
|
+
* A global error handler invoked whenever an internal operation fails.
|
|
259
|
+
*
|
|
260
|
+
* It captures failures in the following areas:
|
|
261
|
+
* - Data parsing and serialization (JSON or custom logic).
|
|
262
|
+
* - Storage access (e.g., `localStorage` quota or permission errors).
|
|
263
|
+
* - Execution of user-provided callbacks like {@link onChange}.
|
|
264
|
+
*
|
|
265
|
+
* **Note:** If this handler itself throws an error, the exception is
|
|
266
|
+
* ignored gracefully to prevent application crashes during storage cycles.
|
|
267
|
+
*/
|
|
268
|
+
onError?: (this: IDataStorage<Key, Value, CacheDisabled>, err: unknown, type: OnErrorType) => ValueOrPromise<void>;
|
|
269
|
+
/**
|
|
270
|
+
* A callback to customize the deserialization of data read from storage.
|
|
271
|
+
*
|
|
272
|
+
* This allows you to transform the raw string from the underlying storage back into a
|
|
273
|
+
* `Map<Key, Value>`. It serves as the functional inverse of {@link stringify}.
|
|
274
|
+
*
|
|
275
|
+
* **Fallback Behavior:** The system falls back to internal `JSON.parse` logic if `parse`:
|
|
276
|
+
* - is not a non-function
|
|
277
|
+
* - throws an error
|
|
278
|
+
* - returns a non-Map value,
|
|
279
|
+
*
|
|
280
|
+
* **Error Triggers:**
|
|
281
|
+
* - If this custom `parse` function fails: {@link onError} is triggered with {@link OnErrorType.parse}.
|
|
282
|
+
* - If the default `JSON.parse` fallback fails: {@link onError} is triggered with {@link OnErrorType.parse_json}.
|
|
283
|
+
*/
|
|
284
|
+
parse?: StorageParseFn<Map<Key, Value>, IDataStorage<Key, Value, CacheDisabled>>;
|
|
234
285
|
/** Get the number of items */
|
|
235
286
|
readonly size: number;
|
|
236
287
|
/** Number of spaces to use when stringifying. Default: `undefined` */
|
|
@@ -249,6 +300,45 @@ interface IDataStorage<Key, Value, CacheDisabled extends boolean = false> {
|
|
|
249
300
|
* - node: `undefined` (in-memory mode)
|
|
250
301
|
*/
|
|
251
302
|
readonly storage?: StorageCompact | null;
|
|
303
|
+
/**
|
|
304
|
+
* A callback function to customize the serialization of data before it is written to storage.
|
|
305
|
+
*
|
|
306
|
+
* This allows you to transform the data `Map<Key, Value>` into a string format suitable
|
|
307
|
+
* for the underlying storage (e.g., JSON). It serves as the functional inverse of {@link parse}.
|
|
308
|
+
*
|
|
309
|
+
* Use this to sanitize data, remove circular references, or optimize the storage size by
|
|
310
|
+
* only persisting necessary fields.
|
|
311
|
+
*
|
|
312
|
+
* **Fallback Behavior:**
|
|
313
|
+
* If this function is not defined, throws an error, or returns `undefined` or a non-string value,
|
|
314
|
+
* the system falls back to internal `JSON.stringify` logic.
|
|
315
|
+
*
|
|
316
|
+
* **Error Triggers:**
|
|
317
|
+
* - If this custom `stringify` function fails: {@link onError} is triggered with {@link OnErrorType.stringify}.
|
|
318
|
+
* - If the default `JSON.stringify` fallback fails: {@link onError} is triggered with
|
|
319
|
+
* {@link OnErrorType.stringify_json}.
|
|
320
|
+
*
|
|
321
|
+
* @param data a map of all values stored in this storage
|
|
322
|
+
*
|
|
323
|
+
* @returns string or undefined
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* #### Sanitize data before saving
|
|
327
|
+
* ```javascript
|
|
328
|
+
* import { DataStorage } from '@superutils/rx'
|
|
329
|
+
*
|
|
330
|
+
* const stringify = data => {
|
|
331
|
+
* // Convert Map to an array of entries, removing sensitive fields
|
|
332
|
+
* const entries = Array.from(data).map(([id, user]) => {
|
|
333
|
+
* const { password, ...publicData } = user
|
|
334
|
+
* return [id, publicData]
|
|
335
|
+
* })
|
|
336
|
+
* return JSON.stringify(entries)
|
|
337
|
+
* }
|
|
338
|
+
* const storage = new DataStorage('users', { stringify })
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
stringify?: StorageStringify<Map<Key, Value>, IDataStorage<Key, Value, CacheDisabled>>;
|
|
252
342
|
/**
|
|
253
343
|
* The underlying RxJS Subject that serves as the primary reactive interface for observing data modifications.
|
|
254
344
|
*
|
|
@@ -263,18 +353,19 @@ interface IDataStorage<Key, Value, CacheDisabled extends boolean = false> {
|
|
|
263
353
|
readonly clear: () => IDataStorage<Key, Value, CacheDisabled>;
|
|
264
354
|
/** Delete one or more items by their respective keys */
|
|
265
355
|
readonly delete: (key: Key | Key[]) => IDataStorage<Key, Value, CacheDisabled>;
|
|
266
|
-
/** Find an item by predicate or search criteria */
|
|
267
|
-
readonly find: StorageFind<Key, Value>;
|
|
268
356
|
/** Filter items by predicate */
|
|
269
|
-
readonly filter:
|
|
357
|
+
readonly filter: <AsArray extends boolean = false>(...args: DropFirst<Parameters<typeof filter<Key, Value, AsArray>>>) => ReturnType<typeof filter<Key, Value, AsArray>>;
|
|
358
|
+
/** Find an item by predicate or search criteria */
|
|
359
|
+
readonly find: <IncludeKey extends boolean = false>(predicateOrOptions: FindOptions<Key, Value, IncludeKey> | Parameters<IDataStorage<Key, Value, CacheDisabled>['filter']>[0]) => ReturnType<typeof find<Key, Value, IncludeKey>>;
|
|
270
360
|
/** Get item by key */
|
|
271
361
|
readonly get: (key: Key) => Value | undefined;
|
|
272
362
|
/**
|
|
273
363
|
* Get all items
|
|
274
364
|
*
|
|
275
365
|
* @param forceUpdate (optional) if `true` and cache is enabled, reads & updates data directly from storage
|
|
366
|
+
* Default: `false`
|
|
276
367
|
*/
|
|
277
|
-
readonly getAll: (forceUpdate
|
|
368
|
+
readonly getAll: (forceUpdate?: boolean) => Map<Key, Value>;
|
|
278
369
|
/** Check if key exists */
|
|
279
370
|
readonly has: (key: Key) => boolean;
|
|
280
371
|
/**
|
|
@@ -292,51 +383,46 @@ interface IDataStorage<Key, Value, CacheDisabled extends boolean = false> {
|
|
|
292
383
|
/** Get all keys */
|
|
293
384
|
readonly keys: () => Key[];
|
|
294
385
|
/** Map each item on the data to an Array */
|
|
295
|
-
readonly map:
|
|
386
|
+
readonly map: <T = unknown>(callback: (value: Value, key: Key, entries: [Key, Value][], index: number) => T) => T[];
|
|
296
387
|
/**
|
|
297
|
-
*
|
|
388
|
+
* Reads and parses data directly from the persistent storage medium.
|
|
298
389
|
*
|
|
299
|
-
* This
|
|
300
|
-
*
|
|
301
|
-
* callback with the type {@link OnErrorType.onChange}.
|
|
390
|
+
* This operation is synchronous and does not trigger reactive updates via `subject`.
|
|
391
|
+
* It is useful for debugging custom `parse` logic or manual data retrieval.
|
|
302
392
|
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
393
|
+
* @param dataStr (optional) A raw string to parse. If omitted, the method fetches
|
|
394
|
+
* the current value associated with the instance `name` from the underlying `storage`.
|
|
305
395
|
*/
|
|
306
|
-
|
|
396
|
+
readonly read: (dataStr?: string | null) => Map<Key, Value>;
|
|
307
397
|
/**
|
|
308
|
-
*
|
|
398
|
+
* Search through the stored data (`Map<Key, Value>`).
|
|
399
|
+
* It supports both a global search (using a string or RegExp) across all properties
|
|
400
|
+
* of an item, and a detailed, field-specific search using a query object.
|
|
309
401
|
*
|
|
310
|
-
*
|
|
311
|
-
* - Data parsing and serialization (JSON or custom logic).
|
|
312
|
-
* - Storage access (e.g., `localStorage` quota or permission errors).
|
|
313
|
-
* - Execution of user-provided callbacks like {@link onChange}.
|
|
402
|
+
* @param options The search criteria. See {@link SearchOptions} for available properties.
|
|
314
403
|
*
|
|
315
|
-
*
|
|
316
|
-
* ignored gracefully to prevent application crashes during storage cycles.
|
|
317
|
-
*/
|
|
318
|
-
onError?: StorageOnErrorFn<Key, Value, CacheDisabled>;
|
|
319
|
-
/**
|
|
320
|
-
* A callback to customize the deserialization of data read from storage.
|
|
404
|
+
* @returns A `Map` or an `Array` containing the matched items, based on the `asMap` option.
|
|
321
405
|
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
406
|
+
* @example
|
|
407
|
+
* #### Search for users in a specific city
|
|
408
|
+
* ```typescript
|
|
409
|
+
* import { DataStorage } from '@superutils/rx'
|
|
324
410
|
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
411
|
+
* const storage = new DataStorage('users', {
|
|
412
|
+
* initialValue: new Map([
|
|
413
|
+
* [1, { name: 'John Doe', city: 'New York' }],
|
|
414
|
+
* [2, { name: 'Jane Doe', city: 'London' }],
|
|
415
|
+
* [3, { name: 'Peter Jones', city: 'New York' }],
|
|
416
|
+
* ])
|
|
417
|
+
* })
|
|
328
418
|
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
419
|
+
* const nyUsers = storage.search({ query: { city: 'New York' } })
|
|
420
|
+
* console.log(nyUsers.size) // 2
|
|
421
|
+
* ```
|
|
332
422
|
*/
|
|
333
|
-
readonly
|
|
334
|
-
/** Read directly from the localStorage (browser) or file (NodeJS) without triggering the `this.subject`. */
|
|
335
|
-
readonly read: () => Map<Key, Value>;
|
|
336
|
-
/** Search items */
|
|
337
|
-
readonly search: StorageSearch<Key, Value>;
|
|
423
|
+
readonly search: <MatchExact extends boolean = false, AsMap extends boolean = true>(...args: DropFirst<Parameters<typeof search<Key, Value, MatchExact, AsMap>>>) => ReturnType<typeof search<Key, Value, MatchExact, AsMap>>;
|
|
338
424
|
/** Set item by key */
|
|
339
|
-
readonly set: (key:
|
|
425
|
+
readonly set: <K extends Key, V extends Value>(key: K, value: V) => IDataStorage<Key, Value, CacheDisabled>;
|
|
340
426
|
/**
|
|
341
427
|
* Set multiple entries at once and/or replace the storage entries
|
|
342
428
|
*
|
|
@@ -361,48 +447,14 @@ interface IDataStorage<Key, Value, CacheDisabled extends boolean = false> {
|
|
|
361
447
|
* @returns The sorted Map.
|
|
362
448
|
*/
|
|
363
449
|
readonly sort: StorageSort<Key, Value>;
|
|
364
|
-
/**
|
|
365
|
-
* A callback function to customize the serialization of data before it is written to storage.
|
|
366
|
-
*
|
|
367
|
-
* This allows you to transform the data `Map<Key, Value>` into a string format suitable
|
|
368
|
-
* for the underlying storage (e.g., JSON). It serves as the functional inverse of {@link parse}.
|
|
369
|
-
*
|
|
370
|
-
* Use this to sanitize data, remove circular references, or optimize the storage size by
|
|
371
|
-
* only persisting necessary fields.
|
|
372
|
-
*
|
|
373
|
-
* **Fallback Behavior:**
|
|
374
|
-
* If this function is not defined, throws an error, or returns a non-string value,
|
|
375
|
-
* the system falls back to internal `JSON.stringify` logic.
|
|
376
|
-
*
|
|
377
|
-
* **Error Triggers:**
|
|
378
|
-
* - If this custom `stringify` function fails: {@link onError} is triggered with {@link OnErrorType.stringify}.
|
|
379
|
-
* - If the default `JSON.stringify` fallback fails: {@link onError} is triggered with {@link OnErrorType.stringify_json}.
|
|
380
|
-
*
|
|
381
|
-
* @example
|
|
382
|
-
* #### Sanitize data before saving
|
|
383
|
-
* ```javascript
|
|
384
|
-
* import { DataStorage } from '@superutils/rx'
|
|
385
|
-
*
|
|
386
|
-
* const stringify = data => {
|
|
387
|
-
* // Convert Map to an array of entries, removing sensitive fields
|
|
388
|
-
* const entries = Array.from(data).map(([id, user]) => {
|
|
389
|
-
* const { password, ...publicData } = user
|
|
390
|
-
* return [id, publicData]
|
|
391
|
-
* })
|
|
392
|
-
* return JSON.stringify(entries)
|
|
393
|
-
* }
|
|
394
|
-
* const storage = new DataStorage('users', { stringify })
|
|
395
|
-
* ```
|
|
396
|
-
*/
|
|
397
|
-
readonly stringify?: StorageStringifyFn<Key, Value, CacheDisabled>;
|
|
398
450
|
/** Convert list of items (Map) to 2D Array */
|
|
399
451
|
readonly toArray: () => [Key, Value][];
|
|
400
452
|
/** Convert list of items (Map) to JSON string of 2D Array */
|
|
401
453
|
readonly toJSON: StorageToJSON<Key, Value>;
|
|
402
454
|
/** Convert list of items into an object */
|
|
403
|
-
readonly toObject: (data?: Map<Key, Value>) =>
|
|
455
|
+
readonly toObject: <T extends object = object>(data?: Map<Key, Value>) => T;
|
|
404
456
|
/** Convert list of items (Map) to JSON string of 2D Array */
|
|
405
|
-
readonly toString: () => string;
|
|
457
|
+
readonly toString: (data?: Map<Key, Value>) => string;
|
|
406
458
|
/**
|
|
407
459
|
* Unsubscribe from all internal subscriptions.
|
|
408
460
|
*
|
|
@@ -420,137 +472,231 @@ interface IDataStorage<Key, Value, CacheDisabled extends boolean = false> {
|
|
|
420
472
|
* @param data (optional) Data to write.
|
|
421
473
|
* - If provided, it overwrites the storage.
|
|
422
474
|
* - If not provided, the current in-memory data is used (if cache is enabled).
|
|
423
|
-
* @param silent (optional) Whether to suppress errors if the write operation fails.
|
|
424
|
-
* - `true`: Returns `false` on failure without throwing.
|
|
425
|
-
* - `false`: Throws an error on failure.
|
|
426
|
-
*
|
|
427
|
-
* Default: `this.silent`
|
|
428
475
|
* @returns `true` if the write was successful, `false` otherwise.
|
|
429
476
|
*/
|
|
430
|
-
readonly write: (data?: Map<Key, Value
|
|
477
|
+
readonly write: (data?: Map<Key, Value>) => void;
|
|
478
|
+
}
|
|
479
|
+
interface IObjectStorage<T extends object, CacheDisabled extends boolean = false, ObjectMap extends TypedMap<T> = TypedMap<T>> extends IDataStorage<keyof T, T[keyof T], CacheDisabled> {
|
|
480
|
+
get<Key extends keyof T>(key: Key): T[Key] | undefined;
|
|
481
|
+
getAll(forceRead?: boolean): TypedMap<T>;
|
|
482
|
+
parse?: StorageParseFn<ObjectMap, IObjectStorage<T, CacheDisabled>>;
|
|
483
|
+
set<Key extends keyof T, Value extends T[Key]>(key: Key, value: Value): IObjectStorage<T, CacheDisabled>;
|
|
484
|
+
setAll(data?: ObjectMap, replace?: boolean): IObjectStorage<T, CacheDisabled>;
|
|
485
|
+
stringify?: StorageStringify<ObjectMap, IObjectStorage<T, CacheDisabled>>;
|
|
486
|
+
toObject<O extends object = T>(data?: Map<keyof T, T[keyof T]>): O;
|
|
431
487
|
}
|
|
432
488
|
|
|
433
489
|
/**
|
|
434
|
-
*
|
|
490
|
+
* RxJS Subject to trigger forced update of cached data from underlying storage of {@link DataStorage} instances.
|
|
491
|
+
*
|
|
492
|
+
* `value`: determines which cache-enabled storage instances to be updated
|
|
493
|
+
* - name (`string` | `string[]`): update all instances with a specific name(s)
|
|
494
|
+
* - global (`true`): update all instances globally
|
|
435
495
|
*
|
|
436
496
|
* @example
|
|
437
497
|
* ```javascript
|
|
498
|
+
* import { DataStorage, forceUpdateCache$ } from '@superutils/rx'
|
|
499
|
+
*
|
|
500
|
+
* const names = ['products', 'users']
|
|
501
|
+
*
|
|
502
|
+
* // Update all DataStorage instances by a list of their names
|
|
503
|
+
* forceUpdateCache$.next(names)
|
|
504
|
+
* // alternatively: DataStorage.forceUpdateCache(names)
|
|
505
|
+
*
|
|
506
|
+
* // Update all DataStorage instances with a specific name
|
|
507
|
+
* forceUpdateCache$.next(names[0])
|
|
508
|
+
* // alternatively: DataStorage.forceUpdateCache(names[0])
|
|
509
|
+
*
|
|
510
|
+
* // Update every single instance of DataStorage that uses storage (has a "name" property)
|
|
511
|
+
* forceUpdateCache$(true)
|
|
512
|
+
* // alternatively: DataStorage.forceUpdateCache(true)
|
|
513
|
+
* ```
|
|
514
|
+
*
|
|
515
|
+
* @example
|
|
516
|
+
*
|
|
517
|
+
* #### Practical example
|
|
518
|
+
* ```typescript
|
|
438
519
|
* import { DataStorage } from '@superutils/rx'
|
|
439
520
|
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
521
|
+
* const name = 'user-profile'
|
|
522
|
+
* type User = {
|
|
523
|
+
* age: number
|
|
524
|
+
* name: string
|
|
525
|
+
* roles?: string[]
|
|
526
|
+
* }
|
|
527
|
+
* const userStore = DataStorage.(name, { delay: 0 }) // delay is set to zero to simplify the example
|
|
528
|
+
* userStore.set('name', 'John Doe')
|
|
529
|
+
* userStore.set('name', 'John Doe')
|
|
443
530
|
*
|
|
444
|
-
* // Update every single instance of DataStorage that uses storage (has a "name")
|
|
445
|
-
* forceUpdateCache$.next(true)
|
|
446
531
|
* ```
|
|
447
532
|
*/
|
|
448
533
|
declare const forceUpdateCache$: Subject<string | boolean | string[]>;
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
534
|
+
/**
|
|
535
|
+
*
|
|
536
|
+
*
|
|
537
|
+
* @remarks
|
|
538
|
+
* **On the `This` template parameter:**
|
|
539
|
+
* Using `This` as a self-referential template is a **good practice** in this context because:
|
|
540
|
+
* - It provides accurate **type inference** for method return types that depend on the generic parameters.
|
|
541
|
+
* - It enables **type-safe property access** through `This['methodName']`, allowing the implementation
|
|
542
|
+
* to reference interface contracts without circular dependencies or casting issues.
|
|
543
|
+
* - It allows **fluent API chains** (returning `this`) while maintaining proper generic type information.
|
|
544
|
+
* - It prevents **type widening** that would occur if methods returned the concrete class type instead
|
|
545
|
+
* of the interface type, which is important for generic constraints and polymorphism.
|
|
546
|
+
*
|
|
547
|
+
* However, it increases **cognitive complexity** and is only warranted when:
|
|
548
|
+
* - The class implements a complex generic interface with interdependent type parameters.
|
|
549
|
+
* - Type-safe property references are essential to avoid runtime errors or casting.
|
|
550
|
+
* - Fluent interfaces or chaining is a core API feature.
|
|
551
|
+
*
|
|
552
|
+
*
|
|
553
|
+
*/
|
|
554
|
+
/**
|
|
555
|
+
* A generic, reactive data storage class that provides a Map-like interface with advanced features
|
|
556
|
+
* such as search, filtering, and sorting. Supports both in-memory caching and persistent storage
|
|
557
|
+
* (LocalStorage in browsers, JSON files in NodeJS via `node-localstorage` NPM module).
|
|
558
|
+
*
|
|
559
|
+
* #### Notes:
|
|
560
|
+
* - **Performance**: `DataStorage` is optimized for small to medium datasets.
|
|
561
|
+
* - For datasets > 1MB, consider increasing the `delay` option to reduce write frequency.
|
|
562
|
+
* - It is **NOT** recommended for datasets larger than 3MB due to synchronous serialization costs.
|
|
563
|
+
* - **RxJS Integration**: Built on RxJS for reactive data handling, though no prior RxJS knowledge is required.
|
|
564
|
+
* - **Storage Behavior**:
|
|
565
|
+
* - If `name` is omitted, the instance operates in-memory only and data is not persisted to storage.
|
|
566
|
+
* - If `cacheDisabled` is `true`, data is not kept in memory; every read/write operation accesses the underlying
|
|
567
|
+
* storage directly.
|
|
568
|
+
*
|
|
569
|
+
* @template Key The type of keys stored in the map.
|
|
570
|
+
* @template Value The type of values stored in the map.
|
|
571
|
+
* @template CacheDisabled A literal boolean type indicating whether in-memory caching is disabled.
|
|
572
|
+
* @template This A self-referential interface type extending {@link IDataStorage} used for accurate
|
|
573
|
+
* method signature inference and type-safe property access. This allows method implementations to
|
|
574
|
+
* reference their return types and other method signatures through the interface definition.
|
|
575
|
+
*
|
|
576
|
+
* @see {@link forceUpdateCache$} for cache invalidation across instances.
|
|
577
|
+
* @see {@link DataStorage.fromObject} for object-oriented storage initialization.
|
|
578
|
+
*
|
|
579
|
+
* @example
|
|
580
|
+
* #### Browser Usage 1: use like a map
|
|
581
|
+
* ```javascript
|
|
582
|
+
* import { DataStorage } from '@superutils/rx'
|
|
583
|
+
*
|
|
584
|
+
* const userStorage = new DataStorage('users')
|
|
585
|
+
* userStorage.set(1, { name: 'Alice', age: 30 })
|
|
586
|
+
* const user = userStorage.get(1)
|
|
587
|
+
* console.log(user) // prints: {name: 'Alice', age: 30}
|
|
588
|
+
* ```
|
|
589
|
+
*
|
|
590
|
+
* @example
|
|
591
|
+
* #### Browser Usage 2:
|
|
592
|
+
* ```javascript
|
|
593
|
+
* import { DataStorage } from '@superutils/rx'
|
|
594
|
+
* import fetch from '@superutils/fetch'
|
|
595
|
+
*
|
|
596
|
+
* const { products } = await fetch('[DUMMYJSON-DOT-COM]/products')
|
|
597
|
+
* const storage = new DataStorage('products', {
|
|
598
|
+
* initialValue: new Map(products.map(p => [p.id, p])) // convert to Map
|
|
599
|
+
* })
|
|
600
|
+
*
|
|
601
|
+
* // print product with id `1`
|
|
602
|
+
* console.log(storage.get(1))
|
|
603
|
+
*
|
|
604
|
+
* // search for items
|
|
605
|
+
* const searchResult = storage.search({
|
|
606
|
+
* query: { availabilityStatus: 'low' }
|
|
607
|
+
* })
|
|
608
|
+
* console.log(searchResult)
|
|
609
|
+
* ```
|
|
610
|
+
* @example
|
|
611
|
+
* #### NodeJS Usage
|
|
612
|
+
* ```javascript
|
|
613
|
+
* import { DataStorage } from '@superutils/rx'
|
|
614
|
+
* import fetch from '@superutils/fetch'
|
|
615
|
+
* import { LocalStorage } from 'node-localstorage'
|
|
616
|
+
*
|
|
617
|
+
* // Add localStorage alternative for NodeJS that reads and writes to JSON files.
|
|
618
|
+
* // This is not necessary for browsers.
|
|
619
|
+
* globalThis.localStorage = new LocalStorage('./data', 1e7)
|
|
620
|
+
*
|
|
621
|
+
* const storage = new DataStorage('products')
|
|
622
|
+
* const { products } = await fetch('[DUMMYJSON-DOT-COM]/products')
|
|
623
|
+
* // save all items to storage
|
|
624
|
+
* storage.setAll(
|
|
625
|
+
* new Map(products.map(p => [p.id, p])), // convert to Map
|
|
626
|
+
* )
|
|
627
|
+
*
|
|
628
|
+
* // print product with id `1`
|
|
629
|
+
* console.log(storage.get(1))
|
|
630
|
+
*
|
|
631
|
+
* // search for items
|
|
632
|
+
* const searchResult = storage.search({
|
|
633
|
+
* query: { availabilityStatus: 'low' }
|
|
634
|
+
* })
|
|
635
|
+
* console.log(searchResult)
|
|
636
|
+
* ```
|
|
637
|
+
*
|
|
638
|
+
* @example
|
|
639
|
+
* #### Advanced: `onChange` and RxJS subject
|
|
640
|
+
*
|
|
641
|
+
* Internally, `DataStorage` uses RxJS subject which is exposed as `subject` property.
|
|
642
|
+
* You can use this to subscribe to changes and do additional operations such as logging or sanitization etc.
|
|
643
|
+
*
|
|
644
|
+
* Alternatively, you can also set the `onChange` callback which is triggered whenever the subject changes and
|
|
645
|
+
* does not require maintaining a subscription or knowledge of RxJS subject.
|
|
646
|
+
*
|
|
647
|
+
* ```javascript
|
|
648
|
+
* import { DataStorage } from '@superutils/rx'
|
|
649
|
+
*
|
|
650
|
+
* const storage = new DataStorage('my-data')
|
|
651
|
+
* const sub = storage.subject.subscribe(data => {
|
|
652
|
+
* // Write to the database whenever data changes
|
|
653
|
+
* console.log('Saving to database...', data)
|
|
654
|
+
* })
|
|
655
|
+
* // unsubscribe from subject
|
|
656
|
+
* setTimeout(()=> sub.unsbuscribe(), 1000)
|
|
657
|
+
*
|
|
658
|
+
* // add an entry to storage
|
|
659
|
+
* storage.set('bob', { age: 99, id: 'bob', name: 'Bob' })
|
|
660
|
+
* ```
|
|
661
|
+
*/
|
|
662
|
+
declare class DataStorage<Key, Value, CacheDisabled extends boolean = false,
|
|
663
|
+
/**
|
|
664
|
+
* @remarks
|
|
665
|
+
* **On the `This` template parameter:**
|
|
666
|
+
* Using `This` as a self-referential template is a **good practice** in this context because:
|
|
667
|
+
* - It provides accurate **type inference** for method return types that depend on the generic parameters.
|
|
668
|
+
* - It enables **type-safe property access** through `This['methodName']`, allowing the implementation
|
|
669
|
+
* to reference interface contracts without circular dependencies or casting issues.
|
|
670
|
+
* - It allows **fluent API chains** (returning `this`) while maintaining proper generic type information.
|
|
671
|
+
* - It prevents **type widening** that would occur if methods returned the concrete class type instead
|
|
672
|
+
* of the interface type, which is important for generic constraints and polymorphism.
|
|
673
|
+
*
|
|
674
|
+
* However, it increases **cognitive complexity** and is only warranted when:
|
|
675
|
+
* - The class implements a complex generic interface with interdependent type parameters.
|
|
676
|
+
* - Type-safe property references are essential to avoid runtime errors or casting.
|
|
677
|
+
* - Fluent interfaces or chaining is a core API feature.
|
|
678
|
+
*/
|
|
679
|
+
This extends IDataStorage<Key, Value, CacheDisabled> = IDataStorage<Key, Value, CacheDisabled>> implements IDataStorage<Key, Value, CacheDisabled> {
|
|
680
|
+
readonly cacheDisabled: This['cacheDisabled'];
|
|
681
|
+
readonly delay: This['delay'];
|
|
452
682
|
/** Debounce and throttle related options */
|
|
453
|
-
readonly delayOptions?:
|
|
454
|
-
readonly initialized:
|
|
455
|
-
readonly name:
|
|
683
|
+
readonly delayOptions?: This['delayOptions'];
|
|
684
|
+
readonly initialized: This['initialized'];
|
|
685
|
+
readonly name: This['name'];
|
|
686
|
+
onChange?: This['onChange'];
|
|
687
|
+
onError?: This['onError'];
|
|
688
|
+
parse?: This['parse'];
|
|
456
689
|
get size(): number;
|
|
457
|
-
spaces?:
|
|
458
|
-
readonly storage?:
|
|
459
|
-
|
|
690
|
+
spaces?: This['spaces'];
|
|
691
|
+
readonly storage?: This['storage'];
|
|
692
|
+
stringify?: This['stringify'];
|
|
693
|
+
readonly subject: This['subject'];
|
|
460
694
|
private subscriptions;
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
* - **Performance**: `DataStorage` is optimized for small to medium datasets.
|
|
467
|
-
* - For datasets > 1MB, consider increasing the `delay` option to reduce write frequency.
|
|
468
|
-
* - It is **NOT** recommended for datasets larger than 3MB due to synchronous serialization costs.
|
|
469
|
-
* - **RxJS Integration**: Built on RxJS for reactive data handling, though no prior RxJS knowledge is required.
|
|
470
|
-
* - **Storage Behavior**:
|
|
471
|
-
* - If `name` is omitted, the instance operates in-memory only and data is not persisted to storage.
|
|
472
|
-
* - If `cacheDisabled` is `true`, data is not kept in memory; every read/write operation accesses the underlying
|
|
473
|
-
* storage directly.
|
|
474
|
-
*
|
|
475
|
-
* @example
|
|
476
|
-
* #### Browser Usage
|
|
477
|
-
* ```javascript
|
|
478
|
-
* import { DataStorage } from '@superutils/rx'
|
|
479
|
-
* import fetch from '@superutils/fetch'
|
|
480
|
-
*
|
|
481
|
-
* const storage = new DataStorage('products')
|
|
482
|
-
* const { products } = await fetch('[DUMMYJSON-DOT-COM]/products')
|
|
483
|
-
* // save all items to storage
|
|
484
|
-
* storage.setAll(
|
|
485
|
-
* new Map(products.map(p => [p.id, p])), // convert to Map
|
|
486
|
-
* )
|
|
487
|
-
*
|
|
488
|
-
* // print product with id `1`
|
|
489
|
-
* console.log(storage.get(1))
|
|
490
|
-
*
|
|
491
|
-
* // search for items
|
|
492
|
-
* const searchResult = storage.search({
|
|
493
|
-
* query: { availabilityStatus: 'low' }
|
|
494
|
-
* })
|
|
495
|
-
* console.log(searchResult)
|
|
496
|
-
* ```
|
|
497
|
-
* @example
|
|
498
|
-
* #### NodeJS Usage
|
|
499
|
-
* ```javascript
|
|
500
|
-
* import { DataStorage } from '@superutils/rx'
|
|
501
|
-
* import fetch from '@superutils/fetch'
|
|
502
|
-
* import { LocalStorage } from 'node-localstorage'
|
|
503
|
-
*
|
|
504
|
-
* // Add localStorage alternative for NodeJS that reads and writes to JSON files.
|
|
505
|
-
* // This is not necessary for browsers.
|
|
506
|
-
* globalThis.localStorage = new LocalStorage('./data', 1e7)
|
|
507
|
-
*
|
|
508
|
-
* const storage = new DataStorage('products')
|
|
509
|
-
* const { products } = await fetch('[DUMMYJSON-DOT-COM]/products')
|
|
510
|
-
* // save all items to storage
|
|
511
|
-
* storage.setAll(
|
|
512
|
-
* new Map(products.map(p => [p.id, p])), // convert to Map
|
|
513
|
-
* )
|
|
514
|
-
*
|
|
515
|
-
* // print product with id `1`
|
|
516
|
-
* console.log(storage.get(1))
|
|
517
|
-
*
|
|
518
|
-
* // search for items
|
|
519
|
-
* const searchResult = storage.search({
|
|
520
|
-
* query: { availabilityStatus: 'low' }
|
|
521
|
-
* })
|
|
522
|
-
* console.log(searchResult)
|
|
523
|
-
* ```
|
|
524
|
-
*
|
|
525
|
-
* @example
|
|
526
|
-
* #### Advanced: `onChange` and RxJS subject
|
|
527
|
-
*
|
|
528
|
-
* Internally, `DataStorage` uses RxJS subject which is exposed as `subject` property.
|
|
529
|
-
* You can use this to subscribe to changes and do additional operations such as logging or sanitization etc.
|
|
530
|
-
*
|
|
531
|
-
* Alternatively, you can also set the `onChange` callback which is triggered whenever the subject changes and
|
|
532
|
-
* does not require maintaining a subscription or knowledge of RxJS subject.
|
|
533
|
-
*
|
|
534
|
-
* ```javascript
|
|
535
|
-
* import { DataStorage } from '@superutils/rx'
|
|
536
|
-
*
|
|
537
|
-
* const storage = new DataStorage('my-data')
|
|
538
|
-
* const sub = storage.subject.subscribe(data => {
|
|
539
|
-
* // Write to the database whenever data changes
|
|
540
|
-
* console.log('Saving to database...', data)
|
|
541
|
-
* })
|
|
542
|
-
* // unsubscribe from subject
|
|
543
|
-
* setTimeout(()=> sub.unsbuscribe(), 1000)
|
|
544
|
-
*
|
|
545
|
-
* // add an entry to storage
|
|
546
|
-
* storage.set('bob', { age: 99, id: 'bob', name: 'Bob' })
|
|
547
|
-
* ```
|
|
548
|
-
*/
|
|
549
|
-
constructor(name?: string | null, options?: StorageOptions<Key, Value, CacheDisabled>);
|
|
550
|
-
clear(): this;
|
|
551
|
-
delete(keys: Key | Key[]): this;
|
|
552
|
-
find(predicateOrOptions: Parameters<StorageFind<Key, Value>>[0]): Value | undefined;
|
|
553
|
-
filter<AsArray extends boolean = false>(...args: Parameters<StorageFilter<Key, Value, AsArray>>): AsArray extends true ? Value[] : Map<Key, Value>;
|
|
695
|
+
constructor(name?: This['name'], options?: StorageOptions<Key, Value, CacheDisabled>);
|
|
696
|
+
clear: This['clear'];
|
|
697
|
+
delete: This['delete'];
|
|
698
|
+
filter: This['filter'];
|
|
699
|
+
find: This['find'];
|
|
554
700
|
/**
|
|
555
701
|
* Creates a {@link DataStorage} instance initialized from a plain object.
|
|
556
702
|
*
|
|
@@ -597,54 +743,40 @@ declare class DataStorage<Key, Value, CacheDisabled extends boolean = false> imp
|
|
|
597
743
|
* console.log(userObj) // { age: 100, name: 'Ninety Nine' }
|
|
598
744
|
* ```
|
|
599
745
|
*/
|
|
600
|
-
static fromObject: <T extends object, CacheDisabled_1 extends boolean = false>(name?: string, options?: Omit<StorageOptions<
|
|
746
|
+
static fromObject: <T extends object, Key_1 extends keyof T = keyof T, Value_1 extends T[Key_1] = T[Key_1], CacheDisabled_1 extends boolean = false>(name?: string | null, options?: Omit<StorageOptions<Key_1, Value_1, CacheDisabled_1>, "initialValue"> & {
|
|
601
747
|
initialValue?: T;
|
|
602
|
-
}) =>
|
|
748
|
+
}) => IObjectStorage<T, CacheDisabled_1>;
|
|
603
749
|
/**
|
|
604
750
|
* Trigger forced update of cached data from storage.
|
|
605
751
|
*
|
|
606
|
-
* @param name determines which storage instances to be updated.
|
|
752
|
+
* @param name determines which cache-enabled storage instances to be updated.
|
|
607
753
|
* - name (`string` | `string[]`): update all instances with a specific name(s)
|
|
608
754
|
* - global (`true`): update all instances globally
|
|
609
755
|
*
|
|
610
|
-
* @
|
|
611
|
-
* ```javascript
|
|
612
|
-
* import { DataStorage } from '@superutils/rx'
|
|
613
|
-
*
|
|
614
|
-
* // Update all DataStorage instances with specific name(s)
|
|
615
|
-
* const name = 'products'
|
|
616
|
-
* DataStorage.forceUpdateCache([name])
|
|
617
|
-
*
|
|
618
|
-
* // Update every single instance of DataStorage that uses storage (has a "name")
|
|
619
|
-
* DataStorage.forceUpdateCache(true)
|
|
620
|
-
* ```
|
|
756
|
+
* See {@link forceUpdateCache$} for more details.
|
|
621
757
|
*/
|
|
622
758
|
static forceUpdateCache: (name: string | string[] | true) => void;
|
|
623
|
-
get
|
|
624
|
-
getAll
|
|
759
|
+
get: This['get'];
|
|
760
|
+
getAll: This['getAll'];
|
|
625
761
|
private handleForceUpdateCacheChange;
|
|
626
762
|
private handleSubjectChange;
|
|
627
|
-
has
|
|
628
|
-
init
|
|
629
|
-
keys
|
|
630
|
-
map
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
toArray(): [Key, Value][];
|
|
641
|
-
toJSON(...[replacer, spacing, data]: Parameters<StorageToJSON<Key, Value>>): string;
|
|
642
|
-
toObject<T extends object = object>(data?: Map<Key, Value>): T;
|
|
643
|
-
toString(data?: Map<Key, Value>): string;
|
|
763
|
+
has: This['has'];
|
|
764
|
+
init: This['init'];
|
|
765
|
+
keys: This['keys'];
|
|
766
|
+
map: This['map'];
|
|
767
|
+
read: This['read'];
|
|
768
|
+
search: This['search'];
|
|
769
|
+
set: This['set'];
|
|
770
|
+
setAll: This['setAll'];
|
|
771
|
+
sort: This['sort'];
|
|
772
|
+
toArray: This['toArray'];
|
|
773
|
+
toJSON: This['toJSON'];
|
|
774
|
+
toObject: This['toObject'];
|
|
775
|
+
toString: This['toString'];
|
|
644
776
|
private triggerOnError;
|
|
645
|
-
unsubscribe
|
|
646
|
-
values
|
|
647
|
-
write
|
|
777
|
+
unsubscribe: This['unsubscribe'];
|
|
778
|
+
values: This['values'];
|
|
779
|
+
write: This['write'];
|
|
648
780
|
}
|
|
649
781
|
|
|
650
782
|
/**
|
|
@@ -834,4 +966,4 @@ declare const isSubscriptionLike: (value: unknown, strict?: boolean) => value is
|
|
|
834
966
|
*/
|
|
835
967
|
declare const unsubscribeAll: (unsub?: UnsubscribeCandidates, onError?: (err: unknown) => void) => void;
|
|
836
968
|
|
|
837
|
-
export { type CopyRxSubjectOptions, DataStorage, type DelayOptions, type IDataStorage, IGNORE_UPDATE_SYMBOL, IntervalRunner, IntervalSubject, OnErrorType, type OnResultType, type StorageCompact, type
|
|
969
|
+
export { type CopyRxSubjectOptions, DataStorage, type DelayOptions, type IDataStorage, IGNORE_UPDATE_SYMBOL, type IObjectStorage, IntervalRunner, IntervalSubject, OnErrorType, type OnResultType, type StorageCompact, type StorageOptions, type StorageParseFn, type StorageSearch, type StorageSort, type StorageSortByComparator, type StorageSortByKey, type StorageSortByPropertyName, type StorageSortOptions, type StorageStringify, type StorageToJSON, type SubjectLike, type SubscriptionLike, type Unsubscribe, type UnsubscribeCandidates, type UnwrapSubjectValue, type ValueModifier, asPromise, copyRxSubject, forceUpdateCache$, isSubjectLike, isSubscriptionLike, type onBeforeExecType, unsubscribeAll };
|