jssm 5.43.2 → 5.45.2

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/jssm.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  declare type StateType = string;
2
2
  import { JssmGenericState, JssmGenericConfig, JssmTransition, JssmTransitionList, // JssmTransitionRule,
3
- JssmMachineInternalState, JssmParseTree, JssmStateDeclaration, JssmArrow, JssmArrowDirection, JssmArrowKind, JssmLayout, FslDirection, FslTheme } from './jssm_types';
3
+ JssmMachineInternalState, JssmParseTree, JssmStateDeclaration, JssmArrow, JssmArrowDirection, JssmArrowKind, JssmLayout, FslDirection, FslTheme, HookDescription } from './jssm_types';
4
4
  import { seq, weighted_rand_select, weighted_sample_select, histograph, weighted_histo_key } from './jssm_util';
5
5
  import { version } from './version';
6
6
  declare function arrow_direction(arrow: JssmArrow): JssmArrowDirection;
@@ -37,6 +37,9 @@ declare class Machine<mDT> {
37
37
  _arrange_end_declaration: Array<Array<StateType>>;
38
38
  _theme: FslTheme;
39
39
  _flow: FslDirection;
40
+ _has_hooks: boolean;
41
+ _hooks: Map<string, Function>;
42
+ _named_hooks: Map<string, Function>;
40
43
  constructor({ start_states, complete, transitions, machine_author, machine_comment, machine_contributor, machine_definition, machine_language, machine_license, machine_name, machine_version, state_declaration, fsl_version, dot_preamble, arrange_declaration, arrange_start_declaration, arrange_end_declaration, theme, flow, graph_layout }: JssmGenericConfig<mDT>);
41
44
  _new_state(state_config: JssmGenericState): StateType;
42
45
  state(): StateType;
@@ -86,6 +89,7 @@ declare class Machine<mDT> {
86
89
  is_complete(): boolean;
87
90
  state_is_complete(whichState: StateType): boolean;
88
91
  has_completes(): boolean;
92
+ set_hook(HookDesc: HookDescription): void;
89
93
  action(name: StateType, newData?: mDT): boolean;
90
94
  transition(newState: StateType, newData?: mDT): boolean;
91
95
  force_transition(newState: StateType, newData?: mDT): boolean;
package/jssm_types.d.ts CHANGED
@@ -148,4 +148,28 @@ declare type JssmCompileSeStart<DataType> = {
148
148
  };
149
149
  declare type JssmParseTree = Array<JssmCompileSeStart<StateType>>;
150
150
  declare type JssmParseFunctionType = (string: any) => JssmParseTree;
151
- export { JssmColor, JssmTransition, JssmTransitions, JssmTransitionList, JssmTransitionRule, JssmArrow, JssmArrowKind, JssmArrowDirection, JssmGenericConfig, JssmGenericState, JssmGenericMachine, JssmParseTree, JssmCompileSe, JssmCompileSeStart, JssmCompileRule, JssmPermitted, JssmPermittedOpt, JssmResult, JssmStateDeclaration, JssmStateDeclarationRule, JssmLayout, JssmParseFunctionType, JssmMachineInternalState, FslDirection, FslTheme };
151
+ declare type BasicHookDescription = {
152
+ kind: 'hook';
153
+ from: string;
154
+ to: string;
155
+ handler: Function;
156
+ };
157
+ declare type HookDescriptionWithAction = {
158
+ kind: 'named';
159
+ from: string;
160
+ to: string;
161
+ action: string;
162
+ handler: Function;
163
+ };
164
+ declare type EntryHook = {
165
+ kind: 'entry';
166
+ to: string;
167
+ handler: Function;
168
+ };
169
+ declare type ExitHook = {
170
+ kind: 'exit';
171
+ from: string;
172
+ handler: Function;
173
+ };
174
+ declare type HookDescription = BasicHookDescription | HookDescriptionWithAction | EntryHook | ExitHook;
175
+ export { JssmColor, JssmTransition, JssmTransitions, JssmTransitionList, JssmTransitionRule, JssmArrow, JssmArrowKind, JssmArrowDirection, JssmGenericConfig, JssmGenericState, JssmGenericMachine, JssmParseTree, JssmCompileSe, JssmCompileSeStart, JssmCompileRule, JssmPermitted, JssmPermittedOpt, JssmResult, JssmStateDeclaration, JssmStateDeclarationRule, JssmLayout, JssmParseFunctionType, JssmMachineInternalState, FslDirection, FslTheme, HookDescription };
package/jssm_util.d.ts CHANGED
@@ -5,4 +5,6 @@ declare const seq: Function;
5
5
  declare const histograph: Function;
6
6
  declare const weighted_sample_select: Function;
7
7
  declare const weighted_histo_key: Function;
8
- export { seq, arr_uniq_p, histograph, weighted_histo_key, weighted_rand_select, weighted_sample_select, array_box_if_string };
8
+ declare const hook_name: (from: string, to: string) => string;
9
+ declare const named_hook_name: (from: string, to: string, action: string) => string;
10
+ export { seq, arr_uniq_p, histograph, weighted_histo_key, weighted_rand_select, weighted_sample_select, array_box_if_string, hook_name, named_hook_name };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jssm",
3
- "version": "5.43.2",
3
+ "version": "5.45.2",
4
4
  "engines": {
5
5
  "node": ">=10.0.0"
6
6
  },
@@ -16,7 +16,7 @@
16
16
  }
17
17
  ]
18
18
  },
19
- "description": "A Javascript finite state machine (FSM) with a terse DSL and a simple API. Well tested, and typed with Flowtype. MIT License.",
19
+ "description": "A Javascript finite state machine (FSM) with a terse DSL and a simple API. Well tested, and typed with TypeScript. MIT License.",
20
20
  "main": "dist/jssm.es5.cjs.js",
21
21
  "module": "dist/es6/jssm.js",
22
22
  "types": "./jssm.d.ts",
@@ -39,8 +39,9 @@
39
39
  "eslint": "eslint --color src/ts/jssm.ts src/ts/jssm_types.ts src/ts/tests/*.ts",
40
40
  "audit": "text_audit -r -t major MAJOR wasteful WASTEFUL any mixed fixme FIXME checkme CHECKME testme TESTME stochable STOCHABLE todo TODO comeback COMEBACK whargarbl WHARGARBL -g ./src/ts/**/*.{js,ts}",
41
41
  "vet": "npm run eslint && npm run audit",
42
- "build": "npm run vet && npm run test && npm run site && npm run docs",
43
- "ci_build": "npm run vet && npm run test",
42
+ "benny": "node ./src/buildjs/benchmark.js",
43
+ "build": "npm run vet && npm run test && npm run site && npm run docs && npm run benny",
44
+ "ci_build": "npm run vet && npm run test && npm run benny",
44
45
  "minify": "mv dist/es6/jssm-dot.js dist/es6/jssm-dot.nonmin.js && terser dist/es6/jssm-dot.nonmin.js > dist/es6/jssm-dot.js",
45
46
  "min_iife": "mv dist/jssm.es5.iife.js dist/jssm.es5.iife.nonmin.js && terser dist/jssm.es5.iife.nonmin.js > dist/jssm.es5.iife.js",
46
47
  "min_cjs": "mv dist/jssm.es5.cjs.js dist/jssm.es5.cjs.nonmin.js && terser dist/jssm.es5.cjs.nonmin.js > dist/jssm.es5.cjs.js",
@@ -69,7 +70,6 @@
69
70
  "tested",
70
71
  "typed",
71
72
  "typed-js",
72
- "flowtype",
73
73
  "mealy",
74
74
  "moore",
75
75
  "mealy machine",
@@ -80,6 +80,7 @@
80
80
  "viz.js",
81
81
  "flowchart",
82
82
  "visualization",
83
+ "TypeScript",
83
84
  "StoneCypher"
84
85
  ],
85
86
  "author": "John Haugeland <stonecypher@gmail.com>",
@@ -92,6 +93,7 @@
92
93
  "@types/jest": "^27.0.2",
93
94
  "@typescript-eslint/eslint-plugin": "^4.13.0",
94
95
  "@typescript-eslint/parser": "^4.13.0",
96
+ "benny": "^3.7.1",
95
97
  "changelog-maker": "^2.3.2",
96
98
  "coveralls": "^3.0.11",
97
99
  "eslint": "^7.32.0",
@@ -114,8 +116,7 @@
114
116
  "text_audit": "^0.9.3",
115
117
  "ts-jest": "^27.0.7",
116
118
  "typedoc": "^0.22.15",
117
- "typescript": "^4.1.3",
118
- "viz.js": "^1.7.1"
119
+ "typescript": "^4.1.3"
119
120
  },
120
121
  "dependencies": {
121
122
  "reduce-to-639-1": "^1.0.4"
package/src/ts/jssm.ts CHANGED
@@ -19,7 +19,8 @@ import {
19
19
  JssmCompileSe, JssmCompileSeStart, JssmCompileRule,
20
20
  JssmArrow, JssmArrowDirection, JssmArrowKind,
21
21
  JssmLayout,
22
- FslDirection, FslTheme
22
+ FslDirection, FslTheme,
23
+ HookDescription
23
24
 
24
25
  } from './jssm_types';
25
26
 
@@ -29,7 +30,7 @@ import {
29
30
 
30
31
  import {
31
32
  seq, weighted_rand_select, weighted_sample_select, histograph,
32
- weighted_histo_key, array_box_if_string
33
+ weighted_histo_key, array_box_if_string, hook_name, named_hook_name
33
34
  } from './jssm_util';
34
35
 
35
36
 
@@ -38,7 +39,7 @@ import {
38
39
 
39
40
  import { parse } from './jssm-dot'; // TODO FIXME WHARGARBL this could be post-typed
40
41
 
41
- import { version } from './version'; // replaced from package.js in build // TODO FIXME currently broken
42
+ import { version } from './version'; // replaced from package.js in build
42
43
 
43
44
 
44
45
 
@@ -229,7 +230,7 @@ function compile_rule_transition_step<mDT>(
229
230
  to : string,
230
231
  this_se : JssmCompileSe,
231
232
  next_se : JssmCompileSe
232
- ) : Array< JssmTransition<mDT> > { // todo flow describe the parser representation of a transition step extension
233
+ ) : Array< JssmTransition<mDT> > { // todo typescript describe the parser representation of a transition step extension
233
234
 
234
235
  const edges : Array< JssmTransition<mDT> > = [];
235
236
 
@@ -260,13 +261,13 @@ function compile_rule_transition_step<mDT>(
260
261
 
261
262
 
262
263
 
263
- function compile_rule_handle_transition(rule: JssmCompileSeStart<StateType>): any { // TODO FIXME no any // todo flow describe the parser representation of a transition
264
+ function compile_rule_handle_transition(rule: JssmCompileSeStart<StateType>): any { // TODO FIXME no any // todo typescript describe the parser representation of a transition
264
265
  return compile_rule_transition_step([], rule.from, rule.se.to, rule.se, rule.se.se);
265
266
  }
266
267
 
267
268
 
268
269
 
269
- function compile_rule_handler(rule: JssmCompileSeStart<StateType>): JssmCompileRule { // todo flow describe the output of the parser
270
+ function compile_rule_handler(rule: JssmCompileSeStart<StateType>): JssmCompileRule {
270
271
 
271
272
  if (rule.key === 'transition') {
272
273
  return { agg_as: 'transition', val: compile_rule_handle_transition(rule) };
@@ -305,7 +306,7 @@ function compile_rule_handler(rule: JssmCompileSeStart<StateType>): JssmCompileR
305
306
 
306
307
 
307
308
 
308
- function compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT> { // todo flow describe the output of the parser
309
+ function compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT> {
309
310
 
310
311
  const results : {
311
312
  graph_layout : Array< JssmLayout >,
@@ -475,11 +476,15 @@ class Machine<mDT> {
475
476
  _theme : FslTheme;
476
477
  _flow : FslDirection;
477
478
 
479
+ _has_hooks : boolean;
480
+ _hooks : Map<string, Function>;
481
+ _named_hooks : Map<string, Function>;
482
+
478
483
 
479
484
  // whargarbl this badly needs to be broken up, monolith master
480
485
  constructor({
481
486
  start_states,
482
- complete = [],
487
+ complete = [],
483
488
  transitions,
484
489
  machine_author,
485
490
  machine_comment,
@@ -530,6 +535,10 @@ class Machine<mDT> {
530
535
  this._flow = flow;
531
536
  this._graph_layout = graph_layout;
532
537
 
538
+ this._has_hooks = false;
539
+ this._hooks = new Map();
540
+ this._named_hooks = new Map();
541
+
533
542
 
534
543
  if (state_declaration) {
535
544
  state_declaration.map( (state_decl: JssmStateDeclaration) => {
@@ -601,7 +610,7 @@ class Machine<mDT> {
601
610
 
602
611
 
603
612
  // forward mapping first by action name
604
- let actionMap: Map<StateType, number> = this._actions.get(tr.action); // TODO FIXME ?Map equiv
613
+ let actionMap: Map<StateType, number> = this._actions.get(tr.action);
605
614
  if (!(actionMap)) {
606
615
  actionMap = new Map();
607
616
  this._actions.set(tr.action, actionMap);
@@ -615,7 +624,7 @@ class Machine<mDT> {
615
624
 
616
625
 
617
626
  // reverse mapping first by state origin name
618
- let rActionMap: Map<StateType, number> = this._reverse_actions.get(tr.from); // TODO FIXME ?Map equiv
627
+ let rActionMap: Map<StateType, number> = this._reverse_actions.get(tr.from);
619
628
  if (!(rActionMap)) {
620
629
  rActionMap = new Map();
621
630
  this._reverse_actions.set(tr.from, rActionMap);
@@ -975,49 +984,165 @@ class Machine<mDT> {
975
984
 
976
985
 
977
986
 
987
+ // basic toolable hook call. convenience wrappers will follow, like
988
+ // hook(from, to, handler) and exit_hook(from, handler) and etc
989
+ set_hook(HookDesc: HookDescription) {
990
+
991
+ switch (HookDesc.kind) {
992
+
993
+ case 'hook':
994
+ this._hooks.set(hook_name(HookDesc.from, HookDesc.to), HookDesc.handler);
995
+ this._has_hooks = true;
996
+ break;
997
+
998
+ case 'named':
999
+ this._named_hooks.set(named_hook_name(HookDesc.from, HookDesc.to, HookDesc.action), HookDesc.handler);
1000
+ this._has_hooks = true;
1001
+ break;
1002
+
1003
+ // case 'entry':
1004
+ // console.log('TODO: Should add entry hook here');
1005
+ // throw 'TODO: Should add entry hook here';
1006
+
1007
+ // case 'exit':
1008
+ // console.log('TODO: Should add exit hook here');
1009
+ // throw 'TODO: Should add exit hook here';
1010
+
1011
+ default:
1012
+ console.log(`Unknown hook type ${(HookDesc as any).kind}, should be impossible`);
1013
+ throw new RangeError(`Unknown hook type ${(HookDesc as any).kind}, should be impossible`);
1014
+
1015
+ }
1016
+ }
1017
+
1018
+ // remove_hook(HookDesc: HookDescription) {
1019
+ // throw 'TODO: Should remove hook here';
1020
+ // }
1021
+
1022
+
1023
+
978
1024
  action(name: StateType, newData?: mDT): boolean {
979
1025
  // todo whargarbl implement hooks
980
1026
  // todo whargarbl implement data stuff
981
1027
  // todo major incomplete whargarbl comeback
982
1028
  if (this.valid_action(name, newData)) {
983
- const edge: JssmTransition<mDT> = this.current_action_edge_for(name);
984
- this._state = edge.to;
985
- return true;
1029
+
1030
+ const edge : JssmTransition<mDT> = this.current_action_edge_for(name);
1031
+
1032
+ if (this._has_hooks) {
1033
+
1034
+ let hook_permits : boolean | undefined = undefined;
1035
+
1036
+ const nhn : string = named_hook_name(this._state, edge.to, name),
1037
+ maybe_hook = this._named_hooks.get(nhn);
1038
+
1039
+ if (maybe_hook === undefined) { hook_permits = true; }
1040
+ else { hook_permits = maybe_hook( { from: this._state, to: edge.to, action: name } ); }
1041
+
1042
+ if (hook_permits !== false) {
1043
+ this._state = edge.to;
1044
+ return true;
1045
+ } else {
1046
+ return false;
1047
+ }
1048
+
1049
+ } else {
1050
+ this._state = edge.to;
1051
+ return true;
1052
+ }
1053
+
986
1054
  } else {
987
1055
  return false;
988
1056
  }
989
1057
  }
990
1058
 
1059
+
1060
+
991
1061
  transition(newState: StateType, newData?: mDT): boolean {
1062
+
992
1063
  // todo whargarbl implement hooks
993
1064
  // todo whargarbl implement data stuff
994
1065
  // todo major incomplete whargarbl comeback
995
1066
  if (this.valid_transition(newState, newData)) {
996
- this._state = newState;
997
- return true;
1067
+
1068
+ if (this._has_hooks) {
1069
+
1070
+ let hook_permits : boolean | undefined = undefined;
1071
+
1072
+ const hn : string = hook_name(this._state, newState),
1073
+ maybe_hook : Function | undefined = this._hooks.get(hn);
1074
+
1075
+ if (maybe_hook === undefined) { hook_permits = true; }
1076
+ else { hook_permits = maybe_hook( { from: this._state, to: newState } ); }
1077
+
1078
+ if (hook_permits !== false) {
1079
+ this._state = newState;
1080
+ return true;
1081
+ } else {
1082
+ return false;
1083
+ }
1084
+
1085
+ } else {
1086
+
1087
+ this._state = newState;
1088
+ return true;
1089
+
1090
+ }
1091
+
998
1092
  } else {
999
1093
  return false;
1000
1094
  }
1095
+
1001
1096
  }
1002
1097
 
1098
+
1099
+
1003
1100
  // can leave machine in inconsistent state. generally do not use
1101
+
1004
1102
  force_transition(newState: StateType, newData?: mDT): boolean {
1103
+
1005
1104
  // todo whargarbl implement hooks
1006
1105
  // todo whargarbl implement data stuff
1007
1106
  // todo major incomplete whargarbl comeback
1008
1107
  if (this.valid_force_transition(newState, newData)) {
1009
- this._state = newState;
1010
- return true;
1108
+
1109
+ if (this._has_hooks) {
1110
+
1111
+ let hook_permits : boolean | undefined = undefined;
1112
+
1113
+ const hn : string = hook_name(this._state, newState),
1114
+ maybe_hook : Function | undefined = this._hooks.get(hn);
1115
+
1116
+ if (maybe_hook === undefined) { hook_permits = true; }
1117
+ else { hook_permits = maybe_hook({ from: this._state, to: newState, forced: true }); }
1118
+
1119
+ if (hook_permits !== false) {
1120
+ this._state = newState;
1121
+ return true;
1122
+ } else {
1123
+ return false;
1124
+ }
1125
+
1126
+ } else {
1127
+
1128
+ this._state = newState;
1129
+ return true;
1130
+
1131
+ }
1132
+
1011
1133
  } else {
1012
1134
  return false;
1013
1135
  }
1136
+
1014
1137
  }
1015
1138
 
1016
1139
 
1017
1140
 
1018
1141
  current_action_for(action: StateType): number {
1019
1142
  const action_base: Map<StateType, number> = this._actions.get(action);
1020
- return action_base? action_base.get(this.state()): undefined;
1143
+ return action_base
1144
+ ? action_base.get(this.state())
1145
+ : undefined;
1021
1146
  }
1022
1147
 
1023
1148
  current_action_edge_for(action: StateType): JssmTransition<mDT> {
@@ -1027,14 +1152,12 @@ class Machine<mDT> {
1027
1152
  }
1028
1153
 
1029
1154
  valid_action(action: StateType, _newData?: mDT): boolean { // todo comeback unignore newData
1030
- // todo whargarbl implement hooks
1031
1155
  // todo whargarbl implement data stuff
1032
1156
  // todo major incomplete whargarbl comeback
1033
1157
  return this.current_action_for(action) !== undefined;
1034
1158
  }
1035
1159
 
1036
1160
  valid_transition(newState: StateType, _newData?: mDT): boolean { // todo comeback unignore newData
1037
- // todo whargarbl implement hooks
1038
1161
  // todo whargarbl implement data stuff
1039
1162
  // todo major incomplete whargarbl comeback
1040
1163
  const transition_for: JssmTransition<mDT> = this.lookup_transition_for(this.state(), newState);
@@ -1047,7 +1170,6 @@ class Machine<mDT> {
1047
1170
  }
1048
1171
 
1049
1172
  valid_force_transition(newState: StateType, _newData?: mDT): boolean { // todo comeback unignore newData
1050
- // todo whargarbl implement hooks
1051
1173
  // todo whargarbl implement data stuff
1052
1174
  // todo major incomplete whargarbl comeback
1053
1175
  return (this.lookup_transition_for(this.state(), newState) !== undefined);
@@ -1081,7 +1203,8 @@ function sm<mDT>(template_strings: TemplateStringsArray, ... remainder /* , argu
1081
1203
  // string notation, as designed, it's not really worth the hassle
1082
1204
 
1083
1205
  /* eslint-disable prefer-rest-params */
1084
- (acc, val, idx): string => `${acc}${remainder[idx-1]}${val}` // arguments[0] is never loaded, so args doesn't need to be gated
1206
+ (acc, val, idx): string =>
1207
+ `${acc}${remainder[idx-1]}${val}` // arguments[0] is never loaded, so args doesn't need to be gated
1085
1208
  /* eslint-enable prefer-rest-params */
1086
1209
 
1087
1210
  )));
@@ -305,6 +305,43 @@ type JssmParseFunctionType =
305
305
 
306
306
 
307
307
 
308
+ type BasicHookDescription = {
309
+ kind : 'hook'
310
+ from : string,
311
+ to : string,
312
+ handler : Function
313
+ };
314
+
315
+ type HookDescriptionWithAction = {
316
+ kind : 'named',
317
+ from : string,
318
+ to : string,
319
+ action : string,
320
+ handler : Function
321
+ };
322
+
323
+ type EntryHook = {
324
+ kind : 'entry',
325
+ to : string,
326
+ handler : Function
327
+ };
328
+
329
+ type ExitHook = {
330
+ kind : 'exit',
331
+ from : string,
332
+ handler : Function
333
+ };
334
+
335
+ type HookDescription
336
+ = BasicHookDescription
337
+ | HookDescriptionWithAction
338
+ | EntryHook
339
+ | ExitHook;
340
+
341
+
342
+
343
+
344
+
308
345
  export {
309
346
 
310
347
  JssmColor,
@@ -341,6 +378,8 @@ export {
341
378
  JssmMachineInternalState,
342
379
 
343
380
  FslDirection,
344
- FslTheme
381
+ FslTheme,
382
+
383
+ HookDescription
345
384
 
346
385
  };
@@ -90,11 +90,28 @@ const weighted_histo_key: Function = (n: number, opts: Array<any>, prob_prop: st
90
90
 
91
91
 
92
92
 
93
+ const hook_name = (from: string, to: string): string =>
94
+
95
+ JSON.stringify([from, to]);
96
+
97
+
98
+
99
+
100
+
101
+ const named_hook_name = (from: string, to: string, action: string): string =>
102
+
103
+ JSON.stringify([from, to, action]);
104
+
105
+
106
+
107
+
108
+
93
109
  export {
94
110
  seq,
95
111
  arr_uniq_p,
96
112
  histograph, weighted_histo_key,
97
113
  weighted_rand_select, weighted_sample_select,
98
- array_box_if_string
114
+ array_box_if_string,
115
+ hook_name, named_hook_name
99
116
  };
100
117