@pithos/core 2.3.0 → 2.4.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.
Files changed (99) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/autocompletion.d.ts +129 -1
  3. package/dist/eidos/abstract-factory/abstract-factory.d.ts +125 -0
  4. package/dist/eidos/abstract-factory/abstract-factory.d.ts.map +1 -0
  5. package/dist/eidos/abstract-factory/abstract-factory.js +128 -0
  6. package/dist/eidos/abstract-factory/abstract-factory.js.map +1 -0
  7. package/dist/eidos/adapter/adapter.d.ts +97 -0
  8. package/dist/eidos/adapter/adapter.d.ts.map +1 -0
  9. package/dist/eidos/adapter/adapter.js +90 -0
  10. package/dist/eidos/adapter/adapter.js.map +1 -0
  11. package/dist/eidos/bridge/bridge.d.ts +81 -0
  12. package/dist/eidos/bridge/bridge.d.ts.map +1 -0
  13. package/dist/eidos/bridge/bridge.js +75 -0
  14. package/dist/eidos/bridge/bridge.js.map +1 -0
  15. package/dist/eidos/builder/builder.d.ts +181 -0
  16. package/dist/eidos/builder/builder.d.ts.map +1 -0
  17. package/dist/eidos/builder/builder.js +139 -0
  18. package/dist/eidos/builder/builder.js.map +1 -0
  19. package/dist/eidos/chain/chain.d.ts +99 -0
  20. package/dist/eidos/chain/chain.d.ts.map +1 -0
  21. package/dist/eidos/chain/chain.js +111 -0
  22. package/dist/eidos/chain/chain.js.map +1 -0
  23. package/dist/eidos/command/command.d.ts +267 -0
  24. package/dist/eidos/command/command.d.ts.map +1 -0
  25. package/dist/eidos/command/command.js +298 -0
  26. package/dist/eidos/command/command.js.map +1 -0
  27. package/dist/eidos/composite/composite.d.ts +168 -0
  28. package/dist/eidos/composite/composite.d.ts.map +1 -0
  29. package/dist/eidos/composite/composite.js +157 -0
  30. package/dist/eidos/composite/composite.js.map +1 -0
  31. package/dist/eidos/decorator/decorator.d.ts +138 -0
  32. package/dist/eidos/decorator/decorator.d.ts.map +1 -0
  33. package/dist/eidos/decorator/decorator.js +143 -0
  34. package/dist/eidos/decorator/decorator.js.map +1 -0
  35. package/dist/eidos/facade/facade.d.ts +61 -0
  36. package/dist/eidos/facade/facade.d.ts.map +1 -0
  37. package/dist/eidos/facade/facade.js +63 -0
  38. package/dist/eidos/facade/facade.js.map +1 -0
  39. package/dist/eidos/factory-method/factory-method.d.ts +76 -0
  40. package/dist/eidos/factory-method/factory-method.d.ts.map +1 -0
  41. package/dist/eidos/factory-method/factory-method.js +60 -0
  42. package/dist/eidos/factory-method/factory-method.js.map +1 -0
  43. package/dist/eidos/flyweight/flyweight.d.ts +40 -0
  44. package/dist/eidos/flyweight/flyweight.d.ts.map +1 -0
  45. package/dist/eidos/flyweight/flyweight.js +41 -0
  46. package/dist/eidos/flyweight/flyweight.js.map +1 -0
  47. package/dist/eidos/interpreter/interpreter.d.ts +82 -0
  48. package/dist/eidos/interpreter/interpreter.d.ts.map +1 -0
  49. package/dist/eidos/interpreter/interpreter.js +84 -0
  50. package/dist/eidos/interpreter/interpreter.js.map +1 -0
  51. package/dist/eidos/iterator/iterator.d.ts +164 -0
  52. package/dist/eidos/iterator/iterator.d.ts.map +1 -0
  53. package/dist/eidos/iterator/iterator.js +258 -0
  54. package/dist/eidos/iterator/iterator.js.map +1 -0
  55. package/dist/eidos/mediator/mediator.d.ts +102 -0
  56. package/dist/eidos/mediator/mediator.d.ts.map +1 -0
  57. package/dist/eidos/mediator/mediator.js +112 -0
  58. package/dist/eidos/mediator/mediator.js.map +1 -0
  59. package/dist/eidos/memento/memento.d.ts +103 -0
  60. package/dist/eidos/memento/memento.d.ts.map +1 -0
  61. package/dist/eidos/memento/memento.js +114 -0
  62. package/dist/eidos/memento/memento.js.map +1 -0
  63. package/dist/eidos/observer/observer.d.ts +96 -0
  64. package/dist/eidos/observer/observer.d.ts.map +1 -0
  65. package/dist/eidos/observer/observer.js +117 -0
  66. package/dist/eidos/observer/observer.js.map +1 -0
  67. package/dist/eidos/prototype/prototype.d.ts +32 -0
  68. package/dist/eidos/prototype/prototype.d.ts.map +1 -0
  69. package/dist/eidos/prototype/prototype.js +33 -0
  70. package/dist/eidos/prototype/prototype.js.map +1 -0
  71. package/dist/eidos/proxy/proxy.d.ts +108 -0
  72. package/dist/eidos/proxy/proxy.d.ts.map +1 -0
  73. package/dist/eidos/proxy/proxy.js +121 -0
  74. package/dist/eidos/proxy/proxy.js.map +1 -0
  75. package/dist/eidos/singleton/singleton.d.ts +76 -0
  76. package/dist/eidos/singleton/singleton.d.ts.map +1 -0
  77. package/dist/eidos/singleton/singleton.js +77 -0
  78. package/dist/eidos/singleton/singleton.js.map +1 -0
  79. package/dist/eidos/state/state.d.ts +152 -0
  80. package/dist/eidos/state/state.d.ts.map +1 -0
  81. package/dist/eidos/state/state.js +85 -0
  82. package/dist/eidos/state/state.js.map +1 -0
  83. package/dist/eidos/strategy/strategy.d.ts +148 -0
  84. package/dist/eidos/strategy/strategy.d.ts.map +1 -0
  85. package/dist/eidos/strategy/strategy.js +167 -0
  86. package/dist/eidos/strategy/strategy.js.map +1 -0
  87. package/dist/eidos/template/template.d.ts +95 -0
  88. package/dist/eidos/template/template.d.ts.map +1 -0
  89. package/dist/eidos/template/template.js +110 -0
  90. package/dist/eidos/template/template.js.map +1 -0
  91. package/dist/eidos/visitor/visitor.d.ts +78 -0
  92. package/dist/eidos/visitor/visitor.d.ts.map +1 -0
  93. package/dist/eidos/visitor/visitor.js +80 -0
  94. package/dist/eidos/visitor/visitor.js.map +1 -0
  95. package/dist/zygos/result/index.d.ts +19 -0
  96. package/dist/zygos/result/index.d.ts.map +1 -0
  97. package/dist/zygos/result/index.js +29 -0
  98. package/dist/zygos/result/index.js.map +1 -0
  99. package/package.json +28 -3
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Functional Chain of Responsibility Pattern.
3
+ *
4
+ * In OOP, Chain of Responsibility requires a Handler interface, a BaseHandler
5
+ * class with a `next` reference, and concrete handler subclasses linked together.
6
+ * In functional TypeScript, a handler is a function that either returns a result
7
+ * or delegates to the next handler. The chain is built by composing handlers.
8
+ *
9
+ * @module eidos/chain
10
+ * @since 2.4.0
11
+ *
12
+ * @see {@link https://pithos.dev/api/eidos/chain/ | Explanations, examples and live demo}
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { createChain, type Handler } from "@pithos/core/eidos/chain/chain";
17
+ *
18
+ * const auth: Handler<Request, Response> = (req, next) =>
19
+ * req.token ? next(req) : { status: 401 };
20
+ *
21
+ * const validate: Handler<Request, Response> = (req, next) =>
22
+ * req.body ? next(req) : { status: 400 };
23
+ *
24
+ * const handle: Handler<Request, Response> = (req) =>
25
+ * { status: 200, data: process(req) };
26
+ *
27
+ * const pipeline = createChain(auth, validate, handle);
28
+ * pipeline({ token: "abc", body: "data" }); // { status: 200, ... }
29
+ * ```
30
+ */
31
+ import { ok, err } from "../../zygos/result/result.js";
32
+ /**
33
+ * Creates a chain from an ordered list of handlers.
34
+ * Each handler can inspect the input and either:
35
+ * - return a result (short-circuit)
36
+ * - call `next(input)` to pass to the next handler
37
+ *
38
+ * The last handler's `next` throws if called, since there is
39
+ * nothing after it. The last handler should always produce a result.
40
+ *
41
+ * @template In - The request/input type
42
+ * @template Out - The response/output type
43
+ * @param handlers - Ordered list of handlers (first = outermost)
44
+ * @returns A function that runs the chain
45
+ * @since 2.4.0
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * const log: Handler<number, string> = (n, next) => {
50
+ * console.log(`processing ${n}`);
51
+ * return next(n);
52
+ * };
53
+ *
54
+ * const double: Handler<number, string> = (n) => String(n * 2);
55
+ *
56
+ * const chain = createChain(log, double);
57
+ * chain(21); // logs "processing 21", returns "42"
58
+ * ```
59
+ */
60
+ export function createChain(...handlers) {
61
+ if (handlers.length === 0) {
62
+ throw new Error("createChain requires at least one handler");
63
+ }
64
+ return (input) => {
65
+ let index = 0;
66
+ const next = (inp) => {
67
+ if (index >= handlers.length) {
68
+ throw new Error("End of chain reached: last handler must not call next()");
69
+ }
70
+ const handler = handlers[index++];
71
+ return handler(inp, next);
72
+ };
73
+ return next(input);
74
+ };
75
+ }
76
+ /**
77
+ * Creates a safe chain that catches errors and returns a zygos `Result`.
78
+ * Behaves like `createChain`, but wraps the execution in a try/catch.
79
+ *
80
+ * @deprecated Use `Result.fromThrowable` from `@zygos/result/result` instead.
81
+ *
82
+ * ```ts
83
+ * import { Result } from "../../zygos/result/result.js";
84
+ *
85
+ * const chain = createChain(auth, validate, handle);
86
+ * const safeRun = Result.fromThrowable(
87
+ * (input: Request) => chain(input),
88
+ * (e) => e instanceof Error ? e : new Error(String(e)),
89
+ * );
90
+ * const result = safeRun(request);
91
+ * ```
92
+ *
93
+ * @see {@link https://pithos.dev/api/eidos/chain/ | Full explanation, examples and live demo}
94
+ * @template In - The request/input type
95
+ * @template Out - The response/output type
96
+ * @param handlers - Ordered list of handlers
97
+ * @returns A function returning `Result<Out, Error>`
98
+ * @since 2.4.0
99
+ */
100
+ export function safeChain(...handlers) {
101
+ const chain = createChain(...handlers);
102
+ return (input) => {
103
+ try {
104
+ return ok(chain(input));
105
+ }
106
+ catch (error) {
107
+ return err(error instanceof Error ? error : new Error(String(error)));
108
+ }
109
+ };
110
+ }
111
+ //# sourceMappingURL=chain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chain.js","sourceRoot":"","sources":["../../../src/eidos/chain/chain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAiB/C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,WAAW,CACzB,GAAG,QAA4B;IAE/B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IAED,OAAO,CAAC,KAAS,EAAO,EAAE;QACxB,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,MAAM,IAAI,GAAG,CAAC,GAAO,EAAO,EAAE;YAC5B,IAAI,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;YAC7E,CAAC;YACD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;YAClC,OAAO,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC;QAEF,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,SAAS,CACvB,GAAG,QAA4B;IAE/B,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,QAAQ,CAAC,CAAC;IACvC,OAAO,CAAC,KAAS,EAAE,EAAE;QACnB,IAAI,CAAC;YACH,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACxE,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Functional Command Pattern.
3
+ *
4
+ * In OOP, the Command pattern requires a Command interface, concrete command
5
+ * classes, a Receiver, and an Invoker - all to encapsulate an action as an object.
6
+ * In functional TypeScript, a command is a thunk (zero-arg function).
7
+ * The real value is in undo/redo, which becomes a pair of closures.
8
+ *
9
+ * ## Two flavors
10
+ *
11
+ * - `createCommandStack`: imperative. Commands are thunks that mutate external state.
12
+ * Works naturally with Vue, Angular, Svelte, or any mutable-reactive system.
13
+ *
14
+ * - `createReactiveCommandStack`: declarative. Commands are pure `(state) => state`
15
+ * transforms. The stack manages state and notifies via `onChange`.
16
+ * Works naturally with React or any immutable-state system.
17
+ *
18
+ * ## Command vs Memento for undo/redo
19
+ *
20
+ * - **Command** (`createCommandStack` / `createReactiveCommandStack`):
21
+ * stores *actions* with their inverses.
22
+ * Undo calls the command's undo function. Use when you have reversible operations.
23
+ *
24
+ * - **Memento** (`createHistory`): stores *state snapshots*.
25
+ * Undo restores the previous state. Use when state is cheap to copy.
26
+ *
27
+ * @module eidos/command
28
+ * @since 2.4.0
29
+ *
30
+ * @see {@link https://pithos.dev/api/eidos/command/ | Explanations, examples and live demo}
31
+ *
32
+ * @example Imperative (side effects, external state)
33
+ * ```ts
34
+ * import { undoable, createCommandStack } from "@pithos/core/eidos/command/command";
35
+ *
36
+ * let counter = 0;
37
+ * const increment = undoable(() => counter++, () => counter--);
38
+ *
39
+ * const stack = createCommandStack();
40
+ * stack.execute(increment); // counter = 1
41
+ * stack.execute(increment); // counter = 2
42
+ * stack.undo(); // counter = 1
43
+ * stack.redo(); // counter = 2
44
+ * ```
45
+ *
46
+ * @example Reactive (pure transforms, framework-friendly)
47
+ * ```ts
48
+ * import { undoableState, createReactiveCommandStack } from "@pithos/core/eidos/command/command";
49
+ *
50
+ * interface Counter { value: number }
51
+ *
52
+ * const increment = undoableState<Counter>(
53
+ * (s) => ({ ...s, value: s.value + 1 }),
54
+ * (s) => ({ ...s, value: s.value - 1 }),
55
+ * );
56
+ *
57
+ * const stack = createReactiveCommandStack({
58
+ * initial: { value: 0 },
59
+ * onChange: (state) => console.log(state), // or setBoard, or ref.value = state
60
+ * });
61
+ *
62
+ * stack.execute(increment); // onChange({ value: 1 })
63
+ * stack.execute(increment); // onChange({ value: 2 })
64
+ * stack.undo(); // onChange({ value: 1 })
65
+ * stack.redo(); // onChange({ value: 2 })
66
+ * ```
67
+ */
68
+ import type { Result as ResultType } from "../../zygos/result/result.js";
69
+ /**
70
+ * A Command is a thunk - a deferred action with no arguments.
71
+ * In FP, this IS the pattern: a function is already a reifiable value.
72
+ *
73
+ * @since 2.4.0
74
+ */
75
+ export type Command = () => void;
76
+ /**
77
+ * An UndoableCommand pairs an execute action with its inverse.
78
+ * Replaces the GoF Command interface + ConcreteCommand classes.
79
+ *
80
+ * @since 2.4.0
81
+ */
82
+ export interface UndoableCommand {
83
+ readonly execute: Command;
84
+ readonly undo: Command;
85
+ }
86
+ /**
87
+ * Creates an undoable command from an execute/undo pair.
88
+ *
89
+ * @param execute - The action to perform
90
+ * @param undo - The action that reverses execute
91
+ * @returns An UndoableCommand
92
+ * @since 2.4.0
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * const items: string[] = [];
97
+ *
98
+ * const addItem = (item: string) => undoable(
99
+ * () => items.push(item),
100
+ * () => items.pop(),
101
+ * );
102
+ *
103
+ * const cmd = addItem("hello");
104
+ * cmd.execute(); // items = ["hello"]
105
+ * cmd.undo(); // items = []
106
+ * ```
107
+ */
108
+ export declare function undoable(execute: Command, undo: Command): UndoableCommand;
109
+ /**
110
+ * Creates a command stack with undo/redo support.
111
+ *
112
+ * This stores *actions* (execute/undo pairs). When you undo, it calls the
113
+ * command's undo function. Use this when you have reversible operations.
114
+ *
115
+ * For storing *states* (snapshots) instead of actions, see the Memento pattern's
116
+ * `createHistory` which stores state snapshots and restores them on undo.
117
+ *
118
+ * @returns Stack with `execute`, `undo`, `redo`, `canUndo`, `canRedo`, `clear`
119
+ * @since 2.4.0
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * let value = 0;
124
+ * const stack = createCommandStack();
125
+ *
126
+ * stack.execute(undoable(() => value += 10, () => value -= 10));
127
+ * stack.execute(undoable(() => value *= 2, () => value /= 2));
128
+ * // value = 20
129
+ *
130
+ * stack.undo(); // value = 10
131
+ * stack.undo(); // value = 0
132
+ * stack.redo(); // value = 10
133
+ * ```
134
+ */
135
+ export declare function createCommandStack(): {
136
+ /** Execute a command and push it to the undo stack. Clears redo stack. */
137
+ execute: (cmd: UndoableCommand) => void;
138
+ /**
139
+ * Undo the last command. Returns false if nothing to undo.
140
+ * Check `canUndo` first if the result matters to your logic.
141
+ */
142
+ undo: () => boolean;
143
+ /**
144
+ * Redo the last undone command. Returns false if nothing to redo.
145
+ * Check `canRedo` first if the result matters to your logic.
146
+ */
147
+ redo: () => boolean;
148
+ readonly canUndo: boolean;
149
+ readonly canRedo: boolean;
150
+ /** Clear all history. */
151
+ clear: () => void;
152
+ };
153
+ /**
154
+ * Executes a command safely, catching any error and returning
155
+ * a zygos `Result` instead of throwing.
156
+ *
157
+ * @deprecated Use `Result.fromThrowable` from `@zygos/result/result` instead.
158
+ *
159
+ * ```ts
160
+ * import { Result } from "../../zygos/result/result.js";
161
+ *
162
+ * const safeCmd = Result.fromThrowable(riskyCommand, (e) =>
163
+ * e instanceof Error ? e : new Error(String(e)),
164
+ * );
165
+ * const result = safeCmd();
166
+ * ```
167
+ *
168
+ * @see {@link https://pithos.dev/api/eidos/command/ | Full explanation, examples and live demo}
169
+ * @param cmd - The command to execute
170
+ * @returns `Ok(void)` on success, `Err(Error)` on failure
171
+ * @since 2.4.0
172
+ */
173
+ export declare function safeExecute(cmd: Command): ResultType<void, Error>;
174
+ /**
175
+ * A state command is a pure function: takes current state, returns next state.
176
+ * No mutation, no side effects. The stack manages the state for you.
177
+ *
178
+ * @since 2.5.0
179
+ */
180
+ export type StateCommand<S> = (state: S) => S;
181
+ /**
182
+ * An undoable state command pairs a forward transform with its inverse.
183
+ * Both are pure functions from state to state.
184
+ *
185
+ * @since 2.5.0
186
+ */
187
+ export interface UndoableStateCommand<S> {
188
+ readonly execute: StateCommand<S>;
189
+ readonly undo: StateCommand<S>;
190
+ }
191
+ /**
192
+ * Creates an undoable state command from an execute/undo pair.
193
+ * Unlike `undoable` (imperative thunks), these are pure state transforms.
194
+ *
195
+ * @param execute - Pure function that produces the next state
196
+ * @param undo - Pure function that reverses execute
197
+ * @returns An UndoableStateCommand
198
+ * @since 2.5.0
199
+ *
200
+ * @example
201
+ * ```ts
202
+ * interface Counter { value: number }
203
+ *
204
+ * const increment = undoableState<Counter>(
205
+ * (s) => ({ ...s, value: s.value + 1 }),
206
+ * (s) => ({ ...s, value: s.value - 1 }),
207
+ * );
208
+ * ```
209
+ */
210
+ export declare function undoableState<S>(execute: StateCommand<S>, undo: StateCommand<S>): UndoableStateCommand<S>;
211
+ /**
212
+ * Options for `createReactiveCommandStack`.
213
+ *
214
+ * @since 2.5.0
215
+ */
216
+ export interface ReactiveCommandStackOptions<S> {
217
+ /** The initial state. */
218
+ readonly initial: S;
219
+ /** Called after every state change (execute, undo, redo, clear). */
220
+ readonly onChange: (state: S) => void;
221
+ }
222
+ /**
223
+ * Creates a reactive command stack with undo/redo support.
224
+ *
225
+ * Unlike `createCommandStack` (imperative, mutation-based), this variant
226
+ * manages state internally and notifies via `onChange` on every transition.
227
+ * Commands are pure functions from state to state, making it naturally
228
+ * compatible with React, Vue, Angular, or any reactive framework.
229
+ *
230
+ * @param options - Initial state and onChange callback
231
+ * @returns Stack with `execute`, `undo`, `redo`, `canUndo`, `canRedo`, `clear`, `state`
232
+ * @since 2.5.0
233
+ *
234
+ * @example
235
+ * ```ts
236
+ * // React
237
+ * const [board, setBoard] = useState(INITIAL);
238
+ * const stack = useRef(createReactiveCommandStack({
239
+ * initial: INITIAL,
240
+ * onChange: setBoard,
241
+ * }));
242
+ *
243
+ * stack.current.execute(undoableState(
244
+ * (b) => moveTask(b, "todo", "done", task),
245
+ * (b) => moveTask(b, "done", "todo", task),
246
+ * ));
247
+ * // setBoard is called automatically with the new state
248
+ *
249
+ * stack.current.undo(); // setBoard called with previous state
250
+ * stack.current.redo(); // setBoard called with next state
251
+ * ```
252
+ */
253
+ export declare function createReactiveCommandStack<S>(options: ReactiveCommandStackOptions<S>): {
254
+ /** Execute a state command. Clears redo stack. */
255
+ execute: (cmd: UndoableStateCommand<S>) => void;
256
+ /** Undo the last command. Returns false if nothing to undo. */
257
+ undo: () => boolean;
258
+ /** Redo the last undone command. Returns false if nothing to redo. */
259
+ redo: () => boolean;
260
+ /** Current state. */
261
+ readonly state: S;
262
+ readonly canUndo: boolean;
263
+ readonly canRedo: boolean;
264
+ /** Reset to initial state and clear all history. */
265
+ clear: () => void;
266
+ };
267
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../src/eidos/command/command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAGH,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,eAAe,CAEzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB;IAK9B,0EAA0E;mBAC3D,eAAe,KAAG,IAAI;IAMrC;;;OAGG;gBACO,OAAO;IAQjB;;;OAGG;gBACO,OAAO;sBAQF,OAAO;sBAIP,OAAO;IAItB,yBAAyB;iBACd,IAAI;EAKlB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAKjE;AAID;;;;;GAKG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB,CAAC,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EACxB,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,GACpB,oBAAoB,CAAC,CAAC,CAAC,CAEzB;AAED;;;;GAIG;AACH,MAAM,WAAW,2BAA2B,CAAC,CAAC;IAC5C,yBAAyB;IACzB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,EAC1C,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC;IAWrC,kDAAkD;mBACnC,oBAAoB,CAAC,CAAC,CAAC,KAAG,IAAI;IAO7C,+DAA+D;gBACrD,OAAO;IASjB,sEAAsE;gBAC5D,OAAO;IASjB,qBAAqB;oBACR,CAAC;sBAIC,OAAO;sBAIP,OAAO;IAItB,oDAAoD;iBACzC,IAAI;EAOlB"}
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Functional Command Pattern.
3
+ *
4
+ * In OOP, the Command pattern requires a Command interface, concrete command
5
+ * classes, a Receiver, and an Invoker - all to encapsulate an action as an object.
6
+ * In functional TypeScript, a command is a thunk (zero-arg function).
7
+ * The real value is in undo/redo, which becomes a pair of closures.
8
+ *
9
+ * ## Two flavors
10
+ *
11
+ * - `createCommandStack`: imperative. Commands are thunks that mutate external state.
12
+ * Works naturally with Vue, Angular, Svelte, or any mutable-reactive system.
13
+ *
14
+ * - `createReactiveCommandStack`: declarative. Commands are pure `(state) => state`
15
+ * transforms. The stack manages state and notifies via `onChange`.
16
+ * Works naturally with React or any immutable-state system.
17
+ *
18
+ * ## Command vs Memento for undo/redo
19
+ *
20
+ * - **Command** (`createCommandStack` / `createReactiveCommandStack`):
21
+ * stores *actions* with their inverses.
22
+ * Undo calls the command's undo function. Use when you have reversible operations.
23
+ *
24
+ * - **Memento** (`createHistory`): stores *state snapshots*.
25
+ * Undo restores the previous state. Use when state is cheap to copy.
26
+ *
27
+ * @module eidos/command
28
+ * @since 2.4.0
29
+ *
30
+ * @see {@link https://pithos.dev/api/eidos/command/ | Explanations, examples and live demo}
31
+ *
32
+ * @example Imperative (side effects, external state)
33
+ * ```ts
34
+ * import { undoable, createCommandStack } from "@pithos/core/eidos/command/command";
35
+ *
36
+ * let counter = 0;
37
+ * const increment = undoable(() => counter++, () => counter--);
38
+ *
39
+ * const stack = createCommandStack();
40
+ * stack.execute(increment); // counter = 1
41
+ * stack.execute(increment); // counter = 2
42
+ * stack.undo(); // counter = 1
43
+ * stack.redo(); // counter = 2
44
+ * ```
45
+ *
46
+ * @example Reactive (pure transforms, framework-friendly)
47
+ * ```ts
48
+ * import { undoableState, createReactiveCommandStack } from "@pithos/core/eidos/command/command";
49
+ *
50
+ * interface Counter { value: number }
51
+ *
52
+ * const increment = undoableState<Counter>(
53
+ * (s) => ({ ...s, value: s.value + 1 }),
54
+ * (s) => ({ ...s, value: s.value - 1 }),
55
+ * );
56
+ *
57
+ * const stack = createReactiveCommandStack({
58
+ * initial: { value: 0 },
59
+ * onChange: (state) => console.log(state), // or setBoard, or ref.value = state
60
+ * });
61
+ *
62
+ * stack.execute(increment); // onChange({ value: 1 })
63
+ * stack.execute(increment); // onChange({ value: 2 })
64
+ * stack.undo(); // onChange({ value: 1 })
65
+ * stack.redo(); // onChange({ value: 2 })
66
+ * ```
67
+ */
68
+ import { Result } from "../../zygos/result/result.js";
69
+ /**
70
+ * Creates an undoable command from an execute/undo pair.
71
+ *
72
+ * @param execute - The action to perform
73
+ * @param undo - The action that reverses execute
74
+ * @returns An UndoableCommand
75
+ * @since 2.4.0
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * const items: string[] = [];
80
+ *
81
+ * const addItem = (item: string) => undoable(
82
+ * () => items.push(item),
83
+ * () => items.pop(),
84
+ * );
85
+ *
86
+ * const cmd = addItem("hello");
87
+ * cmd.execute(); // items = ["hello"]
88
+ * cmd.undo(); // items = []
89
+ * ```
90
+ */
91
+ export function undoable(execute, undo) {
92
+ return { execute, undo };
93
+ }
94
+ /**
95
+ * Creates a command stack with undo/redo support.
96
+ *
97
+ * This stores *actions* (execute/undo pairs). When you undo, it calls the
98
+ * command's undo function. Use this when you have reversible operations.
99
+ *
100
+ * For storing *states* (snapshots) instead of actions, see the Memento pattern's
101
+ * `createHistory` which stores state snapshots and restores them on undo.
102
+ *
103
+ * @returns Stack with `execute`, `undo`, `redo`, `canUndo`, `canRedo`, `clear`
104
+ * @since 2.4.0
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * let value = 0;
109
+ * const stack = createCommandStack();
110
+ *
111
+ * stack.execute(undoable(() => value += 10, () => value -= 10));
112
+ * stack.execute(undoable(() => value *= 2, () => value /= 2));
113
+ * // value = 20
114
+ *
115
+ * stack.undo(); // value = 10
116
+ * stack.undo(); // value = 0
117
+ * stack.redo(); // value = 10
118
+ * ```
119
+ */
120
+ export function createCommandStack() {
121
+ const past = [];
122
+ const future = [];
123
+ return {
124
+ /** Execute a command and push it to the undo stack. Clears redo stack. */
125
+ execute: (cmd) => {
126
+ cmd.execute();
127
+ past.push(cmd);
128
+ future.length = 0;
129
+ },
130
+ /**
131
+ * Undo the last command. Returns false if nothing to undo.
132
+ * Check `canUndo` first if the result matters to your logic.
133
+ */
134
+ undo: () => {
135
+ const cmd = past.pop();
136
+ if (!cmd)
137
+ return false;
138
+ cmd.undo();
139
+ future.push(cmd);
140
+ return true;
141
+ },
142
+ /**
143
+ * Redo the last undone command. Returns false if nothing to redo.
144
+ * Check `canRedo` first if the result matters to your logic.
145
+ */
146
+ redo: () => {
147
+ const cmd = future.pop();
148
+ if (!cmd)
149
+ return false;
150
+ cmd.execute();
151
+ past.push(cmd);
152
+ return true;
153
+ },
154
+ get canUndo() {
155
+ return past.length > 0;
156
+ },
157
+ get canRedo() {
158
+ return future.length > 0;
159
+ },
160
+ /** Clear all history. */
161
+ clear: () => {
162
+ past.length = 0;
163
+ future.length = 0;
164
+ },
165
+ };
166
+ }
167
+ /**
168
+ * Executes a command safely, catching any error and returning
169
+ * a zygos `Result` instead of throwing.
170
+ *
171
+ * @deprecated Use `Result.fromThrowable` from `@zygos/result/result` instead.
172
+ *
173
+ * ```ts
174
+ * import { Result } from "../../zygos/result/result.js";
175
+ *
176
+ * const safeCmd = Result.fromThrowable(riskyCommand, (e) =>
177
+ * e instanceof Error ? e : new Error(String(e)),
178
+ * );
179
+ * const result = safeCmd();
180
+ * ```
181
+ *
182
+ * @see {@link https://pithos.dev/api/eidos/command/ | Full explanation, examples and live demo}
183
+ * @param cmd - The command to execute
184
+ * @returns `Ok(void)` on success, `Err(Error)` on failure
185
+ * @since 2.4.0
186
+ */
187
+ export function safeExecute(cmd) {
188
+ const safe = Result.fromThrowable(cmd, (e) => e instanceof Error ? e : new Error(String(e)));
189
+ return safe();
190
+ }
191
+ /**
192
+ * Creates an undoable state command from an execute/undo pair.
193
+ * Unlike `undoable` (imperative thunks), these are pure state transforms.
194
+ *
195
+ * @param execute - Pure function that produces the next state
196
+ * @param undo - Pure function that reverses execute
197
+ * @returns An UndoableStateCommand
198
+ * @since 2.5.0
199
+ *
200
+ * @example
201
+ * ```ts
202
+ * interface Counter { value: number }
203
+ *
204
+ * const increment = undoableState<Counter>(
205
+ * (s) => ({ ...s, value: s.value + 1 }),
206
+ * (s) => ({ ...s, value: s.value - 1 }),
207
+ * );
208
+ * ```
209
+ */
210
+ export function undoableState(execute, undo) {
211
+ return { execute, undo };
212
+ }
213
+ /**
214
+ * Creates a reactive command stack with undo/redo support.
215
+ *
216
+ * Unlike `createCommandStack` (imperative, mutation-based), this variant
217
+ * manages state internally and notifies via `onChange` on every transition.
218
+ * Commands are pure functions from state to state, making it naturally
219
+ * compatible with React, Vue, Angular, or any reactive framework.
220
+ *
221
+ * @param options - Initial state and onChange callback
222
+ * @returns Stack with `execute`, `undo`, `redo`, `canUndo`, `canRedo`, `clear`, `state`
223
+ * @since 2.5.0
224
+ *
225
+ * @example
226
+ * ```ts
227
+ * // React
228
+ * const [board, setBoard] = useState(INITIAL);
229
+ * const stack = useRef(createReactiveCommandStack({
230
+ * initial: INITIAL,
231
+ * onChange: setBoard,
232
+ * }));
233
+ *
234
+ * stack.current.execute(undoableState(
235
+ * (b) => moveTask(b, "todo", "done", task),
236
+ * (b) => moveTask(b, "done", "todo", task),
237
+ * ));
238
+ * // setBoard is called automatically with the new state
239
+ *
240
+ * stack.current.undo(); // setBoard called with previous state
241
+ * stack.current.redo(); // setBoard called with next state
242
+ * ```
243
+ */
244
+ export function createReactiveCommandStack(options) {
245
+ let current = options.initial;
246
+ const past = [];
247
+ const future = [];
248
+ function notify() {
249
+ options.onChange(current);
250
+ }
251
+ return {
252
+ /** Execute a state command. Clears redo stack. */
253
+ execute: (cmd) => {
254
+ current = cmd.execute(current);
255
+ past.push(cmd);
256
+ future.length = 0;
257
+ notify();
258
+ },
259
+ /** Undo the last command. Returns false if nothing to undo. */
260
+ undo: () => {
261
+ const cmd = past.pop();
262
+ if (!cmd)
263
+ return false;
264
+ current = cmd.undo(current);
265
+ future.push(cmd);
266
+ notify();
267
+ return true;
268
+ },
269
+ /** Redo the last undone command. Returns false if nothing to redo. */
270
+ redo: () => {
271
+ const cmd = future.pop();
272
+ if (!cmd)
273
+ return false;
274
+ current = cmd.execute(current);
275
+ past.push(cmd);
276
+ notify();
277
+ return true;
278
+ },
279
+ /** Current state. */
280
+ get state() {
281
+ return current;
282
+ },
283
+ get canUndo() {
284
+ return past.length > 0;
285
+ },
286
+ get canRedo() {
287
+ return future.length > 0;
288
+ },
289
+ /** Reset to initial state and clear all history. */
290
+ clear: () => {
291
+ current = options.initial;
292
+ past.length = 0;
293
+ future.length = 0;
294
+ notify();
295
+ },
296
+ };
297
+ }
298
+ //# sourceMappingURL=command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../../../src/eidos/command/command.ts"],"names":[],"mappings":"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"}