@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.
- package/README.md +101 -65
- package/dist/cjs/development/core.js +56 -57
- package/dist/cjs/development/core.js.map +4 -4
- package/dist/cjs/development/index.js +99 -58
- package/dist/cjs/development/index.js.map +4 -4
- package/dist/cjs/development/react.js +56 -58
- package/dist/cjs/development/react.js.map +4 -4
- package/dist/cjs/production/core.js +1 -1
- package/dist/cjs/production/index.js +3 -3
- package/dist/cjs/production/react.js +1 -1
- package/dist/esm/development/core.js +56 -57
- package/dist/esm/development/core.js.map +4 -4
- package/dist/esm/development/index.js +99 -58
- package/dist/esm/development/index.js.map +4 -4
- package/dist/esm/development/react.js +56 -58
- package/dist/esm/development/react.js.map +4 -4
- package/dist/esm/production/core.js +1 -1
- package/dist/esm/production/index.js +3 -3
- package/dist/esm/production/react.js +1 -1
- package/dist/types/actor.d.ts +4 -4
- package/dist/types/actor.d.ts.map +1 -1
- package/dist/types/context-bound.d.ts +94 -0
- package/dist/types/context-bound.d.ts.map +1 -0
- package/dist/types/entry-react.d.ts +1 -1
- package/dist/types/entry-react.d.ts.map +1 -1
- package/dist/types/functional-combinators.d.ts +5 -5
- package/dist/types/generators.d.ts +2 -2
- package/dist/types/index.d.ts +14 -34
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/internal-transitions.d.ts +5 -0
- package/dist/types/internal-transitions.d.ts.map +1 -0
- package/dist/types/primitives.d.ts +25 -5
- package/dist/types/primitives.d.ts.map +1 -1
- package/dist/types/react.d.ts.map +1 -1
- package/dist/types/utils.d.ts +22 -22
- package/dist/types/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/actor.ts +1 -1
- package/src/context-bound.ts +160 -0
- package/src/entry-react.ts +9 -2
- package/src/functional-combinators.ts +5 -5
- package/src/generators.ts +2 -2
- package/src/higher-order.ts +2 -2
- package/src/index.ts +47 -80
- package/src/internal-transitions.ts +32 -0
- package/src/middleware/time-travel.ts +2 -2
- package/src/middleware.ts +2 -2
- package/src/multi.ts +4 -4
- package/src/primitives.ts +34 -14
- package/src/prototype_functional.ts +2 -2
- package/src/react.ts +1 -2
- package/src/test.ts +7 -7
- 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
|
+
}
|
package/src/entry-react.ts
CHANGED
|
@@ -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
|
|
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
|
*
|
package/src/higher-order.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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:
|
|
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
|
-
):
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
425
|
-
|
|
426
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
509
|
-
|
|
510
|
-
|
|
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
|
|
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>)(
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
+
}
|