jssm 5.141.4 → 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/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.4";
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;
@@ -23071,6 +23071,62 @@ class Machine {
23071
23071
  this._event_listener_count++;
23072
23072
  return () => { this._unsubscribe_entry(set, entry); };
23073
23073
  }
23074
+ /**
23075
+ * Invoke a single event-handler entry, respecting its filter, once-removal
23076
+ * semantics, and the error re-fire / recursion-guard logic. Extracted so
23077
+ * {@link _fire} can share identical behavior between the size-1 fast-path
23078
+ * and the general snapshotted loop.
23079
+ *
23080
+ * @param entry - The subscriber descriptor to invoke.
23081
+ * @param set - The live Set that owns `entry`; needed for once-removal.
23082
+ * @param name - The event name being dispatched (used in error re-fires).
23083
+ * @param detail - The event payload forwarded to the handler.
23084
+ *
23085
+ * @internal
23086
+ */
23087
+ _fire_one(entry, set, name, detail) {
23088
+ // filter check
23089
+ if (entry.filter !== undefined) {
23090
+ for (const k of Object.keys(entry.filter)) {
23091
+ if (entry.filter[k] !== detail[k]) {
23092
+ return;
23093
+ }
23094
+ }
23095
+ }
23096
+ // once removal happens BEFORE invocation so a throwing handler still
23097
+ // gets removed and so re-entrant `on` calls during the handler see
23098
+ // the post-removal state.
23099
+ if (entry.once) {
23100
+ this._unsubscribe_entry(set, entry);
23101
+ }
23102
+ try {
23103
+ entry.handler(detail);
23104
+ }
23105
+ catch (err) {
23106
+ if (name === 'error' || this._firing_error) {
23107
+ // surface to stderr as a last resort but never recurse;
23108
+ // `console` is in the JS standard library and present in every
23109
+ // supported runtime, so guarding it would just add an untestable
23110
+ // branch. See #638.
23111
+ // eslint-disable-next-line no-console
23112
+ console.error(err);
23113
+ }
23114
+ else {
23115
+ this._firing_error = true;
23116
+ try {
23117
+ this._fire('error', {
23118
+ error: err,
23119
+ source_event: name,
23120
+ source_detail: detail,
23121
+ handler: entry.handler
23122
+ });
23123
+ }
23124
+ finally {
23125
+ this._firing_error = false;
23126
+ }
23127
+ }
23128
+ }
23129
+ }
23074
23130
  /**
23075
23131
  * Dispatch an event to every registered subscriber in registration
23076
23132
  * order. Filters are checked first; non-matching handlers are skipped
@@ -23082,6 +23138,11 @@ class Machine {
23082
23138
  * handler throws, the new exception is swallowed rather than rebroadcast
23083
23139
  * to avoid an infinite loop.
23084
23140
  *
23141
+ * When exactly one subscriber is registered the common case avoids the
23142
+ * `Array.from(set)` snapshot allocation by capturing the lone entry into a
23143
+ * local first — equivalent to a 1-element snapshot but allocation-free.
23144
+ * The general path still snapshots for re-entrancy safety.
23145
+ *
23085
23146
  * @internal
23086
23147
  */
23087
23148
  _fire(name, detail) {
@@ -23089,55 +23150,19 @@ class Machine {
23089
23150
  if (set === undefined || set.size === 0) {
23090
23151
  return;
23091
23152
  }
23092
- // Snapshot so handlers can `off()` mid-loop without disturbing iteration.
23153
+ // Fast-path: single subscriber capture entry before invoking so that
23154
+ // even if the handler mutates `set` (via off/once auto-removal) we hold a
23155
+ // stable reference. Behaviorally identical to a 1-element snapshot.
23156
+ if (set.size === 1) {
23157
+ const only = set.values().next().value;
23158
+ this._fire_one(only, set, name, detail);
23159
+ return;
23160
+ }
23161
+ // General path: snapshot so handlers can `off()` mid-loop without
23162
+ // disturbing iteration.
23093
23163
  const entries = Array.from(set);
23094
23164
  for (const entry of entries) {
23095
- // filter check
23096
- if (entry.filter !== undefined) {
23097
- let matched = true;
23098
- for (const k of Object.keys(entry.filter)) {
23099
- if (entry.filter[k] !== detail[k]) {
23100
- matched = false;
23101
- break;
23102
- }
23103
- }
23104
- if (!matched) {
23105
- continue;
23106
- }
23107
- }
23108
- // once removal happens BEFORE invocation so a throwing handler still
23109
- // gets removed and so re-entrant `on` calls during the handler see
23110
- // the post-removal state.
23111
- if (entry.once) {
23112
- this._unsubscribe_entry(set, entry);
23113
- }
23114
- try {
23115
- entry.handler(detail);
23116
- }
23117
- catch (err) {
23118
- if (name === 'error' || this._firing_error) {
23119
- // surface to stderr as a last resort but never recurse;
23120
- // `console` is in the JS standard library and present in every
23121
- // supported runtime, so guarding it would just add an untestable
23122
- // branch. See #638.
23123
- // eslint-disable-next-line no-console
23124
- console.error(err);
23125
- }
23126
- else {
23127
- this._firing_error = true;
23128
- try {
23129
- this._fire('error', {
23130
- error: err,
23131
- source_event: name,
23132
- source_detail: detail,
23133
- handler: entry.handler
23134
- });
23135
- }
23136
- finally {
23137
- this._firing_error = false;
23138
- }
23139
- }
23140
- }
23165
+ this._fire_one(entry, set, name, detail);
23141
23166
  }
23142
23167
  }
23143
23168
  /** Low-level hook registration. Installs a handler described by a
@@ -23848,6 +23873,44 @@ class Machine {
23848
23873
  throw new JssmError(this, "Code specifies no override, but config tries to permit; config may not be less strict than code");
23849
23874
  }
23850
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
+ }
23851
23914
  /*********
23852
23915
  *
23853
23916
  * Shared transition core used by {@link transition}, {@link force_transition},
@@ -23956,75 +24019,68 @@ class Machine {
23956
24019
  if (this._has_hooks) {
23957
24020
  // once validity is known, clear old 'after' timeout clause
23958
24021
  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
24022
  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
24023
  // 0. pre everything hook (fires before all other pre-hooks)
23980
24024
  if (this._pre_everything_hook !== undefined) {
23981
24025
  const outcome = abstract_everything_hook_step(this._pre_everything_hook, Object.assign(Object.assign({}, hook_args), { hook_name: 'pre everything' }));
23982
24026
  if (outcome.pass === false) {
23983
- fire_rejection('pre everything');
24027
+ this._fire_hook_rejection('pre everything', fromState, newState, fromAction, oldData, newData, wasForced);
23984
24028
  return false;
23985
24029
  }
23986
- update_fields(outcome);
24030
+ if (_update_hook_fields(hook_args, outcome)) {
24031
+ data_changed = true;
24032
+ }
23987
24033
  }
23988
24034
  if (wasAction) {
23989
24035
  // 1a. any action hook
23990
24036
  const outcome = abstract_hook_step(this._any_action_hook, hook_args);
23991
24037
  if (outcome.pass === false) {
23992
- fire_rejection('any action');
24038
+ this._fire_hook_rejection('any action', fromState, newState, fromAction, oldData, newData, wasForced);
23993
24039
  return false;
23994
24040
  }
23995
- update_fields(outcome);
24041
+ if (_update_hook_fields(hook_args, outcome)) {
24042
+ data_changed = true;
24043
+ }
23996
24044
  // 1b. global specific action hook
23997
24045
  const outcome2 = abstract_hook_step(this._global_action_hooks.get(newStateOrAction), hook_args);
23998
24046
  if (outcome2.pass === false) {
23999
- fire_rejection('global action');
24047
+ this._fire_hook_rejection('global action', fromState, newState, fromAction, oldData, newData, wasForced);
24000
24048
  return false;
24001
24049
  }
24002
- update_fields(outcome2);
24050
+ if (_update_hook_fields(hook_args, outcome2)) {
24051
+ data_changed = true;
24052
+ }
24003
24053
  }
24004
24054
  // 2. after hook
24005
24055
  if (this._has_after_hooks) {
24006
24056
  const ah = this._after_hooks.get(newStateOrAction);
24007
24057
  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);
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);
24010
24062
  }
24011
24063
  // 3. any transition hook
24012
24064
  if (this._any_transition_hook !== undefined) {
24013
24065
  const outcome = abstract_hook_step(this._any_transition_hook, hook_args);
24014
24066
  if (outcome.pass === false) {
24015
- fire_rejection('any transition');
24067
+ this._fire_hook_rejection('any transition', fromState, newState, fromAction, oldData, newData, wasForced);
24016
24068
  return false;
24017
24069
  }
24018
- update_fields(outcome);
24070
+ if (_update_hook_fields(hook_args, outcome)) {
24071
+ data_changed = true;
24072
+ }
24019
24073
  }
24020
24074
  // 4. exit hook
24021
24075
  if (this._has_exit_hooks) {
24022
24076
  const outcome = abstract_hook_step(this._exit_hooks.get(this._state), hook_args);
24023
24077
  if (outcome.pass === false) {
24024
- fire_rejection('exit');
24078
+ this._fire_hook_rejection('exit', fromState, newState, fromAction, oldData, newData, wasForced);
24025
24079
  return false;
24026
24080
  }
24027
- update_fields(outcome);
24081
+ if (_update_hook_fields(hook_args, outcome)) {
24082
+ data_changed = true;
24083
+ }
24028
24084
  }
24029
24085
  // 5. named transition / action hook
24030
24086
  if (this._has_named_hooks) {
@@ -24036,10 +24092,12 @@ class Machine {
24036
24092
  const nh = byAct === undefined ? undefined : byAct.get(newStateOrAction);
24037
24093
  const outcome = abstract_hook_step(nh, hook_args);
24038
24094
  if (outcome.pass === false) {
24039
- fire_rejection('named');
24095
+ this._fire_hook_rejection('named', fromState, newState, fromAction, oldData, newData, wasForced);
24040
24096
  return false;
24041
24097
  }
24042
- update_fields(outcome);
24098
+ if (_update_hook_fields(hook_args, outcome)) {
24099
+ data_changed = true;
24100
+ }
24043
24101
  }
24044
24102
  }
24045
24103
  // 6. regular hook
@@ -24049,56 +24107,68 @@ class Machine {
24049
24107
  const h = byTo === undefined ? undefined : byTo.get(newState);
24050
24108
  const outcome = abstract_hook_step(h, hook_args);
24051
24109
  if (outcome.pass === false) {
24052
- fire_rejection('hook');
24110
+ this._fire_hook_rejection('hook', fromState, newState, fromAction, oldData, newData, wasForced);
24053
24111
  return false;
24054
24112
  }
24055
- update_fields(outcome);
24113
+ if (_update_hook_fields(hook_args, outcome)) {
24114
+ data_changed = true;
24115
+ }
24056
24116
  }
24057
24117
  // 7. edge type hook
24058
24118
  // 7a. standard transition hook
24059
24119
  if (trans_type === 'legal') {
24060
24120
  const outcome = abstract_hook_step(this._standard_transition_hook, hook_args);
24061
24121
  if (outcome.pass === false) {
24062
- fire_rejection('standard transition');
24122
+ this._fire_hook_rejection('standard transition', fromState, newState, fromAction, oldData, newData, wasForced);
24063
24123
  return false;
24064
24124
  }
24065
- update_fields(outcome);
24125
+ if (_update_hook_fields(hook_args, outcome)) {
24126
+ data_changed = true;
24127
+ }
24066
24128
  }
24067
24129
  // 7b. main type hook
24068
24130
  if (trans_type === 'main') {
24069
24131
  const outcome = abstract_hook_step(this._main_transition_hook, hook_args);
24070
24132
  if (outcome.pass === false) {
24071
- fire_rejection('main transition');
24133
+ this._fire_hook_rejection('main transition', fromState, newState, fromAction, oldData, newData, wasForced);
24072
24134
  return false;
24073
24135
  }
24074
- update_fields(outcome);
24136
+ if (_update_hook_fields(hook_args, outcome)) {
24137
+ data_changed = true;
24138
+ }
24075
24139
  }
24076
24140
  // 7c. forced transition hook
24077
24141
  if (trans_type === 'forced') {
24078
24142
  const outcome = abstract_hook_step(this._forced_transition_hook, hook_args);
24079
24143
  if (outcome.pass === false) {
24080
- fire_rejection('forced transition');
24144
+ this._fire_hook_rejection('forced transition', fromState, newState, fromAction, oldData, newData, wasForced);
24081
24145
  return false;
24082
24146
  }
24083
- update_fields(outcome);
24147
+ if (_update_hook_fields(hook_args, outcome)) {
24148
+ data_changed = true;
24149
+ }
24084
24150
  }
24085
24151
  // 8. entry hook
24086
24152
  if (this._has_entry_hooks) {
24087
24153
  const outcome = abstract_hook_step(this._entry_hooks.get(newState), hook_args);
24088
24154
  if (outcome.pass === false) {
24089
- fire_rejection('entry');
24155
+ this._fire_hook_rejection('entry', fromState, newState, fromAction, oldData, newData, wasForced);
24090
24156
  return false;
24091
24157
  }
24092
- update_fields(outcome);
24158
+ if (_update_hook_fields(hook_args, outcome)) {
24159
+ data_changed = true;
24160
+ }
24093
24161
  }
24094
24162
  // 9. everything hook (fires after all other pre-hooks)
24095
24163
  if (this._everything_hook !== undefined) {
24096
24164
  const outcome = abstract_everything_hook_step(this._everything_hook, Object.assign(Object.assign({}, hook_args), { hook_name: 'everything' }));
24097
24165
  if (outcome.pass === false) {
24098
- fire_rejection('everything');
24166
+ this._fire_hook_rejection('everything', fromState, newState, fromAction, oldData, newData, wasForced);
24099
24167
  return false;
24100
24168
  }
24101
- update_fields(outcome);
24169
+ if (_update_hook_fields(hook_args, outcome)) {
24170
+ data_changed = true;
24171
+ }
24102
24172
  }
24103
24173
  // all hooks passed! let's now establish the result
24104
24174
  if (this._history_length) {
@@ -25024,6 +25094,46 @@ function is_hook_complex_result(hr) {
25024
25094
  }
25025
25095
  return false;
25026
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
+ }
25027
25137
  /**
25028
25138
  *
25029
25139
  * Invoke an optional transition/action hook and normalize its return value