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