u8-mqtt 0.5.3 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +1 -1
  2. package/cjs/basic-v4.cjs +283 -277
  3. package/cjs/basic-v4.cjs.map +1 -1
  4. package/cjs/basic-v5.cjs +297 -281
  5. package/cjs/basic-v5.cjs.map +1 -1
  6. package/cjs/full-v4.cjs +295 -289
  7. package/cjs/full-v4.cjs.map +1 -1
  8. package/cjs/full-v5.cjs +309 -293
  9. package/cjs/full-v5.cjs.map +1 -1
  10. package/cjs/index.cjs +310 -295
  11. package/cjs/index.cjs.map +1 -1
  12. package/cjs/v4.cjs +295 -289
  13. package/cjs/v4.cjs.map +1 -1
  14. package/cjs/v5.cjs +309 -293
  15. package/cjs/v5.cjs.map +1 -1
  16. package/code/_cmdid_dispatch.jsy +45 -69
  17. package/code/_conn.jsy +96 -72
  18. package/code/_dispatch.jsy +36 -28
  19. package/code/_utils.jsy +17 -0
  20. package/code/base.jsy +33 -61
  21. package/code/core.jsy +73 -51
  22. package/code/router_path.jsy +2 -1
  23. package/esm/basic-v4.js +283 -277
  24. package/esm/basic-v4.js.map +1 -1
  25. package/esm/basic-v5.js +297 -281
  26. package/esm/basic-v5.js.map +1 -1
  27. package/esm/deno/basic-v4.js +287 -281
  28. package/esm/deno/basic-v4.js.map +1 -1
  29. package/esm/deno/basic-v5.js +301 -285
  30. package/esm/deno/basic-v5.js.map +1 -1
  31. package/esm/deno/full-v4.js +299 -293
  32. package/esm/deno/full-v4.js.map +1 -1
  33. package/esm/deno/full-v5.js +313 -297
  34. package/esm/deno/full-v5.js.map +1 -1
  35. package/esm/deno/index.js +314 -298
  36. package/esm/deno/index.js.map +1 -1
  37. package/esm/deno/v4.js +299 -293
  38. package/esm/deno/v4.js.map +1 -1
  39. package/esm/deno/v5.js +313 -297
  40. package/esm/deno/v5.js.map +1 -1
  41. package/esm/full-v4.js +295 -289
  42. package/esm/full-v4.js.map +1 -1
  43. package/esm/full-v5.js +309 -293
  44. package/esm/full-v5.js.map +1 -1
  45. package/esm/index.js +310 -294
  46. package/esm/index.js.map +1 -1
  47. package/esm/node/basic-v4.js +283 -277
  48. package/esm/node/basic-v4.js.map +1 -1
  49. package/esm/node/basic-v4.mjs +283 -277
  50. package/esm/node/basic-v4.mjs.map +1 -1
  51. package/esm/node/basic-v5.js +297 -281
  52. package/esm/node/basic-v5.js.map +1 -1
  53. package/esm/node/basic-v5.mjs +297 -281
  54. package/esm/node/basic-v5.mjs.map +1 -1
  55. package/esm/node/full-v4.js +295 -289
  56. package/esm/node/full-v4.js.map +1 -1
  57. package/esm/node/full-v4.mjs +295 -289
  58. package/esm/node/full-v4.mjs.map +1 -1
  59. package/esm/node/full-v5.js +309 -293
  60. package/esm/node/full-v5.js.map +1 -1
  61. package/esm/node/full-v5.mjs +309 -293
  62. package/esm/node/full-v5.mjs.map +1 -1
  63. package/esm/node/index.js +310 -294
  64. package/esm/node/index.js.map +1 -1
  65. package/esm/node/index.mjs +310 -294
  66. package/esm/node/index.mjs.map +1 -1
  67. package/esm/node/v4.js +295 -289
  68. package/esm/node/v4.js.map +1 -1
  69. package/esm/node/v4.mjs +295 -289
  70. package/esm/node/v4.mjs.map +1 -1
  71. package/esm/node/v5.js +309 -293
  72. package/esm/node/v5.js.map +1 -1
  73. package/esm/node/v5.mjs +309 -293
  74. package/esm/node/v5.mjs.map +1 -1
  75. package/esm/v4.js +295 -289
  76. package/esm/v4.js.map +1 -1
  77. package/esm/v5.js +309 -293
  78. package/esm/v5.js.map +1 -1
  79. package/esm/web/basic-v4.js +283 -277
  80. package/esm/web/basic-v4.js.map +1 -1
  81. package/esm/web/basic-v4.min.js +1 -1
  82. package/esm/web/basic-v4.min.js.br +0 -0
  83. package/esm/web/basic-v4.min.js.gz +0 -0
  84. package/esm/web/basic-v5.js +297 -281
  85. package/esm/web/basic-v5.js.map +1 -1
  86. package/esm/web/basic-v5.min.js +1 -1
  87. package/esm/web/basic-v5.min.js.br +0 -0
  88. package/esm/web/basic-v5.min.js.gz +0 -0
  89. package/esm/web/full-v4.js +295 -289
  90. package/esm/web/full-v4.js.map +1 -1
  91. package/esm/web/full-v4.min.js +1 -1
  92. package/esm/web/full-v4.min.js.br +0 -0
  93. package/esm/web/full-v4.min.js.gz +0 -0
  94. package/esm/web/full-v5.js +309 -293
  95. package/esm/web/full-v5.js.map +1 -1
  96. package/esm/web/full-v5.min.js +1 -1
  97. package/esm/web/full-v5.min.js.br +0 -0
  98. package/esm/web/full-v5.min.js.gz +0 -0
  99. package/esm/web/index.js +310 -294
  100. package/esm/web/index.js.map +1 -1
  101. package/esm/web/index.min.js +1 -1
  102. package/esm/web/index.min.js.br +0 -0
  103. package/esm/web/index.min.js.gz +0 -0
  104. package/esm/web/v4.js +295 -289
  105. package/esm/web/v4.js.map +1 -1
  106. package/esm/web/v4.min.js +1 -1
  107. package/esm/web/v4.min.js.br +0 -0
  108. package/esm/web/v4.min.js.gz +0 -0
  109. package/esm/web/v5.js +309 -293
  110. package/esm/web/v5.js.map +1 -1
  111. package/esm/web/v5.min.js +1 -1
  112. package/esm/web/v5.min.js.br +0 -0
  113. package/esm/web/v5.min.js.gz +0 -0
  114. package/package.json +7 -8
package/esm/full-v4.js CHANGED
@@ -721,6 +721,23 @@ function parse(str, loose) {
721
721
  };
722
722
  }
723
723
 
724
+ const _isfn = v => typeof v === 'function';
725
+ const _isstr = v => typeof v === 'string';
726
+
727
+ function _interval(fn_callback) {
728
+ let tid;
729
+ return (( td ) => {
730
+ tid = clearInterval(tid);
731
+ if (td) {
732
+ tid = setInterval(fn_callback, 1000 * td);
733
+
734
+
735
+
736
+
737
+ // ensure the interval allows the NodeJS event loop to exit
738
+ tid.unref?.();
739
+ return true} }) }
740
+
724
741
  /*
725
742
  class AbstractTopicRouter ::
726
743
  async invoke(pkt, ctx) ::
@@ -811,7 +828,7 @@ function mqtt_topic_path_router() {
811
828
  let fn = args.pop();
812
829
  let priority = args.pop();
813
830
 
814
- if ('function' !== typeof fn) {
831
+ if (! _isfn(fn)) {
815
832
  if (fn) {throw new TypeError()}
816
833
  fn = _ignore;}
817
834
 
@@ -976,238 +993,108 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
976
993
  }
977
994
  }
978
995
 
979
- function ao_defer_ctx(as_res = (...args) => args) {
980
- let y,n,_pset = (a,b) => { y=a, n=b; };
981
- return p =>(
982
- p = new Promise(_pset)
983
- , as_res(p, y, n)) }
984
-
985
- const ao_defer_v = /* #__PURE__ */
986
- ao_defer_ctx();
987
-
988
- Promise.resolve({type:'init'});
989
-
990
- function _mqtt_conn(client, [on_mqtt, pkt_future]) {
991
- let _q_init = ao_defer_v(), _q_ready = ao_defer_v();
992
- let _send_ready = async (...args) => (await _q_ready[0])(...args);
993
- let _send_mqtt_pkt, _has_connected;
994
- client._send = _send_ready;
995
-
996
- return {
997
- async when_ready() {await _q_ready[0];}
998
-
999
- , ping: _ping_interval (() =>_send_mqtt_pkt?.('pingreq'))
1000
-
1001
- , reset(err) {
1002
- if (! _send_mqtt_pkt) {return}
1003
-
1004
- if (err) {
1005
- _q_init[2](err);}
1006
-
1007
- _send_mqtt_pkt = null;
1008
- _q_init = ao_defer_v();
1009
- client._send = _send_ready;
1010
-
1011
- // call client.on_conn_reset in next promise microtask
1012
- client.conn_emit('on_disconnect', false===err, err);}
1013
-
1014
- , async send_connect(... args) {
1015
- if (! _send_mqtt_pkt) {
1016
- await _q_init[0]; }// _send_mqtt_pkt is set before fulfilled
1017
-
1018
- // await connack response
1019
- let res = await _send_mqtt_pkt(...args);
1020
- if (0 == res[0].reason) {
1021
- _has_connected = true;
1022
- // resolve _q_ready[0] with _send_mqtt_pkt closure
1023
- _q_ready[1](client._send = _send_mqtt_pkt);
1024
- _q_ready = ao_defer_v();
1025
- client.conn_emit('on_ready');}
1026
-
1027
- return res}
1028
-
1029
- , is_set: (() =>!! _send_mqtt_pkt)
1030
- , set(mqtt_ctx, send_u8_pkt) {
1031
- if (_send_mqtt_pkt) {
1032
- throw new Error('Already connected')}
1033
-
1034
- mqtt_ctx = mqtt_ctx.mqtt_stream();
1035
- let sess_ctx = {mqtt: client};
1036
- let on_mqtt_chunk = u8_buf =>
1037
- on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
1038
-
1039
- _send_mqtt_pkt = async (type, pkt, key) => {
1040
- let res = undefined !== key
1041
- ? pkt_future(key) : true;
1042
-
1043
- await send_u8_pkt(
1044
- mqtt_ctx.encode_pkt(type, pkt));
996
+ async function _mqtt_cmd_evt(target, answer, pkt, ctx) {
997
+ /* target : on_mqtt_type = {
998
+ mqtt_pkt(pkt, ctx) {}, // generic
999
+
1000
+ mqtt_auth(pkt, ctx) {},
1001
+ mqtt_connect(pkt, ctx) {},
1002
+ mqtt_connack(pkt, ctx) {},
1003
+ mqtt_disconnect(pkt, ctx) {},
1004
+
1005
+ mqtt_publish(pkt, ctx) {},
1006
+ mqtt_subscribe(pkt, ctx) {},
1007
+ mqtt_unsubscribe(pkt, ctx) {},
1008
+
1009
+ mqtt_pingreq(pkt, ctx) {},
1010
+ mqtt_pingresp(pkt, ctx) {},
1011
+ } */
1012
+
1013
+ let pkt_fn = target[`mqtt_${pkt.type}`] || target.mqtt_pkt;
1014
+ await pkt_fn?.call(target, pkt, ctx);}
1015
+
1016
+ function _mqtt_cmd_type(target, answer, pkt, ctx) {
1017
+ answer(pkt.type, pkt);
1018
+ _mqtt_cmd_evt(target, answer, pkt, ctx);}
1019
+
1020
+ function _mqtt_cmd_id(target, answer, pkt) {
1021
+ answer(pkt.pkt_id, pkt);}
1022
+
1023
+
1024
+ const _mqtt_cmdids =[
1025
+ _ => {} // 0x0 reserved
1026
+ , _mqtt_cmd_evt // 0x1 connect
1027
+ , _mqtt_cmd_type // 0x2 connack
1028
+ , _mqtt_cmd_evt // 0x3 publish
1029
+ , _mqtt_cmd_id // 0x4 puback
1030
+ , _mqtt_cmd_id // 0x5 pubrec
1031
+ , _mqtt_cmd_id // 0x6 pubrel
1032
+ , _mqtt_cmd_id // 0x7 pubcomp
1033
+ , _mqtt_cmd_evt // 0x8 subscribe
1034
+ , _mqtt_cmd_id // 0x9 suback
1035
+ , _mqtt_cmd_evt // 0xa unsubscribe
1036
+ , _mqtt_cmd_id // 0xb unsuback
1037
+ , _mqtt_cmd_type // 0xc pingreq
1038
+ , _mqtt_cmd_type // 0xd pingresp
1039
+ , _mqtt_cmd_evt // 0xe disconnect
1040
+ , _mqtt_cmd_type ];// 0xf auth
1045
1041
 
1046
- return res};
1047
-
1048
- _q_init[1](_send_mqtt_pkt); // resolve _q_init with _send_mqtt_pkt closure
1049
-
1050
- // call client.on_live in next promise microtask
1051
- client.conn_emit('on_live', _has_connected);
1052
- return on_mqtt_chunk} } }
1053
-
1054
-
1055
- function _ping_interval(send_ping) {
1056
- let tid;
1057
- return (( td ) => {
1058
- tid = clearInterval(tid);
1059
- if (td) {
1060
- tid = setInterval(send_ping, 1000 * td);
1061
-
1062
-
1063
-
1064
-
1065
- // ensure the interval allows the NodeJS event loop to exit
1066
- tid.unref?.();
1067
- return true} }) }
1042
+ function _mqtt_dispatch(opt, target) {
1043
+ let hashbelt=[], rotate_ts=0;
1044
+ // default rotate at 1s across 5 buckets
1045
+ let { td: rotate_td=1000, n: rotate_n=5 } = opt?.rotate || {};
1068
1046
 
1069
- const _mqtt_cmdid_dispatch ={
1070
- create(target) {
1071
- return {__proto__: this, target, hashbelt: [new Map()]} }
1047
+ // Promise / future scaffolding
1048
+ let _pkt_id=100, _ftr_key; // use _ftr_key to reuse _by_key closure
1049
+ let _ftr_by_key = fn_answer => hashbelt[0].set(_ftr_key, fn_answer);
1072
1050
 
1073
- , bind_pkt_future(_pkt_id=100) {
1074
- let {hashbelt} = this;
1051
+ on_mqtt([]); // init hashbelt and rotate_ts
1052
+ return [on_mqtt, pkt_future]
1075
1053
 
1076
- let _tmp_; // use _tmp_ to reuse _by_key closure
1077
- let _by_key = answer_monad =>
1078
- hashbelt[0].set(_tmp_, answer_monad);
1079
1054
 
1080
- return (( pkt_or_key ) => {
1081
- if ('string' === typeof pkt_or_key) {
1082
- _tmp_ = pkt_or_key;}
1083
- else {
1084
- _pkt_id = (_pkt_id + 1) & 0xffff;
1085
- _tmp_ = pkt_or_key.pkt_id = _pkt_id;}
1055
+ function pkt_future(pkt_or_key) {
1056
+ if (! _isstr(pkt_or_key)) {
1057
+ _pkt_id = (_pkt_id + 1) & 0xffff; // 16-bit unsigned short
1058
+ _ftr_key = pkt_or_key.pkt_id = _pkt_id;}
1059
+ else _ftr_key = pkt_or_key;
1086
1060
 
1087
- return new Promise(_by_key)}) }
1061
+ return new Promise(_ftr_by_key)}
1088
1062
 
1089
- , answer(key, pkt) {
1090
- for (let map of this.hashbelt) {
1091
- let answer_monad = map.get(key);
1092
- if (undefined !== answer_monad) {
1063
+ function answer(key, pkt) {
1064
+ for (let map of hashbelt) {
1065
+ let fn_answer = map.get(key);
1066
+ if (fn_answer) {
1093
1067
  map.delete(key);
1094
1068
 
1095
- answer_monad([pkt, /*err*/]); // option/maybe monad
1069
+ fn_answer([pkt, /*err*/]); // option/maybe monad
1096
1070
  return true} }
1097
1071
  return false}
1098
1072
 
1099
- , rotate_belt(n) {
1100
- let {hashbelt} = this;
1101
- hashbelt.unshift(new Map());
1102
- for (let old of hashbelt.splice(n || 5)) {
1103
- for (let answer_monad of old.values()) {
1104
- answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
1105
-
1106
- , cmdids: ((() => {
1107
- return [
1108
- (() =>{} )// 0x0 reserved
1109
- , by_evt // 0x1 connect
1110
- , by_type // 0x2 connack
1111
- , by_evt // 0x3 publish
1112
- , by_id // 0x4 puback
1113
- , by_id // 0x5 pubrec
1114
- , by_id // 0x6 pubrel
1115
- , by_id // 0x7 pubcomp
1116
- , by_evt // 0x8 subscribe
1117
- , by_id // 0x9 suback
1118
- , by_evt // 0xa unsubscribe
1119
- , by_id // 0xb unsuback
1120
- , by_type // 0xc pingreq
1121
- , by_type // 0xd pingresp
1122
- , by_evt // 0xe disconnect
1123
- , by_type ]// 0xf auth
1124
-
1125
-
1126
- function by_id(disp, pkt) {
1127
- disp.answer(pkt.pkt_id, pkt); }
1128
-
1129
- function by_type(disp, pkt, ctx) {
1130
- disp.answer(pkt.type, pkt);
1131
- by_evt(disp, pkt, ctx);}
1132
-
1133
- async function by_evt({target}, pkt, ctx) {
1134
- let fn = target[`mqtt_${pkt.type}`]
1135
- || target.mqtt_pkt;
1136
-
1137
- await fn?.call(target, pkt, ctx);} })()) };
1138
-
1139
- /*
1140
- on_mqtt_type = {
1141
- mqtt_auth(pkt, ctx) ::
1142
- mqtt_connect(pkt, ctx) ::
1143
- mqtt_connack(pkt, ctx) ::
1144
- mqtt_disconnect(pkt, ctx) ::
1145
-
1146
- mqtt_publish(pkt, ctx)
1147
- mqtt_subscribe(pkt, ctx) ::
1148
- mqtt_unsubscribe(pkt, ctx) ::
1149
-
1150
- mqtt_pingreq(pkt, ctx) ::
1151
- mqtt_pingresp(pkt, ctx) ::
1152
- }
1153
- */
1154
-
1155
- function _mqtt_dispatch(opt, target) {
1156
- let _disp_ = _mqtt_cmdid_dispatch.create(target);
1157
- let { cmdids } = _disp_;
1158
-
1159
- // default rotate at 1s across 5 buckets
1160
- let { td: rotate_td=1000, n: rotate_n=5 } =
1161
- opt && opt.rotate || {};
1162
-
1163
- let rotate_ts = rotate_td + Date.now();
1164
-
1165
- return [on_mqtt,
1166
- _disp_.bind_pkt_future()]
1167
-
1168
1073
  function on_mqtt(pkt_list, ctx) {
1169
1074
  for (let pkt of pkt_list) {
1170
- cmdids[pkt.id](_disp_, pkt, ctx); }
1075
+ _mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
1171
1076
 
1172
- if (Date.now() > rotate_ts) {
1173
- _disp_.rotate_belt(rotate_n);
1174
- rotate_ts = rotate_td + Date.now();} } }
1077
+ // rotate after rotate_ts
1078
+ let now = Date.now();
1079
+ if (now > rotate_ts) {
1080
+ rotate_ts = rotate_td + now;
1081
+ hashbelt.unshift(new Map());
1082
+ while (hashbelt.length > rotate_n) {
1083
+ for (let fn_answer of hashbelt.pop().values()) {
1084
+ fn_answer([/*pkt*/, 'expired']); } } } } }// option/maybe monad
1175
1085
 
1176
1086
  class MQTTError extends Error {
1177
1087
  constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
1088
+ // use hex-encoded reasons to match MQTT spec documentation
1178
1089
  super(`[0x${reason.toString(16)}] ${reason.reason}`);
1179
1090
  this.mqtt_pkt = mqtt_pkt;
1180
1091
  this.reason = reason;} }
1181
1092
 
1182
1093
  class MQTTBase {
1183
- constructor(opt={}) {
1184
- this.with(opt);
1185
- this._conn_ = _mqtt_conn(this,
1186
- this._init_dispatch(opt, this)); }
1187
-
1188
- with(fns_ns) {
1189
- for (let [k,v] of Object.entries(fns_ns)) {
1190
- if ('function' === typeof v) {this[k] = v;} }
1191
- return this}
1192
-
1193
- async conn_emit(evt, arg, err_arg) {
1194
- this.log_conn?.(evt, arg, err_arg);
1195
- try {
1196
- let fn_evt = this[await evt]; // microtask break using `await evt`
1197
- if (fn_evt) {
1198
- await fn_evt.call(this, this, arg, err_arg);}
1199
- else if (err_arg) {throw err_arg} }
1200
- catch (err) {
1201
- this.on_error(err, evt);} }
1202
-
1203
- on_error(err, evt) {
1204
- console.warn('[[u8-mqtt error: %s]]', evt, err); }
1205
-
1206
1094
  // Handshaking Packets
1207
-
1208
1095
  async connect(pkt={}) {
1209
1096
  let cid = pkt.client_id;
1210
- if ('string' !== typeof cid) {
1097
+ if (! _isstr(cid)) {
1211
1098
  // see init_client_id implementation in core.jsy
1212
1099
  pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
1213
1100
  this.client_id = cid;
@@ -1215,23 +1102,20 @@ class MQTTBase {
1215
1102
  if (null == pkt.keep_alive) {
1216
1103
  pkt.keep_alive = 60;}
1217
1104
 
1218
- let res = await this._conn_
1219
- .send_connect('connect', pkt, 'connack');
1220
-
1221
- if (0 != res[0].reason) {
1222
- throw new this.MQTTError(res[0])}
1223
-
1224
- // TODO: merge with server's keep_alive frequency
1225
- this._conn_.ping(pkt.keep_alive);
1226
- return res}
1105
+ let response = await this._send0('connect', pkt, 'connack');
1106
+ if (0 != response[0].reason) {// compare to 0 to coerce to number
1107
+ throw new this.MQTTError(response[0])}
1108
+ return this.conn.on_conn(pkt, response)}
1227
1109
 
1228
1110
  async disconnect(pkt={}) {
1229
- let res = await this._send('disconnect', pkt);
1230
- this._conn_.reset(false);
1231
- return res}
1111
+ let response = await this._send0('disconnect', pkt);
1112
+ return this.conn.on_dis(pkt, response)}
1232
1113
 
1233
- auth(pkt={}) {
1234
- return this._send('auth', pkt, 'auth')}
1114
+ async auth(pkt={}) {
1115
+ let response = await this._send0('auth', pkt, 'auth');
1116
+ if (response[0].reason) {
1117
+ throw new this.MQTTError(response[0])}
1118
+ return this.conn.on_auth(pkt, response)}
1235
1119
 
1236
1120
  ping() {return this._send('pingreq', null, 'pingresp')}
1237
1121
  puback({pkt_id}) {return this._send('puback', {pkt_id})}
@@ -1268,20 +1152,18 @@ class MQTTBase {
1268
1152
  // alias: publish -- because 'pub' is shorter for semantic aliases above
1269
1153
  async pub(pkt, pub_opt) {
1270
1154
  if (undefined === pkt.payload) {
1271
- if ('function' === typeof pub_opt) {
1155
+ if (_isfn(pub_opt)) {
1156
+ // pub_opt as a function is fn_encode value
1272
1157
  pub_opt = {fn_encode: pub_opt};}
1273
1158
 
1274
- let {msg} = pkt;
1275
- switch (typeof msg) {
1276
- case 'function':
1277
- pub_opt = {...pub_opt, fn_encode: msg};
1278
- // flow into 'undefined' case
1279
- case 'undefined':
1280
- // return a single-value closure to publish packets
1281
- return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1159
+ let msg = pkt.msg, fn_encode = pub_opt?.fn_encode;
1160
+ if (null == msg || _isfn(msg)) {
1161
+ // when msg is a function, return closure using fn_encode
1162
+ if (msg) {pub_opt = {...pub_opt, fn_encode: msg};}
1163
+ // return a single-value closure to publish packets
1164
+ return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1282
1165
 
1283
1166
  // Encode payload from msg; fn_encode allows alternative to JSON.stringify
1284
- let {fn_encode} = pub_opt || {};
1285
1167
  pkt.payload = fn_encode
1286
1168
  ? await fn_encode(msg)
1287
1169
  : JSON.stringify(msg);}
@@ -1293,31 +1175,31 @@ class MQTTBase {
1293
1175
  pkt = pub_opt.xform(pkt) || pkt;} }
1294
1176
 
1295
1177
  return this._send('publish', pkt,
1296
- pkt.qos ? pkt : void 0 ) }// key
1178
+ pkt.qos ? pkt : null ) }// key
1297
1179
 
1298
1180
 
1299
1181
  // Internal API
1300
1182
 
1301
- /* async _send(type, pkt) -- provided by _conn_ and transport */
1183
+ /* async _send0(type, pkt) -- provided by conn and transport */
1184
+ /* async _send(type, pkt) -- provided by conn and transport */
1302
1185
 
1303
1186
  _init_dispatch(opt) {
1304
1187
  this.constructor?._once_();
1305
1188
  let target ={__proto__: opt.on_mqtt_type};
1306
1189
  target.mqtt_publish ||=
1307
1190
  this._init_router?.(opt, this, target);
1308
- return _mqtt_dispatch(this, target)}
1191
+ return _mqtt_dispatch(opt, target)}
1309
1192
 
1310
1193
  static _aliases() {
1311
1194
  return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
1312
1195
 
1313
- static _once_(self=this) {
1314
- self._once_ = _=>0;
1315
- let p = self.prototype;
1196
+ static _once_(klass=this) {
1197
+ klass._once_ = _=>0;
1198
+ var alias, name, p = klass.prototype;
1316
1199
  p.MQTTError = MQTTError;
1317
- for (let alias of self._aliases().split(/\s+/)) {
1318
- alias = alias.split(':');
1319
- let fn = alias[1] && p[alias[1]];
1320
- if (fn) {p[alias[0]] = fn;} } } }
1200
+ for (alias of klass._aliases().split(/\s+/)) {
1201
+ [alias, name] = alias.split(':');
1202
+ p[alias] = p[name];} } }
1321
1203
 
1322
1204
 
1323
1205
  function _as_topics(pkt, ex, topic_prefix) {
@@ -1342,37 +1224,167 @@ function _as_topics(pkt, ex, topic_prefix) {
1342
1224
  pkt.topics = pkt.topics.map(_prefix_topics);}
1343
1225
  return pkt}
1344
1226
 
1345
- const pkt_api = {
1346
- utf8(u8) { return new TextDecoder('utf-8').decode(u8 || this.payload ) },
1347
- json(u8) { return JSON.parse( this.utf8(u8) || null ) },
1348
- text(u8) { return this.utf8(u8) },
1349
- };
1227
+ const _defer_obj = o =>(
1228
+ o.p = new Promise((a,e) => { o.a=a; o.e=e; })
1229
+ , o);
1230
+
1231
+ function _dfn_reset(client, attr, fn_after) {
1232
+ // a resetable deferred for a function
1233
+ let self = {set}, afn = async (...args) => (await self.p)(...args);
1234
+ return set()
1235
+
1236
+ function set() {
1237
+ if (afn !== client[attr]) {
1238
+ _defer_obj(self).p.then(fn_after, _=>0);
1239
+ client[attr] = afn;}
1240
+ return self} }
1241
+
1242
+ function _mqtt_conn(opt, client, [on_mqtt, pkt_future]) {
1243
+ let _abort;
1244
+ let _dfn_send0 = _dfn_reset(client, '_send0', // client._send0 getter/setter
1245
+ _=> client.conn_emit('on_live', conn.has_connected));
1246
+ let _dfn_ready = _dfn_reset(client, '_send', // client._send getter/setter
1247
+ _=> client.conn_emit('on_ready'));
1248
+ let _keep_alive_ival = _interval (() =>client._send0('pingreq') );// resettable interval for keep_alive ping
1249
+
1250
+ let conn = Object.create({
1251
+ ping: (td=conn.keep_alive) => _keep_alive_ival(td)
1252
+
1253
+ , on_conn(pkt, response) {
1254
+ conn.has_connected = true;
1255
+ conn.keep_alive = opt.keep_alive || response[0].props?.server_keep_alive || pkt.keep_alive;
1256
+ client.conn_emit('on_conn');
1257
+ return opt.use_auth
1258
+ ? response // wait on enhanced authentication step
1259
+ : conn.on_auth(null, response) }// otherwise, connect is also auth
1260
+
1261
+ , on_auth(pkt, response) {
1262
+ _dfn_ready.a(_dfn_send0.p);
1263
+ if (0 != opt.keep_alive) {
1264
+ conn.ping();}
1265
+ client.conn_emit('on_auth', !pkt);
1266
+ return response}
1267
+
1268
+ , on_dis(pkt, response) {
1269
+ conn.reset(false);
1270
+ return response}
1271
+
1272
+ , reset(err) {
1273
+ if (err) {
1274
+ _dfn_send0.e(err); }// send error to uses of _send0 (connect, auth)
1275
+ _abort.e(err); // abort in-progress connections
1276
+
1277
+ delete conn.is_set;
1278
+ conn.ready = handshake();
1279
+ client.conn_emit('on_disconnect', false===err, err);}
1280
+
1281
+ , abort() {
1282
+ _dfn_ready.e(err); // abort all messages awaiting ready state
1283
+ return conn.reset(err)}
1284
+
1285
+ , async setup(gate, send_u8_pkt, init_msg_loop) {
1286
+ if (conn.is_set) {
1287
+ throw new Error() }// already in-progress
1288
+
1289
+ conn.is_set = true;
1290
+ await gate;
1291
+
1292
+ // setup send/recv MQTT parsing context
1293
+ let mqtt_ctx = client.mqtt_ctx.mqtt_stream();
1294
+
1295
+ {// setup inbound message loop
1296
+ let sess_ctx = {mqtt: client}; // mutable session context
1297
+ let on_mqtt_chunk = u8 => on_mqtt(mqtt_ctx.decode(u8), sess_ctx);
1298
+ init_msg_loop(on_mqtt_chunk, conn);}
1299
+
1300
+ // setup outbound message path and transport connection
1301
+ send_u8_pkt = await send_u8_pkt;
1302
+ _dfn_send0.a(
1303
+ async (type, pkt, key) => {
1304
+ let res = undefined !== key
1305
+ ? pkt_future(key) : true;
1306
+
1307
+ await send_u8_pkt(
1308
+ mqtt_ctx.encode_pkt(type, pkt));
1309
+ return res} ); } });
1310
+
1311
+ conn.ready = handshake();
1312
+ return conn
1313
+
1314
+ async function handshake() {
1315
+ _abort = _defer_obj({});
1316
+
1317
+ _keep_alive_ival(0); // clearInterval on keep alive ping
1318
+ _dfn_send0.set(); // reset client._send0 if necessary
1319
+ _dfn_ready.set(); // reset client._send if necessary
1320
+
1321
+ try {
1322
+ // set client._send0 as passtrhough after transport connection
1323
+ client._send0 = await Promise.race([_dfn_send0.p, _abort.p]);
1324
+
1325
+ // set client._send as passtrhough after ready
1326
+ client._send = await Promise.race([_dfn_ready.p, _abort.p]);
1327
+ return true}
1328
+ catch (err) {
1329
+ return false} } }
1330
+
1331
+ const pkt_api ={
1332
+ utf8(u8) {return new TextDecoder('utf-8').decode(u8 || this.payload )}
1333
+ , json(u8) {return JSON.parse( this.utf8(u8) || null )}
1334
+ , text(u8) {return this.utf8(u8)} };
1335
+
1336
+ const opt_default ={
1337
+ sess_stg: globalThis.sessionStorage};
1350
1338
 
1351
1339
  class MQTTCore extends MQTTBase {
1340
+ constructor(opt) {
1341
+ opt = {...opt_default, ...opt};
1342
+ super();
1343
+ this.with(opt);
1344
+ // settings for MQTTCore
1345
+ this.sess_stg = opt.sess_stg;
1346
+ // setup connection and dispatch
1347
+ this.conn = _mqtt_conn(opt, this,
1348
+ this._init_dispatch(opt)); }
1349
+
1350
+ with(fns_ns) {
1351
+ for (let [k,v] of Object.entries(fns_ns)) {
1352
+ if (_isfn(v)) {this[k] = v;} }
1353
+ return this}
1354
+
1355
+
1352
1356
  static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
1353
- let self = class extends this {};
1354
- self.prototype.mqtt_ctx =
1357
+ let klass = class extends this {};
1358
+ klass.prototype.mqtt_ctx =
1355
1359
  mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
1356
- return self}
1360
+ return klass}
1361
+
1362
+
1363
+ async conn_emit(evt, arg, err_arg) {
1364
+ this.log_conn?.(evt, arg, err_arg);
1365
+ try {
1366
+ let fn_evt = this[await evt]; // microtask break using `await evt`
1367
+ if (fn_evt) {
1368
+ await fn_evt.call(this, this, arg, err_arg);}
1369
+ else if (err_arg) {throw err_arg} }
1370
+ catch (err) {
1371
+ this.on_error(err, evt);} }
1372
+
1373
+ on_error(err, evt) {
1374
+ console.warn('[[u8-mqtt error: %s]]', evt, err); }
1375
+ log_conn(evt, arg, err_arg) {
1376
+ console.info('[[u8-mqtt conn: %s]]', evt, arg, err_arg); }
1357
1377
 
1358
1378
 
1359
1379
  // automatic Client Id for connect()
1360
1380
  init_client_id(parts=['u8-mqtt--','']) {
1361
- let sess_stg=this.sess_stg;
1381
+ let sess_stg = this.sess_stg;
1362
1382
  let key, cid = sess_stg?.getItem(key=parts.join(' '));
1363
1383
  if (! cid) {
1364
1384
  cid = parts.join(Math.random().toString(36).slice(2));
1365
1385
  sess_stg?.setItem(key, cid);}
1366
1386
  return cid}
1367
1387
 
1368
- get sess_stg() {return globalThis.sessionStorage}
1369
-
1370
-
1371
- //on_error(err, evt) ::
1372
- // console.warn @ '[[u8-mqtt error: %s]]', evt, err
1373
-
1374
- //log_conn(evt, arg, err_arg) ::
1375
- // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1376
1388
 
1377
1389
  on_live(client, is_reconnect) {
1378
1390
  if (is_reconnect) {
@@ -1398,19 +1410,16 @@ class MQTTCore extends MQTTBase {
1398
1410
  return new Promise(done => setTimeout(done, ms)) }
1399
1411
 
1400
1412
  with_async_iter(async_iter, write_u8_pkt) {
1401
- let on_mqtt_chunk = this._conn_.set(
1402
- this.mqtt_ctx,
1403
- write_u8_pkt);
1404
-
1405
- this._msg_loop = ((async () => {
1406
- try {
1407
- async_iter = await async_iter;
1408
- for await (let chunk of async_iter)
1409
- on_mqtt_chunk(chunk);
1410
- this._conn_.reset();}
1411
- catch (err) {
1412
- this._conn_.reset(err);} })());
1413
-
1413
+ this.conn.setup(async_iter,
1414
+ write_u8_pkt,
1415
+ async (on_mqtt_chunk, conn) => {
1416
+ try {
1417
+ async_iter = await async_iter;
1418
+ for await (let chunk of async_iter)
1419
+ on_mqtt_chunk(chunk);
1420
+ conn.reset();}
1421
+ catch (err) {
1422
+ conn.reset(err);} } );
1414
1423
  return this}
1415
1424
 
1416
1425
 
@@ -1481,33 +1490,30 @@ class MQTTCore extends MQTTBase {
1481
1490
 
1482
1491
  websock.binaryType = 'arraybuffer';
1483
1492
 
1484
- let ready, {readyState} = websock;
1493
+ let ws_ready, readyState = websock.readyState;
1485
1494
  if (1 !== readyState) {
1486
1495
  if (0 !== readyState) {
1487
- throw new Error('Invalid WebSocket readyState') }
1488
-
1489
- ready = new Promise(fn => websock.onopen = fn); }
1496
+ throw new Error('WS readyState') }
1490
1497
 
1498
+ ws_ready = new Promise(ready => websock.onopen = ready); }
1491
1499
 
1492
- let {_conn_} = this;
1493
- let on_mqtt_chunk = _conn_.set(
1494
- this.mqtt_ctx,
1495
- async u8_pkt =>(
1496
- await ready
1497
- , websock.send(u8_pkt)) );
1500
+ this.conn.setup(ws_ready,
1501
+ u8_pkt => websock.send(u8_pkt),
1502
+ (on_mqtt_chunk, conn) => {
1503
+ websock.onmessage = evt =>(
1504
+ on_mqtt_chunk(new Uint8Array(evt.data)) );
1498
1505
 
1499
- websock.onmessage = evt =>(on_mqtt_chunk(new Uint8Array(evt.data)));
1500
- websock.onclose = evt => {
1501
- if (! evt.wasClean) {
1502
- var err = new Error('websocket connection close');
1503
- err.code = evt.code;
1504
- err.reason = evt.reason;}
1506
+ websock.onclose = evt => {
1507
+ if (! evt.wasClean) {
1508
+ var err = new Error('websocket close');
1509
+ err.code = evt.code;
1510
+ err.reason = evt.reason;}
1505
1511
 
1506
- _conn_.reset(err);};
1512
+ conn.reset(err);}; } );
1507
1513
 
1508
1514
  return this} }
1509
1515
 
1510
- const version = '0.5.3';
1516
+ const version = '0.6.1';
1511
1517
 
1512
1518
  const MQTTClient_v4 = /* #__PURE__ */
1513
1519
  with_topic_path_router(