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());
@@ -873,88 +883,15 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
873
883
  }
874
884
  }
875
885
 
876
- function ao_defer_ctx(as_res = (...args) => args) {
877
- let y,n,_pset = (a,b) => { y=a, n=b; };
878
- return p =>(
879
- p = new Promise(_pset)
880
- , as_res(p, y, n)) }
881
-
882
- const ao_defer_v = /* #__PURE__ */
883
- ao_defer_ctx();
884
-
885
- Promise.resolve({type:'init'});
886
-
887
- function _mqtt_conn(client, [on_mqtt, pkt_future]) {
888
- let _q_init = ao_defer_v(), _q_ready = ao_defer_v();
889
- let _send_ready = async (...args) => (await _q_ready[0])(...args);
890
- let _send_mqtt_pkt, _has_connected;
891
- client._send = _send_ready;
892
-
893
- return {
894
- async when_ready() {await _q_ready[0];}
895
-
896
- , ping: _ping_interval (() =>_send_mqtt_pkt?.('pingreq'))
897
-
898
- , reset(err) {
899
- if (! _send_mqtt_pkt) {return}
900
-
901
- if (err) {
902
- _q_init[2](err);}
903
-
904
- _send_mqtt_pkt = null;
905
- _q_init = ao_defer_v();
906
- client._send = _send_ready;
907
-
908
- // call client.on_conn_reset in next promise microtask
909
- client.conn_emit('on_disconnect', false===err, err);}
910
-
911
- , async send_connect(... args) {
912
- if (! _send_mqtt_pkt) {
913
- await _q_init[0]; }// _send_mqtt_pkt is set before fulfilled
914
-
915
- // await connack response
916
- let res = await _send_mqtt_pkt(...args);
917
- if (0 == res[0].reason) {
918
- _has_connected = true;
919
- // resolve _q_ready[0] with _send_mqtt_pkt closure
920
- _q_ready[1](client._send = _send_mqtt_pkt);
921
- _q_ready = ao_defer_v();
922
- client.conn_emit('on_ready');}
923
-
924
- return res}
925
-
926
- , is_set: (() =>!! _send_mqtt_pkt)
927
- , set(mqtt_ctx, send_u8_pkt) {
928
- if (_send_mqtt_pkt) {
929
- throw new Error('Already connected')}
930
-
931
- mqtt_ctx = mqtt_ctx.mqtt_stream();
932
- let sess_ctx = {mqtt: client};
933
- let on_mqtt_chunk = u8_buf =>
934
- on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
935
-
936
- _send_mqtt_pkt = async (type, pkt, key) => {
937
- let res = undefined !== key
938
- ? pkt_future(key) : true;
939
-
940
- await send_u8_pkt(
941
- mqtt_ctx.encode_pkt(type, pkt));
942
-
943
- return res};
944
-
945
- _q_init[1](_send_mqtt_pkt); // resolve _q_init with _send_mqtt_pkt closure
946
-
947
- // call client.on_live in next promise microtask
948
- client.conn_emit('on_live', _has_connected);
949
- return on_mqtt_chunk} } }
950
-
886
+ const _isfn = v => typeof v === 'function';
887
+ const _isstr = v => typeof v === 'string';
951
888
 
952
- function _ping_interval(send_ping) {
889
+ function _interval(fn_callback) {
953
890
  let tid;
954
891
  return (( td ) => {
955
892
  tid = clearInterval(tid);
956
893
  if (td) {
957
- tid = setInterval(send_ping, 1000 * td);
894
+ tid = setInterval(fn_callback, 1000 * td);
958
895
 
959
896
 
960
897
 
@@ -963,148 +900,108 @@ function _ping_interval(send_ping) {
963
900
  tid.unref?.();
964
901
  return true} }) }
965
902
 
966
- const _mqtt_cmdid_dispatch ={
967
- create(target) {
968
- return {__proto__: this, target, hashbelt: [new Map()]} }
903
+ async function _mqtt_cmd_evt(target, answer, pkt, ctx) {
904
+ /* target : on_mqtt_type = {
905
+ mqtt_pkt(pkt, ctx) {}, // generic
906
+
907
+ mqtt_auth(pkt, ctx) {},
908
+ mqtt_connect(pkt, ctx) {},
909
+ mqtt_connack(pkt, ctx) {},
910
+ mqtt_disconnect(pkt, ctx) {},
911
+
912
+ mqtt_publish(pkt, ctx) {},
913
+ mqtt_subscribe(pkt, ctx) {},
914
+ mqtt_unsubscribe(pkt, ctx) {},
915
+
916
+ mqtt_pingreq(pkt, ctx) {},
917
+ mqtt_pingresp(pkt, ctx) {},
918
+ } */
919
+
920
+ let pkt_fn = target[`mqtt_${pkt.type}`] || target.mqtt_pkt;
921
+ await pkt_fn?.call(target, pkt, ctx);}
922
+
923
+ function _mqtt_cmd_type(target, answer, pkt, ctx) {
924
+ answer(pkt.type, pkt);
925
+ _mqtt_cmd_evt(target, answer, pkt, ctx);}
926
+
927
+ function _mqtt_cmd_id(target, answer, pkt) {
928
+ answer(pkt.pkt_id, pkt);}
929
+
930
+
931
+ const _mqtt_cmdids =[
932
+ _ => {} // 0x0 reserved
933
+ , _mqtt_cmd_evt // 0x1 connect
934
+ , _mqtt_cmd_type // 0x2 connack
935
+ , _mqtt_cmd_evt // 0x3 publish
936
+ , _mqtt_cmd_id // 0x4 puback
937
+ , _mqtt_cmd_id // 0x5 pubrec
938
+ , _mqtt_cmd_id // 0x6 pubrel
939
+ , _mqtt_cmd_id // 0x7 pubcomp
940
+ , _mqtt_cmd_evt // 0x8 subscribe
941
+ , _mqtt_cmd_id // 0x9 suback
942
+ , _mqtt_cmd_evt // 0xa unsubscribe
943
+ , _mqtt_cmd_id // 0xb unsuback
944
+ , _mqtt_cmd_type // 0xc pingreq
945
+ , _mqtt_cmd_type // 0xd pingresp
946
+ , _mqtt_cmd_evt // 0xe disconnect
947
+ , _mqtt_cmd_type ];// 0xf auth
969
948
 
970
- , bind_pkt_future(_pkt_id=100) {
971
- let {hashbelt} = this;
949
+ function _mqtt_dispatch(opt, target) {
950
+ let hashbelt=[], rotate_ts=0;
951
+ // default rotate at 1s across 5 buckets
952
+ let { td: rotate_td=1000, n: rotate_n=5 } = opt?.rotate || {};
953
+
954
+ // Promise / future scaffolding
955
+ let _pkt_id=100, _ftr_key; // use _ftr_key to reuse _by_key closure
956
+ let _ftr_by_key = fn_answer => hashbelt[0].set(_ftr_key, fn_answer);
957
+
958
+ on_mqtt([]); // init hashbelt and rotate_ts
959
+ return [on_mqtt, pkt_future]
972
960
 
973
- let _tmp_; // use _tmp_ to reuse _by_key closure
974
- let _by_key = answer_monad =>
975
- hashbelt[0].set(_tmp_, answer_monad);
976
961
 
977
- return (( pkt_or_key ) => {
978
- if ('string' === typeof pkt_or_key) {
979
- _tmp_ = pkt_or_key;}
980
- else {
981
- _pkt_id = (_pkt_id + 1) & 0xffff;
982
- _tmp_ = pkt_or_key.pkt_id = _pkt_id;}
962
+ function pkt_future(pkt_or_key) {
963
+ if (! _isstr(pkt_or_key)) {
964
+ _pkt_id = (_pkt_id + 1) & 0xffff; // 16-bit unsigned short
965
+ _ftr_key = pkt_or_key.pkt_id = _pkt_id;}
966
+ else _ftr_key = pkt_or_key;
983
967
 
984
- return new Promise(_by_key)}) }
968
+ return new Promise(_ftr_by_key)}
985
969
 
986
- , answer(key, pkt) {
987
- for (let map of this.hashbelt) {
988
- let answer_monad = map.get(key);
989
- if (undefined !== answer_monad) {
970
+ function answer(key, pkt) {
971
+ for (let map of hashbelt) {
972
+ let fn_answer = map.get(key);
973
+ if (fn_answer) {
990
974
  map.delete(key);
991
975
 
992
- answer_monad([pkt, /*err*/]); // option/maybe monad
976
+ fn_answer([pkt, /*err*/]); // option/maybe monad
993
977
  return true} }
994
978
  return false}
995
979
 
996
- , rotate_belt(n) {
997
- let {hashbelt} = this;
998
- hashbelt.unshift(new Map());
999
- for (let old of hashbelt.splice(n || 5)) {
1000
- for (let answer_monad of old.values()) {
1001
- answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
1002
-
1003
- , cmdids: ((() => {
1004
- return [
1005
- (() =>{} )// 0x0 reserved
1006
- , by_evt // 0x1 connect
1007
- , by_type // 0x2 connack
1008
- , by_evt // 0x3 publish
1009
- , by_id // 0x4 puback
1010
- , by_id // 0x5 pubrec
1011
- , by_id // 0x6 pubrel
1012
- , by_id // 0x7 pubcomp
1013
- , by_evt // 0x8 subscribe
1014
- , by_id // 0x9 suback
1015
- , by_evt // 0xa unsubscribe
1016
- , by_id // 0xb unsuback
1017
- , by_type // 0xc pingreq
1018
- , by_type // 0xd pingresp
1019
- , by_evt // 0xe disconnect
1020
- , by_type ]// 0xf auth
1021
-
1022
-
1023
- function by_id(disp, pkt) {
1024
- disp.answer(pkt.pkt_id, pkt); }
1025
-
1026
- function by_type(disp, pkt, ctx) {
1027
- disp.answer(pkt.type, pkt);
1028
- by_evt(disp, pkt, ctx);}
1029
-
1030
- async function by_evt({target}, pkt, ctx) {
1031
- let fn = target[`mqtt_${pkt.type}`]
1032
- || target.mqtt_pkt;
1033
-
1034
- await fn?.call(target, pkt, ctx);} })()) };
1035
-
1036
- /*
1037
- on_mqtt_type = {
1038
- mqtt_auth(pkt, ctx) ::
1039
- mqtt_connect(pkt, ctx) ::
1040
- mqtt_connack(pkt, ctx) ::
1041
- mqtt_disconnect(pkt, ctx) ::
1042
-
1043
- mqtt_publish(pkt, ctx)
1044
- mqtt_subscribe(pkt, ctx) ::
1045
- mqtt_unsubscribe(pkt, ctx) ::
1046
-
1047
- mqtt_pingreq(pkt, ctx) ::
1048
- mqtt_pingresp(pkt, ctx) ::
1049
- }
1050
- */
1051
-
1052
- function _mqtt_dispatch(opt, target) {
1053
- let _disp_ = _mqtt_cmdid_dispatch.create(target);
1054
- let { cmdids } = _disp_;
1055
-
1056
- // default rotate at 1s across 5 buckets
1057
- let { td: rotate_td=1000, n: rotate_n=5 } =
1058
- opt && opt.rotate || {};
1059
-
1060
- let rotate_ts = rotate_td + Date.now();
1061
-
1062
- return [on_mqtt,
1063
- _disp_.bind_pkt_future()]
1064
-
1065
980
  function on_mqtt(pkt_list, ctx) {
1066
981
  for (let pkt of pkt_list) {
1067
- cmdids[pkt.id](_disp_, pkt, ctx); }
982
+ _mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
1068
983
 
1069
- if (Date.now() > rotate_ts) {
1070
- _disp_.rotate_belt(rotate_n);
1071
- rotate_ts = rotate_td + Date.now();} } }
984
+ // rotate after rotate_ts
985
+ let now = Date.now();
986
+ if (now > rotate_ts) {
987
+ rotate_ts = rotate_td + now;
988
+ hashbelt.unshift(new Map());
989
+ while (hashbelt.length > rotate_n) {
990
+ for (let fn_answer of hashbelt.pop().values()) {
991
+ fn_answer([/*pkt*/, 'expired']); } } } } }// option/maybe monad
1072
992
 
1073
993
  class MQTTError extends Error {
1074
994
  constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
995
+ // use hex-encoded reasons to match MQTT spec documentation
1075
996
  super(`[0x${reason.toString(16)}] ${reason.reason}`);
1076
997
  this.mqtt_pkt = mqtt_pkt;
1077
998
  this.reason = reason;} }
1078
999
 
1079
1000
  class MQTTBase {
1080
- constructor(opt={}) {
1081
- this.with(opt);
1082
- this._conn_ = _mqtt_conn(this,
1083
- this._init_dispatch(opt, this)); }
1084
-
1085
- with(fns_ns) {
1086
- for (let [k,v] of Object.entries(fns_ns)) {
1087
- if ('function' === typeof v) {this[k] = v;} }
1088
- return this}
1089
-
1090
- async conn_emit(evt, arg, err_arg) {
1091
- this.log_conn?.(evt, arg, err_arg);
1092
- try {
1093
- let fn_evt = this[await evt]; // microtask break using `await evt`
1094
- if (fn_evt) {
1095
- await fn_evt.call(this, this, arg, err_arg);}
1096
- else if (err_arg) {throw err_arg} }
1097
- catch (err) {
1098
- this.on_error(err, evt);} }
1099
-
1100
- on_error(err, evt) {
1101
- console.warn('[[u8-mqtt error: %s]]', evt, err); }
1102
-
1103
1001
  // Handshaking Packets
1104
-
1105
1002
  async connect(pkt={}) {
1106
1003
  let cid = pkt.client_id;
1107
- if ('string' !== typeof cid) {
1004
+ if (! _isstr(cid)) {
1108
1005
  // see init_client_id implementation in core.jsy
1109
1006
  pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
1110
1007
  this.client_id = cid;
@@ -1112,23 +1009,20 @@ class MQTTBase {
1112
1009
  if (null == pkt.keep_alive) {
1113
1010
  pkt.keep_alive = 60;}
1114
1011
 
1115
- let res = await this._conn_
1116
- .send_connect('connect', pkt, 'connack');
1117
-
1118
- if (0 != res[0].reason) {
1119
- throw new this.MQTTError(res[0])}
1120
-
1121
- // TODO: merge with server's keep_alive frequency
1122
- this._conn_.ping(pkt.keep_alive);
1123
- return res}
1012
+ let response = await this._send0('connect', pkt, 'connack');
1013
+ if (0 != response[0].reason) {// compare to 0 to coerce to number
1014
+ throw new this.MQTTError(response[0])}
1015
+ return this.conn.on_conn(pkt, response)}
1124
1016
 
1125
1017
  async disconnect(pkt={}) {
1126
- let res = await this._send('disconnect', pkt);
1127
- this._conn_.reset(false);
1128
- return res}
1018
+ let response = await this._send0('disconnect', pkt);
1019
+ return this.conn.on_dis(pkt, response)}
1129
1020
 
1130
- auth(pkt={}) {
1131
- return this._send('auth', pkt, 'auth')}
1021
+ async auth(pkt={}) {
1022
+ let response = await this._send0('auth', pkt, 'auth');
1023
+ if (response[0].reason) {
1024
+ throw new this.MQTTError(response[0])}
1025
+ return this.conn.on_auth(pkt, response)}
1132
1026
 
1133
1027
  ping() {return this._send('pingreq', null, 'pingresp')}
1134
1028
  puback({pkt_id}) {return this._send('puback', {pkt_id})}
@@ -1165,20 +1059,18 @@ class MQTTBase {
1165
1059
  // alias: publish -- because 'pub' is shorter for semantic aliases above
1166
1060
  async pub(pkt, pub_opt) {
1167
1061
  if (undefined === pkt.payload) {
1168
- if ('function' === typeof pub_opt) {
1062
+ if (_isfn(pub_opt)) {
1063
+ // pub_opt as a function is fn_encode value
1169
1064
  pub_opt = {fn_encode: pub_opt};}
1170
1065
 
1171
- let {msg} = pkt;
1172
- switch (typeof msg) {
1173
- case 'function':
1174
- pub_opt = {...pub_opt, fn_encode: msg};
1175
- // flow into 'undefined' case
1176
- case 'undefined':
1177
- // return a single-value closure to publish packets
1178
- return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1066
+ let msg = pkt.msg, fn_encode = pub_opt?.fn_encode;
1067
+ if (null == msg || _isfn(msg)) {
1068
+ // when msg is a function, return closure using fn_encode
1069
+ if (msg) {pub_opt = {...pub_opt, fn_encode: msg};}
1070
+ // return a single-value closure to publish packets
1071
+ return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1179
1072
 
1180
1073
  // Encode payload from msg; fn_encode allows alternative to JSON.stringify
1181
- let {fn_encode} = pub_opt || {};
1182
1074
  pkt.payload = fn_encode
1183
1075
  ? await fn_encode(msg)
1184
1076
  : JSON.stringify(msg);}
@@ -1190,31 +1082,31 @@ class MQTTBase {
1190
1082
  pkt = pub_opt.xform(pkt) || pkt;} }
1191
1083
 
1192
1084
  return this._send('publish', pkt,
1193
- pkt.qos ? pkt : void 0 ) }// key
1085
+ pkt.qos ? pkt : null ) }// key
1194
1086
 
1195
1087
 
1196
1088
  // Internal API
1197
1089
 
1198
- /* async _send(type, pkt) -- provided by _conn_ and transport */
1090
+ /* async _send0(type, pkt) -- provided by conn and transport */
1091
+ /* async _send(type, pkt) -- provided by conn and transport */
1199
1092
 
1200
1093
  _init_dispatch(opt) {
1201
1094
  this.constructor?._once_();
1202
1095
  let target ={__proto__: opt.on_mqtt_type};
1203
1096
  target.mqtt_publish ||=
1204
1097
  this._init_router?.(opt, this, target);
1205
- return _mqtt_dispatch(this, target)}
1098
+ return _mqtt_dispatch(opt, target)}
1206
1099
 
1207
1100
  static _aliases() {
1208
1101
  return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
1209
1102
 
1210
- static _once_(self=this) {
1211
- self._once_ = _=>0;
1212
- let p = self.prototype;
1103
+ static _once_(klass=this) {
1104
+ klass._once_ = _=>0;
1105
+ var alias, name, p = klass.prototype;
1213
1106
  p.MQTTError = MQTTError;
1214
- for (let alias of self._aliases().split(/\s+/)) {
1215
- alias = alias.split(':');
1216
- let fn = alias[1] && p[alias[1]];
1217
- if (fn) {p[alias[0]] = fn;} } } }
1107
+ for (alias of klass._aliases().split(/\s+/)) {
1108
+ [alias, name] = alias.split(':');
1109
+ p[alias] = p[name];} } }
1218
1110
 
1219
1111
 
1220
1112
  function _as_topics(pkt, ex, topic_prefix) {
@@ -1239,37 +1131,167 @@ function _as_topics(pkt, ex, topic_prefix) {
1239
1131
  pkt.topics = pkt.topics.map(_prefix_topics);}
1240
1132
  return pkt}
1241
1133
 
1242
- const pkt_api = {
1243
- utf8(u8) { return new TextDecoder('utf-8').decode(u8 || this.payload ) },
1244
- json(u8) { return JSON.parse( this.utf8(u8) || null ) },
1245
- text(u8) { return this.utf8(u8) },
1246
- };
1134
+ const _defer_obj = o =>(
1135
+ o.p = new Promise((a,e) => { o.a=a; o.e=e; })
1136
+ , o);
1137
+
1138
+ function _dfn_reset(client, attr, fn_after) {
1139
+ // a resetable deferred for a function
1140
+ let self = {set}, afn = async (...args) => (await self.p)(...args);
1141
+ return set()
1142
+
1143
+ function set() {
1144
+ if (afn !== client[attr]) {
1145
+ _defer_obj(self).p.then(fn_after, _=>0);
1146
+ client[attr] = afn;}
1147
+ return self} }
1148
+
1149
+ function _mqtt_conn(opt, client, [on_mqtt, pkt_future]) {
1150
+ let _abort;
1151
+ let _dfn_send0 = _dfn_reset(client, '_send0', // client._send0 getter/setter
1152
+ _=> client.conn_emit('on_live', conn.has_connected));
1153
+ let _dfn_ready = _dfn_reset(client, '_send', // client._send getter/setter
1154
+ _=> client.conn_emit('on_ready'));
1155
+ let _keep_alive_ival = _interval (() =>client._send0('pingreq') );// resettable interval for keep_alive ping
1156
+
1157
+ let conn = Object.create({
1158
+ ping: (td=conn.keep_alive) => _keep_alive_ival(td)
1159
+
1160
+ , on_conn(pkt, response) {
1161
+ conn.has_connected = true;
1162
+ conn.keep_alive = opt.keep_alive || response[0].props?.server_keep_alive || pkt.keep_alive;
1163
+ client.conn_emit('on_conn');
1164
+ return opt.use_auth
1165
+ ? response // wait on enhanced authentication step
1166
+ : conn.on_auth(null, response) }// otherwise, connect is also auth
1167
+
1168
+ , on_auth(pkt, response) {
1169
+ _dfn_ready.a(_dfn_send0.p);
1170
+ if (0 != opt.keep_alive) {
1171
+ conn.ping();}
1172
+ client.conn_emit('on_auth', !pkt);
1173
+ return response}
1174
+
1175
+ , on_dis(pkt, response) {
1176
+ conn.reset(false);
1177
+ return response}
1178
+
1179
+ , reset(err) {
1180
+ if (err) {
1181
+ _dfn_send0.e(err); }// send error to uses of _send0 (connect, auth)
1182
+ _abort.e(err); // abort in-progress connections
1183
+
1184
+ delete conn.is_set;
1185
+ conn.ready = handshake();
1186
+ client.conn_emit('on_disconnect', false===err, err);}
1187
+
1188
+ , abort() {
1189
+ _dfn_ready.e(err); // abort all messages awaiting ready state
1190
+ return conn.reset(err)}
1191
+
1192
+ , async setup(gate, send_u8_pkt, init_msg_loop) {
1193
+ if (conn.is_set) {
1194
+ throw new Error() }// already in-progress
1195
+
1196
+ conn.is_set = true;
1197
+ await gate;
1198
+
1199
+ // setup send/recv MQTT parsing context
1200
+ let mqtt_ctx = client.mqtt_ctx.mqtt_stream();
1201
+
1202
+ {// setup inbound message loop
1203
+ let sess_ctx = {mqtt: client}; // mutable session context
1204
+ let on_mqtt_chunk = u8 => on_mqtt(mqtt_ctx.decode(u8), sess_ctx);
1205
+ init_msg_loop(on_mqtt_chunk, conn);}
1206
+
1207
+ // setup outbound message path and transport connection
1208
+ send_u8_pkt = await send_u8_pkt;
1209
+ _dfn_send0.a(
1210
+ async (type, pkt, key) => {
1211
+ let res = undefined !== key
1212
+ ? pkt_future(key) : true;
1213
+
1214
+ await send_u8_pkt(
1215
+ mqtt_ctx.encode_pkt(type, pkt));
1216
+ return res} ); } });
1217
+
1218
+ conn.ready = handshake();
1219
+ return conn
1220
+
1221
+ async function handshake() {
1222
+ _abort = _defer_obj({});
1223
+
1224
+ _keep_alive_ival(0); // clearInterval on keep alive ping
1225
+ _dfn_send0.set(); // reset client._send0 if necessary
1226
+ _dfn_ready.set(); // reset client._send if necessary
1227
+
1228
+ try {
1229
+ // set client._send0 as passtrhough after transport connection
1230
+ client._send0 = await Promise.race([_dfn_send0.p, _abort.p]);
1231
+
1232
+ // set client._send as passtrhough after ready
1233
+ client._send = await Promise.race([_dfn_ready.p, _abort.p]);
1234
+ return true}
1235
+ catch (err) {
1236
+ return false} } }
1237
+
1238
+ const pkt_api ={
1239
+ utf8(u8) {return new TextDecoder('utf-8').decode(u8 || this.payload )}
1240
+ , json(u8) {return JSON.parse( this.utf8(u8) || null )}
1241
+ , text(u8) {return this.utf8(u8)} };
1242
+
1243
+ const opt_default ={
1244
+ sess_stg: globalThis.sessionStorage};
1247
1245
 
1248
1246
  class MQTTCore extends MQTTBase {
1247
+ constructor(opt) {
1248
+ opt = {...opt_default, ...opt};
1249
+ super();
1250
+ this.with(opt);
1251
+ // settings for MQTTCore
1252
+ this.sess_stg = opt.sess_stg;
1253
+ // setup connection and dispatch
1254
+ this.conn = _mqtt_conn(opt, this,
1255
+ this._init_dispatch(opt)); }
1256
+
1257
+ with(fns_ns) {
1258
+ for (let [k,v] of Object.entries(fns_ns)) {
1259
+ if (_isfn(v)) {this[k] = v;} }
1260
+ return this}
1261
+
1262
+
1249
1263
  static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
1250
- let self = class extends this {};
1251
- self.prototype.mqtt_ctx =
1264
+ let klass = class extends this {};
1265
+ klass.prototype.mqtt_ctx =
1252
1266
  mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
1253
- return self}
1267
+ return klass}
1268
+
1269
+
1270
+ async conn_emit(evt, arg, err_arg) {
1271
+ this.log_conn?.(evt, arg, err_arg);
1272
+ try {
1273
+ let fn_evt = this[await evt]; // microtask break using `await evt`
1274
+ if (fn_evt) {
1275
+ await fn_evt.call(this, this, arg, err_arg);}
1276
+ else if (err_arg) {throw err_arg} }
1277
+ catch (err) {
1278
+ this.on_error(err, evt);} }
1279
+
1280
+ on_error(err, evt) {
1281
+ console.warn('[[u8-mqtt error: %s]]', evt, err); }
1282
+ log_conn(evt, arg, err_arg) {
1283
+ console.info('[[u8-mqtt conn: %s]]', evt, arg, err_arg); }
1254
1284
 
1255
1285
 
1256
1286
  // automatic Client Id for connect()
1257
1287
  init_client_id(parts=['u8-mqtt--','']) {
1258
- let sess_stg=this.sess_stg;
1288
+ let sess_stg = this.sess_stg;
1259
1289
  let key, cid = sess_stg?.getItem(key=parts.join(' '));
1260
1290
  if (! cid) {
1261
1291
  cid = parts.join(Math.random().toString(36).slice(2));
1262
1292
  sess_stg?.setItem(key, cid);}
1263
1293
  return cid}
1264
1294
 
1265
- get sess_stg() {return globalThis.sessionStorage}
1266
-
1267
-
1268
- //on_error(err, evt) ::
1269
- // console.warn @ '[[u8-mqtt error: %s]]', evt, err
1270
-
1271
- //log_conn(evt, arg, err_arg) ::
1272
- // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1273
1295
 
1274
1296
  on_live(client, is_reconnect) {
1275
1297
  if (is_reconnect) {
@@ -1295,19 +1317,16 @@ class MQTTCore extends MQTTBase {
1295
1317
  return new Promise(done => setTimeout(done, ms)) }
1296
1318
 
1297
1319
  with_async_iter(async_iter, write_u8_pkt) {
1298
- let on_mqtt_chunk = this._conn_.set(
1299
- this.mqtt_ctx,
1300
- write_u8_pkt);
1301
-
1302
- this._msg_loop = ((async () => {
1303
- try {
1304
- async_iter = await async_iter;
1305
- for await (let chunk of async_iter)
1306
- on_mqtt_chunk(chunk);
1307
- this._conn_.reset();}
1308
- catch (err) {
1309
- this._conn_.reset(err);} })());
1310
-
1320
+ this.conn.setup(async_iter,
1321
+ write_u8_pkt,
1322
+ async (on_mqtt_chunk, conn) => {
1323
+ try {
1324
+ async_iter = await async_iter;
1325
+ for await (let chunk of async_iter)
1326
+ on_mqtt_chunk(chunk);
1327
+ conn.reset();}
1328
+ catch (err) {
1329
+ conn.reset(err);} } );
1311
1330
  return this}
1312
1331
 
1313
1332
 
@@ -1378,33 +1397,30 @@ class MQTTCore extends MQTTBase {
1378
1397
 
1379
1398
  websock.binaryType = 'arraybuffer';
1380
1399
 
1381
- let ready, {readyState} = websock;
1400
+ let ws_ready, readyState = websock.readyState;
1382
1401
  if (1 !== readyState) {
1383
1402
  if (0 !== readyState) {
1384
- throw new Error('Invalid WebSocket readyState') }
1385
-
1386
- ready = new Promise(fn => websock.onopen = fn); }
1403
+ throw new Error('WS readyState') }
1387
1404
 
1405
+ ws_ready = new Promise(ready => websock.onopen = ready); }
1388
1406
 
1389
- let {_conn_} = this;
1390
- let on_mqtt_chunk = _conn_.set(
1391
- this.mqtt_ctx,
1392
- async u8_pkt =>(
1393
- await ready
1394
- , websock.send(u8_pkt)) );
1407
+ this.conn.setup(ws_ready,
1408
+ u8_pkt => websock.send(u8_pkt),
1409
+ (on_mqtt_chunk, conn) => {
1410
+ websock.onmessage = evt =>(
1411
+ on_mqtt_chunk(new Uint8Array(evt.data)) );
1395
1412
 
1396
- websock.onmessage = evt =>(on_mqtt_chunk(new Uint8Array(evt.data)));
1397
- websock.onclose = evt => {
1398
- if (! evt.wasClean) {
1399
- var err = new Error('websocket connection close');
1400
- err.code = evt.code;
1401
- err.reason = evt.reason;}
1413
+ websock.onclose = evt => {
1414
+ if (! evt.wasClean) {
1415
+ var err = new Error('websocket close');
1416
+ err.code = evt.code;
1417
+ err.reason = evt.reason;}
1402
1418
 
1403
- _conn_.reset(err);};
1419
+ conn.reset(err);}; } );
1404
1420
 
1405
1421
  return this} }
1406
1422
 
1407
- const version = '0.5.3-node';
1423
+ const version = '0.6.1-node';
1408
1424
 
1409
1425
  const MQTTClient_v4 = /* #__PURE__ */
1410
1426
  MQTTCore.mqtt_ctx(4, mqtt_opts_v5);