u8-mqtt 0.3.1 → 0.4.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 (78) hide show
  1. package/README.md +5 -5
  2. package/cjs/basic-v4.cjs +1205 -0
  3. package/cjs/basic-v4.cjs.map +1 -0
  4. package/cjs/basic-v5.cjs +1469 -0
  5. package/cjs/basic-v5.cjs.map +1 -0
  6. package/cjs/index.cjs +312 -247
  7. package/cjs/index.cjs.map +1 -1
  8. package/cjs/v4.cjs +307 -242
  9. package/cjs/v4.cjs.map +1 -1
  10. package/cjs/v5.cjs +309 -243
  11. package/cjs/v5.cjs.map +1 -1
  12. package/code/_cmdid_dispatch.jsy +1 -3
  13. package/code/base.jsy +64 -84
  14. package/code/{v4.mjs → basic-v4.js} +1 -1
  15. package/code/{v5.mjs → basic-v5.js} +1 -1
  16. package/code/core.jsy +41 -15
  17. package/code/{index.mjs → index.js} +3 -2
  18. package/code/{_router.jsy → router_path.jsy} +30 -12
  19. package/code/v4.js +20 -0
  20. package/code/v5.js +29 -0
  21. package/code/version.js +1 -0
  22. package/code/with_topic_router.jsy +41 -0
  23. package/esm/deno/basic-v4.js +1193 -0
  24. package/esm/deno/basic-v4.js.map +1 -0
  25. package/esm/deno/basic-v5.js +1455 -0
  26. package/esm/deno/basic-v5.js.map +1 -0
  27. package/esm/deno/index.js +307 -242
  28. package/esm/deno/index.js.map +1 -1
  29. package/esm/deno/v4.js +304 -240
  30. package/esm/deno/v4.js.map +1 -1
  31. package/esm/deno/v5.js +306 -241
  32. package/esm/deno/v5.js.map +1 -1
  33. package/esm/node/basic-v4.js +1196 -0
  34. package/esm/node/basic-v4.js.map +1 -0
  35. package/esm/node/basic-v4.mjs +1196 -0
  36. package/esm/node/basic-v4.mjs.map +1 -0
  37. package/esm/node/basic-v5.js +1458 -0
  38. package/esm/node/basic-v5.js.map +1 -0
  39. package/esm/node/basic-v5.mjs +1458 -0
  40. package/esm/node/basic-v5.mjs.map +1 -0
  41. package/esm/node/index.js +309 -243
  42. package/esm/node/index.js.map +1 -1
  43. package/esm/node/index.mjs +309 -243
  44. package/esm/node/index.mjs.map +1 -1
  45. package/esm/node/v4.js +306 -241
  46. package/esm/node/v4.js.map +1 -1
  47. package/esm/node/v4.mjs +306 -241
  48. package/esm/node/v4.mjs.map +1 -1
  49. package/esm/node/v5.js +308 -242
  50. package/esm/node/v5.js.map +1 -1
  51. package/esm/node/v5.mjs +308 -242
  52. package/esm/node/v5.mjs.map +1 -1
  53. package/esm/web/basic-v4.js +1193 -0
  54. package/esm/web/basic-v4.js.map +1 -0
  55. package/esm/web/basic-v4.min.js +1 -0
  56. package/esm/web/basic-v4.min.js.br +0 -0
  57. package/esm/web/basic-v4.min.js.gz +0 -0
  58. package/esm/web/basic-v5.js +1455 -0
  59. package/esm/web/basic-v5.js.map +1 -0
  60. package/esm/web/basic-v5.min.js +1 -0
  61. package/esm/web/basic-v5.min.js.br +0 -0
  62. package/esm/web/basic-v5.min.js.gz +0 -0
  63. package/esm/web/index.js +307 -242
  64. package/esm/web/index.js.map +1 -1
  65. package/esm/web/index.min.js +1 -1
  66. package/esm/web/index.min.js.br +0 -0
  67. package/esm/web/index.min.js.gz +0 -0
  68. package/esm/web/v4.js +305 -241
  69. package/esm/web/v4.js.map +1 -1
  70. package/esm/web/v4.min.js +1 -1
  71. package/esm/web/v4.min.js.br +0 -0
  72. package/esm/web/v4.min.js.gz +0 -0
  73. package/esm/web/v5.js +306 -241
  74. package/esm/web/v5.js.map +1 -1
  75. package/esm/web/v5.min.js +1 -1
  76. package/esm/web/v5.min.js.br +0 -0
  77. package/esm/web/v5.min.js.gz +0 -0
  78. package/package.json +7 -7
package/esm/node/v5.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { connect } from 'node:net';
2
+ import { connect as connect$1 } from 'node:tls';
2
3
 
3
4
  function encode_varint(n, a=[]) {
4
5
  do {
@@ -771,6 +772,191 @@ const mqtt_opts_v5 =
771
772
  encode_fns: mqtt_encode_v5,
772
773
  mqtt_writer: mqtt_writer_v5, };
773
774
 
775
+ function parse(str, loose) {
776
+ if (str instanceof RegExp) return { keys:false, pattern:str };
777
+ var c, o, tmp, ext, keys=[], pattern='', arr = str.split('/');
778
+ arr[0] || arr.shift();
779
+
780
+ while (tmp = arr.shift()) {
781
+ c = tmp[0];
782
+ if (c === '*') {
783
+ keys.push('wild');
784
+ pattern += '/(.*)';
785
+ } else if (c === ':') {
786
+ o = tmp.indexOf('?', 1);
787
+ ext = tmp.indexOf('.', 1);
788
+ keys.push( tmp.substring(1, !!~o ? o : !!~ext ? ext : tmp.length) );
789
+ pattern += !!~o && !~ext ? '(?:/([^/]+?))?' : '/([^/]+?)';
790
+ if (!!~ext) pattern += (!!~o ? '?' : '') + '\\' + tmp.substring(ext);
791
+ } else {
792
+ pattern += '/' + tmp;
793
+ }
794
+ }
795
+
796
+ return {
797
+ keys: keys,
798
+ pattern: new RegExp('^' + pattern + (loose ? '(?=$|\/)' : '\/?$'), 'i')
799
+ };
800
+ }
801
+
802
+ /*
803
+ class AbstractTopicRouter ::
804
+ async invoke(pkt, ctx) ::
805
+ add(topic_route, ...args) ::
806
+ remove(topic_route, priority) ::
807
+ clear(priority) ::
808
+ find(topic) :: // optional
809
+ mqtt_topic(topic_route)
810
+ */
811
+
812
+ const with_topic_router = mqtt_topic_router =>
813
+ MQTTKlass => class extends MQTTKlass {
814
+ static _aliases() {
815
+ return super._aliases() +
816
+ ' sub_topic:subscribe_topic unsub_topic:unsubscribe_topic'}
817
+
818
+ _init_router(opt) {
819
+ return mqtt_topic_router(opt, this)}
820
+
821
+ get on_topic() {return this.router.add}
822
+
823
+ _sub_chain(topic, ex, topic_prefix) {
824
+ let res = this.subscribe([[ topic ]], ex, topic_prefix);
825
+ let subs = this.subs ||(this.subs = new Map());
826
+ subs.set((res.topic = topic), (subs.last = res));
827
+ return this }// fluent api -- return this and track side effects
828
+
829
+ // alias: sub_topic
830
+ subscribe_topic(topic_route, ...args) {
831
+ let router = this.router;
832
+ router.add(topic_route, true, args.pop() );// handler
833
+ let topic = router.mqtt_topic(topic_route);
834
+ return this._sub_chain(topic, ...args ) }// ex, topic_prefix
835
+
836
+ // alias: unsub_topic
837
+ unsubscribe_topic(topic_route, ...args) {
838
+ let router = this.router;
839
+ router.remove(topic_route, true);
840
+ let topic = router.mqtt_topic(topic_route);
841
+ return this.unsubscribe([[ topic ]], ...args ) } };// topic_prefix
842
+
843
+ // Use [regexparam][] for url-like topic parsing
844
+ // [regexparam]: https://github.com/lukeed/regexparam
845
+
846
+
847
+ const with_topic_path_router = /* #__PURE__ */
848
+ with_topic_router(mqtt_topic_path_router);
849
+
850
+
851
+ const mqtt_topic = topic_route =>
852
+ topic_route
853
+ .replace(/[*].*$/, '#')
854
+ .replace(/:\w+\??/g, '+');
855
+
856
+ const as_topic_path = topic_route =>(
857
+ topic_route
858
+ .replace(/#$/, '*') // replace MQTT # wildcard at end
859
+ .split(/([^\/]*[+][^\/]*)/) // split on MQTT + match tokens
860
+ .reduce (( sz, v, idx ) => sz +(
861
+ idx & 1 // even entires are body, odd are MQTT + tokens
862
+ ? `:$${1 + idx>>1}` // replace with `:$#` sequential ids, using ? for partial entries
863
+ : v ) ) );// pass through body
864
+
865
+ function _ignore(pkt, params, ctx) {ctx.done = true;}
866
+
867
+ function mqtt_topic_path_router() {
868
+ let pri_lsts = [[],[]], rm = Symbol();
869
+ let find = topic => _routes_iter(pri_lsts, topic);
870
+
871
+ // return duck-type compatible with AbstractTopicRouter in ./with_topic_router
872
+ return {find, mqtt_topic,
873
+ add(topic_route, ...args) {
874
+ let fn = args.pop();
875
+ let priority = args.pop();
876
+
877
+ if ('function' !== typeof fn) {
878
+ if (false === fn) {
879
+ fn = _ignore;}
880
+ else throw new TypeError()}
881
+
882
+ let rte = parse(as_topic_path(topic_route));
883
+
884
+ rte.key = topic_route;
885
+ rte.tgt = fn;
886
+ pri_lsts[priority ? 0 : 1].push(rte);
887
+ return this}
888
+
889
+ , remove(topic_route, priority) {
890
+ let lst = pri_lsts[priority ? 0 : 1];
891
+ return _route_remove([lst], topic_route)}
892
+
893
+ , clear(priority) {
894
+ pri_lsts[priority ? 0 : 1] = [];
895
+ if (null == priority) {
896
+ pri_lsts[1] = [];} }
897
+
898
+ , async invoke(pkt, ctx) {
899
+ ctx.idx = 0;
900
+ ctx.rm = rm;
901
+
902
+ for (let [fn, params] of find(pkt.topic)) {
903
+ let res = await fn(pkt, params, ctx);
904
+
905
+ if (rm === res) {
906
+ _route_remove(pri_lsts, fn);}
907
+
908
+ if (ctx.done) {
909
+ break}
910
+ else ctx.idx++;}
911
+
912
+ let {pkt_id, qos} = pkt;
913
+ if (1 === qos) {
914
+ await ctx.mqtt._send('puback', {pkt_id});} } } }
915
+
916
+
917
+ function * _routes_iter(all_route_lists, topic) {
918
+ for (let route_list of all_route_lists) {
919
+ for (let route of route_list) {
920
+ let res = _route_match_one(topic, route);
921
+ if (undefined !== res) {
922
+ yield res;} } } }
923
+
924
+
925
+ function _route_match_one(topic, {keys, pattern, tgt}) {
926
+ let match = '/' !== topic[0]
927
+ ? pattern.exec('/'+topic)
928
+ : pattern.exec(topic);
929
+
930
+ if (null === match) {
931
+ return}
932
+
933
+ if (false === keys) {
934
+ let {groups} = match;
935
+ if (! groups) {
936
+ return [tgt]}
937
+
938
+ let params = {};
939
+ for (let k in groups) {
940
+ params[k] = groups[k];}
941
+
942
+ return [tgt, params]}
943
+
944
+ if (0 === keys.length) {
945
+ return [tgt]}
946
+
947
+ let params = {};
948
+ for (let i=0; i<keys.length; i++) {
949
+ params[ keys[i] ] = match[1+i];}
950
+ return [tgt, params]}
951
+
952
+
953
+ function _route_remove(all_route_lists, query) {
954
+ let match = route => route===query || route.tgt===query || route.key===query;
955
+ for (let lst of all_route_lists) {
956
+ let i = lst.findIndex(match);
957
+ if (0 <= i) {return !! lst.splice(i,1)} }
958
+ return false}
959
+
774
960
  /*
775
961
  export function decode_varint_loop(u8, i=0) {
776
962
  let i0 = i
@@ -869,8 +1055,6 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
869
1055
  }
870
1056
  }
871
1057
 
872
- Object.freeze({ao_done: true});
873
-
874
1058
  function ao_defer_ctx(as_res = (...args) => args) {
875
1059
  let y,n,_pset = (a,b) => { y=a, n=b; };
876
1060
  return p =>(
@@ -954,138 +1138,13 @@ function _ping_interval(send_ping) {
954
1138
  if (td) {
955
1139
  tid = setInterval(send_ping, 1000 * td);
956
1140
 
957
-
958
-
1141
+
1142
+
959
1143
 
960
- // ensure the interval allows the NodeJS event loop to exit
961
- tid.unref?.();
1144
+ // ensure the interval allows the NodeJS event loop to exit
1145
+ tid.unref?.();
962
1146
  return true} }) }
963
1147
 
964
- function parse(str, loose) {
965
- if (str instanceof RegExp) return { keys:false, pattern:str };
966
- var c, o, tmp, ext, keys=[], pattern='', arr = str.split('/');
967
- arr[0] || arr.shift();
968
-
969
- while (tmp = arr.shift()) {
970
- c = tmp[0];
971
- if (c === '*') {
972
- keys.push('wild');
973
- pattern += '/(.*)';
974
- } else if (c === ':') {
975
- o = tmp.indexOf('?', 1);
976
- ext = tmp.indexOf('.', 1);
977
- keys.push( tmp.substring(1, !!~o ? o : !!~ext ? ext : tmp.length) );
978
- pattern += !!~o && !~ext ? '(?:/([^/]+?))?' : '/([^/]+?)';
979
- if (!!~ext) pattern += (!!~o ? '?' : '') + '\\' + tmp.substring(ext);
980
- } else {
981
- pattern += '/' + tmp;
982
- }
983
- }
984
-
985
- return {
986
- keys: keys,
987
- pattern: new RegExp('^' + pattern + (loose ? '(?=$|\/)' : '\/?$'), 'i')
988
- };
989
- }
990
-
991
- // Use [regexparam][] for url-like topic parsing
992
-
993
- function _ignore(pkt, params, ctx) {ctx.done = true;}
994
-
995
- function _mqtt_topic_router() {
996
- let pri_lsts = [[],[]], rm = Symbol();
997
- let find = topic => _mqtt_routes_iter(pri_lsts, topic);
998
-
999
- return {find,
1000
-
1001
- add(topic_route, ...args) {
1002
- let fn = args.pop();
1003
- let priority = args.pop();
1004
-
1005
- if ('function' !== typeof fn) {
1006
- if (false === fn) {
1007
- fn = _ignore;}
1008
- else throw new TypeError()}
1009
-
1010
- let rte = parse(
1011
- topic_route.replace(/[+#]$/, '*'));
1012
-
1013
- rte.key = topic_route;
1014
- rte.tgt = fn;
1015
- pri_lsts[priority ? 0 : 1].push(rte);
1016
- return this}
1017
-
1018
- , remove(topic_route, priority) {
1019
- let lst = pri_lsts[priority ? 0 : 1];
1020
- return _mqtt_route_remove([lst], topic_route)}
1021
-
1022
- , clear(priority) {
1023
- pri_lsts[priority ? 0 : 1] = [];
1024
- if (null == priority) {
1025
- pri_lsts[1] = [];} }
1026
-
1027
- , async invoke(pkt, ctx) {
1028
- ctx.idx = 0;
1029
- ctx.rm = rm;
1030
-
1031
- for (let [fn, params] of find(pkt.topic)) {
1032
- let res = await fn(pkt, params, ctx);
1033
-
1034
- if (rm === res) {
1035
- _mqtt_route_remove(pri_lsts, fn);}
1036
-
1037
- if (ctx.done) {
1038
- break}
1039
- else ctx.idx++;}
1040
-
1041
- let {pkt_id, qos} = pkt;
1042
- if (1 === qos) {
1043
- await ctx.mqtt._send('puback', {pkt_id});} } } }
1044
-
1045
-
1046
- function * _mqtt_routes_iter(all_route_lists, topic) {
1047
- for (let route_list of all_route_lists) {
1048
- for (let route of route_list) {
1049
- let res = _mqtt_route_match_one(topic, route);
1050
- if (undefined !== res) {
1051
- yield res;} } } }
1052
-
1053
-
1054
- function _mqtt_route_match_one(topic, {keys, pattern, tgt}) {
1055
- let match = '/' !== topic[0]
1056
- ? pattern.exec('/'+topic)
1057
- : pattern.exec(topic);
1058
-
1059
- if (null === match) {
1060
- return}
1061
-
1062
- if (false === keys) {
1063
- let {groups} = match;
1064
- if (! groups) {
1065
- return [tgt]}
1066
-
1067
- let params = {};
1068
- for (let k in groups) {
1069
- params[k] = groups[k];}
1070
-
1071
- return [tgt, params]}
1072
-
1073
- if (0 === keys.length) {
1074
- return [tgt]}
1075
-
1076
- let params = {};
1077
- for (let i=0; i<keys.length; i++) {
1078
- params[ keys[i] ] = match[1+i];}
1079
- return [tgt, params]}
1080
-
1081
-
1082
- function _mqtt_route_remove(all_route_lists, query) {
1083
- let match = route => route===query || route.tgt===query || route.key===query;
1084
- for (let lst of all_route_lists) {
1085
- let i = lst.findIndex(match);
1086
- if (0 <= i) {return !! lst.splice(i,1)} }
1087
- return false}
1088
-
1089
1148
  const _mqtt_cmdid_dispatch ={
1090
1149
  create(target) {
1091
1150
  return {__proto__: this, target, hashbelt: [new Map()]} }
@@ -1154,8 +1213,7 @@ const _mqtt_cmdid_dispatch ={
1154
1213
  let fn = target[`mqtt_${pkt.type}`]
1155
1214
  || target.mqtt_pkt;
1156
1215
 
1157
- if (undefined !== fn) {
1158
- await fn.call(target, pkt, ctx);} } })()) };
1216
+ await fn?.call(target, pkt, ctx);} })()) };
1159
1217
 
1160
1218
  function _mqtt_dispatch(opt, target) {
1161
1219
  let _disp_ = _mqtt_cmdid_dispatch.create(target);
@@ -1236,53 +1294,15 @@ class MQTTBase {
1236
1294
 
1237
1295
 
1238
1296
  // alias: sub
1239
- subscribe(pkt, ex) {
1240
- pkt = _as_topics(pkt, ex);
1297
+ subscribe(pkt, ex, topic_prefix) {
1298
+ pkt = _as_topics(pkt, ex, topic_prefix);
1241
1299
  return this._send('subscribe', pkt, pkt)}
1242
- _sub_chain(topic, ex) {
1243
- let res = this.subscribe([[ topic ]], ex);
1244
- let subs = this.subs ||(this.subs = new Map());
1245
- subs.set((res.topic = topic), (subs.last = res));
1246
- return this }// fluent api -- return this and track side effects
1247
1300
 
1248
1301
  // alias: unsub
1249
- unsubscribe(pkt, ex) {
1250
- pkt = _as_topics(pkt, ex);
1302
+ unsubscribe(pkt, ex, topic_prefix) {
1303
+ pkt = _as_topics(pkt, ex, topic_prefix);
1251
1304
  return this._send('unsubscribe', pkt, pkt)}
1252
1305
 
1253
- get on_topic() {return this.router.add}
1254
-
1255
- // alias: sub_topic
1256
- subscribe_topic(topic_route, ...args) {
1257
- this.router.add(topic_route, true, args.pop() );// handler
1258
- let topic = this.topic_for(topic_route);
1259
- return this._sub_chain(topic, args.pop() ) }// ex
1260
-
1261
- // alias: unsub_topic
1262
- unsubscribe_topic(topic_route) {
1263
- this.router.remove(topic_route, true);
1264
- let topic = this.topic_for(topic_route);
1265
- return this.unsubscribe([[ topic ]]) }
1266
-
1267
- // alias: shared_sub
1268
- shared_subscribe(group, topic_route, ...args) {
1269
- this.router.add(topic_route, true, args.pop() );// handler
1270
- let topic = this.topic_for(topic_route);
1271
- if (null != group) {
1272
- topic = `$share/${group}/${topic}`;}
1273
- return this._sub_chain(topic, args.pop() ) }// ex
1274
-
1275
- // alias: shared_unsub
1276
- shared_unsubscribe(group, topic_route) {
1277
- this.router.remove(topic_route, true);
1278
- let topic = this.topic_for(topic_route);
1279
- if (null != group) {
1280
- topic = `$share/${group}/${topic}`;}
1281
- return this.unsubscribe([[ topic ]]) }
1282
-
1283
- topic_for(topic_route) {
1284
- return topic_route.replace(/[:*].*$/, '#')}
1285
-
1286
1306
 
1287
1307
  // alias: pub
1288
1308
  publish(pkt, pub_opt) {return _pub(this, pkt, pub_opt)}
@@ -1308,9 +1328,9 @@ class MQTTBase {
1308
1328
  if (undefined === cid) {
1309
1329
  this.client_id = cid = (
1310
1330
 
1311
-
1331
+
1312
1332
 
1313
- this.new_client_id(parts)
1333
+ this.new_client_id(parts)
1314
1334
  );}
1315
1335
 
1316
1336
  return cid}
@@ -1319,66 +1339,85 @@ class MQTTBase {
1319
1339
  return [parts[0], Math.random().toString(36).slice(2), parts[1]].join('')}
1320
1340
 
1321
1341
 
1322
-
1323
-
1324
-
1325
-
1326
-
1327
-
1328
-
1342
+
1343
+
1344
+
1345
+
1346
+
1347
+
1348
+
1329
1349
 
1330
1350
 
1331
1351
  // Internal API
1332
1352
 
1333
1353
  /* async _send(type, pkt) -- provided by _conn_ and transport */
1334
1354
 
1335
- _init_router(opt) {
1336
- return this.router = _mqtt_topic_router()}
1337
-
1338
1355
  _init_dispatch(opt) {
1356
+ this.constructor?._once_();
1357
+ let router = this.router =
1358
+ this._init_router?.(opt, this);
1359
+
1339
1360
  let tgt ={
1340
1361
  __proto__: opt.on_mqtt_type || {}
1341
- , router: this._init_router(opt, this)};
1362
+ , router};
1342
1363
 
1343
- tgt.mqtt_publish ||= tgt.router.invoke;
1344
- return _mqtt_dispatch(this, tgt)} }
1364
+ tgt.mqtt_publish ||= router?.invoke;
1365
+ return _mqtt_dispatch(this, tgt)}
1345
1366
 
1367
+ static _aliases() {
1368
+ return ' pub:publish sub:subscribe unsub:unsubscribe '}
1346
1369
 
1347
- {
1348
- let p = MQTTBase.prototype;
1349
- Object.assign(p,{
1350
- MQTTError
1351
- , pub: p.publish
1352
- , sub: p.subscribe
1353
- , unsub: p.unsubscribe
1354
- , sub_topic: p.subscribe_topic
1355
- , unsub_topic: p.unsubscribe_topic
1356
- , shared_sub: p.shared_subscribe
1357
- , shared_unsub: p.shared_unsubscribe} );
1370
+ static _once_(self=this) {
1371
+ self._once_ = _=>0;
1372
+ self.MQTTError = MQTTError;
1373
+ let p = self.prototype;
1374
+ for (let alias of self._aliases().split(/\s+/)) {
1375
+ alias = alias.split(':');
1376
+ let fn = alias[1] && p[alias[1]];
1377
+ if (fn) {p[alias[0]] = fn;} } } }
1358
1378
 
1359
- /*
1360
- p.on_mqtt_type = {
1361
- mqtt_auth(pkt, ctx) ::
1362
- mqtt_connect(pkt, ctx) ::
1363
- mqtt_connack(pkt, ctx) ::
1364
- mqtt_disconnect(pkt, ctx) ::
1365
-
1366
- mqtt_publish(pkt, ctx)
1367
- mqtt_subscribe(pkt, ctx) ::
1368
- mqtt_unsubscribe(pkt, ctx) ::
1369
-
1370
- mqtt_pingreq(pkt, ctx) ::
1371
- mqtt_pingresp(pkt, ctx) ::
1372
- }
1373
- */}
1379
+
1380
+ /*
1381
+ on_mqtt_type = {
1382
+ mqtt_auth(pkt, ctx) ::
1383
+ mqtt_connect(pkt, ctx) ::
1384
+ mqtt_connack(pkt, ctx) ::
1385
+ mqtt_disconnect(pkt, ctx) ::
1386
+
1387
+ mqtt_publish(pkt, ctx)
1388
+ mqtt_subscribe(pkt, ctx) ::
1389
+ mqtt_unsubscribe(pkt, ctx) ::
1390
+
1391
+ mqtt_pingreq(pkt, ctx) ::
1392
+ mqtt_pingresp(pkt, ctx) ::
1393
+ }
1394
+ */
1374
1395
 
1375
1396
 
1376
- function _as_topics(pkt, ex) {
1377
- if ('string' === typeof pkt) {
1378
- return {topics:[pkt], ... ex}}
1379
- if (pkt[Symbol.iterator]) {
1380
- return {topics:[... pkt], ... ex}}
1381
- return ex ? {...pkt, ...ex} : pkt}
1397
+ const _prefix_topics = (topic_prefix, iterable) =>
1398
+ Array.from(iterable, value =>(
1399
+ value.trim // string
1400
+ ? _prefix_topics(topic_prefix, value)
1401
+ : topic_prefix + value) );
1402
+
1403
+ function _as_topics(pkt, ex, topic_prefix) {
1404
+ if (ex?.trim) {// string
1405
+ topic_prefix = ex;
1406
+ ex = null;}
1407
+
1408
+ pkt =(
1409
+ pkt.trim // string
1410
+ ? {topics:[pkt], ... ex}
1411
+ : pkt[Symbol.iterator]
1412
+ ? {topics:[... pkt], ... ex}
1413
+ : ex ? {...pkt, ...ex}
1414
+ : pkt);
1415
+
1416
+ if (topic_prefix) {
1417
+ // particularly useful with shared queues, e.g.
1418
+ // topic_prefix = '$share/some-queue-name/'
1419
+ pkt.topics = _prefix_topics(topic_prefix, pkt.topics);}
1420
+ return pkt}
1382
1421
 
1383
1422
 
1384
1423
  async function _pub(self, pkt, pub_opt) {
@@ -1450,7 +1489,7 @@ class MQTTCore extends MQTTBase {
1450
1489
  return this}
1451
1490
 
1452
1491
  //log_conn(evt, arg, err_arg) ::
1453
- //console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1492
+ // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1454
1493
 
1455
1494
  on_live(client, is_reconnect) {
1456
1495
  if (is_reconnect) {
@@ -1492,30 +1531,55 @@ class MQTTCore extends MQTTBase {
1492
1531
  return this}
1493
1532
 
1494
1533
 
1495
-
1496
-
1497
-
1498
-
1499
-
1500
-
1501
-
1502
-
1503
-
1504
-
1505
1534
 
1535
+
1536
+
1537
+
1538
+
1539
+
1540
+
1541
+
1506
1542
 
1543
+
1544
+
1545
+
1546
+
1547
+
1507
1548
 
1549
+
1550
+
1551
+
1552
+
1553
+
1508
1554
 
1555
+
1556
+
1557
+
1558
+
1509
1559
 
1560
+
1561
+
1510
1562
 
1511
1563
 
1512
- with_tcp(port, hostname) {
1513
- if (!Number.isFinite(port)) {
1514
- ({port, hostname} = new URL(port));}
1564
+ with_tcp(...opt) {
1565
+ opt = this._conn_opt(opt);
1566
+ console.log({opt});
1567
+ return this._use_conn (() =>
1568
+ this.with_stream(
1569
+ connect(opt)) ) }
1570
+
1571
+ with_tls(...opt) {
1572
+ opt = this._conn_opt(opt);
1573
+ return this._use_conn (() =>
1574
+ this.with_stream(
1575
+ connect$1(opt)) ) }
1515
1576
 
1516
- return this._use_conn (() =>
1517
- this.with_stream(
1518
- connect(port, hostname)) ) }
1577
+ _conn_opt([a0, a1, a2]) {
1578
+ // (port, hostname, options) or (url, options)
1579
+ if (Number.isFinite(a0)) {
1580
+ return {...a2, port: a0, host: a1}}
1581
+ a0 = new URL(a0);
1582
+ return {...a1, port: a0.port, host: a0.hostname}}
1519
1583
 
1520
1584
 
1521
1585
  with_stream(read_stream, write_stream) {
@@ -1561,13 +1625,15 @@ class MQTTCore extends MQTTBase {
1561
1625
 
1562
1626
  return this} }
1563
1627
 
1564
- var version = "0.3.1";
1628
+ const version = '0.4.0';
1565
1629
 
1566
1630
  const MQTTClient_v4 = /* #__PURE__ */
1567
- MQTTCore.mqtt_ctx(4, mqtt_opts_v5);
1631
+ with_topic_path_router(
1632
+ MQTTCore.mqtt_ctx(4, mqtt_opts_v5) );
1568
1633
 
1569
1634
  const MQTTClient_v5 = /* #__PURE__ */
1570
- MQTTCore.mqtt_ctx(5, mqtt_opts_v5);
1635
+ with_topic_path_router(
1636
+ MQTTCore.mqtt_ctx(5, mqtt_opts_v5) );
1571
1637
 
1572
1638
  const mqtt_v4 = opt =>
1573
1639
  new MQTTClient_v4(opt);