jssm 5.162.10 → 5.162.12

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.
@@ -18,10 +18,10 @@ Please edit the file it's derived from, instead: `./src/md/readme_base.md`
18
18
 
19
19
 
20
20
 
21
- * Generated for version 5.162.10 at 7/12/2026, 5:25:34 AM
21
+ * Generated for version 5.162.12 at 7/12/2026, 10:09:04 AM
22
22
 
23
23
  -->
24
- # jssm 5.162.10
24
+ # jssm 5.162.12
25
25
 
26
26
  [**Try the live editor**](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html) ·
27
27
  [Documentation](https://stonecypher.github.io/jssm/docs/) ·
@@ -333,7 +333,7 @@ That decision shows up everywhere downstream:
333
333
  or run `npm run benny` against your own machine.
334
334
 
335
335
  - **More thoroughly tested than any other JavaScript state-machine
336
- library.** 8,475 tests at 100.0% line coverage
336
+ library.** 8,640 tests at 100.0% line coverage
337
337
  ([report](https://coveralls.io/github/StoneCypher/jssm)), plus
338
338
  fuzz testing via `fast-check`, with parser test data across ten natural
339
339
  languages and Emoji.
@@ -467,11 +467,11 @@ If your contribution is missing here, please open an issue.
467
467
 
468
468
  <br/>
469
469
 
470
- ***8,475 tests***, run 83,913 times.
470
+ ***8,640 tests***, run 98,037 times.
471
471
 
472
- - 7,713 specs with 100.0% coverage
473
- - 762 fuzz tests with 47.9% coverage
474
- - 10,882 TypeScript lines - 0.8 tests per line, 7.7 generated tests per line
472
+ - 7,737 specs with 100.0% coverage
473
+ - 903 fuzz tests with 56.5% coverage
474
+ - 10,946 TypeScript lines - 0.8 tests per line, 9.0 generated tests per line
475
475
 
476
476
  [![Actions Status](https://github.com/StoneCypher/jssm/workflows/Node%20CI/badge.svg)](https://github.com/StoneCypher/jssm/actions)
477
477
  [![NPM version](https://img.shields.io/npm/v/jssm.svg)](https://www.npmjs.com/package/jssm)
@@ -1,6 +1,6 @@
1
1
  type StateType = string;
2
2
  import { JssmGenericState, JssmGenericConfig, JssmStateConfig, JssmTransition, JssmTransitionList, // JssmTransitionRule,
3
- JssmMachineInternalState, JssmAllowsOverride, JssmAllowIslands, JssmEditorConfig, JssmStochasticOptions, JssmStochasticRun, JssmStochasticSummary, JssmDefaultSize, JssmStateDeclaration, JssmStateStyleKeyList, JssmTransitionConfig, JssmGraphConfig, JssmLayout, JssmHistory, JssmSerialization, FslDirection, FslTheme, HookDescription, HookHandler, HookContext, HookResult, HookComplexResult, EverythingHookContext, EverythingHookHandler, PostEverythingHookHandler, HookPhase, HookRegistryEntry, HookQuery, JssmEventName, JssmEventFilter, JssmEventHandler, JssmUnsubscribe, JssmGroupRegistry, JssmGroupHooks, JssmStateHooks, JssmRng } from './jssm_types.js';
3
+ JssmMachineInternalState, JssmAllowsOverride, JssmAllowIslands, JssmEditorConfig, JssmStochasticOptions, JssmStochasticRun, JssmStochasticSummary, JssmDefaultSize, JssmParsedSemver, JssmStateDeclaration, JssmStateStyleKeyList, JssmTransitionConfig, JssmGraphConfig, JssmLayout, JssmHistory, JssmSerialization, FslDirection, FslTheme, HookDescription, HookHandler, HookContext, HookResult, HookComplexResult, EverythingHookContext, EverythingHookHandler, PostEverythingHookHandler, HookPhase, HookRegistryEntry, HookQuery, JssmEventName, JssmEventFilter, JssmEventHandler, JssmUnsubscribe, JssmGroupRegistry, JssmGroupHooks, JssmStateHooks, JssmRng } from './jssm_types.js';
4
4
  import { Interner } from './jssm_intern.js';
5
5
  declare const shapes: string[], gviz_shapes: string[], named_colors: string[], state_name_chars: readonly {
6
6
  from: string;
@@ -58,10 +58,10 @@ declare class Machine<mDT> {
58
58
  _machine_language?: string;
59
59
  _machine_license?: string;
60
60
  _machine_name?: string;
61
- _machine_version?: string;
61
+ _machine_version?: JssmParsedSemver;
62
62
  _npm_name?: string;
63
63
  _default_size?: JssmDefaultSize;
64
- _fsl_version?: string;
64
+ _fsl_version?: JssmParsedSemver;
65
65
  _raw_state_declaration?: Array<object>;
66
66
  _state_declarations: Map<StateType, JssmStateDeclaration>;
67
67
  _data?: mDT;
@@ -684,10 +684,18 @@ declare class Machine<mDT> {
684
684
  */
685
685
  default_size(): JssmDefaultSize | undefined;
686
686
  /**
687
- * Get the machine's version string. Set via the FSL `machine_version` directive.
688
- * @returns The version string.
687
+ * Get the machine's declared version, parsed. Set via the FSL
688
+ * `machine_version` directive, which takes a semver triple; the parser
689
+ * breaks it into numeric `major`/`minor`/`patch` fields and keeps the
690
+ * exact source text in `full`. Returns `undefined` when the directive
691
+ * was not given.
692
+ * @returns The parsed {@link JssmParsedSemver}, or `undefined` if unset.
693
+ * @example
694
+ * const m = sm`machine_version: 1.2.3; a -> b;`;
695
+ * m.machine_version(); // => { major: 1, minor: 2, patch: 3, full: '1.2.3' }
696
+ * @see fsl_version
689
697
  */
690
- machine_version(): string;
698
+ machine_version(): JssmParsedSemver | undefined;
691
699
  /**
692
700
  * Get the raw state declaration objects as parsed from the FSL source.
693
701
  * @returns An array of raw state declaration objects.
@@ -705,10 +713,18 @@ declare class Machine<mDT> {
705
713
  */
706
714
  state_declarations(): Map<StateType, JssmStateDeclaration>;
707
715
  /**
708
- * Get the FSL language version this machine was compiled under.
709
- * @returns The FSL version string.
716
+ * Get the FSL language version this machine declares, parsed. Set via
717
+ * the FSL `fsl_version` directive, which takes a semver triple; the
718
+ * parser breaks it into numeric `major`/`minor`/`patch` fields and keeps
719
+ * the exact source text in `full`. Returns `undefined` when the
720
+ * directive was not given.
721
+ * @returns The parsed {@link JssmParsedSemver}, or `undefined` if unset.
722
+ * @example
723
+ * const m = sm`fsl_version: 1.0.0; a -> b;`;
724
+ * m.fsl_version(); // => { major: 1, minor: 0, patch: 0, full: '1.0.0' }
725
+ * @see machine_version
710
726
  */
711
- fsl_version(): string;
727
+ fsl_version(): JssmParsedSemver | undefined;
712
728
  /**
713
729
  * Get the complete internal state of the machine as a serializable
714
730
  * structure. Includes actions, edges, edge map, named transitions,
@@ -911,7 +927,19 @@ declare class Machine<mDT> {
911
927
  get themes(): FslTheme | FslTheme[];
912
928
  /**
913
929
  * Set the active theme(s). Accepts a single theme name or an array.
930
+ * Also drops every memoized static state config, so styles resolved
931
+ * before the change re-resolve under the new theme stack.
932
+ *
933
+ * ```typescript
934
+ * const m = sm`a -> b;`;
935
+ * m.style_for('b'); // resolved under the default theme
936
+ * m.themes = 'ocean';
937
+ * m.style_for('b').backgroundColor; // 'cadetblue1' — ocean, not a stale default
938
+ * ```
939
+ *
914
940
  * @param to - A theme name or array of theme names to apply.
941
+ *
942
+ * @see resolve_state_config
915
943
  */
916
944
  set themes(to: FslTheme | FslTheme[]);
917
945
  /**
@@ -1021,10 +1049,34 @@ declare class Machine<mDT> {
1021
1049
  * @throws {JssmError} If the state does not exist.
1022
1050
  */
1023
1051
  probable_exits_for(whichState: StateType): Array<JssmTransition<StateType, mDT>>;
1052
+ /**
1053
+ * Guard for the random-selection paths ({@link Machine.probabilistic_transition},
1054
+ * {@link Machine.stochastic_runs}): rejects a candidate pool whose total
1055
+ * selectable weight is zero, because weighted selection over an all-zero
1056
+ * pool has no meaningful answer (StoneCypher/fsl#1248). Undeclared
1057
+ * probabilities count as weight 1, matching {@link weighted_rand_select}.
1058
+ * An empty pool is not this guard's concern (terminality is handled by the
1059
+ * callers) and passes through untouched.
1060
+ *
1061
+ * ```typescript
1062
+ * const m = sm`a 0% -> b; a 0% -> c;`;
1063
+ * m.probabilistic_transition(); // throws JssmError — every exit is 0%
1064
+ * ```
1065
+ * @param whichState - The state the pool exits from, named in the error.
1066
+ * @param exits - The candidate pool, as built by {@link Machine.probable_exits_for}.
1067
+ * @throws {JssmError} If the pool is non-empty and every candidate edge
1068
+ * has probability 0 — including the case where explicit `0%` edges
1069
+ * excluded their unweighted sibling edges from the candidate pool.
1070
+ * @see probable_exits_for
1071
+ */
1072
+ private _assert_selectable_exit_pool;
1024
1073
  /**
1025
1074
  * Take a single random transition from the current state, weighted by
1026
1075
  * edge probabilities.
1027
1076
  * @returns `true` if a transition was taken, `false` otherwise.
1077
+ * @throws {JssmError} If the candidate exit pool is non-empty but its
1078
+ * total weight is zero — every candidate declares `0%` — per
1079
+ * StoneCypher/fsl#1248.
1028
1080
  */
1029
1081
  probabilistic_transition(): boolean;
1030
1082
  /**
@@ -1032,6 +1084,8 @@ declare class Machine<mDT> {
1032
1084
  * of states visited (before each transition).
1033
1085
  * @param n - Number of steps to walk.
1034
1086
  * @returns An array of state names visited during the walk.
1087
+ * @throws {JssmError} If a visited state's candidate exit pool is
1088
+ * non-empty but all-zero-weight (StoneCypher/fsl#1248).
1035
1089
  */
1036
1090
  probabilistic_walk(n: number): Array<StateType>;
1037
1091
  /**
@@ -1039,6 +1093,8 @@ declare class Machine<mDT> {
1039
1093
  * each state was visited.
1040
1094
  * @param n - Number of steps to walk.
1041
1095
  * @returns A `Map` from state name to visit count.
1096
+ * @throws {JssmError} If a visited state's candidate exit pool is
1097
+ * non-empty but all-zero-weight (StoneCypher/fsl#1248).
1042
1098
  */
1043
1099
  probabilistic_histo_walk(n: number): Map<StateType, number>;
1044
1100
  /**
@@ -1048,6 +1104,12 @@ declare class Machine<mDT> {
1048
1104
  * {@link Machine.transition}, so it fires no hooks, mutates no machine
1049
1105
  * state, and touches no `data`. A state with no probabilistic exits
1050
1106
  * (a terminal, or a forced-only `~>` state) ends the walk.
1107
+ *
1108
+ * Terminality is checked before the first transition and after every
1109
+ * transition. A terminal start therefore completes with length zero even
1110
+ * when `max_steps` is zero, and a terminal reached on the final permitted
1111
+ * transition is completed rather than step-capped.
1112
+ *
1051
1113
  * @param start - State to begin the walk from.
1052
1114
  * @param max_steps - Maximum transitions before the walk is step-capped.
1053
1115
  * @param exit_memo - Per-run-set cache of {@link Machine.probable_exits_for}
@@ -1058,6 +1120,9 @@ declare class Machine<mDT> {
1058
1120
  * the derived arrays — RNG draw order is untouched, so seeded walks
1059
1121
  * reproduce exactly.
1060
1122
  * @returns The {@link JssmStochasticRun} for this walk.
1123
+ * @throws {JssmError} If a visited state's candidate exit pool is
1124
+ * non-empty but all-zero-weight — see
1125
+ * {@link Machine._assert_selectable_exit_pool} (StoneCypher/fsl#1248).
1061
1126
  */
1062
1127
  private _stochastic_one_walk;
1063
1128
  /**
@@ -1067,7 +1132,9 @@ declare class Machine<mDT> {
1067
1132
  * current state, each ending at a terminal or after `max_steps`. In
1068
1133
  * `steady_state` mode yields exactly one walk of `max_steps` steps. This
1069
1134
  * is the lazy engine behind {@link Machine.stochastic_summary}; the
1070
- * fsl-stochastic panel drives it across animation frames.
1135
+ * fsl-stochastic panel drives it across animation frames. A walk already
1136
+ * at a terminal is reported as terminated with length zero, including when
1137
+ * `max_steps` is zero.
1071
1138
  *
1072
1139
  * Passing `seed` reseeds the machine for reproducible runs. Unlike
1073
1140
  * {@link Machine.stochastic_summary}, the generator does NOT restore the
@@ -1090,6 +1157,10 @@ declare class Machine<mDT> {
1090
1157
  * per-run `path_lengths`, `terminal_reached`, and `capped`; `steady_state`
1091
1158
  * mode runs one long walk and omits those fields.
1092
1159
  *
1160
+ * Monte-Carlo runs count as `terminal_reached` when they start at a
1161
+ * terminal or reach one on the final permitted transition. Terminal
1162
+ * starts contribute zero to `path_lengths`, even when `max_steps` is zero.
1163
+ *
1093
1164
  * Timing (`after`) decorations and data-guard conditions are not modeled
1094
1165
  * by this sampler; it walks the probabilistic graph topology.
1095
1166
  * @param opts - {@link JssmStochasticOptions}. `runs` defaults to the
@@ -2224,8 +2295,12 @@ declare class Machine<mDT> {
2224
2295
  * For any state OTHER than the current one, this returns the memoized static
2225
2296
  * resolution (tiers 1–5; see `_compose_state_config`) — theme →
2226
2297
  * `default_state_config` → per-kind defaults → depth-ordered group metadata →
2227
- * per-state config. The cache is keyed by state and never invalidated, since
2228
- * those tiers do not depend on which state is current.
2298
+ * per-state config. The cache is keyed by state; those tiers do not depend
2299
+ * on which state is current, so it survives transitions, but the mutable
2300
+ * cascade inputs each clear it when they change — hook registration and
2301
+ * removal ({@link Machine.set_hook}, {@link Machine.remove_hook}; the
2302
+ * hooked layer) and theme assignment (the `themes` setter; tier 1 and the
2303
+ * per-kind theme layers).
2229
2304
  *
2230
2305
  * For the machine's CURRENTLY-occupied state the result is recomputed each
2231
2306
  * call (never cached) and additionally carries the dynamic `active_state`