@asaidimu/utils-remote-store 1.0.0 → 1.2.0
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/index.d.mts +46 -264
- package/index.d.ts +46 -264
- package/index.js +1 -1
- package/index.mjs +1 -1
- package/package.json +2 -2
package/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { QueryCache, CacheMetrics } from '../cache/index.mjs';
|
|
2
|
-
import '../types-
|
|
2
|
+
import '../types-ZDD-bdCz.js';
|
|
3
3
|
|
|
4
4
|
interface StoreErrorDetails {
|
|
5
5
|
code: string;
|
|
@@ -205,7 +205,9 @@ interface PagedQueryResult<T> {
|
|
|
205
205
|
/** Function to fetch a specific page of data.
|
|
206
206
|
* @param page The page number to fetch.
|
|
207
207
|
*/
|
|
208
|
-
|
|
208
|
+
navigate: (page: number) => Promise<void>;
|
|
209
|
+
refresh: (delay?: number) => Promise<void>;
|
|
210
|
+
changeParams: (setter: (params: any) => any) => Promise<void>;
|
|
209
211
|
}
|
|
210
212
|
/**
|
|
211
213
|
* Represents a basic record in the remote store.
|
|
@@ -230,26 +232,6 @@ type PaginationInfo = {
|
|
|
230
232
|
* Represents the name of a resource in the store, e.g., 'todos', 'users'.
|
|
231
233
|
*/
|
|
232
234
|
type ResourceName = string;
|
|
233
|
-
/**
|
|
234
|
-
* Represents the reactive result of a single record query.
|
|
235
|
-
* @template T The type of the data being queried.
|
|
236
|
-
*/
|
|
237
|
-
interface ReactiveQueryResult<T> {
|
|
238
|
-
/** The current query result, including data, loading state, and errors. */
|
|
239
|
-
value: () => QueryResult<T>;
|
|
240
|
-
/** A function to subscribe to changes in the query result. Returns an unsubscribe function. */
|
|
241
|
-
onValueChange: (callback: () => void) => () => void;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Represents the reactive result of a paginated query (list or find).
|
|
245
|
-
* @template T The type of the records in the page.
|
|
246
|
-
*/
|
|
247
|
-
interface ReactivePagedQueryResult<T> {
|
|
248
|
-
/** The current paginated query result, including page data, loading state, and errors. */
|
|
249
|
-
value: () => PagedQueryResult<T>;
|
|
250
|
-
/** A function to subscribe to changes in the query result. Returns an unsubscribe function. */
|
|
251
|
-
onValueChange: (callback: () => void) => () => void;
|
|
252
|
-
}
|
|
253
235
|
/**
|
|
254
236
|
* Internal interface representing an active subscription to a query.
|
|
255
237
|
* @template T The type of the data being queried.
|
|
@@ -267,285 +249,84 @@ interface QuerySubscription<T> {
|
|
|
267
249
|
operation: string;
|
|
268
250
|
/** The parameters for the query. */
|
|
269
251
|
params: any;
|
|
252
|
+
/** A stable reference to the operations results. */
|
|
253
|
+
result: QueryResult<T> | PagedQueryResult<T>;
|
|
270
254
|
}
|
|
271
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Represents the result of a store query with stable React integration.
|
|
258
|
+
* @template T The type of the result data.
|
|
259
|
+
*/
|
|
260
|
+
interface StoreResult<T extends Record<string, any> = Record<string, any>, V = QueryResult<T> | PagedQueryResult<T>> {
|
|
261
|
+
/** Function that returns the current query state */
|
|
262
|
+
value: () => V;
|
|
263
|
+
/** Function to subscribe to state changes */
|
|
264
|
+
onValueChange: (callback: () => void) => () => void;
|
|
265
|
+
}
|
|
272
266
|
/**
|
|
273
267
|
* A reactive remote store that provides cached and observable access to data
|
|
274
268
|
* from a `BaseStore`. It handles caching, invalidation, and real-time updates
|
|
275
269
|
* via server-sent events (SSE).
|
|
276
|
-
*
|
|
277
|
-
* @template T The type of the records managed by the store, extending Record.
|
|
278
|
-
* @template TFindOptions Options for the find operation.
|
|
279
|
-
* @template TReadOptions Options for the read operation.
|
|
280
|
-
* @template TListOptions Options for the list operation.
|
|
281
|
-
* @template TDeleteOptions Options for the delete operation.
|
|
282
|
-
* @template TUpdateOptions Options for the update operation.
|
|
283
|
-
* @template TCreateOptions Options for the create operation.
|
|
284
|
-
* @template TUploadOptions Options for the upload operation.
|
|
285
|
-
* @template TStreamOptions Options for the stream operation.
|
|
286
270
|
*/
|
|
287
271
|
declare class ReactiveRemoteStore<T extends StoreRecord, TFindOptions = Record<string, unknown>, TReadOptions = Record<string, unknown>, TListOptions = Record<string, unknown>, TDeleteOptions = Record<string, unknown>, TUpdateOptions = Record<string, unknown>, TCreateOptions = Record<string, unknown>, TUploadOptions = Record<string, unknown>, TStreamOptions = Record<string, unknown>> {
|
|
288
272
|
private cache;
|
|
289
273
|
private baseStore;
|
|
290
274
|
private correlator?;
|
|
291
275
|
private storeEventCorrelator?;
|
|
292
|
-
private
|
|
276
|
+
private queryStates;
|
|
277
|
+
private stableResults;
|
|
278
|
+
private stablePaginationMethods;
|
|
279
|
+
private errorCache;
|
|
293
280
|
private keyCache;
|
|
294
281
|
private unsubscribeFromBaseStore;
|
|
295
|
-
|
|
296
|
-
* Creates an instance of ReactiveRemoteStore.
|
|
297
|
-
* @param cache The QueryCache instance to use for caching data.
|
|
298
|
-
* @param baseStore The underlying BaseStore for actual data fetching and mutations.
|
|
299
|
-
* @param correlator Optional function to determine which queries to invalidate after a mutation.
|
|
300
|
-
* @param storeEventCorrelator Optional function to determine which queries to invalidate after a store event.
|
|
301
|
-
*/
|
|
282
|
+
private cacheEventCleanups;
|
|
302
283
|
constructor(cache: QueryCache, baseStore: BaseStore<T, TFindOptions, TReadOptions, TListOptions, TDeleteOptions, TUpdateOptions, TCreateOptions, TUploadOptions, TStreamOptions>, correlator?: Correlator | undefined, storeEventCorrelator?: StoreEventCorrelator | undefined);
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
* @template T The type of the data for the subscription.
|
|
306
|
-
* @param queryKey The unique key for the query.
|
|
307
|
-
* @param operation The operation type (e.g., 'read', 'list').
|
|
308
|
-
* @param params The parameters for the query.
|
|
309
|
-
* @param selector A function that returns the current query result.
|
|
310
|
-
* @param notificationCondition A function to determine if a cache event should trigger a notification.
|
|
311
|
-
* @returns The created QuerySubscription.
|
|
312
|
-
*/
|
|
313
|
-
private createSubscription;
|
|
314
|
-
/**
|
|
315
|
-
* Retrieves an existing subscription or creates a new one if it doesn't exist.
|
|
316
|
-
* @template T The type of the data for the subscription.
|
|
317
|
-
* @param queryKey The unique key for the query.
|
|
318
|
-
* @param operation The operation type (e.g., 'read', 'list').
|
|
319
|
-
* @param params The parameters for the query.
|
|
320
|
-
* @param selector A function that returns the current query result.
|
|
321
|
-
* @param notificationCondition A function to determine if a cache event should trigger a notification.
|
|
322
|
-
* @returns The existing or newly created QuerySubscription.
|
|
323
|
-
*/
|
|
324
|
-
private getOrCreateSubscription;
|
|
325
|
-
/**
|
|
326
|
-
* Builds a unique cache key for a given operation and its parameters.
|
|
327
|
-
* Uses a WeakMap for caching keys of object parameters to avoid re-hashing.
|
|
328
|
-
* @param operation The name of the operation (e.g., 'read', 'list').
|
|
329
|
-
* @param params The parameters for the operation.
|
|
330
|
-
* @returns A unique string key for the operation and parameters.
|
|
331
|
-
*/
|
|
284
|
+
private setupCacheEventListeners;
|
|
285
|
+
private handleCacheEvent;
|
|
332
286
|
private buildKey;
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
private
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Retrieves a single record reactively.
|
|
347
|
-
*
|
|
348
|
-
* This method provides a reactive query result for a single record. The data is fetched from the cache if available,
|
|
349
|
-
* otherwise it's fetched from the underlying `baseStore`. The method returns an object containing the current
|
|
350
|
-
* query result and a function to subscribe to future updates.
|
|
351
|
-
*
|
|
352
|
-
* @param params The options for the read operation, used to identify the record.
|
|
353
|
-
* @returns A `ReactiveQueryResult` object containing the reactive value and an `onValueChange` subscription function.
|
|
354
|
-
*/
|
|
355
|
-
read(params: TReadOptions): ReactiveQueryResult<T>;
|
|
356
|
-
/**
|
|
357
|
-
* Creates a selector function for a paginated query (list or find).
|
|
358
|
-
* @template TParams The type of the parameters for the query.
|
|
359
|
-
* @param baseQueryKey The base unique key for the query.
|
|
360
|
-
* @param baseParams The base parameters for the query.
|
|
361
|
-
* @param fetchFn The function to call to fetch a page of data.
|
|
362
|
-
* @returns A function that returns the current PagedQueryResult.
|
|
363
|
-
*/
|
|
364
|
-
private createPagedSelector;
|
|
365
|
-
/**
|
|
366
|
-
* Sets up a paginated query (list or find) with caching and reactivity.
|
|
367
|
-
* @template TParams The type of the parameters for the query.
|
|
368
|
-
* @param type The type of the paginated query ('list' or 'find').
|
|
369
|
-
* @param params The parameters for the query.
|
|
370
|
-
* @param fetchFn The function to call to fetch a page of data.
|
|
371
|
-
* @returns An object containing the current PagedQueryResult and a function to subscribe to changes.
|
|
372
|
-
*/
|
|
287
|
+
private getOrCreateError;
|
|
288
|
+
private getOrCreateStoreError;
|
|
289
|
+
private computeResult;
|
|
290
|
+
private scheduleBackgroundFetch;
|
|
291
|
+
private getCurrentPageForQuery;
|
|
292
|
+
private getOrCreatePaginationMethods;
|
|
293
|
+
private computeResultForParams;
|
|
294
|
+
hasActiveQuery(operation: string, params: any): boolean;
|
|
295
|
+
getActiveQuery<TResult extends Record<string, unknown> = T>(operation: string, params: any): StoreResult<TResult> | undefined;
|
|
296
|
+
read(params: TReadOptions): StoreResult<T, QueryResult<T>>;
|
|
297
|
+
list(params: TListOptions): StoreResult<T, PagedQueryResult<T>>;
|
|
298
|
+
find(params: TFindOptions): StoreResult<T, PagedQueryResult<T>>;
|
|
373
299
|
private setupPagedQuery;
|
|
374
|
-
/**
|
|
375
|
-
* Lists records from the store with pagination and reactivity.
|
|
376
|
-
* @param params The list options.
|
|
377
|
-
* @returns An object containing the current PagedQueryResult and a function to subscribe to changes.
|
|
378
|
-
*/
|
|
379
|
-
/**
|
|
380
|
-
* Retrieves a paginated list of records reactively.
|
|
381
|
-
*
|
|
382
|
-
* This method provides a reactive query result for a list of records. It supports pagination and automatically
|
|
383
|
-
* fetches data from the cache or the `baseStore`. The result includes methods for navigating to the next,
|
|
384
|
-
* previous, or a specific page.
|
|
385
|
-
*
|
|
386
|
-
* @param params The options for the list operation, including pagination details.
|
|
387
|
-
* @returns A `ReactivePagedQueryResult` object containing the reactive paged value and an `onValueChange` subscription function.
|
|
388
|
-
*/
|
|
389
|
-
list(params: TListOptions): ReactivePagedQueryResult<T>;
|
|
390
|
-
/**
|
|
391
|
-
* Finds records from the store with pagination and reactivity.
|
|
392
|
-
* @param params The find options.
|
|
393
|
-
* @returns An object containing the current PagedQueryResult and a function to subscribe to changes.
|
|
394
|
-
*/
|
|
395
|
-
/**
|
|
396
|
-
* Finds and retrieves a paginated list of records reactively based on a query.
|
|
397
|
-
*
|
|
398
|
-
* Similar to `list`, this method provides a reactive query result for a set of records that match the given
|
|
399
|
-
* find options. It supports pagination and provides methods for navigating through the pages.
|
|
400
|
-
*
|
|
401
|
-
* @param params The options for the find operation, used to query for specific records.
|
|
402
|
-
* @returns A `ReactivePagedQueryResult` object containing the reactive paged value and an `onValueChange` subscription function.
|
|
403
|
-
*/
|
|
404
|
-
find(params: TFindOptions): ReactivePagedQueryResult<T>;
|
|
405
|
-
/**
|
|
406
|
-
* Invalidates relevant queries in the cache based on a mutation operation.
|
|
407
|
-
* If a correlator is provided, it will be used to determine which queries to invalidate.
|
|
408
|
-
* Otherwise, it invalidates all 'list' and 'find' queries.
|
|
409
|
-
* @param mutation An object describing the mutation operation and its parameters.
|
|
410
|
-
*/
|
|
411
300
|
private invalidateQueries;
|
|
412
|
-
/**
|
|
413
|
-
* Handles incoming store events, invalidating relevant queries if a `storeEventCorrelator` is provided.
|
|
414
|
-
* @param event The StoreEvent received.
|
|
415
|
-
*/
|
|
416
301
|
private handleStoreEvent;
|
|
417
|
-
/**
|
|
418
|
-
* Creates a new record.
|
|
419
|
-
*
|
|
420
|
-
* This method sends a request to the `baseStore` to create a new record. Upon successful creation,
|
|
421
|
-
* it updates the cache with the new record and invalidates relevant queries to ensure data consistency.
|
|
422
|
-
*
|
|
423
|
-
* @param params An object containing the data for the new record and optional create options.
|
|
424
|
-
* @returns A promise that resolves to the newly created record, or `undefined` if the creation fails.
|
|
425
|
-
* @throws An error if the create operation fails.
|
|
426
|
-
*/
|
|
427
302
|
create(params: {
|
|
428
303
|
data: Partial<T>;
|
|
429
304
|
options?: TCreateOptions;
|
|
430
305
|
}): Promise<T | undefined>;
|
|
431
|
-
/**
|
|
432
|
-
* Updates an existing record.
|
|
433
|
-
*
|
|
434
|
-
* This method sends a request to the `baseStore` to update a record. If the update is successful,
|
|
435
|
-
* it updates the cache with the modified record and invalidates relevant queries.
|
|
436
|
-
*
|
|
437
|
-
* @param params An object containing the ID of the record to update, the partial data, and optional update options.
|
|
438
|
-
* @returns A promise that resolves to the updated record, or `undefined` if the update fails.
|
|
439
|
-
* @throws An error if the update operation fails.
|
|
440
|
-
*/
|
|
441
306
|
update(params: {
|
|
442
307
|
data: Partial<T>;
|
|
443
308
|
options?: TUpdateOptions;
|
|
444
309
|
}): Promise<T | undefined>;
|
|
445
|
-
/**
|
|
446
|
-
* Deletes a record.
|
|
447
|
-
*
|
|
448
|
-
* This method sends a request to the `baseStore` to delete a record. Upon successful deletion,
|
|
449
|
-
* it removes the record from the cache and invalidates relevant queries.
|
|
450
|
-
*
|
|
451
|
-
* @param params The options for the delete operation, used to identify the record to be deleted.
|
|
452
|
-
* @returns A promise that resolves when the deletion is complete.
|
|
453
|
-
* @throws An error if the delete operation fails.
|
|
454
|
-
*/
|
|
455
310
|
delete(params: TDeleteOptions): Promise<void>;
|
|
456
|
-
/**
|
|
457
|
-
* Sends a notification event to the base store.
|
|
458
|
-
*
|
|
459
|
-
* This can be used to trigger server-side events or other custom actions in the `baseStore`.
|
|
460
|
-
*
|
|
461
|
-
* @param event The `StoreEvent` to be sent.
|
|
462
|
-
* @returns A promise that resolves when the notification has been processed.
|
|
463
|
-
*/
|
|
464
311
|
notify(event: StoreEvent): Promise<void>;
|
|
465
|
-
|
|
466
|
-
* Establishes a real-time data stream.
|
|
467
|
-
*
|
|
468
|
-
* This method delegates to the `baseStore`'s `stream` method to create a persistent connection for
|
|
469
|
-
* receiving real-time updates. The provided callback will be invoked when new data is available.
|
|
470
|
-
*
|
|
471
|
-
* @param options The options for the stream operation.
|
|
472
|
-
* @param onStreamChange A callback function that is executed when the stream's data changes.
|
|
473
|
-
* @returns A promise that resolves with a function to close the stream.
|
|
474
|
-
*/
|
|
475
|
-
stream(options: TStreamOptions, onStreamChange: () => void): Promise<{
|
|
312
|
+
stream(options: TStreamOptions, onStreamChange: () => void): {
|
|
476
313
|
stream: () => AsyncIterable<T>;
|
|
477
314
|
cancel: () => void;
|
|
478
315
|
status: () => "active" | "cancelled" | "completed";
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
* Uploads a file and creates a new record associated with it.
|
|
482
|
-
*
|
|
483
|
-
* This method handles file uploads through the `baseStore`. After a successful upload, it updates the cache
|
|
484
|
-
* with the new record and invalidates relevant queries.
|
|
485
|
-
*
|
|
486
|
-
* @param params An object containing the file to upload and optional upload options.
|
|
487
|
-
* @returns A promise that resolves to the newly created record, or `undefined` if the upload fails.
|
|
488
|
-
* @throws An error if the upload operation fails.
|
|
489
|
-
*/
|
|
316
|
+
};
|
|
317
|
+
subscribe(scope: string, callback: (event: StoreEvent) => void): Promise<() => void>;
|
|
490
318
|
upload(params: {
|
|
491
319
|
file: File;
|
|
492
320
|
options?: TUploadOptions;
|
|
493
321
|
}): Promise<T | undefined>;
|
|
494
|
-
/**
|
|
495
|
-
* Forces a refresh of a specific query.
|
|
496
|
-
*
|
|
497
|
-
* This method bypasses the cache's staleness checks and forces a refetch of the data from the `baseStore`.
|
|
498
|
-
* The method is overloaded to support `read`, `list`, and `find` operations.
|
|
499
|
-
*
|
|
500
|
-
* @param operation The type of operation to refresh ('read', 'list', or 'find').
|
|
501
|
-
* @param params The parameters for the operation.
|
|
502
|
-
* @returns A promise that resolves to the refreshed data.
|
|
503
|
-
*/
|
|
504
322
|
refresh(operation: 'read', params: TReadOptions): Promise<T | undefined>;
|
|
505
323
|
refresh(operation: 'list', params: TListOptions): Promise<Page<T> | undefined>;
|
|
506
324
|
refresh(operation: 'find', query: TFindOptions): Promise<Page<T> | undefined>;
|
|
507
|
-
/**
|
|
508
|
-
* Pre-fetches data for a query and caches it.
|
|
509
|
-
*
|
|
510
|
-
* This method is used to proactively fetch data that is likely to be needed soon. It fetches the data
|
|
511
|
-
* and stores it in the cache, so that subsequent requests for the same data can be served instantly.
|
|
512
|
-
* The method is overloaded for `read`, `list`, and `find` operations.
|
|
513
|
-
*
|
|
514
|
-
* @param operation The type of operation to prefetch ('read', 'list', or 'find').
|
|
515
|
-
* @param params The parameters for the operation.
|
|
516
|
-
*/
|
|
517
325
|
prefetch(operation: 'read', params: TReadOptions): void;
|
|
518
326
|
prefetch(operation: 'list', params: TListOptions): void;
|
|
519
327
|
prefetch(operation: 'find', params: TFindOptions): void;
|
|
520
|
-
/**
|
|
521
|
-
* Invalidates the cached data for a specific query.
|
|
522
|
-
*
|
|
523
|
-
* This marks the query's data as stale, forcing a refetch the next time it's accessed. This is useful
|
|
524
|
-
* when you know the data has changed on the server, but the change was not triggered by a mutation
|
|
525
|
-
* through this store.
|
|
526
|
-
*
|
|
527
|
-
* @param operation The type of operation to invalidate ('read', 'list', or 'find').
|
|
528
|
-
* @param params The parameters for the operation.
|
|
529
|
-
* @returns A promise that resolves when the invalidation is complete.
|
|
530
|
-
*/
|
|
531
328
|
invalidate(operation: string, params: any): Promise<void>;
|
|
532
|
-
/**
|
|
533
|
-
* Invalidates all active queries in the store.
|
|
534
|
-
*
|
|
535
|
-
* This method marks all currently active queries as stale, forcing them to be refetched the next time
|
|
536
|
-
* they are accessed. This is a more aggressive approach to cache invalidation.
|
|
537
|
-
*
|
|
538
|
-
* @returns A promise that resolves when all invalidations are complete.
|
|
539
|
-
*/
|
|
540
329
|
invalidateAll(): Promise<void>;
|
|
541
|
-
/**
|
|
542
|
-
* Retrieves statistics about the store and its cache.
|
|
543
|
-
*
|
|
544
|
-
* This method returns an object containing statistics from the underlying cache, plus the number of
|
|
545
|
-
* active subscriptions in the reactive store.
|
|
546
|
-
*
|
|
547
|
-
* @returns An object with cache statistics and the number of active subscriptions.
|
|
548
|
-
*/
|
|
549
330
|
getStats(): {
|
|
550
331
|
activeSubscriptions: number;
|
|
551
332
|
size: number;
|
|
@@ -562,13 +343,14 @@ declare class ReactiveRemoteStore<T extends StoreRecord, TFindOptions = Record<s
|
|
|
562
343
|
error?: boolean;
|
|
563
344
|
}>;
|
|
564
345
|
};
|
|
565
|
-
/**
|
|
566
|
-
* Cleans up all resources used by the store.
|
|
567
|
-
*
|
|
568
|
-
* This method should be called when the store is no longer needed. It unsubscribes from all cache events,
|
|
569
|
-
* clears all query subscriptions, and unsubscribes from the base store's events.
|
|
570
|
-
*/
|
|
571
346
|
destroy(): void;
|
|
572
347
|
}
|
|
573
348
|
|
|
574
|
-
|
|
349
|
+
/**
|
|
350
|
+
* Computes the FNV-1a 64-bit hash of the given data.
|
|
351
|
+
* @param {any} data - The data to hash. Can be any type that can be serialized.
|
|
352
|
+
* @returns {string} The FNV-1a 64-bit hash as a hexadecimal string.
|
|
353
|
+
*/
|
|
354
|
+
declare function hash(data: any): string;
|
|
355
|
+
|
|
356
|
+
export { type ActiveQuery, type BaseStore, type Correlator, type MutationOperation, type Page, type PagedQueryResult, type PaginationInfo, type QueryResult, type QuerySubscription, ReactiveRemoteStore, type ResourceName, StoreError, type StoreErrorDetails, type StoreEvent, type StoreEventCorrelator, type StoreRecord, type StoreResult, hash };
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { QueryCache, CacheMetrics } from '../cache/index.js';
|
|
2
|
-
import '../types-
|
|
2
|
+
import '../types-ZDD-bdCz.js';
|
|
3
3
|
|
|
4
4
|
interface StoreErrorDetails {
|
|
5
5
|
code: string;
|
|
@@ -205,7 +205,9 @@ interface PagedQueryResult<T> {
|
|
|
205
205
|
/** Function to fetch a specific page of data.
|
|
206
206
|
* @param page The page number to fetch.
|
|
207
207
|
*/
|
|
208
|
-
|
|
208
|
+
navigate: (page: number) => Promise<void>;
|
|
209
|
+
refresh: (delay?: number) => Promise<void>;
|
|
210
|
+
changeParams: (setter: (params: any) => any) => Promise<void>;
|
|
209
211
|
}
|
|
210
212
|
/**
|
|
211
213
|
* Represents a basic record in the remote store.
|
|
@@ -230,26 +232,6 @@ type PaginationInfo = {
|
|
|
230
232
|
* Represents the name of a resource in the store, e.g., 'todos', 'users'.
|
|
231
233
|
*/
|
|
232
234
|
type ResourceName = string;
|
|
233
|
-
/**
|
|
234
|
-
* Represents the reactive result of a single record query.
|
|
235
|
-
* @template T The type of the data being queried.
|
|
236
|
-
*/
|
|
237
|
-
interface ReactiveQueryResult<T> {
|
|
238
|
-
/** The current query result, including data, loading state, and errors. */
|
|
239
|
-
value: () => QueryResult<T>;
|
|
240
|
-
/** A function to subscribe to changes in the query result. Returns an unsubscribe function. */
|
|
241
|
-
onValueChange: (callback: () => void) => () => void;
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Represents the reactive result of a paginated query (list or find).
|
|
245
|
-
* @template T The type of the records in the page.
|
|
246
|
-
*/
|
|
247
|
-
interface ReactivePagedQueryResult<T> {
|
|
248
|
-
/** The current paginated query result, including page data, loading state, and errors. */
|
|
249
|
-
value: () => PagedQueryResult<T>;
|
|
250
|
-
/** A function to subscribe to changes in the query result. Returns an unsubscribe function. */
|
|
251
|
-
onValueChange: (callback: () => void) => () => void;
|
|
252
|
-
}
|
|
253
235
|
/**
|
|
254
236
|
* Internal interface representing an active subscription to a query.
|
|
255
237
|
* @template T The type of the data being queried.
|
|
@@ -267,285 +249,84 @@ interface QuerySubscription<T> {
|
|
|
267
249
|
operation: string;
|
|
268
250
|
/** The parameters for the query. */
|
|
269
251
|
params: any;
|
|
252
|
+
/** A stable reference to the operations results. */
|
|
253
|
+
result: QueryResult<T> | PagedQueryResult<T>;
|
|
270
254
|
}
|
|
271
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Represents the result of a store query with stable React integration.
|
|
258
|
+
* @template T The type of the result data.
|
|
259
|
+
*/
|
|
260
|
+
interface StoreResult<T extends Record<string, any> = Record<string, any>, V = QueryResult<T> | PagedQueryResult<T>> {
|
|
261
|
+
/** Function that returns the current query state */
|
|
262
|
+
value: () => V;
|
|
263
|
+
/** Function to subscribe to state changes */
|
|
264
|
+
onValueChange: (callback: () => void) => () => void;
|
|
265
|
+
}
|
|
272
266
|
/**
|
|
273
267
|
* A reactive remote store that provides cached and observable access to data
|
|
274
268
|
* from a `BaseStore`. It handles caching, invalidation, and real-time updates
|
|
275
269
|
* via server-sent events (SSE).
|
|
276
|
-
*
|
|
277
|
-
* @template T The type of the records managed by the store, extending Record.
|
|
278
|
-
* @template TFindOptions Options for the find operation.
|
|
279
|
-
* @template TReadOptions Options for the read operation.
|
|
280
|
-
* @template TListOptions Options for the list operation.
|
|
281
|
-
* @template TDeleteOptions Options for the delete operation.
|
|
282
|
-
* @template TUpdateOptions Options for the update operation.
|
|
283
|
-
* @template TCreateOptions Options for the create operation.
|
|
284
|
-
* @template TUploadOptions Options for the upload operation.
|
|
285
|
-
* @template TStreamOptions Options for the stream operation.
|
|
286
270
|
*/
|
|
287
271
|
declare class ReactiveRemoteStore<T extends StoreRecord, TFindOptions = Record<string, unknown>, TReadOptions = Record<string, unknown>, TListOptions = Record<string, unknown>, TDeleteOptions = Record<string, unknown>, TUpdateOptions = Record<string, unknown>, TCreateOptions = Record<string, unknown>, TUploadOptions = Record<string, unknown>, TStreamOptions = Record<string, unknown>> {
|
|
288
272
|
private cache;
|
|
289
273
|
private baseStore;
|
|
290
274
|
private correlator?;
|
|
291
275
|
private storeEventCorrelator?;
|
|
292
|
-
private
|
|
276
|
+
private queryStates;
|
|
277
|
+
private stableResults;
|
|
278
|
+
private stablePaginationMethods;
|
|
279
|
+
private errorCache;
|
|
293
280
|
private keyCache;
|
|
294
281
|
private unsubscribeFromBaseStore;
|
|
295
|
-
|
|
296
|
-
* Creates an instance of ReactiveRemoteStore.
|
|
297
|
-
* @param cache The QueryCache instance to use for caching data.
|
|
298
|
-
* @param baseStore The underlying BaseStore for actual data fetching and mutations.
|
|
299
|
-
* @param correlator Optional function to determine which queries to invalidate after a mutation.
|
|
300
|
-
* @param storeEventCorrelator Optional function to determine which queries to invalidate after a store event.
|
|
301
|
-
*/
|
|
282
|
+
private cacheEventCleanups;
|
|
302
283
|
constructor(cache: QueryCache, baseStore: BaseStore<T, TFindOptions, TReadOptions, TListOptions, TDeleteOptions, TUpdateOptions, TCreateOptions, TUploadOptions, TStreamOptions>, correlator?: Correlator | undefined, storeEventCorrelator?: StoreEventCorrelator | undefined);
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
* @template T The type of the data for the subscription.
|
|
306
|
-
* @param queryKey The unique key for the query.
|
|
307
|
-
* @param operation The operation type (e.g., 'read', 'list').
|
|
308
|
-
* @param params The parameters for the query.
|
|
309
|
-
* @param selector A function that returns the current query result.
|
|
310
|
-
* @param notificationCondition A function to determine if a cache event should trigger a notification.
|
|
311
|
-
* @returns The created QuerySubscription.
|
|
312
|
-
*/
|
|
313
|
-
private createSubscription;
|
|
314
|
-
/**
|
|
315
|
-
* Retrieves an existing subscription or creates a new one if it doesn't exist.
|
|
316
|
-
* @template T The type of the data for the subscription.
|
|
317
|
-
* @param queryKey The unique key for the query.
|
|
318
|
-
* @param operation The operation type (e.g., 'read', 'list').
|
|
319
|
-
* @param params The parameters for the query.
|
|
320
|
-
* @param selector A function that returns the current query result.
|
|
321
|
-
* @param notificationCondition A function to determine if a cache event should trigger a notification.
|
|
322
|
-
* @returns The existing or newly created QuerySubscription.
|
|
323
|
-
*/
|
|
324
|
-
private getOrCreateSubscription;
|
|
325
|
-
/**
|
|
326
|
-
* Builds a unique cache key for a given operation and its parameters.
|
|
327
|
-
* Uses a WeakMap for caching keys of object parameters to avoid re-hashing.
|
|
328
|
-
* @param operation The name of the operation (e.g., 'read', 'list').
|
|
329
|
-
* @param params The parameters for the operation.
|
|
330
|
-
* @returns A unique string key for the operation and parameters.
|
|
331
|
-
*/
|
|
284
|
+
private setupCacheEventListeners;
|
|
285
|
+
private handleCacheEvent;
|
|
332
286
|
private buildKey;
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
private
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Retrieves a single record reactively.
|
|
347
|
-
*
|
|
348
|
-
* This method provides a reactive query result for a single record. The data is fetched from the cache if available,
|
|
349
|
-
* otherwise it's fetched from the underlying `baseStore`. The method returns an object containing the current
|
|
350
|
-
* query result and a function to subscribe to future updates.
|
|
351
|
-
*
|
|
352
|
-
* @param params The options for the read operation, used to identify the record.
|
|
353
|
-
* @returns A `ReactiveQueryResult` object containing the reactive value and an `onValueChange` subscription function.
|
|
354
|
-
*/
|
|
355
|
-
read(params: TReadOptions): ReactiveQueryResult<T>;
|
|
356
|
-
/**
|
|
357
|
-
* Creates a selector function for a paginated query (list or find).
|
|
358
|
-
* @template TParams The type of the parameters for the query.
|
|
359
|
-
* @param baseQueryKey The base unique key for the query.
|
|
360
|
-
* @param baseParams The base parameters for the query.
|
|
361
|
-
* @param fetchFn The function to call to fetch a page of data.
|
|
362
|
-
* @returns A function that returns the current PagedQueryResult.
|
|
363
|
-
*/
|
|
364
|
-
private createPagedSelector;
|
|
365
|
-
/**
|
|
366
|
-
* Sets up a paginated query (list or find) with caching and reactivity.
|
|
367
|
-
* @template TParams The type of the parameters for the query.
|
|
368
|
-
* @param type The type of the paginated query ('list' or 'find').
|
|
369
|
-
* @param params The parameters for the query.
|
|
370
|
-
* @param fetchFn The function to call to fetch a page of data.
|
|
371
|
-
* @returns An object containing the current PagedQueryResult and a function to subscribe to changes.
|
|
372
|
-
*/
|
|
287
|
+
private getOrCreateError;
|
|
288
|
+
private getOrCreateStoreError;
|
|
289
|
+
private computeResult;
|
|
290
|
+
private scheduleBackgroundFetch;
|
|
291
|
+
private getCurrentPageForQuery;
|
|
292
|
+
private getOrCreatePaginationMethods;
|
|
293
|
+
private computeResultForParams;
|
|
294
|
+
hasActiveQuery(operation: string, params: any): boolean;
|
|
295
|
+
getActiveQuery<TResult extends Record<string, unknown> = T>(operation: string, params: any): StoreResult<TResult> | undefined;
|
|
296
|
+
read(params: TReadOptions): StoreResult<T, QueryResult<T>>;
|
|
297
|
+
list(params: TListOptions): StoreResult<T, PagedQueryResult<T>>;
|
|
298
|
+
find(params: TFindOptions): StoreResult<T, PagedQueryResult<T>>;
|
|
373
299
|
private setupPagedQuery;
|
|
374
|
-
/**
|
|
375
|
-
* Lists records from the store with pagination and reactivity.
|
|
376
|
-
* @param params The list options.
|
|
377
|
-
* @returns An object containing the current PagedQueryResult and a function to subscribe to changes.
|
|
378
|
-
*/
|
|
379
|
-
/**
|
|
380
|
-
* Retrieves a paginated list of records reactively.
|
|
381
|
-
*
|
|
382
|
-
* This method provides a reactive query result for a list of records. It supports pagination and automatically
|
|
383
|
-
* fetches data from the cache or the `baseStore`. The result includes methods for navigating to the next,
|
|
384
|
-
* previous, or a specific page.
|
|
385
|
-
*
|
|
386
|
-
* @param params The options for the list operation, including pagination details.
|
|
387
|
-
* @returns A `ReactivePagedQueryResult` object containing the reactive paged value and an `onValueChange` subscription function.
|
|
388
|
-
*/
|
|
389
|
-
list(params: TListOptions): ReactivePagedQueryResult<T>;
|
|
390
|
-
/**
|
|
391
|
-
* Finds records from the store with pagination and reactivity.
|
|
392
|
-
* @param params The find options.
|
|
393
|
-
* @returns An object containing the current PagedQueryResult and a function to subscribe to changes.
|
|
394
|
-
*/
|
|
395
|
-
/**
|
|
396
|
-
* Finds and retrieves a paginated list of records reactively based on a query.
|
|
397
|
-
*
|
|
398
|
-
* Similar to `list`, this method provides a reactive query result for a set of records that match the given
|
|
399
|
-
* find options. It supports pagination and provides methods for navigating through the pages.
|
|
400
|
-
*
|
|
401
|
-
* @param params The options for the find operation, used to query for specific records.
|
|
402
|
-
* @returns A `ReactivePagedQueryResult` object containing the reactive paged value and an `onValueChange` subscription function.
|
|
403
|
-
*/
|
|
404
|
-
find(params: TFindOptions): ReactivePagedQueryResult<T>;
|
|
405
|
-
/**
|
|
406
|
-
* Invalidates relevant queries in the cache based on a mutation operation.
|
|
407
|
-
* If a correlator is provided, it will be used to determine which queries to invalidate.
|
|
408
|
-
* Otherwise, it invalidates all 'list' and 'find' queries.
|
|
409
|
-
* @param mutation An object describing the mutation operation and its parameters.
|
|
410
|
-
*/
|
|
411
300
|
private invalidateQueries;
|
|
412
|
-
/**
|
|
413
|
-
* Handles incoming store events, invalidating relevant queries if a `storeEventCorrelator` is provided.
|
|
414
|
-
* @param event The StoreEvent received.
|
|
415
|
-
*/
|
|
416
301
|
private handleStoreEvent;
|
|
417
|
-
/**
|
|
418
|
-
* Creates a new record.
|
|
419
|
-
*
|
|
420
|
-
* This method sends a request to the `baseStore` to create a new record. Upon successful creation,
|
|
421
|
-
* it updates the cache with the new record and invalidates relevant queries to ensure data consistency.
|
|
422
|
-
*
|
|
423
|
-
* @param params An object containing the data for the new record and optional create options.
|
|
424
|
-
* @returns A promise that resolves to the newly created record, or `undefined` if the creation fails.
|
|
425
|
-
* @throws An error if the create operation fails.
|
|
426
|
-
*/
|
|
427
302
|
create(params: {
|
|
428
303
|
data: Partial<T>;
|
|
429
304
|
options?: TCreateOptions;
|
|
430
305
|
}): Promise<T | undefined>;
|
|
431
|
-
/**
|
|
432
|
-
* Updates an existing record.
|
|
433
|
-
*
|
|
434
|
-
* This method sends a request to the `baseStore` to update a record. If the update is successful,
|
|
435
|
-
* it updates the cache with the modified record and invalidates relevant queries.
|
|
436
|
-
*
|
|
437
|
-
* @param params An object containing the ID of the record to update, the partial data, and optional update options.
|
|
438
|
-
* @returns A promise that resolves to the updated record, or `undefined` if the update fails.
|
|
439
|
-
* @throws An error if the update operation fails.
|
|
440
|
-
*/
|
|
441
306
|
update(params: {
|
|
442
307
|
data: Partial<T>;
|
|
443
308
|
options?: TUpdateOptions;
|
|
444
309
|
}): Promise<T | undefined>;
|
|
445
|
-
/**
|
|
446
|
-
* Deletes a record.
|
|
447
|
-
*
|
|
448
|
-
* This method sends a request to the `baseStore` to delete a record. Upon successful deletion,
|
|
449
|
-
* it removes the record from the cache and invalidates relevant queries.
|
|
450
|
-
*
|
|
451
|
-
* @param params The options for the delete operation, used to identify the record to be deleted.
|
|
452
|
-
* @returns A promise that resolves when the deletion is complete.
|
|
453
|
-
* @throws An error if the delete operation fails.
|
|
454
|
-
*/
|
|
455
310
|
delete(params: TDeleteOptions): Promise<void>;
|
|
456
|
-
/**
|
|
457
|
-
* Sends a notification event to the base store.
|
|
458
|
-
*
|
|
459
|
-
* This can be used to trigger server-side events or other custom actions in the `baseStore`.
|
|
460
|
-
*
|
|
461
|
-
* @param event The `StoreEvent` to be sent.
|
|
462
|
-
* @returns A promise that resolves when the notification has been processed.
|
|
463
|
-
*/
|
|
464
311
|
notify(event: StoreEvent): Promise<void>;
|
|
465
|
-
|
|
466
|
-
* Establishes a real-time data stream.
|
|
467
|
-
*
|
|
468
|
-
* This method delegates to the `baseStore`'s `stream` method to create a persistent connection for
|
|
469
|
-
* receiving real-time updates. The provided callback will be invoked when new data is available.
|
|
470
|
-
*
|
|
471
|
-
* @param options The options for the stream operation.
|
|
472
|
-
* @param onStreamChange A callback function that is executed when the stream's data changes.
|
|
473
|
-
* @returns A promise that resolves with a function to close the stream.
|
|
474
|
-
*/
|
|
475
|
-
stream(options: TStreamOptions, onStreamChange: () => void): Promise<{
|
|
312
|
+
stream(options: TStreamOptions, onStreamChange: () => void): {
|
|
476
313
|
stream: () => AsyncIterable<T>;
|
|
477
314
|
cancel: () => void;
|
|
478
315
|
status: () => "active" | "cancelled" | "completed";
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
* Uploads a file and creates a new record associated with it.
|
|
482
|
-
*
|
|
483
|
-
* This method handles file uploads through the `baseStore`. After a successful upload, it updates the cache
|
|
484
|
-
* with the new record and invalidates relevant queries.
|
|
485
|
-
*
|
|
486
|
-
* @param params An object containing the file to upload and optional upload options.
|
|
487
|
-
* @returns A promise that resolves to the newly created record, or `undefined` if the upload fails.
|
|
488
|
-
* @throws An error if the upload operation fails.
|
|
489
|
-
*/
|
|
316
|
+
};
|
|
317
|
+
subscribe(scope: string, callback: (event: StoreEvent) => void): Promise<() => void>;
|
|
490
318
|
upload(params: {
|
|
491
319
|
file: File;
|
|
492
320
|
options?: TUploadOptions;
|
|
493
321
|
}): Promise<T | undefined>;
|
|
494
|
-
/**
|
|
495
|
-
* Forces a refresh of a specific query.
|
|
496
|
-
*
|
|
497
|
-
* This method bypasses the cache's staleness checks and forces a refetch of the data from the `baseStore`.
|
|
498
|
-
* The method is overloaded to support `read`, `list`, and `find` operations.
|
|
499
|
-
*
|
|
500
|
-
* @param operation The type of operation to refresh ('read', 'list', or 'find').
|
|
501
|
-
* @param params The parameters for the operation.
|
|
502
|
-
* @returns A promise that resolves to the refreshed data.
|
|
503
|
-
*/
|
|
504
322
|
refresh(operation: 'read', params: TReadOptions): Promise<T | undefined>;
|
|
505
323
|
refresh(operation: 'list', params: TListOptions): Promise<Page<T> | undefined>;
|
|
506
324
|
refresh(operation: 'find', query: TFindOptions): Promise<Page<T> | undefined>;
|
|
507
|
-
/**
|
|
508
|
-
* Pre-fetches data for a query and caches it.
|
|
509
|
-
*
|
|
510
|
-
* This method is used to proactively fetch data that is likely to be needed soon. It fetches the data
|
|
511
|
-
* and stores it in the cache, so that subsequent requests for the same data can be served instantly.
|
|
512
|
-
* The method is overloaded for `read`, `list`, and `find` operations.
|
|
513
|
-
*
|
|
514
|
-
* @param operation The type of operation to prefetch ('read', 'list', or 'find').
|
|
515
|
-
* @param params The parameters for the operation.
|
|
516
|
-
*/
|
|
517
325
|
prefetch(operation: 'read', params: TReadOptions): void;
|
|
518
326
|
prefetch(operation: 'list', params: TListOptions): void;
|
|
519
327
|
prefetch(operation: 'find', params: TFindOptions): void;
|
|
520
|
-
/**
|
|
521
|
-
* Invalidates the cached data for a specific query.
|
|
522
|
-
*
|
|
523
|
-
* This marks the query's data as stale, forcing a refetch the next time it's accessed. This is useful
|
|
524
|
-
* when you know the data has changed on the server, but the change was not triggered by a mutation
|
|
525
|
-
* through this store.
|
|
526
|
-
*
|
|
527
|
-
* @param operation The type of operation to invalidate ('read', 'list', or 'find').
|
|
528
|
-
* @param params The parameters for the operation.
|
|
529
|
-
* @returns A promise that resolves when the invalidation is complete.
|
|
530
|
-
*/
|
|
531
328
|
invalidate(operation: string, params: any): Promise<void>;
|
|
532
|
-
/**
|
|
533
|
-
* Invalidates all active queries in the store.
|
|
534
|
-
*
|
|
535
|
-
* This method marks all currently active queries as stale, forcing them to be refetched the next time
|
|
536
|
-
* they are accessed. This is a more aggressive approach to cache invalidation.
|
|
537
|
-
*
|
|
538
|
-
* @returns A promise that resolves when all invalidations are complete.
|
|
539
|
-
*/
|
|
540
329
|
invalidateAll(): Promise<void>;
|
|
541
|
-
/**
|
|
542
|
-
* Retrieves statistics about the store and its cache.
|
|
543
|
-
*
|
|
544
|
-
* This method returns an object containing statistics from the underlying cache, plus the number of
|
|
545
|
-
* active subscriptions in the reactive store.
|
|
546
|
-
*
|
|
547
|
-
* @returns An object with cache statistics and the number of active subscriptions.
|
|
548
|
-
*/
|
|
549
330
|
getStats(): {
|
|
550
331
|
activeSubscriptions: number;
|
|
551
332
|
size: number;
|
|
@@ -562,13 +343,14 @@ declare class ReactiveRemoteStore<T extends StoreRecord, TFindOptions = Record<s
|
|
|
562
343
|
error?: boolean;
|
|
563
344
|
}>;
|
|
564
345
|
};
|
|
565
|
-
/**
|
|
566
|
-
* Cleans up all resources used by the store.
|
|
567
|
-
*
|
|
568
|
-
* This method should be called when the store is no longer needed. It unsubscribes from all cache events,
|
|
569
|
-
* clears all query subscriptions, and unsubscribes from the base store's events.
|
|
570
|
-
*/
|
|
571
346
|
destroy(): void;
|
|
572
347
|
}
|
|
573
348
|
|
|
574
|
-
|
|
349
|
+
/**
|
|
350
|
+
* Computes the FNV-1a 64-bit hash of the given data.
|
|
351
|
+
* @param {any} data - The data to hash. Can be any type that can be serialized.
|
|
352
|
+
* @returns {string} The FNV-1a 64-bit hash as a hexadecimal string.
|
|
353
|
+
*/
|
|
354
|
+
declare function hash(data: any): string;
|
|
355
|
+
|
|
356
|
+
export { type ActiveQuery, type BaseStore, type Correlator, type MutationOperation, type Page, type PagedQueryResult, type PaginationInfo, type QueryResult, type QuerySubscription, ReactiveRemoteStore, type ResourceName, StoreError, type StoreErrorDetails, type StoreEvent, type StoreEventCorrelator, type StoreRecord, type StoreResult, hash };
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=class e extends Error{code;status;data;isRetryable;originalError;constructor(e,t){super(e.message),this.name="StoreError",this.code=e.code,this.status=e.status,this.data=e.data,this.isRetryable=e.isRetryable,this.originalError=t}static fromError(t,r){if(t.isAbort||"AbortError"===t.name)return new e({code:"ABORTED",message:"Request was cancelled",isRetryable:!1},t);if("NetworkError"===t.name||!navigator.onLine)return new e({code:"NETWORK_ERROR",message:"Network connection failed. Please check your internet connection.",isRetryable:!0},t);if(t.status)switch(t.status){case 400:return new e({code:"VALIDATION_ERROR",message:t.message||"Invalid request data",status:400,data:t.data,isRetryable:!1},t);case 401:return new e({code:"UNAUTHORIZED",message:"Authentication required or invalid credentials",status:401,isRetryable:!1},t);case 403:return new e({code:"FORBIDDEN",message:"Access denied for this resource",status:403,isRetryable:!1},t);case 404:return new e({code:"NOT_FOUND",message:"Resource not found",status:404,isRetryable:!1},t);case 409:return new e({code:"CONFLICT",message:t.message||"Resource conflict occurred",status:409,isRetryable:!1},t);case 429:return new e({code:"RATE_LIMITED",message:"Too many requests. Please try again later.",status:429,isRetryable:!0},t);case 500:case 502:case 503:case 504:return new e({code:"SERVER_ERROR",message:"Server error occurred. Please try again later.",status:t.status,isRetryable:!0},t);default:return new e({code:"HTTP_ERROR",message:t.message||`HTTP ${t.status} error occurred`,status:t.status,isRetryable:t.status>=500},t)}return"TimeoutError"===t.name||"TIMEOUT"===t.code?new e({code:"TIMEOUT",message:"Request timed out. Please try again.",isRetryable:!0},t):new e({code:"UNKNOWN_ERROR",message:t.message||`Unknown error occurred during ${r}`,isRetryable:!0},t)}};function t(e,r=new WeakMap){return void 0===e?"undefined":"function"==typeof e||"symbol"==typeof e?"":"string"==typeof e?`"${e}"`:"number"==typeof e||"boolean"==typeof e||null===e?String(e):Array.isArray(e)?`[${e.map((e=>t(e,r))).join(",")}]`:"object"==typeof e?r.has(e)?'"__circular__"':(r.set(e,!0),`{${Object.keys(e).sort().map((a=>`"${a}":${t(e[a],r)}`)).join(",")}}`):""}function r(e){let r=3421674724,a=2216829733;const s=t(e);for(let e=0;e<s.length;e++){a^=s.charCodeAt(e);const t=Math.imul(a,435),i=Math.imul(a,435)+Math.imul(r,435);a=t>>>0,r=i>>>0}return(r>>>0).toString(16)+(a>>>0).toString(16)}exports.ReactiveRemoteStore=class{constructor(e,t,r,a){if(this.cache=e,this.baseStore=t,this.correlator=r,this.storeEventCorrelator=a,this.storeEventCorrelator){queueMicrotask((async()=>{this.unsubscribeFromBaseStore=await this.baseStore.subscribe("*",this.handleStoreEvent.bind(this))}))}}querySubscriptions=new Map;keyCache=new WeakMap;unsubscribeFromBaseStore;createSubscription(e,t,r,a,s){const i=new Set,o=()=>{i.forEach((e=>{try{e()}catch(e){console.error("ReactiveRemoteStore: Subscriber callback error:",e)}}))},c=[this.cache.on("cache:fetch:success",(e=>{s(e.key)&&o()})),this.cache.on("cache:fetch:error",(e=>{s(e.key)&&o()})),this.cache.on("cache:data:set",(e=>{s(e.key)&&o()})),this.cache.on("cache:data:invalidate",(e=>{s(e.key)&&o()})),this.cache.on("cache:read:hit",(e=>{s(e.key)&&e.isStale&&o()}))],n={unsubscribeCallbacks:c,subscribers:i,cachedSubscribe:t=>(i.add(t),()=>{i.delete(t),0===i.size&&(c.forEach((e=>e())),this.querySubscriptions.delete(e))}),cachedSelector:a,operation:t,params:r};return this.querySubscriptions.set(e,n),n}getOrCreateSubscription(e,t,r,a,s){let i=this.querySubscriptions.get(e);return i||(i=this.createSubscription(e,t,r,a,s)),i}buildKey(e,t){if("object"==typeof t&&null!==t&&this.keyCache.has(t))return this.keyCache.get(t);const a=`${e}:${r(t)}`;return"object"==typeof t&&null!==t&&this.keyCache.set(t,a),a}createSelector(e){return()=>{const t=this.cache.getStats().entries.find((t=>t.key===e)),r=this.cache.peek(e);return t?.isStale&&this.cache.get(e,{waitForFresh:!1}).catch((t=>{console.warn(`ReactiveRemoteStore: Background refetch failed for ${e}:`,t)})),void 0!==r&&this.cache.has(e)||this.cache.get(e,{waitForFresh:!1}).catch((t=>{console.warn(`ReactiveRemoteStore: Background fetch failed for ${e}:`,t)})),{data:r,loading:t?.isLoading??void 0===r,error:t?.error?new Error("Query failed"):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0}}}read(e){const t=this.buildKey("read",e);this.querySubscriptions.has(t)||this.cache.registerQuery(t,(async()=>await this.baseStore.read(e)));const r=this.createSelector(t),a=this.getOrCreateSubscription(t,"read",e,r,(e=>e===t));return{value:a.cachedSelector,onValueChange:a.cachedSubscribe}}createPagedSelector(t,r,a){return()=>{const s=this.cache.getStats().entries.find((e=>e.key===t)),i=this.cache.peek(t);s?.isStale&&this.cache.get(t,{waitForFresh:!1}).catch((e=>{console.warn(`ReactiveRemoteStore: Background refetch failed for ${t}:`,e)})),void 0!==i&&this.cache.has(t)||this.cache.get(t,{waitForFresh:!1}).catch((e=>{console.warn(`ReactiveRemoteStore: Background fetch failed for ${t}:`,e)}));const o=r.page||1;return{page:i,loading:s?.isLoading??void 0===i,error:s?.error?e.fromError(new Error("Query failed"),"query"):void 0,stale:s?.isStale??!0,updated:s?.lastUpdated??0,hasNext:!!i&&o<i.page.pages,hasPrevious:o>1,next:async()=>{if(i&&o<i.page.pages){const e={...r,page:o+1},s=this.buildKey(t.split(":")[0],e);this.querySubscriptions.has(s)||this.cache.registerQuery(s,(()=>a(e))),await this.cache.get(s,{waitForFresh:!0})}},previous:async()=>{if(o>1){const e={...r,page:o-1},s=this.buildKey(t.split(":")[0],e);this.querySubscriptions.has(s)||this.cache.registerQuery(s,(()=>a(e))),await this.cache.get(s,{waitForFresh:!0})}},fetch:async e=>{const s={...r,page:e},i=this.buildKey(t.split(":")[0],s);this.querySubscriptions.has(i)||this.cache.registerQuery(i,(()=>a(s))),await this.cache.get(i,{waitForFresh:!0})}}}}setupPagedQuery(e,t,r){const a=this.buildKey(e,t);this.querySubscriptions.has(a)||this.cache.registerQuery(a,(async()=>r(t)));const s=this.createPagedSelector(a,t,r),i=this.getOrCreateSubscription(a,e,t,s,(t=>t.startsWith(`${e}:`)));return{value:i.cachedSelector,onValueChange:i.cachedSubscribe}}list(e){return this.setupPagedQuery("list",e,this.baseStore.list.bind(this.baseStore))}find(e){return this.setupPagedQuery("find",e,this.baseStore.find.bind(this.baseStore))}async invalidateQueries(e){if(this.correlator){const t=Array.from(this.querySubscriptions.entries()).map((([e,t])=>({queryKey:e,operation:t.operation,params:t.params}))),r=this.correlator(e,t).map((e=>this.cache.invalidate(e)));await Promise.all(r)}else await this.cache.invalidatePattern(/^list:/),await this.cache.invalidatePattern(/^find:/)}handleStoreEvent(e){if(console.log("ReactiveRemoteStore: Received store event:",e),this.storeEventCorrelator){const t=Array.from(this.querySubscriptions.entries()).map((([e,t])=>({queryKey:e,operation:t.operation,params:t.params})));console.log("ReactiveRemoteStore: Active queries:",t);const r=this.storeEventCorrelator(e,t);console.log("ReactiveRemoteStore: Queries to invalidate:",r),r.forEach((e=>this.cache.invalidate(e)))}else console.warn("ReactiveRemoteStore: handleStoreEvent called without _storeEventCorrelator.")}async create(e){try{const t=await this.baseStore.create(e);return t&&(this.cache.setData(this.buildKey("read",{id:t.id}),t),await this.invalidateQueries({operation:"create",params:e})),t}catch(e){throw console.error("ReactiveRemoteStore: Create operation failed:",e),e}}async update(e){try{const t=await this.baseStore.update(e);return t&&await this.invalidateQueries({operation:"update",params:e}),t}catch(e){throw console.error("ReactiveRemoteStore: Update operation failed:",e),e}}async delete(e){try{await this.baseStore.delete(e),this.cache.remove(this.buildKey("read",{id:e.id})),await this.invalidateQueries({operation:"delete",params:e})}catch(e){throw console.error("ReactiveRemoteStore: Delete operation failed:",e),e}}async notify(e){return this.baseStore.notify(e)}async stream(e,t){return this.baseStore.stream(e,t)}async upload(e){try{const t=await this.baseStore.upload(e);return t&&(this.cache.setData(this.buildKey("read",{id:t.id}),t),await this.invalidateQueries({operation:"upload",params:e})),t}catch(e){throw console.error("ReactiveRemoteStore: Upload operation failed:",e),e}}async refresh(e,t){const r=this.buildKey(e,t);return this.cache.refresh(r)}prefetch(e,t){const r=this.buildKey(e,t);this.cache.prefetch(r).catch((e=>{console.warn(`ReactiveRemoteStore: Prefetch failed for ${r}:`,e)}))}async invalidate(e,t){const r=this.buildKey(e,t);await this.cache.invalidate(r)}async invalidateAll(){const e=Array.from(this.querySubscriptions.keys()).map((e=>this.cache.invalidate(e)));await Promise.all(e)}getStats(){return{...this.cache.getStats(),activeSubscriptions:this.querySubscriptions.size}}destroy(){this.querySubscriptions.forEach((e=>{e.unsubscribeCallbacks.forEach((e=>e()))})),this.querySubscriptions.clear(),this.keyCache=new WeakMap,this.unsubscribeFromBaseStore&&this.unsubscribeFromBaseStore()}},exports.StoreError=e;
|
|
1
|
+
"use strict";require("uuid");var e,t,s=Object.create,r=Object.defineProperty,a=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,o=Object.getPrototypeOf,n=Object.prototype.hasOwnProperty,c=(e={"node_modules/@asaidimu/events/index.js"(e,t){var s,r=Object.defineProperty,a=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,n={};((e,t)=>{for(var s in t)r(e,s,{get:t[s],enumerable:!0})})(n,{createEventBus:()=>c}),t.exports=(s=n,((e,t,s,n)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))o.call(e,c)||c===s||r(e,c,{get:()=>t[c],enumerable:!(n=a(t,c))||n.enumerable});return e})(r({},"__esModule",{value:!0}),s));var c=(e={async:!1,batchSize:1e3,batchDelay:16,errorHandler:e=>console.error("EventBus Error:",e),crossTab:!1,channelName:"event-bus-channel"})=>{const t=new Map;let s=[],r=0,a=0;const i=new Map,o=new Map;let n=null;e.crossTab&&"undefined"!=typeof BroadcastChannel?n=new BroadcastChannel(e.channelName):e.crossTab&&console.warn("BroadcastChannel is not supported in this browser. Cross-tab notifications are disabled.");const c=(e,t)=>{r++,a+=t,i.set(e,(i.get(e)||0)+1)},h=()=>{const t=s;s=[],t.forEach((({name:t,payload:s})=>{const r=performance.now();try{(o.get(t)||[]).forEach((e=>e(s)))}catch(r){e.errorHandler({...r,eventName:t,payload:s})}c(t,performance.now()-r)}))},u=(()=>{let t;return()=>{clearTimeout(t),t=setTimeout(h,e.batchDelay)}})(),l=e=>{const s=t.get(e);s?o.set(e,Array.from(s)):o.delete(e)};return n&&(n.onmessage=e=>{const{name:t,payload:s}=e.data;(o.get(t)||[]).forEach((e=>e(s)))}),{subscribe:(e,s)=>{t.has(e)||t.set(e,new Set);const r=t.get(e);return r.add(s),l(e),()=>{r.delete(s),0===r.size?(t.delete(e),o.delete(e)):l(e)}},emit:({name:t,payload:r})=>{if(e.async)return s.push({name:t,payload:r}),s.length>=e.batchSize?h():u(),void(n&&n.postMessage({name:t,payload:r}));const a=performance.now();try{(o.get(t)||[]).forEach((e=>e(r))),n&&n.postMessage({name:t,payload:r})}catch(s){e.errorHandler({...s,eventName:t,payload:r})}c(t,performance.now()-a)},getMetrics:()=>({totalEvents:r,activeSubscriptions:Array.from(t.values()).reduce(((e,t)=>e+t.size),0),eventCounts:i,averageEmitDuration:r>0?a/r:0}),clear:()=>{t.clear(),o.clear(),s=[],r=0,a=0,i.clear(),n&&(n.close(),n=null)}}}}},function(){return t||(0,e[i(e)[0]])((t={exports:{}}).exports,t),t.exports});((e,t,c)=>{c=null!=e?s(o(e)):{},((e,t,s,o)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))n.call(e,c)||c===s||r(e,c,{get:()=>t[c],enumerable:!(o=a(t,c))||o.enumerable})})(e&&e.__esModule?c:r(c,"default",{value:e,enumerable:!0}),e)})(c());var h=class e extends Error{code;status;data;isRetryable;originalError;constructor(e,t){super(e.message),this.name="StoreError",this.code=e.code,this.status=e.status,this.data=e.data,this.isRetryable=e.isRetryable,this.originalError=t}static fromError(t,s){if(t.isAbort||"AbortError"===t.name)return new e({code:"ABORTED",message:"Request was cancelled",isRetryable:!1},t);if("NetworkError"===t.name||!navigator.onLine)return new e({code:"NETWORK_ERROR",message:"Network connection failed. Please check your internet connection.",isRetryable:!0},t);if(t.status)switch(t.status){case 400:return new e({code:"VALIDATION_ERROR",message:t.message||"Invalid request data",status:400,data:t.data,isRetryable:!1},t);case 401:return new e({code:"UNAUTHORIZED",message:"Authentication required or invalid credentials",status:401,isRetryable:!1},t);case 403:return new e({code:"FORBIDDEN",message:"Access denied for this resource",status:403,isRetryable:!1},t);case 404:return new e({code:"NOT_FOUND",message:"Resource not found",status:404,isRetryable:!1},t);case 409:return new e({code:"CONFLICT",message:t.message||"Resource conflict occurred",status:409,isRetryable:!1},t);case 429:return new e({code:"RATE_LIMITED",message:"Too many requests. Please try again later.",status:429,isRetryable:!0},t);case 500:case 502:case 503:case 504:return new e({code:"SERVER_ERROR",message:"Server error occurred. Please try again later.",status:t.status,isRetryable:!0},t);default:return new e({code:"HTTP_ERROR",message:t.message||`HTTP ${t.status} error occurred`,status:t.status,isRetryable:t.status>=500},t)}return"TimeoutError"===t.name||"TIMEOUT"===t.code?new e({code:"TIMEOUT",message:"Request timed out. Please try again.",isRetryable:!0},t):new e({code:"UNKNOWN_ERROR",message:t.message||`Unknown error occurred during ${s}`,isRetryable:!0},t)}};function u(e,t=new WeakMap){return void 0===e?"undefined":"function"==typeof e||"symbol"==typeof e?"":"string"==typeof e?`"${e}"`:"number"==typeof e||"boolean"==typeof e||null===e?String(e):Array.isArray(e)?`[${e.map((e=>u(e,t))).join(",")}]`:"object"==typeof e?t.has(e)?'"__circular__"':(t.set(e,!0),`{${Object.keys(e).sort().map((s=>`"${s}":${u(e[s],t)}`)).join(",")}}`):""}function l(e){let t=3421674724,s=2216829733;const r=u(e);for(let e=0;e<r.length;e++){s^=r.charCodeAt(e);const a=Math.imul(s,435),i=Math.imul(s,435)+Math.imul(t,435);s=a>>>0,t=i>>>0}return(t>>>0).toString(16)+(s>>>0).toString(16)}var d=class{constructor(e,t,s){this.operation=e,this.params=t,this.currentResult=s,this.currentResultHash=l(s),this.defaultResult=s}currentResult;currentResultHash;subscribers=new Set;defaultResult;idle=!1;getResult(){return this.currentResult}getParams(){return this.params}getOperation(){return this.operation}updateParams(e){this.params=e}reset(){this.currentResult=this.defaultResult,this.currentResultHash=l(this.defaultResult),this.notifySubscribers(),this.idle=!0}updateResult(e,t=!1){if(this.idle&&!t)return;this.idle=!1;const s=l(e);this.currentResultHash!==s&&(this.currentResult=e,this.currentResultHash=s,this.notifySubscribers())}subscribe(e){return this.subscribers.add(e),()=>{this.subscribers.delete(e)}}notifySubscribers(){this.subscribers.forEach((e=>{try{e()}catch(e){console.error("QueryState: Subscriber callback error:",e)}}))}hasSubscribers(){return this.subscribers.size>0}};exports.ReactiveRemoteStore=class{constructor(e,t,s,r){if(this.cache=e,this.baseStore=t,this.correlator=s,this.storeEventCorrelator=r,this.storeEventCorrelator){queueMicrotask((async()=>{this.unsubscribeFromBaseStore=await this.baseStore.subscribe("*",this.handleStoreEvent.bind(this))}))}this.setupCacheEventListeners()}queryStates=new Map;stableResults=new Map;stablePaginationMethods=new Map;errorCache=new Map;keyCache=new WeakMap;unsubscribeFromBaseStore;cacheEventCleanups=new Set;setupCacheEventListeners(){["cache:fetch:success","cache:fetch:error","cache:data:set","cache:data:invalidate","cache:read:hit"].forEach((e=>{const t=this.cache.on(e,(e=>{this.handleCacheEvent(e)}));this.cacheEventCleanups.add(t)}))}handleCacheEvent(e){const t=this.queryStates.get(e.key);if(t){const s=this.computeResult(e.key);t.updateResult(s)}}buildKey(e,t){if("object"==typeof t&&null!==t&&this.keyCache.has(t))return this.keyCache.get(t);const s=`${e}:${l(t)}`;return"object"==typeof t&&null!==t&&this.keyCache.set(t,s),s}getOrCreateError(e){if(this.errorCache.has(e))return this.errorCache.get(e);const t=new Error(e);return this.errorCache.set(e,t),t}getOrCreateStoreError(e){const t=`store:${e}`;if(this.errorCache.has(t))return this.errorCache.get(t);const s=h.fromError(new Error(e),"query");return this.errorCache.set(t,s),s}computeResult(e){const t=this.cache.getStats().entries.find((t=>t.key===e)),s=this.cache.peek(e);this.scheduleBackgroundFetch(e,t,s);if("read"===e.split(":")[0])return{data:s,loading:t?.isLoading??void 0===s,error:t?.error?this.getOrCreateError("Query failed"):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0};{const r=this.getCurrentPageForQuery(e),a=this.getOrCreatePaginationMethods(e);return{page:s,loading:t?.isLoading??void 0===s,error:t?.error?this.getOrCreateStoreError("Query failed"):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0,hasNext:!!s&&r<s.page.pages,hasPrevious:r>1,...a}}}scheduleBackgroundFetch(e,t,s){t?.isStale&&!t?.isLoading&&queueMicrotask((()=>{this.cache.get(e,{waitForFresh:!1}).catch((t=>{console.warn(`ReactiveRemoteStore: Background refetch failed for ${e}:`,t)}))})),void 0!==s||this.cache.has(e)||t?.isLoading||queueMicrotask((()=>{this.cache.get(e,{waitForFresh:!1}).catch((t=>{console.warn(`ReactiveRemoteStore: Background fetch failed for ${e}:`,t)}))}))}getCurrentPageForQuery(e){const t=this.queryStates.get(e);if(t){return t.getParams().page||1}return 1}getOrCreatePaginationMethods(e){if(this.stablePaginationMethods.has(e))return this.stablePaginationMethods.get(e);const t=this.queryStates.get(e);if(!t)throw new Error(`QueryState not found for ${e}`);const s=t.getOperation(),r={next:async()=>{const r=t.getParams(),a=r.page||1,i=this.buildKey(s,r),o=this.cache.peek(i);if(o&&a<o.page.pages){const i={...r,page:a+1},o=this.buildKey(s,i);if(!this.cache.has(o)){const e="list"===s?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(o,(()=>e(i)))}t.updateParams(i),await this.cache.get(o,{waitForFresh:!0});const n=this.computeResultForParams(s,i,e);t.updateResult(n)}},previous:async()=>{const r=t.getParams(),a=r.page||1;if(a>1){const i={...r,page:a-1},o=this.buildKey(s,i);if(!this.cache.has(o)){const e="list"===s?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(o,(()=>e(i)))}t.updateParams(i),await this.cache.get(o,{waitForFresh:!0});const n=this.computeResultForParams(s,i,e);t.updateResult(n)}},navigate:async r=>{if(r<1)return;const a={...t.getParams(),page:r},i=this.buildKey(s,a);if(!this.cache.has(i)){const e="list"===s?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(i,(()=>e(a)))}t.updateParams(a),await this.cache.get(i,{waitForFresh:!0});const o=this.computeResultForParams(s,a,e);t.updateResult(o)},refresh:async(s=1e3)=>{const r=t.getParams(),a=this.buildKey(t.getOperation(),r);t.reset();const i=this.cache.refresh(a),o=new Promise((e=>setTimeout(e,s)));await Promise.all([i,o]);const n=this.computeResultForParams(t.getOperation(),t.getParams(),e);t.updateResult(n,!0)},changeParams:async s=>{const r=s(t.getParams());if(!r)return void console.error("Setter function for changeParams must return a new parameters object.");const a=this.buildKey(t.getOperation(),r);if(!this.cache.has(a)){const e="list"===t.getOperation()?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(a,(()=>e(r)))}t.updateParams(r),await this.cache.get(a,{waitForFresh:!0});const i=this.computeResultForParams(t.getOperation(),r,e);t.updateResult(i)}};return this.stablePaginationMethods.set(e,r),r}computeResultForParams(e,t,s){const r=this.buildKey(e,t),a=this.cache.getStats().entries.find((e=>e.key===r)),i=this.cache.peek(r);if("read"===e)return{data:i,loading:a?.isLoading??void 0===i,error:a?.error?this.getOrCreateError("Query failed"):void 0,stale:a?.isStale??!0,updated:a?.lastUpdated??0};{const e=t.page||1,r=this.stablePaginationMethods.get(s)??{};return{page:i,loading:a?.isLoading??void 0===i,error:a?.error?this.getOrCreateStoreError("Query failed"):void 0,stale:a?.isStale??!0,updated:a?.lastUpdated??0,hasNext:!!i&&e<i.page.pages,hasPrevious:e>1,...r}}}hasActiveQuery(e,t){const s=this.buildKey(e,t);return this.stableResults.has(s)}getActiveQuery(e,t){const s=this.buildKey(e,t);return this.stableResults.get(s)}read(e){const t=this.buildKey("read",e);if(this.stableResults.has(t))return this.stableResults.get(t);this.cache.has(t)||this.cache.registerQuery(t,(async()=>await this.baseStore.read(e)));const s=this.computeResult(t),r=new d("read",e,s);this.queryStates.set(t,r);const a={value:()=>r.getResult(),onValueChange:e=>{const t=r.subscribe(e);return()=>{t()}}};return this.stableResults.set(t,a),a}list(e){return this.setupPagedQuery("list",e)}find(e){return this.setupPagedQuery("find",e)}setupPagedQuery(e,t){const s=this.buildKey(e,t);if(this.stableResults.has(s))return this.stableResults.get(s);if(!this.cache.has(s)){const r="list"===e?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(s,(()=>r(t)))}const r=this.cache.peek(s),a=new d(e,t,{page:r,loading:void 0===r,error:void 0,stale:!0,updated:0,hasNext:!1,hasPrevious:!1});this.queryStates.set(s,a);const i=this.computeResult(s);a.updateResult(i);const o={value:()=>a.getResult(),onValueChange:e=>{const t=a.subscribe(e);return()=>{t()}}};return this.stableResults.set(s,o),o}async invalidateQueries(e){if(this.correlator){const t=Array.from(this.queryStates.values()).map((e=>({queryKey:this.buildKey(e.getOperation(),e.getParams()),operation:e.getOperation(),params:e.getParams()}))),s=this.correlator(e,t).map((e=>this.cache.invalidate(e)));await Promise.all(s)}else await this.cache.invalidatePattern(/^list:/),await this.cache.invalidatePattern(/^find:/)}handleStoreEvent(e){if(this.storeEventCorrelator){const t=Array.from(this.queryStates.values()).map((e=>({queryKey:this.buildKey(e.getOperation(),e.getParams()),operation:e.getOperation(),params:e.getParams()})));this.storeEventCorrelator(e,t).forEach((e=>this.cache.invalidate(e)))}}async create(e){try{const t=await this.baseStore.create(e);return t&&(this.cache.setData(this.buildKey("read",{id:t.id}),t),await this.invalidateQueries({operation:"create",params:e})),t}catch(e){throw console.error("ReactiveRemoteStore: Create operation failed:",e),e}}async update(e){try{const t=await this.baseStore.update(e);return t&&await this.invalidateQueries({operation:"update",params:e}),t}catch(e){throw console.error("ReactiveRemoteStore: Update operation failed:",e),e}}async delete(e){try{await this.baseStore.delete(e),await this.invalidateQueries({operation:"delete",params:e})}catch(e){throw console.error("ReactiveRemoteStore: Delete operation failed:",e),e}}async notify(e){return this.baseStore.notify(e)}stream(e,t){return this.baseStore.stream(e,t)}async subscribe(e,t){return this.baseStore.subscribe(e,t)}async upload(e){try{const t=await this.baseStore.upload(e);return t&&(this.cache.setData(this.buildKey("read",{id:t.id}),t),await this.invalidateQueries({operation:"upload",params:e})),t}catch(e){throw console.error("ReactiveRemoteStore: Upload operation failed:",e),e}}async refresh(e,t){const s=this.buildKey(e,t);return this.cache.refresh(s)}prefetch(e,t){const s=this.buildKey(e,t);this.cache.prefetch(s).catch((e=>{console.warn(`ReactiveRemoteStore: Prefetch failed for ${s}:`,e)}))}async invalidate(e,t){const s=this.buildKey(e,t);await this.cache.invalidate(s)}async invalidateAll(){const e=Array.from(this.queryStates.keys()).map((e=>this.cache.invalidate(e)));await Promise.all(e)}getStats(){return{...this.cache.getStats(),activeSubscriptions:this.queryStates.size}}destroy(){this.cacheEventCleanups.forEach((e=>e())),this.cacheEventCleanups.clear(),this.queryStates.clear(),this.stableResults.clear(),this.stablePaginationMethods.clear(),this.errorCache.clear(),this.keyCache=new WeakMap,this.unsubscribeFromBaseStore&&this.unsubscribeFromBaseStore()}},exports.StoreError=h,exports.hash=l;
|
package/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=class e extends Error{code;status;data;isRetryable;originalError;constructor(e,t){super(e.message),this.name="StoreError",this.code=e.code,this.status=e.status,this.data=e.data,this.isRetryable=e.isRetryable,this.originalError=t}static fromError(t,r){if(t.isAbort||"AbortError"===t.name)return new e({code:"ABORTED",message:"Request was cancelled",isRetryable:!1},t);if("NetworkError"===t.name||!navigator.onLine)return new e({code:"NETWORK_ERROR",message:"Network connection failed. Please check your internet connection.",isRetryable:!0},t);if(t.status)switch(t.status){case 400:return new e({code:"VALIDATION_ERROR",message:t.message||"Invalid request data",status:400,data:t.data,isRetryable:!1},t);case 401:return new e({code:"UNAUTHORIZED",message:"Authentication required or invalid credentials",status:401,isRetryable:!1},t);case 403:return new e({code:"FORBIDDEN",message:"Access denied for this resource",status:403,isRetryable:!1},t);case 404:return new e({code:"NOT_FOUND",message:"Resource not found",status:404,isRetryable:!1},t);case 409:return new e({code:"CONFLICT",message:t.message||"Resource conflict occurred",status:409,isRetryable:!1},t);case 429:return new e({code:"RATE_LIMITED",message:"Too many requests. Please try again later.",status:429,isRetryable:!0},t);case 500:case 502:case 503:case 504:return new e({code:"SERVER_ERROR",message:"Server error occurred. Please try again later.",status:t.status,isRetryable:!0},t);default:return new e({code:"HTTP_ERROR",message:t.message||`HTTP ${t.status} error occurred`,status:t.status,isRetryable:t.status>=500},t)}return"TimeoutError"===t.name||"TIMEOUT"===t.code?new e({code:"TIMEOUT",message:"Request timed out. Please try again.",isRetryable:!0},t):new e({code:"UNKNOWN_ERROR",message:t.message||`Unknown error occurred during ${r}`,isRetryable:!0},t)}};function t(e,r=new WeakMap){return void 0===e?"undefined":"function"==typeof e||"symbol"==typeof e?"":"string"==typeof e?`"${e}"`:"number"==typeof e||"boolean"==typeof e||null===e?String(e):Array.isArray(e)?`[${e.map((e=>t(e,r))).join(",")}]`:"object"==typeof e?r.has(e)?'"__circular__"':(r.set(e,!0),`{${Object.keys(e).sort().map((a=>`"${a}":${t(e[a],r)}`)).join(",")}}`):""}function r(e){let r=3421674724,a=2216829733;const s=t(e);for(let e=0;e<s.length;e++){a^=s.charCodeAt(e);const t=Math.imul(a,435),i=Math.imul(a,435)+Math.imul(r,435);a=t>>>0,r=i>>>0}return(r>>>0).toString(16)+(a>>>0).toString(16)}var a=class{constructor(e,t,r,a){if(this.cache=e,this.baseStore=t,this.correlator=r,this.storeEventCorrelator=a,this.storeEventCorrelator){queueMicrotask((async()=>{this.unsubscribeFromBaseStore=await this.baseStore.subscribe("*",this.handleStoreEvent.bind(this))}))}}querySubscriptions=new Map;keyCache=new WeakMap;unsubscribeFromBaseStore;createSubscription(e,t,r,a,s){const i=new Set,o=()=>{i.forEach((e=>{try{e()}catch(e){console.error("ReactiveRemoteStore: Subscriber callback error:",e)}}))},c=[this.cache.on("cache:fetch:success",(e=>{s(e.key)&&o()})),this.cache.on("cache:fetch:error",(e=>{s(e.key)&&o()})),this.cache.on("cache:data:set",(e=>{s(e.key)&&o()})),this.cache.on("cache:data:invalidate",(e=>{s(e.key)&&o()})),this.cache.on("cache:read:hit",(e=>{s(e.key)&&e.isStale&&o()}))],n={unsubscribeCallbacks:c,subscribers:i,cachedSubscribe:t=>(i.add(t),()=>{i.delete(t),0===i.size&&(c.forEach((e=>e())),this.querySubscriptions.delete(e))}),cachedSelector:a,operation:t,params:r};return this.querySubscriptions.set(e,n),n}getOrCreateSubscription(e,t,r,a,s){let i=this.querySubscriptions.get(e);return i||(i=this.createSubscription(e,t,r,a,s)),i}buildKey(e,t){if("object"==typeof t&&null!==t&&this.keyCache.has(t))return this.keyCache.get(t);const a=`${e}:${r(t)}`;return"object"==typeof t&&null!==t&&this.keyCache.set(t,a),a}createSelector(e){return()=>{const t=this.cache.getStats().entries.find((t=>t.key===e)),r=this.cache.peek(e);return t?.isStale&&this.cache.get(e,{waitForFresh:!1}).catch((t=>{console.warn(`ReactiveRemoteStore: Background refetch failed for ${e}:`,t)})),void 0!==r&&this.cache.has(e)||this.cache.get(e,{waitForFresh:!1}).catch((t=>{console.warn(`ReactiveRemoteStore: Background fetch failed for ${e}:`,t)})),{data:r,loading:t?.isLoading??void 0===r,error:t?.error?new Error("Query failed"):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0}}}read(e){const t=this.buildKey("read",e);this.querySubscriptions.has(t)||this.cache.registerQuery(t,(async()=>await this.baseStore.read(e)));const r=this.createSelector(t),a=this.getOrCreateSubscription(t,"read",e,r,(e=>e===t));return{value:a.cachedSelector,onValueChange:a.cachedSubscribe}}createPagedSelector(t,r,a){return()=>{const s=this.cache.getStats().entries.find((e=>e.key===t)),i=this.cache.peek(t);s?.isStale&&this.cache.get(t,{waitForFresh:!1}).catch((e=>{console.warn(`ReactiveRemoteStore: Background refetch failed for ${t}:`,e)})),void 0!==i&&this.cache.has(t)||this.cache.get(t,{waitForFresh:!1}).catch((e=>{console.warn(`ReactiveRemoteStore: Background fetch failed for ${t}:`,e)}));const o=r.page||1;return{page:i,loading:s?.isLoading??void 0===i,error:s?.error?e.fromError(new Error("Query failed"),"query"):void 0,stale:s?.isStale??!0,updated:s?.lastUpdated??0,hasNext:!!i&&o<i.page.pages,hasPrevious:o>1,next:async()=>{if(i&&o<i.page.pages){const e={...r,page:o+1},s=this.buildKey(t.split(":")[0],e);this.querySubscriptions.has(s)||this.cache.registerQuery(s,(()=>a(e))),await this.cache.get(s,{waitForFresh:!0})}},previous:async()=>{if(o>1){const e={...r,page:o-1},s=this.buildKey(t.split(":")[0],e);this.querySubscriptions.has(s)||this.cache.registerQuery(s,(()=>a(e))),await this.cache.get(s,{waitForFresh:!0})}},fetch:async e=>{const s={...r,page:e},i=this.buildKey(t.split(":")[0],s);this.querySubscriptions.has(i)||this.cache.registerQuery(i,(()=>a(s))),await this.cache.get(i,{waitForFresh:!0})}}}}setupPagedQuery(e,t,r){const a=this.buildKey(e,t);this.querySubscriptions.has(a)||this.cache.registerQuery(a,(async()=>r(t)));const s=this.createPagedSelector(a,t,r),i=this.getOrCreateSubscription(a,e,t,s,(t=>t.startsWith(`${e}:`)));return{value:i.cachedSelector,onValueChange:i.cachedSubscribe}}list(e){return this.setupPagedQuery("list",e,this.baseStore.list.bind(this.baseStore))}find(e){return this.setupPagedQuery("find",e,this.baseStore.find.bind(this.baseStore))}async invalidateQueries(e){if(this.correlator){const t=Array.from(this.querySubscriptions.entries()).map((([e,t])=>({queryKey:e,operation:t.operation,params:t.params}))),r=this.correlator(e,t).map((e=>this.cache.invalidate(e)));await Promise.all(r)}else await this.cache.invalidatePattern(/^list:/),await this.cache.invalidatePattern(/^find:/)}handleStoreEvent(e){if(console.log("ReactiveRemoteStore: Received store event:",e),this.storeEventCorrelator){const t=Array.from(this.querySubscriptions.entries()).map((([e,t])=>({queryKey:e,operation:t.operation,params:t.params})));console.log("ReactiveRemoteStore: Active queries:",t);const r=this.storeEventCorrelator(e,t);console.log("ReactiveRemoteStore: Queries to invalidate:",r),r.forEach((e=>this.cache.invalidate(e)))}else console.warn("ReactiveRemoteStore: handleStoreEvent called without _storeEventCorrelator.")}async create(e){try{const t=await this.baseStore.create(e);return t&&(this.cache.setData(this.buildKey("read",{id:t.id}),t),await this.invalidateQueries({operation:"create",params:e})),t}catch(e){throw console.error("ReactiveRemoteStore: Create operation failed:",e),e}}async update(e){try{const t=await this.baseStore.update(e);return t&&await this.invalidateQueries({operation:"update",params:e}),t}catch(e){throw console.error("ReactiveRemoteStore: Update operation failed:",e),e}}async delete(e){try{await this.baseStore.delete(e),this.cache.remove(this.buildKey("read",{id:e.id})),await this.invalidateQueries({operation:"delete",params:e})}catch(e){throw console.error("ReactiveRemoteStore: Delete operation failed:",e),e}}async notify(e){return this.baseStore.notify(e)}async stream(e,t){return this.baseStore.stream(e,t)}async upload(e){try{const t=await this.baseStore.upload(e);return t&&(this.cache.setData(this.buildKey("read",{id:t.id}),t),await this.invalidateQueries({operation:"upload",params:e})),t}catch(e){throw console.error("ReactiveRemoteStore: Upload operation failed:",e),e}}async refresh(e,t){const r=this.buildKey(e,t);return this.cache.refresh(r)}prefetch(e,t){const r=this.buildKey(e,t);this.cache.prefetch(r).catch((e=>{console.warn(`ReactiveRemoteStore: Prefetch failed for ${r}:`,e)}))}async invalidate(e,t){const r=this.buildKey(e,t);await this.cache.invalidate(r)}async invalidateAll(){const e=Array.from(this.querySubscriptions.keys()).map((e=>this.cache.invalidate(e)));await Promise.all(e)}getStats(){return{...this.cache.getStats(),activeSubscriptions:this.querySubscriptions.size}}destroy(){this.querySubscriptions.forEach((e=>{e.unsubscribeCallbacks.forEach((e=>e()))})),this.querySubscriptions.clear(),this.keyCache=new WeakMap,this.unsubscribeFromBaseStore&&this.unsubscribeFromBaseStore()}};export{a as ReactiveRemoteStore,e as StoreError};
|
|
1
|
+
import"uuid";var e,t,s=Object.create,a=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,o=Object.getPrototypeOf,n=Object.prototype.hasOwnProperty,c=(e={"node_modules/@asaidimu/events/index.js"(e,t){var s,a=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,n={};((e,t)=>{for(var s in t)a(e,s,{get:t[s],enumerable:!0})})(n,{createEventBus:()=>c}),t.exports=(s=n,((e,t,s,n)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))o.call(e,c)||c===s||a(e,c,{get:()=>t[c],enumerable:!(n=r(t,c))||n.enumerable});return e})(a({},"__esModule",{value:!0}),s));var c=(e={async:!1,batchSize:1e3,batchDelay:16,errorHandler:e=>console.error("EventBus Error:",e),crossTab:!1,channelName:"event-bus-channel"})=>{const t=new Map;let s=[],a=0,r=0;const i=new Map,o=new Map;let n=null;e.crossTab&&"undefined"!=typeof BroadcastChannel?n=new BroadcastChannel(e.channelName):e.crossTab&&console.warn("BroadcastChannel is not supported in this browser. Cross-tab notifications are disabled.");const c=(e,t)=>{a++,r+=t,i.set(e,(i.get(e)||0)+1)},h=()=>{const t=s;s=[],t.forEach((({name:t,payload:s})=>{const a=performance.now();try{(o.get(t)||[]).forEach((e=>e(s)))}catch(a){e.errorHandler({...a,eventName:t,payload:s})}c(t,performance.now()-a)}))},u=(()=>{let t;return()=>{clearTimeout(t),t=setTimeout(h,e.batchDelay)}})(),l=e=>{const s=t.get(e);s?o.set(e,Array.from(s)):o.delete(e)};return n&&(n.onmessage=e=>{const{name:t,payload:s}=e.data;(o.get(t)||[]).forEach((e=>e(s)))}),{subscribe:(e,s)=>{t.has(e)||t.set(e,new Set);const a=t.get(e);return a.add(s),l(e),()=>{a.delete(s),0===a.size?(t.delete(e),o.delete(e)):l(e)}},emit:({name:t,payload:a})=>{if(e.async)return s.push({name:t,payload:a}),s.length>=e.batchSize?h():u(),void(n&&n.postMessage({name:t,payload:a}));const r=performance.now();try{(o.get(t)||[]).forEach((e=>e(a))),n&&n.postMessage({name:t,payload:a})}catch(s){e.errorHandler({...s,eventName:t,payload:a})}c(t,performance.now()-r)},getMetrics:()=>({totalEvents:a,activeSubscriptions:Array.from(t.values()).reduce(((e,t)=>e+t.size),0),eventCounts:i,averageEmitDuration:a>0?r/a:0}),clear:()=>{t.clear(),o.clear(),s=[],a=0,r=0,i.clear(),n&&(n.close(),n=null)}}}}},function(){return t||(0,e[i(e)[0]])((t={exports:{}}).exports,t),t.exports});((e,t,c)=>{c=null!=e?s(o(e)):{},((e,t,s,o)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))n.call(e,c)||c===s||a(e,c,{get:()=>t[c],enumerable:!(o=r(t,c))||o.enumerable})})(e&&e.__esModule?c:a(c,"default",{value:e,enumerable:!0}),e)})(c());var h=class e extends Error{code;status;data;isRetryable;originalError;constructor(e,t){super(e.message),this.name="StoreError",this.code=e.code,this.status=e.status,this.data=e.data,this.isRetryable=e.isRetryable,this.originalError=t}static fromError(t,s){if(t.isAbort||"AbortError"===t.name)return new e({code:"ABORTED",message:"Request was cancelled",isRetryable:!1},t);if("NetworkError"===t.name||!navigator.onLine)return new e({code:"NETWORK_ERROR",message:"Network connection failed. Please check your internet connection.",isRetryable:!0},t);if(t.status)switch(t.status){case 400:return new e({code:"VALIDATION_ERROR",message:t.message||"Invalid request data",status:400,data:t.data,isRetryable:!1},t);case 401:return new e({code:"UNAUTHORIZED",message:"Authentication required or invalid credentials",status:401,isRetryable:!1},t);case 403:return new e({code:"FORBIDDEN",message:"Access denied for this resource",status:403,isRetryable:!1},t);case 404:return new e({code:"NOT_FOUND",message:"Resource not found",status:404,isRetryable:!1},t);case 409:return new e({code:"CONFLICT",message:t.message||"Resource conflict occurred",status:409,isRetryable:!1},t);case 429:return new e({code:"RATE_LIMITED",message:"Too many requests. Please try again later.",status:429,isRetryable:!0},t);case 500:case 502:case 503:case 504:return new e({code:"SERVER_ERROR",message:"Server error occurred. Please try again later.",status:t.status,isRetryable:!0},t);default:return new e({code:"HTTP_ERROR",message:t.message||`HTTP ${t.status} error occurred`,status:t.status,isRetryable:t.status>=500},t)}return"TimeoutError"===t.name||"TIMEOUT"===t.code?new e({code:"TIMEOUT",message:"Request timed out. Please try again.",isRetryable:!0},t):new e({code:"UNKNOWN_ERROR",message:t.message||`Unknown error occurred during ${s}`,isRetryable:!0},t)}};function u(e,t=new WeakMap){return void 0===e?"undefined":"function"==typeof e||"symbol"==typeof e?"":"string"==typeof e?`"${e}"`:"number"==typeof e||"boolean"==typeof e||null===e?String(e):Array.isArray(e)?`[${e.map((e=>u(e,t))).join(",")}]`:"object"==typeof e?t.has(e)?'"__circular__"':(t.set(e,!0),`{${Object.keys(e).sort().map((s=>`"${s}":${u(e[s],t)}`)).join(",")}}`):""}function l(e){let t=3421674724,s=2216829733;const a=u(e);for(let e=0;e<a.length;e++){s^=a.charCodeAt(e);const r=Math.imul(s,435),i=Math.imul(s,435)+Math.imul(t,435);s=r>>>0,t=i>>>0}return(t>>>0).toString(16)+(s>>>0).toString(16)}var d=class{constructor(e,t,s){this.operation=e,this.params=t,this.currentResult=s,this.currentResultHash=l(s),this.defaultResult=s}currentResult;currentResultHash;subscribers=new Set;defaultResult;idle=!1;getResult(){return this.currentResult}getParams(){return this.params}getOperation(){return this.operation}updateParams(e){this.params=e}reset(){this.currentResult=this.defaultResult,this.currentResultHash=l(this.defaultResult),this.notifySubscribers(),this.idle=!0}updateResult(e,t=!1){if(this.idle&&!t)return;this.idle=!1;const s=l(e);this.currentResultHash!==s&&(this.currentResult=e,this.currentResultHash=s,this.notifySubscribers())}subscribe(e){return this.subscribers.add(e),()=>{this.subscribers.delete(e)}}notifySubscribers(){this.subscribers.forEach((e=>{try{e()}catch(e){console.error("QueryState: Subscriber callback error:",e)}}))}hasSubscribers(){return this.subscribers.size>0}},p=class{constructor(e,t,s,a){if(this.cache=e,this.baseStore=t,this.correlator=s,this.storeEventCorrelator=a,this.storeEventCorrelator){queueMicrotask((async()=>{this.unsubscribeFromBaseStore=await this.baseStore.subscribe("*",this.handleStoreEvent.bind(this))}))}this.setupCacheEventListeners()}queryStates=new Map;stableResults=new Map;stablePaginationMethods=new Map;errorCache=new Map;keyCache=new WeakMap;unsubscribeFromBaseStore;cacheEventCleanups=new Set;setupCacheEventListeners(){["cache:fetch:success","cache:fetch:error","cache:data:set","cache:data:invalidate","cache:read:hit"].forEach((e=>{const t=this.cache.on(e,(e=>{this.handleCacheEvent(e)}));this.cacheEventCleanups.add(t)}))}handleCacheEvent(e){const t=this.queryStates.get(e.key);if(t){const s=this.computeResult(e.key);t.updateResult(s)}}buildKey(e,t){if("object"==typeof t&&null!==t&&this.keyCache.has(t))return this.keyCache.get(t);const s=`${e}:${l(t)}`;return"object"==typeof t&&null!==t&&this.keyCache.set(t,s),s}getOrCreateError(e){if(this.errorCache.has(e))return this.errorCache.get(e);const t=new Error(e);return this.errorCache.set(e,t),t}getOrCreateStoreError(e){const t=`store:${e}`;if(this.errorCache.has(t))return this.errorCache.get(t);const s=h.fromError(new Error(e),"query");return this.errorCache.set(t,s),s}computeResult(e){const t=this.cache.getStats().entries.find((t=>t.key===e)),s=this.cache.peek(e);this.scheduleBackgroundFetch(e,t,s);if("read"===e.split(":")[0])return{data:s,loading:t?.isLoading??void 0===s,error:t?.error?this.getOrCreateError("Query failed"):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0};{const a=this.getCurrentPageForQuery(e),r=this.getOrCreatePaginationMethods(e);return{page:s,loading:t?.isLoading??void 0===s,error:t?.error?this.getOrCreateStoreError("Query failed"):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0,hasNext:!!s&&a<s.page.pages,hasPrevious:a>1,...r}}}scheduleBackgroundFetch(e,t,s){t?.isStale&&!t?.isLoading&&queueMicrotask((()=>{this.cache.get(e,{waitForFresh:!1}).catch((t=>{console.warn(`ReactiveRemoteStore: Background refetch failed for ${e}:`,t)}))})),void 0!==s||this.cache.has(e)||t?.isLoading||queueMicrotask((()=>{this.cache.get(e,{waitForFresh:!1}).catch((t=>{console.warn(`ReactiveRemoteStore: Background fetch failed for ${e}:`,t)}))}))}getCurrentPageForQuery(e){const t=this.queryStates.get(e);if(t){return t.getParams().page||1}return 1}getOrCreatePaginationMethods(e){if(this.stablePaginationMethods.has(e))return this.stablePaginationMethods.get(e);const t=this.queryStates.get(e);if(!t)throw new Error(`QueryState not found for ${e}`);const s=t.getOperation(),a={next:async()=>{const a=t.getParams(),r=a.page||1,i=this.buildKey(s,a),o=this.cache.peek(i);if(o&&r<o.page.pages){const i={...a,page:r+1},o=this.buildKey(s,i);if(!this.cache.has(o)){const e="list"===s?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(o,(()=>e(i)))}t.updateParams(i),await this.cache.get(o,{waitForFresh:!0});const n=this.computeResultForParams(s,i,e);t.updateResult(n)}},previous:async()=>{const a=t.getParams(),r=a.page||1;if(r>1){const i={...a,page:r-1},o=this.buildKey(s,i);if(!this.cache.has(o)){const e="list"===s?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(o,(()=>e(i)))}t.updateParams(i),await this.cache.get(o,{waitForFresh:!0});const n=this.computeResultForParams(s,i,e);t.updateResult(n)}},navigate:async a=>{if(a<1)return;const r={...t.getParams(),page:a},i=this.buildKey(s,r);if(!this.cache.has(i)){const e="list"===s?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(i,(()=>e(r)))}t.updateParams(r),await this.cache.get(i,{waitForFresh:!0});const o=this.computeResultForParams(s,r,e);t.updateResult(o)},refresh:async(s=1e3)=>{const a=t.getParams(),r=this.buildKey(t.getOperation(),a);t.reset();const i=this.cache.refresh(r),o=new Promise((e=>setTimeout(e,s)));await Promise.all([i,o]);const n=this.computeResultForParams(t.getOperation(),t.getParams(),e);t.updateResult(n,!0)},changeParams:async s=>{const a=s(t.getParams());if(!a)return void console.error("Setter function for changeParams must return a new parameters object.");const r=this.buildKey(t.getOperation(),a);if(!this.cache.has(r)){const e="list"===t.getOperation()?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(r,(()=>e(a)))}t.updateParams(a),await this.cache.get(r,{waitForFresh:!0});const i=this.computeResultForParams(t.getOperation(),a,e);t.updateResult(i)}};return this.stablePaginationMethods.set(e,a),a}computeResultForParams(e,t,s){const a=this.buildKey(e,t),r=this.cache.getStats().entries.find((e=>e.key===a)),i=this.cache.peek(a);if("read"===e)return{data:i,loading:r?.isLoading??void 0===i,error:r?.error?this.getOrCreateError("Query failed"):void 0,stale:r?.isStale??!0,updated:r?.lastUpdated??0};{const e=t.page||1,a=this.stablePaginationMethods.get(s)??{};return{page:i,loading:r?.isLoading??void 0===i,error:r?.error?this.getOrCreateStoreError("Query failed"):void 0,stale:r?.isStale??!0,updated:r?.lastUpdated??0,hasNext:!!i&&e<i.page.pages,hasPrevious:e>1,...a}}}hasActiveQuery(e,t){const s=this.buildKey(e,t);return this.stableResults.has(s)}getActiveQuery(e,t){const s=this.buildKey(e,t);return this.stableResults.get(s)}read(e){const t=this.buildKey("read",e);if(this.stableResults.has(t))return this.stableResults.get(t);this.cache.has(t)||this.cache.registerQuery(t,(async()=>await this.baseStore.read(e)));const s=this.computeResult(t),a=new d("read",e,s);this.queryStates.set(t,a);const r={value:()=>a.getResult(),onValueChange:e=>{const t=a.subscribe(e);return()=>{t()}}};return this.stableResults.set(t,r),r}list(e){return this.setupPagedQuery("list",e)}find(e){return this.setupPagedQuery("find",e)}setupPagedQuery(e,t){const s=this.buildKey(e,t);if(this.stableResults.has(s))return this.stableResults.get(s);if(!this.cache.has(s)){const a="list"===e?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(s,(()=>a(t)))}const a=this.cache.peek(s),r=new d(e,t,{page:a,loading:void 0===a,error:void 0,stale:!0,updated:0,hasNext:!1,hasPrevious:!1});this.queryStates.set(s,r);const i=this.computeResult(s);r.updateResult(i);const o={value:()=>r.getResult(),onValueChange:e=>{const t=r.subscribe(e);return()=>{t()}}};return this.stableResults.set(s,o),o}async invalidateQueries(e){if(this.correlator){const t=Array.from(this.queryStates.values()).map((e=>({queryKey:this.buildKey(e.getOperation(),e.getParams()),operation:e.getOperation(),params:e.getParams()}))),s=this.correlator(e,t).map((e=>this.cache.invalidate(e)));await Promise.all(s)}else await this.cache.invalidatePattern(/^list:/),await this.cache.invalidatePattern(/^find:/)}handleStoreEvent(e){if(this.storeEventCorrelator){const t=Array.from(this.queryStates.values()).map((e=>({queryKey:this.buildKey(e.getOperation(),e.getParams()),operation:e.getOperation(),params:e.getParams()})));this.storeEventCorrelator(e,t).forEach((e=>this.cache.invalidate(e)))}}async create(e){try{const t=await this.baseStore.create(e);return t&&(this.cache.setData(this.buildKey("read",{id:t.id}),t),await this.invalidateQueries({operation:"create",params:e})),t}catch(e){throw console.error("ReactiveRemoteStore: Create operation failed:",e),e}}async update(e){try{const t=await this.baseStore.update(e);return t&&await this.invalidateQueries({operation:"update",params:e}),t}catch(e){throw console.error("ReactiveRemoteStore: Update operation failed:",e),e}}async delete(e){try{await this.baseStore.delete(e),await this.invalidateQueries({operation:"delete",params:e})}catch(e){throw console.error("ReactiveRemoteStore: Delete operation failed:",e),e}}async notify(e){return this.baseStore.notify(e)}stream(e,t){return this.baseStore.stream(e,t)}async subscribe(e,t){return this.baseStore.subscribe(e,t)}async upload(e){try{const t=await this.baseStore.upload(e);return t&&(this.cache.setData(this.buildKey("read",{id:t.id}),t),await this.invalidateQueries({operation:"upload",params:e})),t}catch(e){throw console.error("ReactiveRemoteStore: Upload operation failed:",e),e}}async refresh(e,t){const s=this.buildKey(e,t);return this.cache.refresh(s)}prefetch(e,t){const s=this.buildKey(e,t);this.cache.prefetch(s).catch((e=>{console.warn(`ReactiveRemoteStore: Prefetch failed for ${s}:`,e)}))}async invalidate(e,t){const s=this.buildKey(e,t);await this.cache.invalidate(s)}async invalidateAll(){const e=Array.from(this.queryStates.keys()).map((e=>this.cache.invalidate(e)));await Promise.all(e)}getStats(){return{...this.cache.getStats(),activeSubscriptions:this.queryStates.size}}destroy(){this.cacheEventCleanups.forEach((e=>e())),this.cacheEventCleanups.clear(),this.queryStates.clear(),this.stableResults.clear(),this.stablePaginationMethods.clear(),this.errorCache.clear(),this.keyCache=new WeakMap,this.unsubscribeFromBaseStore&&this.unsubscribeFromBaseStore()}};export{p as ReactiveRemoteStore,h as StoreError,l as hash};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asaidimu/utils-remote-store",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A reactive store for remote data, built on top of @asaidimu/utils-cache",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"./*"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@asaidimu/utils-cache": "2.0
|
|
12
|
+
"@asaidimu/utils-cache": "2.1.0",
|
|
13
13
|
"eventsource": "^4.0.0"
|
|
14
14
|
},
|
|
15
15
|
"exports": {
|