@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.js CHANGED
@@ -166,16 +166,30 @@ import {
166
166
  getKeys,
167
167
  getValues,
168
168
  isArr as isArr2,
169
- isFn as isFn5,
170
169
  isMap,
170
+ isObj as isObj2,
171
171
  isPositiveNumber as isPositiveNumber3,
172
172
  isStr,
173
173
  mapJoin,
174
+ objToMap,
174
175
  search,
175
176
  sort
176
177
  } from "@superutils/core";
178
+
179
+ // src/data-storage/types.ts
180
+ var OnErrorType = /* @__PURE__ */ ((OnErrorType2) => {
181
+ OnErrorType2["onChange"] = "onChange";
182
+ OnErrorType2["parse"] = "parse";
183
+ OnErrorType2["parse_json"] = "parse-json";
184
+ OnErrorType2["stringify"] = "stringify";
185
+ OnErrorType2["stringify_json"] = "stringify-json";
186
+ OnErrorType2["write"] = "write";
187
+ return OnErrorType2;
188
+ })(OnErrorType || {});
189
+
190
+ // src/data-storage/DataStorage.ts
177
191
  var forceUpdateCache$ = new Subject();
178
- var DataStorage = class {
192
+ var _DataStorage = class _DataStorage {
179
193
  /**
180
194
  * A wrapper for reading and writing to LocalStorage (browser) or JSON files (NodeJS),
181
195
  * providing a Map-like interface with advanced features like search, filtering, and sorting.
@@ -270,155 +284,30 @@ var DataStorage = class {
270
284
  subject: void 0,
271
285
  forceUpdateCache: void 0
272
286
  };
273
- this.clear = () => {
274
- this.setAll(/* @__PURE__ */ new Map(), true);
275
- return this;
276
- };
277
- this.delete = (keys) => {
278
- if (!isArr2(keys)) keys = [keys];
279
- const data = this.getAll();
280
- for (const k of keys) data.delete(k);
281
- this.setAll(data, true);
282
- return this;
287
+ this.handleForceUpdateCacheChange = (name) => {
288
+ const isTarget = !this.name ? false : isArr2(name) ? name.includes(this.name) : isStr(name) ? name === this.name : name === true;
289
+ if (!isTarget) return;
290
+ const newData = this.read();
291
+ this.subject.next(newData);
283
292
  };
284
- this.find = (predicateOrOptions) => find(
285
- this.getAll(),
286
- predicateOrOptions
287
- );
288
- this.filter = (predicate, limit, asArray, result) => filter(this.getAll(), predicate, limit, asArray, result);
289
- this.get = (key) => this.getAll().get(key);
290
- this.getAll = (forceRead = false) => {
293
+ this.handleSubjectChange = (data) => {
291
294
  var _a;
292
- const wasInitialized = this.initialized;
293
- if (!wasInitialized) this.init();
294
- const readFromStorage = this.cacheDisabled || this.name && forceRead;
295
- if (readFromStorage) {
296
- const data = this.read();
297
- const shouldTrigger = forceRead || !wasInitialized && !!data.size;
298
- shouldTrigger && this.subject.next(data);
299
- return data;
300
- }
301
- return (_a = this.subject) == null ? void 0 : _a.value;
302
- };
303
- this.has = (key) => this.getAll().has(key);
304
- this.init = (initialValue) => {
305
- var _a, _b;
306
- if (this.initialized) return false;
307
- this.initialized = true;
308
- this.subject instanceof BehaviorSubject && this.subject.next(this.read());
309
- unsubscribeAll_default(this.subscriptions);
310
- if (!this.cacheDisabled) {
311
- this.subscriptions.forceUpdateCache = forceUpdateCache$.subscribe(
312
- (refresh) => {
313
- const doRefresh = !this.name ? false : isArr2(refresh) ? refresh.includes(this.name) : isStr(refresh) ? refresh === this.name : refresh === true;
314
- if (!doRefresh) return;
315
- const newData = this.read();
316
- this.subject.next(newData);
317
- }
318
- );
319
- }
320
- !((_b = (_a = this.subject) == null ? void 0 : _a.value) == null ? void 0 : _b.size) && isMap(initialValue) && !!(initialValue == null ? void 0 : initialValue.size) && this.setAll(initialValue);
321
- const piped = this.subject.pipe(
322
- skip(this.cacheDisabled || !!(initialValue == null ? void 0 : initialValue.size) ? 0 : 1)
323
- );
324
- let handleChange = (data) => {
325
- if (!isMap(data)) return this.subject.next(/* @__PURE__ */ new Map());
326
- this.write(data);
327
- fallbackIfFails3(
328
- () => {
329
- var _a2;
330
- return (_a2 = this.onChange) == null ? void 0 : _a2.call(this, data);
331
- },
332
- [],
333
- this.triggerOnError("onChange")
334
- );
335
- };
336
- if (this.delay > 0)
337
- handleChange = deferred2(handleChange, this.delay, this.delayOptions);
338
- this.subscriptions.subject = piped.subscribe(handleChange);
339
- return true;
340
- };
341
- this.keys = () => getKeys(this.getAll());
342
- this.map = (callback) => this.toArray().map(
343
- ([key, value], index, data) => callback(value, key, data, index)
344
- );
345
- this.read = () => {
346
- var _a, _b;
347
- const dataStr = (_b = (_a = this.storage) == null ? void 0 : _a.getItem(this.name)) != null ? _b : "[]";
348
- const data = isFn5(this.parse) && fallbackIfFails3(
349
- this.parse,
350
- [dataStr],
351
- this.triggerOnError("parse")
295
+ if (!isMap(data)) return this.subject.next(/* @__PURE__ */ new Map());
296
+ this.write(data);
297
+ fallbackIfFails3(
298
+ (_a = this.onChange) == null ? void 0 : _a.bind(this),
299
+ [data],
300
+ this.triggerOnError("onChange" /* onChange */)
352
301
  );
353
- if (isMap(data)) return data;
354
- return new Map(
355
- fallbackIfFails3(
356
- () => JSON.parse(dataStr),
357
- [],
358
- this.triggerOnError("parse-json")
359
- )
360
- );
361
- };
362
- this.search = (options) => search(this.getAll(), options);
363
- this.set = (key, value) => {
364
- this.setAll(/* @__PURE__ */ new Map([[key, value]]), false);
365
- return this;
366
302
  };
367
- this.setAll = (data = /* @__PURE__ */ new Map(), replace = false) => {
368
- if (!isMap(data)) return this;
369
- data = replace ? data : mapJoin(this.getAll(), data);
370
- this.subject.next(new Map(data));
371
- return this;
372
- };
373
- this.sort = ((byKeyOrNameOrComparator, options) => {
374
- const result = sort(
375
- this.getAll(),
376
- byKeyOrNameOrComparator,
377
- options
378
- );
379
- (options == null ? void 0 : options.save) && this.setAll(result, true);
380
- return result;
381
- });
382
- this.toArray = () => getEntries(this.getAll());
383
- this.toJSON = (replacer, spacing = this.spaces, data = this.getAll()) => {
384
- const arr = Array.from(data);
385
- const str = fallbackIfFails3(
386
- () => {
387
- var _a;
388
- return (_a = this.stringify) == null ? void 0 : _a.call(this, data);
389
- },
390
- [],
391
- this.triggerOnError("stringify")
392
- );
393
- if (isStr(str)) return str;
394
- return fallbackIfFails3(
395
- () => JSON.stringify(arr, replacer, spacing),
396
- [],
397
- this.triggerOnError("stringify-json")
303
+ this.triggerOnError = (type, returnValue = void 0) => (err) => {
304
+ var _a;
305
+ fallbackIfFails3(
306
+ (_a = this.onError) == null ? void 0 : _a.bind(this),
307
+ [err, type],
308
+ ""
398
309
  );
399
- };
400
- this.toObject = () => {
401
- const obj = {};
402
- for (const [key, value] of this.getAll()) obj[key] = value;
403
- return obj;
404
- };
405
- this.toString = (data) => this.toJSON(void 0, void 0, data);
406
- this.triggerOnError = (type) => (err) => {
407
- this.onError && fallbackIfFails3(this.onError, [err, type], "");
408
- };
409
- this.unsubscribe = () => unsubscribeAll_default(this.subscriptions);
410
- this.values = () => getValues(this.getAll());
411
- this.write = (data) => {
412
- try {
413
- !this.initialized && this.init();
414
- data != null ? data : data = this.subject.value;
415
- if (!this.name || !this.storage || !isMap(data)) return false;
416
- this.storage.setItem(this.name, this.toString(data));
417
- return true;
418
- } catch (err) {
419
- this.triggerOnError("write")(err);
420
- return false;
421
- }
310
+ return returnValue;
422
311
  };
423
312
  const {
424
313
  cacheDisabled = false,
@@ -432,7 +321,7 @@ var DataStorage = class {
432
321
  storage,
433
322
  stringify
434
323
  } = options != null ? options : {};
435
- this.delay = cacheDisabled ? 0 : delay === 0 || isPositiveNumber3(delay) ? delay : 300;
324
+ this.delay = delay === 0 || isPositiveNumber3(delay) ? delay : 300;
436
325
  this.name = `${name != null ? name : ""}`.trim();
437
326
  this.onError = onError;
438
327
  this.onChange = onChange;
@@ -456,7 +345,221 @@ var DataStorage = class {
456
345
  get size() {
457
346
  return this.getAll().size;
458
347
  }
348
+ clear() {
349
+ this.setAll(/* @__PURE__ */ new Map(), true);
350
+ return this;
351
+ }
352
+ delete(keys) {
353
+ if (!isArr2(keys)) keys = [keys];
354
+ const data = this.getAll();
355
+ for (const k of keys) data.delete(k);
356
+ this.setAll(data, true);
357
+ return this;
358
+ }
359
+ find(predicateOrOptions) {
360
+ return find(
361
+ this.getAll(),
362
+ predicateOrOptions
363
+ );
364
+ }
365
+ filter(...args) {
366
+ return filter(this.getAll(), ...args);
367
+ }
368
+ get(key) {
369
+ return this.getAll().get(key);
370
+ }
371
+ getAll(forceRead = false) {
372
+ var _a, _b;
373
+ const wasInitialized = this.initialized;
374
+ if (!wasInitialized) this.init();
375
+ const readFromStorage = this.cacheDisabled || this.name && forceRead;
376
+ if (readFromStorage) {
377
+ const data = this.read();
378
+ const shouldTrigger = forceRead || !wasInitialized && !!data.size;
379
+ shouldTrigger && this.subject.next(data);
380
+ return data;
381
+ }
382
+ return (_b = (_a = this.subject) == null ? void 0 : _a.value) != null ? _b : /* @__PURE__ */ new Map();
383
+ }
384
+ has(key) {
385
+ return this.getAll().has(key);
386
+ }
387
+ init(initialValue) {
388
+ if (this.initialized) return false;
389
+ this.initialized = true;
390
+ let isEmpty = true;
391
+ if (!!(initialValue == null ? void 0 : initialValue.size) || !this.cacheDisabled) {
392
+ const existingValue = this.read();
393
+ if (existingValue.size) initialValue = existingValue;
394
+ isEmpty = this.cacheDisabled || existingValue.size === 0;
395
+ }
396
+ (initialValue == null ? void 0 : initialValue.size) && this.subject.next(initialValue);
397
+ unsubscribeAll_default(this.subscriptions);
398
+ if (!this.cacheDisabled) {
399
+ this.subscriptions.forceUpdateCache = forceUpdateCache$.subscribe(
400
+ this.handleForceUpdateCacheChange
401
+ );
402
+ }
403
+ this.subscriptions.subject = this.subject.pipe(skip(this.cacheDisabled || isEmpty ? 0 : 1)).subscribe(
404
+ !this.cacheDisabled && this.delay > 0 ? deferred2(this.handleSubjectChange, this.delay, {
405
+ thisArg: this,
406
+ ...this.delayOptions
407
+ }) : this.handleSubjectChange
408
+ );
409
+ return true;
410
+ }
411
+ keys() {
412
+ return getKeys(this.getAll());
413
+ }
414
+ map(callback) {
415
+ return this.toArray().map(
416
+ ([key, value], index, data) => callback(value, key, data, index)
417
+ );
418
+ }
419
+ read() {
420
+ var _a, _b, _c;
421
+ const dataStr = (_b = (_a = this.storage) == null ? void 0 : _a.getItem(this.name)) != null ? _b : "";
422
+ const parse = (_c = this.parse) == null ? void 0 : _c.bind(this);
423
+ const data = fallbackIfFails3(
424
+ parse,
425
+ [dataStr],
426
+ this.triggerOnError("parse" /* parse */)
427
+ );
428
+ if (isMap(data)) return data;
429
+ return new Map(
430
+ fallbackIfFails3(
431
+ () => JSON.parse(dataStr),
432
+ [],
433
+ this.triggerOnError("parse-json" /* parse_json */)
434
+ )
435
+ );
436
+ }
437
+ search(options) {
438
+ return search(this.getAll(), options);
439
+ }
440
+ set(key, value) {
441
+ this.setAll(/* @__PURE__ */ new Map([[key, value]]), false);
442
+ return this;
443
+ }
444
+ setAll(data = /* @__PURE__ */ new Map(), replace = false) {
445
+ if (!isMap(data)) return this;
446
+ data = replace ? data : mapJoin(this.getAll(), data);
447
+ this.subject.next(new Map(data));
448
+ return this;
449
+ }
450
+ sort(...args) {
451
+ var _a;
452
+ const result = sort(
453
+ this.getAll(),
454
+ args[0],
455
+ args[1]
456
+ );
457
+ ((_a = args[1]) == null ? void 0 : _a.save) && this.setAll(result, true);
458
+ return result;
459
+ }
460
+ toArray() {
461
+ return getEntries(this.getAll());
462
+ }
463
+ toJSON(...[replacer, spacing = this.spaces, data = this.getAll()]) {
464
+ var _a;
465
+ const stringify = (_a = this.stringify) == null ? void 0 : _a.bind(this);
466
+ const str = fallbackIfFails3(
467
+ stringify,
468
+ [data],
469
+ this.triggerOnError("stringify" /* stringify */)
470
+ );
471
+ if (isStr(str)) return str;
472
+ return fallbackIfFails3(
473
+ () => JSON.stringify(
474
+ Array.from(data),
475
+ replacer,
476
+ spacing
477
+ ),
478
+ [],
479
+ this.triggerOnError("stringify-json" /* stringify_json */, "")
480
+ );
481
+ }
482
+ toObject(data = ((_a) => (_a = this == null ? void 0 : this.getAll) == null ? void 0 : _a.call(this))()) {
483
+ const obj = {};
484
+ data = !isMap(data) ? /* @__PURE__ */ new Map() : data;
485
+ for (const [key, value] of data)
486
+ obj[key] = value;
487
+ return obj;
488
+ }
489
+ toString(data) {
490
+ return this.toJSON(void 0, void 0, data);
491
+ }
492
+ unsubscribe() {
493
+ return unsubscribeAll_default(this.subscriptions);
494
+ }
495
+ values() {
496
+ return getValues(this.getAll());
497
+ }
498
+ write(data) {
499
+ try {
500
+ !this.initialized && this.init();
501
+ data != null ? data : data = this.subject.value;
502
+ if (!this.name || !this.storage || !isMap(data)) return false;
503
+ this.storage.setItem(this.name, this.toString(data));
504
+ return true;
505
+ } catch (err) {
506
+ this.triggerOnError("write" /* write */)(err);
507
+ return false;
508
+ }
509
+ }
459
510
  };
511
+ /**
512
+ * Creates a {@link DataStorage} instance initialized from a plain object.
513
+ *
514
+ * This factory method automatically configures `parse` and `stringify` logic to
515
+ * treat the underlying storage as a serialized object, while providing a
516
+ * type-safe Map-like interface for individual properties.
517
+ *
518
+ * @param name (optional) The name for the storage (e.g., localStorage key or filename).
519
+ * @param options (optional) Configuration options for the storage instance.
520
+ * @param options.initialValue (optional) An optional object to populate the storage if it's currently empty.
521
+ *
522
+ * @template T (optional) The structure of the object being stored. Can auto-infer from `options.initialValue`.
523
+ * @template CacheDisabled (optional) Literal type determining whether to disable in-memory caching.
524
+ *
525
+ * @returns A new DataStorage instance mapped to the object's keys and values.
526
+ *
527
+ * @example
528
+ * #### Store and access a User object
529
+ * ```typescript
530
+ * import { DataStorage } from '@superutils/rx'
531
+ *
532
+ * interface User {
533
+ * age: number;
534
+ * name: string;
535
+ * }
536
+ *
537
+ * const initialValue: User = {
538
+ * age: 99,
539
+ * name: 'Ninety Nine'
540
+ * }
541
+ *
542
+ * // Initialize storage from the object
543
+ * const storage = DataStorage.fromObject<User>('user-profile', { initialValue })
544
+ *
545
+ * // Keys are inferred from the User interface
546
+ * const name = storage.get('name') // Inferred as: string | undefined
547
+ * console.log(name) // Prints: 'Ninety Nine'
548
+ *
549
+ * // Update properties safely
550
+ * storage.set('age', 100)
551
+ *
552
+ * // Reconstruct the updated object
553
+ * const userObj = storage.toObject<User>()
554
+ * console.log(userObj) // { age: 100, name: 'Ninety Nine' }
555
+ * ```
556
+ */
557
+ _DataStorage.fromObject = (name, options) => new _DataStorage(name, {
558
+ parse: (str) => objToMap(JSON.parse(str || "{}")),
559
+ stringify: (data) => JSON.stringify(_DataStorage.prototype.toObject(data)),
560
+ ...options,
561
+ initialValue: !isObj2(options == null ? void 0 : options.initialValue, true) ? options == null ? void 0 : options.initialValue : objToMap(options.initialValue)
562
+ });
460
563
  /**
461
564
  * Trigger forced update of cached data from storage.
462
565
  *
@@ -476,9 +579,10 @@ var DataStorage = class {
476
579
  * DataStorage.forceUpdateCache(true)
477
580
  * ```
478
581
  */
479
- DataStorage.forceUpdateCache = (name) => {
582
+ _DataStorage.forceUpdateCache = (name) => {
480
583
  forceUpdateCache$.next(name);
481
584
  };
585
+ var DataStorage = _DataStorage;
482
586
 
483
587
  // src/IntervalSubject.ts
484
588
  var IntervalSubject = class extends BehaviorSubject {
@@ -630,6 +734,7 @@ export {
630
734
  IGNORE_UPDATE_SYMBOL,
631
735
  IntervalRunner,
632
736
  IntervalSubject,
737
+ OnErrorType,
633
738
  Subject,
634
739
  Subscription,
635
740
  asPromise,
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "author": "Toufiqur Rahaman Chowdhury",
3
- "description": "RxJS utilities and hooks for React applications.",
3
+ "description": "A set of small, focused utilities for working with RxJS observables and subjects.",
4
4
  "dependencies": {
5
- "@superutils/core": "^1.2.9",
6
- "@superutils/promise": "^1.3.8",
5
+ "@superutils/core": "^1.2.10",
6
+ "@superutils/promise": "^1.3.9",
7
7
  "@types/react": "^18.0.0",
8
8
  "react": ">=16.8.0",
9
9
  "rxjs": "^7.8.2"
@@ -49,6 +49,6 @@
49
49
  "module": "./dist/index.js",
50
50
  "type": "module",
51
51
  "types": "./dist/index.d.ts",
52
- "version": "0.1.4",
53
- "gitHead": "1faa1c8734f9892abb9e07616b62abeeff1321b5"
52
+ "version": "0.1.5",
53
+ "gitHead": "9b77219f98125fd231d24c6e44299de7b99b679e"
54
54
  }