u8-mqtt 0.5.3 → 0.6.1

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 (114) hide show
  1. package/README.md +1 -1
  2. package/cjs/basic-v4.cjs +283 -277
  3. package/cjs/basic-v4.cjs.map +1 -1
  4. package/cjs/basic-v5.cjs +297 -281
  5. package/cjs/basic-v5.cjs.map +1 -1
  6. package/cjs/full-v4.cjs +295 -289
  7. package/cjs/full-v4.cjs.map +1 -1
  8. package/cjs/full-v5.cjs +309 -293
  9. package/cjs/full-v5.cjs.map +1 -1
  10. package/cjs/index.cjs +310 -295
  11. package/cjs/index.cjs.map +1 -1
  12. package/cjs/v4.cjs +295 -289
  13. package/cjs/v4.cjs.map +1 -1
  14. package/cjs/v5.cjs +309 -293
  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 +33 -61
  21. package/code/core.jsy +73 -51
  22. package/code/router_path.jsy +2 -1
  23. package/esm/basic-v4.js +283 -277
  24. package/esm/basic-v4.js.map +1 -1
  25. package/esm/basic-v5.js +297 -281
  26. package/esm/basic-v5.js.map +1 -1
  27. package/esm/deno/basic-v4.js +287 -281
  28. package/esm/deno/basic-v4.js.map +1 -1
  29. package/esm/deno/basic-v5.js +301 -285
  30. package/esm/deno/basic-v5.js.map +1 -1
  31. package/esm/deno/full-v4.js +299 -293
  32. package/esm/deno/full-v4.js.map +1 -1
  33. package/esm/deno/full-v5.js +313 -297
  34. package/esm/deno/full-v5.js.map +1 -1
  35. package/esm/deno/index.js +314 -298
  36. package/esm/deno/index.js.map +1 -1
  37. package/esm/deno/v4.js +299 -293
  38. package/esm/deno/v4.js.map +1 -1
  39. package/esm/deno/v5.js +313 -297
  40. package/esm/deno/v5.js.map +1 -1
  41. package/esm/full-v4.js +295 -289
  42. package/esm/full-v4.js.map +1 -1
  43. package/esm/full-v5.js +309 -293
  44. package/esm/full-v5.js.map +1 -1
  45. package/esm/index.js +310 -294
  46. package/esm/index.js.map +1 -1
  47. package/esm/node/basic-v4.js +283 -277
  48. package/esm/node/basic-v4.js.map +1 -1
  49. package/esm/node/basic-v4.mjs +283 -277
  50. package/esm/node/basic-v4.mjs.map +1 -1
  51. package/esm/node/basic-v5.js +297 -281
  52. package/esm/node/basic-v5.js.map +1 -1
  53. package/esm/node/basic-v5.mjs +297 -281
  54. package/esm/node/basic-v5.mjs.map +1 -1
  55. package/esm/node/full-v4.js +295 -289
  56. package/esm/node/full-v4.js.map +1 -1
  57. package/esm/node/full-v4.mjs +295 -289
  58. package/esm/node/full-v4.mjs.map +1 -1
  59. package/esm/node/full-v5.js +309 -293
  60. package/esm/node/full-v5.js.map +1 -1
  61. package/esm/node/full-v5.mjs +309 -293
  62. package/esm/node/full-v5.mjs.map +1 -1
  63. package/esm/node/index.js +310 -294
  64. package/esm/node/index.js.map +1 -1
  65. package/esm/node/index.mjs +310 -294
  66. package/esm/node/index.mjs.map +1 -1
  67. package/esm/node/v4.js +295 -289
  68. package/esm/node/v4.js.map +1 -1
  69. package/esm/node/v4.mjs +295 -289
  70. package/esm/node/v4.mjs.map +1 -1
  71. package/esm/node/v5.js +309 -293
  72. package/esm/node/v5.js.map +1 -1
  73. package/esm/node/v5.mjs +309 -293
  74. package/esm/node/v5.mjs.map +1 -1
  75. package/esm/v4.js +295 -289
  76. package/esm/v4.js.map +1 -1
  77. package/esm/v5.js +309 -293
  78. package/esm/v5.js.map +1 -1
  79. package/esm/web/basic-v4.js +283 -277
  80. package/esm/web/basic-v4.js.map +1 -1
  81. package/esm/web/basic-v4.min.js +1 -1
  82. package/esm/web/basic-v4.min.js.br +0 -0
  83. package/esm/web/basic-v4.min.js.gz +0 -0
  84. package/esm/web/basic-v5.js +297 -281
  85. package/esm/web/basic-v5.js.map +1 -1
  86. package/esm/web/basic-v5.min.js +1 -1
  87. package/esm/web/basic-v5.min.js.br +0 -0
  88. package/esm/web/basic-v5.min.js.gz +0 -0
  89. package/esm/web/full-v4.js +295 -289
  90. package/esm/web/full-v4.js.map +1 -1
  91. package/esm/web/full-v4.min.js +1 -1
  92. package/esm/web/full-v4.min.js.br +0 -0
  93. package/esm/web/full-v4.min.js.gz +0 -0
  94. package/esm/web/full-v5.js +309 -293
  95. package/esm/web/full-v5.js.map +1 -1
  96. package/esm/web/full-v5.min.js +1 -1
  97. package/esm/web/full-v5.min.js.br +0 -0
  98. package/esm/web/full-v5.min.js.gz +0 -0
  99. package/esm/web/index.js +310 -294
  100. package/esm/web/index.js.map +1 -1
  101. package/esm/web/index.min.js +1 -1
  102. package/esm/web/index.min.js.br +0 -0
  103. package/esm/web/index.min.js.gz +0 -0
  104. package/esm/web/v4.js +295 -289
  105. package/esm/web/v4.js.map +1 -1
  106. package/esm/web/v4.min.js +1 -1
  107. package/esm/web/v4.min.js.br +0 -0
  108. package/esm/web/v4.min.js.gz +0 -0
  109. package/esm/web/v5.js +309 -293
  110. package/esm/web/v5.js.map +1 -1
  111. package/esm/web/v5.min.js +1 -1
  112. package/esm/web/v5.min.js.br +0 -0
  113. package/esm/web/v5.min.js.gz +0 -0
  114. package/package.json +7 -8
package/cjs/v4.cjs CHANGED
@@ -547,6 +547,23 @@ function parse(str, loose) {
547
547
  };
548
548
  }
549
549
 
550
+ const _isfn = v => typeof v === 'function';
551
+ const _isstr = v => typeof v === 'string';
552
+
553
+ function _interval(fn_callback) {
554
+ let tid;
555
+ return (( td ) => {
556
+ tid = clearInterval(tid);
557
+ if (td) {
558
+ tid = setInterval(fn_callback, 1000 * td);
559
+
560
+
561
+
562
+
563
+ // ensure the interval allows the NodeJS event loop to exit
564
+ tid.unref?.();
565
+ return true} }) }
566
+
550
567
  /*
551
568
  class AbstractTopicRouter ::
552
569
  async invoke(pkt, ctx) ::
@@ -637,7 +654,7 @@ function mqtt_topic_path_router() {
637
654
  let fn = args.pop();
638
655
  let priority = args.pop();
639
656
 
640
- if ('function' !== typeof fn) {
657
+ if (! _isfn(fn)) {
641
658
  if (fn) {throw new TypeError()}
642
659
  fn = _ignore;}
643
660
 
@@ -802,238 +819,108 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
802
819
  }
803
820
  }
804
821
 
805
- function ao_defer_ctx(as_res = (...args) => args) {
806
- let y,n,_pset = (a,b) => { y=a, n=b; };
807
- return p =>(
808
- p = new Promise(_pset)
809
- , as_res(p, y, n)) }
810
-
811
- const ao_defer_v = /* #__PURE__ */
812
- ao_defer_ctx();
813
-
814
- Promise.resolve({type:'init'});
815
-
816
- function _mqtt_conn(client, [on_mqtt, pkt_future]) {
817
- let _q_init = ao_defer_v(), _q_ready = ao_defer_v();
818
- let _send_ready = async (...args) => (await _q_ready[0])(...args);
819
- let _send_mqtt_pkt, _has_connected;
820
- client._send = _send_ready;
821
-
822
- return {
823
- async when_ready() {await _q_ready[0];}
824
-
825
- , ping: _ping_interval (() =>_send_mqtt_pkt?.('pingreq'))
826
-
827
- , reset(err) {
828
- if (! _send_mqtt_pkt) {return}
829
-
830
- if (err) {
831
- _q_init[2](err);}
832
-
833
- _send_mqtt_pkt = null;
834
- _q_init = ao_defer_v();
835
- client._send = _send_ready;
836
-
837
- // call client.on_conn_reset in next promise microtask
838
- client.conn_emit('on_disconnect', false===err, err);}
839
-
840
- , async send_connect(... args) {
841
- if (! _send_mqtt_pkt) {
842
- await _q_init[0]; }// _send_mqtt_pkt is set before fulfilled
843
-
844
- // await connack response
845
- let res = await _send_mqtt_pkt(...args);
846
- if (0 == res[0].reason) {
847
- _has_connected = true;
848
- // resolve _q_ready[0] with _send_mqtt_pkt closure
849
- _q_ready[1](client._send = _send_mqtt_pkt);
850
- _q_ready = ao_defer_v();
851
- client.conn_emit('on_ready');}
852
-
853
- return res}
854
-
855
- , is_set: (() =>!! _send_mqtt_pkt)
856
- , set(mqtt_ctx, send_u8_pkt) {
857
- if (_send_mqtt_pkt) {
858
- throw new Error('Already connected')}
859
-
860
- mqtt_ctx = mqtt_ctx.mqtt_stream();
861
- let sess_ctx = {mqtt: client};
862
- let on_mqtt_chunk = u8_buf =>
863
- on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
864
-
865
- _send_mqtt_pkt = async (type, pkt, key) => {
866
- let res = undefined !== key
867
- ? pkt_future(key) : true;
868
-
869
- await send_u8_pkt(
870
- mqtt_ctx.encode_pkt(type, pkt));
822
+ async function _mqtt_cmd_evt(target, answer, pkt, ctx) {
823
+ /* target : on_mqtt_type = {
824
+ mqtt_pkt(pkt, ctx) {}, // generic
825
+
826
+ mqtt_auth(pkt, ctx) {},
827
+ mqtt_connect(pkt, ctx) {},
828
+ mqtt_connack(pkt, ctx) {},
829
+ mqtt_disconnect(pkt, ctx) {},
830
+
831
+ mqtt_publish(pkt, ctx) {},
832
+ mqtt_subscribe(pkt, ctx) {},
833
+ mqtt_unsubscribe(pkt, ctx) {},
834
+
835
+ mqtt_pingreq(pkt, ctx) {},
836
+ mqtt_pingresp(pkt, ctx) {},
837
+ } */
838
+
839
+ let pkt_fn = target[`mqtt_${pkt.type}`] || target.mqtt_pkt;
840
+ await pkt_fn?.call(target, pkt, ctx);}
841
+
842
+ function _mqtt_cmd_type(target, answer, pkt, ctx) {
843
+ answer(pkt.type, pkt);
844
+ _mqtt_cmd_evt(target, answer, pkt, ctx);}
845
+
846
+ function _mqtt_cmd_id(target, answer, pkt) {
847
+ answer(pkt.pkt_id, pkt);}
848
+
849
+
850
+ const _mqtt_cmdids =[
851
+ _ => {} // 0x0 reserved
852
+ , _mqtt_cmd_evt // 0x1 connect
853
+ , _mqtt_cmd_type // 0x2 connack
854
+ , _mqtt_cmd_evt // 0x3 publish
855
+ , _mqtt_cmd_id // 0x4 puback
856
+ , _mqtt_cmd_id // 0x5 pubrec
857
+ , _mqtt_cmd_id // 0x6 pubrel
858
+ , _mqtt_cmd_id // 0x7 pubcomp
859
+ , _mqtt_cmd_evt // 0x8 subscribe
860
+ , _mqtt_cmd_id // 0x9 suback
861
+ , _mqtt_cmd_evt // 0xa unsubscribe
862
+ , _mqtt_cmd_id // 0xb unsuback
863
+ , _mqtt_cmd_type // 0xc pingreq
864
+ , _mqtt_cmd_type // 0xd pingresp
865
+ , _mqtt_cmd_evt // 0xe disconnect
866
+ , _mqtt_cmd_type ];// 0xf auth
871
867
 
872
- return res};
873
-
874
- _q_init[1](_send_mqtt_pkt); // resolve _q_init with _send_mqtt_pkt closure
875
-
876
- // call client.on_live in next promise microtask
877
- client.conn_emit('on_live', _has_connected);
878
- return on_mqtt_chunk} } }
879
-
880
-
881
- function _ping_interval(send_ping) {
882
- let tid;
883
- return (( td ) => {
884
- tid = clearInterval(tid);
885
- if (td) {
886
- tid = setInterval(send_ping, 1000 * td);
887
-
888
-
889
-
890
-
891
- // ensure the interval allows the NodeJS event loop to exit
892
- tid.unref?.();
893
- return true} }) }
868
+ function _mqtt_dispatch(opt, target) {
869
+ let hashbelt=[], rotate_ts=0;
870
+ // default rotate at 1s across 5 buckets
871
+ let { td: rotate_td=1000, n: rotate_n=5 } = opt?.rotate || {};
894
872
 
895
- const _mqtt_cmdid_dispatch ={
896
- create(target) {
897
- return {__proto__: this, target, hashbelt: [new Map()]} }
873
+ // Promise / future scaffolding
874
+ let _pkt_id=100, _ftr_key; // use _ftr_key to reuse _by_key closure
875
+ let _ftr_by_key = fn_answer => hashbelt[0].set(_ftr_key, fn_answer);
898
876
 
899
- , bind_pkt_future(_pkt_id=100) {
900
- let {hashbelt} = this;
877
+ on_mqtt([]); // init hashbelt and rotate_ts
878
+ return [on_mqtt, pkt_future]
901
879
 
902
- let _tmp_; // use _tmp_ to reuse _by_key closure
903
- let _by_key = answer_monad =>
904
- hashbelt[0].set(_tmp_, answer_monad);
905
880
 
906
- return (( pkt_or_key ) => {
907
- if ('string' === typeof pkt_or_key) {
908
- _tmp_ = pkt_or_key;}
909
- else {
910
- _pkt_id = (_pkt_id + 1) & 0xffff;
911
- _tmp_ = pkt_or_key.pkt_id = _pkt_id;}
881
+ function pkt_future(pkt_or_key) {
882
+ if (! _isstr(pkt_or_key)) {
883
+ _pkt_id = (_pkt_id + 1) & 0xffff; // 16-bit unsigned short
884
+ _ftr_key = pkt_or_key.pkt_id = _pkt_id;}
885
+ else _ftr_key = pkt_or_key;
912
886
 
913
- return new Promise(_by_key)}) }
887
+ return new Promise(_ftr_by_key)}
914
888
 
915
- , answer(key, pkt) {
916
- for (let map of this.hashbelt) {
917
- let answer_monad = map.get(key);
918
- if (undefined !== answer_monad) {
889
+ function answer(key, pkt) {
890
+ for (let map of hashbelt) {
891
+ let fn_answer = map.get(key);
892
+ if (fn_answer) {
919
893
  map.delete(key);
920
894
 
921
- answer_monad([pkt, /*err*/]); // option/maybe monad
895
+ fn_answer([pkt, /*err*/]); // option/maybe monad
922
896
  return true} }
923
897
  return false}
924
898
 
925
- , rotate_belt(n) {
926
- let {hashbelt} = this;
927
- hashbelt.unshift(new Map());
928
- for (let old of hashbelt.splice(n || 5)) {
929
- for (let answer_monad of old.values()) {
930
- answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
931
-
932
- , cmdids: ((() => {
933
- return [
934
- (() =>{} )// 0x0 reserved
935
- , by_evt // 0x1 connect
936
- , by_type // 0x2 connack
937
- , by_evt // 0x3 publish
938
- , by_id // 0x4 puback
939
- , by_id // 0x5 pubrec
940
- , by_id // 0x6 pubrel
941
- , by_id // 0x7 pubcomp
942
- , by_evt // 0x8 subscribe
943
- , by_id // 0x9 suback
944
- , by_evt // 0xa unsubscribe
945
- , by_id // 0xb unsuback
946
- , by_type // 0xc pingreq
947
- , by_type // 0xd pingresp
948
- , by_evt // 0xe disconnect
949
- , by_type ]// 0xf auth
950
-
951
-
952
- function by_id(disp, pkt) {
953
- disp.answer(pkt.pkt_id, pkt); }
954
-
955
- function by_type(disp, pkt, ctx) {
956
- disp.answer(pkt.type, pkt);
957
- by_evt(disp, pkt, ctx);}
958
-
959
- async function by_evt({target}, pkt, ctx) {
960
- let fn = target[`mqtt_${pkt.type}`]
961
- || target.mqtt_pkt;
962
-
963
- await fn?.call(target, pkt, ctx);} })()) };
964
-
965
- /*
966
- on_mqtt_type = {
967
- mqtt_auth(pkt, ctx) ::
968
- mqtt_connect(pkt, ctx) ::
969
- mqtt_connack(pkt, ctx) ::
970
- mqtt_disconnect(pkt, ctx) ::
971
-
972
- mqtt_publish(pkt, ctx)
973
- mqtt_subscribe(pkt, ctx) ::
974
- mqtt_unsubscribe(pkt, ctx) ::
975
-
976
- mqtt_pingreq(pkt, ctx) ::
977
- mqtt_pingresp(pkt, ctx) ::
978
- }
979
- */
980
-
981
- function _mqtt_dispatch(opt, target) {
982
- let _disp_ = _mqtt_cmdid_dispatch.create(target);
983
- let { cmdids } = _disp_;
984
-
985
- // default rotate at 1s across 5 buckets
986
- let { td: rotate_td=1000, n: rotate_n=5 } =
987
- opt && opt.rotate || {};
988
-
989
- let rotate_ts = rotate_td + Date.now();
990
-
991
- return [on_mqtt,
992
- _disp_.bind_pkt_future()]
993
-
994
899
  function on_mqtt(pkt_list, ctx) {
995
900
  for (let pkt of pkt_list) {
996
- cmdids[pkt.id](_disp_, pkt, ctx); }
901
+ _mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
997
902
 
998
- if (Date.now() > rotate_ts) {
999
- _disp_.rotate_belt(rotate_n);
1000
- rotate_ts = rotate_td + Date.now();} } }
903
+ // rotate after rotate_ts
904
+ let now = Date.now();
905
+ if (now > rotate_ts) {
906
+ rotate_ts = rotate_td + now;
907
+ hashbelt.unshift(new Map());
908
+ while (hashbelt.length > rotate_n) {
909
+ for (let fn_answer of hashbelt.pop().values()) {
910
+ fn_answer([/*pkt*/, 'expired']); } } } } }// option/maybe monad
1001
911
 
1002
912
  class MQTTError extends Error {
1003
913
  constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
914
+ // use hex-encoded reasons to match MQTT spec documentation
1004
915
  super(`[0x${reason.toString(16)}] ${reason.reason}`);
1005
916
  this.mqtt_pkt = mqtt_pkt;
1006
917
  this.reason = reason;} }
1007
918
 
1008
919
  class MQTTBase {
1009
- constructor(opt={}) {
1010
- this.with(opt);
1011
- this._conn_ = _mqtt_conn(this,
1012
- this._init_dispatch(opt, this)); }
1013
-
1014
- with(fns_ns) {
1015
- for (let [k,v] of Object.entries(fns_ns)) {
1016
- if ('function' === typeof v) {this[k] = v;} }
1017
- return this}
1018
-
1019
- async conn_emit(evt, arg, err_arg) {
1020
- this.log_conn?.(evt, arg, err_arg);
1021
- try {
1022
- let fn_evt = this[await evt]; // microtask break using `await evt`
1023
- if (fn_evt) {
1024
- await fn_evt.call(this, this, arg, err_arg);}
1025
- else if (err_arg) {throw err_arg} }
1026
- catch (err) {
1027
- this.on_error(err, evt);} }
1028
-
1029
- on_error(err, evt) {
1030
- console.warn('[[u8-mqtt error: %s]]', evt, err); }
1031
-
1032
920
  // Handshaking Packets
1033
-
1034
921
  async connect(pkt={}) {
1035
922
  let cid = pkt.client_id;
1036
- if ('string' !== typeof cid) {
923
+ if (! _isstr(cid)) {
1037
924
  // see init_client_id implementation in core.jsy
1038
925
  pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
1039
926
  this.client_id = cid;
@@ -1041,23 +928,20 @@ class MQTTBase {
1041
928
  if (null == pkt.keep_alive) {
1042
929
  pkt.keep_alive = 60;}
1043
930
 
1044
- let res = await this._conn_
1045
- .send_connect('connect', pkt, 'connack');
1046
-
1047
- if (0 != res[0].reason) {
1048
- throw new this.MQTTError(res[0])}
1049
-
1050
- // TODO: merge with server's keep_alive frequency
1051
- this._conn_.ping(pkt.keep_alive);
1052
- return res}
931
+ let response = await this._send0('connect', pkt, 'connack');
932
+ if (0 != response[0].reason) {// compare to 0 to coerce to number
933
+ throw new this.MQTTError(response[0])}
934
+ return this.conn.on_conn(pkt, response)}
1053
935
 
1054
936
  async disconnect(pkt={}) {
1055
- let res = await this._send('disconnect', pkt);
1056
- this._conn_.reset(false);
1057
- return res}
937
+ let response = await this._send0('disconnect', pkt);
938
+ return this.conn.on_dis(pkt, response)}
1058
939
 
1059
- auth(pkt={}) {
1060
- return this._send('auth', pkt, 'auth')}
940
+ async auth(pkt={}) {
941
+ let response = await this._send0('auth', pkt, 'auth');
942
+ if (response[0].reason) {
943
+ throw new this.MQTTError(response[0])}
944
+ return this.conn.on_auth(pkt, response)}
1061
945
 
1062
946
  ping() {return this._send('pingreq', null, 'pingresp')}
1063
947
  puback({pkt_id}) {return this._send('puback', {pkt_id})}
@@ -1094,20 +978,18 @@ class MQTTBase {
1094
978
  // alias: publish -- because 'pub' is shorter for semantic aliases above
1095
979
  async pub(pkt, pub_opt) {
1096
980
  if (undefined === pkt.payload) {
1097
- if ('function' === typeof pub_opt) {
981
+ if (_isfn(pub_opt)) {
982
+ // pub_opt as a function is fn_encode value
1098
983
  pub_opt = {fn_encode: pub_opt};}
1099
984
 
1100
- let {msg} = pkt;
1101
- switch (typeof msg) {
1102
- case 'function':
1103
- pub_opt = {...pub_opt, fn_encode: msg};
1104
- // flow into 'undefined' case
1105
- case 'undefined':
1106
- // return a single-value closure to publish packets
1107
- return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
985
+ let msg = pkt.msg, fn_encode = pub_opt?.fn_encode;
986
+ if (null == msg || _isfn(msg)) {
987
+ // when msg is a function, return closure using fn_encode
988
+ if (msg) {pub_opt = {...pub_opt, fn_encode: msg};}
989
+ // return a single-value closure to publish packets
990
+ return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1108
991
 
1109
992
  // Encode payload from msg; fn_encode allows alternative to JSON.stringify
1110
- let {fn_encode} = pub_opt || {};
1111
993
  pkt.payload = fn_encode
1112
994
  ? await fn_encode(msg)
1113
995
  : JSON.stringify(msg);}
@@ -1119,31 +1001,31 @@ class MQTTBase {
1119
1001
  pkt = pub_opt.xform(pkt) || pkt;} }
1120
1002
 
1121
1003
  return this._send('publish', pkt,
1122
- pkt.qos ? pkt : void 0 ) }// key
1004
+ pkt.qos ? pkt : null ) }// key
1123
1005
 
1124
1006
 
1125
1007
  // Internal API
1126
1008
 
1127
- /* async _send(type, pkt) -- provided by _conn_ and transport */
1009
+ /* async _send0(type, pkt) -- provided by conn and transport */
1010
+ /* async _send(type, pkt) -- provided by conn and transport */
1128
1011
 
1129
1012
  _init_dispatch(opt) {
1130
1013
  this.constructor?._once_();
1131
1014
  let target ={__proto__: opt.on_mqtt_type};
1132
1015
  target.mqtt_publish ||=
1133
1016
  this._init_router?.(opt, this, target);
1134
- return _mqtt_dispatch(this, target)}
1017
+ return _mqtt_dispatch(opt, target)}
1135
1018
 
1136
1019
  static _aliases() {
1137
1020
  return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
1138
1021
 
1139
- static _once_(self=this) {
1140
- self._once_ = _=>0;
1141
- let p = self.prototype;
1022
+ static _once_(klass=this) {
1023
+ klass._once_ = _=>0;
1024
+ var alias, name, p = klass.prototype;
1142
1025
  p.MQTTError = MQTTError;
1143
- for (let alias of self._aliases().split(/\s+/)) {
1144
- alias = alias.split(':');
1145
- let fn = alias[1] && p[alias[1]];
1146
- if (fn) {p[alias[0]] = fn;} } } }
1026
+ for (alias of klass._aliases().split(/\s+/)) {
1027
+ [alias, name] = alias.split(':');
1028
+ p[alias] = p[name];} } }
1147
1029
 
1148
1030
 
1149
1031
  function _as_topics(pkt, ex, topic_prefix) {
@@ -1168,37 +1050,167 @@ function _as_topics(pkt, ex, topic_prefix) {
1168
1050
  pkt.topics = pkt.topics.map(_prefix_topics);}
1169
1051
  return pkt}
1170
1052
 
1171
- const pkt_api = {
1172
- utf8(u8) { return new TextDecoder('utf-8').decode(u8 || this.payload ) },
1173
- json(u8) { return JSON.parse( this.utf8(u8) || null ) },
1174
- text(u8) { return this.utf8(u8) },
1175
- };
1053
+ const _defer_obj = o =>(
1054
+ o.p = new Promise((a,e) => { o.a=a; o.e=e; })
1055
+ , o);
1056
+
1057
+ function _dfn_reset(client, attr, fn_after) {
1058
+ // a resetable deferred for a function
1059
+ let self = {set}, afn = async (...args) => (await self.p)(...args);
1060
+ return set()
1061
+
1062
+ function set() {
1063
+ if (afn !== client[attr]) {
1064
+ _defer_obj(self).p.then(fn_after, _=>0);
1065
+ client[attr] = afn;}
1066
+ return self} }
1067
+
1068
+ function _mqtt_conn(opt, client, [on_mqtt, pkt_future]) {
1069
+ let _abort;
1070
+ let _dfn_send0 = _dfn_reset(client, '_send0', // client._send0 getter/setter
1071
+ _=> client.conn_emit('on_live', conn.has_connected));
1072
+ let _dfn_ready = _dfn_reset(client, '_send', // client._send getter/setter
1073
+ _=> client.conn_emit('on_ready'));
1074
+ let _keep_alive_ival = _interval (() =>client._send0('pingreq') );// resettable interval for keep_alive ping
1075
+
1076
+ let conn = Object.create({
1077
+ ping: (td=conn.keep_alive) => _keep_alive_ival(td)
1078
+
1079
+ , on_conn(pkt, response) {
1080
+ conn.has_connected = true;
1081
+ conn.keep_alive = opt.keep_alive || response[0].props?.server_keep_alive || pkt.keep_alive;
1082
+ client.conn_emit('on_conn');
1083
+ return opt.use_auth
1084
+ ? response // wait on enhanced authentication step
1085
+ : conn.on_auth(null, response) }// otherwise, connect is also auth
1086
+
1087
+ , on_auth(pkt, response) {
1088
+ _dfn_ready.a(_dfn_send0.p);
1089
+ if (0 != opt.keep_alive) {
1090
+ conn.ping();}
1091
+ client.conn_emit('on_auth', !pkt);
1092
+ return response}
1093
+
1094
+ , on_dis(pkt, response) {
1095
+ conn.reset(false);
1096
+ return response}
1097
+
1098
+ , reset(err) {
1099
+ if (err) {
1100
+ _dfn_send0.e(err); }// send error to uses of _send0 (connect, auth)
1101
+ _abort.e(err); // abort in-progress connections
1102
+
1103
+ delete conn.is_set;
1104
+ conn.ready = handshake();
1105
+ client.conn_emit('on_disconnect', false===err, err);}
1106
+
1107
+ , abort() {
1108
+ _dfn_ready.e(err); // abort all messages awaiting ready state
1109
+ return conn.reset(err)}
1110
+
1111
+ , async setup(gate, send_u8_pkt, init_msg_loop) {
1112
+ if (conn.is_set) {
1113
+ throw new Error() }// already in-progress
1114
+
1115
+ conn.is_set = true;
1116
+ await gate;
1117
+
1118
+ // setup send/recv MQTT parsing context
1119
+ let mqtt_ctx = client.mqtt_ctx.mqtt_stream();
1120
+
1121
+ {// setup inbound message loop
1122
+ let sess_ctx = {mqtt: client}; // mutable session context
1123
+ let on_mqtt_chunk = u8 => on_mqtt(mqtt_ctx.decode(u8), sess_ctx);
1124
+ init_msg_loop(on_mqtt_chunk, conn);}
1125
+
1126
+ // setup outbound message path and transport connection
1127
+ send_u8_pkt = await send_u8_pkt;
1128
+ _dfn_send0.a(
1129
+ async (type, pkt, key) => {
1130
+ let res = undefined !== key
1131
+ ? pkt_future(key) : true;
1132
+
1133
+ await send_u8_pkt(
1134
+ mqtt_ctx.encode_pkt(type, pkt));
1135
+ return res} ); } });
1136
+
1137
+ conn.ready = handshake();
1138
+ return conn
1139
+
1140
+ async function handshake() {
1141
+ _abort = _defer_obj({});
1142
+
1143
+ _keep_alive_ival(0); // clearInterval on keep alive ping
1144
+ _dfn_send0.set(); // reset client._send0 if necessary
1145
+ _dfn_ready.set(); // reset client._send if necessary
1146
+
1147
+ try {
1148
+ // set client._send0 as passtrhough after transport connection
1149
+ client._send0 = await Promise.race([_dfn_send0.p, _abort.p]);
1150
+
1151
+ // set client._send as passtrhough after ready
1152
+ client._send = await Promise.race([_dfn_ready.p, _abort.p]);
1153
+ return true}
1154
+ catch (err) {
1155
+ return false} } }
1156
+
1157
+ const pkt_api ={
1158
+ utf8(u8) {return new TextDecoder('utf-8').decode(u8 || this.payload )}
1159
+ , json(u8) {return JSON.parse( this.utf8(u8) || null )}
1160
+ , text(u8) {return this.utf8(u8)} };
1161
+
1162
+ const opt_default ={
1163
+ sess_stg: globalThis.sessionStorage};
1176
1164
 
1177
1165
  class MQTTCore extends MQTTBase {
1166
+ constructor(opt) {
1167
+ opt = {...opt_default, ...opt};
1168
+ super();
1169
+ this.with(opt);
1170
+ // settings for MQTTCore
1171
+ this.sess_stg = opt.sess_stg;
1172
+ // setup connection and dispatch
1173
+ this.conn = _mqtt_conn(opt, this,
1174
+ this._init_dispatch(opt)); }
1175
+
1176
+ with(fns_ns) {
1177
+ for (let [k,v] of Object.entries(fns_ns)) {
1178
+ if (_isfn(v)) {this[k] = v;} }
1179
+ return this}
1180
+
1181
+
1178
1182
  static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
1179
- let self = class extends this {};
1180
- self.prototype.mqtt_ctx =
1183
+ let klass = class extends this {};
1184
+ klass.prototype.mqtt_ctx =
1181
1185
  mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
1182
- return self}
1186
+ return klass}
1187
+
1188
+
1189
+ async conn_emit(evt, arg, err_arg) {
1190
+ this.log_conn?.(evt, arg, err_arg);
1191
+ try {
1192
+ let fn_evt = this[await evt]; // microtask break using `await evt`
1193
+ if (fn_evt) {
1194
+ await fn_evt.call(this, this, arg, err_arg);}
1195
+ else if (err_arg) {throw err_arg} }
1196
+ catch (err) {
1197
+ this.on_error(err, evt);} }
1198
+
1199
+ on_error(err, evt) {
1200
+ console.warn('[[u8-mqtt error: %s]]', evt, err); }
1201
+ log_conn(evt, arg, err_arg) {
1202
+ console.info('[[u8-mqtt conn: %s]]', evt, arg, err_arg); }
1183
1203
 
1184
1204
 
1185
1205
  // automatic Client Id for connect()
1186
1206
  init_client_id(parts=['u8-mqtt--','']) {
1187
- let sess_stg=this.sess_stg;
1207
+ let sess_stg = this.sess_stg;
1188
1208
  let key, cid = sess_stg?.getItem(key=parts.join(' '));
1189
1209
  if (! cid) {
1190
1210
  cid = parts.join(Math.random().toString(36).slice(2));
1191
1211
  sess_stg?.setItem(key, cid);}
1192
1212
  return cid}
1193
1213
 
1194
- get sess_stg() {return globalThis.sessionStorage}
1195
-
1196
-
1197
- //on_error(err, evt) ::
1198
- // console.warn @ '[[u8-mqtt error: %s]]', evt, err
1199
-
1200
- //log_conn(evt, arg, err_arg) ::
1201
- // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1202
1214
 
1203
1215
  on_live(client, is_reconnect) {
1204
1216
  if (is_reconnect) {
@@ -1224,19 +1236,16 @@ class MQTTCore extends MQTTBase {
1224
1236
  return new Promise(done => setTimeout(done, ms)) }
1225
1237
 
1226
1238
  with_async_iter(async_iter, write_u8_pkt) {
1227
- let on_mqtt_chunk = this._conn_.set(
1228
- this.mqtt_ctx,
1229
- write_u8_pkt);
1230
-
1231
- this._msg_loop = ((async () => {
1232
- try {
1233
- async_iter = await async_iter;
1234
- for await (let chunk of async_iter)
1235
- on_mqtt_chunk(chunk);
1236
- this._conn_.reset();}
1237
- catch (err) {
1238
- this._conn_.reset(err);} })());
1239
-
1239
+ this.conn.setup(async_iter,
1240
+ write_u8_pkt,
1241
+ async (on_mqtt_chunk, conn) => {
1242
+ try {
1243
+ async_iter = await async_iter;
1244
+ for await (let chunk of async_iter)
1245
+ on_mqtt_chunk(chunk);
1246
+ conn.reset();}
1247
+ catch (err) {
1248
+ conn.reset(err);} } );
1240
1249
  return this}
1241
1250
 
1242
1251
 
@@ -1307,33 +1316,30 @@ class MQTTCore extends MQTTBase {
1307
1316
 
1308
1317
  websock.binaryType = 'arraybuffer';
1309
1318
 
1310
- let ready, {readyState} = websock;
1319
+ let ws_ready, readyState = websock.readyState;
1311
1320
  if (1 !== readyState) {
1312
1321
  if (0 !== readyState) {
1313
- throw new Error('Invalid WebSocket readyState') }
1314
-
1315
- ready = new Promise(fn => websock.onopen = fn); }
1322
+ throw new Error('WS readyState') }
1316
1323
 
1324
+ ws_ready = new Promise(ready => websock.onopen = ready); }
1317
1325
 
1318
- let {_conn_} = this;
1319
- let on_mqtt_chunk = _conn_.set(
1320
- this.mqtt_ctx,
1321
- async u8_pkt =>(
1322
- await ready
1323
- , websock.send(u8_pkt)) );
1326
+ this.conn.setup(ws_ready,
1327
+ u8_pkt => websock.send(u8_pkt),
1328
+ (on_mqtt_chunk, conn) => {
1329
+ websock.onmessage = evt =>(
1330
+ on_mqtt_chunk(new Uint8Array(evt.data)) );
1324
1331
 
1325
- websock.onmessage = evt =>(on_mqtt_chunk(new Uint8Array(evt.data)));
1326
- websock.onclose = evt => {
1327
- if (! evt.wasClean) {
1328
- var err = new Error('websocket connection close');
1329
- err.code = evt.code;
1330
- err.reason = evt.reason;}
1332
+ websock.onclose = evt => {
1333
+ if (! evt.wasClean) {
1334
+ var err = new Error('websocket close');
1335
+ err.code = evt.code;
1336
+ err.reason = evt.reason;}
1331
1337
 
1332
- _conn_.reset(err);};
1338
+ conn.reset(err);}; } );
1333
1339
 
1334
1340
  return this} }
1335
1341
 
1336
- const version = '0.5.3-node';
1342
+ const version = '0.6.1-node';
1337
1343
 
1338
1344
  const MQTTClient_v4 = /* #__PURE__ */
1339
1345
  with_topic_path_router(