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