@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.js CHANGED
@@ -166,6 +166,7 @@ import {
166
166
  getKeys,
167
167
  getValues,
168
168
  isArr as isArr2,
169
+ isDefined,
169
170
  isMap,
170
171
  isObj as isObj2,
171
172
  isPositiveNumber as isPositiveNumber3,
@@ -190,100 +191,36 @@ var OnErrorType = /* @__PURE__ */ ((OnErrorType2) => {
190
191
  // src/data-storage/DataStorage.ts
191
192
  var forceUpdateCache$ = new Subject();
192
193
  var _DataStorage = class _DataStorage {
193
- /**
194
- * A wrapper for reading and writing to LocalStorage (browser) or JSON files (NodeJS),
195
- * providing a Map-like interface with advanced features like search, filtering, and sorting.
196
- *
197
- * #### Notes:
198
- * - **Performance**: `DataStorage` is optimized for small to medium datasets.
199
- * - For datasets > 1MB, consider increasing the `delay` option to reduce write frequency.
200
- * - It is **NOT** recommended for datasets larger than 3MB due to synchronous serialization costs.
201
- * - **RxJS Integration**: Built on RxJS for reactive data handling, though no prior RxJS knowledge is required.
202
- * - **Storage Behavior**:
203
- * - If `name` is omitted, the instance operates in-memory only and data is not persisted to storage.
204
- * - If `cacheDisabled` is `true`, data is not kept in memory; every read/write operation accesses the underlying
205
- * storage directly.
206
- *
207
- * @example
208
- * #### Browser Usage
209
- * ```javascript
210
- * import { DataStorage } from '@superutils/rx'
211
- * import fetch from '@superutils/fetch'
212
- *
213
- * const storage = new DataStorage('products')
214
- * const { products } = await fetch('[DUMMYJSON-DOT-COM]/products')
215
- * // save all items to storage
216
- * storage.setAll(
217
- * new Map(products.map(p => [p.id, p])), // convert to Map
218
- * )
219
- *
220
- * // print product with id `1`
221
- * console.log(storage.get(1))
222
- *
223
- * // search for items
224
- * const searchResult = storage.search({
225
- * query: { availabilityStatus: 'low' }
226
- * })
227
- * console.log(searchResult)
228
- * ```
229
- * @example
230
- * #### NodeJS Usage
231
- * ```javascript
232
- * import { DataStorage } from '@superutils/rx'
233
- * import fetch from '@superutils/fetch'
234
- * import { LocalStorage } from 'node-localstorage'
235
- *
236
- * // Add localStorage alternative for NodeJS that reads and writes to JSON files.
237
- * // This is not necessary for browsers.
238
- * globalThis.localStorage = new LocalStorage('./data', 1e7)
239
- *
240
- * const storage = new DataStorage('products')
241
- * const { products } = await fetch('[DUMMYJSON-DOT-COM]/products')
242
- * // save all items to storage
243
- * storage.setAll(
244
- * new Map(products.map(p => [p.id, p])), // convert to Map
245
- * )
246
- *
247
- * // print product with id `1`
248
- * console.log(storage.get(1))
249
- *
250
- * // search for items
251
- * const searchResult = storage.search({
252
- * query: { availabilityStatus: 'low' }
253
- * })
254
- * console.log(searchResult)
255
- * ```
256
- *
257
- * @example
258
- * #### Advanced: `onChange` and RxJS subject
259
- *
260
- * Internally, `DataStorage` uses RxJS subject which is exposed as `subject` property.
261
- * You can use this to subscribe to changes and do additional operations such as logging or sanitization etc.
262
- *
263
- * Alternatively, you can also set the `onChange` callback which is triggered whenever the subject changes and
264
- * does not require maintaining a subscription or knowledge of RxJS subject.
265
- *
266
- * ```javascript
267
- * import { DataStorage } from '@superutils/rx'
268
- *
269
- * const storage = new DataStorage('my-data')
270
- * const sub = storage.subject.subscribe(data => {
271
- * // Write to the database whenever data changes
272
- * console.log('Saving to database...', data)
273
- * })
274
- * // unsubscribe from subject
275
- * setTimeout(()=> sub.unsbuscribe(), 1000)
276
- *
277
- * // add an entry to storage
278
- * storage.set('bob', { age: 99, id: 'bob', name: 'Bob' })
279
- * ```
280
- */
281
194
  constructor(name, options) {
282
195
  this.initialized = false;
283
196
  this.subscriptions = {
284
197
  subject: void 0,
285
198
  forceUpdateCache: void 0
286
199
  };
200
+ this.clear = () => this.setAll(/* @__PURE__ */ new Map(), true);
201
+ this.delete = (keys) => {
202
+ if (!isArr2(keys)) keys = [keys];
203
+ const data = this.getAll();
204
+ for (const k of keys) data.delete(k);
205
+ this.setAll(data, true);
206
+ return this;
207
+ };
208
+ this.filter = (...args) => filter(this.getAll(), ...args);
209
+ this.find = (predicateOrOptions) => find(this.getAll(), predicateOrOptions);
210
+ this.get = (key) => this.getAll().get(key);
211
+ this.getAll = (forceRead = false) => {
212
+ var _a, _b;
213
+ const wasInitialized = this.initialized;
214
+ if (!wasInitialized) this.init();
215
+ const readFromStorage = this.cacheDisabled || this.name && forceRead;
216
+ if (readFromStorage) {
217
+ const data = this.read();
218
+ const shouldTrigger = forceRead || !wasInitialized && !!data.size;
219
+ shouldTrigger && this.subject.next(data);
220
+ return data;
221
+ }
222
+ return (_b = (_a = this.subject) == null ? void 0 : _a.value) != null ? _b : /* @__PURE__ */ new Map();
223
+ };
287
224
  this.handleForceUpdateCacheChange = (name) => {
288
225
  const isTarget = !this.name ? false : isArr2(name) ? name.includes(this.name) : isStr(name) ? name === this.name : name === true;
289
226
  if (!isTarget) return;
@@ -300,6 +237,107 @@ var _DataStorage = class _DataStorage {
300
237
  this.triggerOnError("onChange" /* onChange */)
301
238
  );
302
239
  };
240
+ this.has = (key) => this.getAll().has(key);
241
+ this.init = (initialValue) => {
242
+ var _a;
243
+ if (this.initialized) return false;
244
+ this.initialized = true;
245
+ let isEmpty = true;
246
+ if (!!(initialValue == null ? void 0 : initialValue.size) || !this.cacheDisabled) {
247
+ const dataStr = this.name ? (_a = this.storage) == null ? void 0 : _a.getItem(this.name) : null;
248
+ const existingValue = this.read(dataStr);
249
+ if (isDefined(dataStr)) initialValue = existingValue;
250
+ isEmpty = this.cacheDisabled || existingValue.size === 0;
251
+ }
252
+ (initialValue == null ? void 0 : initialValue.size) && this.subject.next(initialValue);
253
+ unsubscribeAll_default(this.subscriptions);
254
+ if (!this.cacheDisabled) {
255
+ this.subscriptions.forceUpdateCache = forceUpdateCache$.subscribe(
256
+ this.handleForceUpdateCacheChange
257
+ );
258
+ }
259
+ this.subscriptions.subject = this.subject.pipe(skip(this.cacheDisabled || isEmpty ? 0 : 1)).subscribe(
260
+ !this.cacheDisabled && this.delay > 0 ? deferred2(this.handleSubjectChange, this.delay, {
261
+ thisArg: this,
262
+ ...this.delayOptions
263
+ }) : this.handleSubjectChange
264
+ );
265
+ return true;
266
+ };
267
+ this.keys = () => getKeys(this.getAll());
268
+ this.map = (callback) => this.toArray().map(
269
+ ([key, value], index, entries) => callback(value, key, entries, index)
270
+ );
271
+ this.read = (dataStr = this.name ? ((_a) => (_a = this.storage) == null ? void 0 : _a.getItem(this.name))() : null) => {
272
+ var _a2;
273
+ if (!this.name) {
274
+ return (_a2 = this.subject.value) != null ? _a2 : /* @__PURE__ */ new Map();
275
+ }
276
+ const data = fallbackIfFails3(
277
+ (() => {
278
+ var _a3;
279
+ return (_a3 = this.parse) == null ? void 0 : _a3.call(this, dataStr);
280
+ }),
281
+ [],
282
+ this.triggerOnError("parse" /* parse */)
283
+ );
284
+ if (isMap(data)) return data;
285
+ if (!isStr(dataStr)) return /* @__PURE__ */ new Map();
286
+ return new Map(
287
+ fallbackIfFails3(
288
+ () => JSON.parse(dataStr),
289
+ [],
290
+ this.triggerOnError("parse-json" /* parse_json */)
291
+ )
292
+ );
293
+ };
294
+ this.search = (...args) => search(this.getAll(), ...args);
295
+ this.set = (key, value) => this.setAll(/* @__PURE__ */ new Map([[key, value]]), false);
296
+ this.setAll = (data, replace = false) => {
297
+ if (!isMap(data)) return this;
298
+ data = replace ? data : mapJoin(this.getAll(), data);
299
+ this.subject.next(new Map(data));
300
+ return this;
301
+ };
302
+ this.sort = (...args) => {
303
+ var _a;
304
+ const result = sort(
305
+ this.getAll(),
306
+ args[0],
307
+ args[1]
308
+ );
309
+ ((_a = args[1]) == null ? void 0 : _a.save) && this.setAll(result, true);
310
+ return result;
311
+ };
312
+ this.toArray = () => getEntries(this.getAll());
313
+ this.toJSON = (replacer, spacing = this.spaces, data = this.getAll()) => {
314
+ const str = fallbackIfFails3(
315
+ (() => {
316
+ var _a;
317
+ return (_a = this.stringify) == null ? void 0 : _a.call(this, data);
318
+ }),
319
+ [],
320
+ this.triggerOnError("stringify" /* stringify */)
321
+ );
322
+ if (isStr(str)) return str;
323
+ return fallbackIfFails3(
324
+ () => JSON.stringify(
325
+ Array.from(data),
326
+ replacer,
327
+ spacing
328
+ ),
329
+ [],
330
+ this.triggerOnError("stringify-json" /* stringify_json */, "")
331
+ );
332
+ };
333
+ this.toObject = (data = this.getAll()) => {
334
+ const obj = {};
335
+ if (!isMap(data)) return obj;
336
+ for (const [key, value] of data)
337
+ obj[key] = value;
338
+ return obj;
339
+ };
340
+ this.toString = (data = this.getAll()) => this.toJSON(void 0, void 0, data);
303
341
  this.triggerOnError = (type, returnValue = void 0) => (err) => {
304
342
  var _a;
305
343
  fallbackIfFails3(
@@ -309,6 +347,21 @@ var _DataStorage = class _DataStorage {
309
347
  );
310
348
  return returnValue;
311
349
  };
350
+ this.unsubscribe = () => unsubscribeAll_default(this.subscriptions);
351
+ this.values = () => getValues(this.getAll());
352
+ this.write = (data) => {
353
+ var _a;
354
+ try {
355
+ !this.initialized && this.init();
356
+ const finalData = data != null ? data : (_a = this.subject) == null ? void 0 : _a.value;
357
+ if (!this.name || !this.storage || !isMap(finalData)) return false;
358
+ this.storage.setItem(this.name, this.toString(finalData));
359
+ return true;
360
+ } catch (err) {
361
+ this.triggerOnError("write" /* write */)(err);
362
+ return false;
363
+ }
364
+ };
312
365
  const {
313
366
  cacheDisabled = false,
314
367
  delay,
@@ -345,168 +398,6 @@ var _DataStorage = class _DataStorage {
345
398
  get size() {
346
399
  return this.getAll().size;
347
400
  }
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
- }
510
401
  };
511
402
  /**
512
403
  * Creates a {@link DataStorage} instance initialized from a plain object.
@@ -555,35 +446,30 @@ var _DataStorage = class _DataStorage {
555
446
  * ```
556
447
  */
557
448
  _DataStorage.fromObject = (name, options) => new _DataStorage(name, {
558
- parse: (str) => objToMap(JSON.parse(str || "{}")),
559
- stringify: (data) => JSON.stringify(_DataStorage.prototype.toObject(data)),
449
+ parse: (str) => objToMap(JSON.parse(str != null ? str : "{}")),
450
+ stringify: function(data) {
451
+ return JSON.stringify(this.toObject(data));
452
+ },
560
453
  ...options,
561
454
  initialValue: !isObj2(options == null ? void 0 : options.initialValue, true) ? options == null ? void 0 : options.initialValue : objToMap(options.initialValue)
562
455
  });
563
456
  /**
564
457
  * Trigger forced update of cached data from storage.
565
458
  *
566
- * @param name determines which storage instances to be updated.
459
+ * @param name determines which cache-enabled storage instances to be updated.
567
460
  * - name (`string` | `string[]`): update all instances with a specific name(s)
568
461
  * - global (`true`): update all instances globally
569
462
  *
570
- * @example
571
- * ```javascript
572
- * import { DataStorage } from '@superutils/rx'
573
- *
574
- * // Update all DataStorage instances with specific name(s)
575
- * const name = 'products'
576
- * DataStorage.forceUpdateCache([name])
577
- *
578
- * // Update every single instance of DataStorage that uses storage (has a "name")
579
- * DataStorage.forceUpdateCache(true)
580
- * ```
463
+ * See {@link forceUpdateCache$} for more details.
581
464
  */
582
465
  _DataStorage.forceUpdateCache = (name) => {
583
466
  forceUpdateCache$.next(name);
584
467
  };
585
468
  var DataStorage = _DataStorage;
586
469
 
470
+ // src/data-storage/index.ts
471
+ import { objToMap as objToMap2 } from "@superutils/core";
472
+
587
473
  // src/IntervalSubject.ts
588
474
  var IntervalSubject = class extends BehaviorSubject {
589
475
  constructor(autoStart, _delay = 1e3, initialValue = 0, incrementBy = 1) {
@@ -743,6 +629,7 @@ export {
743
629
  isObservable,
744
630
  isSubjectLike2 as isSubjectLike,
745
631
  isSubscriptionLike,
632
+ objToMap2 as objToMap,
746
633
  skip,
747
634
  unsubscribeAll
748
635
  };
package/package.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "author": "Toufiqur Rahaman Chowdhury",
3
3
  "description": "A set of small, focused utilities for working with RxJS observables and subjects.",
4
4
  "dependencies": {
5
- "@superutils/core": "^1.2.10",
6
- "@superutils/promise": "^1.3.9",
5
+ "@superutils/core": "^1.2.12",
6
+ "@superutils/promise": "^1.3.11",
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.5",
53
- "gitHead": "9b77219f98125fd231d24c6e44299de7b99b679e"
52
+ "version": "0.1.7",
53
+ "gitHead": "22050737e69a5e7a6fea402c2e5540f3d0c2375c"
54
54
  }