@doeixd/machine 0.0.23 → 1.0.1
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 +19 -45
- package/dist/cjs/development/core.js.map +3 -3
- package/dist/cjs/development/index.js +51 -46
- package/dist/cjs/development/index.js.map +4 -4
- package/dist/cjs/development/react.js +19 -46
- package/dist/cjs/development/react.js.map +3 -3
- 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 +19 -45
- package/dist/esm/development/core.js.map +3 -3
- package/dist/esm/development/index.js +51 -46
- package/dist/esm/development/index.js.map +4 -4
- package/dist/esm/development/react.js +19 -46
- package/dist/esm/development/react.js.map +3 -3
- 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 -29
- package/dist/types/index.d.ts.map +1 -1
- 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 +147 -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 +31 -68
- 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
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
|
@@ -198,12 +198,12 @@ export type Event<M extends BaseMachine<any>> = {
|
|
|
198
198
|
* const counterTransitions = {
|
|
199
199
|
* increment() {
|
|
200
200
|
* // `this` is now fully typed!
|
|
201
|
-
* // IntelliSense knows `this.count` is a number and
|
|
201
|
+
* // IntelliSense knows `this.context.count` is a number and
|
|
202
202
|
* // `this.transitions.add` is a function.
|
|
203
|
-
* return createMachine({ count: this.count + 1 }, this.transitions);
|
|
203
|
+
* return createMachine({ count: this.context.count + 1 }, this.transitions);
|
|
204
204
|
* },
|
|
205
205
|
* add(n: number) {
|
|
206
|
-
* return createMachine({ count: this.count + n }, this.transitions);
|
|
206
|
+
* return createMachine({ count: this.context.count + n }, this.transitions);
|
|
207
207
|
* },
|
|
208
208
|
* // ❌ TypeScript will immediately throw a compile error on the next line
|
|
209
209
|
* // because the return type 'string' does not satisfy 'Machine<any>'.
|
|
@@ -220,7 +220,7 @@ export type Event<M extends BaseMachine<any>> = {
|
|
|
220
220
|
* }
|
|
221
221
|
*/
|
|
222
222
|
export type TransitionsFor<C extends object, T extends Record<string, any>> = {
|
|
223
|
-
[K in keyof T]: (this: C
|
|
223
|
+
[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
224
|
};
|
|
225
225
|
|
|
226
226
|
/**
|
|
@@ -228,7 +228,7 @@ export type TransitionsFor<C extends object, T extends Record<string, any>> = {
|
|
|
228
228
|
* type-checking for standalone asynchronous transition objects.
|
|
229
229
|
*/
|
|
230
230
|
export type AsyncTransitionsFor<C extends object, T extends Record<string, any>> = {
|
|
231
|
-
[K in keyof T]: (this: C
|
|
231
|
+
[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
232
|
};
|
|
233
233
|
|
|
234
234
|
/**
|
|
@@ -345,24 +345,26 @@ export type BindTransitions<T> = {
|
|
|
345
345
|
* @param factory - A function that receives a `transition` helper and returns the transitions object.
|
|
346
346
|
* @returns A new machine instance.
|
|
347
347
|
*/
|
|
348
|
-
export function createMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any> = Record<string, (this: C, ...args: any[]) => any>>(
|
|
348
|
+
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
349
|
context: C,
|
|
350
350
|
factory: (transition: (newContext: C) => Machine<C, any>) => T
|
|
351
|
-
): Machine<C,
|
|
351
|
+
): Machine<C, T>;
|
|
352
352
|
|
|
353
353
|
/**
|
|
354
354
|
* Creates a synchronous state machine from a context and transition functions.
|
|
355
355
|
* This is the core factory for the functional approach.
|
|
356
|
+
* Transitions receive the full machine as `this`, allowing them to access
|
|
357
|
+
* `this.context` and call other transitions via `this.otherTransition()`.
|
|
356
358
|
*
|
|
357
359
|
* @template C - The context object type.
|
|
358
360
|
* @param context - The initial state context.
|
|
359
361
|
* @param fns - An object containing transition function definitions.
|
|
360
362
|
* @returns A new machine instance.
|
|
361
363
|
*/
|
|
362
|
-
export function createMachine<C extends object, T extends Record<string, (this:
|
|
364
|
+
export function createMachine<C extends object, T extends Record<string, (this: Machine<C, T>, ...args: any[]) => any> & { context?: any }>(
|
|
363
365
|
context: C,
|
|
364
366
|
fns: T
|
|
365
|
-
):
|
|
367
|
+
): Machine<C, T>;
|
|
366
368
|
|
|
367
369
|
/**
|
|
368
370
|
* Creates a synchronous state machine by copying context and transitions from an existing machine.
|
|
@@ -379,45 +381,15 @@ export function createMachine<C extends object, M extends BaseMachine<C>>(
|
|
|
379
381
|
machine: M
|
|
380
382
|
): Machine<C, Transitions<M>>;
|
|
381
383
|
|
|
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
384
|
export function createMachine(context: any, fnsOrFactory: any): any {
|
|
397
385
|
if (typeof fnsOrFactory === 'function') {
|
|
398
386
|
let transitions: any;
|
|
399
387
|
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);
|
|
388
|
+
return createMachine(newContext, transitions);
|
|
409
389
|
};
|
|
410
390
|
transitions = fnsOrFactory(transition);
|
|
411
391
|
|
|
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);
|
|
392
|
+
return Object.assign({ context }, transitions);
|
|
421
393
|
}
|
|
422
394
|
|
|
423
395
|
// If fns is a machine (has context property), extract just the transition functions
|
|
@@ -425,10 +397,6 @@ export function createMachine(context: any, fnsOrFactory: any): any {
|
|
|
425
397
|
Object.entries(fnsOrFactory).filter(([key]) => key !== 'context')
|
|
426
398
|
) : fnsOrFactory;
|
|
427
399
|
|
|
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.
|
|
431
|
-
|
|
432
400
|
const machine = Object.assign({ context }, transitions);
|
|
433
401
|
return machine;
|
|
434
402
|
}
|
|
@@ -481,27 +449,11 @@ export function createAsyncMachine(context: any, fnsOrFactory: any): any {
|
|
|
481
449
|
if (typeof fnsOrFactory === 'function') {
|
|
482
450
|
let transitions: any;
|
|
483
451
|
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);
|
|
452
|
+
return createAsyncMachine(newContext, transitions);
|
|
493
453
|
};
|
|
494
454
|
transitions = fnsOrFactory(transition);
|
|
495
455
|
|
|
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);
|
|
456
|
+
return Object.assign({ context }, transitions);
|
|
505
457
|
}
|
|
506
458
|
|
|
507
459
|
// If fns is a machine (has context property), extract just the transition functions
|
|
@@ -682,14 +634,14 @@ export function extendTransitions<
|
|
|
682
634
|
* // Define two independent machines
|
|
683
635
|
* const createCounter = (initial: number) =>
|
|
684
636
|
* createMachine({ count: initial }, {
|
|
685
|
-
* increment: function() { return createMachine({ count: this.count + 1 }, this); },
|
|
686
|
-
* decrement: function() { return createMachine({ count: this.count - 1 }, this); }
|
|
637
|
+
* increment: function() { return createMachine({ count: this.context.count + 1 }, this); },
|
|
638
|
+
* decrement: function() { return createMachine({ count: this.context.count - 1 }, this); }
|
|
687
639
|
* });
|
|
688
640
|
*
|
|
689
641
|
* const createLogger = () =>
|
|
690
642
|
* createMachine({ logs: [] as string[] }, {
|
|
691
643
|
* log: function(message: string) {
|
|
692
|
-
* return createMachine({ logs: [...this.logs, message] }, this);
|
|
644
|
+
* return createMachine({ logs: [...this.context.logs, message] }, this);
|
|
693
645
|
* },
|
|
694
646
|
* clear: function() {
|
|
695
647
|
* return createMachine({ logs: [] }, this);
|
|
@@ -872,7 +824,7 @@ export function runMachine<M extends AsyncMachine<any>>(
|
|
|
872
824
|
|
|
873
825
|
try {
|
|
874
826
|
// 3. Pass the signal to the transition function.
|
|
875
|
-
const nextStatePromise = fn.apply(current
|
|
827
|
+
const nextStatePromise = fn.apply(current, [...event.args, { signal: controller.signal }]);
|
|
876
828
|
|
|
877
829
|
const nextState = await nextStatePromise;
|
|
878
830
|
|
|
@@ -1164,4 +1116,15 @@ export {
|
|
|
1164
1116
|
fromObservable,
|
|
1165
1117
|
type ActorRef,
|
|
1166
1118
|
type InspectionEvent
|
|
1167
|
-
} from './actor';
|
|
1119
|
+
} from './actor';
|
|
1120
|
+
|
|
1121
|
+
// =============================================================================
|
|
1122
|
+
// SECTION: CONTEXT-BOUND UTILITIES
|
|
1123
|
+
// =============================================================================
|
|
1124
|
+
|
|
1125
|
+
export {
|
|
1126
|
+
createContextBoundMachine,
|
|
1127
|
+
callWithContext,
|
|
1128
|
+
isContextBound,
|
|
1129
|
+
type ContextBoundMachine
|
|
1130
|
+
} from './context-bound';
|
|
@@ -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
|
+
}
|
package/src/primitives.ts
CHANGED
|
@@ -354,6 +354,14 @@ export type GuardedTransition<
|
|
|
354
354
|
* Creates a synchronous runtime guard that checks conditions before executing transitions.
|
|
355
355
|
* This provides actual runtime protection with synchronous execution - use this for the majority of cases.
|
|
356
356
|
*
|
|
357
|
+
* **IMPORTANT - Context-Bound Limitation:**
|
|
358
|
+
* Guards accept calls with either `this === machine` or `this === context`, but when called
|
|
359
|
+
* with context-only binding, the guard normalizes to `{ context }` before passing to the transition.
|
|
360
|
+
* This means:
|
|
361
|
+
* - ✅ Transitions can access `this.context`
|
|
362
|
+
* - ❌ Transitions CANNOT call `this.otherTransition()` (no transitions property)
|
|
363
|
+
* - Recommended: Use guards only with machine-bound transitions for full composition support
|
|
364
|
+
*
|
|
357
365
|
* @template C - The context type
|
|
358
366
|
* @template TSuccess - The transition return type when condition passes
|
|
359
367
|
* @template TFailure - The fallback return type when condition fails (defaults to Machine<C>)
|
|
@@ -367,8 +375,10 @@ export type GuardedTransition<
|
|
|
367
375
|
* const machine = createMachine({ balance: 100 }, {
|
|
368
376
|
* withdraw: guard(
|
|
369
377
|
* (ctx, amount) => ctx.balance >= amount,
|
|
370
|
-
* function(amount: number) {
|
|
371
|
-
*
|
|
378
|
+
* function(this: Machine<{balance: number}>, amount: number) {
|
|
379
|
+
* // ✅ Can access this.context
|
|
380
|
+
* return createMachine({ balance: this.context.balance - amount }, this);
|
|
381
|
+
* // ❌ Cannot call this.otherTransition() if guard was called with context-only binding
|
|
372
382
|
* },
|
|
373
383
|
* { onFail: 'throw', errorMessage: 'Insufficient funds' }
|
|
374
384
|
* )
|
|
@@ -403,9 +413,9 @@ export function guard<
|
|
|
403
413
|
|
|
404
414
|
if (conditionResult) {
|
|
405
415
|
// Condition passed, execute the transition
|
|
406
|
-
// Transition functions expect 'this' to be the
|
|
407
|
-
const
|
|
408
|
-
return transition.apply(
|
|
416
|
+
// Transition functions expect 'this' to be the machine
|
|
417
|
+
const machineForTransition = isMachine ? (this as Machine<C>) : { context: this as C };
|
|
418
|
+
return transition.apply(machineForTransition, args);
|
|
409
419
|
} else {
|
|
410
420
|
// Condition failed, handle according to options
|
|
411
421
|
if (onFail === 'throw') {
|
|
@@ -453,6 +463,14 @@ export function guard<
|
|
|
453
463
|
* This provides actual runtime protection, unlike the `guarded` primitive which only adds metadata.
|
|
454
464
|
* Use this when your condition or transition logic is asynchronous.
|
|
455
465
|
*
|
|
466
|
+
* **IMPORTANT - Context-Bound Limitation:**
|
|
467
|
+
* Guards accept calls with either `this === machine` or `this === context`, but when called
|
|
468
|
+
* with context-only binding, the guard normalizes to `{ context }` before passing to the transition.
|
|
469
|
+
* This means:
|
|
470
|
+
* - ✅ Transitions can access `this.context`
|
|
471
|
+
* - ❌ Transitions CANNOT call `this.otherTransition()` (no transitions property)
|
|
472
|
+
* - Recommended: Use guards only with machine-bound transitions for full composition support
|
|
473
|
+
*
|
|
456
474
|
* @template C - The context type
|
|
457
475
|
* @template TSuccess - The transition return type when condition passes
|
|
458
476
|
* @template TFailure - The fallback return type when condition fails (defaults to Machine<C>)
|
|
@@ -470,10 +488,12 @@ export function guard<
|
|
|
470
488
|
* await new Promise(resolve => setTimeout(resolve, 100));
|
|
471
489
|
* return ctx.balance >= amount;
|
|
472
490
|
* },
|
|
473
|
-
* async function(amount: number) {
|
|
491
|
+
* async function(this: Machine<{balance: number}>, amount: number) {
|
|
474
492
|
* // Simulate API call to process withdrawal
|
|
475
493
|
* await new Promise(resolve => setTimeout(resolve, 100));
|
|
476
|
-
*
|
|
494
|
+
* // ✅ Can access this.context
|
|
495
|
+
* return createMachine({ balance: this.context.balance - amount }, this);
|
|
496
|
+
* // ❌ Cannot call this.otherTransition() if guard was called with context-only binding
|
|
477
497
|
* },
|
|
478
498
|
* { onFail: 'throw', errorMessage: 'Insufficient funds' }
|
|
479
499
|
* )
|
|
@@ -508,9 +528,9 @@ export function guardAsync<
|
|
|
508
528
|
|
|
509
529
|
if (conditionResult) {
|
|
510
530
|
// Condition passed, execute the transition
|
|
511
|
-
// Transition functions expect 'this' to be the
|
|
512
|
-
const
|
|
513
|
-
return transition.apply(
|
|
531
|
+
// Transition functions expect 'this' to be the machine
|
|
532
|
+
const machineForTransition = isMachine ? (this as Machine<C>) : { context: this as C };
|
|
533
|
+
return transition.apply(machineForTransition, args);
|
|
514
534
|
} else {
|
|
515
535
|
// Condition failed, handle according to options
|
|
516
536
|
if (onFail === 'throw') {
|
|
@@ -571,7 +591,7 @@ export function guardAsync<
|
|
|
571
591
|
* withdraw: guardSync(
|
|
572
592
|
* (ctx, amount) => ctx.balance >= amount,
|
|
573
593
|
* function(amount: number) {
|
|
574
|
-
* return createMachine({ balance: this.balance - amount }, this);
|
|
594
|
+
* return createMachine({ balance: this.context.balance - amount }, this);
|
|
575
595
|
* },
|
|
576
596
|
* { onFail: 'throw', errorMessage: 'Insufficient funds' }
|
|
577
597
|
* )
|
|
@@ -606,9 +626,9 @@ export function guardSync<
|
|
|
606
626
|
|
|
607
627
|
if (conditionResult) {
|
|
608
628
|
// Condition passed, execute the transition
|
|
609
|
-
// Transition functions expect 'this' to be the
|
|
610
|
-
const
|
|
611
|
-
return transition.apply(
|
|
629
|
+
// Transition functions expect 'this' to be the machine
|
|
630
|
+
const machineForTransition = isMachine ? (this as Machine<C>) : { context: this as C };
|
|
631
|
+
return transition.apply(machineForTransition, args);
|
|
612
632
|
} else {
|
|
613
633
|
// Condition failed, handle according to options
|
|
614
634
|
if (onFail === 'throw') {
|
|
@@ -33,10 +33,10 @@ type Context = { count: number };
|
|
|
33
33
|
const machine = createMachine({ count: 0 }, (transition) => ({
|
|
34
34
|
inc() {
|
|
35
35
|
// 'this' should be Context
|
|
36
|
-
return transition({ count: this.count + 1 });
|
|
36
|
+
return transition({ count: this.context.count + 1 });
|
|
37
37
|
},
|
|
38
38
|
add(n: number) {
|
|
39
|
-
return transition({ count: this.count + n });
|
|
39
|
+
return transition({ count: this.context.count + n });
|
|
40
40
|
}
|
|
41
41
|
}));
|
|
42
42
|
|
package/src/react.ts
CHANGED
|
@@ -302,7 +302,6 @@ export function useActorSelector<M extends BaseMachine<any>, T>(
|
|
|
302
302
|
selector: (state: M) => T,
|
|
303
303
|
isEqual: (a: T, b: T) => boolean = Object.is
|
|
304
304
|
): T {
|
|
305
|
-
const subscribe = useMemo(() => actor.subscribe.bind(actor), [actor]);
|
|
306
305
|
const getSnapshot = useMemo(() => actor.getSnapshot.bind(actor), [actor]);
|
|
307
306
|
|
|
308
307
|
const getSelection = () => selector(getSnapshot());
|
|
@@ -333,4 +332,4 @@ export function useActorSelector<M extends BaseMachine<any>, T>(
|
|
|
333
332
|
}, [actor, selector, isEqual]);
|
|
334
333
|
|
|
335
334
|
return selection;
|
|
336
|
-
}
|
|
335
|
+
}
|
package/src/test.ts
CHANGED
|
@@ -27,7 +27,7 @@ type AsyncFunctions<C extends object> =
|
|
|
27
27
|
* const machine: Machine<{ count: number }> = {
|
|
28
28
|
* context: { count: 0 },
|
|
29
29
|
* increment: function() {
|
|
30
|
-
* return createMachine({ count: this.count + 1 }, this)
|
|
30
|
+
* return createMachine({ count: this.context.count + 1 }, this)
|
|
31
31
|
* }
|
|
32
32
|
* }
|
|
33
33
|
*/
|
|
@@ -57,10 +57,10 @@ export type AsyncMachine<C extends object> = { context: C } & AsyncFunctions<C>
|
|
|
57
57
|
* { count: 0 },
|
|
58
58
|
* {
|
|
59
59
|
* increment: function() {
|
|
60
|
-
* return createMachine({ count: this.count + 1 }, this)
|
|
60
|
+
* return createMachine({ count: this.context.count + 1 }, this)
|
|
61
61
|
* },
|
|
62
62
|
* decrement: function() {
|
|
63
|
-
* return createMachine({ count: this.count - 1 }, this)
|
|
63
|
+
* return createMachine({ count: this.context.count - 1 }, this)
|
|
64
64
|
* }
|
|
65
65
|
* }
|
|
66
66
|
* )
|
|
@@ -147,7 +147,7 @@ export type Event<M> = {
|
|
|
147
147
|
* { count: 0 },
|
|
148
148
|
* {
|
|
149
149
|
* increment: async function() {
|
|
150
|
-
* return createAsyncMachine({ count: this.count + 1 }, this)
|
|
150
|
+
* return createAsyncMachine({ count: this.context.count + 1 }, this)
|
|
151
151
|
* }
|
|
152
152
|
* }
|
|
153
153
|
* )
|
|
@@ -164,7 +164,7 @@ export function runMachine<C extends object>(
|
|
|
164
164
|
async function dispatch<E extends Event<typeof current>>(event: E) {
|
|
165
165
|
const fn = current[event.type] as any
|
|
166
166
|
if (!fn) throw new Error(`Unknown event: ${event.type}`)
|
|
167
|
-
const next = await fn.apply(current
|
|
167
|
+
const next = await fn.apply(current as any, event.args)
|
|
168
168
|
current = next
|
|
169
169
|
onChange?.(current)
|
|
170
170
|
return current
|
|
@@ -203,5 +203,5 @@ const counter = createMachine(
|
|
|
203
203
|
);
|
|
204
204
|
|
|
205
205
|
// Test by calling with proper context binding
|
|
206
|
-
const result = counterFns.increment.call(counter
|
|
207
|
-
console.log('Result:', result.context.count);
|
|
206
|
+
const result = counterFns.increment.call(counter);
|
|
207
|
+
console.log('Result:', result.context.count);
|