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/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());
@@ -877,88 +887,15 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
877
887
  }
878
888
  }
879
889
 
880
- function ao_defer_ctx(as_res = (...args) => args) {
881
- let y,n,_pset = (a,b) => { y=a, n=b; };
882
- return p =>(
883
- p = new Promise(_pset)
884
- , as_res(p, y, n)) }
885
-
886
- const ao_defer_v = /* #__PURE__ */
887
- ao_defer_ctx();
888
-
889
- Promise.resolve({type:'init'});
890
-
891
- function _mqtt_conn(client, [on_mqtt, pkt_future]) {
892
- let _q_init = ao_defer_v(), _q_ready = ao_defer_v();
893
- let _send_ready = async (...args) => (await _q_ready[0])(...args);
894
- let _send_mqtt_pkt, _has_connected;
895
- client._send = _send_ready;
896
-
897
- return {
898
- async when_ready() {await _q_ready[0];}
899
-
900
- , ping: _ping_interval (() =>_send_mqtt_pkt?.('pingreq'))
901
-
902
- , reset(err) {
903
- if (! _send_mqtt_pkt) {return}
904
-
905
- if (err) {
906
- _q_init[2](err);}
907
-
908
- _send_mqtt_pkt = null;
909
- _q_init = ao_defer_v();
910
- client._send = _send_ready;
911
-
912
- // call client.on_conn_reset in next promise microtask
913
- client.conn_emit('on_disconnect', false===err, err);}
914
-
915
- , async send_connect(... args) {
916
- if (! _send_mqtt_pkt) {
917
- await _q_init[0]; }// _send_mqtt_pkt is set before fulfilled
918
-
919
- // await connack response
920
- let res = await _send_mqtt_pkt(...args);
921
- if (0 == res[0].reason) {
922
- _has_connected = true;
923
- // resolve _q_ready[0] with _send_mqtt_pkt closure
924
- _q_ready[1](client._send = _send_mqtt_pkt);
925
- _q_ready = ao_defer_v();
926
- client.conn_emit('on_ready');}
927
-
928
- return res}
929
-
930
- , is_set: (() =>!! _send_mqtt_pkt)
931
- , set(mqtt_ctx, send_u8_pkt) {
932
- if (_send_mqtt_pkt) {
933
- throw new Error('Already connected')}
934
-
935
- mqtt_ctx = mqtt_ctx.mqtt_stream();
936
- let sess_ctx = {mqtt: client};
937
- let on_mqtt_chunk = u8_buf =>
938
- on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
939
-
940
- _send_mqtt_pkt = async (type, pkt, key) => {
941
- let res = undefined !== key
942
- ? pkt_future(key) : true;
943
-
944
- await send_u8_pkt(
945
- mqtt_ctx.encode_pkt(type, pkt));
946
-
947
- return res};
948
-
949
- _q_init[1](_send_mqtt_pkt); // resolve _q_init with _send_mqtt_pkt closure
950
-
951
- // call client.on_live in next promise microtask
952
- client.conn_emit('on_live', _has_connected);
953
- return on_mqtt_chunk} } }
954
-
890
+ const _isfn = v => typeof v === 'function';
891
+ const _isstr = v => typeof v === 'string';
955
892
 
956
- function _ping_interval(send_ping) {
893
+ function _interval(fn_callback) {
957
894
  let tid;
958
895
  return (( td ) => {
959
896
  tid = clearInterval(tid);
960
897
  if (td) {
961
- tid = setInterval(send_ping, 1000 * td);
898
+ tid = setInterval(fn_callback, 1000 * td);
962
899
 
963
900
 
964
901
 
@@ -967,148 +904,108 @@ function _ping_interval(send_ping) {
967
904
  tid.unref?.();
968
905
  return true} }) }
969
906
 
970
- const _mqtt_cmdid_dispatch ={
971
- create(target) {
972
- 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
973
952
 
974
- , bind_pkt_future(_pkt_id=100) {
975
- 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);
961
+
962
+ on_mqtt([]); // init hashbelt and rotate_ts
963
+ return [on_mqtt, pkt_future]
976
964
 
977
- let _tmp_; // use _tmp_ to reuse _by_key closure
978
- let _by_key = answer_monad =>
979
- hashbelt[0].set(_tmp_, answer_monad);
980
965
 
981
- return (( pkt_or_key ) => {
982
- if ('string' === typeof pkt_or_key) {
983
- _tmp_ = pkt_or_key;}
984
- else {
985
- _pkt_id = (_pkt_id + 1) & 0xffff;
986
- _tmp_ = pkt_or_key.pkt_id = _pkt_id;}
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;
987
971
 
988
- return new Promise(_by_key)}) }
972
+ return new Promise(_ftr_by_key)}
989
973
 
990
- , answer(key, pkt) {
991
- for (let map of this.hashbelt) {
992
- let answer_monad = map.get(key);
993
- if (undefined !== answer_monad) {
974
+ function answer(key, pkt) {
975
+ for (let map of hashbelt) {
976
+ let fn_answer = map.get(key);
977
+ if (fn_answer) {
994
978
  map.delete(key);
995
979
 
996
- answer_monad([pkt, /*err*/]); // option/maybe monad
980
+ fn_answer([pkt, /*err*/]); // option/maybe monad
997
981
  return true} }
998
982
  return false}
999
983
 
1000
- , rotate_belt(n) {
1001
- let {hashbelt} = this;
1002
- hashbelt.unshift(new Map());
1003
- for (let old of hashbelt.splice(n || 5)) {
1004
- for (let answer_monad of old.values()) {
1005
- answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
1006
-
1007
- , cmdids: ((() => {
1008
- return [
1009
- (() =>{} )// 0x0 reserved
1010
- , by_evt // 0x1 connect
1011
- , by_type // 0x2 connack
1012
- , by_evt // 0x3 publish
1013
- , by_id // 0x4 puback
1014
- , by_id // 0x5 pubrec
1015
- , by_id // 0x6 pubrel
1016
- , by_id // 0x7 pubcomp
1017
- , by_evt // 0x8 subscribe
1018
- , by_id // 0x9 suback
1019
- , by_evt // 0xa unsubscribe
1020
- , by_id // 0xb unsuback
1021
- , by_type // 0xc pingreq
1022
- , by_type // 0xd pingresp
1023
- , by_evt // 0xe disconnect
1024
- , by_type ]// 0xf auth
1025
-
1026
-
1027
- function by_id(disp, pkt) {
1028
- disp.answer(pkt.pkt_id, pkt); }
1029
-
1030
- function by_type(disp, pkt, ctx) {
1031
- disp.answer(pkt.type, pkt);
1032
- by_evt(disp, pkt, ctx);}
1033
-
1034
- async function by_evt({target}, pkt, ctx) {
1035
- let fn = target[`mqtt_${pkt.type}`]
1036
- || target.mqtt_pkt;
1037
-
1038
- await fn?.call(target, pkt, ctx);} })()) };
1039
-
1040
- /*
1041
- on_mqtt_type = {
1042
- mqtt_auth(pkt, ctx) ::
1043
- mqtt_connect(pkt, ctx) ::
1044
- mqtt_connack(pkt, ctx) ::
1045
- mqtt_disconnect(pkt, ctx) ::
1046
-
1047
- mqtt_publish(pkt, ctx)
1048
- mqtt_subscribe(pkt, ctx) ::
1049
- mqtt_unsubscribe(pkt, ctx) ::
1050
-
1051
- mqtt_pingreq(pkt, ctx) ::
1052
- mqtt_pingresp(pkt, ctx) ::
1053
- }
1054
- */
1055
-
1056
- function _mqtt_dispatch(opt, target) {
1057
- let _disp_ = _mqtt_cmdid_dispatch.create(target);
1058
- let { cmdids } = _disp_;
1059
-
1060
- // default rotate at 1s across 5 buckets
1061
- let { td: rotate_td=1000, n: rotate_n=5 } =
1062
- opt && opt.rotate || {};
1063
-
1064
- let rotate_ts = rotate_td + Date.now();
1065
-
1066
- return [on_mqtt,
1067
- _disp_.bind_pkt_future()]
1068
-
1069
984
  function on_mqtt(pkt_list, ctx) {
1070
985
  for (let pkt of pkt_list) {
1071
- cmdids[pkt.id](_disp_, pkt, ctx); }
986
+ _mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
1072
987
 
1073
- if (Date.now() > rotate_ts) {
1074
- _disp_.rotate_belt(rotate_n);
1075
- 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
1076
996
 
1077
997
  class MQTTError extends Error {
1078
998
  constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
999
+ // use hex-encoded reasons to match MQTT spec documentation
1079
1000
  super(`[0x${reason.toString(16)}] ${reason.reason}`);
1080
1001
  this.mqtt_pkt = mqtt_pkt;
1081
1002
  this.reason = reason;} }
1082
1003
 
1083
1004
  class MQTTBase {
1084
- constructor(opt={}) {
1085
- this.with(opt);
1086
- this._conn_ = _mqtt_conn(this,
1087
- this._init_dispatch(opt, this)); }
1088
-
1089
- with(fns_ns) {
1090
- for (let [k,v] of Object.entries(fns_ns)) {
1091
- if ('function' === typeof v) {this[k] = v;} }
1092
- return this}
1093
-
1094
- async conn_emit(evt, arg, err_arg) {
1095
- this.log_conn?.(evt, arg, err_arg);
1096
- try {
1097
- let fn_evt = this[await evt]; // microtask break using `await evt`
1098
- if (fn_evt) {
1099
- await fn_evt.call(this, this, arg, err_arg);}
1100
- else if (err_arg) {throw err_arg} }
1101
- catch (err) {
1102
- this.on_error(err, evt);} }
1103
-
1104
- on_error(err, evt) {
1105
- console.warn('[[u8-mqtt error: %s]]', evt, err); }
1106
-
1107
1005
  // Handshaking Packets
1108
-
1109
1006
  async connect(pkt={}) {
1110
1007
  let cid = pkt.client_id;
1111
- if ('string' !== typeof cid) {
1008
+ if (! _isstr(cid)) {
1112
1009
  // see init_client_id implementation in core.jsy
1113
1010
  pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
1114
1011
  this.client_id = cid;
@@ -1116,23 +1013,20 @@ class MQTTBase {
1116
1013
  if (null == pkt.keep_alive) {
1117
1014
  pkt.keep_alive = 60;}
1118
1015
 
1119
- let res = await this._conn_
1120
- .send_connect('connect', pkt, 'connack');
1121
-
1122
- if (0 != res[0].reason) {
1123
- throw new this.MQTTError(res[0])}
1124
-
1125
- // TODO: merge with server's keep_alive frequency
1126
- this._conn_.ping(pkt.keep_alive);
1127
- 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)}
1128
1020
 
1129
1021
  async disconnect(pkt={}) {
1130
- let res = await this._send('disconnect', pkt);
1131
- this._conn_.reset(false);
1132
- return res}
1022
+ let response = await this._send0('disconnect', pkt);
1023
+ return this.conn.on_dis(pkt, response)}
1133
1024
 
1134
- auth(pkt={}) {
1135
- 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)}
1136
1030
 
1137
1031
  ping() {return this._send('pingreq', null, 'pingresp')}
1138
1032
  puback({pkt_id}) {return this._send('puback', {pkt_id})}
@@ -1169,20 +1063,18 @@ class MQTTBase {
1169
1063
  // alias: publish -- because 'pub' is shorter for semantic aliases above
1170
1064
  async pub(pkt, pub_opt) {
1171
1065
  if (undefined === pkt.payload) {
1172
- if ('function' === typeof pub_opt) {
1066
+ if (_isfn(pub_opt)) {
1067
+ // pub_opt as a function is fn_encode value
1173
1068
  pub_opt = {fn_encode: pub_opt};}
1174
1069
 
1175
- let {msg} = pkt;
1176
- switch (typeof msg) {
1177
- case 'function':
1178
- pub_opt = {...pub_opt, fn_encode: msg};
1179
- // flow into 'undefined' case
1180
- case 'undefined':
1181
- // return a single-value closure to publish packets
1182
- 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)}
1183
1076
 
1184
1077
  // Encode payload from msg; fn_encode allows alternative to JSON.stringify
1185
- let {fn_encode} = pub_opt || {};
1186
1078
  pkt.payload = fn_encode
1187
1079
  ? await fn_encode(msg)
1188
1080
  : JSON.stringify(msg);}
@@ -1194,31 +1086,31 @@ class MQTTBase {
1194
1086
  pkt = pub_opt.xform(pkt) || pkt;} }
1195
1087
 
1196
1088
  return this._send('publish', pkt,
1197
- pkt.qos ? pkt : void 0 ) }// key
1089
+ pkt.qos ? pkt : null ) }// key
1198
1090
 
1199
1091
 
1200
1092
  // Internal API
1201
1093
 
1202
- /* 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 */
1203
1096
 
1204
1097
  _init_dispatch(opt) {
1205
1098
  this.constructor?._once_();
1206
1099
  let target ={__proto__: opt.on_mqtt_type};
1207
1100
  target.mqtt_publish ||=
1208
1101
  this._init_router?.(opt, this, target);
1209
- return _mqtt_dispatch(this, target)}
1102
+ return _mqtt_dispatch(opt, target)}
1210
1103
 
1211
1104
  static _aliases() {
1212
1105
  return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
1213
1106
 
1214
- static _once_(self=this) {
1215
- self._once_ = _=>0;
1216
- let p = self.prototype;
1107
+ static _once_(klass=this) {
1108
+ klass._once_ = _=>0;
1109
+ var alias, name, p = klass.prototype;
1217
1110
  p.MQTTError = MQTTError;
1218
- for (let alias of self._aliases().split(/\s+/)) {
1219
- alias = alias.split(':');
1220
- let fn = alias[1] && p[alias[1]];
1221
- if (fn) {p[alias[0]] = fn;} } } }
1111
+ for (alias of klass._aliases().split(/\s+/)) {
1112
+ [alias, name] = alias.split(':');
1113
+ p[alias] = p[name];} } }
1222
1114
 
1223
1115
 
1224
1116
  function _as_topics(pkt, ex, topic_prefix) {
@@ -1243,37 +1135,167 @@ function _as_topics(pkt, ex, topic_prefix) {
1243
1135
  pkt.topics = pkt.topics.map(_prefix_topics);}
1244
1136
  return pkt}
1245
1137
 
1246
- const pkt_api = {
1247
- utf8(u8) { return new TextDecoder('utf-8').decode(u8 || this.payload ) },
1248
- json(u8) { return JSON.parse( this.utf8(u8) || null ) },
1249
- text(u8) { return this.utf8(u8) },
1250
- };
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};
1251
1249
 
1252
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
+
1253
1267
  static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
1254
- let self = class extends this {};
1255
- self.prototype.mqtt_ctx =
1268
+ let klass = class extends this {};
1269
+ klass.prototype.mqtt_ctx =
1256
1270
  mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
1257
- 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); }
1258
1288
 
1259
1289
 
1260
1290
  // automatic Client Id for connect()
1261
1291
  init_client_id(parts=['u8-mqtt--','']) {
1262
- let sess_stg=this.sess_stg;
1292
+ let sess_stg = this.sess_stg;
1263
1293
  let key, cid = sess_stg?.getItem(key=parts.join(' '));
1264
1294
  if (! cid) {
1265
1295
  cid = parts.join(Math.random().toString(36).slice(2));
1266
1296
  sess_stg?.setItem(key, cid);}
1267
1297
  return cid}
1268
1298
 
1269
- get sess_stg() {return globalThis.sessionStorage}
1270
-
1271
-
1272
- //on_error(err, evt) ::
1273
- // console.warn @ '[[u8-mqtt error: %s]]', evt, err
1274
-
1275
- //log_conn(evt, arg, err_arg) ::
1276
- // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1277
1299
 
1278
1300
  on_live(client, is_reconnect) {
1279
1301
  if (is_reconnect) {
@@ -1299,19 +1321,16 @@ class MQTTCore extends MQTTBase {
1299
1321
  return new Promise(done => setTimeout(done, ms)) }
1300
1322
 
1301
1323
  with_async_iter(async_iter, write_u8_pkt) {
1302
- let on_mqtt_chunk = this._conn_.set(
1303
- this.mqtt_ctx,
1304
- write_u8_pkt);
1305
-
1306
- this._msg_loop = ((async () => {
1307
- try {
1308
- async_iter = await async_iter;
1309
- for await (let chunk of async_iter)
1310
- on_mqtt_chunk(chunk);
1311
- this._conn_.reset();}
1312
- catch (err) {
1313
- this._conn_.reset(err);} })());
1314
-
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);} } );
1315
1334
  return this}
1316
1335
 
1317
1336
 
@@ -1382,33 +1401,30 @@ class MQTTCore extends MQTTBase {
1382
1401
 
1383
1402
  websock.binaryType = 'arraybuffer';
1384
1403
 
1385
- let ready, {readyState} = websock;
1404
+ let ws_ready, readyState = websock.readyState;
1386
1405
  if (1 !== readyState) {
1387
1406
  if (0 !== readyState) {
1388
- throw new Error('Invalid WebSocket readyState') }
1389
-
1390
- ready = new Promise(fn => websock.onopen = fn); }
1407
+ throw new Error('WS readyState') }
1391
1408
 
1409
+ ws_ready = new Promise(ready => websock.onopen = ready); }
1392
1410
 
1393
- let {_conn_} = this;
1394
- let on_mqtt_chunk = _conn_.set(
1395
- this.mqtt_ctx,
1396
- async u8_pkt =>(
1397
- await ready
1398
- , 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)) );
1399
1416
 
1400
- websock.onmessage = evt =>(on_mqtt_chunk(new Uint8Array(evt.data)));
1401
- websock.onclose = evt => {
1402
- if (! evt.wasClean) {
1403
- var err = new Error('websocket connection close');
1404
- err.code = evt.code;
1405
- 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;}
1406
1422
 
1407
- _conn_.reset(err);};
1423
+ conn.reset(err);}; } );
1408
1424
 
1409
1425
  return this} }
1410
1426
 
1411
- const version = '0.5.3-node';
1427
+ const version = '0.6.0-node';
1412
1428
 
1413
1429
  const MQTTClient_v4 = /* #__PURE__ */
1414
1430
  MQTTCore.mqtt_ctx(4, mqtt_opts_v5);