u8-mqtt 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/cjs/basic-v4.cjs +283 -277
  2. package/cjs/basic-v4.cjs.map +1 -1
  3. package/cjs/basic-v5.cjs +297 -281
  4. package/cjs/basic-v5.cjs.map +1 -1
  5. package/cjs/full-v4.cjs +295 -289
  6. package/cjs/full-v4.cjs.map +1 -1
  7. package/cjs/full-v5.cjs +309 -293
  8. package/cjs/full-v5.cjs.map +1 -1
  9. package/cjs/index.cjs +310 -295
  10. package/cjs/index.cjs.map +1 -1
  11. package/cjs/v4.cjs +295 -289
  12. package/cjs/v4.cjs.map +1 -1
  13. package/cjs/v5.cjs +309 -293
  14. package/cjs/v5.cjs.map +1 -1
  15. package/code/_cmdid_dispatch.jsy +45 -69
  16. package/code/_conn.jsy +96 -72
  17. package/code/_dispatch.jsy +36 -28
  18. package/code/_utils.jsy +17 -0
  19. package/code/base.jsy +33 -61
  20. package/code/core.jsy +73 -51
  21. package/code/router_path.jsy +2 -1
  22. package/esm/basic-v4.js +283 -277
  23. package/esm/basic-v4.js.map +1 -1
  24. package/esm/basic-v5.js +297 -281
  25. package/esm/basic-v5.js.map +1 -1
  26. package/esm/deno/basic-v4.js +287 -281
  27. package/esm/deno/basic-v4.js.map +1 -1
  28. package/esm/deno/basic-v5.js +301 -285
  29. package/esm/deno/basic-v5.js.map +1 -1
  30. package/esm/deno/full-v4.js +299 -293
  31. package/esm/deno/full-v4.js.map +1 -1
  32. package/esm/deno/full-v5.js +313 -297
  33. package/esm/deno/full-v5.js.map +1 -1
  34. package/esm/deno/index.js +314 -298
  35. package/esm/deno/index.js.map +1 -1
  36. package/esm/deno/v4.js +299 -293
  37. package/esm/deno/v4.js.map +1 -1
  38. package/esm/deno/v5.js +313 -297
  39. package/esm/deno/v5.js.map +1 -1
  40. package/esm/full-v4.js +295 -289
  41. package/esm/full-v4.js.map +1 -1
  42. package/esm/full-v5.js +309 -293
  43. package/esm/full-v5.js.map +1 -1
  44. package/esm/index.js +310 -294
  45. package/esm/index.js.map +1 -1
  46. package/esm/node/basic-v4.js +283 -277
  47. package/esm/node/basic-v4.js.map +1 -1
  48. package/esm/node/basic-v4.mjs +283 -277
  49. package/esm/node/basic-v4.mjs.map +1 -1
  50. package/esm/node/basic-v5.js +297 -281
  51. package/esm/node/basic-v5.js.map +1 -1
  52. package/esm/node/basic-v5.mjs +297 -281
  53. package/esm/node/basic-v5.mjs.map +1 -1
  54. package/esm/node/full-v4.js +295 -289
  55. package/esm/node/full-v4.js.map +1 -1
  56. package/esm/node/full-v4.mjs +295 -289
  57. package/esm/node/full-v4.mjs.map +1 -1
  58. package/esm/node/full-v5.js +309 -293
  59. package/esm/node/full-v5.js.map +1 -1
  60. package/esm/node/full-v5.mjs +309 -293
  61. package/esm/node/full-v5.mjs.map +1 -1
  62. package/esm/node/index.js +310 -294
  63. package/esm/node/index.js.map +1 -1
  64. package/esm/node/index.mjs +310 -294
  65. package/esm/node/index.mjs.map +1 -1
  66. package/esm/node/v4.js +295 -289
  67. package/esm/node/v4.js.map +1 -1
  68. package/esm/node/v4.mjs +295 -289
  69. package/esm/node/v4.mjs.map +1 -1
  70. package/esm/node/v5.js +309 -293
  71. package/esm/node/v5.js.map +1 -1
  72. package/esm/node/v5.mjs +309 -293
  73. package/esm/node/v5.mjs.map +1 -1
  74. package/esm/v4.js +295 -289
  75. package/esm/v4.js.map +1 -1
  76. package/esm/v5.js +309 -293
  77. package/esm/v5.js.map +1 -1
  78. package/esm/web/basic-v4.js +283 -277
  79. package/esm/web/basic-v4.js.map +1 -1
  80. package/esm/web/basic-v4.min.js +1 -1
  81. package/esm/web/basic-v4.min.js.br +0 -0
  82. package/esm/web/basic-v4.min.js.gz +0 -0
  83. package/esm/web/basic-v5.js +297 -281
  84. package/esm/web/basic-v5.js.map +1 -1
  85. package/esm/web/basic-v5.min.js +1 -1
  86. package/esm/web/basic-v5.min.js.br +0 -0
  87. package/esm/web/basic-v5.min.js.gz +0 -0
  88. package/esm/web/full-v4.js +295 -289
  89. package/esm/web/full-v4.js.map +1 -1
  90. package/esm/web/full-v4.min.js +1 -1
  91. package/esm/web/full-v4.min.js.br +0 -0
  92. package/esm/web/full-v4.min.js.gz +0 -0
  93. package/esm/web/full-v5.js +309 -293
  94. package/esm/web/full-v5.js.map +1 -1
  95. package/esm/web/full-v5.min.js +1 -1
  96. package/esm/web/full-v5.min.js.br +0 -0
  97. package/esm/web/full-v5.min.js.gz +0 -0
  98. package/esm/web/index.js +310 -294
  99. package/esm/web/index.js.map +1 -1
  100. package/esm/web/index.min.js +1 -1
  101. package/esm/web/index.min.js.br +0 -0
  102. package/esm/web/index.min.js.gz +0 -0
  103. package/esm/web/v4.js +295 -289
  104. package/esm/web/v4.js.map +1 -1
  105. package/esm/web/v4.min.js +1 -1
  106. package/esm/web/v4.min.js.br +0 -0
  107. package/esm/web/v4.min.js.gz +0 -0
  108. package/esm/web/v5.js +309 -293
  109. package/esm/web/v5.js.map +1 -1
  110. package/esm/web/v5.min.js +1 -1
  111. package/esm/web/v5.min.js.br +0 -0
  112. package/esm/web/v5.min.js.gz +0 -0
  113. package/package.json +7 -8
@@ -156,10 +156,18 @@ let mqtt_reader_v5$1 = class mqtt_reader_v5 extends mqtt_reader_v4 {
156
156
 
157
157
  let res={}, fork = this.of(buf.subarray(vi, step.k|0));
158
158
  while (fork.has_more()) {
159
- let pt = mqtt_props.get( fork.u8() )
160
- , value = fork[pt.type]();
161
- res[pt.name] = ! pt.op ? value
162
- : fork[pt.op](res[pt.name], value);
159
+ let v, pk = fork.u8(), pt = mqtt_props.get( pk );
160
+
161
+ if (!pt) {
162
+ res.error = `Unknown mqtt_prop enum ${pk}`;
163
+ return res
164
+ }
165
+
166
+ v = fork[pt.type]();
167
+ if (pt.op) // accumulate operation
168
+ v = fork[pt.op](res[pt.name], v);
169
+
170
+ res[pt.name] = v;
163
171
  }
164
172
  return res
165
173
  }
@@ -293,6 +301,8 @@ class mqtt_writer_v5 extends mqtt_writer_v4 {
293
301
  let fork = this.of();
294
302
  for (let [name, value] of props) {
295
303
  let pt = mqtt_props.get(name);
304
+ if (!pt)
305
+ throw new Error(`Unknown mqtt_prop "${name}"`)
296
306
  fork[pt.op || 'one'](value, pt);
297
307
  }
298
308
  this.push(fork.pack());
@@ -976,6 +986,23 @@ function parse(str, loose) {
976
986
  };
977
987
  }
978
988
 
989
+ const _isfn = v => typeof v === 'function';
990
+ const _isstr = v => typeof v === 'string';
991
+
992
+ function _interval(fn_callback) {
993
+ let tid;
994
+ return (( td ) => {
995
+ tid = clearInterval(tid);
996
+ if (td) {
997
+ tid = setInterval(fn_callback, 1000 * td);
998
+
999
+
1000
+
1001
+
1002
+ // ensure the interval allows the NodeJS event loop to exit
1003
+ tid.unref?.();
1004
+ return true} }) }
1005
+
979
1006
  /*
980
1007
  class AbstractTopicRouter ::
981
1008
  async invoke(pkt, ctx) ::
@@ -1066,7 +1093,7 @@ function mqtt_topic_path_router() {
1066
1093
  let fn = args.pop();
1067
1094
  let priority = args.pop();
1068
1095
 
1069
- if ('function' !== typeof fn) {
1096
+ if (! _isfn(fn)) {
1070
1097
  if (fn) {throw new TypeError()}
1071
1098
  fn = _ignore;}
1072
1099
 
@@ -1231,238 +1258,108 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
1231
1258
  }
1232
1259
  }
1233
1260
 
1234
- function ao_defer_ctx(as_res = (...args) => args) {
1235
- let y,n,_pset = (a,b) => { y=a, n=b; };
1236
- return p =>(
1237
- p = new Promise(_pset)
1238
- , as_res(p, y, n)) }
1239
-
1240
- const ao_defer_v = /* #__PURE__ */
1241
- ao_defer_ctx();
1242
-
1243
- Promise.resolve({type:'init'});
1244
-
1245
- function _mqtt_conn(client, [on_mqtt, pkt_future]) {
1246
- let _q_init = ao_defer_v(), _q_ready = ao_defer_v();
1247
- let _send_ready = async (...args) => (await _q_ready[0])(...args);
1248
- let _send_mqtt_pkt, _has_connected;
1249
- client._send = _send_ready;
1250
-
1251
- return {
1252
- async when_ready() {await _q_ready[0];}
1253
-
1254
- , ping: _ping_interval (() =>_send_mqtt_pkt?.('pingreq'))
1255
-
1256
- , reset(err) {
1257
- if (! _send_mqtt_pkt) {return}
1258
-
1259
- if (err) {
1260
- _q_init[2](err);}
1261
-
1262
- _send_mqtt_pkt = null;
1263
- _q_init = ao_defer_v();
1264
- client._send = _send_ready;
1265
-
1266
- // call client.on_conn_reset in next promise microtask
1267
- client.conn_emit('on_disconnect', false===err, err);}
1268
-
1269
- , async send_connect(... args) {
1270
- if (! _send_mqtt_pkt) {
1271
- await _q_init[0]; }// _send_mqtt_pkt is set before fulfilled
1272
-
1273
- // await connack response
1274
- let res = await _send_mqtt_pkt(...args);
1275
- if (0 == res[0].reason) {
1276
- _has_connected = true;
1277
- // resolve _q_ready[0] with _send_mqtt_pkt closure
1278
- _q_ready[1](client._send = _send_mqtt_pkt);
1279
- _q_ready = ao_defer_v();
1280
- client.conn_emit('on_ready');}
1281
-
1282
- return res}
1261
+ async function _mqtt_cmd_evt(target, answer, pkt, ctx) {
1262
+ /* target : on_mqtt_type = {
1263
+ mqtt_pkt(pkt, ctx) {}, // generic
1264
+
1265
+ mqtt_auth(pkt, ctx) {},
1266
+ mqtt_connect(pkt, ctx) {},
1267
+ mqtt_connack(pkt, ctx) {},
1268
+ mqtt_disconnect(pkt, ctx) {},
1269
+
1270
+ mqtt_publish(pkt, ctx) {},
1271
+ mqtt_subscribe(pkt, ctx) {},
1272
+ mqtt_unsubscribe(pkt, ctx) {},
1273
+
1274
+ mqtt_pingreq(pkt, ctx) {},
1275
+ mqtt_pingresp(pkt, ctx) {},
1276
+ } */
1277
+
1278
+ let pkt_fn = target[`mqtt_${pkt.type}`] || target.mqtt_pkt;
1279
+ await pkt_fn?.call(target, pkt, ctx);}
1280
+
1281
+ function _mqtt_cmd_type(target, answer, pkt, ctx) {
1282
+ answer(pkt.type, pkt);
1283
+ _mqtt_cmd_evt(target, answer, pkt, ctx);}
1284
+
1285
+ function _mqtt_cmd_id(target, answer, pkt) {
1286
+ answer(pkt.pkt_id, pkt);}
1287
+
1288
+
1289
+ const _mqtt_cmdids =[
1290
+ _ => {} // 0x0 reserved
1291
+ , _mqtt_cmd_evt // 0x1 connect
1292
+ , _mqtt_cmd_type // 0x2 connack
1293
+ , _mqtt_cmd_evt // 0x3 publish
1294
+ , _mqtt_cmd_id // 0x4 puback
1295
+ , _mqtt_cmd_id // 0x5 pubrec
1296
+ , _mqtt_cmd_id // 0x6 pubrel
1297
+ , _mqtt_cmd_id // 0x7 pubcomp
1298
+ , _mqtt_cmd_evt // 0x8 subscribe
1299
+ , _mqtt_cmd_id // 0x9 suback
1300
+ , _mqtt_cmd_evt // 0xa unsubscribe
1301
+ , _mqtt_cmd_id // 0xb unsuback
1302
+ , _mqtt_cmd_type // 0xc pingreq
1303
+ , _mqtt_cmd_type // 0xd pingresp
1304
+ , _mqtt_cmd_evt // 0xe disconnect
1305
+ , _mqtt_cmd_type ];// 0xf auth
1283
1306
 
1284
- , is_set: (() =>!! _send_mqtt_pkt)
1285
- , set(mqtt_ctx, send_u8_pkt) {
1286
- if (_send_mqtt_pkt) {
1287
- throw new Error('Already connected')}
1288
-
1289
- mqtt_ctx = mqtt_ctx.mqtt_stream();
1290
- let sess_ctx = {mqtt: client};
1291
- let on_mqtt_chunk = u8_buf =>
1292
- on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
1293
-
1294
- _send_mqtt_pkt = async (type, pkt, key) => {
1295
- let res = undefined !== key
1296
- ? pkt_future(key) : true;
1297
-
1298
- await send_u8_pkt(
1299
- mqtt_ctx.encode_pkt(type, pkt));
1300
-
1301
- return res};
1302
-
1303
- _q_init[1](_send_mqtt_pkt); // resolve _q_init with _send_mqtt_pkt closure
1304
-
1305
- // call client.on_live in next promise microtask
1306
- client.conn_emit('on_live', _has_connected);
1307
- return on_mqtt_chunk} } }
1308
-
1309
-
1310
- function _ping_interval(send_ping) {
1311
- let tid;
1312
- return (( td ) => {
1313
- tid = clearInterval(tid);
1314
- if (td) {
1315
- tid = setInterval(send_ping, 1000 * td);
1316
-
1317
-
1318
-
1319
-
1320
- // ensure the interval allows the NodeJS event loop to exit
1321
- tid.unref?.();
1322
- return true} }) }
1307
+ function _mqtt_dispatch(opt, target) {
1308
+ let hashbelt=[], rotate_ts=0;
1309
+ // default rotate at 1s across 5 buckets
1310
+ let { td: rotate_td=1000, n: rotate_n=5 } = opt?.rotate || {};
1323
1311
 
1324
- const _mqtt_cmdid_dispatch ={
1325
- create(target) {
1326
- return {__proto__: this, target, hashbelt: [new Map()]} }
1312
+ // Promise / future scaffolding
1313
+ let _pkt_id=100, _ftr_key; // use _ftr_key to reuse _by_key closure
1314
+ let _ftr_by_key = fn_answer => hashbelt[0].set(_ftr_key, fn_answer);
1327
1315
 
1328
- , bind_pkt_future(_pkt_id=100) {
1329
- let {hashbelt} = this;
1316
+ on_mqtt([]); // init hashbelt and rotate_ts
1317
+ return [on_mqtt, pkt_future]
1330
1318
 
1331
- let _tmp_; // use _tmp_ to reuse _by_key closure
1332
- let _by_key = answer_monad =>
1333
- hashbelt[0].set(_tmp_, answer_monad);
1334
1319
 
1335
- return (( pkt_or_key ) => {
1336
- if ('string' === typeof pkt_or_key) {
1337
- _tmp_ = pkt_or_key;}
1338
- else {
1339
- _pkt_id = (_pkt_id + 1) & 0xffff;
1340
- _tmp_ = pkt_or_key.pkt_id = _pkt_id;}
1320
+ function pkt_future(pkt_or_key) {
1321
+ if (! _isstr(pkt_or_key)) {
1322
+ _pkt_id = (_pkt_id + 1) & 0xffff; // 16-bit unsigned short
1323
+ _ftr_key = pkt_or_key.pkt_id = _pkt_id;}
1324
+ else _ftr_key = pkt_or_key;
1341
1325
 
1342
- return new Promise(_by_key)}) }
1326
+ return new Promise(_ftr_by_key)}
1343
1327
 
1344
- , answer(key, pkt) {
1345
- for (let map of this.hashbelt) {
1346
- let answer_monad = map.get(key);
1347
- if (undefined !== answer_monad) {
1328
+ function answer(key, pkt) {
1329
+ for (let map of hashbelt) {
1330
+ let fn_answer = map.get(key);
1331
+ if (fn_answer) {
1348
1332
  map.delete(key);
1349
1333
 
1350
- answer_monad([pkt, /*err*/]); // option/maybe monad
1334
+ fn_answer([pkt, /*err*/]); // option/maybe monad
1351
1335
  return true} }
1352
1336
  return false}
1353
1337
 
1354
- , rotate_belt(n) {
1355
- let {hashbelt} = this;
1356
- hashbelt.unshift(new Map());
1357
- for (let old of hashbelt.splice(n || 5)) {
1358
- for (let answer_monad of old.values()) {
1359
- answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
1360
-
1361
- , cmdids: ((() => {
1362
- return [
1363
- (() =>{} )// 0x0 reserved
1364
- , by_evt // 0x1 connect
1365
- , by_type // 0x2 connack
1366
- , by_evt // 0x3 publish
1367
- , by_id // 0x4 puback
1368
- , by_id // 0x5 pubrec
1369
- , by_id // 0x6 pubrel
1370
- , by_id // 0x7 pubcomp
1371
- , by_evt // 0x8 subscribe
1372
- , by_id // 0x9 suback
1373
- , by_evt // 0xa unsubscribe
1374
- , by_id // 0xb unsuback
1375
- , by_type // 0xc pingreq
1376
- , by_type // 0xd pingresp
1377
- , by_evt // 0xe disconnect
1378
- , by_type ]// 0xf auth
1379
-
1380
-
1381
- function by_id(disp, pkt) {
1382
- disp.answer(pkt.pkt_id, pkt); }
1383
-
1384
- function by_type(disp, pkt, ctx) {
1385
- disp.answer(pkt.type, pkt);
1386
- by_evt(disp, pkt, ctx);}
1387
-
1388
- async function by_evt({target}, pkt, ctx) {
1389
- let fn = target[`mqtt_${pkt.type}`]
1390
- || target.mqtt_pkt;
1391
-
1392
- await fn?.call(target, pkt, ctx);} })()) };
1393
-
1394
- /*
1395
- on_mqtt_type = {
1396
- mqtt_auth(pkt, ctx) ::
1397
- mqtt_connect(pkt, ctx) ::
1398
- mqtt_connack(pkt, ctx) ::
1399
- mqtt_disconnect(pkt, ctx) ::
1400
-
1401
- mqtt_publish(pkt, ctx)
1402
- mqtt_subscribe(pkt, ctx) ::
1403
- mqtt_unsubscribe(pkt, ctx) ::
1404
-
1405
- mqtt_pingreq(pkt, ctx) ::
1406
- mqtt_pingresp(pkt, ctx) ::
1407
- }
1408
- */
1409
-
1410
- function _mqtt_dispatch(opt, target) {
1411
- let _disp_ = _mqtt_cmdid_dispatch.create(target);
1412
- let { cmdids } = _disp_;
1413
-
1414
- // default rotate at 1s across 5 buckets
1415
- let { td: rotate_td=1000, n: rotate_n=5 } =
1416
- opt && opt.rotate || {};
1417
-
1418
- let rotate_ts = rotate_td + Date.now();
1419
-
1420
- return [on_mqtt,
1421
- _disp_.bind_pkt_future()]
1422
-
1423
1338
  function on_mqtt(pkt_list, ctx) {
1424
1339
  for (let pkt of pkt_list) {
1425
- cmdids[pkt.id](_disp_, pkt, ctx); }
1340
+ _mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
1426
1341
 
1427
- if (Date.now() > rotate_ts) {
1428
- _disp_.rotate_belt(rotate_n);
1429
- rotate_ts = rotate_td + Date.now();} } }
1342
+ // rotate after rotate_ts
1343
+ let now = Date.now();
1344
+ if (now > rotate_ts) {
1345
+ rotate_ts = rotate_td + now;
1346
+ hashbelt.unshift(new Map());
1347
+ while (hashbelt.length > rotate_n) {
1348
+ for (let fn_answer of hashbelt.pop().values()) {
1349
+ fn_answer([/*pkt*/, 'expired']); } } } } }// option/maybe monad
1430
1350
 
1431
1351
  class MQTTError extends Error {
1432
1352
  constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
1353
+ // use hex-encoded reasons to match MQTT spec documentation
1433
1354
  super(`[0x${reason.toString(16)}] ${reason.reason}`);
1434
1355
  this.mqtt_pkt = mqtt_pkt;
1435
1356
  this.reason = reason;} }
1436
1357
 
1437
1358
  class MQTTBase {
1438
- constructor(opt={}) {
1439
- this.with(opt);
1440
- this._conn_ = _mqtt_conn(this,
1441
- this._init_dispatch(opt, this)); }
1442
-
1443
- with(fns_ns) {
1444
- for (let [k,v] of Object.entries(fns_ns)) {
1445
- if ('function' === typeof v) {this[k] = v;} }
1446
- return this}
1447
-
1448
- async conn_emit(evt, arg, err_arg) {
1449
- this.log_conn?.(evt, arg, err_arg);
1450
- try {
1451
- let fn_evt = this[await evt]; // microtask break using `await evt`
1452
- if (fn_evt) {
1453
- await fn_evt.call(this, this, arg, err_arg);}
1454
- else if (err_arg) {throw err_arg} }
1455
- catch (err) {
1456
- this.on_error(err, evt);} }
1457
-
1458
- on_error(err, evt) {
1459
- console.warn('[[u8-mqtt error: %s]]', evt, err); }
1460
-
1461
1359
  // Handshaking Packets
1462
-
1463
1360
  async connect(pkt={}) {
1464
1361
  let cid = pkt.client_id;
1465
- if ('string' !== typeof cid) {
1362
+ if (! _isstr(cid)) {
1466
1363
  // see init_client_id implementation in core.jsy
1467
1364
  pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
1468
1365
  this.client_id = cid;
@@ -1470,23 +1367,20 @@ class MQTTBase {
1470
1367
  if (null == pkt.keep_alive) {
1471
1368
  pkt.keep_alive = 60;}
1472
1369
 
1473
- let res = await this._conn_
1474
- .send_connect('connect', pkt, 'connack');
1475
-
1476
- if (0 != res[0].reason) {
1477
- throw new this.MQTTError(res[0])}
1478
-
1479
- // TODO: merge with server's keep_alive frequency
1480
- this._conn_.ping(pkt.keep_alive);
1481
- return res}
1370
+ let response = await this._send0('connect', pkt, 'connack');
1371
+ if (0 != response[0].reason) {// compare to 0 to coerce to number
1372
+ throw new this.MQTTError(response[0])}
1373
+ return this.conn.on_conn(pkt, response)}
1482
1374
 
1483
1375
  async disconnect(pkt={}) {
1484
- let res = await this._send('disconnect', pkt);
1485
- this._conn_.reset(false);
1486
- return res}
1376
+ let response = await this._send0('disconnect', pkt);
1377
+ return this.conn.on_dis(pkt, response)}
1487
1378
 
1488
- auth(pkt={}) {
1489
- return this._send('auth', pkt, 'auth')}
1379
+ async auth(pkt={}) {
1380
+ let response = await this._send0('auth', pkt, 'auth');
1381
+ if (response[0].reason) {
1382
+ throw new this.MQTTError(response[0])}
1383
+ return this.conn.on_auth(pkt, response)}
1490
1384
 
1491
1385
  ping() {return this._send('pingreq', null, 'pingresp')}
1492
1386
  puback({pkt_id}) {return this._send('puback', {pkt_id})}
@@ -1523,20 +1417,18 @@ class MQTTBase {
1523
1417
  // alias: publish -- because 'pub' is shorter for semantic aliases above
1524
1418
  async pub(pkt, pub_opt) {
1525
1419
  if (undefined === pkt.payload) {
1526
- if ('function' === typeof pub_opt) {
1420
+ if (_isfn(pub_opt)) {
1421
+ // pub_opt as a function is fn_encode value
1527
1422
  pub_opt = {fn_encode: pub_opt};}
1528
1423
 
1529
- let {msg} = pkt;
1530
- switch (typeof msg) {
1531
- case 'function':
1532
- pub_opt = {...pub_opt, fn_encode: msg};
1533
- // flow into 'undefined' case
1534
- case 'undefined':
1535
- // return a single-value closure to publish packets
1536
- return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1424
+ let msg = pkt.msg, fn_encode = pub_opt?.fn_encode;
1425
+ if (null == msg || _isfn(msg)) {
1426
+ // when msg is a function, return closure using fn_encode
1427
+ if (msg) {pub_opt = {...pub_opt, fn_encode: msg};}
1428
+ // return a single-value closure to publish packets
1429
+ return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1537
1430
 
1538
1431
  // Encode payload from msg; fn_encode allows alternative to JSON.stringify
1539
- let {fn_encode} = pub_opt || {};
1540
1432
  pkt.payload = fn_encode
1541
1433
  ? await fn_encode(msg)
1542
1434
  : JSON.stringify(msg);}
@@ -1548,31 +1440,31 @@ class MQTTBase {
1548
1440
  pkt = pub_opt.xform(pkt) || pkt;} }
1549
1441
 
1550
1442
  return this._send('publish', pkt,
1551
- pkt.qos ? pkt : void 0 ) }// key
1443
+ pkt.qos ? pkt : null ) }// key
1552
1444
 
1553
1445
 
1554
1446
  // Internal API
1555
1447
 
1556
- /* async _send(type, pkt) -- provided by _conn_ and transport */
1448
+ /* async _send0(type, pkt) -- provided by conn and transport */
1449
+ /* async _send(type, pkt) -- provided by conn and transport */
1557
1450
 
1558
1451
  _init_dispatch(opt) {
1559
1452
  this.constructor?._once_();
1560
1453
  let target ={__proto__: opt.on_mqtt_type};
1561
1454
  target.mqtt_publish ||=
1562
1455
  this._init_router?.(opt, this, target);
1563
- return _mqtt_dispatch(this, target)}
1456
+ return _mqtt_dispatch(opt, target)}
1564
1457
 
1565
1458
  static _aliases() {
1566
1459
  return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
1567
1460
 
1568
- static _once_(self=this) {
1569
- self._once_ = _=>0;
1570
- let p = self.prototype;
1461
+ static _once_(klass=this) {
1462
+ klass._once_ = _=>0;
1463
+ var alias, name, p = klass.prototype;
1571
1464
  p.MQTTError = MQTTError;
1572
- for (let alias of self._aliases().split(/\s+/)) {
1573
- alias = alias.split(':');
1574
- let fn = alias[1] && p[alias[1]];
1575
- if (fn) {p[alias[0]] = fn;} } } }
1465
+ for (alias of klass._aliases().split(/\s+/)) {
1466
+ [alias, name] = alias.split(':');
1467
+ p[alias] = p[name];} } }
1576
1468
 
1577
1469
 
1578
1470
  function _as_topics(pkt, ex, topic_prefix) {
@@ -1597,37 +1489,167 @@ function _as_topics(pkt, ex, topic_prefix) {
1597
1489
  pkt.topics = pkt.topics.map(_prefix_topics);}
1598
1490
  return pkt}
1599
1491
 
1600
- const pkt_api = {
1601
- utf8(u8) { return new TextDecoder('utf-8').decode(u8 || this.payload ) },
1602
- json(u8) { return JSON.parse( this.utf8(u8) || null ) },
1603
- text(u8) { return this.utf8(u8) },
1604
- };
1492
+ const _defer_obj = o =>(
1493
+ o.p = new Promise((a,e) => { o.a=a; o.e=e; })
1494
+ , o);
1495
+
1496
+ function _dfn_reset(client, attr, fn_after) {
1497
+ // a resetable deferred for a function
1498
+ let self = {set}, afn = async (...args) => (await self.p)(...args);
1499
+ return set()
1500
+
1501
+ function set() {
1502
+ if (afn !== client[attr]) {
1503
+ _defer_obj(self).p.then(fn_after, _=>0);
1504
+ client[attr] = afn;}
1505
+ return self} }
1506
+
1507
+ function _mqtt_conn(opt, client, [on_mqtt, pkt_future]) {
1508
+ let _abort;
1509
+ let _dfn_send0 = _dfn_reset(client, '_send0', // client._send0 getter/setter
1510
+ _=> client.conn_emit('on_live', conn.has_connected));
1511
+ let _dfn_ready = _dfn_reset(client, '_send', // client._send getter/setter
1512
+ _=> client.conn_emit('on_ready'));
1513
+ let _keep_alive_ival = _interval (() =>client._send0('pingreq') );// resettable interval for keep_alive ping
1514
+
1515
+ let conn = Object.create({
1516
+ ping: (td=conn.keep_alive) => _keep_alive_ival(td)
1517
+
1518
+ , on_conn(pkt, response) {
1519
+ conn.has_connected = true;
1520
+ conn.keep_alive = opt.keep_alive || response[0].props?.server_keep_alive || pkt.keep_alive;
1521
+ client.conn_emit('on_conn');
1522
+ return opt.use_auth
1523
+ ? response // wait on enhanced authentication step
1524
+ : conn.on_auth(null, response) }// otherwise, connect is also auth
1525
+
1526
+ , on_auth(pkt, response) {
1527
+ _dfn_ready.a(_dfn_send0.p);
1528
+ if (0 != opt.keep_alive) {
1529
+ conn.ping();}
1530
+ client.conn_emit('on_auth', !pkt);
1531
+ return response}
1532
+
1533
+ , on_dis(pkt, response) {
1534
+ conn.reset(false);
1535
+ return response}
1536
+
1537
+ , reset(err) {
1538
+ if (err) {
1539
+ _dfn_send0.e(err); }// send error to uses of _send0 (connect, auth)
1540
+ _abort.e(err); // abort in-progress connections
1541
+
1542
+ delete conn.is_set;
1543
+ conn.ready = handshake();
1544
+ client.conn_emit('on_disconnect', false===err, err);}
1545
+
1546
+ , abort() {
1547
+ _dfn_ready.e(err); // abort all messages awaiting ready state
1548
+ return conn.reset(err)}
1549
+
1550
+ , async setup(gate, send_u8_pkt, init_msg_loop) {
1551
+ if (conn.is_set) {
1552
+ throw new Error() }// already in-progress
1553
+
1554
+ conn.is_set = true;
1555
+ await gate;
1556
+
1557
+ // setup send/recv MQTT parsing context
1558
+ let mqtt_ctx = client.mqtt_ctx.mqtt_stream();
1559
+
1560
+ {// setup inbound message loop
1561
+ let sess_ctx = {mqtt: client}; // mutable session context
1562
+ let on_mqtt_chunk = u8 => on_mqtt(mqtt_ctx.decode(u8), sess_ctx);
1563
+ init_msg_loop(on_mqtt_chunk, conn);}
1564
+
1565
+ // setup outbound message path and transport connection
1566
+ send_u8_pkt = await send_u8_pkt;
1567
+ _dfn_send0.a(
1568
+ async (type, pkt, key) => {
1569
+ let res = undefined !== key
1570
+ ? pkt_future(key) : true;
1571
+
1572
+ await send_u8_pkt(
1573
+ mqtt_ctx.encode_pkt(type, pkt));
1574
+ return res} ); } });
1575
+
1576
+ conn.ready = handshake();
1577
+ return conn
1578
+
1579
+ async function handshake() {
1580
+ _abort = _defer_obj({});
1581
+
1582
+ _keep_alive_ival(0); // clearInterval on keep alive ping
1583
+ _dfn_send0.set(); // reset client._send0 if necessary
1584
+ _dfn_ready.set(); // reset client._send if necessary
1585
+
1586
+ try {
1587
+ // set client._send0 as passtrhough after transport connection
1588
+ client._send0 = await Promise.race([_dfn_send0.p, _abort.p]);
1589
+
1590
+ // set client._send as passtrhough after ready
1591
+ client._send = await Promise.race([_dfn_ready.p, _abort.p]);
1592
+ return true}
1593
+ catch (err) {
1594
+ return false} } }
1595
+
1596
+ const pkt_api ={
1597
+ utf8(u8) {return new TextDecoder('utf-8').decode(u8 || this.payload )}
1598
+ , json(u8) {return JSON.parse( this.utf8(u8) || null )}
1599
+ , text(u8) {return this.utf8(u8)} };
1600
+
1601
+ const opt_default ={
1602
+ sess_stg: globalThis.sessionStorage};
1605
1603
 
1606
1604
  class MQTTCore extends MQTTBase {
1605
+ constructor(opt) {
1606
+ super();
1607
+ this.with(opt);
1608
+ opt ={...opt_default, ...opt};
1609
+ // settings for MQTTCore
1610
+ this.sess_stg = opt.sess_stg;
1611
+ // setup connection and dispatch
1612
+ this.conn = _mqtt_conn(opt, this,
1613
+ this._init_dispatch(opt)); }
1614
+
1615
+ with(fns_ns) {
1616
+ for (let [k,v] of Object.entries(fns_ns)) {
1617
+ if (_isfn(v)) {this[k] = v;} }
1618
+ return this}
1619
+
1620
+
1607
1621
  static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
1608
- let self = class extends this {};
1609
- self.prototype.mqtt_ctx =
1622
+ let klass = class extends this {};
1623
+ klass.prototype.mqtt_ctx =
1610
1624
  mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
1611
- return self}
1625
+ return klass}
1626
+
1627
+
1628
+ async conn_emit(evt, arg, err_arg) {
1629
+ this.log_conn?.(evt, arg, err_arg);
1630
+ try {
1631
+ let fn_evt = this[await evt]; // microtask break using `await evt`
1632
+ if (fn_evt) {
1633
+ await fn_evt.call(this, this, arg, err_arg);}
1634
+ else if (err_arg) {throw err_arg} }
1635
+ catch (err) {
1636
+ this.on_error(err, evt);} }
1637
+
1638
+ on_error(err, evt) {
1639
+ console.warn('[[u8-mqtt error: %s]]', evt, err); }
1640
+ log_conn(evt, arg, err_arg) {
1641
+ console.info('[[u8-mqtt conn: %s]]', evt, arg, err_arg); }
1612
1642
 
1613
1643
 
1614
1644
  // automatic Client Id for connect()
1615
1645
  init_client_id(parts=['u8-mqtt--','']) {
1616
- let sess_stg=this.sess_stg;
1646
+ let sess_stg = this.sess_stg;
1617
1647
  let key, cid = sess_stg?.getItem(key=parts.join(' '));
1618
1648
  if (! cid) {
1619
1649
  cid = parts.join(Math.random().toString(36).slice(2));
1620
1650
  sess_stg?.setItem(key, cid);}
1621
1651
  return cid}
1622
1652
 
1623
- get sess_stg() {return globalThis.sessionStorage}
1624
-
1625
-
1626
- //on_error(err, evt) ::
1627
- // console.warn @ '[[u8-mqtt error: %s]]', evt, err
1628
-
1629
- //log_conn(evt, arg, err_arg) ::
1630
- // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1631
1653
 
1632
1654
  on_live(client, is_reconnect) {
1633
1655
  if (is_reconnect) {
@@ -1653,19 +1675,16 @@ class MQTTCore extends MQTTBase {
1653
1675
  return new Promise(done => setTimeout(done, ms)) }
1654
1676
 
1655
1677
  with_async_iter(async_iter, write_u8_pkt) {
1656
- let on_mqtt_chunk = this._conn_.set(
1657
- this.mqtt_ctx,
1658
- write_u8_pkt);
1659
-
1660
- this._msg_loop = ((async () => {
1661
- try {
1662
- async_iter = await async_iter;
1663
- for await (let chunk of async_iter)
1664
- on_mqtt_chunk(chunk);
1665
- this._conn_.reset();}
1666
- catch (err) {
1667
- this._conn_.reset(err);} })());
1668
-
1678
+ this.conn.setup(async_iter,
1679
+ write_u8_pkt,
1680
+ async (on_mqtt_chunk, conn) => {
1681
+ try {
1682
+ async_iter = await async_iter;
1683
+ for await (let chunk of async_iter)
1684
+ on_mqtt_chunk(chunk);
1685
+ conn.reset();}
1686
+ catch (err) {
1687
+ conn.reset(err);} } );
1669
1688
  return this}
1670
1689
 
1671
1690
 
@@ -1736,33 +1755,30 @@ class MQTTCore extends MQTTBase {
1736
1755
 
1737
1756
  websock.binaryType = 'arraybuffer';
1738
1757
 
1739
- let ready, {readyState} = websock;
1758
+ let ws_ready, readyState = websock.readyState;
1740
1759
  if (1 !== readyState) {
1741
1760
  if (0 !== readyState) {
1742
- throw new Error('Invalid WebSocket readyState') }
1743
-
1744
- ready = new Promise(fn => websock.onopen = fn); }
1761
+ throw new Error('WS readyState') }
1745
1762
 
1763
+ ws_ready = new Promise(ready => websock.onopen = ready); }
1746
1764
 
1747
- let {_conn_} = this;
1748
- let on_mqtt_chunk = _conn_.set(
1749
- this.mqtt_ctx,
1750
- async u8_pkt =>(
1751
- await ready
1752
- , websock.send(u8_pkt)) );
1765
+ this.conn.setup(ws_ready,
1766
+ u8_pkt => websock.send(u8_pkt),
1767
+ (on_mqtt_chunk, conn) => {
1768
+ websock.onmessage = evt =>(
1769
+ on_mqtt_chunk(new Uint8Array(evt.data)) );
1753
1770
 
1754
- websock.onmessage = evt =>(on_mqtt_chunk(new Uint8Array(evt.data)));
1755
- websock.onclose = evt => {
1756
- if (! evt.wasClean) {
1757
- var err = new Error('websocket connection close');
1758
- err.code = evt.code;
1759
- err.reason = evt.reason;}
1771
+ websock.onclose = evt => {
1772
+ if (! evt.wasClean) {
1773
+ var err = new Error('websocket close');
1774
+ err.code = evt.code;
1775
+ err.reason = evt.reason;}
1760
1776
 
1761
- _conn_.reset(err);};
1777
+ conn.reset(err);}; } );
1762
1778
 
1763
1779
  return this} }
1764
1780
 
1765
- const version = '0.5.3-web';
1781
+ const version = '0.6.0-web';
1766
1782
 
1767
1783
  const MQTTClient_v4 = /* #__PURE__ */
1768
1784
  with_topic_path_router(