@bkincz/clutch 1.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.
@@ -0,0 +1,291 @@
1
+ import { Patch, Draft } from 'immer';
2
+ export { Draft } from 'immer';
3
+ import { DependencyList } from 'react';
4
+
5
+ declare class StateMachineError extends Error {
6
+ readonly code: string;
7
+ constructor(message: string, code: string);
8
+ }
9
+ declare class StateValidationError extends StateMachineError {
10
+ constructor(message: string);
11
+ }
12
+ declare class StatePersistenceError extends StateMachineError {
13
+ constructor(message: string);
14
+ }
15
+ interface StateConfig<T extends object> {
16
+ initialState: T;
17
+ persistenceKey?: string;
18
+ autoSaveInterval?: number;
19
+ maxHistorySize?: number;
20
+ enablePersistence?: boolean;
21
+ enableAutoSave?: boolean;
22
+ enableLogging?: boolean;
23
+ validateState?: (state: T) => boolean;
24
+ }
25
+ interface InternalStateConfig<T extends object> {
26
+ initialState: T;
27
+ persistenceKey: string | null;
28
+ autoSaveInterval: number;
29
+ maxHistorySize: number;
30
+ enablePersistence: boolean;
31
+ enableAutoSave: boolean;
32
+ enableLogging: boolean;
33
+ validateState: (state: T) => boolean;
34
+ }
35
+ interface StateSnapshot {
36
+ patches: Patch[];
37
+ inversePatches: Patch[];
38
+ timestamp: number;
39
+ description?: string;
40
+ }
41
+ interface PersistedState<T> {
42
+ state: T;
43
+ timestamp: number;
44
+ version: string;
45
+ checksum?: string;
46
+ }
47
+ interface StateHistoryInfo {
48
+ canUndo: boolean;
49
+ canRedo: boolean;
50
+ historyLength: number;
51
+ currentIndex: number;
52
+ lastAction: string | null;
53
+ memoryUsage: number;
54
+ }
55
+ declare const createLogger: (enabled: boolean) => {
56
+ debug: (msg: string, ...args: unknown[]) => void;
57
+ info: (msg: string, ...args: unknown[]) => void;
58
+ warn: (msg: string, ...args: unknown[]) => void;
59
+ error: (msg: string, ...args: unknown[]) => void;
60
+ };
61
+ declare abstract class StateMachine<T extends object> {
62
+ protected state: T;
63
+ protected config: InternalStateConfig<T>;
64
+ protected listeners: Set<(state: T) => void>;
65
+ protected history: StateSnapshot[];
66
+ protected historyIndex: number;
67
+ protected isDirty: boolean;
68
+ protected autoSaveTimer: ReturnType<typeof setInterval> | null;
69
+ protected isDestroyed: boolean;
70
+ protected logger: ReturnType<typeof createLogger>;
71
+ protected debouncedNotify: () => void;
72
+ constructor(config: StateConfig<T>);
73
+ protected abstract saveToServer(state: T): Promise<void>;
74
+ protected abstract loadFromServer(): Promise<T | null>;
75
+ getState(): T;
76
+ subscribe(listener: (state: T) => void): () => void;
77
+ mutate(recipe: (draft: Draft<T>) => void, description?: string): void;
78
+ batch(mutations: Array<(draft: Draft<T>) => void>, description?: string): void;
79
+ undo(): boolean;
80
+ redo(): boolean;
81
+ canUndo(): boolean;
82
+ canRedo(): boolean;
83
+ getHistoryInfo(): StateHistoryInfo;
84
+ clearHistory(): void;
85
+ forceSave(): Promise<void>;
86
+ hasUnsavedChanges(): boolean;
87
+ setAutoSaveInterval(minutes: number): void;
88
+ loadFromServerManually(): Promise<boolean>;
89
+ destroy(): void;
90
+ protected setState(newState: T, markDirty?: boolean): void;
91
+ private assertNotDestroyed;
92
+ private validateConfig;
93
+ private validateCurrentState;
94
+ protected validateState(state: T): void;
95
+ private notifyListeners;
96
+ private saveToHistory;
97
+ private persistToLocal;
98
+ private loadPersistedState;
99
+ private startAutoSave;
100
+ private restartAutoSave;
101
+ }
102
+
103
+ /**
104
+ * Basic hook to subscribe to a StateMachine instance
105
+ */
106
+ declare function useStateMachine<T extends object>(engine: StateMachine<T>): {
107
+ state: T;
108
+ mutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
109
+ batch: (mutations: Array<(draft: Draft<T>) => void>, description?: string) => void;
110
+ };
111
+ /**
112
+ * Hook to subscribe to a specific slice of state with a selector function
113
+ */
114
+ declare function useStateSlice<T extends object, TSelected>(engine: StateMachine<T>, selector: (state: T) => TSelected, equalityFn?: (a: TSelected, b: TSelected) => boolean): TSelected;
115
+ /**
116
+ * Hook that provides StateMachine actions/methods
117
+ */
118
+ declare function useStateActions<T extends object>(engine: StateMachine<T>): {
119
+ mutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
120
+ batch: (mutations: Array<(draft: Draft<T>) => void>, description?: string) => void;
121
+ undo: () => boolean;
122
+ redo: () => boolean;
123
+ forceSave: () => Promise<void>;
124
+ loadFromServer: () => Promise<boolean>;
125
+ clearHistory: () => void;
126
+ };
127
+ /**
128
+ * Hook specifically for undo/redo functionality
129
+ */
130
+ declare function useStateHistory<T extends object>(engine: StateMachine<T>): {
131
+ undo: () => boolean;
132
+ redo: () => boolean;
133
+ clearHistory: () => void;
134
+ canUndo: boolean;
135
+ canRedo: boolean;
136
+ historyLength: number;
137
+ currentIndex: number;
138
+ lastAction: string | null;
139
+ memoryUsage: number;
140
+ };
141
+ /**
142
+ * Hook for managing save/load state and persistence
143
+ */
144
+ declare function useStatePersist<T extends object>(engine: StateMachine<T>): {
145
+ isSaving: boolean;
146
+ isLoading: boolean;
147
+ hasUnsavedChanges: boolean;
148
+ lastSaved: Date | null;
149
+ saveError: string | null;
150
+ loadError: string | null;
151
+ clearSaveError: () => void;
152
+ clearLoadError: () => void;
153
+ save: () => Promise<boolean>;
154
+ load: () => Promise<boolean>;
155
+ };
156
+ /**
157
+ * Comprehensive hook that combines all StateMachine functionality
158
+ */
159
+ declare function useStateMachineFull<T extends object>(engine: StateMachine<T>): {
160
+ state: T;
161
+ mutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
162
+ batch: (mutations: ((draft: Draft<T>) => void)[], description?: string) => void;
163
+ history: {
164
+ canUndo: boolean;
165
+ canRedo: boolean;
166
+ historyLength: number;
167
+ currentIndex: number;
168
+ lastAction: string | null;
169
+ memoryUsage: number;
170
+ undo: () => boolean;
171
+ redo: () => boolean;
172
+ clearHistory: () => void;
173
+ };
174
+ persistence: {
175
+ isSaving: boolean;
176
+ isLoading: boolean;
177
+ hasUnsavedChanges: boolean;
178
+ lastSaved: Date | null;
179
+ saveError: string | null;
180
+ loadError: string | null;
181
+ save: () => Promise<boolean>;
182
+ load: () => Promise<boolean>;
183
+ clearSaveError: () => void;
184
+ clearLoadError: () => void;
185
+ };
186
+ forceSave: () => Promise<void>;
187
+ loadFromServer: () => Promise<boolean>;
188
+ };
189
+ /**
190
+ * Hook for creating optimistic updates with automatic rollback on failure
191
+ * -- Still testing this, I'm not sure if this is a good solution.
192
+ */
193
+ declare function useOptimisticUpdate<T extends object>(engine: StateMachine<T>): {
194
+ mutateOptimistic: (optimisticUpdate: (draft: Draft<T>) => void, serverUpdate: () => Promise<void>, description?: string) => Promise<void>;
195
+ };
196
+ /**
197
+ * Hook for debounced state updates (useful for search inputs, etc.)
198
+ */
199
+ declare function useDebouncedStateUpdate<T extends object>(engine: StateMachine<T>, delay?: number): {
200
+ debouncedMutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
201
+ };
202
+ /**
203
+ * Hook for subscribing to state changes with cleanup on component unmount
204
+ */
205
+ declare function useStateSubscription<T extends object>(engine: StateMachine<T>, callback: (state: T) => void, deps?: DependencyList): void;
206
+ /**
207
+ * Utility hook for shallow equality comparison
208
+ */
209
+ declare function useShallowEqual<T>(value: T): T;
210
+ /**
211
+ * Factory function to create typed hooks for a specific StateMachine
212
+ */
213
+ declare function createStateMachineHooks<T extends object>(engine: StateMachine<T>): {
214
+ useState: () => {
215
+ state: T;
216
+ mutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
217
+ batch: (mutations: ((draft: Draft<T>) => void)[], description?: string) => void;
218
+ };
219
+ useSlice: <TSelected>(selector: (state: T) => TSelected, equalityFn?: (a: TSelected, b: TSelected) => boolean) => TSelected;
220
+ useActions: () => {
221
+ mutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
222
+ batch: (mutations: ((draft: Draft<T>) => void)[], description?: string) => void;
223
+ undo: () => boolean;
224
+ redo: () => boolean;
225
+ forceSave: () => Promise<void>;
226
+ loadFromServer: () => Promise<boolean>;
227
+ clearHistory: () => void;
228
+ };
229
+ useHistory: () => {
230
+ undo: () => boolean;
231
+ redo: () => boolean;
232
+ clearHistory: () => void;
233
+ canUndo: boolean;
234
+ canRedo: boolean;
235
+ historyLength: number;
236
+ currentIndex: number;
237
+ lastAction: string | null;
238
+ memoryUsage: number;
239
+ };
240
+ usePersistence: () => {
241
+ isSaving: boolean;
242
+ isLoading: boolean;
243
+ hasUnsavedChanges: boolean;
244
+ lastSaved: Date | null;
245
+ saveError: string | null;
246
+ loadError: string | null;
247
+ clearSaveError: () => void;
248
+ clearLoadError: () => void;
249
+ save: () => Promise<boolean>;
250
+ load: () => Promise<boolean>;
251
+ };
252
+ useComplete: () => {
253
+ state: T;
254
+ mutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
255
+ batch: (mutations: ((draft: Draft<T>) => void)[], description?: string) => void;
256
+ history: {
257
+ canUndo: boolean;
258
+ canRedo: boolean;
259
+ historyLength: number;
260
+ currentIndex: number;
261
+ lastAction: string | null;
262
+ memoryUsage: number;
263
+ undo: () => boolean;
264
+ redo: () => boolean;
265
+ clearHistory: () => void;
266
+ };
267
+ persistence: {
268
+ isSaving: boolean;
269
+ isLoading: boolean;
270
+ hasUnsavedChanges: boolean;
271
+ lastSaved: Date | null;
272
+ saveError: string | null;
273
+ loadError: string | null;
274
+ save: () => Promise<boolean>;
275
+ load: () => Promise<boolean>;
276
+ clearSaveError: () => void;
277
+ clearLoadError: () => void;
278
+ };
279
+ forceSave: () => Promise<void>;
280
+ loadFromServer: () => Promise<boolean>;
281
+ };
282
+ useOptimistic: () => {
283
+ mutateOptimistic: (optimisticUpdate: (draft: Draft<T>) => void, serverUpdate: () => Promise<void>, description?: string) => Promise<void>;
284
+ };
285
+ useDebounced: (delay?: number) => {
286
+ debouncedMutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
287
+ };
288
+ useSubscription: (callback: (state: T) => void, deps?: DependencyList) => void;
289
+ };
290
+
291
+ export { type PersistedState, type StateConfig, type StateHistoryInfo, StateMachine, StateMachineError, StatePersistenceError, type StateSnapshot, StateValidationError, createStateMachineHooks, useDebouncedStateUpdate, useOptimisticUpdate, useShallowEqual, useStateActions, useStateHistory, useStateMachine, useStateMachineFull, useStatePersist, useStateSlice, useStateSubscription };
@@ -0,0 +1,291 @@
1
+ import { Patch, Draft } from 'immer';
2
+ export { Draft } from 'immer';
3
+ import { DependencyList } from 'react';
4
+
5
+ declare class StateMachineError extends Error {
6
+ readonly code: string;
7
+ constructor(message: string, code: string);
8
+ }
9
+ declare class StateValidationError extends StateMachineError {
10
+ constructor(message: string);
11
+ }
12
+ declare class StatePersistenceError extends StateMachineError {
13
+ constructor(message: string);
14
+ }
15
+ interface StateConfig<T extends object> {
16
+ initialState: T;
17
+ persistenceKey?: string;
18
+ autoSaveInterval?: number;
19
+ maxHistorySize?: number;
20
+ enablePersistence?: boolean;
21
+ enableAutoSave?: boolean;
22
+ enableLogging?: boolean;
23
+ validateState?: (state: T) => boolean;
24
+ }
25
+ interface InternalStateConfig<T extends object> {
26
+ initialState: T;
27
+ persistenceKey: string | null;
28
+ autoSaveInterval: number;
29
+ maxHistorySize: number;
30
+ enablePersistence: boolean;
31
+ enableAutoSave: boolean;
32
+ enableLogging: boolean;
33
+ validateState: (state: T) => boolean;
34
+ }
35
+ interface StateSnapshot {
36
+ patches: Patch[];
37
+ inversePatches: Patch[];
38
+ timestamp: number;
39
+ description?: string;
40
+ }
41
+ interface PersistedState<T> {
42
+ state: T;
43
+ timestamp: number;
44
+ version: string;
45
+ checksum?: string;
46
+ }
47
+ interface StateHistoryInfo {
48
+ canUndo: boolean;
49
+ canRedo: boolean;
50
+ historyLength: number;
51
+ currentIndex: number;
52
+ lastAction: string | null;
53
+ memoryUsage: number;
54
+ }
55
+ declare const createLogger: (enabled: boolean) => {
56
+ debug: (msg: string, ...args: unknown[]) => void;
57
+ info: (msg: string, ...args: unknown[]) => void;
58
+ warn: (msg: string, ...args: unknown[]) => void;
59
+ error: (msg: string, ...args: unknown[]) => void;
60
+ };
61
+ declare abstract class StateMachine<T extends object> {
62
+ protected state: T;
63
+ protected config: InternalStateConfig<T>;
64
+ protected listeners: Set<(state: T) => void>;
65
+ protected history: StateSnapshot[];
66
+ protected historyIndex: number;
67
+ protected isDirty: boolean;
68
+ protected autoSaveTimer: ReturnType<typeof setInterval> | null;
69
+ protected isDestroyed: boolean;
70
+ protected logger: ReturnType<typeof createLogger>;
71
+ protected debouncedNotify: () => void;
72
+ constructor(config: StateConfig<T>);
73
+ protected abstract saveToServer(state: T): Promise<void>;
74
+ protected abstract loadFromServer(): Promise<T | null>;
75
+ getState(): T;
76
+ subscribe(listener: (state: T) => void): () => void;
77
+ mutate(recipe: (draft: Draft<T>) => void, description?: string): void;
78
+ batch(mutations: Array<(draft: Draft<T>) => void>, description?: string): void;
79
+ undo(): boolean;
80
+ redo(): boolean;
81
+ canUndo(): boolean;
82
+ canRedo(): boolean;
83
+ getHistoryInfo(): StateHistoryInfo;
84
+ clearHistory(): void;
85
+ forceSave(): Promise<void>;
86
+ hasUnsavedChanges(): boolean;
87
+ setAutoSaveInterval(minutes: number): void;
88
+ loadFromServerManually(): Promise<boolean>;
89
+ destroy(): void;
90
+ protected setState(newState: T, markDirty?: boolean): void;
91
+ private assertNotDestroyed;
92
+ private validateConfig;
93
+ private validateCurrentState;
94
+ protected validateState(state: T): void;
95
+ private notifyListeners;
96
+ private saveToHistory;
97
+ private persistToLocal;
98
+ private loadPersistedState;
99
+ private startAutoSave;
100
+ private restartAutoSave;
101
+ }
102
+
103
+ /**
104
+ * Basic hook to subscribe to a StateMachine instance
105
+ */
106
+ declare function useStateMachine<T extends object>(engine: StateMachine<T>): {
107
+ state: T;
108
+ mutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
109
+ batch: (mutations: Array<(draft: Draft<T>) => void>, description?: string) => void;
110
+ };
111
+ /**
112
+ * Hook to subscribe to a specific slice of state with a selector function
113
+ */
114
+ declare function useStateSlice<T extends object, TSelected>(engine: StateMachine<T>, selector: (state: T) => TSelected, equalityFn?: (a: TSelected, b: TSelected) => boolean): TSelected;
115
+ /**
116
+ * Hook that provides StateMachine actions/methods
117
+ */
118
+ declare function useStateActions<T extends object>(engine: StateMachine<T>): {
119
+ mutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
120
+ batch: (mutations: Array<(draft: Draft<T>) => void>, description?: string) => void;
121
+ undo: () => boolean;
122
+ redo: () => boolean;
123
+ forceSave: () => Promise<void>;
124
+ loadFromServer: () => Promise<boolean>;
125
+ clearHistory: () => void;
126
+ };
127
+ /**
128
+ * Hook specifically for undo/redo functionality
129
+ */
130
+ declare function useStateHistory<T extends object>(engine: StateMachine<T>): {
131
+ undo: () => boolean;
132
+ redo: () => boolean;
133
+ clearHistory: () => void;
134
+ canUndo: boolean;
135
+ canRedo: boolean;
136
+ historyLength: number;
137
+ currentIndex: number;
138
+ lastAction: string | null;
139
+ memoryUsage: number;
140
+ };
141
+ /**
142
+ * Hook for managing save/load state and persistence
143
+ */
144
+ declare function useStatePersist<T extends object>(engine: StateMachine<T>): {
145
+ isSaving: boolean;
146
+ isLoading: boolean;
147
+ hasUnsavedChanges: boolean;
148
+ lastSaved: Date | null;
149
+ saveError: string | null;
150
+ loadError: string | null;
151
+ clearSaveError: () => void;
152
+ clearLoadError: () => void;
153
+ save: () => Promise<boolean>;
154
+ load: () => Promise<boolean>;
155
+ };
156
+ /**
157
+ * Comprehensive hook that combines all StateMachine functionality
158
+ */
159
+ declare function useStateMachineFull<T extends object>(engine: StateMachine<T>): {
160
+ state: T;
161
+ mutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
162
+ batch: (mutations: ((draft: Draft<T>) => void)[], description?: string) => void;
163
+ history: {
164
+ canUndo: boolean;
165
+ canRedo: boolean;
166
+ historyLength: number;
167
+ currentIndex: number;
168
+ lastAction: string | null;
169
+ memoryUsage: number;
170
+ undo: () => boolean;
171
+ redo: () => boolean;
172
+ clearHistory: () => void;
173
+ };
174
+ persistence: {
175
+ isSaving: boolean;
176
+ isLoading: boolean;
177
+ hasUnsavedChanges: boolean;
178
+ lastSaved: Date | null;
179
+ saveError: string | null;
180
+ loadError: string | null;
181
+ save: () => Promise<boolean>;
182
+ load: () => Promise<boolean>;
183
+ clearSaveError: () => void;
184
+ clearLoadError: () => void;
185
+ };
186
+ forceSave: () => Promise<void>;
187
+ loadFromServer: () => Promise<boolean>;
188
+ };
189
+ /**
190
+ * Hook for creating optimistic updates with automatic rollback on failure
191
+ * -- Still testing this, I'm not sure if this is a good solution.
192
+ */
193
+ declare function useOptimisticUpdate<T extends object>(engine: StateMachine<T>): {
194
+ mutateOptimistic: (optimisticUpdate: (draft: Draft<T>) => void, serverUpdate: () => Promise<void>, description?: string) => Promise<void>;
195
+ };
196
+ /**
197
+ * Hook for debounced state updates (useful for search inputs, etc.)
198
+ */
199
+ declare function useDebouncedStateUpdate<T extends object>(engine: StateMachine<T>, delay?: number): {
200
+ debouncedMutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
201
+ };
202
+ /**
203
+ * Hook for subscribing to state changes with cleanup on component unmount
204
+ */
205
+ declare function useStateSubscription<T extends object>(engine: StateMachine<T>, callback: (state: T) => void, deps?: DependencyList): void;
206
+ /**
207
+ * Utility hook for shallow equality comparison
208
+ */
209
+ declare function useShallowEqual<T>(value: T): T;
210
+ /**
211
+ * Factory function to create typed hooks for a specific StateMachine
212
+ */
213
+ declare function createStateMachineHooks<T extends object>(engine: StateMachine<T>): {
214
+ useState: () => {
215
+ state: T;
216
+ mutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
217
+ batch: (mutations: ((draft: Draft<T>) => void)[], description?: string) => void;
218
+ };
219
+ useSlice: <TSelected>(selector: (state: T) => TSelected, equalityFn?: (a: TSelected, b: TSelected) => boolean) => TSelected;
220
+ useActions: () => {
221
+ mutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
222
+ batch: (mutations: ((draft: Draft<T>) => void)[], description?: string) => void;
223
+ undo: () => boolean;
224
+ redo: () => boolean;
225
+ forceSave: () => Promise<void>;
226
+ loadFromServer: () => Promise<boolean>;
227
+ clearHistory: () => void;
228
+ };
229
+ useHistory: () => {
230
+ undo: () => boolean;
231
+ redo: () => boolean;
232
+ clearHistory: () => void;
233
+ canUndo: boolean;
234
+ canRedo: boolean;
235
+ historyLength: number;
236
+ currentIndex: number;
237
+ lastAction: string | null;
238
+ memoryUsage: number;
239
+ };
240
+ usePersistence: () => {
241
+ isSaving: boolean;
242
+ isLoading: boolean;
243
+ hasUnsavedChanges: boolean;
244
+ lastSaved: Date | null;
245
+ saveError: string | null;
246
+ loadError: string | null;
247
+ clearSaveError: () => void;
248
+ clearLoadError: () => void;
249
+ save: () => Promise<boolean>;
250
+ load: () => Promise<boolean>;
251
+ };
252
+ useComplete: () => {
253
+ state: T;
254
+ mutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
255
+ batch: (mutations: ((draft: Draft<T>) => void)[], description?: string) => void;
256
+ history: {
257
+ canUndo: boolean;
258
+ canRedo: boolean;
259
+ historyLength: number;
260
+ currentIndex: number;
261
+ lastAction: string | null;
262
+ memoryUsage: number;
263
+ undo: () => boolean;
264
+ redo: () => boolean;
265
+ clearHistory: () => void;
266
+ };
267
+ persistence: {
268
+ isSaving: boolean;
269
+ isLoading: boolean;
270
+ hasUnsavedChanges: boolean;
271
+ lastSaved: Date | null;
272
+ saveError: string | null;
273
+ loadError: string | null;
274
+ save: () => Promise<boolean>;
275
+ load: () => Promise<boolean>;
276
+ clearSaveError: () => void;
277
+ clearLoadError: () => void;
278
+ };
279
+ forceSave: () => Promise<void>;
280
+ loadFromServer: () => Promise<boolean>;
281
+ };
282
+ useOptimistic: () => {
283
+ mutateOptimistic: (optimisticUpdate: (draft: Draft<T>) => void, serverUpdate: () => Promise<void>, description?: string) => Promise<void>;
284
+ };
285
+ useDebounced: (delay?: number) => {
286
+ debouncedMutate: (recipe: (draft: Draft<T>) => void, description?: string) => void;
287
+ };
288
+ useSubscription: (callback: (state: T) => void, deps?: DependencyList) => void;
289
+ };
290
+
291
+ export { type PersistedState, type StateConfig, type StateHistoryInfo, StateMachine, StateMachineError, StatePersistenceError, type StateSnapshot, StateValidationError, createStateMachineHooks, useDebouncedStateUpdate, useOptimisticUpdate, useShallowEqual, useStateActions, useStateHistory, useStateMachine, useStateMachineFull, useStatePersist, useStateSlice, useStateSubscription };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var t=require("immer"),e=require("react");t.enablePatches();var s=class extends Error{constructor(t,e){super(t),this.name="StateMachineError",this.code=e}},i=class extends s{constructor(t){super(t,"VALIDATION_ERROR")}},r=class extends s{constructor(t){super(t,"PERSISTENCE_ERROR")}},n=t=>{try{return new Blob([JSON.stringify(t)]).size}catch{return 0}},a=t=>{try{const e=JSON.stringify(t);let s=0;for(let t=0;t<e.length;t++)s=(s<<5)-s+e.charCodeAt(t)&s;return s.toString(36)}catch{return""}};function o(t){const[s,i]=e.useState(()=>t.getState());return e.useEffect(()=>t.subscribe(i),[t]),{state:s,mutate:e.useCallback((e,s)=>{t.mutate(e,s)},[t]),batch:e.useCallback((e,s)=>{t.batch(e,s)},[t])}}function h(t,s,i){const[r,n]=e.useState(()=>s(t.getState()));return e.useEffect(()=>t.subscribe(t=>{const e=s(t);i?i(r,e)||n(e):r!==e&&n(e)}),[t,s,r,i]),r}function c(t){return{mutate:e.useCallback((e,s)=>{t.mutate(e,s)},[t]),batch:e.useCallback((e,s)=>{t.batch(e,s)},[t]),undo:e.useCallback(()=>t.undo(),[t]),redo:e.useCallback(()=>t.redo(),[t]),forceSave:e.useCallback(async()=>t.forceSave(),[t]),loadFromServer:e.useCallback(async()=>t.loadFromServerManually(),[t]),clearHistory:e.useCallback(()=>{t.clearHistory()},[t])}}function u(t){const[s,i]=e.useState(()=>t.getHistoryInfo());return e.useEffect(()=>t.subscribe(()=>{i(t.getHistoryInfo())}),[t]),{...s,undo:e.useCallback(()=>{const e=t.undo();return e&&i(t.getHistoryInfo()),e},[t]),redo:e.useCallback(()=>{const e=t.redo();return e&&i(t.getHistoryInfo()),e},[t]),clearHistory:e.useCallback(()=>{t.clearHistory(),i(t.getHistoryInfo())},[t])}}function l(t){const[s,i]=e.useState(!1),[r,n]=e.useState(!1),[a,o]=e.useState(null),[h,c]=e.useState(null),[u,l]=e.useState(null),d=t.hasUnsavedChanges(),f=e.useCallback(async()=>{if(s)return!1;i(!0),c(null);try{return await t.forceSave(),o(new Date),!0}catch(t){const e=t instanceof Error?t.message:"Save failed";return c(e),!1}finally{i(!1)}},[t,s]),p=e.useCallback(async()=>{if(r)return!1;n(!0),l(null);try{const e=await t.loadFromServerManually();return e&&o(new Date),e}catch(t){const e=t instanceof Error?t.message:"Load failed";return l(e),!1}finally{n(!1)}},[t,r]),v=e.useCallback(()=>{c(null)},[]),y=e.useCallback(()=>{l(null)},[]);return{isSaving:s,isLoading:r,hasUnsavedChanges:d,lastSaved:a,saveError:h,loadError:u,clearSaveError:v,clearLoadError:y,save:f,load:p}}function d(t){const{state:e,mutate:s,batch:i}=o(t),r=c(t),n=u(t),a=l(t);return{state:e,mutate:s,batch:i,history:{canUndo:n.canUndo,canRedo:n.canRedo,historyLength:n.historyLength,currentIndex:n.currentIndex,lastAction:n.lastAction,memoryUsage:n.memoryUsage,undo:n.undo,redo:n.redo,clearHistory:n.clearHistory},persistence:{isSaving:a.isSaving,isLoading:a.isLoading,hasUnsavedChanges:a.hasUnsavedChanges,lastSaved:a.lastSaved,saveError:a.saveError,loadError:a.loadError,save:a.save,load:a.load,clearSaveError:a.clearSaveError,clearLoadError:a.clearLoadError},forceSave:r.forceSave,loadFromServer:r.loadFromServer}}function f(t){return{mutateOptimistic:e.useCallback(async(e,s,i)=>{t.mutate(e,`${i} (optimistic)`);try{await s()}catch(e){throw t.undo(),e}},[t])}}function p(t,s=300){const[i]=e.useState(()=>{let e;return(i,r)=>{clearTimeout(e),e=setTimeout(()=>{t.mutate(i,r)},s)}});return e.useEffect(()=>()=>{clearTimeout(i)},[i]),{debouncedMutate:i}}function v(t,s,i=[]){e.useEffect(()=>t.subscribe(s),[t,s,...i])}exports.StateMachine=class{constructor(t){var e;this.listeners=new Set,this.history=[],this.historyIndex=-1,this.isDirty=!1,this.autoSaveTimer=null,this.isDestroyed=!1,this.validateConfig(t),this.config={initialState:t.initialState,persistenceKey:t.persistenceKey||null,autoSaveInterval:t.autoSaveInterval??5,maxHistorySize:t.maxHistorySize??50,enablePersistence:t.enablePersistence??!0,enableAutoSave:t.enableAutoSave??!0,enableLogging:t.enableLogging??!1,validateState:t.validateState??(()=>!0)},this.logger={debug:(e=this.config.enableLogging)?(t,...e)=>{}:()=>{},info:e?(t,...e)=>{}:()=>{},warn:e?(t,...e)=>{}:()=>{},error:e?(t,...e)=>{}:()=>{}},this.debouncedNotify=function(t){let e;return(...s)=>{clearTimeout(e),e=setTimeout(()=>t(...s),16)}}(()=>this.notifyListeners());try{this.state=this.loadPersistedState()||t.initialState,this.validateCurrentState()}catch(e){this.logger.warn("Failed to load persisted state, using initial state",e),this.state=t.initialState,this.validateCurrentState()}this.config.enableAutoSave&&this.startAutoSave(),this.logger.info("StateMachine initialized",{persistenceKey:this.config.persistenceKey,autoSaveInterval:this.config.autoSaveInterval,maxHistorySize:this.config.maxHistorySize})}getState(){return this.assertNotDestroyed(),this.state}subscribe(t){if(this.assertNotDestroyed(),"function"!=typeof t)throw new i("Listener must be a function");this.listeners.add(t);try{t(this.state)}catch(t){this.logger.error("Initial listener call failed",t)}return this.logger.debug("Listener subscribed",{totalListeners:this.listeners.size}),()=>{this.listeners.delete(t),this.logger.debug("Listener unsubscribed",{totalListeners:this.listeners.size})}}mutate(e,r){if(this.assertNotDestroyed(),"function"!=typeof e)throw new i("Recipe must be a function");try{let s=[],i=[];const n=t.produce(this.state,e,(t,e)=>{s=t,i=e});s.length>0&&(this.validateState(n),this.saveToHistory(s,i,r),this.setState(n),this.logger.debug("State mutated",{description:r,patchCount:s.length,historySize:this.history.length}))}catch(t){if(this.logger.error("State mutation failed",{description:r,error:t}),t instanceof i)throw t;throw new s(`State mutation failed: ${t instanceof Error?t.message:"Unknown error"}`,"MUTATION_ERROR")}}batch(e,r){if(this.assertNotDestroyed(),!Array.isArray(e))throw new i("Mutations must be an array");if(0!==e.length)try{const s=[],n=[],a=e.reduce((e,r,a)=>{if("function"!=typeof r)throw new i(`Mutation at index ${a} must be a function`);let o=[],h=[];const c=t.produce(e,r,(t,e)=>{o=t,h=e});return s.push(...o),n.unshift(...h),c},this.state);s.length>0&&(this.validateState(a),this.saveToHistory(s,n,r||"Batch operation"),this.setState(a),this.logger.debug("Batch operation completed",{description:r,mutationCount:e.length,patchCount:s.length}))}catch(t){throw this.logger.error("Batch operation failed",{description:r,error:t}),new s(`Batch operation failed: ${t instanceof Error?t.message:"Unknown error"}`,"BATCH_ERROR")}else this.logger.debug("Empty batch operation ignored")}undo(){if(this.assertNotDestroyed(),!this.canUndo())return this.logger.debug("Undo operation ignored - no history available"),!1;try{const e=this.history[this.historyIndex];if(!e)return!1;const s=t.applyPatches(this.state,e.inversePatches);return this.validateState(s),this.historyIndex--,this.setState(s,!1),this.logger.debug("Undo operation completed",{description:e.description,newHistoryIndex:this.historyIndex}),!0}catch(t){return this.logger.error("Undo operation failed",t),!1}}redo(){if(this.assertNotDestroyed(),!this.canRedo())return this.logger.debug("Redo operation ignored - no future history available"),!1;try{this.historyIndex++;const e=this.history[this.historyIndex];if(!e)return this.historyIndex--,!1;const s=t.applyPatches(this.state,e.patches);return this.validateState(s),this.setState(s,!1),this.logger.debug("Redo operation completed",{description:e.description,newHistoryIndex:this.historyIndex}),!0}catch(t){return this.logger.error("Redo operation failed",t),this.historyIndex--,!1}}canUndo(){return this.historyIndex>=0}canRedo(){return this.historyIndex<this.history.length-1}getHistoryInfo(){return{canUndo:this.canUndo(),canRedo:this.canRedo(),historyLength:this.history.length,currentIndex:this.historyIndex,lastAction:this.history[this.historyIndex]?.description||null,memoryUsage:n(this.history)}}clearHistory(){this.assertNotDestroyed();const t=this.history.length;this.history=[],this.historyIndex=-1,this.logger.info("History cleared",{previousLength:t})}async forceSave(){if(this.assertNotDestroyed(),this.isDirty)try{this.logger.debug("Force save started"),await this.saveToServer(this.state),this.persistToLocal(),this.isDirty=!1,this.logger.info("Force save completed successfully")}catch(t){throw this.logger.error("Force save failed",t),new r(`Force save failed: ${t instanceof Error?t.message:"Unknown error"}`)}else this.logger.debug("Force save skipped - no changes")}hasUnsavedChanges(){return this.isDirty}setAutoSaveInterval(t){if(this.assertNotDestroyed(),"number"!=typeof t||t<=0)throw new i("Auto-save interval must be a positive number");this.config.autoSaveInterval=t,this.restartAutoSave(),this.logger.info("Auto-save interval updated",{minutes:t})}async loadFromServerManually(){this.assertNotDestroyed();try{this.logger.debug("Manual server load started");const t=await this.loadFromServer();return t?(this.validateState(t),this.state=t,this.isDirty=!1,this.clearHistory(),this.notifyListeners(),this.persistToLocal(),this.logger.info("Manual server load completed successfully"),!0):(this.logger.debug("No server state available"),!1)}catch(t){return this.logger.error("Manual server load failed",t),!1}}destroy(){this.isDestroyed||(this.logger.info("Destroying StateMachine"),this.isDestroyed=!0,this.listeners.clear(),this.autoSaveTimer&&(clearInterval(this.autoSaveTimer),this.autoSaveTimer=null),this.history=[],this.listeners=new Set,this.logger.info("StateMachine destroyed"))}setState(t,e=!0){this.state=t,e&&(this.isDirty=!0),this.debouncedNotify(),this.persistToLocal()}assertNotDestroyed(){if(this.isDestroyed)throw new s("Cannot operate on destroyed StateMachine","DESTROYED")}validateConfig(t){if(!t.initialState||"object"!=typeof t.initialState||null===t.initialState)throw new i("Initial state must be an object");if(void 0!==t.autoSaveInterval&&t.autoSaveInterval<=0)throw new i("Auto-save interval must be positive");if(void 0!==t.maxHistorySize&&t.maxHistorySize<=0)throw new i("Max history size must be positive")}validateCurrentState(){this.validateState(this.state)}validateState(t){if(!this.config.validateState(t))throw new i("State validation failed")}notifyListeners(){if(0===this.listeners.size)return;const t=this.state;let e=0;this.listeners.forEach(s=>{try{s(t)}catch(t){e++,this.logger.error("Listener notification failed",t)}}),e>0&&this.logger.warn(`${e} listener(s) failed during notification`)}saveToHistory(t,e,s){this.historyIndex<this.history.length-1&&(this.history=this.history.slice(0,this.historyIndex+1));const i={patches:t,inversePatches:e,timestamp:Date.now()};s&&(i.description=s),this.history.push(i),this.historyIndex++,this.history.length>this.config.maxHistorySize&&(this.history.shift(),this.historyIndex--,this.logger.debug("History trimmed to max size",{maxSize:this.config.maxHistorySize}))}persistToLocal(){if(this.config.enablePersistence&&this.config.persistenceKey)try{const t={state:this.state,timestamp:Date.now(),version:"1.0.0",checksum:a(this.state)};localStorage.setItem(this.config.persistenceKey,JSON.stringify(t))}catch(t){this.logger.warn("Failed to persist state to localStorage",t)}}loadPersistedState(){if(!this.config.enablePersistence||!this.config.persistenceKey)return null;try{const t=localStorage.getItem(this.config.persistenceKey);if(!t)return null;const e=JSON.parse(t);if(e.checksum){const t=a(e.state);if(e.checksum!==t)return this.logger.warn("Persisted state checksum mismatch, ignoring"),null}return this.logger.debug("Loaded persisted state",{timestamp:e.timestamp,version:e.version}),e.state}catch(t){return this.logger.warn("Failed to load persisted state",t),null}}startAutoSave(){if(!this.config.enableAutoSave)return;const t=60*this.config.autoSaveInterval*1e3;this.autoSaveTimer=setInterval(async()=>{if(this.isDirty&&!this.isDestroyed)try{await this.forceSave()}catch(t){this.logger.error("Auto-save failed",t)}},t),this.logger.debug("Auto-save started",{intervalMinutes:this.config.autoSaveInterval})}restartAutoSave(){this.autoSaveTimer&&clearInterval(this.autoSaveTimer),this.startAutoSave()}},exports.StateMachineError=s,exports.StatePersistenceError=r,exports.StateValidationError=i,exports.createStateMachineHooks=function(t){return{useState:()=>o(t),useSlice:(e,s)=>h(t,e,s),useActions:()=>c(t),useHistory:()=>u(t),usePersistence:()=>l(t),useComplete:()=>d(t),useOptimistic:()=>f(t),useDebounced:e=>p(t,e),useSubscription:(e,s)=>v(t,e,s)}},exports.useDebouncedStateUpdate=p,exports.useOptimisticUpdate=f,exports.useShallowEqual=function(t){const[s,i]=e.useState(t);return e.useEffect(()=>{if("object"==typeof t&&null!==t){const e=Object.keys(s),r=Object.keys(t);if(e.length!==r.length)return void i(t);for(const r of e)if(s[r]!==t[r])return void i(t)}else s!==t&&i(t)},[t,s]),s},exports.useStateActions=c,exports.useStateHistory=u,exports.useStateMachine=o,exports.useStateMachineFull=d,exports.useStatePersist=l,exports.useStateSlice=h,exports.useStateSubscription=v;//# sourceMappingURL=index.js.map