@asaidimu/utils-store 5.0.0 → 6.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 +31 -267
- package/index.d.mts +397 -125
- package/index.d.ts +397 -125
- package/index.js +1 -1
- package/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,6 @@ A comprehensive, type-safe, and reactive state management library for TypeScript
|
|
|
22
22
|
- [Middleware System](#middleware-system)
|
|
23
23
|
- [Transaction Support](#transaction-support)
|
|
24
24
|
- [Artifacts (Dependency Injection)](#artifacts-dependency-injection)
|
|
25
|
-
- [React Integration](#react-integration)
|
|
26
25
|
- [Store Observer (Debugging & Observability)](#store-observer-debugging--observability)
|
|
27
26
|
- [Event System](#event-system)
|
|
28
27
|
- [Project Architecture](#project-architecture)
|
|
@@ -49,7 +48,6 @@ This library offers robust tools to handle your state with confidence, enabling
|
|
|
49
48
|
* 📦 **Atomic Transaction Support**: Group multiple state updates into a single, atomic operation. If any update within the transaction fails or an error is thrown, the entire transaction is rolled back to the state before the transaction began, guaranteeing data integrity. Supports both synchronous and asynchronous operations.
|
|
50
49
|
* 💾 **Optional Persistence Layer**: Seamlessly integrate with any `SimplePersistence<T>` implementation (e.g., for local storage, IndexedDB, or backend synchronization) to load an initial state and save subsequent changes. The store emits a `persistence:ready` event and listens for external updates, handling persistence queueing and retries.
|
|
51
50
|
* 🧩 **Artifacts (Dependency Injection)**: A flexible dependency injection system to manage services, utilities, or complex objects that your actions and other artifacts depend on. Supports `Singleton` (re-evaluated on dependencies change) and `Transient` (new instance every time) scopes, and reactive resolution using `use(({select, resolve}) => ...)` context. Handles dependency graphs and circular dependency detection.
|
|
52
|
-
* ⚛️ **React Integration**: Provides a `createStore` factory function that returns a custom React hook (`useStore`). This hook offers `select` (memoized selector hook), `actions` (bound action dispatchers), and `resolve` (reactive artifact resolver) for seamless integration with React components. It leverages React's `useSyncExternalStore` for optimal performance.
|
|
53
51
|
* 👀 **Deep Observer & Debugging (`StoreObserver`)**: An optional but highly recommended class for unparalleled runtime introspection and debugging:
|
|
54
52
|
* **Comprehensive Event History**: Captures a detailed log of all internal store events (`update:start`, `middleware:complete`, `transaction:error`, `persistence:ready`, `middleware:executed`, `action:start`, `selector:accessed`, etc.).
|
|
55
53
|
* **State Snapshots**: Maintains a configurable history of your application's state over time, allowing for easy inspection of changes between updates and post-mortem analysis.
|
|
@@ -82,7 +80,6 @@ yarn add @asaidimu/utils-store
|
|
|
82
80
|
|
|
83
81
|
* **Node.js**: (LTS version recommended) for development and compilation.
|
|
84
82
|
* **TypeScript**: (v4.0+ recommended) for full type-safety during development. Modern TS features (ES2017+ for `async/await`, ES2020+ for `Symbol.for()` and `structuredClone()`) are utilized. `moduleResolution`: `Node16` or `Bundler` is recommended in `tsconfig.json`.
|
|
85
|
-
* **React**: (v16.8+ for hooks) if using the `@asaidimu/utils-store/react` integration.
|
|
86
83
|
|
|
87
84
|
### Verification
|
|
88
85
|
|
|
@@ -698,7 +695,7 @@ console.log('State after manual action:', store.get());
|
|
|
698
695
|
const temporaryLoggerId = store.use({ name: 'TemporaryLogger', action: (s, u) => console.log('Temporary logger saw:', u) });
|
|
699
696
|
await store.set({ counter: 1 });
|
|
700
697
|
// Output: Temporary logger saw: { counter: 1 }
|
|
701
|
-
const removed =
|
|
698
|
+
const removed = temporaryLoggerId(); // Remove the temporary logger
|
|
702
699
|
console.log('Middleware removed:', removed);
|
|
703
700
|
await store.set({ counter: 1 }); // TemporaryLogger will not be called now
|
|
704
701
|
```
|
|
@@ -872,10 +869,8 @@ try {
|
|
|
872
869
|
|
|
873
870
|
The Artifact system provides a powerful way to manage external dependencies, services, or complex objects that your actions or other artifacts might need. It supports `Singleton` (created once, reactive to dependencies) and `Transient` (new instance every time) scopes, and allows artifacts to depend on state changes and other artifacts.
|
|
874
871
|
|
|
875
|
-
This example uses the `ArtifactContainer` directly for clarity, but in React applications, you'd typically use the `createStore` factory, which automatically integrates with `ArtifactContainer` and exposes resolution via `useStore().resolve()`.
|
|
876
|
-
|
|
877
872
|
```typescript
|
|
878
|
-
import { ArtifactContainer, ArtifactScope } from '@asaidimu/utils-store
|
|
873
|
+
import { ReactiveDataStore, ArtifactContainer, ArtifactScope } from '@asaidimu/utils-store';
|
|
879
874
|
|
|
880
875
|
interface AppState {
|
|
881
876
|
user: { id: string; name: string; };
|
|
@@ -883,12 +878,10 @@ interface AppState {
|
|
|
883
878
|
}
|
|
884
879
|
|
|
885
880
|
// Mock DataStore interface for standalone ArtifactContainer usage
|
|
886
|
-
|
|
881
|
+
const store = new ReactiveDataStore<AppState>({
|
|
887
882
|
user: { id: 'user-1', name: 'Alice' },
|
|
888
883
|
config: { apiUrl: '/api/v1', logLevel: 'info' }
|
|
889
|
-
};
|
|
890
|
-
|
|
891
|
-
const store = new ReactiveDataStore<AppState>(currentState);
|
|
884
|
+
});
|
|
892
885
|
|
|
893
886
|
const container = new ArtifactContainer(store);
|
|
894
887
|
|
|
@@ -911,16 +904,17 @@ const apiClientCleanup = () => console.log('API Client connection closed.');
|
|
|
911
904
|
container.register({
|
|
912
905
|
key: 'apiClient',
|
|
913
906
|
scope: ArtifactScope.Singleton,
|
|
914
|
-
factory: async ({ use, current }) => {
|
|
907
|
+
factory: async ({ use, onCleanup, current }) => {
|
|
915
908
|
const apiUrl = await use(({ select }) => select((state: AppState) => state.config.apiUrl));
|
|
916
909
|
console.log(`API Client created/re-created for URL: ${apiUrl}`);
|
|
917
910
|
if (current) {
|
|
918
911
|
console.log('Re-creating API client. Old instance:', current);
|
|
919
912
|
}
|
|
920
|
-
|
|
913
|
+
onCleanup(apiClientCleanup); // Register cleanup for this instance
|
|
914
|
+
return {
|
|
921
915
|
fetchUser: (id: string) => `Fetching ${id} from ${apiUrl}`,
|
|
922
916
|
sendData: (data: any) => `Sending ${JSON.stringify(data)} to ${apiUrl}`
|
|
923
|
-
}
|
|
917
|
+
};
|
|
924
918
|
}
|
|
925
919
|
});
|
|
926
920
|
|
|
@@ -929,17 +923,18 @@ const userServiceCleanup = () => console.log('User Service resources released.')
|
|
|
929
923
|
container.register({
|
|
930
924
|
key: 'userService',
|
|
931
925
|
scope: ArtifactScope.Singleton,
|
|
932
|
-
factory: async ({ use, current }) => {
|
|
926
|
+
factory: async ({ use, onCleanup, current }) => {
|
|
933
927
|
const apiClient = await use(({ resolve }) => resolve('apiClient'));
|
|
934
928
|
const userName = await use(({ select }) => select((state: AppState) => state.user.name));
|
|
935
929
|
console.log(`User Service created/re-created for user: ${userName}`);
|
|
936
930
|
if (current) {
|
|
937
931
|
console.log('Re-creating User Service. Old instance:', current);
|
|
938
932
|
}
|
|
939
|
-
|
|
940
|
-
|
|
933
|
+
onCleanup(userServiceCleanup); // Register cleanup for this instance
|
|
934
|
+
return {
|
|
935
|
+
getUserProfile: () => apiClient.instance!.fetchUser(store.get().user.id),
|
|
941
936
|
updateUserName: (newName: string) => `Updating user name to ${newName} via API. Current: ${userName}`
|
|
942
|
-
}
|
|
937
|
+
};
|
|
943
938
|
}
|
|
944
939
|
});
|
|
945
940
|
|
|
@@ -948,48 +943,47 @@ container.register({
|
|
|
948
943
|
async function runDemo() {
|
|
949
944
|
console.log('\n--- Initial Artifact Resolution ---');
|
|
950
945
|
const logger1 = await container.resolve('logger');
|
|
951
|
-
logger1.log('Application started.');
|
|
946
|
+
logger1.instance!.log('Application started.');
|
|
952
947
|
|
|
953
948
|
const apiClient1 = await container.resolve('apiClient');
|
|
954
|
-
console.log(apiClient1.fetchUser('123'));
|
|
949
|
+
console.log(apiClient1.instance!.fetchUser('123'));
|
|
955
950
|
|
|
956
951
|
const userService1 = await container.resolve('userService');
|
|
957
|
-
console.log(userService1.getUserProfile());
|
|
952
|
+
console.log(userService1.instance!.getUserProfile());
|
|
958
953
|
|
|
959
954
|
// Transient artifact resolves a new instance
|
|
960
955
|
const logger2 = await container.resolve('logger');
|
|
961
|
-
|
|
956
|
+
console.log('Logger instances are different:', logger1.instance !== logger2.instance);
|
|
962
957
|
|
|
963
|
-
// Singleton artifacts resolve the same instance
|
|
958
|
+
// Singleton artifacts resolve the same instance initially
|
|
964
959
|
const apiClient2 = await container.resolve('apiClient');
|
|
965
|
-
|
|
960
|
+
console.log('API Client instances are same:', apiClient1.instance === apiClient2.instance);
|
|
966
961
|
|
|
967
962
|
console.log('\n--- Simulate State Change (config.apiUrl) ---');
|
|
968
|
-
store.set({ config:{ apiUrl: '/api/v2'}})
|
|
963
|
+
await store.set({ config:{ apiUrl: '/api/v2'}})
|
|
964
|
+
// This state change invalidates 'apiClient' which depends on 'config.apiUrl'
|
|
965
|
+
// and then invalidates 'userService' which depends on 'apiClient'.
|
|
969
966
|
|
|
970
967
|
// After config.apiUrl changes, apiClient (and userService) should be re-created
|
|
971
968
|
const apiClient3 = await container.resolve('apiClient'); // This will trigger re-creation
|
|
972
|
-
console.log(apiClient3.fetchUser('123'));
|
|
973
|
-
|
|
969
|
+
console.log(apiClient3.instance!.fetchUser('123'));
|
|
970
|
+
console.log('API Client instances are different:', apiClient3.instance !== apiClient1.instance); // Should be a new instance
|
|
974
971
|
|
|
975
972
|
// userService should also be re-created because apiClient, its dependency, was re-created
|
|
976
973
|
const userService2 = await container.resolve('userService');
|
|
977
|
-
console.log(userService2.getUserProfile());
|
|
978
|
-
|
|
974
|
+
console.log(userService2.instance!.getUserProfile());
|
|
975
|
+
console.log('User Service instances are different:', userService2.instance !== userService1.instance); // Should be a new instance
|
|
979
976
|
|
|
980
977
|
console.log('\n--- Simulate State Change (user.name) ---');
|
|
981
|
-
|
|
982
|
-
{ ...currentState, user: { ...currentState.user, name: 'Bob' } },
|
|
983
|
-
['user.name']
|
|
984
|
-
);
|
|
978
|
+
await store.set({ user: { name: 'Bob' } });
|
|
985
979
|
|
|
986
980
|
// Only userService (which depends on user.name) should be re-created this time, not apiClient
|
|
987
981
|
const apiClient4 = await container.resolve('apiClient');
|
|
988
|
-
|
|
982
|
+
console.log('API Client instances are same:', apiClient4.instance === apiClient3.instance); // Still the same API client instance
|
|
989
983
|
|
|
990
984
|
const userService3 = await container.resolve('userService');
|
|
991
|
-
console.log(userService3.getUserProfile());
|
|
992
|
-
|
|
985
|
+
console.log(userService3.instance!.getUserProfile());
|
|
986
|
+
console.log('User Service instances are different:', userService3.instance !== userService2.instance); // New user service instance
|
|
993
987
|
|
|
994
988
|
console.log('\n--- Unregistering Artifacts ---');
|
|
995
989
|
await container.unregister('userService');
|
|
@@ -1000,220 +994,13 @@ async function runDemo() {
|
|
|
1000
994
|
try {
|
|
1001
995
|
await container.resolve('userService');
|
|
1002
996
|
} catch (e: any) {
|
|
1003
|
-
console.error('Expected error:', e.message);
|
|
997
|
+
console.error('Expected error:', e.message); // Artifact not found
|
|
1004
998
|
}
|
|
1005
999
|
}
|
|
1006
1000
|
|
|
1007
1001
|
runDemo();
|
|
1008
1002
|
```
|
|
1009
1003
|
|
|
1010
|
-
### React Integration
|
|
1011
|
-
|
|
1012
|
-
The `@asaidimu/utils-store/react-store` module provides a `createStore` factory function that integrates the core `ReactiveDataStore` with React's context and `useSyncExternalStore` hook. This makes it easy to consume state, dispatch actions, and resolve artifacts in your React components.
|
|
1013
|
-
|
|
1014
|
-
```typescript
|
|
1015
|
-
// store.ts (example for an E-commerce dashboard)
|
|
1016
|
-
import { ArtifactScope, createStore, ActionMap, ArtifactsMap } from '@asaidimu/utils-store';
|
|
1017
|
-
|
|
1018
|
-
export interface Product {
|
|
1019
|
-
id: number; name: string; price: number; stock: number; image: string;
|
|
1020
|
-
}
|
|
1021
|
-
export interface CartItem extends Product { quantity: number; }
|
|
1022
|
-
export interface Order { id: string; items: CartItem[]; total: number; date: Date; }
|
|
1023
|
-
|
|
1024
|
-
export interface ECommerceState extends Record<string, any> {
|
|
1025
|
-
products: Product[];
|
|
1026
|
-
cart: CartItem[];
|
|
1027
|
-
orders: Order[];
|
|
1028
|
-
topSellers: { id: number; name: string; sales: number }[];
|
|
1029
|
-
activeUsers: number;
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
const initialState: ECommerceState = {
|
|
1033
|
-
products: [
|
|
1034
|
-
{ id: 1, name: 'Wireless Mouse', price: 25.99, stock: 150, image: 'https://placehold.co/600x400/white/black?text=Mouse' },
|
|
1035
|
-
{ id: 2, name: 'Mechanical Keyboard', price: 79.99, stock: 100, image: 'https://placehold.co/600x400/white/black?text=Keyboard' },
|
|
1036
|
-
],
|
|
1037
|
-
cart: [], orders: [], topSellers: [], activeUsers: 1428,
|
|
1038
|
-
};
|
|
1039
|
-
|
|
1040
|
-
// 1. Define Artifacts
|
|
1041
|
-
export const artifacts = {
|
|
1042
|
-
logger: {
|
|
1043
|
-
scope: ArtifactScope.Singleton,
|
|
1044
|
-
factory: () => ((...args:any[]) => console.log('Artifact Logger:', ...args))
|
|
1045
|
-
},
|
|
1046
|
-
analytics: {
|
|
1047
|
-
scope: ArtifactScope.Transient,
|
|
1048
|
-
factory: () => ({
|
|
1049
|
-
trackEvent: (name: string, data: object) => console.log(`[Analytics] ${name}`, data)
|
|
1050
|
-
})
|
|
1051
|
-
}
|
|
1052
|
-
} as const satisfies ArtifactsMap<ECommerceState>
|
|
1053
|
-
|
|
1054
|
-
// 2. Define Actions
|
|
1055
|
-
export const actions = {
|
|
1056
|
-
addToCart: async ({ state, resolve }, product: Product) => {
|
|
1057
|
-
const log = await resolve("logger");
|
|
1058
|
-
const analytics = await resolve("analytics");
|
|
1059
|
-
analytics.trackEvent('add_to_cart', { product: product.name });
|
|
1060
|
-
|
|
1061
|
-
const existingItem = state.cart.find((item) => item.id === product.id);
|
|
1062
|
-
if (existingItem) {
|
|
1063
|
-
return {
|
|
1064
|
-
cart: state.cart.map((item) =>
|
|
1065
|
-
item.id === product.id ? { ...item, quantity: item.quantity + 1 } : item
|
|
1066
|
-
),
|
|
1067
|
-
};
|
|
1068
|
-
}
|
|
1069
|
-
const result = { cart: [...state.cart, { ...product, quantity: 1 }] };
|
|
1070
|
-
log("Item added:", product.name);
|
|
1071
|
-
return result
|
|
1072
|
-
},
|
|
1073
|
-
removeFromCart: ({ state }, productId: number) => ({
|
|
1074
|
-
cart: state.cart.filter((item) => item.id !== productId),
|
|
1075
|
-
}),
|
|
1076
|
-
checkout: ({ state }) => {
|
|
1077
|
-
const total = state.cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
|
1078
|
-
const newOrder: Order = {
|
|
1079
|
-
id: crypto.randomUUID(), items: state.cart, total, date: new Date(),
|
|
1080
|
-
};
|
|
1081
|
-
return {
|
|
1082
|
-
cart: [], orders: [newOrder, ...state.orders],
|
|
1083
|
-
};
|
|
1084
|
-
},
|
|
1085
|
-
// Simulated real-time updates for the dashboard example
|
|
1086
|
-
updateStock: ({ state }) => ({
|
|
1087
|
-
products: state.products.map(p => ({
|
|
1088
|
-
...p,
|
|
1089
|
-
stock: Math.max(0, p.stock + Math.floor(Math.random() * 10) - 5)
|
|
1090
|
-
}))
|
|
1091
|
-
}),
|
|
1092
|
-
updateActiveUsers: ({ state }) => ({
|
|
1093
|
-
activeUsers: state.activeUsers + Math.floor(Math.random() * 20) - 10,
|
|
1094
|
-
}),
|
|
1095
|
-
addRandomOrder: ({ state }) => {
|
|
1096
|
-
const randomProduct = state.products[Math.floor(Math.random() * state.products.length)];
|
|
1097
|
-
const quantity = Math.floor(Math.random() * 3) + 1;
|
|
1098
|
-
const newOrder: Order = {
|
|
1099
|
-
id: crypto.randomUUID(),
|
|
1100
|
-
items: [{ ...randomProduct, quantity }],
|
|
1101
|
-
total: randomProduct.price * quantity,
|
|
1102
|
-
date: new Date(),
|
|
1103
|
-
};
|
|
1104
|
-
return {
|
|
1105
|
-
orders: [newOrder, ...state.orders],
|
|
1106
|
-
};
|
|
1107
|
-
},
|
|
1108
|
-
} as const satisfies ActionMap<ECommerceState, typeof artifacts>
|
|
1109
|
-
|
|
1110
|
-
// 3. Create the React Store Hook
|
|
1111
|
-
export const useStore = createStore(
|
|
1112
|
-
{
|
|
1113
|
-
state: initialState,
|
|
1114
|
-
artifacts,
|
|
1115
|
-
actions,
|
|
1116
|
-
},
|
|
1117
|
-
{ enableMetrics: true, enableConsoleLogging: true }
|
|
1118
|
-
);
|
|
1119
|
-
|
|
1120
|
-
// App.tsx (React Component)
|
|
1121
|
-
import { useEffect, useMemo } from 'react';
|
|
1122
|
-
import { useStore } from './store'; // Import the custom hook
|
|
1123
|
-
|
|
1124
|
-
function ProductCatalog() {
|
|
1125
|
-
const { select, actions } = useStore();
|
|
1126
|
-
const products = select((state) => state.products); // Reactive selector for products
|
|
1127
|
-
|
|
1128
|
-
return (
|
|
1129
|
-
<div>
|
|
1130
|
-
<h2>Products</h2>
|
|
1131
|
-
{products.map((product) => (
|
|
1132
|
-
<div key={product.id}>
|
|
1133
|
-
{product.name} - ${product.price} ({product.stock} in stock)
|
|
1134
|
-
<button onClick={() => actions.addToCart(product)}>Add to Cart</button>
|
|
1135
|
-
</div>
|
|
1136
|
-
))}
|
|
1137
|
-
</div>
|
|
1138
|
-
);
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
function ShoppingCart() {
|
|
1142
|
-
const { select, actions } = useStore();
|
|
1143
|
-
const cart = select((state) => state.cart); // Reactive selector for cart
|
|
1144
|
-
const total = useMemo(() => cart.reduce((sum, item) => sum + item.price * item.quantity, 0), [cart]);
|
|
1145
|
-
|
|
1146
|
-
return (
|
|
1147
|
-
<div>
|
|
1148
|
-
<h2>Shopping Cart</h2>
|
|
1149
|
-
{cart.length === 0 ? (
|
|
1150
|
-
<p>Your cart is empty.</p>
|
|
1151
|
-
) : (
|
|
1152
|
-
<ul>
|
|
1153
|
-
{cart.map((item) => (
|
|
1154
|
-
<li key={item.id}>
|
|
1155
|
-
{item.name} x {item.quantity} - ${item.price * item.quantity}
|
|
1156
|
-
<button onClick={() => actions.removeFromCart(item.id)}>Remove</button>
|
|
1157
|
-
</li>
|
|
1158
|
-
))}
|
|
1159
|
-
</ul>
|
|
1160
|
-
)}
|
|
1161
|
-
<p>Total: ${total.toFixed(2)}</p>
|
|
1162
|
-
{cart.length > 0 && <button onClick={() => actions.checkout()}>Checkout</button>}
|
|
1163
|
-
</div>
|
|
1164
|
-
);
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
function DebugPanel() {
|
|
1168
|
-
const { resolve, watch, state } = useStore();
|
|
1169
|
-
const [logger, loggerReady] = resolve('logger'); // Reactive artifact resolution
|
|
1170
|
-
const [analytics, analyticsReady] = resolve('analytics');
|
|
1171
|
-
|
|
1172
|
-
const isLoadingAddToCart = watch('addToCart'); // Watch specific action loading state
|
|
1173
|
-
|
|
1174
|
-
return (
|
|
1175
|
-
<div>
|
|
1176
|
-
<h3>Debug Panel</h3>
|
|
1177
|
-
<p>Add to Cart Loading: {isLoadingAddToCart ? 'Yes' : 'No'}</p>
|
|
1178
|
-
<p>Store is ready: {useStore().isReady ? 'Yes' : 'No'}</p>
|
|
1179
|
-
<button onClick={() => loggerReady && logger('Debug message from UI')}>Log via Artifact</button>
|
|
1180
|
-
<button onClick={() => analyticsReady && analytics.trackEvent('ui_event', { component: 'DebugPanel' })}>Track UI Event</button>
|
|
1181
|
-
<pre>Full State: {JSON.stringify(state(), null, 2)}</pre>
|
|
1182
|
-
</div>
|
|
1183
|
-
);
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
function App() {
|
|
1187
|
-
const { actions } = useStore();
|
|
1188
|
-
|
|
1189
|
-
// Simulate real-time updates
|
|
1190
|
-
useEffect(() => {
|
|
1191
|
-
const stockInterval = setInterval(() => actions.updateStock(), 2000);
|
|
1192
|
-
const usersInterval = setInterval(() => actions.updateActiveUsers(), 3000);
|
|
1193
|
-
const ordersInterval = setInterval(() => actions.addRandomOrder(), 5000);
|
|
1194
|
-
|
|
1195
|
-
return () => {
|
|
1196
|
-
clearInterval(stockInterval);
|
|
1197
|
-
clearInterval(usersInterval);
|
|
1198
|
-
clearInterval(ordersInterval);
|
|
1199
|
-
};
|
|
1200
|
-
}, [actions]);
|
|
1201
|
-
|
|
1202
|
-
return (
|
|
1203
|
-
<div>
|
|
1204
|
-
<h1>E-Commerce App</h1>
|
|
1205
|
-
<ProductCatalog />
|
|
1206
|
-
<hr />
|
|
1207
|
-
<ShoppingCart />
|
|
1208
|
-
<hr />
|
|
1209
|
-
<DebugPanel />
|
|
1210
|
-
</div>
|
|
1211
|
-
);
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
export default App;
|
|
1215
|
-
```
|
|
1216
|
-
|
|
1217
1004
|
### Store Observer (Debugging & Observability)
|
|
1218
1005
|
|
|
1219
1006
|
The `StoreObserver` class provides advanced debugging and monitoring capabilities for any `ReactiveDataStore` instance. It allows you to inspect event history, state changes, and even time-travel through your application's state. It's an invaluable tool for understanding complex state flows.
|
|
@@ -1509,29 +1296,6 @@ console.log('Final value:', store.get().value, 'Final status:', store.get().stat
|
|
|
1509
1296
|
|
|
1510
1297
|
The `@asaidimu/utils-store` library is built with a modular, component-based architecture to promote maintainability, testability, and extensibility. Each core concern is encapsulated within its own class, with `ReactiveDataStore` acting as the central coordinator.
|
|
1511
1298
|
|
|
1512
|
-
```
|
|
1513
|
-
src/store/
|
|
1514
|
-
├── index.ts # Main entry point (exports all public APIs)
|
|
1515
|
-
├── types.ts # TypeScript interfaces and types for the store
|
|
1516
|
-
├── store.ts # `ReactiveDataStore` - The main orchestrator
|
|
1517
|
-
├── state.ts # `StateManager` - Manages the immutable state and diffing
|
|
1518
|
-
├── merge.ts # `createMerge` - Deep merging utility with deletion support
|
|
1519
|
-
├── diff.ts # `createDiff`, `createDerivePaths` - Change detection utilities
|
|
1520
|
-
├── middleware.ts # `MiddlewareEngine` - Manages and executes middleware pipeline
|
|
1521
|
-
├── transactions.ts # `TransactionManager` - Handles atomic state transactions
|
|
1522
|
-
├── persistence.ts # `PersistenceHandler` - Integrates with `SimplePersistence` layer
|
|
1523
|
-
├── metrics.ts # `MetricsCollector` - Gathers runtime performance metrics
|
|
1524
|
-
├── selector.ts # `SelectorManager` - Manages reactive selectors for derived state
|
|
1525
|
-
├── observer.ts # `StoreObserver` - Debugging and introspection utilities
|
|
1526
|
-
├── artifacts.ts # `ArtifactContainer` - Dependency Injection system for services
|
|
1527
|
-
├── actions.ts # `ActionManager` - Manages registration and dispatch of named actions
|
|
1528
|
-
└── react-store/ # React integration module (hooks, context)
|
|
1529
|
-
├── index.ts
|
|
1530
|
-
├── types.ts
|
|
1531
|
-
├── store.ts # `createStore` - Factory for React hooks
|
|
1532
|
-
└── execution.ts # `ActionTracker` - Tracks action execution history for React integration
|
|
1533
|
-
```
|
|
1534
|
-
|
|
1535
1299
|
### Core Components
|
|
1536
1300
|
|
|
1537
1301
|
* **`ReactiveDataStore<T>`**: The public API and primary entry point. It orchestrates interactions between all other internal components. It manages the update queue, ensures sequential processing of `set` calls, and exposes public methods like `get`, `dispatch`, `select`, `set`, `watch`, `transaction`, `use`, and `on`.
|