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