@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.cjs CHANGED
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  IGNORE_UPDATE_SYMBOL: () => IGNORE_UPDATE_SYMBOL,
36
36
  IntervalRunner: () => IntervalRunner,
37
37
  IntervalSubject: () => IntervalSubject,
38
+ OnErrorType: () => OnErrorType,
38
39
  Subject: () => import_rxjs.Subject,
39
40
  Subscription: () => import_rxjs.Subscription,
40
41
  asPromise: () => asPromise,
@@ -187,8 +188,21 @@ copyRxSubject.IGNORE_UPDATE_SYMBOL = IGNORE_UPDATE_SYMBOL;
187
188
 
188
189
  // src/data-storage/DataStorage.ts
189
190
  var import_core6 = require("@superutils/core");
191
+
192
+ // src/data-storage/types.ts
193
+ var OnErrorType = /* @__PURE__ */ ((OnErrorType2) => {
194
+ OnErrorType2["onChange"] = "onChange";
195
+ OnErrorType2["parse"] = "parse";
196
+ OnErrorType2["parse_json"] = "parse-json";
197
+ OnErrorType2["stringify"] = "stringify";
198
+ OnErrorType2["stringify_json"] = "stringify-json";
199
+ OnErrorType2["write"] = "write";
200
+ return OnErrorType2;
201
+ })(OnErrorType || {});
202
+
203
+ // src/data-storage/DataStorage.ts
190
204
  var forceUpdateCache$ = new import_rxjs.Subject();
191
- var DataStorage = class {
205
+ var _DataStorage = class _DataStorage {
192
206
  /**
193
207
  * A wrapper for reading and writing to LocalStorage (browser) or JSON files (NodeJS),
194
208
  * providing a Map-like interface with advanced features like search, filtering, and sorting.
@@ -283,155 +297,30 @@ var DataStorage = class {
283
297
  subject: void 0,
284
298
  forceUpdateCache: void 0
285
299
  };
286
- this.clear = () => {
287
- this.setAll(/* @__PURE__ */ new Map(), true);
288
- return this;
289
- };
290
- this.delete = (keys) => {
291
- if (!(0, import_core6.isArr)(keys)) keys = [keys];
292
- const data = this.getAll();
293
- for (const k of keys) data.delete(k);
294
- this.setAll(data, true);
295
- return this;
300
+ this.handleForceUpdateCacheChange = (name) => {
301
+ const isTarget = !this.name ? false : (0, import_core6.isArr)(name) ? name.includes(this.name) : (0, import_core6.isStr)(name) ? name === this.name : name === true;
302
+ if (!isTarget) return;
303
+ const newData = this.read();
304
+ this.subject.next(newData);
296
305
  };
297
- this.find = (predicateOrOptions) => (0, import_core6.find)(
298
- this.getAll(),
299
- predicateOrOptions
300
- );
301
- this.filter = (predicate, limit, asArray, result) => (0, import_core6.filter)(this.getAll(), predicate, limit, asArray, result);
302
- this.get = (key) => this.getAll().get(key);
303
- this.getAll = (forceRead = false) => {
306
+ this.handleSubjectChange = (data) => {
304
307
  var _a;
305
- const wasInitialized = this.initialized;
306
- if (!wasInitialized) this.init();
307
- const readFromStorage = this.cacheDisabled || this.name && forceRead;
308
- if (readFromStorage) {
309
- const data = this.read();
310
- const shouldTrigger = forceRead || !wasInitialized && !!data.size;
311
- shouldTrigger && this.subject.next(data);
312
- return data;
313
- }
314
- return (_a = this.subject) == null ? void 0 : _a.value;
315
- };
316
- this.has = (key) => this.getAll().has(key);
317
- this.init = (initialValue) => {
318
- var _a, _b;
319
- if (this.initialized) return false;
320
- this.initialized = true;
321
- this.subject instanceof import_rxjs.BehaviorSubject && this.subject.next(this.read());
322
- unsubscribeAll_default(this.subscriptions);
323
- if (!this.cacheDisabled) {
324
- this.subscriptions.forceUpdateCache = forceUpdateCache$.subscribe(
325
- (refresh) => {
326
- const doRefresh = !this.name ? false : (0, import_core6.isArr)(refresh) ? refresh.includes(this.name) : (0, import_core6.isStr)(refresh) ? refresh === this.name : refresh === true;
327
- if (!doRefresh) return;
328
- const newData = this.read();
329
- this.subject.next(newData);
330
- }
331
- );
332
- }
333
- !((_b = (_a = this.subject) == null ? void 0 : _a.value) == null ? void 0 : _b.size) && (0, import_core6.isMap)(initialValue) && !!(initialValue == null ? void 0 : initialValue.size) && this.setAll(initialValue);
334
- const piped = this.subject.pipe(
335
- (0, import_rxjs.skip)(this.cacheDisabled || !!(initialValue == null ? void 0 : initialValue.size) ? 0 : 1)
336
- );
337
- let handleChange = (data) => {
338
- if (!(0, import_core6.isMap)(data)) return this.subject.next(/* @__PURE__ */ new Map());
339
- this.write(data);
340
- (0, import_core6.fallbackIfFails)(
341
- () => {
342
- var _a2;
343
- return (_a2 = this.onChange) == null ? void 0 : _a2.call(this, data);
344
- },
345
- [],
346
- this.triggerOnError("onChange")
347
- );
348
- };
349
- if (this.delay > 0)
350
- handleChange = (0, import_core6.deferred)(handleChange, this.delay, this.delayOptions);
351
- this.subscriptions.subject = piped.subscribe(handleChange);
352
- return true;
353
- };
354
- this.keys = () => (0, import_core6.getKeys)(this.getAll());
355
- this.map = (callback) => this.toArray().map(
356
- ([key, value], index, data) => callback(value, key, data, index)
357
- );
358
- this.read = () => {
359
- var _a, _b;
360
- const dataStr = (_b = (_a = this.storage) == null ? void 0 : _a.getItem(this.name)) != null ? _b : "[]";
361
- const data = (0, import_core6.isFn)(this.parse) && (0, import_core6.fallbackIfFails)(
362
- this.parse,
363
- [dataStr],
364
- this.triggerOnError("parse")
308
+ if (!(0, import_core6.isMap)(data)) return this.subject.next(/* @__PURE__ */ new Map());
309
+ this.write(data);
310
+ (0, import_core6.fallbackIfFails)(
311
+ (_a = this.onChange) == null ? void 0 : _a.bind(this),
312
+ [data],
313
+ this.triggerOnError("onChange" /* onChange */)
365
314
  );
366
- if ((0, import_core6.isMap)(data)) return data;
367
- return new Map(
368
- (0, import_core6.fallbackIfFails)(
369
- () => JSON.parse(dataStr),
370
- [],
371
- this.triggerOnError("parse-json")
372
- )
373
- );
374
- };
375
- this.search = (options) => (0, import_core6.search)(this.getAll(), options);
376
- this.set = (key, value) => {
377
- this.setAll(/* @__PURE__ */ new Map([[key, value]]), false);
378
- return this;
379
315
  };
380
- this.setAll = (data = /* @__PURE__ */ new Map(), replace = false) => {
381
- if (!(0, import_core6.isMap)(data)) return this;
382
- data = replace ? data : (0, import_core6.mapJoin)(this.getAll(), data);
383
- this.subject.next(new Map(data));
384
- return this;
385
- };
386
- this.sort = ((byKeyOrNameOrComparator, options) => {
387
- const result = (0, import_core6.sort)(
388
- this.getAll(),
389
- byKeyOrNameOrComparator,
390
- options
391
- );
392
- (options == null ? void 0 : options.save) && this.setAll(result, true);
393
- return result;
394
- });
395
- this.toArray = () => (0, import_core6.getEntries)(this.getAll());
396
- this.toJSON = (replacer, spacing = this.spaces, data = this.getAll()) => {
397
- const arr = Array.from(data);
398
- const str = (0, import_core6.fallbackIfFails)(
399
- () => {
400
- var _a;
401
- return (_a = this.stringify) == null ? void 0 : _a.call(this, data);
402
- },
403
- [],
404
- this.triggerOnError("stringify")
405
- );
406
- if ((0, import_core6.isStr)(str)) return str;
407
- return (0, import_core6.fallbackIfFails)(
408
- () => JSON.stringify(arr, replacer, spacing),
409
- [],
410
- this.triggerOnError("stringify-json")
316
+ this.triggerOnError = (type, returnValue = void 0) => (err) => {
317
+ var _a;
318
+ (0, import_core6.fallbackIfFails)(
319
+ (_a = this.onError) == null ? void 0 : _a.bind(this),
320
+ [err, type],
321
+ ""
411
322
  );
412
- };
413
- this.toObject = () => {
414
- const obj = {};
415
- for (const [key, value] of this.getAll()) obj[key] = value;
416
- return obj;
417
- };
418
- this.toString = (data) => this.toJSON(void 0, void 0, data);
419
- this.triggerOnError = (type) => (err) => {
420
- this.onError && (0, import_core6.fallbackIfFails)(this.onError, [err, type], "");
421
- };
422
- this.unsubscribe = () => unsubscribeAll_default(this.subscriptions);
423
- this.values = () => (0, import_core6.getValues)(this.getAll());
424
- this.write = (data) => {
425
- try {
426
- !this.initialized && this.init();
427
- data != null ? data : data = this.subject.value;
428
- if (!this.name || !this.storage || !(0, import_core6.isMap)(data)) return false;
429
- this.storage.setItem(this.name, this.toString(data));
430
- return true;
431
- } catch (err) {
432
- this.triggerOnError("write")(err);
433
- return false;
434
- }
323
+ return returnValue;
435
324
  };
436
325
  const {
437
326
  cacheDisabled = false,
@@ -445,7 +334,7 @@ var DataStorage = class {
445
334
  storage,
446
335
  stringify
447
336
  } = options != null ? options : {};
448
- this.delay = cacheDisabled ? 0 : delay === 0 || (0, import_core6.isPositiveNumber)(delay) ? delay : 300;
337
+ this.delay = delay === 0 || (0, import_core6.isPositiveNumber)(delay) ? delay : 300;
449
338
  this.name = `${name != null ? name : ""}`.trim();
450
339
  this.onError = onError;
451
340
  this.onChange = onChange;
@@ -469,7 +358,221 @@ var DataStorage = class {
469
358
  get size() {
470
359
  return this.getAll().size;
471
360
  }
361
+ clear() {
362
+ this.setAll(/* @__PURE__ */ new Map(), true);
363
+ return this;
364
+ }
365
+ delete(keys) {
366
+ if (!(0, import_core6.isArr)(keys)) keys = [keys];
367
+ const data = this.getAll();
368
+ for (const k of keys) data.delete(k);
369
+ this.setAll(data, true);
370
+ return this;
371
+ }
372
+ find(predicateOrOptions) {
373
+ return (0, import_core6.find)(
374
+ this.getAll(),
375
+ predicateOrOptions
376
+ );
377
+ }
378
+ filter(...args) {
379
+ return (0, import_core6.filter)(this.getAll(), ...args);
380
+ }
381
+ get(key) {
382
+ return this.getAll().get(key);
383
+ }
384
+ getAll(forceRead = false) {
385
+ var _a, _b;
386
+ const wasInitialized = this.initialized;
387
+ if (!wasInitialized) this.init();
388
+ const readFromStorage = this.cacheDisabled || this.name && forceRead;
389
+ if (readFromStorage) {
390
+ const data = this.read();
391
+ const shouldTrigger = forceRead || !wasInitialized && !!data.size;
392
+ shouldTrigger && this.subject.next(data);
393
+ return data;
394
+ }
395
+ return (_b = (_a = this.subject) == null ? void 0 : _a.value) != null ? _b : /* @__PURE__ */ new Map();
396
+ }
397
+ has(key) {
398
+ return this.getAll().has(key);
399
+ }
400
+ init(initialValue) {
401
+ if (this.initialized) return false;
402
+ this.initialized = true;
403
+ let isEmpty = true;
404
+ if (!!(initialValue == null ? void 0 : initialValue.size) || !this.cacheDisabled) {
405
+ const existingValue = this.read();
406
+ if (existingValue.size) initialValue = existingValue;
407
+ isEmpty = this.cacheDisabled || existingValue.size === 0;
408
+ }
409
+ (initialValue == null ? void 0 : initialValue.size) && this.subject.next(initialValue);
410
+ unsubscribeAll_default(this.subscriptions);
411
+ if (!this.cacheDisabled) {
412
+ this.subscriptions.forceUpdateCache = forceUpdateCache$.subscribe(
413
+ this.handleForceUpdateCacheChange
414
+ );
415
+ }
416
+ this.subscriptions.subject = this.subject.pipe((0, import_rxjs.skip)(this.cacheDisabled || isEmpty ? 0 : 1)).subscribe(
417
+ !this.cacheDisabled && this.delay > 0 ? (0, import_core6.deferred)(this.handleSubjectChange, this.delay, {
418
+ thisArg: this,
419
+ ...this.delayOptions
420
+ }) : this.handleSubjectChange
421
+ );
422
+ return true;
423
+ }
424
+ keys() {
425
+ return (0, import_core6.getKeys)(this.getAll());
426
+ }
427
+ map(callback) {
428
+ return this.toArray().map(
429
+ ([key, value], index, data) => callback(value, key, data, index)
430
+ );
431
+ }
432
+ read() {
433
+ var _a, _b, _c;
434
+ const dataStr = (_b = (_a = this.storage) == null ? void 0 : _a.getItem(this.name)) != null ? _b : "";
435
+ const parse = (_c = this.parse) == null ? void 0 : _c.bind(this);
436
+ const data = (0, import_core6.fallbackIfFails)(
437
+ parse,
438
+ [dataStr],
439
+ this.triggerOnError("parse" /* parse */)
440
+ );
441
+ if ((0, import_core6.isMap)(data)) return data;
442
+ return new Map(
443
+ (0, import_core6.fallbackIfFails)(
444
+ () => JSON.parse(dataStr),
445
+ [],
446
+ this.triggerOnError("parse-json" /* parse_json */)
447
+ )
448
+ );
449
+ }
450
+ search(options) {
451
+ return (0, import_core6.search)(this.getAll(), options);
452
+ }
453
+ set(key, value) {
454
+ this.setAll(/* @__PURE__ */ new Map([[key, value]]), false);
455
+ return this;
456
+ }
457
+ setAll(data = /* @__PURE__ */ new Map(), replace = false) {
458
+ if (!(0, import_core6.isMap)(data)) return this;
459
+ data = replace ? data : (0, import_core6.mapJoin)(this.getAll(), data);
460
+ this.subject.next(new Map(data));
461
+ return this;
462
+ }
463
+ sort(...args) {
464
+ var _a;
465
+ const result = (0, import_core6.sort)(
466
+ this.getAll(),
467
+ args[0],
468
+ args[1]
469
+ );
470
+ ((_a = args[1]) == null ? void 0 : _a.save) && this.setAll(result, true);
471
+ return result;
472
+ }
473
+ toArray() {
474
+ return (0, import_core6.getEntries)(this.getAll());
475
+ }
476
+ toJSON(...[replacer, spacing = this.spaces, data = this.getAll()]) {
477
+ var _a;
478
+ const stringify = (_a = this.stringify) == null ? void 0 : _a.bind(this);
479
+ const str = (0, import_core6.fallbackIfFails)(
480
+ stringify,
481
+ [data],
482
+ this.triggerOnError("stringify" /* stringify */)
483
+ );
484
+ if ((0, import_core6.isStr)(str)) return str;
485
+ return (0, import_core6.fallbackIfFails)(
486
+ () => JSON.stringify(
487
+ Array.from(data),
488
+ replacer,
489
+ spacing
490
+ ),
491
+ [],
492
+ this.triggerOnError("stringify-json" /* stringify_json */, "")
493
+ );
494
+ }
495
+ toObject(data = ((_a) => (_a = this == null ? void 0 : this.getAll) == null ? void 0 : _a.call(this))()) {
496
+ const obj = {};
497
+ data = !(0, import_core6.isMap)(data) ? /* @__PURE__ */ new Map() : data;
498
+ for (const [key, value] of data)
499
+ obj[key] = value;
500
+ return obj;
501
+ }
502
+ toString(data) {
503
+ return this.toJSON(void 0, void 0, data);
504
+ }
505
+ unsubscribe() {
506
+ return unsubscribeAll_default(this.subscriptions);
507
+ }
508
+ values() {
509
+ return (0, import_core6.getValues)(this.getAll());
510
+ }
511
+ write(data) {
512
+ try {
513
+ !this.initialized && this.init();
514
+ data != null ? data : data = this.subject.value;
515
+ if (!this.name || !this.storage || !(0, import_core6.isMap)(data)) return false;
516
+ this.storage.setItem(this.name, this.toString(data));
517
+ return true;
518
+ } catch (err) {
519
+ this.triggerOnError("write" /* write */)(err);
520
+ return false;
521
+ }
522
+ }
472
523
  };
524
+ /**
525
+ * Creates a {@link DataStorage} instance initialized from a plain object.
526
+ *
527
+ * This factory method automatically configures `parse` and `stringify` logic to
528
+ * treat the underlying storage as a serialized object, while providing a
529
+ * type-safe Map-like interface for individual properties.
530
+ *
531
+ * @param name (optional) The name for the storage (e.g., localStorage key or filename).
532
+ * @param options (optional) Configuration options for the storage instance.
533
+ * @param options.initialValue (optional) An optional object to populate the storage if it's currently empty.
534
+ *
535
+ * @template T (optional) The structure of the object being stored. Can auto-infer from `options.initialValue`.
536
+ * @template CacheDisabled (optional) Literal type determining whether to disable in-memory caching.
537
+ *
538
+ * @returns A new DataStorage instance mapped to the object's keys and values.
539
+ *
540
+ * @example
541
+ * #### Store and access a User object
542
+ * ```typescript
543
+ * import { DataStorage } from '@superutils/rx'
544
+ *
545
+ * interface User {
546
+ * age: number;
547
+ * name: string;
548
+ * }
549
+ *
550
+ * const initialValue: User = {
551
+ * age: 99,
552
+ * name: 'Ninety Nine'
553
+ * }
554
+ *
555
+ * // Initialize storage from the object
556
+ * const storage = DataStorage.fromObject<User>('user-profile', { initialValue })
557
+ *
558
+ * // Keys are inferred from the User interface
559
+ * const name = storage.get('name') // Inferred as: string | undefined
560
+ * console.log(name) // Prints: 'Ninety Nine'
561
+ *
562
+ * // Update properties safely
563
+ * storage.set('age', 100)
564
+ *
565
+ * // Reconstruct the updated object
566
+ * const userObj = storage.toObject<User>()
567
+ * console.log(userObj) // { age: 100, name: 'Ninety Nine' }
568
+ * ```
569
+ */
570
+ _DataStorage.fromObject = (name, options) => new _DataStorage(name, {
571
+ parse: (str) => (0, import_core6.objToMap)(JSON.parse(str || "{}")),
572
+ stringify: (data) => JSON.stringify(_DataStorage.prototype.toObject(data)),
573
+ ...options,
574
+ initialValue: !(0, import_core6.isObj)(options == null ? void 0 : options.initialValue, true) ? options == null ? void 0 : options.initialValue : (0, import_core6.objToMap)(options.initialValue)
575
+ });
473
576
  /**
474
577
  * Trigger forced update of cached data from storage.
475
578
  *
@@ -489,9 +592,10 @@ var DataStorage = class {
489
592
  * DataStorage.forceUpdateCache(true)
490
593
  * ```
491
594
  */
492
- DataStorage.forceUpdateCache = (name) => {
595
+ _DataStorage.forceUpdateCache = (name) => {
493
596
  forceUpdateCache$.next(name);
494
597
  };
598
+ var DataStorage = _DataStorage;
495
599
 
496
600
  // src/IntervalSubject.ts
497
601
  var IntervalSubject = class extends import_rxjs.BehaviorSubject {
@@ -644,6 +748,7 @@ var IntervalRunner = class {
644
748
  IGNORE_UPDATE_SYMBOL,
645
749
  IntervalRunner,
646
750
  IntervalSubject,
751
+ OnErrorType,
647
752
  Subject,
648
753
  Subscription,
649
754
  asPromise,