@doeixd/machine 0.0.6 → 0.0.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.
@@ -0,0 +1,208 @@
1
+ /**
2
+ * @file A collection of high-level, type-safe utility functions for @doeixd/machine.
3
+ * @description These helpers provide ergonomic improvements for common patterns like
4
+ * state checking, event creation, debugging, and composing transitions.
5
+ */
6
+ import { Machine, AsyncMachine, MaybePromise, Context, Event, Transitions, TransitionArgs } from './index';
7
+ /**
8
+ * A type representing a Class Constructor, used for type guards.
9
+ */
10
+ type ClassConstructor = new (...args: any[]) => any;
11
+ /**
12
+ * A type-safe way to check if a machine is in a specific state, acting as a Type Guard.
13
+ * This is the preferred way to do state checking when using class-based machines.
14
+ *
15
+ * @template T - The class constructor type to check against.
16
+ * @param machine - The machine instance to check.
17
+ * @param machineClass - The class constructor representing the state.
18
+ * @returns {boolean} `true` if the machine is an instance of the class, narrowing its type.
19
+ *
20
+ * @example
21
+ * declare const machine: LoggedInMachine | LoggedOutMachine;
22
+ *
23
+ * if (isState(machine, LoggedInMachine)) {
24
+ * // `machine` is now correctly typed as LoggedInMachine
25
+ * machine.logout();
26
+ * }
27
+ */
28
+ export declare function isState<T extends ClassConstructor>(machine: any, machineClass: T): machine is InstanceType<T>;
29
+ /**
30
+ * A type-safe factory function for creating event objects for `runMachine`.
31
+ * This provides full autocompletion and type checking for event names and their arguments.
32
+ *
33
+ * @template M - The machine type the event belongs to.
34
+ * @template K - The specific event name (transition method name).
35
+ * @param type - The name of the event (e.g., "increment").
36
+ * @param args - The arguments for that event, correctly typed.
37
+ * @returns A type-safe event object ready to be passed to `dispatch`.
38
+ *
39
+ * @example
40
+ * // Given: type MyMachine = Machine<{...}> & { add: (n: number) => any }
41
+ * const event = createEvent<MyMachine, 'add'>('add', 5);
42
+ * // `event` is correctly typed as { type: "add"; args: [number] }
43
+ *
44
+ * await runner.dispatch(event);
45
+ */
46
+ export declare function createEvent<M extends Machine<any>, K extends keyof Transitions<M> & string>(type: K, ...args: TransitionArgs<M, K>): Event<M>;
47
+ /**
48
+ * Creates a new machine instance by shallowly merging a partial context into the
49
+ * current context, preserving all original transitions.
50
+ *
51
+ * @template M - The machine type.
52
+ * @param machine - The original machine instance.
53
+ * @param partialContext - An object with a subset of context properties to update.
54
+ * @returns A new machine instance of the same type with the merged context.
55
+ *
56
+ * @example
57
+ * const user = new User({ name: 'Alex', age: 30, status: 'active' });
58
+ * const updatedUser = mergeContext(user, { status: 'inactive' });
59
+ * // updatedUser.context is { name: 'Alex', age: 30, status: 'inactive' }
60
+ */
61
+ export declare function mergeContext<M extends Machine<any>>(machine: M, partialContext: Partial<Context<M>>): M;
62
+ /**
63
+ * Sequentially applies a series of transitions to a machine.
64
+ * This function correctly handles both synchronous and asynchronous transitions,
65
+ * always returning a Promise with the final machine state.
66
+ *
67
+ * @template M - The machine type, must be compatible with AsyncMachine.
68
+ * @param initialMachine - The starting machine state.
69
+ * @param transitions - An array of functions, each taking a machine and returning the next.
70
+ * @returns A `Promise` that resolves to the final machine state after all transitions complete.
71
+ *
72
+ * @example
73
+ * const finalState = await pipeTransitions(
74
+ * new Counter({ count: 0 }),
75
+ * (m) => m.increment(), // sync
76
+ * (m) => m.addAsync(5), // async
77
+ * (m) => m.increment() // sync
78
+ * );
79
+ * // finalState.context.count will be 6
80
+ */
81
+ export declare function pipeTransitions<M extends AsyncMachine<any>>(initialMachine: M, ...transitions: ((m: M) => MaybePromise<M>)[]): Promise<M>;
82
+ /**
83
+ * A "tap" utility for logging a machine's context without interrupting a chain of operations.
84
+ * It prints the context to the console and returns the machine instance unchanged.
85
+ *
86
+ * @template M - The machine type.
87
+ * @param machine - The machine instance to log.
88
+ * @param label - An optional label to print before the context object.
89
+ * @returns The original, unmodified machine instance.
90
+ *
91
+ * @example
92
+ * import { logState as tap } from './utils';
93
+ *
94
+ * await pipeTransitions(
95
+ * new Counter({ count: 0 }),
96
+ * tap, // Logs: { count: 0 }
97
+ * (m) => m.increment(),
98
+ * (m) => tap(m, 'After increment:') // Logs: After increment: { count: 1 }
99
+ * );
100
+ */
101
+ export declare function logState<M extends Machine<any>>(machine: M, label?: string): M;
102
+ /**
103
+ * Calls a transition function with an explicit `this` context.
104
+ * Useful for invoking transition methods with proper context binding.
105
+ *
106
+ * @template C - The context type that the function expects as `this`.
107
+ * @template F - The function type with a `this` parameter.
108
+ * @template A - The argument types for the function.
109
+ * @param fn - The transition function to call.
110
+ * @param context - The context object to bind as `this`.
111
+ * @param args - Arguments to pass to the function.
112
+ * @returns The result of calling the function with the given context and arguments.
113
+ *
114
+ * @example
115
+ * type MyContext = { count: number };
116
+ * const increment = function(this: MyContext) { return this.count + 1; };
117
+ * const result = call(increment, { count: 5 }); // Returns 6
118
+ *
119
+ * // Particularly useful with machine transitions:
120
+ * import { call } from '@doeixd/machine/utils';
121
+ * const nextMachine = yield* step(call(m.increment, m.context));
122
+ */
123
+ export declare function call<C, F extends (this: C, ...args: any[]) => any>(fn: F, context: C, ...args: Parameters<F> extends [any, ...infer Rest] ? Rest : never): ReturnType<F>;
124
+ /**
125
+ * Binds all transition methods of a machine to its context automatically.
126
+ * Returns a Proxy that intercepts method calls and binds them to `machine.context`.
127
+ * This eliminates the need to use `.call(m.context, ...)` for every transition.
128
+ *
129
+ * Automatically recursively wraps returned machines, enabling seamless chaining
130
+ * in generator-based flows.
131
+ *
132
+ * @template M - The machine type with a `context` property and transition methods.
133
+ * @param machine - The machine instance to wrap.
134
+ * @returns A Proxy of the machine where all callable properties (transitions) are automatically bound to the machine's context.
135
+ *
136
+ * @example
137
+ * type CounterContext = { count: number };
138
+ * const counter = bindTransitions(createMachine({ count: 0 }, {
139
+ * increment(this: CounterContext) { return createCounter(this.count + 1); }
140
+ * }));
141
+ *
142
+ * // Now you can call transitions directly without .call():
143
+ * const next = counter.increment(); // Works! This is automatically bound.
144
+ *
145
+ * // Particularly useful with generators:
146
+ * const result = run(function* (m) {
147
+ * m = yield* step(m.increment()); // Clean syntax
148
+ * m = yield* step(m.add(5)); // No .call() needed
149
+ * return m;
150
+ * }, bindTransitions(counter));
151
+ *
152
+ * @remarks
153
+ * The Proxy preserves all original properties and methods. Non-callable properties
154
+ * are accessed directly from the machine. Callable properties are wrapped to bind
155
+ * them to `machine.context` before invocation. Returned machines are automatically
156
+ * re-wrapped to maintain binding across transition chains.
157
+ */
158
+ export declare function bindTransitions<M extends {
159
+ context: any;
160
+ }>(machine: M): M;
161
+ /**
162
+ * A strongly-typed wrapper class for binding transitions to machine context.
163
+ * Unlike the Proxy-based `bindTransitions`, this class preserves full type safety
164
+ * and provides better IDE support through explicit property forwarding.
165
+ *
166
+ * @template M - The machine type with a `context` property and transition methods.
167
+ *
168
+ * @example
169
+ * type CounterContext = { count: number };
170
+ * const counter = createMachine({ count: 0 }, {
171
+ * increment(this: CounterContext) { return createCounter(this.count + 1); }
172
+ * });
173
+ *
174
+ * const bound = new BoundMachine(counter);
175
+ *
176
+ * // All transitions are automatically bound to context
177
+ * const result = run(function* (m) {
178
+ * m = yield* step(m.increment());
179
+ * m = yield* step(m.add(5));
180
+ * return m.context.count;
181
+ * }, bound);
182
+ *
183
+ * @remarks
184
+ * Advantages over Proxy-based `bindTransitions`:
185
+ * - Full type safety with TypeScript's type system
186
+ * - Returned machines are automatically re-wrapped
187
+ * - Better IDE autocompletion and hover information
188
+ * - No type casting needed
189
+ *
190
+ * Disadvantages:
191
+ * - Requires explicit instance creation: `new BoundMachine(m)` vs `bindTransitions(m)`
192
+ * - Not a transparent drop-in replacement for the original machine
193
+ */
194
+ export declare class BoundMachine<M extends {
195
+ context: any;
196
+ }> {
197
+ private readonly wrappedMachine;
198
+ [key: string | symbol]: any;
199
+ constructor(machine: M);
200
+ /**
201
+ * Access the underlying machine's context directly.
202
+ */
203
+ get context(): M extends {
204
+ context: infer C;
205
+ } ? C : never;
206
+ }
207
+ export {};
208
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,KAAK,EACL,WAAW,EACX,cAAc,EAEf,MAAM,SAAS,CAAC;AAMjB;;GAEG;AACH,KAAK,gBAAgB,GAAG,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;AAEpD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,gBAAgB,EAChD,OAAO,EAAE,GAAG,EACZ,YAAY,EAAE,CAAC,GACd,OAAO,IAAI,YAAY,CAAC,CAAC,CAAC,CAE5B;AAOD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CACzB,CAAC,SAAS,OAAO,CAAC,GAAG,CAAC,EACtB,CAAC,SAAS,MAAM,WAAW,CAAC,CAAC,CAAC,GAAG,MAAM,EACvC,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAElD;AAOD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,OAAO,CAAC,GAAG,CAAC,EACjD,OAAO,EAAE,CAAC,EACV,cAAc,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAClC,CAAC,CAEH;AAOD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,eAAe,CAAC,CAAC,SAAS,YAAY,CAAC,GAAG,CAAC,EAC/D,cAAc,EAAE,CAAC,EACjB,GAAG,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,GAC5C,OAAO,CAAC,CAAC,CAAC,CAMZ;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,CAAC,CAO9E;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAChE,EAAE,EAAE,CAAC,EACL,OAAO,EAAE,CAAC,EACV,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,GACjE,UAAU,CAAC,CAAC,CAAC,CAEf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,OAAO,EAAE,GAAG,CAAA;CAAE,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAqBzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,YAAY,CAAC,CAAC,SAAS;IAAE,OAAO,EAAE,GAAG,CAAA;CAAE;IAClD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAI;IACnC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,GAAG,CAAC;gBAEhB,OAAO,EAAE,CAAC;IA+BtB;;OAEG;IACH,IAAI,OAAO,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,CAAC,GAAG,KAAK,CAExD;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doeixd/machine",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
package/src/extract.ts CHANGED
@@ -20,22 +20,57 @@ import { Project, Type, Node } from 'ts-morph';
20
20
  // SECTION: CONFIGURATION TYPES
21
21
  // =============================================================================
22
22
 
23
+ /**
24
+ * Configuration for a parallel region
25
+ */
26
+ export interface ParallelRegionConfig {
27
+ /** A unique name for this region (e.g., 'fontStyle') */
28
+ name: string;
29
+ /** The initial state class for this region */
30
+ initialState: string;
31
+ /** All reachable state classes within this region */
32
+ classes: string[];
33
+ }
34
+
35
+ /**
36
+ * Configuration for child states in a hierarchical machine
37
+ */
38
+ export interface ChildStatesConfig {
39
+ /** The property in the parent's context that holds the child machine */
40
+ contextProperty: string;
41
+ /** An array of all possible child state class names */
42
+ classes: string[];
43
+ /** The initial child state */
44
+ initialState: string;
45
+ }
46
+
23
47
  /**
24
48
  * Configuration for a single machine to extract
25
49
  */
26
50
  export interface MachineConfig {
27
51
  /** Path to the source file containing the machine */
28
52
  input: string;
29
- /** Array of class names that represent states */
30
- classes: string[];
31
53
  /** Output file path (optional, defaults to stdout) */
32
54
  output?: string;
33
55
  /** Top-level ID for the statechart */
34
56
  id: string;
35
- /** Name of the class that represents the initial state */
36
- initialState: string;
37
57
  /** Optional description of the machine */
38
58
  description?: string;
59
+
60
+ // EITHER `initialState` and `classes` for an FSM...
61
+ /** Array of class names that represent states (for simple FSM) */
62
+ classes?: string[];
63
+ /** Name of the class that represents the initial state (for simple FSM) */
64
+ initialState?: string;
65
+
66
+ // OR `parallel` for a parallel machine.
67
+ /** Configuration for parallel regions (mutually exclusive with initialState/classes) */
68
+ parallel?: {
69
+ regions: ParallelRegionConfig[];
70
+ };
71
+
72
+ /** Configuration for hierarchical/nested states */
73
+ children?: ChildStatesConfig;
39
74
  }
40
75
 
41
76
  /**
@@ -63,11 +98,16 @@ export interface ExtractionConfig {
63
98
  * plain JSON-compatible value. It's smart enough to resolve class constructor
64
99
  * types into their string names.
65
100
  *
101
+ * Note: This function is kept for future extensibility but is not currently used
102
+ * as the AST-based extraction approach (via extractFromCallExpression) is preferred.
103
+ *
66
104
  * @param type - The `ts-morph` Type object to serialize.
67
105
  * @param verbose - Enable debug logging
68
106
  * @returns A JSON-compatible value (string, number, object, array).
107
+ * @internal
69
108
  */
70
- function typeToJson(type: Type, verbose = false): any {
109
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
110
+ function _typeToJson(type: Type, verbose = false): any {
71
111
  // --- Terminal Types ---
72
112
  const symbol = type.getSymbol();
73
113
  if (symbol && symbol.getDeclarations().some(Node.isClassDeclaration)) {
@@ -83,7 +123,7 @@ function typeToJson(type: Type, verbose = false): any {
83
123
  // --- Recursive Types ---
84
124
  if (type.isArray()) {
85
125
  const elementType = type.getArrayElementTypeOrThrow();
86
- return [typeToJson(elementType, verbose)];
126
+ return [_typeToJson(elementType, verbose)];
87
127
  }
88
128
 
89
129
  // --- Object Types ---
@@ -102,7 +142,7 @@ function typeToJson(type: Type, verbose = false): any {
102
142
  if (!declaration) continue;
103
143
 
104
144
  try {
105
- obj[propName] = typeToJson(declaration.getType(), verbose);
145
+ obj[propName] = _typeToJson(declaration.getType(), verbose);
106
146
  } catch (e) {
107
147
  if (verbose) console.error(` Warning: Failed to serialize property ${propName}:`, e);
108
148
  obj[propName] = 'unknown';
@@ -438,6 +478,41 @@ function analyzeStateNode(classSymbol: any, verbose = false): object {
438
478
  // SECTION: MAIN ORCHESTRATOR
439
479
  // =============================================================================
440
480
 
481
+ /**
482
+ * Helper function to analyze a state node with optional nesting support
483
+ */
484
+ function analyzeStateNodeWithNesting(
485
+ className: string,
486
+ classSymbol: any,
487
+ sourceFile: any,
488
+ childConfig: ChildStatesConfig | undefined,
489
+ verbose = false
490
+ ): any {
491
+ const stateNode = analyzeStateNode(classSymbol, verbose) as any;
492
+
493
+ // If this state has children, analyze them recursively
494
+ if (childConfig) {
495
+ if (verbose) {
496
+ console.error(` 👪 Analyzing children for state: ${className}`);
497
+ }
498
+ stateNode.initial = childConfig.initialState;
499
+ stateNode.states = {};
500
+
501
+ // Recursively analyze each child state
502
+ for (const childClassName of childConfig.classes) {
503
+ const childClassDeclaration = sourceFile.getClass(childClassName);
504
+ if (childClassDeclaration) {
505
+ const childSymbol = childClassDeclaration.getSymbolOrThrow();
506
+ stateNode.states[childClassName] = analyzeStateNode(childSymbol, verbose);
507
+ } else {
508
+ console.warn(`⚠️ Warning: Child class '${childClassName}' not found.`);
509
+ }
510
+ }
511
+ }
512
+
513
+ return stateNode;
514
+ }
515
+
441
516
  /**
442
517
  * Extracts a single machine configuration to a statechart
443
518
  *
@@ -461,6 +536,56 @@ export function extractMachine(
461
536
  throw new Error(`Source file not found: ${config.input}`);
462
537
  }
463
538
 
539
+ // Handle parallel machine configuration
540
+ if (config.parallel) {
541
+ if (verbose) {
542
+ console.error(` ⏹️ Parallel machine detected. Analyzing regions.`);
543
+ }
544
+
545
+ const parallelChart: any = {
546
+ id: config.id,
547
+ type: 'parallel',
548
+ states: {},
549
+ };
550
+
551
+ if (config.description) {
552
+ parallelChart.description = config.description;
553
+ }
554
+
555
+ for (const region of config.parallel.regions) {
556
+ if (verbose) {
557
+ console.error(` 📍 Analyzing region: ${region.name}`);
558
+ }
559
+
560
+ const regionStates: any = {};
561
+ for (const className of region.classes) {
562
+ const classDeclaration = sourceFile.getClass(className);
563
+ if (classDeclaration) {
564
+ const classSymbol = classDeclaration.getSymbolOrThrow();
565
+ regionStates[className] = analyzeStateNode(classSymbol, verbose);
566
+ } else {
567
+ console.warn(`⚠️ Warning: Class '${className}' not found for region '${region.name}'.`);
568
+ }
569
+ }
570
+
571
+ parallelChart.states[region.name] = {
572
+ initial: region.initialState,
573
+ states: regionStates,
574
+ };
575
+ }
576
+
577
+ if (verbose) {
578
+ console.error(` ✅ Extracted ${config.parallel.regions.length} parallel regions`);
579
+ }
580
+
581
+ return parallelChart;
582
+ }
583
+
584
+ // Handle standard FSM configuration
585
+ if (!config.initialState || !config.classes) {
586
+ throw new Error(`Machine config for '${config.id}' must have either 'parallel' or 'initialState'/'classes'.`);
587
+ }
588
+
464
589
  const fullChart: any = {
465
590
  id: config.id,
466
591
  initial: config.initialState,
@@ -478,7 +603,17 @@ export function extractMachine(
478
603
  continue;
479
604
  }
480
605
  const classSymbol = classDeclaration.getSymbolOrThrow();
481
- const stateNode = analyzeStateNode(classSymbol, verbose);
606
+
607
+ // Check if this is the initial state and has children configuration
608
+ const hasChildren = className === config.initialState && config.children;
609
+ const stateNode = analyzeStateNodeWithNesting(
610
+ className,
611
+ classSymbol,
612
+ sourceFile,
613
+ hasChildren ? config.children : undefined,
614
+ verbose
615
+ );
616
+
482
617
  fullChart.states[className] = stateNode;
483
618
  }
484
619
 
@@ -569,6 +704,43 @@ export function generateChart() {
569
704
  }
570
705
  }
571
706
 
707
+ /**
708
+ * Example configuration demonstrating hierarchical and parallel machines.
709
+ * This is not used by default but serves as documentation.
710
+ */
711
+ export const ADVANCED_CONFIG_EXAMPLES = {
712
+ hierarchical: {
713
+ input: 'examples/dashboardMachine.ts',
714
+ id: 'dashboard',
715
+ classes: ['DashboardMachine', 'LoggedOutMachine'],
716
+ initialState: 'DashboardMachine',
717
+ children: {
718
+ contextProperty: 'child',
719
+ initialState: 'ViewingChildMachine',
720
+ classes: ['ViewingChildMachine', 'EditingChildMachine'],
721
+ },
722
+ } as MachineConfig,
723
+
724
+ parallel: {
725
+ input: 'examples/editorMachine.ts',
726
+ id: 'editor',
727
+ parallel: {
728
+ regions: [
729
+ {
730
+ name: 'fontWeight',
731
+ initialState: 'NormalWeight',
732
+ classes: ['NormalWeight', 'BoldWeight'],
733
+ },
734
+ {
735
+ name: 'textDecoration',
736
+ initialState: 'NoDecoration',
737
+ classes: ['NoDecoration', 'UnderlineState'],
738
+ },
739
+ ],
740
+ },
741
+ } as MachineConfig,
742
+ };
743
+
572
744
  // This allows the script to be executed directly from the command line.
573
745
  if (require.main === module) {
574
746
  generateChart();
package/src/generators.ts CHANGED
@@ -26,7 +26,7 @@
26
26
  * ```
27
27
  */
28
28
 
29
- import { Machine } from './index';
29
+
30
30
 
31
31
  /**
32
32
  * Runs a generator-based state machine flow to completion.
@@ -117,9 +117,9 @@ import { Machine } from './index';
117
117
  * }, machine);
118
118
  * ```
119
119
  */
120
- export function run<C extends object, T>(
121
- flow: (m: Machine<C>) => Generator<Machine<C>, T, Machine<C>>,
122
- initial: Machine<C>
120
+ export function run<C extends any = any, M extends { context: C } & Record<string, any> = { context: C }, T = any>(
121
+ flow: (m: M) => Generator<M, T, M>,
122
+ initial: M
123
123
  ): T {
124
124
  // Create the generator by calling the flow function with the initial machine
125
125
  const generator = flow(initial);
@@ -197,9 +197,9 @@ export function run<C extends object, T>(
197
197
  * }, machine);
198
198
  * ```
199
199
  */
200
- export function step<C extends object>(
201
- m: Machine<C>
202
- ): Generator<Machine<C>, Machine<C>, Machine<C>> {
200
+ export function step<C extends any = any, M extends { context: C } & Record<string, any> = { context: C }>(
201
+ m: M
202
+ ): Generator<M, M, M> {
203
203
  // Create an immediately-invoked generator that:
204
204
  // 1. Yields the provided machine
205
205
  // 2. Receives a value back (the next state)
@@ -229,7 +229,7 @@ export function step<C extends object>(
229
229
  * }, counter);
230
230
  * ```
231
231
  */
232
- export function yieldMachine<C extends object>(m: Machine<C>): Machine<C> {
232
+ export function yieldMachine<C extends any = any, M extends { context: C } & Record<string, any> = { context: C }>(m: M): M {
233
233
  return m;
234
234
  }
235
235
 
@@ -259,10 +259,10 @@ export function yieldMachine<C extends object>(m: Machine<C>): Machine<C> {
259
259
  * console.log(result.context.count); // 6
260
260
  * ```
261
261
  */
262
- export function runSequence<C extends object>(
263
- initial: Machine<C>,
264
- flows: Array<(m: Machine<C>) => Generator<Machine<C>, Machine<C>, Machine<C>>>
265
- ): Machine<C> {
262
+ export function runSequence<C extends any = any, M extends { context: C } & Record<string, any> = { context: C }>(
263
+ initial: M,
264
+ flows: Array<(m: M) => Generator<M, M, M>>
265
+ ): M {
266
266
  return flows.reduce((machine, flow) => {
267
267
  return run(flow, machine);
268
268
  }, initial);
@@ -295,9 +295,9 @@ export function runSequence<C extends object>(
295
295
  * }, counter);
296
296
  * ```
297
297
  */
298
- export function createFlow<C extends object>(
299
- flow: (m: Machine<C>) => Generator<Machine<C>, Machine<C>, Machine<C>>
300
- ): (m: Machine<C>) => Generator<Machine<C>, Machine<C>, Machine<C>> {
298
+ export function createFlow<C extends any = any, M extends { context: C } & Record<string, any> = { context: C }>(
299
+ flow: (m: M) => Generator<M, M, M>
300
+ ): (m: M) => Generator<M, M, M> {
301
301
  return flow;
302
302
  }
303
303
 
@@ -328,10 +328,10 @@ export function createFlow<C extends object>(
328
328
  * // Final: 6
329
329
  * ```
330
330
  */
331
- export function runWithDebug<C extends object, T>(
332
- flow: (m: Machine<C>) => Generator<Machine<C>, T, Machine<C>>,
333
- initial: Machine<C>,
334
- logger: (step: number, machine: Machine<C>) => void = (step, m) => {
331
+ export function runWithDebug<C extends any = any, M extends { context: C } & Record<string, any> = { context: C }, T = any>(
332
+ flow: (m: M) => Generator<M, T, M>,
333
+ initial: M,
334
+ logger: (step: number, machine: M) => void = (step, m) => {
335
335
  console.log(`Step ${step}:`, m.context);
336
336
  }
337
337
  ): T {
@@ -380,9 +380,9 @@ export function runWithDebug<C extends object, T>(
380
380
  * }, asyncMachine);
381
381
  * ```
382
382
  */
383
- export async function runAsync<C extends object, T>(
384
- flow: (m: Machine<C>) => AsyncGenerator<Machine<C>, T, Machine<C>>,
385
- initial: Machine<C>
383
+ export async function runAsync<C extends any = any, M extends { context: C } & Record<string, any> = { context: C }, T = any>(
384
+ flow: (m: M) => AsyncGenerator<M, T, M>,
385
+ initial: M
386
386
  ): Promise<T> {
387
387
  const generator = flow(initial);
388
388
  let current = initial;
@@ -413,9 +413,9 @@ export async function runAsync<C extends object, T>(
413
413
  * }, machine);
414
414
  * ```
415
415
  */
416
- export async function* stepAsync<C extends object>(
417
- m: Machine<C>
418
- ): AsyncGenerator<Machine<C>, Machine<C>, Machine<C>> {
416
+ export async function* stepAsync<C extends any = any, M extends { context: C } & Record<string, any> = { context: C }>(
417
+ m: M
418
+ ): AsyncGenerator<M, M, M> {
419
419
  const received = yield m;
420
420
  return received;
421
421
  }