@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.
- package/README.md +158 -0
- package/dist/cjs/development/index.js +275 -5
- package/dist/cjs/development/index.js.map +4 -4
- package/dist/cjs/production/index.js +5 -5
- package/dist/esm/development/index.js +275 -5
- package/dist/esm/development/index.js.map +4 -4
- package/dist/esm/production/index.js +5 -5
- package/dist/types/extract.d.ts +40 -4
- package/dist/types/extract.d.ts.map +1 -1
- package/dist/types/generators.d.ts +40 -9
- package/dist/types/generators.d.ts.map +1 -1
- package/dist/types/higher-order.d.ts +221 -0
- package/dist/types/higher-order.d.ts.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/utils.d.ts +208 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/extract.ts +180 -8
- package/src/generators.ts +25 -25
- package/src/higher-order.ts +364 -0
- package/src/index.ts +18 -1
- package/src/utils.ts +171 -5
package/README.md
CHANGED
|
@@ -499,6 +499,68 @@ import { next } from "@doeixd/machine";
|
|
|
499
499
|
const updated = next(counter, (ctx) => ({ count: ctx.count + 1 }));
|
|
500
500
|
```
|
|
501
501
|
|
|
502
|
+
### Transition Binding Helpers
|
|
503
|
+
|
|
504
|
+
These utilities eliminate the need for `.call(m.context, ...)` boilerplate when invoking transitions.
|
|
505
|
+
|
|
506
|
+
#### `call<C, F>(fn, context, ...args)`
|
|
507
|
+
|
|
508
|
+
Explicitly binds a transition function to a context and invokes it. Useful when you need to call a transition with proper `this` binding.
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
import { call } from "@doeixd/machine";
|
|
512
|
+
|
|
513
|
+
type MyContext = { count: number };
|
|
514
|
+
const increment = function(this: MyContext) {
|
|
515
|
+
return { count: this.count + 1 };
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
const result = call(increment, { count: 5 }); // Returns { count: 6 }
|
|
519
|
+
|
|
520
|
+
// Particularly useful with generator-based flows:
|
|
521
|
+
const result = run(function* (m) {
|
|
522
|
+
m = yield* step(call(m.increment, m.context));
|
|
523
|
+
m = yield* step(call(m.add, m.context, 5));
|
|
524
|
+
return m;
|
|
525
|
+
}, counter);
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
#### `bindTransitions<M>(machine)`
|
|
529
|
+
|
|
530
|
+
Returns a Proxy that automatically binds all transition methods to the machine's context. Eliminates `.call(m.context, ...)` boilerplate entirely.
|
|
531
|
+
|
|
532
|
+
```typescript
|
|
533
|
+
import { bindTransitions } from "@doeixd/machine";
|
|
534
|
+
|
|
535
|
+
const counter = bindTransitions(createMachine(
|
|
536
|
+
{ count: 0 },
|
|
537
|
+
{
|
|
538
|
+
increment(this: { count: number }) {
|
|
539
|
+
return createMachine({ count: this.count + 1 }, this);
|
|
540
|
+
},
|
|
541
|
+
add(this: { count: number }, n: number) {
|
|
542
|
+
return createMachine({ count: this.count + n }, this);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
));
|
|
546
|
+
|
|
547
|
+
// All methods are automatically bound - no need for .call()!
|
|
548
|
+
const next = counter.increment(); // Works!
|
|
549
|
+
const result = counter.add(5); // Works!
|
|
550
|
+
|
|
551
|
+
// Great for generator-based flows:
|
|
552
|
+
const result = run(function* (m) {
|
|
553
|
+
m = yield* step(m.increment()); // Clean syntax!
|
|
554
|
+
m = yield* step(m.add(5)); // No .call() needed
|
|
555
|
+
return m;
|
|
556
|
+
}, counter);
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**How it works:**
|
|
560
|
+
The Proxy intercepts all property access on the machine. When a property is a function (transition method), it wraps it to automatically call `.apply(machine.context, args)` before invoking. Non-callable properties are returned as-is.
|
|
561
|
+
|
|
562
|
+
**Note:** The Proxy preserves type safety while providing ergonomic syntax. Use this when writing generator-based flows or any code that frequently calls transitions.
|
|
563
|
+
|
|
502
564
|
#### `matchMachine<M, K, R>(machine, key, handlers)`
|
|
503
565
|
|
|
504
566
|
Type-safe pattern matching on discriminated unions in context.
|
|
@@ -1673,6 +1735,98 @@ const charts = extractMachines({
|
|
|
1673
1735
|
});
|
|
1674
1736
|
```
|
|
1675
1737
|
|
|
1738
|
+
### Advanced Patterns: Hierarchical and Parallel Machines
|
|
1739
|
+
|
|
1740
|
+
**NEW**: The extractor now supports advanced state machine patterns for complex systems.
|
|
1741
|
+
|
|
1742
|
+
#### Hierarchical (Nested States)
|
|
1743
|
+
|
|
1744
|
+
Model parent states containing child states:
|
|
1745
|
+
|
|
1746
|
+
```typescript
|
|
1747
|
+
const config: MachineConfig = {
|
|
1748
|
+
input: 'src/dashboard.ts',
|
|
1749
|
+
classes: ['Dashboard', 'ErrorState'],
|
|
1750
|
+
id: 'dashboard',
|
|
1751
|
+
initialState: 'Dashboard',
|
|
1752
|
+
children: {
|
|
1753
|
+
contextProperty: 'child',
|
|
1754
|
+
initialState: 'ViewingMachine',
|
|
1755
|
+
classes: ['ViewingMachine', 'EditingMachine']
|
|
1756
|
+
}
|
|
1757
|
+
};
|
|
1758
|
+
```
|
|
1759
|
+
|
|
1760
|
+
Generates:
|
|
1761
|
+
|
|
1762
|
+
```json
|
|
1763
|
+
{
|
|
1764
|
+
"id": "dashboard",
|
|
1765
|
+
"initial": "Dashboard",
|
|
1766
|
+
"states": {
|
|
1767
|
+
"Dashboard": {
|
|
1768
|
+
"initial": "ViewingMachine",
|
|
1769
|
+
"states": {
|
|
1770
|
+
"ViewingMachine": { "on": { /* ... */ } },
|
|
1771
|
+
"EditingMachine": { "on": { /* ... */ } }
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
```
|
|
1777
|
+
|
|
1778
|
+
#### Parallel (Orthogonal Regions)
|
|
1779
|
+
|
|
1780
|
+
Model independent regions that evolve simultaneously:
|
|
1781
|
+
|
|
1782
|
+
```typescript
|
|
1783
|
+
const config: MachineConfig = {
|
|
1784
|
+
input: 'src/editor.ts',
|
|
1785
|
+
id: 'editor',
|
|
1786
|
+
parallel: {
|
|
1787
|
+
regions: [
|
|
1788
|
+
{
|
|
1789
|
+
name: 'fontWeight',
|
|
1790
|
+
initialState: 'Normal',
|
|
1791
|
+
classes: ['Normal', 'Bold']
|
|
1792
|
+
},
|
|
1793
|
+
{
|
|
1794
|
+
name: 'textDecoration',
|
|
1795
|
+
initialState: 'None',
|
|
1796
|
+
classes: ['None', 'Underline']
|
|
1797
|
+
}
|
|
1798
|
+
]
|
|
1799
|
+
}
|
|
1800
|
+
};
|
|
1801
|
+
```
|
|
1802
|
+
|
|
1803
|
+
Generates:
|
|
1804
|
+
|
|
1805
|
+
```json
|
|
1806
|
+
{
|
|
1807
|
+
"id": "editor",
|
|
1808
|
+
"type": "parallel",
|
|
1809
|
+
"states": {
|
|
1810
|
+
"fontWeight": {
|
|
1811
|
+
"initial": "Normal",
|
|
1812
|
+
"states": {
|
|
1813
|
+
"Normal": { "on": { /* ... */ } },
|
|
1814
|
+
"Bold": { "on": { /* ... */ } }
|
|
1815
|
+
}
|
|
1816
|
+
},
|
|
1817
|
+
"textDecoration": {
|
|
1818
|
+
"initial": "None",
|
|
1819
|
+
"states": {
|
|
1820
|
+
"None": { "on": { /* ... */ } },
|
|
1821
|
+
"Underline": { "on": { /* ... */ } }
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
```
|
|
1827
|
+
|
|
1828
|
+
**See [docs/ADVANCED_EXTRACTION.md](./docs/ADVANCED_EXTRACTION.md) for complete guide.**
|
|
1829
|
+
|
|
1676
1830
|
### Runtime Extraction API
|
|
1677
1831
|
|
|
1678
1832
|
Extract statecharts from **running machine instances** without requiring source code access:
|
|
@@ -1884,6 +2038,10 @@ overrideTransitions<M, T>(machine: M, overrides: T): M & T
|
|
|
1884
2038
|
extendTransitions<M, T>(machine: M, newTransitions: T): M & T
|
|
1885
2039
|
createMachineBuilder<M>(template: M): (context) => M
|
|
1886
2040
|
|
|
2041
|
+
// Transition Binding
|
|
2042
|
+
call<C, F>(fn: F, context: C, ...args): ReturnType<F>
|
|
2043
|
+
bindTransitions<M extends Machine<any>>(machine: M): M
|
|
2044
|
+
|
|
1887
2045
|
// Pattern Matching
|
|
1888
2046
|
matchMachine<M, K, R>(machine: M, key: K, handlers): R
|
|
1889
2047
|
hasState<M, K, V>(machine: M, key: K, value: V): boolean
|
|
@@ -20,20 +20,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
BoundMachine: () => BoundMachine,
|
|
23
24
|
META_KEY: () => META_KEY,
|
|
24
25
|
MachineBase: () => MachineBase,
|
|
25
26
|
MultiMachineBase: () => MultiMachineBase,
|
|
26
27
|
RUNTIME_META: () => RUNTIME_META,
|
|
27
28
|
action: () => action,
|
|
29
|
+
bindTransitions: () => bindTransitions,
|
|
30
|
+
call: () => call,
|
|
28
31
|
createAsyncMachine: () => createAsyncMachine,
|
|
29
32
|
createEnsemble: () => createEnsemble,
|
|
33
|
+
createEvent: () => createEvent,
|
|
34
|
+
createFetchMachine: () => createFetchMachine,
|
|
30
35
|
createFlow: () => createFlow,
|
|
31
36
|
createMachine: () => createMachine,
|
|
32
37
|
createMachineBuilder: () => createMachineBuilder,
|
|
33
38
|
createMachineFactory: () => createMachineFactory,
|
|
34
39
|
createMultiMachine: () => createMultiMachine,
|
|
35
40
|
createMutableMachine: () => createMutableMachine,
|
|
41
|
+
createParallelMachine: () => createParallelMachine,
|
|
36
42
|
createRunner: () => createRunner,
|
|
43
|
+
delegateToChild: () => delegateToChild,
|
|
37
44
|
describe: () => describe,
|
|
38
45
|
extendTransitions: () => extendTransitions,
|
|
39
46
|
extractFromInstance: () => extractFromInstance,
|
|
@@ -46,10 +53,14 @@ __export(src_exports, {
|
|
|
46
53
|
guarded: () => guarded,
|
|
47
54
|
hasState: () => hasState,
|
|
48
55
|
invoke: () => invoke,
|
|
56
|
+
isState: () => isState,
|
|
57
|
+
logState: () => logState,
|
|
49
58
|
matchMachine: () => matchMachine,
|
|
59
|
+
mergeContext: () => mergeContext,
|
|
50
60
|
metadata: () => metadata,
|
|
51
61
|
next: () => next,
|
|
52
62
|
overrideTransitions: () => overrideTransitions,
|
|
63
|
+
pipeTransitions: () => pipeTransitions,
|
|
53
64
|
run: () => run,
|
|
54
65
|
runAsync: () => runAsync,
|
|
55
66
|
runMachine: () => runMachine,
|
|
@@ -60,6 +71,7 @@ __export(src_exports, {
|
|
|
60
71
|
setContext: () => setContext,
|
|
61
72
|
step: () => step,
|
|
62
73
|
stepAsync: () => stepAsync,
|
|
74
|
+
toggle: () => toggle,
|
|
63
75
|
transitionTo: () => transitionTo,
|
|
64
76
|
yieldMachine: () => yieldMachine
|
|
65
77
|
});
|
|
@@ -256,17 +268,17 @@ function parseInvokeService(obj) {
|
|
|
256
268
|
}
|
|
257
269
|
return service;
|
|
258
270
|
}
|
|
259
|
-
function extractFromCallExpression(
|
|
260
|
-
if (!import_ts_morph.Node.isCallExpression(
|
|
271
|
+
function extractFromCallExpression(call2, verbose = false) {
|
|
272
|
+
if (!import_ts_morph.Node.isCallExpression(call2)) {
|
|
261
273
|
return null;
|
|
262
274
|
}
|
|
263
|
-
const expression =
|
|
275
|
+
const expression = call2.getExpression();
|
|
264
276
|
const fnName = import_ts_morph.Node.isIdentifier(expression) ? expression.getText() : null;
|
|
265
277
|
if (!fnName) {
|
|
266
278
|
return null;
|
|
267
279
|
}
|
|
268
280
|
const metadata2 = {};
|
|
269
|
-
const args =
|
|
281
|
+
const args = call2.getArguments();
|
|
270
282
|
switch (fnName) {
|
|
271
283
|
case "transitionTo":
|
|
272
284
|
if (args[0]) {
|
|
@@ -406,6 +418,26 @@ function analyzeStateNode(classSymbol, verbose = false) {
|
|
|
406
418
|
}
|
|
407
419
|
return chartNode;
|
|
408
420
|
}
|
|
421
|
+
function analyzeStateNodeWithNesting(className, classSymbol, sourceFile, childConfig, verbose = false) {
|
|
422
|
+
const stateNode = analyzeStateNode(classSymbol, verbose);
|
|
423
|
+
if (childConfig) {
|
|
424
|
+
if (verbose) {
|
|
425
|
+
console.error(` 👪 Analyzing children for state: ${className}`);
|
|
426
|
+
}
|
|
427
|
+
stateNode.initial = childConfig.initialState;
|
|
428
|
+
stateNode.states = {};
|
|
429
|
+
for (const childClassName of childConfig.classes) {
|
|
430
|
+
const childClassDeclaration = sourceFile.getClass(childClassName);
|
|
431
|
+
if (childClassDeclaration) {
|
|
432
|
+
const childSymbol = childClassDeclaration.getSymbolOrThrow();
|
|
433
|
+
stateNode.states[childClassName] = analyzeStateNode(childSymbol, verbose);
|
|
434
|
+
} else {
|
|
435
|
+
console.warn(`⚠️ Warning: Child class '${childClassName}' not found.`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return stateNode;
|
|
440
|
+
}
|
|
409
441
|
function extractMachine(config, project, verbose = false) {
|
|
410
442
|
if (verbose) {
|
|
411
443
|
console.error(`
|
|
@@ -416,6 +448,45 @@ function extractMachine(config, project, verbose = false) {
|
|
|
416
448
|
if (!sourceFile) {
|
|
417
449
|
throw new Error(`Source file not found: ${config.input}`);
|
|
418
450
|
}
|
|
451
|
+
if (config.parallel) {
|
|
452
|
+
if (verbose) {
|
|
453
|
+
console.error(` ⏹️ Parallel machine detected. Analyzing regions.`);
|
|
454
|
+
}
|
|
455
|
+
const parallelChart = {
|
|
456
|
+
id: config.id,
|
|
457
|
+
type: "parallel",
|
|
458
|
+
states: {}
|
|
459
|
+
};
|
|
460
|
+
if (config.description) {
|
|
461
|
+
parallelChart.description = config.description;
|
|
462
|
+
}
|
|
463
|
+
for (const region of config.parallel.regions) {
|
|
464
|
+
if (verbose) {
|
|
465
|
+
console.error(` 📍 Analyzing region: ${region.name}`);
|
|
466
|
+
}
|
|
467
|
+
const regionStates = {};
|
|
468
|
+
for (const className of region.classes) {
|
|
469
|
+
const classDeclaration = sourceFile.getClass(className);
|
|
470
|
+
if (classDeclaration) {
|
|
471
|
+
const classSymbol = classDeclaration.getSymbolOrThrow();
|
|
472
|
+
regionStates[className] = analyzeStateNode(classSymbol, verbose);
|
|
473
|
+
} else {
|
|
474
|
+
console.warn(`⚠️ Warning: Class '${className}' not found for region '${region.name}'.`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
parallelChart.states[region.name] = {
|
|
478
|
+
initial: region.initialState,
|
|
479
|
+
states: regionStates
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
if (verbose) {
|
|
483
|
+
console.error(` ✅ Extracted ${config.parallel.regions.length} parallel regions`);
|
|
484
|
+
}
|
|
485
|
+
return parallelChart;
|
|
486
|
+
}
|
|
487
|
+
if (!config.initialState || !config.classes) {
|
|
488
|
+
throw new Error(`Machine config for '${config.id}' must have either 'parallel' or 'initialState'/'classes'.`);
|
|
489
|
+
}
|
|
419
490
|
const fullChart = {
|
|
420
491
|
id: config.id,
|
|
421
492
|
initial: config.initialState,
|
|
@@ -431,7 +502,14 @@ function extractMachine(config, project, verbose = false) {
|
|
|
431
502
|
continue;
|
|
432
503
|
}
|
|
433
504
|
const classSymbol = classDeclaration.getSymbolOrThrow();
|
|
434
|
-
const
|
|
505
|
+
const hasChildren = className === config.initialState && config.children;
|
|
506
|
+
const stateNode = analyzeStateNodeWithNesting(
|
|
507
|
+
className,
|
|
508
|
+
classSymbol,
|
|
509
|
+
sourceFile,
|
|
510
|
+
hasChildren ? config.children : void 0,
|
|
511
|
+
verbose
|
|
512
|
+
);
|
|
435
513
|
fullChart.states[className] = stateNode;
|
|
436
514
|
}
|
|
437
515
|
if (verbose) {
|
|
@@ -797,6 +875,198 @@ function createMutableMachine(sharedContext, factories, getDiscriminant) {
|
|
|
797
875
|
});
|
|
798
876
|
}
|
|
799
877
|
|
|
878
|
+
// src/higher-order.ts
|
|
879
|
+
function delegateToChild(actionName) {
|
|
880
|
+
return function(...args) {
|
|
881
|
+
const child = this.context.child;
|
|
882
|
+
if (typeof child[actionName] === "function") {
|
|
883
|
+
const newChildState = child[actionName](...args);
|
|
884
|
+
return setContext(this, { ...this.context, child: newChildState });
|
|
885
|
+
}
|
|
886
|
+
return this;
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
function toggle(prop) {
|
|
890
|
+
return function() {
|
|
891
|
+
if (typeof this.context[prop] !== "boolean") {
|
|
892
|
+
console.warn(`[toggle primitive] Property '${String(prop)}' is not a boolean. Toggling may have unexpected results.`);
|
|
893
|
+
}
|
|
894
|
+
return setContext(this, {
|
|
895
|
+
...this.context,
|
|
896
|
+
[prop]: !this.context[prop]
|
|
897
|
+
});
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
var IdleMachine = class extends MachineBase {
|
|
901
|
+
constructor(config) {
|
|
902
|
+
super({ status: "idle" });
|
|
903
|
+
this.config = config;
|
|
904
|
+
this.fetch = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
var LoadingMachine = class extends MachineBase {
|
|
908
|
+
constructor(config, params, attempts) {
|
|
909
|
+
super({ status: "loading", abortController: new AbortController(), attempts });
|
|
910
|
+
this.config = config;
|
|
911
|
+
this.params = params;
|
|
912
|
+
this.succeed = (data) => {
|
|
913
|
+
var _a, _b;
|
|
914
|
+
(_b = (_a = this.config).onSuccess) == null ? void 0 : _b.call(_a, data);
|
|
915
|
+
return new SuccessMachine(this.config, { status: "success", data });
|
|
916
|
+
};
|
|
917
|
+
this.fail = (error) => {
|
|
918
|
+
var _a, _b, _c;
|
|
919
|
+
const maxRetries = (_a = this.config.maxRetries) != null ? _a : 3;
|
|
920
|
+
if (this.context.attempts < maxRetries) {
|
|
921
|
+
return new RetryingMachine(this.config, this.params, error, this.context.attempts);
|
|
922
|
+
}
|
|
923
|
+
(_c = (_b = this.config).onError) == null ? void 0 : _c.call(_b, error);
|
|
924
|
+
return new ErrorMachine(this.config, { status: "error", error });
|
|
925
|
+
};
|
|
926
|
+
this.cancel = () => {
|
|
927
|
+
this.context.abortController.abort();
|
|
928
|
+
return new CanceledMachine(this.config);
|
|
929
|
+
};
|
|
930
|
+
this.execute();
|
|
931
|
+
}
|
|
932
|
+
async execute() {
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
var RetryingMachine = class extends MachineBase {
|
|
936
|
+
constructor(config, params, error, attempts) {
|
|
937
|
+
super({ status: "retrying", error, attempts });
|
|
938
|
+
this.config = config;
|
|
939
|
+
this.params = params;
|
|
940
|
+
// This would be called after a delay.
|
|
941
|
+
this.retry = (params) => new LoadingMachine(this.config, params != null ? params : this.params, this.context.attempts + 1);
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
var SuccessMachine = class extends MachineBase {
|
|
945
|
+
constructor(config, context) {
|
|
946
|
+
super(context);
|
|
947
|
+
this.config = config;
|
|
948
|
+
this.refetch = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
var ErrorMachine = class extends MachineBase {
|
|
952
|
+
constructor(config, context) {
|
|
953
|
+
super(context);
|
|
954
|
+
this.config = config;
|
|
955
|
+
this.retry = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
var CanceledMachine = class extends MachineBase {
|
|
959
|
+
constructor(config) {
|
|
960
|
+
super({ status: "canceled" });
|
|
961
|
+
this.config = config;
|
|
962
|
+
this.refetch = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
function createFetchMachine(config) {
|
|
966
|
+
return new IdleMachine(config);
|
|
967
|
+
}
|
|
968
|
+
function createParallelMachine(m1, m2) {
|
|
969
|
+
const combinedContext = { ...m1.context, ...m2.context };
|
|
970
|
+
const transitions1 = { ...m1 };
|
|
971
|
+
const transitions2 = { ...m2 };
|
|
972
|
+
delete transitions1.context;
|
|
973
|
+
delete transitions2.context;
|
|
974
|
+
const combinedTransitions = {};
|
|
975
|
+
for (const key in transitions1) {
|
|
976
|
+
const transitionFn = transitions1[key];
|
|
977
|
+
combinedTransitions[key] = (...args) => {
|
|
978
|
+
const nextM1 = transitionFn.apply(m1.context, args);
|
|
979
|
+
return createParallelMachine(nextM1, m2);
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
for (const key in transitions2) {
|
|
983
|
+
const transitionFn = transitions2[key];
|
|
984
|
+
combinedTransitions[key] = (...args) => {
|
|
985
|
+
const nextM2 = transitionFn.apply(m2.context, args);
|
|
986
|
+
return createParallelMachine(m1, nextM2);
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
return {
|
|
990
|
+
context: combinedContext,
|
|
991
|
+
...combinedTransitions
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// src/utils.ts
|
|
996
|
+
function isState(machine, machineClass) {
|
|
997
|
+
return machine instanceof machineClass;
|
|
998
|
+
}
|
|
999
|
+
function createEvent(type, ...args) {
|
|
1000
|
+
return { type, args };
|
|
1001
|
+
}
|
|
1002
|
+
function mergeContext(machine, partialContext) {
|
|
1003
|
+
return setContext(machine, (ctx) => ({ ...ctx, ...partialContext }));
|
|
1004
|
+
}
|
|
1005
|
+
async function pipeTransitions(initialMachine, ...transitions) {
|
|
1006
|
+
let current = initialMachine;
|
|
1007
|
+
for (const transitionFn of transitions) {
|
|
1008
|
+
current = await transitionFn(current);
|
|
1009
|
+
}
|
|
1010
|
+
return current;
|
|
1011
|
+
}
|
|
1012
|
+
function logState(machine, label) {
|
|
1013
|
+
if (label) {
|
|
1014
|
+
console.log(label, machine.context);
|
|
1015
|
+
} else {
|
|
1016
|
+
console.log(machine.context);
|
|
1017
|
+
}
|
|
1018
|
+
return machine;
|
|
1019
|
+
}
|
|
1020
|
+
function call(fn, context, ...args) {
|
|
1021
|
+
return fn.apply(context, args);
|
|
1022
|
+
}
|
|
1023
|
+
function bindTransitions(machine) {
|
|
1024
|
+
return new Proxy(machine, {
|
|
1025
|
+
get(target, prop) {
|
|
1026
|
+
const value = target[prop];
|
|
1027
|
+
if (typeof value === "function") {
|
|
1028
|
+
return function(...args) {
|
|
1029
|
+
const result = value.apply(target.context, args);
|
|
1030
|
+
if (result && typeof result === "object" && "context" in result) {
|
|
1031
|
+
return bindTransitions(result);
|
|
1032
|
+
}
|
|
1033
|
+
return result;
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
return value;
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
var BoundMachine = class _BoundMachine {
|
|
1041
|
+
constructor(machine) {
|
|
1042
|
+
this.wrappedMachine = machine;
|
|
1043
|
+
return new Proxy(this, {
|
|
1044
|
+
get: (target, prop) => {
|
|
1045
|
+
if (prop === "wrappedMachine" || prop === "context") {
|
|
1046
|
+
return Reflect.get(target, prop);
|
|
1047
|
+
}
|
|
1048
|
+
const value = this.wrappedMachine[prop];
|
|
1049
|
+
if (typeof value === "function") {
|
|
1050
|
+
return (...args) => {
|
|
1051
|
+
const result = value.apply(this.wrappedMachine.context, args);
|
|
1052
|
+
if (result && typeof result === "object" && "context" in result) {
|
|
1053
|
+
return new _BoundMachine(result);
|
|
1054
|
+
}
|
|
1055
|
+
return result;
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
return value;
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Access the underlying machine's context directly.
|
|
1064
|
+
*/
|
|
1065
|
+
get context() {
|
|
1066
|
+
return this.wrappedMachine.context;
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
|
|
800
1070
|
// src/index.ts
|
|
801
1071
|
function createMachine(context, fns) {
|
|
802
1072
|
return Object.assign({ context }, fns);
|