@doeixd/machine 0.0.23 → 1.0.2

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 (53) hide show
  1. package/README.md +101 -65
  2. package/dist/cjs/development/core.js +56 -57
  3. package/dist/cjs/development/core.js.map +4 -4
  4. package/dist/cjs/development/index.js +99 -58
  5. package/dist/cjs/development/index.js.map +4 -4
  6. package/dist/cjs/development/react.js +56 -58
  7. package/dist/cjs/development/react.js.map +4 -4
  8. package/dist/cjs/production/core.js +1 -1
  9. package/dist/cjs/production/index.js +3 -3
  10. package/dist/cjs/production/react.js +1 -1
  11. package/dist/esm/development/core.js +56 -57
  12. package/dist/esm/development/core.js.map +4 -4
  13. package/dist/esm/development/index.js +99 -58
  14. package/dist/esm/development/index.js.map +4 -4
  15. package/dist/esm/development/react.js +56 -58
  16. package/dist/esm/development/react.js.map +4 -4
  17. package/dist/esm/production/core.js +1 -1
  18. package/dist/esm/production/index.js +3 -3
  19. package/dist/esm/production/react.js +1 -1
  20. package/dist/types/actor.d.ts +4 -4
  21. package/dist/types/actor.d.ts.map +1 -1
  22. package/dist/types/context-bound.d.ts +94 -0
  23. package/dist/types/context-bound.d.ts.map +1 -0
  24. package/dist/types/entry-react.d.ts +1 -1
  25. package/dist/types/entry-react.d.ts.map +1 -1
  26. package/dist/types/functional-combinators.d.ts +5 -5
  27. package/dist/types/generators.d.ts +2 -2
  28. package/dist/types/index.d.ts +14 -34
  29. package/dist/types/index.d.ts.map +1 -1
  30. package/dist/types/internal-transitions.d.ts +5 -0
  31. package/dist/types/internal-transitions.d.ts.map +1 -0
  32. package/dist/types/primitives.d.ts +25 -5
  33. package/dist/types/primitives.d.ts.map +1 -1
  34. package/dist/types/react.d.ts.map +1 -1
  35. package/dist/types/utils.d.ts +22 -22
  36. package/dist/types/utils.d.ts.map +1 -1
  37. package/package.json +1 -1
  38. package/src/actor.ts +1 -1
  39. package/src/context-bound.ts +160 -0
  40. package/src/entry-react.ts +9 -2
  41. package/src/functional-combinators.ts +5 -5
  42. package/src/generators.ts +2 -2
  43. package/src/higher-order.ts +2 -2
  44. package/src/index.ts +47 -80
  45. package/src/internal-transitions.ts +32 -0
  46. package/src/middleware/time-travel.ts +2 -2
  47. package/src/middleware.ts +2 -2
  48. package/src/multi.ts +4 -4
  49. package/src/primitives.ts +34 -14
  50. package/src/prototype_functional.ts +2 -2
  51. package/src/react.ts +1 -2
  52. package/src/test.ts +7 -7
  53. package/src/utils.ts +31 -31
@@ -0,0 +1,160 @@
1
+ import type { Machine, Transitions } from './index';
2
+ import { attachTransitions } from './internal-transitions';
3
+
4
+ /**
5
+ * Creates a machine where transformers receive context as `this` and return new contexts.
6
+ * The wrapper automatically converts these context-returning functions into proper
7
+ * machine-returning transitions.
8
+ *
9
+ * **Important Limitations:**
10
+ * - Transformers receive ONLY context as `this` (a lightweight `{ context }` object)
11
+ * - Cannot call other transitions via `this.otherTransition()`
12
+ * - Provides cleaner syntax for simple context transformations
13
+ *
14
+ * @template C - The context type
15
+ * @template T - The transformers record mapping transition names to context transformers
16
+ *
17
+ * @param initialContext - The initial context object
18
+ * @param transformers - Record of transition names to pure context transformers.
19
+ * Each transformer receives `this === context` and returns a new context.
20
+ * The public API returns machines, not contexts.
21
+ * @returns A context-bound machine where public transitions return machines,
22
+ * but internal transformers work with contexts directly.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const machine = createContextBoundMachine({ count: 0 }, {
27
+ * increment() {
28
+ * // Inside: `this` is the context object, return new context
29
+ * return { count: this.context.count + 1 };
30
+ * },
31
+ * add(amount: number) {
32
+ * return { count: this.context.count + amount };
33
+ * }
34
+ * });
35
+ *
36
+ * // Outside: returns a machine (not just context)
37
+ * const result = machine.increment();
38
+ * console.log(result.context.count); // 1
39
+ *
40
+ * // ❌ CANNOT do this inside transformers:
41
+ * // incrementTwice() { return this.increment().increment(); }
42
+ * // (this.increment doesn't exist - `this` is just the context)
43
+ * ```
44
+ */
45
+ export function createContextBoundMachine<
46
+ C extends object,
47
+ T extends Record<string, (this: C, ...args: any[]) => C>
48
+ >(
49
+ initialContext: C,
50
+ transformers: T
51
+ ): ContextBoundMachine<C, T> {
52
+ // Create a closure to hold the transformers for reuse
53
+ const savedTransformers = transformers;
54
+
55
+ // Create transition functions that bind to context
56
+ const boundTransitions = Object.fromEntries(
57
+ Object.entries(transformers).map(([key, transformer]) => [
58
+ key,
59
+ function (this: Machine<C>, ...args: any[]) {
60
+ // Bind transformer to a context-only object ({ context })
61
+ const contextOnly = { context: this.context } as Pick<Machine<C>, 'context'>;
62
+ const newContext = transformer.apply(contextOnly as any, args);
63
+ // Return new machine with same transformers
64
+ return createContextBoundMachine(newContext, savedTransformers);
65
+ },
66
+ ])
67
+ );
68
+
69
+ Object.values(boundTransitions).forEach((fn) => {
70
+ if (typeof fn === 'function') {
71
+ Object.defineProperty(fn, '__contextBound', {
72
+ value: true,
73
+ enumerable: false,
74
+ });
75
+ }
76
+ });
77
+
78
+ return attachTransitions(
79
+ Object.assign({ context: initialContext }, boundTransitions),
80
+ boundTransitions as any
81
+ ) as any;
82
+ }
83
+
84
+ /**
85
+ * Machine type where transitions receive context as `this`.
86
+ *
87
+ * @template C - The context type
88
+ * @template T - The transformers record type
89
+ */
90
+ export type ContextBoundMachine<
91
+ C extends object,
92
+ T extends Record<string, any>
93
+ > = Machine<
94
+ C,
95
+ {
96
+ [K in keyof T]: (
97
+ ...args: Parameters<T[K]>
98
+ ) => ContextBoundMachine<C, T>;
99
+ }
100
+ >;
101
+
102
+
103
+ /**
104
+ * Helper to call a transition with context binding.
105
+ * Equivalent to `fn.call({ context: machine.context }, ...args)`.
106
+ *
107
+ * @template M - The machine type
108
+ * @template K - The transition name (keyof the transitions)
109
+ *
110
+ * @param machine - The machine instance
111
+ * @param transitionName - The name of the transition to call
112
+ * @param args - Arguments to pass to the transition
113
+ * @returns The result of calling the transition
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * const machine = createMachine({ count: 0 }, {
118
+ * increment(this: {count: number}) {
119
+ * return { count: this.context.count + 1 };
120
+ * }
121
+ * });
122
+ *
123
+ * // Instead of: machine.increment.call(machine.context)
124
+ * const result = callWithContext(machine, 'increment');
125
+ * ```
126
+ */
127
+ export function callWithContext<M extends Machine<any>>(
128
+ machine: M,
129
+ transitionName: keyof Transitions<M>,
130
+ ...args: any[]
131
+ ): any {
132
+ const fn = (machine as any)[transitionName];
133
+ const contextOnly = { context: machine.context } as Pick<Machine<any>, 'context'>;
134
+ return fn.apply(contextOnly as any, args);
135
+ }
136
+
137
+ /**
138
+ * Type guard to check if a machine is context-bound.
139
+ *
140
+ * @param machine - The machine to check
141
+ * @returns True if the machine was created with createContextBoundMachine
142
+ *
143
+ * @example
144
+ * ```typescript
145
+ * const cbMachine = createContextBoundMachine({ count: 0 }, {...});
146
+ * const normalMachine = createMachine({ count: 0 }, {...});
147
+ *
148
+ * isContextBound(cbMachine); // true
149
+ * isContextBound(normalMachine); // false
150
+ * ```
151
+ */
152
+ export function isContextBound(machine: Machine<any>): boolean {
153
+ const firstTransition = Object.values(machine).find(
154
+ (v) => typeof v === 'function'
155
+ );
156
+ if (!firstTransition) return false;
157
+
158
+ // Context-bound machines have a marker property
159
+ return (firstTransition as any).__contextBound === true;
160
+ }
@@ -5,5 +5,12 @@
5
5
  // Re-export core functionality
6
6
  export * from './core';
7
7
 
8
- // Re-export React-specific functionality
9
- export * from './react';
8
+ // Re-export React-specific functionality (excluding duplicates like Runner)
9
+ export {
10
+ useMachine,
11
+ useMachineSelector,
12
+ useEnsemble,
13
+ createMachineContext,
14
+ useActor,
15
+ useActorSelector,
16
+ } from './react';
@@ -21,7 +21,7 @@ import { createMachine, Machine, BaseMachine, extendTransitions } from './index'
21
21
  * // Define your machine's transitions object
22
22
  * const counterTransitions = {
23
23
  * increment: function(amount: number) {
24
- * return createMachine({ count: this.count + amount }, counterTransitions);
24
+ * return createMachine({ count: this.context.count + amount }, counterTransitions);
25
25
  * }
26
26
  * };
27
27
  *
@@ -103,7 +103,7 @@ export function createTransitionFactory<C extends object>() {
103
103
  * // Start with a basic counter machine
104
104
  * const basicCounter = createMachine({ count: 0 }, {
105
105
  * increment: function() {
106
- * return createMachine({ count: this.count + 1 }, this);
106
+ * return createMachine({ count: this.context.count + 1 }, this);
107
107
  * }
108
108
  * });
109
109
  *
@@ -276,7 +276,7 @@ export function createFunctionalMachine<C extends object>(initialContext: C) {
276
276
  * 1. **Traditional Pattern** (with transitions object):
277
277
  * ```typescript
278
278
  * const machine = state({ count: 0 }, {
279
- * increment() { return createMachine({ count: this.count + 1 }, this); }
279
+ * increment() { return createMachine({ count: this.context.count + 1 }, this); }
280
280
  * });
281
281
  * ```
282
282
  *
@@ -308,8 +308,8 @@ export function createFunctionalMachine<C extends object>(initialContext: C) {
308
308
  * ```typescript
309
309
  * // Traditional pattern
310
310
  * const counter1 = state({ count: 0 }, {
311
- * increment() { return createMachine({ count: this.count + 1 }, this); },
312
- * decrement() { return createMachine({ count: this.count - 1 }, this); }
311
+ * increment() { return createMachine({ count: this.context.count + 1 }, this); },
312
+ * decrement() { return createMachine({ count: this.context.count - 1 }, this); }
313
313
  * });
314
314
  *
315
315
  * // Functional pattern
package/src/generators.ts CHANGED
@@ -58,10 +58,10 @@
58
58
  * ```typescript
59
59
  * const counter = createMachine({ count: 0 }, {
60
60
  * increment: function() {
61
- * return createMachine({ count: this.count + 1 }, this);
61
+ * return createMachine({ count: this.context.count + 1 }, this);
62
62
  * },
63
63
  * add: function(n: number) {
64
- * return createMachine({ count: this.count + n }, this);
64
+ * return createMachine({ count: this.context.count + n }, this);
65
65
  * }
66
66
  * });
67
67
  *
@@ -333,7 +333,7 @@ export function createParallelMachine<
333
333
  for (const key in transitions1) {
334
334
  const transitionFn = (transitions1 as any)[key];
335
335
  combinedTransitions[key] = (...args: any[]) => {
336
- const nextM1 = transitionFn.apply(m1.context, args);
336
+ const nextM1 = transitionFn.apply(m1, args);
337
337
  // Recursively create a new parallel machine with the new M1 state
338
338
  return createParallelMachine(nextM1, m2);
339
339
  };
@@ -343,7 +343,7 @@ export function createParallelMachine<
343
343
  for (const key in transitions2) {
344
344
  const transitionFn = (transitions2 as any)[key];
345
345
  combinedTransitions[key] = (...args: any[]) => {
346
- const nextM2 = transitionFn.apply(m2.context, args);
346
+ const nextM2 = transitionFn.apply(m2, args);
347
347
  // Recursively create a new parallel machine with the new M2 state
348
348
  return createParallelMachine(m1, nextM2);
349
349
  };
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  * @author doeixd
4
4
  * @version 1.0.0
5
5
  */
6
+ import { attachTransitions, getStoredTransitions, snapshotOwnTransitions } from './internal-transitions';
6
7
 
7
8
  // =============================================================================
8
9
  // SECTION: CORE TYPES & INTERFACES
@@ -198,12 +199,12 @@ export type Event<M extends BaseMachine<any>> = {
198
199
  * const counterTransitions = {
199
200
  * increment() {
200
201
  * // `this` is now fully typed!
201
- * // IntelliSense knows `this.count` is a number and
202
+ * // IntelliSense knows `this.context.count` is a number and
202
203
  * // `this.transitions.add` is a function.
203
- * return createMachine({ count: this.count + 1 }, this.transitions);
204
+ * return createMachine({ count: this.context.count + 1 }, this.transitions);
204
205
  * },
205
206
  * add(n: number) {
206
- * return createMachine({ count: this.count + n }, this.transitions);
207
+ * return createMachine({ count: this.context.count + n }, this.transitions);
207
208
  * },
208
209
  * // ❌ TypeScript will immediately throw a compile error on the next line
209
210
  * // because the return type 'string' does not satisfy 'Machine<any>'.
@@ -220,7 +221,7 @@ export type Event<M extends BaseMachine<any>> = {
220
221
  * }
221
222
  */
222
223
  export type TransitionsFor<C extends object, T extends Record<string, any>> = {
223
- [K in keyof T]: (this: C & { transitions: T }, ...args: Parameters<T[K] extends (...a: infer A) => any ? (...a: A) => any : never>) => Machine<any, any>;
224
+ [K in keyof T]: (this: Machine<C, T>, ...args: Parameters<T[K] extends (...a: infer A) => any ? (...a: A) => any : never>) => Machine<any, any>;
224
225
  };
225
226
 
226
227
  /**
@@ -228,7 +229,7 @@ export type TransitionsFor<C extends object, T extends Record<string, any>> = {
228
229
  * type-checking for standalone asynchronous transition objects.
229
230
  */
230
231
  export type AsyncTransitionsFor<C extends object, T extends Record<string, any>> = {
231
- [K in keyof T]: (this: C & { transitions: T }, ...args: Parameters<T[K] extends (...a: infer A) => any ? (...a: A) => any : never>) => MaybePromise<AsyncMachine<any, any>>;
232
+ [K in keyof T]: (this: Machine<C, T>, ...args: Parameters<T[K] extends (...a: infer A) => any ? (...a: A) => any : never>) => MaybePromise<AsyncMachine<any, any>>;
232
233
  };
233
234
 
234
235
  /**
@@ -345,24 +346,26 @@ export type BindTransitions<T> = {
345
346
  * @param factory - A function that receives a `transition` helper and returns the transitions object.
346
347
  * @returns A new machine instance.
347
348
  */
348
- export function createMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any> = Record<string, (this: C, ...args: any[]) => any>>(
349
+ export function createMachine<C extends object, T extends Record<string, (this: Machine<C, any>, ...args: any[]) => any> = Record<string, (this: Machine<C, any>, ...args: any[]) => any>>(
349
350
  context: C,
350
351
  factory: (transition: (newContext: C) => Machine<C, any>) => T
351
- ): Machine<C, BindTransitions<T>>;
352
+ ): Machine<C, T>;
352
353
 
353
354
  /**
354
355
  * Creates a synchronous state machine from a context and transition functions.
355
356
  * This is the core factory for the functional approach.
357
+ * Transitions receive the full machine as `this`, allowing them to access
358
+ * `this.context` and call other transitions via `this.otherTransition()`.
356
359
  *
357
360
  * @template C - The context object type.
358
361
  * @param context - The initial state context.
359
362
  * @param fns - An object containing transition function definitions.
360
363
  * @returns A new machine instance.
361
364
  */
362
- export function createMachine<C extends object, T extends Record<string, (this: { context: C } & T, ...args: any[]) => any> & { context?: any }>(
365
+ export function createMachine<C extends object, T extends Record<string, (this: Machine<C, T>, ...args: any[]) => any> & { context?: any }>(
363
366
  context: C,
364
367
  fns: T
365
- ): { context: C } & T;
368
+ ): Machine<C, T>;
366
369
 
367
370
  /**
368
371
  * Creates a synchronous state machine by copying context and transitions from an existing machine.
@@ -379,58 +382,25 @@ export function createMachine<C extends object, M extends BaseMachine<C>>(
379
382
  machine: M
380
383
  ): Machine<C, Transitions<M>>;
381
384
 
382
- /**
383
- * Creates a synchronous state machine from a context and transition functions that expect `this` to be the context object.
384
- * This is used internally by utilities that need to bind transitions to context objects.
385
- *
386
- * @template C - The context object type.
387
- * @param context - The initial state context.
388
- * @param fns - An object containing transition function definitions that expect `this` to be the context.
389
- * @returns A new machine instance.
390
- */
391
- export function createMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(
392
- context: C,
393
- fns: T
394
- ): Machine<C, T>;
395
-
396
385
  export function createMachine(context: any, fnsOrFactory: any): any {
397
386
  if (typeof fnsOrFactory === 'function') {
398
387
  let transitions: any;
399
388
  const transition = (newContext: any) => {
400
- const machine = createMachine(newContext, transitions);
401
- // Re-bind transitions to the new context
402
- const boundTransitions = Object.fromEntries(
403
- Object.entries(transitions).map(([key, fn]) => [
404
- key,
405
- (fn as Function).bind(newContext)
406
- ])
407
- );
408
- return Object.assign(machine, boundTransitions);
389
+ return createMachine(newContext, transitions);
409
390
  };
410
391
  transitions = fnsOrFactory(transition);
411
392
 
412
- // Bind transitions to initial context
413
- const boundTransitions = Object.fromEntries(
414
- Object.entries(transitions).map(([key, fn]) => [
415
- key,
416
- (fn as Function).bind(context)
417
- ])
418
- );
419
-
420
- return Object.assign({ context }, boundTransitions);
393
+ return attachTransitions(Object.assign({ context }, transitions), transitions);
421
394
  }
422
395
 
423
396
  // If fns is a machine (has context property), extract just the transition functions
424
- const transitions = 'context' in fnsOrFactory ? Object.fromEntries(
425
- Object.entries(fnsOrFactory).filter(([key]) => key !== 'context')
426
- ) : fnsOrFactory;
427
-
428
- // For normal object transitions, we might also need binding if they use `this`
429
- // But existing code expects `this` to be the machine (context + transitions).
430
- // The new API expects `this` to be just context.
397
+ const stored = getStoredTransitions(fnsOrFactory);
398
+ const transitions = stored ?? ('context' in fnsOrFactory
399
+ ? snapshotOwnTransitions(fnsOrFactory)
400
+ : fnsOrFactory);
431
401
 
432
402
  const machine = Object.assign({ context }, transitions);
433
- return machine;
403
+ return attachTransitions(machine, transitions);
434
404
  }
435
405
 
436
406
  /**
@@ -481,36 +451,21 @@ export function createAsyncMachine(context: any, fnsOrFactory: any): any {
481
451
  if (typeof fnsOrFactory === 'function') {
482
452
  let transitions: any;
483
453
  const transition = (newContext: any) => {
484
- const machine = createAsyncMachine(newContext, transitions);
485
- // Re-bind transitions to the new context
486
- const boundTransitions = Object.fromEntries(
487
- Object.entries(transitions).map(([key, fn]) => [
488
- key,
489
- (fn as Function).bind(newContext)
490
- ])
491
- );
492
- return Object.assign(machine, boundTransitions);
454
+ return createAsyncMachine(newContext, transitions);
493
455
  };
494
456
  transitions = fnsOrFactory(transition);
495
457
 
496
- // Bind transitions to initial context
497
- const boundTransitions = Object.fromEntries(
498
- Object.entries(transitions).map(([key, fn]) => [
499
- key,
500
- (fn as Function).bind(context)
501
- ])
502
- );
503
-
504
- return Object.assign({ context }, boundTransitions);
458
+ return attachTransitions(Object.assign({ context }, transitions), transitions);
505
459
  }
506
460
 
507
461
  // If fns is a machine (has context property), extract just the transition functions
508
- const transitions = 'context' in fnsOrFactory ? Object.fromEntries(
509
- Object.entries(fnsOrFactory).filter(([key]) => key !== 'context')
510
- ) : fnsOrFactory;
462
+ const stored = getStoredTransitions(fnsOrFactory);
463
+ const transitions = stored ?? ('context' in fnsOrFactory
464
+ ? snapshotOwnTransitions(fnsOrFactory)
465
+ : fnsOrFactory);
511
466
 
512
467
  const machine = Object.assign({ context }, transitions);
513
- return machine;
468
+ return attachTransitions(machine, transitions);
514
469
  }
515
470
 
516
471
  /**
@@ -575,10 +530,12 @@ export function setContext<M extends Machine<any>>(
575
530
  machine: M,
576
531
  newContextOrFn: Context<M> | ((ctx: Readonly<Context<M>>) => Context<M>)
577
532
  ): M {
578
- const { context, ...transitions } = machine;
533
+ const currentContext = machine.context;
534
+ const transitions =
535
+ getStoredTransitions(machine) ?? snapshotOwnTransitions(machine);
579
536
  const newContext =
580
537
  typeof newContextOrFn === "function"
581
- ? (newContextOrFn as (ctx: Readonly<Context<M>>) => Context<M>)(context)
538
+ ? (newContextOrFn as (ctx: Readonly<Context<M>>) => Context<M>)(currentContext)
582
539
  : newContextOrFn;
583
540
 
584
541
  return createMachine(newContext, transitions as any) as M;
@@ -682,14 +639,14 @@ export function extendTransitions<
682
639
  * // Define two independent machines
683
640
  * const createCounter = (initial: number) =>
684
641
  * createMachine({ count: initial }, {
685
- * increment: function() { return createMachine({ count: this.count + 1 }, this); },
686
- * decrement: function() { return createMachine({ count: this.count - 1 }, this); }
642
+ * increment: function() { return createMachine({ count: this.context.count + 1 }, this); },
643
+ * decrement: function() { return createMachine({ count: this.context.count - 1 }, this); }
687
644
  * });
688
645
  *
689
646
  * const createLogger = () =>
690
647
  * createMachine({ logs: [] as string[] }, {
691
648
  * log: function(message: string) {
692
- * return createMachine({ logs: [...this.logs, message] }, this);
649
+ * return createMachine({ logs: [...this.context.logs, message] }, this);
693
650
  * },
694
651
  * clear: function() {
695
652
  * return createMachine({ logs: [] }, this);
@@ -872,7 +829,7 @@ export function runMachine<M extends AsyncMachine<any>>(
872
829
 
873
830
  try {
874
831
  // 3. Pass the signal to the transition function.
875
- const nextStatePromise = fn.apply(current.context, [...event.args, { signal: controller.signal }]);
832
+ const nextStatePromise = fn.apply(current, [...event.args, { signal: controller.signal }]);
876
833
 
877
834
  const nextState = await nextStatePromise;
878
835
 
@@ -986,8 +943,7 @@ export function next<C extends object>(
986
943
  m: Machine<C>,
987
944
  update: (ctx: Readonly<C>) => C
988
945
  ): Machine<C> {
989
- const { context, ...transitions } = m;
990
- return createMachine(update(context), transitions as any) as Machine<C>;
946
+ return setContext(m, (ctx) => update(ctx)) as Machine<C>;
991
947
  }
992
948
 
993
949
  /**
@@ -1164,4 +1120,15 @@ export {
1164
1120
  fromObservable,
1165
1121
  type ActorRef,
1166
1122
  type InspectionEvent
1167
- } from './actor';
1123
+ } from './actor';
1124
+
1125
+ // =============================================================================
1126
+ // SECTION: CONTEXT-BOUND UTILITIES
1127
+ // =============================================================================
1128
+
1129
+ export {
1130
+ createContextBoundMachine,
1131
+ callWithContext,
1132
+ isContextBound,
1133
+ type ContextBoundMachine
1134
+ } from './context-bound';
@@ -0,0 +1,32 @@
1
+ const TRANSITIONS_SYMBOL = Symbol.for("__machine_transitions__");
2
+
3
+ export type TransitionMap = Record<string, (...args: any[]) => any>;
4
+
5
+ export function attachTransitions<T extends object>(
6
+ machine: T,
7
+ transitions: TransitionMap
8
+ ): T {
9
+ Object.defineProperty(machine, TRANSITIONS_SYMBOL, {
10
+ value: transitions,
11
+ enumerable: false,
12
+ configurable: false,
13
+ });
14
+ return machine;
15
+ }
16
+
17
+ export function getStoredTransitions(machine: any): TransitionMap | undefined {
18
+ if (!machine || typeof machine !== "object") {
19
+ return undefined;
20
+ }
21
+ return machine[TRANSITIONS_SYMBOL];
22
+ }
23
+
24
+ export function snapshotOwnTransitions(source: any): TransitionMap {
25
+ if (!source || typeof source !== "object") {
26
+ return {};
27
+ }
28
+ const entries = Object.entries(source).filter(
29
+ ([key, value]) => key !== "context" && typeof value === "function"
30
+ );
31
+ return Object.fromEntries(entries) as TransitionMap;
32
+ }
@@ -211,7 +211,7 @@ export function withTimeTravel<M extends BaseMachine<any>>(
211
211
  for (const entry of transitionsToReplay) {
212
212
  const transitionFn = replayedMachine[entry.transitionName as keyof M] as Function;
213
213
  if (transitionFn) {
214
- replayedMachine = transitionFn.apply(replayedMachine.context, entry.args);
214
+ replayedMachine = transitionFn.apply(replayedMachine, entry.args);
215
215
  }
216
216
  }
217
217
 
@@ -233,4 +233,4 @@ export function withTimeTravel<M extends BaseMachine<any>>(
233
233
  restoreSnapshot,
234
234
  replayFrom
235
235
  }) as WithTimeTravel<M>;
236
- }
236
+ }
package/src/middleware.ts CHANGED
@@ -976,7 +976,7 @@ export function withTimeTravel<M extends BaseMachine<any>>(
976
976
  for (const entry of transitionsToReplay) {
977
977
  const transitionFn = replayedMachine[entry.transitionName as keyof M] as Function;
978
978
  if (transitionFn) {
979
- replayedMachine = transitionFn.apply(replayedMachine.context, entry.args);
979
+ replayedMachine = transitionFn.apply(replayedMachine, entry.args);
980
980
  }
981
981
  }
982
982
 
@@ -1443,4 +1443,4 @@ export type WithDebugging<M extends BaseMachine<any>> = WithTimeTravel<WithSnaps
1443
1443
  */
1444
1444
  export function withDebugging<M extends BaseMachine<any>>(machine: M): WithDebugging<M> {
1445
1445
  return withTimeTravel(withSnapshot(withHistory(machine)));
1446
- }
1446
+ }
package/src/multi.ts CHANGED
@@ -211,7 +211,7 @@ export function createRunner<M extends Machine<any>>(
211
211
  }
212
212
 
213
213
  return (...args: any[]) => {
214
- const nextState = transition.apply(currentMachine.context, args);
214
+ const nextState = transition.apply(currentMachine, args);
215
215
  // Ensure the next state has all the original transitions
216
216
  // by reconstructing it with the original transition functions
217
217
  const nextStateWithTransitions = Object.assign(
@@ -498,7 +498,7 @@ export function createEnsemble<
498
498
  // Return a function that, when called, executes the transition.
499
499
  // The transition itself is responsible for calling `store.setContext`.
500
500
  return (...args: any[]) => {
501
- return action.apply(currentMachine.context, args);
501
+ return action.apply(currentMachine, args);
502
502
  };
503
503
  },
504
504
  });
@@ -1153,7 +1153,7 @@ export function createMutableMachine<
1153
1153
  if (typeof transition === 'function') {
1154
1154
  return (...args: any[]) => {
1155
1155
  // This pattern requires transitions to be pure functions that return the next context.
1156
- const nextContext = transition.apply(currentMachine.context, args);
1156
+ const nextContext = transition.apply(currentMachine, args);
1157
1157
  if (typeof nextContext !== 'object' || nextContext === null) {
1158
1158
  console.warn(`[MutableMachine] Transition "${String(prop)}" did not return a valid context object. State may be inconsistent.`);
1159
1159
  return;
@@ -1178,4 +1178,4 @@ export function createMutableMachine<
1178
1178
  return prop in target || typeof (currentMachine as any)[prop] === 'function';
1179
1179
  }
1180
1180
  }) as MutableMachine<C, ReturnType<F[keyof F]>>;
1181
- }
1181
+ }