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