@superutils/rx 0.1.13 → 0.1.14
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/browser/index.min.js +2 -2
- package/dist/browser/index.min.js.map +1 -1
- package/dist/index.cjs +94 -400
- package/dist/index.d.cts +127 -730
- package/dist/index.d.ts +127 -730
- package/dist/index.js +96 -410
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,36 +1,50 @@
|
|
|
1
1
|
import { TimeoutOptions, IPromisE_Timeout } from '@superutils/promise';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { DeferredOptions, ThrottleOptions, DebounceOptions, ValueOrPromise, DropFirst, filter, FindOptions, find, search, sort, SortOptions, TypedMap } from '@superutils/core';
|
|
5
|
-
export { TypedMap, objToMap } from '@superutils/core';
|
|
2
|
+
import { SubscriptionLike, Observable, BehaviorSubject, Subject, Subscription } from 'rxjs';
|
|
3
|
+
import { DeferredOptions, ValueOrPromise } from '@superutils/core';
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
5
|
+
type AsPromise_Defaults = Required<Pick<AsPromise_Options, 'invalidInputMsg' | 'timeoutMsg'>>;
|
|
6
|
+
type AsPromise_Options = Omit<TimeoutOptions, 'batchFunc'> & {
|
|
7
|
+
/** Error message used when input is not a valid observable */
|
|
8
|
+
invalidInputMsg?: string;
|
|
9
|
+
/** Number of emit values to skip/ignore. Default: `0` */
|
|
10
|
+
skip?: number;
|
|
11
|
+
/** Error message to use when times out. */
|
|
12
|
+
timeoutMsg?: string | Error;
|
|
13
|
+
};
|
|
14
|
+
type Unsubscribe = SubscriptionLike['unsubscribe'];
|
|
15
|
+
type UnsubscribeCandidate = Unsubscribe | SubscriptionLike | SubscriptionLike[] | unknown[] | object | undefined | null | boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Recursively extracts the emitted value type from an Observable, an array of Observables,
|
|
18
|
+
* or returns the type itself if it's a static value.
|
|
19
|
+
*
|
|
20
|
+
* @template T - The input type to unwrap.
|
|
21
|
+
*/
|
|
22
|
+
type UnwrapSourceValue<T> = T extends Observable<infer V> ? V : T extends readonly unknown[] ? {
|
|
23
|
+
-readonly [K in keyof T]: UnwrapSourceValue<T[K]>;
|
|
24
|
+
} : T;
|
|
25
|
+
/**
|
|
26
|
+
* Recursively extracts the emitted value type from an Observable or an array of Observables,
|
|
27
|
+
* similar to {@link UnwrapSourceValue}.
|
|
28
|
+
*
|
|
29
|
+
* However, if the Observable does not have a `.value` property (i.e., it is not a `BehaviorSubject`),
|
|
30
|
+
* the resulting type is unioned with `undefined` to reflect that an initial value may not be
|
|
31
|
+
* immediately available.
|
|
32
|
+
*
|
|
33
|
+
* @template T - The input type to unwrap.
|
|
34
|
+
*/
|
|
35
|
+
type UnwrapSourceValueStrict<T> = T extends Observable<infer V> ? T extends {
|
|
36
|
+
value: infer V;
|
|
37
|
+
} ? V : V | undefined : T extends readonly unknown[] ? {
|
|
38
|
+
-readonly [K in keyof T]: UnwrapSourceValueStrict<T[K]>;
|
|
21
39
|
} : T;
|
|
22
|
-
type Unsubscribe = () => void;
|
|
23
|
-
type UnsubscribeCandidates = Unsubscribe | SubscriptionLike | SubscriptionLike[] | unknown[] | Record<PropertyKey, unknown> | undefined | null | boolean;
|
|
24
40
|
|
|
25
41
|
/**
|
|
26
42
|
* @summary Create a promise using RxJS subject and wait until an expected value is received
|
|
27
43
|
*
|
|
28
|
-
* @param
|
|
44
|
+
* @param input$ RxJS subject or observable
|
|
29
45
|
* @param expectedValue (optional) if undefined, will resolve as soon as any value is received.
|
|
30
46
|
* If function, it should return true or false to indicate whether the value should be resolved.
|
|
31
|
-
* @param timeoutOrOptions (optional)
|
|
32
|
-
* @param timeoutOrOptions.timeout (optional) timeout duration in milliseconds if no value received within given time
|
|
33
|
-
* @param timeoutOrOptions.timeoutMsg (optional) error message to use when times out.
|
|
47
|
+
* @param timeoutOrOptions (optional) timeout duration or options
|
|
34
48
|
*
|
|
35
49
|
* @returns timeout promise
|
|
36
50
|
*
|
|
@@ -50,79 +64,108 @@ type UnsubscribeCandidates = Unsubscribe | SubscriptionLike | SubscriptionLike[]
|
|
|
50
64
|
* subjectAsPromise(subject, 5).then(value => console.log('Expected 5, received ', value))
|
|
51
65
|
* ```
|
|
52
66
|
*/
|
|
53
|
-
declare const asPromise:
|
|
54
|
-
|
|
55
|
-
|
|
67
|
+
declare const asPromise: {
|
|
68
|
+
<T = unknown>(input$: Observable<T>, expectedValue?: T | ((value: T) => boolean), timeoutOrOptions?: number | AsPromise_Options): IPromisE_Timeout<T>;
|
|
69
|
+
defaults: Required<Pick<AsPromise_Options, "invalidInputMsg" | "timeoutMsg">>;
|
|
70
|
+
};
|
|
56
71
|
|
|
57
|
-
/** Symbol used to signal to ignore an update when using a `
|
|
72
|
+
/** Symbol used to signal to ignore an update when using a `transform()` callback with {@link copyRx} */
|
|
58
73
|
declare const IGNORE_UPDATE_SYMBOL: unique symbol;
|
|
59
|
-
type
|
|
74
|
+
type CopyRx_Options<TOut, ThisArg> = {
|
|
75
|
+
/**
|
|
76
|
+
* Debounce or throttle delay in milliseconds.
|
|
77
|
+
* If provided, updates to the output subject will be delayed accordingly.
|
|
78
|
+
*
|
|
79
|
+
* Default: `undefined`
|
|
80
|
+
*/
|
|
60
81
|
delay?: number;
|
|
82
|
+
/**
|
|
83
|
+
* The initial value for the output subject.
|
|
84
|
+
* This is particularly useful when creating a new `BehaviorSubject` internally.
|
|
85
|
+
*/
|
|
86
|
+
initialValue?: TOut;
|
|
87
|
+
/**
|
|
88
|
+
* An optional destination subject to which values will be copied.
|
|
89
|
+
* If provided, this subject will be returned by the function.
|
|
90
|
+
* If not provided, a new `BehaviorSubject` is created.
|
|
91
|
+
*/
|
|
92
|
+
output?: BehaviorSubject<TOut> | Subject<TOut>;
|
|
93
|
+
/**
|
|
94
|
+
* Number of initial emissions to skip from the input observable(s).
|
|
95
|
+
*
|
|
96
|
+
* If an array is provided, each element corresponds to the observable at the same index.
|
|
97
|
+
*
|
|
98
|
+
* Default: `1` for `BehaviorSubject` (to avoid redundant updates of the initial value), otherwise `0`.
|
|
99
|
+
*/
|
|
100
|
+
skipEmits?: number | (number | undefined | null)[];
|
|
101
|
+
/** Use this only if */
|
|
102
|
+
transformSequentially?: boolean;
|
|
61
103
|
} & DeferredOptions<ThisArg>;
|
|
62
104
|
/**
|
|
63
|
-
* Value modifier function definition for {@link
|
|
105
|
+
* Value modifier function definition for {@link copyRx}.
|
|
106
|
+
*
|
|
107
|
+
* This function is executed for each new value emitted by the input observable(s).
|
|
108
|
+
*
|
|
109
|
+
* - **`this` context**: The `this` context within the `transform` function can be set using the `options.thisArg`
|
|
110
|
+
* - **Ignoring updates**: Returning {@link copyRx.IGNORE} will prevent the output observable from emitting the current update.
|
|
111
|
+
* - **Asynchronous transformations**: If a `Promise` is returned, the output observable will only be updated once the promise resolves.
|
|
112
|
+
* However, this can introduce race conditions if new input values arrive before the previous promise resolves.
|
|
113
|
+
* To prevent such race conditions, enable the `transformSequentially` flag in `CopyRx_Options`.
|
|
114
|
+
* - **Error handling**: If the `transform` function throws an error, the update is gracefully ignored, and the `onError` callback (if provided in `CopyRx_Options`) will be invoked.
|
|
115
|
+
*/
|
|
116
|
+
type CopyRx_Transform<TIn = unknown, TOut = TIn, ThisArg = unknown> = (this: ThisArg,
|
|
117
|
+
/**
|
|
118
|
+
* The current value from the input observable (or an array of values if multiple sources are provided).
|
|
119
|
+
*
|
|
120
|
+
* - If `input` to `copyRx` is a single observable, `newValue` will be the value emitted by that observable.
|
|
121
|
+
* - If `input` is an array of observables/values, `newValue` will be an array containing the latest values from all sources.
|
|
64
122
|
*
|
|
65
|
-
*
|
|
123
|
+
* **Note:** If the transformation is asynchronous, a `BehaviorSubject` output will emit `undefined` (or the
|
|
124
|
+
* `initialValue`) immediately upon subscription until the first `transform` promise resolves.
|
|
66
125
|
*/
|
|
67
|
-
|
|
126
|
+
newValue: TIn,
|
|
68
127
|
/**
|
|
69
|
-
*
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* @param
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* @param
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* Args: `newValue, previousValue, copy$`
|
|
86
|
-
* @param {CopyRxSubjectOptions} options (optional) options to enable debouce/throttling `copy$` value changes.
|
|
87
|
-
* @param options.delay (optional) delay in milliseconds. Default: `0`
|
|
88
|
-
* @param options.onError (optional) callback invoked whenever `valueModifier` execution fails.
|
|
89
|
-
* @param options.throttle (optional) `true`: throttle, `false`: debounce. Default: `false`
|
|
90
|
-
*
|
|
91
|
-
* @returns `copy$` if provided, otherwise, a new `BehaviorSubject` instance
|
|
128
|
+
* The destination observable/subject where the results are being copied.
|
|
129
|
+
*/
|
|
130
|
+
output: Observable<TOut>) => ValueOrPromise<TOut> | typeof IGNORE_UPDATE_SYMBOL;
|
|
131
|
+
/**
|
|
132
|
+
* Returns a subject that automatically copies the value(s) of the source subject(s).
|
|
133
|
+
*
|
|
134
|
+
* Established a unidirectional data flow from source(s) to a destination subject.
|
|
135
|
+
* Changes to the destination subject are NOT applied back to the source.
|
|
136
|
+
*
|
|
137
|
+
* @param source$ RxJS input observable(s) or static value(s). If an array is provided,
|
|
138
|
+
* the output subject will emit an array of values by default.
|
|
139
|
+
* @param transform (optional) A function to map or filter values before they are emitted by the output subject.
|
|
140
|
+
* Supports async functions. If it throws, the update is ignored.
|
|
141
|
+
* @param options (optional) Configuration for timing (delay), initial state, and error handling.
|
|
142
|
+
*
|
|
143
|
+
* @returns The destination subject (either the one provided in `options.output` or a new `BehaviorSubject`).
|
|
92
144
|
*
|
|
93
145
|
* @example
|
|
94
146
|
* #### Auto-copy values from a single subject
|
|
95
147
|
* ```typescript
|
|
96
|
-
* import { BehaviorSubject,
|
|
148
|
+
* import { BehaviorSubject, copyRx } from '@superutils/rx'
|
|
97
149
|
*
|
|
98
150
|
* const number$ = new BehaviorSubject(1)
|
|
99
|
-
* const even$ =
|
|
100
|
-
* //
|
|
101
|
-
*
|
|
102
|
-
* // create and return a new BehaviorSubject. An existing RxJS subject can also be provided here.
|
|
103
|
-
* null,
|
|
104
|
-
* // copy and transform the value from number$
|
|
105
|
-
* newValue => newValue % 2 === 0,
|
|
106
|
-
* // debounce/throttle value changes to even$
|
|
107
|
-
* // {
|
|
108
|
-
* // delay: 300 //
|
|
109
|
-
* // throttle: false,
|
|
110
|
-
* // }
|
|
151
|
+
* const even$ = copyRx(
|
|
152
|
+
* number$, // input observable
|
|
153
|
+
* newValue => newValue % 2 === 0 ? newValue : copyRx.IGNORE,
|
|
111
154
|
* )
|
|
112
155
|
* // subscribe to even$ changes
|
|
113
|
-
* even$.subscribe(console.log)
|
|
114
|
-
* number$.next(2)
|
|
115
|
-
* number$.next(3) //
|
|
156
|
+
* even$.subscribe(console.log) // prints: 2
|
|
157
|
+
* number$.next(2)
|
|
158
|
+
* number$.next(3) // (ignored)
|
|
116
159
|
* ```
|
|
117
160
|
*
|
|
118
161
|
* @example
|
|
119
162
|
* #### Auto-copy from an array of subjects & values
|
|
120
163
|
* ```javascript
|
|
121
|
-
* import { BehaviorSubject,
|
|
164
|
+
* import { BehaviorSubject, copyRx } from '@superutils/rx'
|
|
122
165
|
*
|
|
123
166
|
* const theme$ = new BehaviorSubject('dark')
|
|
124
167
|
* const userId$ = new BehaviorSubject('username')
|
|
125
|
-
* const settings$ =
|
|
168
|
+
* const settings$ = copyRx(
|
|
126
169
|
* [
|
|
127
170
|
* theme$,
|
|
128
171
|
* userId$,
|
|
@@ -135,649 +178,13 @@ type ValueModifier<T = unknown, TCopy = T> = (newValue: T, previousValue: TCopy
|
|
|
135
178
|
* )
|
|
136
179
|
* ```
|
|
137
180
|
*/
|
|
138
|
-
declare function
|
|
139
|
-
declare function
|
|
140
|
-
declare namespace
|
|
141
|
-
var defaults: {
|
|
181
|
+
declare function copyRx<TOut, Source$ extends Observable<any> | unknown[] = Observable<any> | unknown[], TIn = UnwrapSourceValueStrict<Source$>, Copy$ extends BehaviorSubject<TOut> | Subject<TOut> = BehaviorSubject<TOut>, ThisArg = unknown>(source$: Source$, transform?: CopyRx_Transform<TIn, TOut, ThisArg> | null, options?: CopyRx_Options<TOut, ThisArg>): Copy$;
|
|
182
|
+
declare function copyRx<TOut, Source$ extends Observable<any> | unknown[] = Observable<any> | unknown[], TIn = UnwrapSourceValueStrict<Source$>, Copy$ extends BehaviorSubject<TOut> | Subject<TOut> = BehaviorSubject<TOut>, ThisArg = unknown>(source$: Source$, transform: CopyRx_Transform<TIn, TOut, ThisArg>, options?: CopyRx_Options<TOut, ThisArg>): Copy$;
|
|
183
|
+
declare namespace copyRx {
|
|
184
|
+
var defaults: Required<Omit<DeferredOptions<unknown>, "thisArg"> & {
|
|
142
185
|
delay: number;
|
|
143
|
-
}
|
|
144
|
-
var
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** Throttle & debounce related options */
|
|
148
|
-
type DelayOptions = ({
|
|
149
|
-
throttle: true;
|
|
150
|
-
} & Omit<ThrottleOptions, 'onError' | 'thisArg'>) | ({
|
|
151
|
-
throttle?: false;
|
|
152
|
-
} & Omit<DebounceOptions, 'onError' | 'thisArg'>);
|
|
153
|
-
/**
|
|
154
|
-
* Categorizes errors encountered during DataStorage operations.
|
|
155
|
-
*
|
|
156
|
-
* These types are passed to the `onError` callback to help identify which phase of the
|
|
157
|
-
* data lifecycle (reading, writing, or processing) failed.
|
|
158
|
-
*/
|
|
159
|
-
declare enum OnErrorType {
|
|
160
|
-
/** Occurs when the user-provided `onChange` callback throws an exception. */
|
|
161
|
-
onChange = "onChange",
|
|
162
|
-
/** Occurs when the user-provided `parse` function fails to process the raw storage string. */
|
|
163
|
-
parse = "parse",
|
|
164
|
-
/**
|
|
165
|
-
* Occurs when the default `JSON.parse` fallback fails.
|
|
166
|
-
* This usually happens if the data in the underlying storage is corrupted or not valid JSON.
|
|
167
|
-
*/
|
|
168
|
-
parse_json = "parse-json",
|
|
169
|
-
/** Occurs when the user-provided `stringify` function fails to serialize the data Map. */
|
|
170
|
-
stringify = "stringify",
|
|
171
|
-
/**
|
|
172
|
-
* Occurs when the default `JSON.stringify` fallback fails.
|
|
173
|
-
* This may happen if the Map contains circular references or other non-serializable values.
|
|
174
|
-
*/
|
|
175
|
-
stringify_json = "stringify-json",
|
|
176
|
-
/** Occurs when the attempt to save data to the underlying storage (e.g., `localStorage.setItem`) fails. */
|
|
177
|
-
write = "write"
|
|
178
|
-
}
|
|
179
|
-
/** Storage type with only properties that are used by `DataStorage` */
|
|
180
|
-
type StorageCompact = Pick<Storage, 'getItem' | 'setItem'>;
|
|
181
|
-
/** Initial options provided through the constructor */
|
|
182
|
-
type StorageOptions<Key, Value, CacheDisabled extends boolean = false> = {
|
|
183
|
-
/**
|
|
184
|
-
* An optional `Map` used to seed the storage if no persistent data is found for the instance.
|
|
185
|
-
*
|
|
186
|
-
* **Data Precedence:**
|
|
187
|
-
* Persistent data associated with the instance's specific `name` takes priority. This value is
|
|
188
|
-
* only utilized if the storage entry for that `name` does not exist (e.g., first-time use).
|
|
189
|
-
*
|
|
190
|
-
* **Initialization Behavior:**
|
|
191
|
-
* - If provided and non-empty, the instance initializes immediately during construction.
|
|
192
|
-
* - Otherwise, initialization is lazy, occurring upon an explicit `init()` call or the first read/write operation.
|
|
193
|
-
*
|
|
194
|
-
* **Type Inference:**
|
|
195
|
-
* When provided, it enables automatic inference of the `Key` and `Value` generic types.
|
|
196
|
-
* If omitted, these default to `unknown` and `object` respectively, unless explicitly defined.
|
|
197
|
-
*
|
|
198
|
-
* @default undefined
|
|
199
|
-
*/
|
|
200
|
-
initialValue?: Map<Key, Value>;
|
|
201
|
-
} & Pick<Partial<IDataStorage<Key, Value, CacheDisabled>>, 'cacheDisabled' | 'onChange' | 'onError' | 'parse' | 'spaces' | 'storage' | 'stringify'> & (CacheDisabled extends false ? Pick<Partial<IDataStorage<Key, Value, CacheDisabled>>, 'delay' | 'delayOptions'> : {
|
|
202
|
-
delay?: never;
|
|
203
|
-
delayOptions?: never;
|
|
204
|
-
});
|
|
205
|
-
type StorageParseFn<ResultMap, ThisArg> = (this: ThisArg, text: string | null | undefined) => ResultMap | void;
|
|
206
|
-
type StorageSearch<K, V, MatchExact extends boolean = false, AsMap extends boolean = true> = (...args: DropFirst<Parameters<typeof search<K, V, MatchExact, AsMap>>>) => ReturnType<typeof search<K, V, MatchExact, AsMap>>;
|
|
207
|
-
type StorageSort<K, V> = (...args: StorageSortByComparator<K, V> | StorageSortByPropertyName<V> | StorageSortByKey) => Map<K, V>;
|
|
208
|
-
type StorageSortByComparator<K, V> = [
|
|
209
|
-
comparator: Parameters<typeof sort<K, V>>[1],
|
|
210
|
-
options?: StorageSortOptions
|
|
211
|
-
];
|
|
212
|
-
type StorageSortByKey = [byKey: true, options?: StorageSortOptions];
|
|
213
|
-
type StorageSortByPropertyName<V> = [
|
|
214
|
-
propertyName: keyof V & string,
|
|
215
|
-
options?: StorageSortOptions
|
|
216
|
-
];
|
|
217
|
-
type StorageSortOptions = SortOptions & {
|
|
218
|
-
save?: boolean;
|
|
219
|
-
};
|
|
220
|
-
type StorageStringify<Data, ThisArg> = (this: ThisArg, data: Data) => string | undefined | void;
|
|
221
|
-
type StorageToJSON<K, V> = (replacer?: null | ((key: K, value: V) => unknown), spacing?: string | number, data?: Map<K, V>) => string;
|
|
222
|
-
interface IDataStorage<Key, Value, CacheDisabled extends boolean = false> {
|
|
223
|
-
/** Disable in-memory cache and only directly read/write from storage (local storage or JSON fle) */
|
|
224
|
-
readonly cacheDisabled: CacheDisabled;
|
|
225
|
-
/**
|
|
226
|
-
* Debounce/throttle delay duration in milliseconds for writing to storage when caching is enabled.
|
|
227
|
-
*
|
|
228
|
-
* Increasing this value can improve performance when dealing with large datasets
|
|
229
|
-
* or frequent updates by reducing the number of write operations.
|
|
230
|
-
*
|
|
231
|
-
* Default: `300`
|
|
232
|
-
*/
|
|
233
|
-
readonly delay: number;
|
|
234
|
-
readonly delayOptions?: DelayOptions;
|
|
235
|
-
/**
|
|
236
|
-
* Indicates wherether storage has been initialized (`init()` function invoked).
|
|
237
|
-
*/
|
|
238
|
-
readonly initialized: boolean;
|
|
239
|
-
/**
|
|
240
|
-
* Storage name. Filename (NodeJS) or property name (browser LocalStorage).
|
|
241
|
-
* If empty string or undefined, data will not be saved to storage and will only work in-memory.
|
|
242
|
-
*
|
|
243
|
-
* Default: `''`
|
|
244
|
-
*/
|
|
245
|
-
readonly name?: string | null;
|
|
246
|
-
/**
|
|
247
|
-
* A callback function executed whenever a data change occurs within the storage.
|
|
248
|
-
*
|
|
249
|
-
* This hook allows for reactive side-effects. If the callback throws an error or returns a
|
|
250
|
-
* rejected Promise, the exception is caught gracefully and redirected to the {@link onError}
|
|
251
|
-
* callback with the type {@link OnErrorType.onChange}.
|
|
252
|
-
*
|
|
253
|
-
* Note: Execution of this callback is managed by internal subscriptions and will stop
|
|
254
|
-
* firing once {@link unsubscribe} is called.
|
|
255
|
-
*/
|
|
256
|
-
onChange?: (this: IDataStorage<Key, Value, CacheDisabled>, data: Map<Key, Value>) => ValueOrPromise<void | Map<Key, Value>>;
|
|
257
|
-
/**
|
|
258
|
-
* A global error handler invoked whenever an internal operation fails.
|
|
259
|
-
*
|
|
260
|
-
* It captures failures in the following areas:
|
|
261
|
-
* - Data parsing and serialization (JSON or custom logic).
|
|
262
|
-
* - Storage access (e.g., `localStorage` quota or permission errors).
|
|
263
|
-
* - Execution of user-provided callbacks like {@link onChange}.
|
|
264
|
-
*
|
|
265
|
-
* **Note:** If this handler itself throws an error, the exception is
|
|
266
|
-
* ignored gracefully to prevent application crashes during storage cycles.
|
|
267
|
-
*/
|
|
268
|
-
onError?: (this: IDataStorage<Key, Value, CacheDisabled>, err: unknown, type: OnErrorType) => ValueOrPromise<void>;
|
|
269
|
-
/**
|
|
270
|
-
* A callback to customize the deserialization of data read from storage.
|
|
271
|
-
*
|
|
272
|
-
* This allows you to transform the raw string from the underlying storage back into a
|
|
273
|
-
* `Map<Key, Value>`. It serves as the functional inverse of {@link stringify}.
|
|
274
|
-
*
|
|
275
|
-
* **Fallback Behavior:** The system falls back to internal `JSON.parse` logic if `parse`:
|
|
276
|
-
* - is not a non-function
|
|
277
|
-
* - throws an error
|
|
278
|
-
* - returns a non-Map value,
|
|
279
|
-
*
|
|
280
|
-
* **Error Triggers:**
|
|
281
|
-
* - If this custom `parse` function fails: {@link onError} is triggered with {@link OnErrorType.parse}.
|
|
282
|
-
* - If the default `JSON.parse` fallback fails: {@link onError} is triggered with {@link OnErrorType.parse_json}.
|
|
283
|
-
*/
|
|
284
|
-
parse?: StorageParseFn<Map<Key, Value>, IDataStorage<Key, Value, CacheDisabled>>;
|
|
285
|
-
/** Get the number of items */
|
|
286
|
-
readonly size: number;
|
|
287
|
-
/** Number of spaces to use when stringifying. Default: `undefined` */
|
|
288
|
-
spaces?: number;
|
|
289
|
-
/**
|
|
290
|
-
* `LocalStorage` or equivalent storage instance to be used as the underlying storage and to read & write from.
|
|
291
|
-
*
|
|
292
|
-
* Notes:
|
|
293
|
-
* - Ignored when `name` is falsy (in-memory only mode)
|
|
294
|
-
* - For NodeJS or equivalent, an instance of `LocalStorage` from "node-localstoarge" NPM module can be used.
|
|
295
|
-
* - If `undefined`, will not attempt to use `globalThis.localStorage`, if available
|
|
296
|
-
* - If `null`, will not attempt to use `globalThis.localStorage`
|
|
297
|
-
*
|
|
298
|
-
* Default:
|
|
299
|
-
* - browser: `localStorage`
|
|
300
|
-
* - node: `undefined` (in-memory mode)
|
|
301
|
-
*/
|
|
302
|
-
readonly storage?: StorageCompact | null;
|
|
303
|
-
/**
|
|
304
|
-
* A callback function to customize the serialization of data before it is written to storage.
|
|
305
|
-
*
|
|
306
|
-
* This allows you to transform the data `Map<Key, Value>` into a string format suitable
|
|
307
|
-
* for the underlying storage (e.g., JSON). It serves as the functional inverse of {@link parse}.
|
|
308
|
-
*
|
|
309
|
-
* Use this to sanitize data, remove circular references, or optimize the storage size by
|
|
310
|
-
* only persisting necessary fields.
|
|
311
|
-
*
|
|
312
|
-
* **Fallback Behavior:**
|
|
313
|
-
* If this function is not defined, throws an error, or returns `undefined` or a non-string value,
|
|
314
|
-
* the system falls back to internal `JSON.stringify` logic.
|
|
315
|
-
*
|
|
316
|
-
* **Error Triggers:**
|
|
317
|
-
* - If this custom `stringify` function fails: {@link onError} is triggered with {@link OnErrorType.stringify}.
|
|
318
|
-
* - If the default `JSON.stringify` fallback fails: {@link onError} is triggered with
|
|
319
|
-
* {@link OnErrorType.stringify_json}.
|
|
320
|
-
*
|
|
321
|
-
* @param data a map of all values stored in this storage
|
|
322
|
-
*
|
|
323
|
-
* @returns string or undefined
|
|
324
|
-
*
|
|
325
|
-
* @example
|
|
326
|
-
* #### Sanitize data before saving
|
|
327
|
-
* ```javascript
|
|
328
|
-
* import { DataStorage } from '@superutils/rx'
|
|
329
|
-
*
|
|
330
|
-
* const stringify = data => {
|
|
331
|
-
* // Convert Map to an array of entries, removing sensitive fields
|
|
332
|
-
* const entries = Array.from(data).map(([id, user]) => {
|
|
333
|
-
* const { password, ...publicData } = user
|
|
334
|
-
* return [id, publicData]
|
|
335
|
-
* })
|
|
336
|
-
* return JSON.stringify(entries)
|
|
337
|
-
* }
|
|
338
|
-
* const storage = new DataStorage('users', { stringify })
|
|
339
|
-
* ```
|
|
340
|
-
*/
|
|
341
|
-
stringify?: StorageStringify<Map<Key, Value>, IDataStorage<Key, Value, CacheDisabled>>;
|
|
342
|
-
/**
|
|
343
|
-
* The underlying RxJS Subject that serves as the primary reactive interface for observing data modifications.
|
|
344
|
-
*
|
|
345
|
-
* Its implementation type is determined by the caching strategy:
|
|
346
|
-
* - **BehaviorSubject**: Used when caching is enabled.
|
|
347
|
-
* It maintains the current state and emits it immediately to new subscribers.
|
|
348
|
-
* - **Subject**: Used when caching is disabled.
|
|
349
|
-
* It acts as a pure event pipe, emitting updates only at the moment they occur without retaining an in-memory copy.
|
|
350
|
-
*/
|
|
351
|
-
readonly subject: CacheDisabled extends true ? Subject<Map<Key, Value>> : BehaviorSubject<Map<Key, Value>>;
|
|
352
|
-
/** Clear all items */
|
|
353
|
-
readonly clear: () => IDataStorage<Key, Value, CacheDisabled>;
|
|
354
|
-
/** Delete one or more items by their respective keys */
|
|
355
|
-
readonly delete: (key: Key | Key[]) => IDataStorage<Key, Value, CacheDisabled>;
|
|
356
|
-
/** Filter items by predicate */
|
|
357
|
-
readonly filter: <AsArray extends boolean = false>(...args: DropFirst<Parameters<typeof filter<Key, Value, AsArray>>>) => ReturnType<typeof filter<Key, Value, AsArray>>;
|
|
358
|
-
/** Find an item by predicate or search criteria */
|
|
359
|
-
readonly find: <IncludeKey extends boolean = false>(predicateOrOptions: FindOptions<Key, Value, IncludeKey> | Parameters<IDataStorage<Key, Value, CacheDisabled>['filter']>[0]) => ReturnType<typeof find<Key, Value, IncludeKey>>;
|
|
360
|
-
/** Get item by key */
|
|
361
|
-
readonly get: (key: Key) => Value | undefined;
|
|
362
|
-
/**
|
|
363
|
-
* Get all items
|
|
364
|
-
*
|
|
365
|
-
* @param forceUpdate (optional) if `true` and cache is enabled, reads & updates data directly from storage
|
|
366
|
-
* Default: `false`
|
|
367
|
-
*/
|
|
368
|
-
readonly getAll: (forceUpdate?: boolean) => Map<Key, Value>;
|
|
369
|
-
/** Check if key exists */
|
|
370
|
-
readonly has: (key: Key) => boolean;
|
|
371
|
-
/**
|
|
372
|
-
* Initializes storage and sets up internal subscriptions.
|
|
373
|
-
*
|
|
374
|
-
* Manual invocation is not typically necessary, as initialization occurs automatically
|
|
375
|
-
* in one of the following scenarios:
|
|
376
|
-
* - During construction, if an `initialValue` with at least one entry is provided.
|
|
377
|
-
* - On the first attempt to read or write data.
|
|
378
|
-
*
|
|
379
|
-
* @param initialValue An optional map to initialize the storage with if it's currently empty.
|
|
380
|
-
* @returns `true` if initialization was successful, or `false` if the storage was already initialized.
|
|
381
|
-
*/
|
|
382
|
-
readonly init: (initialValue?: Map<Key, Value>) => boolean;
|
|
383
|
-
/** Get all keys */
|
|
384
|
-
readonly keys: () => Key[];
|
|
385
|
-
/** Map each item on the data to an Array */
|
|
386
|
-
readonly map: <T = unknown>(callback: (value: Value, key: Key, entries: [Key, Value][], index: number) => T) => T[];
|
|
387
|
-
/**
|
|
388
|
-
* Reads and parses data directly from the persistent storage medium.
|
|
389
|
-
*
|
|
390
|
-
* This operation is synchronous and does not trigger reactive updates via `subject`.
|
|
391
|
-
* It is useful for debugging custom `parse` logic or manual data retrieval.
|
|
392
|
-
*
|
|
393
|
-
* @param dataStr (optional) A raw string to parse. If omitted, the method fetches
|
|
394
|
-
* the current value associated with the instance `name` from the underlying `storage`.
|
|
395
|
-
*/
|
|
396
|
-
readonly read: (dataStr?: string | null) => Map<Key, Value>;
|
|
397
|
-
/**
|
|
398
|
-
* Search through the stored data (`Map<Key, Value>`).
|
|
399
|
-
* It supports both a global search (using a string or RegExp) across all properties
|
|
400
|
-
* of an item, and a detailed, field-specific search using a query object.
|
|
401
|
-
*
|
|
402
|
-
* @param options The search criteria. See {@link SearchOptions} for available properties.
|
|
403
|
-
*
|
|
404
|
-
* @returns A `Map` or an `Array` containing the matched items, based on the `asMap` option.
|
|
405
|
-
*
|
|
406
|
-
* @example
|
|
407
|
-
* #### Search for users in a specific city
|
|
408
|
-
* ```javascript
|
|
409
|
-
* import { DataStorage } from '@superutils/rx'
|
|
410
|
-
*
|
|
411
|
-
* const storage = new DataStorage('users', {
|
|
412
|
-
* initialValue: new Map([
|
|
413
|
-
* [1, { name: 'John Doe', city: 'New York' }],
|
|
414
|
-
* [2, { name: 'Jane Doe', city: 'London' }],
|
|
415
|
-
* [3, { name: 'Peter Jones', city: 'New York' }],
|
|
416
|
-
* ])
|
|
417
|
-
* })
|
|
418
|
-
*
|
|
419
|
-
* const nyUsers = storage.search({ query: { city: 'New York' } })
|
|
420
|
-
* console.log(nyUsers.size) // 2
|
|
421
|
-
* ```
|
|
422
|
-
*/
|
|
423
|
-
readonly search: <MatchExact extends boolean = false, AsMap extends boolean = true>(...args: DropFirst<Parameters<typeof search<Key, Value, MatchExact, AsMap>>>) => ReturnType<typeof search<Key, Value, MatchExact, AsMap>>;
|
|
424
|
-
/** Set item by key */
|
|
425
|
-
readonly set: <K extends Key, V extends Value>(key: K, value: V) => IDataStorage<Key, Value, CacheDisabled>;
|
|
426
|
-
/**
|
|
427
|
-
* Set multiple entries at once and/or replace the storage entries
|
|
428
|
-
*
|
|
429
|
-
* @param data (optional) Data to add. Default: `new Map()`
|
|
430
|
-
* @param replace (optional) Whether to merge with or replace current data.
|
|
431
|
-
* - `true`: replace all entries with `data`
|
|
432
|
-
* - `false`: merge with current data (existing entries with matching keys will be overwritten)
|
|
433
|
-
*
|
|
434
|
-
* Default: `false`
|
|
435
|
-
*/
|
|
436
|
-
readonly setAll: (data?: Map<Key, Value>, replace?: boolean) => IDataStorage<Key, Value, CacheDisabled>;
|
|
437
|
-
/**
|
|
438
|
-
* Sort items in the storage.
|
|
439
|
-
*
|
|
440
|
-
* @param nameOrComparator Criteria to sort by. Accepts one of the following:
|
|
441
|
-
* - `function`: A comparator function to sort the data.
|
|
442
|
-
* - `string`: A property name of the value object to sort by.
|
|
443
|
-
* - `true`: Sorts the map by its keys.
|
|
444
|
-
* @param options (optional) Sorting options.
|
|
445
|
-
* @param options.save (optional) Whether to save the sorted data back to storage (localStorage/file).
|
|
446
|
-
*
|
|
447
|
-
* @returns The sorted Map.
|
|
448
|
-
*/
|
|
449
|
-
readonly sort: StorageSort<Key, Value>;
|
|
450
|
-
/** Convert list of items (Map) to 2D Array */
|
|
451
|
-
readonly toArray: () => [Key, Value][];
|
|
452
|
-
/** Convert list of items (Map) to JSON string of 2D Array */
|
|
453
|
-
readonly toJSON: StorageToJSON<Key, Value>;
|
|
454
|
-
/** Convert list of items into an object */
|
|
455
|
-
readonly toObject: <T extends object = object>(data?: Map<Key, Value>) => T;
|
|
456
|
-
/** Convert list of items (Map) to JSON string of 2D Array */
|
|
457
|
-
readonly toString: (data?: Map<Key, Value>) => string;
|
|
458
|
-
/**
|
|
459
|
-
* Unsubscribe from all internal subscriptions.
|
|
460
|
-
*
|
|
461
|
-
* This will result in:
|
|
462
|
-
* - Automatic writing to storage being disabled (manual writes via `instance.write()` will still work).
|
|
463
|
-
* - The `onChange` callback no longer being triggered.
|
|
464
|
-
* - The instance stopping listening to force update cache triggers.
|
|
465
|
-
*/
|
|
466
|
-
readonly unsubscribe: () => void;
|
|
467
|
-
/** Get all values as an array */
|
|
468
|
-
readonly values: () => Value[];
|
|
469
|
-
/**
|
|
470
|
-
* Write data to the underlying storage (localStorage or file).
|
|
471
|
-
*
|
|
472
|
-
* @param data (optional) Data to write.
|
|
473
|
-
* - If provided, it overwrites the storage.
|
|
474
|
-
* - If not provided, the current in-memory data is used (if cache is enabled).
|
|
475
|
-
* @returns `true` if the write was successful, `false` otherwise.
|
|
476
|
-
*/
|
|
477
|
-
readonly write: (data?: Map<Key, Value>) => void;
|
|
478
|
-
}
|
|
479
|
-
interface IObjectStorage<T extends object, CacheDisabled extends boolean = false, ObjectMap extends TypedMap<T> = TypedMap<T>> extends IDataStorage<keyof T, T[keyof T], CacheDisabled> {
|
|
480
|
-
get<Key extends keyof T>(key: Key): T[Key] | undefined;
|
|
481
|
-
getAll(forceRead?: boolean): TypedMap<T>;
|
|
482
|
-
parse?: StorageParseFn<ObjectMap, IObjectStorage<T, CacheDisabled>>;
|
|
483
|
-
set<Key extends keyof T, Value extends T[Key]>(key: Key, value: Value): IObjectStorage<T, CacheDisabled>;
|
|
484
|
-
setAll(data?: ObjectMap, replace?: boolean): IObjectStorage<T, CacheDisabled>;
|
|
485
|
-
stringify?: StorageStringify<ObjectMap, IObjectStorage<T, CacheDisabled>>;
|
|
486
|
-
toObject<O extends object = T>(data?: Map<keyof T, T[keyof T]>): O;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* RxJS Subject to trigger forced update of cached data from underlying storage of {@link DataStorage} instances.
|
|
491
|
-
*
|
|
492
|
-
* `value`: determines which cache-enabled storage instances to be updated
|
|
493
|
-
* - name (`string` | `string[]`): update all instances with a specific name(s)
|
|
494
|
-
* - global (`true`): update all instances globally
|
|
495
|
-
*
|
|
496
|
-
* @example
|
|
497
|
-
* ```javascript
|
|
498
|
-
* import { DataStorage, forceUpdateCache$ } from '@superutils/rx'
|
|
499
|
-
*
|
|
500
|
-
* const names = ['products', 'users']
|
|
501
|
-
*
|
|
502
|
-
* // Update all DataStorage instances by a list of their names
|
|
503
|
-
* forceUpdateCache$.next(names)
|
|
504
|
-
* // alternatively: DataStorage.forceUpdateCache(names)
|
|
505
|
-
*
|
|
506
|
-
* // Update all DataStorage instances with a specific name
|
|
507
|
-
* forceUpdateCache$.next(names[0])
|
|
508
|
-
* // alternatively: DataStorage.forceUpdateCache(names[0])
|
|
509
|
-
*
|
|
510
|
-
* // Update every single instance of DataStorage that uses storage (has a "name" property)
|
|
511
|
-
* forceUpdateCache$(true)
|
|
512
|
-
* // alternatively: DataStorage.forceUpdateCache(true)
|
|
513
|
-
* ```
|
|
514
|
-
*
|
|
515
|
-
* @example
|
|
516
|
-
*
|
|
517
|
-
* #### Practical example
|
|
518
|
-
* ```typescript
|
|
519
|
-
* import { DataStorage } from '@superutils/rx'
|
|
520
|
-
*
|
|
521
|
-
* const name = 'user-profile'
|
|
522
|
-
* type User = {
|
|
523
|
-
* age: number
|
|
524
|
-
* name: string
|
|
525
|
-
* roles?: string[]
|
|
526
|
-
* }
|
|
527
|
-
* const userStore = DataStorage.(name, { delay: 0 }) // delay is set to zero to simplify the example
|
|
528
|
-
* userStore.set('name', 'John Doe')
|
|
529
|
-
* userStore.set('name', 'John Doe')
|
|
530
|
-
* ```
|
|
531
|
-
*/
|
|
532
|
-
declare const forceUpdateCache$: Subject<string | boolean | string[]>;
|
|
533
|
-
/**
|
|
534
|
-
*
|
|
535
|
-
*
|
|
536
|
-
* @remarks
|
|
537
|
-
* **On the `This` template parameter:**
|
|
538
|
-
* Using `This` as a self-referential template is a **good practice** in this context because:
|
|
539
|
-
* - It provides accurate **type inference** for method return types that depend on the generic parameters.
|
|
540
|
-
* - It enables **type-safe property access** through `This['methodName']`, allowing the implementation
|
|
541
|
-
* to reference interface contracts without circular dependencies or casting issues.
|
|
542
|
-
* - It allows **fluent API chains** (returning `this`) while maintaining proper generic type information.
|
|
543
|
-
* - It prevents **type widening** that would occur if methods returned the concrete class type instead
|
|
544
|
-
* of the interface type, which is important for generic constraints and polymorphism.
|
|
545
|
-
*
|
|
546
|
-
* However, it increases **cognitive complexity** and is only warranted when:
|
|
547
|
-
* - The class implements a complex generic interface with interdependent type parameters.
|
|
548
|
-
* - Type-safe property references are essential to avoid runtime errors or casting.
|
|
549
|
-
* - Fluent interfaces or chaining is a core API feature.
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*/
|
|
553
|
-
/**
|
|
554
|
-
* A generic, reactive data storage class that provides a Map-like interface with advanced features
|
|
555
|
-
* such as search, filtering, and sorting. Supports both in-memory caching and persistent storage
|
|
556
|
-
* (LocalStorage in browsers, JSON files in NodeJS via `node-localstorage` NPM module).
|
|
557
|
-
*
|
|
558
|
-
* #### Notes:
|
|
559
|
-
* - **Performance**: `DataStorage` is optimized for small to medium datasets.
|
|
560
|
-
* - For datasets > 1MB, consider increasing the `delay` option to reduce write frequency.
|
|
561
|
-
* - It is **NOT** recommended for datasets larger than 3MB due to synchronous serialization costs.
|
|
562
|
-
* - **RxJS Integration**: Built on RxJS for reactive data handling, though no prior RxJS knowledge is required.
|
|
563
|
-
* - **Storage Behavior**:
|
|
564
|
-
* - If `name` is omitted, the instance operates in-memory only and data is not persisted to storage.
|
|
565
|
-
* - If `cacheDisabled` is `true`, data is not kept in memory; every read/write operation accesses the underlying
|
|
566
|
-
* storage directly.
|
|
567
|
-
*
|
|
568
|
-
* @template Key The type of keys stored in the map.
|
|
569
|
-
* @template Value The type of values stored in the map.
|
|
570
|
-
* @template CacheDisabled A literal boolean type indicating whether in-memory caching is disabled.
|
|
571
|
-
* @template This A self-referential interface type extending {@link IDataStorage} used for accurate
|
|
572
|
-
* method signature inference and type-safe property access. This allows method implementations to
|
|
573
|
-
* reference their return types and other method signatures through the interface definition.
|
|
574
|
-
*
|
|
575
|
-
* @see {@link forceUpdateCache$} for cache invalidation across instances.
|
|
576
|
-
* @see {@link DataStorage.fromObject} for object-oriented storage initialization.
|
|
577
|
-
*
|
|
578
|
-
* @example
|
|
579
|
-
* #### Browser Usage 1: use like a map
|
|
580
|
-
* ```javascript
|
|
581
|
-
* import { DataStorage } from '@superutils/rx'
|
|
582
|
-
*
|
|
583
|
-
* const userStorage = new DataStorage('users')
|
|
584
|
-
* userStorage.set(1, { name: 'Alice', age: 30 })
|
|
585
|
-
* const user = userStorage.get(1)
|
|
586
|
-
* console.log(user) // prints: {name: 'Alice', age: 30}
|
|
587
|
-
* ```
|
|
588
|
-
*
|
|
589
|
-
* @example
|
|
590
|
-
* #### Browser Usage 2:
|
|
591
|
-
* ```javascript
|
|
592
|
-
* import { DataStorage } from '@superutils/rx'
|
|
593
|
-
* import fetch from '@superutils/fetch'
|
|
594
|
-
*
|
|
595
|
-
* const { products } = await fetch.get('[DUMMYJSON-DOT-COM]/products')
|
|
596
|
-
* const storage = new DataStorage('products', {
|
|
597
|
-
* initialValue: new Map(products.map(p => [p.id, p])) // convert to Map
|
|
598
|
-
* })
|
|
599
|
-
*
|
|
600
|
-
* // print product with id `1`
|
|
601
|
-
* console.log(storage.get(1))
|
|
602
|
-
*
|
|
603
|
-
* // search for items
|
|
604
|
-
* const searchResult = storage.search({
|
|
605
|
-
* query: { availabilityStatus: 'low' }
|
|
606
|
-
* })
|
|
607
|
-
* console.log(searchResult)
|
|
608
|
-
* ```
|
|
609
|
-
* @example
|
|
610
|
-
* #### NodeJS Usage
|
|
611
|
-
* ```javascript
|
|
612
|
-
* import { DataStorage } from '@superutils/rx'
|
|
613
|
-
* import fetch from '@superutils/fetch'
|
|
614
|
-
* import { LocalStorage } from 'node-localstorage'
|
|
615
|
-
*
|
|
616
|
-
* // Add localStorage alternative for NodeJS that reads and writes to JSON files.
|
|
617
|
-
* // This is not necessary for browsers.
|
|
618
|
-
* globalThis.localStorage = new LocalStorage('./data', 1e7)
|
|
619
|
-
*
|
|
620
|
-
* const storage = new DataStorage('products')
|
|
621
|
-
* const { products } = await fetch.get('[DUMMYJSON-DOT-COM]/products')
|
|
622
|
-
* // save all items to storage
|
|
623
|
-
* storage.setAll(
|
|
624
|
-
* new Map(products.map(p => [p.id, p])), // convert to Map
|
|
625
|
-
* )
|
|
626
|
-
*
|
|
627
|
-
* // print product with id `1`
|
|
628
|
-
* console.log(storage.get(1))
|
|
629
|
-
*
|
|
630
|
-
* // search for items
|
|
631
|
-
* const searchResult = storage.search({
|
|
632
|
-
* query: { availabilityStatus: 'low' }
|
|
633
|
-
* })
|
|
634
|
-
* console.log(searchResult)
|
|
635
|
-
* ```
|
|
636
|
-
*
|
|
637
|
-
* @example
|
|
638
|
-
* #### Advanced: `onChange` and RxJS subject
|
|
639
|
-
*
|
|
640
|
-
* Internally, `DataStorage` uses RxJS subject which is exposed as `subject` property.
|
|
641
|
-
* You can use this to subscribe to changes and do additional operations such as logging or sanitization etc.
|
|
642
|
-
*
|
|
643
|
-
* Alternatively, you can also set the `onChange` callback which is triggered whenever the subject changes and
|
|
644
|
-
* does not require maintaining a subscription or knowledge of RxJS subject.
|
|
645
|
-
*
|
|
646
|
-
* ```javascript
|
|
647
|
-
* import { DataStorage } from '@superutils/rx'
|
|
648
|
-
*
|
|
649
|
-
* const storage = new DataStorage('my-data')
|
|
650
|
-
* const sub = storage.subject.subscribe(data => {
|
|
651
|
-
* // Write to the database whenever data changes
|
|
652
|
-
* console.log('Saving to database...', data)
|
|
653
|
-
* })
|
|
654
|
-
* // unsubscribe from subject
|
|
655
|
-
* setTimeout(()=> sub.unsubscribe(), 1000)
|
|
656
|
-
*
|
|
657
|
-
* // add an entry to storage
|
|
658
|
-
* storage.set('bob', { age: 99, id: 'bob', name: 'Bob' })
|
|
659
|
-
* ```
|
|
660
|
-
*/
|
|
661
|
-
declare class DataStorage<Key, Value, CacheDisabled extends boolean = false,
|
|
662
|
-
/**
|
|
663
|
-
* @remarks
|
|
664
|
-
* **On the `This` template parameter:**
|
|
665
|
-
* Using `This` as a self-referential template is a **good practice** in this context because:
|
|
666
|
-
* - It provides accurate **type inference** for method return types that depend on the generic parameters.
|
|
667
|
-
* - It enables **type-safe property access** through `This['methodName']`, allowing the implementation
|
|
668
|
-
* to reference interface contracts without circular dependencies or casting issues.
|
|
669
|
-
* - It allows **fluent API chains** (returning `this`) while maintaining proper generic type information.
|
|
670
|
-
* - It prevents **type widening** that would occur if methods returned the concrete class type instead
|
|
671
|
-
* of the interface type, which is important for generic constraints and polymorphism.
|
|
672
|
-
*
|
|
673
|
-
* However, it increases **cognitive complexity** and is only warranted when:
|
|
674
|
-
* - The class implements a complex generic interface with interdependent type parameters.
|
|
675
|
-
* - Type-safe property references are essential to avoid runtime errors or casting.
|
|
676
|
-
* - Fluent interfaces or chaining is a core API feature.
|
|
677
|
-
*/
|
|
678
|
-
This extends IDataStorage<Key, Value, CacheDisabled> = IDataStorage<Key, Value, CacheDisabled>> implements IDataStorage<Key, Value, CacheDisabled> {
|
|
679
|
-
readonly cacheDisabled: This['cacheDisabled'];
|
|
680
|
-
readonly delay: This['delay'];
|
|
681
|
-
/** Debounce and throttle related options */
|
|
682
|
-
readonly delayOptions?: This['delayOptions'];
|
|
683
|
-
readonly initialized: This['initialized'];
|
|
684
|
-
readonly name: This['name'];
|
|
685
|
-
onChange?: This['onChange'];
|
|
686
|
-
onError?: This['onError'];
|
|
687
|
-
parse?: This['parse'];
|
|
688
|
-
get size(): number;
|
|
689
|
-
spaces?: This['spaces'];
|
|
690
|
-
readonly storage?: This['storage'];
|
|
691
|
-
stringify?: This['stringify'];
|
|
692
|
-
readonly subject: This['subject'];
|
|
693
|
-
private subscriptions;
|
|
694
|
-
constructor(name?: This['name'], options?: StorageOptions<Key, Value, CacheDisabled>);
|
|
695
|
-
clear: This['clear'];
|
|
696
|
-
delete: This['delete'];
|
|
697
|
-
filter: This['filter'];
|
|
698
|
-
find: This['find'];
|
|
699
|
-
/**
|
|
700
|
-
* Creates a {@link DataStorage} instance initialized from a plain object.
|
|
701
|
-
*
|
|
702
|
-
* This factory method automatically configures `parse` and `stringify` logic to
|
|
703
|
-
* treat the underlying storage as a serialized object, while providing a
|
|
704
|
-
* type-safe Map-like interface for individual properties.
|
|
705
|
-
*
|
|
706
|
-
* This default behavior can be overridden by providing custom `parse` and `stringify` implementations in `options`.
|
|
707
|
-
*
|
|
708
|
-
* @param name (optional) The name for the storage (e.g., localStorage key or filename).
|
|
709
|
-
* @param options (optional) Configuration options for the storage instance.
|
|
710
|
-
* @param options.initialValue (optional) An optional object to populate the storage if it's currently empty.
|
|
711
|
-
*
|
|
712
|
-
* @template T (optional) The structure of the object being stored. Can auto-infer from `options.initialValue`.
|
|
713
|
-
* @template CacheDisabled (optional) Literal type determining whether to disable in-memory caching.
|
|
714
|
-
*
|
|
715
|
-
* @returns A new DataStorage instance mapped to the object's keys and values.
|
|
716
|
-
*
|
|
717
|
-
* @example
|
|
718
|
-
* #### Store and access a User object
|
|
719
|
-
* ```typescript
|
|
720
|
-
* import { DataStorage } from '@superutils/rx'
|
|
721
|
-
*
|
|
722
|
-
* interface User {
|
|
723
|
-
* age: number;
|
|
724
|
-
* name: string;
|
|
725
|
-
* }
|
|
726
|
-
*
|
|
727
|
-
* const initialValue: User = {
|
|
728
|
-
* age: 99,
|
|
729
|
-
* name: 'Ninety Nine'
|
|
730
|
-
* }
|
|
731
|
-
*
|
|
732
|
-
* // Initialize storage from the object
|
|
733
|
-
* const storage = DataStorage.fromObject<User>('user-profile', { initialValue })
|
|
734
|
-
*
|
|
735
|
-
* // Keys are inferred from the User interface
|
|
736
|
-
* const name = storage.get('name') // Inferred as: string | undefined
|
|
737
|
-
* console.log(name) // Prints: 'Ninety Nine'
|
|
738
|
-
*
|
|
739
|
-
* // Update properties safely
|
|
740
|
-
* storage.set('age', 100)
|
|
741
|
-
*
|
|
742
|
-
* // Reconstruct the updated object
|
|
743
|
-
* const userObj = storage.toObject<User>()
|
|
744
|
-
* console.log(userObj) // { age: 100, name: 'Ninety Nine' }
|
|
745
|
-
* ```
|
|
746
|
-
*/
|
|
747
|
-
static fromObject: <T extends object, Key_1 extends keyof T = keyof T, Value_1 extends T[Key_1] = T[Key_1], CacheDisabled_1 extends boolean = false>(name?: string | null, options?: Omit<StorageOptions<Key_1, Value_1, CacheDisabled_1>, "initialValue"> & {
|
|
748
|
-
initialValue?: T;
|
|
749
|
-
}) => IObjectStorage<T, CacheDisabled_1>;
|
|
750
|
-
/**
|
|
751
|
-
* Trigger forced update of cached data from storage.
|
|
752
|
-
*
|
|
753
|
-
* @param name determines which cache-enabled storage instances to be updated.
|
|
754
|
-
* - name (`string` | `string[]`): update all instances with a specific name(s)
|
|
755
|
-
* - global (`true`): update all instances globally
|
|
756
|
-
*
|
|
757
|
-
* See {@link forceUpdateCache$} for more details.
|
|
758
|
-
*/
|
|
759
|
-
static forceUpdateCache: (name: string | string[] | true) => void;
|
|
760
|
-
get: This['get'];
|
|
761
|
-
getAll: This['getAll'];
|
|
762
|
-
private handleForceUpdateCacheChange;
|
|
763
|
-
private handleSubjectChange;
|
|
764
|
-
has: This['has'];
|
|
765
|
-
init: This['init'];
|
|
766
|
-
keys: This['keys'];
|
|
767
|
-
map: This['map'];
|
|
768
|
-
read: This['read'];
|
|
769
|
-
search: This['search'];
|
|
770
|
-
set: This['set'];
|
|
771
|
-
setAll: This['setAll'];
|
|
772
|
-
sort: This['sort'];
|
|
773
|
-
toArray: This['toArray'];
|
|
774
|
-
toJSON: This['toJSON'];
|
|
775
|
-
toObject: This['toObject'];
|
|
776
|
-
toString: This['toString'];
|
|
777
|
-
private triggerOnError;
|
|
778
|
-
unsubscribe: This['unsubscribe'];
|
|
779
|
-
values: This['values'];
|
|
780
|
-
write: This['write'];
|
|
186
|
+
}>;
|
|
187
|
+
var IGNORE: typeof IGNORE_UPDATE_SYMBOL;
|
|
781
188
|
}
|
|
782
189
|
|
|
783
190
|
/**
|
|
@@ -942,16 +349,6 @@ declare class IntervalRunner<TResult = unknown, TArgs extends unknown[] = unknow
|
|
|
942
349
|
stop: (resetRunCount?: boolean) => this;
|
|
943
350
|
}
|
|
944
351
|
|
|
945
|
-
/**
|
|
946
|
-
* Check if value is similar to a RxJS subject with .subscribe & .next functions
|
|
947
|
-
*
|
|
948
|
-
* @param x The value to check
|
|
949
|
-
* @param withValue When `true`, also checks if `value` property exists in `x`
|
|
950
|
-
*
|
|
951
|
-
* @returns `true` if the value is subject-like, `false` otherwise.
|
|
952
|
-
*/
|
|
953
|
-
declare const isSubjectLike: <T>(x: unknown, withValue?: boolean) => x is SubjectLike<T>;
|
|
954
|
-
|
|
955
352
|
/**
|
|
956
353
|
* Check if value is an instance of RxJS `Subscription` or subscription-like object
|
|
957
354
|
*
|
|
@@ -961,10 +358,10 @@ declare const isSubjectLike: <T>(x: unknown, withValue?: boolean) => x is Subjec
|
|
|
961
358
|
declare const isSubscriptionLike: (value: unknown, strict?: boolean) => value is Subscription;
|
|
962
359
|
|
|
963
360
|
/**
|
|
964
|
-
*
|
|
965
|
-
*
|
|
966
|
-
* @param
|
|
361
|
+
* Unsubscribe from one or mroe RxJS subscriptions
|
|
362
|
+
*
|
|
363
|
+
* @param candidate RxJS subscription, unsubscribe function or mix of both in array/object
|
|
967
364
|
*/
|
|
968
|
-
declare const unsubscribeAll: (
|
|
365
|
+
declare const unsubscribeAll: (candidate?: UnsubscribeCandidate, onError?: (err: unknown) => void) => void;
|
|
969
366
|
|
|
970
|
-
export { type
|
|
367
|
+
export { type AsPromise_Defaults, type AsPromise_Options, type CopyRx_Options, type CopyRx_Transform, IGNORE_UPDATE_SYMBOL, IntervalRunner, IntervalSubject, type OnResultType, type Unsubscribe, type UnsubscribeCandidate, type UnwrapSourceValue, type UnwrapSourceValueStrict, asPromise, copyRx, isSubscriptionLike, type onBeforeExecType, unsubscribeAll };
|