jssm 5.143.24 → 5.143.26

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.24 at 6/12/2026, 2:12:45 PM
21
+ * Generated for version 5.143.26 at 6/12/2026, 2:39:54 PM
22
22
 
23
23
  -->
24
- # jssm 5.143.24
24
+ # jssm 5.143.26
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/) ·
@@ -281,7 +281,7 @@ That decision shows up everywhere downstream:
281
281
  or run `npm run benny` against your own machine.
282
282
 
283
283
  - **More thoroughly tested than any other JavaScript state-machine
284
- library.** 7,012 tests at 100.0% line coverage
284
+ library.** 7,013 tests at 100.0% line coverage
285
285
  ([report](https://coveralls.io/github/StoneCypher/jssm)), plus
286
286
  fuzz testing via `fast-check`, with parser test data across ten natural
287
287
  languages and Emoji.
@@ -414,11 +414,11 @@ If your contribution is missing here, please open an issue.
414
414
 
415
415
  <br/>
416
416
 
417
- ***7,012 tests***, run 81,856 times.
417
+ ***7,013 tests***, run 81,857 times.
418
418
 
419
- - 6,256 specs with 100.0% coverage
419
+ - 6,257 specs with 100.0% coverage
420
420
  - 756 fuzz tests with 83.5% coverage
421
- - 5,876 TypeScript lines - 1.2 tests per line, 13.9 generated tests per line
421
+ - 5,880 TypeScript lines - 1.2 tests per line, 13.9 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)
@@ -3355,49 +3355,35 @@ function peg$parse(input, options) {
3355
3355
  return s0;
3356
3356
  }
3357
3357
  function peg$parseIntegerLiteral() {
3358
- var s0, s1, s2, s3, s4;
3359
- s0 = peg$currPos;
3360
- if (input.charCodeAt(peg$currPos) === 48) {
3361
- s1 = peg$c312;
3358
+ var c, start;
3359
+ c = input.charCodeAt(peg$currPos);
3360
+ if (c === 48) { // '0' alternative
3362
3361
  peg$currPos++;
3362
+ return '0';
3363
3363
  }
3364
- else {
3365
- s1 = peg$FAILED;
3366
- if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
3367
- peg$fail(peg$c313);
3368
- }
3364
+ // the generated code records the "0" alternative's expectation whenever
3365
+ // input doesn't start with 0 — including on the successful 1-9 path
3366
+ if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
3367
+ peg$fail(peg$c313);
3369
3368
  }
3370
- if (s1 === peg$FAILED) {
3371
- s1 = peg$currPos;
3372
- s2 = peg$parseNonZeroDigit();
3373
- if (s2 !== peg$FAILED) {
3374
- s3 = [];
3375
- s4 = peg$parseDecimalDigit();
3376
- while (s4 !== peg$FAILED) {
3377
- s3.push(s4);
3378
- s4 = peg$parseDecimalDigit();
3379
- }
3380
- if (s3 !== peg$FAILED) {
3381
- s2 = [s2, s3];
3382
- s1 = s2;
3383
- }
3384
- else {
3385
- peg$currPos = s1;
3386
- s1 = peg$FAILED;
3387
- }
3369
+ if (c >= 49 && c <= 57) { // 1-9
3370
+ start = peg$currPos;
3371
+ peg$currPos++;
3372
+ c = input.charCodeAt(peg$currPos);
3373
+ while (c >= 48 && c <= 57) { // 0-9 run
3374
+ peg$currPos++;
3375
+ c = input.charCodeAt(peg$currPos);
3388
3376
  }
3389
- else {
3390
- peg$currPos = s1;
3391
- s1 = peg$FAILED;
3377
+ // run-terminating DecimalDigit failure, recorded at the stop position
3378
+ if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
3379
+ peg$fail(peg$c315);
3392
3380
  }
3381
+ return input.substring(start, peg$currPos);
3393
3382
  }
3394
- if (s1 !== peg$FAILED) {
3395
- s0 = input.substring(s0, peg$currPos);
3396
- }
3397
- else {
3398
- s0 = s1;
3383
+ if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
3384
+ peg$fail(peg$c317);
3399
3385
  }
3400
- return s0;
3386
+ return peg$FAILED;
3401
3387
  }
3402
3388
  function peg$parseDecimalDigit() {
3403
3389
  var s0;
@@ -22402,7 +22388,7 @@ var constants = /*#__PURE__*/Object.freeze({
22402
22388
  * Useful for runtime diagnostics and for embedding in serialized machine
22403
22389
  * snapshots so that deserializers can detect version-skew.
22404
22390
  */
22405
- const version = "5.143.24";
22391
+ const version = "5.143.26";
22406
22392
 
22407
22393
  // whargarbl lots of these return arrays could/should be sets
22408
22394
  const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
@@ -25221,7 +25207,19 @@ class Machine {
25221
25207
  const edgeId = (to_id === undefined) ? undefined : this._edge_id_by_pair.get(pair_key(this._state_id, to_id));
25222
25208
  if ((edgeId !== undefined) && (!(this._edges[edgeId].forced_only))) {
25223
25209
  if (this._has_transition_hooks || this._has_post_transition_hooks) {
25224
- trans_type = this.edges_between(this._state, newStateOrAction)[0].kind; // TODO this won't do the right thing if various edges have different types
25210
+ // first matching outbound edge's kind, without building the result
25211
+ // array edges_between allocated here on every hooked transition.
25212
+ // First-match semantics are kept deliberately: _edge_map is
25213
+ // last-wins for multi-edge (from, to) pairs, so lookup_transition_for
25214
+ // could disagree with the old edges_between(...)[0]. #735
25215
+ // TODO this won't do the right thing if various edges have different types
25216
+ for (const ob_eid of this._outbound_edge_ids.get(this._state)) {
25217
+ const ob_edge = this._edges[ob_eid];
25218
+ if (ob_edge.to === newStateOrAction) {
25219
+ trans_type = ob_edge.kind;
25220
+ break;
25221
+ }
25222
+ }
25225
25223
  }
25226
25224
  valid = true;
25227
25225
  newState = newStateOrAction;
@@ -25235,6 +25233,11 @@ class Machine {
25235
25233
  // unchanged for all downstream uses without introducing an impossible
25236
25234
  // (uncoverable) branch; the value is only dereferenced under the guards
25237
25235
  // that imply it was built. #670
25236
+ // NOTE (#735): the { ...hook_args, hook_name } spreads at the four
25237
+ // everything-hook sites are contractual, not waste — handlers may capture
25238
+ // their context, and each captured context must durably carry its own
25239
+ // hook_name (pinned by the simultaneous-everything-hook specs). A shared
25240
+ // mutated object cannot satisfy that; do not "optimize" the spreads away.
25238
25241
  const hook_args_obj = (this._has_hooks || this._has_post_hooks)
25239
25242
  ? {
25240
25243
  data: this._data,
@@ -25594,10 +25597,15 @@ class Machine {
25594
25597
  cause: 'transition'
25595
25598
  });
25596
25599
  }
25597
- if (this.state_is_terminal(newState)) {
25600
+ // one state-record fetch answers both checks; newState is known-valid
25601
+ // here, and the public state_is_terminal / state_is_complete pair would
25602
+ // each redo has_state plus its own map walk. Same predicates:
25603
+ // terminal = no exits, complete = the constructor-set flag. #735
25604
+ const new_state_rec = this._states.get(newState);
25605
+ if (new_state_rec.to.length === 0) {
25598
25606
  this._fire('terminal', { state: newState, data: newData_after });
25599
25607
  }
25600
- if (this.state_is_complete(newState)) {
25608
+ if (new_state_rec.complete) {
25601
25609
  this._fire('complete', { state: newState, data: newData_after });
25602
25610
  }
25603
25611
  }
package/dist/cdn/viz.js CHANGED
@@ -3380,49 +3380,35 @@ function peg$parse(input, options) {
3380
3380
  return s0;
3381
3381
  }
3382
3382
  function peg$parseIntegerLiteral() {
3383
- var s0, s1, s2, s3, s4;
3384
- s0 = peg$currPos;
3385
- if (input.charCodeAt(peg$currPos) === 48) {
3386
- s1 = peg$c312;
3383
+ var c, start;
3384
+ c = input.charCodeAt(peg$currPos);
3385
+ if (c === 48) { // '0' alternative
3387
3386
  peg$currPos++;
3387
+ return '0';
3388
3388
  }
3389
- else {
3390
- s1 = peg$FAILED;
3391
- if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
3392
- peg$fail(peg$c313);
3393
- }
3389
+ // the generated code records the "0" alternative's expectation whenever
3390
+ // input doesn't start with 0 — including on the successful 1-9 path
3391
+ if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
3392
+ peg$fail(peg$c313);
3394
3393
  }
3395
- if (s1 === peg$FAILED) {
3396
- s1 = peg$currPos;
3397
- s2 = peg$parseNonZeroDigit();
3398
- if (s2 !== peg$FAILED) {
3399
- s3 = [];
3400
- s4 = peg$parseDecimalDigit();
3401
- while (s4 !== peg$FAILED) {
3402
- s3.push(s4);
3403
- s4 = peg$parseDecimalDigit();
3404
- }
3405
- if (s3 !== peg$FAILED) {
3406
- s2 = [s2, s3];
3407
- s1 = s2;
3408
- }
3409
- else {
3410
- peg$currPos = s1;
3411
- s1 = peg$FAILED;
3412
- }
3394
+ if (c >= 49 && c <= 57) { // 1-9
3395
+ start = peg$currPos;
3396
+ peg$currPos++;
3397
+ c = input.charCodeAt(peg$currPos);
3398
+ while (c >= 48 && c <= 57) { // 0-9 run
3399
+ peg$currPos++;
3400
+ c = input.charCodeAt(peg$currPos);
3413
3401
  }
3414
- else {
3415
- peg$currPos = s1;
3416
- s1 = peg$FAILED;
3402
+ // run-terminating DecimalDigit failure, recorded at the stop position
3403
+ if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
3404
+ peg$fail(peg$c315);
3417
3405
  }
3406
+ return input.substring(start, peg$currPos);
3418
3407
  }
3419
- if (s1 !== peg$FAILED) {
3420
- s0 = input.substring(s0, peg$currPos);
3421
- }
3422
- else {
3423
- s0 = s1;
3408
+ if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
3409
+ peg$fail(peg$c317);
3424
3410
  }
3425
- return s0;
3411
+ return peg$FAILED;
3426
3412
  }
3427
3413
  function peg$parseDecimalDigit() {
3428
3414
  var s0;
@@ -22427,7 +22413,7 @@ var constants = /*#__PURE__*/Object.freeze({
22427
22413
  * Useful for runtime diagnostics and for embedding in serialized machine
22428
22414
  * snapshots so that deserializers can detect version-skew.
22429
22415
  */
22430
- const version = "5.143.24";
22416
+ const version = "5.143.26";
22431
22417
 
22432
22418
  // whargarbl lots of these return arrays could/should be sets
22433
22419
  const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
@@ -25246,7 +25232,19 @@ class Machine {
25246
25232
  const edgeId = (to_id === undefined) ? undefined : this._edge_id_by_pair.get(pair_key(this._state_id, to_id));
25247
25233
  if ((edgeId !== undefined) && (!(this._edges[edgeId].forced_only))) {
25248
25234
  if (this._has_transition_hooks || this._has_post_transition_hooks) {
25249
- trans_type = this.edges_between(this._state, newStateOrAction)[0].kind; // TODO this won't do the right thing if various edges have different types
25235
+ // first matching outbound edge's kind, without building the result
25236
+ // array edges_between allocated here on every hooked transition.
25237
+ // First-match semantics are kept deliberately: _edge_map is
25238
+ // last-wins for multi-edge (from, to) pairs, so lookup_transition_for
25239
+ // could disagree with the old edges_between(...)[0]. #735
25240
+ // TODO this won't do the right thing if various edges have different types
25241
+ for (const ob_eid of this._outbound_edge_ids.get(this._state)) {
25242
+ const ob_edge = this._edges[ob_eid];
25243
+ if (ob_edge.to === newStateOrAction) {
25244
+ trans_type = ob_edge.kind;
25245
+ break;
25246
+ }
25247
+ }
25250
25248
  }
25251
25249
  valid = true;
25252
25250
  newState = newStateOrAction;
@@ -25260,6 +25258,11 @@ class Machine {
25260
25258
  // unchanged for all downstream uses without introducing an impossible
25261
25259
  // (uncoverable) branch; the value is only dereferenced under the guards
25262
25260
  // that imply it was built. #670
25261
+ // NOTE (#735): the { ...hook_args, hook_name } spreads at the four
25262
+ // everything-hook sites are contractual, not waste — handlers may capture
25263
+ // their context, and each captured context must durably carry its own
25264
+ // hook_name (pinned by the simultaneous-everything-hook specs). A shared
25265
+ // mutated object cannot satisfy that; do not "optimize" the spreads away.
25263
25266
  const hook_args_obj = (this._has_hooks || this._has_post_hooks)
25264
25267
  ? {
25265
25268
  data: this._data,
@@ -25619,10 +25622,15 @@ class Machine {
25619
25622
  cause: 'transition'
25620
25623
  });
25621
25624
  }
25622
- if (this.state_is_terminal(newState)) {
25625
+ // one state-record fetch answers both checks; newState is known-valid
25626
+ // here, and the public state_is_terminal / state_is_complete pair would
25627
+ // each redo has_state plus its own map walk. Same predicates:
25628
+ // terminal = no exits, complete = the constructor-set flag. #735
25629
+ const new_state_rec = this._states.get(newState);
25630
+ if (new_state_rec.to.length === 0) {
25623
25631
  this._fire('terminal', { state: newState, data: newData_after });
25624
25632
  }
25625
- if (this.state_is_complete(newState)) {
25633
+ if (new_state_rec.complete) {
25626
25634
  this._fire('complete', { state: newState, data: newData_after });
25627
25635
  }
25628
25636
  }