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-v5.js CHANGED
@@ -156,10 +156,18 @@ let mqtt_reader_v5$1 = class mqtt_reader_v5 extends mqtt_reader_v4 {
156
156
 
157
157
  let res={}, fork = this.of(buf.subarray(vi, step.k|0));
158
158
  while (fork.has_more()) {
159
- let pt = mqtt_props.get( fork.u8() )
160
- , value = fork[pt.type]();
161
- res[pt.name] = ! pt.op ? value
162
- : fork[pt.op](res[pt.name], value);
159
+ let v, pk = fork.u8(), pt = mqtt_props.get( pk );
160
+
161
+ if (!pt) {
162
+ res.error = `Unknown mqtt_prop enum ${pk}`;
163
+ return res
164
+ }
165
+
166
+ v = fork[pt.type]();
167
+ if (pt.op) // accumulate operation
168
+ v = fork[pt.op](res[pt.name], v);
169
+
170
+ res[pt.name] = v;
163
171
  }
164
172
  return res
165
173
  }
@@ -293,6 +301,8 @@ class mqtt_writer_v5 extends mqtt_writer_v4 {
293
301
  let fork = this.of();
294
302
  for (let [name, value] of props) {
295
303
  let pt = mqtt_props.get(name);
304
+ if (!pt)
305
+ throw new Error(`Unknown mqtt_prop "${name}"`)
296
306
  fork[pt.op || 'one'](value, pt);
297
307
  }
298
308
  this.push(fork.pack());
@@ -855,6 +865,9 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
855
865
  return _encode_by_type[type]( mqtt_level, pkt ) },
856
866
 
857
867
  decode_pkt(b0, u8_body) {
868
+ if (b0.map) // Uint8Array in first arg
869
+ return mqtt_raw_dispatch(this)(b0)[0]
870
+
858
871
  let fn_decode = _decode_by_id[b0>>>4] || _decode_by_id[0];
859
872
  return fn_decode?.({__proto__: this.pkt_ctx, b0}, u8_body) },
860
873
 
@@ -867,88 +880,15 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
867
880
  }
868
881
  }
869
882
 
870
- function ao_defer_ctx(as_res = (...args) => args) {
871
- let y,n,_pset = (a,b) => { y=a, n=b; };
872
- return p =>(
873
- p = new Promise(_pset)
874
- , as_res(p, y, n)) }
875
-
876
- const ao_defer_v = /* #__PURE__ */
877
- ao_defer_ctx();
878
-
879
- Promise.resolve({type:'init'});
880
-
881
- function _mqtt_conn(client, [on_mqtt, pkt_future]) {
882
- let _q_init = ao_defer_v(), _q_ready = ao_defer_v();
883
- let _send_ready = async (...args) => (await _q_ready[0])(...args);
884
- let _send_mqtt_pkt, _has_connected;
885
- client._send = _send_ready;
886
-
887
- return {
888
- async when_ready() {await _q_ready[0];}
889
-
890
- , ping: _ping_interval (() =>_send_mqtt_pkt?.('pingreq'))
891
-
892
- , reset(err) {
893
- if (! _send_mqtt_pkt) {return}
894
-
895
- if (err) {
896
- _q_init[2](err);}
883
+ const _isfn = v => typeof v === 'function';
884
+ const _isstr = v => typeof v === 'string';
897
885
 
898
- _send_mqtt_pkt = null;
899
- _q_init = ao_defer_v();
900
- client._send = _send_ready;
901
-
902
- // call client.on_conn_reset in next promise microtask
903
- client.conn_emit('on_disconnect', false===err, err);}
904
-
905
- , async send_connect(... args) {
906
- if (! _send_mqtt_pkt) {
907
- await _q_init[0]; }// _send_mqtt_pkt is set before fulfilled
908
-
909
- // await connack response
910
- let res = await _send_mqtt_pkt(...args);
911
- if (0 == res[0].reason) {
912
- _has_connected = true;
913
- // resolve _q_ready[0] with _send_mqtt_pkt closure
914
- _q_ready[1](client._send = _send_mqtt_pkt);
915
- _q_ready = ao_defer_v();
916
- client.conn_emit('on_ready');}
917
-
918
- return res}
919
-
920
- , is_set: (() =>!! _send_mqtt_pkt)
921
- , set(mqtt_ctx, send_u8_pkt) {
922
- if (_send_mqtt_pkt) {
923
- throw new Error('Already connected')}
924
-
925
- mqtt_ctx = mqtt_ctx.mqtt_stream();
926
- let sess_ctx = {mqtt: client};
927
- let on_mqtt_chunk = u8_buf =>
928
- on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
929
-
930
- _send_mqtt_pkt = async (type, pkt, key) => {
931
- let res = undefined !== key
932
- ? pkt_future(key) : true;
933
-
934
- await send_u8_pkt(
935
- mqtt_ctx.encode_pkt(type, pkt));
936
-
937
- return res};
938
-
939
- _q_init[1](_send_mqtt_pkt); // resolve _q_init with _send_mqtt_pkt closure
940
-
941
- // call client.on_live in next promise microtask
942
- client.conn_emit('on_live', _has_connected);
943
- return on_mqtt_chunk} } }
944
-
945
-
946
- function _ping_interval(send_ping) {
886
+ function _interval(fn_callback) {
947
887
  let tid;
948
888
  return (( td ) => {
949
889
  tid = clearInterval(tid);
950
890
  if (td) {
951
- tid = setInterval(send_ping, 1000 * td);
891
+ tid = setInterval(fn_callback, 1000 * td);
952
892
 
953
893
 
954
894
 
@@ -957,172 +897,129 @@ function _ping_interval(send_ping) {
957
897
  tid.unref?.();
958
898
  return true} }) }
959
899
 
960
- const _mqtt_cmdid_dispatch ={
961
- create(target) {
962
- return {__proto__: this, target, hashbelt: [new Map()]} }
900
+ async function _mqtt_cmd_evt(target, answer, pkt, ctx) {
901
+ /* target : on_mqtt_type = {
902
+ mqtt_pkt(pkt, ctx) {}, // generic
903
+
904
+ mqtt_auth(pkt, ctx) {},
905
+ mqtt_connect(pkt, ctx) {},
906
+ mqtt_connack(pkt, ctx) {},
907
+ mqtt_disconnect(pkt, ctx) {},
908
+
909
+ mqtt_publish(pkt, ctx) {},
910
+ mqtt_subscribe(pkt, ctx) {},
911
+ mqtt_unsubscribe(pkt, ctx) {},
912
+
913
+ mqtt_pingreq(pkt, ctx) {},
914
+ mqtt_pingresp(pkt, ctx) {},
915
+ } */
916
+
917
+ let pkt_fn = target[`mqtt_${pkt.type}`] || target.mqtt_pkt;
918
+ await pkt_fn?.call(target, pkt, ctx);}
919
+
920
+ function _mqtt_cmd_type(target, answer, pkt, ctx) {
921
+ answer(pkt.type, pkt);
922
+ _mqtt_cmd_evt(target, answer, pkt, ctx);}
923
+
924
+ function _mqtt_cmd_id(target, answer, pkt) {
925
+ answer(pkt.pkt_id, pkt);}
926
+
927
+
928
+ const _mqtt_cmdids =[
929
+ _ => {} // 0x0 reserved
930
+ , _mqtt_cmd_evt // 0x1 connect
931
+ , _mqtt_cmd_type // 0x2 connack
932
+ , _mqtt_cmd_evt // 0x3 publish
933
+ , _mqtt_cmd_id // 0x4 puback
934
+ , _mqtt_cmd_id // 0x5 pubrec
935
+ , _mqtt_cmd_id // 0x6 pubrel
936
+ , _mqtt_cmd_id // 0x7 pubcomp
937
+ , _mqtt_cmd_evt // 0x8 subscribe
938
+ , _mqtt_cmd_id // 0x9 suback
939
+ , _mqtt_cmd_evt // 0xa unsubscribe
940
+ , _mqtt_cmd_id // 0xb unsuback
941
+ , _mqtt_cmd_type // 0xc pingreq
942
+ , _mqtt_cmd_type // 0xd pingresp
943
+ , _mqtt_cmd_evt // 0xe disconnect
944
+ , _mqtt_cmd_type ];// 0xf auth
963
945
 
964
- , bind_pkt_future(_pkt_id=100) {
965
- let {hashbelt} = this;
946
+ function _mqtt_dispatch(opt, target) {
947
+ let hashbelt=[], rotate_ts=0;
948
+ // default rotate at 1s across 5 buckets
949
+ let { td: rotate_td=1000, n: rotate_n=5 } = opt?.rotate || {};
950
+
951
+ // Promise / future scaffolding
952
+ let _pkt_id=100, _ftr_key; // use _ftr_key to reuse _by_key closure
953
+ let _ftr_by_key = fn_answer => hashbelt[0].set(_ftr_key, fn_answer);
966
954
 
967
- let _tmp_; // use _tmp_ to reuse _by_key closure
968
- let _by_key = answer_monad =>
969
- hashbelt[0].set(_tmp_, answer_monad);
955
+ on_mqtt([]); // init hashbelt and rotate_ts
956
+ return [on_mqtt, pkt_future]
970
957
 
971
- return (( pkt_or_key ) => {
972
- if ('string' === typeof pkt_or_key) {
973
- _tmp_ = pkt_or_key;}
974
- else {
975
- _pkt_id = (_pkt_id + 1) & 0xffff;
976
- _tmp_ = pkt_or_key.pkt_id = _pkt_id;}
977
958
 
978
- return new Promise(_by_key)}) }
959
+ function pkt_future(pkt_or_key) {
960
+ if (! _isstr(pkt_or_key)) {
961
+ _pkt_id = (_pkt_id + 1) & 0xffff; // 16-bit unsigned short
962
+ _ftr_key = pkt_or_key.pkt_id = _pkt_id;}
963
+ else _ftr_key = pkt_or_key;
979
964
 
980
- , answer(key, pkt) {
981
- for (let map of this.hashbelt) {
982
- let answer_monad = map.get(key);
983
- if (undefined !== answer_monad) {
965
+ return new Promise(_ftr_by_key)}
966
+
967
+ function answer(key, pkt) {
968
+ for (let map of hashbelt) {
969
+ let fn_answer = map.get(key);
970
+ if (fn_answer) {
984
971
  map.delete(key);
985
972
 
986
- answer_monad([pkt, /*err*/]); // option/maybe monad
973
+ fn_answer([pkt, /*err*/]); // option/maybe monad
987
974
  return true} }
988
975
  return false}
989
976
 
990
- , rotate_belt(n) {
991
- let {hashbelt} = this;
992
- hashbelt.unshift(new Map());
993
- for (let old of hashbelt.splice(n || 5)) {
994
- for (let answer_monad of old.values()) {
995
- answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
996
-
997
- , cmdids: ((() => {
998
- return [
999
- (() =>{} )// 0x0 reserved
1000
- , by_evt // 0x1 connect
1001
- , by_type // 0x2 connack
1002
- , by_evt // 0x3 publish
1003
- , by_id // 0x4 puback
1004
- , by_id // 0x5 pubrec
1005
- , by_id // 0x6 pubrel
1006
- , by_id // 0x7 pubcomp
1007
- , by_evt // 0x8 subscribe
1008
- , by_id // 0x9 suback
1009
- , by_evt // 0xa unsubscribe
1010
- , by_id // 0xb unsuback
1011
- , by_type // 0xc pingreq
1012
- , by_type // 0xd pingresp
1013
- , by_evt // 0xe disconnect
1014
- , by_type ]// 0xf auth
1015
-
1016
-
1017
- function by_id(disp, pkt) {
1018
- disp.answer(pkt.pkt_id, pkt); }
1019
-
1020
- function by_type(disp, pkt, ctx) {
1021
- disp.answer(pkt.type, pkt);
1022
- by_evt(disp, pkt, ctx);}
1023
-
1024
- async function by_evt({target}, pkt, ctx) {
1025
- let fn = target[`mqtt_${pkt.type}`]
1026
- || target.mqtt_pkt;
1027
-
1028
- await fn?.call(target, pkt, ctx);} })()) };
1029
-
1030
- /*
1031
- on_mqtt_type = {
1032
- mqtt_auth(pkt, ctx) ::
1033
- mqtt_connect(pkt, ctx) ::
1034
- mqtt_connack(pkt, ctx) ::
1035
- mqtt_disconnect(pkt, ctx) ::
1036
-
1037
- mqtt_publish(pkt, ctx)
1038
- mqtt_subscribe(pkt, ctx) ::
1039
- mqtt_unsubscribe(pkt, ctx) ::
1040
-
1041
- mqtt_pingreq(pkt, ctx) ::
1042
- mqtt_pingresp(pkt, ctx) ::
1043
- }
1044
- */
1045
-
1046
- function _mqtt_dispatch(opt, target) {
1047
- let _disp_ = _mqtt_cmdid_dispatch.create(target);
1048
- let { cmdids } = _disp_;
1049
-
1050
- // default rotate at 1s across 5 buckets
1051
- let { td: rotate_td=1000, n: rotate_n=5 } =
1052
- opt && opt.rotate || {};
1053
-
1054
- let rotate_ts = rotate_td + Date.now();
1055
-
1056
- return [on_mqtt,
1057
- _disp_.bind_pkt_future()]
1058
-
1059
977
  function on_mqtt(pkt_list, ctx) {
1060
978
  for (let pkt of pkt_list) {
1061
- cmdids[pkt.id](_disp_, pkt, ctx); }
979
+ _mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
1062
980
 
1063
- if (Date.now() > rotate_ts) {
1064
- _disp_.rotate_belt(rotate_n);
1065
- rotate_ts = rotate_td + Date.now();} } }
981
+ // rotate after rotate_ts
982
+ let now = Date.now();
983
+ if (now > rotate_ts) {
984
+ rotate_ts = rotate_td + now;
985
+ hashbelt.unshift(new Map());
986
+ while (hashbelt.length > rotate_n) {
987
+ for (let fn_answer of hashbelt.pop().values()) {
988
+ fn_answer([/*pkt*/, 'expired']); } } } } }// option/maybe monad
1066
989
 
1067
990
  class MQTTError extends Error {
1068
991
  constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
992
+ // use hex-encoded reasons to match MQTT spec documentation
1069
993
  super(`[0x${reason.toString(16)}] ${reason.reason}`);
1070
994
  this.mqtt_pkt = mqtt_pkt;
1071
995
  this.reason = reason;} }
1072
996
 
1073
997
  class MQTTBase {
1074
- constructor(opt={}) {
1075
- this.with(opt);
1076
- this._conn_ = _mqtt_conn(this,
1077
- this._init_dispatch(opt, this)); }
1078
-
1079
- with(fns_ns) {
1080
- for (let [k,v] of Object.entries(fns_ns)) {
1081
- if ('function' === typeof v) {this[k] = v;} }
1082
- return this}
1083
-
1084
- async conn_emit(evt, arg, err_arg) {
1085
- this.log_conn?.(evt, arg, err_arg);
1086
- try {
1087
- let fn_evt = this[await evt]; // microtask break using `await evt`
1088
- if (fn_evt) {
1089
- await fn_evt.call(this, this, arg, err_arg);}
1090
- else if (err_arg) {throw err_arg} }
1091
- catch (err) {
1092
- this.on_error(err, evt);} }
1093
-
1094
- on_error(err, evt) {
1095
- console.warn('[[u8-mqtt error: %s]]', evt, err); }
1096
-
1097
998
  // Handshaking Packets
1098
-
1099
999
  async connect(pkt={}) {
1100
- let cid = pkt.client_id || this.client_id;
1101
- if ('string' !== typeof cid) {
1000
+ let cid = pkt.client_id;
1001
+ if (! _isstr(cid)) {
1102
1002
  // see init_client_id implementation in core.jsy
1103
- pkt.client_id = cid = this.init_client_id(cid);}
1003
+ pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
1104
1004
  this.client_id = cid;
1105
1005
 
1106
1006
  if (null == pkt.keep_alive) {
1107
1007
  pkt.keep_alive = 60;}
1108
1008
 
1109
- let res = await this._conn_
1110
- .send_connect('connect', pkt, 'connack');
1111
-
1112
- if (0 != res[0].reason) {
1113
- throw new this.MQTTError(res[0])}
1114
-
1115
- // TODO: merge with server's keep_alive frequency
1116
- this._conn_.ping(pkt.keep_alive);
1117
- return res}
1009
+ let response = await this._send0('connect', pkt, 'connack');
1010
+ if (0 != response[0].reason) {// compare to 0 to coerce to number
1011
+ throw new this.MQTTError(response[0])}
1012
+ return this.conn.on_conn(pkt, response)}
1118
1013
 
1119
1014
  async disconnect(pkt={}) {
1120
- let res = await this._send('disconnect', pkt);
1121
- this._conn_.reset(false);
1122
- return res}
1015
+ let response = await this._send0('disconnect', pkt);
1016
+ return this.conn.on_dis(pkt, response)}
1123
1017
 
1124
- auth(pkt={}) {
1125
- return this._send('auth', pkt, 'auth')}
1018
+ async auth(pkt={}) {
1019
+ let response = await this._send0('auth', pkt, 'auth');
1020
+ if (response[0].reason) {
1021
+ throw new this.MQTTError(response[0])}
1022
+ return this.conn.on_auth(pkt, response)}
1126
1023
 
1127
1024
  ping() {return this._send('pingreq', null, 'pingresp')}
1128
1025
  puback({pkt_id}) {return this._send('puback', {pkt_id})}
@@ -1159,20 +1056,18 @@ class MQTTBase {
1159
1056
  // alias: publish -- because 'pub' is shorter for semantic aliases above
1160
1057
  async pub(pkt, pub_opt) {
1161
1058
  if (undefined === pkt.payload) {
1162
- if ('function' === typeof pub_opt) {
1059
+ if (_isfn(pub_opt)) {
1060
+ // pub_opt as a function is fn_encode value
1163
1061
  pub_opt = {fn_encode: pub_opt};}
1164
1062
 
1165
- let {msg} = pkt;
1166
- switch (typeof msg) {
1167
- case 'function':
1168
- pub_opt = {...pub_opt, fn_encode: msg};
1169
- // flow into 'undefined' case
1170
- case 'undefined':
1171
- // return a single-value closure to publish packets
1172
- return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1063
+ let msg = pkt.msg, fn_encode = pub_opt?.fn_encode;
1064
+ if (null == msg || _isfn(msg)) {
1065
+ // when msg is a function, return closure using fn_encode
1066
+ if (msg) {pub_opt = {...pub_opt, fn_encode: msg};}
1067
+ // return a single-value closure to publish packets
1068
+ return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1173
1069
 
1174
1070
  // Encode payload from msg; fn_encode allows alternative to JSON.stringify
1175
- let {fn_encode} = pub_opt || {};
1176
1071
  pkt.payload = fn_encode
1177
1072
  ? await fn_encode(msg)
1178
1073
  : JSON.stringify(msg);}
@@ -1184,31 +1079,31 @@ class MQTTBase {
1184
1079
  pkt = pub_opt.xform(pkt) || pkt;} }
1185
1080
 
1186
1081
  return this._send('publish', pkt,
1187
- pkt.qos ? pkt : void 0 ) }// key
1082
+ pkt.qos ? pkt : null ) }// key
1188
1083
 
1189
1084
 
1190
1085
  // Internal API
1191
1086
 
1192
- /* async _send(type, pkt) -- provided by _conn_ and transport */
1087
+ /* async _send0(type, pkt) -- provided by conn and transport */
1088
+ /* async _send(type, pkt) -- provided by conn and transport */
1193
1089
 
1194
1090
  _init_dispatch(opt) {
1195
1091
  this.constructor?._once_();
1196
1092
  let target ={__proto__: opt.on_mqtt_type};
1197
1093
  target.mqtt_publish ||=
1198
1094
  this._init_router?.(opt, this, target);
1199
- return _mqtt_dispatch(this, target)}
1095
+ return _mqtt_dispatch(opt, target)}
1200
1096
 
1201
1097
  static _aliases() {
1202
1098
  return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
1203
1099
 
1204
- static _once_(self=this) {
1205
- self._once_ = _=>0;
1206
- let p = self.prototype;
1100
+ static _once_(klass=this) {
1101
+ klass._once_ = _=>0;
1102
+ var alias, name, p = klass.prototype;
1207
1103
  p.MQTTError = MQTTError;
1208
- for (let alias of self._aliases().split(/\s+/)) {
1209
- alias = alias.split(':');
1210
- let fn = alias[1] && p[alias[1]];
1211
- if (fn) {p[alias[0]] = fn;} } } }
1104
+ for (alias of klass._aliases().split(/\s+/)) {
1105
+ [alias, name] = alias.split(':');
1106
+ p[alias] = p[name];} } }
1212
1107
 
1213
1108
 
1214
1109
  function _as_topics(pkt, ex, topic_prefix) {
@@ -1233,43 +1128,177 @@ function _as_topics(pkt, ex, topic_prefix) {
1233
1128
  pkt.topics = pkt.topics.map(_prefix_topics);}
1234
1129
  return pkt}
1235
1130
 
1236
- const pkt_api = {
1237
- utf8(u8) { return new TextDecoder('utf-8').decode(u8 || this.payload ) },
1238
- json(u8) { return JSON.parse( this.utf8(u8) || null ) },
1239
- text(u8) { return this.utf8(u8) },
1240
- };
1131
+ const _defer_obj = o =>(
1132
+ o.p = new Promise((a,e) => { o.a=a; o.e=e; })
1133
+ , o);
1134
+
1135
+ function _dfn_reset(client, attr, fn_after) {
1136
+ // a resetable deferred for a function
1137
+ let self = {set}, afn = async (...args) => (await self.p)(...args);
1138
+ return set()
1139
+
1140
+ function set() {
1141
+ if (afn !== client[attr]) {
1142
+ _defer_obj(self).p.then(fn_after, _=>0);
1143
+ client[attr] = afn;}
1144
+ return self} }
1145
+
1146
+ function _mqtt_conn(opt, client, [on_mqtt, pkt_future]) {
1147
+ let _abort;
1148
+ let _dfn_send0 = _dfn_reset(client, '_send0', // client._send0 getter/setter
1149
+ _=> client.conn_emit('on_live', conn.has_connected));
1150
+ let _dfn_ready = _dfn_reset(client, '_send', // client._send getter/setter
1151
+ _=> client.conn_emit('on_ready'));
1152
+ let _keep_alive_ival = _interval (() =>client._send0('pingreq') );// resettable interval for keep_alive ping
1153
+
1154
+ let conn = Object.create({
1155
+ ping: (td=conn.keep_alive) => _keep_alive_ival(td)
1156
+
1157
+ , on_conn(pkt, response) {
1158
+ conn.has_connected = true;
1159
+ conn.keep_alive = opt.keep_alive || response[0].props?.server_keep_alive || pkt.keep_alive;
1160
+ client.conn_emit('on_conn');
1161
+ return opt.use_auth
1162
+ ? response // wait on enhanced authentication step
1163
+ : conn.on_auth(null, response) }// otherwise, connect is also auth
1164
+
1165
+ , on_auth(pkt, response) {
1166
+ _dfn_ready.a(_dfn_send0.p);
1167
+ if (0 != opt.keep_alive) {
1168
+ conn.ping();}
1169
+ client.conn_emit('on_auth', !pkt);
1170
+ return response}
1171
+
1172
+ , on_dis(pkt, response) {
1173
+ conn.reset(false);
1174
+ return response}
1175
+
1176
+ , reset(err) {
1177
+ if (err) {
1178
+ _dfn_send0.e(err); }// send error to uses of _send0 (connect, auth)
1179
+ _abort.e(err); // abort in-progress connections
1180
+
1181
+ delete conn.is_set;
1182
+ conn.ready = handshake();
1183
+ client.conn_emit('on_disconnect', false===err, err);}
1184
+
1185
+ , abort() {
1186
+ _dfn_ready.e(err); // abort all messages awaiting ready state
1187
+ return conn.reset(err)}
1188
+
1189
+ , async setup(gate, send_u8_pkt, init_msg_loop) {
1190
+ if (conn.is_set) {
1191
+ throw new Error() }// already in-progress
1192
+
1193
+ conn.is_set = true;
1194
+ await gate;
1195
+
1196
+ // setup send/recv MQTT parsing context
1197
+ let mqtt_ctx = client.mqtt_ctx.mqtt_stream();
1198
+
1199
+ {// setup inbound message loop
1200
+ let sess_ctx = {mqtt: client}; // mutable session context
1201
+ let on_mqtt_chunk = u8 => on_mqtt(mqtt_ctx.decode(u8), sess_ctx);
1202
+ init_msg_loop(on_mqtt_chunk, conn);}
1203
+
1204
+ // setup outbound message path and transport connection
1205
+ send_u8_pkt = await send_u8_pkt;
1206
+ _dfn_send0.a(
1207
+ async (type, pkt, key) => {
1208
+ let res = undefined !== key
1209
+ ? pkt_future(key) : true;
1210
+
1211
+ await send_u8_pkt(
1212
+ mqtt_ctx.encode_pkt(type, pkt));
1213
+ return res} ); } });
1214
+
1215
+ conn.ready = handshake();
1216
+ return conn
1217
+
1218
+ async function handshake() {
1219
+ _abort = _defer_obj({});
1220
+
1221
+ _keep_alive_ival(0); // clearInterval on keep alive ping
1222
+ _dfn_send0.set(); // reset client._send0 if necessary
1223
+ _dfn_ready.set(); // reset client._send if necessary
1224
+
1225
+ try {
1226
+ // set client._send0 as passtrhough after transport connection
1227
+ client._send0 = await Promise.race([_dfn_send0.p, _abort.p]);
1228
+
1229
+ // set client._send as passtrhough after ready
1230
+ client._send = await Promise.race([_dfn_ready.p, _abort.p]);
1231
+ return true}
1232
+ catch (err) {
1233
+ return false} } }
1234
+
1235
+ const pkt_api ={
1236
+ utf8(u8) {return new TextDecoder('utf-8').decode(u8 || this.payload )}
1237
+ , json(u8) {return JSON.parse( this.utf8(u8) || null )}
1238
+ , text(u8) {return this.utf8(u8)} };
1239
+
1240
+ const opt_default ={
1241
+ sess_stg: globalThis.sessionStorage};
1241
1242
 
1242
1243
  class MQTTCore extends MQTTBase {
1244
+ constructor(opt) {
1245
+ super();
1246
+ this.with(opt);
1247
+ opt ={...opt_default, ...opt};
1248
+ // settings for MQTTCore
1249
+ this.sess_stg = opt.sess_stg;
1250
+ // setup connection and dispatch
1251
+ this.conn = _mqtt_conn(opt, this,
1252
+ this._init_dispatch(opt)); }
1253
+
1254
+ with(fns_ns) {
1255
+ for (let [k,v] of Object.entries(fns_ns)) {
1256
+ if (_isfn(v)) {this[k] = v;} }
1257
+ return this}
1258
+
1259
+
1243
1260
  static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
1244
- let self = class extends this {};
1245
- self.prototype.mqtt_ctx =
1261
+ let klass = class extends this {};
1262
+ klass.prototype.mqtt_ctx =
1246
1263
  mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
1247
- return self}
1264
+ return klass}
1265
+
1266
+
1267
+ async conn_emit(evt, arg, err_arg) {
1268
+ this.log_conn?.(evt, arg, err_arg);
1269
+ try {
1270
+ let fn_evt = this[await evt]; // microtask break using `await evt`
1271
+ if (fn_evt) {
1272
+ await fn_evt.call(this, this, arg, err_arg);}
1273
+ else if (err_arg) {throw err_arg} }
1274
+ catch (err) {
1275
+ this.on_error(err, evt);} }
1276
+
1277
+ on_error(err, evt) {
1278
+ console.warn('[[u8-mqtt error: %s]]', evt, err); }
1279
+ log_conn(evt, arg, err_arg) {
1280
+ console.info('[[u8-mqtt conn: %s]]', evt, arg, err_arg); }
1248
1281
 
1249
1282
 
1250
1283
  // automatic Client Id for connect()
1251
1284
  init_client_id(parts=['u8-mqtt--','']) {
1252
- let sess_stg=this.sess_stg;
1285
+ let sess_stg = this.sess_stg;
1253
1286
  let key, cid = sess_stg?.getItem(key=parts.join(' '));
1254
1287
  if (! cid) {
1255
1288
  cid = parts.join(Math.random().toString(36).slice(2));
1256
1289
  sess_stg?.setItem(key, cid);}
1257
1290
  return cid}
1258
1291
 
1259
- get sess_stg() {return globalThis.sessionStorage}
1260
-
1261
-
1262
- //on_error(err, evt) ::
1263
- // console.warn @ '[[u8-mqtt error: %s]]', evt, err
1264
-
1265
- //log_conn(evt, arg, err_arg) ::
1266
- // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1267
1292
 
1268
1293
  on_live(client, is_reconnect) {
1269
1294
  if (is_reconnect) {
1270
1295
  return client.connect()} }
1271
1296
 
1272
- //on_reconnect(client) ::
1297
+ // on_ready(client) ::
1298
+ // on_reconnect(client) ::
1299
+ on_disconnect(client, intentional) {
1300
+ if (! intentional) {
1301
+ return client.on_reconnect?.()} }
1273
1302
 
1274
1303
  _use_conn(fn_reconnect) {
1275
1304
  return (this.reconnect = fn_reconnect)?.()}
@@ -1281,27 +1310,20 @@ class MQTTCore extends MQTTBase {
1281
1310
  .then(this.reconnect)
1282
1311
  .then(opt.reconnect, opt.error);} }) }
1283
1312
 
1284
- on_disconnect(client, intentional) {
1285
- if (! intentional) {
1286
- return client.on_reconnect?.()} }
1287
-
1288
1313
  delay(ms) {
1289
1314
  return new Promise(done => setTimeout(done, ms)) }
1290
1315
 
1291
1316
  with_async_iter(async_iter, write_u8_pkt) {
1292
- let on_mqtt_chunk = this._conn_.set(
1293
- this.mqtt_ctx,
1294
- write_u8_pkt);
1295
-
1296
- this._msg_loop = ((async () => {
1297
- try {
1298
- async_iter = await async_iter;
1299
- for await (let chunk of async_iter)
1300
- on_mqtt_chunk(chunk);
1301
- this._conn_.reset();}
1302
- catch (err) {
1303
- this._conn_.reset(err);} })());
1304
-
1317
+ this.conn.setup(async_iter,
1318
+ write_u8_pkt,
1319
+ async (on_mqtt_chunk, conn) => {
1320
+ try {
1321
+ async_iter = await async_iter;
1322
+ for await (let chunk of async_iter)
1323
+ on_mqtt_chunk(chunk);
1324
+ conn.reset();}
1325
+ catch (err) {
1326
+ conn.reset(err);} } );
1305
1327
  return this}
1306
1328
 
1307
1329
 
@@ -1372,33 +1394,30 @@ class MQTTCore extends MQTTBase {
1372
1394
 
1373
1395
  websock.binaryType = 'arraybuffer';
1374
1396
 
1375
- let ready, {readyState} = websock;
1397
+ let ws_ready, readyState = websock.readyState;
1376
1398
  if (1 !== readyState) {
1377
1399
  if (0 !== readyState) {
1378
- throw new Error('Invalid WebSocket readyState') }
1379
-
1380
- ready = new Promise(fn => websock.onopen = fn); }
1400
+ throw new Error('WS readyState') }
1381
1401
 
1402
+ ws_ready = new Promise(ready => websock.onopen = ready); }
1382
1403
 
1383
- let {_conn_} = this;
1384
- let on_mqtt_chunk = _conn_.set(
1385
- this.mqtt_ctx,
1386
- async u8_pkt =>(
1387
- await ready
1388
- , websock.send(u8_pkt)) );
1404
+ this.conn.setup(ws_ready,
1405
+ u8_pkt => websock.send(u8_pkt),
1406
+ (on_mqtt_chunk, conn) => {
1407
+ websock.onmessage = evt =>(
1408
+ on_mqtt_chunk(new Uint8Array(evt.data)) );
1389
1409
 
1390
- websock.onmessage = evt =>(on_mqtt_chunk(new Uint8Array(evt.data)));
1391
- websock.onclose = evt => {
1392
- if (! evt.wasClean) {
1393
- var err = new Error('websocket connection close');
1394
- err.code = evt.code;
1395
- err.reason = evt.reason;}
1410
+ websock.onclose = evt => {
1411
+ if (! evt.wasClean) {
1412
+ var err = new Error('websocket close');
1413
+ err.code = evt.code;
1414
+ err.reason = evt.reason;}
1396
1415
 
1397
- _conn_.reset(err);};
1416
+ conn.reset(err);}; } );
1398
1417
 
1399
1418
  return this} }
1400
1419
 
1401
- const version = '0.5.2';
1420
+ const version = '0.6.0';
1402
1421
 
1403
1422
  const MQTTClient_v4 = /* #__PURE__ */
1404
1423
  MQTTCore.mqtt_ctx(4, mqtt_opts_v5);