@voidhash/mimic-react 0.0.1-alpha.5 → 0.0.1-alpha.7
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/.turbo/turbo-build.log +3 -3
- package/package.json +10 -4
- package/src/zustand/middleware.ts +1 -1
- package/src/zustand-commander/commander.ts +395 -0
- package/src/zustand-commander/hooks.ts +259 -0
- package/src/zustand-commander/index.ts +139 -0
- package/src/zustand-commander/types.ts +347 -0
- package/tests/zustand-commander/commander.test.ts +774 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @voidhash/mimic-react/zustand-commander
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the zustand-commander package.
|
|
5
|
+
*
|
|
6
|
+
* @since 0.0.1
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Schema } from "effect";
|
|
10
|
+
import type { StoreApi, UseBoundStore } from "zustand";
|
|
11
|
+
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// Schema Types
|
|
14
|
+
// =============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Any Effect Schema type (used for type constraints).
|
|
18
|
+
*/
|
|
19
|
+
export type AnyEffectSchema = Schema.Schema<any, any, any>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Infer the Type from an Effect Schema.
|
|
23
|
+
*/
|
|
24
|
+
export type InferSchemaType<T> = T extends Schema.Schema<infer A, any, any>
|
|
25
|
+
? A
|
|
26
|
+
: never;
|
|
27
|
+
|
|
28
|
+
// =============================================================================
|
|
29
|
+
// Command Symbol & Type Guard
|
|
30
|
+
// =============================================================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Symbol used to identify Command objects at runtime.
|
|
34
|
+
*/
|
|
35
|
+
export const COMMAND_SYMBOL = Symbol.for("zustand-commander/command");
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Symbol used to identify UndoableCommand objects at runtime.
|
|
39
|
+
*/
|
|
40
|
+
export const UNDOABLE_COMMAND_SYMBOL = Symbol.for(
|
|
41
|
+
"zustand-commander/undoable-command"
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// =============================================================================
|
|
45
|
+
// Command Context
|
|
46
|
+
// =============================================================================
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Context provided to command functions.
|
|
50
|
+
* Gives access to store state and dispatch capabilities.
|
|
51
|
+
*/
|
|
52
|
+
export interface CommandContext<TStore> {
|
|
53
|
+
/**
|
|
54
|
+
* Get the current store state.
|
|
55
|
+
*/
|
|
56
|
+
readonly getState: () => TStore;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Set partial store state (for local/browser state updates).
|
|
60
|
+
*/
|
|
61
|
+
readonly setState: (partial: Partial<TStore>) => void;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Dispatch another command.
|
|
65
|
+
* Returns the result of the dispatched command.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* dispatch(otherCommand)({ param: "value" });
|
|
69
|
+
*/
|
|
70
|
+
readonly dispatch: CommandDispatch<TStore>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// =============================================================================
|
|
74
|
+
// Command Function Types
|
|
75
|
+
// =============================================================================
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The function signature for a command handler.
|
|
79
|
+
*/
|
|
80
|
+
export type CommandFn<TStore, TParams, TReturn> = (
|
|
81
|
+
ctx: CommandContext<TStore>,
|
|
82
|
+
params: TParams
|
|
83
|
+
) => TReturn;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The function signature for an undoable command's revert handler.
|
|
87
|
+
* Receives the original params and the result from the forward execution.
|
|
88
|
+
*/
|
|
89
|
+
export type RevertFn<TStore, TParams, TReturn> = (
|
|
90
|
+
ctx: CommandContext<TStore>,
|
|
91
|
+
params: TParams,
|
|
92
|
+
result: TReturn
|
|
93
|
+
) => void;
|
|
94
|
+
|
|
95
|
+
// =============================================================================
|
|
96
|
+
// Command Types
|
|
97
|
+
// =============================================================================
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* A command that can be dispatched to modify store state.
|
|
101
|
+
* Regular commands do not support undo/redo.
|
|
102
|
+
*/
|
|
103
|
+
export interface Command<TStore, TParams, TReturn> {
|
|
104
|
+
readonly [COMMAND_SYMBOL]: true;
|
|
105
|
+
readonly fn: CommandFn<TStore, TParams, TReturn>;
|
|
106
|
+
readonly paramsSchema: AnyEffectSchema | null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* An undoable command that supports undo/redo.
|
|
111
|
+
* Must provide a revert function that knows how to undo the change.
|
|
112
|
+
*/
|
|
113
|
+
export interface UndoableCommand<TStore, TParams, TReturn>
|
|
114
|
+
extends Command<TStore, TParams, TReturn> {
|
|
115
|
+
readonly [UNDOABLE_COMMAND_SYMBOL]: true;
|
|
116
|
+
readonly revert: RevertFn<TStore, TParams, TReturn>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Any command type (regular or undoable).
|
|
121
|
+
*/
|
|
122
|
+
export type AnyCommand = Command<any, any, any>;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Any undoable command type.
|
|
126
|
+
*/
|
|
127
|
+
export type AnyUndoableCommand = UndoableCommand<any, any, any>;
|
|
128
|
+
|
|
129
|
+
// =============================================================================
|
|
130
|
+
// Command Dispatch
|
|
131
|
+
// =============================================================================
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Dispatch function that accepts commands and returns a function to call with params.
|
|
135
|
+
* Returns the result of the command execution.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* const result = dispatch(myCommand)({ param: "value" });
|
|
139
|
+
*/
|
|
140
|
+
export type CommandDispatch<TStore> = <TParams, TReturn>(
|
|
141
|
+
command: Command<TStore, TParams, TReturn>
|
|
142
|
+
) => (params: TParams) => TReturn;
|
|
143
|
+
|
|
144
|
+
// =============================================================================
|
|
145
|
+
// Undo/Redo Stack Types
|
|
146
|
+
// =============================================================================
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* An entry in the undo/redo stack.
|
|
150
|
+
* Contains all information needed to revert or redo a command.
|
|
151
|
+
*/
|
|
152
|
+
export interface UndoEntry<TParams = unknown, TReturn = unknown> {
|
|
153
|
+
/** The undoable command that was executed */
|
|
154
|
+
readonly command: AnyUndoableCommand;
|
|
155
|
+
/** The parameters that were passed to the command */
|
|
156
|
+
readonly params: TParams;
|
|
157
|
+
/** The result returned by the command (passed to revert) */
|
|
158
|
+
readonly result: TReturn;
|
|
159
|
+
/** Timestamp when the command was executed */
|
|
160
|
+
readonly timestamp: number;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* State slice for undo/redo functionality.
|
|
165
|
+
*/
|
|
166
|
+
export interface CommanderSlice {
|
|
167
|
+
readonly _commander: {
|
|
168
|
+
/** Stack of commands that can be undone */
|
|
169
|
+
readonly undoStack: ReadonlyArray<UndoEntry>;
|
|
170
|
+
/** Stack of commands that can be redone */
|
|
171
|
+
readonly redoStack: ReadonlyArray<UndoEntry>;
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// =============================================================================
|
|
176
|
+
// Commander Types
|
|
177
|
+
// =============================================================================
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Options for creating a commander.
|
|
181
|
+
*/
|
|
182
|
+
export interface CommanderOptions {
|
|
183
|
+
/**
|
|
184
|
+
* Maximum number of undo entries to keep.
|
|
185
|
+
* @default 100
|
|
186
|
+
*/
|
|
187
|
+
readonly maxUndoStackSize?: number;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* A commander instance bound to a specific store type.
|
|
192
|
+
* Used to create commands and the middleware.
|
|
193
|
+
*/
|
|
194
|
+
export interface Commander<TStore> {
|
|
195
|
+
/**
|
|
196
|
+
* Create a regular command (no undo support).
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* // With params schema
|
|
200
|
+
* const addItem = commander.action(
|
|
201
|
+
* Schema.Struct({ name: Schema.String }),
|
|
202
|
+
* (ctx, params) => {
|
|
203
|
+
* // modify state
|
|
204
|
+
* }
|
|
205
|
+
* );
|
|
206
|
+
*
|
|
207
|
+
* // Without params
|
|
208
|
+
* const clearAll = commander.action((ctx) => {
|
|
209
|
+
* // modify state
|
|
210
|
+
* });
|
|
211
|
+
*/
|
|
212
|
+
readonly action: {
|
|
213
|
+
// With params schema
|
|
214
|
+
<TParamsSchema extends AnyEffectSchema, TReturn = void>(
|
|
215
|
+
paramsSchema: TParamsSchema,
|
|
216
|
+
fn: CommandFn<TStore, InferSchemaType<TParamsSchema>, TReturn>
|
|
217
|
+
): Command<TStore, InferSchemaType<TParamsSchema>, TReturn>;
|
|
218
|
+
|
|
219
|
+
// Without params (void)
|
|
220
|
+
<TReturn = void>(
|
|
221
|
+
fn: CommandFn<TStore, void, TReturn>
|
|
222
|
+
): Command<TStore, void, TReturn>;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Create an undoable command with undo/redo support.
|
|
227
|
+
* The revert function is called when undoing the command.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* const moveItem = commander.undoableAction(
|
|
231
|
+
* Schema.Struct({ id: Schema.String, toIndex: Schema.Number }),
|
|
232
|
+
* (ctx, params) => {
|
|
233
|
+
* const fromIndex = // get current index
|
|
234
|
+
* // perform move
|
|
235
|
+
* return { fromIndex }; // return data needed for revert
|
|
236
|
+
* },
|
|
237
|
+
* (ctx, params, result) => {
|
|
238
|
+
* // revert: move back to original position
|
|
239
|
+
* ctx.dispatch(moveItem)({ id: params.id, toIndex: result.fromIndex });
|
|
240
|
+
* }
|
|
241
|
+
* );
|
|
242
|
+
*/
|
|
243
|
+
readonly undoableAction: {
|
|
244
|
+
// With params schema
|
|
245
|
+
<TParamsSchema extends AnyEffectSchema, TReturn>(
|
|
246
|
+
paramsSchema: TParamsSchema,
|
|
247
|
+
fn: CommandFn<TStore, InferSchemaType<TParamsSchema>, TReturn>,
|
|
248
|
+
revert: RevertFn<TStore, InferSchemaType<TParamsSchema>, TReturn>
|
|
249
|
+
): UndoableCommand<TStore, InferSchemaType<TParamsSchema>, TReturn>;
|
|
250
|
+
|
|
251
|
+
// Without params (void)
|
|
252
|
+
<TReturn>(
|
|
253
|
+
fn: CommandFn<TStore, void, TReturn>,
|
|
254
|
+
revert: RevertFn<TStore, void, TReturn>
|
|
255
|
+
): UndoableCommand<TStore, void, TReturn>;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Zustand middleware that adds commander functionality.
|
|
260
|
+
* Adds undo/redo stacks to the store state.
|
|
261
|
+
*/
|
|
262
|
+
readonly middleware: CommanderMiddleware<TStore>;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Type for the commander middleware.
|
|
267
|
+
* Note: TStore is intentionally unused here to match the Commander interface signature.
|
|
268
|
+
* The middleware is generic over T (the inner store type) and adds CommanderSlice.
|
|
269
|
+
*/
|
|
270
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
271
|
+
export type CommanderMiddleware<_TStore> = <T extends object>(
|
|
272
|
+
config: (
|
|
273
|
+
set: StoreApi<T & CommanderSlice>["setState"],
|
|
274
|
+
get: StoreApi<T & CommanderSlice>["getState"],
|
|
275
|
+
api: StoreApi<T & CommanderSlice>
|
|
276
|
+
) => T
|
|
277
|
+
) => (
|
|
278
|
+
set: StoreApi<T & CommanderSlice>["setState"],
|
|
279
|
+
get: StoreApi<T & CommanderSlice>["getState"],
|
|
280
|
+
api: StoreApi<T & CommanderSlice>
|
|
281
|
+
) => T & CommanderSlice;
|
|
282
|
+
|
|
283
|
+
// =============================================================================
|
|
284
|
+
// Type Helpers
|
|
285
|
+
// =============================================================================
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Extract the params type from a command.
|
|
289
|
+
*/
|
|
290
|
+
export type CommandParams<T> = T extends Command<any, infer P, any> ? P : never;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Extract the return type from a command.
|
|
294
|
+
*/
|
|
295
|
+
export type CommandReturn<T> = T extends Command<any, any, infer R>
|
|
296
|
+
? R
|
|
297
|
+
: undefined;
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Extract the store type from a command.
|
|
301
|
+
*/
|
|
302
|
+
export type CommandStore<T> = T extends Command<infer S, any, any> ? S : never;
|
|
303
|
+
|
|
304
|
+
// =============================================================================
|
|
305
|
+
// Type Guards
|
|
306
|
+
// =============================================================================
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Type guard to check if a value is a Command.
|
|
310
|
+
*/
|
|
311
|
+
export function isCommand(value: unknown): value is AnyCommand {
|
|
312
|
+
return (
|
|
313
|
+
typeof value === "object" &&
|
|
314
|
+
value !== null &&
|
|
315
|
+
COMMAND_SYMBOL in value &&
|
|
316
|
+
value[COMMAND_SYMBOL] === true
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Type guard to check if a command is undoable.
|
|
322
|
+
*/
|
|
323
|
+
export function isUndoableCommand(
|
|
324
|
+
value: unknown
|
|
325
|
+
): value is AnyUndoableCommand {
|
|
326
|
+
return (
|
|
327
|
+
isCommand(value) &&
|
|
328
|
+
UNDOABLE_COMMAND_SYMBOL in value &&
|
|
329
|
+
value[UNDOABLE_COMMAND_SYMBOL] === true
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// =============================================================================
|
|
334
|
+
// Store Type Helper
|
|
335
|
+
// =============================================================================
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Helper type to extract the state type from a zustand store.
|
|
339
|
+
*/
|
|
340
|
+
export type ExtractState<TStore> = TStore extends UseBoundStore<
|
|
341
|
+
StoreApi<infer S>
|
|
342
|
+
>
|
|
343
|
+
? S
|
|
344
|
+
: TStore extends StoreApi<infer S>
|
|
345
|
+
? S
|
|
346
|
+
: never;
|
|
347
|
+
|