@doeixd/machine 0.0.13 → 0.0.17

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