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/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
 
@@ -1058,238 +1085,108 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
1058
1085
  }
1059
1086
  }
1060
1087
 
1061
- function ao_defer_ctx(as_res = (...args) => args) {
1062
- let y,n,_pset = (a,b) => { y=a, n=b; };
1063
- return p =>(
1064
- p = new Promise(_pset)
1065
- , as_res(p, y, n)) }
1066
-
1067
- const ao_defer_v = /* #__PURE__ */
1068
- ao_defer_ctx();
1069
-
1070
- Promise.resolve({type:'init'});
1071
-
1072
- function _mqtt_conn(client, [on_mqtt, pkt_future]) {
1073
- let _q_init = ao_defer_v(), _q_ready = ao_defer_v();
1074
- let _send_ready = async (...args) => (await _q_ready[0])(...args);
1075
- let _send_mqtt_pkt, _has_connected;
1076
- client._send = _send_ready;
1077
-
1078
- return {
1079
- async when_ready() {await _q_ready[0];}
1080
-
1081
- , ping: _ping_interval (() =>_send_mqtt_pkt?.('pingreq'))
1082
-
1083
- , reset(err) {
1084
- if (! _send_mqtt_pkt) {return}
1085
-
1086
- if (err) {
1087
- _q_init[2](err);}
1088
-
1089
- _send_mqtt_pkt = null;
1090
- _q_init = ao_defer_v();
1091
- client._send = _send_ready;
1092
-
1093
- // call client.on_conn_reset in next promise microtask
1094
- client.conn_emit('on_disconnect', false===err, err);}
1095
-
1096
- , async send_connect(... args) {
1097
- if (! _send_mqtt_pkt) {
1098
- await _q_init[0]; }// _send_mqtt_pkt is set before fulfilled
1099
-
1100
- // await connack response
1101
- let res = await _send_mqtt_pkt(...args);
1102
- if (0 == res[0].reason) {
1103
- _has_connected = true;
1104
- // resolve _q_ready[0] with _send_mqtt_pkt closure
1105
- _q_ready[1](client._send = _send_mqtt_pkt);
1106
- _q_ready = ao_defer_v();
1107
- client.conn_emit('on_ready');}
1108
-
1109
- return res}
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
1110
1133
 
1111
- , is_set: (() =>!! _send_mqtt_pkt)
1112
- , set(mqtt_ctx, send_u8_pkt) {
1113
- if (_send_mqtt_pkt) {
1114
- throw new Error('Already connected')}
1115
-
1116
- mqtt_ctx = mqtt_ctx.mqtt_stream();
1117
- let sess_ctx = {mqtt: client};
1118
- let on_mqtt_chunk = u8_buf =>
1119
- on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
1120
-
1121
- _send_mqtt_pkt = async (type, pkt, key) => {
1122
- let res = undefined !== key
1123
- ? pkt_future(key) : true;
1124
-
1125
- await send_u8_pkt(
1126
- mqtt_ctx.encode_pkt(type, pkt));
1127
-
1128
- return res};
1129
-
1130
- _q_init[1](_send_mqtt_pkt); // resolve _q_init with _send_mqtt_pkt closure
1131
-
1132
- // call client.on_live in next promise microtask
1133
- client.conn_emit('on_live', _has_connected);
1134
- return on_mqtt_chunk} } }
1135
-
1136
-
1137
- function _ping_interval(send_ping) {
1138
- let tid;
1139
- return (( td ) => {
1140
- tid = clearInterval(tid);
1141
- if (td) {
1142
- tid = setInterval(send_ping, 1000 * td);
1143
-
1144
-
1145
-
1146
-
1147
- // ensure the interval allows the NodeJS event loop to exit
1148
- tid.unref?.();
1149
- 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 || {};
1150
1138
 
1151
- const _mqtt_cmdid_dispatch ={
1152
- create(target) {
1153
- 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);
1154
1142
 
1155
- , bind_pkt_future(_pkt_id=100) {
1156
- let {hashbelt} = this;
1143
+ on_mqtt([]); // init hashbelt and rotate_ts
1144
+ return [on_mqtt, pkt_future]
1157
1145
 
1158
- let _tmp_; // use _tmp_ to reuse _by_key closure
1159
- let _by_key = answer_monad =>
1160
- hashbelt[0].set(_tmp_, answer_monad);
1161
1146
 
1162
- return (( pkt_or_key ) => {
1163
- if ('string' === typeof pkt_or_key) {
1164
- _tmp_ = pkt_or_key;}
1165
- else {
1166
- _pkt_id = (_pkt_id + 1) & 0xffff;
1167
- _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;
1168
1152
 
1169
- return new Promise(_by_key)}) }
1153
+ return new Promise(_ftr_by_key)}
1170
1154
 
1171
- , answer(key, pkt) {
1172
- for (let map of this.hashbelt) {
1173
- let answer_monad = map.get(key);
1174
- 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) {
1175
1159
  map.delete(key);
1176
1160
 
1177
- answer_monad([pkt, /*err*/]); // option/maybe monad
1161
+ fn_answer([pkt, /*err*/]); // option/maybe monad
1178
1162
  return true} }
1179
1163
  return false}
1180
1164
 
1181
- , rotate_belt(n) {
1182
- let {hashbelt} = this;
1183
- hashbelt.unshift(new Map());
1184
- for (let old of hashbelt.splice(n || 5)) {
1185
- for (let answer_monad of old.values()) {
1186
- answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
1187
-
1188
- , cmdids: ((() => {
1189
- return [
1190
- (() =>{} )// 0x0 reserved
1191
- , by_evt // 0x1 connect
1192
- , by_type // 0x2 connack
1193
- , by_evt // 0x3 publish
1194
- , by_id // 0x4 puback
1195
- , by_id // 0x5 pubrec
1196
- , by_id // 0x6 pubrel
1197
- , by_id // 0x7 pubcomp
1198
- , by_evt // 0x8 subscribe
1199
- , by_id // 0x9 suback
1200
- , by_evt // 0xa unsubscribe
1201
- , by_id // 0xb unsuback
1202
- , by_type // 0xc pingreq
1203
- , by_type // 0xd pingresp
1204
- , by_evt // 0xe disconnect
1205
- , by_type ]// 0xf auth
1206
-
1207
-
1208
- function by_id(disp, pkt) {
1209
- disp.answer(pkt.pkt_id, pkt); }
1210
-
1211
- function by_type(disp, pkt, ctx) {
1212
- disp.answer(pkt.type, pkt);
1213
- by_evt(disp, pkt, ctx);}
1214
-
1215
- async function by_evt({target}, pkt, ctx) {
1216
- let fn = target[`mqtt_${pkt.type}`]
1217
- || target.mqtt_pkt;
1218
-
1219
- await fn?.call(target, pkt, ctx);} })()) };
1220
-
1221
- /*
1222
- on_mqtt_type = {
1223
- mqtt_auth(pkt, ctx) ::
1224
- mqtt_connect(pkt, ctx) ::
1225
- mqtt_connack(pkt, ctx) ::
1226
- mqtt_disconnect(pkt, ctx) ::
1227
-
1228
- mqtt_publish(pkt, ctx)
1229
- mqtt_subscribe(pkt, ctx) ::
1230
- mqtt_unsubscribe(pkt, ctx) ::
1231
-
1232
- mqtt_pingreq(pkt, ctx) ::
1233
- mqtt_pingresp(pkt, ctx) ::
1234
- }
1235
- */
1236
-
1237
- function _mqtt_dispatch(opt, target) {
1238
- let _disp_ = _mqtt_cmdid_dispatch.create(target);
1239
- let { cmdids } = _disp_;
1240
-
1241
- // default rotate at 1s across 5 buckets
1242
- let { td: rotate_td=1000, n: rotate_n=5 } =
1243
- opt && opt.rotate || {};
1244
-
1245
- let rotate_ts = rotate_td + Date.now();
1246
-
1247
- return [on_mqtt,
1248
- _disp_.bind_pkt_future()]
1249
-
1250
1165
  function on_mqtt(pkt_list, ctx) {
1251
1166
  for (let pkt of pkt_list) {
1252
- cmdids[pkt.id](_disp_, pkt, ctx); }
1167
+ _mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
1253
1168
 
1254
- if (Date.now() > rotate_ts) {
1255
- _disp_.rotate_belt(rotate_n);
1256
- 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
1257
1177
 
1258
1178
  class MQTTError extends Error {
1259
1179
  constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
1180
+ // use hex-encoded reasons to match MQTT spec documentation
1260
1181
  super(`[0x${reason.toString(16)}] ${reason.reason}`);
1261
1182
  this.mqtt_pkt = mqtt_pkt;
1262
1183
  this.reason = reason;} }
1263
1184
 
1264
1185
  class MQTTBase {
1265
- constructor(opt={}) {
1266
- this.with(opt);
1267
- this._conn_ = _mqtt_conn(this,
1268
- this._init_dispatch(opt, this)); }
1269
-
1270
- with(fns_ns) {
1271
- for (let [k,v] of Object.entries(fns_ns)) {
1272
- if ('function' === typeof v) {this[k] = v;} }
1273
- return this}
1274
-
1275
- async conn_emit(evt, arg, err_arg) {
1276
- this.log_conn?.(evt, arg, err_arg);
1277
- try {
1278
- let fn_evt = this[await evt]; // microtask break using `await evt`
1279
- if (fn_evt) {
1280
- await fn_evt.call(this, this, arg, err_arg);}
1281
- else if (err_arg) {throw err_arg} }
1282
- catch (err) {
1283
- this.on_error(err, evt);} }
1284
-
1285
- on_error(err, evt) {
1286
- console.warn('[[u8-mqtt error: %s]]', evt, err); }
1287
-
1288
1186
  // Handshaking Packets
1289
-
1290
1187
  async connect(pkt={}) {
1291
1188
  let cid = pkt.client_id;
1292
- if ('string' !== typeof cid) {
1189
+ if (! _isstr(cid)) {
1293
1190
  // see init_client_id implementation in core.jsy
1294
1191
  pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
1295
1192
  this.client_id = cid;
@@ -1297,23 +1194,20 @@ class MQTTBase {
1297
1194
  if (null == pkt.keep_alive) {
1298
1195
  pkt.keep_alive = 60;}
1299
1196
 
1300
- let res = await this._conn_
1301
- .send_connect('connect', pkt, 'connack');
1302
-
1303
- if (0 != res[0].reason) {
1304
- throw new this.MQTTError(res[0])}
1305
-
1306
- // TODO: merge with server's keep_alive frequency
1307
- this._conn_.ping(pkt.keep_alive);
1308
- 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)}
1309
1201
 
1310
1202
  async disconnect(pkt={}) {
1311
- let res = await this._send('disconnect', pkt);
1312
- this._conn_.reset(false);
1313
- return res}
1203
+ let response = await this._send0('disconnect', pkt);
1204
+ return this.conn.on_dis(pkt, response)}
1314
1205
 
1315
- auth(pkt={}) {
1316
- 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)}
1317
1211
 
1318
1212
  ping() {return this._send('pingreq', null, 'pingresp')}
1319
1213
  puback({pkt_id}) {return this._send('puback', {pkt_id})}
@@ -1350,20 +1244,18 @@ class MQTTBase {
1350
1244
  // alias: publish -- because 'pub' is shorter for semantic aliases above
1351
1245
  async pub(pkt, pub_opt) {
1352
1246
  if (undefined === pkt.payload) {
1353
- if ('function' === typeof pub_opt) {
1247
+ if (_isfn(pub_opt)) {
1248
+ // pub_opt as a function is fn_encode value
1354
1249
  pub_opt = {fn_encode: pub_opt};}
1355
1250
 
1356
- let {msg} = pkt;
1357
- switch (typeof msg) {
1358
- case 'function':
1359
- pub_opt = {...pub_opt, fn_encode: msg};
1360
- // flow into 'undefined' case
1361
- case 'undefined':
1362
- // return a single-value closure to publish packets
1363
- 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)}
1364
1257
 
1365
1258
  // Encode payload from msg; fn_encode allows alternative to JSON.stringify
1366
- let {fn_encode} = pub_opt || {};
1367
1259
  pkt.payload = fn_encode
1368
1260
  ? await fn_encode(msg)
1369
1261
  : JSON.stringify(msg);}
@@ -1375,31 +1267,31 @@ class MQTTBase {
1375
1267
  pkt = pub_opt.xform(pkt) || pkt;} }
1376
1268
 
1377
1269
  return this._send('publish', pkt,
1378
- pkt.qos ? pkt : void 0 ) }// key
1270
+ pkt.qos ? pkt : null ) }// key
1379
1271
 
1380
1272
 
1381
1273
  // Internal API
1382
1274
 
1383
- /* 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 */
1384
1277
 
1385
1278
  _init_dispatch(opt) {
1386
1279
  this.constructor?._once_();
1387
1280
  let target ={__proto__: opt.on_mqtt_type};
1388
1281
  target.mqtt_publish ||=
1389
1282
  this._init_router?.(opt, this, target);
1390
- return _mqtt_dispatch(this, target)}
1283
+ return _mqtt_dispatch(opt, target)}
1391
1284
 
1392
1285
  static _aliases() {
1393
1286
  return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
1394
1287
 
1395
- static _once_(self=this) {
1396
- self._once_ = _=>0;
1397
- let p = self.prototype;
1288
+ static _once_(klass=this) {
1289
+ klass._once_ = _=>0;
1290
+ var alias, name, p = klass.prototype;
1398
1291
  p.MQTTError = MQTTError;
1399
- for (let alias of self._aliases().split(/\s+/)) {
1400
- alias = alias.split(':');
1401
- let fn = alias[1] && p[alias[1]];
1402
- if (fn) {p[alias[0]] = fn;} } } }
1292
+ for (alias of klass._aliases().split(/\s+/)) {
1293
+ [alias, name] = alias.split(':');
1294
+ p[alias] = p[name];} } }
1403
1295
 
1404
1296
 
1405
1297
  function _as_topics(pkt, ex, topic_prefix) {
@@ -1424,37 +1316,167 @@ function _as_topics(pkt, ex, topic_prefix) {
1424
1316
  pkt.topics = pkt.topics.map(_prefix_topics);}
1425
1317
  return pkt}
1426
1318
 
1427
- const pkt_api = {
1428
- utf8(u8) { return new TextDecoder('utf-8').decode(u8 || this.payload ) },
1429
- json(u8) { return JSON.parse( this.utf8(u8) || null ) },
1430
- text(u8) { return this.utf8(u8) },
1431
- };
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};
1432
1430
 
1433
1431
  class MQTTCore extends MQTTBase {
1432
+ constructor(opt) {
1433
+ opt = {...opt_default, ...opt};
1434
+ super();
1435
+ this.with(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
+
1434
1448
  static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
1435
- let self = class extends this {};
1436
- self.prototype.mqtt_ctx =
1449
+ let klass = class extends this {};
1450
+ klass.prototype.mqtt_ctx =
1437
1451
  mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
1438
- 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); }
1439
1469
 
1440
1470
 
1441
1471
  // automatic Client Id for connect()
1442
1472
  init_client_id(parts=['u8-mqtt--','']) {
1443
- let sess_stg=this.sess_stg;
1473
+ let sess_stg = this.sess_stg;
1444
1474
  let key, cid = sess_stg?.getItem(key=parts.join(' '));
1445
1475
  if (! cid) {
1446
1476
  cid = parts.join(Math.random().toString(36).slice(2));
1447
1477
  sess_stg?.setItem(key, cid);}
1448
1478
  return cid}
1449
1479
 
1450
- get sess_stg() {return globalThis.sessionStorage}
1451
-
1452
-
1453
- //on_error(err, evt) ::
1454
- // console.warn @ '[[u8-mqtt error: %s]]', evt, err
1455
-
1456
- //log_conn(evt, arg, err_arg) ::
1457
- // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1458
1480
 
1459
1481
  on_live(client, is_reconnect) {
1460
1482
  if (is_reconnect) {
@@ -1480,19 +1502,16 @@ class MQTTCore extends MQTTBase {
1480
1502
  return new Promise(done => setTimeout(done, ms)) }
1481
1503
 
1482
1504
  with_async_iter(async_iter, write_u8_pkt) {
1483
- let on_mqtt_chunk = this._conn_.set(
1484
- this.mqtt_ctx,
1485
- write_u8_pkt);
1486
-
1487
- this._msg_loop = ((async () => {
1488
- try {
1489
- async_iter = await async_iter;
1490
- for await (let chunk of async_iter)
1491
- on_mqtt_chunk(chunk);
1492
- this._conn_.reset();}
1493
- catch (err) {
1494
- this._conn_.reset(err);} })());
1495
-
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);} } );
1496
1515
  return this}
1497
1516
 
1498
1517
 
@@ -1563,33 +1582,30 @@ class MQTTCore extends MQTTBase {
1563
1582
 
1564
1583
  websock.binaryType = 'arraybuffer';
1565
1584
 
1566
- let ready, {readyState} = websock;
1585
+ let ws_ready, readyState = websock.readyState;
1567
1586
  if (1 !== readyState) {
1568
1587
  if (0 !== readyState) {
1569
- throw new Error('Invalid WebSocket readyState') }
1570
-
1571
- ready = new Promise(fn => websock.onopen = fn); }
1588
+ throw new Error('WS readyState') }
1572
1589
 
1590
+ ws_ready = new Promise(ready => websock.onopen = ready); }
1573
1591
 
1574
- let {_conn_} = this;
1575
- let on_mqtt_chunk = _conn_.set(
1576
- this.mqtt_ctx,
1577
- async u8_pkt =>(
1578
- await ready
1579
- , 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)) );
1580
1597
 
1581
- websock.onmessage = evt =>(on_mqtt_chunk(new Uint8Array(evt.data)));
1582
- websock.onclose = evt => {
1583
- if (! evt.wasClean) {
1584
- var err = new Error('websocket connection close');
1585
- err.code = evt.code;
1586
- 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;}
1587
1603
 
1588
- _conn_.reset(err);};
1604
+ conn.reset(err);}; } );
1589
1605
 
1590
1606
  return this} }
1591
1607
 
1592
- const version = '0.5.3-node';
1608
+ const version = '0.6.1-node';
1593
1609
 
1594
1610
  const MQTTClient_v4 = /* #__PURE__ */
1595
1611
  with_topic_path_router(
@@ -1610,10 +1626,9 @@ exports.MQTTClient_v4 = MQTTClient_v4;
1610
1626
  exports.MQTTClient_v5 = MQTTClient_v5;
1611
1627
  exports.MQTTCore = MQTTCore;
1612
1628
  exports.MQTTError = MQTTError;
1613
- exports._mqtt_cmdid_dispatch = _mqtt_cmdid_dispatch;
1629
+ exports._mqtt_cmdids = _mqtt_cmdids;
1614
1630
  exports._mqtt_conn = _mqtt_conn;
1615
1631
  exports._mqtt_dispatch = _mqtt_dispatch;
1616
- exports.ao_defer_v = ao_defer_v;
1617
1632
  exports.as_topic_path = as_topic_path;
1618
1633
  exports.default = mqtt_v4;
1619
1634
  exports.mqtt_v4 = mqtt_v4;