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