jssm 5.141.5 → 5.141.6

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.141.5 at 6/4/2026, 3:42:26 PM
21
+ * Generated for version 5.141.6 at 6/4/2026, 4:19:34 PM
22
22
 
23
23
  -->
24
- # jssm 5.141.5
24
+ # jssm 5.141.6
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/) ·
@@ -418,7 +418,7 @@ If your contribution is missing here, please open an issue.
418
418
 
419
419
  - 5,971 specs with 100.0% coverage
420
420
  - 513 fuzz tests with 3.4% coverage
421
- - 5,581 TypeScript lines - 1.2 tests per line, 10.3 generated tests per line
421
+ - 5,590 TypeScript lines - 1.2 tests per line, 10.2 generated tests per line
422
422
 
423
423
  [![Actions Status](https://github.com/StoneCypher/jssm/workflows/Node%20CI/badge.svg)](https://github.com/StoneCypher/jssm/actions)
424
424
  [![NPM version](https://img.shields.io/npm/v/jssm.svg)](https://www.npmjs.com/package/jssm)
@@ -21327,7 +21327,7 @@ var constants = /*#__PURE__*/Object.freeze({
21327
21327
  * Useful for runtime diagnostics and for embedding in serialized machine
21328
21328
  * snapshots so that deserializers can detect version-skew.
21329
21329
  */
21330
- const version = "5.141.5";
21330
+ const version = "5.141.6";
21331
21331
 
21332
21332
  // whargarbl lots of these return arrays could/should be sets
21333
21333
  const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
@@ -23848,6 +23848,44 @@ class Machine {
23848
23848
  throw new JssmError(this, "Code specifies no override, but config tries to permit; config may not be less strict than code");
23849
23849
  }
23850
23850
  }
23851
+ /*********
23852
+ *
23853
+ * Fire a `'rejection'` event caused by a hook vetoing a pending transition.
23854
+ * Extracted from the per-call closures inside {@link transition_impl} so
23855
+ * that it is allocated once at class-definition time rather than on every
23856
+ * hooked transition.
23857
+ *
23858
+ * @param hook_name Name of the hook that rejected (e.g. `'exit'`).
23859
+ * @param fromState State the machine was in when the transition was
23860
+ * attempted; used as the `from` field of the rejection event.
23861
+ * @param newState State that would have been entered had the hook
23862
+ * passed; used as the `to` field of the rejection event.
23863
+ * @param fromAction Action name when the transition was initiated by an
23864
+ * action call; `undefined` for plain state transitions.
23865
+ * @param oldData Machine data at the moment the transition was
23866
+ * attempted, before any hook mutations.
23867
+ * @param newData The `next_data` value passed to the transition call.
23868
+ * @param wasForced Whether the transition was attempted via
23869
+ * `force_transition`.
23870
+ *
23871
+ * @see transition_impl
23872
+ * @see _fire
23873
+ *
23874
+ * @internal
23875
+ *
23876
+ */
23877
+ _fire_hook_rejection(hook_name, fromState, newState, fromAction, oldData, newData, wasForced) {
23878
+ this._fire('rejection', {
23879
+ from: fromState,
23880
+ to: newState,
23881
+ action: fromAction,
23882
+ data: oldData,
23883
+ next_data: newData,
23884
+ reason: 'hook',
23885
+ hook_name,
23886
+ forced: wasForced
23887
+ });
23888
+ }
23851
23889
  /*********
23852
23890
  *
23853
23891
  * Shared transition core used by {@link transition}, {@link force_transition},
@@ -23956,75 +23994,68 @@ class Machine {
23956
23994
  if (this._has_hooks) {
23957
23995
  // once validity is known, clear old 'after' timeout clause
23958
23996
  this.clear_state_timeout();
23959
- function update_fields(res) {
23960
- if (res.hasOwnProperty('data')) {
23961
- hook_args.data = res.data;
23962
- hook_args.next_data = res.next_data;
23963
- data_changed = true;
23964
- }
23965
- }
23966
23997
  let data_changed = false;
23967
- const fire_rejection = (hook_name) => {
23968
- this._fire('rejection', {
23969
- from: fromState,
23970
- to: newState,
23971
- action: fromAction,
23972
- data: oldData,
23973
- next_data: newData,
23974
- reason: 'hook',
23975
- hook_name,
23976
- forced: wasForced
23977
- });
23978
- };
23979
23998
  // 0. pre everything hook (fires before all other pre-hooks)
23980
23999
  if (this._pre_everything_hook !== undefined) {
23981
24000
  const outcome = abstract_everything_hook_step(this._pre_everything_hook, Object.assign(Object.assign({}, hook_args), { hook_name: 'pre everything' }));
23982
24001
  if (outcome.pass === false) {
23983
- fire_rejection('pre everything');
24002
+ this._fire_hook_rejection('pre everything', fromState, newState, fromAction, oldData, newData, wasForced);
23984
24003
  return false;
23985
24004
  }
23986
- update_fields(outcome);
24005
+ if (_update_hook_fields(hook_args, outcome)) {
24006
+ data_changed = true;
24007
+ }
23987
24008
  }
23988
24009
  if (wasAction) {
23989
24010
  // 1a. any action hook
23990
24011
  const outcome = abstract_hook_step(this._any_action_hook, hook_args);
23991
24012
  if (outcome.pass === false) {
23992
- fire_rejection('any action');
24013
+ this._fire_hook_rejection('any action', fromState, newState, fromAction, oldData, newData, wasForced);
23993
24014
  return false;
23994
24015
  }
23995
- update_fields(outcome);
24016
+ if (_update_hook_fields(hook_args, outcome)) {
24017
+ data_changed = true;
24018
+ }
23996
24019
  // 1b. global specific action hook
23997
24020
  const outcome2 = abstract_hook_step(this._global_action_hooks.get(newStateOrAction), hook_args);
23998
24021
  if (outcome2.pass === false) {
23999
- fire_rejection('global action');
24022
+ this._fire_hook_rejection('global action', fromState, newState, fromAction, oldData, newData, wasForced);
24000
24023
  return false;
24001
24024
  }
24002
- update_fields(outcome2);
24025
+ if (_update_hook_fields(hook_args, outcome2)) {
24026
+ data_changed = true;
24027
+ }
24003
24028
  }
24004
24029
  // 2. after hook
24005
24030
  if (this._has_after_hooks) {
24006
24031
  const ah = this._after_hooks.get(newStateOrAction);
24007
24032
  const outcome = abstract_hook_step(ah, hook_args);
24008
- // there's no such thing as after not passing, so, omit the result pass check
24009
- update_fields(outcome);
24033
+ // there's no such thing as after not passing, so, omit the result pass check.
24034
+ // after hooks are post-exit and informational — their outcome never changes
24035
+ // data, so there's no data_changed branch here (it would be unreachable).
24036
+ _update_hook_fields(hook_args, outcome);
24010
24037
  }
24011
24038
  // 3. any transition hook
24012
24039
  if (this._any_transition_hook !== undefined) {
24013
24040
  const outcome = abstract_hook_step(this._any_transition_hook, hook_args);
24014
24041
  if (outcome.pass === false) {
24015
- fire_rejection('any transition');
24042
+ this._fire_hook_rejection('any transition', fromState, newState, fromAction, oldData, newData, wasForced);
24016
24043
  return false;
24017
24044
  }
24018
- update_fields(outcome);
24045
+ if (_update_hook_fields(hook_args, outcome)) {
24046
+ data_changed = true;
24047
+ }
24019
24048
  }
24020
24049
  // 4. exit hook
24021
24050
  if (this._has_exit_hooks) {
24022
24051
  const outcome = abstract_hook_step(this._exit_hooks.get(this._state), hook_args);
24023
24052
  if (outcome.pass === false) {
24024
- fire_rejection('exit');
24053
+ this._fire_hook_rejection('exit', fromState, newState, fromAction, oldData, newData, wasForced);
24025
24054
  return false;
24026
24055
  }
24027
- update_fields(outcome);
24056
+ if (_update_hook_fields(hook_args, outcome)) {
24057
+ data_changed = true;
24058
+ }
24028
24059
  }
24029
24060
  // 5. named transition / action hook
24030
24061
  if (this._has_named_hooks) {
@@ -24036,10 +24067,12 @@ class Machine {
24036
24067
  const nh = byAct === undefined ? undefined : byAct.get(newStateOrAction);
24037
24068
  const outcome = abstract_hook_step(nh, hook_args);
24038
24069
  if (outcome.pass === false) {
24039
- fire_rejection('named');
24070
+ this._fire_hook_rejection('named', fromState, newState, fromAction, oldData, newData, wasForced);
24040
24071
  return false;
24041
24072
  }
24042
- update_fields(outcome);
24073
+ if (_update_hook_fields(hook_args, outcome)) {
24074
+ data_changed = true;
24075
+ }
24043
24076
  }
24044
24077
  }
24045
24078
  // 6. regular hook
@@ -24049,56 +24082,68 @@ class Machine {
24049
24082
  const h = byTo === undefined ? undefined : byTo.get(newState);
24050
24083
  const outcome = abstract_hook_step(h, hook_args);
24051
24084
  if (outcome.pass === false) {
24052
- fire_rejection('hook');
24085
+ this._fire_hook_rejection('hook', fromState, newState, fromAction, oldData, newData, wasForced);
24053
24086
  return false;
24054
24087
  }
24055
- update_fields(outcome);
24088
+ if (_update_hook_fields(hook_args, outcome)) {
24089
+ data_changed = true;
24090
+ }
24056
24091
  }
24057
24092
  // 7. edge type hook
24058
24093
  // 7a. standard transition hook
24059
24094
  if (trans_type === 'legal') {
24060
24095
  const outcome = abstract_hook_step(this._standard_transition_hook, hook_args);
24061
24096
  if (outcome.pass === false) {
24062
- fire_rejection('standard transition');
24097
+ this._fire_hook_rejection('standard transition', fromState, newState, fromAction, oldData, newData, wasForced);
24063
24098
  return false;
24064
24099
  }
24065
- update_fields(outcome);
24100
+ if (_update_hook_fields(hook_args, outcome)) {
24101
+ data_changed = true;
24102
+ }
24066
24103
  }
24067
24104
  // 7b. main type hook
24068
24105
  if (trans_type === 'main') {
24069
24106
  const outcome = abstract_hook_step(this._main_transition_hook, hook_args);
24070
24107
  if (outcome.pass === false) {
24071
- fire_rejection('main transition');
24108
+ this._fire_hook_rejection('main transition', fromState, newState, fromAction, oldData, newData, wasForced);
24072
24109
  return false;
24073
24110
  }
24074
- update_fields(outcome);
24111
+ if (_update_hook_fields(hook_args, outcome)) {
24112
+ data_changed = true;
24113
+ }
24075
24114
  }
24076
24115
  // 7c. forced transition hook
24077
24116
  if (trans_type === 'forced') {
24078
24117
  const outcome = abstract_hook_step(this._forced_transition_hook, hook_args);
24079
24118
  if (outcome.pass === false) {
24080
- fire_rejection('forced transition');
24119
+ this._fire_hook_rejection('forced transition', fromState, newState, fromAction, oldData, newData, wasForced);
24081
24120
  return false;
24082
24121
  }
24083
- update_fields(outcome);
24122
+ if (_update_hook_fields(hook_args, outcome)) {
24123
+ data_changed = true;
24124
+ }
24084
24125
  }
24085
24126
  // 8. entry hook
24086
24127
  if (this._has_entry_hooks) {
24087
24128
  const outcome = abstract_hook_step(this._entry_hooks.get(newState), hook_args);
24088
24129
  if (outcome.pass === false) {
24089
- fire_rejection('entry');
24130
+ this._fire_hook_rejection('entry', fromState, newState, fromAction, oldData, newData, wasForced);
24090
24131
  return false;
24091
24132
  }
24092
- update_fields(outcome);
24133
+ if (_update_hook_fields(hook_args, outcome)) {
24134
+ data_changed = true;
24135
+ }
24093
24136
  }
24094
24137
  // 9. everything hook (fires after all other pre-hooks)
24095
24138
  if (this._everything_hook !== undefined) {
24096
24139
  const outcome = abstract_everything_hook_step(this._everything_hook, Object.assign(Object.assign({}, hook_args), { hook_name: 'everything' }));
24097
24140
  if (outcome.pass === false) {
24098
- fire_rejection('everything');
24141
+ this._fire_hook_rejection('everything', fromState, newState, fromAction, oldData, newData, wasForced);
24099
24142
  return false;
24100
24143
  }
24101
- update_fields(outcome);
24144
+ if (_update_hook_fields(hook_args, outcome)) {
24145
+ data_changed = true;
24146
+ }
24102
24147
  }
24103
24148
  // all hooks passed! let's now establish the result
24104
24149
  if (this._history_length) {
@@ -25024,6 +25069,46 @@ function is_hook_complex_result(hr) {
25024
25069
  }
25025
25070
  return false;
25026
25071
  }
25072
+ /**
25073
+ *
25074
+ * Apply any data-field updates from a hook's complex result into `hook_args`,
25075
+ * and return whether data actually changed.
25076
+ *
25077
+ * This is the hoisted, allocation-free replacement for the `update_fields`
25078
+ * inner function that used to be re-created on every hooked transition inside
25079
+ * {@link Machine.transition_impl}. By moving it to module scope the function
25080
+ * object is allocated once at module load time.
25081
+ *
25082
+ * When the result does not carry a `data` property (the common case —
25083
+ * most hooks return `true` or `undefined`) the function returns `false`
25084
+ * immediately without touching `hook_args`.
25085
+ *
25086
+ * ```typescript
25087
+ * const args = { data: 'old', next_data: undefined, ... };
25088
+ * const changed = _update_hook_fields(args, { pass: true, data: 'new', next_data: undefined });
25089
+ * // changed === true, args.data === 'new'
25090
+ * ```
25091
+ *
25092
+ * @param hook_args The shared hook-argument object for the current
25093
+ * transition. Mutated in-place when the result carries `data`.
25094
+ * @param res The normalised complex result returned by
25095
+ * {@link abstract_hook_step} or {@link abstract_everything_hook_step}.
25096
+ *
25097
+ * @returns `true` if `res` contained a `data` property (i.e. the hook
25098
+ * mutated the machine's data); `false` otherwise.
25099
+ *
25100
+ * @see Machine.transition_impl
25101
+ * @see abstract_hook_step
25102
+ *
25103
+ */
25104
+ function _update_hook_fields(hook_args, res) {
25105
+ if (Object.prototype.hasOwnProperty.call(res, 'data')) {
25106
+ hook_args.data = res.data;
25107
+ hook_args.next_data = res.next_data;
25108
+ return true;
25109
+ }
25110
+ return false;
25111
+ }
25027
25112
  /**
25028
25113
  *
25029
25114
  * Invoke an optional transition/action hook and normalize its return value
package/dist/cdn/viz.js CHANGED
@@ -21352,7 +21352,7 @@ var constants = /*#__PURE__*/Object.freeze({
21352
21352
  * Useful for runtime diagnostics and for embedding in serialized machine
21353
21353
  * snapshots so that deserializers can detect version-skew.
21354
21354
  */
21355
- const version = "5.141.5";
21355
+ const version = "5.141.6";
21356
21356
 
21357
21357
  // whargarbl lots of these return arrays could/should be sets
21358
21358
  const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
@@ -23873,6 +23873,44 @@ class Machine {
23873
23873
  throw new JssmError(this, "Code specifies no override, but config tries to permit; config may not be less strict than code");
23874
23874
  }
23875
23875
  }
23876
+ /*********
23877
+ *
23878
+ * Fire a `'rejection'` event caused by a hook vetoing a pending transition.
23879
+ * Extracted from the per-call closures inside {@link transition_impl} so
23880
+ * that it is allocated once at class-definition time rather than on every
23881
+ * hooked transition.
23882
+ *
23883
+ * @param hook_name Name of the hook that rejected (e.g. `'exit'`).
23884
+ * @param fromState State the machine was in when the transition was
23885
+ * attempted; used as the `from` field of the rejection event.
23886
+ * @param newState State that would have been entered had the hook
23887
+ * passed; used as the `to` field of the rejection event.
23888
+ * @param fromAction Action name when the transition was initiated by an
23889
+ * action call; `undefined` for plain state transitions.
23890
+ * @param oldData Machine data at the moment the transition was
23891
+ * attempted, before any hook mutations.
23892
+ * @param newData The `next_data` value passed to the transition call.
23893
+ * @param wasForced Whether the transition was attempted via
23894
+ * `force_transition`.
23895
+ *
23896
+ * @see transition_impl
23897
+ * @see _fire
23898
+ *
23899
+ * @internal
23900
+ *
23901
+ */
23902
+ _fire_hook_rejection(hook_name, fromState, newState, fromAction, oldData, newData, wasForced) {
23903
+ this._fire('rejection', {
23904
+ from: fromState,
23905
+ to: newState,
23906
+ action: fromAction,
23907
+ data: oldData,
23908
+ next_data: newData,
23909
+ reason: 'hook',
23910
+ hook_name,
23911
+ forced: wasForced
23912
+ });
23913
+ }
23876
23914
  /*********
23877
23915
  *
23878
23916
  * Shared transition core used by {@link transition}, {@link force_transition},
@@ -23981,75 +24019,68 @@ class Machine {
23981
24019
  if (this._has_hooks) {
23982
24020
  // once validity is known, clear old 'after' timeout clause
23983
24021
  this.clear_state_timeout();
23984
- function update_fields(res) {
23985
- if (res.hasOwnProperty('data')) {
23986
- hook_args.data = res.data;
23987
- hook_args.next_data = res.next_data;
23988
- data_changed = true;
23989
- }
23990
- }
23991
24022
  let data_changed = false;
23992
- const fire_rejection = (hook_name) => {
23993
- this._fire('rejection', {
23994
- from: fromState,
23995
- to: newState,
23996
- action: fromAction,
23997
- data: oldData,
23998
- next_data: newData,
23999
- reason: 'hook',
24000
- hook_name,
24001
- forced: wasForced
24002
- });
24003
- };
24004
24023
  // 0. pre everything hook (fires before all other pre-hooks)
24005
24024
  if (this._pre_everything_hook !== undefined) {
24006
24025
  const outcome = abstract_everything_hook_step(this._pre_everything_hook, Object.assign(Object.assign({}, hook_args), { hook_name: 'pre everything' }));
24007
24026
  if (outcome.pass === false) {
24008
- fire_rejection('pre everything');
24027
+ this._fire_hook_rejection('pre everything', fromState, newState, fromAction, oldData, newData, wasForced);
24009
24028
  return false;
24010
24029
  }
24011
- update_fields(outcome);
24030
+ if (_update_hook_fields(hook_args, outcome)) {
24031
+ data_changed = true;
24032
+ }
24012
24033
  }
24013
24034
  if (wasAction) {
24014
24035
  // 1a. any action hook
24015
24036
  const outcome = abstract_hook_step(this._any_action_hook, hook_args);
24016
24037
  if (outcome.pass === false) {
24017
- fire_rejection('any action');
24038
+ this._fire_hook_rejection('any action', fromState, newState, fromAction, oldData, newData, wasForced);
24018
24039
  return false;
24019
24040
  }
24020
- update_fields(outcome);
24041
+ if (_update_hook_fields(hook_args, outcome)) {
24042
+ data_changed = true;
24043
+ }
24021
24044
  // 1b. global specific action hook
24022
24045
  const outcome2 = abstract_hook_step(this._global_action_hooks.get(newStateOrAction), hook_args);
24023
24046
  if (outcome2.pass === false) {
24024
- fire_rejection('global action');
24047
+ this._fire_hook_rejection('global action', fromState, newState, fromAction, oldData, newData, wasForced);
24025
24048
  return false;
24026
24049
  }
24027
- update_fields(outcome2);
24050
+ if (_update_hook_fields(hook_args, outcome2)) {
24051
+ data_changed = true;
24052
+ }
24028
24053
  }
24029
24054
  // 2. after hook
24030
24055
  if (this._has_after_hooks) {
24031
24056
  const ah = this._after_hooks.get(newStateOrAction);
24032
24057
  const outcome = abstract_hook_step(ah, hook_args);
24033
- // there's no such thing as after not passing, so, omit the result pass check
24034
- update_fields(outcome);
24058
+ // there's no such thing as after not passing, so, omit the result pass check.
24059
+ // after hooks are post-exit and informational — their outcome never changes
24060
+ // data, so there's no data_changed branch here (it would be unreachable).
24061
+ _update_hook_fields(hook_args, outcome);
24035
24062
  }
24036
24063
  // 3. any transition hook
24037
24064
  if (this._any_transition_hook !== undefined) {
24038
24065
  const outcome = abstract_hook_step(this._any_transition_hook, hook_args);
24039
24066
  if (outcome.pass === false) {
24040
- fire_rejection('any transition');
24067
+ this._fire_hook_rejection('any transition', fromState, newState, fromAction, oldData, newData, wasForced);
24041
24068
  return false;
24042
24069
  }
24043
- update_fields(outcome);
24070
+ if (_update_hook_fields(hook_args, outcome)) {
24071
+ data_changed = true;
24072
+ }
24044
24073
  }
24045
24074
  // 4. exit hook
24046
24075
  if (this._has_exit_hooks) {
24047
24076
  const outcome = abstract_hook_step(this._exit_hooks.get(this._state), hook_args);
24048
24077
  if (outcome.pass === false) {
24049
- fire_rejection('exit');
24078
+ this._fire_hook_rejection('exit', fromState, newState, fromAction, oldData, newData, wasForced);
24050
24079
  return false;
24051
24080
  }
24052
- update_fields(outcome);
24081
+ if (_update_hook_fields(hook_args, outcome)) {
24082
+ data_changed = true;
24083
+ }
24053
24084
  }
24054
24085
  // 5. named transition / action hook
24055
24086
  if (this._has_named_hooks) {
@@ -24061,10 +24092,12 @@ class Machine {
24061
24092
  const nh = byAct === undefined ? undefined : byAct.get(newStateOrAction);
24062
24093
  const outcome = abstract_hook_step(nh, hook_args);
24063
24094
  if (outcome.pass === false) {
24064
- fire_rejection('named');
24095
+ this._fire_hook_rejection('named', fromState, newState, fromAction, oldData, newData, wasForced);
24065
24096
  return false;
24066
24097
  }
24067
- update_fields(outcome);
24098
+ if (_update_hook_fields(hook_args, outcome)) {
24099
+ data_changed = true;
24100
+ }
24068
24101
  }
24069
24102
  }
24070
24103
  // 6. regular hook
@@ -24074,56 +24107,68 @@ class Machine {
24074
24107
  const h = byTo === undefined ? undefined : byTo.get(newState);
24075
24108
  const outcome = abstract_hook_step(h, hook_args);
24076
24109
  if (outcome.pass === false) {
24077
- fire_rejection('hook');
24110
+ this._fire_hook_rejection('hook', fromState, newState, fromAction, oldData, newData, wasForced);
24078
24111
  return false;
24079
24112
  }
24080
- update_fields(outcome);
24113
+ if (_update_hook_fields(hook_args, outcome)) {
24114
+ data_changed = true;
24115
+ }
24081
24116
  }
24082
24117
  // 7. edge type hook
24083
24118
  // 7a. standard transition hook
24084
24119
  if (trans_type === 'legal') {
24085
24120
  const outcome = abstract_hook_step(this._standard_transition_hook, hook_args);
24086
24121
  if (outcome.pass === false) {
24087
- fire_rejection('standard transition');
24122
+ this._fire_hook_rejection('standard transition', fromState, newState, fromAction, oldData, newData, wasForced);
24088
24123
  return false;
24089
24124
  }
24090
- update_fields(outcome);
24125
+ if (_update_hook_fields(hook_args, outcome)) {
24126
+ data_changed = true;
24127
+ }
24091
24128
  }
24092
24129
  // 7b. main type hook
24093
24130
  if (trans_type === 'main') {
24094
24131
  const outcome = abstract_hook_step(this._main_transition_hook, hook_args);
24095
24132
  if (outcome.pass === false) {
24096
- fire_rejection('main transition');
24133
+ this._fire_hook_rejection('main transition', fromState, newState, fromAction, oldData, newData, wasForced);
24097
24134
  return false;
24098
24135
  }
24099
- update_fields(outcome);
24136
+ if (_update_hook_fields(hook_args, outcome)) {
24137
+ data_changed = true;
24138
+ }
24100
24139
  }
24101
24140
  // 7c. forced transition hook
24102
24141
  if (trans_type === 'forced') {
24103
24142
  const outcome = abstract_hook_step(this._forced_transition_hook, hook_args);
24104
24143
  if (outcome.pass === false) {
24105
- fire_rejection('forced transition');
24144
+ this._fire_hook_rejection('forced transition', fromState, newState, fromAction, oldData, newData, wasForced);
24106
24145
  return false;
24107
24146
  }
24108
- update_fields(outcome);
24147
+ if (_update_hook_fields(hook_args, outcome)) {
24148
+ data_changed = true;
24149
+ }
24109
24150
  }
24110
24151
  // 8. entry hook
24111
24152
  if (this._has_entry_hooks) {
24112
24153
  const outcome = abstract_hook_step(this._entry_hooks.get(newState), hook_args);
24113
24154
  if (outcome.pass === false) {
24114
- fire_rejection('entry');
24155
+ this._fire_hook_rejection('entry', fromState, newState, fromAction, oldData, newData, wasForced);
24115
24156
  return false;
24116
24157
  }
24117
- update_fields(outcome);
24158
+ if (_update_hook_fields(hook_args, outcome)) {
24159
+ data_changed = true;
24160
+ }
24118
24161
  }
24119
24162
  // 9. everything hook (fires after all other pre-hooks)
24120
24163
  if (this._everything_hook !== undefined) {
24121
24164
  const outcome = abstract_everything_hook_step(this._everything_hook, Object.assign(Object.assign({}, hook_args), { hook_name: 'everything' }));
24122
24165
  if (outcome.pass === false) {
24123
- fire_rejection('everything');
24166
+ this._fire_hook_rejection('everything', fromState, newState, fromAction, oldData, newData, wasForced);
24124
24167
  return false;
24125
24168
  }
24126
- update_fields(outcome);
24169
+ if (_update_hook_fields(hook_args, outcome)) {
24170
+ data_changed = true;
24171
+ }
24127
24172
  }
24128
24173
  // all hooks passed! let's now establish the result
24129
24174
  if (this._history_length) {
@@ -25049,6 +25094,46 @@ function is_hook_complex_result(hr) {
25049
25094
  }
25050
25095
  return false;
25051
25096
  }
25097
+ /**
25098
+ *
25099
+ * Apply any data-field updates from a hook's complex result into `hook_args`,
25100
+ * and return whether data actually changed.
25101
+ *
25102
+ * This is the hoisted, allocation-free replacement for the `update_fields`
25103
+ * inner function that used to be re-created on every hooked transition inside
25104
+ * {@link Machine.transition_impl}. By moving it to module scope the function
25105
+ * object is allocated once at module load time.
25106
+ *
25107
+ * When the result does not carry a `data` property (the common case —
25108
+ * most hooks return `true` or `undefined`) the function returns `false`
25109
+ * immediately without touching `hook_args`.
25110
+ *
25111
+ * ```typescript
25112
+ * const args = { data: 'old', next_data: undefined, ... };
25113
+ * const changed = _update_hook_fields(args, { pass: true, data: 'new', next_data: undefined });
25114
+ * // changed === true, args.data === 'new'
25115
+ * ```
25116
+ *
25117
+ * @param hook_args The shared hook-argument object for the current
25118
+ * transition. Mutated in-place when the result carries `data`.
25119
+ * @param res The normalised complex result returned by
25120
+ * {@link abstract_hook_step} or {@link abstract_everything_hook_step}.
25121
+ *
25122
+ * @returns `true` if `res` contained a `data` property (i.e. the hook
25123
+ * mutated the machine's data); `false` otherwise.
25124
+ *
25125
+ * @see Machine.transition_impl
25126
+ * @see abstract_hook_step
25127
+ *
25128
+ */
25129
+ function _update_hook_fields(hook_args, res) {
25130
+ if (Object.prototype.hasOwnProperty.call(res, 'data')) {
25131
+ hook_args.data = res.data;
25132
+ hook_args.next_data = res.next_data;
25133
+ return true;
25134
+ }
25135
+ return false;
25136
+ }
25052
25137
  /**
25053
25138
  *
25054
25139
  * Invoke an optional transition/action hook and normalize its return value