@doeixd/machine 0.0.18 → 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.
- package/README.md +164 -3
- package/dist/cjs/development/core.js.map +2 -2
- package/dist/cjs/development/extract.js +500 -0
- package/dist/cjs/development/extract.js.map +7 -0
- package/dist/cjs/development/index.js +135 -477
- package/dist/cjs/development/index.js.map +4 -4
- package/dist/cjs/production/extract.js +5 -0
- package/dist/cjs/production/index.js +4 -5
- package/dist/esm/development/core.js.map +2 -2
- package/dist/esm/development/extract.js +486 -0
- package/dist/esm/development/extract.js.map +7 -0
- package/dist/esm/development/index.js +135 -484
- package/dist/esm/development/index.js.map +4 -4
- package/dist/esm/production/extract.js +5 -0
- package/dist/esm/production/index.js +4 -5
- package/dist/types/index.d.ts +45 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/matcher.d.ts +318 -0
- package/dist/types/matcher.d.ts.map +1 -0
- package/package.json +13 -1
- package/src/index.ts +73 -9
- package/src/matcher.ts +544 -0
|
@@ -20,24 +20,24 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
-
ADVANCED_CONFIG_EXAMPLES: () => ADVANCED_CONFIG_EXAMPLES,
|
|
24
23
|
BoundMachine: () => BoundMachine,
|
|
25
24
|
CANCEL: () => CANCEL,
|
|
26
25
|
META_KEY: () => META_KEY,
|
|
27
26
|
MachineBase: () => MachineBase,
|
|
28
27
|
MiddlewareBuilder: () => MiddlewareBuilder,
|
|
29
28
|
MultiMachineBase: () => MultiMachineBase,
|
|
30
|
-
_typeToJson: () => _typeToJson,
|
|
31
29
|
action: () => action,
|
|
32
30
|
bindTransitions: () => bindTransitions,
|
|
33
31
|
branch: () => branch,
|
|
34
32
|
call: () => call,
|
|
35
33
|
chain: () => chain,
|
|
34
|
+
classCase: () => classCase,
|
|
36
35
|
combine: () => combine,
|
|
37
36
|
combineFactories: () => combineFactories,
|
|
38
37
|
compose: () => compose,
|
|
39
38
|
composeTyped: () => composeTyped,
|
|
40
39
|
createAsyncMachine: () => createAsyncMachine,
|
|
40
|
+
createContext: () => createContext,
|
|
41
41
|
createCustomMiddleware: () => createCustomMiddleware,
|
|
42
42
|
createEnsemble: () => createEnsemble,
|
|
43
43
|
createEnsembleFactory: () => createEnsembleFactory,
|
|
@@ -48,6 +48,7 @@ __export(src_exports, {
|
|
|
48
48
|
createMachine: () => createMachine,
|
|
49
49
|
createMachineBuilder: () => createMachineBuilder,
|
|
50
50
|
createMachineFactory: () => createMachineFactory,
|
|
51
|
+
createMatcher: () => createMatcher,
|
|
51
52
|
createMiddleware: () => createMiddleware,
|
|
52
53
|
createMiddlewareFactory: () => createMiddlewareFactory,
|
|
53
54
|
createMiddlewareRegistry: () => createMiddlewareRegistry,
|
|
@@ -59,12 +60,12 @@ __export(src_exports, {
|
|
|
59
60
|
createTransition: () => createTransition,
|
|
60
61
|
createTransitionExtender: () => createTransitionExtender,
|
|
61
62
|
createTransitionFactory: () => createTransitionFactory,
|
|
63
|
+
customCase: () => customCase,
|
|
62
64
|
delegateToChild: () => delegateToChild,
|
|
63
65
|
describe: () => describe,
|
|
66
|
+
discriminantCase: () => discriminantCase,
|
|
64
67
|
extendTransitions: () => extendTransitions,
|
|
65
|
-
|
|
66
|
-
extractMachines: () => extractMachines,
|
|
67
|
-
generateChart: () => generateChart,
|
|
68
|
+
forContext: () => forContext,
|
|
68
69
|
guard: () => guard,
|
|
69
70
|
guardAsync: () => guardAsync,
|
|
70
71
|
guarded: () => guarded,
|
|
@@ -707,478 +708,6 @@ function createParallelMachine(m1, m2) {
|
|
|
707
708
|
};
|
|
708
709
|
}
|
|
709
710
|
|
|
710
|
-
// src/extract.ts
|
|
711
|
-
var import_ts_morph = require("ts-morph");
|
|
712
|
-
function _typeToJson(type, verbose = false) {
|
|
713
|
-
const symbol = type.getSymbol();
|
|
714
|
-
if (symbol && symbol.getDeclarations().some(import_ts_morph.Node.isClassDeclaration)) {
|
|
715
|
-
return symbol.getName();
|
|
716
|
-
}
|
|
717
|
-
if (type.isStringLiteral()) return type.getLiteralValue();
|
|
718
|
-
if (type.isNumberLiteral()) return type.getLiteralValue();
|
|
719
|
-
if (type.isBooleanLiteral()) return type.getLiteralValue();
|
|
720
|
-
if (type.isString()) return "string";
|
|
721
|
-
if (type.isNumber()) return "number";
|
|
722
|
-
if (type.isBoolean()) return "boolean";
|
|
723
|
-
if (type.isArray()) {
|
|
724
|
-
const elementType = type.getArrayElementTypeOrThrow();
|
|
725
|
-
return [_typeToJson(elementType, verbose)];
|
|
726
|
-
}
|
|
727
|
-
if (type.isObject() || type.isIntersection()) {
|
|
728
|
-
const obj = {};
|
|
729
|
-
const properties = type.getProperties();
|
|
730
|
-
for (const prop of properties) {
|
|
731
|
-
const propName = prop.getName();
|
|
732
|
-
if (propName.startsWith("__@")) continue;
|
|
733
|
-
const declaration = prop.getValueDeclaration();
|
|
734
|
-
if (!declaration) continue;
|
|
735
|
-
try {
|
|
736
|
-
obj[propName] = _typeToJson(declaration.getType(), verbose);
|
|
737
|
-
} catch (e) {
|
|
738
|
-
if (verbose) console.error(` Warning: Failed to serialize property ${propName}:`, e);
|
|
739
|
-
obj[propName] = "unknown";
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
return Object.keys(obj).length > 0 ? obj : null;
|
|
743
|
-
}
|
|
744
|
-
if (verbose) {
|
|
745
|
-
console.error(` Unhandled type: ${type.getText()}`);
|
|
746
|
-
}
|
|
747
|
-
return "unknown";
|
|
748
|
-
}
|
|
749
|
-
function resolveClassName(node) {
|
|
750
|
-
if (import_ts_morph.Node.isIdentifier(node)) {
|
|
751
|
-
return node.getText();
|
|
752
|
-
}
|
|
753
|
-
if (import_ts_morph.Node.isTypeOfExpression(node)) {
|
|
754
|
-
return node.getExpression().getText();
|
|
755
|
-
}
|
|
756
|
-
return "unknown";
|
|
757
|
-
}
|
|
758
|
-
function parseObjectLiteral(obj) {
|
|
759
|
-
if (!import_ts_morph.Node.isObjectLiteralExpression(obj)) {
|
|
760
|
-
return {};
|
|
761
|
-
}
|
|
762
|
-
const result = {};
|
|
763
|
-
for (const prop of obj.getProperties()) {
|
|
764
|
-
if (import_ts_morph.Node.isPropertyAssignment(prop)) {
|
|
765
|
-
const name = prop.getName();
|
|
766
|
-
const init = prop.getInitializer();
|
|
767
|
-
if (init) {
|
|
768
|
-
if (import_ts_morph.Node.isStringLiteral(init)) {
|
|
769
|
-
result[name] = init.getLiteralValue();
|
|
770
|
-
} else if (import_ts_morph.Node.isNumericLiteral(init)) {
|
|
771
|
-
result[name] = init.getLiteralValue();
|
|
772
|
-
} else if (init.getText() === "true" || init.getText() === "false") {
|
|
773
|
-
result[name] = init.getText() === "true";
|
|
774
|
-
} else if (import_ts_morph.Node.isIdentifier(init)) {
|
|
775
|
-
result[name] = init.getText();
|
|
776
|
-
} else if (import_ts_morph.Node.isObjectLiteralExpression(init)) {
|
|
777
|
-
result[name] = parseObjectLiteral(init);
|
|
778
|
-
} else if (import_ts_morph.Node.isArrayLiteralExpression(init)) {
|
|
779
|
-
result[name] = init.getElements().map((el) => {
|
|
780
|
-
if (import_ts_morph.Node.isObjectLiteralExpression(el)) {
|
|
781
|
-
return parseObjectLiteral(el);
|
|
782
|
-
}
|
|
783
|
-
return el.getText();
|
|
784
|
-
});
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
return result;
|
|
790
|
-
}
|
|
791
|
-
function parseInvokeService(obj) {
|
|
792
|
-
if (!import_ts_morph.Node.isObjectLiteralExpression(obj)) {
|
|
793
|
-
return {};
|
|
794
|
-
}
|
|
795
|
-
const service = {};
|
|
796
|
-
for (const prop of obj.getProperties()) {
|
|
797
|
-
if (import_ts_morph.Node.isPropertyAssignment(prop)) {
|
|
798
|
-
const name = prop.getName();
|
|
799
|
-
const init = prop.getInitializer();
|
|
800
|
-
if (!init) continue;
|
|
801
|
-
if (name === "onDone" || name === "onError") {
|
|
802
|
-
service[name] = resolveClassName(init);
|
|
803
|
-
} else if (import_ts_morph.Node.isStringLiteral(init)) {
|
|
804
|
-
service[name] = init.getLiteralValue();
|
|
805
|
-
} else if (import_ts_morph.Node.isIdentifier(init)) {
|
|
806
|
-
service[name] = init.getText();
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
return service;
|
|
811
|
-
}
|
|
812
|
-
function extractFromCallExpression(call2, verbose = false) {
|
|
813
|
-
if (!import_ts_morph.Node.isCallExpression(call2)) {
|
|
814
|
-
return null;
|
|
815
|
-
}
|
|
816
|
-
const expression = call2.getExpression();
|
|
817
|
-
const fnName = import_ts_morph.Node.isIdentifier(expression) ? expression.getText() : null;
|
|
818
|
-
if (!fnName) {
|
|
819
|
-
return null;
|
|
820
|
-
}
|
|
821
|
-
const metadata2 = {};
|
|
822
|
-
const args = call2.getArguments();
|
|
823
|
-
switch (fnName) {
|
|
824
|
-
case "transitionTo":
|
|
825
|
-
if (args[0]) {
|
|
826
|
-
metadata2.target = resolveClassName(args[0]);
|
|
827
|
-
}
|
|
828
|
-
break;
|
|
829
|
-
case "describe":
|
|
830
|
-
if (args[0] && import_ts_morph.Node.isStringLiteral(args[0])) {
|
|
831
|
-
metadata2.description = args[0].getLiteralValue();
|
|
832
|
-
}
|
|
833
|
-
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
834
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
835
|
-
if (nested) {
|
|
836
|
-
Object.assign(metadata2, nested);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
break;
|
|
840
|
-
case "guarded":
|
|
841
|
-
if (args[0]) {
|
|
842
|
-
const guard2 = parseObjectLiteral(args[0]);
|
|
843
|
-
if (Object.keys(guard2).length > 0) {
|
|
844
|
-
metadata2.guards = [guard2];
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
848
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
849
|
-
if (nested) {
|
|
850
|
-
Object.assign(metadata2, nested);
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
break;
|
|
854
|
-
case "invoke":
|
|
855
|
-
if (args[0]) {
|
|
856
|
-
const service = parseInvokeService(args[0]);
|
|
857
|
-
if (Object.keys(service).length > 0) {
|
|
858
|
-
metadata2.invoke = service;
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
break;
|
|
862
|
-
case "action":
|
|
863
|
-
if (args[0]) {
|
|
864
|
-
const actionMeta = parseObjectLiteral(args[0]);
|
|
865
|
-
if (Object.keys(actionMeta).length > 0) {
|
|
866
|
-
metadata2.actions = [actionMeta];
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
870
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
871
|
-
if (nested) {
|
|
872
|
-
Object.assign(metadata2, nested);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
break;
|
|
876
|
-
case "guard":
|
|
877
|
-
if (args[2]) {
|
|
878
|
-
const options = parseObjectLiteral(args[2]);
|
|
879
|
-
if (options.description) {
|
|
880
|
-
metadata2.description = options.description;
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
metadata2.guards = [{ name: "runtime_guard", description: metadata2.description || "Synchronous condition check" }];
|
|
884
|
-
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
885
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
886
|
-
if (nested) {
|
|
887
|
-
Object.assign(metadata2, nested);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
break;
|
|
891
|
-
case "guardAsync":
|
|
892
|
-
if (args[2]) {
|
|
893
|
-
const options = parseObjectLiteral(args[2]);
|
|
894
|
-
if (options.description) {
|
|
895
|
-
metadata2.description = options.description;
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
metadata2.guards = [{ name: "runtime_guard_async", description: metadata2.description || "Asynchronous condition check" }];
|
|
899
|
-
if (args[1] && import_ts_morph.Node.isCallExpression(args[1])) {
|
|
900
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
901
|
-
if (nested) {
|
|
902
|
-
Object.assign(metadata2, nested);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
break;
|
|
906
|
-
default:
|
|
907
|
-
return null;
|
|
908
|
-
}
|
|
909
|
-
return Object.keys(metadata2).length > 0 ? metadata2 : null;
|
|
910
|
-
}
|
|
911
|
-
function extractMetaFromMember(member, verbose = false) {
|
|
912
|
-
if (!import_ts_morph.Node.isPropertyDeclaration(member)) {
|
|
913
|
-
if (verbose) console.error(` ⚠️ Not a property declaration`);
|
|
914
|
-
return null;
|
|
915
|
-
}
|
|
916
|
-
const initializer = member.getInitializer();
|
|
917
|
-
if (!initializer) {
|
|
918
|
-
if (verbose) console.error(` ⚠️ No initializer`);
|
|
919
|
-
return null;
|
|
920
|
-
}
|
|
921
|
-
if (!import_ts_morph.Node.isCallExpression(initializer)) {
|
|
922
|
-
if (verbose) console.error(` ⚠️ Initializer is not a call expression`);
|
|
923
|
-
return null;
|
|
924
|
-
}
|
|
925
|
-
const metadata2 = extractFromCallExpression(initializer, verbose);
|
|
926
|
-
if (metadata2 && verbose) {
|
|
927
|
-
console.error(` ✅ Extracted metadata:`, JSON.stringify(metadata2, null, 2));
|
|
928
|
-
}
|
|
929
|
-
return metadata2;
|
|
930
|
-
}
|
|
931
|
-
function analyzeStateNode(classSymbol, verbose = false) {
|
|
932
|
-
const chartNode = { on: {} };
|
|
933
|
-
const classDeclaration = classSymbol.getDeclarations()[0];
|
|
934
|
-
if (!classDeclaration || !import_ts_morph.Node.isClassDeclaration(classDeclaration)) {
|
|
935
|
-
if (verbose) {
|
|
936
|
-
console.error(`⚠️ Warning: Could not get class declaration for ${classSymbol.getName()}`);
|
|
937
|
-
}
|
|
938
|
-
return chartNode;
|
|
939
|
-
}
|
|
940
|
-
const className = classSymbol.getName();
|
|
941
|
-
if (verbose) {
|
|
942
|
-
console.error(` Analyzing state: ${className}`);
|
|
943
|
-
}
|
|
944
|
-
for (const member of classDeclaration.getInstanceMembers()) {
|
|
945
|
-
const memberName = member.getName();
|
|
946
|
-
if (verbose) {
|
|
947
|
-
console.error(` Checking member: ${memberName}`);
|
|
948
|
-
}
|
|
949
|
-
const meta = extractMetaFromMember(member, verbose);
|
|
950
|
-
if (!meta) continue;
|
|
951
|
-
if (verbose) {
|
|
952
|
-
console.error(` Found transition: ${memberName}`);
|
|
953
|
-
}
|
|
954
|
-
const { invoke: invoke2, actions, guards, ...onEntry } = meta;
|
|
955
|
-
if (invoke2) {
|
|
956
|
-
if (!chartNode.invoke) chartNode.invoke = [];
|
|
957
|
-
chartNode.invoke.push({
|
|
958
|
-
src: invoke2.src,
|
|
959
|
-
onDone: { target: invoke2.onDone },
|
|
960
|
-
onError: { target: invoke2.onError },
|
|
961
|
-
description: invoke2.description
|
|
962
|
-
});
|
|
963
|
-
if (verbose) {
|
|
964
|
-
console.error(` → Invoke: ${invoke2.src}`);
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
if (onEntry.target) {
|
|
968
|
-
const transition = { target: onEntry.target };
|
|
969
|
-
if (onEntry.description) {
|
|
970
|
-
transition.description = onEntry.description;
|
|
971
|
-
}
|
|
972
|
-
if (guards) {
|
|
973
|
-
transition.cond = guards.map((g) => g.name).join(" && ");
|
|
974
|
-
if (verbose) {
|
|
975
|
-
console.error(` → Guard: ${transition.cond}`);
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
if (actions && actions.length > 0) {
|
|
979
|
-
transition.actions = actions.map((a) => a.name);
|
|
980
|
-
if (verbose) {
|
|
981
|
-
console.error(` → Actions: ${transition.actions.join(", ")}`);
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
chartNode.on[memberName] = transition;
|
|
985
|
-
if (verbose) {
|
|
986
|
-
console.error(` → Target: ${onEntry.target}`);
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
return chartNode;
|
|
991
|
-
}
|
|
992
|
-
function analyzeStateNodeWithNesting(className, classSymbol, sourceFile, childConfig, verbose = false) {
|
|
993
|
-
const stateNode = analyzeStateNode(classSymbol, verbose);
|
|
994
|
-
if (childConfig) {
|
|
995
|
-
if (verbose) {
|
|
996
|
-
console.error(` 👪 Analyzing children for state: ${className}`);
|
|
997
|
-
}
|
|
998
|
-
stateNode.initial = childConfig.initialState;
|
|
999
|
-
stateNode.states = {};
|
|
1000
|
-
for (const childClassName of childConfig.classes) {
|
|
1001
|
-
const childClassDeclaration = sourceFile.getClass(childClassName);
|
|
1002
|
-
if (childClassDeclaration) {
|
|
1003
|
-
const childSymbol = childClassDeclaration.getSymbolOrThrow();
|
|
1004
|
-
stateNode.states[childClassName] = analyzeStateNode(childSymbol, verbose);
|
|
1005
|
-
} else {
|
|
1006
|
-
console.warn(`⚠️ Warning: Child class '${childClassName}' not found.`);
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
return stateNode;
|
|
1011
|
-
}
|
|
1012
|
-
function extractMachine(config, project, verbose = false) {
|
|
1013
|
-
if (verbose) {
|
|
1014
|
-
console.error(`
|
|
1015
|
-
🔍 Analyzing machine: ${config.id}`);
|
|
1016
|
-
console.error(` Source: ${config.input}`);
|
|
1017
|
-
}
|
|
1018
|
-
const sourceFile = project.getSourceFile(config.input);
|
|
1019
|
-
if (!sourceFile) {
|
|
1020
|
-
throw new Error(`Source file not found: ${config.input}`);
|
|
1021
|
-
}
|
|
1022
|
-
if (config.parallel) {
|
|
1023
|
-
if (verbose) {
|
|
1024
|
-
console.error(` ⏹️ Parallel machine detected. Analyzing regions.`);
|
|
1025
|
-
}
|
|
1026
|
-
const parallelChart = {
|
|
1027
|
-
id: config.id,
|
|
1028
|
-
type: "parallel",
|
|
1029
|
-
states: {}
|
|
1030
|
-
};
|
|
1031
|
-
if (config.description) {
|
|
1032
|
-
parallelChart.description = config.description;
|
|
1033
|
-
}
|
|
1034
|
-
for (const region of config.parallel.regions) {
|
|
1035
|
-
if (verbose) {
|
|
1036
|
-
console.error(` 📍 Analyzing region: ${region.name}`);
|
|
1037
|
-
}
|
|
1038
|
-
const regionStates = {};
|
|
1039
|
-
for (const className of region.classes) {
|
|
1040
|
-
const classDeclaration = sourceFile.getClass(className);
|
|
1041
|
-
if (classDeclaration) {
|
|
1042
|
-
const classSymbol = classDeclaration.getSymbolOrThrow();
|
|
1043
|
-
regionStates[className] = analyzeStateNode(classSymbol, verbose);
|
|
1044
|
-
} else {
|
|
1045
|
-
console.warn(`⚠️ Warning: Class '${className}' not found for region '${region.name}'.`);
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
parallelChart.states[region.name] = {
|
|
1049
|
-
initial: region.initialState,
|
|
1050
|
-
states: regionStates
|
|
1051
|
-
};
|
|
1052
|
-
}
|
|
1053
|
-
if (verbose) {
|
|
1054
|
-
console.error(` ✅ Extracted ${config.parallel.regions.length} parallel regions`);
|
|
1055
|
-
}
|
|
1056
|
-
return parallelChart;
|
|
1057
|
-
}
|
|
1058
|
-
if (!config.initialState || !config.classes) {
|
|
1059
|
-
throw new Error(`Machine config for '${config.id}' must have either 'parallel' or 'initialState'/'classes'.`);
|
|
1060
|
-
}
|
|
1061
|
-
const fullChart = {
|
|
1062
|
-
id: config.id,
|
|
1063
|
-
initial: config.initialState,
|
|
1064
|
-
states: {}
|
|
1065
|
-
};
|
|
1066
|
-
if (config.description) {
|
|
1067
|
-
fullChart.description = config.description;
|
|
1068
|
-
}
|
|
1069
|
-
for (const className of config.classes) {
|
|
1070
|
-
const classDeclaration = sourceFile.getClass(className);
|
|
1071
|
-
if (!classDeclaration) {
|
|
1072
|
-
console.warn(`⚠️ Warning: Class '${className}' not found in '${config.input}'. Skipping.`);
|
|
1073
|
-
continue;
|
|
1074
|
-
}
|
|
1075
|
-
const classSymbol = classDeclaration.getSymbolOrThrow();
|
|
1076
|
-
const hasChildren = className === config.initialState && config.children;
|
|
1077
|
-
const stateNode = analyzeStateNodeWithNesting(
|
|
1078
|
-
className,
|
|
1079
|
-
classSymbol,
|
|
1080
|
-
sourceFile,
|
|
1081
|
-
hasChildren ? config.children : void 0,
|
|
1082
|
-
verbose
|
|
1083
|
-
);
|
|
1084
|
-
fullChart.states[className] = stateNode;
|
|
1085
|
-
}
|
|
1086
|
-
if (verbose) {
|
|
1087
|
-
console.error(` ✅ Extracted ${config.classes.length} states`);
|
|
1088
|
-
}
|
|
1089
|
-
return fullChart;
|
|
1090
|
-
}
|
|
1091
|
-
function extractMachines(config) {
|
|
1092
|
-
var _a;
|
|
1093
|
-
const verbose = (_a = config.verbose) != null ? _a : false;
|
|
1094
|
-
if (verbose) {
|
|
1095
|
-
console.error(`
|
|
1096
|
-
📊 Starting statechart extraction`);
|
|
1097
|
-
console.error(` Machines to extract: ${config.machines.length}`);
|
|
1098
|
-
}
|
|
1099
|
-
const project = new import_ts_morph.Project();
|
|
1100
|
-
project.addSourceFilesAtPaths("src/**/*.ts");
|
|
1101
|
-
project.addSourceFilesAtPaths("examples/**/*.ts");
|
|
1102
|
-
const results = [];
|
|
1103
|
-
for (const machineConfig of config.machines) {
|
|
1104
|
-
try {
|
|
1105
|
-
const chart = extractMachine(machineConfig, project, verbose);
|
|
1106
|
-
results.push(chart);
|
|
1107
|
-
} catch (error) {
|
|
1108
|
-
console.error(`❌ Error extracting machine '${machineConfig.id}':`, error);
|
|
1109
|
-
if (!verbose) {
|
|
1110
|
-
console.error(` Run with --verbose for more details`);
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
if (verbose) {
|
|
1115
|
-
console.error(`
|
|
1116
|
-
✅ Extraction complete: ${results.length}/${config.machines.length} machines extracted`);
|
|
1117
|
-
}
|
|
1118
|
-
return results;
|
|
1119
|
-
}
|
|
1120
|
-
function generateChart() {
|
|
1121
|
-
const config = {
|
|
1122
|
-
input: "examples/authMachine.ts",
|
|
1123
|
-
classes: [
|
|
1124
|
-
"LoggedOutMachine",
|
|
1125
|
-
"LoggingInMachine",
|
|
1126
|
-
"LoggedInMachine",
|
|
1127
|
-
"SessionExpiredMachine",
|
|
1128
|
-
"ErrorMachine"
|
|
1129
|
-
],
|
|
1130
|
-
id: "auth",
|
|
1131
|
-
initialState: "LoggedOutMachine",
|
|
1132
|
-
description: "Authentication state machine"
|
|
1133
|
-
};
|
|
1134
|
-
console.error("🔍 Using legacy generateChart function");
|
|
1135
|
-
console.error("⚠️ Consider using extractMachines() with a config file instead\n");
|
|
1136
|
-
const project = new import_ts_morph.Project();
|
|
1137
|
-
project.addSourceFilesAtPaths("src/**/*.ts");
|
|
1138
|
-
project.addSourceFilesAtPaths("examples/**/*.ts");
|
|
1139
|
-
try {
|
|
1140
|
-
const chart = extractMachine(config, project, true);
|
|
1141
|
-
console.log(JSON.stringify(chart, null, 2));
|
|
1142
|
-
} catch (error) {
|
|
1143
|
-
console.error(`❌ Error:`, error);
|
|
1144
|
-
process.exit(1);
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
var ADVANCED_CONFIG_EXAMPLES = {
|
|
1148
|
-
hierarchical: {
|
|
1149
|
-
input: "examples/dashboardMachine.ts",
|
|
1150
|
-
id: "dashboard",
|
|
1151
|
-
classes: ["DashboardMachine", "LoggedOutMachine"],
|
|
1152
|
-
initialState: "DashboardMachine",
|
|
1153
|
-
children: {
|
|
1154
|
-
contextProperty: "child",
|
|
1155
|
-
initialState: "ViewingChildMachine",
|
|
1156
|
-
classes: ["ViewingChildMachine", "EditingChildMachine"]
|
|
1157
|
-
}
|
|
1158
|
-
},
|
|
1159
|
-
parallel: {
|
|
1160
|
-
input: "examples/editorMachine.ts",
|
|
1161
|
-
id: "editor",
|
|
1162
|
-
parallel: {
|
|
1163
|
-
regions: [
|
|
1164
|
-
{
|
|
1165
|
-
name: "fontWeight",
|
|
1166
|
-
initialState: "NormalWeight",
|
|
1167
|
-
classes: ["NormalWeight", "BoldWeight"]
|
|
1168
|
-
},
|
|
1169
|
-
{
|
|
1170
|
-
name: "textDecoration",
|
|
1171
|
-
initialState: "NoDecoration",
|
|
1172
|
-
classes: ["NoDecoration", "UnderlineState"]
|
|
1173
|
-
}
|
|
1174
|
-
]
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
};
|
|
1178
|
-
if (require.main === module) {
|
|
1179
|
-
generateChart();
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
711
|
// src/middleware/core.ts
|
|
1183
712
|
var CANCEL = Symbol("CANCEL");
|
|
1184
713
|
function createMiddleware(machine, hooks, options = {}) {
|
|
@@ -2152,6 +1681,132 @@ function state(context, transitions) {
|
|
|
2152
1681
|
return createFunctionalMachine(context);
|
|
2153
1682
|
}
|
|
2154
1683
|
|
|
1684
|
+
// src/matcher.ts
|
|
1685
|
+
function createMatcher(...cases) {
|
|
1686
|
+
const nameToCase = /* @__PURE__ */ new Map();
|
|
1687
|
+
for (const [name, _, predicate] of cases) {
|
|
1688
|
+
if (nameToCase.has(name)) {
|
|
1689
|
+
throw new Error(`Duplicate matcher case name: "${name}"`);
|
|
1690
|
+
}
|
|
1691
|
+
nameToCase.set(name, { predicate });
|
|
1692
|
+
}
|
|
1693
|
+
const isProxy = new Proxy({}, {
|
|
1694
|
+
get(_target, prop) {
|
|
1695
|
+
return function isGuard(machine) {
|
|
1696
|
+
const caseConfig = nameToCase.get(prop);
|
|
1697
|
+
if (!caseConfig) {
|
|
1698
|
+
const available = Array.from(nameToCase.keys()).join(", ");
|
|
1699
|
+
throw new Error(
|
|
1700
|
+
`Unknown matcher case: "${prop}". Available cases: ${available}`
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
return caseConfig.predicate(machine);
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
});
|
|
1707
|
+
const caseProxy = new Proxy({}, {
|
|
1708
|
+
get(_target, prop) {
|
|
1709
|
+
return function createCaseHandler(handler) {
|
|
1710
|
+
if (!nameToCase.has(prop)) {
|
|
1711
|
+
const available = Array.from(nameToCase.keys()).join(", ");
|
|
1712
|
+
throw new Error(
|
|
1713
|
+
`Unknown matcher case: "${prop}". Available cases: ${available}`
|
|
1714
|
+
);
|
|
1715
|
+
}
|
|
1716
|
+
return {
|
|
1717
|
+
__brand: "CaseHandler",
|
|
1718
|
+
__name: prop,
|
|
1719
|
+
__machine: void 0,
|
|
1720
|
+
__return: void 0,
|
|
1721
|
+
handler
|
|
1722
|
+
};
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
});
|
|
1726
|
+
const exhaustive = { __exhaustive: true };
|
|
1727
|
+
function when2(machine) {
|
|
1728
|
+
return {
|
|
1729
|
+
is(...handlers) {
|
|
1730
|
+
if (handlers.length === 0) {
|
|
1731
|
+
throw new Error("Pattern match requires at least one handler and exhaustiveness marker");
|
|
1732
|
+
}
|
|
1733
|
+
const lastHandler = handlers[handlers.length - 1];
|
|
1734
|
+
if (!lastHandler || typeof lastHandler !== "object" || !("__exhaustive" in lastHandler)) {
|
|
1735
|
+
throw new Error(
|
|
1736
|
+
"Pattern match must end with match.exhaustive for compile-time exhaustiveness checking"
|
|
1737
|
+
);
|
|
1738
|
+
}
|
|
1739
|
+
const actualHandlers = handlers.slice(0, -1);
|
|
1740
|
+
for (const caseHandler of actualHandlers) {
|
|
1741
|
+
const caseName = caseHandler.__name;
|
|
1742
|
+
const caseConfig = nameToCase.get(caseName);
|
|
1743
|
+
if (!caseConfig) {
|
|
1744
|
+
throw new Error(`Internal error: Unknown matcher case in handler: ${caseName}`);
|
|
1745
|
+
}
|
|
1746
|
+
if (caseConfig.predicate(machine)) {
|
|
1747
|
+
return caseHandler.handler(machine);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
const handledCases = actualHandlers.map((h) => h.__name).join(", ");
|
|
1751
|
+
const allCases = Array.from(nameToCase.keys()).join(", ");
|
|
1752
|
+
throw new Error(
|
|
1753
|
+
`Non-exhaustive pattern match at runtime: no handler matched the machine.
|
|
1754
|
+
Handled cases: [${handledCases}]
|
|
1755
|
+
All cases: [${allCases}]
|
|
1756
|
+
This may occur if predicates don't cover all runtime possibilities.`
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
function simpleMatcher(machine) {
|
|
1762
|
+
for (const [name, _, predicate] of cases) {
|
|
1763
|
+
if (predicate(machine)) {
|
|
1764
|
+
return name;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
return null;
|
|
1768
|
+
}
|
|
1769
|
+
return Object.assign(simpleMatcher, {
|
|
1770
|
+
is: isProxy,
|
|
1771
|
+
when: when2,
|
|
1772
|
+
case: caseProxy,
|
|
1773
|
+
exhaustive
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
function classCase(name, machineClass) {
|
|
1777
|
+
return [
|
|
1778
|
+
name,
|
|
1779
|
+
void 0,
|
|
1780
|
+
// Type-only, not used at runtime
|
|
1781
|
+
(m) => m instanceof machineClass
|
|
1782
|
+
];
|
|
1783
|
+
}
|
|
1784
|
+
function discriminantCase(name, key, value) {
|
|
1785
|
+
return [
|
|
1786
|
+
name,
|
|
1787
|
+
void 0,
|
|
1788
|
+
// Type-only, not used at runtime
|
|
1789
|
+
(m) => hasState(m, key, value)
|
|
1790
|
+
];
|
|
1791
|
+
}
|
|
1792
|
+
function customCase(name, predicate) {
|
|
1793
|
+
return [name, void 0, predicate];
|
|
1794
|
+
}
|
|
1795
|
+
function forContext() {
|
|
1796
|
+
return {
|
|
1797
|
+
/**
|
|
1798
|
+
* Creates a discriminated union case with full type inference.
|
|
1799
|
+
*/
|
|
1800
|
+
case(name, key, value) {
|
|
1801
|
+
return [
|
|
1802
|
+
name,
|
|
1803
|
+
void 0,
|
|
1804
|
+
(m) => hasState(m, key, value)
|
|
1805
|
+
];
|
|
1806
|
+
}
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
|
|
2155
1810
|
// src/index.ts
|
|
2156
1811
|
function createMachine(context, fnsOrFactory) {
|
|
2157
1812
|
if (typeof fnsOrFactory === "function") {
|
|
@@ -2230,6 +1885,9 @@ function setContext(machine, newContextOrFn) {
|
|
|
2230
1885
|
const newContext = typeof newContextOrFn === "function" ? newContextOrFn(context) : newContextOrFn;
|
|
2231
1886
|
return createMachine(newContext, transitions);
|
|
2232
1887
|
}
|
|
1888
|
+
function createContext(context) {
|
|
1889
|
+
return { context };
|
|
1890
|
+
}
|
|
2233
1891
|
function overrideTransitions(machine, overrides) {
|
|
2234
1892
|
const { context, ...originalTransitions } = machine;
|
|
2235
1893
|
const newTransitions = { ...originalTransitions, ...overrides };
|