@asaidimu/utils-store 2.1.3 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.mts +220 -36
- package/index.d.ts +220 -36
- package/index.js +1 -1
- package/index.mjs +1 -1
- package/package.json +2 -2
package/index.d.mts
CHANGED
|
@@ -1,21 +1,11 @@
|
|
|
1
|
-
import { S as SimplePersistence } from '../types-
|
|
1
|
+
import { S as SimplePersistence } from '../types-DUZGkNEB.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Utility type for representing partial updates to the state.
|
|
5
5
|
*/
|
|
6
|
-
type DeepPartial<T> = {
|
|
7
|
-
[
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Interface for performance metrics of the state.
|
|
11
|
-
*/
|
|
12
|
-
interface StoreMetrics {
|
|
13
|
-
updateCount: number;
|
|
14
|
-
listenerExecutions: number;
|
|
15
|
-
averageUpdateTime: number;
|
|
16
|
-
largestUpdateSize: number;
|
|
17
|
-
mostActiveListenerPaths: string[];
|
|
18
|
-
}
|
|
6
|
+
type DeepPartial<T> = T extends object ? T extends readonly (infer U)[] ? readonly (DeepPartial<U> | undefined)[] | undefined | T : T extends (infer U)[] ? (DeepPartial<U> | undefined)[] | undefined | T : {
|
|
7
|
+
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> | undefined : T[K] | undefined;
|
|
8
|
+
} | undefined | T : T | undefined;
|
|
19
9
|
/**
|
|
20
10
|
* Extended store state for monitoring execution status
|
|
21
11
|
*/
|
|
@@ -34,15 +24,11 @@ interface StoreExecutionState<T> {
|
|
|
34
24
|
/**
|
|
35
25
|
* Event types emitted by the state for observability
|
|
36
26
|
*/
|
|
37
|
-
type StoreEvent = "update:start" | "update:complete" | "middleware:start" | "middleware:complete" | "middleware:error" | "middleware:blocked" | "middleware:executed" | "transaction:start" | "transaction:complete" | "transaction:error" | "persistence:ready";
|
|
27
|
+
type StoreEvent = "update:start" | "update:complete" | "middleware:start" | "middleware:complete" | "middleware:error" | "middleware:blocked" | "middleware:executed" | "transaction:start" | "transaction:complete" | "transaction:error" | "persistence:ready" | "persistence:queued" | "persistence:success" | "persistence:retry" | "persistence:failed" | "persistence:queue_cleared" | "persistence:init_error";
|
|
38
28
|
/**
|
|
39
29
|
* Type for middleware functions. Return a partial state to transform the update.
|
|
40
30
|
*/
|
|
41
31
|
type Middleware<T> = (state: T, update: DeepPartial<T>) => Promise<DeepPartial<T>> | Promise<void> | DeepPartial<T> | void;
|
|
42
|
-
/**
|
|
43
|
-
* Type for blocking middleware functions that can prevent updates.
|
|
44
|
-
*/
|
|
45
|
-
type BlockingMiddleware<T> = (state: T, update: DeepPartial<T>) => boolean | Promise<boolean>;
|
|
46
32
|
/**
|
|
47
33
|
* Middleware execution information for event emissions
|
|
48
34
|
*/
|
|
@@ -59,10 +45,49 @@ interface MiddlewareExecution {
|
|
|
59
45
|
/**
|
|
60
46
|
* Represents a state update, which can be either a partial state object or a function.
|
|
61
47
|
*/
|
|
62
|
-
type StateUpdater<T> = DeepPartial<T> | ((state: T) => DeepPartial<T> | Promise<DeepPartial<T>>);
|
|
48
|
+
type StateUpdater<T> = T | DeepPartial<T> | ((state: T) => DeepPartial<T> | Promise<DeepPartial<T>>);
|
|
49
|
+
declare const DELETE_SYMBOL: unique symbol;
|
|
63
50
|
/**
|
|
64
|
-
|
|
51
|
+
* Core types for the reactive data store
|
|
52
|
+
*/
|
|
53
|
+
type TransformMiddleware<T> = (state: T, changes: DeepPartial<T>) => Promise<DeepPartial<T>> | DeepPartial<T>;
|
|
54
|
+
/**
|
|
55
|
+
* Type for blocking middleware functions that can prevent updates.
|
|
65
56
|
*/
|
|
57
|
+
type BlockingMiddleware<T> = (state: T, changes: DeepPartial<T>) => Promise<boolean | {
|
|
58
|
+
block: boolean;
|
|
59
|
+
error?: Error;
|
|
60
|
+
}> | boolean | {
|
|
61
|
+
block: boolean;
|
|
62
|
+
error?: Error;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Type representing the configuration object passed to `use`.
|
|
66
|
+
*/
|
|
67
|
+
interface MiddlewareConfig<T> {
|
|
68
|
+
action: TransformMiddleware<T> | BlockingMiddleware<T>;
|
|
69
|
+
name?: string;
|
|
70
|
+
block?: boolean;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Interface for performance metrics of the state.
|
|
74
|
+
*/
|
|
75
|
+
interface StoreMetrics {
|
|
76
|
+
updateCount: number;
|
|
77
|
+
listenerExecutions: number;
|
|
78
|
+
averageUpdateTime: number;
|
|
79
|
+
largestUpdateSize: number;
|
|
80
|
+
mostActiveListenerPaths: string[];
|
|
81
|
+
}
|
|
82
|
+
interface StoreMetrics {
|
|
83
|
+
totalUpdates: number;
|
|
84
|
+
blockedUpdates: number;
|
|
85
|
+
listenerExecutions: number;
|
|
86
|
+
averageUpdateDuration: number;
|
|
87
|
+
middlewareExecutions: number;
|
|
88
|
+
transactionCount: number;
|
|
89
|
+
totalEventsFired: number;
|
|
90
|
+
}
|
|
66
91
|
interface DataStore<T extends object> {
|
|
67
92
|
get(clone?: boolean): T;
|
|
68
93
|
set(update: StateUpdater<T>): Promise<void>;
|
|
@@ -72,18 +97,55 @@ interface DataStore<T extends object> {
|
|
|
72
97
|
onStoreEvent(event: StoreEvent, listener: (data: any) => void): () => void;
|
|
73
98
|
}
|
|
74
99
|
/**
|
|
75
|
-
*
|
|
100
|
+
* Interface defining the contract for the data state.
|
|
76
101
|
*/
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
102
|
+
interface DataStore<T extends object> {
|
|
103
|
+
get(clone?: boolean): T;
|
|
104
|
+
set(update: StateUpdater<T>): Promise<void>;
|
|
105
|
+
subscribe(path: string | Array<string>, callback: (state: T) => void): () => void;
|
|
106
|
+
transaction<R>(operation: () => R | Promise<R>): Promise<R>;
|
|
107
|
+
use(props: MiddlewareConfig<T>): string;
|
|
108
|
+
unuse(id: string): boolean;
|
|
109
|
+
onStoreEvent(event: StoreEvent, listener: (data: any) => void): () => void;
|
|
110
|
+
id(): string;
|
|
111
|
+
isReady(): boolean;
|
|
112
|
+
state(): Readonly<StoreExecutionState<T>>;
|
|
113
|
+
}
|
|
114
|
+
interface PersistenceQueuedPayload {
|
|
115
|
+
taskId: string;
|
|
116
|
+
changedPaths: string[];
|
|
117
|
+
queueSize: number;
|
|
118
|
+
timestamp: number;
|
|
119
|
+
}
|
|
120
|
+
interface PersistenceSuccessPayload {
|
|
121
|
+
taskId: string;
|
|
122
|
+
changedPaths: string[];
|
|
123
|
+
duration: number;
|
|
124
|
+
timestamp: number;
|
|
125
|
+
}
|
|
126
|
+
interface PersistenceRetryPayload {
|
|
127
|
+
taskId: string;
|
|
128
|
+
attempt: number;
|
|
129
|
+
maxRetries: number;
|
|
130
|
+
nextRetryIn: number;
|
|
131
|
+
error: any;
|
|
132
|
+
timestamp: number;
|
|
133
|
+
}
|
|
134
|
+
interface PersistenceFailedPayload {
|
|
135
|
+
taskId: string;
|
|
136
|
+
changedPaths: string[];
|
|
137
|
+
attempts: number;
|
|
138
|
+
error: any;
|
|
139
|
+
timestamp: number;
|
|
140
|
+
}
|
|
141
|
+
interface PersistenceQueueClearedPayload {
|
|
142
|
+
clearedTasks: number;
|
|
143
|
+
timestamp: number;
|
|
144
|
+
}
|
|
145
|
+
interface PersistenceInitErrorPayload {
|
|
146
|
+
error: any;
|
|
147
|
+
timestamp: number;
|
|
148
|
+
}
|
|
87
149
|
declare const author = "https://github.com/asaidimu";
|
|
88
150
|
|
|
89
151
|
/**
|
|
@@ -93,35 +155,157 @@ declare const author = "https://github.com/asaidimu";
|
|
|
93
155
|
*/
|
|
94
156
|
|
|
95
157
|
/**
|
|
96
|
-
* Main ReactiveDataStore -
|
|
158
|
+
* Main ReactiveDataStore - a robust, type-safe state management solution.
|
|
159
|
+
* It features optimistic updates for a responsive user experience and
|
|
160
|
+
* handles data persistence in the background.
|
|
161
|
+
*
|
|
162
|
+
* @template T The type of the data object managed by the store.
|
|
97
163
|
*/
|
|
98
164
|
declare class ReactiveDataStore<T extends object> implements DataStore<T> {
|
|
165
|
+
/** Manages the core application state. */
|
|
99
166
|
private coreState;
|
|
167
|
+
/** Orchestrates and executes middleware for state changes. */
|
|
100
168
|
private middlewareEngine;
|
|
169
|
+
/** Manages saving state changes to a persistence layer. */
|
|
101
170
|
private persistenceHandler;
|
|
171
|
+
/** Manages atomic state updates via transactions. */
|
|
102
172
|
private transactionManager;
|
|
173
|
+
/** Collects and provides performance metrics for the store. */
|
|
103
174
|
private metricsCollector;
|
|
175
|
+
/** Queue for pending state updates when the store is busy. */
|
|
104
176
|
private pendingUpdates;
|
|
177
|
+
/** Flag to prevent concurrent updates. */
|
|
105
178
|
private isUpdating;
|
|
179
|
+
/** Event bus for notifying subscribers of state updates. */
|
|
106
180
|
private updateBus;
|
|
181
|
+
/** General event bus for broadcasting internal store events. */
|
|
107
182
|
private eventBus;
|
|
183
|
+
/** Represents the current execution state of the store. */
|
|
108
184
|
private executionState;
|
|
185
|
+
/** A unique identifier for this store instance. */
|
|
109
186
|
private instanceID;
|
|
187
|
+
/** Function to deeply merge partial state updates. */
|
|
110
188
|
private merge;
|
|
189
|
+
/** Function to calculate the difference between two state objects. */
|
|
111
190
|
private diff;
|
|
112
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Creates a new ReactiveDataStore instance.
|
|
193
|
+
*
|
|
194
|
+
* @param initialData The initial state of the data store.
|
|
195
|
+
* @param persistence An optional persistence handler for saving state.
|
|
196
|
+
* @param deleteMarker A symbol used to mark properties for deletion.
|
|
197
|
+
* @param options Configuration options for the store.
|
|
198
|
+
*/
|
|
199
|
+
constructor(initialData: T, persistence?: SimplePersistence<T>, deleteMarker?: symbol, options?: {
|
|
200
|
+
persistenceMaxRetries?: number;
|
|
201
|
+
persistenceRetryDelay?: number;
|
|
202
|
+
});
|
|
203
|
+
/**
|
|
204
|
+
* Checks if the persistence layer is ready to save data.
|
|
205
|
+
*
|
|
206
|
+
* @returns `true` if ready, otherwise `false`.
|
|
207
|
+
*/
|
|
113
208
|
isReady(): boolean;
|
|
209
|
+
/**
|
|
210
|
+
* Returns the current read-only execution state of the store.
|
|
211
|
+
*/
|
|
114
212
|
state(): Readonly<StoreExecutionState<T>>;
|
|
213
|
+
/**
|
|
214
|
+
* Gets the current state of the data store.
|
|
215
|
+
*
|
|
216
|
+
* @param clone If `true`, returns a deep clone of the state to prevent direct modification.
|
|
217
|
+
* @returns The current state object.
|
|
218
|
+
*/
|
|
115
219
|
get(clone?: boolean): T;
|
|
220
|
+
/**
|
|
221
|
+
* Optimized method to update the state. It handles optimistic updates by
|
|
222
|
+
* applying changes immediately and persisting them in the background.
|
|
223
|
+
* Middleware is executed before and after the update.
|
|
224
|
+
*
|
|
225
|
+
* If another update is in progress, the new update is queued.
|
|
226
|
+
*
|
|
227
|
+
* @param update A partial state object or a function that returns a partial state object.
|
|
228
|
+
*/
|
|
116
229
|
set(update: StateUpdater<T>): Promise<void>;
|
|
230
|
+
/**
|
|
231
|
+
* Subscribes a callback function to state changes at a specific path.
|
|
232
|
+
* The callback is triggered only when the specified path changes.
|
|
233
|
+
*
|
|
234
|
+
* @param path The path or array of paths to subscribe to.
|
|
235
|
+
* @param callback The function to call when the state at the path changes.
|
|
236
|
+
* @returns A function to unsubscribe the callback.
|
|
237
|
+
*/
|
|
117
238
|
subscribe(path: string | Array<string>, callback: (state: T) => void): () => void;
|
|
239
|
+
/**
|
|
240
|
+
* Gets the unique identifier for this store instance.
|
|
241
|
+
*
|
|
242
|
+
* @returns The instance ID as a string.
|
|
243
|
+
*/
|
|
118
244
|
id(): string;
|
|
245
|
+
/**
|
|
246
|
+
* Executes a series of state updates atomically within a single transaction.
|
|
247
|
+
*
|
|
248
|
+
* @param operation A function containing the state updates to be performed.
|
|
249
|
+
* @returns The result of the operation.
|
|
250
|
+
*/
|
|
119
251
|
transaction<R>(operation: () => R | Promise<R>): Promise<R>;
|
|
252
|
+
/**
|
|
253
|
+
* Adds a middleware function to the store.
|
|
254
|
+
*
|
|
255
|
+
* @param props The middleware configuration.
|
|
256
|
+
* @returns A unique ID for the added middleware.
|
|
257
|
+
*/
|
|
120
258
|
use(props: MiddlewareConfig<T>): string;
|
|
259
|
+
/**
|
|
260
|
+
* Removes a middleware function by its unique ID.
|
|
261
|
+
*
|
|
262
|
+
* @param id The ID of the middleware to remove.
|
|
263
|
+
* @returns `true` if the middleware was successfully removed, `false` otherwise.
|
|
264
|
+
*/
|
|
121
265
|
unuse(id: string): boolean;
|
|
122
|
-
/**
|
|
266
|
+
/**
|
|
267
|
+
* Get the performance metrics for the store.
|
|
268
|
+
* @deprecated WILL BE REMOVED in a future version.
|
|
269
|
+
*/
|
|
123
270
|
metrics(): StoreMetrics;
|
|
271
|
+
/**
|
|
272
|
+
* Subscribes a listener to a specific store event.
|
|
273
|
+
*
|
|
274
|
+
* @param event The name of the event to listen for.
|
|
275
|
+
* @param listener The callback function to execute when the event is emitted.
|
|
276
|
+
* @returns A function to unsubscribe the listener.
|
|
277
|
+
*/
|
|
124
278
|
onStoreEvent(event: StoreEvent, listener: (data: any) => void): () => void;
|
|
279
|
+
/**
|
|
280
|
+
* Get the current status of the persistence queue.
|
|
281
|
+
*
|
|
282
|
+
* @returns An object containing the queue size, processing status, and the age of the oldest task.
|
|
283
|
+
*/
|
|
284
|
+
getPersistenceStatus(): {
|
|
285
|
+
queueSize: number;
|
|
286
|
+
isProcessing: boolean;
|
|
287
|
+
oldestTask?: number;
|
|
288
|
+
};
|
|
289
|
+
/**
|
|
290
|
+
* Forces immediate processing of any pending persistence operations.
|
|
291
|
+
*/
|
|
292
|
+
flushPersistence(): Promise<void>;
|
|
293
|
+
/**
|
|
294
|
+
* Clears all pending persistence operations from the queue.
|
|
295
|
+
*/
|
|
296
|
+
clearPersistenceQueue(): void;
|
|
297
|
+
/**
|
|
298
|
+
* Cleans up resources, such as event listeners, to prevent memory leaks.
|
|
299
|
+
*/
|
|
300
|
+
dispose(): void;
|
|
301
|
+
/**
|
|
302
|
+
* A private utility method to emit an event on a given event bus.
|
|
303
|
+
* Uses `queueMicrotask` to ensure events are emitted asynchronously after the current call stack clears.
|
|
304
|
+
*
|
|
305
|
+
* @param bus The event bus to emit the event on.
|
|
306
|
+
* @param event The event object to emit.
|
|
307
|
+
*/
|
|
308
|
+
private emit;
|
|
125
309
|
}
|
|
126
310
|
|
|
127
311
|
/**
|
|
@@ -285,4 +469,4 @@ declare class StoreObserver<T extends object> {
|
|
|
285
469
|
disconnect(): void;
|
|
286
470
|
}
|
|
287
471
|
|
|
288
|
-
export { type BlockingMiddleware, DELETE_SYMBOL, type DataStore, type DebugEvent, type DeepPartial, type Middleware, type MiddlewareConfig, type MiddlewareExecution, type ObservabilityOptions, ReactiveDataStore, type StateUpdater, type StoreEvent, type StoreExecutionState, type StoreMetrics, StoreObserver, author };
|
|
472
|
+
export { type BlockingMiddleware, DELETE_SYMBOL, type DataStore, type DebugEvent, type DeepPartial, type Middleware, type MiddlewareConfig, type MiddlewareExecution, type ObservabilityOptions, type PersistenceFailedPayload, type PersistenceInitErrorPayload, type PersistenceQueueClearedPayload, type PersistenceQueuedPayload, type PersistenceRetryPayload, type PersistenceSuccessPayload, ReactiveDataStore, type StateUpdater, type StoreEvent, type StoreExecutionState, type StoreMetrics, StoreObserver, type TransformMiddleware, author };
|
package/index.d.ts
CHANGED
|
@@ -1,21 +1,11 @@
|
|
|
1
|
-
import { S as SimplePersistence } from '../types-
|
|
1
|
+
import { S as SimplePersistence } from '../types-DUZGkNEB.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Utility type for representing partial updates to the state.
|
|
5
5
|
*/
|
|
6
|
-
type DeepPartial<T> = {
|
|
7
|
-
[
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* Interface for performance metrics of the state.
|
|
11
|
-
*/
|
|
12
|
-
interface StoreMetrics {
|
|
13
|
-
updateCount: number;
|
|
14
|
-
listenerExecutions: number;
|
|
15
|
-
averageUpdateTime: number;
|
|
16
|
-
largestUpdateSize: number;
|
|
17
|
-
mostActiveListenerPaths: string[];
|
|
18
|
-
}
|
|
6
|
+
type DeepPartial<T> = T extends object ? T extends readonly (infer U)[] ? readonly (DeepPartial<U> | undefined)[] | undefined | T : T extends (infer U)[] ? (DeepPartial<U> | undefined)[] | undefined | T : {
|
|
7
|
+
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> | undefined : T[K] | undefined;
|
|
8
|
+
} | undefined | T : T | undefined;
|
|
19
9
|
/**
|
|
20
10
|
* Extended store state for monitoring execution status
|
|
21
11
|
*/
|
|
@@ -34,15 +24,11 @@ interface StoreExecutionState<T> {
|
|
|
34
24
|
/**
|
|
35
25
|
* Event types emitted by the state for observability
|
|
36
26
|
*/
|
|
37
|
-
type StoreEvent = "update:start" | "update:complete" | "middleware:start" | "middleware:complete" | "middleware:error" | "middleware:blocked" | "middleware:executed" | "transaction:start" | "transaction:complete" | "transaction:error" | "persistence:ready";
|
|
27
|
+
type StoreEvent = "update:start" | "update:complete" | "middleware:start" | "middleware:complete" | "middleware:error" | "middleware:blocked" | "middleware:executed" | "transaction:start" | "transaction:complete" | "transaction:error" | "persistence:ready" | "persistence:queued" | "persistence:success" | "persistence:retry" | "persistence:failed" | "persistence:queue_cleared" | "persistence:init_error";
|
|
38
28
|
/**
|
|
39
29
|
* Type for middleware functions. Return a partial state to transform the update.
|
|
40
30
|
*/
|
|
41
31
|
type Middleware<T> = (state: T, update: DeepPartial<T>) => Promise<DeepPartial<T>> | Promise<void> | DeepPartial<T> | void;
|
|
42
|
-
/**
|
|
43
|
-
* Type for blocking middleware functions that can prevent updates.
|
|
44
|
-
*/
|
|
45
|
-
type BlockingMiddleware<T> = (state: T, update: DeepPartial<T>) => boolean | Promise<boolean>;
|
|
46
32
|
/**
|
|
47
33
|
* Middleware execution information for event emissions
|
|
48
34
|
*/
|
|
@@ -59,10 +45,49 @@ interface MiddlewareExecution {
|
|
|
59
45
|
/**
|
|
60
46
|
* Represents a state update, which can be either a partial state object or a function.
|
|
61
47
|
*/
|
|
62
|
-
type StateUpdater<T> = DeepPartial<T> | ((state: T) => DeepPartial<T> | Promise<DeepPartial<T>>);
|
|
48
|
+
type StateUpdater<T> = T | DeepPartial<T> | ((state: T) => DeepPartial<T> | Promise<DeepPartial<T>>);
|
|
49
|
+
declare const DELETE_SYMBOL: unique symbol;
|
|
63
50
|
/**
|
|
64
|
-
|
|
51
|
+
* Core types for the reactive data store
|
|
52
|
+
*/
|
|
53
|
+
type TransformMiddleware<T> = (state: T, changes: DeepPartial<T>) => Promise<DeepPartial<T>> | DeepPartial<T>;
|
|
54
|
+
/**
|
|
55
|
+
* Type for blocking middleware functions that can prevent updates.
|
|
65
56
|
*/
|
|
57
|
+
type BlockingMiddleware<T> = (state: T, changes: DeepPartial<T>) => Promise<boolean | {
|
|
58
|
+
block: boolean;
|
|
59
|
+
error?: Error;
|
|
60
|
+
}> | boolean | {
|
|
61
|
+
block: boolean;
|
|
62
|
+
error?: Error;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Type representing the configuration object passed to `use`.
|
|
66
|
+
*/
|
|
67
|
+
interface MiddlewareConfig<T> {
|
|
68
|
+
action: TransformMiddleware<T> | BlockingMiddleware<T>;
|
|
69
|
+
name?: string;
|
|
70
|
+
block?: boolean;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Interface for performance metrics of the state.
|
|
74
|
+
*/
|
|
75
|
+
interface StoreMetrics {
|
|
76
|
+
updateCount: number;
|
|
77
|
+
listenerExecutions: number;
|
|
78
|
+
averageUpdateTime: number;
|
|
79
|
+
largestUpdateSize: number;
|
|
80
|
+
mostActiveListenerPaths: string[];
|
|
81
|
+
}
|
|
82
|
+
interface StoreMetrics {
|
|
83
|
+
totalUpdates: number;
|
|
84
|
+
blockedUpdates: number;
|
|
85
|
+
listenerExecutions: number;
|
|
86
|
+
averageUpdateDuration: number;
|
|
87
|
+
middlewareExecutions: number;
|
|
88
|
+
transactionCount: number;
|
|
89
|
+
totalEventsFired: number;
|
|
90
|
+
}
|
|
66
91
|
interface DataStore<T extends object> {
|
|
67
92
|
get(clone?: boolean): T;
|
|
68
93
|
set(update: StateUpdater<T>): Promise<void>;
|
|
@@ -72,18 +97,55 @@ interface DataStore<T extends object> {
|
|
|
72
97
|
onStoreEvent(event: StoreEvent, listener: (data: any) => void): () => void;
|
|
73
98
|
}
|
|
74
99
|
/**
|
|
75
|
-
*
|
|
100
|
+
* Interface defining the contract for the data state.
|
|
76
101
|
*/
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
102
|
+
interface DataStore<T extends object> {
|
|
103
|
+
get(clone?: boolean): T;
|
|
104
|
+
set(update: StateUpdater<T>): Promise<void>;
|
|
105
|
+
subscribe(path: string | Array<string>, callback: (state: T) => void): () => void;
|
|
106
|
+
transaction<R>(operation: () => R | Promise<R>): Promise<R>;
|
|
107
|
+
use(props: MiddlewareConfig<T>): string;
|
|
108
|
+
unuse(id: string): boolean;
|
|
109
|
+
onStoreEvent(event: StoreEvent, listener: (data: any) => void): () => void;
|
|
110
|
+
id(): string;
|
|
111
|
+
isReady(): boolean;
|
|
112
|
+
state(): Readonly<StoreExecutionState<T>>;
|
|
113
|
+
}
|
|
114
|
+
interface PersistenceQueuedPayload {
|
|
115
|
+
taskId: string;
|
|
116
|
+
changedPaths: string[];
|
|
117
|
+
queueSize: number;
|
|
118
|
+
timestamp: number;
|
|
119
|
+
}
|
|
120
|
+
interface PersistenceSuccessPayload {
|
|
121
|
+
taskId: string;
|
|
122
|
+
changedPaths: string[];
|
|
123
|
+
duration: number;
|
|
124
|
+
timestamp: number;
|
|
125
|
+
}
|
|
126
|
+
interface PersistenceRetryPayload {
|
|
127
|
+
taskId: string;
|
|
128
|
+
attempt: number;
|
|
129
|
+
maxRetries: number;
|
|
130
|
+
nextRetryIn: number;
|
|
131
|
+
error: any;
|
|
132
|
+
timestamp: number;
|
|
133
|
+
}
|
|
134
|
+
interface PersistenceFailedPayload {
|
|
135
|
+
taskId: string;
|
|
136
|
+
changedPaths: string[];
|
|
137
|
+
attempts: number;
|
|
138
|
+
error: any;
|
|
139
|
+
timestamp: number;
|
|
140
|
+
}
|
|
141
|
+
interface PersistenceQueueClearedPayload {
|
|
142
|
+
clearedTasks: number;
|
|
143
|
+
timestamp: number;
|
|
144
|
+
}
|
|
145
|
+
interface PersistenceInitErrorPayload {
|
|
146
|
+
error: any;
|
|
147
|
+
timestamp: number;
|
|
148
|
+
}
|
|
87
149
|
declare const author = "https://github.com/asaidimu";
|
|
88
150
|
|
|
89
151
|
/**
|
|
@@ -93,35 +155,157 @@ declare const author = "https://github.com/asaidimu";
|
|
|
93
155
|
*/
|
|
94
156
|
|
|
95
157
|
/**
|
|
96
|
-
* Main ReactiveDataStore -
|
|
158
|
+
* Main ReactiveDataStore - a robust, type-safe state management solution.
|
|
159
|
+
* It features optimistic updates for a responsive user experience and
|
|
160
|
+
* handles data persistence in the background.
|
|
161
|
+
*
|
|
162
|
+
* @template T The type of the data object managed by the store.
|
|
97
163
|
*/
|
|
98
164
|
declare class ReactiveDataStore<T extends object> implements DataStore<T> {
|
|
165
|
+
/** Manages the core application state. */
|
|
99
166
|
private coreState;
|
|
167
|
+
/** Orchestrates and executes middleware for state changes. */
|
|
100
168
|
private middlewareEngine;
|
|
169
|
+
/** Manages saving state changes to a persistence layer. */
|
|
101
170
|
private persistenceHandler;
|
|
171
|
+
/** Manages atomic state updates via transactions. */
|
|
102
172
|
private transactionManager;
|
|
173
|
+
/** Collects and provides performance metrics for the store. */
|
|
103
174
|
private metricsCollector;
|
|
175
|
+
/** Queue for pending state updates when the store is busy. */
|
|
104
176
|
private pendingUpdates;
|
|
177
|
+
/** Flag to prevent concurrent updates. */
|
|
105
178
|
private isUpdating;
|
|
179
|
+
/** Event bus for notifying subscribers of state updates. */
|
|
106
180
|
private updateBus;
|
|
181
|
+
/** General event bus for broadcasting internal store events. */
|
|
107
182
|
private eventBus;
|
|
183
|
+
/** Represents the current execution state of the store. */
|
|
108
184
|
private executionState;
|
|
185
|
+
/** A unique identifier for this store instance. */
|
|
109
186
|
private instanceID;
|
|
187
|
+
/** Function to deeply merge partial state updates. */
|
|
110
188
|
private merge;
|
|
189
|
+
/** Function to calculate the difference between two state objects. */
|
|
111
190
|
private diff;
|
|
112
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Creates a new ReactiveDataStore instance.
|
|
193
|
+
*
|
|
194
|
+
* @param initialData The initial state of the data store.
|
|
195
|
+
* @param persistence An optional persistence handler for saving state.
|
|
196
|
+
* @param deleteMarker A symbol used to mark properties for deletion.
|
|
197
|
+
* @param options Configuration options for the store.
|
|
198
|
+
*/
|
|
199
|
+
constructor(initialData: T, persistence?: SimplePersistence<T>, deleteMarker?: symbol, options?: {
|
|
200
|
+
persistenceMaxRetries?: number;
|
|
201
|
+
persistenceRetryDelay?: number;
|
|
202
|
+
});
|
|
203
|
+
/**
|
|
204
|
+
* Checks if the persistence layer is ready to save data.
|
|
205
|
+
*
|
|
206
|
+
* @returns `true` if ready, otherwise `false`.
|
|
207
|
+
*/
|
|
113
208
|
isReady(): boolean;
|
|
209
|
+
/**
|
|
210
|
+
* Returns the current read-only execution state of the store.
|
|
211
|
+
*/
|
|
114
212
|
state(): Readonly<StoreExecutionState<T>>;
|
|
213
|
+
/**
|
|
214
|
+
* Gets the current state of the data store.
|
|
215
|
+
*
|
|
216
|
+
* @param clone If `true`, returns a deep clone of the state to prevent direct modification.
|
|
217
|
+
* @returns The current state object.
|
|
218
|
+
*/
|
|
115
219
|
get(clone?: boolean): T;
|
|
220
|
+
/**
|
|
221
|
+
* Optimized method to update the state. It handles optimistic updates by
|
|
222
|
+
* applying changes immediately and persisting them in the background.
|
|
223
|
+
* Middleware is executed before and after the update.
|
|
224
|
+
*
|
|
225
|
+
* If another update is in progress, the new update is queued.
|
|
226
|
+
*
|
|
227
|
+
* @param update A partial state object or a function that returns a partial state object.
|
|
228
|
+
*/
|
|
116
229
|
set(update: StateUpdater<T>): Promise<void>;
|
|
230
|
+
/**
|
|
231
|
+
* Subscribes a callback function to state changes at a specific path.
|
|
232
|
+
* The callback is triggered only when the specified path changes.
|
|
233
|
+
*
|
|
234
|
+
* @param path The path or array of paths to subscribe to.
|
|
235
|
+
* @param callback The function to call when the state at the path changes.
|
|
236
|
+
* @returns A function to unsubscribe the callback.
|
|
237
|
+
*/
|
|
117
238
|
subscribe(path: string | Array<string>, callback: (state: T) => void): () => void;
|
|
239
|
+
/**
|
|
240
|
+
* Gets the unique identifier for this store instance.
|
|
241
|
+
*
|
|
242
|
+
* @returns The instance ID as a string.
|
|
243
|
+
*/
|
|
118
244
|
id(): string;
|
|
245
|
+
/**
|
|
246
|
+
* Executes a series of state updates atomically within a single transaction.
|
|
247
|
+
*
|
|
248
|
+
* @param operation A function containing the state updates to be performed.
|
|
249
|
+
* @returns The result of the operation.
|
|
250
|
+
*/
|
|
119
251
|
transaction<R>(operation: () => R | Promise<R>): Promise<R>;
|
|
252
|
+
/**
|
|
253
|
+
* Adds a middleware function to the store.
|
|
254
|
+
*
|
|
255
|
+
* @param props The middleware configuration.
|
|
256
|
+
* @returns A unique ID for the added middleware.
|
|
257
|
+
*/
|
|
120
258
|
use(props: MiddlewareConfig<T>): string;
|
|
259
|
+
/**
|
|
260
|
+
* Removes a middleware function by its unique ID.
|
|
261
|
+
*
|
|
262
|
+
* @param id The ID of the middleware to remove.
|
|
263
|
+
* @returns `true` if the middleware was successfully removed, `false` otherwise.
|
|
264
|
+
*/
|
|
121
265
|
unuse(id: string): boolean;
|
|
122
|
-
/**
|
|
266
|
+
/**
|
|
267
|
+
* Get the performance metrics for the store.
|
|
268
|
+
* @deprecated WILL BE REMOVED in a future version.
|
|
269
|
+
*/
|
|
123
270
|
metrics(): StoreMetrics;
|
|
271
|
+
/**
|
|
272
|
+
* Subscribes a listener to a specific store event.
|
|
273
|
+
*
|
|
274
|
+
* @param event The name of the event to listen for.
|
|
275
|
+
* @param listener The callback function to execute when the event is emitted.
|
|
276
|
+
* @returns A function to unsubscribe the listener.
|
|
277
|
+
*/
|
|
124
278
|
onStoreEvent(event: StoreEvent, listener: (data: any) => void): () => void;
|
|
279
|
+
/**
|
|
280
|
+
* Get the current status of the persistence queue.
|
|
281
|
+
*
|
|
282
|
+
* @returns An object containing the queue size, processing status, and the age of the oldest task.
|
|
283
|
+
*/
|
|
284
|
+
getPersistenceStatus(): {
|
|
285
|
+
queueSize: number;
|
|
286
|
+
isProcessing: boolean;
|
|
287
|
+
oldestTask?: number;
|
|
288
|
+
};
|
|
289
|
+
/**
|
|
290
|
+
* Forces immediate processing of any pending persistence operations.
|
|
291
|
+
*/
|
|
292
|
+
flushPersistence(): Promise<void>;
|
|
293
|
+
/**
|
|
294
|
+
* Clears all pending persistence operations from the queue.
|
|
295
|
+
*/
|
|
296
|
+
clearPersistenceQueue(): void;
|
|
297
|
+
/**
|
|
298
|
+
* Cleans up resources, such as event listeners, to prevent memory leaks.
|
|
299
|
+
*/
|
|
300
|
+
dispose(): void;
|
|
301
|
+
/**
|
|
302
|
+
* A private utility method to emit an event on a given event bus.
|
|
303
|
+
* Uses `queueMicrotask` to ensure events are emitted asynchronously after the current call stack clears.
|
|
304
|
+
*
|
|
305
|
+
* @param bus The event bus to emit the event on.
|
|
306
|
+
* @param event The event object to emit.
|
|
307
|
+
*/
|
|
308
|
+
private emit;
|
|
125
309
|
}
|
|
126
310
|
|
|
127
311
|
/**
|
|
@@ -285,4 +469,4 @@ declare class StoreObserver<T extends object> {
|
|
|
285
469
|
disconnect(): void;
|
|
286
470
|
}
|
|
287
471
|
|
|
288
|
-
export { type BlockingMiddleware, DELETE_SYMBOL, type DataStore, type DebugEvent, type DeepPartial, type Middleware, type MiddlewareConfig, type MiddlewareExecution, type ObservabilityOptions, ReactiveDataStore, type StateUpdater, type StoreEvent, type StoreExecutionState, type StoreMetrics, StoreObserver, author };
|
|
472
|
+
export { type BlockingMiddleware, DELETE_SYMBOL, type DataStore, type DebugEvent, type DeepPartial, type Middleware, type MiddlewareConfig, type MiddlewareExecution, type ObservabilityOptions, type PersistenceFailedPayload, type PersistenceInitErrorPayload, type PersistenceQueueClearedPayload, type PersistenceQueuedPayload, type PersistenceRetryPayload, type PersistenceSuccessPayload, ReactiveDataStore, type StateUpdater, type StoreEvent, type StoreExecutionState, type StoreMetrics, StoreObserver, type TransformMiddleware, author };
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e,t,r=require("uuid"),s=Object.create,a=Object.defineProperty,i=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,o=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,d=(e,t,r)=>(r=null!=e?s(o(e)):{},((e,t,r,s)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let o of n(t))c.call(e,o)||o===r||a(e,o,{get:()=>t[o],enumerable:!(s=i(t,o))||s.enumerable});return e})(e&&e.__esModule?r:a(r,"default",{value:e,enumerable:!0}),e)),l=(e={"node_modules/@asaidimu/events/index.js"(e,t){var r,s=Object.defineProperty,a=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,n=Object.prototype.hasOwnProperty,o={};((e,t)=>{for(var r in t)s(e,r,{get:t[r],enumerable:!0})})(o,{createEventBus:()=>c}),t.exports=(r=o,((e,t,r,o)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of i(t))n.call(e,c)||c===r||s(e,c,{get:()=>t[c],enumerable:!(o=a(t,c))||o.enumerable});return e})(s({},"__esModule",{value:!0}),r));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 r=[],s=0,a=0;const i=new Map,n=new Map;let o=null;e.crossTab&&"undefined"!=typeof BroadcastChannel?o=new BroadcastChannel(e.channelName):e.crossTab&&console.warn("BroadcastChannel is not supported in this browser. Cross-tab notifications are disabled.");const c=(e,t)=>{s++,a+=t,i.set(e,(i.get(e)||0)+1)},d=()=>{const t=r;r=[],t.forEach((({name:t,payload:r})=>{const s=performance.now();try{(n.get(t)||[]).forEach((e=>e(r)))}catch(s){e.errorHandler({...s,eventName:t,payload:r})}c(t,performance.now()-s)}))},l=(()=>{let t;return()=>{clearTimeout(t),t=setTimeout(d,e.batchDelay)}})(),h=e=>{const r=t.get(e);r?n.set(e,Array.from(r)):n.delete(e)};return o&&(o.onmessage=e=>{const{name:t,payload:r}=e.data;(n.get(t)||[]).forEach((e=>e(r)))}),{subscribe:(e,r)=>{t.has(e)||t.set(e,new Set);const s=t.get(e);return s.add(r),h(e),()=>{s.delete(r),0===s.size?(t.delete(e),n.delete(e)):h(e)}},emit:({name:t,payload:s})=>{if(e.async)return r.push({name:t,payload:s}),r.length>=e.batchSize?d():l(),void(o&&o.postMessage({name:t,payload:s}));const a=performance.now();try{(n.get(t)||[]).forEach((e=>e(s))),o&&o.postMessage({name:t,payload:s})}catch(r){e.errorHandler({...r,eventName:t,payload:s})}c(t,performance.now()-a)},getMetrics:()=>({totalEvents:s,activeSubscriptions:Array.from(t.values()).reduce(((e,t)=>e+t.size),0),eventCounts:i,averageEmitDuration:s>0?a/s:0}),clear:()=>{t.clear(),n.clear(),r=[],s=0,a=0,i.clear(),o&&(o.close(),o=null)}}}}},function(){return t||(0,e[n(e)[0]])((t={exports:{}}).exports,t),t.exports}),h=d(l()),u=Symbol.for("delete"),m=e=>Array.isArray(e)?[...e]:{...e};function p(e){const t=e?.deleteMarker||u;return function(e,r){if("object"!=typeof e||null===e)return"object"==typeof r&&null!==r?i(r):r===t?{}:r;if("object"!=typeof r||null===r)return e;const s=m(e),a=[{target:s,source:r}];for(;a.length>0;){const{target:e,source:r}=a.pop();for(const s of Object.keys(r)){if(!Object.prototype.hasOwnProperty.call(r,s))continue;const n=r[s];n!==t?Array.isArray(n)?e[s]=n.map((e=>"object"!=typeof e||null===e||Array.isArray(e)?e===t?void 0:e:i(e))):"object"==typeof n&&null!==n?(e[s]=m(s in e&&"object"==typeof e[s]&&null!==e[s]?e[s]:{}),a.push({target:e[s],source:n})):e[s]=n:delete e[s]}}return s;function i(e){if(null==e)return e;if(Array.isArray(e))return e.filter((e=>e!==t)).map((e=>"object"!=typeof e||null===e||Array.isArray(e)?e===t?void 0:e:i(e)));if("object"==typeof e){const r={};for(const[s,a]of Object.entries(e))if(a!==t)if("object"==typeof a&&null!==a){const e=i(a);void 0!==e&&(r[s]=e)}else r[s]=a;return r}return e===t?void 0:e}}}function g(e){const t=e?.deleteMarker||u;return function(e,r){const s=new Set,a=e=>s.add(e);function i(e){if(!e)return;const t=e.split(".");a(e);for(let e=t.length-1;e>0;e--){const r=t.slice(0,e).join(".");if(s.has(r))break;a(r)}}const n=[{path:"",orig:e||{},part:r||{}}];for(;n.length>0;){const{path:e,orig:r,part:s}=n.pop();if(null!=s)if(Array.isArray(s))Array.isArray(r)&&JSON.stringify(r)===JSON.stringify(s)||i(e);else if("object"==typeof s){const a=Object.keys(s);for(let o=0;o<a.length;o++){const c=a[o],d=e?`${e}.${c}`:c,l=s[c];if(l===t||null==r){i(d);continue}const h=r[c];"object"==typeof l&&null!==l?n.push({path:d,orig:h,part:l}):h!==l&&i(d)}}}return Array.from(s)}}p(),g();var f=class{constructor(e,t,r){this.updateBus=t,this.diff=r,this.cache=structuredClone(e)}cache;get(e){return e?structuredClone(this.cache):this.cache}applyChanges(e){const t=this.diff(this.get(!0),e);return t.length>0&&(this.cache=structuredClone(e),this.notifyListeners(t)),t}notifyListeners(e){e.forEach((e=>this.updateBus.emit({name:"update",payload:e})))}};d(l());var y=class{constructor(e,t,r){this.eventBus=e,this.executionState=t,this.merge=r}middleware=[];blockingMiddleware=[];async executeBlocking(e,t){for(const{fn:r,name:s,id:a}of this.blockingMiddleware){const i={id:a,name:s,startTime:performance.now()};this.executionState.runningMiddleware={id:a,name:s,startTime:performance.now()},this.emitMiddlewareLifecycle("start",{id:a,name:s,type:"blocking"});try{const n=await Promise.resolve(r(e,t));if(i.endTime=performance.now(),i.duration=i.endTime-i.startTime,!1===n)return i.blocked=!0,this.emitMiddlewareLifecycle("blocked",{id:a,name:s,duration:i.duration}),this.eventBus.emit({name:"middleware:executed",payload:i}),{blocked:!0};this.emitMiddlewareLifecycle("complete",{id:a,name:s,type:"blocking",duration:i.duration}),this.eventBus.emit({name:"middleware:executed",payload:{...i,blocked:!1}})}catch(e){return i.endTime=performance.now(),i.duration=i.endTime-i.startTime,i.error=e instanceof Error?e:new Error(String(e)),i.blocked=!0,this.emitMiddlewareError(a,s,i.error,i.duration),this.eventBus.emit({name:"middleware:executed",payload:i}),{blocked:!0,error:i.error}}finally{this.executionState.runningMiddleware=null}}return{blocked:!1}}async executeTransform(e,t){let r=e,s=t;for(const{fn:e,name:a,id:i}of this.middleware){const n={id:i,name:a,startTime:performance.now()};this.executionState.runningMiddleware={id:i,name:a,startTime:performance.now()},this.emitMiddlewareLifecycle("start",{id:i,name:a,type:"transform"});try{const o=await Promise.resolve(e(r,t));n.endTime=performance.now(),n.duration=n.endTime-n.startTime,n.blocked=!1,o&&"object"==typeof o&&(r=this.merge(r,o),s=this.merge(s,o)),this.eventBus.emit({name:"middleware:executed",payload:n}),this.emitMiddlewareLifecycle("complete",{id:i,name:a,type:"transform",duration:n.duration})}catch(e){n.endTime=performance.now(),n.duration=n.endTime-n.startTime,n.error=e instanceof Error?e:new Error(String(e)),n.blocked=!1,this.eventBus.emit({name:"middleware:executed",payload:n}),this.emitMiddlewareError(i,a,n.error,n.duration),console.error(`Middleware ${a} error:`,e)}finally{this.executionState.runningMiddleware=null}}return s}addMiddleware(e,t="unnamed-middleware"){const r=this.generateId();return this.middleware.push({fn:e,name:t,id:r}),this.updateExecutionState(),r}addBlockingMiddleware(e,t="unnamed-blocking-middleware"){const r=this.generateId();return this.blockingMiddleware.push({fn:e,name:t,id:r}),this.updateExecutionState(),r}removeMiddleware(e){const t=this.middleware.length+this.blockingMiddleware.length;return this.middleware=this.middleware.filter((t=>t.id!==e)),this.blockingMiddleware=this.blockingMiddleware.filter((t=>t.id!==e)),this.updateExecutionState(),this.middleware.length+this.blockingMiddleware.length<t}updateExecutionState(){this.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))]}emitMiddlewareLifecycle(e,t){this.eventBus.emit({name:`middleware:${e}`,payload:{...t,timestamp:Date.now()}})}emitMiddlewareError(e,t,r,s){this.eventBus.emit({name:"middleware:error",payload:{id:e,name:t,error:r,duration:s,timestamp:Date.now()}})}generateId(){return crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,15)}`}};d(l());var w=class{constructor(e,t,r){this.eventBus=e,this.coreState=t,this.instanceID=r}persistence;instanceID;persistenceReady=!1;async initialize(e){e?await this.setPersistence(e):this.setPersistenceReady()}isReady(){return this.persistenceReady}async handleStateChange(e,t){if(this.persistence&&0!==e.length)try{await this.persistence.set(this.instanceID,t)||this.eventBus.emit({name:"update:complete",payload:{persistence:!1,timestamp:Date.now()}})}catch(e){this.eventBus.emit({name:"update:complete",payload:{persistence:!1,error:e,timestamp:Date.now()}})}}setPersistenceReady(){this.persistenceReady=!0,this.eventBus.emit({name:"persistence:ready",payload:{timestamp:Date.now()}})}async setPersistence(e){this.persistence=e;try{const e=await this.persistence.get();e&&this.coreState.applyChanges(e)}catch(e){console.error("Failed to initialize persistence:",e)}finally{this.setPersistenceReady()}this.persistence.subscribe(this.instanceID,(async e=>{const t=this.coreState.applyChanges(e);t.length>0&&this.eventBus.emit({name:"update:complete",payload:{changedPaths:t,source:"external",timestamp:Date.now()}})}))}};d(l());var v=class{constructor(e,t,r){this.eventBus=e,this.coreState=t,this.executionState=r}async execute(e){const t=this.coreState.get(!0);this.executionState.transactionActive=!0,this.eventBus.emit({name:"transaction:start",payload:{timestamp:Date.now()}});try{const t=await Promise.resolve(e());return this.eventBus.emit({name:"transaction:complete",payload:{timestamp:Date.now()}}),this.executionState.transactionActive=!1,t}catch(e){throw this.coreState.applyChanges(t),this.eventBus.emit({name:"transaction:error",payload:{error:e instanceof Error?e:new Error(String(e)),timestamp:Date.now()}}),this.executionState.transactionActive=!1,e}}};d(l());var b=class{updateCount=0;listenerExecutions=0;averageUpdateTime=0;largestUpdateSize=0;mostActiveListenerPaths=[];updateTimes=[];constructor(e){this.setupEventListeners(e)}getMetrics(){return{updateCount:this.updateCount,listenerExecutions:this.listenerExecutions,averageUpdateTime:this.averageUpdateTime,largestUpdateSize:this.largestUpdateSize,mostActiveListenerPaths:[...this.mostActiveListenerPaths]}}setupEventListeners(e){e.subscribe("update:complete",(e=>{e.duration&&(this.updateTimes.push(e.duration),this.updateTimes.length>100&&this.updateTimes.shift(),this.averageUpdateTime=this.updateTimes.reduce(((e,t)=>e+t),0)/this.updateTimes.length),e.changedPaths?.length&&(this.updateCount++,this.largestUpdateSize=Math.max(this.largestUpdateSize,e.changedPaths.length))}))}},S=g();exports.DELETE_SYMBOL=u,exports.ReactiveDataStore=class{coreState;middlewareEngine;persistenceHandler;transactionManager;metricsCollector;pendingUpdates=[];isUpdating=!1;updateBus=(0,h.createEventBus)();eventBus=(0,h.createEventBus)();executionState;instanceID=r.v4();merge;diff;constructor(e,t,r=u){this.executionState={executing:!1,changes:null,pendingChanges:[],middlewares:[],runningMiddleware:null,transactionActive:!1},this.merge=p({deleteMarker:r}),this.diff=g({deleteMarker:r}),this.coreState=new f(e,this.updateBus,this.diff),this.middlewareEngine=new y(this.eventBus,this.executionState,this.merge),this.persistenceHandler=new w(this.eventBus,this.coreState,this.instanceID),this.transactionManager=new v(this.eventBus,this.coreState,this.executionState),this.metricsCollector=new b(this.eventBus),this.persistenceHandler.initialize(t)}isReady(){return this.persistenceHandler.isReady()}state(){return this.executionState}get(e){return this.coreState.get(e)}async set(e){if(this.isUpdating)return this.pendingUpdates.push(e),void(this.executionState.pendingChanges=[...this.pendingUpdates]);this.isUpdating=!0,this.executionState.executing=!0;const t=performance.now();this.eventBus.emit({name:"update:start",payload:{timestamp:t}});try{let r=e;if("function"==typeof e){const t=e(this.get(!0));r=t instanceof Promise?await t:t}const s=await this.middlewareEngine.executeBlocking(this.get(),r);if(s.blocked)return void this.eventBus.emit({name:"update:complete",payload:{blocked:!0,error:s.error,timestamp:Date.now()}});const a=this.merge(this.get(!0),r),i=await this.middlewareEngine.executeTransform(a,r),n=this.merge(this.get(!0),i),o=this.coreState.applyChanges(n);await this.persistenceHandler.handleStateChange(o,this.get());const c=performance.now();this.eventBus.emit({name:"update:complete",payload:{changedPaths:o,duration:c-t,timestamp:Date.now()}})}catch(e){throw this.eventBus.emit({name:"update:complete",payload:{blocked:!0,error:e,timestamp:Date.now()}}),e}finally{if(this.isUpdating=!1,this.executionState.executing=!1,this.executionState.changes=null,this.executionState.runningMiddleware=null,this.pendingUpdates.length>0){const e=this.pendingUpdates.shift();if(this.executionState.pendingChanges=[...this.pendingUpdates],e)return this.set(e)}}}subscribe(e,t){const r=Array.isArray(e)?e:[e];return this.updateBus.subscribe("update",(s=>{(r.includes(s)||""===e)&&(t(this.get(!0)),this.metricsCollector.listenerExecutions++)}))}id(){return this.instanceID}async transaction(e){return this.transactionManager.execute(e)}use(e){return e.block?this.middlewareEngine.addBlockingMiddleware(e.action,e.name):this.middlewareEngine.addMiddleware(e.action,e.name)}unuse(e){return this.middlewareEngine.removeMiddleware(e)}metrics(){return this.metricsCollector.getMetrics()}onStoreEvent(e,t){return this.eventBus.subscribe(e,t)}},exports.StoreObserver=class{store;eventHistory=[];maxEvents;enableConsoleLogging;logEvents;performanceThresholds;unsubscribers=[];stateHistory=[];maxStateHistory=20;activeTransactionCount=0;activeBatches=new Set;middlewareExecutions=[];constructor(e,t={}){this.store=e,this.maxEvents=t.maxEvents??500,this.maxStateHistory=t.maxStateHistory??20,this.enableConsoleLogging=t.enableConsoleLogging??!1,this.logEvents={updates:t.logEvents?.updates??!0,middleware:t.logEvents?.middleware??!0,transactions:t.logEvents?.transactions??!0},this.performanceThresholds={updateTime:t.performanceThresholds?.updateTime??50,middlewareTime:t.performanceThresholds?.middlewareTime??20},this.recordStateSnapshot(),this.setupEventListeners()}setupEventListeners(){const e=["update:start","update:complete","middleware:start","middleware:complete","middleware:error","middleware:blocked","transaction:start","transaction:complete","transaction:error","middleware:executed"];this.unsubscribers.push(this.store.subscribe("",(()=>{this.recordStateSnapshot()})));for(const t of e){const e=t.startsWith("update")&&this.logEvents.updates||t.startsWith("middleware")&&this.logEvents.middleware||t.startsWith("transaction")&&this.logEvents.transactions;this.unsubscribers.push(this.store.onStoreEvent(t,(r=>{"middleware:executed"===t?this.middlewareExecutions.push(r):"transaction:start"===t?this.activeTransactionCount++:"transaction:complete"!==t&&"transaction:error"!==t||(this.activeTransactionCount=Math.max(0,this.activeTransactionCount-1)),r.batchId&&(t.endsWith("start")?this.activeBatches.add(r.batchId):(t.endsWith("complete")||t.endsWith("error"))&&this.activeBatches.delete(r.batchId)),this.recordEvent(t,r),this.enableConsoleLogging&&e&&this.logEventToConsole(t,r),this.checkPerformance(t,r)})))}}recordStateSnapshot(){const e=structuredClone(this.store.get());this.stateHistory.length>0&&0===S(e,this.stateHistory[0]).length||(this.stateHistory.unshift(e),this.stateHistory.length>this.maxStateHistory&&this.stateHistory.pop())}recordEvent(e,t){const r={type:e,timestamp:Date.now(),data:structuredClone(t)};this.eventHistory.unshift(r),this.eventHistory.length>this.maxEvents&&this.eventHistory.pop()}logEventToConsole(e,t){const r=new Date(t.timestamp||Date.now()).toISOString().split("T")[1].replace("Z","");if("update:start"===e)console.group(`%c⚡ Store Update Started [${r}]`,"color: #4a6da7");else if("update:complete"===e){if(t.blocked)console.warn(`%c✋ Update Blocked [${r}]`,"color: #bf8c0a",t.error);else{const e=t.changedPaths||[];e.length>0&&console.log(`%c✅ Update Complete [${r}] - ${e.length} paths changed in ${t.duration?.toFixed(2)}ms`,"color: #2a9d8f",e)}console.groupEnd()}else"middleware:start"===e?console.debug(`%c◀ Middleware "${t.name}" started [${r}] (${t.type})`,"color: #8c8c8c"):"middleware:complete"===e?console.debug(`%c▶ Middleware "${t.name}" completed [${r}] in ${t.duration?.toFixed(2)}ms`,"color: #7c9c7c"):"middleware:error"===e?console.error(`%c❌ Middleware "${t.name}" error [${r}]:`,"color: #e63946",t.error):"middleware:blocked"===e?console.warn(`%c🛑 Middleware "${t.name}" blocked update [${r}]`,"color: #e76f51"):"transaction:start"===e?console.group(`%c📦 Transaction Started [${r}]`,"color: #6d597a"):"transaction:complete"===e?(console.log(`%c📦 Transaction Complete [${r}]`,"color: #355070"),console.groupEnd()):"transaction:error"===e&&(console.error(`%c📦 Transaction Error [${r}]:`,"color: #e56b6f",t.error),console.groupEnd())}checkPerformance(e,t){this.enableConsoleLogging&&("update:complete"===e&&!t.blocked&&t.duration>this.performanceThresholds.updateTime&&console.warn(`%c⚠️ Slow update detected [${t.duration.toFixed(2)}ms]`,"color: #ff9f1c",{changedPaths:t.changedPaths,threshold:this.performanceThresholds.updateTime}),"middleware:complete"===e&&t.duration>this.performanceThresholds.middlewareTime&&console.warn(`%c⚠️ Slow middleware "${t.name}" [${t.duration.toFixed(2)}ms]`,"color: #ff9f1c",{threshold:this.performanceThresholds.middlewareTime}))}getEventHistory(){return structuredClone(this.eventHistory)}getStateHistory(){return structuredClone(this.stateHistory)}getMiddlewareExecutions(){return this.middlewareExecutions}getPerformanceMetrics(){return this.store.metrics()}getTransactionStatus(){return{activeTransactions:this.activeTransactionCount,activeBatches:Array.from(this.activeBatches)}}createLoggingMiddleware(e={}){const{logLevel:t="debug",logUpdates:r=!0}=e;return(e,s)=>{if(r){(console[t]||console.log)("State Update:",s)}return s}}createValidationMiddleware(e){return(t,r)=>{const s=e(t,r);return"boolean"==typeof s?s:(!s.valid&&s.reason&&console.warn("Validation failed:",s.reason),s.valid)}}clearHistory(){if(this.eventHistory=[],this.stateHistory.length>0){const e=this.stateHistory[0];this.stateHistory=[e]}}getRecentChanges(e=5){const t=[],r=Math.min(e,this.stateHistory.length-1);for(let e=0;e<r;e++){const r=this.stateHistory[e],s=this.stateHistory[e+1],a=S(s,r),i={},n={};for(const e of a){const t=e.split("."),a=(e,t)=>t.reduce(((e,t)=>e&&void 0!==e[t]?e[t]:void 0),e),o=(e,t,r)=>{const s=t.length-1,a=t[s];t.slice(0,s).reduce(((e,t)=>(e[t]=e[t]??{},e[t])),e)[a]=r},c=a(s,t),d=a(r,t);o(i,t,c),o(n,t,d)}let o=Date.now();for(const e of this.eventHistory)if("update:complete"===e.type&&e.data.changedPaths?.length>0){o=e.timestamp;break}0!==a.length&&t.push({timestamp:o,changedPaths:a,from:i,to:n})}return t}createTimeTravel(){let e=0,t=[];return{canUndo:()=>e<this.stateHistory.length-1,canRedo:()=>t.length>0,undo:async()=>{if(e<this.stateHistory.length-1){const r=e+1,s=this.stateHistory[r];t.unshift(this.stateHistory[e]),e=r,await this.store.set(s)}},redo:async()=>{if(t.length>0){const r=t.shift();e=Math.max(0,e-1),await this.store.set(r)}},getHistoryLength:()=>this.stateHistory.length,clear:()=>{t=[],e=0}}}disconnect(){this.unsubscribers.forEach((e=>e())),this.unsubscribers=[],this.clearHistory()}},exports.author="https://github.com/asaidimu";
|
|
1
|
+
"use strict";var e,t,s=require("uuid"),r=Object.create,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,o=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,d=(e,t,s)=>(s=null!=e?r(o(e)):{},((e,t,s,r)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let o of n(t))c.call(e,o)||o===s||i(e,o,{get:()=>t[o],enumerable:!(r=a(t,o))||r.enumerable});return e})(e&&e.__esModule?s:i(s,"default",{value:e,enumerable:!0}),e)),h=(e={"node_modules/@asaidimu/events/index.js"(e,t){var s,r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,n=Object.prototype.hasOwnProperty,o={};((e,t)=>{for(var s in t)r(e,s,{get:t[s],enumerable:!0})})(o,{createEventBus:()=>c}),t.exports=(s=o,((e,t,s,o)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of a(t))n.call(e,c)||c===s||r(e,c,{get:()=>t[c],enumerable:!(o=i(t,c))||o.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,i=0;const a=new Map,n=new Map;let o=null;e.crossTab&&"undefined"!=typeof BroadcastChannel?o=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++,i+=t,a.set(e,(a.get(e)||0)+1)},d=()=>{const t=s;s=[],t.forEach((({name:t,payload:s})=>{const r=performance.now();try{(n.get(t)||[]).forEach((e=>e(s)))}catch(r){e.errorHandler({...r,eventName:t,payload:s})}c(t,performance.now()-r)}))},h=(()=>{let t;return()=>{clearTimeout(t),t=setTimeout(d,e.batchDelay)}})(),l=e=>{const s=t.get(e);s?n.set(e,Array.from(s)):n.delete(e)};return o&&(o.onmessage=e=>{const{name:t,payload:s}=e.data;(n.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),n.delete(e)):l(e)}},emit:({name:t,payload:r})=>{if(e.async)return s.push({name:t,payload:r}),s.length>=e.batchSize?d():h(),void(o&&o.postMessage({name:t,payload:r}));const i=performance.now();try{(n.get(t)||[]).forEach((e=>e(r))),o&&o.postMessage({name:t,payload:r})}catch(s){e.errorHandler({...s,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(),n.clear(),s=[],r=0,i=0,a.clear(),o&&(o.close(),o=null)}}}}},function(){return t||(0,e[n(e)[0]])((t={exports:{}}).exports,t),t.exports}),l=d(h()),u=Symbol.for("delete"),p=e=>Array.isArray(e)?[...e]:{...e};function m(e){const t=e?.deleteMarker||u;return function(e,s){if("object"!=typeof e||null===e)return"object"==typeof s&&null!==s?a(s):s===t?{}:s;if("object"!=typeof s||null===s)return e;const r=p(e),i=[{target:r,source:s}];for(;i.length>0;){const{target:e,source:s}=i.pop();for(const r of Object.keys(s)){if(!Object.prototype.hasOwnProperty.call(s,r))continue;const n=s[r];n!==t?Array.isArray(n)?e[r]=n.map((e=>"object"!=typeof e||null===e||Array.isArray(e)?e===t?void 0:e:a(e))):"object"==typeof n&&null!==n?(e[r]=p(r in e&&"object"==typeof e[r]&&null!==e[r]?e[r]:{}),i.push({target:e[r],source:n})):e[r]=n:delete e[r]}}return r;function a(e){if(null==e)return e;if(Array.isArray(e))return e.filter((e=>e!==t)).map((e=>"object"!=typeof e||null===e||Array.isArray(e)?e===t?void 0:e:a(e)));if("object"==typeof e){const s={};for(const[r,i]of Object.entries(e))if(i!==t)if("object"==typeof i&&null!==i){const e=a(i);void 0!==e&&(s[r]=e)}else s[r]=i;return s}return e===t?void 0:e}}}function g(e,t){if(e===t)return!0;if(e&&t&&"object"==typeof e&&"object"==typeof t){if(e.constructor!==t.constructor)return!1;let s,r;if(Array.isArray(e)){if(s=e.length,s!=t.length)return!1;for(r=s;r-- >0;)if(!g(e[r],t[r]))return!1;return!0}const i=Object.keys(e);if(s=i.length,s!==Object.keys(t).length)return!1;for(r=s;r-- >0;){const s=i[r];if(!Object.prototype.hasOwnProperty.call(t,s)||!g(e[s],t[s]))return!1}return!0}return e!=e&&t!=t}function f(e){const t=e?.deleteMarker||u;return function(e,s){const r=new Set,i=[{pathArray:[],pathStr:"",orig:e||{},part:s||{}}];for(;i.length>0;){const{pathArray:e,pathStr:s,orig:a,part:n}=i.pop();if(null!=n&&!g(a,n))if("object"!=typeof n||Array.isArray(n))s&&r.add(s);else for(const o of Object.keys(n)){const c=[...e,o],d=s?s+"."+o:o,h=n[o],l=a&&"object"==typeof a?a[o]:void 0;h!==t?"object"==typeof h&&null!==h?i.push({pathArray:c,pathStr:d,orig:l,part:h}):g(l,h)||r.add(d):void 0===l&&a&&"object"==typeof a||r.add(d)}}const a=new Set(r);for(const e of r){let t=e.lastIndexOf(".");for(;t>0;){const s=e.slice(0,t);if(a.has(s))break;a.add(s),t=e.lastIndexOf(".",t-1)}}return Array.from(a)}}m(),f();var y=class{constructor(e,t,s){this.updateBus=t,this.diff=s,this.cache=structuredClone(e)}cache;get(e){return e?structuredClone(this.cache):this.cache}applyChanges(e){const t=this.diff(this.get(!0),e);return t.length>0&&(this.cache=structuredClone(e),this.notifyListeners(t)),t}notifyListeners(e){e.forEach((e=>this.updateBus.emit({name:"update",payload:e})))}};d(h());var w=class{constructor(e,t,s){this.eventBus=e,this.executionState=t,this.merge=s}middleware=[];blockingMiddleware=[];async executeBlocking(e,t){for(const{fn:s,name:r,id:i}of this.blockingMiddleware){const a={id:i,name:r,startTime:performance.now()};this.executionState.runningMiddleware={id:i,name:r,startTime:performance.now()},this.emitMiddlewareLifecycle("start",{id:i,name:r,type:"blocking"});try{const n=await Promise.resolve(s(e,t));if(a.endTime=performance.now(),a.duration=a.endTime-a.startTime,!1===n)return a.blocked=!0,this.emitMiddlewareLifecycle("blocked",{id:i,name:r,duration:a.duration}),this.emit(this.eventBus,{name:"middleware:executed",payload:a}),{blocked:!0};this.emitMiddlewareLifecycle("complete",{id:i,name:r,type:"blocking",duration:a.duration}),this.emit(this.eventBus,{name:"middleware:executed",payload:{...a,blocked:!1}})}catch(e){return a.endTime=performance.now(),a.duration=a.endTime-a.startTime,a.error=e instanceof Error?e:new Error(String(e)),a.blocked=!0,this.emitMiddlewareError(i,r,a.error,a.duration),this.emit(this.eventBus,{name:"middleware:executed",payload:a}),{blocked:!0,error:a.error}}finally{this.executionState.runningMiddleware=null}}return{blocked:!1}}async executeTransform(e,t){let s=e,r=t;for(const{fn:e,name:i,id:a}of this.middleware){const n={id:a,name:i,startTime:performance.now()};this.executionState.runningMiddleware={id:a,name:i,startTime:performance.now()},this.emitMiddlewareLifecycle("start",{id:a,name:i,type:"transform"});try{const o=await Promise.resolve(e(s,t));n.endTime=performance.now(),n.duration=n.endTime-n.startTime,n.blocked=!1,o&&"object"==typeof o&&(s=this.merge(s,o),r=this.merge(r,o)),this.emit(this.eventBus,{name:"middleware:executed",payload:n}),this.emitMiddlewareLifecycle("complete",{id:a,name:i,type:"transform",duration:n.duration})}catch(e){n.endTime=performance.now(),n.duration=n.endTime-n.startTime,n.error=e instanceof Error?e:new Error(String(e)),n.blocked=!1,this.emit(this.eventBus,{name:"middleware:executed",payload:n}),this.emitMiddlewareError(a,i,n.error,n.duration),console.error(`Middleware ${i} error:`,e)}finally{this.executionState.runningMiddleware=null}}return r}addMiddleware(e,t="unnamed-middleware"){const s=this.generateId();return this.middleware.push({fn:e,name:t,id:s}),this.updateExecutionState(),s}addBlockingMiddleware(e,t="unnamed-blocking-middleware"){const s=this.generateId();return this.blockingMiddleware.push({fn:e,name:t,id:s}),this.updateExecutionState(),s}removeMiddleware(e){const t=this.middleware.length+this.blockingMiddleware.length;return this.middleware=this.middleware.filter((t=>t.id!==e)),this.blockingMiddleware=this.blockingMiddleware.filter((t=>t.id!==e)),this.updateExecutionState(),this.middleware.length+this.blockingMiddleware.length<t}updateExecutionState(){this.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))]}emitMiddlewareLifecycle(e,t){this.emit(this.eventBus,{name:`middleware:${e}`,payload:{...t,timestamp:Date.now()}})}emitMiddlewareError(e,t,s,r){this.emit(this.eventBus,{name:"middleware:error",payload:{id:e,name:t,error:s,duration:r,timestamp:Date.now()}})}generateId(){return crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,15)}`}emit(e,t){queueMicrotask((()=>{e.emit(t)}))}};d(h());var b=class{constructor(e,t,s,r){this.eventBus=e,this.coreState=t,this.instanceID=s,this.maxRetries=r?.maxRetries??3,this.retryDelay=r?.retryDelay??1e3}persistence;instanceID;persistenceReady=!1;backgroundQueue=[];isProcessingQueue=!1;maxRetries=3;retryDelay=1e3;queueProcessor;async initialize(e){e?await this.setPersistence(e):this.setPersistenceReady()}isReady(){return this.persistenceReady}handleStateChange(e,t){if(!this.persistence||0===e.length)return;const s={id:`${Date.now()}-${Math.random().toString(36).substr(2,9)}`,state:structuredClone(t),changedPaths:[...e],timestamp:Date.now(),retries:0};this.backgroundQueue.push(s),this.scheduleQueueProcessing(),this.emit(this.eventBus,{name:"persistence:queued",payload:{taskId:s.id,changedPaths:e,queueSize:this.backgroundQueue.length,timestamp:s.timestamp}})}getQueueStatus(){return{queueSize:this.backgroundQueue.length,isProcessing:this.isProcessingQueue,oldestTask:this.backgroundQueue[0]?.timestamp}}async flush(){this.isProcessingQueue||await this.processQueue()}clearQueue(){const e=this.backgroundQueue.length;this.backgroundQueue=[],this.queueProcessor&&(clearTimeout(this.queueProcessor),this.queueProcessor=void 0),this.emit(this.eventBus,{name:"persistence:queue_cleared",payload:{clearedTasks:e,timestamp:Date.now()}})}scheduleQueueProcessing(){this.queueProcessor||this.isProcessingQueue||(this.queueProcessor=setTimeout((()=>{this.processQueue().catch((e=>{console.error("Queue processing failed:",e)}))}),10))}async processQueue(){if(!this.isProcessingQueue&&0!==this.backgroundQueue.length){this.isProcessingQueue=!0,this.queueProcessor=void 0;try{for(;this.backgroundQueue.length>0;){const e=this.backgroundQueue.shift();await this.processTask(e)}}finally{this.isProcessingQueue=!1}}}async processTask(e){try{await this.persistence.set(this.instanceID,e.state)?this.emit(this.eventBus,{name:"persistence:success",payload:{taskId:e.id,changedPaths:e.changedPaths,duration:Date.now()-e.timestamp,timestamp:Date.now()}}):await this.handleTaskFailure(e,new Error("Persistence returned false"))}catch(t){await this.handleTaskFailure(e,t)}}async handleTaskFailure(e,t){if(e.retries++,e.retries<=this.maxRetries){const s=this.retryDelay*Math.pow(2,e.retries-1);this.emit(this.eventBus,{name:"persistence:retry",payload:{taskId:e.id,attempt:e.retries,maxRetries:this.maxRetries,nextRetryIn:s,error:t,timestamp:Date.now()}}),setTimeout((()=>{this.backgroundQueue.unshift(e),this.scheduleQueueProcessing()}),s)}else this.emit(this.eventBus,{name:"persistence:failed",payload:{taskId:e.id,changedPaths:e.changedPaths,attempts:e.retries,error:t,timestamp:Date.now()}})}setPersistenceReady(){this.persistenceReady=!0,this.emit(this.eventBus,{name:"persistence:ready",payload:{timestamp:Date.now()}})}async setPersistence(e){this.persistence=e;try{const e=await this.persistence.get();e&&this.coreState.applyChanges(e)}catch(e){console.error("Failed to initialize persistence:",e),this.emit(this.eventBus,{name:"persistence:init_error",payload:{error:e,timestamp:Date.now()}})}finally{this.setPersistenceReady()}this.persistence.subscribe&&this.persistence.subscribe(this.instanceID,(async e=>{const t=this.coreState.applyChanges(e);t.length>0&&this.emit(this.eventBus,{name:"update:complete",payload:{changedPaths:t,source:"external",timestamp:Date.now()}})}))}dispose(){this.clearQueue(),this.isProcessingQueue=!1,this.persistenceReady=!1}emit(e,t){queueMicrotask((()=>{e.emit(t)}))}};d(h());var v=class{constructor(e,t,s){this.eventBus=e,this.coreState=t,this.executionState=s}async execute(e){const t=this.coreState.get(!0);this.executionState.transactionActive=!0,this.emit(this.eventBus,{name:"transaction:start",payload:{timestamp:Date.now()}});try{const t=await Promise.resolve(e());return this.emit(this.eventBus,{name:"transaction:complete",payload:{timestamp:Date.now()}}),this.executionState.transactionActive=!1,t}catch(e){throw this.coreState.applyChanges(t),this.emit(this.eventBus,{name:"transaction:error",payload:{error:e instanceof Error?e:new Error(String(e)),timestamp:Date.now()}}),this.executionState.transactionActive=!1,e}}emit(e,t){queueMicrotask((()=>{e.emit(t)}))}};d(h());var x=class{updateCount=0;listenerExecutions=0;averageUpdateTime=0;largestUpdateSize=0;mostActiveListenerPaths=[];totalUpdates=0;blockedUpdates=0;averageUpdateDuration=0;middlewareExecutions=0;transactionCount=0;totalEventsFired=0;updateTimes=[];pathExecutionCounts=new Map;constructor(e){this.setupEventListeners(e)}getMetrics(){return{updateCount:this.updateCount,listenerExecutions:this.listenerExecutions,averageUpdateTime:this.averageUpdateTime,largestUpdateSize:this.largestUpdateSize,mostActiveListenerPaths:[...this.mostActiveListenerPaths],totalUpdates:this.totalUpdates,blockedUpdates:this.blockedUpdates,averageUpdateDuration:this.averageUpdateDuration,middlewareExecutions:this.middlewareExecutions,transactionCount:this.transactionCount,totalEventsFired:this.totalEventsFired}}setupEventListeners(e){const t=e.emit;e.emit=s=>(this.totalEventsFired++,t.call(e,s)),e.subscribe("update:complete",(e=>{if(this.totalUpdates++,e.blocked)this.blockedUpdates++;else{if(e.duration){this.updateTimes.push(e.duration),this.updateTimes.length>100&&this.updateTimes.shift();const t=this.updateTimes.reduce(((e,t)=>e+t),0)/this.updateTimes.length;this.averageUpdateTime=t,this.averageUpdateDuration=t}e.changedPaths?.length&&(this.updateCount++,this.largestUpdateSize=Math.max(this.largestUpdateSize,e.changedPaths.length),e.changedPaths.forEach((e=>{const t=this.pathExecutionCounts.get(e)||0;this.pathExecutionCounts.set(e,t+1)})),this.mostActiveListenerPaths=Array.from(this.pathExecutionCounts.entries()).sort((([,e],[,t])=>t-e)).slice(0,5).map((([e])=>e)))}})),e.subscribe("middleware:start",(()=>{this.middlewareExecutions++})),e.subscribe("transaction:start",(()=>{this.transactionCount++}))}reset(){this.updateCount=0,this.listenerExecutions=0,this.averageUpdateTime=0,this.largestUpdateSize=0,this.mostActiveListenerPaths=[],this.totalUpdates=0,this.blockedUpdates=0,this.averageUpdateDuration=0,this.middlewareExecutions=0,this.transactionCount=0,this.totalEventsFired=0,this.updateTimes=[],this.pathExecutionCounts.clear()}getDetailedMetrics(){return{pathExecutionCounts:new Map(this.pathExecutionCounts),recentUpdateTimes:[...this.updateTimes],successRate:this.totalUpdates>0?(this.totalUpdates-this.blockedUpdates)/this.totalUpdates:1,averagePathsPerUpdate:this.updateCount>0?Array.from(this.pathExecutionCounts.values()).reduce(((e,t)=>e+t),0)/this.updateCount:0}}dispose(){this.reset()}},E=f();exports.DELETE_SYMBOL=u,exports.ReactiveDataStore=class{coreState;middlewareEngine;persistenceHandler;transactionManager;metricsCollector;pendingUpdates=[];isUpdating=!1;updateBus=(0,l.createEventBus)();eventBus=(0,l.createEventBus)();executionState;instanceID=s.v4();merge;diff;constructor(e,t,s=u,r){this.executionState={executing:!1,changes:null,pendingChanges:[],middlewares:[],runningMiddleware:null,transactionActive:!1},this.merge=m({deleteMarker:s}),this.diff=f({deleteMarker:s}),this.coreState=new y(e,this.updateBus,this.diff),this.middlewareEngine=new w(this.eventBus,this.executionState,this.merge),this.persistenceHandler=new b(this.eventBus,this.coreState,this.instanceID,{maxRetries:r?.persistenceMaxRetries,retryDelay:r?.persistenceRetryDelay}),this.transactionManager=new v(this.eventBus,this.coreState,this.executionState),this.metricsCollector=new x(this.eventBus),this.persistenceHandler.initialize(t)}isReady(){return this.persistenceHandler.isReady()}state(){return this.executionState}get(e){return this.coreState.get(e)}async set(e){if(this.isUpdating)return this.pendingUpdates.push(e),void(this.executionState.pendingChanges=[...this.pendingUpdates]);this.isUpdating=!0,this.executionState.executing=!0;const t=performance.now();this.emit(this.eventBus,{name:"update:start",payload:{timestamp:t}});try{let s=e;if("function"==typeof e){const t=e(this.get(!0));s=t instanceof Promise?await t:t}const r=await this.middlewareEngine.executeBlocking(this.get(),s);if(r.blocked)return void this.emit(this.eventBus,{name:"update:complete",payload:{blocked:!0,error:r.error,timestamp:Date.now()}});const i=this.merge(this.get(!0),s),a=await this.middlewareEngine.executeTransform(i,s),n=this.merge(this.get(!0),a),o=this.coreState.applyChanges(n),c=performance.now();this.emit(this.eventBus,{name:"update:complete",payload:{changedPaths:o,duration:c-t,timestamp:Date.now()}}),o.length>0&&this.persistenceHandler.handleStateChange(o,this.get())}catch(e){throw this.emit(this.eventBus,{name:"update:complete",payload:{blocked:!0,error:e,timestamp:Date.now()}}),e}finally{if(this.isUpdating=!1,this.executionState.executing=!1,this.executionState.changes=null,this.executionState.runningMiddleware=null,this.pendingUpdates.length>0){const e=this.pendingUpdates.shift();if(this.executionState.pendingChanges=[...this.pendingUpdates],e)return this.set(e)}}}subscribe(e,t){const s=Array.isArray(e)?e:[e];return this.updateBus.subscribe("update",(r=>{(s.includes(r)||""===e)&&(t(this.get(!0)),this.metricsCollector.listenerExecutions++)}))}id(){return this.instanceID}async transaction(e){return this.transactionManager.execute(e)}use(e){return e.block?this.middlewareEngine.addBlockingMiddleware(e.action,e.name):this.middlewareEngine.addMiddleware(e.action,e.name)}unuse(e){return this.middlewareEngine.removeMiddleware(e)}metrics(){return this.metricsCollector.getMetrics()}onStoreEvent(e,t){return this.eventBus.subscribe(e,t)}getPersistenceStatus(){return this.persistenceHandler.getQueueStatus()}async flushPersistence(){return this.persistenceHandler.flush()}clearPersistenceQueue(){this.persistenceHandler.clearQueue()}dispose(){this.persistenceHandler.dispose(),this.metricsCollector.dispose?.()}emit(e,t){queueMicrotask((()=>{e.emit(t)}))}},exports.StoreObserver=class{store;eventHistory=[];maxEvents;enableConsoleLogging;logEvents;performanceThresholds;unsubscribers=[];stateHistory=[];maxStateHistory=20;activeTransactionCount=0;activeBatches=new Set;middlewareExecutions=[];constructor(e,t={}){this.store=e,this.maxEvents=t.maxEvents??500,this.maxStateHistory=t.maxStateHistory??20,this.enableConsoleLogging=t.enableConsoleLogging??!1,this.logEvents={updates:t.logEvents?.updates??!0,middleware:t.logEvents?.middleware??!0,transactions:t.logEvents?.transactions??!0},this.performanceThresholds={updateTime:t.performanceThresholds?.updateTime??50,middlewareTime:t.performanceThresholds?.middlewareTime??20},this.recordStateSnapshot(),this.setupEventListeners()}setupEventListeners(){const e=["update:start","update:complete","middleware:start","middleware:complete","middleware:error","middleware:blocked","transaction:start","transaction:complete","transaction:error","middleware:executed"];this.unsubscribers.push(this.store.subscribe("",(()=>{this.recordStateSnapshot()})));for(const t of e){const e=t.startsWith("update")&&this.logEvents.updates||t.startsWith("middleware")&&this.logEvents.middleware||t.startsWith("transaction")&&this.logEvents.transactions;this.unsubscribers.push(this.store.onStoreEvent(t,(s=>{"middleware:executed"===t?this.middlewareExecutions.push(s):"transaction:start"===t?this.activeTransactionCount++:"transaction:complete"!==t&&"transaction:error"!==t||(this.activeTransactionCount=Math.max(0,this.activeTransactionCount-1)),s.batchId&&(t.endsWith("start")?this.activeBatches.add(s.batchId):(t.endsWith("complete")||t.endsWith("error"))&&this.activeBatches.delete(s.batchId)),this.recordEvent(t,s),this.enableConsoleLogging&&e&&this.logEventToConsole(t,s),this.checkPerformance(t,s)})))}}recordStateSnapshot(){const e=structuredClone(this.store.get());this.stateHistory.length>0&&0===E(e,this.stateHistory[0]).length||(this.stateHistory.unshift(e),this.stateHistory.length>this.maxStateHistory&&this.stateHistory.pop())}recordEvent(e,t){const s={type:e,timestamp:Date.now(),data:structuredClone(t)};this.eventHistory.unshift(s),this.eventHistory.length>this.maxEvents&&this.eventHistory.pop()}logEventToConsole(e,t){const s=new Date(t.timestamp||Date.now()).toISOString().split("T")[1].replace("Z","");if("update:start"===e)console.group(`%c⚡ Store Update Started [${s}]`,"color: #4a6da7");else if("update:complete"===e){if(t.blocked)console.warn(`%c✋ Update Blocked [${s}]`,"color: #bf8c0a",t.error);else{const e=t.changedPaths||[];e.length>0&&console.log(`%c✅ Update Complete [${s}] - ${e.length} paths changed in ${t.duration?.toFixed(2)}ms`,"color: #2a9d8f",e)}console.groupEnd()}else"middleware:start"===e?console.debug(`%c◀ Middleware "${t.name}" started [${s}] (${t.type})`,"color: #8c8c8c"):"middleware:complete"===e?console.debug(`%c▶ Middleware "${t.name}" completed [${s}] in ${t.duration?.toFixed(2)}ms`,"color: #7c9c7c"):"middleware:error"===e?console.error(`%c❌ Middleware "${t.name}" error [${s}]:`,"color: #e63946",t.error):"middleware:blocked"===e?console.warn(`%c🛑 Middleware "${t.name}" blocked update [${s}]`,"color: #e76f51"):"transaction:start"===e?console.group(`%c📦 Transaction Started [${s}]`,"color: #6d597a"):"transaction:complete"===e?(console.log(`%c📦 Transaction Complete [${s}]`,"color: #355070"),console.groupEnd()):"transaction:error"===e&&(console.error(`%c📦 Transaction Error [${s}]:`,"color: #e56b6f",t.error),console.groupEnd())}checkPerformance(e,t){this.enableConsoleLogging&&("update:complete"===e&&!t.blocked&&t.duration>this.performanceThresholds.updateTime&&console.warn(`%c⚠️ Slow update detected [${t.duration.toFixed(2)}ms]`,"color: #ff9f1c",{changedPaths:t.changedPaths,threshold:this.performanceThresholds.updateTime}),"middleware:complete"===e&&t.duration>this.performanceThresholds.middlewareTime&&console.warn(`%c⚠️ Slow middleware "${t.name}" [${t.duration.toFixed(2)}ms]`,"color: #ff9f1c",{threshold:this.performanceThresholds.middlewareTime}))}getEventHistory(){return structuredClone(this.eventHistory)}getStateHistory(){return structuredClone(this.stateHistory)}getMiddlewareExecutions(){return this.middlewareExecutions}getPerformanceMetrics(){return this.store.metrics()}getTransactionStatus(){return{activeTransactions:this.activeTransactionCount,activeBatches:Array.from(this.activeBatches)}}createLoggingMiddleware(e={}){const{logLevel:t="debug",logUpdates:s=!0}=e;return(e,r)=>{if(s){(console[t]||console.log)("State Update:",r)}return r}}createValidationMiddleware(e){return(t,s)=>{const r=e(t,s);return"boolean"==typeof r?r:(!r.valid&&r.reason&&console.warn("Validation failed:",r.reason),r.valid)}}clearHistory(){if(this.eventHistory=[],this.stateHistory.length>0){const e=this.stateHistory[0];this.stateHistory=[e]}}getRecentChanges(e=5){const t=[],s=Math.min(e,this.stateHistory.length-1);for(let e=0;e<s;e++){const s=this.stateHistory[e],r=this.stateHistory[e+1],i=E(r,s),a={},n={};for(const e of i){const t=e.split("."),i=(e,t)=>t.reduce(((e,t)=>e&&void 0!==e[t]?e[t]:void 0),e),o=(e,t,s)=>{const r=t.length-1,i=t[r];t.slice(0,r).reduce(((e,t)=>(e[t]=e[t]??{},e[t])),e)[i]=s},c=i(r,t),d=i(s,t);o(a,t,c),o(n,t,d)}let o=Date.now();for(const e of this.eventHistory)if("update:complete"===e.type&&e.data.changedPaths?.length>0){o=e.timestamp;break}0!==i.length&&t.push({timestamp:o,changedPaths:i,from:a,to:n})}return t}createTimeTravel(){let e=0,t=[];return{canUndo:()=>e<this.stateHistory.length-1,canRedo:()=>t.length>0,undo:async()=>{if(e<this.stateHistory.length-1){const s=e+1,r=this.stateHistory[s];t.unshift(this.stateHistory[e]),e=s,await this.store.set(r)}},redo:async()=>{if(t.length>0){const s=t.shift();e=Math.max(0,e-1),await this.store.set(s)}},getHistoryLength:()=>this.stateHistory.length,clear:()=>{t=[],e=0}}}disconnect(){this.unsubscribers.forEach((e=>e())),this.unsubscribers=[],this.clearHistory()}},exports.author="https://github.com/asaidimu";
|
package/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{v4 as e}from"uuid";var t,r,s=Object.create,a=Object.defineProperty,n=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,o=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,d=(e,t,r)=>(r=null!=e?s(o(e)):{},((e,t,r,s)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let o of i(t))c.call(e,o)||o===r||a(e,o,{get:()=>t[o],enumerable:!(s=n(t,o))||s.enumerable});return e})(e&&e.__esModule?r:a(r,"default",{value:e,enumerable:!0}),e)),l=(t={"node_modules/@asaidimu/events/index.js"(e,t){var r,s=Object.defineProperty,a=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,o={};((e,t)=>{for(var r in t)s(e,r,{get:t[r],enumerable:!0})})(o,{createEventBus:()=>c}),t.exports=(r=o,((e,t,r,o)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of n(t))i.call(e,c)||c===r||s(e,c,{get:()=>t[c],enumerable:!(o=a(t,c))||o.enumerable});return e})(s({},"__esModule",{value:!0}),r));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 r=[],s=0,a=0;const n=new Map,i=new Map;let o=null;e.crossTab&&"undefined"!=typeof BroadcastChannel?o=new BroadcastChannel(e.channelName):e.crossTab&&console.warn("BroadcastChannel is not supported in this browser. Cross-tab notifications are disabled.");const c=(e,t)=>{s++,a+=t,n.set(e,(n.get(e)||0)+1)},d=()=>{const t=r;r=[],t.forEach((({name:t,payload:r})=>{const s=performance.now();try{(i.get(t)||[]).forEach((e=>e(r)))}catch(s){e.errorHandler({...s,eventName:t,payload:r})}c(t,performance.now()-s)}))},l=(()=>{let t;return()=>{clearTimeout(t),t=setTimeout(d,e.batchDelay)}})(),h=e=>{const r=t.get(e);r?i.set(e,Array.from(r)):i.delete(e)};return o&&(o.onmessage=e=>{const{name:t,payload:r}=e.data;(i.get(t)||[]).forEach((e=>e(r)))}),{subscribe:(e,r)=>{t.has(e)||t.set(e,new Set);const s=t.get(e);return s.add(r),h(e),()=>{s.delete(r),0===s.size?(t.delete(e),i.delete(e)):h(e)}},emit:({name:t,payload:s})=>{if(e.async)return r.push({name:t,payload:s}),r.length>=e.batchSize?d():l(),void(o&&o.postMessage({name:t,payload:s}));const a=performance.now();try{(i.get(t)||[]).forEach((e=>e(s))),o&&o.postMessage({name:t,payload:s})}catch(r){e.errorHandler({...r,eventName:t,payload:s})}c(t,performance.now()-a)},getMetrics:()=>({totalEvents:s,activeSubscriptions:Array.from(t.values()).reduce(((e,t)=>e+t.size),0),eventCounts:n,averageEmitDuration:s>0?a/s:0}),clear:()=>{t.clear(),i.clear(),r=[],s=0,a=0,n.clear(),o&&(o.close(),o=null)}}}}},function(){return r||(0,t[i(t)[0]])((r={exports:{}}).exports,r),r.exports}),h=d(l()),u=Symbol.for("delete"),m="https://github.com/asaidimu",p=e=>Array.isArray(e)?[...e]:{...e};function g(e){const t=e?.deleteMarker||u;return function(e,r){if("object"!=typeof e||null===e)return"object"==typeof r&&null!==r?n(r):r===t?{}:r;if("object"!=typeof r||null===r)return e;const s=p(e),a=[{target:s,source:r}];for(;a.length>0;){const{target:e,source:r}=a.pop();for(const s of Object.keys(r)){if(!Object.prototype.hasOwnProperty.call(r,s))continue;const i=r[s];i!==t?Array.isArray(i)?e[s]=i.map((e=>"object"!=typeof e||null===e||Array.isArray(e)?e===t?void 0:e:n(e))):"object"==typeof i&&null!==i?(e[s]=p(s in e&&"object"==typeof e[s]&&null!==e[s]?e[s]:{}),a.push({target:e[s],source:i})):e[s]=i:delete e[s]}}return s;function n(e){if(null==e)return e;if(Array.isArray(e))return e.filter((e=>e!==t)).map((e=>"object"!=typeof e||null===e||Array.isArray(e)?e===t?void 0:e:n(e)));if("object"==typeof e){const r={};for(const[s,a]of Object.entries(e))if(a!==t)if("object"==typeof a&&null!==a){const e=n(a);void 0!==e&&(r[s]=e)}else r[s]=a;return r}return e===t?void 0:e}}}function f(e){const t=e?.deleteMarker||u;return function(e,r){const s=new Set,a=e=>s.add(e);function n(e){if(!e)return;const t=e.split(".");a(e);for(let e=t.length-1;e>0;e--){const r=t.slice(0,e).join(".");if(s.has(r))break;a(r)}}const i=[{path:"",orig:e||{},part:r||{}}];for(;i.length>0;){const{path:e,orig:r,part:s}=i.pop();if(null!=s)if(Array.isArray(s))Array.isArray(r)&&JSON.stringify(r)===JSON.stringify(s)||n(e);else if("object"==typeof s){const a=Object.keys(s);for(let o=0;o<a.length;o++){const c=a[o],d=e?`${e}.${c}`:c,l=s[c];if(l===t||null==r){n(d);continue}const h=r[c];"object"==typeof l&&null!==l?i.push({path:d,orig:h,part:l}):h!==l&&n(d)}}}return Array.from(s)}}g(),f();var y=class{constructor(e,t,r){this.updateBus=t,this.diff=r,this.cache=structuredClone(e)}cache;get(e){return e?structuredClone(this.cache):this.cache}applyChanges(e){const t=this.diff(this.get(!0),e);return t.length>0&&(this.cache=structuredClone(e),this.notifyListeners(t)),t}notifyListeners(e){e.forEach((e=>this.updateBus.emit({name:"update",payload:e})))}};d(l());var w=class{constructor(e,t,r){this.eventBus=e,this.executionState=t,this.merge=r}middleware=[];blockingMiddleware=[];async executeBlocking(e,t){for(const{fn:r,name:s,id:a}of this.blockingMiddleware){const n={id:a,name:s,startTime:performance.now()};this.executionState.runningMiddleware={id:a,name:s,startTime:performance.now()},this.emitMiddlewareLifecycle("start",{id:a,name:s,type:"blocking"});try{const i=await Promise.resolve(r(e,t));if(n.endTime=performance.now(),n.duration=n.endTime-n.startTime,!1===i)return n.blocked=!0,this.emitMiddlewareLifecycle("blocked",{id:a,name:s,duration:n.duration}),this.eventBus.emit({name:"middleware:executed",payload:n}),{blocked:!0};this.emitMiddlewareLifecycle("complete",{id:a,name:s,type:"blocking",duration:n.duration}),this.eventBus.emit({name:"middleware:executed",payload:{...n,blocked:!1}})}catch(e){return n.endTime=performance.now(),n.duration=n.endTime-n.startTime,n.error=e instanceof Error?e:new Error(String(e)),n.blocked=!0,this.emitMiddlewareError(a,s,n.error,n.duration),this.eventBus.emit({name:"middleware:executed",payload:n}),{blocked:!0,error:n.error}}finally{this.executionState.runningMiddleware=null}}return{blocked:!1}}async executeTransform(e,t){let r=e,s=t;for(const{fn:e,name:a,id:n}of this.middleware){const i={id:n,name:a,startTime:performance.now()};this.executionState.runningMiddleware={id:n,name:a,startTime:performance.now()},this.emitMiddlewareLifecycle("start",{id:n,name:a,type:"transform"});try{const o=await Promise.resolve(e(r,t));i.endTime=performance.now(),i.duration=i.endTime-i.startTime,i.blocked=!1,o&&"object"==typeof o&&(r=this.merge(r,o),s=this.merge(s,o)),this.eventBus.emit({name:"middleware:executed",payload:i}),this.emitMiddlewareLifecycle("complete",{id:n,name:a,type:"transform",duration:i.duration})}catch(e){i.endTime=performance.now(),i.duration=i.endTime-i.startTime,i.error=e instanceof Error?e:new Error(String(e)),i.blocked=!1,this.eventBus.emit({name:"middleware:executed",payload:i}),this.emitMiddlewareError(n,a,i.error,i.duration),console.error(`Middleware ${a} error:`,e)}finally{this.executionState.runningMiddleware=null}}return s}addMiddleware(e,t="unnamed-middleware"){const r=this.generateId();return this.middleware.push({fn:e,name:t,id:r}),this.updateExecutionState(),r}addBlockingMiddleware(e,t="unnamed-blocking-middleware"){const r=this.generateId();return this.blockingMiddleware.push({fn:e,name:t,id:r}),this.updateExecutionState(),r}removeMiddleware(e){const t=this.middleware.length+this.blockingMiddleware.length;return this.middleware=this.middleware.filter((t=>t.id!==e)),this.blockingMiddleware=this.blockingMiddleware.filter((t=>t.id!==e)),this.updateExecutionState(),this.middleware.length+this.blockingMiddleware.length<t}updateExecutionState(){this.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))]}emitMiddlewareLifecycle(e,t){this.eventBus.emit({name:`middleware:${e}`,payload:{...t,timestamp:Date.now()}})}emitMiddlewareError(e,t,r,s){this.eventBus.emit({name:"middleware:error",payload:{id:e,name:t,error:r,duration:s,timestamp:Date.now()}})}generateId(){return crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,15)}`}};d(l());var v=class{constructor(e,t,r){this.eventBus=e,this.coreState=t,this.instanceID=r}persistence;instanceID;persistenceReady=!1;async initialize(e){e?await this.setPersistence(e):this.setPersistenceReady()}isReady(){return this.persistenceReady}async handleStateChange(e,t){if(this.persistence&&0!==e.length)try{await this.persistence.set(this.instanceID,t)||this.eventBus.emit({name:"update:complete",payload:{persistence:!1,timestamp:Date.now()}})}catch(e){this.eventBus.emit({name:"update:complete",payload:{persistence:!1,error:e,timestamp:Date.now()}})}}setPersistenceReady(){this.persistenceReady=!0,this.eventBus.emit({name:"persistence:ready",payload:{timestamp:Date.now()}})}async setPersistence(e){this.persistence=e;try{const e=await this.persistence.get();e&&this.coreState.applyChanges(e)}catch(e){console.error("Failed to initialize persistence:",e)}finally{this.setPersistenceReady()}this.persistence.subscribe(this.instanceID,(async e=>{const t=this.coreState.applyChanges(e);t.length>0&&this.eventBus.emit({name:"update:complete",payload:{changedPaths:t,source:"external",timestamp:Date.now()}})}))}};d(l());var b=class{constructor(e,t,r){this.eventBus=e,this.coreState=t,this.executionState=r}async execute(e){const t=this.coreState.get(!0);this.executionState.transactionActive=!0,this.eventBus.emit({name:"transaction:start",payload:{timestamp:Date.now()}});try{const t=await Promise.resolve(e());return this.eventBus.emit({name:"transaction:complete",payload:{timestamp:Date.now()}}),this.executionState.transactionActive=!1,t}catch(e){throw this.coreState.applyChanges(t),this.eventBus.emit({name:"transaction:error",payload:{error:e instanceof Error?e:new Error(String(e)),timestamp:Date.now()}}),this.executionState.transactionActive=!1,e}}};d(l());var S=class{updateCount=0;listenerExecutions=0;averageUpdateTime=0;largestUpdateSize=0;mostActiveListenerPaths=[];updateTimes=[];constructor(e){this.setupEventListeners(e)}getMetrics(){return{updateCount:this.updateCount,listenerExecutions:this.listenerExecutions,averageUpdateTime:this.averageUpdateTime,largestUpdateSize:this.largestUpdateSize,mostActiveListenerPaths:[...this.mostActiveListenerPaths]}}setupEventListeners(e){e.subscribe("update:complete",(e=>{e.duration&&(this.updateTimes.push(e.duration),this.updateTimes.length>100&&this.updateTimes.shift(),this.averageUpdateTime=this.updateTimes.reduce(((e,t)=>e+t),0)/this.updateTimes.length),e.changedPaths?.length&&(this.updateCount++,this.largestUpdateSize=Math.max(this.largestUpdateSize,e.changedPaths.length))}))}},x=class{coreState;middlewareEngine;persistenceHandler;transactionManager;metricsCollector;pendingUpdates=[];isUpdating=!1;updateBus=(0,h.createEventBus)();eventBus=(0,h.createEventBus)();executionState;instanceID=e();merge;diff;constructor(e,t,r=u){this.executionState={executing:!1,changes:null,pendingChanges:[],middlewares:[],runningMiddleware:null,transactionActive:!1},this.merge=g({deleteMarker:r}),this.diff=f({deleteMarker:r}),this.coreState=new y(e,this.updateBus,this.diff),this.middlewareEngine=new w(this.eventBus,this.executionState,this.merge),this.persistenceHandler=new v(this.eventBus,this.coreState,this.instanceID),this.transactionManager=new b(this.eventBus,this.coreState,this.executionState),this.metricsCollector=new S(this.eventBus),this.persistenceHandler.initialize(t)}isReady(){return this.persistenceHandler.isReady()}state(){return this.executionState}get(e){return this.coreState.get(e)}async set(e){if(this.isUpdating)return this.pendingUpdates.push(e),void(this.executionState.pendingChanges=[...this.pendingUpdates]);this.isUpdating=!0,this.executionState.executing=!0;const t=performance.now();this.eventBus.emit({name:"update:start",payload:{timestamp:t}});try{let r=e;if("function"==typeof e){const t=e(this.get(!0));r=t instanceof Promise?await t:t}const s=await this.middlewareEngine.executeBlocking(this.get(),r);if(s.blocked)return void this.eventBus.emit({name:"update:complete",payload:{blocked:!0,error:s.error,timestamp:Date.now()}});const a=this.merge(this.get(!0),r),n=await this.middlewareEngine.executeTransform(a,r),i=this.merge(this.get(!0),n),o=this.coreState.applyChanges(i);await this.persistenceHandler.handleStateChange(o,this.get());const c=performance.now();this.eventBus.emit({name:"update:complete",payload:{changedPaths:o,duration:c-t,timestamp:Date.now()}})}catch(e){throw this.eventBus.emit({name:"update:complete",payload:{blocked:!0,error:e,timestamp:Date.now()}}),e}finally{if(this.isUpdating=!1,this.executionState.executing=!1,this.executionState.changes=null,this.executionState.runningMiddleware=null,this.pendingUpdates.length>0){const e=this.pendingUpdates.shift();if(this.executionState.pendingChanges=[...this.pendingUpdates],e)return this.set(e)}}}subscribe(e,t){const r=Array.isArray(e)?e:[e];return this.updateBus.subscribe("update",(s=>{(r.includes(s)||""===e)&&(t(this.get(!0)),this.metricsCollector.listenerExecutions++)}))}id(){return this.instanceID}async transaction(e){return this.transactionManager.execute(e)}use(e){return e.block?this.middlewareEngine.addBlockingMiddleware(e.action,e.name):this.middlewareEngine.addMiddleware(e.action,e.name)}unuse(e){return this.middlewareEngine.removeMiddleware(e)}metrics(){return this.metricsCollector.getMetrics()}onStoreEvent(e,t){return this.eventBus.subscribe(e,t)}},E=f(),T=class{store;eventHistory=[];maxEvents;enableConsoleLogging;logEvents;performanceThresholds;unsubscribers=[];stateHistory=[];maxStateHistory=20;activeTransactionCount=0;activeBatches=new Set;middlewareExecutions=[];constructor(e,t={}){this.store=e,this.maxEvents=t.maxEvents??500,this.maxStateHistory=t.maxStateHistory??20,this.enableConsoleLogging=t.enableConsoleLogging??!1,this.logEvents={updates:t.logEvents?.updates??!0,middleware:t.logEvents?.middleware??!0,transactions:t.logEvents?.transactions??!0},this.performanceThresholds={updateTime:t.performanceThresholds?.updateTime??50,middlewareTime:t.performanceThresholds?.middlewareTime??20},this.recordStateSnapshot(),this.setupEventListeners()}setupEventListeners(){const e=["update:start","update:complete","middleware:start","middleware:complete","middleware:error","middleware:blocked","transaction:start","transaction:complete","transaction:error","middleware:executed"];this.unsubscribers.push(this.store.subscribe("",(()=>{this.recordStateSnapshot()})));for(const t of e){const e=t.startsWith("update")&&this.logEvents.updates||t.startsWith("middleware")&&this.logEvents.middleware||t.startsWith("transaction")&&this.logEvents.transactions;this.unsubscribers.push(this.store.onStoreEvent(t,(r=>{"middleware:executed"===t?this.middlewareExecutions.push(r):"transaction:start"===t?this.activeTransactionCount++:"transaction:complete"!==t&&"transaction:error"!==t||(this.activeTransactionCount=Math.max(0,this.activeTransactionCount-1)),r.batchId&&(t.endsWith("start")?this.activeBatches.add(r.batchId):(t.endsWith("complete")||t.endsWith("error"))&&this.activeBatches.delete(r.batchId)),this.recordEvent(t,r),this.enableConsoleLogging&&e&&this.logEventToConsole(t,r),this.checkPerformance(t,r)})))}}recordStateSnapshot(){const e=structuredClone(this.store.get());this.stateHistory.length>0&&0===E(e,this.stateHistory[0]).length||(this.stateHistory.unshift(e),this.stateHistory.length>this.maxStateHistory&&this.stateHistory.pop())}recordEvent(e,t){const r={type:e,timestamp:Date.now(),data:structuredClone(t)};this.eventHistory.unshift(r),this.eventHistory.length>this.maxEvents&&this.eventHistory.pop()}logEventToConsole(e,t){const r=new Date(t.timestamp||Date.now()).toISOString().split("T")[1].replace("Z","");if("update:start"===e)console.group(`%c⚡ Store Update Started [${r}]`,"color: #4a6da7");else if("update:complete"===e){if(t.blocked)console.warn(`%c✋ Update Blocked [${r}]`,"color: #bf8c0a",t.error);else{const e=t.changedPaths||[];e.length>0&&console.log(`%c✅ Update Complete [${r}] - ${e.length} paths changed in ${t.duration?.toFixed(2)}ms`,"color: #2a9d8f",e)}console.groupEnd()}else"middleware:start"===e?console.debug(`%c◀ Middleware "${t.name}" started [${r}] (${t.type})`,"color: #8c8c8c"):"middleware:complete"===e?console.debug(`%c▶ Middleware "${t.name}" completed [${r}] in ${t.duration?.toFixed(2)}ms`,"color: #7c9c7c"):"middleware:error"===e?console.error(`%c❌ Middleware "${t.name}" error [${r}]:`,"color: #e63946",t.error):"middleware:blocked"===e?console.warn(`%c🛑 Middleware "${t.name}" blocked update [${r}]`,"color: #e76f51"):"transaction:start"===e?console.group(`%c📦 Transaction Started [${r}]`,"color: #6d597a"):"transaction:complete"===e?(console.log(`%c📦 Transaction Complete [${r}]`,"color: #355070"),console.groupEnd()):"transaction:error"===e&&(console.error(`%c📦 Transaction Error [${r}]:`,"color: #e56b6f",t.error),console.groupEnd())}checkPerformance(e,t){this.enableConsoleLogging&&("update:complete"===e&&!t.blocked&&t.duration>this.performanceThresholds.updateTime&&console.warn(`%c⚠️ Slow update detected [${t.duration.toFixed(2)}ms]`,"color: #ff9f1c",{changedPaths:t.changedPaths,threshold:this.performanceThresholds.updateTime}),"middleware:complete"===e&&t.duration>this.performanceThresholds.middlewareTime&&console.warn(`%c⚠️ Slow middleware "${t.name}" [${t.duration.toFixed(2)}ms]`,"color: #ff9f1c",{threshold:this.performanceThresholds.middlewareTime}))}getEventHistory(){return structuredClone(this.eventHistory)}getStateHistory(){return structuredClone(this.stateHistory)}getMiddlewareExecutions(){return this.middlewareExecutions}getPerformanceMetrics(){return this.store.metrics()}getTransactionStatus(){return{activeTransactions:this.activeTransactionCount,activeBatches:Array.from(this.activeBatches)}}createLoggingMiddleware(e={}){const{logLevel:t="debug",logUpdates:r=!0}=e;return(e,s)=>{if(r){(console[t]||console.log)("State Update:",s)}return s}}createValidationMiddleware(e){return(t,r)=>{const s=e(t,r);return"boolean"==typeof s?s:(!s.valid&&s.reason&&console.warn("Validation failed:",s.reason),s.valid)}}clearHistory(){if(this.eventHistory=[],this.stateHistory.length>0){const e=this.stateHistory[0];this.stateHistory=[e]}}getRecentChanges(e=5){const t=[],r=Math.min(e,this.stateHistory.length-1);for(let e=0;e<r;e++){const r=this.stateHistory[e],s=this.stateHistory[e+1],a=E(s,r),n={},i={};for(const e of a){const t=e.split("."),a=(e,t)=>t.reduce(((e,t)=>e&&void 0!==e[t]?e[t]:void 0),e),o=(e,t,r)=>{const s=t.length-1,a=t[s];t.slice(0,s).reduce(((e,t)=>(e[t]=e[t]??{},e[t])),e)[a]=r},c=a(s,t),d=a(r,t);o(n,t,c),o(i,t,d)}let o=Date.now();for(const e of this.eventHistory)if("update:complete"===e.type&&e.data.changedPaths?.length>0){o=e.timestamp;break}0!==a.length&&t.push({timestamp:o,changedPaths:a,from:n,to:i})}return t}createTimeTravel(){let e=0,t=[];return{canUndo:()=>e<this.stateHistory.length-1,canRedo:()=>t.length>0,undo:async()=>{if(e<this.stateHistory.length-1){const r=e+1,s=this.stateHistory[r];t.unshift(this.stateHistory[e]),e=r,await this.store.set(s)}},redo:async()=>{if(t.length>0){const r=t.shift();e=Math.max(0,e-1),await this.store.set(r)}},getHistoryLength:()=>this.stateHistory.length,clear:()=>{t=[],e=0}}}disconnect(){this.unsubscribers.forEach((e=>e())),this.unsubscribers=[],this.clearHistory()}};export{u as DELETE_SYMBOL,x as ReactiveDataStore,T as StoreObserver,m as author};
|
|
1
|
+
import{v4 as e}from"uuid";var t,s,i=Object.create,r=Object.defineProperty,a=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,o=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,d=(e,t,s)=>(s=null!=e?i(o(e)):{},((e,t,s,i)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let o of n(t))c.call(e,o)||o===s||r(e,o,{get:()=>t[o],enumerable:!(i=a(t,o))||i.enumerable});return e})(e&&e.__esModule?s:r(s,"default",{value:e,enumerable:!0}),e)),h=(t={"node_modules/@asaidimu/events/index.js"(e,t){var s,i=Object.defineProperty,r=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,n=Object.prototype.hasOwnProperty,o={};((e,t)=>{for(var s in t)i(e,s,{get:t[s],enumerable:!0})})(o,{createEventBus:()=>c}),t.exports=(s=o,((e,t,s,o)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let c of a(t))n.call(e,c)||c===s||i(e,c,{get:()=>t[c],enumerable:!(o=r(t,c))||o.enumerable});return e})(i({},"__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=[],i=0,r=0;const a=new Map,n=new Map;let o=null;e.crossTab&&"undefined"!=typeof BroadcastChannel?o=new BroadcastChannel(e.channelName):e.crossTab&&console.warn("BroadcastChannel is not supported in this browser. Cross-tab notifications are disabled.");const c=(e,t)=>{i++,r+=t,a.set(e,(a.get(e)||0)+1)},d=()=>{const t=s;s=[],t.forEach((({name:t,payload:s})=>{const i=performance.now();try{(n.get(t)||[]).forEach((e=>e(s)))}catch(i){e.errorHandler({...i,eventName:t,payload:s})}c(t,performance.now()-i)}))},h=(()=>{let t;return()=>{clearTimeout(t),t=setTimeout(d,e.batchDelay)}})(),l=e=>{const s=t.get(e);s?n.set(e,Array.from(s)):n.delete(e)};return o&&(o.onmessage=e=>{const{name:t,payload:s}=e.data;(n.get(t)||[]).forEach((e=>e(s)))}),{subscribe:(e,s)=>{t.has(e)||t.set(e,new Set);const i=t.get(e);return i.add(s),l(e),()=>{i.delete(s),0===i.size?(t.delete(e),n.delete(e)):l(e)}},emit:({name:t,payload:i})=>{if(e.async)return s.push({name:t,payload:i}),s.length>=e.batchSize?d():h(),void(o&&o.postMessage({name:t,payload:i}));const r=performance.now();try{(n.get(t)||[]).forEach((e=>e(i))),o&&o.postMessage({name:t,payload:i})}catch(s){e.errorHandler({...s,eventName:t,payload:i})}c(t,performance.now()-r)},getMetrics:()=>({totalEvents:i,activeSubscriptions:Array.from(t.values()).reduce(((e,t)=>e+t.size),0),eventCounts:a,averageEmitDuration:i>0?r/i:0}),clear:()=>{t.clear(),n.clear(),s=[],i=0,r=0,a.clear(),o&&(o.close(),o=null)}}}}},function(){return s||(0,t[n(t)[0]])((s={exports:{}}).exports,s),s.exports}),l=d(h()),u=Symbol.for("delete"),p="https://github.com/asaidimu",m=e=>Array.isArray(e)?[...e]:{...e};function g(e){const t=e?.deleteMarker||u;return function(e,s){if("object"!=typeof e||null===e)return"object"==typeof s&&null!==s?a(s):s===t?{}:s;if("object"!=typeof s||null===s)return e;const i=m(e),r=[{target:i,source:s}];for(;r.length>0;){const{target:e,source:s}=r.pop();for(const i of Object.keys(s)){if(!Object.prototype.hasOwnProperty.call(s,i))continue;const n=s[i];n!==t?Array.isArray(n)?e[i]=n.map((e=>"object"!=typeof e||null===e||Array.isArray(e)?e===t?void 0:e:a(e))):"object"==typeof n&&null!==n?(e[i]=m(i in e&&"object"==typeof e[i]&&null!==e[i]?e[i]:{}),r.push({target:e[i],source:n})):e[i]=n:delete e[i]}}return i;function a(e){if(null==e)return e;if(Array.isArray(e))return e.filter((e=>e!==t)).map((e=>"object"!=typeof e||null===e||Array.isArray(e)?e===t?void 0:e:a(e)));if("object"==typeof e){const s={};for(const[i,r]of Object.entries(e))if(r!==t)if("object"==typeof r&&null!==r){const e=a(r);void 0!==e&&(s[i]=e)}else s[i]=r;return s}return e===t?void 0:e}}}function f(e,t){if(e===t)return!0;if(e&&t&&"object"==typeof e&&"object"==typeof t){if(e.constructor!==t.constructor)return!1;let s,i;if(Array.isArray(e)){if(s=e.length,s!=t.length)return!1;for(i=s;i-- >0;)if(!f(e[i],t[i]))return!1;return!0}const r=Object.keys(e);if(s=r.length,s!==Object.keys(t).length)return!1;for(i=s;i-- >0;){const s=r[i];if(!Object.prototype.hasOwnProperty.call(t,s)||!f(e[s],t[s]))return!1}return!0}return e!=e&&t!=t}function y(e){const t=e?.deleteMarker||u;return function(e,s){const i=new Set,r=[{pathArray:[],pathStr:"",orig:e||{},part:s||{}}];for(;r.length>0;){const{pathArray:e,pathStr:s,orig:a,part:n}=r.pop();if(null!=n&&!f(a,n))if("object"!=typeof n||Array.isArray(n))s&&i.add(s);else for(const o of Object.keys(n)){const c=[...e,o],d=s?s+"."+o:o,h=n[o],l=a&&"object"==typeof a?a[o]:void 0;h!==t?"object"==typeof h&&null!==h?r.push({pathArray:c,pathStr:d,orig:l,part:h}):f(l,h)||i.add(d):void 0===l&&a&&"object"==typeof a||i.add(d)}}const a=new Set(i);for(const e of i){let t=e.lastIndexOf(".");for(;t>0;){const s=e.slice(0,t);if(a.has(s))break;a.add(s),t=e.lastIndexOf(".",t-1)}}return Array.from(a)}}g(),y();var w=class{constructor(e,t,s){this.updateBus=t,this.diff=s,this.cache=structuredClone(e)}cache;get(e){return e?structuredClone(this.cache):this.cache}applyChanges(e){const t=this.diff(this.get(!0),e);return t.length>0&&(this.cache=structuredClone(e),this.notifyListeners(t)),t}notifyListeners(e){e.forEach((e=>this.updateBus.emit({name:"update",payload:e})))}};d(h());var b=class{constructor(e,t,s){this.eventBus=e,this.executionState=t,this.merge=s}middleware=[];blockingMiddleware=[];async executeBlocking(e,t){for(const{fn:s,name:i,id:r}of this.blockingMiddleware){const a={id:r,name:i,startTime:performance.now()};this.executionState.runningMiddleware={id:r,name:i,startTime:performance.now()},this.emitMiddlewareLifecycle("start",{id:r,name:i,type:"blocking"});try{const n=await Promise.resolve(s(e,t));if(a.endTime=performance.now(),a.duration=a.endTime-a.startTime,!1===n)return a.blocked=!0,this.emitMiddlewareLifecycle("blocked",{id:r,name:i,duration:a.duration}),this.emit(this.eventBus,{name:"middleware:executed",payload:a}),{blocked:!0};this.emitMiddlewareLifecycle("complete",{id:r,name:i,type:"blocking",duration:a.duration}),this.emit(this.eventBus,{name:"middleware:executed",payload:{...a,blocked:!1}})}catch(e){return a.endTime=performance.now(),a.duration=a.endTime-a.startTime,a.error=e instanceof Error?e:new Error(String(e)),a.blocked=!0,this.emitMiddlewareError(r,i,a.error,a.duration),this.emit(this.eventBus,{name:"middleware:executed",payload:a}),{blocked:!0,error:a.error}}finally{this.executionState.runningMiddleware=null}}return{blocked:!1}}async executeTransform(e,t){let s=e,i=t;for(const{fn:e,name:r,id:a}of this.middleware){const n={id:a,name:r,startTime:performance.now()};this.executionState.runningMiddleware={id:a,name:r,startTime:performance.now()},this.emitMiddlewareLifecycle("start",{id:a,name:r,type:"transform"});try{const o=await Promise.resolve(e(s,t));n.endTime=performance.now(),n.duration=n.endTime-n.startTime,n.blocked=!1,o&&"object"==typeof o&&(s=this.merge(s,o),i=this.merge(i,o)),this.emit(this.eventBus,{name:"middleware:executed",payload:n}),this.emitMiddlewareLifecycle("complete",{id:a,name:r,type:"transform",duration:n.duration})}catch(e){n.endTime=performance.now(),n.duration=n.endTime-n.startTime,n.error=e instanceof Error?e:new Error(String(e)),n.blocked=!1,this.emit(this.eventBus,{name:"middleware:executed",payload:n}),this.emitMiddlewareError(a,r,n.error,n.duration),console.error(`Middleware ${r} error:`,e)}finally{this.executionState.runningMiddleware=null}}return i}addMiddleware(e,t="unnamed-middleware"){const s=this.generateId();return this.middleware.push({fn:e,name:t,id:s}),this.updateExecutionState(),s}addBlockingMiddleware(e,t="unnamed-blocking-middleware"){const s=this.generateId();return this.blockingMiddleware.push({fn:e,name:t,id:s}),this.updateExecutionState(),s}removeMiddleware(e){const t=this.middleware.length+this.blockingMiddleware.length;return this.middleware=this.middleware.filter((t=>t.id!==e)),this.blockingMiddleware=this.blockingMiddleware.filter((t=>t.id!==e)),this.updateExecutionState(),this.middleware.length+this.blockingMiddleware.length<t}updateExecutionState(){this.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))]}emitMiddlewareLifecycle(e,t){this.emit(this.eventBus,{name:`middleware:${e}`,payload:{...t,timestamp:Date.now()}})}emitMiddlewareError(e,t,s,i){this.emit(this.eventBus,{name:"middleware:error",payload:{id:e,name:t,error:s,duration:i,timestamp:Date.now()}})}generateId(){return crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,15)}`}emit(e,t){queueMicrotask((()=>{e.emit(t)}))}};d(h());var v=class{constructor(e,t,s,i){this.eventBus=e,this.coreState=t,this.instanceID=s,this.maxRetries=i?.maxRetries??3,this.retryDelay=i?.retryDelay??1e3}persistence;instanceID;persistenceReady=!1;backgroundQueue=[];isProcessingQueue=!1;maxRetries=3;retryDelay=1e3;queueProcessor;async initialize(e){e?await this.setPersistence(e):this.setPersistenceReady()}isReady(){return this.persistenceReady}handleStateChange(e,t){if(!this.persistence||0===e.length)return;const s={id:`${Date.now()}-${Math.random().toString(36).substr(2,9)}`,state:structuredClone(t),changedPaths:[...e],timestamp:Date.now(),retries:0};this.backgroundQueue.push(s),this.scheduleQueueProcessing(),this.emit(this.eventBus,{name:"persistence:queued",payload:{taskId:s.id,changedPaths:e,queueSize:this.backgroundQueue.length,timestamp:s.timestamp}})}getQueueStatus(){return{queueSize:this.backgroundQueue.length,isProcessing:this.isProcessingQueue,oldestTask:this.backgroundQueue[0]?.timestamp}}async flush(){this.isProcessingQueue||await this.processQueue()}clearQueue(){const e=this.backgroundQueue.length;this.backgroundQueue=[],this.queueProcessor&&(clearTimeout(this.queueProcessor),this.queueProcessor=void 0),this.emit(this.eventBus,{name:"persistence:queue_cleared",payload:{clearedTasks:e,timestamp:Date.now()}})}scheduleQueueProcessing(){this.queueProcessor||this.isProcessingQueue||(this.queueProcessor=setTimeout((()=>{this.processQueue().catch((e=>{console.error("Queue processing failed:",e)}))}),10))}async processQueue(){if(!this.isProcessingQueue&&0!==this.backgroundQueue.length){this.isProcessingQueue=!0,this.queueProcessor=void 0;try{for(;this.backgroundQueue.length>0;){const e=this.backgroundQueue.shift();await this.processTask(e)}}finally{this.isProcessingQueue=!1}}}async processTask(e){try{await this.persistence.set(this.instanceID,e.state)?this.emit(this.eventBus,{name:"persistence:success",payload:{taskId:e.id,changedPaths:e.changedPaths,duration:Date.now()-e.timestamp,timestamp:Date.now()}}):await this.handleTaskFailure(e,new Error("Persistence returned false"))}catch(t){await this.handleTaskFailure(e,t)}}async handleTaskFailure(e,t){if(e.retries++,e.retries<=this.maxRetries){const s=this.retryDelay*Math.pow(2,e.retries-1);this.emit(this.eventBus,{name:"persistence:retry",payload:{taskId:e.id,attempt:e.retries,maxRetries:this.maxRetries,nextRetryIn:s,error:t,timestamp:Date.now()}}),setTimeout((()=>{this.backgroundQueue.unshift(e),this.scheduleQueueProcessing()}),s)}else this.emit(this.eventBus,{name:"persistence:failed",payload:{taskId:e.id,changedPaths:e.changedPaths,attempts:e.retries,error:t,timestamp:Date.now()}})}setPersistenceReady(){this.persistenceReady=!0,this.emit(this.eventBus,{name:"persistence:ready",payload:{timestamp:Date.now()}})}async setPersistence(e){this.persistence=e;try{const e=await this.persistence.get();e&&this.coreState.applyChanges(e)}catch(e){console.error("Failed to initialize persistence:",e),this.emit(this.eventBus,{name:"persistence:init_error",payload:{error:e,timestamp:Date.now()}})}finally{this.setPersistenceReady()}this.persistence.subscribe&&this.persistence.subscribe(this.instanceID,(async e=>{const t=this.coreState.applyChanges(e);t.length>0&&this.emit(this.eventBus,{name:"update:complete",payload:{changedPaths:t,source:"external",timestamp:Date.now()}})}))}dispose(){this.clearQueue(),this.isProcessingQueue=!1,this.persistenceReady=!1}emit(e,t){queueMicrotask((()=>{e.emit(t)}))}};d(h());var x=class{constructor(e,t,s){this.eventBus=e,this.coreState=t,this.executionState=s}async execute(e){const t=this.coreState.get(!0);this.executionState.transactionActive=!0,this.emit(this.eventBus,{name:"transaction:start",payload:{timestamp:Date.now()}});try{const t=await Promise.resolve(e());return this.emit(this.eventBus,{name:"transaction:complete",payload:{timestamp:Date.now()}}),this.executionState.transactionActive=!1,t}catch(e){throw this.coreState.applyChanges(t),this.emit(this.eventBus,{name:"transaction:error",payload:{error:e instanceof Error?e:new Error(String(e)),timestamp:Date.now()}}),this.executionState.transactionActive=!1,e}}emit(e,t){queueMicrotask((()=>{e.emit(t)}))}};d(h());var E=class{updateCount=0;listenerExecutions=0;averageUpdateTime=0;largestUpdateSize=0;mostActiveListenerPaths=[];totalUpdates=0;blockedUpdates=0;averageUpdateDuration=0;middlewareExecutions=0;transactionCount=0;totalEventsFired=0;updateTimes=[];pathExecutionCounts=new Map;constructor(e){this.setupEventListeners(e)}getMetrics(){return{updateCount:this.updateCount,listenerExecutions:this.listenerExecutions,averageUpdateTime:this.averageUpdateTime,largestUpdateSize:this.largestUpdateSize,mostActiveListenerPaths:[...this.mostActiveListenerPaths],totalUpdates:this.totalUpdates,blockedUpdates:this.blockedUpdates,averageUpdateDuration:this.averageUpdateDuration,middlewareExecutions:this.middlewareExecutions,transactionCount:this.transactionCount,totalEventsFired:this.totalEventsFired}}setupEventListeners(e){const t=e.emit;e.emit=s=>(this.totalEventsFired++,t.call(e,s)),e.subscribe("update:complete",(e=>{if(this.totalUpdates++,e.blocked)this.blockedUpdates++;else{if(e.duration){this.updateTimes.push(e.duration),this.updateTimes.length>100&&this.updateTimes.shift();const t=this.updateTimes.reduce(((e,t)=>e+t),0)/this.updateTimes.length;this.averageUpdateTime=t,this.averageUpdateDuration=t}e.changedPaths?.length&&(this.updateCount++,this.largestUpdateSize=Math.max(this.largestUpdateSize,e.changedPaths.length),e.changedPaths.forEach((e=>{const t=this.pathExecutionCounts.get(e)||0;this.pathExecutionCounts.set(e,t+1)})),this.mostActiveListenerPaths=Array.from(this.pathExecutionCounts.entries()).sort((([,e],[,t])=>t-e)).slice(0,5).map((([e])=>e)))}})),e.subscribe("middleware:start",(()=>{this.middlewareExecutions++})),e.subscribe("transaction:start",(()=>{this.transactionCount++}))}reset(){this.updateCount=0,this.listenerExecutions=0,this.averageUpdateTime=0,this.largestUpdateSize=0,this.mostActiveListenerPaths=[],this.totalUpdates=0,this.blockedUpdates=0,this.averageUpdateDuration=0,this.middlewareExecutions=0,this.transactionCount=0,this.totalEventsFired=0,this.updateTimes=[],this.pathExecutionCounts.clear()}getDetailedMetrics(){return{pathExecutionCounts:new Map(this.pathExecutionCounts),recentUpdateTimes:[...this.updateTimes],successRate:this.totalUpdates>0?(this.totalUpdates-this.blockedUpdates)/this.totalUpdates:1,averagePathsPerUpdate:this.updateCount>0?Array.from(this.pathExecutionCounts.values()).reduce(((e,t)=>e+t),0)/this.updateCount:0}}dispose(){this.reset()}},S=class{coreState;middlewareEngine;persistenceHandler;transactionManager;metricsCollector;pendingUpdates=[];isUpdating=!1;updateBus=(0,l.createEventBus)();eventBus=(0,l.createEventBus)();executionState;instanceID=e();merge;diff;constructor(e,t,s=u,i){this.executionState={executing:!1,changes:null,pendingChanges:[],middlewares:[],runningMiddleware:null,transactionActive:!1},this.merge=g({deleteMarker:s}),this.diff=y({deleteMarker:s}),this.coreState=new w(e,this.updateBus,this.diff),this.middlewareEngine=new b(this.eventBus,this.executionState,this.merge),this.persistenceHandler=new v(this.eventBus,this.coreState,this.instanceID,{maxRetries:i?.persistenceMaxRetries,retryDelay:i?.persistenceRetryDelay}),this.transactionManager=new x(this.eventBus,this.coreState,this.executionState),this.metricsCollector=new E(this.eventBus),this.persistenceHandler.initialize(t)}isReady(){return this.persistenceHandler.isReady()}state(){return this.executionState}get(e){return this.coreState.get(e)}async set(e){if(this.isUpdating)return this.pendingUpdates.push(e),void(this.executionState.pendingChanges=[...this.pendingUpdates]);this.isUpdating=!0,this.executionState.executing=!0;const t=performance.now();this.emit(this.eventBus,{name:"update:start",payload:{timestamp:t}});try{let s=e;if("function"==typeof e){const t=e(this.get(!0));s=t instanceof Promise?await t:t}const i=await this.middlewareEngine.executeBlocking(this.get(),s);if(i.blocked)return void this.emit(this.eventBus,{name:"update:complete",payload:{blocked:!0,error:i.error,timestamp:Date.now()}});const r=this.merge(this.get(!0),s),a=await this.middlewareEngine.executeTransform(r,s),n=this.merge(this.get(!0),a),o=this.coreState.applyChanges(n),c=performance.now();this.emit(this.eventBus,{name:"update:complete",payload:{changedPaths:o,duration:c-t,timestamp:Date.now()}}),o.length>0&&this.persistenceHandler.handleStateChange(o,this.get())}catch(e){throw this.emit(this.eventBus,{name:"update:complete",payload:{blocked:!0,error:e,timestamp:Date.now()}}),e}finally{if(this.isUpdating=!1,this.executionState.executing=!1,this.executionState.changes=null,this.executionState.runningMiddleware=null,this.pendingUpdates.length>0){const e=this.pendingUpdates.shift();if(this.executionState.pendingChanges=[...this.pendingUpdates],e)return this.set(e)}}}subscribe(e,t){const s=Array.isArray(e)?e:[e];return this.updateBus.subscribe("update",(i=>{(s.includes(i)||""===e)&&(t(this.get(!0)),this.metricsCollector.listenerExecutions++)}))}id(){return this.instanceID}async transaction(e){return this.transactionManager.execute(e)}use(e){return e.block?this.middlewareEngine.addBlockingMiddleware(e.action,e.name):this.middlewareEngine.addMiddleware(e.action,e.name)}unuse(e){return this.middlewareEngine.removeMiddleware(e)}metrics(){return this.metricsCollector.getMetrics()}onStoreEvent(e,t){return this.eventBus.subscribe(e,t)}getPersistenceStatus(){return this.persistenceHandler.getQueueStatus()}async flushPersistence(){return this.persistenceHandler.flush()}clearPersistenceQueue(){this.persistenceHandler.clearQueue()}dispose(){this.persistenceHandler.dispose(),this.metricsCollector.dispose?.()}emit(e,t){queueMicrotask((()=>{e.emit(t)}))}},k=y(),T=class{store;eventHistory=[];maxEvents;enableConsoleLogging;logEvents;performanceThresholds;unsubscribers=[];stateHistory=[];maxStateHistory=20;activeTransactionCount=0;activeBatches=new Set;middlewareExecutions=[];constructor(e,t={}){this.store=e,this.maxEvents=t.maxEvents??500,this.maxStateHistory=t.maxStateHistory??20,this.enableConsoleLogging=t.enableConsoleLogging??!1,this.logEvents={updates:t.logEvents?.updates??!0,middleware:t.logEvents?.middleware??!0,transactions:t.logEvents?.transactions??!0},this.performanceThresholds={updateTime:t.performanceThresholds?.updateTime??50,middlewareTime:t.performanceThresholds?.middlewareTime??20},this.recordStateSnapshot(),this.setupEventListeners()}setupEventListeners(){const e=["update:start","update:complete","middleware:start","middleware:complete","middleware:error","middleware:blocked","transaction:start","transaction:complete","transaction:error","middleware:executed"];this.unsubscribers.push(this.store.subscribe("",(()=>{this.recordStateSnapshot()})));for(const t of e){const e=t.startsWith("update")&&this.logEvents.updates||t.startsWith("middleware")&&this.logEvents.middleware||t.startsWith("transaction")&&this.logEvents.transactions;this.unsubscribers.push(this.store.onStoreEvent(t,(s=>{"middleware:executed"===t?this.middlewareExecutions.push(s):"transaction:start"===t?this.activeTransactionCount++:"transaction:complete"!==t&&"transaction:error"!==t||(this.activeTransactionCount=Math.max(0,this.activeTransactionCount-1)),s.batchId&&(t.endsWith("start")?this.activeBatches.add(s.batchId):(t.endsWith("complete")||t.endsWith("error"))&&this.activeBatches.delete(s.batchId)),this.recordEvent(t,s),this.enableConsoleLogging&&e&&this.logEventToConsole(t,s),this.checkPerformance(t,s)})))}}recordStateSnapshot(){const e=structuredClone(this.store.get());this.stateHistory.length>0&&0===k(e,this.stateHistory[0]).length||(this.stateHistory.unshift(e),this.stateHistory.length>this.maxStateHistory&&this.stateHistory.pop())}recordEvent(e,t){const s={type:e,timestamp:Date.now(),data:structuredClone(t)};this.eventHistory.unshift(s),this.eventHistory.length>this.maxEvents&&this.eventHistory.pop()}logEventToConsole(e,t){const s=new Date(t.timestamp||Date.now()).toISOString().split("T")[1].replace("Z","");if("update:start"===e)console.group(`%c⚡ Store Update Started [${s}]`,"color: #4a6da7");else if("update:complete"===e){if(t.blocked)console.warn(`%c✋ Update Blocked [${s}]`,"color: #bf8c0a",t.error);else{const e=t.changedPaths||[];e.length>0&&console.log(`%c✅ Update Complete [${s}] - ${e.length} paths changed in ${t.duration?.toFixed(2)}ms`,"color: #2a9d8f",e)}console.groupEnd()}else"middleware:start"===e?console.debug(`%c◀ Middleware "${t.name}" started [${s}] (${t.type})`,"color: #8c8c8c"):"middleware:complete"===e?console.debug(`%c▶ Middleware "${t.name}" completed [${s}] in ${t.duration?.toFixed(2)}ms`,"color: #7c9c7c"):"middleware:error"===e?console.error(`%c❌ Middleware "${t.name}" error [${s}]:`,"color: #e63946",t.error):"middleware:blocked"===e?console.warn(`%c🛑 Middleware "${t.name}" blocked update [${s}]`,"color: #e76f51"):"transaction:start"===e?console.group(`%c📦 Transaction Started [${s}]`,"color: #6d597a"):"transaction:complete"===e?(console.log(`%c📦 Transaction Complete [${s}]`,"color: #355070"),console.groupEnd()):"transaction:error"===e&&(console.error(`%c📦 Transaction Error [${s}]:`,"color: #e56b6f",t.error),console.groupEnd())}checkPerformance(e,t){this.enableConsoleLogging&&("update:complete"===e&&!t.blocked&&t.duration>this.performanceThresholds.updateTime&&console.warn(`%c⚠️ Slow update detected [${t.duration.toFixed(2)}ms]`,"color: #ff9f1c",{changedPaths:t.changedPaths,threshold:this.performanceThresholds.updateTime}),"middleware:complete"===e&&t.duration>this.performanceThresholds.middlewareTime&&console.warn(`%c⚠️ Slow middleware "${t.name}" [${t.duration.toFixed(2)}ms]`,"color: #ff9f1c",{threshold:this.performanceThresholds.middlewareTime}))}getEventHistory(){return structuredClone(this.eventHistory)}getStateHistory(){return structuredClone(this.stateHistory)}getMiddlewareExecutions(){return this.middlewareExecutions}getPerformanceMetrics(){return this.store.metrics()}getTransactionStatus(){return{activeTransactions:this.activeTransactionCount,activeBatches:Array.from(this.activeBatches)}}createLoggingMiddleware(e={}){const{logLevel:t="debug",logUpdates:s=!0}=e;return(e,i)=>{if(s){(console[t]||console.log)("State Update:",i)}return i}}createValidationMiddleware(e){return(t,s)=>{const i=e(t,s);return"boolean"==typeof i?i:(!i.valid&&i.reason&&console.warn("Validation failed:",i.reason),i.valid)}}clearHistory(){if(this.eventHistory=[],this.stateHistory.length>0){const e=this.stateHistory[0];this.stateHistory=[e]}}getRecentChanges(e=5){const t=[],s=Math.min(e,this.stateHistory.length-1);for(let e=0;e<s;e++){const s=this.stateHistory[e],i=this.stateHistory[e+1],r=k(i,s),a={},n={};for(const e of r){const t=e.split("."),r=(e,t)=>t.reduce(((e,t)=>e&&void 0!==e[t]?e[t]:void 0),e),o=(e,t,s)=>{const i=t.length-1,r=t[i];t.slice(0,i).reduce(((e,t)=>(e[t]=e[t]??{},e[t])),e)[r]=s},c=r(i,t),d=r(s,t);o(a,t,c),o(n,t,d)}let o=Date.now();for(const e of this.eventHistory)if("update:complete"===e.type&&e.data.changedPaths?.length>0){o=e.timestamp;break}0!==r.length&&t.push({timestamp:o,changedPaths:r,from:a,to:n})}return t}createTimeTravel(){let e=0,t=[];return{canUndo:()=>e<this.stateHistory.length-1,canRedo:()=>t.length>0,undo:async()=>{if(e<this.stateHistory.length-1){const s=e+1,i=this.stateHistory[s];t.unshift(this.stateHistory[e]),e=s,await this.store.set(i)}},redo:async()=>{if(t.length>0){const s=t.shift();e=Math.max(0,e-1),await this.store.set(s)}},getHistoryLength:()=>this.stateHistory.length,clear:()=>{t=[],e=0}}}disconnect(){this.unsubscribers.forEach((e=>e())),this.unsubscribers=[],this.clearHistory()}};export{u as DELETE_SYMBOL,S as ReactiveDataStore,T as StoreObserver,p as author};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asaidimu/utils-store",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "A reactive data store",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@asaidimu/events": "^1.1.1",
|
|
33
|
-
"@asaidimu/utils-persistence": "
|
|
33
|
+
"@asaidimu/utils-persistence": "2.1.0",
|
|
34
34
|
"uuid": "^11.1.0"
|
|
35
35
|
},
|
|
36
36
|
"exports": {
|