@doeixd/machine 0.0.13 → 0.0.18
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 +77 -25
- package/dist/cjs/development/core.js +1852 -0
- package/dist/cjs/development/core.js.map +7 -0
- package/dist/cjs/development/index.js +1377 -1372
- package/dist/cjs/development/index.js.map +4 -4
- package/dist/cjs/production/core.js +1 -0
- package/dist/cjs/production/index.js +5 -5
- package/dist/esm/development/core.js +1829 -0
- package/dist/esm/development/core.js.map +7 -0
- package/dist/esm/development/index.js +1377 -1372
- package/dist/esm/development/index.js.map +4 -4
- package/dist/esm/production/core.js +1 -0
- package/dist/esm/production/index.js +5 -5
- package/dist/types/core.d.ts +18 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/extract.d.ts +15 -1
- package/dist/types/extract.d.ts.map +1 -1
- package/dist/types/functional-combinators.d.ts +3 -5
- package/dist/types/functional-combinators.d.ts.map +1 -1
- package/dist/types/index.d.ts +254 -18
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/middleware/composition.d.ts +460 -0
- package/dist/types/middleware/composition.d.ts.map +1 -0
- package/dist/types/middleware/core.d.ts +196 -0
- package/dist/types/middleware/core.d.ts.map +1 -0
- package/dist/types/middleware/history.d.ts +54 -0
- package/dist/types/middleware/history.d.ts.map +1 -0
- package/dist/types/middleware/index.d.ts +10 -0
- package/dist/types/middleware/index.d.ts.map +1 -0
- package/dist/types/middleware/snapshot.d.ts +63 -0
- package/dist/types/middleware/snapshot.d.ts.map +1 -0
- package/dist/types/middleware/time-travel.d.ts +81 -0
- package/dist/types/middleware/time-travel.d.ts.map +1 -0
- package/package.json +19 -6
- package/src/core.ts +167 -0
- package/src/entry-react.ts +9 -0
- package/src/entry-solid.ts +9 -0
- package/src/extract.ts +61 -61
- package/src/functional-combinators.ts +3 -3
- package/src/generators.ts +6 -6
- package/src/index.ts +389 -101
- package/src/middleware/composition.ts +944 -0
- package/src/middleware/core.ts +573 -0
- package/src/middleware/history.ts +104 -0
- package/src/middleware/index.ts +13 -0
- package/src/middleware/snapshot.ts +153 -0
- package/src/middleware/time-travel.ts +236 -0
- package/src/middleware.ts +735 -1614
- package/src/prototype_functional.ts +46 -0
- package/src/reproduce_issue.ts +26 -0
- package/dist/types/middleware.d.ts +0 -1048
- package/dist/types/middleware.d.ts.map +0 -1
- package/dist/types/runtime-extract.d.ts +0 -53
- package/dist/types/runtime-extract.d.ts.map +0 -1
|
@@ -22,10 +22,12 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
ADVANCED_CONFIG_EXAMPLES: () => ADVANCED_CONFIG_EXAMPLES,
|
|
24
24
|
BoundMachine: () => BoundMachine,
|
|
25
|
+
CANCEL: () => CANCEL,
|
|
25
26
|
META_KEY: () => META_KEY,
|
|
26
27
|
MachineBase: () => MachineBase,
|
|
28
|
+
MiddlewareBuilder: () => MiddlewareBuilder,
|
|
27
29
|
MultiMachineBase: () => MultiMachineBase,
|
|
28
|
-
|
|
30
|
+
_typeToJson: () => _typeToJson,
|
|
29
31
|
action: () => action,
|
|
30
32
|
bindTransitions: () => bindTransitions,
|
|
31
33
|
branch: () => branch,
|
|
@@ -47,6 +49,7 @@ __export(src_exports, {
|
|
|
47
49
|
createMachineBuilder: () => createMachineBuilder,
|
|
48
50
|
createMachineFactory: () => createMachineFactory,
|
|
49
51
|
createMiddleware: () => createMiddleware,
|
|
52
|
+
createMiddlewareFactory: () => createMiddlewareFactory,
|
|
50
53
|
createMiddlewareRegistry: () => createMiddlewareRegistry,
|
|
51
54
|
createMultiMachine: () => createMultiMachine,
|
|
52
55
|
createMutableMachine: () => createMutableMachine,
|
|
@@ -59,13 +62,9 @@ __export(src_exports, {
|
|
|
59
62
|
delegateToChild: () => delegateToChild,
|
|
60
63
|
describe: () => describe,
|
|
61
64
|
extendTransitions: () => extendTransitions,
|
|
62
|
-
extractFromInstance: () => extractFromInstance,
|
|
63
|
-
extractFunctionMetadata: () => extractFunctionMetadata,
|
|
64
65
|
extractMachine: () => extractMachine,
|
|
65
66
|
extractMachines: () => extractMachines,
|
|
66
|
-
extractStateNode: () => extractStateNode,
|
|
67
67
|
generateChart: () => generateChart,
|
|
68
|
-
generateStatechart: () => generateStatechart,
|
|
69
68
|
guard: () => guard,
|
|
70
69
|
guardAsync: () => guardAsync,
|
|
71
70
|
guarded: () => guarded,
|
|
@@ -73,12 +72,20 @@ __export(src_exports, {
|
|
|
73
72
|
inDevelopment: () => inDevelopment,
|
|
74
73
|
invoke: () => invoke,
|
|
75
74
|
isConditionalMiddleware: () => isConditionalMiddleware,
|
|
75
|
+
isMiddlewareContext: () => isMiddlewareContext,
|
|
76
|
+
isMiddlewareError: () => isMiddlewareError,
|
|
76
77
|
isMiddlewareFn: () => isMiddlewareFn,
|
|
78
|
+
isMiddlewareHooks: () => isMiddlewareHooks,
|
|
79
|
+
isMiddlewareOptions: () => isMiddlewareOptions,
|
|
80
|
+
isMiddlewareResult: () => isMiddlewareResult,
|
|
81
|
+
isNamedMiddleware: () => isNamedMiddleware,
|
|
82
|
+
isPipelineConfig: () => isPipelineConfig,
|
|
77
83
|
isState: () => isState,
|
|
78
84
|
logState: () => logState,
|
|
79
85
|
matchMachine: () => matchMachine,
|
|
80
86
|
mergeContext: () => mergeContext,
|
|
81
87
|
metadata: () => metadata,
|
|
88
|
+
middlewareBuilder: () => middlewareBuilder,
|
|
82
89
|
next: () => next,
|
|
83
90
|
overrideTransitions: () => overrideTransitions,
|
|
84
91
|
pipeTransitions: () => pipeTransitions,
|
|
@@ -352,1209 +359,1120 @@ function metadata(_meta, value) {
|
|
|
352
359
|
return value;
|
|
353
360
|
}
|
|
354
361
|
|
|
355
|
-
// src/
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
return {};
|
|
369
|
-
}
|
|
370
|
-
const result = {};
|
|
371
|
-
for (const prop of obj.getProperties()) {
|
|
372
|
-
if (import_ts_morph.Node.isPropertyAssignment(prop)) {
|
|
373
|
-
const name = prop.getName();
|
|
374
|
-
const init = prop.getInitializer();
|
|
375
|
-
if (init) {
|
|
376
|
-
if (import_ts_morph.Node.isStringLiteral(init)) {
|
|
377
|
-
result[name] = init.getLiteralValue();
|
|
378
|
-
} else if (import_ts_morph.Node.isNumericLiteral(init)) {
|
|
379
|
-
result[name] = init.getLiteralValue();
|
|
380
|
-
} else if (init.getText() === "true" || init.getText() === "false") {
|
|
381
|
-
result[name] = init.getText() === "true";
|
|
382
|
-
} else if (import_ts_morph.Node.isIdentifier(init)) {
|
|
383
|
-
result[name] = init.getText();
|
|
384
|
-
} else if (import_ts_morph.Node.isObjectLiteralExpression(init)) {
|
|
385
|
-
result[name] = parseObjectLiteral(init);
|
|
386
|
-
} else if (import_ts_morph.Node.isArrayLiteralExpression(init)) {
|
|
387
|
-
result[name] = init.getElements().map((el) => {
|
|
388
|
-
if (import_ts_morph.Node.isObjectLiteralExpression(el)) {
|
|
389
|
-
return parseObjectLiteral(el);
|
|
390
|
-
}
|
|
391
|
-
return el.getText();
|
|
392
|
-
});
|
|
393
|
-
}
|
|
362
|
+
// src/multi.ts
|
|
363
|
+
function createRunner(initialMachine, onChange) {
|
|
364
|
+
let currentMachine = initialMachine;
|
|
365
|
+
const setState = (newState) => {
|
|
366
|
+
currentMachine = newState;
|
|
367
|
+
onChange == null ? void 0 : onChange(newState);
|
|
368
|
+
};
|
|
369
|
+
const { context: _initialContext, ...originalTransitions } = initialMachine;
|
|
370
|
+
const actions = new Proxy({}, {
|
|
371
|
+
get(_target, prop) {
|
|
372
|
+
const transition = currentMachine[prop];
|
|
373
|
+
if (typeof transition !== "function") {
|
|
374
|
+
return void 0;
|
|
394
375
|
}
|
|
376
|
+
return (...args) => {
|
|
377
|
+
const nextState = transition.apply(currentMachine.context, args);
|
|
378
|
+
const nextStateWithTransitions = Object.assign(
|
|
379
|
+
{ context: nextState.context },
|
|
380
|
+
originalTransitions
|
|
381
|
+
);
|
|
382
|
+
setState(nextStateWithTransitions);
|
|
383
|
+
return nextStateWithTransitions;
|
|
384
|
+
};
|
|
395
385
|
}
|
|
396
|
-
}
|
|
397
|
-
return
|
|
386
|
+
});
|
|
387
|
+
return {
|
|
388
|
+
get state() {
|
|
389
|
+
return currentMachine;
|
|
390
|
+
},
|
|
391
|
+
get context() {
|
|
392
|
+
return currentMachine.context;
|
|
393
|
+
},
|
|
394
|
+
actions,
|
|
395
|
+
setState
|
|
396
|
+
};
|
|
398
397
|
}
|
|
399
|
-
function
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
398
|
+
function createEnsemble(store, factories, getDiscriminant) {
|
|
399
|
+
const getCurrentMachine = () => {
|
|
400
|
+
const context = store.getContext();
|
|
401
|
+
const currentStateName = getDiscriminant(context);
|
|
402
|
+
const factory = factories[currentStateName];
|
|
403
|
+
if (!factory) {
|
|
404
|
+
throw new Error(
|
|
405
|
+
`[Ensemble] Invalid state: No factory found for state "${String(currentStateName)}".`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
return factory(context);
|
|
409
|
+
};
|
|
410
|
+
const actions = new Proxy({}, {
|
|
411
|
+
get(_target, prop) {
|
|
412
|
+
const currentMachine = getCurrentMachine();
|
|
413
|
+
const action2 = currentMachine[prop];
|
|
414
|
+
if (typeof action2 !== "function") {
|
|
415
|
+
throw new Error(
|
|
416
|
+
`[Ensemble] Transition "${prop}" is not valid in the current state.`
|
|
417
|
+
);
|
|
415
418
|
}
|
|
419
|
+
return (...args) => {
|
|
420
|
+
return action2.apply(currentMachine.context, args);
|
|
421
|
+
};
|
|
416
422
|
}
|
|
417
|
-
}
|
|
418
|
-
return
|
|
423
|
+
});
|
|
424
|
+
return {
|
|
425
|
+
get context() {
|
|
426
|
+
return store.getContext();
|
|
427
|
+
},
|
|
428
|
+
get state() {
|
|
429
|
+
return getCurrentMachine();
|
|
430
|
+
},
|
|
431
|
+
actions
|
|
432
|
+
};
|
|
419
433
|
}
|
|
420
|
-
function
|
|
421
|
-
|
|
422
|
-
return
|
|
423
|
-
}
|
|
424
|
-
const expression = call2.getExpression();
|
|
425
|
-
const fnName = import_ts_morph.Node.isIdentifier(expression) ? expression.getText() : null;
|
|
426
|
-
if (!fnName) {
|
|
427
|
-
return null;
|
|
428
|
-
}
|
|
429
|
-
const metadata2 = {};
|
|
430
|
-
const args = call2.getArguments();
|
|
431
|
-
switch (fnName) {
|
|
432
|
-
case "transitionTo":
|
|
433
|
-
if (args[0]) {
|
|
434
|
-
metadata2.target = resolveClassName(args[0]);
|
|
435
|
-
}
|
|
436
|
-
break;
|
|
437
|
-
case "describe":
|
|
438
|
-
if (args[0] && import_ts_morph.Node.isStringLiteral(args[0])) {
|
|
439
|
-
metadata2.description = args[0].getLiteralValue();
|
|
440
|
-
}
|
|
441
|
-
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
442
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
443
|
-
if (nested) {
|
|
444
|
-
Object.assign(metadata2, nested);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
break;
|
|
448
|
-
case "guarded":
|
|
449
|
-
if (args[0]) {
|
|
450
|
-
const guard2 = parseObjectLiteral(args[0]);
|
|
451
|
-
if (Object.keys(guard2).length > 0) {
|
|
452
|
-
metadata2.guards = [guard2];
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
456
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
457
|
-
if (nested) {
|
|
458
|
-
Object.assign(metadata2, nested);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
break;
|
|
462
|
-
case "invoke":
|
|
463
|
-
if (args[0]) {
|
|
464
|
-
const service = parseInvokeService(args[0]);
|
|
465
|
-
if (Object.keys(service).length > 0) {
|
|
466
|
-
metadata2.invoke = service;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
break;
|
|
470
|
-
case "action":
|
|
471
|
-
if (args[0]) {
|
|
472
|
-
const actionMeta = parseObjectLiteral(args[0]);
|
|
473
|
-
if (Object.keys(actionMeta).length > 0) {
|
|
474
|
-
metadata2.actions = [actionMeta];
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
478
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
479
|
-
if (nested) {
|
|
480
|
-
Object.assign(metadata2, nested);
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
break;
|
|
484
|
-
case "guard":
|
|
485
|
-
if (args[2]) {
|
|
486
|
-
const options = parseObjectLiteral(args[2]);
|
|
487
|
-
if (options.description) {
|
|
488
|
-
metadata2.description = options.description;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
metadata2.guards = [{ name: "runtime_guard", description: metadata2.description || "Synchronous condition check" }];
|
|
492
|
-
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
493
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
494
|
-
if (nested) {
|
|
495
|
-
Object.assign(metadata2, nested);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
break;
|
|
499
|
-
case "guardAsync":
|
|
500
|
-
if (args[2]) {
|
|
501
|
-
const options = parseObjectLiteral(args[2]);
|
|
502
|
-
if (options.description) {
|
|
503
|
-
metadata2.description = options.description;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
metadata2.guards = [{ name: "runtime_guard_async", description: metadata2.description || "Asynchronous condition check" }];
|
|
507
|
-
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
508
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
509
|
-
if (nested) {
|
|
510
|
-
Object.assign(metadata2, nested);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
break;
|
|
514
|
-
default:
|
|
515
|
-
return null;
|
|
516
|
-
}
|
|
517
|
-
return Object.keys(metadata2).length > 0 ? metadata2 : null;
|
|
434
|
+
function createEnsembleFactory(store, getDiscriminant) {
|
|
435
|
+
return function withFactories(factories) {
|
|
436
|
+
return createEnsemble(store, factories, getDiscriminant);
|
|
437
|
+
};
|
|
518
438
|
}
|
|
519
|
-
function
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
if (!initializer) {
|
|
526
|
-
if (verbose) console.error(` ⚠️ No initializer`);
|
|
527
|
-
return null;
|
|
528
|
-
}
|
|
529
|
-
if (!import_ts_morph.Node.isCallExpression(initializer)) {
|
|
530
|
-
if (verbose) console.error(` ⚠️ Initializer is not a call expression`);
|
|
531
|
-
return null;
|
|
439
|
+
function runWithRunner(flow, initialMachine) {
|
|
440
|
+
const runner = createRunner(initialMachine);
|
|
441
|
+
const generator = flow(runner);
|
|
442
|
+
let result = generator.next();
|
|
443
|
+
while (!result.done) {
|
|
444
|
+
result = generator.next();
|
|
532
445
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
446
|
+
return result.value;
|
|
447
|
+
}
|
|
448
|
+
function runWithEnsemble(flow, ensemble) {
|
|
449
|
+
const generator = flow(ensemble);
|
|
450
|
+
let result = generator.next();
|
|
451
|
+
while (!result.done) {
|
|
452
|
+
result = generator.next();
|
|
536
453
|
}
|
|
537
|
-
return
|
|
454
|
+
return result.value;
|
|
538
455
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
546
|
-
return chartNode;
|
|
456
|
+
var MultiMachineBase = class {
|
|
457
|
+
/**
|
|
458
|
+
* @param store - The StateStore that will manage this machine's context.
|
|
459
|
+
*/
|
|
460
|
+
constructor(store) {
|
|
461
|
+
this.store = store;
|
|
547
462
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
463
|
+
/**
|
|
464
|
+
* Read-only access to the current context from the external store.
|
|
465
|
+
* This getter always returns the latest context from the store.
|
|
466
|
+
*
|
|
467
|
+
* @protected
|
|
468
|
+
*
|
|
469
|
+
* @example
|
|
470
|
+
* const currentStatus = this.context.status;
|
|
471
|
+
* const currentData = this.context.data;
|
|
472
|
+
*/
|
|
473
|
+
get context() {
|
|
474
|
+
return this.store.getContext();
|
|
551
475
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
476
|
+
/**
|
|
477
|
+
* Update the shared context in the external store.
|
|
478
|
+
* Call this method in your transition methods to update the state.
|
|
479
|
+
*
|
|
480
|
+
* @protected
|
|
481
|
+
* @param newContext - The new context object. Should typically be a shallow
|
|
482
|
+
* copy with only the properties you're changing, merged with the current
|
|
483
|
+
* context using spread operators.
|
|
484
|
+
*
|
|
485
|
+
* @example
|
|
486
|
+
* // In a transition method:
|
|
487
|
+
* this.setContext({ ...this.context, status: 'loading' });
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* // Updating nested properties:
|
|
491
|
+
* this.setContext({
|
|
492
|
+
* ...this.context,
|
|
493
|
+
* user: { ...this.context.user, name: 'Alice' }
|
|
494
|
+
* });
|
|
495
|
+
*/
|
|
496
|
+
setContext(newContext) {
|
|
497
|
+
this.store.setContext(newContext);
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
function createMultiMachine(MachineClass, store) {
|
|
501
|
+
const instance = new MachineClass(store);
|
|
502
|
+
return new Proxy({}, {
|
|
503
|
+
get(_target, prop) {
|
|
504
|
+
const context = store.getContext();
|
|
505
|
+
if (prop in context) {
|
|
506
|
+
return context[prop];
|
|
579
507
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
508
|
+
const method = instance[prop];
|
|
509
|
+
if (typeof method === "function") {
|
|
510
|
+
return (...args) => {
|
|
511
|
+
return method.apply(instance, args);
|
|
512
|
+
};
|
|
585
513
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
514
|
+
return void 0;
|
|
515
|
+
},
|
|
516
|
+
set(_target, prop, value) {
|
|
517
|
+
const context = store.getContext();
|
|
518
|
+
if (prop in context) {
|
|
519
|
+
const newContext = { ...context, [prop]: value };
|
|
520
|
+
store.setContext(newContext);
|
|
521
|
+
return true;
|
|
591
522
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
523
|
+
return false;
|
|
524
|
+
},
|
|
525
|
+
has(_target, prop) {
|
|
526
|
+
const context = store.getContext();
|
|
527
|
+
return prop in context || typeof instance[prop] === "function";
|
|
528
|
+
},
|
|
529
|
+
ownKeys(_target) {
|
|
530
|
+
const context = store.getContext();
|
|
531
|
+
const contextKeys = Object.keys(context);
|
|
532
|
+
const methodKeys = Object.getOwnPropertyNames(
|
|
533
|
+
Object.getPrototypeOf(instance)
|
|
534
|
+
).filter((key) => key !== "constructor" && typeof instance[key] === "function");
|
|
535
|
+
return Array.from(/* @__PURE__ */ new Set([...contextKeys, ...methodKeys]));
|
|
536
|
+
},
|
|
537
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
538
|
+
const context = store.getContext();
|
|
539
|
+
if (prop in context || typeof instance[prop] === "function") {
|
|
540
|
+
return {
|
|
541
|
+
value: void 0,
|
|
542
|
+
writable: true,
|
|
543
|
+
enumerable: true,
|
|
544
|
+
configurable: true
|
|
545
|
+
};
|
|
595
546
|
}
|
|
547
|
+
return void 0;
|
|
596
548
|
}
|
|
597
|
-
}
|
|
598
|
-
return chartNode;
|
|
549
|
+
});
|
|
599
550
|
}
|
|
600
|
-
function
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
551
|
+
function createMutableMachine(sharedContext, factories, getDiscriminant) {
|
|
552
|
+
const getCurrentMachine = () => {
|
|
553
|
+
const currentStateName = getDiscriminant(sharedContext);
|
|
554
|
+
const factory = factories[currentStateName];
|
|
555
|
+
if (!factory) {
|
|
556
|
+
throw new Error(
|
|
557
|
+
`[MutableMachine] Invalid state: No factory for state "${String(currentStateName)}".`
|
|
558
|
+
);
|
|
605
559
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
if (
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
560
|
+
return factory(sharedContext);
|
|
561
|
+
};
|
|
562
|
+
return new Proxy(sharedContext, {
|
|
563
|
+
get(target, prop, _receiver) {
|
|
564
|
+
if (prop in target) {
|
|
565
|
+
return target[prop];
|
|
566
|
+
}
|
|
567
|
+
const currentMachine = getCurrentMachine();
|
|
568
|
+
const transition = currentMachine[prop];
|
|
569
|
+
if (typeof transition === "function") {
|
|
570
|
+
return (...args) => {
|
|
571
|
+
const nextContext = transition.apply(currentMachine.context, args);
|
|
572
|
+
if (typeof nextContext !== "object" || nextContext === null) {
|
|
573
|
+
console.warn(`[MutableMachine] Transition "${String(prop)}" did not return a valid context object. State may be inconsistent.`);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
Object.keys(target).forEach((key) => delete target[key]);
|
|
577
|
+
Object.assign(target, nextContext);
|
|
578
|
+
};
|
|
615
579
|
}
|
|
580
|
+
return void 0;
|
|
581
|
+
},
|
|
582
|
+
set(target, prop, value, _receiver) {
|
|
583
|
+
target[prop] = value;
|
|
584
|
+
return true;
|
|
585
|
+
},
|
|
586
|
+
has(target, prop) {
|
|
587
|
+
const currentMachine = getCurrentMachine();
|
|
588
|
+
return prop in target || typeof currentMachine[prop] === "function";
|
|
616
589
|
}
|
|
617
|
-
}
|
|
618
|
-
return stateNode;
|
|
590
|
+
});
|
|
619
591
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
throw new Error(`Source file not found: ${config.input}`);
|
|
629
|
-
}
|
|
630
|
-
if (config.parallel) {
|
|
631
|
-
if (verbose) {
|
|
632
|
-
console.error(` ⏹️ Parallel machine detected. Analyzing regions.`);
|
|
592
|
+
|
|
593
|
+
// src/higher-order.ts
|
|
594
|
+
function delegateToChild(actionName) {
|
|
595
|
+
return function(...args) {
|
|
596
|
+
const child = this.context.child;
|
|
597
|
+
if (typeof child[actionName] === "function") {
|
|
598
|
+
const newChildState = child[actionName](...args);
|
|
599
|
+
return setContext(this, { ...this.context, child: newChildState });
|
|
633
600
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
if (
|
|
640
|
-
|
|
601
|
+
return this;
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
function toggle(prop) {
|
|
605
|
+
return function() {
|
|
606
|
+
if (typeof this.context[prop] !== "boolean") {
|
|
607
|
+
console.warn(`[toggle primitive] Property '${String(prop)}' is not a boolean. Toggling may have unexpected results.`);
|
|
641
608
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
609
|
+
return setContext(this, {
|
|
610
|
+
...this.context,
|
|
611
|
+
[prop]: !this.context[prop]
|
|
612
|
+
});
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
var IdleMachine = class extends MachineBase {
|
|
616
|
+
constructor(config) {
|
|
617
|
+
super({ status: "idle" });
|
|
618
|
+
this.config = config;
|
|
619
|
+
this.fetch = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
var LoadingMachine = class extends MachineBase {
|
|
623
|
+
constructor(config, params, attempts) {
|
|
624
|
+
super({ status: "loading", abortController: new AbortController(), attempts });
|
|
625
|
+
this.config = config;
|
|
626
|
+
this.params = params;
|
|
627
|
+
this.succeed = (data) => {
|
|
628
|
+
var _a, _b;
|
|
629
|
+
(_b = (_a = this.config).onSuccess) == null ? void 0 : _b.call(_a, data);
|
|
630
|
+
return new SuccessMachine(this.config, { status: "success", data });
|
|
631
|
+
};
|
|
632
|
+
this.fail = (error) => {
|
|
633
|
+
var _a, _b, _c;
|
|
634
|
+
const maxRetries = (_a = this.config.maxRetries) != null ? _a : 3;
|
|
635
|
+
if (this.context.attempts < maxRetries) {
|
|
636
|
+
return new RetryingMachine(this.config, this.params, error, this.context.attempts);
|
|
655
637
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
638
|
+
(_c = (_b = this.config).onError) == null ? void 0 : _c.call(_b, error);
|
|
639
|
+
return new ErrorMachine(this.config, { status: "error", error });
|
|
640
|
+
};
|
|
641
|
+
this.cancel = () => {
|
|
642
|
+
this.context.abortController.abort();
|
|
643
|
+
return new CanceledMachine(this.config);
|
|
644
|
+
};
|
|
645
|
+
this.execute();
|
|
646
|
+
}
|
|
647
|
+
async execute() {
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
var RetryingMachine = class extends MachineBase {
|
|
651
|
+
constructor(config, params, error, attempts) {
|
|
652
|
+
super({ status: "retrying", error, attempts });
|
|
653
|
+
this.config = config;
|
|
654
|
+
this.params = params;
|
|
655
|
+
// This would be called after a delay.
|
|
656
|
+
this.retry = (params) => new LoadingMachine(this.config, params != null ? params : this.params, this.context.attempts + 1);
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
var SuccessMachine = class extends MachineBase {
|
|
660
|
+
constructor(config, context) {
|
|
661
|
+
super(context);
|
|
662
|
+
this.config = config;
|
|
663
|
+
this.refetch = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
var ErrorMachine = class extends MachineBase {
|
|
667
|
+
constructor(config, context) {
|
|
668
|
+
super(context);
|
|
669
|
+
this.config = config;
|
|
670
|
+
this.retry = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
var CanceledMachine = class extends MachineBase {
|
|
674
|
+
constructor(config) {
|
|
675
|
+
super({ status: "canceled" });
|
|
676
|
+
this.config = config;
|
|
677
|
+
this.refetch = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
function createFetchMachine(config) {
|
|
681
|
+
return new IdleMachine(config);
|
|
682
|
+
}
|
|
683
|
+
function createParallelMachine(m1, m2) {
|
|
684
|
+
const combinedContext = { ...m1.context, ...m2.context };
|
|
685
|
+
const transitions1 = { ...m1 };
|
|
686
|
+
const transitions2 = { ...m2 };
|
|
687
|
+
delete transitions1.context;
|
|
688
|
+
delete transitions2.context;
|
|
689
|
+
const combinedTransitions = {};
|
|
690
|
+
for (const key in transitions1) {
|
|
691
|
+
const transitionFn = transitions1[key];
|
|
692
|
+
combinedTransitions[key] = (...args) => {
|
|
693
|
+
const nextM1 = transitionFn.apply(m1.context, args);
|
|
694
|
+
return createParallelMachine(nextM1, m2);
|
|
695
|
+
};
|
|
665
696
|
}
|
|
666
|
-
|
|
667
|
-
|
|
697
|
+
for (const key in transitions2) {
|
|
698
|
+
const transitionFn = transitions2[key];
|
|
699
|
+
combinedTransitions[key] = (...args) => {
|
|
700
|
+
const nextM2 = transitionFn.apply(m2.context, args);
|
|
701
|
+
return createParallelMachine(m1, nextM2);
|
|
702
|
+
};
|
|
668
703
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
states: {}
|
|
704
|
+
return {
|
|
705
|
+
context: combinedContext,
|
|
706
|
+
...combinedTransitions
|
|
673
707
|
};
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// src/extract.ts
|
|
711
|
+
var import_ts_morph = require("ts-morph");
|
|
712
|
+
function _typeToJson(type, verbose = false) {
|
|
713
|
+
const symbol = type.getSymbol();
|
|
714
|
+
if (symbol && symbol.getDeclarations().some(import_ts_morph.Node.isClassDeclaration)) {
|
|
715
|
+
return symbol.getName();
|
|
716
|
+
}
|
|
717
|
+
if (type.isStringLiteral()) return type.getLiteralValue();
|
|
718
|
+
if (type.isNumberLiteral()) return type.getLiteralValue();
|
|
719
|
+
if (type.isBooleanLiteral()) return type.getLiteralValue();
|
|
720
|
+
if (type.isString()) return "string";
|
|
721
|
+
if (type.isNumber()) return "number";
|
|
722
|
+
if (type.isBoolean()) return "boolean";
|
|
723
|
+
if (type.isArray()) {
|
|
724
|
+
const elementType = type.getArrayElementTypeOrThrow();
|
|
725
|
+
return [_typeToJson(elementType, verbose)];
|
|
726
|
+
}
|
|
727
|
+
if (type.isObject() || type.isIntersection()) {
|
|
728
|
+
const obj = {};
|
|
729
|
+
const properties = type.getProperties();
|
|
730
|
+
for (const prop of properties) {
|
|
731
|
+
const propName = prop.getName();
|
|
732
|
+
if (propName.startsWith("__@")) continue;
|
|
733
|
+
const declaration = prop.getValueDeclaration();
|
|
734
|
+
if (!declaration) continue;
|
|
735
|
+
try {
|
|
736
|
+
obj[propName] = _typeToJson(declaration.getType(), verbose);
|
|
737
|
+
} catch (e) {
|
|
738
|
+
if (verbose) console.error(` Warning: Failed to serialize property ${propName}:`, e);
|
|
739
|
+
obj[propName] = "unknown";
|
|
740
|
+
}
|
|
682
741
|
}
|
|
683
|
-
|
|
684
|
-
const hasChildren = className === config.initialState && config.children;
|
|
685
|
-
const stateNode = analyzeStateNodeWithNesting(
|
|
686
|
-
className,
|
|
687
|
-
classSymbol,
|
|
688
|
-
sourceFile,
|
|
689
|
-
hasChildren ? config.children : void 0,
|
|
690
|
-
verbose
|
|
691
|
-
);
|
|
692
|
-
fullChart.states[className] = stateNode;
|
|
742
|
+
return Object.keys(obj).length > 0 ? obj : null;
|
|
693
743
|
}
|
|
694
744
|
if (verbose) {
|
|
695
|
-
console.error(`
|
|
745
|
+
console.error(` Unhandled type: ${type.getText()}`);
|
|
696
746
|
}
|
|
697
|
-
return
|
|
747
|
+
return "unknown";
|
|
698
748
|
}
|
|
699
|
-
function
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
if (verbose) {
|
|
703
|
-
console.error(`
|
|
704
|
-
📊 Starting statechart extraction`);
|
|
705
|
-
console.error(` Machines to extract: ${config.machines.length}`);
|
|
749
|
+
function resolveClassName(node) {
|
|
750
|
+
if (import_ts_morph.Node.isIdentifier(node)) {
|
|
751
|
+
return node.getText();
|
|
706
752
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
753
|
+
if (import_ts_morph.Node.isTypeOfExpression(node)) {
|
|
754
|
+
return node.getExpression().getText();
|
|
755
|
+
}
|
|
756
|
+
return "unknown";
|
|
757
|
+
}
|
|
758
|
+
function parseObjectLiteral(obj) {
|
|
759
|
+
if (!import_ts_morph.Node.isObjectLiteralExpression(obj)) {
|
|
760
|
+
return {};
|
|
761
|
+
}
|
|
762
|
+
const result = {};
|
|
763
|
+
for (const prop of obj.getProperties()) {
|
|
764
|
+
if (import_ts_morph.Node.isPropertyAssignment(prop)) {
|
|
765
|
+
const name = prop.getName();
|
|
766
|
+
const init = prop.getInitializer();
|
|
767
|
+
if (init) {
|
|
768
|
+
if (import_ts_morph.Node.isStringLiteral(init)) {
|
|
769
|
+
result[name] = init.getLiteralValue();
|
|
770
|
+
} else if (import_ts_morph.Node.isNumericLiteral(init)) {
|
|
771
|
+
result[name] = init.getLiteralValue();
|
|
772
|
+
} else if (init.getText() === "true" || init.getText() === "false") {
|
|
773
|
+
result[name] = init.getText() === "true";
|
|
774
|
+
} else if (import_ts_morph.Node.isIdentifier(init)) {
|
|
775
|
+
result[name] = init.getText();
|
|
776
|
+
} else if (import_ts_morph.Node.isObjectLiteralExpression(init)) {
|
|
777
|
+
result[name] = parseObjectLiteral(init);
|
|
778
|
+
} else if (import_ts_morph.Node.isArrayLiteralExpression(init)) {
|
|
779
|
+
result[name] = init.getElements().map((el) => {
|
|
780
|
+
if (import_ts_morph.Node.isObjectLiteralExpression(el)) {
|
|
781
|
+
return parseObjectLiteral(el);
|
|
782
|
+
}
|
|
783
|
+
return el.getText();
|
|
784
|
+
});
|
|
785
|
+
}
|
|
719
786
|
}
|
|
720
787
|
}
|
|
721
788
|
}
|
|
722
|
-
|
|
723
|
-
console.error(`
|
|
724
|
-
✅ Extraction complete: ${results.length}/${config.machines.length} machines extracted`);
|
|
725
|
-
}
|
|
726
|
-
return results;
|
|
789
|
+
return result;
|
|
727
790
|
}
|
|
728
|
-
function
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
classes: [
|
|
732
|
-
"LoggedOutMachine",
|
|
733
|
-
"LoggingInMachine",
|
|
734
|
-
"LoggedInMachine",
|
|
735
|
-
"SessionExpiredMachine",
|
|
736
|
-
"ErrorMachine"
|
|
737
|
-
],
|
|
738
|
-
id: "auth",
|
|
739
|
-
initialState: "LoggedOutMachine",
|
|
740
|
-
description: "Authentication state machine"
|
|
741
|
-
};
|
|
742
|
-
console.error("🔍 Using legacy generateChart function");
|
|
743
|
-
console.error("⚠️ Consider using extractMachines() with a config file instead\n");
|
|
744
|
-
const project = new import_ts_morph.Project();
|
|
745
|
-
project.addSourceFilesAtPaths("src/**/*.ts");
|
|
746
|
-
project.addSourceFilesAtPaths("examples/**/*.ts");
|
|
747
|
-
try {
|
|
748
|
-
const chart = extractMachine(config, project, true);
|
|
749
|
-
console.log(JSON.stringify(chart, null, 2));
|
|
750
|
-
} catch (error) {
|
|
751
|
-
console.error(`❌ Error:`, error);
|
|
752
|
-
process.exit(1);
|
|
791
|
+
function parseInvokeService(obj) {
|
|
792
|
+
if (!import_ts_morph.Node.isObjectLiteralExpression(obj)) {
|
|
793
|
+
return {};
|
|
753
794
|
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
parallel: {
|
|
768
|
-
input: "examples/editorMachine.ts",
|
|
769
|
-
id: "editor",
|
|
770
|
-
parallel: {
|
|
771
|
-
regions: [
|
|
772
|
-
{
|
|
773
|
-
name: "fontWeight",
|
|
774
|
-
initialState: "NormalWeight",
|
|
775
|
-
classes: ["NormalWeight", "BoldWeight"]
|
|
776
|
-
},
|
|
777
|
-
{
|
|
778
|
-
name: "textDecoration",
|
|
779
|
-
initialState: "NoDecoration",
|
|
780
|
-
classes: ["NoDecoration", "UnderlineState"]
|
|
781
|
-
}
|
|
782
|
-
]
|
|
795
|
+
const service = {};
|
|
796
|
+
for (const prop of obj.getProperties()) {
|
|
797
|
+
if (import_ts_morph.Node.isPropertyAssignment(prop)) {
|
|
798
|
+
const name = prop.getName();
|
|
799
|
+
const init = prop.getInitializer();
|
|
800
|
+
if (!init) continue;
|
|
801
|
+
if (name === "onDone" || name === "onError") {
|
|
802
|
+
service[name] = resolveClassName(init);
|
|
803
|
+
} else if (import_ts_morph.Node.isStringLiteral(init)) {
|
|
804
|
+
service[name] = init.getLiteralValue();
|
|
805
|
+
} else if (import_ts_morph.Node.isIdentifier(init)) {
|
|
806
|
+
service[name] = init.getText();
|
|
807
|
+
}
|
|
783
808
|
}
|
|
784
809
|
}
|
|
785
|
-
|
|
786
|
-
if (require.main === module) {
|
|
787
|
-
generateChart();
|
|
810
|
+
return service;
|
|
788
811
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
function extractFunctionMetadata(fn) {
|
|
792
|
-
if (typeof fn !== "function") {
|
|
812
|
+
function extractFromCallExpression(call2, verbose = false) {
|
|
813
|
+
if (!import_ts_morph.Node.isCallExpression(call2)) {
|
|
793
814
|
return null;
|
|
794
815
|
}
|
|
795
|
-
const
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
if (
|
|
821
|
-
|
|
816
|
+
const expression = call2.getExpression();
|
|
817
|
+
const fnName = import_ts_morph.Node.isIdentifier(expression) ? expression.getText() : null;
|
|
818
|
+
if (!fnName) {
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
const metadata2 = {};
|
|
822
|
+
const args = call2.getArguments();
|
|
823
|
+
switch (fnName) {
|
|
824
|
+
case "transitionTo":
|
|
825
|
+
if (args[0]) {
|
|
826
|
+
metadata2.target = resolveClassName(args[0]);
|
|
827
|
+
}
|
|
828
|
+
break;
|
|
829
|
+
case "describe":
|
|
830
|
+
if (args[0] && import_ts_morph.Node.isStringLiteral(args[0])) {
|
|
831
|
+
metadata2.description = args[0].getLiteralValue();
|
|
832
|
+
}
|
|
833
|
+
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
834
|
+
const nested = extractFromCallExpression(args[1], verbose);
|
|
835
|
+
if (nested) {
|
|
836
|
+
Object.assign(metadata2, nested);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
break;
|
|
840
|
+
case "guarded":
|
|
841
|
+
if (args[0]) {
|
|
842
|
+
const guard2 = parseObjectLiteral(args[0]);
|
|
843
|
+
if (Object.keys(guard2).length > 0) {
|
|
844
|
+
metadata2.guards = [guard2];
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
848
|
+
const nested = extractFromCallExpression(args[1], verbose);
|
|
849
|
+
if (nested) {
|
|
850
|
+
Object.assign(metadata2, nested);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
break;
|
|
854
|
+
case "invoke":
|
|
855
|
+
if (args[0]) {
|
|
856
|
+
const service = parseInvokeService(args[0]);
|
|
857
|
+
if (Object.keys(service).length > 0) {
|
|
858
|
+
metadata2.invoke = service;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
break;
|
|
862
|
+
case "action":
|
|
863
|
+
if (args[0]) {
|
|
864
|
+
const actionMeta = parseObjectLiteral(args[0]);
|
|
865
|
+
if (Object.keys(actionMeta).length > 0) {
|
|
866
|
+
metadata2.actions = [actionMeta];
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
870
|
+
const nested = extractFromCallExpression(args[1], verbose);
|
|
871
|
+
if (nested) {
|
|
872
|
+
Object.assign(metadata2, nested);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
break;
|
|
876
|
+
case "guard":
|
|
877
|
+
if (args[2]) {
|
|
878
|
+
const options = parseObjectLiteral(args[2]);
|
|
879
|
+
if (options.description) {
|
|
880
|
+
metadata2.description = options.description;
|
|
881
|
+
}
|
|
822
882
|
}
|
|
823
|
-
|
|
824
|
-
|
|
883
|
+
metadata2.guards = [{ name: "runtime_guard", description: metadata2.description || "Synchronous condition check" }];
|
|
884
|
+
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
885
|
+
const nested = extractFromCallExpression(args[1], verbose);
|
|
886
|
+
if (nested) {
|
|
887
|
+
Object.assign(metadata2, nested);
|
|
888
|
+
}
|
|
825
889
|
}
|
|
826
|
-
|
|
827
|
-
|
|
890
|
+
break;
|
|
891
|
+
case "guardAsync":
|
|
892
|
+
if (args[2]) {
|
|
893
|
+
const options = parseObjectLiteral(args[2]);
|
|
894
|
+
if (options.description) {
|
|
895
|
+
metadata2.description = options.description;
|
|
896
|
+
}
|
|
828
897
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
};
|
|
836
|
-
if (meta.description) {
|
|
837
|
-
transition.description = meta.description;
|
|
898
|
+
metadata2.guards = [{ name: "runtime_guard_async", description: metadata2.description || "Asynchronous condition check" }];
|
|
899
|
+
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
900
|
+
const nested = extractFromCallExpression(args[1], verbose);
|
|
901
|
+
if (nested) {
|
|
902
|
+
Object.assign(metadata2, nested);
|
|
903
|
+
}
|
|
838
904
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
if (invoke2.length > 0) {
|
|
843
|
-
stateNode.invoke = invoke2;
|
|
905
|
+
break;
|
|
906
|
+
default:
|
|
907
|
+
return null;
|
|
844
908
|
}
|
|
845
|
-
return
|
|
909
|
+
return Object.keys(metadata2).length > 0 ? metadata2 : null;
|
|
846
910
|
}
|
|
847
|
-
function
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
states: {}
|
|
852
|
-
};
|
|
853
|
-
if (config.description) {
|
|
854
|
-
chart.description = config.description;
|
|
911
|
+
function extractMetaFromMember(member, verbose = false) {
|
|
912
|
+
if (!import_ts_morph.Node.isPropertyDeclaration(member)) {
|
|
913
|
+
if (verbose) console.error(` ⚠️ Not a property declaration`);
|
|
914
|
+
return null;
|
|
855
915
|
}
|
|
856
|
-
|
|
857
|
-
|
|
916
|
+
const initializer = member.getInitializer();
|
|
917
|
+
if (!initializer) {
|
|
918
|
+
if (verbose) console.error(` ⚠️ No initializer`);
|
|
919
|
+
return null;
|
|
858
920
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
const stateName = config.stateName || machineInstance.constructor.name || "State";
|
|
863
|
-
return {
|
|
864
|
-
id: config.id,
|
|
865
|
-
initial: stateName,
|
|
866
|
-
states: {
|
|
867
|
-
[stateName]: extractStateNode(machineInstance)
|
|
868
|
-
}
|
|
869
|
-
};
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
// src/multi.ts
|
|
873
|
-
function createRunner(initialMachine, onChange) {
|
|
874
|
-
let currentMachine = initialMachine;
|
|
875
|
-
const setState = (newState) => {
|
|
876
|
-
currentMachine = newState;
|
|
877
|
-
onChange == null ? void 0 : onChange(newState);
|
|
878
|
-
};
|
|
879
|
-
const { context: _initialContext, ...originalTransitions } = initialMachine;
|
|
880
|
-
const actions = new Proxy({}, {
|
|
881
|
-
get(_target, prop) {
|
|
882
|
-
const transition = currentMachine[prop];
|
|
883
|
-
if (typeof transition !== "function") {
|
|
884
|
-
return void 0;
|
|
885
|
-
}
|
|
886
|
-
return (...args) => {
|
|
887
|
-
const nextState = transition.apply(currentMachine.context, args);
|
|
888
|
-
const nextStateWithTransitions = Object.assign(
|
|
889
|
-
{ context: nextState.context },
|
|
890
|
-
originalTransitions
|
|
891
|
-
);
|
|
892
|
-
setState(nextStateWithTransitions);
|
|
893
|
-
return nextStateWithTransitions;
|
|
894
|
-
};
|
|
895
|
-
}
|
|
896
|
-
});
|
|
897
|
-
return {
|
|
898
|
-
get state() {
|
|
899
|
-
return currentMachine;
|
|
900
|
-
},
|
|
901
|
-
get context() {
|
|
902
|
-
return currentMachine.context;
|
|
903
|
-
},
|
|
904
|
-
actions,
|
|
905
|
-
setState
|
|
906
|
-
};
|
|
907
|
-
}
|
|
908
|
-
function createEnsemble(store, factories, getDiscriminant) {
|
|
909
|
-
const getCurrentMachine = () => {
|
|
910
|
-
const context = store.getContext();
|
|
911
|
-
const currentStateName = getDiscriminant(context);
|
|
912
|
-
const factory = factories[currentStateName];
|
|
913
|
-
if (!factory) {
|
|
914
|
-
throw new Error(
|
|
915
|
-
`[Ensemble] Invalid state: No factory found for state "${String(currentStateName)}".`
|
|
916
|
-
);
|
|
917
|
-
}
|
|
918
|
-
return factory(context);
|
|
919
|
-
};
|
|
920
|
-
const actions = new Proxy({}, {
|
|
921
|
-
get(_target, prop) {
|
|
922
|
-
const currentMachine = getCurrentMachine();
|
|
923
|
-
const action2 = currentMachine[prop];
|
|
924
|
-
if (typeof action2 !== "function") {
|
|
925
|
-
throw new Error(
|
|
926
|
-
`[Ensemble] Transition "${prop}" is not valid in the current state.`
|
|
927
|
-
);
|
|
928
|
-
}
|
|
929
|
-
return (...args) => {
|
|
930
|
-
return action2.apply(currentMachine.context, args);
|
|
931
|
-
};
|
|
932
|
-
}
|
|
933
|
-
});
|
|
934
|
-
return {
|
|
935
|
-
get context() {
|
|
936
|
-
return store.getContext();
|
|
937
|
-
},
|
|
938
|
-
get state() {
|
|
939
|
-
return getCurrentMachine();
|
|
940
|
-
},
|
|
941
|
-
actions
|
|
942
|
-
};
|
|
943
|
-
}
|
|
944
|
-
function createEnsembleFactory(store, getDiscriminant) {
|
|
945
|
-
return function withFactories(factories) {
|
|
946
|
-
return createEnsemble(store, factories, getDiscriminant);
|
|
947
|
-
};
|
|
948
|
-
}
|
|
949
|
-
function runWithRunner(flow, initialMachine) {
|
|
950
|
-
const runner = createRunner(initialMachine);
|
|
951
|
-
const generator = flow(runner);
|
|
952
|
-
let result = generator.next();
|
|
953
|
-
while (!result.done) {
|
|
954
|
-
result = generator.next();
|
|
921
|
+
if (!import_ts_morph.Node.isCallExpression(initializer)) {
|
|
922
|
+
if (verbose) console.error(` ⚠️ Initializer is not a call expression`);
|
|
923
|
+
return null;
|
|
955
924
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
const generator = flow(ensemble);
|
|
960
|
-
let result = generator.next();
|
|
961
|
-
while (!result.done) {
|
|
962
|
-
result = generator.next();
|
|
925
|
+
const metadata2 = extractFromCallExpression(initializer, verbose);
|
|
926
|
+
if (metadata2 && verbose) {
|
|
927
|
+
console.error(` ✅ Extracted metadata:`, JSON.stringify(metadata2, null, 2));
|
|
963
928
|
}
|
|
964
|
-
return
|
|
929
|
+
return metadata2;
|
|
965
930
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
* Read-only access to the current context from the external store.
|
|
975
|
-
* This getter always returns the latest context from the store.
|
|
976
|
-
*
|
|
977
|
-
* @protected
|
|
978
|
-
*
|
|
979
|
-
* @example
|
|
980
|
-
* const currentStatus = this.context.status;
|
|
981
|
-
* const currentData = this.context.data;
|
|
982
|
-
*/
|
|
983
|
-
get context() {
|
|
984
|
-
return this.store.getContext();
|
|
931
|
+
function analyzeStateNode(classSymbol, verbose = false) {
|
|
932
|
+
const chartNode = { on: {} };
|
|
933
|
+
const classDeclaration = classSymbol.getDeclarations()[0];
|
|
934
|
+
if (!classDeclaration || !import_ts_morph.Node.isClassDeclaration(classDeclaration)) {
|
|
935
|
+
if (verbose) {
|
|
936
|
+
console.error(`⚠️ Warning: Could not get class declaration for ${classSymbol.getName()}`);
|
|
937
|
+
}
|
|
938
|
+
return chartNode;
|
|
985
939
|
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
*
|
|
990
|
-
* @protected
|
|
991
|
-
* @param newContext - The new context object. Should typically be a shallow
|
|
992
|
-
* copy with only the properties you're changing, merged with the current
|
|
993
|
-
* context using spread operators.
|
|
994
|
-
*
|
|
995
|
-
* @example
|
|
996
|
-
* // In a transition method:
|
|
997
|
-
* this.setContext({ ...this.context, status: 'loading' });
|
|
998
|
-
*
|
|
999
|
-
* @example
|
|
1000
|
-
* // Updating nested properties:
|
|
1001
|
-
* this.setContext({
|
|
1002
|
-
* ...this.context,
|
|
1003
|
-
* user: { ...this.context.user, name: 'Alice' }
|
|
1004
|
-
* });
|
|
1005
|
-
*/
|
|
1006
|
-
setContext(newContext) {
|
|
1007
|
-
this.store.setContext(newContext);
|
|
940
|
+
const className = classSymbol.getName();
|
|
941
|
+
if (verbose) {
|
|
942
|
+
console.error(` Analyzing state: ${className}`);
|
|
1008
943
|
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
if (
|
|
1029
|
-
|
|
1030
|
-
store.setContext(newContext);
|
|
1031
|
-
return true;
|
|
1032
|
-
}
|
|
1033
|
-
return false;
|
|
1034
|
-
},
|
|
1035
|
-
has(_target, prop) {
|
|
1036
|
-
const context = store.getContext();
|
|
1037
|
-
return prop in context || typeof instance[prop] === "function";
|
|
1038
|
-
},
|
|
1039
|
-
ownKeys(_target) {
|
|
1040
|
-
const context = store.getContext();
|
|
1041
|
-
const contextKeys = Object.keys(context);
|
|
1042
|
-
const methodKeys = Object.getOwnPropertyNames(
|
|
1043
|
-
Object.getPrototypeOf(instance)
|
|
1044
|
-
).filter((key) => key !== "constructor" && typeof instance[key] === "function");
|
|
1045
|
-
return Array.from(/* @__PURE__ */ new Set([...contextKeys, ...methodKeys]));
|
|
1046
|
-
},
|
|
1047
|
-
getOwnPropertyDescriptor(_target, prop) {
|
|
1048
|
-
const context = store.getContext();
|
|
1049
|
-
if (prop in context || typeof instance[prop] === "function") {
|
|
1050
|
-
return {
|
|
1051
|
-
value: void 0,
|
|
1052
|
-
writable: true,
|
|
1053
|
-
enumerable: true,
|
|
1054
|
-
configurable: true
|
|
1055
|
-
};
|
|
944
|
+
for (const member of classDeclaration.getInstanceMembers()) {
|
|
945
|
+
const memberName = member.getName();
|
|
946
|
+
if (verbose) {
|
|
947
|
+
console.error(` Checking member: ${memberName}`);
|
|
948
|
+
}
|
|
949
|
+
const meta = extractMetaFromMember(member, verbose);
|
|
950
|
+
if (!meta) continue;
|
|
951
|
+
if (verbose) {
|
|
952
|
+
console.error(` Found transition: ${memberName}`);
|
|
953
|
+
}
|
|
954
|
+
const { invoke: invoke2, actions, guards, ...onEntry } = meta;
|
|
955
|
+
if (invoke2) {
|
|
956
|
+
if (!chartNode.invoke) chartNode.invoke = [];
|
|
957
|
+
chartNode.invoke.push({
|
|
958
|
+
src: invoke2.src,
|
|
959
|
+
onDone: { target: invoke2.onDone },
|
|
960
|
+
onError: { target: invoke2.onError },
|
|
961
|
+
description: invoke2.description
|
|
962
|
+
});
|
|
963
|
+
if (verbose) {
|
|
964
|
+
console.error(` → Invoke: ${invoke2.src}`);
|
|
1056
965
|
}
|
|
1057
|
-
return void 0;
|
|
1058
|
-
}
|
|
1059
|
-
});
|
|
1060
|
-
}
|
|
1061
|
-
function createMutableMachine(sharedContext, factories, getDiscriminant) {
|
|
1062
|
-
const getCurrentMachine = () => {
|
|
1063
|
-
const currentStateName = getDiscriminant(sharedContext);
|
|
1064
|
-
const factory = factories[currentStateName];
|
|
1065
|
-
if (!factory) {
|
|
1066
|
-
throw new Error(
|
|
1067
|
-
`[MutableMachine] Invalid state: No factory for state "${String(currentStateName)}".`
|
|
1068
|
-
);
|
|
1069
966
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
if (prop in target) {
|
|
1075
|
-
return target[prop];
|
|
967
|
+
if (onEntry.target) {
|
|
968
|
+
const transition = { target: onEntry.target };
|
|
969
|
+
if (onEntry.description) {
|
|
970
|
+
transition.description = onEntry.description;
|
|
1076
971
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
972
|
+
if (guards) {
|
|
973
|
+
transition.cond = guards.map((g) => g.name).join(" && ");
|
|
974
|
+
if (verbose) {
|
|
975
|
+
console.error(` → Guard: ${transition.cond}`);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
if (actions && actions.length > 0) {
|
|
979
|
+
transition.actions = actions.map((a) => a.name);
|
|
980
|
+
if (verbose) {
|
|
981
|
+
console.error(` → Actions: ${transition.actions.join(", ")}`);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
chartNode.on[memberName] = transition;
|
|
985
|
+
if (verbose) {
|
|
986
|
+
console.error(` → Target: ${onEntry.target}`);
|
|
1089
987
|
}
|
|
1090
|
-
return void 0;
|
|
1091
|
-
},
|
|
1092
|
-
set(target, prop, value, _receiver) {
|
|
1093
|
-
target[prop] = value;
|
|
1094
|
-
return true;
|
|
1095
|
-
},
|
|
1096
|
-
has(target, prop) {
|
|
1097
|
-
const currentMachine = getCurrentMachine();
|
|
1098
|
-
return prop in target || typeof currentMachine[prop] === "function";
|
|
1099
988
|
}
|
|
1100
|
-
}
|
|
989
|
+
}
|
|
990
|
+
return chartNode;
|
|
1101
991
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
if (typeof child[actionName] === "function") {
|
|
1108
|
-
const newChildState = child[actionName](...args);
|
|
1109
|
-
return setContext(this, { ...this.context, child: newChildState });
|
|
992
|
+
function analyzeStateNodeWithNesting(className, classSymbol, sourceFile, childConfig, verbose = false) {
|
|
993
|
+
const stateNode = analyzeStateNode(classSymbol, verbose);
|
|
994
|
+
if (childConfig) {
|
|
995
|
+
if (verbose) {
|
|
996
|
+
console.error(` 👪 Analyzing children for state: ${className}`);
|
|
1110
997
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
998
|
+
stateNode.initial = childConfig.initialState;
|
|
999
|
+
stateNode.states = {};
|
|
1000
|
+
for (const childClassName of childConfig.classes) {
|
|
1001
|
+
const childClassDeclaration = sourceFile.getClass(childClassName);
|
|
1002
|
+
if (childClassDeclaration) {
|
|
1003
|
+
const childSymbol = childClassDeclaration.getSymbolOrThrow();
|
|
1004
|
+
stateNode.states[childClassName] = analyzeStateNode(childSymbol, verbose);
|
|
1005
|
+
} else {
|
|
1006
|
+
console.warn(`⚠️ Warning: Child class '${childClassName}' not found.`);
|
|
1007
|
+
}
|
|
1118
1008
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
[prop]: !this.context[prop]
|
|
1122
|
-
});
|
|
1123
|
-
};
|
|
1009
|
+
}
|
|
1010
|
+
return stateNode;
|
|
1124
1011
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1012
|
+
function extractMachine(config, project, verbose = false) {
|
|
1013
|
+
if (verbose) {
|
|
1014
|
+
console.error(`
|
|
1015
|
+
🔍 Analyzing machine: ${config.id}`);
|
|
1016
|
+
console.error(` Source: ${config.input}`);
|
|
1130
1017
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1018
|
+
const sourceFile = project.getSourceFile(config.input);
|
|
1019
|
+
if (!sourceFile) {
|
|
1020
|
+
throw new Error(`Source file not found: ${config.input}`);
|
|
1021
|
+
}
|
|
1022
|
+
if (config.parallel) {
|
|
1023
|
+
if (verbose) {
|
|
1024
|
+
console.error(` ⏹️ Parallel machine detected. Analyzing regions.`);
|
|
1025
|
+
}
|
|
1026
|
+
const parallelChart = {
|
|
1027
|
+
id: config.id,
|
|
1028
|
+
type: "parallel",
|
|
1029
|
+
states: {}
|
|
1141
1030
|
};
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1031
|
+
if (config.description) {
|
|
1032
|
+
parallelChart.description = config.description;
|
|
1033
|
+
}
|
|
1034
|
+
for (const region of config.parallel.regions) {
|
|
1035
|
+
if (verbose) {
|
|
1036
|
+
console.error(` 📍 Analyzing region: ${region.name}`);
|
|
1147
1037
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1038
|
+
const regionStates = {};
|
|
1039
|
+
for (const className of region.classes) {
|
|
1040
|
+
const classDeclaration = sourceFile.getClass(className);
|
|
1041
|
+
if (classDeclaration) {
|
|
1042
|
+
const classSymbol = classDeclaration.getSymbolOrThrow();
|
|
1043
|
+
regionStates[className] = analyzeStateNode(classSymbol, verbose);
|
|
1044
|
+
} else {
|
|
1045
|
+
console.warn(`⚠️ Warning: Class '${className}' not found for region '${region.name}'.`);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
parallelChart.states[region.name] = {
|
|
1049
|
+
initial: region.initialState,
|
|
1050
|
+
states: regionStates
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
if (verbose) {
|
|
1054
|
+
console.error(` ✅ Extracted ${config.parallel.regions.length} parallel regions`);
|
|
1055
|
+
}
|
|
1056
|
+
return parallelChart;
|
|
1156
1057
|
}
|
|
1157
|
-
|
|
1058
|
+
if (!config.initialState || !config.classes) {
|
|
1059
|
+
throw new Error(`Machine config for '${config.id}' must have either 'parallel' or 'initialState'/'classes'.`);
|
|
1158
1060
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
this.retry = (params) => new LoadingMachine(this.config, params != null ? params : this.params, this.context.attempts + 1);
|
|
1061
|
+
const fullChart = {
|
|
1062
|
+
id: config.id,
|
|
1063
|
+
initial: config.initialState,
|
|
1064
|
+
states: {}
|
|
1065
|
+
};
|
|
1066
|
+
if (config.description) {
|
|
1067
|
+
fullChart.description = config.description;
|
|
1167
1068
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1069
|
+
for (const className of config.classes) {
|
|
1070
|
+
const classDeclaration = sourceFile.getClass(className);
|
|
1071
|
+
if (!classDeclaration) {
|
|
1072
|
+
console.warn(`⚠️ Warning: Class '${className}' not found in '${config.input}'. Skipping.`);
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1075
|
+
const classSymbol = classDeclaration.getSymbolOrThrow();
|
|
1076
|
+
const hasChildren = className === config.initialState && config.children;
|
|
1077
|
+
const stateNode = analyzeStateNodeWithNesting(
|
|
1078
|
+
className,
|
|
1079
|
+
classSymbol,
|
|
1080
|
+
sourceFile,
|
|
1081
|
+
hasChildren ? config.children : void 0,
|
|
1082
|
+
verbose
|
|
1083
|
+
);
|
|
1084
|
+
fullChart.states[className] = stateNode;
|
|
1174
1085
|
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
constructor(config, context) {
|
|
1178
|
-
super(context);
|
|
1179
|
-
this.config = config;
|
|
1180
|
-
this.retry = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
1086
|
+
if (verbose) {
|
|
1087
|
+
console.error(` ✅ Extracted ${config.classes.length} states`);
|
|
1181
1088
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1089
|
+
return fullChart;
|
|
1090
|
+
}
|
|
1091
|
+
function extractMachines(config) {
|
|
1092
|
+
var _a;
|
|
1093
|
+
const verbose = (_a = config.verbose) != null ? _a : false;
|
|
1094
|
+
if (verbose) {
|
|
1095
|
+
console.error(`
|
|
1096
|
+
📊 Starting statechart extraction`);
|
|
1097
|
+
console.error(` Machines to extract: ${config.machines.length}`);
|
|
1098
|
+
}
|
|
1099
|
+
const project = new import_ts_morph.Project();
|
|
1100
|
+
project.addSourceFilesAtPaths("src/**/*.ts");
|
|
1101
|
+
project.addSourceFilesAtPaths("examples/**/*.ts");
|
|
1102
|
+
const results = [];
|
|
1103
|
+
for (const machineConfig of config.machines) {
|
|
1104
|
+
try {
|
|
1105
|
+
const chart = extractMachine(machineConfig, project, verbose);
|
|
1106
|
+
results.push(chart);
|
|
1107
|
+
} catch (error) {
|
|
1108
|
+
console.error(`❌ Error extracting machine '${machineConfig.id}':`, error);
|
|
1109
|
+
if (!verbose) {
|
|
1110
|
+
console.error(` Run with --verbose for more details`);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
if (verbose) {
|
|
1115
|
+
console.error(`
|
|
1116
|
+
✅ Extraction complete: ${results.length}/${config.machines.length} machines extracted`);
|
|
1188
1117
|
}
|
|
1189
|
-
|
|
1190
|
-
function createFetchMachine(config) {
|
|
1191
|
-
return new IdleMachine(config);
|
|
1118
|
+
return results;
|
|
1192
1119
|
}
|
|
1193
|
-
function
|
|
1194
|
-
const
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1120
|
+
function generateChart() {
|
|
1121
|
+
const config = {
|
|
1122
|
+
input: "examples/authMachine.ts",
|
|
1123
|
+
classes: [
|
|
1124
|
+
"LoggedOutMachine",
|
|
1125
|
+
"LoggingInMachine",
|
|
1126
|
+
"LoggedInMachine",
|
|
1127
|
+
"SessionExpiredMachine",
|
|
1128
|
+
"ErrorMachine"
|
|
1129
|
+
],
|
|
1130
|
+
id: "auth",
|
|
1131
|
+
initialState: "LoggedOutMachine",
|
|
1132
|
+
description: "Authentication state machine"
|
|
1133
|
+
};
|
|
1134
|
+
console.error("🔍 Using legacy generateChart function");
|
|
1135
|
+
console.error("⚠️ Consider using extractMachines() with a config file instead\n");
|
|
1136
|
+
const project = new import_ts_morph.Project();
|
|
1137
|
+
project.addSourceFilesAtPaths("src/**/*.ts");
|
|
1138
|
+
project.addSourceFilesAtPaths("examples/**/*.ts");
|
|
1139
|
+
try {
|
|
1140
|
+
const chart = extractMachine(config, project, true);
|
|
1141
|
+
console.log(JSON.stringify(chart, null, 2));
|
|
1142
|
+
} catch (error) {
|
|
1143
|
+
console.error(`❌ Error:`, error);
|
|
1144
|
+
process.exit(1);
|
|
1206
1145
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1146
|
+
}
|
|
1147
|
+
var ADVANCED_CONFIG_EXAMPLES = {
|
|
1148
|
+
hierarchical: {
|
|
1149
|
+
input: "examples/dashboardMachine.ts",
|
|
1150
|
+
id: "dashboard",
|
|
1151
|
+
classes: ["DashboardMachine", "LoggedOutMachine"],
|
|
1152
|
+
initialState: "DashboardMachine",
|
|
1153
|
+
children: {
|
|
1154
|
+
contextProperty: "child",
|
|
1155
|
+
initialState: "ViewingChildMachine",
|
|
1156
|
+
classes: ["ViewingChildMachine", "EditingChildMachine"]
|
|
1157
|
+
}
|
|
1158
|
+
},
|
|
1159
|
+
parallel: {
|
|
1160
|
+
input: "examples/editorMachine.ts",
|
|
1161
|
+
id: "editor",
|
|
1162
|
+
parallel: {
|
|
1163
|
+
regions: [
|
|
1164
|
+
{
|
|
1165
|
+
name: "fontWeight",
|
|
1166
|
+
initialState: "NormalWeight",
|
|
1167
|
+
classes: ["NormalWeight", "BoldWeight"]
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
name: "textDecoration",
|
|
1171
|
+
initialState: "NoDecoration",
|
|
1172
|
+
classes: ["NoDecoration", "UnderlineState"]
|
|
1173
|
+
}
|
|
1174
|
+
]
|
|
1175
|
+
}
|
|
1213
1176
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
};
|
|
1177
|
+
};
|
|
1178
|
+
if (require.main === module) {
|
|
1179
|
+
generateChart();
|
|
1218
1180
|
}
|
|
1219
1181
|
|
|
1220
|
-
// src/middleware.ts
|
|
1182
|
+
// src/middleware/core.ts
|
|
1221
1183
|
var CANCEL = Symbol("CANCEL");
|
|
1222
1184
|
function createMiddleware(machine, hooks, options = {}) {
|
|
1223
|
-
const {
|
|
1224
|
-
const
|
|
1185
|
+
const { continueOnError = false, logErrors = true, onError } = options;
|
|
1186
|
+
const wrappedMachine = { ...machine };
|
|
1225
1187
|
for (const prop in machine) {
|
|
1226
1188
|
if (!Object.prototype.hasOwnProperty.call(machine, prop)) continue;
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
wrapped.context = value;
|
|
1230
|
-
continue;
|
|
1231
|
-
}
|
|
1232
|
-
if (exclude.includes(prop)) {
|
|
1233
|
-
wrapped[prop] = value;
|
|
1234
|
-
continue;
|
|
1189
|
+
if (prop !== "context" && typeof machine[prop] !== "function") {
|
|
1190
|
+
wrappedMachine[prop] = machine[prop];
|
|
1235
1191
|
}
|
|
1236
|
-
if (typeof value !== "function" || prop.startsWith("_")) {
|
|
1237
|
-
wrapped[prop] = value;
|
|
1238
|
-
continue;
|
|
1239
|
-
}
|
|
1240
|
-
wrapped[prop] = createTransitionWrapper(
|
|
1241
|
-
prop,
|
|
1242
|
-
value,
|
|
1243
|
-
machine,
|
|
1244
|
-
hooks,
|
|
1245
|
-
mode
|
|
1246
|
-
);
|
|
1247
1192
|
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
if (hooks.after) {
|
|
1276
|
-
const middlewareResult = {
|
|
1277
|
-
transitionName,
|
|
1278
|
-
prevContext: context,
|
|
1279
|
-
nextContext: result.context,
|
|
1280
|
-
args
|
|
1281
|
-
};
|
|
1282
|
-
const afterResult = hooks.after(middlewareResult);
|
|
1283
|
-
if (afterResult instanceof Promise) {
|
|
1284
|
-
throw new Error(
|
|
1285
|
-
`Middleware mode is 'sync' but after hook returned Promise for transition: ${transitionName}`
|
|
1286
|
-
);
|
|
1193
|
+
for (const prop in machine) {
|
|
1194
|
+
if (!Object.prototype.hasOwnProperty.call(machine, prop)) continue;
|
|
1195
|
+
const value = machine[prop];
|
|
1196
|
+
if (typeof value === "function" && prop !== "context") {
|
|
1197
|
+
wrappedMachine[prop] = function(...args) {
|
|
1198
|
+
const transitionName = prop;
|
|
1199
|
+
const context = wrappedMachine.context;
|
|
1200
|
+
const executeTransition = () => {
|
|
1201
|
+
let nextMachine;
|
|
1202
|
+
try {
|
|
1203
|
+
nextMachine = value.apply(this, args);
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
if (hooks.error) {
|
|
1206
|
+
try {
|
|
1207
|
+
hooks.error({
|
|
1208
|
+
transitionName,
|
|
1209
|
+
context,
|
|
1210
|
+
args: [...args],
|
|
1211
|
+
error
|
|
1212
|
+
});
|
|
1213
|
+
} catch (hookError) {
|
|
1214
|
+
if (!continueOnError) throw hookError;
|
|
1215
|
+
if (logErrors) console.error(`Middleware error hook error for ${transitionName}:`, hookError);
|
|
1216
|
+
onError == null ? void 0 : onError(hookError, "error", { transitionName, context, args, error });
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
throw error;
|
|
1287
1220
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1221
|
+
const ensureMiddlewareProperties = (machine2) => {
|
|
1222
|
+
if (machine2 && typeof machine2 === "object" && machine2.context) {
|
|
1223
|
+
for (const prop2 in wrappedMachine) {
|
|
1224
|
+
if (!Object.prototype.hasOwnProperty.call(wrappedMachine, prop2)) continue;
|
|
1225
|
+
if (prop2 !== "context" && !(prop2 in machine2)) {
|
|
1226
|
+
machine2[prop2] = wrappedMachine[prop2];
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
for (const prop2 in machine2) {
|
|
1230
|
+
if (!Object.prototype.hasOwnProperty.call(machine2, prop2)) continue;
|
|
1231
|
+
const value2 = machine2[prop2];
|
|
1232
|
+
if (typeof value2 === "function" && prop2 !== "context" && wrappedMachine[prop2]) {
|
|
1233
|
+
machine2[prop2] = wrappedMachine[prop2];
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
return machine2;
|
|
1297
1238
|
};
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1239
|
+
if (nextMachine && typeof nextMachine.then === "function") {
|
|
1240
|
+
const asyncResult = nextMachine.then((resolvedMachine) => {
|
|
1241
|
+
ensureMiddlewareProperties(resolvedMachine);
|
|
1242
|
+
if (hooks.after) {
|
|
1243
|
+
try {
|
|
1244
|
+
const result = hooks.after({
|
|
1245
|
+
transitionName,
|
|
1246
|
+
prevContext: context,
|
|
1247
|
+
nextContext: resolvedMachine.context,
|
|
1248
|
+
args: [...args]
|
|
1249
|
+
});
|
|
1250
|
+
if (result && typeof result.then === "function") {
|
|
1251
|
+
return result.then(() => resolvedMachine);
|
|
1252
|
+
}
|
|
1253
|
+
} catch (error) {
|
|
1254
|
+
if (!continueOnError) throw error;
|
|
1255
|
+
if (logErrors) console.error(`Middleware after hook error for ${transitionName}:`, error);
|
|
1256
|
+
onError == null ? void 0 : onError(error, "after", {
|
|
1257
|
+
transitionName,
|
|
1258
|
+
prevContext: context,
|
|
1259
|
+
nextContext: resolvedMachine.context,
|
|
1260
|
+
args
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
return resolvedMachine;
|
|
1301
1265
|
});
|
|
1302
|
-
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1266
|
+
return asyncResult;
|
|
1267
|
+
} else {
|
|
1268
|
+
ensureMiddlewareProperties(nextMachine);
|
|
1269
|
+
if (hooks.after) {
|
|
1270
|
+
try {
|
|
1271
|
+
const result = hooks.after({
|
|
1272
|
+
transitionName,
|
|
1273
|
+
prevContext: context,
|
|
1274
|
+
nextContext: nextMachine.context,
|
|
1275
|
+
args: [...args]
|
|
1276
|
+
});
|
|
1277
|
+
if (result && typeof result === "object" && result && "then" in result) {
|
|
1278
|
+
return result.then(() => nextMachine).catch((error) => {
|
|
1279
|
+
if (!continueOnError) throw error;
|
|
1280
|
+
if (logErrors) console.error(`Middleware after hook error for ${transitionName}:`, error);
|
|
1281
|
+
onError == null ? void 0 : onError(error, "after", {
|
|
1282
|
+
transitionName,
|
|
1283
|
+
prevContext: context,
|
|
1284
|
+
nextContext: nextMachine.context,
|
|
1285
|
+
args
|
|
1286
|
+
});
|
|
1287
|
+
return nextMachine;
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
} catch (error) {
|
|
1291
|
+
if (!continueOnError) throw error;
|
|
1292
|
+
if (logErrors) console.error(`Middleware after hook error for ${transitionName}:`, error);
|
|
1293
|
+
onError == null ? void 0 : onError(error, "after", {
|
|
1294
|
+
transitionName,
|
|
1295
|
+
prevContext: context,
|
|
1296
|
+
nextContext: nextMachine.context,
|
|
1297
|
+
args
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
return nextMachine;
|
|
1335
1302
|
}
|
|
1336
|
-
}
|
|
1337
|
-
throw err;
|
|
1338
|
-
}
|
|
1339
|
-
};
|
|
1340
|
-
const executeAsyncTransition = async () => {
|
|
1341
|
-
try {
|
|
1303
|
+
};
|
|
1342
1304
|
if (hooks.before) {
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
return errorResult;
|
|
1305
|
+
try {
|
|
1306
|
+
const result = hooks.before({
|
|
1307
|
+
transitionName,
|
|
1308
|
+
context,
|
|
1309
|
+
args: [...args]
|
|
1310
|
+
});
|
|
1311
|
+
if (result && typeof result === "object" && result && "then" in result) {
|
|
1312
|
+
return result.then((hookResult) => {
|
|
1313
|
+
if (hookResult === CANCEL) {
|
|
1314
|
+
return wrappedMachine;
|
|
1315
|
+
}
|
|
1316
|
+
return executeTransition();
|
|
1317
|
+
}).catch((error) => {
|
|
1318
|
+
if (!continueOnError) throw error;
|
|
1319
|
+
if (logErrors) console.error(`Middleware before hook error for ${transitionName}:`, error);
|
|
1320
|
+
onError == null ? void 0 : onError(error, "before", { transitionName, context, args });
|
|
1321
|
+
return executeTransition();
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
if (result === CANCEL) {
|
|
1325
|
+
return wrappedMachine;
|
|
1326
|
+
}
|
|
1327
|
+
} catch (error) {
|
|
1328
|
+
if (!continueOnError) throw error;
|
|
1329
|
+
if (logErrors) console.error(`Middleware before hook error for ${transitionName}:`, error);
|
|
1330
|
+
onError == null ? void 0 : onError(error, "before", { transitionName, context, args });
|
|
1370
1331
|
}
|
|
1371
1332
|
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
if (mode === "async") {
|
|
1376
|
-
return executeAsyncTransition();
|
|
1377
|
-
} else if (mode === "sync") {
|
|
1378
|
-
return executeSyncTransition();
|
|
1379
|
-
} else {
|
|
1380
|
-
return executeSyncTransition();
|
|
1333
|
+
;
|
|
1334
|
+
return executeTransition();
|
|
1335
|
+
};
|
|
1381
1336
|
}
|
|
1382
|
-
}
|
|
1337
|
+
}
|
|
1338
|
+
return wrappedMachine;
|
|
1383
1339
|
}
|
|
1384
1340
|
function withLogging(machine, options = {}) {
|
|
1385
|
-
const {
|
|
1386
|
-
logger = console.log,
|
|
1387
|
-
includeContext = true,
|
|
1388
|
-
includeArgs = true
|
|
1389
|
-
} = options;
|
|
1341
|
+
const { logger = console.log, includeArgs = false, includeContext = true } = options;
|
|
1390
1342
|
return createMiddleware(machine, {
|
|
1391
1343
|
before: ({ transitionName, args }) => {
|
|
1392
|
-
const
|
|
1393
|
-
logger(
|
|
1344
|
+
const message = includeArgs ? `→ ${transitionName} [${args.join(", ")}]` : `→ ${transitionName}`;
|
|
1345
|
+
logger(message);
|
|
1394
1346
|
},
|
|
1395
1347
|
after: ({ transitionName, nextContext }) => {
|
|
1396
1348
|
const contextStr = includeContext ? ` ${JSON.stringify(nextContext)}` : "";
|
|
1397
1349
|
logger(`✓ ${transitionName}${contextStr}`);
|
|
1350
|
+
},
|
|
1351
|
+
error: ({ transitionName, error }) => {
|
|
1352
|
+
console.error(`[Machine] ${transitionName} failed:`, error);
|
|
1398
1353
|
}
|
|
1399
1354
|
});
|
|
1400
1355
|
}
|
|
1401
1356
|
function withAnalytics(machine, track, options = {}) {
|
|
1402
|
-
const {
|
|
1403
|
-
eventPrefix = "state_transition",
|
|
1404
|
-
includePrevContext = false,
|
|
1405
|
-
includeArgs = true
|
|
1406
|
-
} = options;
|
|
1357
|
+
const { eventPrefix = "state_transition", includePrevContext = false, includeArgs = false } = options;
|
|
1407
1358
|
return createMiddleware(machine, {
|
|
1408
|
-
after:
|
|
1409
|
-
const
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
if (
|
|
1414
|
-
|
|
1415
|
-
}
|
|
1416
|
-
if (includeArgs && args.length > 0) {
|
|
1417
|
-
properties.args = args;
|
|
1418
|
-
}
|
|
1419
|
-
await track(`${eventPrefix}.${transitionName}`, properties);
|
|
1359
|
+
after: ({ transitionName, prevContext, nextContext, args }) => {
|
|
1360
|
+
const event = `${eventPrefix}.${transitionName}`;
|
|
1361
|
+
const data = { transition: transitionName };
|
|
1362
|
+
if (includePrevContext) data.from = prevContext;
|
|
1363
|
+
data.to = nextContext;
|
|
1364
|
+
if (includeArgs) data.args = args;
|
|
1365
|
+
track(event, data);
|
|
1420
1366
|
}
|
|
1421
|
-
}
|
|
1367
|
+
});
|
|
1422
1368
|
}
|
|
1423
|
-
function withValidation(machine,
|
|
1369
|
+
function withValidation(machine, validator) {
|
|
1424
1370
|
return createMiddleware(machine, {
|
|
1425
1371
|
before: (ctx) => {
|
|
1426
|
-
const result =
|
|
1427
|
-
if (result instanceof Promise) {
|
|
1428
|
-
return result.then((r) => {
|
|
1429
|
-
if (r === false) {
|
|
1430
|
-
throw new Error(`Validation failed for transition: ${ctx.transitionName}`);
|
|
1431
|
-
}
|
|
1432
|
-
return void 0;
|
|
1433
|
-
});
|
|
1434
|
-
}
|
|
1372
|
+
const result = validator(ctx);
|
|
1435
1373
|
if (result === false) {
|
|
1436
1374
|
throw new Error(`Validation failed for transition: ${ctx.transitionName}`);
|
|
1437
1375
|
}
|
|
1438
|
-
return void 0;
|
|
1439
1376
|
}
|
|
1440
|
-
}
|
|
1377
|
+
});
|
|
1441
1378
|
}
|
|
1442
|
-
function withPermissions(machine,
|
|
1379
|
+
function withPermissions(machine, checker) {
|
|
1443
1380
|
return createMiddleware(machine, {
|
|
1444
1381
|
before: (ctx) => {
|
|
1445
|
-
|
|
1446
|
-
if (result instanceof Promise) {
|
|
1447
|
-
return result.then((allowed) => {
|
|
1448
|
-
if (!allowed) {
|
|
1449
|
-
throw new Error(`Unauthorized transition: ${ctx.transitionName}`);
|
|
1450
|
-
}
|
|
1451
|
-
return void 0;
|
|
1452
|
-
});
|
|
1453
|
-
}
|
|
1454
|
-
if (!result) {
|
|
1382
|
+
if (!checker(ctx)) {
|
|
1455
1383
|
throw new Error(`Unauthorized transition: ${ctx.transitionName}`);
|
|
1456
1384
|
}
|
|
1457
|
-
return void 0;
|
|
1458
1385
|
}
|
|
1459
|
-
}
|
|
1386
|
+
});
|
|
1460
1387
|
}
|
|
1461
|
-
function withErrorReporting(machine,
|
|
1462
|
-
const {
|
|
1388
|
+
function withErrorReporting(machine, reporter, options = {}) {
|
|
1389
|
+
const { includeArgs = false } = options;
|
|
1463
1390
|
return createMiddleware(machine, {
|
|
1464
|
-
error:
|
|
1465
|
-
const
|
|
1466
|
-
transition: transitionName
|
|
1391
|
+
error: (errorCtx) => {
|
|
1392
|
+
const formattedCtx = {
|
|
1393
|
+
transition: errorCtx.transitionName,
|
|
1394
|
+
context: errorCtx.context,
|
|
1395
|
+
...includeArgs && { args: errorCtx.args }
|
|
1467
1396
|
};
|
|
1468
|
-
|
|
1469
|
-
errorContext.context = context;
|
|
1470
|
-
}
|
|
1471
|
-
if (includeArgs && args.length > 0) {
|
|
1472
|
-
errorContext.args = args;
|
|
1473
|
-
}
|
|
1474
|
-
await Promise.resolve(captureError(error, errorContext));
|
|
1397
|
+
reporter(errorCtx.error, formattedCtx);
|
|
1475
1398
|
}
|
|
1476
|
-
}
|
|
1399
|
+
});
|
|
1477
1400
|
}
|
|
1478
|
-
function withPerformanceMonitoring(machine,
|
|
1479
|
-
const
|
|
1401
|
+
function withPerformanceMonitoring(machine, tracker) {
|
|
1402
|
+
const startTimes = /* @__PURE__ */ new Map();
|
|
1480
1403
|
return createMiddleware(machine, {
|
|
1481
|
-
before: (
|
|
1482
|
-
|
|
1483
|
-
return void 0;
|
|
1404
|
+
before: (ctx) => {
|
|
1405
|
+
startTimes.set(ctx.transitionName, Date.now());
|
|
1484
1406
|
},
|
|
1485
|
-
after: (
|
|
1486
|
-
const startTime =
|
|
1407
|
+
after: (result) => {
|
|
1408
|
+
const startTime = startTimes.get(result.transitionName);
|
|
1487
1409
|
if (startTime) {
|
|
1488
|
-
const duration =
|
|
1489
|
-
|
|
1490
|
-
const
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1410
|
+
const duration = Date.now() - startTime;
|
|
1411
|
+
startTimes.delete(result.transitionName);
|
|
1412
|
+
const testResult = {
|
|
1413
|
+
transitionName: result.transitionName,
|
|
1414
|
+
duration,
|
|
1415
|
+
context: result.nextContext || result.prevContext
|
|
1416
|
+
};
|
|
1417
|
+
tracker(testResult);
|
|
1494
1418
|
}
|
|
1495
|
-
return void 0;
|
|
1496
1419
|
}
|
|
1497
|
-
}
|
|
1420
|
+
});
|
|
1498
1421
|
}
|
|
1499
1422
|
function withRetry(machine, options = {}) {
|
|
1423
|
+
var _a, _b;
|
|
1500
1424
|
const {
|
|
1501
|
-
|
|
1502
|
-
delay = 1e3,
|
|
1503
|
-
backoffMultiplier = 1,
|
|
1425
|
+
maxAttempts = (_a = options.maxRetries) != null ? _a : 3,
|
|
1504
1426
|
shouldRetry = () => true,
|
|
1427
|
+
backoffMs = (_b = options.delay) != null ? _b : 100,
|
|
1428
|
+
backoffMultiplier = 2,
|
|
1505
1429
|
onRetry
|
|
1506
1430
|
} = options;
|
|
1507
|
-
const
|
|
1431
|
+
const wrappedMachine = { ...machine };
|
|
1508
1432
|
for (const prop in machine) {
|
|
1509
1433
|
if (!Object.prototype.hasOwnProperty.call(machine, prop)) continue;
|
|
1510
1434
|
const value = machine[prop];
|
|
1511
|
-
if (
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1435
|
+
if (typeof value === "function" && prop !== "context") {
|
|
1436
|
+
wrappedMachine[prop] = async function(...args) {
|
|
1437
|
+
let lastError;
|
|
1438
|
+
let attempt = 0;
|
|
1439
|
+
while (attempt < maxAttempts) {
|
|
1440
|
+
try {
|
|
1441
|
+
return await value.apply(this, args);
|
|
1442
|
+
} catch (error) {
|
|
1443
|
+
lastError = error;
|
|
1444
|
+
attempt++;
|
|
1445
|
+
if (attempt < maxAttempts && shouldRetry(lastError, attempt)) {
|
|
1446
|
+
onRetry == null ? void 0 : onRetry(lastError, attempt);
|
|
1447
|
+
const baseDelay = typeof backoffMs === "function" ? backoffMs(attempt) : backoffMs;
|
|
1448
|
+
const delay = baseDelay * Math.pow(backoffMultiplier, attempt - 1);
|
|
1449
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1450
|
+
} else {
|
|
1451
|
+
throw lastError;
|
|
1452
|
+
}
|
|
1527
1453
|
}
|
|
1528
|
-
onRetry == null ? void 0 : onRetry(attempt + 1, lastError);
|
|
1529
|
-
const currentDelay = delay * Math.pow(backoffMultiplier, attempt);
|
|
1530
|
-
await new Promise((resolve) => setTimeout(resolve, currentDelay));
|
|
1531
1454
|
}
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
}
|
|
1455
|
+
throw lastError;
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1535
1458
|
}
|
|
1536
|
-
return
|
|
1459
|
+
return wrappedMachine;
|
|
1537
1460
|
}
|
|
1461
|
+
function createCustomMiddleware(hooks, options) {
|
|
1462
|
+
return (machine) => createMiddleware(machine, hooks, options);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
// src/middleware/history.ts
|
|
1538
1466
|
function withHistory(machine, options = {}) {
|
|
1539
|
-
const {
|
|
1540
|
-
maxSize,
|
|
1541
|
-
serializer,
|
|
1542
|
-
filter,
|
|
1543
|
-
onEntry,
|
|
1544
|
-
_isRewrap = false
|
|
1545
|
-
} = options;
|
|
1467
|
+
const { maxSize, serializer, onEntry } = options;
|
|
1546
1468
|
const history = [];
|
|
1547
1469
|
let entryId = 0;
|
|
1548
1470
|
const instrumentedMachine = createMiddleware(machine, {
|
|
1549
1471
|
before: ({ transitionName, args }) => {
|
|
1550
|
-
if (filter && !filter(transitionName, args)) {
|
|
1551
|
-
return;
|
|
1552
|
-
}
|
|
1553
1472
|
const entry = {
|
|
1554
1473
|
id: `entry-${entryId++}`,
|
|
1555
1474
|
transitionName,
|
|
1556
1475
|
args: [...args],
|
|
1557
|
-
// Shallow clone args (fast, works with any type)
|
|
1558
1476
|
timestamp: Date.now()
|
|
1559
1477
|
};
|
|
1560
1478
|
if (serializer) {
|
|
@@ -1570,54 +1488,7 @@ function withHistory(machine, options = {}) {
|
|
|
1570
1488
|
}
|
|
1571
1489
|
onEntry == null ? void 0 : onEntry(entry);
|
|
1572
1490
|
}
|
|
1573
|
-
}
|
|
1574
|
-
if (!_isRewrap) {
|
|
1575
|
-
for (const prop in instrumentedMachine) {
|
|
1576
|
-
if (!Object.prototype.hasOwnProperty.call(instrumentedMachine, prop)) continue;
|
|
1577
|
-
const value = instrumentedMachine[prop];
|
|
1578
|
-
if (typeof value === "function" && !prop.startsWith("_") && prop !== "context" && !["history", "clearHistory"].includes(prop)) {
|
|
1579
|
-
const originalFn = value;
|
|
1580
|
-
instrumentedMachine[prop] = function(...args) {
|
|
1581
|
-
const result = originalFn.apply(this, args);
|
|
1582
|
-
if (result && typeof result === "object" && "context" in result && !("history" in result)) {
|
|
1583
|
-
const rewrappedResult = createMiddleware(result, {
|
|
1584
|
-
before: ({ transitionName, args: transArgs }) => {
|
|
1585
|
-
if (filter && !filter(transitionName, transArgs)) {
|
|
1586
|
-
return;
|
|
1587
|
-
}
|
|
1588
|
-
const entry = {
|
|
1589
|
-
id: `entry-${entryId++}`,
|
|
1590
|
-
transitionName,
|
|
1591
|
-
args: [...transArgs],
|
|
1592
|
-
timestamp: Date.now()
|
|
1593
|
-
};
|
|
1594
|
-
if (serializer) {
|
|
1595
|
-
try {
|
|
1596
|
-
entry.serializedArgs = serializer.serialize(transArgs);
|
|
1597
|
-
} catch (err) {
|
|
1598
|
-
console.error("Failed to serialize history args:", err);
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
history.push(entry);
|
|
1602
|
-
if (maxSize && history.length > maxSize) {
|
|
1603
|
-
history.shift();
|
|
1604
|
-
}
|
|
1605
|
-
onEntry == null ? void 0 : onEntry(entry);
|
|
1606
|
-
}
|
|
1607
|
-
}, { exclude: ["context", "history", "clearHistory"] });
|
|
1608
|
-
return Object.assign(rewrappedResult, {
|
|
1609
|
-
history,
|
|
1610
|
-
clearHistory: () => {
|
|
1611
|
-
history.length = 0;
|
|
1612
|
-
entryId = 0;
|
|
1613
|
-
}
|
|
1614
|
-
});
|
|
1615
|
-
}
|
|
1616
|
-
return result;
|
|
1617
|
-
};
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1491
|
+
});
|
|
1621
1492
|
return Object.assign(instrumentedMachine, {
|
|
1622
1493
|
history,
|
|
1623
1494
|
clearHistory: () => {
|
|
@@ -1626,37 +1497,27 @@ function withHistory(machine, options = {}) {
|
|
|
1626
1497
|
}
|
|
1627
1498
|
});
|
|
1628
1499
|
}
|
|
1500
|
+
|
|
1501
|
+
// src/middleware/snapshot.ts
|
|
1629
1502
|
function withSnapshot(machine, options = {}) {
|
|
1630
1503
|
const {
|
|
1631
1504
|
maxSize,
|
|
1632
1505
|
serializer,
|
|
1633
1506
|
captureSnapshot,
|
|
1634
|
-
|
|
1635
|
-
filter,
|
|
1636
|
-
onSnapshot,
|
|
1637
|
-
_extraExclusions = [],
|
|
1638
|
-
_isRewrap = false
|
|
1507
|
+
onlyOnChange = false
|
|
1639
1508
|
} = options;
|
|
1640
1509
|
const snapshots = [];
|
|
1641
1510
|
let snapshotId = 0;
|
|
1642
1511
|
const instrumentedMachine = createMiddleware(machine, {
|
|
1643
1512
|
after: ({ transitionName, prevContext, nextContext }) => {
|
|
1644
|
-
if (
|
|
1513
|
+
if (onlyOnChange && JSON.stringify(prevContext) === JSON.stringify(nextContext)) {
|
|
1645
1514
|
return;
|
|
1646
1515
|
}
|
|
1647
|
-
if (onlyIfChanged) {
|
|
1648
|
-
const changed = JSON.stringify(prevContext) !== JSON.stringify(nextContext);
|
|
1649
|
-
if (!changed) {
|
|
1650
|
-
return;
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
1516
|
const snapshot = {
|
|
1654
1517
|
id: `snapshot-${snapshotId++}`,
|
|
1655
1518
|
transitionName,
|
|
1656
1519
|
before: { ...prevContext },
|
|
1657
|
-
// Clone
|
|
1658
1520
|
after: { ...nextContext },
|
|
1659
|
-
// Clone
|
|
1660
1521
|
timestamp: Date.now()
|
|
1661
1522
|
};
|
|
1662
1523
|
if (serializer) {
|
|
@@ -1678,92 +1539,16 @@ function withSnapshot(machine, options = {}) {
|
|
|
1678
1539
|
if (maxSize && snapshots.length > maxSize) {
|
|
1679
1540
|
snapshots.shift();
|
|
1680
1541
|
}
|
|
1681
|
-
onSnapshot == null ? void 0 : onSnapshot(snapshot);
|
|
1682
1542
|
}
|
|
1683
|
-
}
|
|
1543
|
+
});
|
|
1684
1544
|
const restoreSnapshot = (context) => {
|
|
1685
|
-
const
|
|
1686
|
-
|
|
1545
|
+
const transitions = Object.fromEntries(
|
|
1546
|
+
Object.entries(machine).filter(
|
|
1547
|
+
([key]) => key !== "context" && key !== "snapshots" && key !== "clearSnapshots" && key !== "restoreSnapshot" && typeof machine[key] === "function"
|
|
1548
|
+
)
|
|
1549
|
+
);
|
|
1550
|
+
return Object.assign({ context }, transitions);
|
|
1687
1551
|
};
|
|
1688
|
-
if (!_isRewrap) {
|
|
1689
|
-
for (const prop in instrumentedMachine) {
|
|
1690
|
-
if (!Object.prototype.hasOwnProperty.call(instrumentedMachine, prop)) continue;
|
|
1691
|
-
const value = instrumentedMachine[prop];
|
|
1692
|
-
if (typeof value === "function" && !prop.startsWith("_") && prop !== "context" && !["snapshots", "clearSnapshots", "restoreSnapshot", "history", "clearHistory"].includes(prop)) {
|
|
1693
|
-
const originalWrappedFn = value;
|
|
1694
|
-
instrumentedMachine[prop] = function(...args) {
|
|
1695
|
-
const result = originalWrappedFn.apply(this, args);
|
|
1696
|
-
if (result && typeof result === "object" && "context" in result && !("snapshots" in result)) {
|
|
1697
|
-
for (const transProp in result) {
|
|
1698
|
-
if (!Object.prototype.hasOwnProperty.call(result, transProp)) continue;
|
|
1699
|
-
const transValue = result[transProp];
|
|
1700
|
-
if (typeof transValue === "function" && !transProp.startsWith("_") && transProp !== "context" && !["snapshots", "clearSnapshots", "restoreSnapshot", "history", "clearHistory"].includes(transProp)) {
|
|
1701
|
-
const origTransFn = transValue;
|
|
1702
|
-
result[transProp] = function(...transArgs) {
|
|
1703
|
-
const prevCtx = result.context;
|
|
1704
|
-
const transResult = origTransFn.apply(this, transArgs);
|
|
1705
|
-
if (transResult && typeof transResult === "object" && "context" in transResult) {
|
|
1706
|
-
const nextCtx = transResult.context;
|
|
1707
|
-
if (!(filter && !filter(transProp))) {
|
|
1708
|
-
let shouldRecord = true;
|
|
1709
|
-
if (onlyIfChanged) {
|
|
1710
|
-
const changed = JSON.stringify(prevCtx) !== JSON.stringify(nextCtx);
|
|
1711
|
-
shouldRecord = changed;
|
|
1712
|
-
}
|
|
1713
|
-
if (shouldRecord) {
|
|
1714
|
-
const snapshot = {
|
|
1715
|
-
id: `snapshot-${snapshotId++}`,
|
|
1716
|
-
transitionName: transProp,
|
|
1717
|
-
before: { ...prevCtx },
|
|
1718
|
-
after: { ...nextCtx },
|
|
1719
|
-
timestamp: Date.now()
|
|
1720
|
-
};
|
|
1721
|
-
if (serializer) {
|
|
1722
|
-
try {
|
|
1723
|
-
snapshot.serializedBefore = serializer.serialize(prevCtx);
|
|
1724
|
-
snapshot.serializedAfter = serializer.serialize(nextCtx);
|
|
1725
|
-
} catch (err) {
|
|
1726
|
-
console.error("Failed to serialize snapshot:", err);
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
if (captureSnapshot) {
|
|
1730
|
-
try {
|
|
1731
|
-
snapshot.diff = captureSnapshot(prevCtx, nextCtx);
|
|
1732
|
-
} catch (err) {
|
|
1733
|
-
console.error("Failed to capture snapshot:", err);
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
snapshots.push(snapshot);
|
|
1737
|
-
if (maxSize && snapshots.length > maxSize) {
|
|
1738
|
-
snapshots.shift();
|
|
1739
|
-
}
|
|
1740
|
-
onSnapshot == null ? void 0 : onSnapshot(snapshot);
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
return transResult;
|
|
1745
|
-
};
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1748
|
-
const resultWithTracking = Object.assign(result, {
|
|
1749
|
-
snapshots,
|
|
1750
|
-
clearSnapshots: () => {
|
|
1751
|
-
snapshots.length = 0;
|
|
1752
|
-
snapshotId = 0;
|
|
1753
|
-
},
|
|
1754
|
-
restoreSnapshot
|
|
1755
|
-
});
|
|
1756
|
-
if (machine.history) {
|
|
1757
|
-
resultWithTracking.history = machine.history;
|
|
1758
|
-
resultWithTracking.clearHistory = machine.clearHistory;
|
|
1759
|
-
}
|
|
1760
|
-
return resultWithTracking;
|
|
1761
|
-
}
|
|
1762
|
-
return result;
|
|
1763
|
-
};
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
}
|
|
1767
1552
|
return Object.assign(instrumentedMachine, {
|
|
1768
1553
|
snapshots,
|
|
1769
1554
|
clearSnapshots: () => {
|
|
@@ -1773,139 +1558,122 @@ function withSnapshot(machine, options = {}) {
|
|
|
1773
1558
|
restoreSnapshot
|
|
1774
1559
|
});
|
|
1775
1560
|
}
|
|
1561
|
+
|
|
1562
|
+
// src/middleware/time-travel.ts
|
|
1776
1563
|
function withTimeTravel(machine, options = {}) {
|
|
1777
1564
|
const { maxSize, serializer, onRecord } = options;
|
|
1778
1565
|
const history = [];
|
|
1779
1566
|
const snapshots = [];
|
|
1780
|
-
let
|
|
1567
|
+
let historyId = 0;
|
|
1781
1568
|
let snapshotId = 0;
|
|
1782
|
-
const
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1569
|
+
const instrumentedMachine = createMiddleware(machine, {
|
|
1570
|
+
before: ({ transitionName, args }) => {
|
|
1571
|
+
const entry = {
|
|
1572
|
+
id: `entry-${historyId++}`,
|
|
1573
|
+
transitionName,
|
|
1574
|
+
args: [...args],
|
|
1575
|
+
timestamp: Date.now()
|
|
1576
|
+
};
|
|
1577
|
+
if (serializer) {
|
|
1578
|
+
try {
|
|
1579
|
+
entry.serializedArgs = serializer.serialize(args);
|
|
1580
|
+
} catch (err) {
|
|
1581
|
+
console.error("Failed to serialize history args:", err);
|
|
1582
|
+
}
|
|
1794
1583
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
history.shift();
|
|
1799
|
-
}
|
|
1800
|
-
onRecord == null ? void 0 : onRecord("history", entry);
|
|
1801
|
-
};
|
|
1802
|
-
const recordSnapshot = (transitionName, prevContext, nextContext) => {
|
|
1803
|
-
const snapshot = {
|
|
1804
|
-
id: `snapshot-${snapshotId++}`,
|
|
1805
|
-
transitionName,
|
|
1806
|
-
before: { ...prevContext },
|
|
1807
|
-
after: { ...nextContext },
|
|
1808
|
-
timestamp: Date.now()
|
|
1809
|
-
};
|
|
1810
|
-
if (serializer) {
|
|
1811
|
-
try {
|
|
1812
|
-
snapshot.serializedBefore = serializer.serialize(prevContext);
|
|
1813
|
-
snapshot.serializedAfter = serializer.serialize(nextContext);
|
|
1814
|
-
} catch (err) {
|
|
1815
|
-
console.error("Failed to serialize snapshot:", err);
|
|
1584
|
+
history.push(entry);
|
|
1585
|
+
if (maxSize && history.length > maxSize) {
|
|
1586
|
+
history.shift();
|
|
1816
1587
|
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
const replayFrom = (snapshotIndex = 0) => {
|
|
1829
|
-
if (snapshotIndex < 0 || snapshotIndex >= snapshots.length) {
|
|
1830
|
-
throw new Error(`Invalid snapshot index: ${snapshotIndex}`);
|
|
1831
|
-
}
|
|
1832
|
-
let current = restoreSnapshot(snapshots[snapshotIndex].before);
|
|
1833
|
-
const snapshot = snapshots[snapshotIndex];
|
|
1834
|
-
const historyStartIndex = history.findIndex(
|
|
1835
|
-
(entry) => entry.transitionName === snapshot.transitionName && entry.timestamp === snapshot.timestamp
|
|
1836
|
-
);
|
|
1837
|
-
if (historyStartIndex === -1) {
|
|
1838
|
-
throw new Error("Could not find matching history entry for snapshot");
|
|
1839
|
-
}
|
|
1840
|
-
for (let i = historyStartIndex; i < history.length; i++) {
|
|
1841
|
-
const entry = history[i];
|
|
1842
|
-
const transition = current[entry.transitionName];
|
|
1843
|
-
if (typeof transition === "function") {
|
|
1588
|
+
onRecord == null ? void 0 : onRecord("history", entry);
|
|
1589
|
+
},
|
|
1590
|
+
after: ({ transitionName, prevContext, nextContext }) => {
|
|
1591
|
+
const snapshot = {
|
|
1592
|
+
id: `snapshot-${snapshotId++}`,
|
|
1593
|
+
transitionName,
|
|
1594
|
+
before: { ...prevContext },
|
|
1595
|
+
after: { ...nextContext },
|
|
1596
|
+
timestamp: Date.now()
|
|
1597
|
+
};
|
|
1598
|
+
if (serializer) {
|
|
1844
1599
|
try {
|
|
1845
|
-
|
|
1600
|
+
snapshot.serializedBefore = serializer.serialize(prevContext);
|
|
1601
|
+
snapshot.serializedAfter = serializer.serialize(nextContext);
|
|
1846
1602
|
} catch (err) {
|
|
1847
|
-
console.error(
|
|
1848
|
-
throw err;
|
|
1603
|
+
console.error("Failed to serialize snapshot:", err);
|
|
1849
1604
|
}
|
|
1850
1605
|
}
|
|
1606
|
+
snapshots.push(snapshot);
|
|
1607
|
+
if (maxSize && snapshots.length > maxSize) {
|
|
1608
|
+
snapshots.shift();
|
|
1609
|
+
}
|
|
1610
|
+
onRecord == null ? void 0 : onRecord("snapshot", snapshot);
|
|
1851
1611
|
}
|
|
1852
|
-
|
|
1612
|
+
});
|
|
1613
|
+
const restoreSnapshot = (context) => {
|
|
1614
|
+
const transitions = Object.fromEntries(
|
|
1615
|
+
Object.entries(machine).filter(
|
|
1616
|
+
([key]) => key !== "context" && key !== "history" && key !== "snapshots" && key !== "clearHistory" && key !== "clearSnapshots" && key !== "restoreSnapshot" && key !== "clearTimeTravel" && key !== "replayFrom" && typeof machine[key] === "function"
|
|
1617
|
+
)
|
|
1618
|
+
);
|
|
1619
|
+
return Object.assign({ context }, transitions);
|
|
1853
1620
|
};
|
|
1854
|
-
const
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1621
|
+
const replayFrom = (startIndex) => {
|
|
1622
|
+
var _a;
|
|
1623
|
+
if (startIndex < 0 || startIndex >= history.length) {
|
|
1624
|
+
throw new Error(`Invalid replay start index: ${startIndex}`);
|
|
1625
|
+
}
|
|
1626
|
+
let currentContext = (_a = snapshots[startIndex]) == null ? void 0 : _a.before;
|
|
1627
|
+
if (!currentContext) {
|
|
1628
|
+
throw new Error(`No snapshot available for index ${startIndex}`);
|
|
1629
|
+
}
|
|
1630
|
+
const transitionsToReplay = history.slice(startIndex);
|
|
1631
|
+
const freshMachine = Object.assign(
|
|
1632
|
+
{ context: currentContext },
|
|
1633
|
+
Object.fromEntries(
|
|
1634
|
+
Object.entries(machine).filter(
|
|
1635
|
+
([key]) => key !== "context" && typeof machine[key] === "function"
|
|
1636
|
+
)
|
|
1637
|
+
)
|
|
1638
|
+
);
|
|
1639
|
+
let replayedMachine = freshMachine;
|
|
1640
|
+
for (const entry of transitionsToReplay) {
|
|
1641
|
+
const transitionFn = replayedMachine[entry.transitionName];
|
|
1642
|
+
if (transitionFn) {
|
|
1643
|
+
replayedMachine = transitionFn.apply(replayedMachine.context, entry.args);
|
|
1872
1644
|
}
|
|
1873
1645
|
}
|
|
1874
|
-
return
|
|
1875
|
-
history,
|
|
1876
|
-
snapshots,
|
|
1877
|
-
clearHistory: () => {
|
|
1878
|
-
history.length = 0;
|
|
1879
|
-
entryId = 0;
|
|
1880
|
-
},
|
|
1881
|
-
clearSnapshots: () => {
|
|
1882
|
-
snapshots.length = 0;
|
|
1883
|
-
snapshotId = 0;
|
|
1884
|
-
},
|
|
1885
|
-
clearTimeTravel: () => {
|
|
1886
|
-
history.length = 0;
|
|
1887
|
-
snapshots.length = 0;
|
|
1888
|
-
entryId = 0;
|
|
1889
|
-
snapshotId = 0;
|
|
1890
|
-
},
|
|
1891
|
-
restoreSnapshot,
|
|
1892
|
-
replayFrom
|
|
1893
|
-
});
|
|
1646
|
+
return replayedMachine;
|
|
1894
1647
|
};
|
|
1895
|
-
return
|
|
1648
|
+
return Object.assign(instrumentedMachine, {
|
|
1649
|
+
history,
|
|
1650
|
+
snapshots,
|
|
1651
|
+
clearHistory: () => {
|
|
1652
|
+
history.length = 0;
|
|
1653
|
+
historyId = 0;
|
|
1654
|
+
},
|
|
1655
|
+
clearSnapshots: () => {
|
|
1656
|
+
snapshots.length = 0;
|
|
1657
|
+
snapshotId = 0;
|
|
1658
|
+
},
|
|
1659
|
+
clearTimeTravel: () => {
|
|
1660
|
+
history.length = 0;
|
|
1661
|
+
snapshots.length = 0;
|
|
1662
|
+
historyId = 0;
|
|
1663
|
+
snapshotId = 0;
|
|
1664
|
+
},
|
|
1665
|
+
restoreSnapshot,
|
|
1666
|
+
replayFrom
|
|
1667
|
+
});
|
|
1896
1668
|
}
|
|
1669
|
+
|
|
1670
|
+
// src/middleware/composition.ts
|
|
1897
1671
|
function compose(machine, ...middlewares) {
|
|
1898
1672
|
return middlewares.reduce((acc, middleware) => middleware(acc), machine);
|
|
1899
1673
|
}
|
|
1900
|
-
function createCustomMiddleware(hooks, options) {
|
|
1901
|
-
return (machine) => createMiddleware(machine, hooks, options);
|
|
1902
|
-
}
|
|
1903
1674
|
function composeTyped(machine, ...middlewares) {
|
|
1904
1675
|
return middlewares.reduce((acc, middleware) => middleware(acc), machine);
|
|
1905
1676
|
}
|
|
1906
|
-
function chain(machine) {
|
|
1907
|
-
return new MiddlewareChainBuilder(machine);
|
|
1908
|
-
}
|
|
1909
1677
|
var MiddlewareChainBuilder = class _MiddlewareChainBuilder {
|
|
1910
1678
|
constructor(machine) {
|
|
1911
1679
|
this.machine = machine;
|
|
@@ -1926,53 +1694,30 @@ var MiddlewareChainBuilder = class _MiddlewareChainBuilder {
|
|
|
1926
1694
|
return this.machine;
|
|
1927
1695
|
}
|
|
1928
1696
|
};
|
|
1929
|
-
function
|
|
1930
|
-
return
|
|
1697
|
+
function chain(machine) {
|
|
1698
|
+
return new MiddlewareChainBuilder(machine);
|
|
1931
1699
|
}
|
|
1932
|
-
function
|
|
1933
|
-
const {
|
|
1934
|
-
|
|
1935
|
-
logErrors = true,
|
|
1936
|
-
onError
|
|
1937
|
-
} = config;
|
|
1938
|
-
return (machine, ...middlewares) => {
|
|
1939
|
-
let currentMachine = machine;
|
|
1940
|
-
const errors = [];
|
|
1941
|
-
for (let i = 0; i < middlewares.length; i++) {
|
|
1942
|
-
const middleware = middlewares[i];
|
|
1943
|
-
try {
|
|
1944
|
-
if ("middleware" in middleware && "when" in middleware) {
|
|
1945
|
-
if (!middleware.when(currentMachine)) {
|
|
1946
|
-
continue;
|
|
1947
|
-
}
|
|
1948
|
-
currentMachine = middleware.middleware(currentMachine);
|
|
1949
|
-
} else {
|
|
1950
|
-
currentMachine = middleware(currentMachine);
|
|
1951
|
-
}
|
|
1952
|
-
} catch (error) {
|
|
1953
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
1954
|
-
errors.push({ error: err, middlewareIndex: i });
|
|
1955
|
-
if (logErrors) {
|
|
1956
|
-
console.error(`Middleware pipeline error at index ${i}:`, err);
|
|
1957
|
-
}
|
|
1958
|
-
onError == null ? void 0 : onError(err, `middleware-${i}`);
|
|
1959
|
-
if (!continueOnError) {
|
|
1960
|
-
break;
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
return {
|
|
1965
|
-
machine: currentMachine,
|
|
1966
|
-
errors,
|
|
1967
|
-
success: errors.length === 0
|
|
1968
|
-
};
|
|
1700
|
+
function when(middleware, predicate) {
|
|
1701
|
+
const conditional = function(machine) {
|
|
1702
|
+
return predicate(machine) ? middleware(machine) : machine;
|
|
1969
1703
|
};
|
|
1704
|
+
conditional.middleware = middleware;
|
|
1705
|
+
conditional.when = predicate;
|
|
1706
|
+
return conditional;
|
|
1707
|
+
}
|
|
1708
|
+
function inDevelopment(middleware) {
|
|
1709
|
+
return when(middleware, () => {
|
|
1710
|
+
return typeof process !== "undefined" ? true : typeof window !== "undefined" ? !window.location.hostname.includes("production") : false;
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
function whenContext(key, value, middleware) {
|
|
1714
|
+
return when(middleware, (machine) => machine.context[key] === value);
|
|
1970
1715
|
}
|
|
1971
1716
|
function createMiddlewareRegistry() {
|
|
1972
1717
|
const registry = /* @__PURE__ */ new Map();
|
|
1973
1718
|
return {
|
|
1974
1719
|
/**
|
|
1975
|
-
* Register a middleware
|
|
1720
|
+
* Register a middleware by name.
|
|
1976
1721
|
*/
|
|
1977
1722
|
register(name, middleware, description, priority) {
|
|
1978
1723
|
if (registry.has(name)) {
|
|
@@ -2034,21 +1779,45 @@ function createMiddlewareRegistry() {
|
|
|
2034
1779
|
}
|
|
2035
1780
|
};
|
|
2036
1781
|
}
|
|
2037
|
-
function
|
|
2038
|
-
const
|
|
2039
|
-
|
|
1782
|
+
function createPipeline(config = {}) {
|
|
1783
|
+
const {
|
|
1784
|
+
continueOnError = false,
|
|
1785
|
+
logErrors = true,
|
|
1786
|
+
onError
|
|
1787
|
+
} = config;
|
|
1788
|
+
return (machine, ...middlewares) => {
|
|
1789
|
+
let currentMachine = machine;
|
|
1790
|
+
const errors = [];
|
|
1791
|
+
let success = true;
|
|
1792
|
+
for (let i = 0; i < middlewares.length; i++) {
|
|
1793
|
+
const middleware = middlewares[i];
|
|
1794
|
+
try {
|
|
1795
|
+
if ("middleware" in middleware && "when" in middleware) {
|
|
1796
|
+
if (!middleware.when(currentMachine)) {
|
|
1797
|
+
continue;
|
|
1798
|
+
}
|
|
1799
|
+
currentMachine = middleware.middleware(currentMachine);
|
|
1800
|
+
} else {
|
|
1801
|
+
currentMachine = middleware(currentMachine);
|
|
1802
|
+
}
|
|
1803
|
+
} catch (error) {
|
|
1804
|
+
success = false;
|
|
1805
|
+
if (!continueOnError) {
|
|
1806
|
+
throw error;
|
|
1807
|
+
}
|
|
1808
|
+
errors.push({
|
|
1809
|
+
error,
|
|
1810
|
+
middlewareIndex: i,
|
|
1811
|
+
middlewareName: middleware.name
|
|
1812
|
+
});
|
|
1813
|
+
if (logErrors) {
|
|
1814
|
+
console.error(`Pipeline middleware error at index ${i}:`, error);
|
|
1815
|
+
}
|
|
1816
|
+
onError == null ? void 0 : onError(error, i, middleware.name);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
return { machine: currentMachine, errors, success };
|
|
2040
1820
|
};
|
|
2041
|
-
conditional.middleware = middleware;
|
|
2042
|
-
conditional.when = predicate;
|
|
2043
|
-
return conditional;
|
|
2044
|
-
}
|
|
2045
|
-
function inDevelopment(middleware) {
|
|
2046
|
-
return when(middleware, () => {
|
|
2047
|
-
return typeof process !== "undefined" ? true : typeof window !== "undefined" ? !window.location.hostname.includes("production") : false;
|
|
2048
|
-
});
|
|
2049
|
-
}
|
|
2050
|
-
function whenContext(key, value, middleware) {
|
|
2051
|
-
return when(middleware, (machine) => machine.context[key] === value);
|
|
2052
1821
|
}
|
|
2053
1822
|
function combine(...middlewares) {
|
|
2054
1823
|
return (machine) => composeTyped(machine, ...middlewares);
|
|
@@ -2067,7 +1836,197 @@ function isMiddlewareFn(value) {
|
|
|
2067
1836
|
return typeof value === "function" && value.length === 1;
|
|
2068
1837
|
}
|
|
2069
1838
|
function isConditionalMiddleware(value) {
|
|
2070
|
-
return value !== null && "middleware" in value && "when" in value && isMiddlewareFn(value.middleware) && typeof value.when === "function";
|
|
1839
|
+
return value !== null && (typeof value === "object" || typeof value === "function") && "middleware" in value && "when" in value && isMiddlewareFn(value.middleware) && typeof value.when === "function";
|
|
1840
|
+
}
|
|
1841
|
+
function isMiddlewareResult(value, contextType) {
|
|
1842
|
+
return value !== null && typeof value === "object" && "transitionName" in value && "prevContext" in value && "nextContext" in value && "args" in value && typeof value.transitionName === "string" && Array.isArray(value.args) && (!contextType || isValidContext(value.prevContext, contextType) && isValidContext(value.nextContext, contextType));
|
|
1843
|
+
}
|
|
1844
|
+
function isMiddlewareContext(value, contextType) {
|
|
1845
|
+
return value !== null && typeof value === "object" && "transitionName" in value && "context" in value && "args" in value && typeof value.transitionName === "string" && Array.isArray(value.args) && (!contextType || isValidContext(value.context, contextType));
|
|
1846
|
+
}
|
|
1847
|
+
function isMiddlewareError(value, contextType) {
|
|
1848
|
+
return value !== null && typeof value === "object" && "transitionName" in value && "context" in value && "args" in value && "error" in value && typeof value.transitionName === "string" && Array.isArray(value.args) && value.error instanceof Error && (!contextType || isValidContext(value.context, contextType));
|
|
1849
|
+
}
|
|
1850
|
+
function isMiddlewareHooks(value, _contextType) {
|
|
1851
|
+
if (value === null || typeof value !== "object") return false;
|
|
1852
|
+
const hooks = value;
|
|
1853
|
+
if ("before" in hooks && hooks.before !== void 0) {
|
|
1854
|
+
if (typeof hooks.before !== "function") return false;
|
|
1855
|
+
}
|
|
1856
|
+
if ("after" in hooks && hooks.after !== void 0) {
|
|
1857
|
+
if (typeof hooks.after !== "function") return false;
|
|
1858
|
+
}
|
|
1859
|
+
if ("error" in hooks && hooks.error !== void 0) {
|
|
1860
|
+
if (typeof hooks.error !== "function") return false;
|
|
1861
|
+
}
|
|
1862
|
+
return true;
|
|
1863
|
+
}
|
|
1864
|
+
function isMiddlewareOptions(value) {
|
|
1865
|
+
return value === void 0 || value !== null && typeof value === "object" && ("continueOnError" in value ? typeof value.continueOnError === "boolean" : true) && ("logErrors" in value ? typeof value.logErrors === "boolean" : true) && ("onError" in value ? typeof value.onError === "function" || value.onError === void 0 : true);
|
|
1866
|
+
}
|
|
1867
|
+
function isValidContext(value, _contextType) {
|
|
1868
|
+
return value !== null && typeof value === "object";
|
|
1869
|
+
}
|
|
1870
|
+
function isNamedMiddleware(value) {
|
|
1871
|
+
return value !== null && typeof value === "object" && "name" in value && "middleware" in value && typeof value.name === "string" && isMiddlewareFn(value.middleware) && ("description" in value ? typeof value.description === "string" || value.description === void 0 : true) && ("priority" in value ? typeof value.priority === "number" || value.priority === void 0 : true);
|
|
1872
|
+
}
|
|
1873
|
+
function isPipelineConfig(value) {
|
|
1874
|
+
return value === void 0 || value !== null && typeof value === "object" && ("continueOnError" in value ? typeof value.continueOnError === "boolean" : true) && ("logErrors" in value ? typeof value.logErrors === "boolean" : true) && ("onError" in value ? typeof value.onError === "function" || value.onError === void 0 : true);
|
|
1875
|
+
}
|
|
1876
|
+
var MiddlewareBuilder = class {
|
|
1877
|
+
constructor(machine) {
|
|
1878
|
+
this.machine = machine;
|
|
1879
|
+
this.middlewares = [];
|
|
1880
|
+
}
|
|
1881
|
+
/**
|
|
1882
|
+
* Add logging middleware with type-safe configuration.
|
|
1883
|
+
*/
|
|
1884
|
+
withLogging(options) {
|
|
1885
|
+
this.middlewares.push((machine) => withLogging(machine, options));
|
|
1886
|
+
return this;
|
|
1887
|
+
}
|
|
1888
|
+
/**
|
|
1889
|
+
* Add analytics middleware with type-safe configuration.
|
|
1890
|
+
*/
|
|
1891
|
+
withAnalytics(track, options) {
|
|
1892
|
+
this.middlewares.push((machine) => withAnalytics(machine, track, options));
|
|
1893
|
+
return this;
|
|
1894
|
+
}
|
|
1895
|
+
/**
|
|
1896
|
+
* Add validation middleware with type-safe configuration.
|
|
1897
|
+
*/
|
|
1898
|
+
withValidation(validator, _options) {
|
|
1899
|
+
this.middlewares.push((machine) => withValidation(machine, validator));
|
|
1900
|
+
return this;
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Add permission checking middleware with type-safe configuration.
|
|
1904
|
+
*/
|
|
1905
|
+
withPermissions(checker) {
|
|
1906
|
+
this.middlewares.push((machine) => withPermissions(machine, checker));
|
|
1907
|
+
return this;
|
|
1908
|
+
}
|
|
1909
|
+
/**
|
|
1910
|
+
* Add error reporting middleware with type-safe configuration.
|
|
1911
|
+
*/
|
|
1912
|
+
withErrorReporting(reporter, options) {
|
|
1913
|
+
this.middlewares.push((machine) => withErrorReporting(machine, reporter, options));
|
|
1914
|
+
return this;
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* Add performance monitoring middleware with type-safe configuration.
|
|
1918
|
+
*/
|
|
1919
|
+
withPerformanceMonitoring(tracker, _options) {
|
|
1920
|
+
this.middlewares.push((machine) => withPerformanceMonitoring(machine, tracker));
|
|
1921
|
+
return this;
|
|
1922
|
+
}
|
|
1923
|
+
/**
|
|
1924
|
+
* Add retry middleware with type-safe configuration.
|
|
1925
|
+
*/
|
|
1926
|
+
withRetry(options) {
|
|
1927
|
+
this.middlewares.push((machine) => withRetry(machine, options));
|
|
1928
|
+
return this;
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Add history tracking middleware with type-safe configuration.
|
|
1932
|
+
*/
|
|
1933
|
+
withHistory(options) {
|
|
1934
|
+
this.middlewares.push((machine) => withHistory(machine, options));
|
|
1935
|
+
return this;
|
|
1936
|
+
}
|
|
1937
|
+
/**
|
|
1938
|
+
* Add snapshot tracking middleware with type-safe configuration.
|
|
1939
|
+
*/
|
|
1940
|
+
withSnapshot(options) {
|
|
1941
|
+
this.middlewares.push((machine) => withSnapshot(machine, options));
|
|
1942
|
+
return this;
|
|
1943
|
+
}
|
|
1944
|
+
/**
|
|
1945
|
+
* Add time travel middleware with type-safe configuration.
|
|
1946
|
+
*/
|
|
1947
|
+
withTimeTravel(options) {
|
|
1948
|
+
this.middlewares.push((machine) => withTimeTravel(machine, options));
|
|
1949
|
+
return this;
|
|
1950
|
+
}
|
|
1951
|
+
/**
|
|
1952
|
+
* Add debugging middleware (combination of history, snapshot, and time travel).
|
|
1953
|
+
*/
|
|
1954
|
+
withDebugging() {
|
|
1955
|
+
this.middlewares.push((machine) => withDebugging(machine));
|
|
1956
|
+
return this;
|
|
1957
|
+
}
|
|
1958
|
+
/**
|
|
1959
|
+
* Add a custom middleware function.
|
|
1960
|
+
*/
|
|
1961
|
+
withCustom(middleware) {
|
|
1962
|
+
this.middlewares.push(middleware);
|
|
1963
|
+
return this;
|
|
1964
|
+
}
|
|
1965
|
+
/**
|
|
1966
|
+
* Add a conditional middleware.
|
|
1967
|
+
*/
|
|
1968
|
+
withConditional(middleware, predicate) {
|
|
1969
|
+
this.middlewares.push(when(middleware, predicate));
|
|
1970
|
+
return this;
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* Build the final machine with all configured middleware applied.
|
|
1974
|
+
*/
|
|
1975
|
+
build() {
|
|
1976
|
+
let result = this.machine;
|
|
1977
|
+
for (const middleware of this.middlewares) {
|
|
1978
|
+
result = middleware(result);
|
|
1979
|
+
}
|
|
1980
|
+
return result;
|
|
1981
|
+
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Get the middleware chain without building (for inspection or further composition).
|
|
1984
|
+
*/
|
|
1985
|
+
getChain() {
|
|
1986
|
+
return [...this.middlewares];
|
|
1987
|
+
}
|
|
1988
|
+
/**
|
|
1989
|
+
* Clear all configured middleware.
|
|
1990
|
+
*/
|
|
1991
|
+
clear() {
|
|
1992
|
+
this.middlewares = [];
|
|
1993
|
+
return this;
|
|
1994
|
+
}
|
|
1995
|
+
};
|
|
1996
|
+
function middlewareBuilder(machine) {
|
|
1997
|
+
return new MiddlewareBuilder(machine);
|
|
1998
|
+
}
|
|
1999
|
+
function createMiddlewareFactory(defaultOptions = {}) {
|
|
2000
|
+
return {
|
|
2001
|
+
create: (machine) => {
|
|
2002
|
+
const builder = middlewareBuilder(machine);
|
|
2003
|
+
if (defaultOptions.logging) {
|
|
2004
|
+
builder.withLogging(defaultOptions.logging);
|
|
2005
|
+
}
|
|
2006
|
+
if (defaultOptions.analytics) {
|
|
2007
|
+
builder.withAnalytics(
|
|
2008
|
+
defaultOptions.analytics.track,
|
|
2009
|
+
defaultOptions.analytics.options
|
|
2010
|
+
);
|
|
2011
|
+
}
|
|
2012
|
+
if (defaultOptions.history) {
|
|
2013
|
+
builder.withHistory(defaultOptions.history);
|
|
2014
|
+
}
|
|
2015
|
+
if (defaultOptions.snapshot) {
|
|
2016
|
+
builder.withSnapshot(defaultOptions.snapshot);
|
|
2017
|
+
}
|
|
2018
|
+
if (defaultOptions.timeTravel) {
|
|
2019
|
+
builder.withTimeTravel(defaultOptions.timeTravel);
|
|
2020
|
+
}
|
|
2021
|
+
if (defaultOptions.retry) {
|
|
2022
|
+
builder.withRetry(defaultOptions.retry);
|
|
2023
|
+
}
|
|
2024
|
+
return builder;
|
|
2025
|
+
}
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
function withDebugging(machine) {
|
|
2029
|
+
return withTimeTravel(withSnapshot(withHistory(machine)));
|
|
2071
2030
|
}
|
|
2072
2031
|
|
|
2073
2032
|
// src/utils.ts
|
|
@@ -2194,15 +2153,61 @@ function state(context, transitions) {
|
|
|
2194
2153
|
}
|
|
2195
2154
|
|
|
2196
2155
|
// src/index.ts
|
|
2197
|
-
function createMachine(context,
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2156
|
+
function createMachine(context, fnsOrFactory) {
|
|
2157
|
+
if (typeof fnsOrFactory === "function") {
|
|
2158
|
+
let transitions2;
|
|
2159
|
+
const transition = (newContext) => {
|
|
2160
|
+
const machine2 = createMachine(newContext, transitions2);
|
|
2161
|
+
const boundTransitions2 = Object.fromEntries(
|
|
2162
|
+
Object.entries(transitions2).map(([key, fn]) => [
|
|
2163
|
+
key,
|
|
2164
|
+
fn.bind(newContext)
|
|
2165
|
+
])
|
|
2166
|
+
);
|
|
2167
|
+
return Object.assign(machine2, boundTransitions2);
|
|
2168
|
+
};
|
|
2169
|
+
transitions2 = fnsOrFactory(transition);
|
|
2170
|
+
const boundTransitions = Object.fromEntries(
|
|
2171
|
+
Object.entries(transitions2).map(([key, fn]) => [
|
|
2172
|
+
key,
|
|
2173
|
+
fn.bind(context)
|
|
2174
|
+
])
|
|
2175
|
+
);
|
|
2176
|
+
return Object.assign({ context }, boundTransitions);
|
|
2177
|
+
}
|
|
2178
|
+
const transitions = "context" in fnsOrFactory ? Object.fromEntries(
|
|
2179
|
+
Object.entries(fnsOrFactory).filter(([key]) => key !== "context")
|
|
2180
|
+
) : fnsOrFactory;
|
|
2201
2181
|
const machine = Object.assign({ context }, transitions);
|
|
2202
2182
|
return machine;
|
|
2203
2183
|
}
|
|
2204
|
-
function createAsyncMachine(context,
|
|
2205
|
-
|
|
2184
|
+
function createAsyncMachine(context, fnsOrFactory) {
|
|
2185
|
+
if (typeof fnsOrFactory === "function") {
|
|
2186
|
+
let transitions2;
|
|
2187
|
+
const transition = (newContext) => {
|
|
2188
|
+
const machine2 = createAsyncMachine(newContext, transitions2);
|
|
2189
|
+
const boundTransitions2 = Object.fromEntries(
|
|
2190
|
+
Object.entries(transitions2).map(([key, fn]) => [
|
|
2191
|
+
key,
|
|
2192
|
+
fn.bind(newContext)
|
|
2193
|
+
])
|
|
2194
|
+
);
|
|
2195
|
+
return Object.assign(machine2, boundTransitions2);
|
|
2196
|
+
};
|
|
2197
|
+
transitions2 = fnsOrFactory(transition);
|
|
2198
|
+
const boundTransitions = Object.fromEntries(
|
|
2199
|
+
Object.entries(transitions2).map(([key, fn]) => [
|
|
2200
|
+
key,
|
|
2201
|
+
fn.bind(context)
|
|
2202
|
+
])
|
|
2203
|
+
);
|
|
2204
|
+
return Object.assign({ context }, boundTransitions);
|
|
2205
|
+
}
|
|
2206
|
+
const transitions = "context" in fnsOrFactory ? Object.fromEntries(
|
|
2207
|
+
Object.entries(fnsOrFactory).filter(([key]) => key !== "context")
|
|
2208
|
+
) : fnsOrFactory;
|
|
2209
|
+
const machine = Object.assign({ context }, transitions);
|
|
2210
|
+
return machine;
|
|
2206
2211
|
}
|
|
2207
2212
|
function createMachineFactory() {
|
|
2208
2213
|
return (transformers) => {
|
|
@@ -2210,7 +2215,7 @@ function createMachineFactory() {
|
|
|
2210
2215
|
Object.entries(transformers).map(([key, transform]) => [
|
|
2211
2216
|
key,
|
|
2212
2217
|
function(...args) {
|
|
2213
|
-
const newContext = transform(this, ...args);
|
|
2218
|
+
const newContext = transform(this.context, ...args);
|
|
2214
2219
|
return createMachine(newContext, fns);
|
|
2215
2220
|
}
|
|
2216
2221
|
])
|