u8-mqtt 0.5.2 → 0.6.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 (116) hide show
  1. package/README.md +23 -6
  2. package/cjs/basic-v4.cjs +293 -284
  3. package/cjs/basic-v4.cjs.map +1 -1
  4. package/cjs/basic-v5.cjs +307 -288
  5. package/cjs/basic-v5.cjs.map +1 -1
  6. package/cjs/full-v4.cjs +1538 -0
  7. package/cjs/full-v4.cjs.map +1 -0
  8. package/cjs/full-v5.cjs +1812 -0
  9. package/cjs/full-v5.cjs.map +1 -0
  10. package/cjs/index.cjs +320 -302
  11. package/cjs/index.cjs.map +1 -1
  12. package/cjs/v4.cjs +305 -296
  13. package/cjs/v4.cjs.map +1 -1
  14. package/cjs/v5.cjs +319 -300
  15. package/cjs/v5.cjs.map +1 -1
  16. package/code/_cmdid_dispatch.jsy +45 -69
  17. package/code/_conn.jsy +96 -72
  18. package/code/_dispatch.jsy +36 -28
  19. package/code/_utils.jsy +17 -0
  20. package/code/base.jsy +35 -63
  21. package/code/core.jsy +78 -56
  22. package/code/full-v4.js +20 -0
  23. package/code/full-v5.js +29 -0
  24. package/code/router_path.jsy +2 -1
  25. package/esm/basic-v4.js +293 -284
  26. package/esm/basic-v4.js.map +1 -1
  27. package/esm/basic-v5.js +307 -288
  28. package/esm/basic-v5.js.map +1 -1
  29. package/esm/deno/basic-v4.js +297 -288
  30. package/esm/deno/basic-v4.js.map +1 -1
  31. package/esm/deno/basic-v5.js +311 -292
  32. package/esm/deno/basic-v5.js.map +1 -1
  33. package/esm/deno/full-v4.js +1526 -0
  34. package/esm/deno/full-v4.js.map +1 -0
  35. package/esm/deno/full-v5.js +1798 -0
  36. package/esm/deno/full-v5.js.map +1 -0
  37. package/esm/deno/index.js +324 -305
  38. package/esm/deno/index.js.map +1 -1
  39. package/esm/deno/v4.js +309 -300
  40. package/esm/deno/v4.js.map +1 -1
  41. package/esm/deno/v5.js +323 -304
  42. package/esm/deno/v5.js.map +1 -1
  43. package/esm/full-v4.js +1526 -0
  44. package/esm/full-v4.js.map +1 -0
  45. package/esm/full-v5.js +1798 -0
  46. package/esm/full-v5.js.map +1 -0
  47. package/esm/index.js +320 -301
  48. package/esm/index.js.map +1 -1
  49. package/esm/node/basic-v4.js +293 -284
  50. package/esm/node/basic-v4.js.map +1 -1
  51. package/esm/node/basic-v4.mjs +293 -284
  52. package/esm/node/basic-v4.mjs.map +1 -1
  53. package/esm/node/basic-v5.js +307 -288
  54. package/esm/node/basic-v5.js.map +1 -1
  55. package/esm/node/basic-v5.mjs +307 -288
  56. package/esm/node/basic-v5.mjs.map +1 -1
  57. package/esm/node/full-v4.js +1529 -0
  58. package/esm/node/full-v4.js.map +1 -0
  59. package/esm/node/full-v4.mjs +1529 -0
  60. package/esm/node/full-v4.mjs.map +1 -0
  61. package/esm/node/full-v5.js +1801 -0
  62. package/esm/node/full-v5.js.map +1 -0
  63. package/esm/node/full-v5.mjs +1801 -0
  64. package/esm/node/full-v5.mjs.map +1 -0
  65. package/esm/node/index.js +320 -301
  66. package/esm/node/index.js.map +1 -1
  67. package/esm/node/index.mjs +320 -301
  68. package/esm/node/index.mjs.map +1 -1
  69. package/esm/node/v4.js +305 -296
  70. package/esm/node/v4.js.map +1 -1
  71. package/esm/node/v4.mjs +305 -296
  72. package/esm/node/v4.mjs.map +1 -1
  73. package/esm/node/v5.js +319 -300
  74. package/esm/node/v5.js.map +1 -1
  75. package/esm/node/v5.mjs +319 -300
  76. package/esm/node/v5.mjs.map +1 -1
  77. package/esm/v4.js +305 -296
  78. package/esm/v4.js.map +1 -1
  79. package/esm/v5.js +319 -300
  80. package/esm/v5.js.map +1 -1
  81. package/esm/web/basic-v4.js +293 -284
  82. package/esm/web/basic-v4.js.map +1 -1
  83. package/esm/web/basic-v4.min.js +1 -1
  84. package/esm/web/basic-v4.min.js.br +0 -0
  85. package/esm/web/basic-v4.min.js.gz +0 -0
  86. package/esm/web/basic-v5.js +307 -288
  87. package/esm/web/basic-v5.js.map +1 -1
  88. package/esm/web/basic-v5.min.js +1 -1
  89. package/esm/web/basic-v5.min.js.br +0 -0
  90. package/esm/web/basic-v5.min.js.gz +0 -0
  91. package/esm/web/full-v4.js +1526 -0
  92. package/esm/web/full-v4.js.map +1 -0
  93. package/esm/web/full-v4.min.js +1 -0
  94. package/esm/web/full-v4.min.js.br +0 -0
  95. package/esm/web/full-v4.min.js.gz +0 -0
  96. package/esm/web/full-v5.js +1798 -0
  97. package/esm/web/full-v5.js.map +1 -0
  98. package/esm/web/full-v5.min.js +1 -0
  99. package/esm/web/full-v5.min.js.br +0 -0
  100. package/esm/web/full-v5.min.js.gz +0 -0
  101. package/esm/web/index.js +320 -301
  102. package/esm/web/index.js.map +1 -1
  103. package/esm/web/index.min.js +1 -1
  104. package/esm/web/index.min.js.br +0 -0
  105. package/esm/web/index.min.js.gz +0 -0
  106. package/esm/web/v4.js +305 -296
  107. package/esm/web/v4.js.map +1 -1
  108. package/esm/web/v4.min.js +1 -1
  109. package/esm/web/v4.min.js.br +0 -0
  110. package/esm/web/v4.min.js.gz +0 -0
  111. package/esm/web/v5.js +319 -300
  112. package/esm/web/v5.js.map +1 -1
  113. package/esm/web/v5.min.js +1 -1
  114. package/esm/web/v5.min.js.br +0 -0
  115. package/esm/web/v5.min.js.gz +0 -0
  116. package/package.json +7 -8
package/cjs/basic-v4.cjs CHANGED
@@ -606,6 +606,9 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
606
606
  return _encode_by_type[type]( mqtt_level, pkt ) },
607
607
 
608
608
  decode_pkt(b0, u8_body) {
609
+ if (b0.map) // Uint8Array in first arg
610
+ return mqtt_raw_dispatch(this)(b0)[0]
611
+
609
612
  let fn_decode = _decode_by_id[b0>>>4] || _decode_by_id[0];
610
613
  return fn_decode?.({__proto__: this.pkt_ctx, b0}, u8_body) },
611
614
 
@@ -618,88 +621,15 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
618
621
  }
619
622
  }
620
623
 
621
- function ao_defer_ctx(as_res = (...args) => args) {
622
- let y,n,_pset = (a,b) => { y=a, n=b; };
623
- return p =>(
624
- p = new Promise(_pset)
625
- , as_res(p, y, n)) }
626
-
627
- const ao_defer_v = /* #__PURE__ */
628
- ao_defer_ctx();
629
-
630
- Promise.resolve({type:'init'});
631
-
632
- function _mqtt_conn(client, [on_mqtt, pkt_future]) {
633
- let _q_init = ao_defer_v(), _q_ready = ao_defer_v();
634
- let _send_ready = async (...args) => (await _q_ready[0])(...args);
635
- let _send_mqtt_pkt, _has_connected;
636
- client._send = _send_ready;
637
-
638
- return {
639
- async when_ready() {await _q_ready[0];}
640
-
641
- , ping: _ping_interval (() =>_send_mqtt_pkt?.('pingreq'))
642
-
643
- , reset(err) {
644
- if (! _send_mqtt_pkt) {return}
645
-
646
- if (err) {
647
- _q_init[2](err);}
648
-
649
- _send_mqtt_pkt = null;
650
- _q_init = ao_defer_v();
651
- client._send = _send_ready;
652
-
653
- // call client.on_conn_reset in next promise microtask
654
- client.conn_emit('on_disconnect', false===err, err);}
655
-
656
- , async send_connect(... args) {
657
- if (! _send_mqtt_pkt) {
658
- await _q_init[0]; }// _send_mqtt_pkt is set before fulfilled
624
+ const _isfn = v => typeof v === 'function';
625
+ const _isstr = v => typeof v === 'string';
659
626
 
660
- // await connack response
661
- let res = await _send_mqtt_pkt(...args);
662
- if (0 == res[0].reason) {
663
- _has_connected = true;
664
- // resolve _q_ready[0] with _send_mqtt_pkt closure
665
- _q_ready[1](client._send = _send_mqtt_pkt);
666
- _q_ready = ao_defer_v();
667
- client.conn_emit('on_ready');}
668
-
669
- return res}
670
-
671
- , is_set: (() =>!! _send_mqtt_pkt)
672
- , set(mqtt_ctx, send_u8_pkt) {
673
- if (_send_mqtt_pkt) {
674
- throw new Error('Already connected')}
675
-
676
- mqtt_ctx = mqtt_ctx.mqtt_stream();
677
- let sess_ctx = {mqtt: client};
678
- let on_mqtt_chunk = u8_buf =>
679
- on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
680
-
681
- _send_mqtt_pkt = async (type, pkt, key) => {
682
- let res = undefined !== key
683
- ? pkt_future(key) : true;
684
-
685
- await send_u8_pkt(
686
- mqtt_ctx.encode_pkt(type, pkt));
687
-
688
- return res};
689
-
690
- _q_init[1](_send_mqtt_pkt); // resolve _q_init with _send_mqtt_pkt closure
691
-
692
- // call client.on_live in next promise microtask
693
- client.conn_emit('on_live', _has_connected);
694
- return on_mqtt_chunk} } }
695
-
696
-
697
- function _ping_interval(send_ping) {
627
+ function _interval(fn_callback) {
698
628
  let tid;
699
629
  return (( td ) => {
700
630
  tid = clearInterval(tid);
701
631
  if (td) {
702
- tid = setInterval(send_ping, 1000 * td);
632
+ tid = setInterval(fn_callback, 1000 * td);
703
633
 
704
634
 
705
635
 
@@ -708,172 +638,129 @@ function _ping_interval(send_ping) {
708
638
  tid.unref?.();
709
639
  return true} }) }
710
640
 
711
- const _mqtt_cmdid_dispatch ={
712
- create(target) {
713
- return {__proto__: this, target, hashbelt: [new Map()]} }
641
+ async function _mqtt_cmd_evt(target, answer, pkt, ctx) {
642
+ /* target : on_mqtt_type = {
643
+ mqtt_pkt(pkt, ctx) {}, // generic
644
+
645
+ mqtt_auth(pkt, ctx) {},
646
+ mqtt_connect(pkt, ctx) {},
647
+ mqtt_connack(pkt, ctx) {},
648
+ mqtt_disconnect(pkt, ctx) {},
649
+
650
+ mqtt_publish(pkt, ctx) {},
651
+ mqtt_subscribe(pkt, ctx) {},
652
+ mqtt_unsubscribe(pkt, ctx) {},
653
+
654
+ mqtt_pingreq(pkt, ctx) {},
655
+ mqtt_pingresp(pkt, ctx) {},
656
+ } */
657
+
658
+ let pkt_fn = target[`mqtt_${pkt.type}`] || target.mqtt_pkt;
659
+ await pkt_fn?.call(target, pkt, ctx);}
660
+
661
+ function _mqtt_cmd_type(target, answer, pkt, ctx) {
662
+ answer(pkt.type, pkt);
663
+ _mqtt_cmd_evt(target, answer, pkt, ctx);}
664
+
665
+ function _mqtt_cmd_id(target, answer, pkt) {
666
+ answer(pkt.pkt_id, pkt);}
667
+
668
+
669
+ const _mqtt_cmdids =[
670
+ _ => {} // 0x0 reserved
671
+ , _mqtt_cmd_evt // 0x1 connect
672
+ , _mqtt_cmd_type // 0x2 connack
673
+ , _mqtt_cmd_evt // 0x3 publish
674
+ , _mqtt_cmd_id // 0x4 puback
675
+ , _mqtt_cmd_id // 0x5 pubrec
676
+ , _mqtt_cmd_id // 0x6 pubrel
677
+ , _mqtt_cmd_id // 0x7 pubcomp
678
+ , _mqtt_cmd_evt // 0x8 subscribe
679
+ , _mqtt_cmd_id // 0x9 suback
680
+ , _mqtt_cmd_evt // 0xa unsubscribe
681
+ , _mqtt_cmd_id // 0xb unsuback
682
+ , _mqtt_cmd_type // 0xc pingreq
683
+ , _mqtt_cmd_type // 0xd pingresp
684
+ , _mqtt_cmd_evt // 0xe disconnect
685
+ , _mqtt_cmd_type ];// 0xf auth
714
686
 
715
- , bind_pkt_future(_pkt_id=100) {
716
- let {hashbelt} = this;
687
+ function _mqtt_dispatch(opt, target) {
688
+ let hashbelt=[], rotate_ts=0;
689
+ // default rotate at 1s across 5 buckets
690
+ let { td: rotate_td=1000, n: rotate_n=5 } = opt?.rotate || {};
717
691
 
718
- let _tmp_; // use _tmp_ to reuse _by_key closure
719
- let _by_key = answer_monad =>
720
- hashbelt[0].set(_tmp_, answer_monad);
692
+ // Promise / future scaffolding
693
+ let _pkt_id=100, _ftr_key; // use _ftr_key to reuse _by_key closure
694
+ let _ftr_by_key = fn_answer => hashbelt[0].set(_ftr_key, fn_answer);
721
695
 
722
- return (( pkt_or_key ) => {
723
- if ('string' === typeof pkt_or_key) {
724
- _tmp_ = pkt_or_key;}
725
- else {
726
- _pkt_id = (_pkt_id + 1) & 0xffff;
727
- _tmp_ = pkt_or_key.pkt_id = _pkt_id;}
696
+ on_mqtt([]); // init hashbelt and rotate_ts
697
+ return [on_mqtt, pkt_future]
728
698
 
729
- return new Promise(_by_key)}) }
730
699
 
731
- , answer(key, pkt) {
732
- for (let map of this.hashbelt) {
733
- let answer_monad = map.get(key);
734
- if (undefined !== answer_monad) {
700
+ function pkt_future(pkt_or_key) {
701
+ if (! _isstr(pkt_or_key)) {
702
+ _pkt_id = (_pkt_id + 1) & 0xffff; // 16-bit unsigned short
703
+ _ftr_key = pkt_or_key.pkt_id = _pkt_id;}
704
+ else _ftr_key = pkt_or_key;
705
+
706
+ return new Promise(_ftr_by_key)}
707
+
708
+ function answer(key, pkt) {
709
+ for (let map of hashbelt) {
710
+ let fn_answer = map.get(key);
711
+ if (fn_answer) {
735
712
  map.delete(key);
736
713
 
737
- answer_monad([pkt, /*err*/]); // option/maybe monad
714
+ fn_answer([pkt, /*err*/]); // option/maybe monad
738
715
  return true} }
739
716
  return false}
740
717
 
741
- , rotate_belt(n) {
742
- let {hashbelt} = this;
743
- hashbelt.unshift(new Map());
744
- for (let old of hashbelt.splice(n || 5)) {
745
- for (let answer_monad of old.values()) {
746
- answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
747
-
748
- , cmdids: ((() => {
749
- return [
750
- (() =>{} )// 0x0 reserved
751
- , by_evt // 0x1 connect
752
- , by_type // 0x2 connack
753
- , by_evt // 0x3 publish
754
- , by_id // 0x4 puback
755
- , by_id // 0x5 pubrec
756
- , by_id // 0x6 pubrel
757
- , by_id // 0x7 pubcomp
758
- , by_evt // 0x8 subscribe
759
- , by_id // 0x9 suback
760
- , by_evt // 0xa unsubscribe
761
- , by_id // 0xb unsuback
762
- , by_type // 0xc pingreq
763
- , by_type // 0xd pingresp
764
- , by_evt // 0xe disconnect
765
- , by_type ]// 0xf auth
766
-
767
-
768
- function by_id(disp, pkt) {
769
- disp.answer(pkt.pkt_id, pkt); }
770
-
771
- function by_type(disp, pkt, ctx) {
772
- disp.answer(pkt.type, pkt);
773
- by_evt(disp, pkt, ctx);}
774
-
775
- async function by_evt({target}, pkt, ctx) {
776
- let fn = target[`mqtt_${pkt.type}`]
777
- || target.mqtt_pkt;
778
-
779
- await fn?.call(target, pkt, ctx);} })()) };
780
-
781
- /*
782
- on_mqtt_type = {
783
- mqtt_auth(pkt, ctx) ::
784
- mqtt_connect(pkt, ctx) ::
785
- mqtt_connack(pkt, ctx) ::
786
- mqtt_disconnect(pkt, ctx) ::
787
-
788
- mqtt_publish(pkt, ctx)
789
- mqtt_subscribe(pkt, ctx) ::
790
- mqtt_unsubscribe(pkt, ctx) ::
791
-
792
- mqtt_pingreq(pkt, ctx) ::
793
- mqtt_pingresp(pkt, ctx) ::
794
- }
795
- */
796
-
797
- function _mqtt_dispatch(opt, target) {
798
- let _disp_ = _mqtt_cmdid_dispatch.create(target);
799
- let { cmdids } = _disp_;
800
-
801
- // default rotate at 1s across 5 buckets
802
- let { td: rotate_td=1000, n: rotate_n=5 } =
803
- opt && opt.rotate || {};
804
-
805
- let rotate_ts = rotate_td + Date.now();
806
-
807
- return [on_mqtt,
808
- _disp_.bind_pkt_future()]
809
-
810
718
  function on_mqtt(pkt_list, ctx) {
811
719
  for (let pkt of pkt_list) {
812
- cmdids[pkt.id](_disp_, pkt, ctx); }
720
+ _mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
813
721
 
814
- if (Date.now() > rotate_ts) {
815
- _disp_.rotate_belt(rotate_n);
816
- rotate_ts = rotate_td + Date.now();} } }
722
+ // rotate after rotate_ts
723
+ let now = Date.now();
724
+ if (now > rotate_ts) {
725
+ rotate_ts = rotate_td + now;
726
+ hashbelt.unshift(new Map());
727
+ while (hashbelt.length > rotate_n) {
728
+ for (let fn_answer of hashbelt.pop().values()) {
729
+ fn_answer([/*pkt*/, 'expired']); } } } } }// option/maybe monad
817
730
 
818
731
  class MQTTError extends Error {
819
732
  constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
733
+ // use hex-encoded reasons to match MQTT spec documentation
820
734
  super(`[0x${reason.toString(16)}] ${reason.reason}`);
821
735
  this.mqtt_pkt = mqtt_pkt;
822
736
  this.reason = reason;} }
823
737
 
824
738
  class MQTTBase {
825
- constructor(opt={}) {
826
- this.with(opt);
827
- this._conn_ = _mqtt_conn(this,
828
- this._init_dispatch(opt, this)); }
829
-
830
- with(fns_ns) {
831
- for (let [k,v] of Object.entries(fns_ns)) {
832
- if ('function' === typeof v) {this[k] = v;} }
833
- return this}
834
-
835
- async conn_emit(evt, arg, err_arg) {
836
- this.log_conn?.(evt, arg, err_arg);
837
- try {
838
- let fn_evt = this[await evt]; // microtask break using `await evt`
839
- if (fn_evt) {
840
- await fn_evt.call(this, this, arg, err_arg);}
841
- else if (err_arg) {throw err_arg} }
842
- catch (err) {
843
- this.on_error(err, evt);} }
844
-
845
- on_error(err, evt) {
846
- console.warn('[[u8-mqtt error: %s]]', evt, err); }
847
-
848
739
  // Handshaking Packets
849
-
850
740
  async connect(pkt={}) {
851
- let cid = pkt.client_id || this.client_id;
852
- if ('string' !== typeof cid) {
741
+ let cid = pkt.client_id;
742
+ if (! _isstr(cid)) {
853
743
  // see init_client_id implementation in core.jsy
854
- pkt.client_id = cid = this.init_client_id(cid);}
744
+ pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
855
745
  this.client_id = cid;
856
746
 
857
747
  if (null == pkt.keep_alive) {
858
748
  pkt.keep_alive = 60;}
859
749
 
860
- let res = await this._conn_
861
- .send_connect('connect', pkt, 'connack');
862
-
863
- if (0 != res[0].reason) {
864
- throw new this.MQTTError(res[0])}
865
-
866
- // TODO: merge with server's keep_alive frequency
867
- this._conn_.ping(pkt.keep_alive);
868
- return res}
750
+ let response = await this._send0('connect', pkt, 'connack');
751
+ if (0 != response[0].reason) {// compare to 0 to coerce to number
752
+ throw new this.MQTTError(response[0])}
753
+ return this.conn.on_conn(pkt, response)}
869
754
 
870
755
  async disconnect(pkt={}) {
871
- let res = await this._send('disconnect', pkt);
872
- this._conn_.reset(false);
873
- return res}
756
+ let response = await this._send0('disconnect', pkt);
757
+ return this.conn.on_dis(pkt, response)}
874
758
 
875
- auth(pkt={}) {
876
- return this._send('auth', pkt, 'auth')}
759
+ async auth(pkt={}) {
760
+ let response = await this._send0('auth', pkt, 'auth');
761
+ if (response[0].reason) {
762
+ throw new this.MQTTError(response[0])}
763
+ return this.conn.on_auth(pkt, response)}
877
764
 
878
765
  ping() {return this._send('pingreq', null, 'pingresp')}
879
766
  puback({pkt_id}) {return this._send('puback', {pkt_id})}
@@ -910,20 +797,18 @@ class MQTTBase {
910
797
  // alias: publish -- because 'pub' is shorter for semantic aliases above
911
798
  async pub(pkt, pub_opt) {
912
799
  if (undefined === pkt.payload) {
913
- if ('function' === typeof pub_opt) {
800
+ if (_isfn(pub_opt)) {
801
+ // pub_opt as a function is fn_encode value
914
802
  pub_opt = {fn_encode: pub_opt};}
915
803
 
916
- let {msg} = pkt;
917
- switch (typeof msg) {
918
- case 'function':
919
- pub_opt = {...pub_opt, fn_encode: msg};
920
- // flow into 'undefined' case
921
- case 'undefined':
922
- // return a single-value closure to publish packets
923
- return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
804
+ let msg = pkt.msg, fn_encode = pub_opt?.fn_encode;
805
+ if (null == msg || _isfn(msg)) {
806
+ // when msg is a function, return closure using fn_encode
807
+ if (msg) {pub_opt = {...pub_opt, fn_encode: msg};}
808
+ // return a single-value closure to publish packets
809
+ return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
924
810
 
925
811
  // Encode payload from msg; fn_encode allows alternative to JSON.stringify
926
- let {fn_encode} = pub_opt || {};
927
812
  pkt.payload = fn_encode
928
813
  ? await fn_encode(msg)
929
814
  : JSON.stringify(msg);}
@@ -935,31 +820,31 @@ class MQTTBase {
935
820
  pkt = pub_opt.xform(pkt) || pkt;} }
936
821
 
937
822
  return this._send('publish', pkt,
938
- pkt.qos ? pkt : void 0 ) }// key
823
+ pkt.qos ? pkt : null ) }// key
939
824
 
940
825
 
941
826
  // Internal API
942
827
 
943
- /* async _send(type, pkt) -- provided by _conn_ and transport */
828
+ /* async _send0(type, pkt) -- provided by conn and transport */
829
+ /* async _send(type, pkt) -- provided by conn and transport */
944
830
 
945
831
  _init_dispatch(opt) {
946
832
  this.constructor?._once_();
947
833
  let target ={__proto__: opt.on_mqtt_type};
948
834
  target.mqtt_publish ||=
949
835
  this._init_router?.(opt, this, target);
950
- return _mqtt_dispatch(this, target)}
836
+ return _mqtt_dispatch(opt, target)}
951
837
 
952
838
  static _aliases() {
953
839
  return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
954
840
 
955
- static _once_(self=this) {
956
- self._once_ = _=>0;
957
- let p = self.prototype;
841
+ static _once_(klass=this) {
842
+ klass._once_ = _=>0;
843
+ var alias, name, p = klass.prototype;
958
844
  p.MQTTError = MQTTError;
959
- for (let alias of self._aliases().split(/\s+/)) {
960
- alias = alias.split(':');
961
- let fn = alias[1] && p[alias[1]];
962
- if (fn) {p[alias[0]] = fn;} } } }
845
+ for (alias of klass._aliases().split(/\s+/)) {
846
+ [alias, name] = alias.split(':');
847
+ p[alias] = p[name];} } }
963
848
 
964
849
 
965
850
  function _as_topics(pkt, ex, topic_prefix) {
@@ -984,43 +869,177 @@ function _as_topics(pkt, ex, topic_prefix) {
984
869
  pkt.topics = pkt.topics.map(_prefix_topics);}
985
870
  return pkt}
986
871
 
987
- const pkt_api = {
988
- utf8(u8) { return new TextDecoder('utf-8').decode(u8 || this.payload ) },
989
- json(u8) { return JSON.parse( this.utf8(u8) || null ) },
990
- text(u8) { return this.utf8(u8) },
991
- };
872
+ const _defer_obj = o =>(
873
+ o.p = new Promise((a,e) => { o.a=a; o.e=e; })
874
+ , o);
875
+
876
+ function _dfn_reset(client, attr, fn_after) {
877
+ // a resetable deferred for a function
878
+ let self = {set}, afn = async (...args) => (await self.p)(...args);
879
+ return set()
880
+
881
+ function set() {
882
+ if (afn !== client[attr]) {
883
+ _defer_obj(self).p.then(fn_after, _=>0);
884
+ client[attr] = afn;}
885
+ return self} }
886
+
887
+ function _mqtt_conn(opt, client, [on_mqtt, pkt_future]) {
888
+ let _abort;
889
+ let _dfn_send0 = _dfn_reset(client, '_send0', // client._send0 getter/setter
890
+ _=> client.conn_emit('on_live', conn.has_connected));
891
+ let _dfn_ready = _dfn_reset(client, '_send', // client._send getter/setter
892
+ _=> client.conn_emit('on_ready'));
893
+ let _keep_alive_ival = _interval (() =>client._send0('pingreq') );// resettable interval for keep_alive ping
894
+
895
+ let conn = Object.create({
896
+ ping: (td=conn.keep_alive) => _keep_alive_ival(td)
897
+
898
+ , on_conn(pkt, response) {
899
+ conn.has_connected = true;
900
+ conn.keep_alive = opt.keep_alive || response[0].props?.server_keep_alive || pkt.keep_alive;
901
+ client.conn_emit('on_conn');
902
+ return opt.use_auth
903
+ ? response // wait on enhanced authentication step
904
+ : conn.on_auth(null, response) }// otherwise, connect is also auth
905
+
906
+ , on_auth(pkt, response) {
907
+ _dfn_ready.a(_dfn_send0.p);
908
+ if (0 != opt.keep_alive) {
909
+ conn.ping();}
910
+ client.conn_emit('on_auth', !pkt);
911
+ return response}
912
+
913
+ , on_dis(pkt, response) {
914
+ conn.reset(false);
915
+ return response}
916
+
917
+ , reset(err) {
918
+ if (err) {
919
+ _dfn_send0.e(err); }// send error to uses of _send0 (connect, auth)
920
+ _abort.e(err); // abort in-progress connections
921
+
922
+ delete conn.is_set;
923
+ conn.ready = handshake();
924
+ client.conn_emit('on_disconnect', false===err, err);}
925
+
926
+ , abort() {
927
+ _dfn_ready.e(err); // abort all messages awaiting ready state
928
+ return conn.reset(err)}
929
+
930
+ , async setup(gate, send_u8_pkt, init_msg_loop) {
931
+ if (conn.is_set) {
932
+ throw new Error() }// already in-progress
933
+
934
+ conn.is_set = true;
935
+ await gate;
936
+
937
+ // setup send/recv MQTT parsing context
938
+ let mqtt_ctx = client.mqtt_ctx.mqtt_stream();
939
+
940
+ {// setup inbound message loop
941
+ let sess_ctx = {mqtt: client}; // mutable session context
942
+ let on_mqtt_chunk = u8 => on_mqtt(mqtt_ctx.decode(u8), sess_ctx);
943
+ init_msg_loop(on_mqtt_chunk, conn);}
944
+
945
+ // setup outbound message path and transport connection
946
+ send_u8_pkt = await send_u8_pkt;
947
+ _dfn_send0.a(
948
+ async (type, pkt, key) => {
949
+ let res = undefined !== key
950
+ ? pkt_future(key) : true;
951
+
952
+ await send_u8_pkt(
953
+ mqtt_ctx.encode_pkt(type, pkt));
954
+ return res} ); } });
955
+
956
+ conn.ready = handshake();
957
+ return conn
958
+
959
+ async function handshake() {
960
+ _abort = _defer_obj({});
961
+
962
+ _keep_alive_ival(0); // clearInterval on keep alive ping
963
+ _dfn_send0.set(); // reset client._send0 if necessary
964
+ _dfn_ready.set(); // reset client._send if necessary
965
+
966
+ try {
967
+ // set client._send0 as passtrhough after transport connection
968
+ client._send0 = await Promise.race([_dfn_send0.p, _abort.p]);
969
+
970
+ // set client._send as passtrhough after ready
971
+ client._send = await Promise.race([_dfn_ready.p, _abort.p]);
972
+ return true}
973
+ catch (err) {
974
+ return false} } }
975
+
976
+ const pkt_api ={
977
+ utf8(u8) {return new TextDecoder('utf-8').decode(u8 || this.payload )}
978
+ , json(u8) {return JSON.parse( this.utf8(u8) || null )}
979
+ , text(u8) {return this.utf8(u8)} };
980
+
981
+ const opt_default ={
982
+ sess_stg: globalThis.sessionStorage};
992
983
 
993
984
  class MQTTCore extends MQTTBase {
985
+ constructor(opt) {
986
+ super();
987
+ this.with(opt);
988
+ opt ={...opt_default, ...opt};
989
+ // settings for MQTTCore
990
+ this.sess_stg = opt.sess_stg;
991
+ // setup connection and dispatch
992
+ this.conn = _mqtt_conn(opt, this,
993
+ this._init_dispatch(opt)); }
994
+
995
+ with(fns_ns) {
996
+ for (let [k,v] of Object.entries(fns_ns)) {
997
+ if (_isfn(v)) {this[k] = v;} }
998
+ return this}
999
+
1000
+
994
1001
  static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
995
- let self = class extends this {};
996
- self.prototype.mqtt_ctx =
1002
+ let klass = class extends this {};
1003
+ klass.prototype.mqtt_ctx =
997
1004
  mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
998
- return self}
1005
+ return klass}
1006
+
1007
+
1008
+ async conn_emit(evt, arg, err_arg) {
1009
+ this.log_conn?.(evt, arg, err_arg);
1010
+ try {
1011
+ let fn_evt = this[await evt]; // microtask break using `await evt`
1012
+ if (fn_evt) {
1013
+ await fn_evt.call(this, this, arg, err_arg);}
1014
+ else if (err_arg) {throw err_arg} }
1015
+ catch (err) {
1016
+ this.on_error(err, evt);} }
1017
+
1018
+ on_error(err, evt) {
1019
+ console.warn('[[u8-mqtt error: %s]]', evt, err); }
1020
+ log_conn(evt, arg, err_arg) {
1021
+ console.info('[[u8-mqtt conn: %s]]', evt, arg, err_arg); }
999
1022
 
1000
1023
 
1001
1024
  // automatic Client Id for connect()
1002
1025
  init_client_id(parts=['u8-mqtt--','']) {
1003
- let sess_stg=this.sess_stg;
1026
+ let sess_stg = this.sess_stg;
1004
1027
  let key, cid = sess_stg?.getItem(key=parts.join(' '));
1005
1028
  if (! cid) {
1006
1029
  cid = parts.join(Math.random().toString(36).slice(2));
1007
1030
  sess_stg?.setItem(key, cid);}
1008
1031
  return cid}
1009
1032
 
1010
- get sess_stg() {return globalThis.sessionStorage}
1011
-
1012
-
1013
- //on_error(err, evt) ::
1014
- // console.warn @ '[[u8-mqtt error: %s]]', evt, err
1015
-
1016
- //log_conn(evt, arg, err_arg) ::
1017
- // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1018
1033
 
1019
1034
  on_live(client, is_reconnect) {
1020
1035
  if (is_reconnect) {
1021
1036
  return client.connect()} }
1022
1037
 
1023
- //on_reconnect(client) ::
1038
+ // on_ready(client) ::
1039
+ // on_reconnect(client) ::
1040
+ on_disconnect(client, intentional) {
1041
+ if (! intentional) {
1042
+ return client.on_reconnect?.()} }
1024
1043
 
1025
1044
  _use_conn(fn_reconnect) {
1026
1045
  return (this.reconnect = fn_reconnect)?.()}
@@ -1032,27 +1051,20 @@ class MQTTCore extends MQTTBase {
1032
1051
  .then(this.reconnect)
1033
1052
  .then(opt.reconnect, opt.error);} }) }
1034
1053
 
1035
- on_disconnect(client, intentional) {
1036
- if (! intentional) {
1037
- return client.on_reconnect?.()} }
1038
-
1039
1054
  delay(ms) {
1040
1055
  return new Promise(done => setTimeout(done, ms)) }
1041
1056
 
1042
1057
  with_async_iter(async_iter, write_u8_pkt) {
1043
- let on_mqtt_chunk = this._conn_.set(
1044
- this.mqtt_ctx,
1045
- write_u8_pkt);
1046
-
1047
- this._msg_loop = ((async () => {
1048
- try {
1049
- async_iter = await async_iter;
1050
- for await (let chunk of async_iter)
1051
- on_mqtt_chunk(chunk);
1052
- this._conn_.reset();}
1053
- catch (err) {
1054
- this._conn_.reset(err);} })());
1055
-
1058
+ this.conn.setup(async_iter,
1059
+ write_u8_pkt,
1060
+ async (on_mqtt_chunk, conn) => {
1061
+ try {
1062
+ async_iter = await async_iter;
1063
+ for await (let chunk of async_iter)
1064
+ on_mqtt_chunk(chunk);
1065
+ conn.reset();}
1066
+ catch (err) {
1067
+ conn.reset(err);} } );
1056
1068
  return this}
1057
1069
 
1058
1070
 
@@ -1123,33 +1135,30 @@ class MQTTCore extends MQTTBase {
1123
1135
 
1124
1136
  websock.binaryType = 'arraybuffer';
1125
1137
 
1126
- let ready, {readyState} = websock;
1138
+ let ws_ready, readyState = websock.readyState;
1127
1139
  if (1 !== readyState) {
1128
1140
  if (0 !== readyState) {
1129
- throw new Error('Invalid WebSocket readyState') }
1130
-
1131
- ready = new Promise(fn => websock.onopen = fn); }
1141
+ throw new Error('WS readyState') }
1132
1142
 
1143
+ ws_ready = new Promise(ready => websock.onopen = ready); }
1133
1144
 
1134
- let {_conn_} = this;
1135
- let on_mqtt_chunk = _conn_.set(
1136
- this.mqtt_ctx,
1137
- async u8_pkt =>(
1138
- await ready
1139
- , websock.send(u8_pkt)) );
1145
+ this.conn.setup(ws_ready,
1146
+ u8_pkt => websock.send(u8_pkt),
1147
+ (on_mqtt_chunk, conn) => {
1148
+ websock.onmessage = evt =>(
1149
+ on_mqtt_chunk(new Uint8Array(evt.data)) );
1140
1150
 
1141
- websock.onmessage = evt =>(on_mqtt_chunk(new Uint8Array(evt.data)));
1142
- websock.onclose = evt => {
1143
- if (! evt.wasClean) {
1144
- var err = new Error('websocket connection close');
1145
- err.code = evt.code;
1146
- err.reason = evt.reason;}
1151
+ websock.onclose = evt => {
1152
+ if (! evt.wasClean) {
1153
+ var err = new Error('websocket close');
1154
+ err.code = evt.code;
1155
+ err.reason = evt.reason;}
1147
1156
 
1148
- _conn_.reset(err);};
1157
+ conn.reset(err);}; } );
1149
1158
 
1150
1159
  return this} }
1151
1160
 
1152
- const version = '0.5.2-node';
1161
+ const version = '0.6.0-node';
1153
1162
 
1154
1163
  const MQTTClient_v4 = /* #__PURE__ */
1155
1164
  MQTTCore.mqtt_ctx(4, mqtt_opts_v4);