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