@superutils/rx 0.1.4 → 0.1.5

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/index.d.ts CHANGED
@@ -143,50 +143,71 @@ declare namespace copyRxSubject {
143
143
  var IGNORE_UPDATE_SYMBOL: typeof IGNORE_UPDATE_SYMBOL;
144
144
  }
145
145
 
146
+ /** Throttle & debounce related options */
146
147
  type DelayOptions = ({
147
148
  throttle: true;
148
149
  } & Omit<ThrottleOptions, 'onError' | 'thisArg'>) | ({
149
150
  throttle?: false;
150
151
  } & Omit<DebounceOptions, 'onError' | 'thisArg'>);
151
- type OnErrorType = 'onChange' | 'parse' | 'parse-json' | 'stringify' | 'stringify-json' | 'write';
152
- /** Storage type with only properties that are used by `DataStorage` */
153
- type StorageCompact = Pick<Storage, 'getItem' | 'setItem'>;
154
- type StorageFilter<K, V extends StorageValue> = <IncludeKey extends boolean = false>(...args: DropFirst<Parameters<typeof filter<K, V, IncludeKey>>>) => ReturnType<typeof filter<K, V, IncludeKey>>;
155
- type StorageFind<K, V extends StorageValue> = (predicateOrOptions: Parameters<StorageFilter<K, V>>[0] | Parameters<StorageSearch<K, V>>[0]) => V | undefined;
156
- type StorageMap<K, V extends StorageValue> = <T>(callback: (value: V, key: K, data: [K, V][], index: number) => T) => T[];
157
152
  /**
158
- * Callback triggered when value changes and/or a force read is triggered
153
+ * Categorizes errors encountered during DataStorage operations.
154
+ *
155
+ * These types are passed to the `onError` callback to help identify which phase of the
156
+ * data lifecycle (reading, writing, or processing) failed.
159
157
  */
160
- type StorageOnChangeFn<K, V extends StorageValue> = (data: Map<K, V>) => ValueOrPromise<void | Map<K, V>>;
161
- type StorageOnErrorFn = (err: unknown, type: OnErrorType) => ValueOrPromise<void>;
158
+ declare enum OnErrorType {
159
+ /** Occurs when the user-provided `onChange` callback throws an exception. */
160
+ onChange = "onChange",
161
+ /** Occurs when the user-provided `parse` function fails to process the raw storage string. */
162
+ parse = "parse",
163
+ /**
164
+ * Occurs when the default `JSON.parse` fallback fails.
165
+ * This usually happens if the data in the underlying storage is corrupted or not valid JSON.
166
+ */
167
+ parse_json = "parse-json",
168
+ /** Occurs when the user-provided `stringify` function fails to serialize the data Map. */
169
+ stringify = "stringify",
170
+ /**
171
+ * Occurs when the default `JSON.stringify` fallback fails.
172
+ * This may happen if the Map contains circular references or other non-serializable values.
173
+ */
174
+ stringify_json = "stringify-json",
175
+ /** Occurs when the attempt to save data to the underlying storage (e.g., `localStorage.setItem`) fails. */
176
+ write = "write"
177
+ }
178
+ /** Storage type with only properties that are used by `DataStorage` */
179
+ 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>;
162
185
  /** Initial options provided through the constructor */
163
- type StorageOptions<Key extends StorageKey, Value extends StorageValue, CacheDisabled extends boolean = false> = {
186
+ type StorageOptions<Key, Value, CacheDisabled extends boolean = false> = {
164
187
  /** value to set, only if storage is empty. Default: `new Map()` */
165
188
  initialValue?: Map<Key, Value>;
166
189
  } & Pick<Partial<IDataStorage<Key, Value, CacheDisabled>>, 'cacheDisabled' | 'onChange' | 'onError' | 'parse' | 'spaces' | 'storage' | 'stringify'> & (CacheDisabled extends false ? Pick<Partial<IDataStorage<Key, Value, CacheDisabled>>, 'delay' | 'delayOptions'> : {
167
190
  delay?: never;
168
191
  delayOptions?: never;
169
192
  });
170
- type StorageParseFn<K, V extends StorageValue> = (data: string) => Map<K, V>;
171
- type StorageSearch<K, V extends StorageValue> = <MatchExact extends boolean = false, AsMap extends boolean = true>(...args: DropFirst<Parameters<typeof search<K, V, MatchExact, AsMap>>>) => ReturnType<typeof search<K, V, MatchExact, AsMap>>;
172
- type StorageSort<K, V extends StorageValue> = (...args: StorageSortByComparator<K, V> | StorageSortByPropertyName<V> | StorageSortByKey) => Map<K, V>;
173
- type StorageSortByComparator<K, V extends StorageValue> = [
193
+ type StorageParseFn<K, V, CD extends boolean = false> = (this: IDataStorage<K, V, CD>, data: string) => Map<K, V>;
194
+ 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
+ type StorageSort<K, V> = (...args: StorageSortByComparator<K, V> | StorageSortByPropertyName<V> | StorageSortByKey) => Map<K, V>;
196
+ type StorageSortByComparator<K, V> = [
174
197
  comparator: Parameters<typeof sort<K, V>>[1],
175
198
  options?: StorageSortOptions
176
199
  ];
177
200
  type StorageSortByKey = [byKey: true, options?: StorageSortOptions];
178
- type StorageSortByPropertyName<V extends StorageValue> = [
201
+ type StorageSortByPropertyName<V> = [
179
202
  propertyName: keyof V & string,
180
203
  options?: StorageSortOptions
181
204
  ];
182
205
  type StorageSortOptions = SortOptions & {
183
206
  save?: boolean;
184
207
  };
185
- type StorageStringifyFn<K, V extends StorageValue> = (data: Map<K, V>) => string;
208
+ type StorageStringifyFn<K, V, CD extends boolean = false> = (this: IDataStorage<K, V, CD>, data: Map<K, V>) => string;
186
209
  type StorageToJSON<K, V> = (replacer?: null | ((key: K, value: V) => unknown), spacing?: string | number, data?: Map<K, V>) => string;
187
- type StorageKey = string;
188
- type StorageValue = object;
189
- interface IDataStorage<Key extends StorageKey, Value extends StorageValue, CacheDisabled extends boolean = false> {
210
+ interface IDataStorage<Key, Value, CacheDisabled extends boolean = false> {
190
211
  /** Disable in-memory cache and only directly read/write from storage (local storage or JSON fle) */
191
212
  readonly cacheDisabled: CacheDisabled;
192
213
  /**
@@ -273,25 +294,43 @@ interface IDataStorage<Key extends StorageKey, Value extends StorageValue, Cache
273
294
  /** Map each item on the data to an Array */
274
295
  readonly map: StorageMap<Key, Value>;
275
296
  /**
276
- * Callback to be invoked whenever value change is triggered.
297
+ * A callback function executed whenever a data change occurs within the storage.
277
298
  *
278
- * If `onChange` invocation fails, it will be ignored gracefully.
299
+ * This hook allows for reactive side-effects. If the callback throws an error or returns a
300
+ * rejected Promise, the exception is caught gracefully and redirected to the {@link onError}
301
+ * callback with the type {@link OnErrorType.onChange}.
302
+ *
303
+ * Note: Execution of this callback is managed by internal subscriptions and will stop
304
+ * firing once {@link unsubscribe} is called.
279
305
  */
280
- onChange?: StorageOnChangeFn<Key, Value>;
306
+ onChange?: StorageOnChangeFn<Key, Value, CacheDisabled>;
281
307
  /**
282
- * Callback to be invoked whenever read/write operation fails.
308
+ * A global error handler invoked whenever an internal operation fails.
309
+ *
310
+ * It captures failures in the following areas:
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}.
283
314
  *
284
- * If `onError` invocation failure will be ignored gracefully.
315
+ * **Note:** If this handler itself throws an error, the exception is
316
+ * ignored gracefully to prevent application crashes during storage cycles.
285
317
  */
286
- onError?: StorageOnErrorFn;
318
+ onError?: StorageOnErrorFn<Key, Value, CacheDisabled>;
287
319
  /**
288
320
  * A callback to customize the deserialization of data read from storage.
289
321
  *
290
- * This can be used to override the default `JSON.parse` behavior and serves as the counterpart to `stringify`.
291
- * If this function is not provided, throws an error, or returns `undefined`, the default
292
- * `JSON.parse` will be used as a fallback.
322
+ * This allows you to transform the raw string from the underlying storage back into a
323
+ * `Map<Key, Value>`. It serves as the functional inverse of {@link stringify}.
324
+ *
325
+ * **Fallback Behavior:**
326
+ * If this function is not defined, throws an error, or returns a non-Map value,
327
+ * the system falls back to internal `JSON.parse` logic.
328
+ *
329
+ * **Error Triggers:**
330
+ * - If this custom `parse` function fails: {@link onError} is triggered with {@link OnErrorType.parse}.
331
+ * - If the default `JSON.parse` fallback fails: {@link onError} is triggered with {@link OnErrorType.parse_json}.
293
332
  */
294
- readonly parse?: StorageParseFn<Key, Value>;
333
+ readonly parse?: StorageParseFn<Key, Value, CacheDisabled>;
295
334
  /** Read directly from the localStorage (browser) or file (NodeJS) without triggering the `this.subject`. */
296
335
  readonly read: () => Map<Key, Value>;
297
336
  /** Search items */
@@ -323,41 +362,44 @@ interface IDataStorage<Key extends StorageKey, Value extends StorageValue, Cache
323
362
  */
324
363
  readonly sort: StorageSort<Key, Value>;
325
364
  /**
326
- * Callback function to customize the serialization of data to be stored to storage.
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.
327
376
  *
328
- * Useful when data needs to be sanitised before storing and/or remove circlar references.
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}.
329
380
  *
330
381
  * @example
382
+ * #### Sanitize data before saving
331
383
  * ```javascript
332
- * import fetch from '@superutils/fetch'
333
384
  * import { DataStorage } from '@superutils/rx'
334
- * import { LocalStorage } from 'node-localstorage'
335
385
  *
336
- * // Create a localStorage alternative for NodeJS that reads and writes to JSON files.
337
- * // This is not necessary for browsers
338
- * globalThis.localStorage = new LocalStorage('./data', 1e7)
339
- *
340
- * const storage = new DataStorage('products.json')
341
- * storage.stringify = data => Array
342
- * .from(data)
343
- * .map(([key, product]) => [
344
- * key,
345
- * { id: product.id, title: product.title } // only store what's needed
346
- * ])
347
- *
348
- * const { products } = await fetch('[DUMMYJSON-DOT-COM]/products)
349
- * const productsMap = result.products.map(p => [p.id, p])
350
- * storage.setAll(productsMap, true)
351
- * console.log(storage.getAll())
352
- * ```
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 })
353
395
  */
354
- readonly stringify?: StorageStringifyFn<Key, Value>;
396
+ readonly stringify?: StorageStringifyFn<Key, Value, CacheDisabled>;
355
397
  /** Convert list of items (Map) to 2D Array */
356
398
  readonly toArray: () => [Key, Value][];
357
399
  /** Convert list of items (Map) to JSON string of 2D Array */
358
400
  readonly toJSON: StorageToJSON<Key, Value>;
359
401
  /** Convert list of items into an object */
360
- readonly toObject: () => Record<Key, Value>;
402
+ readonly toObject: (data?: Map<Key, Value>) => Record<Key & string, Value>;
361
403
  /** Convert list of items (Map) to JSON string of 2D Array */
362
404
  readonly toString: () => string;
363
405
  /**
@@ -403,7 +445,7 @@ interface IDataStorage<Key extends StorageKey, Value extends StorageValue, Cache
403
445
  * ```
404
446
  */
405
447
  declare const forceUpdateCache$: Subject<string | boolean | string[]>;
406
- declare class DataStorage<Key extends StorageKey, Value extends StorageValue, CacheDisabled extends boolean = false> implements IDataStorage<Key, Value, CacheDisabled> {
448
+ declare class DataStorage<Key, Value, CacheDisabled extends boolean = false> implements IDataStorage<Key, Value, CacheDisabled> {
407
449
  readonly cacheDisabled: CacheDisabled;
408
450
  readonly delay: number;
409
451
  /** Debounce and throttle related options */
@@ -504,10 +546,59 @@ declare class DataStorage<Key extends StorageKey, Value extends StorageValue, Ca
504
546
  * ```
505
547
  */
506
548
  constructor(name?: string | null, options?: StorageOptions<Key, Value, CacheDisabled>);
507
- readonly clear: () => this;
508
- readonly delete: (keys: Key | Key[]) => this;
509
- readonly find: StorageFind<Key, Value>;
510
- readonly filter: StorageFilter<Key, Value>;
549
+ clear(): this;
550
+ delete(keys: Key | Key[]): this;
551
+ find(predicateOrOptions: Parameters<StorageFind<Key, Value>>[0]): Value | undefined;
552
+ filter<AsArray extends boolean = false>(...args: Parameters<StorageFilter<Key, Value, AsArray>>): AsArray extends true ? Value[] : Map<Key, Value>;
553
+ /**
554
+ * Creates a {@link DataStorage} instance initialized from a plain object.
555
+ *
556
+ * This factory method automatically configures `parse` and `stringify` logic to
557
+ * treat the underlying storage as a serialized object, while providing a
558
+ * type-safe Map-like interface for individual properties.
559
+ *
560
+ * @param name (optional) The name for the storage (e.g., localStorage key or filename).
561
+ * @param options (optional) Configuration options for the storage instance.
562
+ * @param options.initialValue (optional) An optional object to populate the storage if it's currently empty.
563
+ *
564
+ * @template T (optional) The structure of the object being stored. Can auto-infer from `options.initialValue`.
565
+ * @template CacheDisabled (optional) Literal type determining whether to disable in-memory caching.
566
+ *
567
+ * @returns A new DataStorage instance mapped to the object's keys and values.
568
+ *
569
+ * @example
570
+ * #### Store and access a User object
571
+ * ```typescript
572
+ * import { DataStorage } from '@superutils/rx'
573
+ *
574
+ * interface User {
575
+ * age: number;
576
+ * name: string;
577
+ * }
578
+ *
579
+ * const initialValue: User = {
580
+ * age: 99,
581
+ * name: 'Ninety Nine'
582
+ * }
583
+ *
584
+ * // Initialize storage from the object
585
+ * const storage = DataStorage.fromObject<User>('user-profile', { initialValue })
586
+ *
587
+ * // Keys are inferred from the User interface
588
+ * const name = storage.get('name') // Inferred as: string | undefined
589
+ * console.log(name) // Prints: 'Ninety Nine'
590
+ *
591
+ * // Update properties safely
592
+ * storage.set('age', 100)
593
+ *
594
+ * // Reconstruct the updated object
595
+ * const userObj = storage.toObject<User>()
596
+ * console.log(userObj) // { age: 100, name: 'Ninety Nine' }
597
+ * ```
598
+ */
599
+ static fromObject: <T extends object, CacheDisabled_1 extends boolean = false>(name?: string, options?: Omit<StorageOptions<keyof T, T[keyof T], CacheDisabled_1>, "initialValue"> & {
600
+ initialValue?: T;
601
+ }) => DataStorage<keyof T, T[keyof T], CacheDisabled_1>;
511
602
  /**
512
603
  * Trigger forced update of cached data from storage.
513
604
  *
@@ -528,29 +619,31 @@ declare class DataStorage<Key extends StorageKey, Value extends StorageValue, Ca
528
619
  * ```
529
620
  */
530
621
  static forceUpdateCache: (name: string | string[] | true) => void;
531
- readonly get: (key: Key) => Value | undefined;
532
- readonly getAll: (forceRead?: boolean) => Map<Key, Value>;
533
- readonly has: (key: Key) => boolean;
534
- readonly init: (initialValue?: Map<Key, Value>) => boolean;
535
- readonly keys: () => Key[];
536
- readonly map: StorageMap<Key, Value>;
537
- onChange?: StorageOnChangeFn<Key, Value>;
538
- onError?: StorageOnErrorFn;
539
- readonly parse?: StorageParseFn<Key, Value>;
540
- readonly read: () => Map<Key, Value>;
541
- readonly search: StorageSearch<Key, Value>;
542
- readonly set: (key: Key, value: Value) => this;
543
- readonly setAll: (data?: Map<Key, Value>, replace?: boolean) => this;
544
- readonly sort: StorageSort<Key, Value>;
545
- readonly stringify?: StorageStringifyFn<Key, Value>;
546
- readonly toArray: () => [Key, Value][];
547
- readonly toJSON: StorageToJSON<Key, Value>;
548
- readonly toObject: () => Record<Key, Value>;
549
- readonly toString: (data?: Map<Key, Value>) => string;
622
+ get(key: Key): Value | undefined;
623
+ getAll(forceRead?: boolean): Map<Key, Value>;
624
+ private handleForceUpdateCacheChange;
625
+ private handleSubjectChange;
626
+ has(key: Key): boolean;
627
+ init(initialValue?: Map<Key, Value>): boolean;
628
+ keys(): Key[];
629
+ map<T>(callback: Parameters<StorageMap<Key, Value, T>>[0]): T[];
630
+ onChange?: StorageOnChangeFn<Key, Value, CacheDisabled>;
631
+ onError?: StorageOnErrorFn<Key, Value, CacheDisabled>;
632
+ readonly parse?: StorageParseFn<Key, Value, CacheDisabled>;
633
+ read(): Map<Key, Value>;
634
+ search<MatchExact extends boolean = false, AsMap extends boolean = true>(options: Parameters<StorageSearch<Key, Value, MatchExact, AsMap>>[0]): AsMap extends true ? Map<Key, Value> : Value[];
635
+ set(key: Key, value: Value): this;
636
+ setAll(data?: Map<Key, Value>, replace?: boolean): this;
637
+ sort(...args: Parameters<StorageSort<Key, Value>>): Map<Key, Value>;
638
+ readonly stringify?: StorageStringifyFn<Key, Value, CacheDisabled>;
639
+ toArray(): [Key, Value][];
640
+ toJSON(...[replacer, spacing, data]: Parameters<StorageToJSON<Key, Value>>): string;
641
+ toObject<T extends object = object>(data?: Map<Key, Value>): T;
642
+ toString(data?: Map<Key, Value>): string;
550
643
  private triggerOnError;
551
- readonly unsubscribe: () => void;
552
- readonly values: () => Value[];
553
- readonly write: (data?: Map<Key, Value>) => boolean;
644
+ unsubscribe(): void;
645
+ values(): Value[];
646
+ write(data?: Map<Key, Value>): boolean;
554
647
  }
555
648
 
556
649
  /**
@@ -740,4 +833,4 @@ declare const isSubscriptionLike: (value: unknown, strict?: boolean) => value is
740
833
  */
741
834
  declare const unsubscribeAll: (unsub?: UnsubscribeCandidates, onError?: (err: unknown) => void) => void;
742
835
 
743
- export { type CopyRxSubjectOptions, DataStorage, type DelayOptions, type IDataStorage, IGNORE_UPDATE_SYMBOL, IntervalRunner, IntervalSubject, type OnErrorType, type OnResultType, type StorageCompact, type StorageFilter, type StorageFind, type StorageKey, type StorageMap, type StorageOnChangeFn, type StorageOnErrorFn, type StorageOptions, type StorageParseFn, type StorageSearch, type StorageSort, type StorageSortByComparator, type StorageSortByKey, type StorageSortByPropertyName, type StorageSortOptions, type StorageStringifyFn, type StorageToJSON, type StorageValue, type SubjectLike, type SubscriptionLike, type Unsubscribe, type UnsubscribeCandidates, type UnwrapSubjectValue, type ValueModifier, asPromise, copyRxSubject, forceUpdateCache$, isSubjectLike, isSubscriptionLike, type onBeforeExecType, unsubscribeAll };
836
+ export { type CopyRxSubjectOptions, DataStorage, type DelayOptions, type IDataStorage, IGNORE_UPDATE_SYMBOL, IntervalRunner, IntervalSubject, OnErrorType, type OnResultType, type StorageCompact, type StorageFilter, type StorageFind, type StorageMap, type StorageOnChangeFn, type StorageOnErrorFn, type StorageOptions, type StorageParseFn, type StorageSearch, type StorageSort, type StorageSortByComparator, type StorageSortByKey, type StorageSortByPropertyName, type StorageSortOptions, type StorageStringifyFn, type StorageToJSON, type SubjectLike, type SubscriptionLike, type Unsubscribe, type UnsubscribeCandidates, type UnwrapSubjectValue, type ValueModifier, asPromise, copyRxSubject, forceUpdateCache$, isSubjectLike, isSubscriptionLike, type onBeforeExecType, unsubscribeAll };