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