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