@asaidimu/utils-remote-store 1.3.12 → 1.3.13

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.ts CHANGED
@@ -1,154 +1,5 @@
1
- import { SimplePersistence } from "@asaidimu/utils-persistence";
1
+ import { QueryCache } from "@asaidimu/utils-cache";
2
2
 
3
- //#region src/cache/types.d.ts
4
- interface CacheOptions {
5
- staleTime?: number;
6
- cacheTime?: number;
7
- retryAttempts?: number;
8
- retryDelay?: number;
9
- maxSize?: number;
10
- enableMetrics?: boolean;
11
- persistence?: SimplePersistence<SerializableCacheState>;
12
- persistenceId?: string;
13
- serializeValue?: (value: any) => any;
14
- deserializeValue?: (value: any) => any;
15
- persistenceDebounceTime?: number;
16
- }
17
- interface CacheMetrics {
18
- hits: number;
19
- misses: number;
20
- fetches: number;
21
- errors: number;
22
- evictions: number;
23
- staleHits: number;
24
- }
25
- interface SerializableCacheEntry {
26
- data: any;
27
- lastUpdated: number;
28
- lastAccessed: number;
29
- accessCount: number;
30
- error?: {
31
- name: string;
32
- message: string;
33
- stack?: string;
34
- };
35
- }
36
- type SerializableCacheState = Array<[string, SerializableCacheEntry]>;
37
- type CacheEventBase<Type extends string, Payload = {}> = {
38
- type: Type;
39
- key: string;
40
- timestamp: number;
41
- } & Payload;
42
- type CacheReadHitEvent<T = any> = CacheEventBase<"cache:read:hit", {
43
- data: T;
44
- isStale: boolean;
45
- }>;
46
- type CacheReadMissEvent = CacheEventBase<"cache:read:miss">;
47
- type CacheFetchStartEvent = CacheEventBase<"cache:fetch:start", {
48
- attempt: number;
49
- }>;
50
- type CacheFetchSuccessEvent<T = any> = CacheEventBase<"cache:fetch:success", {
51
- data: T;
52
- }>;
53
- type CacheFetchErrorEvent = CacheEventBase<"cache:fetch:error", {
54
- error: Error;
55
- attempt: number;
56
- }>;
57
- type CacheDataEvictEvent = CacheEventBase<"cache:data:evict", {
58
- reason?: string;
59
- }>;
60
- type CacheDataInvalidateEvent = CacheEventBase<"cache:data:invalidate">;
61
- type CacheDataSetEvent<T = any> = CacheEventBase<"cache:data:set", {
62
- newData: T;
63
- oldData?: T;
64
- }>;
65
- type CachePersistenceLoadSuccessEvent = CacheEventBase<"cache:persistence:load:success", {
66
- message?: string;
67
- }>;
68
- type CachePersistenceLoadErrorEvent = CacheEventBase<"cache:persistence:load:error", {
69
- message?: string;
70
- error?: any;
71
- }>;
72
- type CachePersistenceSaveSuccessEvent = CacheEventBase<"cache:persistence:save:success">;
73
- type CachePersistenceSaveErrorEvent = CacheEventBase<"cache:persistence:save:error", {
74
- message?: string;
75
- error?: any;
76
- }>;
77
- type CachePersistenceClearSuccessEvent = CacheEventBase<"cache:persistence:clear:success">;
78
- type CachePersistenceClearErrorEvent = CacheEventBase<"cache:persistence:clear:error", {
79
- message?: string;
80
- error?: any;
81
- }>;
82
- type CachePersistenceSyncEvent = CacheEventBase<"cache:persistence:sync", {
83
- message?: string;
84
- }>;
85
- type CacheEvent = CacheReadHitEvent | CacheReadMissEvent | CacheFetchStartEvent | CacheFetchSuccessEvent | CacheFetchErrorEvent | CacheDataEvictEvent | CacheDataInvalidateEvent | CacheDataSetEvent | CachePersistenceLoadSuccessEvent | CachePersistenceLoadErrorEvent | CachePersistenceSaveSuccessEvent | CachePersistenceSaveErrorEvent | CachePersistenceClearSuccessEvent | CachePersistenceClearErrorEvent | CachePersistenceSyncEvent;
86
- type CacheEventType = CacheEvent["type"];
87
- //#endregion
88
- //#region src/cache/cache.d.ts
89
- declare class QueryCache {
90
- private cache;
91
- private queries;
92
- private fetching;
93
- private readonly defaultOptions;
94
- private metrics;
95
- private eventBus;
96
- private gcTimer?;
97
- private readonly persistenceId;
98
- private persistenceUnsubscribe?;
99
- private persistenceDebounceTimer?;
100
- private isHandlingRemoteUpdate;
101
- constructor(defaultOptions?: CacheOptions);
102
- private initializePersistence;
103
- private serializeCache;
104
- private deserializeAndLoadCache;
105
- private schedulePersistState;
106
- private handleRemoteStateChange;
107
- registerQuery<T>(key: string, fetchFunction: () => Promise<T>, options?: CacheOptions): void;
108
- get<T>(key: string, options?: {
109
- waitForFresh?: boolean;
110
- throwOnError?: boolean;
111
- }): Promise<T | undefined>;
112
- peek<T>(key: string): T | undefined;
113
- has(key: string): boolean;
114
- private fetch;
115
- private fetchAndWait;
116
- private performFetchWithRetry;
117
- private isStale;
118
- invalidate(key: string, refetch?: boolean): Promise<void>;
119
- invalidatePattern(pattern: RegExp, refetch?: boolean): Promise<void>;
120
- prefetch(key: string): Promise<void>;
121
- refresh<T>(key: string): Promise<T | undefined>;
122
- setData<T>(key: string, data: T): void;
123
- remove(key: string): boolean;
124
- private enforceSizeLimit;
125
- private startGarbageCollection;
126
- garbageCollect(): number;
127
- getStats(): {
128
- size: number;
129
- metrics: CacheMetrics;
130
- hitRate: number;
131
- staleHitRate: number;
132
- entries: Array<{
133
- key: string;
134
- lastAccessed: number;
135
- lastUpdated: number;
136
- accessCount: number;
137
- isStale: boolean;
138
- isLoading?: boolean;
139
- error?: boolean;
140
- }>;
141
- };
142
- on<EType extends CacheEventType>(event: EType, listener: (ev: Extract<CacheEvent, {
143
- type: EType;
144
- }>) => void): () => void;
145
- private emitEvent;
146
- private updateMetrics;
147
- private delay;
148
- clear(): Promise<void>;
149
- destroy(): void;
150
- }
151
- //#endregion
152
3
  //#region src/remote-store/error.d.ts
153
4
  interface StoreErrorDetails {
154
5
  code: string;
@@ -474,7 +325,7 @@ declare class ReactiveRemoteStore<T extends StoreRecord, TFindOptions = Record<s
474
325
  getStats(): {
475
326
  activeSubscriptions: number;
476
327
  size: number;
477
- metrics: CacheMetrics;
328
+ metrics: import("@core/cache").CacheMetrics;
478
329
  hitRate: number;
479
330
  staleHitRate: number;
480
331
  entries: Array<{
package/index.js CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require("uuid"),((e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports))(((e,t)=>{var n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o=(e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})},s=(e,t,o,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of i(t))!a.call(e,c)&&c!==o&&n(e,c,{get:()=>t[c],enumerable:!(s=r(t,c))||s.enumerable});return e},c=e=>s(n({},`__esModule`,{value:!0}),e),l={};o(l,{createEventBus:()=>u}),t.exports=c(l);var u=(e={async:!1,batchSize:1e3,batchDelay:16,errorHandler:e=>console.error(`EventBus Error:`,e),crossTab:!1,channelName:`event-bus-channel`})=>{let t=new Map,n=[],r=0,i=0,a=new Map,o=new Map,s=null;e.crossTab&&typeof BroadcastChannel<`u`?s=new BroadcastChannel(e.channelName):e.crossTab&&console.warn(`BroadcastChannel is not supported in this browser. Cross-tab notifications are disabled.`);let c=(e,t)=>{r++,i+=t,a.set(e,(a.get(e)||0)+1)},l=()=>{let t=n;n=[],t.forEach(({name:t,payload:n})=>{let r=performance.now();try{(o.get(t)||[]).forEach(e=>e(n))}catch(r){e.errorHandler({...r,eventName:t,payload:n})}c(t,performance.now()-r)})},u=(()=>{let t;return()=>{clearTimeout(t),t=setTimeout(l,e.batchDelay)}})(),d=e=>{let n=t.get(e);n?o.set(e,Array.from(n)):o.delete(e)};return s&&(s.onmessage=e=>{let{name:t,payload:n}=e.data;(o.get(t)||[]).forEach(e=>e(n))}),{subscribe:(e,n)=>{t.has(e)||t.set(e,new Set);let r=t.get(e);return r.add(n),d(e),()=>{r.delete(n),r.size===0?(t.delete(e),o.delete(e)):d(e)}},emit:({name:t,payload:r})=>{if(e.async){n.push({name:t,payload:r}),n.length>=e.batchSize?l():u(),s&&s.postMessage({name:t,payload:r});return}let i=performance.now();try{(o.get(t)||[]).forEach(e=>e(r)),s&&s.postMessage({name:t,payload:r})}catch(n){e.errorHandler({...n,eventName:t,payload:r})}c(t,performance.now()-i)},getMetrics:()=>({totalEvents:r,activeSubscriptions:Array.from(t.values()).reduce((e,t)=>e+t.size,0),eventCounts:a,averageEmitDuration:r>0?i/r:0}),clear:()=>{t.clear(),o.clear(),n=[],r=0,i=0,a.clear(),s&&=(s.close(),null)}}};0&&(t.exports={createEventBus:u})}))();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,n){if(t.isAbort||t.name===`AbortError`)return new e({code:`ABORTED`,message:`Request was cancelled`,isRetryable:!1},t);if(t.name===`NetworkError`||!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 t.name===`TimeoutError`||t.code===`TIMEOUT`?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 ${n}`,isRetryable:!0},t)}};function t(e,n=new WeakMap){return e===void 0?`undefined`:typeof e==`function`||typeof e==`symbol`?``:typeof e==`string`?`"${e}"`:typeof e==`number`||typeof e==`boolean`||e===null?String(e):Array.isArray(e)?`[${e.map(e=>t(e,n)).join(`,`)}]`:typeof e==`object`?n.has(e)?`"__circular__"`:(n.set(e,!0),`{${Object.keys(e).sort().map(r=>`"${r}":${t(e[r],n)}`).join(`,`)}}`):``}function n(e){let n=3421674724,r=2216829733,i=t(e);for(let e=0;e<i.length;e++){r^=i.charCodeAt(e);let t=Math.imul(r,435),a=Math.imul(r,435)+Math.imul(n,435);r=t>>>0,n=a>>>0}return(n>>>0).toString(16)+(r>>>0).toString(16)}var r=class{operation;params;currentResult;currentResultHash;subscribers=new Set;defaultResult;idle=!1;constructor(e,t,r){this.operation=e,this.params=t,this.currentResult=r,this.currentResultHash=n(r),this.defaultResult=r}getResult(){return this.currentResult}getParams(){return this.params}getOperation(){return this.operation}updateParams(e){this.params=e}reset(){this.currentResult=this.defaultResult,this.currentResultHash=n(this.defaultResult),this.notifySubscribers(),this.idle=!0}updateResult(e,t=!1){if(this.idle&&!t)return;this.idle=!1;let r=n(e);this.currentResultHash!==r&&(this.currentResult=e,this.currentResultHash=r,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}},i=class{cache;baseStore;correlator;storeEventCorrelator;queryStates=new Map;stableResults=new Map;stablePaginationMethods=new Map;errorCache=new Map;keyCache=new WeakMap;unsubscribeFromBaseStore;cacheEventCleanups=new Set;constructor(e,t,n,r){this.cache=e,this.baseStore=t,this.correlator=n,this.storeEventCorrelator=r,this.storeEventCorrelator&&queueMicrotask(async()=>{this.unsubscribeFromBaseStore=await this.baseStore.subscribe(`*`,this.handleStoreEvent.bind(this))}),this.setupCacheEventListeners()}setupCacheEventListeners(){[`cache:fetch:success`,`cache:fetch:error`,`cache:data:set`,`cache:data:invalidate`,`cache:read:hit`].forEach(e=>{let t=this.cache.on(e,e=>{this.handleCacheEvent(e)});this.cacheEventCleanups.add(t)})}handleCacheEvent(e){let t=this.queryStates.get(e.key);if(t){let n=this.computeResult(e.key);t.updateResult(n)}}buildKey(e,t){if(typeof t==`object`&&t&&this.keyCache.has(t))return this.keyCache.get(t);let r=`${e}:${n(t)}`;return typeof t==`object`&&t&&this.keyCache.set(t,r),r}getOrCreateError(e){if(this.errorCache.has(e))return this.errorCache.get(e);let t=Error(e);return this.errorCache.set(e,t),t}getOrCreateStoreError(t){let n=`store:${t}`;if(this.errorCache.has(n))return this.errorCache.get(n);let r=e.fromError(Error(t),`query`);return this.errorCache.set(n,r),r}computeResult(e){let t=this.cache.getStats().entries.find(t=>t.key===e),n=this.cache.peek(e);if(this.scheduleBackgroundFetch(e,t,n),e.split(`:`)[0]===`read`)return{data:n,loading:t?.isLoading??n===void 0,error:t?.error?this.getOrCreateError(`Query failed`):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0};{let r=this.getCurrentPageForQuery(e),i=this.getOrCreatePaginationMethods(e);return{page:n,loading:t?.isLoading??n===void 0,error:t?.error?this.getOrCreateStoreError(`Query failed`):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0,hasNext:n?r<n.page.pages:!1,hasPrevious:r>1,...i}}}scheduleBackgroundFetch(e,t,n){t?.isStale&&!t?.isLoading&&queueMicrotask(()=>{this.cache.get(e,{waitForFresh:!1}).catch(t=>{console.warn(`ReactiveRemoteStore: Background refetch failed for ${e}:`,t)})}),n===void 0&&!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){let t=this.queryStates.get(e);return t&&t.getParams().page||1}getOrCreatePaginationMethods(e){if(this.stablePaginationMethods.has(e))return this.stablePaginationMethods.get(e);let t=this.queryStates.get(e);if(!t)throw Error(`QueryState not found for ${e}`);let n=t.getOperation(),r={next:async()=>{let r=t.getParams(),i=r.page||1,a=this.buildKey(n,r),o=this.cache.peek(a);if(o&&i<o.page.pages){let a={...r,page:i+1},o=this.buildKey(n,a);if(!this.cache.has(o)){let e=n===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(o,()=>e(a))}t.updateParams(a),await this.cache.get(o,{waitForFresh:!0});let s=this.computeResultForParams(n,a,e);t.updateResult(s)}},previous:async()=>{let r=t.getParams(),i=r.page||1;if(i>1){let a={...r,page:i-1},o=this.buildKey(n,a);if(!this.cache.has(o)){let e=n===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(o,()=>e(a))}t.updateParams(a),await this.cache.get(o,{waitForFresh:!0});let s=this.computeResultForParams(n,a,e);t.updateResult(s)}},navigate:async r=>{if(r<1)return;let i={...t.getParams(),page:r},a=this.buildKey(n,i);if(!this.cache.has(a)){let e=n===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(a,()=>e(i))}t.updateParams(i),await this.cache.get(a,{waitForFresh:!0});let o=this.computeResultForParams(n,i,e);t.updateResult(o)},refresh:async(n=1e3)=>{let r=t.getParams(),i=this.buildKey(t.getOperation(),r);t.reset();let a=this.cache.refresh(i),o=new Promise(e=>setTimeout(e,n));await Promise.all([a,o]);let s=this.computeResultForParams(t.getOperation(),t.getParams(),e);t.updateResult(s,!0)},changeParams:async n=>{let r=n(t.getParams());if(!r){console.error(`Setter function for changeParams must return a new parameters object.`);return}let i=this.buildKey(t.getOperation(),r);if(!this.cache.has(i)){let e=t.getOperation()===`list`?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});let a=this.computeResultForParams(t.getOperation(),r,e);t.updateResult(a)}};return this.stablePaginationMethods.set(e,r),r}computeResultForParams(e,t,n){let r=this.buildKey(e,t),i=this.cache.getStats().entries.find(e=>e.key===r),a=this.cache.peek(r);if(e===`read`)return{data:a,loading:i?.isLoading??a===void 0,error:i?.error?this.getOrCreateError(`Query failed`):void 0,stale:i?.isStale??!0,updated:i?.lastUpdated??0};{let e=t.page||1,r=this.stablePaginationMethods.get(n)??{};return{page:a,loading:i?.isLoading??a===void 0,error:i?.error?this.getOrCreateStoreError(`Query failed`):void 0,stale:i?.isStale??!0,updated:i?.lastUpdated??0,hasNext:a?e<a.page.pages:!1,hasPrevious:e>1,...r}}}hasActiveQuery(e,t){let n=this.buildKey(e,t);return this.stableResults.has(n)}getActiveQuery(e,t){let n=this.buildKey(e,t);return this.stableResults.get(n)}read(e){let 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));let n=new r(`read`,e,this.computeResult(t));this.queryStates.set(t,n);let i={value:()=>n.getResult(),onValueChange:e=>{let t=n.subscribe(e);return()=>{t()}}};return this.stableResults.set(t,i),i}list(e){return this.setupPagedQuery(`list`,e)}find(e){return this.setupPagedQuery(`find`,e)}setupPagedQuery(e,t){let n=this.buildKey(e,t);if(this.stableResults.has(n))return this.stableResults.get(n);if(!this.cache.has(n)){let r=e===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(n,()=>r(t))}let i=this.cache.peek(n),a=new r(e,t,{page:i,loading:i===void 0,error:void 0,stale:!0,updated:0,hasNext:!1,hasPrevious:!1});this.queryStates.set(n,a);let o=this.computeResult(n);a.updateResult(o);let s={value:()=>a.getResult(),onValueChange:e=>{let t=a.subscribe(e);return()=>{t()}}};return this.stableResults.set(n,s),s}async invalidateQueries(e){if(this.correlator){let t=Array.from(this.queryStates.values()).map(e=>({queryKey:this.buildKey(e.getOperation(),e.getParams()),operation:e.getOperation(),params:e.getParams()})),n=this.correlator(e,t).map(e=>this.cache.invalidate(e));await Promise.all(n)}else await this.cache.invalidatePattern(/^list:/),await this.cache.invalidatePattern(/^find:/)}handleStoreEvent(e){if(this.storeEventCorrelator){let 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{let 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{let 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{let 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){let n=this.buildKey(e,t);return this.cache.refresh(n)}prefetch(e,t){let n=this.buildKey(e,t);this.cache.prefetch(n).catch(e=>{console.warn(`ReactiveRemoteStore: Prefetch failed for ${n}:`,e)})}async invalidate(e,t){let n=this.buildKey(e,t);await this.cache.invalidate(n)}async invalidateAll(){let 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.ReactiveRemoteStore=i,exports.StoreError=e,exports.hash=n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require("@asaidimu/utils-cache");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,n){if(t.isAbort||t.name===`AbortError`)return new e({code:`ABORTED`,message:`Request was cancelled`,isRetryable:!1},t);if(t.name===`NetworkError`||!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 t.name===`TimeoutError`||t.code===`TIMEOUT`?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 ${n}`,isRetryable:!0},t)}};function t(e,n=new WeakMap){return e===void 0?`undefined`:typeof e==`function`||typeof e==`symbol`?``:typeof e==`string`?`"${e}"`:typeof e==`number`||typeof e==`boolean`||e===null?String(e):Array.isArray(e)?`[${e.map(e=>t(e,n)).join(`,`)}]`:typeof e==`object`?n.has(e)?`"__circular__"`:(n.set(e,!0),`{${Object.keys(e).sort().map(r=>`"${r}":${t(e[r],n)}`).join(`,`)}}`):``}function n(e){let n=3421674724,r=2216829733,i=t(e);for(let e=0;e<i.length;e++){r^=i.charCodeAt(e);let t=Math.imul(r,435),a=Math.imul(r,435)+Math.imul(n,435);r=t>>>0,n=a>>>0}return(n>>>0).toString(16)+(r>>>0).toString(16)}var r=class{operation;params;currentResult;currentResultHash;subscribers=new Set;defaultResult;idle=!1;constructor(e,t,r){this.operation=e,this.params=t,this.currentResult=r,this.currentResultHash=n(r),this.defaultResult=r}getResult(){return this.currentResult}getParams(){return this.params}getOperation(){return this.operation}updateParams(e){this.params=e}reset(){this.currentResult=this.defaultResult,this.currentResultHash=n(this.defaultResult),this.notifySubscribers(),this.idle=!0}updateResult(e,t=!1){if(this.idle&&!t)return;this.idle=!1;let r=n(e);this.currentResultHash!==r&&(this.currentResult=e,this.currentResultHash=r,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}},i=class{cache;baseStore;correlator;storeEventCorrelator;queryStates=new Map;stableResults=new Map;stablePaginationMethods=new Map;errorCache=new Map;keyCache=new WeakMap;unsubscribeFromBaseStore;cacheEventCleanups=new Set;constructor(e,t,n,r){this.cache=e,this.baseStore=t,this.correlator=n,this.storeEventCorrelator=r,this.storeEventCorrelator&&queueMicrotask(async()=>{this.unsubscribeFromBaseStore=await this.baseStore.subscribe(`*`,this.handleStoreEvent.bind(this))}),this.setupCacheEventListeners()}setupCacheEventListeners(){[`cache:fetch:success`,`cache:fetch:error`,`cache:data:set`,`cache:data:invalidate`,`cache:read:hit`].forEach(e=>{let t=this.cache.on(e,e=>{this.handleCacheEvent(e)});this.cacheEventCleanups.add(t)})}handleCacheEvent(e){let t=this.queryStates.get(e.key);if(t){let n=this.computeResult(e.key);t.updateResult(n)}}buildKey(e,t){if(typeof t==`object`&&t&&this.keyCache.has(t))return this.keyCache.get(t);let r=`${e}:${n(t)}`;return typeof t==`object`&&t&&this.keyCache.set(t,r),r}getOrCreateError(e){if(this.errorCache.has(e))return this.errorCache.get(e);let t=Error(e);return this.errorCache.set(e,t),t}getOrCreateStoreError(t){let n=`store:${t}`;if(this.errorCache.has(n))return this.errorCache.get(n);let r=e.fromError(Error(t),`query`);return this.errorCache.set(n,r),r}computeResult(e){let t=this.cache.getStats().entries.find(t=>t.key===e),n=this.cache.peek(e);if(this.scheduleBackgroundFetch(e,t,n),e.split(`:`)[0]===`read`)return{data:n,loading:t?.isLoading??n===void 0,error:t?.error?this.getOrCreateError(`Query failed`):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0};{let r=this.getCurrentPageForQuery(e),i=this.getOrCreatePaginationMethods(e);return{page:n,loading:t?.isLoading??n===void 0,error:t?.error?this.getOrCreateStoreError(`Query failed`):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0,hasNext:n?r<n.page.pages:!1,hasPrevious:r>1,...i}}}scheduleBackgroundFetch(e,t,n){t?.isStale&&!t?.isLoading&&queueMicrotask(()=>{this.cache.get(e,{waitForFresh:!1}).catch(t=>{console.warn(`ReactiveRemoteStore: Background refetch failed for ${e}:`,t)})}),n===void 0&&!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){let t=this.queryStates.get(e);return t&&t.getParams().page||1}getOrCreatePaginationMethods(e){if(this.stablePaginationMethods.has(e))return this.stablePaginationMethods.get(e);let t=this.queryStates.get(e);if(!t)throw Error(`QueryState not found for ${e}`);let n=t.getOperation(),r={next:async()=>{let r=t.getParams(),i=r.page||1,a=this.buildKey(n,r),o=this.cache.peek(a);if(o&&i<o.page.pages){let a={...r,page:i+1},o=this.buildKey(n,a);if(!this.cache.has(o)){let e=n===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(o,()=>e(a))}t.updateParams(a),await this.cache.get(o,{waitForFresh:!0});let s=this.computeResultForParams(n,a,e);t.updateResult(s)}},previous:async()=>{let r=t.getParams(),i=r.page||1;if(i>1){let a={...r,page:i-1},o=this.buildKey(n,a);if(!this.cache.has(o)){let e=n===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(o,()=>e(a))}t.updateParams(a),await this.cache.get(o,{waitForFresh:!0});let s=this.computeResultForParams(n,a,e);t.updateResult(s)}},navigate:async r=>{if(r<1)return;let i={...t.getParams(),page:r},a=this.buildKey(n,i);if(!this.cache.has(a)){let e=n===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(a,()=>e(i))}t.updateParams(i),await this.cache.get(a,{waitForFresh:!0});let o=this.computeResultForParams(n,i,e);t.updateResult(o)},refresh:async(n=1e3)=>{let r=t.getParams(),i=this.buildKey(t.getOperation(),r);t.reset();let a=this.cache.refresh(i),o=new Promise(e=>setTimeout(e,n));await Promise.all([a,o]);let s=this.computeResultForParams(t.getOperation(),t.getParams(),e);t.updateResult(s,!0)},changeParams:async n=>{let r=n(t.getParams());if(!r){console.error(`Setter function for changeParams must return a new parameters object.`);return}let i=this.buildKey(t.getOperation(),r);if(!this.cache.has(i)){let e=t.getOperation()===`list`?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});let a=this.computeResultForParams(t.getOperation(),r,e);t.updateResult(a)}};return this.stablePaginationMethods.set(e,r),r}computeResultForParams(e,t,n){let r=this.buildKey(e,t),i=this.cache.getStats().entries.find(e=>e.key===r),a=this.cache.peek(r);if(e===`read`)return{data:a,loading:i?.isLoading??a===void 0,error:i?.error?this.getOrCreateError(`Query failed`):void 0,stale:i?.isStale??!0,updated:i?.lastUpdated??0};{let e=t.page||1,r=this.stablePaginationMethods.get(n)??{};return{page:a,loading:i?.isLoading??a===void 0,error:i?.error?this.getOrCreateStoreError(`Query failed`):void 0,stale:i?.isStale??!0,updated:i?.lastUpdated??0,hasNext:a?e<a.page.pages:!1,hasPrevious:e>1,...r}}}hasActiveQuery(e,t){let n=this.buildKey(e,t);return this.stableResults.has(n)}getActiveQuery(e,t){let n=this.buildKey(e,t);return this.stableResults.get(n)}read(e){let 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));let n=new r(`read`,e,this.computeResult(t));this.queryStates.set(t,n);let i={value:()=>n.getResult(),onValueChange:e=>{let t=n.subscribe(e);return()=>{t()}}};return this.stableResults.set(t,i),i}list(e){return this.setupPagedQuery(`list`,e)}find(e){return this.setupPagedQuery(`find`,e)}setupPagedQuery(e,t){let n=this.buildKey(e,t);if(this.stableResults.has(n))return this.stableResults.get(n);if(!this.cache.has(n)){let r=e===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(n,()=>r(t))}let i=this.cache.peek(n),a=new r(e,t,{page:i,loading:i===void 0,error:void 0,stale:!0,updated:0,hasNext:!1,hasPrevious:!1});this.queryStates.set(n,a);let o=this.computeResult(n);a.updateResult(o);let s={value:()=>a.getResult(),onValueChange:e=>{let t=a.subscribe(e);return()=>{t()}}};return this.stableResults.set(n,s),s}async invalidateQueries(e){if(this.correlator){let t=Array.from(this.queryStates.values()).map(e=>({queryKey:this.buildKey(e.getOperation(),e.getParams()),operation:e.getOperation(),params:e.getParams()})),n=this.correlator(e,t).map(e=>this.cache.invalidate(e));await Promise.all(n)}else await this.cache.invalidatePattern(/^list:/),await this.cache.invalidatePattern(/^find:/)}handleStoreEvent(e){if(this.storeEventCorrelator){let 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{let 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{let 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{let 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){let n=this.buildKey(e,t);return this.cache.refresh(n)}prefetch(e,t){let n=this.buildKey(e,t);this.cache.prefetch(n).catch(e=>{console.warn(`ReactiveRemoteStore: Prefetch failed for ${n}:`,e)})}async invalidate(e,t){let n=this.buildKey(e,t);await this.cache.invalidate(n)}async invalidateAll(){let 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.ReactiveRemoteStore=i,exports.StoreError=e,exports.hash=n;
package/index.mjs CHANGED
@@ -1 +1 @@
1
- import"uuid";((e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports))(((e,t)=>{var n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o=(e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})},s=(e,t,o,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of i(t))!a.call(e,c)&&c!==o&&n(e,c,{get:()=>t[c],enumerable:!(s=r(t,c))||s.enumerable});return e},c=e=>s(n({},`__esModule`,{value:!0}),e),l={};o(l,{createEventBus:()=>u}),t.exports=c(l);var u=(e={async:!1,batchSize:1e3,batchDelay:16,errorHandler:e=>console.error(`EventBus Error:`,e),crossTab:!1,channelName:`event-bus-channel`})=>{let t=new Map,n=[],r=0,i=0,a=new Map,o=new Map,s=null;e.crossTab&&typeof BroadcastChannel<`u`?s=new BroadcastChannel(e.channelName):e.crossTab&&console.warn(`BroadcastChannel is not supported in this browser. Cross-tab notifications are disabled.`);let c=(e,t)=>{r++,i+=t,a.set(e,(a.get(e)||0)+1)},l=()=>{let t=n;n=[],t.forEach(({name:t,payload:n})=>{let r=performance.now();try{(o.get(t)||[]).forEach(e=>e(n))}catch(r){e.errorHandler({...r,eventName:t,payload:n})}c(t,performance.now()-r)})},u=(()=>{let t;return()=>{clearTimeout(t),t=setTimeout(l,e.batchDelay)}})(),d=e=>{let n=t.get(e);n?o.set(e,Array.from(n)):o.delete(e)};return s&&(s.onmessage=e=>{let{name:t,payload:n}=e.data;(o.get(t)||[]).forEach(e=>e(n))}),{subscribe:(e,n)=>{t.has(e)||t.set(e,new Set);let r=t.get(e);return r.add(n),d(e),()=>{r.delete(n),r.size===0?(t.delete(e),o.delete(e)):d(e)}},emit:({name:t,payload:r})=>{if(e.async){n.push({name:t,payload:r}),n.length>=e.batchSize?l():u(),s&&s.postMessage({name:t,payload:r});return}let i=performance.now();try{(o.get(t)||[]).forEach(e=>e(r)),s&&s.postMessage({name:t,payload:r})}catch(n){e.errorHandler({...n,eventName:t,payload:r})}c(t,performance.now()-i)},getMetrics:()=>({totalEvents:r,activeSubscriptions:Array.from(t.values()).reduce((e,t)=>e+t.size,0),eventCounts:a,averageEmitDuration:r>0?i/r:0}),clear:()=>{t.clear(),o.clear(),n=[],r=0,i=0,a.clear(),s&&=(s.close(),null)}}};0&&(t.exports={createEventBus:u})}))();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,n){if(t.isAbort||t.name===`AbortError`)return new e({code:`ABORTED`,message:`Request was cancelled`,isRetryable:!1},t);if(t.name===`NetworkError`||!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 t.name===`TimeoutError`||t.code===`TIMEOUT`?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 ${n}`,isRetryable:!0},t)}};function t(e,n=new WeakMap){return e===void 0?`undefined`:typeof e==`function`||typeof e==`symbol`?``:typeof e==`string`?`"${e}"`:typeof e==`number`||typeof e==`boolean`||e===null?String(e):Array.isArray(e)?`[${e.map(e=>t(e,n)).join(`,`)}]`:typeof e==`object`?n.has(e)?`"__circular__"`:(n.set(e,!0),`{${Object.keys(e).sort().map(r=>`"${r}":${t(e[r],n)}`).join(`,`)}}`):``}function n(e){let n=3421674724,r=2216829733,i=t(e);for(let e=0;e<i.length;e++){r^=i.charCodeAt(e);let t=Math.imul(r,435),a=Math.imul(r,435)+Math.imul(n,435);r=t>>>0,n=a>>>0}return(n>>>0).toString(16)+(r>>>0).toString(16)}var r=class{operation;params;currentResult;currentResultHash;subscribers=new Set;defaultResult;idle=!1;constructor(e,t,r){this.operation=e,this.params=t,this.currentResult=r,this.currentResultHash=n(r),this.defaultResult=r}getResult(){return this.currentResult}getParams(){return this.params}getOperation(){return this.operation}updateParams(e){this.params=e}reset(){this.currentResult=this.defaultResult,this.currentResultHash=n(this.defaultResult),this.notifySubscribers(),this.idle=!0}updateResult(e,t=!1){if(this.idle&&!t)return;this.idle=!1;let r=n(e);this.currentResultHash!==r&&(this.currentResult=e,this.currentResultHash=r,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}},i=class{cache;baseStore;correlator;storeEventCorrelator;queryStates=new Map;stableResults=new Map;stablePaginationMethods=new Map;errorCache=new Map;keyCache=new WeakMap;unsubscribeFromBaseStore;cacheEventCleanups=new Set;constructor(e,t,n,r){this.cache=e,this.baseStore=t,this.correlator=n,this.storeEventCorrelator=r,this.storeEventCorrelator&&queueMicrotask(async()=>{this.unsubscribeFromBaseStore=await this.baseStore.subscribe(`*`,this.handleStoreEvent.bind(this))}),this.setupCacheEventListeners()}setupCacheEventListeners(){[`cache:fetch:success`,`cache:fetch:error`,`cache:data:set`,`cache:data:invalidate`,`cache:read:hit`].forEach(e=>{let t=this.cache.on(e,e=>{this.handleCacheEvent(e)});this.cacheEventCleanups.add(t)})}handleCacheEvent(e){let t=this.queryStates.get(e.key);if(t){let n=this.computeResult(e.key);t.updateResult(n)}}buildKey(e,t){if(typeof t==`object`&&t&&this.keyCache.has(t))return this.keyCache.get(t);let r=`${e}:${n(t)}`;return typeof t==`object`&&t&&this.keyCache.set(t,r),r}getOrCreateError(e){if(this.errorCache.has(e))return this.errorCache.get(e);let t=Error(e);return this.errorCache.set(e,t),t}getOrCreateStoreError(t){let n=`store:${t}`;if(this.errorCache.has(n))return this.errorCache.get(n);let r=e.fromError(Error(t),`query`);return this.errorCache.set(n,r),r}computeResult(e){let t=this.cache.getStats().entries.find(t=>t.key===e),n=this.cache.peek(e);if(this.scheduleBackgroundFetch(e,t,n),e.split(`:`)[0]===`read`)return{data:n,loading:t?.isLoading??n===void 0,error:t?.error?this.getOrCreateError(`Query failed`):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0};{let r=this.getCurrentPageForQuery(e),i=this.getOrCreatePaginationMethods(e);return{page:n,loading:t?.isLoading??n===void 0,error:t?.error?this.getOrCreateStoreError(`Query failed`):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0,hasNext:n?r<n.page.pages:!1,hasPrevious:r>1,...i}}}scheduleBackgroundFetch(e,t,n){t?.isStale&&!t?.isLoading&&queueMicrotask(()=>{this.cache.get(e,{waitForFresh:!1}).catch(t=>{console.warn(`ReactiveRemoteStore: Background refetch failed for ${e}:`,t)})}),n===void 0&&!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){let t=this.queryStates.get(e);return t&&t.getParams().page||1}getOrCreatePaginationMethods(e){if(this.stablePaginationMethods.has(e))return this.stablePaginationMethods.get(e);let t=this.queryStates.get(e);if(!t)throw Error(`QueryState not found for ${e}`);let n=t.getOperation(),r={next:async()=>{let r=t.getParams(),i=r.page||1,a=this.buildKey(n,r),o=this.cache.peek(a);if(o&&i<o.page.pages){let a={...r,page:i+1},o=this.buildKey(n,a);if(!this.cache.has(o)){let e=n===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(o,()=>e(a))}t.updateParams(a),await this.cache.get(o,{waitForFresh:!0});let s=this.computeResultForParams(n,a,e);t.updateResult(s)}},previous:async()=>{let r=t.getParams(),i=r.page||1;if(i>1){let a={...r,page:i-1},o=this.buildKey(n,a);if(!this.cache.has(o)){let e=n===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(o,()=>e(a))}t.updateParams(a),await this.cache.get(o,{waitForFresh:!0});let s=this.computeResultForParams(n,a,e);t.updateResult(s)}},navigate:async r=>{if(r<1)return;let i={...t.getParams(),page:r},a=this.buildKey(n,i);if(!this.cache.has(a)){let e=n===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(a,()=>e(i))}t.updateParams(i),await this.cache.get(a,{waitForFresh:!0});let o=this.computeResultForParams(n,i,e);t.updateResult(o)},refresh:async(n=1e3)=>{let r=t.getParams(),i=this.buildKey(t.getOperation(),r);t.reset();let a=this.cache.refresh(i),o=new Promise(e=>setTimeout(e,n));await Promise.all([a,o]);let s=this.computeResultForParams(t.getOperation(),t.getParams(),e);t.updateResult(s,!0)},changeParams:async n=>{let r=n(t.getParams());if(!r){console.error(`Setter function for changeParams must return a new parameters object.`);return}let i=this.buildKey(t.getOperation(),r);if(!this.cache.has(i)){let e=t.getOperation()===`list`?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});let a=this.computeResultForParams(t.getOperation(),r,e);t.updateResult(a)}};return this.stablePaginationMethods.set(e,r),r}computeResultForParams(e,t,n){let r=this.buildKey(e,t),i=this.cache.getStats().entries.find(e=>e.key===r),a=this.cache.peek(r);if(e===`read`)return{data:a,loading:i?.isLoading??a===void 0,error:i?.error?this.getOrCreateError(`Query failed`):void 0,stale:i?.isStale??!0,updated:i?.lastUpdated??0};{let e=t.page||1,r=this.stablePaginationMethods.get(n)??{};return{page:a,loading:i?.isLoading??a===void 0,error:i?.error?this.getOrCreateStoreError(`Query failed`):void 0,stale:i?.isStale??!0,updated:i?.lastUpdated??0,hasNext:a?e<a.page.pages:!1,hasPrevious:e>1,...r}}}hasActiveQuery(e,t){let n=this.buildKey(e,t);return this.stableResults.has(n)}getActiveQuery(e,t){let n=this.buildKey(e,t);return this.stableResults.get(n)}read(e){let 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));let n=new r(`read`,e,this.computeResult(t));this.queryStates.set(t,n);let i={value:()=>n.getResult(),onValueChange:e=>{let t=n.subscribe(e);return()=>{t()}}};return this.stableResults.set(t,i),i}list(e){return this.setupPagedQuery(`list`,e)}find(e){return this.setupPagedQuery(`find`,e)}setupPagedQuery(e,t){let n=this.buildKey(e,t);if(this.stableResults.has(n))return this.stableResults.get(n);if(!this.cache.has(n)){let r=e===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(n,()=>r(t))}let i=this.cache.peek(n),a=new r(e,t,{page:i,loading:i===void 0,error:void 0,stale:!0,updated:0,hasNext:!1,hasPrevious:!1});this.queryStates.set(n,a);let o=this.computeResult(n);a.updateResult(o);let s={value:()=>a.getResult(),onValueChange:e=>{let t=a.subscribe(e);return()=>{t()}}};return this.stableResults.set(n,s),s}async invalidateQueries(e){if(this.correlator){let t=Array.from(this.queryStates.values()).map(e=>({queryKey:this.buildKey(e.getOperation(),e.getParams()),operation:e.getOperation(),params:e.getParams()})),n=this.correlator(e,t).map(e=>this.cache.invalidate(e));await Promise.all(n)}else await this.cache.invalidatePattern(/^list:/),await this.cache.invalidatePattern(/^find:/)}handleStoreEvent(e){if(this.storeEventCorrelator){let 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{let 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{let 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{let 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){let n=this.buildKey(e,t);return this.cache.refresh(n)}prefetch(e,t){let n=this.buildKey(e,t);this.cache.prefetch(n).catch(e=>{console.warn(`ReactiveRemoteStore: Prefetch failed for ${n}:`,e)})}async invalidate(e,t){let n=this.buildKey(e,t);await this.cache.invalidate(n)}async invalidateAll(){let 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{i as ReactiveRemoteStore,e as StoreError,n as hash};
1
+ import"@asaidimu/utils-cache";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,n){if(t.isAbort||t.name===`AbortError`)return new e({code:`ABORTED`,message:`Request was cancelled`,isRetryable:!1},t);if(t.name===`NetworkError`||!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 t.name===`TimeoutError`||t.code===`TIMEOUT`?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 ${n}`,isRetryable:!0},t)}};function t(e,n=new WeakMap){return e===void 0?`undefined`:typeof e==`function`||typeof e==`symbol`?``:typeof e==`string`?`"${e}"`:typeof e==`number`||typeof e==`boolean`||e===null?String(e):Array.isArray(e)?`[${e.map(e=>t(e,n)).join(`,`)}]`:typeof e==`object`?n.has(e)?`"__circular__"`:(n.set(e,!0),`{${Object.keys(e).sort().map(r=>`"${r}":${t(e[r],n)}`).join(`,`)}}`):``}function n(e){let n=3421674724,r=2216829733,i=t(e);for(let e=0;e<i.length;e++){r^=i.charCodeAt(e);let t=Math.imul(r,435),a=Math.imul(r,435)+Math.imul(n,435);r=t>>>0,n=a>>>0}return(n>>>0).toString(16)+(r>>>0).toString(16)}var r=class{operation;params;currentResult;currentResultHash;subscribers=new Set;defaultResult;idle=!1;constructor(e,t,r){this.operation=e,this.params=t,this.currentResult=r,this.currentResultHash=n(r),this.defaultResult=r}getResult(){return this.currentResult}getParams(){return this.params}getOperation(){return this.operation}updateParams(e){this.params=e}reset(){this.currentResult=this.defaultResult,this.currentResultHash=n(this.defaultResult),this.notifySubscribers(),this.idle=!0}updateResult(e,t=!1){if(this.idle&&!t)return;this.idle=!1;let r=n(e);this.currentResultHash!==r&&(this.currentResult=e,this.currentResultHash=r,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}},i=class{cache;baseStore;correlator;storeEventCorrelator;queryStates=new Map;stableResults=new Map;stablePaginationMethods=new Map;errorCache=new Map;keyCache=new WeakMap;unsubscribeFromBaseStore;cacheEventCleanups=new Set;constructor(e,t,n,r){this.cache=e,this.baseStore=t,this.correlator=n,this.storeEventCorrelator=r,this.storeEventCorrelator&&queueMicrotask(async()=>{this.unsubscribeFromBaseStore=await this.baseStore.subscribe(`*`,this.handleStoreEvent.bind(this))}),this.setupCacheEventListeners()}setupCacheEventListeners(){[`cache:fetch:success`,`cache:fetch:error`,`cache:data:set`,`cache:data:invalidate`,`cache:read:hit`].forEach(e=>{let t=this.cache.on(e,e=>{this.handleCacheEvent(e)});this.cacheEventCleanups.add(t)})}handleCacheEvent(e){let t=this.queryStates.get(e.key);if(t){let n=this.computeResult(e.key);t.updateResult(n)}}buildKey(e,t){if(typeof t==`object`&&t&&this.keyCache.has(t))return this.keyCache.get(t);let r=`${e}:${n(t)}`;return typeof t==`object`&&t&&this.keyCache.set(t,r),r}getOrCreateError(e){if(this.errorCache.has(e))return this.errorCache.get(e);let t=Error(e);return this.errorCache.set(e,t),t}getOrCreateStoreError(t){let n=`store:${t}`;if(this.errorCache.has(n))return this.errorCache.get(n);let r=e.fromError(Error(t),`query`);return this.errorCache.set(n,r),r}computeResult(e){let t=this.cache.getStats().entries.find(t=>t.key===e),n=this.cache.peek(e);if(this.scheduleBackgroundFetch(e,t,n),e.split(`:`)[0]===`read`)return{data:n,loading:t?.isLoading??n===void 0,error:t?.error?this.getOrCreateError(`Query failed`):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0};{let r=this.getCurrentPageForQuery(e),i=this.getOrCreatePaginationMethods(e);return{page:n,loading:t?.isLoading??n===void 0,error:t?.error?this.getOrCreateStoreError(`Query failed`):void 0,stale:t?.isStale??!0,updated:t?.lastUpdated??0,hasNext:n?r<n.page.pages:!1,hasPrevious:r>1,...i}}}scheduleBackgroundFetch(e,t,n){t?.isStale&&!t?.isLoading&&queueMicrotask(()=>{this.cache.get(e,{waitForFresh:!1}).catch(t=>{console.warn(`ReactiveRemoteStore: Background refetch failed for ${e}:`,t)})}),n===void 0&&!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){let t=this.queryStates.get(e);return t&&t.getParams().page||1}getOrCreatePaginationMethods(e){if(this.stablePaginationMethods.has(e))return this.stablePaginationMethods.get(e);let t=this.queryStates.get(e);if(!t)throw Error(`QueryState not found for ${e}`);let n=t.getOperation(),r={next:async()=>{let r=t.getParams(),i=r.page||1,a=this.buildKey(n,r),o=this.cache.peek(a);if(o&&i<o.page.pages){let a={...r,page:i+1},o=this.buildKey(n,a);if(!this.cache.has(o)){let e=n===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(o,()=>e(a))}t.updateParams(a),await this.cache.get(o,{waitForFresh:!0});let s=this.computeResultForParams(n,a,e);t.updateResult(s)}},previous:async()=>{let r=t.getParams(),i=r.page||1;if(i>1){let a={...r,page:i-1},o=this.buildKey(n,a);if(!this.cache.has(o)){let e=n===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(o,()=>e(a))}t.updateParams(a),await this.cache.get(o,{waitForFresh:!0});let s=this.computeResultForParams(n,a,e);t.updateResult(s)}},navigate:async r=>{if(r<1)return;let i={...t.getParams(),page:r},a=this.buildKey(n,i);if(!this.cache.has(a)){let e=n===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(a,()=>e(i))}t.updateParams(i),await this.cache.get(a,{waitForFresh:!0});let o=this.computeResultForParams(n,i,e);t.updateResult(o)},refresh:async(n=1e3)=>{let r=t.getParams(),i=this.buildKey(t.getOperation(),r);t.reset();let a=this.cache.refresh(i),o=new Promise(e=>setTimeout(e,n));await Promise.all([a,o]);let s=this.computeResultForParams(t.getOperation(),t.getParams(),e);t.updateResult(s,!0)},changeParams:async n=>{let r=n(t.getParams());if(!r){console.error(`Setter function for changeParams must return a new parameters object.`);return}let i=this.buildKey(t.getOperation(),r);if(!this.cache.has(i)){let e=t.getOperation()===`list`?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});let a=this.computeResultForParams(t.getOperation(),r,e);t.updateResult(a)}};return this.stablePaginationMethods.set(e,r),r}computeResultForParams(e,t,n){let r=this.buildKey(e,t),i=this.cache.getStats().entries.find(e=>e.key===r),a=this.cache.peek(r);if(e===`read`)return{data:a,loading:i?.isLoading??a===void 0,error:i?.error?this.getOrCreateError(`Query failed`):void 0,stale:i?.isStale??!0,updated:i?.lastUpdated??0};{let e=t.page||1,r=this.stablePaginationMethods.get(n)??{};return{page:a,loading:i?.isLoading??a===void 0,error:i?.error?this.getOrCreateStoreError(`Query failed`):void 0,stale:i?.isStale??!0,updated:i?.lastUpdated??0,hasNext:a?e<a.page.pages:!1,hasPrevious:e>1,...r}}}hasActiveQuery(e,t){let n=this.buildKey(e,t);return this.stableResults.has(n)}getActiveQuery(e,t){let n=this.buildKey(e,t);return this.stableResults.get(n)}read(e){let 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));let n=new r(`read`,e,this.computeResult(t));this.queryStates.set(t,n);let i={value:()=>n.getResult(),onValueChange:e=>{let t=n.subscribe(e);return()=>{t()}}};return this.stableResults.set(t,i),i}list(e){return this.setupPagedQuery(`list`,e)}find(e){return this.setupPagedQuery(`find`,e)}setupPagedQuery(e,t){let n=this.buildKey(e,t);if(this.stableResults.has(n))return this.stableResults.get(n);if(!this.cache.has(n)){let r=e===`list`?this.baseStore.list.bind(this.baseStore):this.baseStore.find.bind(this.baseStore);this.cache.registerQuery(n,()=>r(t))}let i=this.cache.peek(n),a=new r(e,t,{page:i,loading:i===void 0,error:void 0,stale:!0,updated:0,hasNext:!1,hasPrevious:!1});this.queryStates.set(n,a);let o=this.computeResult(n);a.updateResult(o);let s={value:()=>a.getResult(),onValueChange:e=>{let t=a.subscribe(e);return()=>{t()}}};return this.stableResults.set(n,s),s}async invalidateQueries(e){if(this.correlator){let t=Array.from(this.queryStates.values()).map(e=>({queryKey:this.buildKey(e.getOperation(),e.getParams()),operation:e.getOperation(),params:e.getParams()})),n=this.correlator(e,t).map(e=>this.cache.invalidate(e));await Promise.all(n)}else await this.cache.invalidatePattern(/^list:/),await this.cache.invalidatePattern(/^find:/)}handleStoreEvent(e){if(this.storeEventCorrelator){let 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{let 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{let 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{let 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){let n=this.buildKey(e,t);return this.cache.refresh(n)}prefetch(e,t){let n=this.buildKey(e,t);this.cache.prefetch(n).catch(e=>{console.warn(`ReactiveRemoteStore: Prefetch failed for ${n}:`,e)})}async invalidate(e,t){let n=this.buildKey(e,t);await this.cache.invalidate(n)}async invalidateAll(){let 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{i as ReactiveRemoteStore,e as StoreError,n as hash};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asaidimu/utils-remote-store",
3
- "version": "1.3.12",
3
+ "version": "1.3.13",
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": "^3.1.11",
12
+ "@asaidimu/utils-cache": "^3.1.12",
13
13
  "eventsource": "^4.0.0"
14
14
  },
15
15
  "exports": {