u8-mqtt 0.3.1 → 0.4.0

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.
Files changed (78) hide show
  1. package/README.md +5 -5
  2. package/cjs/basic-v4.cjs +1205 -0
  3. package/cjs/basic-v4.cjs.map +1 -0
  4. package/cjs/basic-v5.cjs +1469 -0
  5. package/cjs/basic-v5.cjs.map +1 -0
  6. package/cjs/index.cjs +312 -247
  7. package/cjs/index.cjs.map +1 -1
  8. package/cjs/v4.cjs +307 -242
  9. package/cjs/v4.cjs.map +1 -1
  10. package/cjs/v5.cjs +309 -243
  11. package/cjs/v5.cjs.map +1 -1
  12. package/code/_cmdid_dispatch.jsy +1 -3
  13. package/code/base.jsy +64 -84
  14. package/code/{v4.mjs → basic-v4.js} +1 -1
  15. package/code/{v5.mjs → basic-v5.js} +1 -1
  16. package/code/core.jsy +41 -15
  17. package/code/{index.mjs → index.js} +3 -2
  18. package/code/{_router.jsy → router_path.jsy} +30 -12
  19. package/code/v4.js +20 -0
  20. package/code/v5.js +29 -0
  21. package/code/version.js +1 -0
  22. package/code/with_topic_router.jsy +41 -0
  23. package/esm/deno/basic-v4.js +1193 -0
  24. package/esm/deno/basic-v4.js.map +1 -0
  25. package/esm/deno/basic-v5.js +1455 -0
  26. package/esm/deno/basic-v5.js.map +1 -0
  27. package/esm/deno/index.js +307 -242
  28. package/esm/deno/index.js.map +1 -1
  29. package/esm/deno/v4.js +304 -240
  30. package/esm/deno/v4.js.map +1 -1
  31. package/esm/deno/v5.js +306 -241
  32. package/esm/deno/v5.js.map +1 -1
  33. package/esm/node/basic-v4.js +1196 -0
  34. package/esm/node/basic-v4.js.map +1 -0
  35. package/esm/node/basic-v4.mjs +1196 -0
  36. package/esm/node/basic-v4.mjs.map +1 -0
  37. package/esm/node/basic-v5.js +1458 -0
  38. package/esm/node/basic-v5.js.map +1 -0
  39. package/esm/node/basic-v5.mjs +1458 -0
  40. package/esm/node/basic-v5.mjs.map +1 -0
  41. package/esm/node/index.js +309 -243
  42. package/esm/node/index.js.map +1 -1
  43. package/esm/node/index.mjs +309 -243
  44. package/esm/node/index.mjs.map +1 -1
  45. package/esm/node/v4.js +306 -241
  46. package/esm/node/v4.js.map +1 -1
  47. package/esm/node/v4.mjs +306 -241
  48. package/esm/node/v4.mjs.map +1 -1
  49. package/esm/node/v5.js +308 -242
  50. package/esm/node/v5.js.map +1 -1
  51. package/esm/node/v5.mjs +308 -242
  52. package/esm/node/v5.mjs.map +1 -1
  53. package/esm/web/basic-v4.js +1193 -0
  54. package/esm/web/basic-v4.js.map +1 -0
  55. package/esm/web/basic-v4.min.js +1 -0
  56. package/esm/web/basic-v4.min.js.br +0 -0
  57. package/esm/web/basic-v4.min.js.gz +0 -0
  58. package/esm/web/basic-v5.js +1455 -0
  59. package/esm/web/basic-v5.js.map +1 -0
  60. package/esm/web/basic-v5.min.js +1 -0
  61. package/esm/web/basic-v5.min.js.br +0 -0
  62. package/esm/web/basic-v5.min.js.gz +0 -0
  63. package/esm/web/index.js +307 -242
  64. package/esm/web/index.js.map +1 -1
  65. package/esm/web/index.min.js +1 -1
  66. package/esm/web/index.min.js.br +0 -0
  67. package/esm/web/index.min.js.gz +0 -0
  68. package/esm/web/v4.js +305 -241
  69. package/esm/web/v4.js.map +1 -1
  70. package/esm/web/v4.min.js +1 -1
  71. package/esm/web/v4.min.js.br +0 -0
  72. package/esm/web/v4.min.js.gz +0 -0
  73. package/esm/web/v5.js +306 -241
  74. package/esm/web/v5.js.map +1 -1
  75. package/esm/web/v5.min.js +1 -1
  76. package/esm/web/v5.min.js.br +0 -0
  77. package/esm/web/v5.min.js.gz +0 -0
  78. package/package.json +7 -7
package/esm/web/v4.js CHANGED
@@ -513,6 +513,191 @@ const mqtt_opts_v4 =
513
513
  encode_fns: mqtt_encode_v4,
514
514
  mqtt_writer: mqtt_writer_v4, };
515
515
 
516
+ function parse(str, loose) {
517
+ if (str instanceof RegExp) return { keys:false, pattern:str };
518
+ var c, o, tmp, ext, keys=[], pattern='', arr = str.split('/');
519
+ arr[0] || arr.shift();
520
+
521
+ while (tmp = arr.shift()) {
522
+ c = tmp[0];
523
+ if (c === '*') {
524
+ keys.push('wild');
525
+ pattern += '/(.*)';
526
+ } else if (c === ':') {
527
+ o = tmp.indexOf('?', 1);
528
+ ext = tmp.indexOf('.', 1);
529
+ keys.push( tmp.substring(1, !!~o ? o : !!~ext ? ext : tmp.length) );
530
+ pattern += !!~o && !~ext ? '(?:/([^/]+?))?' : '/([^/]+?)';
531
+ if (!!~ext) pattern += (!!~o ? '?' : '') + '\\' + tmp.substring(ext);
532
+ } else {
533
+ pattern += '/' + tmp;
534
+ }
535
+ }
536
+
537
+ return {
538
+ keys: keys,
539
+ pattern: new RegExp('^' + pattern + (loose ? '(?=$|\/)' : '\/?$'), 'i')
540
+ };
541
+ }
542
+
543
+ /*
544
+ class AbstractTopicRouter ::
545
+ async invoke(pkt, ctx) ::
546
+ add(topic_route, ...args) ::
547
+ remove(topic_route, priority) ::
548
+ clear(priority) ::
549
+ find(topic) :: // optional
550
+ mqtt_topic(topic_route)
551
+ */
552
+
553
+ const with_topic_router = mqtt_topic_router =>
554
+ MQTTKlass => class extends MQTTKlass {
555
+ static _aliases() {
556
+ return super._aliases() +
557
+ ' sub_topic:subscribe_topic unsub_topic:unsubscribe_topic'}
558
+
559
+ _init_router(opt) {
560
+ return mqtt_topic_router(opt, this)}
561
+
562
+ get on_topic() {return this.router.add}
563
+
564
+ _sub_chain(topic, ex, topic_prefix) {
565
+ let res = this.subscribe([[ topic ]], ex, topic_prefix);
566
+ let subs = this.subs ||(this.subs = new Map());
567
+ subs.set((res.topic = topic), (subs.last = res));
568
+ return this }// fluent api -- return this and track side effects
569
+
570
+ // alias: sub_topic
571
+ subscribe_topic(topic_route, ...args) {
572
+ let router = this.router;
573
+ router.add(topic_route, true, args.pop() );// handler
574
+ let topic = router.mqtt_topic(topic_route);
575
+ return this._sub_chain(topic, ...args ) }// ex, topic_prefix
576
+
577
+ // alias: unsub_topic
578
+ unsubscribe_topic(topic_route, ...args) {
579
+ let router = this.router;
580
+ router.remove(topic_route, true);
581
+ let topic = router.mqtt_topic(topic_route);
582
+ return this.unsubscribe([[ topic ]], ...args ) } };// topic_prefix
583
+
584
+ // Use [regexparam][] for url-like topic parsing
585
+ // [regexparam]: https://github.com/lukeed/regexparam
586
+
587
+
588
+ const with_topic_path_router = /* #__PURE__ */
589
+ with_topic_router(mqtt_topic_path_router);
590
+
591
+
592
+ const mqtt_topic = topic_route =>
593
+ topic_route
594
+ .replace(/[*].*$/, '#')
595
+ .replace(/:\w+\??/g, '+');
596
+
597
+ const as_topic_path = topic_route =>(
598
+ topic_route
599
+ .replace(/#$/, '*') // replace MQTT # wildcard at end
600
+ .split(/([^\/]*[+][^\/]*)/) // split on MQTT + match tokens
601
+ .reduce (( sz, v, idx ) => sz +(
602
+ idx & 1 // even entires are body, odd are MQTT + tokens
603
+ ? `:$${1 + idx>>1}` // replace with `:$#` sequential ids, using ? for partial entries
604
+ : v ) ) );// pass through body
605
+
606
+ function _ignore(pkt, params, ctx) {ctx.done = true;}
607
+
608
+ function mqtt_topic_path_router() {
609
+ let pri_lsts = [[],[]], rm = Symbol();
610
+ let find = topic => _routes_iter(pri_lsts, topic);
611
+
612
+ // return duck-type compatible with AbstractTopicRouter in ./with_topic_router
613
+ return {find, mqtt_topic,
614
+ add(topic_route, ...args) {
615
+ let fn = args.pop();
616
+ let priority = args.pop();
617
+
618
+ if ('function' !== typeof fn) {
619
+ if (false === fn) {
620
+ fn = _ignore;}
621
+ else throw new TypeError()}
622
+
623
+ let rte = parse(as_topic_path(topic_route));
624
+
625
+ rte.key = topic_route;
626
+ rte.tgt = fn;
627
+ pri_lsts[priority ? 0 : 1].push(rte);
628
+ return this}
629
+
630
+ , remove(topic_route, priority) {
631
+ let lst = pri_lsts[priority ? 0 : 1];
632
+ return _route_remove([lst], topic_route)}
633
+
634
+ , clear(priority) {
635
+ pri_lsts[priority ? 0 : 1] = [];
636
+ if (null == priority) {
637
+ pri_lsts[1] = [];} }
638
+
639
+ , async invoke(pkt, ctx) {
640
+ ctx.idx = 0;
641
+ ctx.rm = rm;
642
+
643
+ for (let [fn, params] of find(pkt.topic)) {
644
+ let res = await fn(pkt, params, ctx);
645
+
646
+ if (rm === res) {
647
+ _route_remove(pri_lsts, fn);}
648
+
649
+ if (ctx.done) {
650
+ break}
651
+ else ctx.idx++;}
652
+
653
+ let {pkt_id, qos} = pkt;
654
+ if (1 === qos) {
655
+ await ctx.mqtt._send('puback', {pkt_id});} } } }
656
+
657
+
658
+ function * _routes_iter(all_route_lists, topic) {
659
+ for (let route_list of all_route_lists) {
660
+ for (let route of route_list) {
661
+ let res = _route_match_one(topic, route);
662
+ if (undefined !== res) {
663
+ yield res;} } } }
664
+
665
+
666
+ function _route_match_one(topic, {keys, pattern, tgt}) {
667
+ let match = '/' !== topic[0]
668
+ ? pattern.exec('/'+topic)
669
+ : pattern.exec(topic);
670
+
671
+ if (null === match) {
672
+ return}
673
+
674
+ if (false === keys) {
675
+ let {groups} = match;
676
+ if (! groups) {
677
+ return [tgt]}
678
+
679
+ let params = {};
680
+ for (let k in groups) {
681
+ params[k] = groups[k];}
682
+
683
+ return [tgt, params]}
684
+
685
+ if (0 === keys.length) {
686
+ return [tgt]}
687
+
688
+ let params = {};
689
+ for (let i=0; i<keys.length; i++) {
690
+ params[ keys[i] ] = match[1+i];}
691
+ return [tgt, params]}
692
+
693
+
694
+ function _route_remove(all_route_lists, query) {
695
+ let match = route => route===query || route.tgt===query || route.key===query;
696
+ for (let lst of all_route_lists) {
697
+ let i = lst.findIndex(match);
698
+ if (0 <= i) {return !! lst.splice(i,1)} }
699
+ return false}
700
+
516
701
  /*
517
702
  export function decode_varint_loop(u8, i=0) {
518
703
  let i0 = i
@@ -611,8 +796,6 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
611
796
  }
612
797
  }
613
798
 
614
- Object.freeze({ao_done: true});
615
-
616
799
  function ao_defer_ctx(as_res = (...args) => args) {
617
800
  let y,n,_pset = (a,b) => { y=a, n=b; };
618
801
  return p =>(
@@ -696,138 +879,13 @@ function _ping_interval(send_ping) {
696
879
  if (td) {
697
880
  tid = setInterval(send_ping, 1000 * td);
698
881
 
699
-
700
-
882
+
883
+
701
884
 
702
- // ensure the interval allows the NodeJS event loop to exit
703
- tid.unref?.();
885
+ // ensure the interval allows the NodeJS event loop to exit
886
+ tid.unref?.();
704
887
  return true} }) }
705
888
 
706
- function parse(str, loose) {
707
- if (str instanceof RegExp) return { keys:false, pattern:str };
708
- var c, o, tmp, ext, keys=[], pattern='', arr = str.split('/');
709
- arr[0] || arr.shift();
710
-
711
- while (tmp = arr.shift()) {
712
- c = tmp[0];
713
- if (c === '*') {
714
- keys.push('wild');
715
- pattern += '/(.*)';
716
- } else if (c === ':') {
717
- o = tmp.indexOf('?', 1);
718
- ext = tmp.indexOf('.', 1);
719
- keys.push( tmp.substring(1, !!~o ? o : !!~ext ? ext : tmp.length) );
720
- pattern += !!~o && !~ext ? '(?:/([^/]+?))?' : '/([^/]+?)';
721
- if (!!~ext) pattern += (!!~o ? '?' : '') + '\\' + tmp.substring(ext);
722
- } else {
723
- pattern += '/' + tmp;
724
- }
725
- }
726
-
727
- return {
728
- keys: keys,
729
- pattern: new RegExp('^' + pattern + (loose ? '(?=$|\/)' : '\/?$'), 'i')
730
- };
731
- }
732
-
733
- // Use [regexparam][] for url-like topic parsing
734
-
735
- function _ignore(pkt, params, ctx) {ctx.done = true;}
736
-
737
- function _mqtt_topic_router() {
738
- let pri_lsts = [[],[]], rm = Symbol();
739
- let find = topic => _mqtt_routes_iter(pri_lsts, topic);
740
-
741
- return {find,
742
-
743
- add(topic_route, ...args) {
744
- let fn = args.pop();
745
- let priority = args.pop();
746
-
747
- if ('function' !== typeof fn) {
748
- if (false === fn) {
749
- fn = _ignore;}
750
- else throw new TypeError()}
751
-
752
- let rte = parse(
753
- topic_route.replace(/[+#]$/, '*'));
754
-
755
- rte.key = topic_route;
756
- rte.tgt = fn;
757
- pri_lsts[priority ? 0 : 1].push(rte);
758
- return this}
759
-
760
- , remove(topic_route, priority) {
761
- let lst = pri_lsts[priority ? 0 : 1];
762
- return _mqtt_route_remove([lst], topic_route)}
763
-
764
- , clear(priority) {
765
- pri_lsts[priority ? 0 : 1] = [];
766
- if (null == priority) {
767
- pri_lsts[1] = [];} }
768
-
769
- , async invoke(pkt, ctx) {
770
- ctx.idx = 0;
771
- ctx.rm = rm;
772
-
773
- for (let [fn, params] of find(pkt.topic)) {
774
- let res = await fn(pkt, params, ctx);
775
-
776
- if (rm === res) {
777
- _mqtt_route_remove(pri_lsts, fn);}
778
-
779
- if (ctx.done) {
780
- break}
781
- else ctx.idx++;}
782
-
783
- let {pkt_id, qos} = pkt;
784
- if (1 === qos) {
785
- await ctx.mqtt._send('puback', {pkt_id});} } } }
786
-
787
-
788
- function * _mqtt_routes_iter(all_route_lists, topic) {
789
- for (let route_list of all_route_lists) {
790
- for (let route of route_list) {
791
- let res = _mqtt_route_match_one(topic, route);
792
- if (undefined !== res) {
793
- yield res;} } } }
794
-
795
-
796
- function _mqtt_route_match_one(topic, {keys, pattern, tgt}) {
797
- let match = '/' !== topic[0]
798
- ? pattern.exec('/'+topic)
799
- : pattern.exec(topic);
800
-
801
- if (null === match) {
802
- return}
803
-
804
- if (false === keys) {
805
- let {groups} = match;
806
- if (! groups) {
807
- return [tgt]}
808
-
809
- let params = {};
810
- for (let k in groups) {
811
- params[k] = groups[k];}
812
-
813
- return [tgt, params]}
814
-
815
- if (0 === keys.length) {
816
- return [tgt]}
817
-
818
- let params = {};
819
- for (let i=0; i<keys.length; i++) {
820
- params[ keys[i] ] = match[1+i];}
821
- return [tgt, params]}
822
-
823
-
824
- function _mqtt_route_remove(all_route_lists, query) {
825
- let match = route => route===query || route.tgt===query || route.key===query;
826
- for (let lst of all_route_lists) {
827
- let i = lst.findIndex(match);
828
- if (0 <= i) {return !! lst.splice(i,1)} }
829
- return false}
830
-
831
889
  const _mqtt_cmdid_dispatch ={
832
890
  create(target) {
833
891
  return {__proto__: this, target, hashbelt: [new Map()]} }
@@ -896,8 +954,7 @@ const _mqtt_cmdid_dispatch ={
896
954
  let fn = target[`mqtt_${pkt.type}`]
897
955
  || target.mqtt_pkt;
898
956
 
899
- if (undefined !== fn) {
900
- await fn.call(target, pkt, ctx);} } })()) };
957
+ await fn?.call(target, pkt, ctx);} })()) };
901
958
 
902
959
  function _mqtt_dispatch(opt, target) {
903
960
  let _disp_ = _mqtt_cmdid_dispatch.create(target);
@@ -978,53 +1035,15 @@ class MQTTBase {
978
1035
 
979
1036
 
980
1037
  // alias: sub
981
- subscribe(pkt, ex) {
982
- pkt = _as_topics(pkt, ex);
1038
+ subscribe(pkt, ex, topic_prefix) {
1039
+ pkt = _as_topics(pkt, ex, topic_prefix);
983
1040
  return this._send('subscribe', pkt, pkt)}
984
- _sub_chain(topic, ex) {
985
- let res = this.subscribe([[ topic ]], ex);
986
- let subs = this.subs ||(this.subs = new Map());
987
- subs.set((res.topic = topic), (subs.last = res));
988
- return this }// fluent api -- return this and track side effects
989
1041
 
990
1042
  // alias: unsub
991
- unsubscribe(pkt, ex) {
992
- pkt = _as_topics(pkt, ex);
1043
+ unsubscribe(pkt, ex, topic_prefix) {
1044
+ pkt = _as_topics(pkt, ex, topic_prefix);
993
1045
  return this._send('unsubscribe', pkt, pkt)}
994
1046
 
995
- get on_topic() {return this.router.add}
996
-
997
- // alias: sub_topic
998
- subscribe_topic(topic_route, ...args) {
999
- this.router.add(topic_route, true, args.pop() );// handler
1000
- let topic = this.topic_for(topic_route);
1001
- return this._sub_chain(topic, args.pop() ) }// ex
1002
-
1003
- // alias: unsub_topic
1004
- unsubscribe_topic(topic_route) {
1005
- this.router.remove(topic_route, true);
1006
- let topic = this.topic_for(topic_route);
1007
- return this.unsubscribe([[ topic ]]) }
1008
-
1009
- // alias: shared_sub
1010
- shared_subscribe(group, topic_route, ...args) {
1011
- this.router.add(topic_route, true, args.pop() );// handler
1012
- let topic = this.topic_for(topic_route);
1013
- if (null != group) {
1014
- topic = `$share/${group}/${topic}`;}
1015
- return this._sub_chain(topic, args.pop() ) }// ex
1016
-
1017
- // alias: shared_unsub
1018
- shared_unsubscribe(group, topic_route) {
1019
- this.router.remove(topic_route, true);
1020
- let topic = this.topic_for(topic_route);
1021
- if (null != group) {
1022
- topic = `$share/${group}/${topic}`;}
1023
- return this.unsubscribe([[ topic ]]) }
1024
-
1025
- topic_for(topic_route) {
1026
- return topic_route.replace(/[:*].*$/, '#')}
1027
-
1028
1047
 
1029
1048
  // alias: pub
1030
1049
  publish(pkt, pub_opt) {return _pub(this, pkt, pub_opt)}
@@ -1050,9 +1069,9 @@ class MQTTBase {
1050
1069
  if (undefined === cid) {
1051
1070
  this.client_id = cid = (
1052
1071
 
1053
- this.sess_client_id(parts)
1072
+ this.sess_client_id(parts)
1054
1073
 
1055
-
1074
+
1056
1075
  );}
1057
1076
 
1058
1077
  return cid}
@@ -1061,66 +1080,85 @@ class MQTTBase {
1061
1080
  return [parts[0], Math.random().toString(36).slice(2), parts[1]].join('')}
1062
1081
 
1063
1082
 
1064
- sess_client_id(parts) {
1065
- let key = parts.join('\x20');
1066
- let cid = sessionStorage.getItem(key);
1067
- if (null == cid) {
1068
- cid = this.new_client_id(parts);
1069
- sessionStorage.setItem(key, cid);}
1070
- return cid}
1083
+ sess_client_id(parts) {
1084
+ let key = parts.join('\x20');
1085
+ let cid = sessionStorage.getItem(key);
1086
+ if (null == cid) {
1087
+ cid = this.new_client_id(parts);
1088
+ sessionStorage.setItem(key, cid);}
1089
+ return cid}
1071
1090
 
1072
1091
 
1073
1092
  // Internal API
1074
1093
 
1075
1094
  /* async _send(type, pkt) -- provided by _conn_ and transport */
1076
1095
 
1077
- _init_router(opt) {
1078
- return this.router = _mqtt_topic_router()}
1079
-
1080
1096
  _init_dispatch(opt) {
1097
+ this.constructor?._once_();
1098
+ let router = this.router =
1099
+ this._init_router?.(opt, this);
1100
+
1081
1101
  let tgt ={
1082
1102
  __proto__: opt.on_mqtt_type || {}
1083
- , router: this._init_router(opt, this)};
1103
+ , router};
1084
1104
 
1085
- tgt.mqtt_publish ||= tgt.router.invoke;
1086
- return _mqtt_dispatch(this, tgt)} }
1105
+ tgt.mqtt_publish ||= router?.invoke;
1106
+ return _mqtt_dispatch(this, tgt)}
1087
1107
 
1108
+ static _aliases() {
1109
+ return ' pub:publish sub:subscribe unsub:unsubscribe '}
1110
+
1111
+ static _once_(self=this) {
1112
+ self._once_ = _=>0;
1113
+ self.MQTTError = MQTTError;
1114
+ let p = self.prototype;
1115
+ for (let alias of self._aliases().split(/\s+/)) {
1116
+ alias = alias.split(':');
1117
+ let fn = alias[1] && p[alias[1]];
1118
+ if (fn) {p[alias[0]] = fn;} } } }
1088
1119
 
1089
- {
1090
- let p = MQTTBase.prototype;
1091
- Object.assign(p,{
1092
- MQTTError
1093
- , pub: p.publish
1094
- , sub: p.subscribe
1095
- , unsub: p.unsubscribe
1096
- , sub_topic: p.subscribe_topic
1097
- , unsub_topic: p.unsubscribe_topic
1098
- , shared_sub: p.shared_subscribe
1099
- , shared_unsub: p.shared_unsubscribe} );
1100
-
1101
- /*
1102
- p.on_mqtt_type = {
1103
- mqtt_auth(pkt, ctx) ::
1104
- mqtt_connect(pkt, ctx) ::
1105
- mqtt_connack(pkt, ctx) ::
1106
- mqtt_disconnect(pkt, ctx) ::
1107
-
1108
- mqtt_publish(pkt, ctx)
1109
- mqtt_subscribe(pkt, ctx) ::
1110
- mqtt_unsubscribe(pkt, ctx) ::
1111
-
1112
- mqtt_pingreq(pkt, ctx) ::
1113
- mqtt_pingresp(pkt, ctx) ::
1114
- }
1115
- */}
1116
1120
 
1121
+ /*
1122
+ on_mqtt_type = {
1123
+ mqtt_auth(pkt, ctx) ::
1124
+ mqtt_connect(pkt, ctx) ::
1125
+ mqtt_connack(pkt, ctx) ::
1126
+ mqtt_disconnect(pkt, ctx) ::
1127
+
1128
+ mqtt_publish(pkt, ctx)
1129
+ mqtt_subscribe(pkt, ctx) ::
1130
+ mqtt_unsubscribe(pkt, ctx) ::
1131
+
1132
+ mqtt_pingreq(pkt, ctx) ::
1133
+ mqtt_pingresp(pkt, ctx) ::
1134
+ }
1135
+ */
1117
1136
 
1118
- function _as_topics(pkt, ex) {
1119
- if ('string' === typeof pkt) {
1120
- return {topics:[pkt], ... ex}}
1121
- if (pkt[Symbol.iterator]) {
1122
- return {topics:[... pkt], ... ex}}
1123
- return ex ? {...pkt, ...ex} : pkt}
1137
+
1138
+ const _prefix_topics = (topic_prefix, iterable) =>
1139
+ Array.from(iterable, value =>(
1140
+ value.trim // string
1141
+ ? _prefix_topics(topic_prefix, value)
1142
+ : topic_prefix + value) );
1143
+
1144
+ function _as_topics(pkt, ex, topic_prefix) {
1145
+ if (ex?.trim) {// string
1146
+ topic_prefix = ex;
1147
+ ex = null;}
1148
+
1149
+ pkt =(
1150
+ pkt.trim // string
1151
+ ? {topics:[pkt], ... ex}
1152
+ : pkt[Symbol.iterator]
1153
+ ? {topics:[... pkt], ... ex}
1154
+ : ex ? {...pkt, ...ex}
1155
+ : pkt);
1156
+
1157
+ if (topic_prefix) {
1158
+ // particularly useful with shared queues, e.g.
1159
+ // topic_prefix = '$share/some-queue-name/'
1160
+ pkt.topics = _prefix_topics(topic_prefix, pkt.topics);}
1161
+ return pkt}
1124
1162
 
1125
1163
 
1126
1164
  async function _pub(self, pkt, pub_opt) {
@@ -1192,7 +1230,7 @@ class MQTTCore extends MQTTBase {
1192
1230
  return this}
1193
1231
 
1194
1232
  //log_conn(evt, arg, err_arg) ::
1195
- //console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1233
+ // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1196
1234
 
1197
1235
  on_live(client, is_reconnect) {
1198
1236
  if (is_reconnect) {
@@ -1234,30 +1272,55 @@ class MQTTCore extends MQTTBase {
1234
1272
  return this}
1235
1273
 
1236
1274
 
1237
-
1238
-
1239
-
1240
-
1241
-
1242
-
1243
-
1244
-
1245
-
1246
-
1247
-
1248
-
1249
-
1250
-
1251
-
1252
1275
 
1253
1276
 
1277
+
1278
+
1279
+
1280
+
1281
+
1282
+
1254
1283
 
1284
+
1285
+
1286
+
1287
+
1288
+
1255
1289
 
1290
+
1291
+
1292
+
1293
+
1294
+
1256
1295
 
1296
+
1297
+
1298
+
1299
+
1257
1300
 
1301
+
1302
+
1258
1303
 
1304
+
1305
+
1306
+
1307
+
1308
+
1309
+
1310
+
1259
1311
 
1312
+
1313
+
1314
+
1315
+
1316
+
1260
1317
 
1318
+
1319
+
1320
+
1321
+
1322
+
1323
+
1261
1324
 
1262
1325
 
1263
1326
  with_stream(read_stream, write_stream) {
@@ -1303,10 +1366,11 @@ class MQTTCore extends MQTTBase {
1303
1366
 
1304
1367
  return this} }
1305
1368
 
1306
- var version = "0.3.1";
1369
+ const version = '0.4.0';
1307
1370
 
1308
1371
  const MQTTClient_v4 = /* #__PURE__ */
1309
- MQTTCore.mqtt_ctx(4, mqtt_opts_v4);
1372
+ with_topic_path_router(
1373
+ MQTTCore.mqtt_ctx(4, mqtt_opts_v4) );
1310
1374
 
1311
1375
  const mqtt_v4 = opt =>
1312
1376
  new MQTTClient_v4(opt);