@asaidimu/react-store 3.0.0 → 5.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/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("react"),t=require("@asaidimu/utils-store"),r=class{executions=[];maxHistory=100;listeners=new Set;track(e){this.executions.unshift(e),this.executions.length>this.maxHistory&&this.executions.pop(),this.notify()}getExecutions(){return[...this.executions]}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){this.listeners.forEach((e=>e()))}};exports.createStore=function(s,{enableMetrics:n,...a}={}){const i=new t.ReactiveDataStore(s.state,a.persistence),c=Object.keys(s.actions).reduce(((e,t)=>(e[t]=!1,e)),{}),o=new t.ReactiveDataStore(c),u=n?new t.StoreObserver(i,a):void 0,d=n?new r:void 0;n&&d&&(i.on("action:start",(({name:e})=>{o.set((()=>({[e]:!0})))})),i.on("action:complete",(e=>{o.set((()=>({[e.name]:!1}))),d.track({id:e.actionId,name:e.name,params:e.params,startTime:e.startTime,endTime:e.endTime,duration:e.duration,status:"success",result:e.result})})),i.on("action:error",(e=>{o.set((()=>({[e.name]:!1}))),d.track({id:e.actionId,name:e.name,params:e.params,startTime:e.startTime,endTime:e.endTime,duration:e.duration,status:"error",error:e.error})}))),s.middleware&&Object.entries(s.middleware).forEach((([e,t])=>i.use({name:e,action:t}))),s.blockingMiddleware&&Object.entries(s.blockingMiddleware).forEach((([e,t])=>i.use({block:!0,name:e,action:t})));const l=e=>(i.isReady()&&e(),i.on("persistence:ready",e)),m=()=>i.isReady(),h=new t.ArtifactContainer(i);s.artifacts&&Object.entries(s.artifacts).forEach((([e,t])=>{h.register({key:e,factory:t.factory,scope:t.scope,lazy:t.lazy})}));const y=Object.entries(s.actions).reduce(((e,[t,r])=>(i.register({name:t,fn:(e,...t)=>{const s={state:e,resolve:h.resolve.bind(h)};return r(s,...t)}}),e[t]=(...e)=>i.dispatch(t,...e),e)),{});return function(){const t=()=>e.useSyncExternalStore((e=>i.watch("",e)),(()=>i.get()),(()=>i.get())),r=e.useSyncExternalStore(l,m,m);return{store:i,observer:u,select:t=>{const r=i.select(t);return e.useSyncExternalStore((e=>r.subscribe(e)),(()=>r.get()),(()=>r.get()))},actions:y,isReady:r,actionTracker:d,watch:t=>e.useSyncExternalStore((e=>o.watch(t,e)),(()=>!!o.get()[t]),(()=>!!o.get()[t])),resolve:t=>{const r=e.useSyncExternalStore((e=>h.subscribeToArtifact(t,e)),(()=>h.get(t)),(()=>h.get(t)));e.useEffect((()=>{void 0!==r||h.isLoading(t)||h.resolve(t).catch(console.error)}),[t,r]);return[r,null!=r]},get state(){return t}}}};
1
+ "use strict";var e=require("react"),t=require("@asaidimu/utils-store"),r=require("@asaidimu/utils-artifacts"),s=class{executions=[];maxHistory=100;listeners=new Set;track(e){this.executions.unshift(e),this.executions.length>this.maxHistory&&this.executions.pop(),this.notify()}getExecutions(){return[...this.executions]}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){this.listeners.forEach(e=>e())}};exports.createStore=function(a,{enableMetrics:n,...i}={}){const c=new t.ReactiveDataStore(a.state,i.persistence),o=Object.keys(a.actions).reduce((e,t)=>(e[t]=!1,e),{}),u=new t.ReactiveDataStore(o),l=n?new t.StoreObserver(c,i):void 0,d=n?new s:void 0;n&&d&&(c.on("action:start",({name:e})=>{u.set(()=>({[e]:!0}))}),c.on("action:complete",e=>{u.set(()=>({[e.name]:!1})),d.track({id:e.actionId,name:e.name,params:e.params,startTime:e.startTime,endTime:e.endTime,duration:e.duration,status:"success",result:e.result})}),c.on("action:error",e=>{u.set(()=>({[e.name]:!1})),d.track({id:e.actionId,name:e.name,params:e.params,startTime:e.startTime,endTime:e.endTime,duration:e.duration,status:"error",error:e.error})}));const m=e=>(c.isReady()&&e(),c.on("persistence:ready",e)),h=()=>c.isReady(),y=new r.ArtifactContainer(c),f=y.resolve.bind(y);a.artifacts&&Object.entries(a.artifacts).forEach(([e,t])=>{y.register({key:e,factory:t.factory,scope:t.scope,lazy:t.lazy})}),a.transform&&Object.entries(a.transform).forEach(([e,t])=>{c.use({name:e,action:(e,r)=>t({state:e,resolve:f},r)})}),a.validate&&Object.entries(a.validate).forEach(([e,t])=>{c.use({block:!0,name:e,action:(e,r)=>t({state:e,resolve:f},r)})});const b=Object.entries(a.actions).reduce((e,[t,r])=>(c.register({name:t,fn:(e,...t)=>r({state:e,resolve:f},...t)}),e[t]=(...e)=>c.dispatch(t,...e),e),{});return function(){const t=()=>e.useSyncExternalStore(e=>c.watch("",e),()=>c.get(),()=>c.get()),r=e.useSyncExternalStore(m,h,h);return{select:t=>{const r=c.select(t);return e.useSyncExternalStore(e=>r.subscribe(e),()=>r.get(),()=>r.get())},watch:t=>e.useSyncExternalStore(e=>u.watch(t,e),()=>!!u.get()[t],()=>!!u.get()[t]),resolve:t=>{const r=y.watch(t);return e.useSyncExternalStore(e=>r.subscribe(()=>e()),()=>r.get(),()=>r.get())},actions:b,isReady:r,store:c,observer:l,actionTracker:d,get state(){return t}}}};
package/index.d.cts CHANGED
@@ -1,65 +1,191 @@
1
1
  import { SimplePersistence } from '@asaidimu/utils-persistence';
2
- import { DeepPartial, ArtifactFactory, ArtifactScope, ObserverOptions, BlockingMiddleware, Middleware } from '@asaidimu/utils-store';
2
+ import { DeepPartial, ObserverOptions, ReactiveDataStore, StoreObserver } from '@asaidimu/utils-store';
3
+ import { ResolvedArtifact, ArtifactScope as ArtifactScope$1, ArtifactTemplate, InferRegistry, ArtifactValue } from '@asaidimu/utils-artifacts';
3
4
 
4
- interface ActionContext<TState extends object, TResolvedArtifacts extends object> {
5
+ interface ActionExecution {
6
+ id: string;
7
+ name: string;
8
+ params: unknown[];
9
+ startTime: number;
10
+ endTime: number;
11
+ duration: number;
12
+ status: 'success' | 'error';
13
+ error?: Error;
14
+ result?: unknown;
15
+ }
16
+ declare class ActionTracker {
17
+ private executions;
18
+ private maxHistory;
19
+ private listeners;
20
+ track(execution: ActionExecution): void;
21
+ getExecutions(): ActionExecution[];
22
+ subscribe(listener: () => void): () => boolean;
23
+ private notify;
24
+ }
25
+
26
+ /**
27
+ * The context object provided as the first argument to all actions, transforms, and validators.
28
+ * It provides access to the current state and the artifact resolution system.
29
+ *
30
+ * @template TState The shape of the main store state.
31
+ * @template TRegistry The inferred type of the entire artifact registry.
32
+ */
33
+ interface ActionContext<TState extends object, TRegistry extends Record<string, any>> {
5
34
  /**
6
- * Resolve an artifact.
7
- * This records a dependency between the caller and the requested artifact.
35
+ * Resolve an artifact, recording a dependency between the caller and the requested artifact.
36
+ * This function is asynchronous because artifact factories can be asynchronous.
37
+ *
38
+ * @param key The name of the artifact to resolve.
39
+ * @returns A promise that resolves to the resolved artifact instance and its metadata.
40
+ */
41
+ resolve<K extends keyof TRegistry>(key: K): Promise<ResolvedArtifact<TRegistry[K]>>;
42
+ /**
43
+ * The current snapshot of the store state.
8
44
  */
9
- resolve<K extends keyof TResolvedArtifacts>(key: K): Promise<TResolvedArtifacts[K]>;
10
45
  state: TState;
11
46
  }
12
- type ActionImplementation<TState extends object, TResolvedArtifacts extends object, TArgs extends any[]> = (ctx: ActionContext<TState, TResolvedArtifacts>, ...args: TArgs) => DeepPartial<TState> | Promise<DeepPartial<TState>>;
13
- type ArtifactDefinition<TState extends Object, R> = {
14
- factory: ArtifactFactory<TState, R>;
15
- scope?: ArtifactScope;
16
- lazy?: boolean;
17
- };
18
- type ArtifactsMap<TState extends object> = Record<string, ArtifactDefinition<TState, any>>;
19
- type ExtractArtifactInstanceFromConfig<T> = T extends {
20
- factory: ArtifactFactory<any, infer I>;
21
- } ? I : never;
22
- type ExtractInstanceFromMap<TMap extends ArtifactsMap<any>, TKey extends keyof TMap> = ExtractArtifactInstanceFromConfig<TMap[TKey]>;
23
- type ResolvedArtifactsMap<TArtifactsMap extends ArtifactsMap<any>> = {
24
- [K in keyof TArtifactsMap]: ExtractInstanceFromMap<TArtifactsMap, K>;
25
- };
26
- type ActionMap<TState extends object, TArtifactsMap extends ArtifactsMap<TState>> = Record<string, ActionImplementation<TState, ResolvedArtifactsMap<TArtifactsMap>, any[]>>;
27
47
  /**
28
- * Bounds the actions for the resulting store hook, removing the context argument.
48
+ * Re-exports the ArtifactScope for use in the store's definition.
49
+ */
50
+ type ArtifactScope = ArtifactScope$1;
51
+ /**
52
+ * The core function signature for an Action.
53
+ * It takes the context and any custom arguments, and returns a (potentially partial)
54
+ * update to the state or a Promise resolving to the update.
55
+ *
56
+ * @template TState The shape of the main store state.
57
+ * @template TRegistry The inferred type of the entire artifact registry.
58
+ * @template TArgs The custom arguments accepted by the action.
59
+ */
60
+ type ActionImplementation<TState extends object, TRegistry extends Record<string, any>, TArgs extends any[]> = (ctx: ActionContext<TState, TRegistry>, ...args: TArgs) => DeepPartial<TState> | Promise<DeepPartial<TState>>;
61
+ /**
62
+ * A map of all artifact definitions keyed by their names.
63
+ *
64
+ * @template TState The shape of the main store state.
65
+ */
66
+ type ArtifactsMap<TState extends object> = Record<string, Omit<ArtifactTemplate<TState, any, any>, "key">>;
67
+ /**
68
+ * A map of all ActionImplementations keyed by their action names.
69
+ *
70
+ * @template TState The shape of the main store state.
71
+ * @template TArtifactsMap The map of artifact definitions.
72
+ */
73
+ type ActionMap<TState extends object, TArtifactsMap extends ArtifactsMap<TState>> = Record<string, ActionImplementation<TState, InferRegistry<TArtifactsMap>, any[]>>;
74
+ /**
75
+ * Utility type to extract the non-context arguments (the "tail" parameters) from an ActionImplementation.
76
+ */
77
+ type TailParams<F, TState extends object, TRegistry extends Record<string, any>> = F extends (ctx: ActionContext<TState, TRegistry>, ...args: infer R) => any ? R : never;
78
+ /**
79
+ * Defines the public, bound signature of the store's actions.
80
+ * This removes the internal `ActionContext` argument and makes the return type a Promise
81
+ * that resolves when the state update is complete.
82
+ *
83
+ * @template TState The shape of the main store state.
84
+ * @template TArtifactsMap The map of artifact definitions.
85
+ * @template TActions The map of ActionImplementations.
29
86
  */
30
87
  type BoundActions<TState extends object, TArtifactsMap extends ArtifactsMap<TState>, TActions extends ActionMap<TState, TArtifactsMap>> = {
31
- [K in keyof TActions]: (...args: Parameters<TActions[K]> extends [ActionContext<any, any>, ...infer R] ? R : never) => Promise<TState>;
88
+ [K in keyof TActions]: (...args: TailParams<TActions[K], TState, InferRegistry<TArtifactsMap>> extends [] ? [] : TailParams<TActions[K], TState, InferRegistry<TArtifactsMap>>) => Promise<TState>;
32
89
  };
90
+ /**
91
+ * Tracks the loading status of specific actions, used for UI feedback.
92
+ */
33
93
  type LoadingState<TActions> = Partial<Record<keyof TActions, boolean>>;
94
+ /**
95
+ * Configuration options for the store instance, governing features like metrics and persistence.
96
+ *
97
+ * @template T The shape of the store state.
98
+ */
34
99
  type StoreOptions<T> = ObserverOptions & {
35
100
  enableMetrics?: boolean;
36
101
  persistence?: SimplePersistence<T>;
37
102
  };
103
+ /**
104
+ * A function that transforms a state update before it is committed.
105
+ * Runs after the action implementation but before state merge.
106
+ *
107
+ * @template TState The shape of the main store state.
108
+ * @template TArtifactsMap The map of artifact definitions.
109
+ */
110
+ type Transformer<TState extends object, TArtifactsMap extends ArtifactsMap<TState>> = (ctx: ActionContext<TState, InferRegistry<TArtifactsMap>>, update: DeepPartial<TState>) => Promise<DeepPartial<TState>> | Promise<void> | DeepPartial<TState> | void;
111
+ /**
112
+ * A function that validates a state update. If it returns false (or a Promise resolving to false),
113
+ * the update is cancelled, maintaining state consistency.
114
+ *
115
+ * @template TState The shape of the main store state.
116
+ * @template TArtifactsMap The map of artifact definitions.
117
+ */
118
+ type Validator<TState extends object, TArtifactsMap extends ArtifactsMap<TState>> = (ctx: ActionContext<TState, InferRegistry<TArtifactsMap>>, update: DeepPartial<TState>) => Promise<boolean> | boolean;
119
+ /**
120
+ * The main configuration object used to create a store.
121
+ *
122
+ * @template TState The initial shape of the store state.
123
+ * @template TArtifactsMap The map of service/dependency definitions (Artifacts).
124
+ * @template TActions The map of state mutation functions (Actions).
125
+ */
38
126
  interface StoreDefinition<TState extends object, TArtifactsMap extends ArtifactsMap<TState>, TActions extends ActionMap<TState, TArtifactsMap>> {
127
+ /** The initial state of the store. */
39
128
  state: TState;
129
+ /** A map of functions that mutate the state. */
40
130
  actions: TActions;
131
+ /** An optional map of asynchronous resources or services (Artifacts). */
41
132
  artifacts?: TArtifactsMap;
133
+ /** Optional initial loading state for specific actions. */
42
134
  loading?: LoadingState<TActions>;
135
+ /** Optional function to synchronize state with an external system after changes. */
43
136
  sync?: (args: TState) => void;
44
- blockingMiddleware?: Record<string, BlockingMiddleware<TState>>;
45
- middleware?: Record<string, Middleware<TState>>;
137
+ /** Optional map of transformers that run after an action but before state merge. */
138
+ transform?: Record<string, Transformer<TState, TArtifactsMap>>;
139
+ /** Optional map of validators that run before state merge to check for correctness. */
140
+ validate?: Record<string, Validator<TState, TArtifactsMap>>;
46
141
  }
142
+ /**
143
+ * The resulting React hook function returned by `createStore`.
144
+ * When called inside a component, it provides reactive access to the store's capabilities.
145
+ *
146
+ * @template TState The shape of the store state.
147
+ * @template TArtifactsMap The map of artifact definitions.
148
+ * @template TActions The map of action definitions.
149
+ */
47
150
  type StoreHook<TState extends object, TArtifactsMap extends ArtifactsMap<TState>, TActions extends ActionMap<TState, TArtifactsMap>> = () => {
48
- store: any;
49
- observer: any;
151
+ /** Internal store instance. */
152
+ store: ReactiveDataStore<TState>;
153
+ /** Internal observer instance. */
154
+ observer: StoreObserver<TState>;
155
+ /**
156
+ * Selects a value from the state using a memoized selector function.
157
+ * Only causes a re-render if the selected value changes.
158
+ *
159
+ * @param selector A function to derive data from the store state.
160
+ */
50
161
  select: <S>(selector: (state: TState) => S) => S;
162
+ /** The publicly available, bound actions for mutating state. */
51
163
  actions: BoundActions<TState, TArtifactsMap, TActions>;
52
164
  /**
53
165
  * Reactive Artifact Resolver.
54
- * Returns [instance, isReady].
166
+ * Returns the resolved artifact with instance and metadata (ready status).
167
+ * Causes a re-render when the artifact's dependencies (e.g., state) change.
168
+ *
169
+ * @param key The name of the artifact to resolve.
55
170
  */
56
- resolve: <K extends keyof TArtifactsMap>(key: K) => readonly [ExtractInstanceFromMap<TArtifactsMap, K>, true] | readonly [ExtractInstanceFromMap<TArtifactsMap, K> | undefined, false];
171
+ resolve: <K extends keyof TArtifactsMap>(key: K) => ResolvedArtifact<ArtifactValue<TArtifactsMap[K]>>;
172
+ /** Flag indicating if the store and its essential artifacts are ready for use. */
57
173
  isReady: boolean;
58
- actionTracker: any;
174
+ /** Tracker for monitoring in-flight or completed actions. */
175
+ actionTracker: ActionTracker;
176
+ /**
177
+ * Watches a specific action and returns its loading state (true if running).
178
+ *
179
+ * @param action The name of the action to watch.
180
+ */
59
181
  watch: (action: keyof TActions) => boolean;
182
+ /**
183
+ * Returns the current, non-reactive snapshot of the store state.
184
+ * Calling this will NOT trigger component re-renders when state changes.
185
+ */
60
186
  state: () => TState;
61
187
  };
62
188
 
63
189
  declare function createStore<TState extends Record<string, unknown>, TArtifactsMap extends ArtifactsMap<TState>, TActions extends ActionMap<TState, TArtifactsMap>>(definition: StoreDefinition<TState, TArtifactsMap, TActions>, { enableMetrics, ...options }?: StoreOptions<TState>): StoreHook<TState, TArtifactsMap, TActions>;
64
190
 
65
- export { type ActionContext, type ActionImplementation, type ActionMap, type ArtifactDefinition, type ArtifactsMap, type BoundActions, type ExtractArtifactInstanceFromConfig, type ExtractInstanceFromMap, type LoadingState, type ResolvedArtifactsMap, type StoreDefinition, type StoreHook, type StoreOptions, createStore };
191
+ export { type ActionContext, type ActionImplementation, type ActionMap, type ArtifactScope, type ArtifactsMap, type BoundActions, type LoadingState, type StoreDefinition, type StoreHook, type StoreOptions, type Transformer, type Validator, createStore };
package/index.d.ts CHANGED
@@ -1,65 +1,191 @@
1
1
  import { SimplePersistence } from '@asaidimu/utils-persistence';
2
- import { DeepPartial, ArtifactFactory, ArtifactScope, ObserverOptions, BlockingMiddleware, Middleware } from '@asaidimu/utils-store';
2
+ import { DeepPartial, ObserverOptions, ReactiveDataStore, StoreObserver } from '@asaidimu/utils-store';
3
+ import { ResolvedArtifact, ArtifactScope as ArtifactScope$1, ArtifactTemplate, InferRegistry, ArtifactValue } from '@asaidimu/utils-artifacts';
3
4
 
4
- interface ActionContext<TState extends object, TResolvedArtifacts extends object> {
5
+ interface ActionExecution {
6
+ id: string;
7
+ name: string;
8
+ params: unknown[];
9
+ startTime: number;
10
+ endTime: number;
11
+ duration: number;
12
+ status: 'success' | 'error';
13
+ error?: Error;
14
+ result?: unknown;
15
+ }
16
+ declare class ActionTracker {
17
+ private executions;
18
+ private maxHistory;
19
+ private listeners;
20
+ track(execution: ActionExecution): void;
21
+ getExecutions(): ActionExecution[];
22
+ subscribe(listener: () => void): () => boolean;
23
+ private notify;
24
+ }
25
+
26
+ /**
27
+ * The context object provided as the first argument to all actions, transforms, and validators.
28
+ * It provides access to the current state and the artifact resolution system.
29
+ *
30
+ * @template TState The shape of the main store state.
31
+ * @template TRegistry The inferred type of the entire artifact registry.
32
+ */
33
+ interface ActionContext<TState extends object, TRegistry extends Record<string, any>> {
5
34
  /**
6
- * Resolve an artifact.
7
- * This records a dependency between the caller and the requested artifact.
35
+ * Resolve an artifact, recording a dependency between the caller and the requested artifact.
36
+ * This function is asynchronous because artifact factories can be asynchronous.
37
+ *
38
+ * @param key The name of the artifact to resolve.
39
+ * @returns A promise that resolves to the resolved artifact instance and its metadata.
40
+ */
41
+ resolve<K extends keyof TRegistry>(key: K): Promise<ResolvedArtifact<TRegistry[K]>>;
42
+ /**
43
+ * The current snapshot of the store state.
8
44
  */
9
- resolve<K extends keyof TResolvedArtifacts>(key: K): Promise<TResolvedArtifacts[K]>;
10
45
  state: TState;
11
46
  }
12
- type ActionImplementation<TState extends object, TResolvedArtifacts extends object, TArgs extends any[]> = (ctx: ActionContext<TState, TResolvedArtifacts>, ...args: TArgs) => DeepPartial<TState> | Promise<DeepPartial<TState>>;
13
- type ArtifactDefinition<TState extends Object, R> = {
14
- factory: ArtifactFactory<TState, R>;
15
- scope?: ArtifactScope;
16
- lazy?: boolean;
17
- };
18
- type ArtifactsMap<TState extends object> = Record<string, ArtifactDefinition<TState, any>>;
19
- type ExtractArtifactInstanceFromConfig<T> = T extends {
20
- factory: ArtifactFactory<any, infer I>;
21
- } ? I : never;
22
- type ExtractInstanceFromMap<TMap extends ArtifactsMap<any>, TKey extends keyof TMap> = ExtractArtifactInstanceFromConfig<TMap[TKey]>;
23
- type ResolvedArtifactsMap<TArtifactsMap extends ArtifactsMap<any>> = {
24
- [K in keyof TArtifactsMap]: ExtractInstanceFromMap<TArtifactsMap, K>;
25
- };
26
- type ActionMap<TState extends object, TArtifactsMap extends ArtifactsMap<TState>> = Record<string, ActionImplementation<TState, ResolvedArtifactsMap<TArtifactsMap>, any[]>>;
27
47
  /**
28
- * Bounds the actions for the resulting store hook, removing the context argument.
48
+ * Re-exports the ArtifactScope for use in the store's definition.
49
+ */
50
+ type ArtifactScope = ArtifactScope$1;
51
+ /**
52
+ * The core function signature for an Action.
53
+ * It takes the context and any custom arguments, and returns a (potentially partial)
54
+ * update to the state or a Promise resolving to the update.
55
+ *
56
+ * @template TState The shape of the main store state.
57
+ * @template TRegistry The inferred type of the entire artifact registry.
58
+ * @template TArgs The custom arguments accepted by the action.
59
+ */
60
+ type ActionImplementation<TState extends object, TRegistry extends Record<string, any>, TArgs extends any[]> = (ctx: ActionContext<TState, TRegistry>, ...args: TArgs) => DeepPartial<TState> | Promise<DeepPartial<TState>>;
61
+ /**
62
+ * A map of all artifact definitions keyed by their names.
63
+ *
64
+ * @template TState The shape of the main store state.
65
+ */
66
+ type ArtifactsMap<TState extends object> = Record<string, Omit<ArtifactTemplate<TState, any, any>, "key">>;
67
+ /**
68
+ * A map of all ActionImplementations keyed by their action names.
69
+ *
70
+ * @template TState The shape of the main store state.
71
+ * @template TArtifactsMap The map of artifact definitions.
72
+ */
73
+ type ActionMap<TState extends object, TArtifactsMap extends ArtifactsMap<TState>> = Record<string, ActionImplementation<TState, InferRegistry<TArtifactsMap>, any[]>>;
74
+ /**
75
+ * Utility type to extract the non-context arguments (the "tail" parameters) from an ActionImplementation.
76
+ */
77
+ type TailParams<F, TState extends object, TRegistry extends Record<string, any>> = F extends (ctx: ActionContext<TState, TRegistry>, ...args: infer R) => any ? R : never;
78
+ /**
79
+ * Defines the public, bound signature of the store's actions.
80
+ * This removes the internal `ActionContext` argument and makes the return type a Promise
81
+ * that resolves when the state update is complete.
82
+ *
83
+ * @template TState The shape of the main store state.
84
+ * @template TArtifactsMap The map of artifact definitions.
85
+ * @template TActions The map of ActionImplementations.
29
86
  */
30
87
  type BoundActions<TState extends object, TArtifactsMap extends ArtifactsMap<TState>, TActions extends ActionMap<TState, TArtifactsMap>> = {
31
- [K in keyof TActions]: (...args: Parameters<TActions[K]> extends [ActionContext<any, any>, ...infer R] ? R : never) => Promise<TState>;
88
+ [K in keyof TActions]: (...args: TailParams<TActions[K], TState, InferRegistry<TArtifactsMap>> extends [] ? [] : TailParams<TActions[K], TState, InferRegistry<TArtifactsMap>>) => Promise<TState>;
32
89
  };
90
+ /**
91
+ * Tracks the loading status of specific actions, used for UI feedback.
92
+ */
33
93
  type LoadingState<TActions> = Partial<Record<keyof TActions, boolean>>;
94
+ /**
95
+ * Configuration options for the store instance, governing features like metrics and persistence.
96
+ *
97
+ * @template T The shape of the store state.
98
+ */
34
99
  type StoreOptions<T> = ObserverOptions & {
35
100
  enableMetrics?: boolean;
36
101
  persistence?: SimplePersistence<T>;
37
102
  };
103
+ /**
104
+ * A function that transforms a state update before it is committed.
105
+ * Runs after the action implementation but before state merge.
106
+ *
107
+ * @template TState The shape of the main store state.
108
+ * @template TArtifactsMap The map of artifact definitions.
109
+ */
110
+ type Transformer<TState extends object, TArtifactsMap extends ArtifactsMap<TState>> = (ctx: ActionContext<TState, InferRegistry<TArtifactsMap>>, update: DeepPartial<TState>) => Promise<DeepPartial<TState>> | Promise<void> | DeepPartial<TState> | void;
111
+ /**
112
+ * A function that validates a state update. If it returns false (or a Promise resolving to false),
113
+ * the update is cancelled, maintaining state consistency.
114
+ *
115
+ * @template TState The shape of the main store state.
116
+ * @template TArtifactsMap The map of artifact definitions.
117
+ */
118
+ type Validator<TState extends object, TArtifactsMap extends ArtifactsMap<TState>> = (ctx: ActionContext<TState, InferRegistry<TArtifactsMap>>, update: DeepPartial<TState>) => Promise<boolean> | boolean;
119
+ /**
120
+ * The main configuration object used to create a store.
121
+ *
122
+ * @template TState The initial shape of the store state.
123
+ * @template TArtifactsMap The map of service/dependency definitions (Artifacts).
124
+ * @template TActions The map of state mutation functions (Actions).
125
+ */
38
126
  interface StoreDefinition<TState extends object, TArtifactsMap extends ArtifactsMap<TState>, TActions extends ActionMap<TState, TArtifactsMap>> {
127
+ /** The initial state of the store. */
39
128
  state: TState;
129
+ /** A map of functions that mutate the state. */
40
130
  actions: TActions;
131
+ /** An optional map of asynchronous resources or services (Artifacts). */
41
132
  artifacts?: TArtifactsMap;
133
+ /** Optional initial loading state for specific actions. */
42
134
  loading?: LoadingState<TActions>;
135
+ /** Optional function to synchronize state with an external system after changes. */
43
136
  sync?: (args: TState) => void;
44
- blockingMiddleware?: Record<string, BlockingMiddleware<TState>>;
45
- middleware?: Record<string, Middleware<TState>>;
137
+ /** Optional map of transformers that run after an action but before state merge. */
138
+ transform?: Record<string, Transformer<TState, TArtifactsMap>>;
139
+ /** Optional map of validators that run before state merge to check for correctness. */
140
+ validate?: Record<string, Validator<TState, TArtifactsMap>>;
46
141
  }
142
+ /**
143
+ * The resulting React hook function returned by `createStore`.
144
+ * When called inside a component, it provides reactive access to the store's capabilities.
145
+ *
146
+ * @template TState The shape of the store state.
147
+ * @template TArtifactsMap The map of artifact definitions.
148
+ * @template TActions The map of action definitions.
149
+ */
47
150
  type StoreHook<TState extends object, TArtifactsMap extends ArtifactsMap<TState>, TActions extends ActionMap<TState, TArtifactsMap>> = () => {
48
- store: any;
49
- observer: any;
151
+ /** Internal store instance. */
152
+ store: ReactiveDataStore<TState>;
153
+ /** Internal observer instance. */
154
+ observer: StoreObserver<TState>;
155
+ /**
156
+ * Selects a value from the state using a memoized selector function.
157
+ * Only causes a re-render if the selected value changes.
158
+ *
159
+ * @param selector A function to derive data from the store state.
160
+ */
50
161
  select: <S>(selector: (state: TState) => S) => S;
162
+ /** The publicly available, bound actions for mutating state. */
51
163
  actions: BoundActions<TState, TArtifactsMap, TActions>;
52
164
  /**
53
165
  * Reactive Artifact Resolver.
54
- * Returns [instance, isReady].
166
+ * Returns the resolved artifact with instance and metadata (ready status).
167
+ * Causes a re-render when the artifact's dependencies (e.g., state) change.
168
+ *
169
+ * @param key The name of the artifact to resolve.
55
170
  */
56
- resolve: <K extends keyof TArtifactsMap>(key: K) => readonly [ExtractInstanceFromMap<TArtifactsMap, K>, true] | readonly [ExtractInstanceFromMap<TArtifactsMap, K> | undefined, false];
171
+ resolve: <K extends keyof TArtifactsMap>(key: K) => ResolvedArtifact<ArtifactValue<TArtifactsMap[K]>>;
172
+ /** Flag indicating if the store and its essential artifacts are ready for use. */
57
173
  isReady: boolean;
58
- actionTracker: any;
174
+ /** Tracker for monitoring in-flight or completed actions. */
175
+ actionTracker: ActionTracker;
176
+ /**
177
+ * Watches a specific action and returns its loading state (true if running).
178
+ *
179
+ * @param action The name of the action to watch.
180
+ */
59
181
  watch: (action: keyof TActions) => boolean;
182
+ /**
183
+ * Returns the current, non-reactive snapshot of the store state.
184
+ * Calling this will NOT trigger component re-renders when state changes.
185
+ */
60
186
  state: () => TState;
61
187
  };
62
188
 
63
189
  declare function createStore<TState extends Record<string, unknown>, TArtifactsMap extends ArtifactsMap<TState>, TActions extends ActionMap<TState, TArtifactsMap>>(definition: StoreDefinition<TState, TArtifactsMap, TActions>, { enableMetrics, ...options }?: StoreOptions<TState>): StoreHook<TState, TArtifactsMap, TActions>;
64
190
 
65
- export { type ActionContext, type ActionImplementation, type ActionMap, type ArtifactDefinition, type ArtifactsMap, type BoundActions, type ExtractArtifactInstanceFromConfig, type ExtractInstanceFromMap, type LoadingState, type ResolvedArtifactsMap, type StoreDefinition, type StoreHook, type StoreOptions, createStore };
191
+ export { type ActionContext, type ActionImplementation, type ActionMap, type ArtifactScope, type ArtifactsMap, type BoundActions, type LoadingState, type StoreDefinition, type StoreHook, type StoreOptions, type Transformer, type Validator, createStore };
package/index.js CHANGED
@@ -1 +1 @@
1
- import{useSyncExternalStore as e,useEffect as t}from"react";import{ReactiveDataStore as s,StoreObserver as r,ArtifactContainer as i}from"@asaidimu/utils-store";var n=class{executions=[];maxHistory=100;listeners=new Set;track(e){this.executions.unshift(e),this.executions.length>this.maxHistory&&this.executions.pop(),this.notify()}getExecutions(){return[...this.executions]}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){this.listeners.forEach((e=>e()))}};function a(a,{enableMetrics:o,...c}={}){const d=new s(a.state,c.persistence),u=Object.keys(a.actions).reduce(((e,t)=>(e[t]=!1,e)),{}),m=new s(u),l=o?new r(d,c):void 0,h=o?new n:void 0;o&&h&&(d.on("action:start",(({name:e})=>{m.set((()=>({[e]:!0})))})),d.on("action:complete",(e=>{m.set((()=>({[e.name]:!1}))),h.track({id:e.actionId,name:e.name,params:e.params,startTime:e.startTime,endTime:e.endTime,duration:e.duration,status:"success",result:e.result})})),d.on("action:error",(e=>{m.set((()=>({[e.name]:!1}))),h.track({id:e.actionId,name:e.name,params:e.params,startTime:e.startTime,endTime:e.endTime,duration:e.duration,status:"error",error:e.error})}))),a.middleware&&Object.entries(a.middleware).forEach((([e,t])=>d.use({name:e,action:t}))),a.blockingMiddleware&&Object.entries(a.blockingMiddleware).forEach((([e,t])=>d.use({block:!0,name:e,action:t})));const b=e=>(d.isReady()&&e(),d.on("persistence:ready",e)),f=()=>d.isReady(),g=new i(d);a.artifacts&&Object.entries(a.artifacts).forEach((([e,t])=>{g.register({key:e,factory:t.factory,scope:t.scope,lazy:t.lazy})}));const p=Object.entries(a.actions).reduce(((e,[t,s])=>(d.register({name:t,fn:(e,...t)=>{const r={state:e,resolve:g.resolve.bind(g)};return s(r,...t)}}),e[t]=(...e)=>d.dispatch(t,...e),e)),{});return function(){const s=()=>e((e=>d.watch("",e)),(()=>d.get()),(()=>d.get())),r=e(b,f,f);return{store:d,observer:l,select:t=>{const s=d.select(t);return e((e=>s.subscribe(e)),(()=>s.get()),(()=>s.get()))},actions:p,isReady:r,actionTracker:h,watch:t=>e((e=>m.watch(t,e)),(()=>!!m.get()[t]),(()=>!!m.get()[t])),resolve:s=>{const r=e((e=>g.subscribeToArtifact(s,e)),(()=>g.get(s)),(()=>g.get(s)));t((()=>{void 0!==r||g.isLoading(s)||g.resolve(s).catch(console.error)}),[s,r]);return[r,null!=r]},get state(){return s}}}}export{a as createStore};
1
+ import{useSyncExternalStore as e}from"react";import{ReactiveDataStore as t,StoreObserver as s}from"@asaidimu/utils-store";import{ArtifactContainer as r}from"@asaidimu/utils-artifacts";var a=class{executions=[];maxHistory=100;listeners=new Set;track(e){this.executions.unshift(e),this.executions.length>this.maxHistory&&this.executions.pop(),this.notify()}getExecutions(){return[...this.executions]}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){this.listeners.forEach(e=>e())}};function i(i,{enableMetrics:n,...o}={}){const c=new t(i.state,o.persistence),u=Object.keys(i.actions).reduce((e,t)=>(e[t]=!1,e),{}),m=new t(u),d=n?new s(c,o):void 0,l=n?new a:void 0;n&&l&&(c.on("action:start",({name:e})=>{m.set(()=>({[e]:!0}))}),c.on("action:complete",e=>{m.set(()=>({[e.name]:!1})),l.track({id:e.actionId,name:e.name,params:e.params,startTime:e.startTime,endTime:e.endTime,duration:e.duration,status:"success",result:e.result})}),c.on("action:error",e=>{m.set(()=>({[e.name]:!1})),l.track({id:e.actionId,name:e.name,params:e.params,startTime:e.startTime,endTime:e.endTime,duration:e.duration,status:"error",error:e.error})}));const f=e=>(c.isReady()&&e(),c.on("persistence:ready",e)),h=()=>c.isReady(),p=new r(c),b=p.resolve.bind(p);i.artifacts&&Object.entries(i.artifacts).forEach(([e,t])=>{p.register({key:e,factory:t.factory,scope:t.scope,lazy:t.lazy})}),i.transform&&Object.entries(i.transform).forEach(([e,t])=>{c.use({name:e,action:(e,s)=>t({state:e,resolve:b},s)})}),i.validate&&Object.entries(i.validate).forEach(([e,t])=>{c.use({block:!0,name:e,action:(e,s)=>t({state:e,resolve:b},s)})});const y=Object.entries(i.actions).reduce((e,[t,s])=>(c.register({name:t,fn:(e,...t)=>s({state:e,resolve:b},...t)}),e[t]=(...e)=>c.dispatch(t,...e),e),{});return function(){const t=()=>e(e=>c.watch("",e),()=>c.get(),()=>c.get()),s=e(f,h,h);return{select:t=>{const s=c.select(t);return e(e=>s.subscribe(e),()=>s.get(),()=>s.get())},watch:t=>e(e=>m.watch(t,e),()=>!!m.get()[t],()=>!!m.get()[t]),resolve:t=>{const s=p.watch(t);return e(e=>s.subscribe(()=>e()),()=>s.get(),()=>s.get())},actions:y,isReady:s,store:c,observer:d,actionTracker:l,get state(){return t}}}}export{i as createStore};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asaidimu/react-store",
3
- "version": "3.0.0",
3
+ "version": "5.0.0",
4
4
  "description": "Efficient react state manager.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -26,10 +26,11 @@
26
26
  "access": "public"
27
27
  },
28
28
  "dependencies": {
29
- "@asaidimu/utils-persistence": "^2.1.0",
30
- "@asaidimu/utils-store": "^4.0.0",
29
+ "@asaidimu/utils-artifacts": "^3.1.0",
30
+ "@asaidimu/utils-persistence": "^2.2.2",
31
+ "@asaidimu/utils-store": "^7.0.0",
31
32
  "hash-wasm": "^4.12.0",
32
- "react": "^19.0.0",
33
+ "react": "^19.2.0",
33
34
  "uuid": "^11.1.0"
34
35
  }
35
36
  }