@peter.naydenov/fsm-hub 2.1.5 → 2.1.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.
@@ -1,6 +1,14 @@
1
1
  ## Release History
2
2
 
3
3
 
4
+ ### 2.1.6 ( 2026-07-22)
5
+ - [x] Fix: `_callback.js` crashed when a callback rule fired with a `null` response (or a transformer returning `null`). Guarded the `.answer` access so the placeholder-clear no-ops and the callback still runs.
6
+ - [x] Fix: `MISSING_FSM` debugger call logged the whole subscribers array instead of the missing name. Now passes the specific missing entry.
7
+ - [x] Tests: 3 regression cases added (1 for the `MISSING_FSM` data, 2 for the null-response crashes). 14 → 17 passing;
8
+ - [x] Dev dependencies updates. @peter.naydenov/fsm - v.5.2.7;
9
+
10
+
11
+
4
12
  ### 2.1.5 ( 2026-07-21)
5
13
  - [x] Dependency update. ask-for-promise@3.2.0;
6
14
  - [x] Dev dependency update. @peter.naydenov/fsm - v.5.2.6;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@peter.naydenov/fsm-hub",
3
3
  "description": "FSM orchestration and state-managment tool",
4
- "version": "2.1.5",
4
+ "version": "2.1.6",
5
5
  "license": "MIT",
6
6
  "author": "Peter Naydenov",
7
7
  "main": "./dist/fsm-hub.umd.js",
@@ -26,7 +26,7 @@
26
26
  "ask-for-promise": "^3.2.0"
27
27
  },
28
28
  "devDependencies": {
29
- "@peter.naydenov/fsm": "5.2.6",
29
+ "@peter.naydenov/fsm": "5.2.7",
30
30
  "@rollup/plugin-commonjs": "^29.0.3",
31
31
  "@rollup/plugin-node-resolve": "^16.0.3",
32
32
  "@rollup/plugin-terser": "^1.0.0",
@@ -17,10 +17,18 @@ return function _callback ( task, fsmName, state, response ) {
17
17
  return
18
18
  }
19
19
 
20
- if ( fsmSubscriber ) { // Execute fsm rules
20
+ if ( fsmSubscriber ) { // Execute fsm rules
21
21
  fsmSubscriber.forEach ( subscriberName => {
22
22
  if ( !hub.fsm[ subscriberName ] ) {
23
- hub._debugger ( msg.MISSING_FSM, fsmSubscriber )
23
+ // Log the missing subscriber's name, not the
24
+ // whole subscribers array. The debugger formats
25
+ // the warning as `Warning: Fsm "%s" is not
26
+ // registered to the hub.` and the array makes
27
+ // the message look like
28
+ // `... to the hub. [ 'two' ]`
29
+ // instead of the intended
30
+ // `... to the hub. two`
31
+ hub._debugger ( msg.MISSING_FSM, subscriberName )
24
32
  return
25
33
  }
26
34
  const
@@ -57,8 +65,16 @@ return function _callback ( task, fsmName, state, response ) {
57
65
  if ( typeof transformFn == 'function' ) data = transformFn (state, response )
58
66
  else data = response
59
67
 
60
- if ( data.answer && data.answer.hasOwnProperty ( '___internalFlag') ) data.answer = undefined
61
-
68
+ // `data` can be null/undefined if the response is
69
+ // null and no transformer is set, or if the
70
+ // transformer itself returns null. Guard the
71
+ // `.answer` access so we don't blow up — the
72
+ // placeholder-clear below is a no-op when
73
+ // `data` is falsy, and `fn(data)` will still
74
+ // receive whatever value the user/transformer
75
+ // produced (null included).
76
+ if ( data && data.answer && data.answer.hasOwnProperty ( '___internalFlag') ) data.answer = undefined
77
+
62
78
  if ( typeof fn == 'function' ) fn(data)
63
79
  else hub._debugger ( msg.MISSING_FN, cbName )
64
80
 
package/test/01-basics.js CHANGED
@@ -529,7 +529,7 @@ it ( 'Callback-function with data argument', done => {
529
529
 
530
530
 
531
531
  it ( 'Test a Debugger', () => {
532
- const
532
+ const
533
533
  machine = {
534
534
  reactivity : [
535
535
  [ 'one', 'active', 'two', 'activate' ]
@@ -543,6 +543,142 @@ it ( 'Callback-function with data argument', done => {
543
543
 
544
544
 
545
545
 
546
+
547
+ // =====================================================================
548
+ // BUG REGRESSIONS
549
+ // =====================================================================
550
+
551
+ // -----------------------------------------------------------------
552
+ // BUG A — _callback.js passed `fsmSubscriber` (the whole array of
553
+ // subscriber names) as the data arg to MISSING_FSM, instead of
554
+ // `subscriberName` (the specific missing one). The warning message
555
+ // therefore looked like
556
+ // `Warning: Fsm "%s" is not registered to the hub. [ 'two' ]`
557
+ // instead of the intended
558
+ // `Warning: Fsm "%s" is not registered to the hub. two`.
559
+ // The existing test "Not registered fsm subscriber" only checks the
560
+ // `str` half of the call, so the wrong data was never noticed.
561
+ // -----------------------------------------------------------------
562
+ it ( 'BUG A — MISSING_FSM is logged with the missing name, not the array', done => {
563
+ const
564
+ mini = {
565
+ init : 'none'
566
+ , behavior : [
567
+ [ 'none', 'activate', 'active', 'switchOn' ]
568
+ ]
569
+ }
570
+ , lib = {
571
+ switchOn ({ task }, data) {
572
+ task.done ({ success : true, response : data })
573
+ }
574
+ }
575
+ , one = new Fsm ( mini, lib )
576
+ ;
577
+ const hub = new FsmHub ({
578
+ reactivity : [
579
+ [ 'one', 'active', 'two', 'activate' ]
580
+ ]
581
+ });
582
+ const calls = [];
583
+ hub._debugger = ( str, data ) => calls.push ({ str, data });
584
+ hub.addFsm ({ one }); // 'two' is intentionally NOT registered
585
+
586
+ one.update ( 'activate', 'try' );
587
+
588
+ setTimeout ( () => {
589
+ const missing = calls.find ( c => c.str === MISSING_FSM )
590
+ expect ( missing, 'MISSING_FSM was not logged' ).to.exist
591
+ // The data should be the missing name 'two' (a string),
592
+ // NOT the whole subscribers array.
593
+ expect ( missing.data ).to.equal ( 'two' )
594
+ done ()
595
+ }, 50 )
596
+ }) // it BUG A
597
+
598
+ // -----------------------------------------------------------------
599
+ // BUG B — _callback.js crashed with
600
+ // `TypeError: Cannot read properties of null (reading 'answer')`
601
+ // when a callback rule (length 3 in `reactivity`) was triggered
602
+ // and the FSM's response was `null` (and no transformer was
603
+ // configured, or the transformer itself returned `null`).
604
+ //
605
+ // The existing test "Callback-function with data argument" always
606
+ // passes a non-null `response`, so this path was never exercised.
607
+ // -----------------------------------------------------------------
608
+ it ( 'BUG B — callback rule with null response does not crash', done => {
609
+ const
610
+ mini = {
611
+ init : 'none'
612
+ , behavior : [
613
+ [ 'none', 'activate', 'active', 'switchOn' ]
614
+ ]
615
+ }
616
+ // Transition returns response: null. No transformer.
617
+ , lib = {
618
+ switchOn ({ task }) {
619
+ task.done ({ success : true, response : null })
620
+ }
621
+ }
622
+ , one = new Fsm ( mini, lib )
623
+ ;
624
+ const hub = new FsmHub ({
625
+ reactivity : [
626
+ // length-3 rule = callback, no fsm subscriber
627
+ [ 'one', 'active', 'showme' ]
628
+ ]
629
+ });
630
+ function showme ( data ) {
631
+ // The crash happened BEFORE this was called.
632
+ // We just want to assert: the callback fires
633
+ // and `data` is what the response was (null).
634
+ expect ( data ).to.equal ( null )
635
+ done ()
636
+ } // showme func.
637
+ hub.addFsm ({ one });
638
+ hub.addFunctions ({ showme });
639
+
640
+ one.update ( 'activate' );
641
+ }) // it BUG B — null response
642
+
643
+ it ( 'BUG B — callback rule with a transformer that returns null does not crash', done => {
644
+ const
645
+ mini = {
646
+ init : 'none'
647
+ , behavior : [
648
+ [ 'none', 'activate', 'active', 'switchOn' ]
649
+ ]
650
+ }
651
+ , lib = {
652
+ switchOn ({ task }, data) {
653
+ task.done ({ success : true, response : data })
654
+ }
655
+ }
656
+ , one = new Fsm ( mini, lib )
657
+ ;
658
+ const hub = new FsmHub ({
659
+ reactivity : [
660
+ [ 'one', 'active', 'showme' ]
661
+ ]
662
+ , transformers : {
663
+ 'one/showme' : 'nullTransformer'
664
+ }
665
+ }, {
666
+ // A transformer that returns null. This used to
667
+ // crash in _callback.js's `data.answer` check.
668
+ nullTransformer () { return null }
669
+ });
670
+ function showme ( data ) {
671
+ expect ( data ).to.equal ( null )
672
+ done ()
673
+ } // showme func.
674
+ hub.addFsm ({ one });
675
+ hub.addFunctions ({ showme });
676
+
677
+ one.update ( 'activate', 'try' );
678
+ }) // it BUG B — null transformer
679
+
680
+
681
+
546
682
 
547
683
  }) // describe
548
684