jssm 5.61.2 → 5.62.0

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 CHANGED
@@ -42,18 +42,39 @@ log( TrafficLight.state() ); // 'Green'
42
42
  What if the notation supported action names easily?
43
43
 
44
44
  ```javascript
45
- const TrafficLightWithActions = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;`;
45
+ const TLWA = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;`; // TLWA = Traffic Light With Actions
46
46
 
47
- log( TrafficLightWithActions.state() ); // 'Red'
47
+ log( TLWA.state() ); // 'Red'
48
48
 
49
- TrafficLightWithActions.action('next'); // true
50
- log( TrafficLightWithActions.state() ); // 'Green'
49
+ TLWA.action('next'); // true
50
+ log( TLWA.state() ); // 'Green'
51
51
 
52
- TrafficLightWithActions.action('next'); // true
53
- log( TrafficLightWithActions.state() ); // 'Yellow'
52
+ TLWA.action('next'); // true
53
+ log( TLWA.state() ); // 'Yellow'
54
54
 
55
- TrafficLightWithActions.action('next'); // true
56
- log( TrafficLightWithActions.state() ); // 'Red'
55
+ TLWA.action('next'); // true
56
+ log( TLWA.state() ); // 'Red'
57
+ ```
58
+
59
+ <br/>
60
+
61
+ What if integration with the outside was straightforward?
62
+
63
+ ```javascript
64
+ const MTL = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;` // MTL = More Traffic Lights
65
+ .hook('Red', 'Green', () => log('GO GO GO') ) // node will jump the gun when you hit return, though
66
+ .hook_entry('Red', () => log('STOP') ); // so put it on one line in node
67
+
68
+ log( MTL.state() ); // 'Red'
69
+
70
+ TLWA.action('next'); // true, console logs 'GO GO GO'
71
+ log( TLWA.state() ); // 'Green'
72
+
73
+ TLWA.action('next'); // true
74
+ log( TLWA.state() ); // 'Yellow'
75
+
76
+ TLWA.action('next'); // true, console logs 'STOP'
77
+ log( TLWA.state() ); // 'Red'
57
78
  ```
58
79
 
59
80
  <br/>
@@ -61,11 +82,11 @@ log( TrafficLightWithActions.state() ); // 'Red'
61
82
  What if the machine followed JS standards, and distinguished refusals as `false` from mistakes as `throw`n?
62
83
 
63
84
  ```javascript
64
- const AnotherTrafficLight = sm`Red -> Green -> Yellow -> Red;`;
85
+ const ATL = sm`Red -> Green -> Yellow -> Red;`; // ATL = Another Traffic Light
65
86
 
66
- log( AnotherTrafficLight.state() ); // 'Red' - uses 1st state unless told otherwise
67
- AnotherTrafficLight.transition('Yellow'); // false (Yellow isn't allowed from Red)
68
- AnotherTrafficLight.transition('Blue'); // throws (Blue isn't a state at all)
87
+ log( ATL.state() ); // 'Red' - uses 1st state unless told otherwise
88
+ ATL.transition('Yellow'); // false (Yellow isn't allowed from Red)
89
+ ATL.transition('Blue'); // throws (Blue isn't a state at all)
69
90
  ```
70
91
 
71
92
  <br/>
@@ -30,6 +30,7 @@ declare class Machine<mDT> {
30
30
  _fsl_version?: string;
31
31
  _raw_state_declaration?: Array<Object>;
32
32
  _state_declarations: Map<StateType, JssmStateDeclaration>;
33
+ _instance_name: string;
33
34
  _graph_layout: JssmLayout;
34
35
  _dot_preamble: string;
35
36
  _arrange_declaration: Array<Array<StateType>>;
@@ -54,7 +55,7 @@ declare class Machine<mDT> {
54
55
  _main_transition_hook: HookHandler | undefined;
55
56
  _forced_transition_hook: HookHandler | undefined;
56
57
  _any_transition_hook: HookHandler | undefined;
57
- 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>);
58
+ 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, instance_name }: JssmGenericConfig<mDT>);
58
59
  _new_state(state_config: JssmGenericState): StateType;
59
60
  state(): StateType;
60
61
  state_is_final(whichState: StateType): boolean;
@@ -124,7 +125,9 @@ declare class Machine<mDT> {
124
125
  valid_action(action: StateType, _newData?: mDT): boolean;
125
126
  valid_transition(newState: StateType, _newData?: mDT): boolean;
126
127
  valid_force_transition(newState: StateType, _newData?: mDT): boolean;
128
+ instance_name(): string | undefined;
127
129
  sm(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>;
128
130
  }
129
131
  declare function sm<mDT>(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>;
130
- export { version, transfer_state_properties, Machine, make, wrap_parse as parse, compile, sm, arrow_direction, arrow_left_kind, arrow_right_kind, seq, weighted_rand_select, histograph, weighted_sample_select, weighted_histo_key };
132
+ declare function from<mDT>(MachineAsString: string, ExtraConstructorFields?: Partial<JssmGenericConfig<mDT>> | undefined): Machine<mDT>;
133
+ export { version, transfer_state_properties, Machine, make, wrap_parse as parse, compile, sm, from, arrow_direction, arrow_left_kind, arrow_right_kind, seq, weighted_rand_select, histograph, weighted_sample_select, weighted_histo_key };
package/dist/es6/jssm.js CHANGED
@@ -3,6 +3,9 @@ import { reduce as reduce_to_639 } from 'reduce-to-639-1';
3
3
  import { seq, weighted_rand_select, weighted_sample_select, histograph, weighted_histo_key, array_box_if_string, hook_name, named_hook_name } from './jssm_util';
4
4
  import { parse } from './jssm-dot'; // TODO FIXME WHARGARBL this could be post-typed
5
5
  import { version } from './version'; // replaced from package.js in build
6
+ function xthrow(machine, message) {
7
+ throw new Error(`${(machine.instance_name !== undefined) ? `[[${machine.instance_name}]]: ` : ''}${message}${machine.state !== undefined ? ` (at ${machine.state})` : ''}`);
8
+ }
6
9
  /* eslint-disable complexity */
7
10
  function arrow_direction(arrow) {
8
11
  switch (String(arrow)) {
@@ -52,7 +55,7 @@ function arrow_direction(arrow) {
52
55
  case '<~⇒':
53
56
  return 'both';
54
57
  default:
55
- throw new Error(`arrow_direction: unknown arrow type ${arrow}`);
58
+ xthrow(this, `arrow_direction: unknown arrow type ${arrow}`);
56
59
  }
57
60
  }
58
61
  /* eslint-enable complexity */
@@ -94,7 +97,7 @@ function arrow_left_kind(arrow) {
94
97
  case '↚⇒':
95
98
  return 'forced';
96
99
  default:
97
- throw new Error(`arrow_direction: unknown arrow type ${arrow}`);
100
+ xthrow(this, `arrow_direction: unknown arrow type ${arrow}`);
98
101
  }
99
102
  }
100
103
  /* eslint-enable complexity */
@@ -136,7 +139,7 @@ function arrow_right_kind(arrow) {
136
139
  case '⇐↛':
137
140
  return 'forced';
138
141
  default:
139
- throw new Error(`arrow_direction: unknown arrow type ${arrow}`);
142
+ xthrow(this, `arrow_direction: unknown arrow type ${arrow}`);
140
143
  }
141
144
  }
142
145
  /* eslint-enable complexity */
@@ -148,13 +151,13 @@ function makeTransition(this_se, from, to, isRight, _wasList, _wasIndex) {
148
151
  forced_only: kind === 'forced',
149
152
  main_path: kind === 'main'
150
153
  };
151
- // if ((wasList !== undefined) && (wasIndex === undefined)) { throw new TypeError("Must have an index if transition was in a list"); }
152
- // if ((wasIndex !== undefined) && (wasList === undefined)) { throw new TypeError("Must be in a list if transition has an index"); }
154
+ // if ((wasList !== undefined) && (wasIndex === undefined)) { xthrow(this, `Must have an index if transition was in a list"); }
155
+ // if ((wasIndex !== undefined) && (wasList === undefined)) { xthrow(this, `Must be in a list if transition has an index"); }
153
156
  /*
154
157
  if (typeof edge.to === 'object') {
155
158
 
156
159
  if (edge.to.key === 'cycle') {
157
- if (wasList === undefined) { throw "Must have a waslist if a to is type cycle"; }
160
+ if (wasList === undefined) { xthrow(this, "Must have a waslist if a to is type cycle"); }
158
161
  const nextIndex = wrapBy(wasIndex, edge.to.value, wasList.length);
159
162
  edge.to = wasList[nextIndex];
160
163
  }
@@ -208,7 +211,7 @@ function compile_rule_handler(rule) {
208
211
  }
209
212
  if (rule.key === 'state_declaration') {
210
213
  if (!rule.name) {
211
- throw new Error('State declarations must have a name');
214
+ xthrow(this, 'State declarations must have a name');
212
215
  }
213
216
  return { agg_as: 'state_declaration', val: { state: rule.name, declarations: rule.value } };
214
217
  }
@@ -225,7 +228,7 @@ function compile_rule_handler(rule) {
225
228
  if (tautologies.includes(rule.key)) {
226
229
  return { agg_as: rule.key, val: rule.value };
227
230
  }
228
- throw new Error(`compile_rule_handler: Unknown rule: ${JSON.stringify(rule)}`);
231
+ xthrow(this, `compile_rule_handler: Unknown rule: ${JSON.stringify(rule)}`);
229
232
  }
230
233
  function compile(tree) {
231
234
  const results = {
@@ -268,7 +271,7 @@ function compile(tree) {
268
271
  ];
269
272
  oneOnlyKeys.map((oneOnlyKey) => {
270
273
  if (results[oneOnlyKey].length > 1) {
271
- throw new Error(`May only have one ${oneOnlyKey} statement maximum: ${JSON.stringify(results[oneOnlyKey])}`);
274
+ xthrow(this, `May only have one ${oneOnlyKey} statement maximum: ${JSON.stringify(results[oneOnlyKey])}`);
272
275
  }
273
276
  else {
274
277
  if (results[oneOnlyKey].length) {
@@ -311,14 +314,15 @@ function transfer_state_properties(state_decl) {
311
314
  case 'border-color':
312
315
  state_decl.borderColor = d.value;
313
316
  break;
314
- default: throw new Error(`Unknown state property: '${JSON.stringify(d)}'`);
317
+ default: xthrow(this, `Unknown state property: '${JSON.stringify(d)}'`);
315
318
  }
316
319
  });
317
320
  return state_decl;
318
321
  }
319
322
  class Machine {
320
323
  // whargarbl this badly needs to be broken up, monolith master
321
- 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 = undefined, arrange_declaration = [], arrange_start_declaration = [], arrange_end_declaration = [], theme = 'default', flow = 'down', graph_layout = 'dot' }) {
324
+ 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 = undefined, arrange_declaration = [], arrange_start_declaration = [], arrange_end_declaration = [], theme = 'default', flow = 'down', graph_layout = 'dot', instance_name }) {
325
+ this._instance_name = instance_name;
322
326
  this._state = start_states[0];
323
327
  this._states = new Map();
324
328
  this._state_declarations = new Map();
@@ -367,17 +371,17 @@ class Machine {
367
371
  if (state_declaration) {
368
372
  state_declaration.map((state_decl) => {
369
373
  if (this._state_declarations.has(state_decl.state)) { // no repeats
370
- throw new Error(`Added the same state declaration twice: ${JSON.stringify(state_decl.state)}`);
374
+ xthrow(this, `Added the same state declaration twice: ${JSON.stringify(state_decl.state)}`);
371
375
  }
372
376
  this._state_declarations.set(state_decl.state, transfer_state_properties(state_decl));
373
377
  });
374
378
  }
375
379
  transitions.map((tr) => {
376
380
  if (tr.from === undefined) {
377
- throw new Error(`transition must define 'from': ${JSON.stringify(tr)}`);
381
+ xthrow(this, `transition must define 'from': ${JSON.stringify(tr)}`);
378
382
  }
379
383
  if (tr.to === undefined) {
380
- throw new Error(`transition must define 'to': ${JSON.stringify(tr)}`);
384
+ xthrow(this, `transition must define 'to': ${JSON.stringify(tr)}`);
381
385
  }
382
386
  // get the cursors. what a mess
383
387
  const cursor_from = this._states.get(tr.from)
@@ -392,7 +396,7 @@ class Machine {
392
396
  }
393
397
  // guard against existing connections being re-added
394
398
  if (cursor_from.to.includes(tr.to)) {
395
- throw new Error(`already has ${JSON.stringify(tr.from)} to ${JSON.stringify(tr.to)}`);
399
+ xthrow(this, `already has ${JSON.stringify(tr.from)} to ${JSON.stringify(tr.to)}`);
396
400
  }
397
401
  else {
398
402
  cursor_from.to.push(tr.to);
@@ -404,7 +408,7 @@ class Machine {
404
408
  // guard against repeating a transition name
405
409
  if (tr.name) {
406
410
  if (this._named_transitions.has(tr.name)) {
407
- throw new Error(`named transition "${JSON.stringify(tr.name)}" already created`);
411
+ xthrow(this, `named transition "${JSON.stringify(tr.name)}" already created`);
408
412
  }
409
413
  else {
410
414
  this._named_transitions.set(tr.name, thisEdgeId);
@@ -426,7 +430,7 @@ class Machine {
426
430
  this._actions.set(tr.action, actionMap);
427
431
  }
428
432
  if (actionMap.has(tr.from)) {
429
- throw new Error(`action ${JSON.stringify(tr.action)} already attached to origin ${JSON.stringify(tr.from)}`);
433
+ xthrow(this, `action ${JSON.stringify(tr.action)} already attached to origin ${JSON.stringify(tr.from)}`);
430
434
  }
431
435
  else {
432
436
  actionMap.set(tr.from, thisEdgeId);
@@ -449,12 +453,12 @@ class Machine {
449
453
  const roActionMap = this._reverse_action_targets.get(tr.to); // wasteful - already did has - refactor
450
454
  if (roActionMap) {
451
455
  if (roActionMap.has(tr.action)) {
452
- throw new Error(`ro-action ${tr.to} already attached to action ${tr.action}`);
456
+ xthrow(this, `ro-action ${tr.to} already attached to action ${tr.action}`);
453
457
  } else {
454
458
  roActionMap.set(tr.action, thisEdgeId);
455
459
  }
456
460
  } else {
457
- throw new Error('should be impossible - flow doesn\'t know .set precedes .get yet again. severe error?');
461
+ xthrow(this, `should be impossible - flow doesn\'t know .set precedes .get yet again. severe error?');
458
462
  }
459
463
  */
460
464
  }
@@ -462,7 +466,7 @@ class Machine {
462
466
  }
463
467
  _new_state(state_config) {
464
468
  if (this._states.has(state_config.name)) {
465
- throw new Error(`state ${JSON.stringify(state_config.name)} already exists`);
469
+ xthrow(this, `state ${JSON.stringify(state_config.name)} already exists`);
466
470
  }
467
471
  this._states.set(state_config.name, state_config);
468
472
  return state_config.name;
@@ -553,7 +557,7 @@ class Machine {
553
557
  return state;
554
558
  }
555
559
  else {
556
- throw new Error(`no such state ${JSON.stringify(state)}`);
560
+ xthrow(this, `no such state ${JSON.stringify(state)}`);
557
561
  }
558
562
  }
559
563
  has_state(whichState) {
@@ -603,7 +607,7 @@ class Machine {
603
607
  probable_exits_for(whichState) {
604
608
  const wstate = this._states.get(whichState);
605
609
  if (!(wstate)) {
606
- throw new Error(`No such state ${JSON.stringify(whichState)} in probable_exits_for`);
610
+ xthrow(this, `No such state ${JSON.stringify(whichState)} in probable_exits_for`);
607
611
  }
608
612
  const wstate_to = wstate.to, wtf = wstate_to
609
613
  .map((ws) => this.lookup_transition_for(this.state(), ws))
@@ -632,7 +636,7 @@ class Machine {
632
636
  return Array.from(wstate.keys());
633
637
  }
634
638
  else {
635
- throw new Error(`No such state ${JSON.stringify(whichState)}`);
639
+ xthrow(this, `No such state ${JSON.stringify(whichState)}`);
636
640
  }
637
641
  }
638
642
  list_states_having_action(whichState) {
@@ -641,7 +645,7 @@ class Machine {
641
645
  return Array.from(wstate.keys());
642
646
  }
643
647
  else {
644
- throw new Error(`No such state ${JSON.stringify(whichState)}`);
648
+ xthrow(this, `No such state ${JSON.stringify(whichState)}`);
645
649
  }
646
650
  }
647
651
  // comeback
@@ -656,7 +660,7 @@ class Machine {
656
660
  list_exit_actions(whichState = this.state()) {
657
661
  const ra_base = this._reverse_actions.get(whichState);
658
662
  if (!(ra_base)) {
659
- throw new Error(`No such state ${JSON.stringify(whichState)}`);
663
+ xthrow(this, `No such state ${JSON.stringify(whichState)}`);
660
664
  }
661
665
  return Array.from(ra_base.values())
662
666
  .map((edgeId) => this._edges[edgeId])
@@ -666,7 +670,7 @@ class Machine {
666
670
  probable_action_exits(whichState = this.state()) {
667
671
  const ra_base = this._reverse_actions.get(whichState);
668
672
  if (!(ra_base)) {
669
- throw new Error(`No such state ${JSON.stringify(whichState)}`);
673
+ xthrow(this, `No such state ${JSON.stringify(whichState)}`);
670
674
  }
671
675
  return Array.from(ra_base.values())
672
676
  .map((edgeId) => this._edges[edgeId])
@@ -679,7 +683,7 @@ class Machine {
679
683
  // TODO FIXME test that is_unenterable on non-state throws
680
684
  is_unenterable(whichState) {
681
685
  if (!(this.has_state(whichState))) {
682
- throw new Error(`No such state ${whichState}`);
686
+ xthrow(this, `No such state ${whichState}`);
683
687
  }
684
688
  return this.list_entrances(whichState).length === 0;
685
689
  }
@@ -692,7 +696,7 @@ class Machine {
692
696
  // TODO FIXME test that state_is_terminal on non-state throws
693
697
  state_is_terminal(whichState) {
694
698
  if (!(this.has_state(whichState))) {
695
- throw new Error(`No such state ${whichState}`);
699
+ xthrow(this, `No such state ${whichState}`);
696
700
  }
697
701
  return this.list_exits(whichState).length === 0;
698
702
  }
@@ -708,7 +712,7 @@ class Machine {
708
712
  return wstate.complete;
709
713
  }
710
714
  else {
711
- throw new Error(`No such state ${JSON.stringify(whichState)}`);
715
+ xthrow(this, `No such state ${JSON.stringify(whichState)}`);
712
716
  }
713
717
  }
714
718
  has_completes() {
@@ -767,8 +771,7 @@ class Machine {
767
771
  this._has_exit_hooks = true;
768
772
  break;
769
773
  default:
770
- console.log(`Unknown hook type ${HookDesc.kind}, should be impossible`);
771
- throw new RangeError(`Unknown hook type ${HookDesc.kind}, should be impossible`);
774
+ xthrow(this, `Unknown hook type ${HookDesc.kind}, should be impossible`);
772
775
  }
773
776
  }
774
777
  hook(from, to, handler) {
@@ -822,7 +825,7 @@ class Machine {
822
825
  return this;
823
826
  }
824
827
  // remove_hook(HookDesc: HookDescription) {
825
- // throw 'TODO: Should remove hook here';
828
+ // xthrow(this, 'TODO: Should remove hook here');
826
829
  // }
827
830
  edges_between(from, to) {
828
831
  return this._edges.filter(edge => ((edge.from === from) && (edge.to === to)));
@@ -982,7 +985,7 @@ class Machine {
982
985
  current_action_edge_for(action) {
983
986
  const idx = this.current_action_for(action);
984
987
  if ((idx === undefined) || (idx === null)) {
985
- throw new Error(`No such action ${JSON.stringify(action)}`);
988
+ xthrow(this, `No such action ${JSON.stringify(action)}`);
986
989
  }
987
990
  return this._edges[idx];
988
991
  }
@@ -1008,6 +1011,9 @@ class Machine {
1008
1011
  // todo major incomplete whargarbl comeback
1009
1012
  return (this.lookup_transition_for(this.state(), newState) !== undefined);
1010
1013
  }
1014
+ instance_name() {
1015
+ return this._instance_name;
1016
+ }
1011
1017
  /* eslint-disable no-use-before-define */
1012
1018
  /* eslint-disable class-methods-use-this */
1013
1019
  sm(template_strings, ...remainder /* , arguments */) {
@@ -1027,6 +1033,13 @@ function sm(template_strings, ...remainder /* , arguments */) {
1027
1033
  /* eslint-enable prefer-rest-params */
1028
1034
  )));
1029
1035
  }
1030
- export { version, transfer_state_properties, Machine, make, wrap_parse as parse, compile, sm, arrow_direction, arrow_left_kind, arrow_right_kind,
1036
+ function from(MachineAsString, ExtraConstructorFields) {
1037
+ const to_decorate = make(MachineAsString);
1038
+ if (ExtraConstructorFields !== undefined) {
1039
+ Object.keys(ExtraConstructorFields).map(key => to_decorate[key] = ExtraConstructorFields[key]);
1040
+ }
1041
+ return new Machine(to_decorate);
1042
+ }
1043
+ export { version, transfer_state_properties, Machine, make, wrap_parse as parse, compile, sm, from, arrow_direction, arrow_left_kind, arrow_right_kind,
1031
1044
  // WHARGARBL TODO these should be exported to a utility library
1032
1045
  seq, weighted_rand_select, histograph, weighted_sample_select, weighted_histo_key };
@@ -125,6 +125,7 @@ declare type JssmGenericConfig<DataType> = {
125
125
  machine_version?: string;
126
126
  fsl_version?: string;
127
127
  auto_api?: boolean | string;
128
+ instance_name?: string | undefined;
128
129
  };
129
130
  declare type JssmCompileRule = {
130
131
  agg_as: string;
@@ -1,2 +1,2 @@
1
- const version = "5.61.2";
1
+ const version = "5.62.0";
2
2
  export { version };