@superutils/rx 0.1.1
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/LICENSE +0 -0
- package/README.md +3 -0
- package/dist/browser/index.min.js +4 -0
- package/dist/browser/index.min.js.map +1 -0
- package/dist/index.cjs +651 -0
- package/dist/index.d.cts +730 -0
- package/dist/index.d.ts +730 -0
- package/dist/index.js +636 -0
- package/package.json +53 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
// src/asPromise.ts
|
|
2
|
+
import {
|
|
3
|
+
fallbackIfFails,
|
|
4
|
+
isError,
|
|
5
|
+
isFn,
|
|
6
|
+
isObj,
|
|
7
|
+
isPositiveNumber,
|
|
8
|
+
isSubjectLike
|
|
9
|
+
} from "@superutils/core";
|
|
10
|
+
import PromisE, {
|
|
11
|
+
TIMEOUT_MAX
|
|
12
|
+
} from "@superutils/promise";
|
|
13
|
+
|
|
14
|
+
// src/rxjs.ts
|
|
15
|
+
import {
|
|
16
|
+
BehaviorSubject,
|
|
17
|
+
isObservable,
|
|
18
|
+
skip,
|
|
19
|
+
Subject,
|
|
20
|
+
Subscription
|
|
21
|
+
} from "rxjs";
|
|
22
|
+
|
|
23
|
+
// src/asPromise.ts
|
|
24
|
+
var asPromise = (subject, expectedValue, timeoutOrOptions) => {
|
|
25
|
+
if (!isSubjectLike(subject) && !isObservable(subject))
|
|
26
|
+
return PromisE.reject(
|
|
27
|
+
new Error("subject must be an instance of BehaviorSubject")
|
|
28
|
+
);
|
|
29
|
+
let subscription;
|
|
30
|
+
const options = isObj(timeoutOrOptions) ? timeoutOrOptions : { timeout: timeoutOrOptions };
|
|
31
|
+
options.timeout = isPositiveNumber(options.timeout) ? options.timeout : TIMEOUT_MAX;
|
|
32
|
+
const promise = PromisE.timeout(
|
|
33
|
+
{
|
|
34
|
+
...options,
|
|
35
|
+
onTimeout: async () => {
|
|
36
|
+
const { onTimeout, timeoutMsg } = options;
|
|
37
|
+
const msg = await fallbackIfFails(onTimeout, [], void 0);
|
|
38
|
+
return msg != null ? msg : isError(timeoutMsg) ? timeoutMsg : new Error(
|
|
39
|
+
timeoutMsg != null ? timeoutMsg : "request timed out before an expected value is received"
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
new PromisE((resolve) => {
|
|
44
|
+
subscription = subject.subscribe((value) => {
|
|
45
|
+
const shouldResolve = value === expectedValue || expectedValue === void 0 || isFn(expectedValue) && fallbackIfFails(expectedValue, [value], false);
|
|
46
|
+
shouldResolve && resolve(value);
|
|
47
|
+
});
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
promise.onFinalize.push(() => {
|
|
51
|
+
var _a;
|
|
52
|
+
(_a = subscription == null ? void 0 : subscription.unsubscribe) == null ? void 0 : _a.call(subscription);
|
|
53
|
+
});
|
|
54
|
+
return promise;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/copyRxSubject.ts
|
|
58
|
+
import {
|
|
59
|
+
deferred,
|
|
60
|
+
fallbackIfFails as fallbackIfFails2,
|
|
61
|
+
isArr,
|
|
62
|
+
isFn as isFn4,
|
|
63
|
+
isPositiveNumber as isPositiveNumber2
|
|
64
|
+
} from "@superutils/core";
|
|
65
|
+
|
|
66
|
+
// src/isSubjectLike.ts
|
|
67
|
+
import { isSubjectLike as _isSubjectLike } from "@superutils/core";
|
|
68
|
+
var isSubjectLike2 = (x, withValue = false) => _isSubjectLike(x, withValue);
|
|
69
|
+
var isSubjectLike_default = isSubjectLike2;
|
|
70
|
+
|
|
71
|
+
// src/unsubscribeAll.ts
|
|
72
|
+
import { isFn as isFn3 } from "@superutils/core";
|
|
73
|
+
|
|
74
|
+
// src/isSubscriptionLike.ts
|
|
75
|
+
import { isBool, isFn as isFn2 } from "@superutils/core";
|
|
76
|
+
var isSubscriptionLike = (value, strict = false) => {
|
|
77
|
+
if (value instanceof Subscription) return true;
|
|
78
|
+
const sub = value;
|
|
79
|
+
return !strict && !!sub && isBool(sub.closed) && isFn2(sub.unsubscribe);
|
|
80
|
+
};
|
|
81
|
+
var isSubscriptionLike_default = isSubscriptionLike;
|
|
82
|
+
|
|
83
|
+
// src/unsubscribeAll.ts
|
|
84
|
+
var unsubscribeAll = (unsub = {}, onError) => {
|
|
85
|
+
if (!unsub) return;
|
|
86
|
+
try {
|
|
87
|
+
if (isFn3(unsub)) return unsub();
|
|
88
|
+
if (isSubscriptionLike_default(unsub)) return unsub.unsubscribe();
|
|
89
|
+
Object.values(unsub).forEach(
|
|
90
|
+
(value) => unsubscribeAll(value, onError)
|
|
91
|
+
);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
onError == null ? void 0 : onError(err);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
var unsubscribeAll_default = unsubscribeAll;
|
|
97
|
+
|
|
98
|
+
// src/copyRxSubject.ts
|
|
99
|
+
var IGNORE_UPDATE_SYMBOL = /* @__PURE__ */ Symbol("ignore-rx-update");
|
|
100
|
+
function copyRxSubject(source$, copy$, valueModifier, options) {
|
|
101
|
+
var _a;
|
|
102
|
+
copy$ = isSubjectLike_default(copy$) ? copy$ : new BehaviorSubject(void 0);
|
|
103
|
+
options = {
|
|
104
|
+
...copyRxSubject.defaults,
|
|
105
|
+
...options != null ? options : {}
|
|
106
|
+
};
|
|
107
|
+
const sourceArr = isSubjectLike_default(source$) ? [source$] : isArr(source$) ? source$ : [source$];
|
|
108
|
+
const cache = new Map(
|
|
109
|
+
sourceArr.map((subject, i) => [
|
|
110
|
+
i,
|
|
111
|
+
isSubjectLike_default(subject) ? subject.value : subject
|
|
112
|
+
// fixed value provided
|
|
113
|
+
])
|
|
114
|
+
);
|
|
115
|
+
const triggerChange = () => {
|
|
116
|
+
const currentValue = isArr(source$) ? [...cache.values()] : cache.get(0);
|
|
117
|
+
if (!isFn4(valueModifier)) return copy$.next(currentValue);
|
|
118
|
+
const modifiedValue = fallbackIfFails2(
|
|
119
|
+
valueModifier,
|
|
120
|
+
[currentValue, void 0, copy$],
|
|
121
|
+
(err) => {
|
|
122
|
+
var _a2;
|
|
123
|
+
fallbackIfFails2(
|
|
124
|
+
(_a2 = options.onError) == null ? void 0 : _a2.bind(options.thisArg),
|
|
125
|
+
[err],
|
|
126
|
+
void 0
|
|
127
|
+
);
|
|
128
|
+
return IGNORE_UPDATE_SYMBOL;
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
modifiedValue !== IGNORE_UPDATE_SYMBOL && copy$.next(modifiedValue);
|
|
132
|
+
};
|
|
133
|
+
const triggerChangeDeferred = isPositiveNumber2(options.delay) ? deferred(triggerChange, options.delay, options) : triggerChange;
|
|
134
|
+
const subscriptions = sourceArr.map((subject, i) => {
|
|
135
|
+
if (!isSubjectLike_default(subject) || subject.closed) return;
|
|
136
|
+
return (subject instanceof BehaviorSubject ? subject.pipe(
|
|
137
|
+
skip(1)
|
|
138
|
+
// skip initial value
|
|
139
|
+
) : subject).subscribe((newValue) => {
|
|
140
|
+
cache.set(i, newValue);
|
|
141
|
+
triggerChangeDeferred();
|
|
142
|
+
});
|
|
143
|
+
}).filter(Boolean);
|
|
144
|
+
const unsubscribeOrg = (_a = copy$ == null ? void 0 : copy$.unsubscribe) == null ? void 0 : _a.bind(copy$);
|
|
145
|
+
copy$.unsubscribe = (...args) => {
|
|
146
|
+
unsubscribeOrg == null ? void 0 : unsubscribeOrg(...args);
|
|
147
|
+
unsubscribeAll(subscriptions);
|
|
148
|
+
cache.clear();
|
|
149
|
+
};
|
|
150
|
+
triggerChange();
|
|
151
|
+
return copy$;
|
|
152
|
+
}
|
|
153
|
+
copyRxSubject.defaults = {
|
|
154
|
+
delay: 0,
|
|
155
|
+
throttle: false
|
|
156
|
+
};
|
|
157
|
+
copyRxSubject.IGNORE_UPDATE_SYMBOL = IGNORE_UPDATE_SYMBOL;
|
|
158
|
+
|
|
159
|
+
// src/data-storage/DataStorage.ts
|
|
160
|
+
import {
|
|
161
|
+
deferred as deferred2,
|
|
162
|
+
fallbackIfFails as fallbackIfFails3,
|
|
163
|
+
filter,
|
|
164
|
+
find,
|
|
165
|
+
getEntries,
|
|
166
|
+
getKeys,
|
|
167
|
+
isArr as isArr2,
|
|
168
|
+
isFn as isFn5,
|
|
169
|
+
isMap,
|
|
170
|
+
isPositiveNumber as isPositiveNumber3,
|
|
171
|
+
isStr,
|
|
172
|
+
mapJoin,
|
|
173
|
+
search,
|
|
174
|
+
sort
|
|
175
|
+
} from "@superutils/core";
|
|
176
|
+
var forceUpdateCache$ = new Subject();
|
|
177
|
+
var DataStorage = class {
|
|
178
|
+
/**
|
|
179
|
+
* A wrapper for reading and writing to LocalStorage (browser) or JSON files (NodeJS),
|
|
180
|
+
* providing a Map-like interface with advanced features like search, filtering, and sorting.
|
|
181
|
+
*
|
|
182
|
+
* #### Notes:
|
|
183
|
+
* - **Performance**: `DataStorage` is optimized for small to medium datasets.
|
|
184
|
+
* - For datasets > 1MB, consider increasing the `delay` option to reduce write frequency.
|
|
185
|
+
* - It is **NOT** recommended for datasets larger than 3MB due to synchronous serialization costs.
|
|
186
|
+
* - **RxJS Integration**: Built on RxJS for reactive data handling, though no prior RxJS knowledge is required.
|
|
187
|
+
* - **Storage Behavior**:
|
|
188
|
+
* - If `name` is omitted, the instance operates in-memory only and data is not persisted to storage.
|
|
189
|
+
* - If `cacheDisabled` is `true`, data is not kept in memory; every read/write operation accesses the underlying
|
|
190
|
+
* storage directly.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* #### Browser Usage
|
|
194
|
+
* ```javascript
|
|
195
|
+
* import { DataStorage } from '@superutils/rx'
|
|
196
|
+
* import fetch from '@superutils/fetch'
|
|
197
|
+
*
|
|
198
|
+
* const storage = new DataStorage('products')
|
|
199
|
+
* const { products } = await fetch('[DUMMYJSON-DOT-COM]/products')
|
|
200
|
+
* // save all items to storage
|
|
201
|
+
* storage.setAll(
|
|
202
|
+
* new Map(products.map(p => [p.id, p])), // convert to Map
|
|
203
|
+
* )
|
|
204
|
+
*
|
|
205
|
+
* // print product with id `1`
|
|
206
|
+
* console.log(storage.get(1))
|
|
207
|
+
*
|
|
208
|
+
* // search for items
|
|
209
|
+
* const searchResult = storage.search({
|
|
210
|
+
* query: { availabilityStatus: 'low' }
|
|
211
|
+
* })
|
|
212
|
+
* console.log(searchResult)
|
|
213
|
+
* ```
|
|
214
|
+
* @example
|
|
215
|
+
* #### NodeJS Usage
|
|
216
|
+
* ```javascript
|
|
217
|
+
* import { DataStorage } from '@superutils/rx'
|
|
218
|
+
* import fetch from '@superutils/fetch'
|
|
219
|
+
* import { LocalStorage } from 'node-localstorage'
|
|
220
|
+
*
|
|
221
|
+
* // Add localStorage alternative for NodeJS that reads and writes to JSON files.
|
|
222
|
+
* // This is not necessary for browsers.
|
|
223
|
+
* globalThis.localStorage = new LocalStorage('./data', 1e7)
|
|
224
|
+
*
|
|
225
|
+
* const storage = new DataStorage('products')
|
|
226
|
+
* const { products } = await fetch('[DUMMYJSON-DOT-COM]/products')
|
|
227
|
+
* // save all items to storage
|
|
228
|
+
* storage.setAll(
|
|
229
|
+
* new Map(products.map(p => [p.id, p])), // convert to Map
|
|
230
|
+
* )
|
|
231
|
+
*
|
|
232
|
+
* // print product with id `1`
|
|
233
|
+
* console.log(storage.get(1))
|
|
234
|
+
*
|
|
235
|
+
* // search for items
|
|
236
|
+
* const searchResult = storage.search({
|
|
237
|
+
* query: { availabilityStatus: 'low' }
|
|
238
|
+
* })
|
|
239
|
+
* console.log(searchResult)
|
|
240
|
+
* ```
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* #### Advanced: `onChange` and RxJS subject
|
|
244
|
+
*
|
|
245
|
+
* Internally, `DataStorage` uses RxJS subject which is exposed as `subject` property.
|
|
246
|
+
* You can use this to subscribe to changes and do additional operations such as logging or sanitization etc.
|
|
247
|
+
*
|
|
248
|
+
* Alternatively, you can also set the `onChange` callback which is triggered whenever the subject changes and
|
|
249
|
+
* does not require maintaining a subscription or knowledge of RxJS subject.
|
|
250
|
+
*
|
|
251
|
+
* ```javascript
|
|
252
|
+
* import { DataStorage } from '@superutils/rx'
|
|
253
|
+
*
|
|
254
|
+
* const storage = new DataStorage('my-data')
|
|
255
|
+
* const sub = storage.subject.subscribe(data => {
|
|
256
|
+
* // Write to the database whenever data changes
|
|
257
|
+
* console.log('Saving to database...', data)
|
|
258
|
+
* })
|
|
259
|
+
* // unsubscribe from subject
|
|
260
|
+
* setTimeout(()=> sub.unsbuscribe(), 1000)
|
|
261
|
+
*
|
|
262
|
+
* // add an entry to storage
|
|
263
|
+
* storage.set('bob', { age: 99, id: 'bob', name: 'Bob' })
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
constructor(name, options) {
|
|
267
|
+
/** Debounce and throttle related options */
|
|
268
|
+
// readonly deferOptions
|
|
269
|
+
this.initialized = false;
|
|
270
|
+
this.subscriptions = {
|
|
271
|
+
subject: void 0,
|
|
272
|
+
forceUpdateCache: void 0
|
|
273
|
+
};
|
|
274
|
+
this.clear = () => {
|
|
275
|
+
this.setAll(/* @__PURE__ */ new Map(), true);
|
|
276
|
+
return this;
|
|
277
|
+
};
|
|
278
|
+
this.delete = (keys) => {
|
|
279
|
+
if (!isArr2(keys)) keys = [keys];
|
|
280
|
+
const data = this.getAll();
|
|
281
|
+
for (const k of keys) data.delete(k);
|
|
282
|
+
this.setAll(data, true);
|
|
283
|
+
return this;
|
|
284
|
+
};
|
|
285
|
+
this.find = (predicateOrOptions) => find(this.getAll(), predicateOrOptions);
|
|
286
|
+
this.filter = (predicate, limit, asArray, result) => filter(this.getAll(), predicate, limit, asArray, result);
|
|
287
|
+
this.get = (key) => this.getAll().get(key);
|
|
288
|
+
this.getAll = (forceRead = false) => {
|
|
289
|
+
var _a;
|
|
290
|
+
const wasInitialized = this.initialized;
|
|
291
|
+
if (!wasInitialized) this.init();
|
|
292
|
+
const readFromStorage = this.cacheDisabled || this.name && forceRead;
|
|
293
|
+
if (readFromStorage) {
|
|
294
|
+
const data = this.read();
|
|
295
|
+
const shouldTrigger = forceRead || !wasInitialized && !!data.size;
|
|
296
|
+
shouldTrigger && this.subject.next(data);
|
|
297
|
+
return data;
|
|
298
|
+
}
|
|
299
|
+
return (_a = this.subject) == null ? void 0 : _a.value;
|
|
300
|
+
};
|
|
301
|
+
this.has = (key) => this.getAll().has(key);
|
|
302
|
+
this.init = (initialValue) => {
|
|
303
|
+
var _a, _b;
|
|
304
|
+
if (this.initialized) return false;
|
|
305
|
+
this.initialized = true;
|
|
306
|
+
this.subject instanceof BehaviorSubject && this.subject.next(this.read());
|
|
307
|
+
unsubscribeAll_default(this.subscriptions);
|
|
308
|
+
if (!this.cacheDisabled) {
|
|
309
|
+
this.subscriptions.forceUpdateCache = forceUpdateCache$.subscribe(
|
|
310
|
+
(refresh) => {
|
|
311
|
+
const doRefresh = !this.name ? false : isArr2(refresh) ? refresh.includes(this.name) : isStr(refresh) ? refresh === this.name : refresh === true;
|
|
312
|
+
if (!doRefresh) return;
|
|
313
|
+
const newData = this.read();
|
|
314
|
+
this.subject.next(newData);
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
!((_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);
|
|
319
|
+
const piped = this.subject.pipe(
|
|
320
|
+
skip(this.cacheDisabled || !!(initialValue == null ? void 0 : initialValue.size) ? 0 : 1)
|
|
321
|
+
);
|
|
322
|
+
let handleChange = (data) => {
|
|
323
|
+
if (!isMap(data)) return this.subject.next(/* @__PURE__ */ new Map());
|
|
324
|
+
this.write(data);
|
|
325
|
+
fallbackIfFails3(
|
|
326
|
+
async () => {
|
|
327
|
+
var _a2;
|
|
328
|
+
return await ((_a2 = this.onChange) == null ? void 0 : _a2.call(this, data));
|
|
329
|
+
},
|
|
330
|
+
[],
|
|
331
|
+
this.triggerOnError("onChange")
|
|
332
|
+
);
|
|
333
|
+
};
|
|
334
|
+
if (this.delay > 0)
|
|
335
|
+
handleChange = deferred2(handleChange, this.delay, this.delayOptions);
|
|
336
|
+
this.subscriptions.subject = piped.subscribe(handleChange);
|
|
337
|
+
return true;
|
|
338
|
+
};
|
|
339
|
+
this.keys = () => getKeys(this.getAll());
|
|
340
|
+
this.map = (callback) => this.toArray().map(
|
|
341
|
+
([key, value], index, data) => callback(value, key, data, index)
|
|
342
|
+
);
|
|
343
|
+
this.read = () => {
|
|
344
|
+
var _a, _b;
|
|
345
|
+
const dataStr = (_b = (_a = this.storage) == null ? void 0 : _a.getItem(this.name)) != null ? _b : "[]";
|
|
346
|
+
const data = isFn5(this.parse) && fallbackIfFails3(
|
|
347
|
+
this.parse,
|
|
348
|
+
[dataStr],
|
|
349
|
+
this.triggerOnError("parse")
|
|
350
|
+
);
|
|
351
|
+
if (isMap(data)) return data;
|
|
352
|
+
return new Map(
|
|
353
|
+
fallbackIfFails3(
|
|
354
|
+
() => JSON.parse(dataStr),
|
|
355
|
+
[],
|
|
356
|
+
this.triggerOnError("parse-json")
|
|
357
|
+
)
|
|
358
|
+
);
|
|
359
|
+
};
|
|
360
|
+
this.search = (options) => search(this.getAll(), options);
|
|
361
|
+
this.set = (key, value) => {
|
|
362
|
+
this.setAll(/* @__PURE__ */ new Map([[key, value]]), false);
|
|
363
|
+
return this;
|
|
364
|
+
};
|
|
365
|
+
this.setAll = (data = /* @__PURE__ */ new Map(), replace = false) => {
|
|
366
|
+
if (!isMap(data)) return this;
|
|
367
|
+
data = replace ? data : mapJoin(this.getAll(), data);
|
|
368
|
+
this.subject.next(new Map(data));
|
|
369
|
+
return this;
|
|
370
|
+
};
|
|
371
|
+
this.sort = ((nameOrComparator, options) => {
|
|
372
|
+
const result = sort(
|
|
373
|
+
this.getAll(),
|
|
374
|
+
nameOrComparator,
|
|
375
|
+
options
|
|
376
|
+
);
|
|
377
|
+
(options == null ? void 0 : options.save) && this.setAll(result, true);
|
|
378
|
+
return result;
|
|
379
|
+
});
|
|
380
|
+
this.toArray = () => getEntries(this.getAll());
|
|
381
|
+
this.toJSON = (replacer, spacing = this.spaces, data = this.getAll()) => {
|
|
382
|
+
const arr = Array.from(data);
|
|
383
|
+
const str = fallbackIfFails3(
|
|
384
|
+
() => {
|
|
385
|
+
var _a;
|
|
386
|
+
return (_a = this.stringify) == null ? void 0 : _a.call(this, data);
|
|
387
|
+
},
|
|
388
|
+
[],
|
|
389
|
+
this.triggerOnError("stringify")
|
|
390
|
+
);
|
|
391
|
+
if (isStr(str)) return str;
|
|
392
|
+
return fallbackIfFails3(
|
|
393
|
+
() => JSON.stringify(arr, replacer, spacing),
|
|
394
|
+
[],
|
|
395
|
+
this.triggerOnError("stringify-json")
|
|
396
|
+
);
|
|
397
|
+
};
|
|
398
|
+
this.toString = (data) => this.toJSON(void 0, void 0, data);
|
|
399
|
+
this.triggerOnError = (type) => (err) => {
|
|
400
|
+
this.onError && fallbackIfFails3(this.onError, [err, type], "");
|
|
401
|
+
};
|
|
402
|
+
this.unsubscribe = () => unsubscribeAll_default(this.subscriptions);
|
|
403
|
+
this.values = () => [...this.getAll().values()];
|
|
404
|
+
this.write = (data) => {
|
|
405
|
+
try {
|
|
406
|
+
!this.initialized && this.init();
|
|
407
|
+
data != null ? data : data = this.subject.value;
|
|
408
|
+
if (!this.name || !this.storage || !isMap(data)) return false;
|
|
409
|
+
this.storage.setItem(this.name, this.toString(data));
|
|
410
|
+
return true;
|
|
411
|
+
} catch (err) {
|
|
412
|
+
this.triggerOnError("write")(err);
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
const {
|
|
417
|
+
cacheDisabled = false,
|
|
418
|
+
delay,
|
|
419
|
+
delayOptions,
|
|
420
|
+
initialValue,
|
|
421
|
+
onError,
|
|
422
|
+
onChange,
|
|
423
|
+
parse,
|
|
424
|
+
spaces,
|
|
425
|
+
storage,
|
|
426
|
+
stringify
|
|
427
|
+
} = options != null ? options : {};
|
|
428
|
+
this.delay = cacheDisabled ? 0 : delay === 0 || isPositiveNumber3(delay) ? delay : 300;
|
|
429
|
+
this.name = `${name != null ? name : ""}`.trim();
|
|
430
|
+
this.onError = onError;
|
|
431
|
+
this.onChange = onChange;
|
|
432
|
+
this.parse = parse;
|
|
433
|
+
this.storage = storage === null ? null : storage != null ? storage : fallbackIfFails3(
|
|
434
|
+
() => globalThis.localStorage,
|
|
435
|
+
[],
|
|
436
|
+
void 0
|
|
437
|
+
);
|
|
438
|
+
if (this.name && !this.storage)
|
|
439
|
+
throw new Error(
|
|
440
|
+
"options.storage: LocalStorage instance or equivalent required"
|
|
441
|
+
);
|
|
442
|
+
this.cacheDisabled = !!this.storage && cacheDisabled;
|
|
443
|
+
this.stringify = stringify;
|
|
444
|
+
this.spaces = spaces;
|
|
445
|
+
this.subject = this.cacheDisabled ? new Subject() : new BehaviorSubject(void 0);
|
|
446
|
+
this.delayOptions = delayOptions;
|
|
447
|
+
isMap(initialValue) && initialValue.size && this.init(initialValue);
|
|
448
|
+
}
|
|
449
|
+
get size() {
|
|
450
|
+
return this.getAll().size;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
/**
|
|
454
|
+
* Trigger forced update of cached data from storage.
|
|
455
|
+
*
|
|
456
|
+
* @param name determines which storage instances to be updated.
|
|
457
|
+
* - name (`string` | `string[]`): update all instances with a specific name(s)
|
|
458
|
+
* - global (`true`): update all instances globally
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* ```javascript
|
|
462
|
+
* import { DataStorage } from '@superutils/rx'
|
|
463
|
+
*
|
|
464
|
+
* // Update all DataStorage instances with specific name(s)
|
|
465
|
+
* const name = 'products'
|
|
466
|
+
* DataStorage.forceUpdateCache([name])
|
|
467
|
+
*
|
|
468
|
+
* // Update every single instance of DataStorage that uses storage (has a "name")
|
|
469
|
+
* DataStorage.forceUpdateCache(true)
|
|
470
|
+
* ```
|
|
471
|
+
*/
|
|
472
|
+
DataStorage.forceUpdateCache = (name) => {
|
|
473
|
+
forceUpdateCache$.next(name);
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
// src/IntervalSubject.ts
|
|
477
|
+
var IntervalSubject = class extends BehaviorSubject {
|
|
478
|
+
constructor(autoStart, _delay = 1e3, initialValue = 0, incrementBy = 1) {
|
|
479
|
+
super(initialValue);
|
|
480
|
+
this.autoStart = autoStart;
|
|
481
|
+
this._delay = _delay;
|
|
482
|
+
this.initialValue = initialValue;
|
|
483
|
+
this.incrementBy = incrementBy;
|
|
484
|
+
this._running = false;
|
|
485
|
+
this.pause = () => {
|
|
486
|
+
clearInterval(this._intervalId);
|
|
487
|
+
this._running = false;
|
|
488
|
+
return this;
|
|
489
|
+
};
|
|
490
|
+
this.resume = () => this.start();
|
|
491
|
+
this.start = () => {
|
|
492
|
+
if (!this._running) {
|
|
493
|
+
this._running = true;
|
|
494
|
+
this._intervalId = setInterval(
|
|
495
|
+
() => this.next(this.value + this.incrementBy),
|
|
496
|
+
this._delay
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
return this;
|
|
500
|
+
};
|
|
501
|
+
this.stop = () => {
|
|
502
|
+
this.pause();
|
|
503
|
+
this.next(0);
|
|
504
|
+
return this;
|
|
505
|
+
};
|
|
506
|
+
this.autoStart && this.start();
|
|
507
|
+
}
|
|
508
|
+
get delay() {
|
|
509
|
+
return this._delay;
|
|
510
|
+
}
|
|
511
|
+
set delay(newDelay) {
|
|
512
|
+
if (!this._running) this._delay = newDelay;
|
|
513
|
+
}
|
|
514
|
+
get running() {
|
|
515
|
+
return this._running;
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
// src/IntervalRunner.ts
|
|
520
|
+
import { fallbackIfFails as fallbackIfFails4, noop } from "@superutils/core";
|
|
521
|
+
var IntervalRunner = class {
|
|
522
|
+
constructor(taskFn, taskArgs, intervalMs, sequential = true, preExecute = true) {
|
|
523
|
+
this.taskFn = taskFn;
|
|
524
|
+
this.taskArgs = taskArgs;
|
|
525
|
+
this.sequential = sequential;
|
|
526
|
+
this.preExecute = preExecute;
|
|
527
|
+
this.minIntervalMs = 1e3;
|
|
528
|
+
this.runCount = 0;
|
|
529
|
+
this.started = false;
|
|
530
|
+
this.clearInterval = () => {
|
|
531
|
+
this.sequential ? clearTimeout(this.idInterval) : clearInterval(this.idInterval);
|
|
532
|
+
this.idInterval = void 0;
|
|
533
|
+
};
|
|
534
|
+
this.executeTask = async (once = false) => {
|
|
535
|
+
var _a;
|
|
536
|
+
let err;
|
|
537
|
+
let result;
|
|
538
|
+
if (this.sequential || once) this.clearInterval();
|
|
539
|
+
try {
|
|
540
|
+
++this.runCount;
|
|
541
|
+
await ((_a = this.onBeforeExec) == null ? void 0 : _a.call(this, this.runCount, once));
|
|
542
|
+
result = await this.taskFn.apply(void 0, this.taskArgs);
|
|
543
|
+
this.lastResult = result;
|
|
544
|
+
} catch (_err) {
|
|
545
|
+
err = _err;
|
|
546
|
+
}
|
|
547
|
+
fallbackIfFails4(
|
|
548
|
+
this.onResult,
|
|
549
|
+
[err != null ? err : null, result, this.runCount, once],
|
|
550
|
+
void 0
|
|
551
|
+
);
|
|
552
|
+
if (!once && this.sequential && this.intervalMs$.value > this.minIntervalMs) {
|
|
553
|
+
this.idInterval = setTimeout(
|
|
554
|
+
this.executeTask,
|
|
555
|
+
this.intervalMs$.value
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
return this.lastResult;
|
|
559
|
+
};
|
|
560
|
+
/** Execute the task function regardless of the interval runner state */
|
|
561
|
+
this.executeOnce = async () => await this.executeTask(true);
|
|
562
|
+
/** Check if interval is running*/
|
|
563
|
+
this.isStarted = () => this.started;
|
|
564
|
+
/**
|
|
565
|
+
* Restart interval
|
|
566
|
+
*
|
|
567
|
+
* @param resetRunCount (optional) whether to reset run count
|
|
568
|
+
*
|
|
569
|
+
* @returns {Boolean} indicates whether restart was successful
|
|
570
|
+
*/
|
|
571
|
+
this.restart = (resetRunCount = false) => {
|
|
572
|
+
if (!this.onResult) return false;
|
|
573
|
+
this.stop(resetRunCount);
|
|
574
|
+
this.start(this.onResult, this.onBeforeExec);
|
|
575
|
+
return true;
|
|
576
|
+
};
|
|
577
|
+
/**
|
|
578
|
+
* @summary set `onResult` & `onBeforeExec` callbacks and start execution.
|
|
579
|
+
*
|
|
580
|
+
* If it's already running, the callbacks will be used on the next execution.
|
|
581
|
+
*
|
|
582
|
+
* In order to start using callbacks immediately, invoke the `intervalRunner.stop()` function first.
|
|
583
|
+
*
|
|
584
|
+
* @returns {Boolean} indicates whether starting interveral waa successful
|
|
585
|
+
*/
|
|
586
|
+
this.start = (onResult, onBeforeExec) => {
|
|
587
|
+
if (!onResult) return false;
|
|
588
|
+
this.onResult = onResult;
|
|
589
|
+
this.onBeforeExec = onBeforeExec;
|
|
590
|
+
if (this.started) return false;
|
|
591
|
+
this.started = true;
|
|
592
|
+
let delayMs;
|
|
593
|
+
this.subscription = this.intervalMs$.subscribe((newDelayMs) => {
|
|
594
|
+
var _a;
|
|
595
|
+
if (delayMs === newDelayMs && newDelayMs !== void 0) return;
|
|
596
|
+
delayMs = Math.max((_a = newDelayMs != null ? newDelayMs : delayMs) != null ? _a : 0, this.minIntervalMs);
|
|
597
|
+
this.clearInterval();
|
|
598
|
+
const preExec = this.lastResult === void 0 && this.preExecute;
|
|
599
|
+
preExec && this.executeTask().catch(noop);
|
|
600
|
+
this.idInterval = !this.sequential ? setInterval(this.executeTask, delayMs) : !preExec ? setTimeout(this.executeTask, delayMs) : void 0;
|
|
601
|
+
});
|
|
602
|
+
return true;
|
|
603
|
+
};
|
|
604
|
+
/**
|
|
605
|
+
* Stop interval runner
|
|
606
|
+
*
|
|
607
|
+
* @param resetRunCount (optional) whether to reset the run counter
|
|
608
|
+
*/
|
|
609
|
+
this.stop = (resetRunCount = false) => {
|
|
610
|
+
var _a, _b;
|
|
611
|
+
if (resetRunCount) this.runCount = 0;
|
|
612
|
+
this.started = false;
|
|
613
|
+
(_b = (_a = this.subscription) == null ? void 0 : _a.unsubscribe) == null ? void 0 : _b.call(_a);
|
|
614
|
+
this.clearInterval();
|
|
615
|
+
return this;
|
|
616
|
+
};
|
|
617
|
+
this.intervalMs$ = intervalMs instanceof BehaviorSubject ? intervalMs : new BehaviorSubject(intervalMs);
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
export {
|
|
621
|
+
BehaviorSubject,
|
|
622
|
+
DataStorage,
|
|
623
|
+
IGNORE_UPDATE_SYMBOL,
|
|
624
|
+
IntervalRunner,
|
|
625
|
+
IntervalSubject,
|
|
626
|
+
Subject,
|
|
627
|
+
Subscription,
|
|
628
|
+
asPromise,
|
|
629
|
+
copyRxSubject,
|
|
630
|
+
forceUpdateCache$,
|
|
631
|
+
isObservable,
|
|
632
|
+
isSubjectLike2 as isSubjectLike,
|
|
633
|
+
isSubscriptionLike,
|
|
634
|
+
skip,
|
|
635
|
+
unsubscribeAll
|
|
636
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "Toufiqur Rahaman Chowdhury",
|
|
3
|
+
"description": "RxJS utilities and hooks for React applications.",
|
|
4
|
+
"dependencies": {
|
|
5
|
+
"@superutils/core": "^1.2.8",
|
|
6
|
+
"@superutils/promise": "^1.3.6",
|
|
7
|
+
"@types/react": "^18.0.0",
|
|
8
|
+
"react": ">=16.8.0",
|
|
9
|
+
"rxjs": "^7.8.2"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"keywords": [
|
|
17
|
+
"rxjs",
|
|
18
|
+
"utils",
|
|
19
|
+
"typescript",
|
|
20
|
+
"react",
|
|
21
|
+
"hooks"
|
|
22
|
+
],
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"name": "@superutils/rx",
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@superutils/core": "latest",
|
|
27
|
+
"@superutils/promise": "latest"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"_build": "tsc -p tsconfig.json",
|
|
34
|
+
"_watch": "tsc -p tsconfig.json --watch",
|
|
35
|
+
"build": "tsup --config ../../tsup.config.js",
|
|
36
|
+
"dev": "npm run build -- --watch",
|
|
37
|
+
"start": "npm run dev",
|
|
38
|
+
"test": "cd ../../ && npm run test rx"
|
|
39
|
+
},
|
|
40
|
+
"sideEffects": false,
|
|
41
|
+
"exports": {
|
|
42
|
+
".": {
|
|
43
|
+
"types": "./dist/index.d.ts",
|
|
44
|
+
"import": "./dist/index.js",
|
|
45
|
+
"require": "./dist/index.cjs"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"main": "./dist/index.cjs",
|
|
49
|
+
"module": "./dist/index.js",
|
|
50
|
+
"type": "module",
|
|
51
|
+
"types": "./dist/index.d.ts",
|
|
52
|
+
"version": "0.1.1"
|
|
53
|
+
}
|