jssm 5.143.31 → 5.143.34

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
@@ -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.143.31 at 6/16/2026, 10:07:31 PM
21
+ * Generated for version 5.143.34 at 6/17/2026, 9:26:23 PM
22
22
 
23
23
  -->
24
- # jssm 5.143.31
24
+ # jssm 5.143.34
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/) ·
@@ -312,7 +312,7 @@ That decision shows up everywhere downstream:
312
312
  or run `npm run benny` against your own machine.
313
313
 
314
314
  - **More thoroughly tested than any other JavaScript state-machine
315
- library.** 7,228 tests at 100.0% line coverage
315
+ library.** 7,231 tests at 100.0% line coverage
316
316
  ([report](https://coveralls.io/github/StoneCypher/jssm)), plus
317
317
  fuzz testing via `fast-check`, with parser test data across ten natural
318
318
  languages and Emoji.
@@ -445,11 +445,11 @@ If your contribution is missing here, please open an issue.
445
445
 
446
446
  <br/>
447
447
 
448
- ***7,228 tests***, run 82,270 times.
448
+ ***7,231 tests***, run 82,273 times.
449
449
 
450
- - 6,470 specs with 100.0% coverage
451
- - 758 fuzz tests with 76.7% coverage
452
- - 6,610 TypeScript lines - 1.1 tests per line, 12.4 generated tests per line
450
+ - 6,473 specs with 100.0% coverage
451
+ - 758 fuzz tests with 76.8% coverage
452
+ - 6,605 TypeScript lines - 1.1 tests per line, 12.5 generated tests per line
453
453
 
454
454
  [![Actions Status](https://github.com/StoneCypher/jssm/workflows/Node%20CI/badge.svg)](https://github.com/StoneCypher/jssm/actions)
455
455
  [![NPM version](https://img.shields.io/npm/v/jssm.svg)](https://www.npmjs.com/package/jssm)
@@ -23486,7 +23486,7 @@ var constants = /*#__PURE__*/Object.freeze({
23486
23486
  * Useful for runtime diagnostics and for embedding in serialized machine
23487
23487
  * snapshots so that deserializers can detect version-skew.
23488
23488
  */
23489
- const version = "5.143.31";
23489
+ const version = "5.143.34";
23490
23490
 
23491
23491
  // whargarbl lots of these return arrays could/should be sets
23492
23492
  const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
@@ -26161,10 +26161,31 @@ class Machine {
26161
26161
  this.set_hook({ kind: 'exit', from, handler });
26162
26162
  return this;
26163
26163
  }
26164
- /** Register a hook that fires after leaving a specific state (post-exit).
26165
- * @param from - The state that was exited.
26166
- * @param handler - Callback invoked after exit completes.
26164
+ /** Register a hook that fires when a state's `after` timer elapses the
26165
+ * delay-over companion to `a after 5s -> b;` style time transitions. It
26166
+ * does NOT fire when the state is entered or left by ordinary dispatch;
26167
+ * use {@link hook_entry} / {@link hook_exit} for those. (Versions through
26168
+ * 5.143.28 also spuriously fired it on entering the state, the jssm side
26169
+ * of StoneCypher/fsl#1327.)
26170
+ * @param from - The state whose `after` timer is being watched.
26171
+ * @param handler - Callback invoked when the timer fires, just before the
26172
+ * timed transition is taken; informational — its outcome
26173
+ * cannot reject the transition.
26167
26174
  * @returns `this` for chaining.
26175
+ *
26176
+ * @example
26177
+ * const m = sm`a after 1000 -> b; a -> c; c -> a;`;
26178
+ * let calls = 0;
26179
+ * m.hook_after('a', () => { calls += 1; });
26180
+ * m.go('c');
26181
+ * m.go('a');
26182
+ * // ordinary dispatch never fires it; only the timer elapsing does:
26183
+ * calls; // => 0
26184
+ * m.clear_state_timeout();
26185
+ *
26186
+ * @see hook_entry
26187
+ * @see hook_exit
26188
+ * @see set_state_timeout
26168
26189
  */
26169
26190
  hook_after(from, handler) {
26170
26191
  this.set_hook({ kind: 'after', from, handler });
@@ -26774,15 +26795,12 @@ class Machine {
26774
26795
  data_changed = true;
26775
26796
  }
26776
26797
  }
26777
- // 2. after hook
26778
- if (this._has_after_hooks) {
26779
- const ah = this._after_hooks.get(newStateOrAction);
26780
- const outcome = abstract_hook_step(ah, hook_args);
26781
- // there's no such thing as after not passing, so, omit the result pass check.
26782
- // after hooks are post-exit and informational — their outcome never changes
26783
- // data, so there's no data_changed branch here (it would be unreachable).
26784
- _update_hook_fields(hook_args, outcome);
26785
- }
26798
+ // 2. (removed) After hooks do NOT fire on dispatch. They are the
26799
+ // `after`-timer's companion (fsl#698: "delay over!") and fire only from
26800
+ // the state-timeout path. Through v5.143.28 a probe here keyed on
26801
+ // newStateOrAction spuriously fired them on entering the hooked state —
26802
+ // or on a same-named action making one timer elapse read as two
26803
+ // handler calls (StoneCypher/fsl#1327).
26786
26804
  // 3. any transition hook
26787
26805
  if (this._any_transition_hook !== undefined) {
26788
26806
  const outcome = abstract_hook_step(this._any_transition_hook, hook_args);
package/dist/cdn/viz.js CHANGED
@@ -23511,7 +23511,7 @@ var constants = /*#__PURE__*/Object.freeze({
23511
23511
  * Useful for runtime diagnostics and for embedding in serialized machine
23512
23512
  * snapshots so that deserializers can detect version-skew.
23513
23513
  */
23514
- const version = "5.143.31";
23514
+ const version = "5.143.34";
23515
23515
 
23516
23516
  // whargarbl lots of these return arrays could/should be sets
23517
23517
  const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
@@ -26186,10 +26186,31 @@ class Machine {
26186
26186
  this.set_hook({ kind: 'exit', from, handler });
26187
26187
  return this;
26188
26188
  }
26189
- /** Register a hook that fires after leaving a specific state (post-exit).
26190
- * @param from - The state that was exited.
26191
- * @param handler - Callback invoked after exit completes.
26189
+ /** Register a hook that fires when a state's `after` timer elapses the
26190
+ * delay-over companion to `a after 5s -> b;` style time transitions. It
26191
+ * does NOT fire when the state is entered or left by ordinary dispatch;
26192
+ * use {@link hook_entry} / {@link hook_exit} for those. (Versions through
26193
+ * 5.143.28 also spuriously fired it on entering the state, the jssm side
26194
+ * of StoneCypher/fsl#1327.)
26195
+ * @param from - The state whose `after` timer is being watched.
26196
+ * @param handler - Callback invoked when the timer fires, just before the
26197
+ * timed transition is taken; informational — its outcome
26198
+ * cannot reject the transition.
26192
26199
  * @returns `this` for chaining.
26200
+ *
26201
+ * @example
26202
+ * const m = sm`a after 1000 -> b; a -> c; c -> a;`;
26203
+ * let calls = 0;
26204
+ * m.hook_after('a', () => { calls += 1; });
26205
+ * m.go('c');
26206
+ * m.go('a');
26207
+ * // ordinary dispatch never fires it; only the timer elapsing does:
26208
+ * calls; // => 0
26209
+ * m.clear_state_timeout();
26210
+ *
26211
+ * @see hook_entry
26212
+ * @see hook_exit
26213
+ * @see set_state_timeout
26193
26214
  */
26194
26215
  hook_after(from, handler) {
26195
26216
  this.set_hook({ kind: 'after', from, handler });
@@ -26799,15 +26820,12 @@ class Machine {
26799
26820
  data_changed = true;
26800
26821
  }
26801
26822
  }
26802
- // 2. after hook
26803
- if (this._has_after_hooks) {
26804
- const ah = this._after_hooks.get(newStateOrAction);
26805
- const outcome = abstract_hook_step(ah, hook_args);
26806
- // there's no such thing as after not passing, so, omit the result pass check.
26807
- // after hooks are post-exit and informational — their outcome never changes
26808
- // data, so there's no data_changed branch here (it would be unreachable).
26809
- _update_hook_fields(hook_args, outcome);
26810
- }
26823
+ // 2. (removed) After hooks do NOT fire on dispatch. They are the
26824
+ // `after`-timer's companion (fsl#698: "delay over!") and fire only from
26825
+ // the state-timeout path. Through v5.143.28 a probe here keyed on
26826
+ // newStateOrAction spuriously fired them on entering the hooked state —
26827
+ // or on a same-named action making one timer elapse read as two
26828
+ // handler calls (StoneCypher/fsl#1327).
26811
26829
  // 3. any transition hook
26812
26830
  if (this._any_transition_hook !== undefined) {
26813
26831
  const outcome = abstract_hook_step(this._any_transition_hook, hook_args);