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