@asaidimu/react-store 1.4.1 → 1.4.2
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/README.md +267 -114
- package/index.cjs +1 -1
- package/index.d.cts +5 -1
- package/index.d.ts +5 -1
- package/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,22 +3,22 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@asaidimu/react-store)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
A performant, type-safe state management solution for React with built-in observability and middleware support.
|
|
6
|
+
A performant, type-safe state management solution for React with built-in persistence, observability, and middleware support.
|
|
7
7
|
|
|
8
8
|
⚠️ **Beta Warning**
|
|
9
|
-
This package is currently in **beta**.
|
|
10
|
-
|
|
11
|
-
We will update this warning once the package reaches a stable release. Use at your own risk and feel free to share feedback or report issues to help us improve!
|
|
9
|
+
This package is currently in **beta**. The API is subject to rapid changes and should not be considered stable. Breaking changes may occur frequently without notice as we iterate and improve. We’ll update this warning once the package reaches a stable release. Use at your own risk and share feedback or report issues to help us improve!
|
|
12
10
|
|
|
13
11
|
## Features
|
|
14
12
|
|
|
15
|
-
- **Reactive State Management**: Automatically track dependencies and optimize renders
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
13
|
+
- **Reactive State Management**: Automatically track dependencies and optimize renders.
|
|
14
|
+
- **Type-Safe**: Full TypeScript support with strict type checking.
|
|
15
|
+
- **Middleware Pipeline**: Transform or block updates with regular and blocking middleware.
|
|
16
|
+
- **Transactions**: Atomic state updates with rollback capabilities.
|
|
17
|
+
- **Persistence**: Seamless integration with IndexedDB or LocalStorage, including cross-tab synchronization.
|
|
18
|
+
- **Observability**: Built-in metrics, event logging, state history, and time-travel debugging.
|
|
19
|
+
- **Remote Observability**: Send metrics to external systems like OpenTelemetry, Prometheus, or Grafana Cloud.
|
|
20
|
+
- **Performance Optimized**: Smart selector caching and efficient update propagation.
|
|
21
|
+
- **React 18+ Ready**: Built with modern React APIs for maximum compatibility.
|
|
22
22
|
|
|
23
23
|
## Installation
|
|
24
24
|
|
|
@@ -36,24 +36,15 @@ import { createStore } from '@asaidimu/react-store';
|
|
|
36
36
|
const counterStore = createStore({
|
|
37
37
|
state: {
|
|
38
38
|
count: 0,
|
|
39
|
-
user: {
|
|
40
|
-
name: 'Guest',
|
|
41
|
-
loggedIn: false
|
|
42
|
-
}
|
|
39
|
+
user: { name: 'Guest', loggedIn: false },
|
|
43
40
|
},
|
|
44
41
|
actions: {
|
|
45
42
|
increment: (state, amount: number) => ({ count: state.count + amount }),
|
|
46
43
|
login: async (state, username: string) => {
|
|
47
|
-
// Async example
|
|
48
44
|
await fetch('/api/login');
|
|
49
|
-
return {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
loggedIn: true
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
}
|
|
45
|
+
return { user: { name: username, loggedIn: true } };
|
|
46
|
+
},
|
|
47
|
+
},
|
|
57
48
|
});
|
|
58
49
|
|
|
59
50
|
export const useCounter = counterStore;
|
|
@@ -63,12 +54,19 @@ export const useCounter = counterStore;
|
|
|
63
54
|
|
|
64
55
|
```tsx
|
|
65
56
|
function Counter() {
|
|
66
|
-
const { select, actions } = useCounter();
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
const { select, actions, isReady } = useCounter();
|
|
58
|
+
|
|
59
|
+
const count = select((state) => state.count);
|
|
60
|
+
const userName = select((state) => state.user.name);
|
|
61
|
+
|
|
62
|
+
if (!isReady) {
|
|
63
|
+
return <div>Loading persistence...</div>;
|
|
64
|
+
}
|
|
65
|
+
|
|
69
66
|
return (
|
|
70
67
|
<div>
|
|
71
|
-
<
|
|
68
|
+
<p>Count: {count}</p>
|
|
69
|
+
<p>User: {userName}</p>
|
|
72
70
|
<button onClick={() => actions.increment(1)}>+</button>
|
|
73
71
|
<button onClick={() => actions.login('user123')}>Login</button>
|
|
74
72
|
</div>
|
|
@@ -78,133 +76,288 @@ function Counter() {
|
|
|
78
76
|
|
|
79
77
|
## Advanced Features
|
|
80
78
|
|
|
79
|
+
### Persistence
|
|
80
|
+
Persist state across sessions or synchronize across tabs with built-in adapters.
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { createStore, IndexedDBPersistence } from '@asaidimu/react-store';
|
|
84
|
+
|
|
85
|
+
const persistence = new IndexedDBPersistence('my-store');
|
|
86
|
+
const useStore = createStore(
|
|
87
|
+
{
|
|
88
|
+
state: { count: 0 },
|
|
89
|
+
actions: {
|
|
90
|
+
increment: (state) => ({ count: state.count + 1 }),
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{ persistence },
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
function App() {
|
|
97
|
+
const { store, isReady } = useStore();
|
|
98
|
+
if (!isReady) return <div>Loading...</div>;
|
|
99
|
+
// Use store...
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- **IndexedDBPersistence**: High-capacity storage with cross-tab sync.
|
|
104
|
+
- **LocalStoragePersistence**: Lightweight storage with cross-tab notifications.
|
|
105
|
+
|
|
81
106
|
### Middleware
|
|
82
107
|
|
|
83
108
|
```tsx
|
|
84
|
-
const logger
|
|
85
|
-
console.log('
|
|
86
|
-
return update;
|
|
109
|
+
const logger = (state, update) => {
|
|
110
|
+
console.log('Update:', update);
|
|
111
|
+
return update;
|
|
87
112
|
};
|
|
88
113
|
|
|
89
|
-
const validator
|
|
114
|
+
const validator = (state, update) => {
|
|
90
115
|
if (update.count > 100) {
|
|
91
116
|
console.warn('Count too high!');
|
|
92
|
-
return false;
|
|
117
|
+
return false;
|
|
93
118
|
}
|
|
119
|
+
return true;
|
|
94
120
|
};
|
|
95
121
|
|
|
96
|
-
createStore({
|
|
97
|
-
state: {
|
|
98
|
-
actions: {
|
|
99
|
-
middleware:
|
|
100
|
-
blockingMiddleware:
|
|
122
|
+
const useStore = createStore({
|
|
123
|
+
state: { count: 0 },
|
|
124
|
+
actions: { increment: (state) => ({ count: state.count + 1 }) },
|
|
125
|
+
middleware: { logger },
|
|
126
|
+
blockingMiddleware: { validator },
|
|
101
127
|
});
|
|
102
128
|
```
|
|
129
|
+
|
|
103
130
|
### Observability
|
|
104
131
|
|
|
132
|
+
Enable metrics and debugging via the `store` and `observer` objects:
|
|
133
|
+
|
|
105
134
|
```tsx
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
135
|
+
const useStore = createStore(
|
|
136
|
+
{
|
|
137
|
+
state: { count: 0 },
|
|
138
|
+
actions: { increment: (state) => ({ count: state.count + 1 }) },
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
enableMetrics: true,
|
|
142
|
+
enableConsoleLogging: true,
|
|
143
|
+
logEvents: { updates: true, middleware: false },
|
|
144
|
+
},
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
function DebugPanel() {
|
|
148
|
+
const { observer } = useStore();
|
|
149
|
+
const metrics = observer.getPerformanceMetrics();
|
|
150
|
+
const timeTravel = observer.createTimeTravel();
|
|
113
151
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
152
|
+
return (
|
|
153
|
+
<div>
|
|
154
|
+
<p>Updates: {metrics.updateCount}</p>
|
|
155
|
+
<button onClick={() => timeTravel.undo()}>Undo</button>
|
|
156
|
+
<button onClick={() => timeTravel.redo()}>Redo</button>
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Note**: `ReactiveDataStore` and `StoreObservability` are not directly exported but are accessible via the `store` and `observer` properties returned by `useStore`.
|
|
163
|
+
|
|
164
|
+
### Remote Observability
|
|
165
|
+
|
|
166
|
+
Send metrics to external systems:
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import { createStore, useRemoteObservability } from '@asaidimu/react-store';
|
|
170
|
+
|
|
171
|
+
const useStore = createStore({ state: { count: 0 }, actions: {} });
|
|
172
|
+
function Monitoring() {
|
|
173
|
+
const { store } = useStore();
|
|
174
|
+
const { remote, addOpenTelemetryDestination } = useRemoteObservability(store, {
|
|
175
|
+
serviceName: 'my-app',
|
|
176
|
+
environment: 'dev',
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
addOpenTelemetryDestination({
|
|
181
|
+
endpoint: 'https://otel-collector.example.com',
|
|
182
|
+
apiKey: 'your-api-key',
|
|
183
|
+
});
|
|
184
|
+
}, []);
|
|
185
|
+
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
128
188
|
```
|
|
129
189
|
|
|
130
190
|
## API Reference
|
|
131
191
|
|
|
132
|
-
### `createStore(
|
|
192
|
+
### `createStore(definition, options)`
|
|
133
193
|
|
|
134
194
|
```typescript
|
|
135
195
|
type StoreDefinition<T, R extends Actions<T>> = {
|
|
136
196
|
state: T;
|
|
137
197
|
actions: R;
|
|
138
|
-
middleware?: Middleware<T
|
|
139
|
-
blockingMiddleware?:
|
|
198
|
+
middleware?: Record<string, Middleware<T>>;
|
|
199
|
+
blockingMiddleware?: Record<string, BlockingMiddleware<T>>;
|
|
140
200
|
};
|
|
141
201
|
|
|
142
|
-
interface
|
|
143
|
-
/** Maximum number of events to keep in history */
|
|
144
|
-
maxEvents?: number;
|
|
145
|
-
/** Whether to enable console logging */
|
|
146
|
-
enableConsoleLogging?: boolean;
|
|
147
|
-
/* Limits the number of snapshots to be saved */
|
|
148
|
-
maxStateHistory?: number;
|
|
149
|
-
/** Options for specific event types to log */
|
|
150
|
-
logEvents?: {
|
|
151
|
-
updates?: boolean;
|
|
152
|
-
middleware?: boolean;
|
|
153
|
-
transactions?: boolean;
|
|
154
|
-
};
|
|
155
|
-
/** Time threshold in ms to highlight slow operations */
|
|
156
|
-
performanceThresholds?: {
|
|
157
|
-
updateTime?: number;
|
|
158
|
-
middlewareTime?: number;
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
type StoreOptions = ObservabilityOptions & {
|
|
202
|
+
interface StoreOptions<T> {
|
|
163
203
|
enableMetrics?: boolean;
|
|
204
|
+
enableConsoleLogging?: boolean;
|
|
205
|
+
maxEvents?: number;
|
|
206
|
+
maxStateHistory?: number;
|
|
207
|
+
logEvents?: { updates?: boolean; middleware?: boolean; transactions?: boolean };
|
|
208
|
+
performanceThresholds?: { updateTime?: number; middlewareTime?: number };
|
|
209
|
+
persistence?: DataStorePersistence<T>;
|
|
164
210
|
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### `ReactiveDataStore`
|
|
168
211
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
- `set(update: StateUpdater<T>): Promise<void>` - Update state
|
|
172
|
-
- `subscribe(path, listener)` - Subscribe to state changes
|
|
173
|
-
- `transaction(operation)` - Execute transactional update
|
|
174
|
-
|
|
175
|
-
### `StoreObservability`
|
|
212
|
+
const useStore = createStore(definition, options);
|
|
213
|
+
```
|
|
176
214
|
|
|
177
|
-
|
|
178
|
-
- `
|
|
179
|
-
- `
|
|
180
|
-
- `
|
|
181
|
-
- `
|
|
215
|
+
**Returns**: A `useStore` hook with:
|
|
216
|
+
- `store`: `ReactiveDataStore` instance.
|
|
217
|
+
- `observer`: `StoreObservability` instance (if `enableMetrics` is true).
|
|
218
|
+
- `select`: Memoized selector for state slices.
|
|
219
|
+
- `actions`: Action dispatchers.
|
|
220
|
+
- `actionTracker`: Tracks action executions.
|
|
221
|
+
- `state`: Getter for full state.
|
|
222
|
+
- `isReady`: Boolean indicating persistence initialization completion.
|
|
223
|
+
|
|
224
|
+
### `ReactiveDataStore` (via `store`)
|
|
225
|
+
|
|
226
|
+
- `get(): T`
|
|
227
|
+
- `set(update: StateUpdater<T>): Promise<void>`
|
|
228
|
+
- `subscribe(path: string | string[], listener: (state: T) => void): () => void`
|
|
229
|
+
- `transaction<R>(operation: () => R | Promise<R>): Promise<R>`
|
|
230
|
+
- `use(middleware: Middleware<T>, name?: string)`
|
|
231
|
+
- `useBlockingMiddleware(middleware: BlockingMiddleware<T>, name?: string)`
|
|
232
|
+
- `isReady(): boolean`
|
|
233
|
+
- `onStoreEvent(event: StoreEvent, listener): () => void`
|
|
234
|
+
|
|
235
|
+
### `StoreObservability` (via `observer`)
|
|
236
|
+
|
|
237
|
+
- `getEventHistory(): DebugEvent[]`
|
|
238
|
+
- `getStateHistory(): T[]`
|
|
239
|
+
- `getRecentChanges(limit?: number): { timestamp, changedPaths, from, to }[]`
|
|
240
|
+
- `getPerformanceMetrics(): StoreMetrics`
|
|
241
|
+
- `createTimeTravel(): { undo, redo, canUndo, canRedo }`
|
|
242
|
+
- `createLoggingMiddleware(options)`
|
|
243
|
+
- `createValidationMiddleware(validator)`
|
|
244
|
+
|
|
245
|
+
### `RemoteObservability`
|
|
246
|
+
|
|
247
|
+
- Extend `StoreObservability` with `useRemoteObservability(store, options)`:
|
|
248
|
+
- `addDestination(destination: RemoteDestination)`
|
|
249
|
+
- `beginTrace(name: string)`
|
|
250
|
+
- `beginSpan(traceId, name, labels)`
|
|
251
|
+
- `endSpan(traceId, spanName)`
|
|
252
|
+
- `endTrace(traceId)`
|
|
253
|
+
|
|
254
|
+
### Persistence Adapters
|
|
255
|
+
|
|
256
|
+
- **`IndexedDBPersistence(storeId: string)`**
|
|
257
|
+
- **`LocalStoragePersistence(storageKey: string)`**
|
|
258
|
+
- Methods: `set(id, state)`, `get()`, `subscribe(id, callback)`, `clear()`
|
|
182
259
|
|
|
183
260
|
## Best Practices
|
|
184
261
|
|
|
185
|
-
1. **
|
|
186
|
-
```
|
|
187
|
-
//
|
|
188
|
-
select(state => state
|
|
189
|
-
|
|
190
|
-
// Avoid
|
|
191
|
-
select(state => state);
|
|
262
|
+
1. **Granular Selectors**:
|
|
263
|
+
```tsx
|
|
264
|
+
const count = select((state) => state.count); // Good
|
|
265
|
+
const allState = select((state) => state); // Avoid
|
|
192
266
|
```
|
|
193
267
|
|
|
194
|
-
2. **
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
268
|
+
2. **Action Design**:
|
|
269
|
+
```tsx
|
|
270
|
+
actions: {
|
|
271
|
+
fetchData: async (state, id: string) => {
|
|
272
|
+
const data = await fetch(`/api/${id}`).then((res) => res.json());
|
|
273
|
+
return { data };
|
|
274
|
+
},
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
3. **Persistence**:
|
|
279
|
+
- Use unique `storeId` or `storageKey` to avoid conflicts.
|
|
280
|
+
- Check `isReady` for UI dependent on persisted state:
|
|
281
|
+
```tsx
|
|
282
|
+
if (!isReady) return <div>Loading...</div>;
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
4. **Middleware**:
|
|
286
|
+
```tsx
|
|
287
|
+
const apiMonitor = (state, update) => {
|
|
288
|
+
if (update.apiCalls) telemetry.log(update.apiCalls);
|
|
201
289
|
return update;
|
|
202
290
|
};
|
|
203
291
|
```
|
|
204
292
|
|
|
293
|
+
## Example with Persistence and Observability
|
|
294
|
+
|
|
295
|
+
```tsx
|
|
296
|
+
import { createStore, IndexedDBPersistence } from '@asaidimu/react-store';
|
|
297
|
+
|
|
298
|
+
const persistence = new IndexedDBPersistence('counter-store');
|
|
299
|
+
const useCounter = createStore(
|
|
300
|
+
{
|
|
301
|
+
state: { count: 0 },
|
|
302
|
+
actions: {
|
|
303
|
+
increment: (state) => ({ count: state.count + 1 }),
|
|
304
|
+
},
|
|
305
|
+
middleware: {
|
|
306
|
+
log: (state, update) => {
|
|
307
|
+
console.log('Update:', update);
|
|
308
|
+
return update;
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
persistence,
|
|
314
|
+
enableMetrics: true,
|
|
315
|
+
enableConsoleLogging: true,
|
|
316
|
+
},
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
function App() {
|
|
320
|
+
const { select, actions, isReady, observer } = useCounter();
|
|
321
|
+
const count = select((state) => state.count);
|
|
322
|
+
|
|
323
|
+
if (!isReady) return <div>Loading...</div>;
|
|
324
|
+
|
|
325
|
+
return (
|
|
326
|
+
<div>
|
|
327
|
+
<p>Count: {count}</p>
|
|
328
|
+
<button onClick={() => actions.increment()}>+1</button>
|
|
329
|
+
<p>Updates: {observer.getPerformanceMetrics().updateCount}</p>
|
|
330
|
+
</div>
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Comparison with Other State Management Solutions
|
|
336
|
+
|
|
337
|
+
`@asaidimu/react-store` is a competitor in the React state management ecosystem, offering a unique blend of reactivity, persistence, and observability. Here’s how it stacks up against popular alternatives:
|
|
338
|
+
|
|
339
|
+
| **Feature** | **@asaidimu/react-store** | **Redux** | **Zustand** | **MobX** | **Recoil** |
|
|
340
|
+
|-------------------------|---------------------------|--------------------|--------------------|--------------------|--------------------|
|
|
341
|
+
| **Dev Experience** | Intuitive hook-based API with rich tooling. | Verbose setup with reducers and middleware. | Minimalist, hook-friendly API. | Reactive, class-based approach. | Atom-based, React-native feel. |
|
|
342
|
+
| **Learning Curve** | Moderate (middleware, observability add complexity). | Steep (boilerplate-heavy). | Low (simple API). | Moderate (reactive concepts). | Low to moderate (atom model). |
|
|
343
|
+
| **API Complexity** | Medium (rich feature set balanced with simplicity). | High (many concepts: actions, reducers, etc.). | Low (straightforward). | Medium (proxies, decorators). | Medium (atom/selectors). |
|
|
344
|
+
| **Scalability** | High (transactions, persistence, remote metrics). | High (structured but verbose). | High (small but flexible). | High (reactive scaling). | High (granular atoms). |
|
|
345
|
+
| **Extensibility** | Excellent (middleware, custom persistence, observability). | Good (middleware, enhancers). | Good (middleware-like). | Moderate (custom reactions). | Moderate (custom selectors). |
|
|
346
|
+
| **Performance** | Optimized (selectors, reactive updates). | Good (predictable but manual optimization). | Excellent (minimal overhead). | Good (reactive overhead). | Good (granular updates). |
|
|
347
|
+
| **Bundle Size** | Moderate (includes observability, persistence). | Large (core + toolkit). | Tiny (~1KB). | Moderate (~20KB). | Moderate (~10KB). |
|
|
348
|
+
| **Persistence** | Built-in (IndexedDB, LocalStorage, cross-tab). | Manual (via middleware). | Manual (via middleware). | Manual (custom). | Manual (custom). |
|
|
349
|
+
| **Observability** | Excellent (metrics, time-travel, remote). | Good (dev tools). | Basic (via plugins). | Good (reactive logs). | Basic (via plugins). |
|
|
350
|
+
| **React Integration** | Native(hooks) | Manual (React-Redux). | Native (hooks). | Native (observers). | Native (atoms). |
|
|
351
|
+
|
|
352
|
+
### Where `@asaidimu/react-store` Shines
|
|
353
|
+
- **All-in-One**: Combines state management, persistence, and observability without external dependencies.
|
|
354
|
+
- **Flexibility**: Middleware and transactions make it adaptable to complex use cases.
|
|
355
|
+
- **Modern React**: Leverages `useSyncExternalStore` for optimal performance and compatibility.
|
|
356
|
+
|
|
357
|
+
### Trade-Offs
|
|
358
|
+
- Larger bundle size compared to minimalist solutions like Zustand.
|
|
359
|
+
- Slightly steeper learning curve due to advanced features.
|
|
205
360
|
## License
|
|
206
361
|
|
|
207
362
|
MIT © [Saidimu](https://github.com/asaidimu)
|
|
208
363
|
|
|
209
|
-
---
|
|
210
|
-
|
package/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("react"),t=require("@asaidimu/events"),r=require("uuid"),s=require("@asaidimu/indexed"),a=require("@asaidimu/query");function n(e,t){const r=new Set;function s(e,t){const r=e.split(".");for(let e=0;e<r.length;e++){const s=r.slice(0,e+1).join(".");t.add(s)}}return function e(t,a,n){const i=Symbol.for("delete");if(Array.isArray(a)&&Array.isArray(n))JSON.stringify(a)!==JSON.stringify(n)&&s(t,r);else for(const o of Object.keys(n)){const c=t?`${t}.${o}`:o;if(o in a){const t=a[o],d=n[o];d===i?(s(c,r),e(c,{},n[o])):"object"==typeof t&&null!==t&&"object"==typeof d&&null!==d?e(c,t,d):t!==d&&s(c,r)}else s(c,r),e(c,{},n[o])}}("",e||{},t||{}),Array.from(r)}var i=class{store;eventHistory=[];maxEvents;enableConsoleLogging;logEvents;performanceThresholds;unsubscribers=[];stateHistory=[];maxStateHistory=20;activeTransactionCount=0;activeBatches=new Set;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"];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=>{"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===n(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.store.getMiddlewareExecutions()}getPerformanceMetrics(){return this.store.getPerformanceMetrics()}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=n(s,r),i={},o={};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),n=(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);n(i,t,c),n(o,t,d)}let c=Date.now();for(const e of this.eventHistory)if("update:complete"===e.type&&e.data.changedPaths?.length>0){c=e.timestamp;break}0!==a.length&&t.push({timestamp:c,changedPaths:a,from:i,to:o})}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()}};function o(e,t){const r=Symbol.for("delete");if("object"!=typeof e||null===e)return t;if("object"!=typeof t||null===t)return e;const s=structuredClone(e),a=[{target:s,source:t}];for(;a.length>0;){const{target:e,source:t}=a.pop();Object.keys(t).forEach((s=>{const n=t[s],i=e[s];n===r?delete e[s]:Array.isArray(n)?e[s]=n:"object"==typeof n&&null!==n&&"object"==typeof i&&null!==i?(e.hasOwnProperty(s)||(e[s]=structuredClone(n)),a.push({target:e[s],source:n})):e[s]=n}))}return s}var c=class{metrics;middleware;blockingMiddleware;middlewareExecutions=[];maxExecutionHistory=100;pendingUpdates;isUpdating;updateTimes;cache=null;updateBus=t.createEventBus();eventBus=t.createEventBus();executionState;persistence;instanceID=r.v4();constructor(e,t){this.middleware=[],this.blockingMiddleware=[],this.pendingUpdates=[],this.isUpdating=!1,this.updateTimes=[],this.metrics={updateCount:0,listenerExecutions:0,averageUpdateTime:0,largestUpdateSize:0,mostActiveListenerPaths:[]},this.executionState={executing:!1,changes:null,pendingChanges:[],middlewares:[],runningMiddleware:null,transactionActive:!1},this.cache=e,t&&this.setPersistence(t)}async setPersistence(e){this.persistence=e;const t=await Promise.resolve(this.persistence.get());t&&(this.cache=t),this.persistence.subscribe(this.instanceID,(async e=>{this.isUpdating=!0;try{const t=n(this.get(),e);t.length>0&&(this.cache=e,this.notifyListeners(t),this.eventBus.emit({name:"update:complete",payload:{changedPaths:t,source:"external",timestamp:Date.now()}}))}finally{this.isUpdating=!1}}))}getExecutionState(){return this.executionState}get(){return this.cache}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}});let r=e;if("function"==typeof e){const t=e(structuredClone(this.get()));r=t instanceof Promise?await t:t}try{const e=await this.applyBlockingMiddleware(r);if(e.blocked)return void this.eventBus.emit({name:"update:complete",payload:{blocked:!0,error:e.error,timestamp:Date.now()}})}catch(e){throw this.eventBus.emit({name:"update:complete",payload:{blocked:!0,error:e,timestamp:Date.now()}}),e}const s=o(this.get(),r),a=await this.applyMiddleware(s,r);try{const e=n(this.get(),a);if(this.metrics.updateCount++,this.metrics.largestUpdateSize=Math.max(this.metrics.largestUpdateSize,e.length),e.length>0){this.cache=a;try{await(this.persistence?.set(this.instanceID,this.get()))||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()}})}this.notifyListeners(e)}const r=performance.now();this.updateTimes.push(r-t),this.updateTimes.length>100&&this.updateTimes.shift(),this.metrics.averageUpdateTime=this.updateTimes.reduce(((e,t)=>e+t),0)/this.updateTimes.length,this.eventBus.emit({name:"update:complete",payload:{changedPaths:e,duration:r-t,timestamp:Date.now()}})}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)}}}async applyBlockingMiddleware(e){for(const{fn:t,name:r,id:s}of this.blockingMiddleware){const a={id:s,name:r,startTime:performance.now()};this.executionState.runningMiddleware={id:s,name:r,startTime:performance.now()},this.eventBus.emit({name:"middleware:start",payload:{id:s,name:r,type:"blocking",timestamp:Date.now()}});try{const n=await Promise.resolve(t(this.get(),e));if(a.endTime=performance.now(),a.duration=void 0!==a.startTime?a.endTime-a.startTime:0,!1===n)return a.blocked=!0,this.trackMiddlewareExecution(a),this.eventBus.emit({name:"middleware:blocked",payload:{id:s,name:r,duration:a.duration,timestamp:Date.now()}}),{blocked:!0};this.eventBus.emit({name:"middleware:complete",payload:{id:s,name:r,type:"blocking",duration:a.duration,timestamp:Date.now()}}),this.trackMiddlewareExecution({...a,blocked:!1})}catch(e){return a.endTime=performance.now(),a.duration=void 0!==a.startTime?a.endTime-a.startTime:0,a.error=e instanceof Error?e:new Error(String(e)),a.blocked=!0,this.trackMiddlewareExecution(a),this.eventBus.emit({name:"middleware:error",payload:{id:s,name:r,error:a.error,duration:a.duration,timestamp:Date.now()}}),{blocked:!0,error:a.error}}finally{this.executionState.runningMiddleware=null}}return{blocked:!1}}async applyMiddleware(e,t){let r=e;for(const{fn:e,name:s,id:a}of this.middleware){const n={id:a,name:s,startTime:performance.now()};this.executionState.runningMiddleware={id:a,name:s,startTime:performance.now()},this.eventBus.emit({name:"middleware:start",payload:{id:a,name:s,type:"transform",timestamp:Date.now()}});try{const i=await Promise.resolve(e(r,t));n.endTime=performance.now(),n.duration=void 0!==n.startTime?n.endTime-n.startTime:0,n.blocked=!1,i&&"object"==typeof i&&(r=o(r,i)),this.trackMiddlewareExecution(n),this.eventBus.emit({name:"middleware:complete",payload:{id:a,name:s,type:"transform",duration:n.duration,timestamp:Date.now()}})}catch(e){n.endTime=performance.now(),n.duration=void 0!==n.startTime?n.endTime-n.startTime:0,n.error=e instanceof Error?e:new Error(String(e)),n.blocked=!1,this.trackMiddlewareExecution(n),this.eventBus.emit({name:"middleware:error",payload:{id:a,name:s,error:n.error,duration:n.duration,timestamp:Date.now()}}),console.error(`Middleware ${s} error:`,e)}finally{this.executionState.runningMiddleware=null}}return r}subscribe(e,t){const r=Array.isArray(e)?e:[e];return this.updateBus.subscribe("update",(s=>{(r.includes(s)||""===e)&&(t(this.get()),this.metrics.listenerExecutions++)}))}id(){return this.instanceID}async transaction(e){const t=structuredClone(this.get());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.cache=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}}use(e,t="unnamed-middleware"){const r=crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,15)}`;return this.middleware.push({fn:e,name:t,id:r}),this.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))],r}useBlockingMiddleware(e,t="unnamed-blocking-middleware"){const r=crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,15)}`;return this.blockingMiddleware.push({fn:e,name:t,id:r}),this.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))],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.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))],this.middleware.length+this.blockingMiddleware.length<t}getPerformanceMetrics(){return structuredClone(this.metrics)}getMiddlewareExecutions(){return structuredClone(this.middlewareExecutions)}onStoreEvent(e,t){return this.eventBus.subscribe(e,t)}notifyListeners(e){e.forEach((e=>this.updateBus.emit({name:"update",payload:e})))}trackMiddlewareExecution(e){this.middlewareExecutions.unshift(e),this.middlewareExecutions.length>this.maxExecutionHistory&&this.middlewareExecutions.pop()}};var d=class{selectorCache=new WeakMap;create(e){return t=>{let r=this.selectorCache.get(e);r||(r=new WeakMap,this.selectorCache.set(e,r));const s=r.get(t);if(s)return s.result;const a=e(t);return r.set(t,{result:a,deps:[]}),a}}},l=class{executions=[];maxHistory=100;listeners=new Set;track(e){this.executions.unshift(e),this.executions.length>this.maxHistory&&this.executions.pop(),this.notify()}getExecutions(){return[...this.executions]}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){this.listeners.forEach((e=>e()))}};function u(e){return{id:"opentelemetry",name:"OpenTelemetry Collector",config:e,testConnection:async()=>{try{return(await fetch(`${e.endpoint}/v1/metrics`,{method:"OPTIONS",headers:{...e.apiKey?{"api-key":e.apiKey}:{}}})).ok}catch(e){return!1}},send:async t=>{const r={resourceMetrics:[{resource:{attributes:{"service.name":t.source,...e.resource}},scopeMetrics:[{metrics:t.metrics.map((e=>"counter"===e.type?{name:e.name,unit:e.unit||"1",sum:{dataPoints:[{timeUnixNano:BigInt(1e6*t.timestamp),asInt:Number(e.value),attributes:Object.entries(e.labels||{}).map((([e,t])=>({key:e,value:{stringValue:t}})))}]}}:"histogram"===e.type?{name:e.name,unit:e.unit||"ms",histogram:{dataPoints:[{timeUnixNano:BigInt(1e6*t.timestamp),count:1,sum:Number(e.value),attributes:Object.entries(e.labels||{}).map((([e,t])=>({key:e,value:{stringValue:t}})))}]}}:{name:e.name,unit:e.unit||"1",gauge:{dataPoints:[{timeUnixNano:BigInt(1e6*t.timestamp),asDouble:"number"==typeof e.value?e.value:1,attributes:Object.entries(e.labels||{}).map((([e,t])=>({key:e,value:{stringValue:t}})))}]}}))}]}]};await fetch(`${e.endpoint}/v1/metrics`,{method:"POST",headers:{"Content-Type":"application/json",...e.apiKey?{"api-key":e.apiKey}:{}},body:JSON.stringify(r)})}}}function h(e){return{id:"prometheus",name:"Prometheus Pushgateway",config:e,testConnection:async()=>{try{const t=e.username&&e.password?`Basic ${btoa(`${e.username}:${e.password}`)}`:void 0;return(await fetch(e.pushgatewayUrl,{method:"HEAD",headers:{...t?{Authorization:t}:{}}})).ok}catch(e){return!1}},send:async t=>{let r="";for(const e of t.metrics){if("log"===e.type||"trace"===e.type)continue;const s=Object.entries({...e.labels,source:t.source,instance:t.metrics.find((e=>e.labels?.instanceId))?.labels?.instanceId||"unknown"}).map((([e,t])=>`${e}="${t.replace(/"/g,'\\"')}"`)).join(","),a=e.name.replace(/\./g,"_");"counter"===e.type?r+=`# TYPE ${a} counter\n`:r+=`# TYPE ${a} gauge\n`,r+=`${a}{${s}} ${e.value}\n`}const s=e.username&&e.password?`Basic ${btoa(`${e.username}:${e.password}`)}`:void 0;await fetch(`${e.pushgatewayUrl}/metrics/job/${e.jobName}`,{method:"POST",headers:{"Content-Type":"text/plain",...s?{Authorization:s}:{}},body:r})}}}function m(e){return{id:"grafana-cloud",name:"Grafana Cloud",config:e,testConnection:async()=>{try{return(await fetch(`${e.url}/api/v1/query?query=up`,{method:"GET",headers:{Authorization:`Bearer ${e.apiKey}`}})).ok}catch(e){return!1}},send:async t=>{const r={streams:[{stream:{service:t.source,job:"store-metrics"},values:t.metrics.map((e=>{const r=1e6*t.timestamp;let s="";if("object"==typeof e.value)s=JSON.stringify(e.value);else if(s=`level=info metric=${e.name} value=${e.value} type=${e.type}`,e.labels)for(const[t,r]of Object.entries(e.labels))s+=` ${t}=${r}`;return[String(r),s]}))}]};await fetch(`${e.url}/loki/api/v1/push`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},body:JSON.stringify(r)})}}}function p(e){return{id:"http-endpoint",name:"HTTP Metrics Endpoint",config:e,testConnection:async()=>{try{return(await fetch(e.url,{method:"OPTIONS",headers:e.headers})).ok}catch(e){return!1}},send:async t=>{const r=e.transformPayload?e.transformPayload(t):t;await fetch(e.url,{method:e.method||"POST",headers:{"Content-Type":"application/json",...e.headers},body:JSON.stringify(r)})}}}var g=class extends i{destinations=new Map;metricsBatch=[];reportingInterval;batchSize;serviceName;environment;instanceId;immediateReporting;collectCategories;compressPayloads;reportingTimer=null;traceIdCounter=0;activeTraces=new Map;constructor(e,t){super(e,t),this.serviceName=t.serviceName,this.environment=t.environment,this.instanceId=t.instanceId||this.generateInstanceId(),this.reportingInterval=t.reportingInterval||3e4,this.batchSize=t.batchSize||100,this.immediateReporting=t.immediateReporting||!1,this.collectCategories=t.collectCategories||{performance:!0,errors:!0,stateChanges:!0,middleware:!0},this.compressPayloads=t.compressPayloads||!1,this.immediateReporting||this.startReportingCycle(),this.setupRemoteEventListeners()}generateInstanceId(){return`${Date.now()}-${Math.random().toString(36).substring(2,9)}`}setupRemoteEventListeners(){this.store.onStoreEvent("update:complete",(e=>{this.collectCategories?.stateChanges&&(this.trackMetric({name:"store.update.duration",type:"histogram",value:e.duration||0,unit:"ms",labels:{pathCount:String(e.changedPaths?.length||0)}}),this.trackMetric({name:"store.update.paths_changed",type:"counter",value:e.changedPaths?.length||0,labels:{blocked:String(!!e.blocked)}}))})),this.store.onStoreEvent("middleware:error",(e=>{this.collectCategories?.errors&&this.trackMetric({name:"store.middleware.error",type:"log",value:{middleware:e.name,error:e.error?.message||"Unknown error",stack:e.error?.stack},labels:{middlewareName:e.name}})})),this.store.onStoreEvent("transaction:start",(e=>{if(!this.collectCategories?.performance)return;const t=this.beginTrace("transaction");e.traceId=t,this.beginSpan(t,"transaction.execution",{transactionId:e.id||String(Date.now())})})),this.store.onStoreEvent("transaction:complete",(e=>{this.collectCategories?.performance&&e.traceId&&(this.endSpan(e.traceId,"transaction.execution"),this.endTrace(e.traceId))}))}beginTrace(e){const t=`trace-${++this.traceIdCounter}-${Date.now()}`;return this.activeTraces.set(t,{startTime:performance.now(),name:e,spans:{}}),t}beginSpan(e,t,r={}){const s=this.activeTraces.get(e);if(!s)return"";const a=`span-${Date.now()}-${Math.random().toString(36).substring(2,9)}`;return s.spans[a]={startTime:performance.now(),name:t,labels:r},a}endSpan(e,t){const r=this.activeTraces.get(e);if(!r)return;const s=Object.entries(r.spans).filter((([e,r])=>r.name===t&&!r.endTime)).map((([e])=>e));if(s.length>0){const e=s[s.length-1];r.spans[e].endTime=performance.now()}}endTrace(e){const t=this.activeTraces.get(e);if(!t)return;const r=performance.now(),s=r-t.startTime;this.trackMetric({name:"store.trace.duration",type:"histogram",value:s,unit:"ms",labels:{traceName:t.name,traceId:e}}),Object.entries(t.spans).forEach((([t,s])=>{s.endTime||(s.endTime=r);const a=s.endTime-s.startTime;this.trackMetric({name:"store.trace.span.duration",type:"trace",value:a,unit:"ms",labels:{...s.labels,spanName:s.name},traceId:e,parentId:t})})),this.activeTraces.delete(e)}addDestination(e){return this.destinations.has(e.id)?(console.warn(`Destination with ID ${e.id} already exists`),!1):(this.destinations.set(e.id,e),!0)}removeDestination(e){return this.destinations.delete(e)}getDestinations(){return Array.from(this.destinations.values()).map((e=>({id:e.id,name:e.name})))}async testAllConnections(){const e={};for(const[t,r]of this.destinations.entries())if(r.testConnection)try{e[t]=await r.testConnection()}catch(r){e[t]=!1}else e[t]=!0;return e}trackMetric(e){e.labels={...e.labels,environment:this.environment,service:this.serviceName,instanceId:this.instanceId},this.metricsBatch.push(e),(this.immediateReporting||this.metricsBatch.length>=this.batchSize)&&this.flushMetrics()}async flushMetrics(){if(0===this.metricsBatch.length)return;const e={timestamp:Date.now(),source:this.serviceName,metrics:[...this.metricsBatch]};this.metricsBatch=[];const t=Array.from(this.destinations.values()).map((async t=>{try{let r=e;if(this.compressPayloads&&"undefined"!=typeof window&&window.CompressionStream){const t=JSON.stringify(e),s=(new TextEncoder).encode(t),a=new CompressionStream("gzip"),n=new Blob([s]).stream().pipeThrough(a);await new Response(n).blob();r._compressed=!0,r._originalSize=t.length}await t.send(r)}catch(r){if(console.error(`Failed to send metrics to destination ${t.name}:`,r),e.metrics.some((e=>"log"===e.type&&e.name.includes("error")))){const t=e.metrics.filter((e=>"log"===e.type&&e.name.includes("error")));this.metricsBatch.push(...t)}}}));await Promise.all(t)}startReportingCycle(){this.reportingTimer&&clearInterval(this.reportingTimer),this.reportingTimer=setInterval((()=>{this.flushMetrics()}),this.reportingInterval)}setReportingInterval(e){this.reportingInterval=e,this.startReportingCycle()}reportCurrentMetrics(){const e=this.store.getPerformanceMetrics();this.trackMetric({name:"store.performance.update_count",type:"counter",value:e.updateCount,labels:{}}),this.trackMetric({name:"store.performance.listener_executions",type:"counter",value:e.listenerExecutions,labels:{}}),this.trackMetric({name:"store.performance.average_update_time",type:"gauge",value:e.averageUpdateTime,unit:"ms",labels:{}}),this.trackMetric({name:"store.performance.largest_update_size",type:"gauge",value:e.largestUpdateSize,unit:"paths",labels:{}})}disconnect(){this.reportingTimer&&(clearInterval(this.reportingTimer),this.reportingTimer=null),this.flushMetrics().catch((e=>console.error("Error flushing metrics during disconnect:",e))),super.disconnect()}};var y=class e{static dbInstance=null;static eventBusMap=new Map;static dbName="ReactiveDataStore";static modelName="stores";static getDatabase(){return e.dbInstance||(e.dbInstance=s.DatabaseConnection({name:e.dbName,enableTelemetry:!1}).then((async t=>{const r={name:e.modelName,version:"1.0.0",fields:{storeId:{type:"string",required:!0},data:{type:"object",required:!0}}};try{await t.createModel(r)}catch(e){if(e instanceof s.DatabaseError&&"SCHEMA_ALREADY_EXISTS"!==e.type)throw e}return t}))),e.dbInstance}static getEventBus(r){const s=`store_${r}`;return e.eventBusMap.has(s)||e.eventBusMap.set(s,t.createEventBus({batchSize:5,async:!0,batchDelay:16,errorHandler:e=>console.error(`Event bus error for ${r}:`,e),crossTab:!0,channelName:s})),e.eventBusMap.get(s)}static async getCursor(){return(await e.getDatabase()).cursor(e.modelName)}static async close(){const t=await e.dbInstance;t&&t.close(),e.dbInstance=null,e.eventBusMap.forEach((e=>e.clear())),e.eventBusMap.clear()}};exports.IndexedDBPersistence=class{cursorPromise;storeId;eventBus;constructor(e){this.storeId=e,this.cursorPromise=y.getCursor(),this.eventBus=y.getEventBus(e)}async set(e,t){try{const r=await this.cursorPromise,s=(new a.QueryBuilder).where({field:"storeId",operator:"eq",value:this.storeId}).build(),n=await r.find(s.filters),i={storeId:this.storeId,data:t};let o;return n?o=await n.update(i):(await r.create(i),o=!0),o&&this.eventBus.emit({name:"store:updated",payload:{storeId:this.storeId,instanceId:e,state:t}}),o}catch(t){return console.error(`Failed to set state for store ${this.storeId} by instance ${e}:`,t),!1}}async get(){try{const e=await this.cursorPromise,t=(new a.QueryBuilder).where({field:"storeId",operator:"eq",value:this.storeId}).build(),r=await e.find(t.filters);return r?r.read().then((()=>r.data)):null}catch(e){return console.error(`Failed to get state for store ${this.storeId}:`,e),null}}subscribe(e,t){return this.eventBus.subscribe("store:updated",(({storeId:r,instanceId:s,state:a})=>{r===this.storeId&&s!==e&&t(a)}))}async clear(){try{const e=await this.cursorPromise,t=(new a.QueryBuilder).where({field:"storeId",operator:"eq",value:this.storeId}).build(),r=await e.find(t.filters);return!r||await r.delete()}catch(e){return console.error(`Failed to clear state for store ${this.storeId}:`,e),!1}}static async closeAll(){await y.close()}},exports.LocalStoragePersistence=class{storageKey;bus;constructor(e){this.storageKey=e,this.bus=t.createEventBus({async:!0,batchSize:5,batchDelay:16,errorHandler:t=>console.error(`Event bus error for ${e}:`,t),crossTab:!0,channelName:`storage_${e}`}),window.addEventListener("storage",(e=>{if(e.key===this.storageKey&&e.newValue)try{const t=JSON.parse(e.newValue);this.bus.emit({name:"storage:updated",payload:{storageKey:this.storageKey,instanceId:"external",state:t}})}catch(e){console.error("Failed to parse storage event data:",e)}}))}set(e,t){try{const r=JSON.stringify(t);return localStorage.setItem(this.storageKey,r),this.bus.emit({name:"storage:updated",payload:{storageKey:this.storageKey,instanceId:e,state:t}}),!0}catch(e){return console.error(`Failed to persist state to localStorage for ${this.storageKey}:`,e),!1}}get(){try{const e=localStorage.getItem(this.storageKey);return e?JSON.parse(e):null}catch(e){return console.error(`Failed to retrieve state from localStorage for ${this.storageKey}:`,e),null}}subscribe(e,t){return this.bus.subscribe("storage:updated",(({storageKey:r,instanceId:s,state:a})=>{r===this.storageKey&&s!==e&&t(a)}))}clear(){try{return localStorage.removeItem(this.storageKey),!0}catch(e){return console.error(`Failed to clear persisted state for ${this.storageKey}:`,e),!1}}},exports.ReactiveDataStore=c,exports.RemoteObservability=g,exports.StoreObservability=i,exports.createGrafanaCloudDestination=m,exports.createHttpDestination=p,exports.createOpenTelemetryDestination=u,exports.createPrometheusDestination=h,exports.createStore=function(t,{enableMetrics:r,...s}={}){const a=new c(t.state,s.persistence),n=r?new i(a,s):void 0,o=new d;t.middleware&&Object.entries(t.middleware).forEach((([e,t])=>a.use(t,e))),t.blockingMiddleware&&Object.entries(t.blockingMiddleware).forEach((([e,t])=>a.useBlockingMiddleware(t,e)));const u=new l,h=Object.entries(t.actions).reduce(((e,[t,r])=>(e[t]=async(...e)=>{const s=crypto.randomUUID(),n=performance.now(),i={id:s,name:t,params:e,startTime:n};try{await a.set((async t=>{const s=await r(t,...e);return i.result=s,s})),i.status="success"}catch(e){throw i.status="error",i.error=e instanceof Error?e:new Error(String(e)),console.error(`Error in action ${t}:`,e),e}finally{i.endTime=performance.now(),i.duration=i.endTime-n,u.track(i)}},e)),{});function m(e,t){const r=function(e,t="."){const r=[],s={get:(e,t)=>{const a=[];return r.length>0&&a.push(...r[r.length-1]),a.push(t),r.push(a),new Proxy({},s)}};return e(new Proxy({},s)),r.map((e=>e.join(t)))}(e);return a.subscribe(r,t)}const p=()=>a.get();return function(){const t=e.useCallback((t=>{const r=o.create(t);return e.useSyncExternalStore((e=>m(r,e)),(()=>r(a.get())),(()=>r(a.get())))}),[]),r=e.useCallback((()=>e.useSyncExternalStore((e=>a.subscribe("",e)),p,p)),[]);return{store:a,observer:n,select:t,actions:h,actionTracker:u,get state(){return r}}}},exports.useRemoteObservability=function(e,t){const r=new g(e,t);return{remote:r,addOpenTelemetryDestination:e=>r.addDestination(u(e)),addPrometheusDestination:e=>r.addDestination(h(e)),addGrafanaCloudDestination:e=>r.addDestination(m(e)),addHttpDestination:e=>r.addDestination(p(e))}};
|
|
1
|
+
"use strict";var e=require("react"),t=require("@asaidimu/events"),s=require("uuid"),r=require("@asaidimu/indexed"),a=require("@asaidimu/query");function n(e,t){const s=new Set;function r(e,t){const s=e.split(".");for(let e=0;e<s.length;e++){const r=s.slice(0,e+1).join(".");t.add(r)}}return function e(t,a,n){const i=Symbol.for("delete");if(Array.isArray(a)&&Array.isArray(n))JSON.stringify(a)!==JSON.stringify(n)&&r(t,s);else for(const o of Object.keys(n)){const c=t?`${t}.${o}`:o;if(o in a){const t=a[o],d=n[o];d===i?(r(c,s),e(c,{},n[o])):"object"==typeof t&&null!==t&&"object"==typeof d&&null!==d?e(c,t,d):t!==d&&r(c,s)}else r(c,s),e(c,{},n[o])}}("",e||{},t||{}),Array.from(s)}var i=class{store;eventHistory=[];maxEvents;enableConsoleLogging;logEvents;performanceThresholds;unsubscribers=[];stateHistory=[];maxStateHistory=20;activeTransactionCount=0;activeBatches=new Set;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"];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=>{"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===n(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.store.getMiddlewareExecutions()}getPerformanceMetrics(){return this.store.getPerformanceMetrics()}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],a=n(r,s),i={},o={};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),n=(e,t,s)=>{const r=t.length-1,a=t[r];t.slice(0,r).reduce(((e,t)=>(e[t]=e[t]??{},e[t])),e)[a]=s},c=a(r,t),d=a(s,t);n(i,t,c),n(o,t,d)}let c=Date.now();for(const e of this.eventHistory)if("update:complete"===e.type&&e.data.changedPaths?.length>0){c=e.timestamp;break}0!==a.length&&t.push({timestamp:c,changedPaths:a,from:i,to:o})}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()}};function o(e,t){const s=Symbol.for("delete");if("object"!=typeof e||null===e)return t;if("object"!=typeof t||null===t)return e;const r=structuredClone(e),a=[{target:r,source:t}];for(;a.length>0;){const{target:e,source:t}=a.pop();Object.keys(t).forEach((r=>{const n=t[r],i=e[r];n===s?delete e[r]:Array.isArray(n)?e[r]=n:"object"==typeof n&&null!==n&&"object"==typeof i&&null!==i?(e.hasOwnProperty(r)||(e[r]=structuredClone(n)),a.push({target:e[r],source:n})):e[r]=n}))}return r}var c=class{metrics;middleware;blockingMiddleware;middlewareExecutions=[];maxExecutionHistory=100;pendingUpdates;isUpdating;updateTimes;cache=null;updateBus=t.createEventBus();eventBus=t.createEventBus();executionState;persistence;instanceID=s.v4();persistenceReady=!1;constructor(e,t){this.middleware=[],this.blockingMiddleware=[],this.pendingUpdates=[],this.isUpdating=!1,this.updateTimes=[],this.metrics={updateCount:0,listenerExecutions:0,averageUpdateTime:0,largestUpdateSize:0,mostActiveListenerPaths:[]},this.executionState={executing:!1,changes:null,pendingChanges:[],middlewares:[],runningMiddleware:null,transactionActive:!1},this.cache=e,t?this.setPersistence(t):this.setPersistenceReady()}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&&await this.set(e)}catch(e){console.error("Failed to initialize persistence:",e)}finally{this.setPersistenceReady()}this.persistence.subscribe(this.instanceID,(async e=>{this.isUpdating=!0;try{const t=n(this.get(),e);t.length>0&&(await this.set(e),this.eventBus.emit({name:"update:complete",payload:{changedPaths:t,source:"external",timestamp:Date.now()}}))}finally{this.isUpdating=!1}}))}isReady(){return this.persistenceReady}getExecutionState(){return this.executionState}get(){return this.cache}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}});let s=e;if("function"==typeof e){const t=e(structuredClone(this.get()));s=t instanceof Promise?await t:t}try{const e=await this.applyBlockingMiddleware(s);if(e.blocked)return void this.eventBus.emit({name:"update:complete",payload:{blocked:!0,error:e.error,timestamp:Date.now()}})}catch(e){throw this.eventBus.emit({name:"update:complete",payload:{blocked:!0,error:e,timestamp:Date.now()}}),e}const r=o(this.get(),s),a=await this.applyMiddleware(r,s);try{const e=n(this.get(),a);if(this.metrics.updateCount++,this.metrics.largestUpdateSize=Math.max(this.metrics.largestUpdateSize,e.length),e.length>0){this.cache=a;try{await(this.persistence?.set(this.instanceID,this.get()))||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()}})}this.notifyListeners(e)}const s=performance.now();this.updateTimes.push(s-t),this.updateTimes.length>100&&this.updateTimes.shift(),this.metrics.averageUpdateTime=this.updateTimes.reduce(((e,t)=>e+t),0)/this.updateTimes.length,this.eventBus.emit({name:"update:complete",payload:{changedPaths:e,duration:s-t,timestamp:Date.now()}})}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)}}}async applyBlockingMiddleware(e){for(const{fn:t,name:s,id:r}of this.blockingMiddleware){const a={id:r,name:s,startTime:performance.now()};this.executionState.runningMiddleware={id:r,name:s,startTime:performance.now()},this.eventBus.emit({name:"middleware:start",payload:{id:r,name:s,type:"blocking",timestamp:Date.now()}});try{const n=await Promise.resolve(t(this.get(),e));if(a.endTime=performance.now(),a.duration=void 0!==a.startTime?a.endTime-a.startTime:0,!1===n)return a.blocked=!0,this.trackMiddlewareExecution(a),this.eventBus.emit({name:"middleware:blocked",payload:{id:r,name:s,duration:a.duration,timestamp:Date.now()}}),{blocked:!0};this.eventBus.emit({name:"middleware:complete",payload:{id:r,name:s,type:"blocking",duration:a.duration,timestamp:Date.now()}}),this.trackMiddlewareExecution({...a,blocked:!1})}catch(e){return a.endTime=performance.now(),a.duration=void 0!==a.startTime?a.endTime-a.startTime:0,a.error=e instanceof Error?e:new Error(String(e)),a.blocked=!0,this.trackMiddlewareExecution(a),this.eventBus.emit({name:"middleware:error",payload:{id:r,name:s,error:a.error,duration:a.duration,timestamp:Date.now()}}),{blocked:!0,error:a.error}}finally{this.executionState.runningMiddleware=null}}return{blocked:!1}}async applyMiddleware(e,t){let s=e;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.eventBus.emit({name:"middleware:start",payload:{id:a,name:r,type:"transform",timestamp:Date.now()}});try{const i=await Promise.resolve(e(s,t));n.endTime=performance.now(),n.duration=void 0!==n.startTime?n.endTime-n.startTime:0,n.blocked=!1,i&&"object"==typeof i&&(s=o(s,i)),this.trackMiddlewareExecution(n),this.eventBus.emit({name:"middleware:complete",payload:{id:a,name:r,type:"transform",duration:n.duration,timestamp:Date.now()}})}catch(e){n.endTime=performance.now(),n.duration=void 0!==n.startTime?n.endTime-n.startTime:0,n.error=e instanceof Error?e:new Error(String(e)),n.blocked=!1,this.trackMiddlewareExecution(n),this.eventBus.emit({name:"middleware:error",payload:{id:a,name:r,error:n.error,duration:n.duration,timestamp:Date.now()}}),console.error(`Middleware ${r} error:`,e)}finally{this.executionState.runningMiddleware=null}}return s}subscribe(e,t){const s=Array.isArray(e)?e:[e];return this.updateBus.subscribe("update",(r=>{(s.includes(r)||""===e)&&(t(this.get()),this.metrics.listenerExecutions++)}))}id(){return this.instanceID}async transaction(e){const t=structuredClone(this.get());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.cache=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}}use(e,t="unnamed-middleware"){const s=crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,15)}`;return this.middleware.push({fn:e,name:t,id:s}),this.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))],s}useBlockingMiddleware(e,t="unnamed-blocking-middleware"){const s=crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,15)}`;return this.blockingMiddleware.push({fn:e,name:t,id:s}),this.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))],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.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))],this.middleware.length+this.blockingMiddleware.length<t}getPerformanceMetrics(){return structuredClone(this.metrics)}getMiddlewareExecutions(){return structuredClone(this.middlewareExecutions)}onStoreEvent(e,t){return this.eventBus.subscribe(e,t)}notifyListeners(e){e.forEach((e=>this.updateBus.emit({name:"update",payload:e})))}trackMiddlewareExecution(e){this.middlewareExecutions.unshift(e),this.middlewareExecutions.length>this.maxExecutionHistory&&this.middlewareExecutions.pop()}};var d=class{selectorCache=new WeakMap;create(e){return t=>{let s=this.selectorCache.get(e);s||(s=new WeakMap,this.selectorCache.set(e,s));const r=s.get(t);if(r)return r.result;const a=e(t);return s.set(t,{result:a,deps:[]}),a}}},l=class{executions=[];maxHistory=100;listeners=new Set;track(e){this.executions.unshift(e),this.executions.length>this.maxHistory&&this.executions.pop(),this.notify()}getExecutions(){return[...this.executions]}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){this.listeners.forEach((e=>e()))}};function u(e){return{id:"opentelemetry",name:"OpenTelemetry Collector",config:e,testConnection:async()=>{try{return(await fetch(`${e.endpoint}/v1/metrics`,{method:"OPTIONS",headers:{...e.apiKey?{"api-key":e.apiKey}:{}}})).ok}catch(e){return!1}},send:async t=>{const s={resourceMetrics:[{resource:{attributes:{"service.name":t.source,...e.resource}},scopeMetrics:[{metrics:t.metrics.map((e=>"counter"===e.type?{name:e.name,unit:e.unit||"1",sum:{dataPoints:[{timeUnixNano:BigInt(1e6*t.timestamp),asInt:Number(e.value),attributes:Object.entries(e.labels||{}).map((([e,t])=>({key:e,value:{stringValue:t}})))}]}}:"histogram"===e.type?{name:e.name,unit:e.unit||"ms",histogram:{dataPoints:[{timeUnixNano:BigInt(1e6*t.timestamp),count:1,sum:Number(e.value),attributes:Object.entries(e.labels||{}).map((([e,t])=>({key:e,value:{stringValue:t}})))}]}}:{name:e.name,unit:e.unit||"1",gauge:{dataPoints:[{timeUnixNano:BigInt(1e6*t.timestamp),asDouble:"number"==typeof e.value?e.value:1,attributes:Object.entries(e.labels||{}).map((([e,t])=>({key:e,value:{stringValue:t}})))}]}}))}]}]};await fetch(`${e.endpoint}/v1/metrics`,{method:"POST",headers:{"Content-Type":"application/json",...e.apiKey?{"api-key":e.apiKey}:{}},body:JSON.stringify(s)})}}}function h(e){return{id:"prometheus",name:"Prometheus Pushgateway",config:e,testConnection:async()=>{try{const t=e.username&&e.password?`Basic ${btoa(`${e.username}:${e.password}`)}`:void 0;return(await fetch(e.pushgatewayUrl,{method:"HEAD",headers:{...t?{Authorization:t}:{}}})).ok}catch(e){return!1}},send:async t=>{let s="";for(const e of t.metrics){if("log"===e.type||"trace"===e.type)continue;const r=Object.entries({...e.labels,source:t.source,instance:t.metrics.find((e=>e.labels?.instanceId))?.labels?.instanceId||"unknown"}).map((([e,t])=>`${e}="${t.replace(/"/g,'\\"')}"`)).join(","),a=e.name.replace(/\./g,"_");"counter"===e.type?s+=`# TYPE ${a} counter\n`:s+=`# TYPE ${a} gauge\n`,s+=`${a}{${r}} ${e.value}\n`}const r=e.username&&e.password?`Basic ${btoa(`${e.username}:${e.password}`)}`:void 0;await fetch(`${e.pushgatewayUrl}/metrics/job/${e.jobName}`,{method:"POST",headers:{"Content-Type":"text/plain",...r?{Authorization:r}:{}},body:s})}}}function m(e){return{id:"grafana-cloud",name:"Grafana Cloud",config:e,testConnection:async()=>{try{return(await fetch(`${e.url}/api/v1/query?query=up`,{method:"GET",headers:{Authorization:`Bearer ${e.apiKey}`}})).ok}catch(e){return!1}},send:async t=>{const s={streams:[{stream:{service:t.source,job:"store-metrics"},values:t.metrics.map((e=>{const s=1e6*t.timestamp;let r="";if("object"==typeof e.value)r=JSON.stringify(e.value);else if(r=`level=info metric=${e.name} value=${e.value} type=${e.type}`,e.labels)for(const[t,s]of Object.entries(e.labels))r+=` ${t}=${s}`;return[String(s),r]}))}]};await fetch(`${e.url}/loki/api/v1/push`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},body:JSON.stringify(s)})}}}function p(e){return{id:"http-endpoint",name:"HTTP Metrics Endpoint",config:e,testConnection:async()=>{try{return(await fetch(e.url,{method:"OPTIONS",headers:e.headers})).ok}catch(e){return!1}},send:async t=>{const s=e.transformPayload?e.transformPayload(t):t;await fetch(e.url,{method:e.method||"POST",headers:{"Content-Type":"application/json",...e.headers},body:JSON.stringify(s)})}}}var g=class extends i{destinations=new Map;metricsBatch=[];reportingInterval;batchSize;serviceName;environment;instanceId;immediateReporting;collectCategories;compressPayloads;reportingTimer=null;traceIdCounter=0;activeTraces=new Map;constructor(e,t){super(e,t),this.serviceName=t.serviceName,this.environment=t.environment,this.instanceId=t.instanceId||this.generateInstanceId(),this.reportingInterval=t.reportingInterval||3e4,this.batchSize=t.batchSize||100,this.immediateReporting=t.immediateReporting||!1,this.collectCategories=t.collectCategories||{performance:!0,errors:!0,stateChanges:!0,middleware:!0},this.compressPayloads=t.compressPayloads||!1,this.immediateReporting||this.startReportingCycle(),this.setupRemoteEventListeners()}generateInstanceId(){return`${Date.now()}-${Math.random().toString(36).substring(2,9)}`}setupRemoteEventListeners(){this.store.onStoreEvent("update:complete",(e=>{this.collectCategories?.stateChanges&&(this.trackMetric({name:"store.update.duration",type:"histogram",value:e.duration||0,unit:"ms",labels:{pathCount:String(e.changedPaths?.length||0)}}),this.trackMetric({name:"store.update.paths_changed",type:"counter",value:e.changedPaths?.length||0,labels:{blocked:String(!!e.blocked)}}))})),this.store.onStoreEvent("middleware:error",(e=>{this.collectCategories?.errors&&this.trackMetric({name:"store.middleware.error",type:"log",value:{middleware:e.name,error:e.error?.message||"Unknown error",stack:e.error?.stack},labels:{middlewareName:e.name}})})),this.store.onStoreEvent("transaction:start",(e=>{if(!this.collectCategories?.performance)return;const t=this.beginTrace("transaction");e.traceId=t,this.beginSpan(t,"transaction.execution",{transactionId:e.id||String(Date.now())})})),this.store.onStoreEvent("transaction:complete",(e=>{this.collectCategories?.performance&&e.traceId&&(this.endSpan(e.traceId,"transaction.execution"),this.endTrace(e.traceId))}))}beginTrace(e){const t=`trace-${++this.traceIdCounter}-${Date.now()}`;return this.activeTraces.set(t,{startTime:performance.now(),name:e,spans:{}}),t}beginSpan(e,t,s={}){const r=this.activeTraces.get(e);if(!r)return"";const a=`span-${Date.now()}-${Math.random().toString(36).substring(2,9)}`;return r.spans[a]={startTime:performance.now(),name:t,labels:s},a}endSpan(e,t){const s=this.activeTraces.get(e);if(!s)return;const r=Object.entries(s.spans).filter((([e,s])=>s.name===t&&!s.endTime)).map((([e])=>e));if(r.length>0){const e=r[r.length-1];s.spans[e].endTime=performance.now()}}endTrace(e){const t=this.activeTraces.get(e);if(!t)return;const s=performance.now(),r=s-t.startTime;this.trackMetric({name:"store.trace.duration",type:"histogram",value:r,unit:"ms",labels:{traceName:t.name,traceId:e}}),Object.entries(t.spans).forEach((([t,r])=>{r.endTime||(r.endTime=s);const a=r.endTime-r.startTime;this.trackMetric({name:"store.trace.span.duration",type:"trace",value:a,unit:"ms",labels:{...r.labels,spanName:r.name},traceId:e,parentId:t})})),this.activeTraces.delete(e)}addDestination(e){return this.destinations.has(e.id)?(console.warn(`Destination with ID ${e.id} already exists`),!1):(this.destinations.set(e.id,e),!0)}removeDestination(e){return this.destinations.delete(e)}getDestinations(){return Array.from(this.destinations.values()).map((e=>({id:e.id,name:e.name})))}async testAllConnections(){const e={};for(const[t,s]of this.destinations.entries())if(s.testConnection)try{e[t]=await s.testConnection()}catch(s){e[t]=!1}else e[t]=!0;return e}trackMetric(e){e.labels={...e.labels,environment:this.environment,service:this.serviceName,instanceId:this.instanceId},this.metricsBatch.push(e),(this.immediateReporting||this.metricsBatch.length>=this.batchSize)&&this.flushMetrics()}async flushMetrics(){if(0===this.metricsBatch.length)return;const e={timestamp:Date.now(),source:this.serviceName,metrics:[...this.metricsBatch]};this.metricsBatch=[];const t=Array.from(this.destinations.values()).map((async t=>{try{let s=e;if(this.compressPayloads&&"undefined"!=typeof window&&window.CompressionStream){const t=JSON.stringify(e),r=(new TextEncoder).encode(t),a=new CompressionStream("gzip"),n=new Blob([r]).stream().pipeThrough(a);await new Response(n).blob();s._compressed=!0,s._originalSize=t.length}await t.send(s)}catch(s){if(console.error(`Failed to send metrics to destination ${t.name}:`,s),e.metrics.some((e=>"log"===e.type&&e.name.includes("error")))){const t=e.metrics.filter((e=>"log"===e.type&&e.name.includes("error")));this.metricsBatch.push(...t)}}}));await Promise.all(t)}startReportingCycle(){this.reportingTimer&&clearInterval(this.reportingTimer),this.reportingTimer=setInterval((()=>{this.flushMetrics()}),this.reportingInterval)}setReportingInterval(e){this.reportingInterval=e,this.startReportingCycle()}reportCurrentMetrics(){const e=this.store.getPerformanceMetrics();this.trackMetric({name:"store.performance.update_count",type:"counter",value:e.updateCount,labels:{}}),this.trackMetric({name:"store.performance.listener_executions",type:"counter",value:e.listenerExecutions,labels:{}}),this.trackMetric({name:"store.performance.average_update_time",type:"gauge",value:e.averageUpdateTime,unit:"ms",labels:{}}),this.trackMetric({name:"store.performance.largest_update_size",type:"gauge",value:e.largestUpdateSize,unit:"paths",labels:{}})}disconnect(){this.reportingTimer&&(clearInterval(this.reportingTimer),this.reportingTimer=null),this.flushMetrics().catch((e=>console.error("Error flushing metrics during disconnect:",e))),super.disconnect()}};var y=class e{static dbInstance=null;static eventBusMap=new Map;static dbName="ReactiveDataStore";static modelName="stores";static getDatabase(){return e.dbInstance||(e.dbInstance=r.DatabaseConnection({name:e.dbName,enableTelemetry:!1}).then((async t=>{const s={name:e.modelName,version:"1.0.0",fields:{storeId:{type:"string",required:!0},data:{type:"object",required:!0}}};try{await t.createModel(s)}catch(e){if(e instanceof r.DatabaseError&&"SCHEMA_ALREADY_EXISTS"!==e.type)throw e}return t}))),e.dbInstance}static getEventBus(s){const r=`store_${s}`;return e.eventBusMap.has(r)||e.eventBusMap.set(r,t.createEventBus({batchSize:5,async:!0,batchDelay:16,errorHandler:e=>console.error(`Event bus error for ${s}:`,e),crossTab:!0,channelName:r})),e.eventBusMap.get(r)}static async getCursor(){return(await e.getDatabase()).cursor(e.modelName)}static async close(){const t=await e.dbInstance;t&&t.close(),e.dbInstance=null,e.eventBusMap.forEach((e=>e.clear())),e.eventBusMap.clear()}};exports.IndexedDBPersistence=class{cursorPromise;storeId;eventBus;constructor(e){this.storeId=e,this.cursorPromise=y.getCursor(),this.eventBus=y.getEventBus(e)}async set(e,t){try{const s=await this.cursorPromise,r=(new a.QueryBuilder).where({field:"storeId",operator:"eq",value:this.storeId}).build(),n=await s.find(r.filters),i={storeId:this.storeId,data:t};let o;return n?o=await n.update(i):(await s.create(i),o=!0),o&&this.eventBus.emit({name:"store:updated",payload:{storeId:this.storeId,instanceId:e,state:t}}),o}catch(t){return console.error(`Failed to set state for store ${this.storeId} by instance ${e}:`,t),!1}}async get(){try{const e=await this.cursorPromise,t=(new a.QueryBuilder).where({field:"storeId",operator:"eq",value:this.storeId}).build(),s=await e.find(t.filters);return s?s.read().then((()=>s.data)):null}catch(e){return console.error(`Failed to get state for store ${this.storeId}:`,e),null}}subscribe(e,t){return this.eventBus.subscribe("store:updated",(({storeId:s,instanceId:r,state:a})=>{s===this.storeId&&r!==e&&t(a)}))}async clear(){try{const e=await this.cursorPromise,t=(new a.QueryBuilder).where({field:"storeId",operator:"eq",value:this.storeId}).build(),s=await e.find(t.filters);return!s||await s.delete()}catch(e){return console.error(`Failed to clear state for store ${this.storeId}:`,e),!1}}static async closeAll(){await y.close()}},exports.LocalStoragePersistence=class{storageKey;bus;constructor(e){this.storageKey=e,this.bus=t.createEventBus({async:!0,batchSize:5,batchDelay:16,errorHandler:t=>console.error(`Event bus error for ${e}:`,t),crossTab:!0,channelName:`storage_${e}`}),window.addEventListener("storage",(e=>{if(e.key===this.storageKey&&e.newValue)try{const t=JSON.parse(e.newValue);this.bus.emit({name:"storage:updated",payload:{storageKey:this.storageKey,instanceId:"external",state:t}})}catch(e){console.error("Failed to parse storage event data:",e)}}))}set(e,t){try{const s=JSON.stringify(t);return localStorage.setItem(this.storageKey,s),this.bus.emit({name:"storage:updated",payload:{storageKey:this.storageKey,instanceId:e,state:t}}),!0}catch(e){return console.error(`Failed to persist state to localStorage for ${this.storageKey}:`,e),!1}}get(){try{const e=localStorage.getItem(this.storageKey);return e?JSON.parse(e):null}catch(e){return console.error(`Failed to retrieve state from localStorage for ${this.storageKey}:`,e),null}}subscribe(e,t){return this.bus.subscribe("storage:updated",(({storageKey:s,instanceId:r,state:a})=>{s===this.storageKey&&r!==e&&t(a)}))}clear(){try{return localStorage.removeItem(this.storageKey),!0}catch(e){return console.error(`Failed to clear persisted state for ${this.storageKey}:`,e),!1}}},exports.ReactiveDataStore=c,exports.RemoteObservability=g,exports.StoreObservability=i,exports.createGrafanaCloudDestination=m,exports.createHttpDestination=p,exports.createOpenTelemetryDestination=u,exports.createPrometheusDestination=h,exports.createStore=function(t,{enableMetrics:s,...r}={}){const a=new c(t.state,r.persistence),n=s?new i(a,r):void 0,o=new d;t.middleware&&Object.entries(t.middleware).forEach((([e,t])=>a.use(t,e))),t.blockingMiddleware&&Object.entries(t.blockingMiddleware).forEach((([e,t])=>a.useBlockingMiddleware(t,e)));const u=new l,h=Object.entries(t.actions).reduce(((e,[t,s])=>(e[t]=async(...e)=>{const r=crypto.randomUUID(),n=performance.now(),i={id:r,name:t,params:e,startTime:n};try{await a.set((async t=>{const r=await s(t,...e);return i.result=r,r})),i.status="success"}catch(e){throw i.status="error",i.error=e instanceof Error?e:new Error(String(e)),console.error(`Error in action ${t}:`,e),e}finally{i.endTime=performance.now(),i.duration=i.endTime-n,u.track(i)}},e)),{});function m(e,t){const s=function(e,t="."){const s=[],r={get:(e,t)=>{const a=[];return s.length>0&&a.push(...s[s.length-1]),a.push(t),s.push(a),new Proxy({},r)}};return e(new Proxy({},r)),s.map((e=>e.join(t)))}(e);return a.subscribe(s,t)}const p=()=>a.get(),g=e=>(a.isReady()&&e(),a.onStoreEvent("persistence:ready",e)),y=()=>a.isReady();return function(){const t=e.useCallback((t=>{const s=o.create(t);return e.useSyncExternalStore((e=>m(s,e)),(()=>s(a.get())),(()=>s(a.get())))}),[]),s=e.useCallback((()=>e.useSyncExternalStore((e=>a.subscribe("",e)),p,p)),[]),r=e.useSyncExternalStore(g,y,y);return{store:a,observer:n,select:t,actions:h,isReady:r,actionTracker:u,get state(){return s}}}},exports.useRemoteObservability=function(e,t){const s=new g(e,t);return{remote:s,addOpenTelemetryDestination:e=>s.addDestination(u(e)),addPrometheusDestination:e=>s.addDestination(h(e)),addGrafanaCloudDestination:e=>s.addDestination(m(e)),addHttpDestination:e=>s.addDestination(p(e))}};
|
package/index.d.cts
CHANGED
|
@@ -68,7 +68,7 @@ interface StoreExecutionState<T> {
|
|
|
68
68
|
/**
|
|
69
69
|
* Event types emitted by the state for observability
|
|
70
70
|
*/
|
|
71
|
-
type StoreEvent = "update:start" | "update:complete" | "middleware:start" | "middleware:complete" | "middleware:error" | "middleware:blocked" | "transaction:start" | "transaction:complete" | "transaction:error";
|
|
71
|
+
type StoreEvent = "update:start" | "update:complete" | "middleware:start" | "middleware:complete" | "middleware:error" | "middleware:blocked" | "transaction:start" | "transaction:complete" | "transaction:error" | "persistence:ready";
|
|
72
72
|
/**
|
|
73
73
|
* Type for middleware functions. Return false to block the update.
|
|
74
74
|
*/
|
|
@@ -128,12 +128,15 @@ declare class ReactiveDataStore<T extends object> implements DataStore<T> {
|
|
|
128
128
|
private executionState;
|
|
129
129
|
private persistence?;
|
|
130
130
|
private instanceID;
|
|
131
|
+
private persistenceReady;
|
|
131
132
|
/**
|
|
132
133
|
* Creates a new instance of ReactiveDataStore.
|
|
133
134
|
* @param initialData The initial state data.
|
|
134
135
|
*/
|
|
135
136
|
constructor(initialData: T, persistence?: DataStorePersistence<T>);
|
|
137
|
+
private setPersistenceReady;
|
|
136
138
|
private setPersistence;
|
|
139
|
+
isReady(): boolean;
|
|
137
140
|
/**
|
|
138
141
|
* Gets current execution state for monitoring
|
|
139
142
|
* @returns The current execution state of the store
|
|
@@ -457,6 +460,7 @@ declare function createStore<T extends Record<string, unknown>, R extends Action
|
|
|
457
460
|
readonly select: <S>(selector: (state: T) => S) => S;
|
|
458
461
|
/** Action dispatchers */
|
|
459
462
|
readonly actions: { [K in keyof R]: (...args: Parameters<R[K]> extends [T, ...infer P] ? P : never) => void; };
|
|
463
|
+
readonly isReady: boolean;
|
|
460
464
|
/** track actions */
|
|
461
465
|
readonly actionTracker: ActionTracker;
|
|
462
466
|
/** Complete store state */
|
package/index.d.ts
CHANGED
|
@@ -68,7 +68,7 @@ interface StoreExecutionState<T> {
|
|
|
68
68
|
/**
|
|
69
69
|
* Event types emitted by the state for observability
|
|
70
70
|
*/
|
|
71
|
-
type StoreEvent = "update:start" | "update:complete" | "middleware:start" | "middleware:complete" | "middleware:error" | "middleware:blocked" | "transaction:start" | "transaction:complete" | "transaction:error";
|
|
71
|
+
type StoreEvent = "update:start" | "update:complete" | "middleware:start" | "middleware:complete" | "middleware:error" | "middleware:blocked" | "transaction:start" | "transaction:complete" | "transaction:error" | "persistence:ready";
|
|
72
72
|
/**
|
|
73
73
|
* Type for middleware functions. Return false to block the update.
|
|
74
74
|
*/
|
|
@@ -128,12 +128,15 @@ declare class ReactiveDataStore<T extends object> implements DataStore<T> {
|
|
|
128
128
|
private executionState;
|
|
129
129
|
private persistence?;
|
|
130
130
|
private instanceID;
|
|
131
|
+
private persistenceReady;
|
|
131
132
|
/**
|
|
132
133
|
* Creates a new instance of ReactiveDataStore.
|
|
133
134
|
* @param initialData The initial state data.
|
|
134
135
|
*/
|
|
135
136
|
constructor(initialData: T, persistence?: DataStorePersistence<T>);
|
|
137
|
+
private setPersistenceReady;
|
|
136
138
|
private setPersistence;
|
|
139
|
+
isReady(): boolean;
|
|
137
140
|
/**
|
|
138
141
|
* Gets current execution state for monitoring
|
|
139
142
|
* @returns The current execution state of the store
|
|
@@ -457,6 +460,7 @@ declare function createStore<T extends Record<string, unknown>, R extends Action
|
|
|
457
460
|
readonly select: <S>(selector: (state: T) => S) => S;
|
|
458
461
|
/** Action dispatchers */
|
|
459
462
|
readonly actions: { [K in keyof R]: (...args: Parameters<R[K]> extends [T, ...infer P] ? P : never) => void; };
|
|
463
|
+
readonly isReady: boolean;
|
|
460
464
|
/** track actions */
|
|
461
465
|
readonly actionTracker: ActionTracker;
|
|
462
466
|
/** Complete store state */
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useCallback as e,useSyncExternalStore as t}from"react";import{createEventBus as r}from"@asaidimu/events";import{v4 as s}from"uuid";import{DatabaseConnection as a,DatabaseError as n}from"@asaidimu/indexed";import{QueryBuilder as i}from"@asaidimu/query";function o(e,t){const r=new Set;function s(e,t){const r=e.split(".");for(let e=0;e<r.length;e++){const s=r.slice(0,e+1).join(".");t.add(s)}}return function e(t,a,n){const i=Symbol.for("delete");if(Array.isArray(a)&&Array.isArray(n))JSON.stringify(a)!==JSON.stringify(n)&&s(t,r);else for(const o of Object.keys(n)){const c=t?`${t}.${o}`:o;if(o in a){const t=a[o],d=n[o];d===i?(s(c,r),e(c,{},n[o])):"object"==typeof t&&null!==t&&"object"==typeof d&&null!==d?e(c,t,d):t!==d&&s(c,r)}else s(c,r),e(c,{},n[o])}}("",e||{},t||{}),Array.from(r)}var c=class{store;eventHistory=[];maxEvents;enableConsoleLogging;logEvents;performanceThresholds;unsubscribers=[];stateHistory=[];maxStateHistory=20;activeTransactionCount=0;activeBatches=new Set;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"];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=>{"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===o(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.store.getMiddlewareExecutions()}getPerformanceMetrics(){return this.store.getPerformanceMetrics()}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=o(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 c=Date.now();for(const e of this.eventHistory)if("update:complete"===e.type&&e.data.changedPaths?.length>0){c=e.timestamp;break}0!==a.length&&t.push({timestamp:c,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()}};function d(e,t){const r=Symbol.for("delete");if("object"!=typeof e||null===e)return t;if("object"!=typeof t||null===t)return e;const s=structuredClone(e),a=[{target:s,source:t}];for(;a.length>0;){const{target:e,source:t}=a.pop();Object.keys(t).forEach((s=>{const n=t[s],i=e[s];n===r?delete e[s]:Array.isArray(n)?e[s]=n:"object"==typeof n&&null!==n&&"object"==typeof i&&null!==i?(e.hasOwnProperty(s)||(e[s]=structuredClone(n)),a.push({target:e[s],source:n})):e[s]=n}))}return s}var l=class{metrics;middleware;blockingMiddleware;middlewareExecutions=[];maxExecutionHistory=100;pendingUpdates;isUpdating;updateTimes;cache=null;updateBus=r();eventBus=r();executionState;persistence;instanceID=s();constructor(e,t){this.middleware=[],this.blockingMiddleware=[],this.pendingUpdates=[],this.isUpdating=!1,this.updateTimes=[],this.metrics={updateCount:0,listenerExecutions:0,averageUpdateTime:0,largestUpdateSize:0,mostActiveListenerPaths:[]},this.executionState={executing:!1,changes:null,pendingChanges:[],middlewares:[],runningMiddleware:null,transactionActive:!1},this.cache=e,t&&this.setPersistence(t)}async setPersistence(e){this.persistence=e;const t=await Promise.resolve(this.persistence.get());t&&(this.cache=t),this.persistence.subscribe(this.instanceID,(async e=>{this.isUpdating=!0;try{const t=o(this.get(),e);t.length>0&&(this.cache=e,this.notifyListeners(t),this.eventBus.emit({name:"update:complete",payload:{changedPaths:t,source:"external",timestamp:Date.now()}}))}finally{this.isUpdating=!1}}))}getExecutionState(){return this.executionState}get(){return this.cache}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}});let r=e;if("function"==typeof e){const t=e(structuredClone(this.get()));r=t instanceof Promise?await t:t}try{const e=await this.applyBlockingMiddleware(r);if(e.blocked)return void this.eventBus.emit({name:"update:complete",payload:{blocked:!0,error:e.error,timestamp:Date.now()}})}catch(e){throw this.eventBus.emit({name:"update:complete",payload:{blocked:!0,error:e,timestamp:Date.now()}}),e}const s=d(this.get(),r),a=await this.applyMiddleware(s,r);try{const e=o(this.get(),a);if(this.metrics.updateCount++,this.metrics.largestUpdateSize=Math.max(this.metrics.largestUpdateSize,e.length),e.length>0){this.cache=a;try{await(this.persistence?.set(this.instanceID,this.get()))||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()}})}this.notifyListeners(e)}const r=performance.now();this.updateTimes.push(r-t),this.updateTimes.length>100&&this.updateTimes.shift(),this.metrics.averageUpdateTime=this.updateTimes.reduce(((e,t)=>e+t),0)/this.updateTimes.length,this.eventBus.emit({name:"update:complete",payload:{changedPaths:e,duration:r-t,timestamp:Date.now()}})}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)}}}async applyBlockingMiddleware(e){for(const{fn:t,name:r,id:s}of this.blockingMiddleware){const a={id:s,name:r,startTime:performance.now()};this.executionState.runningMiddleware={id:s,name:r,startTime:performance.now()},this.eventBus.emit({name:"middleware:start",payload:{id:s,name:r,type:"blocking",timestamp:Date.now()}});try{const n=await Promise.resolve(t(this.get(),e));if(a.endTime=performance.now(),a.duration=void 0!==a.startTime?a.endTime-a.startTime:0,!1===n)return a.blocked=!0,this.trackMiddlewareExecution(a),this.eventBus.emit({name:"middleware:blocked",payload:{id:s,name:r,duration:a.duration,timestamp:Date.now()}}),{blocked:!0};this.eventBus.emit({name:"middleware:complete",payload:{id:s,name:r,type:"blocking",duration:a.duration,timestamp:Date.now()}}),this.trackMiddlewareExecution({...a,blocked:!1})}catch(e){return a.endTime=performance.now(),a.duration=void 0!==a.startTime?a.endTime-a.startTime:0,a.error=e instanceof Error?e:new Error(String(e)),a.blocked=!0,this.trackMiddlewareExecution(a),this.eventBus.emit({name:"middleware:error",payload:{id:s,name:r,error:a.error,duration:a.duration,timestamp:Date.now()}}),{blocked:!0,error:a.error}}finally{this.executionState.runningMiddleware=null}}return{blocked:!1}}async applyMiddleware(e,t){let r=e;for(const{fn:e,name:s,id:a}of this.middleware){const n={id:a,name:s,startTime:performance.now()};this.executionState.runningMiddleware={id:a,name:s,startTime:performance.now()},this.eventBus.emit({name:"middleware:start",payload:{id:a,name:s,type:"transform",timestamp:Date.now()}});try{const i=await Promise.resolve(e(r,t));n.endTime=performance.now(),n.duration=void 0!==n.startTime?n.endTime-n.startTime:0,n.blocked=!1,i&&"object"==typeof i&&(r=d(r,i)),this.trackMiddlewareExecution(n),this.eventBus.emit({name:"middleware:complete",payload:{id:a,name:s,type:"transform",duration:n.duration,timestamp:Date.now()}})}catch(e){n.endTime=performance.now(),n.duration=void 0!==n.startTime?n.endTime-n.startTime:0,n.error=e instanceof Error?e:new Error(String(e)),n.blocked=!1,this.trackMiddlewareExecution(n),this.eventBus.emit({name:"middleware:error",payload:{id:a,name:s,error:n.error,duration:n.duration,timestamp:Date.now()}}),console.error(`Middleware ${s} error:`,e)}finally{this.executionState.runningMiddleware=null}}return r}subscribe(e,t){const r=Array.isArray(e)?e:[e];return this.updateBus.subscribe("update",(s=>{(r.includes(s)||""===e)&&(t(this.get()),this.metrics.listenerExecutions++)}))}id(){return this.instanceID}async transaction(e){const t=structuredClone(this.get());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.cache=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}}use(e,t="unnamed-middleware"){const r=crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,15)}`;return this.middleware.push({fn:e,name:t,id:r}),this.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))],r}useBlockingMiddleware(e,t="unnamed-blocking-middleware"){const r=crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,15)}`;return this.blockingMiddleware.push({fn:e,name:t,id:r}),this.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))],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.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))],this.middleware.length+this.blockingMiddleware.length<t}getPerformanceMetrics(){return structuredClone(this.metrics)}getMiddlewareExecutions(){return structuredClone(this.middlewareExecutions)}onStoreEvent(e,t){return this.eventBus.subscribe(e,t)}notifyListeners(e){e.forEach((e=>this.updateBus.emit({name:"update",payload:e})))}trackMiddlewareExecution(e){this.middlewareExecutions.unshift(e),this.middlewareExecutions.length>this.maxExecutionHistory&&this.middlewareExecutions.pop()}};var h=class{selectorCache=new WeakMap;create(e){return t=>{let r=this.selectorCache.get(e);r||(r=new WeakMap,this.selectorCache.set(e,r));const s=r.get(t);if(s)return s.result;const a=e(t);return r.set(t,{result:a,deps:[]}),a}}},u=class{executions=[];maxHistory=100;listeners=new Set;track(e){this.executions.unshift(e),this.executions.length>this.maxHistory&&this.executions.pop(),this.notify()}getExecutions(){return[...this.executions]}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){this.listeners.forEach((e=>e()))}};function m(r,{enableMetrics:s,...a}={}){const n=new l(r.state,a.persistence),i=s?new c(n,a):void 0,o=new h;r.middleware&&Object.entries(r.middleware).forEach((([e,t])=>n.use(t,e))),r.blockingMiddleware&&Object.entries(r.blockingMiddleware).forEach((([e,t])=>n.useBlockingMiddleware(t,e)));const d=new u,m=Object.entries(r.actions).reduce(((e,[t,r])=>(e[t]=async(...e)=>{const s=crypto.randomUUID(),a=performance.now(),i={id:s,name:t,params:e,startTime:a};try{await n.set((async t=>{const s=await r(t,...e);return i.result=s,s})),i.status="success"}catch(e){throw i.status="error",i.error=e instanceof Error?e:new Error(String(e)),console.error(`Error in action ${t}:`,e),e}finally{i.endTime=performance.now(),i.duration=i.endTime-a,d.track(i)}},e)),{});function p(e,t){const r=function(e,t="."){const r=[],s={get:(e,t)=>{const a=[];return r.length>0&&a.push(...r[r.length-1]),a.push(t),r.push(a),new Proxy({},s)}};return e(new Proxy({},s)),r.map((e=>e.join(t)))}(e);return n.subscribe(r,t)}const g=()=>n.get();return function(){const r=e((e=>{const r=o.create(e);return t((e=>p(r,e)),(()=>r(n.get())),(()=>r(n.get())))}),[]),s=e((()=>t((e=>n.subscribe("",e)),g,g)),[]);return{store:n,observer:i,select:r,actions:m,actionTracker:d,get state(){return s}}}}function p(e){return{id:"opentelemetry",name:"OpenTelemetry Collector",config:e,testConnection:async()=>{try{return(await fetch(`${e.endpoint}/v1/metrics`,{method:"OPTIONS",headers:{...e.apiKey?{"api-key":e.apiKey}:{}}})).ok}catch(e){return!1}},send:async t=>{const r={resourceMetrics:[{resource:{attributes:{"service.name":t.source,...e.resource}},scopeMetrics:[{metrics:t.metrics.map((e=>"counter"===e.type?{name:e.name,unit:e.unit||"1",sum:{dataPoints:[{timeUnixNano:BigInt(1e6*t.timestamp),asInt:Number(e.value),attributes:Object.entries(e.labels||{}).map((([e,t])=>({key:e,value:{stringValue:t}})))}]}}:"histogram"===e.type?{name:e.name,unit:e.unit||"ms",histogram:{dataPoints:[{timeUnixNano:BigInt(1e6*t.timestamp),count:1,sum:Number(e.value),attributes:Object.entries(e.labels||{}).map((([e,t])=>({key:e,value:{stringValue:t}})))}]}}:{name:e.name,unit:e.unit||"1",gauge:{dataPoints:[{timeUnixNano:BigInt(1e6*t.timestamp),asDouble:"number"==typeof e.value?e.value:1,attributes:Object.entries(e.labels||{}).map((([e,t])=>({key:e,value:{stringValue:t}})))}]}}))}]}]};await fetch(`${e.endpoint}/v1/metrics`,{method:"POST",headers:{"Content-Type":"application/json",...e.apiKey?{"api-key":e.apiKey}:{}},body:JSON.stringify(r)})}}}function g(e){return{id:"prometheus",name:"Prometheus Pushgateway",config:e,testConnection:async()=>{try{const t=e.username&&e.password?`Basic ${btoa(`${e.username}:${e.password}`)}`:void 0;return(await fetch(e.pushgatewayUrl,{method:"HEAD",headers:{...t?{Authorization:t}:{}}})).ok}catch(e){return!1}},send:async t=>{let r="";for(const e of t.metrics){if("log"===e.type||"trace"===e.type)continue;const s=Object.entries({...e.labels,source:t.source,instance:t.metrics.find((e=>e.labels?.instanceId))?.labels?.instanceId||"unknown"}).map((([e,t])=>`${e}="${t.replace(/"/g,'\\"')}"`)).join(","),a=e.name.replace(/\./g,"_");"counter"===e.type?r+=`# TYPE ${a} counter\n`:r+=`# TYPE ${a} gauge\n`,r+=`${a}{${s}} ${e.value}\n`}const s=e.username&&e.password?`Basic ${btoa(`${e.username}:${e.password}`)}`:void 0;await fetch(`${e.pushgatewayUrl}/metrics/job/${e.jobName}`,{method:"POST",headers:{"Content-Type":"text/plain",...s?{Authorization:s}:{}},body:r})}}}function y(e){return{id:"grafana-cloud",name:"Grafana Cloud",config:e,testConnection:async()=>{try{return(await fetch(`${e.url}/api/v1/query?query=up`,{method:"GET",headers:{Authorization:`Bearer ${e.apiKey}`}})).ok}catch(e){return!1}},send:async t=>{const r={streams:[{stream:{service:t.source,job:"store-metrics"},values:t.metrics.map((e=>{const r=1e6*t.timestamp;let s="";if("object"==typeof e.value)s=JSON.stringify(e.value);else if(s=`level=info metric=${e.name} value=${e.value} type=${e.type}`,e.labels)for(const[t,r]of Object.entries(e.labels))s+=` ${t}=${r}`;return[String(r),s]}))}]};await fetch(`${e.url}/loki/api/v1/push`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},body:JSON.stringify(r)})}}}function w(e){return{id:"http-endpoint",name:"HTTP Metrics Endpoint",config:e,testConnection:async()=>{try{return(await fetch(e.url,{method:"OPTIONS",headers:e.headers})).ok}catch(e){return!1}},send:async t=>{const r=e.transformPayload?e.transformPayload(t):t;await fetch(e.url,{method:e.method||"POST",headers:{"Content-Type":"application/json",...e.headers},body:JSON.stringify(r)})}}}var f=class extends c{destinations=new Map;metricsBatch=[];reportingInterval;batchSize;serviceName;environment;instanceId;immediateReporting;collectCategories;compressPayloads;reportingTimer=null;traceIdCounter=0;activeTraces=new Map;constructor(e,t){super(e,t),this.serviceName=t.serviceName,this.environment=t.environment,this.instanceId=t.instanceId||this.generateInstanceId(),this.reportingInterval=t.reportingInterval||3e4,this.batchSize=t.batchSize||100,this.immediateReporting=t.immediateReporting||!1,this.collectCategories=t.collectCategories||{performance:!0,errors:!0,stateChanges:!0,middleware:!0},this.compressPayloads=t.compressPayloads||!1,this.immediateReporting||this.startReportingCycle(),this.setupRemoteEventListeners()}generateInstanceId(){return`${Date.now()}-${Math.random().toString(36).substring(2,9)}`}setupRemoteEventListeners(){this.store.onStoreEvent("update:complete",(e=>{this.collectCategories?.stateChanges&&(this.trackMetric({name:"store.update.duration",type:"histogram",value:e.duration||0,unit:"ms",labels:{pathCount:String(e.changedPaths?.length||0)}}),this.trackMetric({name:"store.update.paths_changed",type:"counter",value:e.changedPaths?.length||0,labels:{blocked:String(!!e.blocked)}}))})),this.store.onStoreEvent("middleware:error",(e=>{this.collectCategories?.errors&&this.trackMetric({name:"store.middleware.error",type:"log",value:{middleware:e.name,error:e.error?.message||"Unknown error",stack:e.error?.stack},labels:{middlewareName:e.name}})})),this.store.onStoreEvent("transaction:start",(e=>{if(!this.collectCategories?.performance)return;const t=this.beginTrace("transaction");e.traceId=t,this.beginSpan(t,"transaction.execution",{transactionId:e.id||String(Date.now())})})),this.store.onStoreEvent("transaction:complete",(e=>{this.collectCategories?.performance&&e.traceId&&(this.endSpan(e.traceId,"transaction.execution"),this.endTrace(e.traceId))}))}beginTrace(e){const t=`trace-${++this.traceIdCounter}-${Date.now()}`;return this.activeTraces.set(t,{startTime:performance.now(),name:e,spans:{}}),t}beginSpan(e,t,r={}){const s=this.activeTraces.get(e);if(!s)return"";const a=`span-${Date.now()}-${Math.random().toString(36).substring(2,9)}`;return s.spans[a]={startTime:performance.now(),name:t,labels:r},a}endSpan(e,t){const r=this.activeTraces.get(e);if(!r)return;const s=Object.entries(r.spans).filter((([e,r])=>r.name===t&&!r.endTime)).map((([e])=>e));if(s.length>0){const e=s[s.length-1];r.spans[e].endTime=performance.now()}}endTrace(e){const t=this.activeTraces.get(e);if(!t)return;const r=performance.now(),s=r-t.startTime;this.trackMetric({name:"store.trace.duration",type:"histogram",value:s,unit:"ms",labels:{traceName:t.name,traceId:e}}),Object.entries(t.spans).forEach((([t,s])=>{s.endTime||(s.endTime=r);const a=s.endTime-s.startTime;this.trackMetric({name:"store.trace.span.duration",type:"trace",value:a,unit:"ms",labels:{...s.labels,spanName:s.name},traceId:e,parentId:t})})),this.activeTraces.delete(e)}addDestination(e){return this.destinations.has(e.id)?(console.warn(`Destination with ID ${e.id} already exists`),!1):(this.destinations.set(e.id,e),!0)}removeDestination(e){return this.destinations.delete(e)}getDestinations(){return Array.from(this.destinations.values()).map((e=>({id:e.id,name:e.name})))}async testAllConnections(){const e={};for(const[t,r]of this.destinations.entries())if(r.testConnection)try{e[t]=await r.testConnection()}catch(r){e[t]=!1}else e[t]=!0;return e}trackMetric(e){e.labels={...e.labels,environment:this.environment,service:this.serviceName,instanceId:this.instanceId},this.metricsBatch.push(e),(this.immediateReporting||this.metricsBatch.length>=this.batchSize)&&this.flushMetrics()}async flushMetrics(){if(0===this.metricsBatch.length)return;const e={timestamp:Date.now(),source:this.serviceName,metrics:[...this.metricsBatch]};this.metricsBatch=[];const t=Array.from(this.destinations.values()).map((async t=>{try{let r=e;if(this.compressPayloads&&"undefined"!=typeof window&&window.CompressionStream){const t=JSON.stringify(e),s=(new TextEncoder).encode(t),a=new CompressionStream("gzip"),n=new Blob([s]).stream().pipeThrough(a);await new Response(n).blob();r._compressed=!0,r._originalSize=t.length}await t.send(r)}catch(r){if(console.error(`Failed to send metrics to destination ${t.name}:`,r),e.metrics.some((e=>"log"===e.type&&e.name.includes("error")))){const t=e.metrics.filter((e=>"log"===e.type&&e.name.includes("error")));this.metricsBatch.push(...t)}}}));await Promise.all(t)}startReportingCycle(){this.reportingTimer&&clearInterval(this.reportingTimer),this.reportingTimer=setInterval((()=>{this.flushMetrics()}),this.reportingInterval)}setReportingInterval(e){this.reportingInterval=e,this.startReportingCycle()}reportCurrentMetrics(){const e=this.store.getPerformanceMetrics();this.trackMetric({name:"store.performance.update_count",type:"counter",value:e.updateCount,labels:{}}),this.trackMetric({name:"store.performance.listener_executions",type:"counter",value:e.listenerExecutions,labels:{}}),this.trackMetric({name:"store.performance.average_update_time",type:"gauge",value:e.averageUpdateTime,unit:"ms",labels:{}}),this.trackMetric({name:"store.performance.largest_update_size",type:"gauge",value:e.largestUpdateSize,unit:"paths",labels:{}})}disconnect(){this.reportingTimer&&(clearInterval(this.reportingTimer),this.reportingTimer=null),this.flushMetrics().catch((e=>console.error("Error flushing metrics during disconnect:",e))),super.disconnect()}};function b(e,t){const r=new f(e,t);return{remote:r,addOpenTelemetryDestination:e=>r.addDestination(p(e)),addPrometheusDestination:e=>r.addDestination(g(e)),addGrafanaCloudDestination:e=>r.addDestination(y(e)),addHttpDestination:e=>r.addDestination(w(e))}}var v=class{storageKey;bus;constructor(e){this.storageKey=e,this.bus=r({async:!0,batchSize:5,batchDelay:16,errorHandler:t=>console.error(`Event bus error for ${e}:`,t),crossTab:!0,channelName:`storage_${e}`}),window.addEventListener("storage",(e=>{if(e.key===this.storageKey&&e.newValue)try{const t=JSON.parse(e.newValue);this.bus.emit({name:"storage:updated",payload:{storageKey:this.storageKey,instanceId:"external",state:t}})}catch(e){console.error("Failed to parse storage event data:",e)}}))}set(e,t){try{const r=JSON.stringify(t);return localStorage.setItem(this.storageKey,r),this.bus.emit({name:"storage:updated",payload:{storageKey:this.storageKey,instanceId:e,state:t}}),!0}catch(e){return console.error(`Failed to persist state to localStorage for ${this.storageKey}:`,e),!1}}get(){try{const e=localStorage.getItem(this.storageKey);return e?JSON.parse(e):null}catch(e){return console.error(`Failed to retrieve state from localStorage for ${this.storageKey}:`,e),null}}subscribe(e,t){return this.bus.subscribe("storage:updated",(({storageKey:r,instanceId:s,state:a})=>{r===this.storageKey&&s!==e&&t(a)}))}clear(){try{return localStorage.removeItem(this.storageKey),!0}catch(e){return console.error(`Failed to clear persisted state for ${this.storageKey}:`,e),!1}}},T=class e{static dbInstance=null;static eventBusMap=new Map;static dbName="ReactiveDataStore";static modelName="stores";static getDatabase(){return e.dbInstance||(e.dbInstance=a({name:e.dbName,enableTelemetry:!1}).then((async t=>{const r={name:e.modelName,version:"1.0.0",fields:{storeId:{type:"string",required:!0},data:{type:"object",required:!0}}};try{await t.createModel(r)}catch(e){if(e instanceof n&&"SCHEMA_ALREADY_EXISTS"!==e.type)throw e}return t}))),e.dbInstance}static getEventBus(t){const s=`store_${t}`;return e.eventBusMap.has(s)||e.eventBusMap.set(s,r({batchSize:5,async:!0,batchDelay:16,errorHandler:e=>console.error(`Event bus error for ${t}:`,e),crossTab:!0,channelName:s})),e.eventBusMap.get(s)}static async getCursor(){return(await e.getDatabase()).cursor(e.modelName)}static async close(){const t=await e.dbInstance;t&&t.close(),e.dbInstance=null,e.eventBusMap.forEach((e=>e.clear())),e.eventBusMap.clear()}},S=class{cursorPromise;storeId;eventBus;constructor(e){this.storeId=e,this.cursorPromise=T.getCursor(),this.eventBus=T.getEventBus(e)}async set(e,t){try{const r=await this.cursorPromise,s=(new i).where({field:"storeId",operator:"eq",value:this.storeId}).build(),a=await r.find(s.filters),n={storeId:this.storeId,data:t};let o;return a?o=await a.update(n):(await r.create(n),o=!0),o&&this.eventBus.emit({name:"store:updated",payload:{storeId:this.storeId,instanceId:e,state:t}}),o}catch(t){return console.error(`Failed to set state for store ${this.storeId} by instance ${e}:`,t),!1}}async get(){try{const e=await this.cursorPromise,t=(new i).where({field:"storeId",operator:"eq",value:this.storeId}).build(),r=await e.find(t.filters);return r?r.read().then((()=>r.data)):null}catch(e){return console.error(`Failed to get state for store ${this.storeId}:`,e),null}}subscribe(e,t){return this.eventBus.subscribe("store:updated",(({storeId:r,instanceId:s,state:a})=>{r===this.storeId&&s!==e&&t(a)}))}async clear(){try{const e=await this.cursorPromise,t=(new i).where({field:"storeId",operator:"eq",value:this.storeId}).build(),r=await e.find(t.filters);return!r||await r.delete()}catch(e){return console.error(`Failed to clear state for store ${this.storeId}:`,e),!1}}static async closeAll(){await T.close()}};export{S as IndexedDBPersistence,v as LocalStoragePersistence,l as ReactiveDataStore,f as RemoteObservability,c as StoreObservability,y as createGrafanaCloudDestination,w as createHttpDestination,p as createOpenTelemetryDestination,g as createPrometheusDestination,m as createStore,b as useRemoteObservability};
|
|
1
|
+
import{useCallback as e,useSyncExternalStore as t}from"react";import{createEventBus as s}from"@asaidimu/events";import{v4 as r}from"uuid";import{DatabaseConnection as a,DatabaseError as n}from"@asaidimu/indexed";import{QueryBuilder as i}from"@asaidimu/query";function o(e,t){const s=new Set;function r(e,t){const s=e.split(".");for(let e=0;e<s.length;e++){const r=s.slice(0,e+1).join(".");t.add(r)}}return function e(t,a,n){const i=Symbol.for("delete");if(Array.isArray(a)&&Array.isArray(n))JSON.stringify(a)!==JSON.stringify(n)&&r(t,s);else for(const o of Object.keys(n)){const c=t?`${t}.${o}`:o;if(o in a){const t=a[o],d=n[o];d===i?(r(c,s),e(c,{},n[o])):"object"==typeof t&&null!==t&&"object"==typeof d&&null!==d?e(c,t,d):t!==d&&r(c,s)}else r(c,s),e(c,{},n[o])}}("",e||{},t||{}),Array.from(s)}var c=class{store;eventHistory=[];maxEvents;enableConsoleLogging;logEvents;performanceThresholds;unsubscribers=[];stateHistory=[];maxStateHistory=20;activeTransactionCount=0;activeBatches=new Set;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"];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=>{"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===o(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.store.getMiddlewareExecutions()}getPerformanceMetrics(){return this.store.getPerformanceMetrics()}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],a=o(r,s),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,s)=>{const r=t.length-1,a=t[r];t.slice(0,r).reduce(((e,t)=>(e[t]=e[t]??{},e[t])),e)[a]=s},c=a(r,t),d=a(s,t);o(n,t,c),o(i,t,d)}let c=Date.now();for(const e of this.eventHistory)if("update:complete"===e.type&&e.data.changedPaths?.length>0){c=e.timestamp;break}0!==a.length&&t.push({timestamp:c,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 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()}};function d(e,t){const s=Symbol.for("delete");if("object"!=typeof e||null===e)return t;if("object"!=typeof t||null===t)return e;const r=structuredClone(e),a=[{target:r,source:t}];for(;a.length>0;){const{target:e,source:t}=a.pop();Object.keys(t).forEach((r=>{const n=t[r],i=e[r];n===s?delete e[r]:Array.isArray(n)?e[r]=n:"object"==typeof n&&null!==n&&"object"==typeof i&&null!==i?(e.hasOwnProperty(r)||(e[r]=structuredClone(n)),a.push({target:e[r],source:n})):e[r]=n}))}return r}var l=class{metrics;middleware;blockingMiddleware;middlewareExecutions=[];maxExecutionHistory=100;pendingUpdates;isUpdating;updateTimes;cache=null;updateBus=s();eventBus=s();executionState;persistence;instanceID=r();persistenceReady=!1;constructor(e,t){this.middleware=[],this.blockingMiddleware=[],this.pendingUpdates=[],this.isUpdating=!1,this.updateTimes=[],this.metrics={updateCount:0,listenerExecutions:0,averageUpdateTime:0,largestUpdateSize:0,mostActiveListenerPaths:[]},this.executionState={executing:!1,changes:null,pendingChanges:[],middlewares:[],runningMiddleware:null,transactionActive:!1},this.cache=e,t?this.setPersistence(t):this.setPersistenceReady()}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&&await this.set(e)}catch(e){console.error("Failed to initialize persistence:",e)}finally{this.setPersistenceReady()}this.persistence.subscribe(this.instanceID,(async e=>{this.isUpdating=!0;try{const t=o(this.get(),e);t.length>0&&(await this.set(e),this.eventBus.emit({name:"update:complete",payload:{changedPaths:t,source:"external",timestamp:Date.now()}}))}finally{this.isUpdating=!1}}))}isReady(){return this.persistenceReady}getExecutionState(){return this.executionState}get(){return this.cache}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}});let s=e;if("function"==typeof e){const t=e(structuredClone(this.get()));s=t instanceof Promise?await t:t}try{const e=await this.applyBlockingMiddleware(s);if(e.blocked)return void this.eventBus.emit({name:"update:complete",payload:{blocked:!0,error:e.error,timestamp:Date.now()}})}catch(e){throw this.eventBus.emit({name:"update:complete",payload:{blocked:!0,error:e,timestamp:Date.now()}}),e}const r=d(this.get(),s),a=await this.applyMiddleware(r,s);try{const e=o(this.get(),a);if(this.metrics.updateCount++,this.metrics.largestUpdateSize=Math.max(this.metrics.largestUpdateSize,e.length),e.length>0){this.cache=a;try{await(this.persistence?.set(this.instanceID,this.get()))||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()}})}this.notifyListeners(e)}const s=performance.now();this.updateTimes.push(s-t),this.updateTimes.length>100&&this.updateTimes.shift(),this.metrics.averageUpdateTime=this.updateTimes.reduce(((e,t)=>e+t),0)/this.updateTimes.length,this.eventBus.emit({name:"update:complete",payload:{changedPaths:e,duration:s-t,timestamp:Date.now()}})}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)}}}async applyBlockingMiddleware(e){for(const{fn:t,name:s,id:r}of this.blockingMiddleware){const a={id:r,name:s,startTime:performance.now()};this.executionState.runningMiddleware={id:r,name:s,startTime:performance.now()},this.eventBus.emit({name:"middleware:start",payload:{id:r,name:s,type:"blocking",timestamp:Date.now()}});try{const n=await Promise.resolve(t(this.get(),e));if(a.endTime=performance.now(),a.duration=void 0!==a.startTime?a.endTime-a.startTime:0,!1===n)return a.blocked=!0,this.trackMiddlewareExecution(a),this.eventBus.emit({name:"middleware:blocked",payload:{id:r,name:s,duration:a.duration,timestamp:Date.now()}}),{blocked:!0};this.eventBus.emit({name:"middleware:complete",payload:{id:r,name:s,type:"blocking",duration:a.duration,timestamp:Date.now()}}),this.trackMiddlewareExecution({...a,blocked:!1})}catch(e){return a.endTime=performance.now(),a.duration=void 0!==a.startTime?a.endTime-a.startTime:0,a.error=e instanceof Error?e:new Error(String(e)),a.blocked=!0,this.trackMiddlewareExecution(a),this.eventBus.emit({name:"middleware:error",payload:{id:r,name:s,error:a.error,duration:a.duration,timestamp:Date.now()}}),{blocked:!0,error:a.error}}finally{this.executionState.runningMiddleware=null}}return{blocked:!1}}async applyMiddleware(e,t){let s=e;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.eventBus.emit({name:"middleware:start",payload:{id:a,name:r,type:"transform",timestamp:Date.now()}});try{const i=await Promise.resolve(e(s,t));n.endTime=performance.now(),n.duration=void 0!==n.startTime?n.endTime-n.startTime:0,n.blocked=!1,i&&"object"==typeof i&&(s=d(s,i)),this.trackMiddlewareExecution(n),this.eventBus.emit({name:"middleware:complete",payload:{id:a,name:r,type:"transform",duration:n.duration,timestamp:Date.now()}})}catch(e){n.endTime=performance.now(),n.duration=void 0!==n.startTime?n.endTime-n.startTime:0,n.error=e instanceof Error?e:new Error(String(e)),n.blocked=!1,this.trackMiddlewareExecution(n),this.eventBus.emit({name:"middleware:error",payload:{id:a,name:r,error:n.error,duration:n.duration,timestamp:Date.now()}}),console.error(`Middleware ${r} error:`,e)}finally{this.executionState.runningMiddleware=null}}return s}subscribe(e,t){const s=Array.isArray(e)?e:[e];return this.updateBus.subscribe("update",(r=>{(s.includes(r)||""===e)&&(t(this.get()),this.metrics.listenerExecutions++)}))}id(){return this.instanceID}async transaction(e){const t=structuredClone(this.get());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.cache=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}}use(e,t="unnamed-middleware"){const s=crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,15)}`;return this.middleware.push({fn:e,name:t,id:s}),this.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))],s}useBlockingMiddleware(e,t="unnamed-blocking-middleware"){const s=crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substring(2,15)}`;return this.blockingMiddleware.push({fn:e,name:t,id:s}),this.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))],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.executionState.middlewares=[...this.middleware.map((e=>e.name)),...this.blockingMiddleware.map((e=>e.name))],this.middleware.length+this.blockingMiddleware.length<t}getPerformanceMetrics(){return structuredClone(this.metrics)}getMiddlewareExecutions(){return structuredClone(this.middlewareExecutions)}onStoreEvent(e,t){return this.eventBus.subscribe(e,t)}notifyListeners(e){e.forEach((e=>this.updateBus.emit({name:"update",payload:e})))}trackMiddlewareExecution(e){this.middlewareExecutions.unshift(e),this.middlewareExecutions.length>this.maxExecutionHistory&&this.middlewareExecutions.pop()}};var h=class{selectorCache=new WeakMap;create(e){return t=>{let s=this.selectorCache.get(e);s||(s=new WeakMap,this.selectorCache.set(e,s));const r=s.get(t);if(r)return r.result;const a=e(t);return s.set(t,{result:a,deps:[]}),a}}},u=class{executions=[];maxHistory=100;listeners=new Set;track(e){this.executions.unshift(e),this.executions.length>this.maxHistory&&this.executions.pop(),this.notify()}getExecutions(){return[...this.executions]}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){this.listeners.forEach((e=>e()))}};function m(s,{enableMetrics:r,...a}={}){const n=new l(s.state,a.persistence),i=r?new c(n,a):void 0,o=new h;s.middleware&&Object.entries(s.middleware).forEach((([e,t])=>n.use(t,e))),s.blockingMiddleware&&Object.entries(s.blockingMiddleware).forEach((([e,t])=>n.useBlockingMiddleware(t,e)));const d=new u,m=Object.entries(s.actions).reduce(((e,[t,s])=>(e[t]=async(...e)=>{const r=crypto.randomUUID(),a=performance.now(),i={id:r,name:t,params:e,startTime:a};try{await n.set((async t=>{const r=await s(t,...e);return i.result=r,r})),i.status="success"}catch(e){throw i.status="error",i.error=e instanceof Error?e:new Error(String(e)),console.error(`Error in action ${t}:`,e),e}finally{i.endTime=performance.now(),i.duration=i.endTime-a,d.track(i)}},e)),{});function p(e,t){const s=function(e,t="."){const s=[],r={get:(e,t)=>{const a=[];return s.length>0&&a.push(...s[s.length-1]),a.push(t),s.push(a),new Proxy({},r)}};return e(new Proxy({},r)),s.map((e=>e.join(t)))}(e);return n.subscribe(s,t)}const g=()=>n.get(),y=e=>(n.isReady()&&e(),n.onStoreEvent("persistence:ready",e)),w=()=>n.isReady();return function(){const s=e((e=>{const s=o.create(e);return t((e=>p(s,e)),(()=>s(n.get())),(()=>s(n.get())))}),[]),r=e((()=>t((e=>n.subscribe("",e)),g,g)),[]),a=t(y,w,w);return{store:n,observer:i,select:s,actions:m,isReady:a,actionTracker:d,get state(){return r}}}}function p(e){return{id:"opentelemetry",name:"OpenTelemetry Collector",config:e,testConnection:async()=>{try{return(await fetch(`${e.endpoint}/v1/metrics`,{method:"OPTIONS",headers:{...e.apiKey?{"api-key":e.apiKey}:{}}})).ok}catch(e){return!1}},send:async t=>{const s={resourceMetrics:[{resource:{attributes:{"service.name":t.source,...e.resource}},scopeMetrics:[{metrics:t.metrics.map((e=>"counter"===e.type?{name:e.name,unit:e.unit||"1",sum:{dataPoints:[{timeUnixNano:BigInt(1e6*t.timestamp),asInt:Number(e.value),attributes:Object.entries(e.labels||{}).map((([e,t])=>({key:e,value:{stringValue:t}})))}]}}:"histogram"===e.type?{name:e.name,unit:e.unit||"ms",histogram:{dataPoints:[{timeUnixNano:BigInt(1e6*t.timestamp),count:1,sum:Number(e.value),attributes:Object.entries(e.labels||{}).map((([e,t])=>({key:e,value:{stringValue:t}})))}]}}:{name:e.name,unit:e.unit||"1",gauge:{dataPoints:[{timeUnixNano:BigInt(1e6*t.timestamp),asDouble:"number"==typeof e.value?e.value:1,attributes:Object.entries(e.labels||{}).map((([e,t])=>({key:e,value:{stringValue:t}})))}]}}))}]}]};await fetch(`${e.endpoint}/v1/metrics`,{method:"POST",headers:{"Content-Type":"application/json",...e.apiKey?{"api-key":e.apiKey}:{}},body:JSON.stringify(s)})}}}function g(e){return{id:"prometheus",name:"Prometheus Pushgateway",config:e,testConnection:async()=>{try{const t=e.username&&e.password?`Basic ${btoa(`${e.username}:${e.password}`)}`:void 0;return(await fetch(e.pushgatewayUrl,{method:"HEAD",headers:{...t?{Authorization:t}:{}}})).ok}catch(e){return!1}},send:async t=>{let s="";for(const e of t.metrics){if("log"===e.type||"trace"===e.type)continue;const r=Object.entries({...e.labels,source:t.source,instance:t.metrics.find((e=>e.labels?.instanceId))?.labels?.instanceId||"unknown"}).map((([e,t])=>`${e}="${t.replace(/"/g,'\\"')}"`)).join(","),a=e.name.replace(/\./g,"_");"counter"===e.type?s+=`# TYPE ${a} counter\n`:s+=`# TYPE ${a} gauge\n`,s+=`${a}{${r}} ${e.value}\n`}const r=e.username&&e.password?`Basic ${btoa(`${e.username}:${e.password}`)}`:void 0;await fetch(`${e.pushgatewayUrl}/metrics/job/${e.jobName}`,{method:"POST",headers:{"Content-Type":"text/plain",...r?{Authorization:r}:{}},body:s})}}}function y(e){return{id:"grafana-cloud",name:"Grafana Cloud",config:e,testConnection:async()=>{try{return(await fetch(`${e.url}/api/v1/query?query=up`,{method:"GET",headers:{Authorization:`Bearer ${e.apiKey}`}})).ok}catch(e){return!1}},send:async t=>{const s={streams:[{stream:{service:t.source,job:"store-metrics"},values:t.metrics.map((e=>{const s=1e6*t.timestamp;let r="";if("object"==typeof e.value)r=JSON.stringify(e.value);else if(r=`level=info metric=${e.name} value=${e.value} type=${e.type}`,e.labels)for(const[t,s]of Object.entries(e.labels))r+=` ${t}=${s}`;return[String(s),r]}))}]};await fetch(`${e.url}/loki/api/v1/push`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.apiKey}`},body:JSON.stringify(s)})}}}function w(e){return{id:"http-endpoint",name:"HTTP Metrics Endpoint",config:e,testConnection:async()=>{try{return(await fetch(e.url,{method:"OPTIONS",headers:e.headers})).ok}catch(e){return!1}},send:async t=>{const s=e.transformPayload?e.transformPayload(t):t;await fetch(e.url,{method:e.method||"POST",headers:{"Content-Type":"application/json",...e.headers},body:JSON.stringify(s)})}}}var f=class extends c{destinations=new Map;metricsBatch=[];reportingInterval;batchSize;serviceName;environment;instanceId;immediateReporting;collectCategories;compressPayloads;reportingTimer=null;traceIdCounter=0;activeTraces=new Map;constructor(e,t){super(e,t),this.serviceName=t.serviceName,this.environment=t.environment,this.instanceId=t.instanceId||this.generateInstanceId(),this.reportingInterval=t.reportingInterval||3e4,this.batchSize=t.batchSize||100,this.immediateReporting=t.immediateReporting||!1,this.collectCategories=t.collectCategories||{performance:!0,errors:!0,stateChanges:!0,middleware:!0},this.compressPayloads=t.compressPayloads||!1,this.immediateReporting||this.startReportingCycle(),this.setupRemoteEventListeners()}generateInstanceId(){return`${Date.now()}-${Math.random().toString(36).substring(2,9)}`}setupRemoteEventListeners(){this.store.onStoreEvent("update:complete",(e=>{this.collectCategories?.stateChanges&&(this.trackMetric({name:"store.update.duration",type:"histogram",value:e.duration||0,unit:"ms",labels:{pathCount:String(e.changedPaths?.length||0)}}),this.trackMetric({name:"store.update.paths_changed",type:"counter",value:e.changedPaths?.length||0,labels:{blocked:String(!!e.blocked)}}))})),this.store.onStoreEvent("middleware:error",(e=>{this.collectCategories?.errors&&this.trackMetric({name:"store.middleware.error",type:"log",value:{middleware:e.name,error:e.error?.message||"Unknown error",stack:e.error?.stack},labels:{middlewareName:e.name}})})),this.store.onStoreEvent("transaction:start",(e=>{if(!this.collectCategories?.performance)return;const t=this.beginTrace("transaction");e.traceId=t,this.beginSpan(t,"transaction.execution",{transactionId:e.id||String(Date.now())})})),this.store.onStoreEvent("transaction:complete",(e=>{this.collectCategories?.performance&&e.traceId&&(this.endSpan(e.traceId,"transaction.execution"),this.endTrace(e.traceId))}))}beginTrace(e){const t=`trace-${++this.traceIdCounter}-${Date.now()}`;return this.activeTraces.set(t,{startTime:performance.now(),name:e,spans:{}}),t}beginSpan(e,t,s={}){const r=this.activeTraces.get(e);if(!r)return"";const a=`span-${Date.now()}-${Math.random().toString(36).substring(2,9)}`;return r.spans[a]={startTime:performance.now(),name:t,labels:s},a}endSpan(e,t){const s=this.activeTraces.get(e);if(!s)return;const r=Object.entries(s.spans).filter((([e,s])=>s.name===t&&!s.endTime)).map((([e])=>e));if(r.length>0){const e=r[r.length-1];s.spans[e].endTime=performance.now()}}endTrace(e){const t=this.activeTraces.get(e);if(!t)return;const s=performance.now(),r=s-t.startTime;this.trackMetric({name:"store.trace.duration",type:"histogram",value:r,unit:"ms",labels:{traceName:t.name,traceId:e}}),Object.entries(t.spans).forEach((([t,r])=>{r.endTime||(r.endTime=s);const a=r.endTime-r.startTime;this.trackMetric({name:"store.trace.span.duration",type:"trace",value:a,unit:"ms",labels:{...r.labels,spanName:r.name},traceId:e,parentId:t})})),this.activeTraces.delete(e)}addDestination(e){return this.destinations.has(e.id)?(console.warn(`Destination with ID ${e.id} already exists`),!1):(this.destinations.set(e.id,e),!0)}removeDestination(e){return this.destinations.delete(e)}getDestinations(){return Array.from(this.destinations.values()).map((e=>({id:e.id,name:e.name})))}async testAllConnections(){const e={};for(const[t,s]of this.destinations.entries())if(s.testConnection)try{e[t]=await s.testConnection()}catch(s){e[t]=!1}else e[t]=!0;return e}trackMetric(e){e.labels={...e.labels,environment:this.environment,service:this.serviceName,instanceId:this.instanceId},this.metricsBatch.push(e),(this.immediateReporting||this.metricsBatch.length>=this.batchSize)&&this.flushMetrics()}async flushMetrics(){if(0===this.metricsBatch.length)return;const e={timestamp:Date.now(),source:this.serviceName,metrics:[...this.metricsBatch]};this.metricsBatch=[];const t=Array.from(this.destinations.values()).map((async t=>{try{let s=e;if(this.compressPayloads&&"undefined"!=typeof window&&window.CompressionStream){const t=JSON.stringify(e),r=(new TextEncoder).encode(t),a=new CompressionStream("gzip"),n=new Blob([r]).stream().pipeThrough(a);await new Response(n).blob();s._compressed=!0,s._originalSize=t.length}await t.send(s)}catch(s){if(console.error(`Failed to send metrics to destination ${t.name}:`,s),e.metrics.some((e=>"log"===e.type&&e.name.includes("error")))){const t=e.metrics.filter((e=>"log"===e.type&&e.name.includes("error")));this.metricsBatch.push(...t)}}}));await Promise.all(t)}startReportingCycle(){this.reportingTimer&&clearInterval(this.reportingTimer),this.reportingTimer=setInterval((()=>{this.flushMetrics()}),this.reportingInterval)}setReportingInterval(e){this.reportingInterval=e,this.startReportingCycle()}reportCurrentMetrics(){const e=this.store.getPerformanceMetrics();this.trackMetric({name:"store.performance.update_count",type:"counter",value:e.updateCount,labels:{}}),this.trackMetric({name:"store.performance.listener_executions",type:"counter",value:e.listenerExecutions,labels:{}}),this.trackMetric({name:"store.performance.average_update_time",type:"gauge",value:e.averageUpdateTime,unit:"ms",labels:{}}),this.trackMetric({name:"store.performance.largest_update_size",type:"gauge",value:e.largestUpdateSize,unit:"paths",labels:{}})}disconnect(){this.reportingTimer&&(clearInterval(this.reportingTimer),this.reportingTimer=null),this.flushMetrics().catch((e=>console.error("Error flushing metrics during disconnect:",e))),super.disconnect()}};function b(e,t){const s=new f(e,t);return{remote:s,addOpenTelemetryDestination:e=>s.addDestination(p(e)),addPrometheusDestination:e=>s.addDestination(g(e)),addGrafanaCloudDestination:e=>s.addDestination(y(e)),addHttpDestination:e=>s.addDestination(w(e))}}var v=class{storageKey;bus;constructor(e){this.storageKey=e,this.bus=s({async:!0,batchSize:5,batchDelay:16,errorHandler:t=>console.error(`Event bus error for ${e}:`,t),crossTab:!0,channelName:`storage_${e}`}),window.addEventListener("storage",(e=>{if(e.key===this.storageKey&&e.newValue)try{const t=JSON.parse(e.newValue);this.bus.emit({name:"storage:updated",payload:{storageKey:this.storageKey,instanceId:"external",state:t}})}catch(e){console.error("Failed to parse storage event data:",e)}}))}set(e,t){try{const s=JSON.stringify(t);return localStorage.setItem(this.storageKey,s),this.bus.emit({name:"storage:updated",payload:{storageKey:this.storageKey,instanceId:e,state:t}}),!0}catch(e){return console.error(`Failed to persist state to localStorage for ${this.storageKey}:`,e),!1}}get(){try{const e=localStorage.getItem(this.storageKey);return e?JSON.parse(e):null}catch(e){return console.error(`Failed to retrieve state from localStorage for ${this.storageKey}:`,e),null}}subscribe(e,t){return this.bus.subscribe("storage:updated",(({storageKey:s,instanceId:r,state:a})=>{s===this.storageKey&&r!==e&&t(a)}))}clear(){try{return localStorage.removeItem(this.storageKey),!0}catch(e){return console.error(`Failed to clear persisted state for ${this.storageKey}:`,e),!1}}},T=class e{static dbInstance=null;static eventBusMap=new Map;static dbName="ReactiveDataStore";static modelName="stores";static getDatabase(){return e.dbInstance||(e.dbInstance=a({name:e.dbName,enableTelemetry:!1}).then((async t=>{const s={name:e.modelName,version:"1.0.0",fields:{storeId:{type:"string",required:!0},data:{type:"object",required:!0}}};try{await t.createModel(s)}catch(e){if(e instanceof n&&"SCHEMA_ALREADY_EXISTS"!==e.type)throw e}return t}))),e.dbInstance}static getEventBus(t){const r=`store_${t}`;return e.eventBusMap.has(r)||e.eventBusMap.set(r,s({batchSize:5,async:!0,batchDelay:16,errorHandler:e=>console.error(`Event bus error for ${t}:`,e),crossTab:!0,channelName:r})),e.eventBusMap.get(r)}static async getCursor(){return(await e.getDatabase()).cursor(e.modelName)}static async close(){const t=await e.dbInstance;t&&t.close(),e.dbInstance=null,e.eventBusMap.forEach((e=>e.clear())),e.eventBusMap.clear()}},S=class{cursorPromise;storeId;eventBus;constructor(e){this.storeId=e,this.cursorPromise=T.getCursor(),this.eventBus=T.getEventBus(e)}async set(e,t){try{const s=await this.cursorPromise,r=(new i).where({field:"storeId",operator:"eq",value:this.storeId}).build(),a=await s.find(r.filters),n={storeId:this.storeId,data:t};let o;return a?o=await a.update(n):(await s.create(n),o=!0),o&&this.eventBus.emit({name:"store:updated",payload:{storeId:this.storeId,instanceId:e,state:t}}),o}catch(t){return console.error(`Failed to set state for store ${this.storeId} by instance ${e}:`,t),!1}}async get(){try{const e=await this.cursorPromise,t=(new i).where({field:"storeId",operator:"eq",value:this.storeId}).build(),s=await e.find(t.filters);return s?s.read().then((()=>s.data)):null}catch(e){return console.error(`Failed to get state for store ${this.storeId}:`,e),null}}subscribe(e,t){return this.eventBus.subscribe("store:updated",(({storeId:s,instanceId:r,state:a})=>{s===this.storeId&&r!==e&&t(a)}))}async clear(){try{const e=await this.cursorPromise,t=(new i).where({field:"storeId",operator:"eq",value:this.storeId}).build(),s=await e.find(t.filters);return!s||await s.delete()}catch(e){return console.error(`Failed to clear state for store ${this.storeId}:`,e),!1}}static async closeAll(){await T.close()}};export{S as IndexedDBPersistence,v as LocalStoragePersistence,l as ReactiveDataStore,f as RemoteObservability,c as StoreObservability,y as createGrafanaCloudDestination,w as createHttpDestination,p as createOpenTelemetryDestination,g as createPrometheusDestination,m as createStore,b as useRemoteObservability};
|