@doeixd/machine 1.0.2 → 1.1.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 (51) hide show
  1. package/README.md +48 -0
  2. package/dist/cjs/development/core.js +11 -9
  3. package/dist/cjs/development/core.js.map +3 -3
  4. package/dist/cjs/development/delegate.js +89 -0
  5. package/dist/cjs/development/delegate.js.map +7 -0
  6. package/dist/cjs/development/index.js +385 -167
  7. package/dist/cjs/development/index.js.map +4 -4
  8. package/dist/cjs/development/minimal.js +163 -0
  9. package/dist/cjs/development/minimal.js.map +7 -0
  10. package/dist/cjs/development/react.js +11 -9
  11. package/dist/cjs/development/react.js.map +3 -3
  12. package/dist/cjs/production/core.js +1 -1
  13. package/dist/cjs/production/delegate.js +1 -0
  14. package/dist/cjs/production/index.js +3 -3
  15. package/dist/cjs/production/minimal.js +1 -0
  16. package/dist/cjs/production/react.js +1 -1
  17. package/dist/esm/development/core.js +11 -9
  18. package/dist/esm/development/core.js.map +3 -3
  19. package/dist/esm/development/delegate.js +68 -0
  20. package/dist/esm/development/delegate.js.map +7 -0
  21. package/dist/esm/development/index.js +391 -167
  22. package/dist/esm/development/index.js.map +4 -4
  23. package/dist/esm/development/minimal.js +140 -0
  24. package/dist/esm/development/minimal.js.map +7 -0
  25. package/dist/esm/development/react.js +11 -9
  26. package/dist/esm/development/react.js.map +3 -3
  27. package/dist/esm/production/core.js +1 -1
  28. package/dist/esm/production/delegate.js +1 -0
  29. package/dist/esm/production/index.js +3 -3
  30. package/dist/esm/production/minimal.js +1 -0
  31. package/dist/esm/production/react.js +1 -1
  32. package/dist/types/base.d.ts +56 -0
  33. package/dist/types/base.d.ts.map +1 -0
  34. package/dist/types/delegate.d.ts +101 -0
  35. package/dist/types/delegate.d.ts.map +1 -0
  36. package/dist/types/higher-order.d.ts +2 -1
  37. package/dist/types/higher-order.d.ts.map +1 -1
  38. package/dist/types/index.d.ts +4 -49
  39. package/dist/types/index.d.ts.map +1 -1
  40. package/dist/types/minimal.d.ts +95 -0
  41. package/dist/types/minimal.d.ts.map +1 -0
  42. package/dist/types/types.d.ts +63 -0
  43. package/dist/types/types.d.ts.map +1 -0
  44. package/package.json +25 -1
  45. package/src/base.ts +62 -0
  46. package/src/delegate.ts +267 -0
  47. package/src/higher-order.ts +2 -2
  48. package/src/index.ts +15 -55
  49. package/src/middleware.ts +1049 -1050
  50. package/src/minimal.ts +269 -0
  51. package/src/types.ts +85 -0
@@ -0,0 +1,267 @@
1
+ // ============================================================================
2
+ // DELEGATION: delegate()
3
+ // ============================================================================
4
+
5
+ /**
6
+ * Extracts the names of all function properties (transitions) from a type.
7
+ */
8
+ type TransitionNamesOf<T> = {
9
+ [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
10
+ }[keyof T];
11
+
12
+ /**
13
+ * Extracts the argument types of a function.
14
+ */
15
+ type ArgsOf<F> = F extends (...args: infer A) => any ? A : never;
16
+
17
+ /**
18
+ * Creates delegated transitions that forward to a child and return the parent.
19
+ *
20
+ * @typeParam Child - Child machine type
21
+ * @typeParam Parent - Parent machine return type
22
+ */
23
+ type DelegatedTransitions<
24
+ Child extends object,
25
+ Parent
26
+ > = {
27
+ [K in TransitionNamesOf<Child>]: (
28
+ ...args: ArgsOf<Child[K]>
29
+ ) => Parent;
30
+ };
31
+
32
+ /**
33
+ * Renamed delegated transitions using a mapping object.
34
+ *
35
+ * @typeParam Child - Child machine type
36
+ * @typeParam Parent - Parent machine return type
37
+ * @typeParam Mapping - Object mapping child transition names to parent names
38
+ */
39
+ type RenamedDelegatedTransitions<
40
+ Child extends object,
41
+ Parent,
42
+ Mapping extends Partial<Record<TransitionNamesOf<Child>, string>>
43
+ > = {
44
+ [K in keyof Mapping as Mapping[K] extends string ? Mapping[K] : never]: K extends TransitionNamesOf<Child>
45
+ ? (...args: ArgsOf<Child[K]>) => Parent
46
+ : never;
47
+ };
48
+
49
+ /**
50
+ * Subset of delegated transitions.
51
+ *
52
+ * @typeParam Child - Child machine type
53
+ * @typeParam Parent - Parent machine return type
54
+ * @typeParam Keys - Subset of child transition names to delegate
55
+ */
56
+ type PickedDelegatedTransitions<
57
+ Child extends object,
58
+ Parent,
59
+ Keys extends TransitionNamesOf<Child>
60
+ > = {
61
+ [K in Keys]: (...args: ArgsOf<Child[K]>) => Parent;
62
+ };
63
+
64
+ /**
65
+ * Options for delegate function.
66
+ */
67
+ type DelegateOptions<Child extends object> =
68
+ | { pick: Array<TransitionNamesOf<Child>> }
69
+ | { omit: Array<TransitionNamesOf<Child>> }
70
+ | { rename: Partial<Record<TransitionNamesOf<Child>, string>> };
71
+
72
+ /**
73
+ * Delegates child machine transitions to the parent level.
74
+ *
75
+ * When a delegated transition is called on the parent, it:
76
+ * 1. Calls the corresponding transition on the child
77
+ * 2. Updates the parent's context with the new child state
78
+ * 3. Returns a new parent machine
79
+ *
80
+ * This enables "flat" composition where child transitions appear directly
81
+ * on the parent, as opposed to `withChildren()` which namespaces them.
82
+ *
83
+ * @typeParam Ctx - Parent context type
84
+ * @typeParam Key - Key where child is stored in parent context
85
+ * @typeParam R - Parent machine return type (e.g. Machine<Ctx, T>)
86
+ *
87
+ * @param ctx - Current parent context (from transition factory)
88
+ * @param key - Property key of the child machine in context
89
+ * @param next - The `next` function from the parent's transition factory
90
+ * @param options - Optional: pick, omit, or rename specific transitions
91
+ *
92
+ * @returns Object of delegated transitions to spread into parent's transitions
93
+ */
94
+ export function delegate<
95
+ Ctx extends object,
96
+ Key extends keyof Ctx,
97
+ R,
98
+ Child extends Ctx[Key] & object = Ctx[Key] & object
99
+ >(
100
+ ctx: Ctx,
101
+ key: Key,
102
+ next: (c: Ctx) => R
103
+ ): DelegatedTransitions<Child, R>;
104
+
105
+ export function delegate<
106
+ Ctx extends object,
107
+ Key extends keyof Ctx,
108
+ R,
109
+ Child extends Ctx[Key] & object,
110
+ Keys extends TransitionNamesOf<Child>
111
+ >(
112
+ ctx: Ctx,
113
+ key: Key,
114
+ next: (c: Ctx) => R,
115
+ options: { pick: Keys[] }
116
+ ): PickedDelegatedTransitions<Child, R, Keys>;
117
+
118
+ export function delegate<
119
+ Ctx extends object,
120
+ Key extends keyof Ctx,
121
+ R,
122
+ Child extends Ctx[Key] & object,
123
+ Keys extends TransitionNamesOf<Child>
124
+ >(
125
+ ctx: Ctx,
126
+ key: Key,
127
+ next: (c: Ctx) => R,
128
+ options: { omit: Keys[] }
129
+ ): DelegatedTransitions<Omit<Child, Keys>, R>;
130
+
131
+ export function delegate<
132
+ Ctx extends object,
133
+ Key extends keyof Ctx,
134
+ R,
135
+ Child extends Ctx[Key] & object,
136
+ Mapping extends Partial<Record<TransitionNamesOf<Child>, string>>
137
+ >(
138
+ ctx: Ctx,
139
+ key: Key,
140
+ next: (c: Ctx) => R,
141
+ options: { rename: Mapping }
142
+ ): RenamedDelegatedTransitions<Child, R, Mapping>;
143
+
144
+ export function delegate<
145
+ Ctx extends object,
146
+ Key extends keyof Ctx,
147
+ R
148
+ >(
149
+ ctx: Ctx,
150
+ key: Key,
151
+ next: (c: Ctx) => R,
152
+ options?: DelegateOptions<Ctx[Key] & object>
153
+ ): Record<string, (...args: unknown[]) => R> {
154
+ const child = ctx[key] as Record<string, unknown>;
155
+ const delegated: Record<string, (...args: unknown[]) => R> = {};
156
+
157
+ // Get all function property names from child
158
+ const allTransitions = Object.keys(child).filter(
159
+ (k) => typeof child[k] === 'function'
160
+ );
161
+
162
+ // Determine which transitions to include and how to name them
163
+ let transitionMap: Record<string, string>; // childName -> parentName
164
+
165
+ if (!options) {
166
+ // Delegate all with same names
167
+ transitionMap = Object.fromEntries(allTransitions.map((t) => [t, t]));
168
+ } else if ('pick' in options) {
169
+ // Only picked transitions
170
+ transitionMap = Object.fromEntries(
171
+ (options.pick as string[]).filter((t) => allTransitions.includes(t)).map((t) => [t, t])
172
+ );
173
+ } else if ('omit' in options) {
174
+ // All except omitted
175
+ const omitSet = new Set(options.omit as string[]);
176
+ transitionMap = Object.fromEntries(
177
+ allTransitions.filter((t) => !omitSet.has(t)).map((t) => [t, t])
178
+ );
179
+ } else if ('rename' in options) {
180
+ // Only renamed transitions with new names
181
+ transitionMap = Object.fromEntries(
182
+ Object.entries(options.rename as Record<string, string>).filter(
183
+ ([childName]) => allTransitions.includes(childName)
184
+ )
185
+ );
186
+ } else {
187
+ transitionMap = {};
188
+ }
189
+
190
+ // Create delegated transitions
191
+ for (const [childName, parentName] of Object.entries(transitionMap)) {
192
+ const childTransition = child[childName] as (...args: unknown[]) => unknown;
193
+
194
+ delegated[parentName] = (...args: unknown[]) => {
195
+ const nextChild = childTransition.apply(child, args);
196
+ return next({ ...ctx, [key]: nextChild } as Ctx);
197
+ };
198
+ }
199
+
200
+ return delegated;
201
+ }
202
+
203
+ /**
204
+ * Type helper to get transition names from a machine or object.
205
+ * Useful for type-safe pick/omit/rename options.
206
+ */
207
+ export type TransitionsOf<M> = TransitionNamesOf<M>;
208
+
209
+ // ============================================================================
210
+ // DELEGATION UTILITIES
211
+ // ============================================================================
212
+
213
+ /**
214
+ * Creates a delegate helper bound to a specific context and next function.
215
+ * Useful when delegating multiple children to avoid repetition.
216
+ */
217
+ export function createDelegate<Ctx extends object, R>(
218
+ ctx: Ctx,
219
+ next: (c: Ctx) => R
220
+ ) {
221
+ return <Key extends keyof Ctx>(
222
+ key: Key,
223
+ options?: DelegateOptions<Ctx[Key] & object>
224
+ ) => delegate(ctx, key, next, options as any);
225
+ }
226
+
227
+ /**
228
+ * Delegates all transitions from multiple children, optionally with a prefix.
229
+ */
230
+ export function delegateAll<
231
+ Ctx extends object,
232
+ Keys extends keyof Ctx,
233
+ R
234
+ >(
235
+ ctx: Ctx,
236
+ keys: Keys[],
237
+ next: (c: Ctx) => R,
238
+ prefix: boolean = false
239
+ ): Record<string, (...args: unknown[]) => R> {
240
+ const result: Record<string, (...args: unknown[]) => R> = {};
241
+
242
+ for (const key of keys) {
243
+ const child = ctx[key] as Record<string, unknown>;
244
+ const transitions = Object.keys(child).filter(
245
+ (k) => typeof child[k] === 'function'
246
+ );
247
+
248
+ for (const transitionName of transitions) {
249
+ const parentName = prefix ? `${String(key)}_${transitionName}` : transitionName;
250
+ const childTransition = child[transitionName] as (...args: unknown[]) => unknown;
251
+
252
+ result[parentName] = (...args: unknown[]) => {
253
+ const nextChild = childTransition.apply(child, args);
254
+ return next({ ...ctx, [key]: nextChild } as Ctx);
255
+ };
256
+ }
257
+ }
258
+
259
+ return result;
260
+ }
261
+
262
+ /**
263
+ * Type-safe helper to create a rename mapping for delegate.
264
+ */
265
+ export function renameMap<M extends object>() {
266
+ return <T extends Partial<Record<TransitionNamesOf<M>, string>>>(mapping: T): T => mapping;
267
+ }
@@ -9,15 +9,15 @@
9
9
  * Think of this as the "standard library" of common machine patterns.
10
10
  */
11
11
 
12
+ import { MachineBase } from './base'; // Import from base to avoid circular dependency
12
13
  import {
13
- MachineBase,
14
14
  Machine,
15
15
  Transitions,
16
16
  // AsyncMachine,
17
17
  setContext,
18
18
  Context,
19
19
  // MaybePromise,
20
- } from './index'; // Assuming this is a sibling package or in the same project
20
+ } from './index';
21
21
 
22
22
  // =============================================================================
23
23
  // SECTION 1: CUSTOM PRIMITIVES FOR COMPOSITION
package/src/index.ts CHANGED
@@ -870,61 +870,8 @@ export function runMachine<M extends AsyncMachine<any>>(
870
870
  };
871
871
  }
872
872
 
873
- /**
874
- * An optional base class for creating machines using an Object-Oriented style.
875
- *
876
- * This class provides the fundamental structure required by the library: a `context`
877
- * property to hold the state. By extending `MachineBase`, you get a clear and
878
- * type-safe starting point for defining states and transitions as classes and methods.
879
- *
880
- * Transitions should be implemented as methods that return a new instance of a
881
- * state machine class (often `new MyClass(...)` or by using a `createMachineBuilder`).
882
- * The `context` is marked `readonly` to enforce the immutable update pattern.
883
- *
884
- * @template C - The context object type that defines the state for this machine.
885
- *
886
- * @example
887
- * // Define a simple counter state
888
- * class Counter extends MachineBase<{ readonly count: number }> {
889
- * constructor(count = 0) {
890
- * super({ count });
891
- * }
892
- *
893
- * increment(): Counter {
894
- * // Return a new instance for the next state
895
- * return new Counter(this.context.count + 1);
896
- * }
897
- *
898
- * add(n: number): Counter {
899
- * return new Counter(this.context.count + n);
900
- * }
901
- * }
902
- *
903
- * const machine = new Counter(5);
904
- * const nextState = machine.increment(); // Returns a new Counter instance
905
- *
906
- * console.log(machine.context.count); // 5 (original is unchanged)
907
- * console.log(nextState.context.count); // 6 (new state)
908
- */
909
- export class MachineBase<C extends object> {
910
- /**
911
- * The immutable state of the machine.
912
- * To change the state, a transition method must return a new machine instance
913
- * with a new context object.
914
- */
915
- public readonly context: C;
916
-
917
- /**
918
- * Initializes a new machine instance with its starting context.
919
- * @param context - The initial state of the machine.
920
- */
921
- constructor(context: C) {
922
- this.context = context;
923
- // Object.freeze can provide additional runtime safety against accidental mutation,
924
- // though it comes with a minor performance cost. It's a good practice for ensuring purity.
925
- // Object.freeze(this.context);
926
- }
927
- }
873
+ // Export MachineBase from separate file to avoid circular dependency issues
874
+ export { MachineBase } from './base';
928
875
 
929
876
 
930
877
  /**
@@ -1132,3 +1079,16 @@ export {
1132
1079
  isContextBound,
1133
1080
  type ContextBoundMachine
1134
1081
  } from './context-bound';
1082
+
1083
+ // =============================================================================
1084
+ // SECTION: MINIMAL & DELEGATED API
1085
+ // =============================================================================
1086
+
1087
+ export * as minimal from './minimal';
1088
+ export * as delegate from './delegate';
1089
+
1090
+ // =============================================================================
1091
+ // SECTION: TAGGED HELPERS & UTILITIES
1092
+ // =============================================================================
1093
+
1094
+ export * from './types';