jssm 5.157.0 → 5.157.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.
- package/README.md +7 -7
- package/dist/cdn/instance.js +80 -29
- package/dist/cdn/viz.js +80 -29
- package/dist/cli/fsl-export-system-prompt.cjs +1 -1
- package/dist/cli/fsl-render.cjs +1 -1
- package/dist/cli/fsl.cjs +1 -1
- package/dist/cli/lib.cjs +1 -1
- package/dist/cli/lib.mjs +1 -1
- package/dist/deno/README.md +7 -7
- package/dist/deno/jssm.js +1 -1
- package/dist/jssm.es5.cjs +1 -1
- package/dist/jssm.es5.iife.js +1 -1
- package/dist/jssm.es6.mjs +1 -1
- package/dist/jssm_viz.cjs +1 -1
- package/dist/jssm_viz.iife.cjs +1 -1
- package/dist/jssm_viz.mjs +1 -1
- package/package.json +1 -1
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.157.
|
|
21
|
+
* Generated for version 5.157.1 at 7/2/2026, 1:36:48 AM
|
|
22
22
|
|
|
23
23
|
-->
|
|
24
|
-
# jssm 5.157.
|
|
24
|
+
# jssm 5.157.1
|
|
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,
|
|
315
|
+
library.** 7,866 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,
|
|
448
|
+
***7,866 tests***, run 82,908 times.
|
|
449
449
|
|
|
450
|
-
- 7,
|
|
451
|
-
- 758 fuzz tests with 53.
|
|
452
|
-
- 9,
|
|
450
|
+
- 7,108 specs with 100.0% coverage
|
|
451
|
+
- 758 fuzz tests with 53.5% coverage
|
|
452
|
+
- 9,879 TypeScript lines - 0.8 tests per line, 8.4 generated tests per line
|
|
453
453
|
|
|
454
454
|
[](https://github.com/StoneCypher/jssm/actions)
|
|
455
455
|
[](https://www.npmjs.com/package/jssm)
|
package/dist/cdn/instance.js
CHANGED
|
@@ -20769,11 +20769,26 @@ const weighted_rand_select = (options, probability_property = 'probability', rng
|
|
|
20769
20769
|
if (!(typeof options[0] === 'object')) {
|
|
20770
20770
|
throw new TypeError('options must be a non-empty array of objects');
|
|
20771
20771
|
}
|
|
20772
|
-
|
|
20773
|
-
|
|
20774
|
-
|
|
20772
|
+
// called once per probabilistic walk step: plain loops, no per-call closure
|
|
20773
|
+
// allocations (previously frand, or_one, and a reduce callback each call).
|
|
20774
|
+
// undefined weights count as 1, as before.
|
|
20775
|
+
let prob_sum = 0;
|
|
20776
|
+
for (const opt of options) { // eslint-disable-line fp/no-loops
|
|
20777
|
+
const p = opt[probability_property];
|
|
20778
|
+
prob_sum += (p === undefined) ? 1 : p;
|
|
20779
|
+
}
|
|
20780
|
+
const rnd = (rng ? rng() : Math.random()) * prob_sum;
|
|
20775
20781
|
let cursor = 0, cursor_sum = 0;
|
|
20776
|
-
|
|
20782
|
+
// advance past each element whose running sum is <= rnd; the element that
|
|
20783
|
+
// pushes the sum over rnd is the selection
|
|
20784
|
+
while (cursor < options.length) { // eslint-disable-line fp/no-loops
|
|
20785
|
+
const p = options[cursor][probability_property];
|
|
20786
|
+
cursor_sum += (p === undefined) ? 1 : p;
|
|
20787
|
+
++cursor;
|
|
20788
|
+
if (cursor_sum > rnd) {
|
|
20789
|
+
break;
|
|
20790
|
+
}
|
|
20791
|
+
}
|
|
20777
20792
|
return options[cursor - 1];
|
|
20778
20793
|
};
|
|
20779
20794
|
/* eslint-enable flowtype/no-weak-types */
|
|
@@ -23563,7 +23578,7 @@ var constants = /*#__PURE__*/Object.freeze({
|
|
|
23563
23578
|
* Useful for runtime diagnostics and for embedding in serialized machine
|
|
23564
23579
|
* snapshots so that deserializers can detect version-skew.
|
|
23565
23580
|
*/
|
|
23566
|
-
const version = "5.157.
|
|
23581
|
+
const version = "5.157.1";
|
|
23567
23582
|
|
|
23568
23583
|
// whargarbl lots of these return arrays could/should be sets
|
|
23569
23584
|
const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
|
|
@@ -23864,8 +23879,11 @@ function find_connected_components(states, edges) {
|
|
|
23864
23879
|
const component = [];
|
|
23865
23880
|
const queue = [start];
|
|
23866
23881
|
visited.add(start);
|
|
23867
|
-
|
|
23868
|
-
|
|
23882
|
+
// index-pointer pop: Array.shift is O(n) per pop, making the BFS O(V²)
|
|
23883
|
+
// worst case; reading by cursor keeps it O(V + E)
|
|
23884
|
+
let head = 0;
|
|
23885
|
+
while (head < queue.length) {
|
|
23886
|
+
const node = queue[head++];
|
|
23869
23887
|
component.push(node);
|
|
23870
23888
|
for (const neighbor of adj.get(node)) {
|
|
23871
23889
|
if (!visited.has(neighbor)) {
|
|
@@ -24080,6 +24098,9 @@ class Machine {
|
|
|
24080
24098
|
// nested Map+Set keeps the check O(1) per edge rather than an O(out-degree)
|
|
24081
24099
|
// scan (which made construction O(V*E) on dense graphs). #673
|
|
24082
24100
|
const seen_edges = new Map();
|
|
24101
|
+
// complete.includes was an O(|complete|) array scan per newly-created
|
|
24102
|
+
// state — O(V·C) overall; one Set turns it into O(V)
|
|
24103
|
+
const complete_set = new Set(complete);
|
|
24083
24104
|
// walk the transitions. single-lookup cursor fetches: each endpoint was
|
|
24084
24105
|
// previously a get followed by a has on the same key (four hashes per
|
|
24085
24106
|
// edge); the undefined check on the get's result carries the same
|
|
@@ -24094,12 +24115,12 @@ class Machine {
|
|
|
24094
24115
|
// get the cursors. what a mess
|
|
24095
24116
|
let cursor_from = this._states.get(tr.from);
|
|
24096
24117
|
if (cursor_from === undefined) {
|
|
24097
|
-
cursor_from = { name: tr.from, from: [], to: [], complete:
|
|
24118
|
+
cursor_from = { name: tr.from, from: [], to: [], complete: complete_set.has(tr.from) };
|
|
24098
24119
|
this._new_state(cursor_from);
|
|
24099
24120
|
}
|
|
24100
24121
|
let cursor_to = this._states.get(tr.to);
|
|
24101
24122
|
if (cursor_to === undefined) {
|
|
24102
|
-
cursor_to = { name: tr.to, from: [], to: [], complete:
|
|
24123
|
+
cursor_to = { name: tr.to, from: [], to: [], complete: complete_set.has(tr.to) };
|
|
24103
24124
|
this._new_state(cursor_to);
|
|
24104
24125
|
}
|
|
24105
24126
|
// record (from -> to) adjacency once per distinct target, even when
|
|
@@ -25345,11 +25366,17 @@ class Machine {
|
|
|
25345
25366
|
// filter -> filter chain and its three intermediate arrays; selection and
|
|
25346
25367
|
// ordering semantics are unchanged
|
|
25347
25368
|
const legal_exits = [], probability_bearing = [];
|
|
25369
|
+
// hoisted: every exit shares whichState, so probe _edge_map for the
|
|
25370
|
+
// from-side once instead of re-hashing the same key per exit inside
|
|
25371
|
+
// lookup_transition_for. wstate.to is non-empty only when at least one
|
|
25372
|
+
// outbound edge exists, and every outbound edge creates the from-side
|
|
25373
|
+
// mapping at construction — so emg is defined whenever the loop runs.
|
|
25374
|
+
const emg = this._edge_map.get(whichState);
|
|
25348
25375
|
for (const ws of wstate.to) {
|
|
25349
|
-
// wstate.to is built from the same edge set
|
|
25350
|
-
//
|
|
25351
|
-
//
|
|
25352
|
-
const edge = this.
|
|
25376
|
+
// wstate.to is built from the same edge set _edge_map indexes, so the
|
|
25377
|
+
// per-target get cannot miss; the guard mirrors the old defensive
|
|
25378
|
+
// .filter(Boolean) and is equally unreachable.
|
|
25379
|
+
const edge = this._edges[emg.get(ws)];
|
|
25353
25380
|
/* v8 ignore next */
|
|
25354
25381
|
if (!edge) {
|
|
25355
25382
|
continue;
|
|
@@ -26750,12 +26777,23 @@ class Machine {
|
|
|
26750
26777
|
// instead of O(E) — a large win on dense graphs where d << E. The `?? []`
|
|
26751
26778
|
// covers from-states that have no outgoing edges (terminal states) and
|
|
26752
26779
|
// states that don't exist at all, both of which return [] without iterating.
|
|
26780
|
+
//
|
|
26781
|
+
// The match itself compares interned numeric state ids against the packed
|
|
26782
|
+
// _edge_to_ids array rather than dereferencing each edge object for a
|
|
26783
|
+
// string compare: non-matching edges never touch an edge object, which is
|
|
26784
|
+
// most of the cost on dense shapes (heavier edge objects degrade a deref
|
|
26785
|
+
// loop — the 5.142/5.143 regression mechanism). Every state named by any
|
|
26786
|
+
// edge is interned at construction, so an unknown `to` provably has no
|
|
26787
|
+
// edges and returns [] immediately.
|
|
26788
|
+
const to_id = this._state_interner.id_of(to);
|
|
26789
|
+
if (to_id === undefined) {
|
|
26790
|
+
return [];
|
|
26791
|
+
}
|
|
26753
26792
|
const outbound = (_a = this._outbound_edge_ids.get(from)) !== null && _a !== void 0 ? _a : [];
|
|
26754
26793
|
const result = [];
|
|
26755
26794
|
for (const edgeId of outbound) {
|
|
26756
|
-
|
|
26757
|
-
|
|
26758
|
-
result.push(edge);
|
|
26795
|
+
if (this._edge_to_ids[edgeId] === to_id) {
|
|
26796
|
+
result.push(this._edges[edgeId]);
|
|
26759
26797
|
}
|
|
26760
26798
|
}
|
|
26761
26799
|
return result;
|
|
@@ -27045,19 +27083,15 @@ class Machine {
|
|
|
27045
27083
|
const edgeId = (to_id === undefined) ? undefined : this._edge_id_by_pair.get(pair_key(this._state_id, to_id));
|
|
27046
27084
|
if ((edgeId !== undefined) && (!(this._edges[edgeId].forced_only))) {
|
|
27047
27085
|
if (this._has_transition_hooks || this._has_post_transition_hooks) {
|
|
27048
|
-
//
|
|
27049
|
-
//
|
|
27050
|
-
//
|
|
27051
|
-
//
|
|
27052
|
-
//
|
|
27053
|
-
//
|
|
27054
|
-
|
|
27055
|
-
|
|
27056
|
-
|
|
27057
|
-
trans_type = ob_edge.kind;
|
|
27058
|
-
break;
|
|
27059
|
-
}
|
|
27060
|
-
}
|
|
27086
|
+
// kind of the dispatched edge. _edge_id_by_pair and _edge_map are
|
|
27087
|
+
// both first-declared-wins for parallel (from, to) pairs (see the
|
|
27088
|
+
// constructor around _edge_map / _edge_id_by_pair), and
|
|
27089
|
+
// _outbound_edge_ids fills in declaration order — so the old
|
|
27090
|
+
// first-match outbound scan always resolved to this same edgeId.
|
|
27091
|
+
// Direct read replaces the O(out-degree) object-deref scan; the
|
|
27092
|
+
// first-declared-kind semantics are pinned by the parallel-edge
|
|
27093
|
+
// transition-kind hook spec. #735
|
|
27094
|
+
trans_type = this._edges[edgeId].kind;
|
|
27061
27095
|
}
|
|
27062
27096
|
valid = true;
|
|
27063
27097
|
newState = newStateOrAction;
|
|
@@ -27457,6 +27491,13 @@ class Machine {
|
|
|
27457
27491
|
* Called internally after each transition.
|
|
27458
27492
|
*/
|
|
27459
27493
|
auto_set_state_timeout() {
|
|
27494
|
+
// called on every successful transition-commit. Machines with no `after`
|
|
27495
|
+
// clauses at all (the overwhelmingly common case) previously still paid a
|
|
27496
|
+
// string hash + map probe here per transition; one integer size read
|
|
27497
|
+
// short-circuits that.
|
|
27498
|
+
if (this._after_mapping.size === 0) {
|
|
27499
|
+
return;
|
|
27500
|
+
}
|
|
27460
27501
|
const after_res = this._after_mapping.get(this._state);
|
|
27461
27502
|
if (after_res !== undefined) {
|
|
27462
27503
|
const [next_state, after_time] = after_res;
|
|
@@ -28742,6 +28783,14 @@ function is_hook_complex_result(hr) {
|
|
|
28742
28783
|
*
|
|
28743
28784
|
*/
|
|
28744
28785
|
function _update_hook_fields(hook_args, res) {
|
|
28786
|
+
// HOOK_PASSED is the shared frozen outcome for "no hook installed" and for
|
|
28787
|
+
// hooks returning true/undefined — the overwhelming majority of the up-to-
|
|
28788
|
+
// ~10 steps per hooked transition. It can never carry `data` (frozen, built
|
|
28789
|
+
// without one), so one pointer compare replaces the hasOwnProperty
|
|
28790
|
+
// reflection call for the common case.
|
|
28791
|
+
if (res === HOOK_PASSED) {
|
|
28792
|
+
return false;
|
|
28793
|
+
}
|
|
28745
28794
|
if (Object.prototype.hasOwnProperty.call(res, 'data')) {
|
|
28746
28795
|
hook_args.data = res.data;
|
|
28747
28796
|
hook_args.next_data = res.next_data;
|
|
@@ -28760,6 +28809,8 @@ function _update_hook_fields(hook_args, res) {
|
|
|
28760
28809
|
* so a shared instance is observationally identical; freezing turns that
|
|
28761
28810
|
* read-only contract from incidental into enforced. Complex results (hooks
|
|
28762
28811
|
* returning `{ pass, data, ... }`) still pass through untouched. #705
|
|
28812
|
+
* _update_hook_fields additionally identity-checks HOOK_PASSED to skip its
|
|
28813
|
+
* own-property probe on the common no-op outcome.
|
|
28763
28814
|
*
|
|
28764
28815
|
* @see abstract_hook_step
|
|
28765
28816
|
* @see abstract_everything_hook_step
|
package/dist/cdn/viz.js
CHANGED
|
@@ -20794,11 +20794,26 @@ const weighted_rand_select = (options, probability_property = 'probability', rng
|
|
|
20794
20794
|
if (!(typeof options[0] === 'object')) {
|
|
20795
20795
|
throw new TypeError('options must be a non-empty array of objects');
|
|
20796
20796
|
}
|
|
20797
|
-
|
|
20798
|
-
|
|
20799
|
-
|
|
20797
|
+
// called once per probabilistic walk step: plain loops, no per-call closure
|
|
20798
|
+
// allocations (previously frand, or_one, and a reduce callback each call).
|
|
20799
|
+
// undefined weights count as 1, as before.
|
|
20800
|
+
let prob_sum = 0;
|
|
20801
|
+
for (const opt of options) { // eslint-disable-line fp/no-loops
|
|
20802
|
+
const p = opt[probability_property];
|
|
20803
|
+
prob_sum += (p === undefined) ? 1 : p;
|
|
20804
|
+
}
|
|
20805
|
+
const rnd = (rng ? rng() : Math.random()) * prob_sum;
|
|
20800
20806
|
let cursor = 0, cursor_sum = 0;
|
|
20801
|
-
|
|
20807
|
+
// advance past each element whose running sum is <= rnd; the element that
|
|
20808
|
+
// pushes the sum over rnd is the selection
|
|
20809
|
+
while (cursor < options.length) { // eslint-disable-line fp/no-loops
|
|
20810
|
+
const p = options[cursor][probability_property];
|
|
20811
|
+
cursor_sum += (p === undefined) ? 1 : p;
|
|
20812
|
+
++cursor;
|
|
20813
|
+
if (cursor_sum > rnd) {
|
|
20814
|
+
break;
|
|
20815
|
+
}
|
|
20816
|
+
}
|
|
20802
20817
|
return options[cursor - 1];
|
|
20803
20818
|
};
|
|
20804
20819
|
/* eslint-enable flowtype/no-weak-types */
|
|
@@ -23588,7 +23603,7 @@ var constants = /*#__PURE__*/Object.freeze({
|
|
|
23588
23603
|
* Useful for runtime diagnostics and for embedding in serialized machine
|
|
23589
23604
|
* snapshots so that deserializers can detect version-skew.
|
|
23590
23605
|
*/
|
|
23591
|
-
const version = "5.157.
|
|
23606
|
+
const version = "5.157.1";
|
|
23592
23607
|
|
|
23593
23608
|
// whargarbl lots of these return arrays could/should be sets
|
|
23594
23609
|
const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
|
|
@@ -23889,8 +23904,11 @@ function find_connected_components(states, edges) {
|
|
|
23889
23904
|
const component = [];
|
|
23890
23905
|
const queue = [start];
|
|
23891
23906
|
visited.add(start);
|
|
23892
|
-
|
|
23893
|
-
|
|
23907
|
+
// index-pointer pop: Array.shift is O(n) per pop, making the BFS O(V²)
|
|
23908
|
+
// worst case; reading by cursor keeps it O(V + E)
|
|
23909
|
+
let head = 0;
|
|
23910
|
+
while (head < queue.length) {
|
|
23911
|
+
const node = queue[head++];
|
|
23894
23912
|
component.push(node);
|
|
23895
23913
|
for (const neighbor of adj.get(node)) {
|
|
23896
23914
|
if (!visited.has(neighbor)) {
|
|
@@ -24105,6 +24123,9 @@ class Machine {
|
|
|
24105
24123
|
// nested Map+Set keeps the check O(1) per edge rather than an O(out-degree)
|
|
24106
24124
|
// scan (which made construction O(V*E) on dense graphs). #673
|
|
24107
24125
|
const seen_edges = new Map();
|
|
24126
|
+
// complete.includes was an O(|complete|) array scan per newly-created
|
|
24127
|
+
// state — O(V·C) overall; one Set turns it into O(V)
|
|
24128
|
+
const complete_set = new Set(complete);
|
|
24108
24129
|
// walk the transitions. single-lookup cursor fetches: each endpoint was
|
|
24109
24130
|
// previously a get followed by a has on the same key (four hashes per
|
|
24110
24131
|
// edge); the undefined check on the get's result carries the same
|
|
@@ -24119,12 +24140,12 @@ class Machine {
|
|
|
24119
24140
|
// get the cursors. what a mess
|
|
24120
24141
|
let cursor_from = this._states.get(tr.from);
|
|
24121
24142
|
if (cursor_from === undefined) {
|
|
24122
|
-
cursor_from = { name: tr.from, from: [], to: [], complete:
|
|
24143
|
+
cursor_from = { name: tr.from, from: [], to: [], complete: complete_set.has(tr.from) };
|
|
24123
24144
|
this._new_state(cursor_from);
|
|
24124
24145
|
}
|
|
24125
24146
|
let cursor_to = this._states.get(tr.to);
|
|
24126
24147
|
if (cursor_to === undefined) {
|
|
24127
|
-
cursor_to = { name: tr.to, from: [], to: [], complete:
|
|
24148
|
+
cursor_to = { name: tr.to, from: [], to: [], complete: complete_set.has(tr.to) };
|
|
24128
24149
|
this._new_state(cursor_to);
|
|
24129
24150
|
}
|
|
24130
24151
|
// record (from -> to) adjacency once per distinct target, even when
|
|
@@ -25370,11 +25391,17 @@ class Machine {
|
|
|
25370
25391
|
// filter -> filter chain and its three intermediate arrays; selection and
|
|
25371
25392
|
// ordering semantics are unchanged
|
|
25372
25393
|
const legal_exits = [], probability_bearing = [];
|
|
25394
|
+
// hoisted: every exit shares whichState, so probe _edge_map for the
|
|
25395
|
+
// from-side once instead of re-hashing the same key per exit inside
|
|
25396
|
+
// lookup_transition_for. wstate.to is non-empty only when at least one
|
|
25397
|
+
// outbound edge exists, and every outbound edge creates the from-side
|
|
25398
|
+
// mapping at construction — so emg is defined whenever the loop runs.
|
|
25399
|
+
const emg = this._edge_map.get(whichState);
|
|
25373
25400
|
for (const ws of wstate.to) {
|
|
25374
|
-
// wstate.to is built from the same edge set
|
|
25375
|
-
//
|
|
25376
|
-
//
|
|
25377
|
-
const edge = this.
|
|
25401
|
+
// wstate.to is built from the same edge set _edge_map indexes, so the
|
|
25402
|
+
// per-target get cannot miss; the guard mirrors the old defensive
|
|
25403
|
+
// .filter(Boolean) and is equally unreachable.
|
|
25404
|
+
const edge = this._edges[emg.get(ws)];
|
|
25378
25405
|
/* v8 ignore next */
|
|
25379
25406
|
if (!edge) {
|
|
25380
25407
|
continue;
|
|
@@ -26775,12 +26802,23 @@ class Machine {
|
|
|
26775
26802
|
// instead of O(E) — a large win on dense graphs where d << E. The `?? []`
|
|
26776
26803
|
// covers from-states that have no outgoing edges (terminal states) and
|
|
26777
26804
|
// states that don't exist at all, both of which return [] without iterating.
|
|
26805
|
+
//
|
|
26806
|
+
// The match itself compares interned numeric state ids against the packed
|
|
26807
|
+
// _edge_to_ids array rather than dereferencing each edge object for a
|
|
26808
|
+
// string compare: non-matching edges never touch an edge object, which is
|
|
26809
|
+
// most of the cost on dense shapes (heavier edge objects degrade a deref
|
|
26810
|
+
// loop — the 5.142/5.143 regression mechanism). Every state named by any
|
|
26811
|
+
// edge is interned at construction, so an unknown `to` provably has no
|
|
26812
|
+
// edges and returns [] immediately.
|
|
26813
|
+
const to_id = this._state_interner.id_of(to);
|
|
26814
|
+
if (to_id === undefined) {
|
|
26815
|
+
return [];
|
|
26816
|
+
}
|
|
26778
26817
|
const outbound = (_a = this._outbound_edge_ids.get(from)) !== null && _a !== void 0 ? _a : [];
|
|
26779
26818
|
const result = [];
|
|
26780
26819
|
for (const edgeId of outbound) {
|
|
26781
|
-
|
|
26782
|
-
|
|
26783
|
-
result.push(edge);
|
|
26820
|
+
if (this._edge_to_ids[edgeId] === to_id) {
|
|
26821
|
+
result.push(this._edges[edgeId]);
|
|
26784
26822
|
}
|
|
26785
26823
|
}
|
|
26786
26824
|
return result;
|
|
@@ -27070,19 +27108,15 @@ class Machine {
|
|
|
27070
27108
|
const edgeId = (to_id === undefined) ? undefined : this._edge_id_by_pair.get(pair_key(this._state_id, to_id));
|
|
27071
27109
|
if ((edgeId !== undefined) && (!(this._edges[edgeId].forced_only))) {
|
|
27072
27110
|
if (this._has_transition_hooks || this._has_post_transition_hooks) {
|
|
27073
|
-
//
|
|
27074
|
-
//
|
|
27075
|
-
//
|
|
27076
|
-
//
|
|
27077
|
-
//
|
|
27078
|
-
//
|
|
27079
|
-
|
|
27080
|
-
|
|
27081
|
-
|
|
27082
|
-
trans_type = ob_edge.kind;
|
|
27083
|
-
break;
|
|
27084
|
-
}
|
|
27085
|
-
}
|
|
27111
|
+
// kind of the dispatched edge. _edge_id_by_pair and _edge_map are
|
|
27112
|
+
// both first-declared-wins for parallel (from, to) pairs (see the
|
|
27113
|
+
// constructor around _edge_map / _edge_id_by_pair), and
|
|
27114
|
+
// _outbound_edge_ids fills in declaration order — so the old
|
|
27115
|
+
// first-match outbound scan always resolved to this same edgeId.
|
|
27116
|
+
// Direct read replaces the O(out-degree) object-deref scan; the
|
|
27117
|
+
// first-declared-kind semantics are pinned by the parallel-edge
|
|
27118
|
+
// transition-kind hook spec. #735
|
|
27119
|
+
trans_type = this._edges[edgeId].kind;
|
|
27086
27120
|
}
|
|
27087
27121
|
valid = true;
|
|
27088
27122
|
newState = newStateOrAction;
|
|
@@ -27482,6 +27516,13 @@ class Machine {
|
|
|
27482
27516
|
* Called internally after each transition.
|
|
27483
27517
|
*/
|
|
27484
27518
|
auto_set_state_timeout() {
|
|
27519
|
+
// called on every successful transition-commit. Machines with no `after`
|
|
27520
|
+
// clauses at all (the overwhelmingly common case) previously still paid a
|
|
27521
|
+
// string hash + map probe here per transition; one integer size read
|
|
27522
|
+
// short-circuits that.
|
|
27523
|
+
if (this._after_mapping.size === 0) {
|
|
27524
|
+
return;
|
|
27525
|
+
}
|
|
27485
27526
|
const after_res = this._after_mapping.get(this._state);
|
|
27486
27527
|
if (after_res !== undefined) {
|
|
27487
27528
|
const [next_state, after_time] = after_res;
|
|
@@ -28731,6 +28772,14 @@ function is_hook_complex_result(hr) {
|
|
|
28731
28772
|
*
|
|
28732
28773
|
*/
|
|
28733
28774
|
function _update_hook_fields(hook_args, res) {
|
|
28775
|
+
// HOOK_PASSED is the shared frozen outcome for "no hook installed" and for
|
|
28776
|
+
// hooks returning true/undefined — the overwhelming majority of the up-to-
|
|
28777
|
+
// ~10 steps per hooked transition. It can never carry `data` (frozen, built
|
|
28778
|
+
// without one), so one pointer compare replaces the hasOwnProperty
|
|
28779
|
+
// reflection call for the common case.
|
|
28780
|
+
if (res === HOOK_PASSED) {
|
|
28781
|
+
return false;
|
|
28782
|
+
}
|
|
28734
28783
|
if (Object.prototype.hasOwnProperty.call(res, 'data')) {
|
|
28735
28784
|
hook_args.data = res.data;
|
|
28736
28785
|
hook_args.next_data = res.next_data;
|
|
@@ -28749,6 +28798,8 @@ function _update_hook_fields(hook_args, res) {
|
|
|
28749
28798
|
* so a shared instance is observationally identical; freezing turns that
|
|
28750
28799
|
* read-only contract from incidental into enforced. Complex results (hooks
|
|
28751
28800
|
* returning `{ pass, data, ... }`) still pass through untouched. #705
|
|
28801
|
+
* _update_hook_fields additionally identity-checks HOOK_PASSED to skip its
|
|
28802
|
+
* own-property probe on the common no-op outcome.
|
|
28752
28803
|
*
|
|
28753
28804
|
* @see abstract_hook_step
|
|
28754
28805
|
* @see abstract_everything_hook_step
|