@asaidimu/react-store 2.1.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@asaidimu/react-store.svg)](https://www.npmjs.com/package/@asaidimu/react-store)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)](https://github.com/asaidimu/node-react/actions)
5
6
 
6
7
  A performant, type-safe state management solution for React with built-in persistence, extensive observability, and a robust middleware system.
7
8
 
@@ -21,6 +22,7 @@ This package is currently in **beta**. The API is subject to rapid changes and s
21
22
  * [Persistence](#persistence)
22
23
  * [Middleware](#middleware)
23
24
  * [Observability](#observability)
25
+ * [Remote Observability](#remote-observability)
24
26
  * [Transaction Support](#transaction-support)
25
27
  * [Event System](#event-system)
26
28
  * [Watching Action Loading States](#watching-action-loading-states)
@@ -31,6 +33,8 @@ This package is currently in **beta**. The API is subject to rapid changes and s
31
33
  * [Best Practices](#best-practices)
32
34
  * [API Reference](#api-reference)
33
35
  * [Comparison with Other State Management Solutions](#comparison-with-other-state-management-solutions)
36
+ * [Troubleshooting](#troubleshooting)
37
+ * [FAQ](#faq)
34
38
  * [Changelog](#changelog)
35
39
  * [License](#license)
36
40
  * [Acknowledgments](#acknowledgments)
@@ -41,19 +45,19 @@ This package is currently in **beta**. The API is subject to rapid changes and s
41
45
 
42
46
  `@asaidimu/react-store` provides an efficient and predictable way to manage complex application state in React applications. It goes beyond basic state management by integrating features typically found in separate libraries, such as data persistence and comprehensive observability tools, directly into its core. This allows developers to build robust, high-performance applications with deep insights into state changes and application behavior.
43
47
 
44
- Designed with modern React in mind, it leverages `useSyncExternalStore` for optimal performance and reactivity, ensuring components re-render only when relevant parts of the state change. Its flexible design supports a variety of use cases, from simple counter applications to complex data flows requiring atomic updates and cross-tab synchronization.
48
+ Designed with modern React in mind, it leverages `useSyncExternalStore` for optimal performance and reactivity, ensuring components re-render only when relevant parts of the state change. Its flexible design supports a variety of use cases, from simple counter applications to complex data flows requiring atomic updates and cross-tab synchronization. The library is built with TypeScript from the ground up, offering strong type safety throughout your application's state and actions.
45
49
 
46
50
  ### Key Features
47
51
 
48
- * 📊 **Reactive State Management**: Automatically tracks dependencies to optimize component renders and ensure efficient updates.
49
- * 🛡️ **Type-Safe**: Built with TypeScript from the ground up, providing strict type checking and a safer development experience.
52
+ * 📊 **Reactive State Management**: Automatically tracks dependencies to optimize component renders and ensure efficient updates using `useSyncExternalStore`.
53
+ * 🛡️ **Type-Safe**: Developed entirely in TypeScript, providing strict type checking for state, actions, and middleware.
50
54
  * ⚙️ **Middleware Pipeline**: Implement custom logic to transform, validate, or log state changes before they are applied. Supports both transforming and blocking middleware.
51
55
  * 📦 **Transaction Support**: Group multiple state updates into a single atomic operation, with automatic rollback if any part of the transaction fails.
52
56
  * 💾 **Built-in Persistence**: Seamlessly integrate with web storage mechanisms like `IndexedDB` and `WebStorage` (localStorage/sessionStorage), including cross-tab synchronization.
53
- * 🔍 **Deep Observability**: Gain profound insights into your application's state with built-in metrics, detailed event logging, state history, and time-travel debugging capabilities.
57
+ * 🔍 **Deep Observability**: Gain profound insights into your application's state with built-in metrics, detailed event logging, state history, and time-travel debugging capabilities via the `StoreObservability` instance.
54
58
  * ⚡ **Performance Optimized**: Features intelligent selector caching (now using `WeakMap` for enhanced efficiency) and debounced actions with configurable immediate execution to prevent rapid successive calls and ensure smooth application performance.
55
59
  * 🚀 **Action Loading States**: Track the real-time loading status of individual actions, providing immediate feedback on asynchronous operations.
56
- * ⚛️ **React 18+ Ready**: Fully compatible with the latest React versions, leveraging modern APIs for enhanced performance and development ergonomics.
60
+ * ⚛️ **React 19+ Ready**: Fully compatible with the latest React versions, leveraging modern APIs for enhanced performance and development ergonomics.
57
61
  * 🗑️ **Explicit Deletions**: Use `Symbol.for("delete")` to explicitly remove properties from nested state objects.
58
62
 
59
63
  ## Installation & Setup
@@ -61,8 +65,8 @@ Designed with modern React in mind, it leverages `useSyncExternalStore` for opti
61
65
  ### Prerequisites
62
66
 
63
67
  * Node.js (v18 or higher recommended)
64
- * React (v18 or higher recommended)
65
- * A package manager like `bun`, `npm`, or `yarn`.
68
+ * React (v19 or higher recommended)
69
+ * A package manager like `bun`, `npm`, or `yarn`. This project explicitly uses `bun`.
66
70
 
67
71
  ### Installation Steps
68
72
 
@@ -78,7 +82,7 @@ yarn add @asaidimu/react-store
78
82
 
79
83
  ### Configuration
80
84
 
81
- No global configuration is required. All options are passed during store creation.
85
+ No global configuration is required. All options are passed during store creation via the `createStore` function. Configuration includes enabling metrics, logging, persistence, and performance thresholds.
82
86
 
83
87
  ### Verification
84
88
 
@@ -87,98 +91,242 @@ You can verify the installation by importing `createStore` and setting up a basi
87
91
  ```typescript
88
92
  import { createStore } from '@asaidimu/react-store';
89
93
 
90
- const myStore = createStore({
94
+ interface MyState {
95
+ value: string;
96
+ }
97
+
98
+ const myStore = createStore<MyState, any>({
91
99
  state: { value: 'hello' },
92
100
  actions: {
93
- setValue: (state, newValue: string) => ({ value: newValue }),
101
+ setValue: (_, newValue: string) => ({ value: newValue }),
94
102
  },
95
103
  });
96
104
 
105
+ const { select, actions } = myStore(); // Instantiate the hook
106
+
107
+ // Access state
108
+ const currentValue = select(s => s.value);
109
+ console.log(currentValue); // Expected: 'hello'
110
+
111
+ // Dispatch an action
112
+ actions.setValue('world');
97
113
  ```
98
114
 
99
- If no errors are thrown, the package is correctly installed.
115
+ If no errors are thrown during installation or when running this basic example, the package is correctly installed and configured.
100
116
 
101
117
  ## Usage Documentation
102
118
 
103
119
  ### Creating a Store
104
120
 
105
- Define your application state and actions, then create a store using `createStore`.
121
+ Define your application state and actions, then create a store using `createStore`. The `actions` object maps action names to functions that receive an `ActionContext` (containing the current `state` and an `resolve` function for artifacts) and any additional arguments.
106
122
 
107
123
  ```tsx
108
- // src/stores/myStore.ts
109
- import { createStore } from '@asaidimu/react-store';
124
+ // ui/store.tsx (Example from codebase)
125
+ import { createStore } from '@asaidimu/react-store'; // Assuming direct import or wrapper
126
+
127
+ export interface Product {
128
+ id: number;
129
+ name: string;
130
+ price: number;
131
+ stock: number;
132
+ image: string;
133
+ }
134
+
135
+ export interface CartItem extends Product {
136
+ quantity: number;
137
+ }
138
+
139
+ export interface Order {
140
+ id: string;
141
+ items: CartItem[];
142
+ total: number;
143
+ date: Date;
144
+ }
110
145
 
111
- interface AppState {
112
- count: number;
113
- user: { name: string; loggedIn: boolean; email?: string };
114
- settings: { theme: 'light' | 'dark'; notifications: boolean };
146
+ export interface ECommerceState extends Record<string, any>{
147
+ products: Product[];
148
+ cart: CartItem[];
149
+ orders: Order[];
150
+ topSellers: { id: number; name: string; sales: number }[];
151
+ activeUsers: number;
115
152
  }
116
153
 
117
- const initialState: AppState = {
118
- count: 0,
119
- user: { name: 'Guest', loggedIn: false },
120
- settings: { theme: 'light', notifications: true },
154
+ const initialState: ECommerceState = {
155
+ products: [
156
+ { id: 1, name: 'Wireless Mouse', price: 25.99, stock: 150, image: 'https://placehold.co/600x400/white/black?text=Mouse' },
157
+ { id: 2, name: 'Mechanical Keyboard', price: 79.99, stock: 100, image: 'https://placehold.co/600x400/white/black?text=Keyboard' },
158
+ { id: 3, name: '4K Monitor', price: 349.99, stock: 75, image: 'https://placehold.co/600x400/white/black?text=Monitor' },
159
+ { id: 4, name: 'Webcam', price: 45.50, stock: 120, image: 'https://placehold.co/600x400/white/black?text=Webcam' },
160
+ { id: 5, name: 'USB-C Hub', price: 39.99, stock: 200, image: 'https://placehold.co/600x400/white/black?text=Hub' },
161
+ ],
162
+ cart: [],
163
+ orders: [],
164
+ topSellers: [
165
+ { id: 2, name: 'Mechanical Keyboard', sales: 120 },
166
+ { id: 3, name: '4K Monitor', sales: 85 },
167
+ { id: 1, name: 'Wireless Mouse', sales: 80 },
168
+ { id: 5, name: 'USB-C Hub', sales: 70 },
169
+ { id: 4, name: 'Webcam', sales: 65 },
170
+ ],
171
+ activeUsers: 1428,
121
172
  };
122
173
 
123
- const myStore = createStore({
124
- state: initialState,
125
- actions: {
126
- increment: (state, amount: number) => ({ count: state.count + amount }),
127
- login: async (state, username: string, email: string) => {
128
- // Simulate an asynchronous API call
129
- await new Promise(resolve => setTimeout(resolve, 500));
130
- return { user: { name: username, loggedIn: true, email } };
131
- },
132
- toggleTheme: (state) => ({
133
- settings: { theme: state.settings.theme === 'light' ? 'dark' : 'light' },
134
- }),
174
+ const actions = {
175
+ addToCart: ({ state }: any, product: Product) => {
176
+ const existingItem = state.cart.find((item:any) => item.id === product.id);
177
+ if (existingItem) {
178
+ return {
179
+ cart: state.cart.map((item:any) =>
180
+ item.id === product.id ? { ...item, quantity: item.quantity + 1 } : item
181
+ ),
182
+ };
183
+ }
184
+ return { cart: [...state.cart, { ...product, quantity: 1 }] };
135
185
  },
136
- }, {
137
- debounceTime: 0, // Actions execute immediately by default
138
- enableConsoleLogging: true, // Enable console logs for store events
139
- logEvents: { updates: true, transactions: true, middleware: true },
140
- performanceThresholds: { updateTime: 20, middlewareTime: 5 }, // Warn for slow operations
141
- });
186
+ removeFromCart: ({state}: {state:ECommerceState}, productId: number) => ({
187
+ cart: state.cart.filter((item) => item.id !== productId),
188
+ }),
189
+ updateQuantity: ({state}: {state:ECommerceState}, { productId, quantity }: { productId: number; quantity: number }) => ({
190
+ cart: state.cart.map((item) =>
191
+ item.id === productId ? { ...item, quantity } : item
192
+ ).filter(item => item.quantity > 0),
193
+ }),
194
+ checkout: ({state}: {state:ECommerceState}) => {
195
+ const total = state.cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
196
+ const newOrder: Order = {
197
+ id: crypto.randomUUID(),
198
+ items: state.cart,
199
+ total,
200
+ date: new Date(),
201
+ };
202
+ return {
203
+ cart: [],
204
+ orders: [newOrder, ...state.orders],
205
+ products: state.products.map(p => {
206
+ const cartItem = state.cart.find(item => item.id === p.id);
207
+ return cartItem ? { ...p, stock: p.stock - cartItem.quantity } : p;
208
+ }),
209
+ topSellers: state.topSellers.map(s => {
210
+ const cartItem = state.cart.find(item => item.id === s.id);
211
+ return cartItem ? { ...s, sales: s.sales + cartItem.quantity } : s;
212
+ }).sort((a, b) => b.sales - a.sales),
213
+ };
214
+ },
215
+
216
+ updateStock: ({state}: {state:ECommerceState}) => ({
217
+ products: state.products.map((p:any) => ({
218
+ ...p,
219
+ stock: Math.max(0, p.stock + Math.floor(Math.random() * 10) - 5)
220
+ }))
221
+ }),
222
+ updateActiveUsers: ({state}: {state:ECommerceState}) => ({
223
+ activeUsers: state.activeUsers + Math.floor(Math.random() * 20) - 10,
224
+ }),
225
+ addRandomOrder: ({state}: {state:ECommerceState}) => {
226
+ const randomProduct = state.products[Math.floor(Math.random() * state.products.length)];
227
+ const quantity = Math.floor(Math.random() * 3) + 1;
228
+ const newOrder: Order = {
229
+ id: crypto.randomUUID(),
230
+ items: [{ ...randomProduct, quantity }],
231
+ total: randomProduct.price * quantity,
232
+ date: new Date(),
233
+ };
234
+ return {
235
+ orders: [newOrder, ...state.orders],
236
+ };
237
+ },
238
+ };
142
239
 
143
- // Export the hook for use in components
144
- export const useMyStore = myStore;
240
+ export const useStore = createStore(
241
+ {
242
+ state: initialState,
243
+ actions,
244
+ },
245
+ { enableMetrics: true } // Enables metrics for observability
246
+ );
145
247
  ```
146
248
 
147
249
  ### Using in Components
148
250
 
149
- Consume your store's state and actions within your React components using the exported hook.
251
+ Consume your store's state and actions within your React components using the exported hook. The `select` function allows you to subscribe to specific parts of the state, ensuring that your components only re-render when the selected data changes.
150
252
 
151
253
  ```tsx
152
- // src/components/MyComponent.tsx
153
- import React from 'react';
154
- import { useMyStore } from '../stores/myStore';
254
+ // ui/App.tsx (Excerpt from codebase)
255
+ import { useEffect, useMemo } from 'react';
256
+ import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
257
+ import { useStore, Product, CartItem, Order } from './store'; // Assuming these imports resolve correctly
155
258
 
156
- function MyComponent() {
157
- const { select, actions, isReady } = useMyStore();
259
+ // ... (ShadCN-like UI Components for brevity) ...
158
260
 
159
- // Select specific parts of the state for granular re-renders
160
- const count = select((state) => state.count);
161
- const userName = select((state) => state.user.name);
162
- const theme = select((state) => state.settings.theme);
261
+ const ProductCatalog = () => {
262
+ const { select, actions } = useStore();
263
+ const products = select((state) => state.products); // Granular selection
163
264
 
164
- // isReady indicates if persistence has loaded initial state
165
- if (!isReady) {
166
- return <div>Loading store data...</div>;
167
- }
265
+ return (
266
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
267
+ {products.map((product: Product) => (
268
+ <Card key={product.id}>
269
+ <CardHeader>
270
+ <CardTitle>{product.name}</CardTitle>
271
+ </CardHeader>
272
+ <CardContent className="flex-grow">
273
+ <img src={product.image} alt={product.name} className="w-full h-40 object-cover rounded-lg mb-4" />
274
+ <div className="flex justify-between items-center">
275
+ <p className="text-lg font-semibold text-gray-700">${product.price.toFixed(2)}</p>
276
+ <p className="text-sm text-gray-500">{product.stock} in stock</p>
277
+ </div>
278
+ </CardContent>
279
+ <CardFooter>
280
+ <Button onClick={() => actions.addToCart(product)} className="w-full">Add to Cart</Button>
281
+ </CardFooter>
282
+ </Card>
283
+ ))}
284
+ </div>
285
+ );
286
+ };
287
+
288
+ function App() {
289
+ const { actions } = useStore();
290
+
291
+ useEffect(() => {
292
+ // Real-time simulations for the dashboard
293
+ const stockInterval = setInterval(() => actions.updateStock(), 2000);
294
+ const usersInterval = setInterval(() => actions.updateActiveUsers(), 3000);
295
+ const ordersInterval = setInterval(() => actions.addRandomOrder(), 5000);
296
+
297
+ return () => {
298
+ clearInterval(stockInterval);
299
+ clearInterval(usersInterval);
300
+ clearInterval(ordersInterval);
301
+ };
302
+ }, [actions]); // `actions` object is stable due to `useMemo` in createStore
168
303
 
169
304
  return (
170
- <div style={{ background: theme === 'dark' ? '#333' : '#FFF', color: theme === 'dark' ? '#FFF' : '#333' }}>
171
- <h1>Welcome, {userName}!</h1>
172
- <p>Current Count: {count}</p>
173
- <button onClick={() => actions.increment(1)}>Increment</button>
174
- <button onClick={() => actions.increment(5)}>Increment by 5 (debounced)</button>
175
- <button onClick={() => actions.login('Alice', 'alice@example.com')}>Login as Alice</button>
176
- <button onClick={() => actions.toggleTheme()}>Toggle Theme</button>
305
+ <div className="bg-gray-50 text-gray-900 min-h-screen">
306
+ <header className="border-b">
307
+ <div className="container mx-auto px-4 h-16 flex items-center">
308
+ <h1 className="text-xl font-bold">E-Commerce Dashboard</h1>
309
+ </div>
310
+ </header>
311
+ <main className="container mx-auto p-4 sm:p-6 lg:p-8">
312
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
313
+ <div className="lg:col-span-2 space-y-6">
314
+ <ProductCatalog />
315
+ <LiveInventoryChart />
316
+ </div>
317
+ <div className="space-y-6">
318
+ <ShoppingCart />
319
+ <UserAnalytics />
320
+ <TopSellingProducts />
321
+ <RecentOrdersFeed />
322
+ </div>
323
+ </div>
324
+ </main>
177
325
  </div>
178
326
  );
179
327
  }
180
328
 
181
- export default MyComponent;
329
+ export default App;
182
330
  ```
183
331
 
184
332
  ### Handling Deletions
@@ -202,12 +350,12 @@ const deleteStore = createStore({
202
350
  tags: ['electronics', 'new']
203
351
  },
204
352
  actions: {
205
- removeDetails: (state) => ({ details: Symbol.for("delete") }),
206
- removeDimensions: (state) => ({ details: { dimensions: Symbol.for("delete") } }),
207
- removeTag: (state, tagToRemove: string) => ({
353
+ removeDetails: (ctx) => ({ details: Symbol.for("delete") }),
354
+ removeDimensions: (ctx) => ({ details: { dimensions: Symbol.for("delete") } }),
355
+ removeTag: ({state}, tagToRemove: string) => ({
208
356
  tags: state.tags.filter(tag => tag !== tagToRemove)
209
357
  }),
210
- clearAllExceptId: (state) => ({
358
+ clearAllExceptId: (ctx) => ({
211
359
  name: Symbol.for("delete"),
212
360
  details: Symbol.for("delete"),
213
361
  tags: Symbol.for("delete")
@@ -243,10 +391,12 @@ runDeleteExample();
243
391
 
244
392
  ### Persistence
245
393
 
246
- Persist your store's state across browser sessions or synchronize it across multiple tabs.
394
+ Persist your store's state across browser sessions or synchronize it across multiple tabs using persistence adapters from `@asaidimu/utils-persistence`. You can choose between `WebStoragePersistence` (for `localStorage` or `sessionStorage`) and `IndexedDBPersistence` for more robust storage.
247
395
 
248
396
  ```tsx
249
- import { createStore, WebStoragePersistence, IndexedDBPersistence } from '@asaidimu/react-store';
397
+ import { createStore } from '@asaidimu/react-store';
398
+ import { WebStoragePersistence, IndexedDBPersistence } from '@asaidimu/utils-persistence';
399
+ import React, { useEffect } from 'react';
250
400
 
251
401
  // 1. Using WebStoragePersistence (localStorage by default)
252
402
  // Data persists even if the browser tab is closed and reopened.
@@ -255,7 +405,7 @@ const useLocalStore = createStore(
255
405
  {
256
406
  state: { sessionCount: 0, lastVisited: new Date().toISOString() },
257
407
  actions: {
258
- incrementSessionCount: (state) => ({ sessionCount: state.sessionCount + 1 }),
408
+ incrementSessionCount: ({state}) => ({ sessionCount: state.sessionCount + 1 }),
259
409
  updateLastVisited: () => ({ lastVisited: new Date().toISOString() }),
260
410
  },
261
411
  },
@@ -283,7 +433,7 @@ const useUserProfileStore = createStore(
283
433
  state: { userId: '', preferences: { language: 'en', darkMode: false } },
284
434
  actions: {
285
435
  setUserId: (state, id: string) => ({ userId: id }),
286
- toggleDarkMode: (state) => ({ preferences: { darkMode: !state.preferences.darkMode } }),
436
+ toggleDarkMode: ({state}) => ({ preferences: { darkMode: !state.preferences.darkMode } }),
287
437
  },
288
438
  },
289
439
  { persistence: indexedDBPersistence },
@@ -296,7 +446,7 @@ function AppWithPersistence() {
296
446
  const sessionCount = selectLocal(s => s.sessionCount);
297
447
  const darkMode = selectProfile(s => s.preferences.darkMode);
298
448
 
299
- React.useEffect(() => {
449
+ useEffect(() => {
300
450
  if (localReady) {
301
451
  actionsLocal.incrementSessionCount();
302
452
  actionsLocal.updateLastVisited();
@@ -304,7 +454,7 @@ function AppWithPersistence() {
304
454
  if (profileReady && !selectProfile(s => s.userId)) {
305
455
  actionsProfile.setUserId('user-' + Math.random().toString(36).substring(2, 9));
306
456
  }
307
- }, [localReady, profileReady]);
457
+ }, [localReady, profileReady, actionsLocal, actionsProfile, selectProfile]); // Added dependencies for useEffect
308
458
 
309
459
  if (!localReady || !profileReady) {
310
460
  return <div>Loading persisted data...</div>;
@@ -325,10 +475,11 @@ function AppWithPersistence() {
325
475
 
326
476
  ### Middleware
327
477
 
328
- Middleware functions can intercept and modify or block state updates. They are executed in the order they are added.
478
+ Middleware functions can intercept and modify or block state updates. They are executed in the order they are added. Transforming middleware can alter the state update payload, while blocking middleware can prevent an update from proceeding.
329
479
 
330
480
  ```typescript
331
- import { createStore, Middleware, BlockingMiddleware } from '@asaidimu/react-store';
481
+ import { createStore, type Middleware, type BlockingMiddleware } from '@asaidimu/react-store';
482
+ import React from 'react';
332
483
 
333
484
  interface CartState {
334
485
  items: Array<{ id: string; name: string; quantity: number; price: number }>;
@@ -359,7 +510,7 @@ const validateItemMiddleware: BlockingMiddleware<CartState> = (state, update) =>
359
510
  const useCartStore = createStore({
360
511
  state: { items: [], total: 0 },
361
512
  actions: {
362
- addItem: (state, item: { id: string; name: string; price: number }) => {
513
+ addItem: ({state}, item: { id: string; name: string; price: number }) => {
363
514
  const existingItem = state.items.find(i => i.id === item.id);
364
515
  if (existingItem) {
365
516
  return {
@@ -372,10 +523,11 @@ const useCartStore = createStore({
372
523
  items: [...state.items, { ...item, quantity: 1 }],
373
524
  };
374
525
  },
375
- updateQuantity: (state, id: string, quantity: number) => ({
526
+ updateQuantity: ({state}, id: string, quantity: number) => ({
376
527
  items: state.items.map(item => (item.id === id ? { ...item, quantity } : item)),
377
528
  }),
378
529
  },
530
+ // Middleware now accepts an object mapping names to middleware functions
379
531
  middleware: { calculateTotal: calculateTotalMiddleware },
380
532
  blockingMiddleware: { validateItem: validateItemMiddleware },
381
533
  });
@@ -407,10 +559,11 @@ function CartComponent() {
407
559
 
408
560
  ### Observability
409
561
 
410
- Enable metrics and debugging via the `store` and `observer` objects:
562
+ Enable metrics and debugging via the `observer` and `actionTracker` objects. The `enableMetrics` option in `createStore` is crucial for activating these features.
411
563
 
412
564
  ```tsx
413
565
  import { createStore } from '@asaidimu/react-store';
566
+ import React from 'react';
414
567
 
415
568
  const useObservedStore = createStore(
416
569
  {
@@ -421,7 +574,7 @@ const useObservedStore = createStore(
421
574
  },
422
575
  },
423
576
  {
424
- enableMetrics: true, // Crucial for enabling the 'observer' object
577
+ enableMetrics: true, // Crucial for enabling the 'observer' and 'actionTracker' objects
425
578
  enableConsoleLogging: true, // Log events directly to browser console
426
579
  logEvents: { updates: true, middleware: true, transactions: true }, // Which event types to log
427
580
  performanceThresholds: {
@@ -440,16 +593,16 @@ function DebugPanel() {
440
593
  // Access performance metrics
441
594
  const metrics = observer?.getPerformanceMetrics();
442
595
 
443
- // Access state history for time travel
596
+ // Access state history for time travel (if maxStateHistory > 0)
444
597
  const timeTravel = observer?.createTimeTravel();
445
598
 
446
599
  // Access action execution history
447
- const actionHistory = actionTracker.getExecutions();
600
+ const actionHistory = actionTracker?.getExecutions() || []; // actionTracker is only available if enableMetrics is true
448
601
 
449
602
  return (
450
603
  <div>
451
604
  <h2>Debug Panel</h2>
452
- {observer && (
605
+ {observer && ( // Check if observer is enabled
453
606
  <>
454
607
  <h3>Performance Metrics</h3>
455
608
  <p>Update Count: {metrics?.updateCount}</p>
@@ -480,47 +633,50 @@ function DebugPanel() {
480
633
 
481
634
  ### Remote Observability
482
635
 
483
- Send collected metrics and traces to external systems like OpenTelemetry, Prometheus, or Grafana Cloud for centralized monitoring.
636
+ Send collected metrics and traces to external systems like OpenTelemetry, Prometheus, or Grafana Cloud for centralized monitoring. This functionality typically resides in the `@asaidimu/utils-store` ecosystem.
484
637
 
485
638
  ```tsx
486
- import { createStore, useRemoteObservability } from '@asaidimu/react-store';
639
+ import { createStore } from '@asaidimu/react-store';
640
+ import { useRemoteObservability } from '@asaidimu/utils-store';
487
641
  import React, { useEffect } from 'react';
488
642
 
489
643
  const useRemoteStore = createStore(
490
644
  {
491
645
  state: { apiCallsMade: 0, lastApiError: null },
492
646
  actions: {
493
- simulateApiCall: async (state) => {
494
- // Simulate an error 10% of the time
647
+ simulateApiCall: async ({state}) => {
495
648
  if (Math.random() < 0.1) {
496
649
  throw new Error('API request failed');
497
650
  }
498
651
  return { apiCallsMade: state.apiCallsMade + 1, lastApiError: null };
499
652
  },
500
- handleApiError: (state, error: string) => ({ lastApiError: error })
653
+ handleApiError: ({state}, error: string) => ({ lastApiError: error })
501
654
  },
502
655
  },
503
656
  {
504
657
  enableMetrics: true, // Required for RemoteObservability
505
658
  enableConsoleLogging: false,
506
- collectCategories: {
507
- performance: true,
508
- errors: true,
509
- stateChanges: true,
510
- middleware: true,
511
- },
512
- reportingInterval: 10000, // Send metrics every 10 seconds
513
- batchSize: 10, // Send after 10 metrics or interval, whichever comes first
514
- immediateReporting: false, // Don't send immediately after each metric
659
+ // collectCategories configuration would typically be passed to useRemoteObservability
660
+ // reportingInterval, batchSize, immediateReporting would also be part of useRemoteObservability options
515
661
  }
516
662
  );
517
663
 
518
664
  function MonitoringIntegration() {
519
665
  const { store, observer } = useRemoteStore();
666
+ // useRemoteObservability is assumed to be part of utils-store or a separate utility
520
667
  const { remote, addOpenTelemetryDestination, addPrometheusDestination, addGrafanaCloudDestination } = useRemoteObservability(store, {
521
668
  serviceName: 'my-react-app',
522
669
  environment: 'development',
523
670
  instanceId: `web-client-${Math.random().toString(36).substring(2, 9)}`,
671
+ collectCategories: {
672
+ performance: true,
673
+ errors: true,
674
+ stateChanges: true,
675
+ middleware: true,
676
+ },
677
+ reportingInterval: 10000, // Send metrics every 10 seconds
678
+ batchSize: 10, // Send after 10 metrics or interval, whichever comes first
679
+ immediateReporting: false, // Don't send immediately after each metric
524
680
  });
525
681
 
526
682
  useEffect(() => {
@@ -551,24 +707,31 @@ function MonitoringIntegration() {
551
707
  }, 5000); // Report every 5 seconds
552
708
 
553
709
  return () => clearInterval(interval);
554
- }, []);
710
+ }, [observer, addOpenTelemetryDestination, addPrometheusDestination, addGrafanaCloudDestination]); // Added dependencies
555
711
 
556
712
  return null; // This component doesn't render anything visually
557
713
  }
558
714
 
559
- // In your App component:
560
- // <MonitoringIntegration />
561
- // <button onClick={() => useRemoteStore().actions.simulateApiCall().catch(e => useRemoteStore().actions.handleApiError(e.message))}>
562
- // Simulate API Call
563
- // </button>
715
+ // In your App component, you would use it like:
716
+ // function MyApp() {
717
+ // return (
718
+ // <>
719
+ // <MonitoringIntegration />
720
+ // <button onClick={() => useRemoteStore().actions.simulateApiCall().catch(e => useRemoteStore().actions.handleApiError(e.message))}>
721
+ // Simulate API Call
722
+ // </button>
723
+ // </>
724
+ // );
725
+ // }
564
726
  ```
565
727
 
566
728
  ### Transaction Support
567
729
 
568
- Group related updates that should succeed or fail together. If an error occurs within the transaction, all changes made during that transaction are automatically rolled back.
730
+ Group related updates that should succeed or fail together. If an error occurs within the transaction, all changes made during that transaction are automatically rolled back, maintaining state consistency.
569
731
 
570
732
  ```typescript
571
733
  import { createStore } from '@asaidimu/react-store';
734
+ import React from 'react';
572
735
 
573
736
  interface BankState {
574
737
  checking: number;
@@ -579,7 +742,7 @@ interface BankState {
579
742
  const useBankStore = createStore<BankState, any>({
580
743
  state: { checking: 1000, savings: 500, transactions: [] },
581
744
  actions: {
582
- transferFunds: async (state, fromAccount: 'checking' | 'savings', toAccount: 'checking' | 'savings', amount: number) => {
745
+ transferFunds: async ({state}, fromAccount: 'checking' | 'savings', toAccount: 'checking' | 'savings', amount: number) => {
583
746
  if (amount <= 0) {
584
747
  throw new Error('Transfer amount must be positive.');
585
748
  }
@@ -607,14 +770,15 @@ const useBankStore = createStore<BankState, any>({
607
770
  });
608
771
 
609
772
  function BankApp() {
610
- const { select, actions } = useBankStore();
773
+ const { select, actions, store } = useBankStore();
611
774
  const checkingBalance = select(s => s.checking);
612
775
  const savingsBalance = select(s => s.savings);
613
776
  const transactions = select(s => s.transactions);
614
777
 
615
778
  const handleTransfer = async (from: 'checking' | 'savings', to: 'checking' | 'savings', amount: number) => {
616
779
  try {
617
- await actions.transferFunds(from, to, amount);
780
+ // Wrap the action call in a store transaction
781
+ await store.transaction(() => actions.transferFunds(from, to, amount));
618
782
  alert(`Successfully transferred ${amount} from ${from} to ${to}.`);
619
783
  } catch (error) {
620
784
  alert(`Transfer failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -642,7 +806,7 @@ function BankApp() {
642
806
 
643
807
  ### Event System
644
808
 
645
- The store emits various events during its lifecycle, which you can subscribe to for logging, analytics, or custom side effects.
809
+ The store emits various events during its lifecycle, which you can subscribe to for logging, analytics, or custom side effects. This is done via `store.onStoreEvent()`.
646
810
 
647
811
  ```typescript
648
812
  import { createStore } from '@asaidimu/react-store';
@@ -652,7 +816,7 @@ const useEventStore = createStore(
652
816
  {
653
817
  state: { data: 'initial', processedCount: 0 },
654
818
  actions: {
655
- processData: (state, newData: string) => ({ data: newData, processedCount: state.processedCount + 1 }),
819
+ processData: ({state}, newData: string) => ({ data: newData, processedCount: state.processedCount + 1 }),
656
820
  triggerError: () => { throw new Error("Action failed intentionally"); }
657
821
  },
658
822
  middleware: {
@@ -665,7 +829,7 @@ const useEventStore = createStore(
665
829
  );
666
830
 
667
831
  function EventMonitor() {
668
- const { store } = useEventStore();
832
+ const { store, actions } = useEventStore();
669
833
  const [eventLogs, setEventLogs] = React.useState<string[]>([]);
670
834
 
671
835
  useEffect(() => {
@@ -718,8 +882,6 @@ function EventMonitor() {
718
882
  };
719
883
  }, [store]); // Re-subscribe if store instance changes (unlikely)
720
884
 
721
- const { actions } = useEventStore();
722
-
723
885
  return (
724
886
  <div>
725
887
  <h3>Store Event Log</h3>
@@ -756,7 +918,7 @@ const useDataStore = createStore({
756
918
  await new Promise(resolve => setTimeout(resolve, 2000));
757
919
  return { items: ['Item A', 'Item B', 'Item C'] };
758
920
  },
759
- addItem: async (state, item: string) => {
921
+ addItem: async ({state}, item: string) => {
760
922
  // Simulate a quick add operation
761
923
  await new Promise(resolve => setTimeout(resolve, 500));
762
924
  return { items: [...state.items, item] };
@@ -798,19 +960,22 @@ function DataLoader() {
798
960
  The hook returned by `createStore` provides several properties for advanced usage and debugging, beyond the commonly used `select`, `actions`, and `isReady`:
799
961
 
800
962
  ```tsx
963
+ import { useStore as useMyStore } from '../ui/store'; // Assuming this is your store definition
964
+
801
965
  function MyAdvancedComponent() {
802
966
  const {
803
- select, // Function to select state parts (memoized)
804
- actions, // Object containing your defined actions (debounced)
967
+ select, // Function to select state parts (memoized, reactive)
968
+ actions, // Object containing your defined actions (debounced, promise-returning)
805
969
  isReady, // Boolean indicating if persistence is ready
806
- store, // Direct access to the ReactiveDataStore instance
807
- observer, // StoreObservability instance (if `enableMetrics` was true)
808
- actionTracker, // Instance of ActionTracker for monitoring action executions
809
- state, // A hook `() => T` to get the entire reactive state object
970
+ store, // Direct access to the ReactiveDataStore instance (from @asaidimu/utils-store)
971
+ observer, // StoreObservability instance (from @asaidimu/utils-store, if `enableMetrics` was true)
972
+ actionTracker, // Instance of ActionTracker for monitoring action executions (if `enableMetrics` was true)
973
+ state, // A getter function `() => TState` to get the entire current state object (reactive)
810
974
  watch, // Function to watch the loading status of actions
975
+ resolve, // Function to resolve an artifact (if artifacts are defined)
811
976
  } = useMyStore(); // Assuming useMyStore is defined from createStore
812
977
 
813
- // Example: Accessing the full state (use with caution for performance, `select` is preferred)
978
+ // Example: Accessing the full state (use with caution for performance; `select` is preferred)
814
979
  const fullCurrentState = state();
815
980
  console.log("Full reactive state:", fullCurrentState);
816
981
 
@@ -821,15 +986,24 @@ function MyAdvancedComponent() {
821
986
  }
822
987
 
823
988
  // Example: Accessing action history
824
- console.log("Action executions:", actionTracker.getExecutions());
989
+ if (actionTracker) { // actionTracker is only available if enableMetrics is true
990
+ console.log("Action executions:", actionTracker.getExecutions());
991
+ }
825
992
 
826
993
  // Example: Watching a specific action's loading state
827
- const isLoadingSomeAction = watch('someActionName');
828
- console.log("Is 'someActionName' loading?", isLoadingSomeAction);
994
+ const isLoadingSomeAction = watch('checkout'); // Assuming 'checkout' is an action
995
+ console.log("Is 'checkout' action loading?", isLoadingSomeAction);
996
+
997
+ // Example: Resolving an artifact (if defined in your store)
998
+ // const [myArtifact, isArtifactReady] = resolve('myArtifactKey');
999
+ // if (isArtifactReady) {
1000
+ // console.log("My artifact is ready:", myArtifact);
1001
+ // }
829
1002
 
830
1003
  return (
831
1004
  <div>
832
1005
  {/* ... your component content ... */}
1006
+ <p>Store Ready: {isReady ? 'Yes' : 'No'}</p>
833
1007
  </div>
834
1008
  );
835
1009
  }
@@ -837,22 +1011,19 @@ function MyAdvancedComponent() {
837
1011
 
838
1012
  ## Project Architecture
839
1013
 
840
- `@asaidimu/react-store` is structured to provide a modular yet integrated state management solution.
1014
+ `@asaidimu/react-store` is structured to provide a modular yet integrated state management solution. It separates core state logic into reusable utilities while offering a streamlined React-specific API.
841
1015
 
842
1016
  ### Internal Structure
843
1017
 
844
- The core logic of this package resides directly within the `src/` directory:
1018
+ The core logic of this package integrates and extends external utility packages, with `src/store.ts` serving as the main entry point for the React hook.
845
1019
 
846
1020
  ```
847
1021
  .
848
1022
  ├── src/
849
- │ ├── execution.ts # Action tracking interface and class
850
- │ ├── hash.ts # Utilities for hashing objects (e.g., for selectors)
851
- ├── paths.ts # Utility for building selector paths
852
- ├── selector.ts # Selector memoization manager
853
- │ ├── store.ts # Main `createStore` React hook implementation
854
- │ └── types.ts # Core TypeScript types for the library
855
- ├── index.ts # Main entry point for the library (re-exports `createStore` and other public APIs)
1023
+ │ ├── execution.ts # Manages tracking and history of action executions.
1024
+ │ ├── store.ts # The main `createStore` function, integrating `ReactiveDataStore` and `StoreObservability` from `@asaidimu/utils-store` with React's `useSyncExternalStore`.
1025
+ └── types.ts # Defines core TypeScript interfaces for store definitions, actions, middleware, and the store hook.
1026
+ ├── index.ts # Main library entry point, re-exporting `createStore` and core types.
856
1027
  ├── package.json
857
1028
  └── tsconfig.json
858
1029
  ```
@@ -861,37 +1032,39 @@ The core logic of this package resides directly within the `src/` directory:
861
1032
 
862
1033
  This library leverages the following `@asaidimu` packages for its core functionalities:
863
1034
 
864
- * **`@asaidimu/utils-store`**: Provides the foundational `ReactiveDataStore` for state management, including immutable updates, transactions, and core event emission. It also includes `StoreObservability` for deep insights into state changes.
865
- * **`@asaidimu/utils-persistence`**: Offers persistence adapters like `WebStoragePersistence` and `IndexedDBPersistence` for saving and loading state.
1035
+ * **`@asaidimu/utils-store`**: Provides the foundational `ReactiveDataStore` for immutable state management, transactions, core event emission, and the `StoreObservability` instance for deep insights into state changes.
1036
+ * **`@asaidimu/utils-persistence`**: Offers various persistence adapters like `WebStoragePersistence` and `IndexedDBPersistence` for saving and loading state, including cross-tab synchronization.
866
1037
 
867
1038
  ### Core Components
868
1039
 
869
- * **`ReactiveDataStore` (from `@asaidimu/utils-store`)**: The foundational state management layer. This external library manages the immutable state, processes updates, handles middleware, transactions, and interacts with persistence adapters. It also emits detailed internal events for observability.
870
- * **`StoreObservability` (from `@asaidimu/utils-store`)**: An extension built on top of the external `ReactiveDataStore`'s event system. It provides debugging features like event history, state snapshots for time-travel, performance metrics, and utilities to create logging/validation middleware.
871
- * **`createStore` Hook (`src/store.ts`)**: The primary React-facing API. It instantiates the external `ReactiveDataStore` and `StoreObservability`, wraps actions with debouncing and tracking, and provides the `select` hook powered by `useSyncExternalStore` for efficient component updates, and the `watch` function for action loading states.
872
- * **Persistence Adapters (from `@asaidimu/utils-persistence`)**: Implement the `DataStorePersistence` interface. `WebStoragePersistence` and `IndexedDBPersistence` provide concrete storage solutions with cross-tab synchronization.
1040
+ * **`ReactiveDataStore` (from `@asaidimu/utils-store`)**: The heart of the state management. It handles immutable state updates, middleware processing, transaction management, and emits detailed internal events about state changes.
1041
+ * **`StoreObservability` (from `@asaidimu/utils-store`)**: Built on top of `ReactiveDataStore`'s event system, this component provides comprehensive debugging and monitoring features. This includes event history, state snapshots for time-travel, performance metrics, and utilities for logging or validation middleware.
1042
+ * **`ActionTracker` (`src/execution.ts`)**: A dedicated class for tracking the lifecycle and performance of individual action executions, capturing details like start/end times, duration, parameters, and outcomes (success/error).
1043
+ * **`createStore` Hook (`src/store.ts`)**: The primary React-facing API. It instantiates `ReactiveDataStore`, `StoreObservability`, and `ActionTracker`. It wraps user-defined actions with debouncing and tracking, and provides the `select` function (powered by `useSyncExternalStore` for efficient component updates) and the `watch` function for action loading states.
1044
+ * **Persistence Adapters (from `@asaidimu/utils-persistence`)**: Implement the `DataStorePersistence` interface. `WebStoragePersistence` (for `localStorage`/`sessionStorage`) and `IndexedDBPersistence` provide concrete, ready-to-use storage solutions with cross-tab synchronization capabilities.
873
1045
 
874
1046
  ### Data Flow
875
1047
 
876
- 1. **Action Dispatch**: A React component calls an action (e.g., `actions.increment(1)`). Actions are debounced by default.
877
- 2. **Action Loading State Update**: The store immediately updates the loading state for the dispatched action to `true`.
878
- 3. **Action Execution Tracking**: The `ActionTracker` records the action's details (name, params, start time).
879
- 4. **State Update Request**: The action, after potential debouncing, initiates a `store.set()` call with a partial state update or a function returning a partial object.
880
- 5. **Transaction Context**: If within a `store.transaction()`, the state is snapshotted for potential rollback.
881
- 6. **Blocking Middleware**: Updates first pass through `blockingMiddleware`. If any middleware returns `false` or throws, the update is halted, and the state is not modified.
882
- 7. **Transform Middleware**: If not blocked, updates then pass through transforming `middleware`. These functions can modify the partial update.
883
- 8. **State Merging**: The final transformed update is immutably merged into the current state using `ReactiveDataStore`'s internal merge utility.
884
- 9. **Change Detection**: `ReactiveDataStore`'s internal diffing mechanism identifies which paths in the state have truly changed.
885
- 10. **Persistence**: If changes occurred, the new state is saved via the configured `DataStorePersistence` adapter (e.g., `localStorage`, `IndexedDB`). External changes from persistence are also subscribed to and applied.
886
- 11. **Listener Notification**: Only `React.useSyncExternalStore` subscribers whose selected paths have changed are notified, triggering re-renders of relevant components.
887
- 12. **Action Loading State Reset**: Once the action completes (success or error), the loading state for that action is reset to `false`.
888
- 13. **Observability Events**: Throughout this flow, the `ReactiveDataStore` emits fine-grained events (`update:start`, `middleware:complete`, `transaction:error`, etc.) that `StoreObservability` captures for debugging, metrics, and remote reporting.
1048
+ 1. **Action Dispatch**: A React component calls a bound action (e.g., `actions.addItem()`).
1049
+ 2. **Action Debouncing**: Actions are debounced by default (configurable), preventing rapid successive calls.
1050
+ 3. **Action Loading State Update**: The store immediately updates the loading state for the dispatched action to `true` via `ReactiveDataStore`.
1051
+ 4. **Action Execution Tracking**: The `ActionTracker` records the action's details (name, parameters, start time).
1052
+ 5. **State Update Request**: The action's implementation returns a partial state update or a promise resolving to one.
1053
+ 6. **Transaction Context**: If the action is wrapped within `store.transaction()`, the current state is snapshotted to enable potential rollback.
1054
+ 7. **Blocking Middleware**: The update first passes through any registered `blockingMiddleware`. If any middleware returns `false` or throws an error, the update is halted, and the state remains unchanged (and rolled back if in a transaction).
1055
+ 8. **Transform Middleware**: If not blocked, the update then passes through transforming `middleware`. These functions can modify the partial update payload.
1056
+ 9. **State Merging**: The final, possibly transformed, update is immutably merged into the current state using `ReactiveDataStore`'s internal utility.
1057
+ 10. **Change Detection**: `ReactiveDataStore` performs a deep diff to identify precisely which paths in the state have changed.
1058
+ 11. **Persistence**: If changes occurred, the new state is saved via the configured `DataStorePersistence` adapter (e.g., `localStorage`, `IndexedDB`). The system also subscribes to external changes from persistence for cross-tab synchronization.
1059
+ 12. **Listener Notification**: `React.useSyncExternalStore` subscribers (used by `select`) whose selected paths have changed are notified, triggering efficient re-renders of only the relevant components.
1060
+ 13. **Action Loading State Reset**: Once the action completes (either successfully or with an error), the loading state for that action is reset to `false`.
1061
+ 14. **Observability Events**: Throughout this entire flow, `ReactiveDataStore` emits fine-grained events (`update:start`, `middleware:complete`, `transaction:error`, etc.) which `StoreObservability` captures for debugging, metrics collection, and remote reporting.
889
1062
 
890
1063
  ### Extension Points
891
1064
 
892
- * **Custom Middleware**: Easily add your own `Middleware` or `BlockingMiddleware` functions for custom logic.
893
- * **Custom Persistence Adapters**: Implement the `DataStorePersistence<T>` interface to integrate with any storage solution (e.g., a backend API, WebSockets, or a custom in-memory store).
894
- * **Remote Observability Destinations**: Create new `RemoteDestination` implementations to send metrics and traces to any external observability platform not already supported.
1065
+ * **Custom Middleware**: Easily add your own `Middleware` or `BlockingMiddleware` functions for custom logic (e.g., advanced logging, validation, side effects).
1066
+ * **Custom Persistence Adapters**: Implement the `DataStorePersistence<T>` interface (from `@asaidimu/utils-persistence`) to integrate with any storage solution (e.g., a backend API, WebSockets, or a custom in-memory store).
1067
+ * **Remote Observability Destinations**: Create new `RemoteDestination` implementations (part of `@asaidimu/utils-store`) to send metrics and traces to any external observability platform not already supported by default.
895
1068
 
896
1069
  ## Development & Contributing
897
1070
 
@@ -901,7 +1074,7 @@ We welcome contributions! Please follow the guidelines below.
901
1074
 
902
1075
  1. **Clone the repository:**
903
1076
  ```bash
904
- git clone https://github.com/asaidimu/react-store.git
1077
+ git clone https://github.com/asaidimu/node-react.git
905
1078
  cd react-store
906
1079
  ```
907
1080
  2. **Install dependencies:**
@@ -912,18 +1085,18 @@ We welcome contributions! Please follow the guidelines below.
912
1085
 
913
1086
  ### Scripts
914
1087
 
915
- * `bun ci`: Installs dependencies (for CI/CD environments).
916
- * `bun test`: Runs all unit tests using `Vitest`.
917
- * `bun test:ci`: Runs tests in CI mode (single run).
918
- * `bun clean`: Removes the `dist` directory.
919
- * `bun prebuild`: Cleans `dist` and runs a sync script (internal).
920
- * `bun build`: Compiles the TypeScript source into `dist/` for CJS and ESM formats, generates type definitions, and minifies.
921
- * `bun dev`: Starts the e-commerce dashboard demonstration.
922
- * `bun postbuild`: Copies `README.md`, `LICENSE.md`, and `dist.package.json` into the `dist` folder.
1088
+ * `bun ci`: Installs dependencies, typically used in CI/CD environments to ensure a clean install.
1089
+ * `bun test`: Runs all unit tests using `Vitest` in interactive watch mode.
1090
+ * `bun test:ci`: Runs all unit tests once, suitable for CI/CD pipelines.
1091
+ * `bun clean`: Removes the `dist` directory, cleaning up previous build artifacts.
1092
+ * `bun prebuild`: Pre-build step that cleans the `dist` directory and runs an internal package synchronization script.
1093
+ * `bun build`: Compiles the TypeScript source into `dist/` for CJS and ESM formats, generates type definitions, and minifies the code using `Terser`.
1094
+ * `bun dev`: Starts the e-commerce dashboard demonstration application using `Vite`.
1095
+ * `bun postbuild`: Post-build step that copies `README.md`, `LICENSE.md`, and the specialized `dist.package.json` into the `dist` folder, preparing the package for publishing.
923
1096
 
924
1097
  ### Testing
925
1098
 
926
- Tests are written using `Vitest` and `React Testing Library`.
1099
+ Tests are written using `Vitest` and `React Testing Library` for component and hook testing.
927
1100
 
928
1101
  To run tests:
929
1102
 
@@ -937,103 +1110,103 @@ bun test --watch
937
1110
 
938
1111
  1. **Fork** the repository and create your branch from `main`.
939
1112
  2. **Code Standards**: Ensure your code adheres to existing coding styles (TypeScript, ESLint, Prettier are configured).
940
- 3. **Tests**: Add unit and integration tests for new features or bug fixes. Ensure all tests pass.
941
- 4. **Commits**: Follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit messages.
942
- 5. **Pull Requests**: Submit a pull request to the `main` branch. Provide a clear description of your changes.
1113
+ 3. **Tests**: Add unit and integration tests for new features or bug fixes. Ensure all tests pass (`bun test`).
1114
+ 4. **Commits**: Follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit messages. This project uses `semantic-release` for automated versioning and changelog generation.
1115
+ 5. **Pull Requests**: Submit a pull request to the `main` branch. Provide a clear description of your changes, referencing any relevant issues.
943
1116
 
944
1117
  ### Issue Reporting
945
1118
 
946
- For bugs, feature requests, or questions, please open an issue on the [GitHub Issues page](https://github.com/asaidimu/react-store/issues).
1119
+ For bugs, feature requests, or questions, please open an issue on the [GitHub Issues page](https://github.com/asaidimu/node-react/issues).
947
1120
 
948
1121
  ## Additional Information
949
1122
 
950
1123
  ### Best Practices
951
1124
 
952
- 1. **Granular Selectors**: Always use `select((state) => state.path.to.value)` instead of `select((state) => state)` to prevent unnecessary re-renders of components.
953
- 2. **Action Design**: Keep actions focused on a single responsibility. Use `async` actions for asynchronous operations and return partial updates upon completion.
1125
+ 1. **Granular Selectors**: Always use `select((state) => state.path.to.value)` instead of `select((state) => state)` to prevent unnecessary re-renders of components. The more specific your selector, the more optimized your component updates will be.
1126
+ 2. **Action Design**: Keep actions focused on a single responsibility. Use `async` actions for asynchronous operations (e.g., API calls) and return partial updates or promises resolving to partial updates upon completion. Actions should describe *what* happened, not *how*.
954
1127
  3. **Persistence**:
955
1128
  * Use unique `storeId` or `storageKey` for each distinct store to avoid data conflicts.
956
- * Always check the `isReady` flag for UI elements that depend on the initial state loaded from persistence.
957
- 4. **Middleware**: Leverage middleware for cross-cutting concerns like logging, analytics, or complex validation logic.
958
- 5. **`Symbol.for("delete")`**: Use this explicit symbol for property removal to maintain clarity and avoid accidental data mutations.
1129
+ * Always check the `isReady` flag for UI elements that depend on the initial state loaded from persistence, to prevent rendering incomplete data.
1130
+ 4. **Middleware**: Leverage middleware for cross-cutting concerns like logging, analytics, data transformation, or complex validation logic that applies to multiple actions.
1131
+ 5. **`Symbol.for("delete")`**: Use this explicit symbol for property removal to maintain clarity and avoid accidental data mutations or unexpected behavior when merging partial updates.
1132
+ 6. **Debounce Time**: Adjust the `debounceTime` in `createStore` options for actions that might be called rapidly (e.g., search input, scroll events) to optimize performance. A `debounceTime` of `0` means actions execute immediately.
959
1133
 
960
1134
  ### API Reference
961
1135
 
962
1136
  #### `createStore(definition, options)`
963
1137
 
964
- The main entry point for creating a store.
1138
+ The main entry point for creating a store hook.
965
1139
 
966
1140
  ```typescript
967
- type StoreDefinition<T, R extends Actions<T>> = {
968
- state: T; // Initial state object
969
- actions: R; // Object mapping action names to action functions
970
- middleware?: Record<string, Middleware<T>>; // Optional transforming middleware
971
- blockingMiddleware?: Record<string, BlockingMiddleware<T>>; // Optional blocking middleware
972
- };
1141
+ // From src/types.ts
1142
+ export interface StoreDefinition<
1143
+ TState extends object,
1144
+ TArtifactsMap extends ArtifactsMap<TState>, // Artifacts can be defined but not shown in this example
1145
+ TActions extends ActionMap<TState, TArtifactsMap>,
1146
+ > {
1147
+ state: TState;
1148
+ actions: TActions;
1149
+ artifacts?: TArtifactsMap; // Optional artifact definitions
1150
+ blockingMiddleware?: Record<string, BlockingMiddleware<TState>>; // Optional blocking middleware
1151
+ middleware?: Record<string, Middleware<TState>>; // Optional transforming middleware
1152
+ }
973
1153
 
974
- interface StoreOptions<T> {
975
- enableMetrics?: boolean; // Enable StoreObservability features (default: false)
976
- enableConsoleLogging?: boolean; // Log store events to console (default: false)
977
- maxEvents?: number; // Maximum number of events to keep in history (default: 500)
978
- maxStateHistory?: number; // Maximum number of state snapshots for time travel (default: 20)
979
- logEvents?: { // Which event categories to log (defaults to all true if enableConsoleLogging is true)
980
- updates?: boolean;
981
- middleware?: boolean;
982
- transactions?: boolean;
983
- };
984
- performanceThresholds?: { // Thresholds for logging slow operations (in ms)
985
- updateTime?: number; // default: 50ms
986
- middlewareTime?: number; // default: 20ms
987
- };
988
- persistence?: DataStorePersistence<T>; // Optional persistence adapter instance
989
- debounceTime?: number; // Time in milliseconds to debounce actions (default: 0ms)
1154
+ interface StoreOptions<T> extends ObserverOptions { // ObserverOptions from @asaidimu/utils-store
1155
+ enableMetrics?: boolean; // Enable StoreObservability and ActionTracker features (default: false)
1156
+ persistence?: SimplePersistence<T>; // Optional persistence adapter instance (from @asaidimu/utils-persistence)
1157
+ debounceTime?: number; // Time in milliseconds to debounce actions (default: 0ms)
1158
+ // Other ObserverOptions for logging, performanceThresholds, maxEvents, maxStateHistory are inherited
990
1159
  }
991
1160
 
992
- const useStore = createStore(definition, options);
1161
+ const useStoreHook = createStore(definition, options);
993
1162
  ```
994
1163
 
995
- **Returns**: A `useStore` hook which, when called in a component, returns an object with:
996
- * `store`: Direct access to the `ReactiveDataStore` instance.
997
- * `observer`: The `StoreObservability` instance (available if `enableMetrics` is `true`). Provides debug and monitoring utilities.
998
- * `select`: A memoized selector function to extract specific state slices. Re-renders components only when selected data changes.
999
- * `actions`: An object containing your defined actions. These actions are debounced and tracked.
1000
- * `actionTracker`: An instance of `ActionTracker` for monitoring the execution history of your actions.
1001
- * `state`: A hook `() => T` that returns the entire current state object. Use sparingly as it will cause re-renders on *any* state change.
1164
+ **Returns**: A React hook (`useStoreHook`) which, when called in a component, returns an object with the following properties:
1165
+
1166
+ * `store`: Direct access to the underlying `ReactiveDataStore` instance (from `@asaidimu/utils-store`). This provides low-level control and event subscription.
1167
+ * `observer`: The `StoreObservability` instance (from `@asaidimu/utils-store`). Available only if `enableMetrics` is `true`. Provides debug, time-travel, and monitoring utilities.
1168
+ * `select`: A memoized selector function `(<S>(selector: (state: TState) => S) => S)`. Extracts specific state slices. Re-renders components only when selected data changes.
1169
+ * `actions`: An object containing your defined actions, fully typed and bound to the store. These actions are debounced (if `debounceTime` > 0) and their loading states are tracked. Each action returns a `Promise<TState>` resolving to the new state after the action completes.
1170
+ * `actionTracker`: An instance of `ActionTracker` (from `src/execution.ts`). Available only if `enableMetrics` is `true`. Provides methods for monitoring the execution history of your actions.
1171
+ * `state`: A getter function `(() => TState)` that returns the entire current state object. Use sparingly, as components relying on this will re-render on *any* state change. `select` is generally preferred for performance.
1002
1172
  * `isReady`: A boolean indicating whether the store's persistence layer (if configured) has finished loading its initial state.
1003
- * `watch`: A function to watch the loading status of actions.
1173
+ * `watch`: A function `(<K extends keyof TActions>(action: K) => boolean)` to watch the loading status of individual actions. Returns `true` if the action is currently executing.
1174
+ * `resolve`: A reactive artifact resolver `(<K extends keyof TArtifactsMap>(key: K) => readonly [ExtractInstanceFromMap<TArtifactsMap, K>, true] | readonly [ExtractInstanceFromMap<TArtifactsMap, K> | undefined, false])`. If `artifacts` are defined in the store, this hook returns `[instance, isReady]` for a specific artifact, reactively updating if the artifact instance changes or becomes ready.
1004
1175
 
1005
- #### `ReactiveDataStore` (accessed via `useStore().store`)
1176
+ #### `ReactiveDataStore` (accessed via `useStoreHook().store` from `@asaidimu/utils-store`)
1006
1177
 
1007
- * `get(clone?: boolean): T`: Retrieves the current state. Pass `true` to get a deep clone (recommended for mutations outside of actions).
1008
- * `set(update: StateUpdater<T>): Promise<void>`: Updates the state with a partial object or a function returning a partial object.
1009
- * `subscribe(path: string | string[], listener: (state: T) => void): () => void`: Subscribes a listener to changes at a specific path or array of paths. Returns an unsubscribe function.
1010
- * `transaction<R>(operation: () => R | Promise<R>): Promise<R}`: Executes a function as an atomic transaction. Rolls back all changes if an error occurs.
1011
- * `use(middleware: Middleware<T>, name?: string): string`: Adds a transforming middleware. Returns its ID.
1012
- * `useBlockingMiddleware(middleware: BlockingMiddleware<T>, name?: string): string`: Adds a blocking middleware. Returns its ID.
1178
+ * `get(clone?: boolean): TState`: Retrieves the current state. Pass `true` to get a deep clone (recommended for mutations outside of actions).
1179
+ * `set(update: StateUpdater<TState>): Promise<void>`: Updates the state with a partial object or a function returning a partial object.
1180
+ * `subscribe(path: string | string[], listener: (state: TState) => void): () => void`: Subscribes a listener to changes at a specific path or array of paths. Returns an unsubscribe function.
1181
+ * `transaction<R>(operation: () => R | Promise<R>): Promise<R>`: Executes a function as an atomic transaction. Rolls back all changes if an error occurs if the operation throws.
1182
+ * `use(middleware: Middleware<TState>, name?: string): string`: Adds a transforming middleware. Returns its ID.
1183
+ * `useBlockingMiddleware(middleware: BlockingMiddleware<TState>, name?: string): string`: Adds a blocking middleware. Returns its ID.
1013
1184
  * `removeMiddleware(id: string): boolean`: Removes a middleware by its ID.
1014
1185
  * `isReady(): boolean`: Checks if the persistence layer has loaded its initial state.
1015
- * `onStoreEvent(event: StoreEvent, listener: (data: any) => void): () => void`: Subscribes to internal store events (e.g., `'update:complete'`, `'middleware:error'`).
1186
+ * `onStoreEvent(event: StoreEvent, listener: (data: any) => void): () => void`: Subscribes to internal store events (e.g., `'update:complete'`, `'middleware:error'`, `'transaction:start'`).
1187
+
1188
+ #### `StoreObservability` (accessed via `useStoreHook().observer` from `@asaidimu/utils-store`)
1016
1189
 
1017
- #### `StoreObservability` (accessed via `useStore().observer`)
1190
+ Available only if `enableMetrics` is `true` in `createStore` options.
1018
1191
 
1019
1192
  * `getEventHistory(): DebugEvent[]`: Retrieves a history of all captured store events.
1020
- * `getStateHistory(): T[]`: Returns a history of state snapshots, enabling time-travel.
1021
- * `getRecentChanges(limit?: number): Array<{ timestamp: number; changedPaths: string[]; from: Partial<T>; to: Partial<T>; }>`: Provides a simplified view of recent state changes.
1193
+ * `getStateHistory(): TState[]`: Returns a history of state snapshots, enabling time-travel debugging (if `maxStateHistory` > 0).
1194
+ * `getRecentChanges(limit?: number): Array<{ timestamp: number; changedPaths: string[]; from: DeepPartial<TState>; to: DeepPartial<TState>; }>`: Provides a simplified view of recent state changes.
1022
1195
  * `getPerformanceMetrics(): StoreMetrics`: Returns an object containing performance statistics (e.g., `updateCount`, `averageUpdateTime`).
1023
1196
  * `createTimeTravel(): { canUndo: () => boolean; canRedo: () => boolean; undo: () => Promise<void>; redo: () => Promise<void>; getHistoryLength: () => number; clear: () => void; }`: Returns controls for time-travel debugging.
1024
- * `createLoggingMiddleware(options?: object): Middleware<T>`: A factory for a simple logging middleware.
1025
- * `createValidationMiddleware(validator: (state: T, update: DeepPartial<T>) => boolean | { valid: boolean; reason?: string }): BlockingMiddleware<T>`: A factory for a schema validation middleware.
1197
+ * `createLoggingMiddleware(options?: object): Middleware<TState>`: A factory for a simple logging middleware.
1198
+ * `createValidationMiddleware(validator: (state: TState, update: DeepPartial<TState>) => boolean | { valid: boolean; reason?: string }): BlockingMiddleware<TState>`: A factory for a schema validation middleware.
1026
1199
  * `clearHistory(): void`: Clears the event and state history.
1027
1200
  * `disconnect(): void`: Cleans up all listeners and resources.
1028
1201
 
1029
- #### Persistence Adapters
1202
+ #### Persistence Adapters (from `@asaidimu/utils-persistence`)
1030
1203
 
1031
- All adapters implement `DataStorePersistence<T>`:
1204
+ All adapters implement `SimplePersistence<T>`:
1032
1205
 
1033
1206
  * `set(id:string, state: T): boolean | Promise<boolean>`: Persists data.
1034
- * `get(): T | null | Promise<T | null>`: Retrieves data.
1035
- * `subscribe(id:string, callback: (state:T) => void): () => void`: Subscribes to external changes.
1036
- * `clear(): boolean | Promise<boolean>`: Clears persisted data.
1207
+ * `get(id:string): T | null | Promise<T | null>`: Retrieves data.
1208
+ * `subscribe(id:string, callback: (state:T) => void): () => void`: Subscribes to external changes (e.g., from other tabs).
1209
+ * `clear(id:string): boolean | Promise<boolean>`: Clears persisted data.
1037
1210
 
1038
1211
  ##### `IndexedDBPersistence(storeId: string)`
1039
1212
 
@@ -1044,13 +1217,9 @@ All adapters implement `DataStorePersistence<T>`:
1044
1217
  * **`storageKey`**: The key under which data is stored (e.g., `'app-config'`).
1045
1218
  * **`session`**: Optional. If `true`, uses `sessionStorage`; otherwise, uses `localStorage` (default: `false`).
1046
1219
 
1047
- ##### `LocalStoragePersistence(storageKey: string)` (Deprecated)
1048
-
1049
- * This is an alias for `WebStoragePersistence`. Use `WebStoragePersistence` instead.
1050
-
1051
1220
  ### Comparison with Other State Management Solutions
1052
1221
 
1053
- `@asaidimu/react-store` aims to be a comprehensive, all-in-one solution for React state management. Here's a comparison to popular alternatives:
1222
+ `@asaidimu/react-store` aims to be a comprehensive, all-in-one solution for React state management, integrating features that often require multiple libraries in other ecosystems. Here's a comparison to popular alternatives:
1054
1223
 
1055
1224
  | **Feature** | **@asaidimu/react-store** | **Redux** | **Zustand** | **MobX** | **Recoil** |
1056
1225
  | :--------------------- | :------------------------ | :----------------- | :----------------- | :----------------- | :----------------- |
@@ -1059,20 +1228,59 @@ All adapters implement `DataStorePersistence<T>`:
1059
1228
  | **API Complexity** | Medium (rich feature set balanced with simplicity). | High (many concepts: actions, reducers, etc.). | Low (straightforward). | Medium (proxies, decorators). | Medium (atom/selectors). |
1060
1229
  | **Scalability** | High (transactions, persistence, remote metrics). | High (structured but verbose). | High (small but flexible). | High (reactive scaling). | High (granular atoms). |
1061
1230
  | **Extensibility** | Excellent (middleware, custom persistence, observability). | Good (middleware, enhancers). | Good (middleware-like). | Moderate (custom reactions). | Moderate (custom selectors). |
1062
- | **Performance** | Optimized (selectors, reactive updates). | Good (predictable but manual optimization). | Excellent (minimal overhead). | Good (reactive overhead). | Good (granular updates). |
1063
- | **Bundle Size** | Moderate (includes observability, persistence, remote observability). | Large (core + toolkit). | Tiny (~1KB). | Moderate (~20KB). | Moderate (~10KB). |
1231
+ | **Performance** | Optimized (selectors, reactive updates via `useSyncExternalStore`). | Good (predictable but manual optimization). | Excellent (minimal overhead). | Good (reactive overhead). | Good (granular updates). |
1232
+ | **Bundle Size** | Moderate (includes observability, persistence, remote observability framework). | Large (core + toolkit). | Tiny (~1KB). | Moderate (~20KB). | Moderate (~10KB). |
1064
1233
  | **Persistence** | Built-in (IndexedDB, WebStorage, cross-tab). | Manual (via middleware). | Manual (via middleware). | Manual (custom). | Manual (custom). |
1065
- | **Observability** | Excellent (metrics, time-travel, remote). | Good (dev tools). | Basic (via plugins). | Good (reactive logs). | Basic (via plugins). |
1066
- | **React Integration** | Native (hooks, `useSyncExternalStore`) | Manual (React-Redux). | Native (hooks). | Native (observers). | Native (atoms). |
1234
+ | **Observability** | Excellent (metrics, time-travel, event logging, remote). | Good (dev tools). | Basic (via plugins). | Good (reactive logs). | Basic (via plugins). |
1235
+ | **React Integration** | Native (hooks, `useSyncExternalStore`). | Manual (React-Redux). | Native (hooks). | Native (observers). | Native (atoms). |
1067
1236
 
1068
1237
  #### Where `@asaidimu/react-store` Shines
1069
- * **All-in-One**: It aims to be a single solution for state management, persistence, and observability, reducing the need for multiple external dependencies.
1070
- * **Flexibility**: The robust middleware system and transaction support make it highly adaptable to complex business logic and data flows.
1071
- * **Modern React**: It leverages `useSyncExternalStore` for direct integration with React's concurrency model, ensuring efficient and up-to-date component renders.
1238
+ * **All-in-One**: It aims to be a single solution for state management, persistence, and observability, reducing the need for multiple external dependencies and their integration complexities.
1239
+ * **Flexibility**: The robust middleware system, transaction support, and artifact management make it highly adaptable to complex business logic, asynchronous data flows, and dependency injection patterns.
1240
+ * **Modern React**: It leverages `useSyncExternalStore` for direct integration with React's concurrency model, ensuring efficient and up-to-date component renders with minimal overhead.
1072
1241
 
1073
1242
  #### Trade-Offs
1074
- * **Bundle Size**: While comprehensive, it naturally has a larger bundle size compared to minimalist alternatives like Zustand, as it includes a wider range of features out-of-the-box.
1075
- * **Learning Curve**: The rich feature set might present a slightly steeper learning curve for developers new to advanced state management concepts, though the API strives for simplicity.
1243
+ * **Bundle Size**: While comprehensive, it naturally has a larger bundle size compared to minimalist alternatives like Zustand, as it includes a wider range of features out-of-the-box. Tree-shaking is applied, but the rich feature set contributes to the baseline.
1244
+ * **Learning Curve**: The rich feature set and advanced concepts (middleware, transactions, observability) might present a slightly steeper initial learning curve for developers new to advanced state management, though the API strives for simplicity and clear documentation.
1245
+
1246
+ ### Troubleshooting
1247
+
1248
+ * **Components not re-rendering:**
1249
+ * Ensure you are using `select` with a specific path (e.g., `select(s => s.user.name)`) instead of the entire state object.
1250
+ * Verify that the data at the selected path is actually changing (reference equality matters for objects/arrays).
1251
+ * **Persistence not loading/saving:**
1252
+ * Check if `isReady` is `true` before interacting with state dependent on persistence.
1253
+ * Ensure your `persistence` instance is correctly passed to `createStore`.
1254
+ * Check browser developer tools for `localStorage`, `sessionStorage`, or `IndexedDB` to see if data is being stored/retrieved.
1255
+ * Look for console errors related to persistence.
1256
+ * **Actions not executing or seem delayed:**
1257
+ * Check the `debounceTime` option in your `createStore` configuration. If set, actions will be delayed.
1258
+ * If actions are `async`, ensure you `await` them where necessary in your components.
1259
+ * Use the `watch` function to check if an action is in a loading state.
1260
+ * **Middleware not working:**
1261
+ * Ensure middleware functions are correctly registered in the `middleware` or `blockingMiddleware` objects in `createStore`.
1262
+ * Check console logs (if `enableConsoleLogging` is `true`) for `middleware:start`, `middleware:complete`, or `middleware:error` events.
1263
+ * **TypeScript errors:**
1264
+ * Verify your state interfaces match the `initialState` structure.
1265
+ * Ensure action return types are `DeepPartial<TState>` as expected.
1266
+ * Update `@asaidimu/react-store` and `@types/react` to their latest compatible versions.
1267
+
1268
+ ### FAQ
1269
+
1270
+ **Q: Does it support React Server Components (RSC)?**
1271
+ A: `useSyncExternalStore` is generally a client-side hook. While the underlying `ReactiveDataStore` is framework-agnostic, the `createStore` hook is designed for client-side React components and is not directly compatible with RSCs for reactive state consumption. You can, however, hydrate the client-side store with initial state from RSCs.
1272
+
1273
+ **Q: How do I share state between multiple stores?**
1274
+ A: Currently, `@asaidimu/react-store` promotes independent stores. For shared logic or derived state, consider creating a utility function that both stores can call, or a parent component that manages shared data and passes it down. Direct cross-store subscription or merging is not a primary pattern.
1275
+
1276
+ **Q: Can I use it without React?**
1277
+ A: The core state management (`ReactiveDataStore`, `StoreObservability` from `@asaidimu/utils-store`) is framework-agnostic. The `createStore` function is React-specific, but you can use the underlying `ReactiveDataStore` directly in non-React environments.
1278
+
1279
+ **Q: What about immutability?**
1280
+ A: All state updates are handled immutably. The library ensures that direct state mutations within actions are prevented and that new state objects are created for changed paths, preserving reference equality for unchanged parts of the state.
1281
+
1282
+ **Q: Is there a dev tools extension?**
1283
+ A: Currently, there is no dedicated browser extension. However, the built-in `StoreObservability` (`observer` object) and `ActionTracker` provide extensive programmatic debugging capabilities, including event history, state snapshots, and performance metrics, which can be integrated into your application's dev panel. Console logging can also be enabled for quick insights.
1076
1284
 
1077
1285
  ### Changelog
1078
1286
 
@@ -1085,3 +1293,4 @@ This project is licensed under the MIT License. See the [LICENSE.md](./LICENSE.m
1085
1293
  ### Acknowledgments
1086
1294
 
1087
1295
  Developed by [Saidimu](https://github.com/asaidimu).
1296
+ This library leverages the robust capabilities of [@asaidimu/utils-store](https://github.com/asaidimu/utils-store) and [@asaidimu/utils-persistence](https://github.com/asaidimu/utils-persistence) for its core state management and persistence features.