@superutils/rx 0.1.5 → 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/index.cjs CHANGED
@@ -44,6 +44,7 @@ __export(index_exports, {
44
44
  isObservable: () => import_rxjs.isObservable,
45
45
  isSubjectLike: () => isSubjectLike2,
46
46
  isSubscriptionLike: () => isSubscriptionLike,
47
+ objToMap: () => import_core7.objToMap,
47
48
  skip: () => import_rxjs.skip,
48
49
  unsubscribeAll: () => unsubscribeAll
49
50
  });
@@ -203,100 +204,36 @@ var OnErrorType = /* @__PURE__ */ ((OnErrorType2) => {
203
204
  // src/data-storage/DataStorage.ts
204
205
  var forceUpdateCache$ = new import_rxjs.Subject();
205
206
  var _DataStorage = class _DataStorage {
206
- /**
207
- * A wrapper for reading and writing to LocalStorage (browser) or JSON files (NodeJS),
208
- * providing a Map-like interface with advanced features like search, filtering, and sorting.
209
- *
210
- * #### Notes:
211
- * - **Performance**: `DataStorage` is optimized for small to medium datasets.
212
- * - For datasets > 1MB, consider increasing the `delay` option to reduce write frequency.
213
- * - It is **NOT** recommended for datasets larger than 3MB due to synchronous serialization costs.
214
- * - **RxJS Integration**: Built on RxJS for reactive data handling, though no prior RxJS knowledge is required.
215
- * - **Storage Behavior**:
216
- * - If `name` is omitted, the instance operates in-memory only and data is not persisted to storage.
217
- * - If `cacheDisabled` is `true`, data is not kept in memory; every read/write operation accesses the underlying
218
- * storage directly.
219
- *
220
- * @example
221
- * #### Browser Usage
222
- * ```javascript
223
- * import { DataStorage } from '@superutils/rx'
224
- * import fetch from '@superutils/fetch'
225
- *
226
- * const storage = new DataStorage('products')
227
- * const { products } = await fetch('[DUMMYJSON-DOT-COM]/products')
228
- * // save all items to storage
229
- * storage.setAll(
230
- * new Map(products.map(p => [p.id, p])), // convert to Map
231
- * )
232
- *
233
- * // print product with id `1`
234
- * console.log(storage.get(1))
235
- *
236
- * // search for items
237
- * const searchResult = storage.search({
238
- * query: { availabilityStatus: 'low' }
239
- * })
240
- * console.log(searchResult)
241
- * ```
242
- * @example
243
- * #### NodeJS Usage
244
- * ```javascript
245
- * import { DataStorage } from '@superutils/rx'
246
- * import fetch from '@superutils/fetch'
247
- * import { LocalStorage } from 'node-localstorage'
248
- *
249
- * // Add localStorage alternative for NodeJS that reads and writes to JSON files.
250
- * // This is not necessary for browsers.
251
- * globalThis.localStorage = new LocalStorage('./data', 1e7)
252
- *
253
- * const storage = new DataStorage('products')
254
- * const { products } = await fetch('[DUMMYJSON-DOT-COM]/products')
255
- * // save all items to storage
256
- * storage.setAll(
257
- * new Map(products.map(p => [p.id, p])), // convert to Map
258
- * )
259
- *
260
- * // print product with id `1`
261
- * console.log(storage.get(1))
262
- *
263
- * // search for items
264
- * const searchResult = storage.search({
265
- * query: { availabilityStatus: 'low' }
266
- * })
267
- * console.log(searchResult)
268
- * ```
269
- *
270
- * @example
271
- * #### Advanced: `onChange` and RxJS subject
272
- *
273
- * Internally, `DataStorage` uses RxJS subject which is exposed as `subject` property.
274
- * You can use this to subscribe to changes and do additional operations such as logging or sanitization etc.
275
- *
276
- * Alternatively, you can also set the `onChange` callback which is triggered whenever the subject changes and
277
- * does not require maintaining a subscription or knowledge of RxJS subject.
278
- *
279
- * ```javascript
280
- * import { DataStorage } from '@superutils/rx'
281
- *
282
- * const storage = new DataStorage('my-data')
283
- * const sub = storage.subject.subscribe(data => {
284
- * // Write to the database whenever data changes
285
- * console.log('Saving to database...', data)
286
- * })
287
- * // unsubscribe from subject
288
- * setTimeout(()=> sub.unsbuscribe(), 1000)
289
- *
290
- * // add an entry to storage
291
- * storage.set('bob', { age: 99, id: 'bob', name: 'Bob' })
292
- * ```
293
- */
294
207
  constructor(name, options) {
295
208
  this.initialized = false;
296
209
  this.subscriptions = {
297
210
  subject: void 0,
298
211
  forceUpdateCache: void 0
299
212
  };
213
+ this.clear = () => this.setAll(/* @__PURE__ */ new Map(), true);
214
+ this.delete = (keys) => {
215
+ if (!(0, import_core6.isArr)(keys)) keys = [keys];
216
+ const data = this.getAll();
217
+ for (const k of keys) data.delete(k);
218
+ this.setAll(data, true);
219
+ return this;
220
+ };
221
+ this.filter = (...args) => (0, import_core6.filter)(this.getAll(), ...args);
222
+ this.find = (predicateOrOptions) => (0, import_core6.find)(this.getAll(), predicateOrOptions);
223
+ this.get = (key) => this.getAll().get(key);
224
+ this.getAll = (forceRead = false) => {
225
+ var _a, _b;
226
+ const wasInitialized = this.initialized;
227
+ if (!wasInitialized) this.init();
228
+ const readFromStorage = this.cacheDisabled || this.name && forceRead;
229
+ if (readFromStorage) {
230
+ const data = this.read();
231
+ const shouldTrigger = forceRead || !wasInitialized && !!data.size;
232
+ shouldTrigger && this.subject.next(data);
233
+ return data;
234
+ }
235
+ return (_b = (_a = this.subject) == null ? void 0 : _a.value) != null ? _b : /* @__PURE__ */ new Map();
236
+ };
300
237
  this.handleForceUpdateCacheChange = (name) => {
301
238
  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
239
  if (!isTarget) return;
@@ -313,6 +250,107 @@ var _DataStorage = class _DataStorage {
313
250
  this.triggerOnError("onChange" /* onChange */)
314
251
  );
315
252
  };
253
+ this.has = (key) => this.getAll().has(key);
254
+ this.init = (initialValue) => {
255
+ var _a;
256
+ if (this.initialized) return false;
257
+ this.initialized = true;
258
+ let isEmpty = true;
259
+ if (!!(initialValue == null ? void 0 : initialValue.size) || !this.cacheDisabled) {
260
+ const dataStr = this.name ? (_a = this.storage) == null ? void 0 : _a.getItem(this.name) : null;
261
+ const existingValue = this.read(dataStr);
262
+ if ((0, import_core6.isDefined)(dataStr)) initialValue = existingValue;
263
+ isEmpty = this.cacheDisabled || existingValue.size === 0;
264
+ }
265
+ (initialValue == null ? void 0 : initialValue.size) && this.subject.next(initialValue);
266
+ unsubscribeAll_default(this.subscriptions);
267
+ if (!this.cacheDisabled) {
268
+ this.subscriptions.forceUpdateCache = forceUpdateCache$.subscribe(
269
+ this.handleForceUpdateCacheChange
270
+ );
271
+ }
272
+ this.subscriptions.subject = this.subject.pipe((0, import_rxjs.skip)(this.cacheDisabled || isEmpty ? 0 : 1)).subscribe(
273
+ !this.cacheDisabled && this.delay > 0 ? (0, import_core6.deferred)(this.handleSubjectChange, this.delay, {
274
+ thisArg: this,
275
+ ...this.delayOptions
276
+ }) : this.handleSubjectChange
277
+ );
278
+ return true;
279
+ };
280
+ this.keys = () => (0, import_core6.getKeys)(this.getAll());
281
+ this.map = (callback) => this.toArray().map(
282
+ ([key, value], index, entries) => callback(value, key, entries, index)
283
+ );
284
+ this.read = (dataStr = this.name ? ((_a) => (_a = this.storage) == null ? void 0 : _a.getItem(this.name))() : null) => {
285
+ var _a2;
286
+ if (!this.name) {
287
+ return (_a2 = this.subject.value) != null ? _a2 : /* @__PURE__ */ new Map();
288
+ }
289
+ const data = (0, import_core6.fallbackIfFails)(
290
+ (() => {
291
+ var _a3;
292
+ return (_a3 = this.parse) == null ? void 0 : _a3.call(this, dataStr);
293
+ }),
294
+ [],
295
+ this.triggerOnError("parse" /* parse */)
296
+ );
297
+ if ((0, import_core6.isMap)(data)) return data;
298
+ if (!(0, import_core6.isStr)(dataStr)) return /* @__PURE__ */ new Map();
299
+ return new Map(
300
+ (0, import_core6.fallbackIfFails)(
301
+ () => JSON.parse(dataStr),
302
+ [],
303
+ this.triggerOnError("parse-json" /* parse_json */)
304
+ )
305
+ );
306
+ };
307
+ this.search = (...args) => (0, import_core6.search)(this.getAll(), ...args);
308
+ this.set = (key, value) => this.setAll(/* @__PURE__ */ new Map([[key, value]]), false);
309
+ this.setAll = (data, replace = false) => {
310
+ if (!(0, import_core6.isMap)(data)) return this;
311
+ data = replace ? data : (0, import_core6.mapJoin)(this.getAll(), data);
312
+ this.subject.next(new Map(data));
313
+ return this;
314
+ };
315
+ this.sort = (...args) => {
316
+ var _a;
317
+ const result = (0, import_core6.sort)(
318
+ this.getAll(),
319
+ args[0],
320
+ args[1]
321
+ );
322
+ ((_a = args[1]) == null ? void 0 : _a.save) && this.setAll(result, true);
323
+ return result;
324
+ };
325
+ this.toArray = () => (0, import_core6.getEntries)(this.getAll());
326
+ this.toJSON = (replacer, spacing = this.spaces, data = this.getAll()) => {
327
+ const str = (0, import_core6.fallbackIfFails)(
328
+ (() => {
329
+ var _a;
330
+ return (_a = this.stringify) == null ? void 0 : _a.call(this, data);
331
+ }),
332
+ [],
333
+ this.triggerOnError("stringify" /* stringify */)
334
+ );
335
+ if ((0, import_core6.isStr)(str)) return str;
336
+ return (0, import_core6.fallbackIfFails)(
337
+ () => JSON.stringify(
338
+ Array.from(data),
339
+ replacer,
340
+ spacing
341
+ ),
342
+ [],
343
+ this.triggerOnError("stringify-json" /* stringify_json */, "")
344
+ );
345
+ };
346
+ this.toObject = (data = this.getAll()) => {
347
+ const obj = {};
348
+ if (!(0, import_core6.isMap)(data)) return obj;
349
+ for (const [key, value] of data)
350
+ obj[key] = value;
351
+ return obj;
352
+ };
353
+ this.toString = (data = this.getAll()) => this.toJSON(void 0, void 0, data);
316
354
  this.triggerOnError = (type, returnValue = void 0) => (err) => {
317
355
  var _a;
318
356
  (0, import_core6.fallbackIfFails)(
@@ -322,6 +360,21 @@ var _DataStorage = class _DataStorage {
322
360
  );
323
361
  return returnValue;
324
362
  };
363
+ this.unsubscribe = () => unsubscribeAll_default(this.subscriptions);
364
+ this.values = () => (0, import_core6.getValues)(this.getAll());
365
+ this.write = (data) => {
366
+ var _a;
367
+ try {
368
+ !this.initialized && this.init();
369
+ const finalData = data != null ? data : (_a = this.subject) == null ? void 0 : _a.value;
370
+ if (!this.name || !this.storage || !(0, import_core6.isMap)(finalData)) return false;
371
+ this.storage.setItem(this.name, this.toString(finalData));
372
+ return true;
373
+ } catch (err) {
374
+ this.triggerOnError("write" /* write */)(err);
375
+ return false;
376
+ }
377
+ };
325
378
  const {
326
379
  cacheDisabled = false,
327
380
  delay,
@@ -358,168 +411,6 @@ var _DataStorage = class _DataStorage {
358
411
  get size() {
359
412
  return this.getAll().size;
360
413
  }
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
- }
523
414
  };
524
415
  /**
525
416
  * Creates a {@link DataStorage} instance initialized from a plain object.
@@ -568,35 +459,30 @@ var _DataStorage = class _DataStorage {
568
459
  * ```
569
460
  */
570
461
  _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)),
462
+ parse: (str) => (0, import_core6.objToMap)(JSON.parse(str != null ? str : "{}")),
463
+ stringify: function(data) {
464
+ return JSON.stringify(this.toObject(data));
465
+ },
573
466
  ...options,
574
467
  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
468
  });
576
469
  /**
577
470
  * Trigger forced update of cached data from storage.
578
471
  *
579
- * @param name determines which storage instances to be updated.
472
+ * @param name determines which cache-enabled storage instances to be updated.
580
473
  * - name (`string` | `string[]`): update all instances with a specific name(s)
581
474
  * - global (`true`): update all instances globally
582
475
  *
583
- * @example
584
- * ```javascript
585
- * import { DataStorage } from '@superutils/rx'
586
- *
587
- * // Update all DataStorage instances with specific name(s)
588
- * const name = 'products'
589
- * DataStorage.forceUpdateCache([name])
590
- *
591
- * // Update every single instance of DataStorage that uses storage (has a "name")
592
- * DataStorage.forceUpdateCache(true)
593
- * ```
476
+ * See {@link forceUpdateCache$} for more details.
594
477
  */
595
478
  _DataStorage.forceUpdateCache = (name) => {
596
479
  forceUpdateCache$.next(name);
597
480
  };
598
481
  var DataStorage = _DataStorage;
599
482
 
483
+ // src/data-storage/index.ts
484
+ var import_core7 = require("@superutils/core");
485
+
600
486
  // src/IntervalSubject.ts
601
487
  var IntervalSubject = class extends import_rxjs.BehaviorSubject {
602
488
  constructor(autoStart, _delay = 1e3, initialValue = 0, incrementBy = 1) {
@@ -641,7 +527,7 @@ var IntervalSubject = class extends import_rxjs.BehaviorSubject {
641
527
  };
642
528
 
643
529
  // src/IntervalRunner.ts
644
- var import_core7 = require("@superutils/core");
530
+ var import_core8 = require("@superutils/core");
645
531
  var IntervalRunner = class {
646
532
  constructor(taskFn, taskArgs, intervalMs, sequential = true, preExecute = true) {
647
533
  this.taskFn = taskFn;
@@ -668,7 +554,7 @@ var IntervalRunner = class {
668
554
  } catch (_err) {
669
555
  err = _err;
670
556
  }
671
- (0, import_core7.fallbackIfFails)(
557
+ (0, import_core8.fallbackIfFails)(
672
558
  this.onResult,
673
559
  [err != null ? err : null, result, this.runCount, once],
674
560
  void 0
@@ -720,7 +606,7 @@ var IntervalRunner = class {
720
606
  delayMs = Math.max((_a = newDelayMs != null ? newDelayMs : delayMs) != null ? _a : 0, this.minIntervalMs);
721
607
  this.clearInterval();
722
608
  const preExec = this.lastResult === void 0 && this.preExecute;
723
- preExec && this.executeTask().catch(import_core7.noop);
609
+ preExec && this.executeTask().catch(import_core8.noop);
724
610
  this.idInterval = !this.sequential ? setInterval(this.executeTask, delayMs) : !preExec ? setTimeout(this.executeTask, delayMs) : void 0;
725
611
  });
726
612
  return true;
@@ -757,6 +643,7 @@ var IntervalRunner = class {
757
643
  isObservable,
758
644
  isSubjectLike,
759
645
  isSubscriptionLike,
646
+ objToMap,
760
647
  skip,
761
648
  unsubscribeAll
762
649
  });