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/index.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());
@@ -803,6 +813,23 @@ function parse(str, loose) {
803
813
  };
804
814
  }
805
815
 
816
+ const _isfn = v => typeof v === 'function';
817
+ const _isstr = v => typeof v === 'string';
818
+
819
+ function _interval(fn_callback) {
820
+ let tid;
821
+ return (( td ) => {
822
+ tid = clearInterval(tid);
823
+ if (td) {
824
+ tid = setInterval(fn_callback, 1000 * td);
825
+
826
+
827
+
828
+
829
+ // ensure the interval allows the NodeJS event loop to exit
830
+ tid.unref?.();
831
+ return true} }) }
832
+
806
833
  /*
807
834
  class AbstractTopicRouter ::
808
835
  async invoke(pkt, ctx) ::
@@ -893,7 +920,7 @@ function mqtt_topic_path_router() {
893
920
  let fn = args.pop();
894
921
  let priority = args.pop();
895
922
 
896
- if ('function' !== typeof fn) {
923
+ if (! _isfn(fn)) {
897
924
  if (fn) {throw new TypeError()}
898
925
  fn = _ignore;}
899
926
 
@@ -1043,6 +1070,9 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
1043
1070
  return _encode_by_type[type]( mqtt_level, pkt ) },
1044
1071
 
1045
1072
  decode_pkt(b0, u8_body) {
1073
+ if (b0.map) // Uint8Array in first arg
1074
+ return mqtt_raw_dispatch(this)(b0)[0]
1075
+
1046
1076
  let fn_decode = _decode_by_id[b0>>>4] || _decode_by_id[0];
1047
1077
  return fn_decode?.({__proto__: this.pkt_ctx, b0}, u8_body) },
1048
1078
 
@@ -1055,262 +1085,129 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
1055
1085
  }
1056
1086
  }
1057
1087
 
1058
- function ao_defer_ctx(as_res = (...args) => args) {
1059
- let y,n,_pset = (a,b) => { y=a, n=b; };
1060
- return p =>(
1061
- p = new Promise(_pset)
1062
- , as_res(p, y, n)) }
1063
-
1064
- const ao_defer_v = /* #__PURE__ */
1065
- ao_defer_ctx();
1066
-
1067
- Promise.resolve({type:'init'});
1068
-
1069
- function _mqtt_conn(client, [on_mqtt, pkt_future]) {
1070
- let _q_init = ao_defer_v(), _q_ready = ao_defer_v();
1071
- let _send_ready = async (...args) => (await _q_ready[0])(...args);
1072
- let _send_mqtt_pkt, _has_connected;
1073
- client._send = _send_ready;
1074
-
1075
- return {
1076
- async when_ready() {await _q_ready[0];}
1077
-
1078
- , ping: _ping_interval (() =>_send_mqtt_pkt?.('pingreq'))
1079
-
1080
- , reset(err) {
1081
- if (! _send_mqtt_pkt) {return}
1082
-
1083
- if (err) {
1084
- _q_init[2](err);}
1085
-
1086
- _send_mqtt_pkt = null;
1087
- _q_init = ao_defer_v();
1088
- client._send = _send_ready;
1089
-
1090
- // call client.on_conn_reset in next promise microtask
1091
- client.conn_emit('on_disconnect', false===err, err);}
1092
-
1093
- , async send_connect(... args) {
1094
- if (! _send_mqtt_pkt) {
1095
- await _q_init[0]; }// _send_mqtt_pkt is set before fulfilled
1096
-
1097
- // await connack response
1098
- let res = await _send_mqtt_pkt(...args);
1099
- if (0 == res[0].reason) {
1100
- _has_connected = true;
1101
- // resolve _q_ready[0] with _send_mqtt_pkt closure
1102
- _q_ready[1](client._send = _send_mqtt_pkt);
1103
- _q_ready = ao_defer_v();
1104
- client.conn_emit('on_ready');}
1105
-
1106
- return res}
1107
-
1108
- , is_set: (() =>!! _send_mqtt_pkt)
1109
- , set(mqtt_ctx, send_u8_pkt) {
1110
- if (_send_mqtt_pkt) {
1111
- throw new Error('Already connected')}
1088
+ async function _mqtt_cmd_evt(target, answer, pkt, ctx) {
1089
+ /* target : on_mqtt_type = {
1090
+ mqtt_pkt(pkt, ctx) {}, // generic
1091
+
1092
+ mqtt_auth(pkt, ctx) {},
1093
+ mqtt_connect(pkt, ctx) {},
1094
+ mqtt_connack(pkt, ctx) {},
1095
+ mqtt_disconnect(pkt, ctx) {},
1096
+
1097
+ mqtt_publish(pkt, ctx) {},
1098
+ mqtt_subscribe(pkt, ctx) {},
1099
+ mqtt_unsubscribe(pkt, ctx) {},
1100
+
1101
+ mqtt_pingreq(pkt, ctx) {},
1102
+ mqtt_pingresp(pkt, ctx) {},
1103
+ } */
1104
+
1105
+ let pkt_fn = target[`mqtt_${pkt.type}`] || target.mqtt_pkt;
1106
+ await pkt_fn?.call(target, pkt, ctx);}
1107
+
1108
+ function _mqtt_cmd_type(target, answer, pkt, ctx) {
1109
+ answer(pkt.type, pkt);
1110
+ _mqtt_cmd_evt(target, answer, pkt, ctx);}
1111
+
1112
+ function _mqtt_cmd_id(target, answer, pkt) {
1113
+ answer(pkt.pkt_id, pkt);}
1114
+
1115
+
1116
+ const _mqtt_cmdids =[
1117
+ _ => {} // 0x0 reserved
1118
+ , _mqtt_cmd_evt // 0x1 connect
1119
+ , _mqtt_cmd_type // 0x2 connack
1120
+ , _mqtt_cmd_evt // 0x3 publish
1121
+ , _mqtt_cmd_id // 0x4 puback
1122
+ , _mqtt_cmd_id // 0x5 pubrec
1123
+ , _mqtt_cmd_id // 0x6 pubrel
1124
+ , _mqtt_cmd_id // 0x7 pubcomp
1125
+ , _mqtt_cmd_evt // 0x8 subscribe
1126
+ , _mqtt_cmd_id // 0x9 suback
1127
+ , _mqtt_cmd_evt // 0xa unsubscribe
1128
+ , _mqtt_cmd_id // 0xb unsuback
1129
+ , _mqtt_cmd_type // 0xc pingreq
1130
+ , _mqtt_cmd_type // 0xd pingresp
1131
+ , _mqtt_cmd_evt // 0xe disconnect
1132
+ , _mqtt_cmd_type ];// 0xf auth
1112
1133
 
1113
- mqtt_ctx = mqtt_ctx.mqtt_stream();
1114
- let sess_ctx = {mqtt: client};
1115
- let on_mqtt_chunk = u8_buf =>
1116
- on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
1117
-
1118
- _send_mqtt_pkt = async (type, pkt, key) => {
1119
- let res = undefined !== key
1120
- ? pkt_future(key) : true;
1121
-
1122
- await send_u8_pkt(
1123
- mqtt_ctx.encode_pkt(type, pkt));
1124
-
1125
- return res};
1126
-
1127
- _q_init[1](_send_mqtt_pkt); // resolve _q_init with _send_mqtt_pkt closure
1128
-
1129
- // call client.on_live in next promise microtask
1130
- client.conn_emit('on_live', _has_connected);
1131
- return on_mqtt_chunk} } }
1132
-
1133
-
1134
- function _ping_interval(send_ping) {
1135
- let tid;
1136
- return (( td ) => {
1137
- tid = clearInterval(tid);
1138
- if (td) {
1139
- tid = setInterval(send_ping, 1000 * td);
1140
-
1141
-
1142
-
1143
-
1144
- // ensure the interval allows the NodeJS event loop to exit
1145
- tid.unref?.();
1146
- return true} }) }
1134
+ function _mqtt_dispatch(opt, target) {
1135
+ let hashbelt=[], rotate_ts=0;
1136
+ // default rotate at 1s across 5 buckets
1137
+ let { td: rotate_td=1000, n: rotate_n=5 } = opt?.rotate || {};
1147
1138
 
1148
- const _mqtt_cmdid_dispatch ={
1149
- create(target) {
1150
- return {__proto__: this, target, hashbelt: [new Map()]} }
1139
+ // Promise / future scaffolding
1140
+ let _pkt_id=100, _ftr_key; // use _ftr_key to reuse _by_key closure
1141
+ let _ftr_by_key = fn_answer => hashbelt[0].set(_ftr_key, fn_answer);
1151
1142
 
1152
- , bind_pkt_future(_pkt_id=100) {
1153
- let {hashbelt} = this;
1143
+ on_mqtt([]); // init hashbelt and rotate_ts
1144
+ return [on_mqtt, pkt_future]
1154
1145
 
1155
- let _tmp_; // use _tmp_ to reuse _by_key closure
1156
- let _by_key = answer_monad =>
1157
- hashbelt[0].set(_tmp_, answer_monad);
1158
1146
 
1159
- return (( pkt_or_key ) => {
1160
- if ('string' === typeof pkt_or_key) {
1161
- _tmp_ = pkt_or_key;}
1162
- else {
1163
- _pkt_id = (_pkt_id + 1) & 0xffff;
1164
- _tmp_ = pkt_or_key.pkt_id = _pkt_id;}
1147
+ function pkt_future(pkt_or_key) {
1148
+ if (! _isstr(pkt_or_key)) {
1149
+ _pkt_id = (_pkt_id + 1) & 0xffff; // 16-bit unsigned short
1150
+ _ftr_key = pkt_or_key.pkt_id = _pkt_id;}
1151
+ else _ftr_key = pkt_or_key;
1165
1152
 
1166
- return new Promise(_by_key)}) }
1153
+ return new Promise(_ftr_by_key)}
1167
1154
 
1168
- , answer(key, pkt) {
1169
- for (let map of this.hashbelt) {
1170
- let answer_monad = map.get(key);
1171
- if (undefined !== answer_monad) {
1155
+ function answer(key, pkt) {
1156
+ for (let map of hashbelt) {
1157
+ let fn_answer = map.get(key);
1158
+ if (fn_answer) {
1172
1159
  map.delete(key);
1173
1160
 
1174
- answer_monad([pkt, /*err*/]); // option/maybe monad
1161
+ fn_answer([pkt, /*err*/]); // option/maybe monad
1175
1162
  return true} }
1176
1163
  return false}
1177
1164
 
1178
- , rotate_belt(n) {
1179
- let {hashbelt} = this;
1180
- hashbelt.unshift(new Map());
1181
- for (let old of hashbelt.splice(n || 5)) {
1182
- for (let answer_monad of old.values()) {
1183
- answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
1184
-
1185
- , cmdids: ((() => {
1186
- return [
1187
- (() =>{} )// 0x0 reserved
1188
- , by_evt // 0x1 connect
1189
- , by_type // 0x2 connack
1190
- , by_evt // 0x3 publish
1191
- , by_id // 0x4 puback
1192
- , by_id // 0x5 pubrec
1193
- , by_id // 0x6 pubrel
1194
- , by_id // 0x7 pubcomp
1195
- , by_evt // 0x8 subscribe
1196
- , by_id // 0x9 suback
1197
- , by_evt // 0xa unsubscribe
1198
- , by_id // 0xb unsuback
1199
- , by_type // 0xc pingreq
1200
- , by_type // 0xd pingresp
1201
- , by_evt // 0xe disconnect
1202
- , by_type ]// 0xf auth
1203
-
1204
-
1205
- function by_id(disp, pkt) {
1206
- disp.answer(pkt.pkt_id, pkt); }
1207
-
1208
- function by_type(disp, pkt, ctx) {
1209
- disp.answer(pkt.type, pkt);
1210
- by_evt(disp, pkt, ctx);}
1211
-
1212
- async function by_evt({target}, pkt, ctx) {
1213
- let fn = target[`mqtt_${pkt.type}`]
1214
- || target.mqtt_pkt;
1215
-
1216
- await fn?.call(target, pkt, ctx);} })()) };
1217
-
1218
- /*
1219
- on_mqtt_type = {
1220
- mqtt_auth(pkt, ctx) ::
1221
- mqtt_connect(pkt, ctx) ::
1222
- mqtt_connack(pkt, ctx) ::
1223
- mqtt_disconnect(pkt, ctx) ::
1224
-
1225
- mqtt_publish(pkt, ctx)
1226
- mqtt_subscribe(pkt, ctx) ::
1227
- mqtt_unsubscribe(pkt, ctx) ::
1228
-
1229
- mqtt_pingreq(pkt, ctx) ::
1230
- mqtt_pingresp(pkt, ctx) ::
1231
- }
1232
- */
1233
-
1234
- function _mqtt_dispatch(opt, target) {
1235
- let _disp_ = _mqtt_cmdid_dispatch.create(target);
1236
- let { cmdids } = _disp_;
1237
-
1238
- // default rotate at 1s across 5 buckets
1239
- let { td: rotate_td=1000, n: rotate_n=5 } =
1240
- opt && opt.rotate || {};
1241
-
1242
- let rotate_ts = rotate_td + Date.now();
1243
-
1244
- return [on_mqtt,
1245
- _disp_.bind_pkt_future()]
1246
-
1247
1165
  function on_mqtt(pkt_list, ctx) {
1248
1166
  for (let pkt of pkt_list) {
1249
- cmdids[pkt.id](_disp_, pkt, ctx); }
1167
+ _mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
1250
1168
 
1251
- if (Date.now() > rotate_ts) {
1252
- _disp_.rotate_belt(rotate_n);
1253
- rotate_ts = rotate_td + Date.now();} } }
1169
+ // rotate after rotate_ts
1170
+ let now = Date.now();
1171
+ if (now > rotate_ts) {
1172
+ rotate_ts = rotate_td + now;
1173
+ hashbelt.unshift(new Map());
1174
+ while (hashbelt.length > rotate_n) {
1175
+ for (let fn_answer of hashbelt.pop().values()) {
1176
+ fn_answer([/*pkt*/, 'expired']); } } } } }// option/maybe monad
1254
1177
 
1255
1178
  class MQTTError extends Error {
1256
1179
  constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
1180
+ // use hex-encoded reasons to match MQTT spec documentation
1257
1181
  super(`[0x${reason.toString(16)}] ${reason.reason}`);
1258
1182
  this.mqtt_pkt = mqtt_pkt;
1259
1183
  this.reason = reason;} }
1260
1184
 
1261
1185
  class MQTTBase {
1262
- constructor(opt={}) {
1263
- this.with(opt);
1264
- this._conn_ = _mqtt_conn(this,
1265
- this._init_dispatch(opt, this)); }
1266
-
1267
- with(fns_ns) {
1268
- for (let [k,v] of Object.entries(fns_ns)) {
1269
- if ('function' === typeof v) {this[k] = v;} }
1270
- return this}
1271
-
1272
- async conn_emit(evt, arg, err_arg) {
1273
- this.log_conn?.(evt, arg, err_arg);
1274
- try {
1275
- let fn_evt = this[await evt]; // microtask break using `await evt`
1276
- if (fn_evt) {
1277
- await fn_evt.call(this, this, arg, err_arg);}
1278
- else if (err_arg) {throw err_arg} }
1279
- catch (err) {
1280
- this.on_error(err, evt);} }
1281
-
1282
- on_error(err, evt) {
1283
- console.warn('[[u8-mqtt error: %s]]', evt, err); }
1284
-
1285
1186
  // Handshaking Packets
1286
-
1287
1187
  async connect(pkt={}) {
1288
- let cid = pkt.client_id || this.client_id;
1289
- if ('string' !== typeof cid) {
1188
+ let cid = pkt.client_id;
1189
+ if (! _isstr(cid)) {
1290
1190
  // see init_client_id implementation in core.jsy
1291
- pkt.client_id = cid = this.init_client_id(cid);}
1191
+ pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
1292
1192
  this.client_id = cid;
1293
1193
 
1294
1194
  if (null == pkt.keep_alive) {
1295
1195
  pkt.keep_alive = 60;}
1296
1196
 
1297
- let res = await this._conn_
1298
- .send_connect('connect', pkt, 'connack');
1299
-
1300
- if (0 != res[0].reason) {
1301
- throw new this.MQTTError(res[0])}
1302
-
1303
- // TODO: merge with server's keep_alive frequency
1304
- this._conn_.ping(pkt.keep_alive);
1305
- return res}
1197
+ let response = await this._send0('connect', pkt, 'connack');
1198
+ if (0 != response[0].reason) {// compare to 0 to coerce to number
1199
+ throw new this.MQTTError(response[0])}
1200
+ return this.conn.on_conn(pkt, response)}
1306
1201
 
1307
1202
  async disconnect(pkt={}) {
1308
- let res = await this._send('disconnect', pkt);
1309
- this._conn_.reset(false);
1310
- return res}
1203
+ let response = await this._send0('disconnect', pkt);
1204
+ return this.conn.on_dis(pkt, response)}
1311
1205
 
1312
- auth(pkt={}) {
1313
- return this._send('auth', pkt, 'auth')}
1206
+ async auth(pkt={}) {
1207
+ let response = await this._send0('auth', pkt, 'auth');
1208
+ if (response[0].reason) {
1209
+ throw new this.MQTTError(response[0])}
1210
+ return this.conn.on_auth(pkt, response)}
1314
1211
 
1315
1212
  ping() {return this._send('pingreq', null, 'pingresp')}
1316
1213
  puback({pkt_id}) {return this._send('puback', {pkt_id})}
@@ -1347,20 +1244,18 @@ class MQTTBase {
1347
1244
  // alias: publish -- because 'pub' is shorter for semantic aliases above
1348
1245
  async pub(pkt, pub_opt) {
1349
1246
  if (undefined === pkt.payload) {
1350
- if ('function' === typeof pub_opt) {
1247
+ if (_isfn(pub_opt)) {
1248
+ // pub_opt as a function is fn_encode value
1351
1249
  pub_opt = {fn_encode: pub_opt};}
1352
1250
 
1353
- let {msg} = pkt;
1354
- switch (typeof msg) {
1355
- case 'function':
1356
- pub_opt = {...pub_opt, fn_encode: msg};
1357
- // flow into 'undefined' case
1358
- case 'undefined':
1359
- // return a single-value closure to publish packets
1360
- return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1251
+ let msg = pkt.msg, fn_encode = pub_opt?.fn_encode;
1252
+ if (null == msg || _isfn(msg)) {
1253
+ // when msg is a function, return closure using fn_encode
1254
+ if (msg) {pub_opt = {...pub_opt, fn_encode: msg};}
1255
+ // return a single-value closure to publish packets
1256
+ return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1361
1257
 
1362
1258
  // Encode payload from msg; fn_encode allows alternative to JSON.stringify
1363
- let {fn_encode} = pub_opt || {};
1364
1259
  pkt.payload = fn_encode
1365
1260
  ? await fn_encode(msg)
1366
1261
  : JSON.stringify(msg);}
@@ -1372,31 +1267,31 @@ class MQTTBase {
1372
1267
  pkt = pub_opt.xform(pkt) || pkt;} }
1373
1268
 
1374
1269
  return this._send('publish', pkt,
1375
- pkt.qos ? pkt : void 0 ) }// key
1270
+ pkt.qos ? pkt : null ) }// key
1376
1271
 
1377
1272
 
1378
1273
  // Internal API
1379
1274
 
1380
- /* async _send(type, pkt) -- provided by _conn_ and transport */
1275
+ /* async _send0(type, pkt) -- provided by conn and transport */
1276
+ /* async _send(type, pkt) -- provided by conn and transport */
1381
1277
 
1382
1278
  _init_dispatch(opt) {
1383
1279
  this.constructor?._once_();
1384
1280
  let target ={__proto__: opt.on_mqtt_type};
1385
1281
  target.mqtt_publish ||=
1386
1282
  this._init_router?.(opt, this, target);
1387
- return _mqtt_dispatch(this, target)}
1283
+ return _mqtt_dispatch(opt, target)}
1388
1284
 
1389
1285
  static _aliases() {
1390
1286
  return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
1391
1287
 
1392
- static _once_(self=this) {
1393
- self._once_ = _=>0;
1394
- let p = self.prototype;
1288
+ static _once_(klass=this) {
1289
+ klass._once_ = _=>0;
1290
+ var alias, name, p = klass.prototype;
1395
1291
  p.MQTTError = MQTTError;
1396
- for (let alias of self._aliases().split(/\s+/)) {
1397
- alias = alias.split(':');
1398
- let fn = alias[1] && p[alias[1]];
1399
- if (fn) {p[alias[0]] = fn;} } } }
1292
+ for (alias of klass._aliases().split(/\s+/)) {
1293
+ [alias, name] = alias.split(':');
1294
+ p[alias] = p[name];} } }
1400
1295
 
1401
1296
 
1402
1297
  function _as_topics(pkt, ex, topic_prefix) {
@@ -1421,43 +1316,177 @@ function _as_topics(pkt, ex, topic_prefix) {
1421
1316
  pkt.topics = pkt.topics.map(_prefix_topics);}
1422
1317
  return pkt}
1423
1318
 
1424
- const pkt_api = {
1425
- utf8(u8) { return new TextDecoder('utf-8').decode(u8 || this.payload ) },
1426
- json(u8) { return JSON.parse( this.utf8(u8) || null ) },
1427
- text(u8) { return this.utf8(u8) },
1428
- };
1319
+ const _defer_obj = o =>(
1320
+ o.p = new Promise((a,e) => { o.a=a; o.e=e; })
1321
+ , o);
1322
+
1323
+ function _dfn_reset(client, attr, fn_after) {
1324
+ // a resetable deferred for a function
1325
+ let self = {set}, afn = async (...args) => (await self.p)(...args);
1326
+ return set()
1327
+
1328
+ function set() {
1329
+ if (afn !== client[attr]) {
1330
+ _defer_obj(self).p.then(fn_after, _=>0);
1331
+ client[attr] = afn;}
1332
+ return self} }
1333
+
1334
+ function _mqtt_conn(opt, client, [on_mqtt, pkt_future]) {
1335
+ let _abort;
1336
+ let _dfn_send0 = _dfn_reset(client, '_send0', // client._send0 getter/setter
1337
+ _=> client.conn_emit('on_live', conn.has_connected));
1338
+ let _dfn_ready = _dfn_reset(client, '_send', // client._send getter/setter
1339
+ _=> client.conn_emit('on_ready'));
1340
+ let _keep_alive_ival = _interval (() =>client._send0('pingreq') );// resettable interval for keep_alive ping
1341
+
1342
+ let conn = Object.create({
1343
+ ping: (td=conn.keep_alive) => _keep_alive_ival(td)
1344
+
1345
+ , on_conn(pkt, response) {
1346
+ conn.has_connected = true;
1347
+ conn.keep_alive = opt.keep_alive || response[0].props?.server_keep_alive || pkt.keep_alive;
1348
+ client.conn_emit('on_conn');
1349
+ return opt.use_auth
1350
+ ? response // wait on enhanced authentication step
1351
+ : conn.on_auth(null, response) }// otherwise, connect is also auth
1352
+
1353
+ , on_auth(pkt, response) {
1354
+ _dfn_ready.a(_dfn_send0.p);
1355
+ if (0 != opt.keep_alive) {
1356
+ conn.ping();}
1357
+ client.conn_emit('on_auth', !pkt);
1358
+ return response}
1359
+
1360
+ , on_dis(pkt, response) {
1361
+ conn.reset(false);
1362
+ return response}
1363
+
1364
+ , reset(err) {
1365
+ if (err) {
1366
+ _dfn_send0.e(err); }// send error to uses of _send0 (connect, auth)
1367
+ _abort.e(err); // abort in-progress connections
1368
+
1369
+ delete conn.is_set;
1370
+ conn.ready = handshake();
1371
+ client.conn_emit('on_disconnect', false===err, err);}
1372
+
1373
+ , abort() {
1374
+ _dfn_ready.e(err); // abort all messages awaiting ready state
1375
+ return conn.reset(err)}
1376
+
1377
+ , async setup(gate, send_u8_pkt, init_msg_loop) {
1378
+ if (conn.is_set) {
1379
+ throw new Error() }// already in-progress
1380
+
1381
+ conn.is_set = true;
1382
+ await gate;
1383
+
1384
+ // setup send/recv MQTT parsing context
1385
+ let mqtt_ctx = client.mqtt_ctx.mqtt_stream();
1386
+
1387
+ {// setup inbound message loop
1388
+ let sess_ctx = {mqtt: client}; // mutable session context
1389
+ let on_mqtt_chunk = u8 => on_mqtt(mqtt_ctx.decode(u8), sess_ctx);
1390
+ init_msg_loop(on_mqtt_chunk, conn);}
1391
+
1392
+ // setup outbound message path and transport connection
1393
+ send_u8_pkt = await send_u8_pkt;
1394
+ _dfn_send0.a(
1395
+ async (type, pkt, key) => {
1396
+ let res = undefined !== key
1397
+ ? pkt_future(key) : true;
1398
+
1399
+ await send_u8_pkt(
1400
+ mqtt_ctx.encode_pkt(type, pkt));
1401
+ return res} ); } });
1402
+
1403
+ conn.ready = handshake();
1404
+ return conn
1405
+
1406
+ async function handshake() {
1407
+ _abort = _defer_obj({});
1408
+
1409
+ _keep_alive_ival(0); // clearInterval on keep alive ping
1410
+ _dfn_send0.set(); // reset client._send0 if necessary
1411
+ _dfn_ready.set(); // reset client._send if necessary
1412
+
1413
+ try {
1414
+ // set client._send0 as passtrhough after transport connection
1415
+ client._send0 = await Promise.race([_dfn_send0.p, _abort.p]);
1416
+
1417
+ // set client._send as passtrhough after ready
1418
+ client._send = await Promise.race([_dfn_ready.p, _abort.p]);
1419
+ return true}
1420
+ catch (err) {
1421
+ return false} } }
1422
+
1423
+ const pkt_api ={
1424
+ utf8(u8) {return new TextDecoder('utf-8').decode(u8 || this.payload )}
1425
+ , json(u8) {return JSON.parse( this.utf8(u8) || null )}
1426
+ , text(u8) {return this.utf8(u8)} };
1427
+
1428
+ const opt_default ={
1429
+ sess_stg: globalThis.sessionStorage};
1429
1430
 
1430
1431
  class MQTTCore extends MQTTBase {
1432
+ constructor(opt) {
1433
+ super();
1434
+ this.with(opt);
1435
+ opt ={...opt_default, ...opt};
1436
+ // settings for MQTTCore
1437
+ this.sess_stg = opt.sess_stg;
1438
+ // setup connection and dispatch
1439
+ this.conn = _mqtt_conn(opt, this,
1440
+ this._init_dispatch(opt)); }
1441
+
1442
+ with(fns_ns) {
1443
+ for (let [k,v] of Object.entries(fns_ns)) {
1444
+ if (_isfn(v)) {this[k] = v;} }
1445
+ return this}
1446
+
1447
+
1431
1448
  static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
1432
- let self = class extends this {};
1433
- self.prototype.mqtt_ctx =
1449
+ let klass = class extends this {};
1450
+ klass.prototype.mqtt_ctx =
1434
1451
  mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
1435
- return self}
1452
+ return klass}
1453
+
1454
+
1455
+ async conn_emit(evt, arg, err_arg) {
1456
+ this.log_conn?.(evt, arg, err_arg);
1457
+ try {
1458
+ let fn_evt = this[await evt]; // microtask break using `await evt`
1459
+ if (fn_evt) {
1460
+ await fn_evt.call(this, this, arg, err_arg);}
1461
+ else if (err_arg) {throw err_arg} }
1462
+ catch (err) {
1463
+ this.on_error(err, evt);} }
1464
+
1465
+ on_error(err, evt) {
1466
+ console.warn('[[u8-mqtt error: %s]]', evt, err); }
1467
+ log_conn(evt, arg, err_arg) {
1468
+ console.info('[[u8-mqtt conn: %s]]', evt, arg, err_arg); }
1436
1469
 
1437
1470
 
1438
1471
  // automatic Client Id for connect()
1439
1472
  init_client_id(parts=['u8-mqtt--','']) {
1440
- let sess_stg=this.sess_stg;
1473
+ let sess_stg = this.sess_stg;
1441
1474
  let key, cid = sess_stg?.getItem(key=parts.join(' '));
1442
1475
  if (! cid) {
1443
1476
  cid = parts.join(Math.random().toString(36).slice(2));
1444
1477
  sess_stg?.setItem(key, cid);}
1445
1478
  return cid}
1446
1479
 
1447
- get sess_stg() {return globalThis.sessionStorage}
1448
-
1449
-
1450
- //on_error(err, evt) ::
1451
- // console.warn @ '[[u8-mqtt error: %s]]', evt, err
1452
-
1453
- //log_conn(evt, arg, err_arg) ::
1454
- // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1455
1480
 
1456
1481
  on_live(client, is_reconnect) {
1457
1482
  if (is_reconnect) {
1458
1483
  return client.connect()} }
1459
1484
 
1460
- //on_reconnect(client) ::
1485
+ // on_ready(client) ::
1486
+ // on_reconnect(client) ::
1487
+ on_disconnect(client, intentional) {
1488
+ if (! intentional) {
1489
+ return client.on_reconnect?.()} }
1461
1490
 
1462
1491
  _use_conn(fn_reconnect) {
1463
1492
  return (this.reconnect = fn_reconnect)?.()}
@@ -1469,27 +1498,20 @@ class MQTTCore extends MQTTBase {
1469
1498
  .then(this.reconnect)
1470
1499
  .then(opt.reconnect, opt.error);} }) }
1471
1500
 
1472
- on_disconnect(client, intentional) {
1473
- if (! intentional) {
1474
- return client.on_reconnect?.()} }
1475
-
1476
1501
  delay(ms) {
1477
1502
  return new Promise(done => setTimeout(done, ms)) }
1478
1503
 
1479
1504
  with_async_iter(async_iter, write_u8_pkt) {
1480
- let on_mqtt_chunk = this._conn_.set(
1481
- this.mqtt_ctx,
1482
- write_u8_pkt);
1483
-
1484
- this._msg_loop = ((async () => {
1485
- try {
1486
- async_iter = await async_iter;
1487
- for await (let chunk of async_iter)
1488
- on_mqtt_chunk(chunk);
1489
- this._conn_.reset();}
1490
- catch (err) {
1491
- this._conn_.reset(err);} })());
1492
-
1505
+ this.conn.setup(async_iter,
1506
+ write_u8_pkt,
1507
+ async (on_mqtt_chunk, conn) => {
1508
+ try {
1509
+ async_iter = await async_iter;
1510
+ for await (let chunk of async_iter)
1511
+ on_mqtt_chunk(chunk);
1512
+ conn.reset();}
1513
+ catch (err) {
1514
+ conn.reset(err);} } );
1493
1515
  return this}
1494
1516
 
1495
1517
 
@@ -1560,33 +1582,30 @@ class MQTTCore extends MQTTBase {
1560
1582
 
1561
1583
  websock.binaryType = 'arraybuffer';
1562
1584
 
1563
- let ready, {readyState} = websock;
1585
+ let ws_ready, readyState = websock.readyState;
1564
1586
  if (1 !== readyState) {
1565
1587
  if (0 !== readyState) {
1566
- throw new Error('Invalid WebSocket readyState') }
1567
-
1568
- ready = new Promise(fn => websock.onopen = fn); }
1588
+ throw new Error('WS readyState') }
1569
1589
 
1590
+ ws_ready = new Promise(ready => websock.onopen = ready); }
1570
1591
 
1571
- let {_conn_} = this;
1572
- let on_mqtt_chunk = _conn_.set(
1573
- this.mqtt_ctx,
1574
- async u8_pkt =>(
1575
- await ready
1576
- , websock.send(u8_pkt)) );
1592
+ this.conn.setup(ws_ready,
1593
+ u8_pkt => websock.send(u8_pkt),
1594
+ (on_mqtt_chunk, conn) => {
1595
+ websock.onmessage = evt =>(
1596
+ on_mqtt_chunk(new Uint8Array(evt.data)) );
1577
1597
 
1578
- websock.onmessage = evt =>(on_mqtt_chunk(new Uint8Array(evt.data)));
1579
- websock.onclose = evt => {
1580
- if (! evt.wasClean) {
1581
- var err = new Error('websocket connection close');
1582
- err.code = evt.code;
1583
- err.reason = evt.reason;}
1598
+ websock.onclose = evt => {
1599
+ if (! evt.wasClean) {
1600
+ var err = new Error('websocket close');
1601
+ err.code = evt.code;
1602
+ err.reason = evt.reason;}
1584
1603
 
1585
- _conn_.reset(err);};
1604
+ conn.reset(err);}; } );
1586
1605
 
1587
1606
  return this} }
1588
1607
 
1589
- const version = '0.5.2-node';
1608
+ const version = '0.6.0-node';
1590
1609
 
1591
1610
  const MQTTClient_v4 = /* #__PURE__ */
1592
1611
  with_topic_path_router(
@@ -1607,10 +1626,9 @@ exports.MQTTClient_v4 = MQTTClient_v4;
1607
1626
  exports.MQTTClient_v5 = MQTTClient_v5;
1608
1627
  exports.MQTTCore = MQTTCore;
1609
1628
  exports.MQTTError = MQTTError;
1610
- exports._mqtt_cmdid_dispatch = _mqtt_cmdid_dispatch;
1629
+ exports._mqtt_cmdids = _mqtt_cmdids;
1611
1630
  exports._mqtt_conn = _mqtt_conn;
1612
1631
  exports._mqtt_dispatch = _mqtt_dispatch;
1613
- exports.ao_defer_v = ao_defer_v;
1614
1632
  exports.as_topic_path = as_topic_path;
1615
1633
  exports.default = mqtt_v4;
1616
1634
  exports.mqtt_v4 = mqtt_v4;