@asaidimu/utils-remote-store 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.mts +195 -2
- package/index.d.ts +195 -2
- package/index.js +1 -1
- package/index.mjs +1 -1
- package/package.json +2 -2
package/index.d.mts
CHANGED
|
@@ -1,5 +1,198 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
interface SimplePersistence<T> {
|
|
2
|
+
/**
|
|
3
|
+
* Persists data to storage.
|
|
4
|
+
*
|
|
5
|
+
* @param id The **unique identifier of the *consumer instance*** making the change. This is NOT the ID of the data (`T`) itself.
|
|
6
|
+
* Think of it as the ID of the specific browser tab, component, or module that's currently interacting with the persistence layer.
|
|
7
|
+
* It should typically be a **UUID** generated once at the consumer instance's instantiation.
|
|
8
|
+
* This `id` is crucial for the `subscribe` method, helping to differentiate updates originating from the current instance versus other instances/tabs, thereby preventing self-triggered notification loops.
|
|
9
|
+
* @param state The state (of type T) to persist. This state is generally considered the **global or shared state** that all instances interact with.
|
|
10
|
+
* @returns `true` if the operation was successful, `false` if an error occurred. For asynchronous implementations (like `IndexedDBPersistence`), this returns a `Promise<boolean>`.
|
|
11
|
+
*/
|
|
12
|
+
set(id: string, state: T): boolean | Promise<boolean>;
|
|
13
|
+
/**
|
|
14
|
+
* Retrieves the global persisted data from storage.
|
|
15
|
+
*
|
|
16
|
+
* @returns The retrieved state of type `T`, or `null` if no data is found or if an error occurs during retrieval/parsing.
|
|
17
|
+
* For asynchronous implementations, this returns a `Promise<T | null>`.
|
|
18
|
+
*/
|
|
19
|
+
get(): (T | null) | (Promise<T | null>);
|
|
20
|
+
/**
|
|
21
|
+
* Subscribes to changes in the global persisted data that originate from *other* instances of your application (e.g., other tabs or independent components using the same persistence layer).
|
|
22
|
+
*
|
|
23
|
+
* @param id The **unique identifier of the *consumer instance* subscribing**. This allows the persistence implementation to filter out notifications that were initiated by the subscribing instance itself.
|
|
24
|
+
* @param callback The function to call when the global persisted data changes from *another* source. The new state (`T`) is passed as an argument to this callback.
|
|
25
|
+
* @returns A function that, when called, will unsubscribe the provided callback from future updates. Call this when your component or instance is no longer active to prevent memory leaks.
|
|
26
|
+
*/
|
|
27
|
+
subscribe(id: string, callback: (state: T) => void): () => void;
|
|
28
|
+
/**
|
|
29
|
+
* Clears (removes) the entire global persisted data from storage.
|
|
30
|
+
*
|
|
31
|
+
* @returns `true` if the operation was successful, `false` if an error occurred. For asynchronous implementations, this returns a `Promise<boolean>`.
|
|
32
|
+
*/
|
|
33
|
+
clear(): boolean | Promise<boolean>;
|
|
34
|
+
/**
|
|
35
|
+
* Returns metadata about the persistence layer.
|
|
36
|
+
*
|
|
37
|
+
* This is useful for distinguishing between multiple apps running on the same host
|
|
38
|
+
* (e.g., several apps served at `localhost:3000` that share the same storage key).
|
|
39
|
+
*
|
|
40
|
+
* @returns An object containing:
|
|
41
|
+
* - `version`: The semantic version string of the persistence schema or application.
|
|
42
|
+
* - `id`: A unique identifier for the application using this persistence instance.
|
|
43
|
+
*/
|
|
44
|
+
stats(): {
|
|
45
|
+
version: string;
|
|
46
|
+
id: string;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface CacheOptions {
|
|
51
|
+
staleTime?: number;
|
|
52
|
+
cacheTime?: number;
|
|
53
|
+
retryAttempts?: number;
|
|
54
|
+
retryDelay?: number;
|
|
55
|
+
maxSize?: number;
|
|
56
|
+
enableMetrics?: boolean;
|
|
57
|
+
persistence?: SimplePersistence<SerializableCacheState>;
|
|
58
|
+
persistenceId?: string;
|
|
59
|
+
serializeValue?: (value: any) => any;
|
|
60
|
+
deserializeValue?: (value: any) => any;
|
|
61
|
+
persistenceDebounceTime?: number;
|
|
62
|
+
}
|
|
63
|
+
interface CacheMetrics {
|
|
64
|
+
hits: number;
|
|
65
|
+
misses: number;
|
|
66
|
+
fetches: number;
|
|
67
|
+
errors: number;
|
|
68
|
+
evictions: number;
|
|
69
|
+
staleHits: number;
|
|
70
|
+
}
|
|
71
|
+
interface SerializableCacheEntry {
|
|
72
|
+
data: any;
|
|
73
|
+
lastUpdated: number;
|
|
74
|
+
lastAccessed: number;
|
|
75
|
+
accessCount: number;
|
|
76
|
+
error?: {
|
|
77
|
+
name: string;
|
|
78
|
+
message: string;
|
|
79
|
+
stack?: string;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
type SerializableCacheState = Array<[string, SerializableCacheEntry]>;
|
|
83
|
+
type CacheEventBase<Type extends string, Payload = {}> = {
|
|
84
|
+
type: Type;
|
|
85
|
+
key: string;
|
|
86
|
+
timestamp: number;
|
|
87
|
+
} & Payload;
|
|
88
|
+
type CacheReadHitEvent<T = any> = CacheEventBase<'cache:read:hit', {
|
|
89
|
+
data: T;
|
|
90
|
+
isStale: boolean;
|
|
91
|
+
}>;
|
|
92
|
+
type CacheReadMissEvent = CacheEventBase<'cache:read:miss'>;
|
|
93
|
+
type CacheFetchStartEvent = CacheEventBase<'cache:fetch:start', {
|
|
94
|
+
attempt: number;
|
|
95
|
+
}>;
|
|
96
|
+
type CacheFetchSuccessEvent<T = any> = CacheEventBase<'cache:fetch:success', {
|
|
97
|
+
data: T;
|
|
98
|
+
}>;
|
|
99
|
+
type CacheFetchErrorEvent = CacheEventBase<'cache:fetch:error', {
|
|
100
|
+
error: Error;
|
|
101
|
+
attempt: number;
|
|
102
|
+
}>;
|
|
103
|
+
type CacheDataEvictEvent = CacheEventBase<'cache:data:evict', {
|
|
104
|
+
reason?: string;
|
|
105
|
+
}>;
|
|
106
|
+
type CacheDataInvalidateEvent = CacheEventBase<'cache:data:invalidate'>;
|
|
107
|
+
type CacheDataSetEvent<T = any> = CacheEventBase<'cache:data:set', {
|
|
108
|
+
newData: T;
|
|
109
|
+
oldData?: T;
|
|
110
|
+
}>;
|
|
111
|
+
type CachePersistenceLoadSuccessEvent = CacheEventBase<'cache:persistence:load:success', {
|
|
112
|
+
message?: string;
|
|
113
|
+
}>;
|
|
114
|
+
type CachePersistenceLoadErrorEvent = CacheEventBase<'cache:persistence:load:error', {
|
|
115
|
+
message?: string;
|
|
116
|
+
error?: any;
|
|
117
|
+
}>;
|
|
118
|
+
type CachePersistenceSaveSuccessEvent = CacheEventBase<'cache:persistence:save:success'>;
|
|
119
|
+
type CachePersistenceSaveErrorEvent = CacheEventBase<'cache:persistence:save:error', {
|
|
120
|
+
message?: string;
|
|
121
|
+
error?: any;
|
|
122
|
+
}>;
|
|
123
|
+
type CachePersistenceClearSuccessEvent = CacheEventBase<'cache:persistence:clear:success'>;
|
|
124
|
+
type CachePersistenceClearErrorEvent = CacheEventBase<'cache:persistence:clear:error', {
|
|
125
|
+
message?: string;
|
|
126
|
+
error?: any;
|
|
127
|
+
}>;
|
|
128
|
+
type CachePersistenceSyncEvent = CacheEventBase<'cache:persistence:sync', {
|
|
129
|
+
message?: string;
|
|
130
|
+
}>;
|
|
131
|
+
type CacheEvent = CacheReadHitEvent | CacheReadMissEvent | CacheFetchStartEvent | CacheFetchSuccessEvent | CacheFetchErrorEvent | CacheDataEvictEvent | CacheDataInvalidateEvent | CacheDataSetEvent | CachePersistenceLoadSuccessEvent | CachePersistenceLoadErrorEvent | CachePersistenceSaveSuccessEvent | CachePersistenceSaveErrorEvent | CachePersistenceClearSuccessEvent | CachePersistenceClearErrorEvent | CachePersistenceSyncEvent;
|
|
132
|
+
type CacheEventType = CacheEvent['type'];
|
|
133
|
+
|
|
134
|
+
declare class QueryCache {
|
|
135
|
+
private cache;
|
|
136
|
+
private queries;
|
|
137
|
+
private fetching;
|
|
138
|
+
private readonly defaultOptions;
|
|
139
|
+
private metrics;
|
|
140
|
+
private eventBus;
|
|
141
|
+
private gcTimer?;
|
|
142
|
+
private readonly persistenceId;
|
|
143
|
+
private persistenceUnsubscribe?;
|
|
144
|
+
private persistenceDebounceTimer?;
|
|
145
|
+
private isHandlingRemoteUpdate;
|
|
146
|
+
constructor(defaultOptions?: CacheOptions);
|
|
147
|
+
private initializePersistence;
|
|
148
|
+
private serializeCache;
|
|
149
|
+
private deserializeAndLoadCache;
|
|
150
|
+
private schedulePersistState;
|
|
151
|
+
private handleRemoteStateChange;
|
|
152
|
+
registerQuery<T>(key: string, fetchFunction: () => Promise<T>, options?: CacheOptions): void;
|
|
153
|
+
get<T>(key: string, options?: {
|
|
154
|
+
waitForFresh?: boolean;
|
|
155
|
+
throwOnError?: boolean;
|
|
156
|
+
}): Promise<T | undefined>;
|
|
157
|
+
peek<T>(key: string): T | undefined;
|
|
158
|
+
has(key: string): boolean;
|
|
159
|
+
private fetch;
|
|
160
|
+
private fetchAndWait;
|
|
161
|
+
private performFetchWithRetry;
|
|
162
|
+
private isStale;
|
|
163
|
+
invalidate(key: string, refetch?: boolean): Promise<void>;
|
|
164
|
+
invalidatePattern(pattern: RegExp, refetch?: boolean): Promise<void>;
|
|
165
|
+
prefetch(key: string): Promise<void>;
|
|
166
|
+
refresh<T>(key: string): Promise<T | undefined>;
|
|
167
|
+
setData<T>(key: string, data: T): void;
|
|
168
|
+
remove(key: string): boolean;
|
|
169
|
+
private enforceSizeLimit;
|
|
170
|
+
private startGarbageCollection;
|
|
171
|
+
garbageCollect(): number;
|
|
172
|
+
getStats(): {
|
|
173
|
+
size: number;
|
|
174
|
+
metrics: CacheMetrics;
|
|
175
|
+
hitRate: number;
|
|
176
|
+
staleHitRate: number;
|
|
177
|
+
entries: Array<{
|
|
178
|
+
key: string;
|
|
179
|
+
lastAccessed: number;
|
|
180
|
+
lastUpdated: number;
|
|
181
|
+
accessCount: number;
|
|
182
|
+
isStale: boolean;
|
|
183
|
+
isLoading?: boolean;
|
|
184
|
+
error?: boolean;
|
|
185
|
+
}>;
|
|
186
|
+
};
|
|
187
|
+
on<EType extends CacheEventType>(event: EType, listener: (ev: Extract<CacheEvent, {
|
|
188
|
+
type: EType;
|
|
189
|
+
}>) => void): () => void;
|
|
190
|
+
private emitEvent;
|
|
191
|
+
private updateMetrics;
|
|
192
|
+
private delay;
|
|
193
|
+
clear(): Promise<void>;
|
|
194
|
+
destroy(): void;
|
|
195
|
+
}
|
|
3
196
|
|
|
4
197
|
interface StoreErrorDetails {
|
|
5
198
|
code: string;
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,198 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
interface SimplePersistence<T> {
|
|
2
|
+
/**
|
|
3
|
+
* Persists data to storage.
|
|
4
|
+
*
|
|
5
|
+
* @param id The **unique identifier of the *consumer instance*** making the change. This is NOT the ID of the data (`T`) itself.
|
|
6
|
+
* Think of it as the ID of the specific browser tab, component, or module that's currently interacting with the persistence layer.
|
|
7
|
+
* It should typically be a **UUID** generated once at the consumer instance's instantiation.
|
|
8
|
+
* This `id` is crucial for the `subscribe` method, helping to differentiate updates originating from the current instance versus other instances/tabs, thereby preventing self-triggered notification loops.
|
|
9
|
+
* @param state The state (of type T) to persist. This state is generally considered the **global or shared state** that all instances interact with.
|
|
10
|
+
* @returns `true` if the operation was successful, `false` if an error occurred. For asynchronous implementations (like `IndexedDBPersistence`), this returns a `Promise<boolean>`.
|
|
11
|
+
*/
|
|
12
|
+
set(id: string, state: T): boolean | Promise<boolean>;
|
|
13
|
+
/**
|
|
14
|
+
* Retrieves the global persisted data from storage.
|
|
15
|
+
*
|
|
16
|
+
* @returns The retrieved state of type `T`, or `null` if no data is found or if an error occurs during retrieval/parsing.
|
|
17
|
+
* For asynchronous implementations, this returns a `Promise<T | null>`.
|
|
18
|
+
*/
|
|
19
|
+
get(): (T | null) | (Promise<T | null>);
|
|
20
|
+
/**
|
|
21
|
+
* Subscribes to changes in the global persisted data that originate from *other* instances of your application (e.g., other tabs or independent components using the same persistence layer).
|
|
22
|
+
*
|
|
23
|
+
* @param id The **unique identifier of the *consumer instance* subscribing**. This allows the persistence implementation to filter out notifications that were initiated by the subscribing instance itself.
|
|
24
|
+
* @param callback The function to call when the global persisted data changes from *another* source. The new state (`T`) is passed as an argument to this callback.
|
|
25
|
+
* @returns A function that, when called, will unsubscribe the provided callback from future updates. Call this when your component or instance is no longer active to prevent memory leaks.
|
|
26
|
+
*/
|
|
27
|
+
subscribe(id: string, callback: (state: T) => void): () => void;
|
|
28
|
+
/**
|
|
29
|
+
* Clears (removes) the entire global persisted data from storage.
|
|
30
|
+
*
|
|
31
|
+
* @returns `true` if the operation was successful, `false` if an error occurred. For asynchronous implementations, this returns a `Promise<boolean>`.
|
|
32
|
+
*/
|
|
33
|
+
clear(): boolean | Promise<boolean>;
|
|
34
|
+
/**
|
|
35
|
+
* Returns metadata about the persistence layer.
|
|
36
|
+
*
|
|
37
|
+
* This is useful for distinguishing between multiple apps running on the same host
|
|
38
|
+
* (e.g., several apps served at `localhost:3000` that share the same storage key).
|
|
39
|
+
*
|
|
40
|
+
* @returns An object containing:
|
|
41
|
+
* - `version`: The semantic version string of the persistence schema or application.
|
|
42
|
+
* - `id`: A unique identifier for the application using this persistence instance.
|
|
43
|
+
*/
|
|
44
|
+
stats(): {
|
|
45
|
+
version: string;
|
|
46
|
+
id: string;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface CacheOptions {
|
|
51
|
+
staleTime?: number;
|
|
52
|
+
cacheTime?: number;
|
|
53
|
+
retryAttempts?: number;
|
|
54
|
+
retryDelay?: number;
|
|
55
|
+
maxSize?: number;
|
|
56
|
+
enableMetrics?: boolean;
|
|
57
|
+
persistence?: SimplePersistence<SerializableCacheState>;
|
|
58
|
+
persistenceId?: string;
|
|
59
|
+
serializeValue?: (value: any) => any;
|
|
60
|
+
deserializeValue?: (value: any) => any;
|
|
61
|
+
persistenceDebounceTime?: number;
|
|
62
|
+
}
|
|
63
|
+
interface CacheMetrics {
|
|
64
|
+
hits: number;
|
|
65
|
+
misses: number;
|
|
66
|
+
fetches: number;
|
|
67
|
+
errors: number;
|
|
68
|
+
evictions: number;
|
|
69
|
+
staleHits: number;
|
|
70
|
+
}
|
|
71
|
+
interface SerializableCacheEntry {
|
|
72
|
+
data: any;
|
|
73
|
+
lastUpdated: number;
|
|
74
|
+
lastAccessed: number;
|
|
75
|
+
accessCount: number;
|
|
76
|
+
error?: {
|
|
77
|
+
name: string;
|
|
78
|
+
message: string;
|
|
79
|
+
stack?: string;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
type SerializableCacheState = Array<[string, SerializableCacheEntry]>;
|
|
83
|
+
type CacheEventBase<Type extends string, Payload = {}> = {
|
|
84
|
+
type: Type;
|
|
85
|
+
key: string;
|
|
86
|
+
timestamp: number;
|
|
87
|
+
} & Payload;
|
|
88
|
+
type CacheReadHitEvent<T = any> = CacheEventBase<'cache:read:hit', {
|
|
89
|
+
data: T;
|
|
90
|
+
isStale: boolean;
|
|
91
|
+
}>;
|
|
92
|
+
type CacheReadMissEvent = CacheEventBase<'cache:read:miss'>;
|
|
93
|
+
type CacheFetchStartEvent = CacheEventBase<'cache:fetch:start', {
|
|
94
|
+
attempt: number;
|
|
95
|
+
}>;
|
|
96
|
+
type CacheFetchSuccessEvent<T = any> = CacheEventBase<'cache:fetch:success', {
|
|
97
|
+
data: T;
|
|
98
|
+
}>;
|
|
99
|
+
type CacheFetchErrorEvent = CacheEventBase<'cache:fetch:error', {
|
|
100
|
+
error: Error;
|
|
101
|
+
attempt: number;
|
|
102
|
+
}>;
|
|
103
|
+
type CacheDataEvictEvent = CacheEventBase<'cache:data:evict', {
|
|
104
|
+
reason?: string;
|
|
105
|
+
}>;
|
|
106
|
+
type CacheDataInvalidateEvent = CacheEventBase<'cache:data:invalidate'>;
|
|
107
|
+
type CacheDataSetEvent<T = any> = CacheEventBase<'cache:data:set', {
|
|
108
|
+
newData: T;
|
|
109
|
+
oldData?: T;
|
|
110
|
+
}>;
|
|
111
|
+
type CachePersistenceLoadSuccessEvent = CacheEventBase<'cache:persistence:load:success', {
|
|
112
|
+
message?: string;
|
|
113
|
+
}>;
|
|
114
|
+
type CachePersistenceLoadErrorEvent = CacheEventBase<'cache:persistence:load:error', {
|
|
115
|
+
message?: string;
|
|
116
|
+
error?: any;
|
|
117
|
+
}>;
|
|
118
|
+
type CachePersistenceSaveSuccessEvent = CacheEventBase<'cache:persistence:save:success'>;
|
|
119
|
+
type CachePersistenceSaveErrorEvent = CacheEventBase<'cache:persistence:save:error', {
|
|
120
|
+
message?: string;
|
|
121
|
+
error?: any;
|
|
122
|
+
}>;
|
|
123
|
+
type CachePersistenceClearSuccessEvent = CacheEventBase<'cache:persistence:clear:success'>;
|
|
124
|
+
type CachePersistenceClearErrorEvent = CacheEventBase<'cache:persistence:clear:error', {
|
|
125
|
+
message?: string;
|
|
126
|
+
error?: any;
|
|
127
|
+
}>;
|
|
128
|
+
type CachePersistenceSyncEvent = CacheEventBase<'cache:persistence:sync', {
|
|
129
|
+
message?: string;
|
|
130
|
+
}>;
|
|
131
|
+
type CacheEvent = CacheReadHitEvent | CacheReadMissEvent | CacheFetchStartEvent | CacheFetchSuccessEvent | CacheFetchErrorEvent | CacheDataEvictEvent | CacheDataInvalidateEvent | CacheDataSetEvent | CachePersistenceLoadSuccessEvent | CachePersistenceLoadErrorEvent | CachePersistenceSaveSuccessEvent | CachePersistenceSaveErrorEvent | CachePersistenceClearSuccessEvent | CachePersistenceClearErrorEvent | CachePersistenceSyncEvent;
|
|
132
|
+
type CacheEventType = CacheEvent['type'];
|
|
133
|
+
|
|
134
|
+
declare class QueryCache {
|
|
135
|
+
private cache;
|
|
136
|
+
private queries;
|
|
137
|
+
private fetching;
|
|
138
|
+
private readonly defaultOptions;
|
|
139
|
+
private metrics;
|
|
140
|
+
private eventBus;
|
|
141
|
+
private gcTimer?;
|
|
142
|
+
private readonly persistenceId;
|
|
143
|
+
private persistenceUnsubscribe?;
|
|
144
|
+
private persistenceDebounceTimer?;
|
|
145
|
+
private isHandlingRemoteUpdate;
|
|
146
|
+
constructor(defaultOptions?: CacheOptions);
|
|
147
|
+
private initializePersistence;
|
|
148
|
+
private serializeCache;
|
|
149
|
+
private deserializeAndLoadCache;
|
|
150
|
+
private schedulePersistState;
|
|
151
|
+
private handleRemoteStateChange;
|
|
152
|
+
registerQuery<T>(key: string, fetchFunction: () => Promise<T>, options?: CacheOptions): void;
|
|
153
|
+
get<T>(key: string, options?: {
|
|
154
|
+
waitForFresh?: boolean;
|
|
155
|
+
throwOnError?: boolean;
|
|
156
|
+
}): Promise<T | undefined>;
|
|
157
|
+
peek<T>(key: string): T | undefined;
|
|
158
|
+
has(key: string): boolean;
|
|
159
|
+
private fetch;
|
|
160
|
+
private fetchAndWait;
|
|
161
|
+
private performFetchWithRetry;
|
|
162
|
+
private isStale;
|
|
163
|
+
invalidate(key: string, refetch?: boolean): Promise<void>;
|
|
164
|
+
invalidatePattern(pattern: RegExp, refetch?: boolean): Promise<void>;
|
|
165
|
+
prefetch(key: string): Promise<void>;
|
|
166
|
+
refresh<T>(key: string): Promise<T | undefined>;
|
|
167
|
+
setData<T>(key: string, data: T): void;
|
|
168
|
+
remove(key: string): boolean;
|
|
169
|
+
private enforceSizeLimit;
|
|
170
|
+
private startGarbageCollection;
|
|
171
|
+
garbageCollect(): number;
|
|
172
|
+
getStats(): {
|
|
173
|
+
size: number;
|
|
174
|
+
metrics: CacheMetrics;
|
|
175
|
+
hitRate: number;
|
|
176
|
+
staleHitRate: number;
|
|
177
|
+
entries: Array<{
|
|
178
|
+
key: string;
|
|
179
|
+
lastAccessed: number;
|
|
180
|
+
lastUpdated: number;
|
|
181
|
+
accessCount: number;
|
|
182
|
+
isStale: boolean;
|
|
183
|
+
isLoading?: boolean;
|
|
184
|
+
error?: boolean;
|
|
185
|
+
}>;
|
|
186
|
+
};
|
|
187
|
+
on<EType extends CacheEventType>(event: EType, listener: (ev: Extract<CacheEvent, {
|
|
188
|
+
type: EType;
|
|
189
|
+
}>) => void): () => void;
|
|
190
|
+
private emitEvent;
|
|
191
|
+
private updateMetrics;
|
|
192
|
+
private delay;
|
|
193
|
+
clear(): Promise<void>;
|
|
194
|
+
destroy(): void;
|
|
195
|
+
}
|
|
3
196
|
|
|
4
197
|
interface StoreErrorDetails {
|
|
5
198
|
code: string;
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
"use strict";var e=require("./store"),t=require("./hash"),r=require("./error"),o=require("./types");Object.keys(e).forEach((function(t){"default"===t||Object.prototype.hasOwnProperty.call(exports,t)||Object.defineProperty(exports,t,{enumerable:!0,get:function(){return e[t]}})})),Object.keys(t).forEach((function(e){"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:function(){return t[e]}})})),Object.keys(r).forEach((function(e){"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:function(){return r[e]}})})),Object.keys(o).forEach((function(e){"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:function(){return o[e]}})}));
|
package/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
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};
|
|
1
|
+
export*from"./store";export*from"./hash";export*from"./error";export*from"./types";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asaidimu/utils-remote-store",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
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.1.
|
|
12
|
+
"@asaidimu/utils-cache": "2.1.1",
|
|
13
13
|
"eventsource": "^4.0.0"
|
|
14
14
|
},
|
|
15
15
|
"exports": {
|