jssm 5.77.1 → 5.79.1

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,8 +1,7 @@
1
1
  declare type StateType = string;
2
- import { circular_buffer } from 'circular_buffer_js';
3
2
  import { JssmGenericState, JssmGenericConfig, JssmTransition, JssmTransitionList, // JssmTransitionRule,
4
- JssmMachineInternalState, JssmParseTree, JssmStateDeclaration, JssmArrow, JssmArrowDirection, JssmArrowKind, JssmLayout, FslDirection, FslTheme, HookDescription, HookHandler, HookContext, HookResult, HookComplexResult } from './jssm_types';
5
- import { seq, weighted_rand_select, weighted_sample_select, histograph, weighted_histo_key } from './jssm_util';
3
+ JssmMachineInternalState, JssmParseTree, JssmStateDeclaration, JssmArrow, JssmArrowDirection, JssmArrowKind, JssmLayout, JssmHistory, JssmSerialization, FslDirection, FslTheme, HookDescription, HookHandler, HookContext, HookResult, HookComplexResult } from './jssm_types';
4
+ import { seq, unique, find_repeated, weighted_rand_select, weighted_sample_select, histograph, weighted_histo_key } from './jssm_util';
6
5
  import { shapes, gviz_shapes, named_colors } from './jssm_constants';
7
6
  import { version } from './version';
8
7
  /*********
@@ -251,9 +250,12 @@ declare class Machine<mDT> {
251
250
  _post_main_transition_hook: HookHandler<mDT> | undefined;
252
251
  _post_forced_transition_hook: HookHandler<mDT> | undefined;
253
252
  _post_any_transition_hook: HookHandler<mDT> | undefined;
254
- _history: circular_buffer<[StateType, mDT]>;
253
+ _property_keys: Set<string>;
254
+ _default_properties: Map<string, any>;
255
+ _state_properties: Map<string, any>;
256
+ _history: JssmHistory<mDT>;
255
257
  _history_length: number;
256
- 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, history, data }: JssmGenericConfig<mDT>);
258
+ constructor({ start_states, complete, transitions, machine_author, machine_comment, machine_contributor, machine_definition, machine_language, machine_license, machine_name, machine_version, state_declaration, property_definition, state_property, fsl_version, dot_preamble, arrange_declaration, arrange_start_declaration, arrange_end_declaration, theme, flow, graph_layout, instance_name, history, data }: JssmGenericConfig<mDT>);
257
259
  /********
258
260
  *
259
261
  * Internal method for fabricating states. Not meant for external use.
@@ -295,6 +297,122 @@ declare class Machine<mDT> {
295
297
  *
296
298
  */
297
299
  data(): mDT;
300
+ /*********
301
+ *
302
+ * Get the current value of a given property name.
303
+ *
304
+ * ```typescript
305
+ *
306
+ * ```
307
+ *
308
+ * @param name The relevant property name to look up
309
+ *
310
+ * @returns The value behind the prop name. Because functional props are
311
+ * evaluated as getters, this can be anything.
312
+ *
313
+ */
314
+ prop(name: string): any;
315
+ /*********
316
+ *
317
+ * Get the current value of a given property name. If missing on the state
318
+ * and without a global default, throw, unlike {@link prop}, which would
319
+ * return `undefined` instead.
320
+ *
321
+ * ```typescript
322
+ *
323
+ * ```
324
+ *
325
+ * @param name The relevant property name to look up
326
+ *
327
+ * @returns The value behind the prop name. Because functional props are
328
+ * evaluated as getters, this can be anything.
329
+ *
330
+ */
331
+ strict_prop(name: string): any;
332
+ /*********
333
+ *
334
+ * Get the current value of every prop, as an object. If no current definition
335
+ * exists for a prop - that is, if the prop was defined without a default and
336
+ * the current state also doesn't define the prop - then that prop will be listed
337
+ * in the returned object with a value of `undefined`.
338
+ *
339
+ * ```typescript
340
+ * const traffic_light = sm`
341
+ *
342
+ * property can_go default true;
343
+ * property hesitate default true;
344
+ * property stop_first default false;
345
+ *
346
+ * Off -> Red => Green => Yellow => Red;
347
+ * [Red Yellow Green] ~> [Off FlashingRed];
348
+ * FlashingRed -> Red;
349
+ *
350
+ * state Red: { property stop_first true; property can_go false; };
351
+ * state Off: { property stop_first true; };
352
+ * state FlashingRed: { property stop_first true; };
353
+ * state Green: { property hesitate false; };
354
+ *
355
+ * `;
356
+ *
357
+ * traffic_light.state(); // Off
358
+ * traffic_light.props(); // { can_go: true, hesitate: true, stop_first: true; }
359
+ *
360
+ * traffic_light.go('Red');
361
+ * traffic_light.props(); // { can_go: false, hesitate: true, stop_first: true; }
362
+ *
363
+ * traffic_light.go('Green');
364
+ * traffic_light.props(); // { can_go: true, hesitate: false, stop_first: false; }
365
+ * ```
366
+ *
367
+ */
368
+ props(): object;
369
+ /*********
370
+ *
371
+ * Get the current value of every prop, as an object. Compare
372
+ * {@link prop_map}, which returns a `Map`.
373
+ *
374
+ * ```typescript
375
+ *
376
+ * ```
377
+ *
378
+ */
379
+ /*********
380
+ *
381
+ * Get the current value of every prop, as an object. Compare
382
+ * {@link prop_map}, which returns a `Map`. Akin to {@link strict_prop},
383
+ * this throws if a required prop is missing.
384
+ *
385
+ * ```typescript
386
+ *
387
+ * ```
388
+ *
389
+ */
390
+ /*********
391
+ *
392
+ * Check whether a given string is a known property's name.
393
+ *
394
+ * ```typescript
395
+ * const example = sm`property foo default 1; a->b;`;
396
+ *
397
+ * example.known_prop('foo'); // true
398
+ * example.known_prop('bar'); // false
399
+ * ```
400
+ *
401
+ * @param prop_name The relevant property name to look up
402
+ *
403
+ */
404
+ known_prop(prop_name: string): boolean;
405
+ /*********
406
+ *
407
+ * List all known property names. If you'd also like values, use
408
+ * {@link props} instead. The order of the properties is not defined, and
409
+ * the properties generally will not be sorted.
410
+ *
411
+ * ```typescript
412
+ * ```
413
+ *
414
+ */
415
+ known_props(): string[];
298
416
  /********
299
417
  *
300
418
  * Check whether a given state is final (either has no exits or is marked
@@ -330,10 +448,19 @@ declare class Machine<mDT> {
330
448
  * console.log( final_test.is_final() ); // true
331
449
  * ```
332
450
  *
451
+ */
452
+ is_final(): boolean;
453
+ /********
454
+ *
455
+ * Serialize the current machine, including all defining state but not the
456
+ * machine string, to a structure. This means you will need the machine
457
+ * string to recreate (to not waste repeated space;) if you want the machine
458
+ * string embedded, call {@link serialize_with_string} instead.
459
+ *
333
460
  * @typeparam mDT The type of the machine data member; usually omitted
334
461
  *
335
462
  */
336
- is_final(): boolean;
463
+ serialize(comment?: string | undefined): JssmSerialization<mDT>;
337
464
  graph_layout(): string;
338
465
  dot_preamble(): string;
339
466
  machine_author(): Array<string>;
@@ -688,15 +815,27 @@ declare class Machine<mDT> {
688
815
  * Instruct the machine to complete an action. Synonym for {@link action}.
689
816
  *
690
817
  * ```typescript
691
- * const light = sm`red 'next' -> green 'next' -> yellow 'next' -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`;
818
+ * const light = sm`
819
+ * off 'start' -> red;
820
+ * red 'next' -> green 'next' -> yellow 'next' -> red;
821
+ * [red yellow green] 'shutdown' ~> off;
822
+ * `;
692
823
  *
693
- * light.state(); // 'red'
694
- * light.do('next'); // true
695
- * light.state(); // 'green'
824
+ * light.state(); // 'off'
825
+ * light.do('start'); // true
826
+ * light.state(); // 'red'
827
+ * light.do('next'); // true
828
+ * light.state(); // 'green'
829
+ * light.do('next'); // true
830
+ * light.state(); // 'yellow'
831
+ * light.do('dance'); // !! false - no such action
832
+ * light.state(); // 'yellow'
833
+ * light.do('start'); // !! false - yellow does not have the action start
834
+ * light.state(); // 'yellow'
696
835
  * ```
697
836
  *
698
837
  * @typeparam mDT The type of the machine data member; usually omitted
699
- *
838
+ b *
700
839
  * @param actionName The action to engage
701
840
  *
702
841
  * @param newData The data change to insert during the action
@@ -708,11 +847,21 @@ declare class Machine<mDT> {
708
847
  * Instruct the machine to complete a transition. Synonym for {@link go}.
709
848
  *
710
849
  * ```typescript
711
- * const light = sm`red -> green -> yellow -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`;
850
+ * const light = sm`
851
+ * off 'start' -> red;
852
+ * red 'next' -> green 'next' -> yellow 'next' -> red;
853
+ * [red yellow green] 'shutdown' ~> off;
854
+ * `;
712
855
  *
713
- * light.state(); // 'red'
714
- * light.transition('green'); // true
715
- * light.state(); // 'green'
856
+ * light.state(); // 'off'
857
+ * light.go('red'); // true
858
+ * light.state(); // 'red'
859
+ * light.go('green'); // true
860
+ * light.state(); // 'green'
861
+ * light.go('blue'); // !! false - no such state
862
+ * light.state(); // 'green'
863
+ * light.go('red'); // !! false - green may not go directly to red, only to yellow
864
+ * light.state(); // 'green'
716
865
  * ```
717
866
  *
718
867
  * @typeparam mDT The type of the machine data member; usually omitted
@@ -824,4 +973,5 @@ declare function from<mDT>(MachineAsString: string, ExtraConstructorFields?: Par
824
973
  declare function is_hook_complex_result<mDT>(hr: unknown): hr is HookComplexResult<mDT>;
825
974
  declare function is_hook_rejection<mDT>(hr: HookResult<mDT>): boolean;
826
975
  declare function abstract_hook_step<mDT>(maybe_hook: HookHandler<mDT> | undefined, hook_args: HookContext<mDT>): HookComplexResult<mDT>;
827
- 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, shapes, gviz_shapes, named_colors, is_hook_rejection, is_hook_complex_result, abstract_hook_step };
976
+ declare function deserialize<mDT>(machine_string: string, ser: JssmSerialization<mDT>): Machine<mDT>;
977
+ export { version, transfer_state_properties, Machine, deserialize, make, wrap_parse as parse, compile, sm, from, arrow_direction, arrow_left_kind, arrow_right_kind, seq, unique, find_repeated, weighted_rand_select, histograph, weighted_sample_select, weighted_histo_key, shapes, gviz_shapes, named_colors, is_hook_rejection, is_hook_complex_result, abstract_hook_step };
package/dist/es6/jssm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // whargarbl lots of these return arrays could/should be sets
2
2
  import { reduce as reduce_to_639 } from 'reduce-to-639-1';
3
3
  import { circular_buffer } from 'circular_buffer_js';
4
- 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
+ import { seq, unique, find_repeated, weighted_rand_select, weighted_sample_select, histograph, weighted_histo_key, array_box_if_string, name_bind_prop_and_state, hook_name, named_hook_name } from './jssm_util';
5
5
  import { shapes, gviz_shapes, named_colors } from './jssm_constants';
6
6
  import { parse } from './jssm-dot';
7
7
  import { version } from './version'; // replaced from package.js in build
@@ -351,6 +351,16 @@ function compile_rule_handler(rule) {
351
351
  if (rule.key === 'machine_language') {
352
352
  return { agg_as: 'machine_language', val: reduce_to_639(rule.value) };
353
353
  }
354
+ // manually rehandled to make `undefined` as a property safe
355
+ if (rule.key === 'property_definition') {
356
+ if (rule.hasOwnProperty('default_value')) {
357
+ return { agg_as: 'property_definition', val: { name: rule.name, default_value: rule.default_value } };
358
+ }
359
+ else {
360
+ return { agg_as: 'property_definition', val: { name: rule.name } };
361
+ }
362
+ }
363
+ // state properties are in here
354
364
  if (rule.key === 'state_declaration') {
355
365
  if (!rule.name) {
356
366
  throw new JssmError(undefined, 'State declarations must have a name');
@@ -361,6 +371,7 @@ function compile_rule_handler(rule) {
361
371
  'arrange_end_declaration'].includes(rule.key)) {
362
372
  return { agg_as: rule.key, val: [rule.value] };
363
373
  }
374
+ // things that can only exist once and are just a value under their own name
364
375
  const tautologies = [
365
376
  'graph_layout', 'start_states', 'end_states', 'machine_name', 'machine_version',
366
377
  'machine_comment', 'machine_author', 'machine_contributor', 'machine_definition',
@@ -440,6 +451,8 @@ function compile(tree) {
440
451
  machine_license: [],
441
452
  machine_name: [],
442
453
  machine_reference: [],
454
+ property_definition: [],
455
+ state_property: {},
443
456
  theme: [],
444
457
  flow: [],
445
458
  dot_preamble: [],
@@ -452,10 +465,15 @@ function compile(tree) {
452
465
  const rule = compile_rule_handler(tr), agg_as = rule.agg_as, val = rule.val; // TODO FIXME no any
453
466
  results[agg_as] = results[agg_as].concat(val);
454
467
  });
468
+ const property_keys = results['property_definition'].map(pd => pd.name), repeat_props = find_repeated(property_keys);
469
+ if (repeat_props.length) {
470
+ throw new JssmError(undefined, `Cannot repeat property definitions. Saw ${JSON.stringify(repeat_props)}`);
471
+ }
455
472
  const assembled_transitions = [].concat(...results['transition']);
456
473
  const result_cfg = {
457
474
  start_states: results.start_states.length ? results.start_states : [assembled_transitions[0].from],
458
- transitions: assembled_transitions
475
+ transitions: assembled_transitions,
476
+ state_property: []
459
477
  };
460
478
  const oneOnlyKeys = [
461
479
  'graph_layout', 'machine_name', 'machine_version', 'machine_comment',
@@ -473,11 +491,28 @@ function compile(tree) {
473
491
  }
474
492
  });
475
493
  ['arrange_declaration', 'arrange_start_declaration', 'arrange_end_declaration',
476
- 'machine_author', 'machine_contributor', 'machine_reference', 'state_declaration'].map((multiKey) => {
494
+ 'machine_author', 'machine_contributor', 'machine_reference',
495
+ 'state_declaration', 'property_definition'].map((multiKey) => {
477
496
  if (results[multiKey].length) {
478
497
  result_cfg[multiKey] = results[multiKey];
479
498
  }
480
499
  });
500
+ // re-walk state declarations, already wrapped up, to get state properties,
501
+ // which go out in a different datastructure
502
+ results.state_declaration.forEach(sd => {
503
+ sd.declarations.forEach(decl => {
504
+ if (decl.key === 'state_property') {
505
+ const label = name_bind_prop_and_state(decl.name, sd.state);
506
+ console.log(`Bind ${sd.state}:${decl.name} as ${label}`);
507
+ if (result_cfg.state_property.findIndex(c => c.name === label) !== -1) {
508
+ throw new JssmError(undefined, `A state may only bind a property once (${sd.state} re-binds ${decl.name})`);
509
+ }
510
+ else {
511
+ result_cfg.state_property.push({ name: label, default_value: decl.value });
512
+ }
513
+ }
514
+ });
515
+ });
481
516
  return result_cfg;
482
517
  }
483
518
  /*********
@@ -527,6 +562,9 @@ function transfer_state_properties(state_decl) {
527
562
  case 'border-color':
528
563
  state_decl.borderColor = d.value;
529
564
  break;
565
+ case 'state_property':
566
+ state_decl.property = { name: d.name, value: d.value };
567
+ break;
530
568
  default: throw new JssmError(undefined, `Unknown state property: '${JSON.stringify(d)}'`);
531
569
  }
532
570
  });
@@ -535,7 +573,7 @@ function transfer_state_properties(state_decl) {
535
573
  // TODO add a lotta docblock here
536
574
  class Machine {
537
575
  // whargarbl this badly needs to be broken up, monolith master
538
- 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, history, data }) {
576
+ constructor({ start_states, complete = [], transitions, machine_author, machine_comment, machine_contributor, machine_definition, machine_language, machine_license, machine_name, machine_version, state_declaration, property_definition, state_property, fsl_version, dot_preamble = undefined, arrange_declaration = [], arrange_start_declaration = [], arrange_end_declaration = [], theme = 'default', flow = 'down', graph_layout = 'dot', instance_name, history, data }) {
539
577
  this._instance_name = instance_name;
540
578
  this._state = start_states[0];
541
579
  this._states = new Map();
@@ -600,6 +638,9 @@ class Machine {
600
638
  this._post_forced_transition_hook = undefined;
601
639
  this._post_any_transition_hook = undefined;
602
640
  this._data = data;
641
+ this._property_keys = new Set();
642
+ this._default_properties = new Map();
643
+ this._state_properties = new Map();
603
644
  this._history_length = history || 0;
604
645
  this._history = new circular_buffer(this._history_length);
605
646
  if (state_declaration) {
@@ -697,6 +738,19 @@ class Machine {
697
738
  */
698
739
  }
699
740
  });
741
+ if (Array.isArray(property_definition)) {
742
+ property_definition.forEach(pr => {
743
+ this._property_keys.add(pr.name);
744
+ if (pr.hasOwnProperty('default_value')) {
745
+ this._default_properties.set(pr.name, pr.default_value);
746
+ }
747
+ });
748
+ }
749
+ if (Array.isArray(state_property)) {
750
+ state_property.forEach(sp => {
751
+ this._state_properties.set(sp.name, sp.default_value);
752
+ });
753
+ }
700
754
  }
701
755
  /********
702
756
  *
@@ -763,6 +817,165 @@ class Machine {
763
817
  return true; // todo whargarbl
764
818
  }
765
819
  */
820
+ // NEEDS_DOCS
821
+ /*********
822
+ *
823
+ * Get the current value of a given property name.
824
+ *
825
+ * ```typescript
826
+ *
827
+ * ```
828
+ *
829
+ * @param name The relevant property name to look up
830
+ *
831
+ * @returns The value behind the prop name. Because functional props are
832
+ * evaluated as getters, this can be anything.
833
+ *
834
+ */
835
+ prop(name) {
836
+ const bound_name = name_bind_prop_and_state(name, this.state());
837
+ if (this._state_properties.has(bound_name)) {
838
+ return this._state_properties.get(bound_name);
839
+ }
840
+ else if (this._default_properties.has(name)) {
841
+ return this._default_properties.get(name);
842
+ }
843
+ else {
844
+ return undefined;
845
+ }
846
+ }
847
+ // NEEDS_DOCS
848
+ /*********
849
+ *
850
+ * Get the current value of a given property name. If missing on the state
851
+ * and without a global default, throw, unlike {@link prop}, which would
852
+ * return `undefined` instead.
853
+ *
854
+ * ```typescript
855
+ *
856
+ * ```
857
+ *
858
+ * @param name The relevant property name to look up
859
+ *
860
+ * @returns The value behind the prop name. Because functional props are
861
+ * evaluated as getters, this can be anything.
862
+ *
863
+ */
864
+ strict_prop(name) {
865
+ const bound_name = name_bind_prop_and_state(name, this.state());
866
+ if (this._state_properties.has(bound_name)) {
867
+ return this._state_properties.get(bound_name);
868
+ }
869
+ else if (this._default_properties.has(name)) {
870
+ return this._default_properties.get(name);
871
+ }
872
+ else {
873
+ throw new JssmError(this, `Strictly requested a prop '${name}' which doesn't exist on current state '${this.state()}' and has no default`);
874
+ }
875
+ }
876
+ // NEEDS_DOCS
877
+ // COMEBACK add prop_map, sparse_props and strict_props to doc text when implemented
878
+ /*********
879
+ *
880
+ * Get the current value of every prop, as an object. If no current definition
881
+ * exists for a prop - that is, if the prop was defined without a default and
882
+ * the current state also doesn't define the prop - then that prop will be listed
883
+ * in the returned object with a value of `undefined`.
884
+ *
885
+ * ```typescript
886
+ * const traffic_light = sm`
887
+ *
888
+ * property can_go default true;
889
+ * property hesitate default true;
890
+ * property stop_first default false;
891
+ *
892
+ * Off -> Red => Green => Yellow => Red;
893
+ * [Red Yellow Green] ~> [Off FlashingRed];
894
+ * FlashingRed -> Red;
895
+ *
896
+ * state Red: { property stop_first true; property can_go false; };
897
+ * state Off: { property stop_first true; };
898
+ * state FlashingRed: { property stop_first true; };
899
+ * state Green: { property hesitate false; };
900
+ *
901
+ * `;
902
+ *
903
+ * traffic_light.state(); // Off
904
+ * traffic_light.props(); // { can_go: true, hesitate: true, stop_first: true; }
905
+ *
906
+ * traffic_light.go('Red');
907
+ * traffic_light.props(); // { can_go: false, hesitate: true, stop_first: true; }
908
+ *
909
+ * traffic_light.go('Green');
910
+ * traffic_light.props(); // { can_go: true, hesitate: false, stop_first: false; }
911
+ * ```
912
+ *
913
+ */
914
+ props() {
915
+ const ret = {};
916
+ this.known_props().forEach(p => ret[p] = this.prop(p));
917
+ return ret;
918
+ }
919
+ // NEEDS_DOCS
920
+ // TODO COMEBACK
921
+ /*********
922
+ *
923
+ * Get the current value of every prop, as an object. Compare
924
+ * {@link prop_map}, which returns a `Map`.
925
+ *
926
+ * ```typescript
927
+ *
928
+ * ```
929
+ *
930
+ */
931
+ // sparse_props(name: string): object {
932
+ // }
933
+ // NEEDS_DOCS
934
+ // TODO COMEBACK
935
+ /*********
936
+ *
937
+ * Get the current value of every prop, as an object. Compare
938
+ * {@link prop_map}, which returns a `Map`. Akin to {@link strict_prop},
939
+ * this throws if a required prop is missing.
940
+ *
941
+ * ```typescript
942
+ *
943
+ * ```
944
+ *
945
+ */
946
+ // strict_props(name: string): object {
947
+ // }
948
+ /*********
949
+ *
950
+ * Check whether a given string is a known property's name.
951
+ *
952
+ * ```typescript
953
+ * const example = sm`property foo default 1; a->b;`;
954
+ *
955
+ * example.known_prop('foo'); // true
956
+ * example.known_prop('bar'); // false
957
+ * ```
958
+ *
959
+ * @param prop_name The relevant property name to look up
960
+ *
961
+ */
962
+ known_prop(prop_name) {
963
+ return this._property_keys.has(prop_name);
964
+ }
965
+ // NEEDS_DOCS
966
+ /*********
967
+ *
968
+ * List all known property names. If you'd also like values, use
969
+ * {@link props} instead. The order of the properties is not defined, and
970
+ * the properties generally will not be sorted.
971
+ *
972
+ * ```typescript
973
+ * ```
974
+ *
975
+ */
976
+ known_props() {
977
+ return [...this._property_keys];
978
+ }
766
979
  /********
767
980
  *
768
981
  * Check whether a given state is final (either has no exits or is marked
@@ -800,13 +1013,32 @@ class Machine {
800
1013
  * console.log( final_test.is_final() ); // true
801
1014
  * ```
802
1015
  *
803
- * @typeparam mDT The type of the machine data member; usually omitted
804
- *
805
1016
  */
806
1017
  is_final() {
807
1018
  // return ((!this.is_changing()) && this.state_is_final(this.state()));
808
1019
  return this.state_is_final(this.state());
809
1020
  }
1021
+ /********
1022
+ *
1023
+ * Serialize the current machine, including all defining state but not the
1024
+ * machine string, to a structure. This means you will need the machine
1025
+ * string to recreate (to not waste repeated space;) if you want the machine
1026
+ * string embedded, call {@link serialize_with_string} instead.
1027
+ *
1028
+ * @typeparam mDT The type of the machine data member; usually omitted
1029
+ *
1030
+ */
1031
+ serialize(comment) {
1032
+ return {
1033
+ comment,
1034
+ state: this._state,
1035
+ data: this._data,
1036
+ jssm_version: version,
1037
+ history: this._history.toArray(),
1038
+ history_capacity: this._history.capacity,
1039
+ timestamp: new Date().getTime(),
1040
+ };
1041
+ }
810
1042
  graph_layout() {
811
1043
  return this._graph_layout;
812
1044
  }
@@ -862,11 +1094,6 @@ class Machine {
862
1094
  states: this._states
863
1095
  };
864
1096
  }
865
- /*
866
- load_machine_state(): boolean {
867
- return false; // todo whargarbl
868
- }
869
- */
870
1097
  /*********
871
1098
  *
872
1099
  * List all the states known by the machine. Please note that the order of
@@ -1749,15 +1976,27 @@ class Machine {
1749
1976
  * Instruct the machine to complete an action. Synonym for {@link action}.
1750
1977
  *
1751
1978
  * ```typescript
1752
- * const light = sm`red 'next' -> green 'next' -> yellow 'next' -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`;
1979
+ * const light = sm`
1980
+ * off 'start' -> red;
1981
+ * red 'next' -> green 'next' -> yellow 'next' -> red;
1982
+ * [red yellow green] 'shutdown' ~> off;
1983
+ * `;
1753
1984
  *
1754
- * light.state(); // 'red'
1755
- * light.do('next'); // true
1756
- * light.state(); // 'green'
1985
+ * light.state(); // 'off'
1986
+ * light.do('start'); // true
1987
+ * light.state(); // 'red'
1988
+ * light.do('next'); // true
1989
+ * light.state(); // 'green'
1990
+ * light.do('next'); // true
1991
+ * light.state(); // 'yellow'
1992
+ * light.do('dance'); // !! false - no such action
1993
+ * light.state(); // 'yellow'
1994
+ * light.do('start'); // !! false - yellow does not have the action start
1995
+ * light.state(); // 'yellow'
1757
1996
  * ```
1758
1997
  *
1759
1998
  * @typeparam mDT The type of the machine data member; usually omitted
1760
- *
1999
+ b *
1761
2000
  * @param actionName The action to engage
1762
2001
  *
1763
2002
  * @param newData The data change to insert during the action
@@ -1771,11 +2010,21 @@ class Machine {
1771
2010
  * Instruct the machine to complete a transition. Synonym for {@link go}.
1772
2011
  *
1773
2012
  * ```typescript
1774
- * const light = sm`red -> green -> yellow -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`;
2013
+ * const light = sm`
2014
+ * off 'start' -> red;
2015
+ * red 'next' -> green 'next' -> yellow 'next' -> red;
2016
+ * [red yellow green] 'shutdown' ~> off;
2017
+ * `;
1775
2018
  *
1776
- * light.state(); // 'red'
1777
- * light.transition('green'); // true
1778
- * light.state(); // 'green'
2019
+ * light.state(); // 'off'
2020
+ * light.go('red'); // true
2021
+ * light.state(); // 'red'
2022
+ * light.go('green'); // true
2023
+ * light.state(); // 'green'
2024
+ * light.go('blue'); // !! false - no such state
2025
+ * light.state(); // 'green'
2026
+ * light.go('red'); // !! false - green may not go directly to red, only to yellow
2027
+ * light.state(); // 'green'
1779
2028
  * ```
1780
2029
  *
1781
2030
  * @typeparam mDT The type of the machine data member; usually omitted
@@ -1988,6 +2237,12 @@ function abstract_hook_step(maybe_hook, hook_args) {
1988
2237
  return { pass: true };
1989
2238
  }
1990
2239
  }
1991
- export { version, transfer_state_properties, Machine, make, wrap_parse as parse, compile, sm, from, arrow_direction, arrow_left_kind, arrow_right_kind,
2240
+ function deserialize(machine_string, ser) {
2241
+ const machine = from(machine_string, { data: ser.data, history: ser.history_capacity });
2242
+ machine._state = ser.state;
2243
+ ser.history.forEach(history_item => machine._history.push(history_item));
2244
+ return machine;
2245
+ }
2246
+ export { version, transfer_state_properties, Machine, deserialize, make, wrap_parse as parse, compile, sm, from, arrow_direction, arrow_left_kind, arrow_right_kind,
1992
2247
  // WHARGARBL TODO these should be exported to a utility library
1993
- seq, weighted_rand_select, histograph, weighted_sample_select, weighted_histo_key, shapes, gviz_shapes, named_colors, is_hook_rejection, is_hook_complex_result, abstract_hook_step };
2248
+ seq, unique, find_repeated, weighted_rand_select, histograph, weighted_sample_select, weighted_histo_key, shapes, gviz_shapes, named_colors, is_hook_rejection, is_hook_complex_result, abstract_hook_step };