@doeixd/machine 0.0.17 → 0.0.19

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.
@@ -1,10 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/generators.ts
9
2
  function run(flow, initial) {
10
3
  const generator = flow(initial);
@@ -591,441 +584,6 @@ function createParallelMachine(m1, m2) {
591
584
  };
592
585
  }
593
586
 
594
- // src/extract.ts
595
- import { Project, Node } from "ts-morph";
596
- function resolveClassName(node) {
597
- if (Node.isIdentifier(node)) {
598
- return node.getText();
599
- }
600
- if (Node.isTypeOfExpression(node)) {
601
- return node.getExpression().getText();
602
- }
603
- return "unknown";
604
- }
605
- function parseObjectLiteral(obj) {
606
- if (!Node.isObjectLiteralExpression(obj)) {
607
- return {};
608
- }
609
- const result = {};
610
- for (const prop of obj.getProperties()) {
611
- if (Node.isPropertyAssignment(prop)) {
612
- const name = prop.getName();
613
- const init = prop.getInitializer();
614
- if (init) {
615
- if (Node.isStringLiteral(init)) {
616
- result[name] = init.getLiteralValue();
617
- } else if (Node.isNumericLiteral(init)) {
618
- result[name] = init.getLiteralValue();
619
- } else if (init.getText() === "true" || init.getText() === "false") {
620
- result[name] = init.getText() === "true";
621
- } else if (Node.isIdentifier(init)) {
622
- result[name] = init.getText();
623
- } else if (Node.isObjectLiteralExpression(init)) {
624
- result[name] = parseObjectLiteral(init);
625
- } else if (Node.isArrayLiteralExpression(init)) {
626
- result[name] = init.getElements().map((el) => {
627
- if (Node.isObjectLiteralExpression(el)) {
628
- return parseObjectLiteral(el);
629
- }
630
- return el.getText();
631
- });
632
- }
633
- }
634
- }
635
- }
636
- return result;
637
- }
638
- function parseInvokeService(obj) {
639
- if (!Node.isObjectLiteralExpression(obj)) {
640
- return {};
641
- }
642
- const service = {};
643
- for (const prop of obj.getProperties()) {
644
- if (Node.isPropertyAssignment(prop)) {
645
- const name = prop.getName();
646
- const init = prop.getInitializer();
647
- if (!init) continue;
648
- if (name === "onDone" || name === "onError") {
649
- service[name] = resolveClassName(init);
650
- } else if (Node.isStringLiteral(init)) {
651
- service[name] = init.getLiteralValue();
652
- } else if (Node.isIdentifier(init)) {
653
- service[name] = init.getText();
654
- }
655
- }
656
- }
657
- return service;
658
- }
659
- function extractFromCallExpression(call2, verbose = false) {
660
- if (!Node.isCallExpression(call2)) {
661
- return null;
662
- }
663
- const expression = call2.getExpression();
664
- const fnName = Node.isIdentifier(expression) ? expression.getText() : null;
665
- if (!fnName) {
666
- return null;
667
- }
668
- const metadata2 = {};
669
- const args = call2.getArguments();
670
- switch (fnName) {
671
- case "transitionTo":
672
- if (args[0]) {
673
- metadata2.target = resolveClassName(args[0]);
674
- }
675
- break;
676
- case "describe":
677
- if (args[0] && Node.isStringLiteral(args[0])) {
678
- metadata2.description = args[0].getLiteralValue();
679
- }
680
- if (args[1] && Node.isCallExpression(args[1])) {
681
- const nested = extractFromCallExpression(args[1], verbose);
682
- if (nested) {
683
- Object.assign(metadata2, nested);
684
- }
685
- }
686
- break;
687
- case "guarded":
688
- if (args[0]) {
689
- const guard2 = parseObjectLiteral(args[0]);
690
- if (Object.keys(guard2).length > 0) {
691
- metadata2.guards = [guard2];
692
- }
693
- }
694
- if (args[1] && Node.isCallExpression(args[1])) {
695
- const nested = extractFromCallExpression(args[1], verbose);
696
- if (nested) {
697
- Object.assign(metadata2, nested);
698
- }
699
- }
700
- break;
701
- case "invoke":
702
- if (args[0]) {
703
- const service = parseInvokeService(args[0]);
704
- if (Object.keys(service).length > 0) {
705
- metadata2.invoke = service;
706
- }
707
- }
708
- break;
709
- case "action":
710
- if (args[0]) {
711
- const actionMeta = parseObjectLiteral(args[0]);
712
- if (Object.keys(actionMeta).length > 0) {
713
- metadata2.actions = [actionMeta];
714
- }
715
- }
716
- if (args[1] && Node.isCallExpression(args[1])) {
717
- const nested = extractFromCallExpression(args[1], verbose);
718
- if (nested) {
719
- Object.assign(metadata2, nested);
720
- }
721
- }
722
- break;
723
- case "guard":
724
- if (args[2]) {
725
- const options = parseObjectLiteral(args[2]);
726
- if (options.description) {
727
- metadata2.description = options.description;
728
- }
729
- }
730
- metadata2.guards = [{ name: "runtime_guard", description: metadata2.description || "Synchronous condition check" }];
731
- if (args[1] && Node.isCallExpression(args[1])) {
732
- const nested = extractFromCallExpression(args[1], verbose);
733
- if (nested) {
734
- Object.assign(metadata2, nested);
735
- }
736
- }
737
- break;
738
- case "guardAsync":
739
- if (args[2]) {
740
- const options = parseObjectLiteral(args[2]);
741
- if (options.description) {
742
- metadata2.description = options.description;
743
- }
744
- }
745
- metadata2.guards = [{ name: "runtime_guard_async", description: metadata2.description || "Asynchronous condition check" }];
746
- if (args[1] && Node.isCallExpression(args[1])) {
747
- const nested = extractFromCallExpression(args[1], verbose);
748
- if (nested) {
749
- Object.assign(metadata2, nested);
750
- }
751
- }
752
- break;
753
- default:
754
- return null;
755
- }
756
- return Object.keys(metadata2).length > 0 ? metadata2 : null;
757
- }
758
- function extractMetaFromMember(member, verbose = false) {
759
- if (!Node.isPropertyDeclaration(member)) {
760
- if (verbose) console.error(` ⚠️ Not a property declaration`);
761
- return null;
762
- }
763
- const initializer = member.getInitializer();
764
- if (!initializer) {
765
- if (verbose) console.error(` ⚠️ No initializer`);
766
- return null;
767
- }
768
- if (!Node.isCallExpression(initializer)) {
769
- if (verbose) console.error(` ⚠️ Initializer is not a call expression`);
770
- return null;
771
- }
772
- const metadata2 = extractFromCallExpression(initializer, verbose);
773
- if (metadata2 && verbose) {
774
- console.error(` ✅ Extracted metadata:`, JSON.stringify(metadata2, null, 2));
775
- }
776
- return metadata2;
777
- }
778
- function analyzeStateNode(classSymbol, verbose = false) {
779
- const chartNode = { on: {} };
780
- const classDeclaration = classSymbol.getDeclarations()[0];
781
- if (!classDeclaration || !Node.isClassDeclaration(classDeclaration)) {
782
- if (verbose) {
783
- console.error(`⚠️ Warning: Could not get class declaration for ${classSymbol.getName()}`);
784
- }
785
- return chartNode;
786
- }
787
- const className = classSymbol.getName();
788
- if (verbose) {
789
- console.error(` Analyzing state: ${className}`);
790
- }
791
- for (const member of classDeclaration.getInstanceMembers()) {
792
- const memberName = member.getName();
793
- if (verbose) {
794
- console.error(` Checking member: ${memberName}`);
795
- }
796
- const meta = extractMetaFromMember(member, verbose);
797
- if (!meta) continue;
798
- if (verbose) {
799
- console.error(` Found transition: ${memberName}`);
800
- }
801
- const { invoke: invoke2, actions, guards, ...onEntry } = meta;
802
- if (invoke2) {
803
- if (!chartNode.invoke) chartNode.invoke = [];
804
- chartNode.invoke.push({
805
- src: invoke2.src,
806
- onDone: { target: invoke2.onDone },
807
- onError: { target: invoke2.onError },
808
- description: invoke2.description
809
- });
810
- if (verbose) {
811
- console.error(` → Invoke: ${invoke2.src}`);
812
- }
813
- }
814
- if (onEntry.target) {
815
- const transition = { target: onEntry.target };
816
- if (onEntry.description) {
817
- transition.description = onEntry.description;
818
- }
819
- if (guards) {
820
- transition.cond = guards.map((g) => g.name).join(" && ");
821
- if (verbose) {
822
- console.error(` → Guard: ${transition.cond}`);
823
- }
824
- }
825
- if (actions && actions.length > 0) {
826
- transition.actions = actions.map((a) => a.name);
827
- if (verbose) {
828
- console.error(` → Actions: ${transition.actions.join(", ")}`);
829
- }
830
- }
831
- chartNode.on[memberName] = transition;
832
- if (verbose) {
833
- console.error(` → Target: ${onEntry.target}`);
834
- }
835
- }
836
- }
837
- return chartNode;
838
- }
839
- function analyzeStateNodeWithNesting(className, classSymbol, sourceFile, childConfig, verbose = false) {
840
- const stateNode = analyzeStateNode(classSymbol, verbose);
841
- if (childConfig) {
842
- if (verbose) {
843
- console.error(` 👪 Analyzing children for state: ${className}`);
844
- }
845
- stateNode.initial = childConfig.initialState;
846
- stateNode.states = {};
847
- for (const childClassName of childConfig.classes) {
848
- const childClassDeclaration = sourceFile.getClass(childClassName);
849
- if (childClassDeclaration) {
850
- const childSymbol = childClassDeclaration.getSymbolOrThrow();
851
- stateNode.states[childClassName] = analyzeStateNode(childSymbol, verbose);
852
- } else {
853
- console.warn(`⚠️ Warning: Child class '${childClassName}' not found.`);
854
- }
855
- }
856
- }
857
- return stateNode;
858
- }
859
- function extractMachine(config, project, verbose = false) {
860
- if (verbose) {
861
- console.error(`
862
- 🔍 Analyzing machine: ${config.id}`);
863
- console.error(` Source: ${config.input}`);
864
- }
865
- const sourceFile = project.getSourceFile(config.input);
866
- if (!sourceFile) {
867
- throw new Error(`Source file not found: ${config.input}`);
868
- }
869
- if (config.parallel) {
870
- if (verbose) {
871
- console.error(` ⏹️ Parallel machine detected. Analyzing regions.`);
872
- }
873
- const parallelChart = {
874
- id: config.id,
875
- type: "parallel",
876
- states: {}
877
- };
878
- if (config.description) {
879
- parallelChart.description = config.description;
880
- }
881
- for (const region of config.parallel.regions) {
882
- if (verbose) {
883
- console.error(` 📍 Analyzing region: ${region.name}`);
884
- }
885
- const regionStates = {};
886
- for (const className of region.classes) {
887
- const classDeclaration = sourceFile.getClass(className);
888
- if (classDeclaration) {
889
- const classSymbol = classDeclaration.getSymbolOrThrow();
890
- regionStates[className] = analyzeStateNode(classSymbol, verbose);
891
- } else {
892
- console.warn(`⚠️ Warning: Class '${className}' not found for region '${region.name}'.`);
893
- }
894
- }
895
- parallelChart.states[region.name] = {
896
- initial: region.initialState,
897
- states: regionStates
898
- };
899
- }
900
- if (verbose) {
901
- console.error(` ✅ Extracted ${config.parallel.regions.length} parallel regions`);
902
- }
903
- return parallelChart;
904
- }
905
- if (!config.initialState || !config.classes) {
906
- throw new Error(`Machine config for '${config.id}' must have either 'parallel' or 'initialState'/'classes'.`);
907
- }
908
- const fullChart = {
909
- id: config.id,
910
- initial: config.initialState,
911
- states: {}
912
- };
913
- if (config.description) {
914
- fullChart.description = config.description;
915
- }
916
- for (const className of config.classes) {
917
- const classDeclaration = sourceFile.getClass(className);
918
- if (!classDeclaration) {
919
- console.warn(`⚠️ Warning: Class '${className}' not found in '${config.input}'. Skipping.`);
920
- continue;
921
- }
922
- const classSymbol = classDeclaration.getSymbolOrThrow();
923
- const hasChildren = className === config.initialState && config.children;
924
- const stateNode = analyzeStateNodeWithNesting(
925
- className,
926
- classSymbol,
927
- sourceFile,
928
- hasChildren ? config.children : void 0,
929
- verbose
930
- );
931
- fullChart.states[className] = stateNode;
932
- }
933
- if (verbose) {
934
- console.error(` ✅ Extracted ${config.classes.length} states`);
935
- }
936
- return fullChart;
937
- }
938
- function extractMachines(config) {
939
- var _a;
940
- const verbose = (_a = config.verbose) != null ? _a : false;
941
- if (verbose) {
942
- console.error(`
943
- 📊 Starting statechart extraction`);
944
- console.error(` Machines to extract: ${config.machines.length}`);
945
- }
946
- const project = new Project();
947
- project.addSourceFilesAtPaths("src/**/*.ts");
948
- project.addSourceFilesAtPaths("examples/**/*.ts");
949
- const results = [];
950
- for (const machineConfig of config.machines) {
951
- try {
952
- const chart = extractMachine(machineConfig, project, verbose);
953
- results.push(chart);
954
- } catch (error) {
955
- console.error(`❌ Error extracting machine '${machineConfig.id}':`, error);
956
- if (!verbose) {
957
- console.error(` Run with --verbose for more details`);
958
- }
959
- }
960
- }
961
- if (verbose) {
962
- console.error(`
963
- ✅ Extraction complete: ${results.length}/${config.machines.length} machines extracted`);
964
- }
965
- return results;
966
- }
967
- function generateChart() {
968
- const config = {
969
- input: "examples/authMachine.ts",
970
- classes: [
971
- "LoggedOutMachine",
972
- "LoggingInMachine",
973
- "LoggedInMachine",
974
- "SessionExpiredMachine",
975
- "ErrorMachine"
976
- ],
977
- id: "auth",
978
- initialState: "LoggedOutMachine",
979
- description: "Authentication state machine"
980
- };
981
- console.error("🔍 Using legacy generateChart function");
982
- console.error("⚠️ Consider using extractMachines() with a config file instead\n");
983
- const project = new Project();
984
- project.addSourceFilesAtPaths("src/**/*.ts");
985
- project.addSourceFilesAtPaths("examples/**/*.ts");
986
- try {
987
- const chart = extractMachine(config, project, true);
988
- console.log(JSON.stringify(chart, null, 2));
989
- } catch (error) {
990
- console.error(`❌ Error:`, error);
991
- process.exit(1);
992
- }
993
- }
994
- var ADVANCED_CONFIG_EXAMPLES = {
995
- hierarchical: {
996
- input: "examples/dashboardMachine.ts",
997
- id: "dashboard",
998
- classes: ["DashboardMachine", "LoggedOutMachine"],
999
- initialState: "DashboardMachine",
1000
- children: {
1001
- contextProperty: "child",
1002
- initialState: "ViewingChildMachine",
1003
- classes: ["ViewingChildMachine", "EditingChildMachine"]
1004
- }
1005
- },
1006
- parallel: {
1007
- input: "examples/editorMachine.ts",
1008
- id: "editor",
1009
- parallel: {
1010
- regions: [
1011
- {
1012
- name: "fontWeight",
1013
- initialState: "NormalWeight",
1014
- classes: ["NormalWeight", "BoldWeight"]
1015
- },
1016
- {
1017
- name: "textDecoration",
1018
- initialState: "NoDecoration",
1019
- classes: ["NoDecoration", "UnderlineState"]
1020
- }
1021
- ]
1022
- }
1023
- }
1024
- };
1025
- if (__require.main === module) {
1026
- generateChart();
1027
- }
1028
-
1029
587
  // src/middleware/core.ts
1030
588
  var CANCEL = Symbol("CANCEL");
1031
589
  function createMiddleware(machine, hooks, options = {}) {
@@ -1999,6 +1557,132 @@ function state(context, transitions) {
1999
1557
  return createFunctionalMachine(context);
2000
1558
  }
2001
1559
 
1560
+ // src/matcher.ts
1561
+ function createMatcher(...cases) {
1562
+ const nameToCase = /* @__PURE__ */ new Map();
1563
+ for (const [name, _, predicate] of cases) {
1564
+ if (nameToCase.has(name)) {
1565
+ throw new Error(`Duplicate matcher case name: "${name}"`);
1566
+ }
1567
+ nameToCase.set(name, { predicate });
1568
+ }
1569
+ const isProxy = new Proxy({}, {
1570
+ get(_target, prop) {
1571
+ return function isGuard(machine) {
1572
+ const caseConfig = nameToCase.get(prop);
1573
+ if (!caseConfig) {
1574
+ const available = Array.from(nameToCase.keys()).join(", ");
1575
+ throw new Error(
1576
+ `Unknown matcher case: "${prop}". Available cases: ${available}`
1577
+ );
1578
+ }
1579
+ return caseConfig.predicate(machine);
1580
+ };
1581
+ }
1582
+ });
1583
+ const caseProxy = new Proxy({}, {
1584
+ get(_target, prop) {
1585
+ return function createCaseHandler(handler) {
1586
+ if (!nameToCase.has(prop)) {
1587
+ const available = Array.from(nameToCase.keys()).join(", ");
1588
+ throw new Error(
1589
+ `Unknown matcher case: "${prop}". Available cases: ${available}`
1590
+ );
1591
+ }
1592
+ return {
1593
+ __brand: "CaseHandler",
1594
+ __name: prop,
1595
+ __machine: void 0,
1596
+ __return: void 0,
1597
+ handler
1598
+ };
1599
+ };
1600
+ }
1601
+ });
1602
+ const exhaustive = { __exhaustive: true };
1603
+ function when2(machine) {
1604
+ return {
1605
+ is(...handlers) {
1606
+ if (handlers.length === 0) {
1607
+ throw new Error("Pattern match requires at least one handler and exhaustiveness marker");
1608
+ }
1609
+ const lastHandler = handlers[handlers.length - 1];
1610
+ if (!lastHandler || typeof lastHandler !== "object" || !("__exhaustive" in lastHandler)) {
1611
+ throw new Error(
1612
+ "Pattern match must end with match.exhaustive for compile-time exhaustiveness checking"
1613
+ );
1614
+ }
1615
+ const actualHandlers = handlers.slice(0, -1);
1616
+ for (const caseHandler of actualHandlers) {
1617
+ const caseName = caseHandler.__name;
1618
+ const caseConfig = nameToCase.get(caseName);
1619
+ if (!caseConfig) {
1620
+ throw new Error(`Internal error: Unknown matcher case in handler: ${caseName}`);
1621
+ }
1622
+ if (caseConfig.predicate(machine)) {
1623
+ return caseHandler.handler(machine);
1624
+ }
1625
+ }
1626
+ const handledCases = actualHandlers.map((h) => h.__name).join(", ");
1627
+ const allCases = Array.from(nameToCase.keys()).join(", ");
1628
+ throw new Error(
1629
+ `Non-exhaustive pattern match at runtime: no handler matched the machine.
1630
+ Handled cases: [${handledCases}]
1631
+ All cases: [${allCases}]
1632
+ This may occur if predicates don't cover all runtime possibilities.`
1633
+ );
1634
+ }
1635
+ };
1636
+ }
1637
+ function simpleMatcher(machine) {
1638
+ for (const [name, _, predicate] of cases) {
1639
+ if (predicate(machine)) {
1640
+ return name;
1641
+ }
1642
+ }
1643
+ return null;
1644
+ }
1645
+ return Object.assign(simpleMatcher, {
1646
+ is: isProxy,
1647
+ when: when2,
1648
+ case: caseProxy,
1649
+ exhaustive
1650
+ });
1651
+ }
1652
+ function classCase(name, machineClass) {
1653
+ return [
1654
+ name,
1655
+ void 0,
1656
+ // Type-only, not used at runtime
1657
+ (m) => m instanceof machineClass
1658
+ ];
1659
+ }
1660
+ function discriminantCase(name, key, value) {
1661
+ return [
1662
+ name,
1663
+ void 0,
1664
+ // Type-only, not used at runtime
1665
+ (m) => hasState(m, key, value)
1666
+ ];
1667
+ }
1668
+ function customCase(name, predicate) {
1669
+ return [name, void 0, predicate];
1670
+ }
1671
+ function forContext() {
1672
+ return {
1673
+ /**
1674
+ * Creates a discriminated union case with full type inference.
1675
+ */
1676
+ case(name, key, value) {
1677
+ return [
1678
+ name,
1679
+ void 0,
1680
+ (m) => hasState(m, key, value)
1681
+ ];
1682
+ }
1683
+ };
1684
+ }
1685
+
2002
1686
  // src/index.ts
2003
1687
  function createMachine(context, fnsOrFactory) {
2004
1688
  if (typeof fnsOrFactory === "function") {
@@ -2077,6 +1761,9 @@ function setContext(machine, newContextOrFn) {
2077
1761
  const newContext = typeof newContextOrFn === "function" ? newContextOrFn(context) : newContextOrFn;
2078
1762
  return createMachine(newContext, transitions);
2079
1763
  }
1764
+ function createContext(context) {
1765
+ return { context };
1766
+ }
2080
1767
  function overrideTransitions(machine, overrides) {
2081
1768
  const { context, ...originalTransitions } = machine;
2082
1769
  const newTransitions = { ...originalTransitions, ...overrides };
@@ -2174,7 +1861,6 @@ function next(m, update) {
2174
1861
  return createMachine(update(context), transitions);
2175
1862
  }
2176
1863
  export {
2177
- ADVANCED_CONFIG_EXAMPLES,
2178
1864
  BoundMachine,
2179
1865
  CANCEL,
2180
1866
  META_KEY,
@@ -2186,11 +1872,13 @@ export {
2186
1872
  branch,
2187
1873
  call,
2188
1874
  chain,
1875
+ classCase,
2189
1876
  combine,
2190
1877
  combineFactories,
2191
1878
  compose,
2192
1879
  composeTyped,
2193
1880
  createAsyncMachine,
1881
+ createContext,
2194
1882
  createCustomMiddleware,
2195
1883
  createEnsemble,
2196
1884
  createEnsembleFactory,
@@ -2201,6 +1889,7 @@ export {
2201
1889
  createMachine,
2202
1890
  createMachineBuilder,
2203
1891
  createMachineFactory,
1892
+ createMatcher,
2204
1893
  createMiddleware,
2205
1894
  createMiddlewareFactory,
2206
1895
  createMiddlewareRegistry,
@@ -2212,12 +1901,12 @@ export {
2212
1901
  createTransition,
2213
1902
  createTransitionExtender,
2214
1903
  createTransitionFactory,
1904
+ customCase,
2215
1905
  delegateToChild,
2216
1906
  describe,
1907
+ discriminantCase,
2217
1908
  extendTransitions,
2218
- extractMachine,
2219
- extractMachines,
2220
- generateChart,
1909
+ forContext,
2221
1910
  guard,
2222
1911
  guardAsync,
2223
1912
  guarded,