u8-mqtt 0.5.2 → 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 (116) hide show
  1. package/README.md +23 -6
  2. package/cjs/basic-v4.cjs +293 -284
  3. package/cjs/basic-v4.cjs.map +1 -1
  4. package/cjs/basic-v5.cjs +307 -288
  5. package/cjs/basic-v5.cjs.map +1 -1
  6. package/cjs/full-v4.cjs +1538 -0
  7. package/cjs/full-v4.cjs.map +1 -0
  8. package/cjs/full-v5.cjs +1812 -0
  9. package/cjs/full-v5.cjs.map +1 -0
  10. package/cjs/index.cjs +320 -302
  11. package/cjs/index.cjs.map +1 -1
  12. package/cjs/v4.cjs +305 -296
  13. package/cjs/v4.cjs.map +1 -1
  14. package/cjs/v5.cjs +319 -300
  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 +35 -63
  21. package/code/core.jsy +78 -56
  22. package/code/full-v4.js +20 -0
  23. package/code/full-v5.js +29 -0
  24. package/code/router_path.jsy +2 -1
  25. package/esm/basic-v4.js +293 -284
  26. package/esm/basic-v4.js.map +1 -1
  27. package/esm/basic-v5.js +307 -288
  28. package/esm/basic-v5.js.map +1 -1
  29. package/esm/deno/basic-v4.js +297 -288
  30. package/esm/deno/basic-v4.js.map +1 -1
  31. package/esm/deno/basic-v5.js +311 -292
  32. package/esm/deno/basic-v5.js.map +1 -1
  33. package/esm/deno/full-v4.js +1526 -0
  34. package/esm/deno/full-v4.js.map +1 -0
  35. package/esm/deno/full-v5.js +1798 -0
  36. package/esm/deno/full-v5.js.map +1 -0
  37. package/esm/deno/index.js +324 -305
  38. package/esm/deno/index.js.map +1 -1
  39. package/esm/deno/v4.js +309 -300
  40. package/esm/deno/v4.js.map +1 -1
  41. package/esm/deno/v5.js +323 -304
  42. package/esm/deno/v5.js.map +1 -1
  43. package/esm/full-v4.js +1526 -0
  44. package/esm/full-v4.js.map +1 -0
  45. package/esm/full-v5.js +1798 -0
  46. package/esm/full-v5.js.map +1 -0
  47. package/esm/index.js +320 -301
  48. package/esm/index.js.map +1 -1
  49. package/esm/node/basic-v4.js +293 -284
  50. package/esm/node/basic-v4.js.map +1 -1
  51. package/esm/node/basic-v4.mjs +293 -284
  52. package/esm/node/basic-v4.mjs.map +1 -1
  53. package/esm/node/basic-v5.js +307 -288
  54. package/esm/node/basic-v5.js.map +1 -1
  55. package/esm/node/basic-v5.mjs +307 -288
  56. package/esm/node/basic-v5.mjs.map +1 -1
  57. package/esm/node/full-v4.js +1529 -0
  58. package/esm/node/full-v4.js.map +1 -0
  59. package/esm/node/full-v4.mjs +1529 -0
  60. package/esm/node/full-v4.mjs.map +1 -0
  61. package/esm/node/full-v5.js +1801 -0
  62. package/esm/node/full-v5.js.map +1 -0
  63. package/esm/node/full-v5.mjs +1801 -0
  64. package/esm/node/full-v5.mjs.map +1 -0
  65. package/esm/node/index.js +320 -301
  66. package/esm/node/index.js.map +1 -1
  67. package/esm/node/index.mjs +320 -301
  68. package/esm/node/index.mjs.map +1 -1
  69. package/esm/node/v4.js +305 -296
  70. package/esm/node/v4.js.map +1 -1
  71. package/esm/node/v4.mjs +305 -296
  72. package/esm/node/v4.mjs.map +1 -1
  73. package/esm/node/v5.js +319 -300
  74. package/esm/node/v5.js.map +1 -1
  75. package/esm/node/v5.mjs +319 -300
  76. package/esm/node/v5.mjs.map +1 -1
  77. package/esm/v4.js +305 -296
  78. package/esm/v4.js.map +1 -1
  79. package/esm/v5.js +319 -300
  80. package/esm/v5.js.map +1 -1
  81. package/esm/web/basic-v4.js +293 -284
  82. package/esm/web/basic-v4.js.map +1 -1
  83. package/esm/web/basic-v4.min.js +1 -1
  84. package/esm/web/basic-v4.min.js.br +0 -0
  85. package/esm/web/basic-v4.min.js.gz +0 -0
  86. package/esm/web/basic-v5.js +307 -288
  87. package/esm/web/basic-v5.js.map +1 -1
  88. package/esm/web/basic-v5.min.js +1 -1
  89. package/esm/web/basic-v5.min.js.br +0 -0
  90. package/esm/web/basic-v5.min.js.gz +0 -0
  91. package/esm/web/full-v4.js +1526 -0
  92. package/esm/web/full-v4.js.map +1 -0
  93. package/esm/web/full-v4.min.js +1 -0
  94. package/esm/web/full-v4.min.js.br +0 -0
  95. package/esm/web/full-v4.min.js.gz +0 -0
  96. package/esm/web/full-v5.js +1798 -0
  97. package/esm/web/full-v5.js.map +1 -0
  98. package/esm/web/full-v5.min.js +1 -0
  99. package/esm/web/full-v5.min.js.br +0 -0
  100. package/esm/web/full-v5.min.js.gz +0 -0
  101. package/esm/web/index.js +320 -301
  102. package/esm/web/index.js.map +1 -1
  103. package/esm/web/index.min.js +1 -1
  104. package/esm/web/index.min.js.br +0 -0
  105. package/esm/web/index.min.js.gz +0 -0
  106. package/esm/web/v4.js +305 -296
  107. package/esm/web/v4.js.map +1 -1
  108. package/esm/web/v4.min.js +1 -1
  109. package/esm/web/v4.min.js.br +0 -0
  110. package/esm/web/v4.min.js.gz +0 -0
  111. package/esm/web/v5.js +319 -300
  112. package/esm/web/v5.js.map +1 -1
  113. package/esm/web/v5.min.js +1 -1
  114. package/esm/web/v5.min.js.br +0 -0
  115. package/esm/web/v5.min.js.gz +0 -0
  116. package/package.json +7 -8
package/esm/deno/index.js CHANGED
@@ -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());
@@ -796,6 +806,23 @@ function parse(str, loose) {
796
806
  };
797
807
  }
798
808
 
809
+ const _isfn = v => typeof v === 'function';
810
+ const _isstr = v => typeof v === 'string';
811
+
812
+ function _interval(fn_callback) {
813
+ let tid;
814
+ return (( td ) => {
815
+ tid = clearInterval(tid);
816
+ if (td) {
817
+ tid = setInterval(fn_callback, 1000 * td);
818
+
819
+ // ensure the interval allows the Deno event loop to exit
820
+ Deno.unrefTimer(tid);
821
+
822
+
823
+
824
+ return true} }) }
825
+
799
826
  /*
800
827
  class AbstractTopicRouter ::
801
828
  async invoke(pkt, ctx) ::
@@ -886,7 +913,7 @@ function mqtt_topic_path_router() {
886
913
  let fn = args.pop();
887
914
  let priority = args.pop();
888
915
 
889
- if ('function' !== typeof fn) {
916
+ if (! _isfn(fn)) {
890
917
  if (fn) {throw new TypeError()}
891
918
  fn = _ignore;}
892
919
 
@@ -1036,6 +1063,9 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
1036
1063
  return _encode_by_type[type]( mqtt_level, pkt ) },
1037
1064
 
1038
1065
  decode_pkt(b0, u8_body) {
1066
+ if (b0.map) // Uint8Array in first arg
1067
+ return mqtt_raw_dispatch(this)(b0)[0]
1068
+
1039
1069
  let fn_decode = _decode_by_id[b0>>>4] || _decode_by_id[0];
1040
1070
  return fn_decode?.({__proto__: this.pkt_ctx, b0}, u8_body) },
1041
1071
 
@@ -1048,262 +1078,129 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
1048
1078
  }
1049
1079
  }
1050
1080
 
1051
- function ao_defer_ctx(as_res = (...args) => args) {
1052
- let y,n,_pset = (a,b) => { y=a, n=b; };
1053
- return p =>(
1054
- p = new Promise(_pset)
1055
- , as_res(p, y, n)) }
1056
-
1057
- const ao_defer_v = /* #__PURE__ */
1058
- ao_defer_ctx();
1059
-
1060
- Promise.resolve({type:'init'});
1061
-
1062
- function _mqtt_conn(client, [on_mqtt, pkt_future]) {
1063
- let _q_init = ao_defer_v(), _q_ready = ao_defer_v();
1064
- let _send_ready = async (...args) => (await _q_ready[0])(...args);
1065
- let _send_mqtt_pkt, _has_connected;
1066
- client._send = _send_ready;
1067
-
1068
- return {
1069
- async when_ready() {await _q_ready[0];}
1070
-
1071
- , ping: _ping_interval (() =>_send_mqtt_pkt?.('pingreq'))
1072
-
1073
- , reset(err) {
1074
- if (! _send_mqtt_pkt) {return}
1075
-
1076
- if (err) {
1077
- _q_init[2](err);}
1078
-
1079
- _send_mqtt_pkt = null;
1080
- _q_init = ao_defer_v();
1081
- client._send = _send_ready;
1082
-
1083
- // call client.on_conn_reset in next promise microtask
1084
- client.conn_emit('on_disconnect', false===err, err);}
1085
-
1086
- , async send_connect(... args) {
1087
- if (! _send_mqtt_pkt) {
1088
- await _q_init[0]; }// _send_mqtt_pkt is set before fulfilled
1089
-
1090
- // await connack response
1091
- let res = await _send_mqtt_pkt(...args);
1092
- if (0 == res[0].reason) {
1093
- _has_connected = true;
1094
- // resolve _q_ready[0] with _send_mqtt_pkt closure
1095
- _q_ready[1](client._send = _send_mqtt_pkt);
1096
- _q_ready = ao_defer_v();
1097
- client.conn_emit('on_ready');}
1098
-
1099
- return res}
1100
-
1101
- , is_set: (() =>!! _send_mqtt_pkt)
1102
- , set(mqtt_ctx, send_u8_pkt) {
1103
- if (_send_mqtt_pkt) {
1104
- throw new Error('Already connected')}
1081
+ async function _mqtt_cmd_evt(target, answer, pkt, ctx) {
1082
+ /* target : on_mqtt_type = {
1083
+ mqtt_pkt(pkt, ctx) {}, // generic
1084
+
1085
+ mqtt_auth(pkt, ctx) {},
1086
+ mqtt_connect(pkt, ctx) {},
1087
+ mqtt_connack(pkt, ctx) {},
1088
+ mqtt_disconnect(pkt, ctx) {},
1089
+
1090
+ mqtt_publish(pkt, ctx) {},
1091
+ mqtt_subscribe(pkt, ctx) {},
1092
+ mqtt_unsubscribe(pkt, ctx) {},
1093
+
1094
+ mqtt_pingreq(pkt, ctx) {},
1095
+ mqtt_pingresp(pkt, ctx) {},
1096
+ } */
1097
+
1098
+ let pkt_fn = target[`mqtt_${pkt.type}`] || target.mqtt_pkt;
1099
+ await pkt_fn?.call(target, pkt, ctx);}
1100
+
1101
+ function _mqtt_cmd_type(target, answer, pkt, ctx) {
1102
+ answer(pkt.type, pkt);
1103
+ _mqtt_cmd_evt(target, answer, pkt, ctx);}
1104
+
1105
+ function _mqtt_cmd_id(target, answer, pkt) {
1106
+ answer(pkt.pkt_id, pkt);}
1107
+
1108
+
1109
+ const _mqtt_cmdids =[
1110
+ _ => {} // 0x0 reserved
1111
+ , _mqtt_cmd_evt // 0x1 connect
1112
+ , _mqtt_cmd_type // 0x2 connack
1113
+ , _mqtt_cmd_evt // 0x3 publish
1114
+ , _mqtt_cmd_id // 0x4 puback
1115
+ , _mqtt_cmd_id // 0x5 pubrec
1116
+ , _mqtt_cmd_id // 0x6 pubrel
1117
+ , _mqtt_cmd_id // 0x7 pubcomp
1118
+ , _mqtt_cmd_evt // 0x8 subscribe
1119
+ , _mqtt_cmd_id // 0x9 suback
1120
+ , _mqtt_cmd_evt // 0xa unsubscribe
1121
+ , _mqtt_cmd_id // 0xb unsuback
1122
+ , _mqtt_cmd_type // 0xc pingreq
1123
+ , _mqtt_cmd_type // 0xd pingresp
1124
+ , _mqtt_cmd_evt // 0xe disconnect
1125
+ , _mqtt_cmd_type ];// 0xf auth
1105
1126
 
1106
- mqtt_ctx = mqtt_ctx.mqtt_stream();
1107
- let sess_ctx = {mqtt: client};
1108
- let on_mqtt_chunk = u8_buf =>
1109
- on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
1110
-
1111
- _send_mqtt_pkt = async (type, pkt, key) => {
1112
- let res = undefined !== key
1113
- ? pkt_future(key) : true;
1114
-
1115
- await send_u8_pkt(
1116
- mqtt_ctx.encode_pkt(type, pkt));
1117
-
1118
- return res};
1119
-
1120
- _q_init[1](_send_mqtt_pkt); // resolve _q_init with _send_mqtt_pkt closure
1121
-
1122
- // call client.on_live in next promise microtask
1123
- client.conn_emit('on_live', _has_connected);
1124
- return on_mqtt_chunk} } }
1125
-
1126
-
1127
- function _ping_interval(send_ping) {
1128
- let tid;
1129
- return (( td ) => {
1130
- tid = clearInterval(tid);
1131
- if (td) {
1132
- tid = setInterval(send_ping, 1000 * td);
1133
-
1134
- // ensure the interval allows the Deno event loop to exit
1135
- Deno.unrefTimer(tid);
1136
-
1137
-
1138
-
1139
- return true} }) }
1127
+ function _mqtt_dispatch(opt, target) {
1128
+ let hashbelt=[], rotate_ts=0;
1129
+ // default rotate at 1s across 5 buckets
1130
+ let { td: rotate_td=1000, n: rotate_n=5 } = opt?.rotate || {};
1140
1131
 
1141
- const _mqtt_cmdid_dispatch ={
1142
- create(target) {
1143
- return {__proto__: this, target, hashbelt: [new Map()]} }
1132
+ // Promise / future scaffolding
1133
+ let _pkt_id=100, _ftr_key; // use _ftr_key to reuse _by_key closure
1134
+ let _ftr_by_key = fn_answer => hashbelt[0].set(_ftr_key, fn_answer);
1144
1135
 
1145
- , bind_pkt_future(_pkt_id=100) {
1146
- let {hashbelt} = this;
1136
+ on_mqtt([]); // init hashbelt and rotate_ts
1137
+ return [on_mqtt, pkt_future]
1147
1138
 
1148
- let _tmp_; // use _tmp_ to reuse _by_key closure
1149
- let _by_key = answer_monad =>
1150
- hashbelt[0].set(_tmp_, answer_monad);
1151
1139
 
1152
- return (( pkt_or_key ) => {
1153
- if ('string' === typeof pkt_or_key) {
1154
- _tmp_ = pkt_or_key;}
1155
- else {
1156
- _pkt_id = (_pkt_id + 1) & 0xffff;
1157
- _tmp_ = pkt_or_key.pkt_id = _pkt_id;}
1140
+ function pkt_future(pkt_or_key) {
1141
+ if (! _isstr(pkt_or_key)) {
1142
+ _pkt_id = (_pkt_id + 1) & 0xffff; // 16-bit unsigned short
1143
+ _ftr_key = pkt_or_key.pkt_id = _pkt_id;}
1144
+ else _ftr_key = pkt_or_key;
1158
1145
 
1159
- return new Promise(_by_key)}) }
1146
+ return new Promise(_ftr_by_key)}
1160
1147
 
1161
- , answer(key, pkt) {
1162
- for (let map of this.hashbelt) {
1163
- let answer_monad = map.get(key);
1164
- if (undefined !== answer_monad) {
1148
+ function answer(key, pkt) {
1149
+ for (let map of hashbelt) {
1150
+ let fn_answer = map.get(key);
1151
+ if (fn_answer) {
1165
1152
  map.delete(key);
1166
1153
 
1167
- answer_monad([pkt, /*err*/]); // option/maybe monad
1154
+ fn_answer([pkt, /*err*/]); // option/maybe monad
1168
1155
  return true} }
1169
1156
  return false}
1170
1157
 
1171
- , rotate_belt(n) {
1172
- let {hashbelt} = this;
1173
- hashbelt.unshift(new Map());
1174
- for (let old of hashbelt.splice(n || 5)) {
1175
- for (let answer_monad of old.values()) {
1176
- answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
1177
-
1178
- , cmdids: ((() => {
1179
- return [
1180
- (() =>{} )// 0x0 reserved
1181
- , by_evt // 0x1 connect
1182
- , by_type // 0x2 connack
1183
- , by_evt // 0x3 publish
1184
- , by_id // 0x4 puback
1185
- , by_id // 0x5 pubrec
1186
- , by_id // 0x6 pubrel
1187
- , by_id // 0x7 pubcomp
1188
- , by_evt // 0x8 subscribe
1189
- , by_id // 0x9 suback
1190
- , by_evt // 0xa unsubscribe
1191
- , by_id // 0xb unsuback
1192
- , by_type // 0xc pingreq
1193
- , by_type // 0xd pingresp
1194
- , by_evt // 0xe disconnect
1195
- , by_type ]// 0xf auth
1196
-
1197
-
1198
- function by_id(disp, pkt) {
1199
- disp.answer(pkt.pkt_id, pkt); }
1200
-
1201
- function by_type(disp, pkt, ctx) {
1202
- disp.answer(pkt.type, pkt);
1203
- by_evt(disp, pkt, ctx);}
1204
-
1205
- async function by_evt({target}, pkt, ctx) {
1206
- let fn = target[`mqtt_${pkt.type}`]
1207
- || target.mqtt_pkt;
1208
-
1209
- await fn?.call(target, pkt, ctx);} })()) };
1210
-
1211
- /*
1212
- on_mqtt_type = {
1213
- mqtt_auth(pkt, ctx) ::
1214
- mqtt_connect(pkt, ctx) ::
1215
- mqtt_connack(pkt, ctx) ::
1216
- mqtt_disconnect(pkt, ctx) ::
1217
-
1218
- mqtt_publish(pkt, ctx)
1219
- mqtt_subscribe(pkt, ctx) ::
1220
- mqtt_unsubscribe(pkt, ctx) ::
1221
-
1222
- mqtt_pingreq(pkt, ctx) ::
1223
- mqtt_pingresp(pkt, ctx) ::
1224
- }
1225
- */
1226
-
1227
- function _mqtt_dispatch(opt, target) {
1228
- let _disp_ = _mqtt_cmdid_dispatch.create(target);
1229
- let { cmdids } = _disp_;
1230
-
1231
- // default rotate at 1s across 5 buckets
1232
- let { td: rotate_td=1000, n: rotate_n=5 } =
1233
- opt && opt.rotate || {};
1234
-
1235
- let rotate_ts = rotate_td + Date.now();
1236
-
1237
- return [on_mqtt,
1238
- _disp_.bind_pkt_future()]
1239
-
1240
1158
  function on_mqtt(pkt_list, ctx) {
1241
1159
  for (let pkt of pkt_list) {
1242
- cmdids[pkt.id](_disp_, pkt, ctx); }
1160
+ _mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
1243
1161
 
1244
- if (Date.now() > rotate_ts) {
1245
- _disp_.rotate_belt(rotate_n);
1246
- rotate_ts = rotate_td + Date.now();} } }
1162
+ // rotate after rotate_ts
1163
+ let now = Date.now();
1164
+ if (now > rotate_ts) {
1165
+ rotate_ts = rotate_td + now;
1166
+ hashbelt.unshift(new Map());
1167
+ while (hashbelt.length > rotate_n) {
1168
+ for (let fn_answer of hashbelt.pop().values()) {
1169
+ fn_answer([/*pkt*/, 'expired']); } } } } }// option/maybe monad
1247
1170
 
1248
1171
  class MQTTError extends Error {
1249
1172
  constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
1173
+ // use hex-encoded reasons to match MQTT spec documentation
1250
1174
  super(`[0x${reason.toString(16)}] ${reason.reason}`);
1251
1175
  this.mqtt_pkt = mqtt_pkt;
1252
1176
  this.reason = reason;} }
1253
1177
 
1254
1178
  class MQTTBase {
1255
- constructor(opt={}) {
1256
- this.with(opt);
1257
- this._conn_ = _mqtt_conn(this,
1258
- this._init_dispatch(opt, this)); }
1259
-
1260
- with(fns_ns) {
1261
- for (let [k,v] of Object.entries(fns_ns)) {
1262
- if ('function' === typeof v) {this[k] = v;} }
1263
- return this}
1264
-
1265
- async conn_emit(evt, arg, err_arg) {
1266
- this.log_conn?.(evt, arg, err_arg);
1267
- try {
1268
- let fn_evt = this[await evt]; // microtask break using `await evt`
1269
- if (fn_evt) {
1270
- await fn_evt.call(this, this, arg, err_arg);}
1271
- else if (err_arg) {throw err_arg} }
1272
- catch (err) {
1273
- this.on_error(err, evt);} }
1274
-
1275
- on_error(err, evt) {
1276
- console.warn('[[u8-mqtt error: %s]]', evt, err); }
1277
-
1278
1179
  // Handshaking Packets
1279
-
1280
1180
  async connect(pkt={}) {
1281
- let cid = pkt.client_id || this.client_id;
1282
- if ('string' !== typeof cid) {
1181
+ let cid = pkt.client_id;
1182
+ if (! _isstr(cid)) {
1283
1183
  // see init_client_id implementation in core.jsy
1284
- pkt.client_id = cid = this.init_client_id(cid);}
1184
+ pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
1285
1185
  this.client_id = cid;
1286
1186
 
1287
1187
  if (null == pkt.keep_alive) {
1288
1188
  pkt.keep_alive = 60;}
1289
1189
 
1290
- let res = await this._conn_
1291
- .send_connect('connect', pkt, 'connack');
1292
-
1293
- if (0 != res[0].reason) {
1294
- throw new this.MQTTError(res[0])}
1295
-
1296
- // TODO: merge with server's keep_alive frequency
1297
- this._conn_.ping(pkt.keep_alive);
1298
- return res}
1190
+ let response = await this._send0('connect', pkt, 'connack');
1191
+ if (0 != response[0].reason) {// compare to 0 to coerce to number
1192
+ throw new this.MQTTError(response[0])}
1193
+ return this.conn.on_conn(pkt, response)}
1299
1194
 
1300
1195
  async disconnect(pkt={}) {
1301
- let res = await this._send('disconnect', pkt);
1302
- this._conn_.reset(false);
1303
- return res}
1196
+ let response = await this._send0('disconnect', pkt);
1197
+ return this.conn.on_dis(pkt, response)}
1304
1198
 
1305
- auth(pkt={}) {
1306
- return this._send('auth', pkt, 'auth')}
1199
+ async auth(pkt={}) {
1200
+ let response = await this._send0('auth', pkt, 'auth');
1201
+ if (response[0].reason) {
1202
+ throw new this.MQTTError(response[0])}
1203
+ return this.conn.on_auth(pkt, response)}
1307
1204
 
1308
1205
  ping() {return this._send('pingreq', null, 'pingresp')}
1309
1206
  puback({pkt_id}) {return this._send('puback', {pkt_id})}
@@ -1340,20 +1237,18 @@ class MQTTBase {
1340
1237
  // alias: publish -- because 'pub' is shorter for semantic aliases above
1341
1238
  async pub(pkt, pub_opt) {
1342
1239
  if (undefined === pkt.payload) {
1343
- if ('function' === typeof pub_opt) {
1240
+ if (_isfn(pub_opt)) {
1241
+ // pub_opt as a function is fn_encode value
1344
1242
  pub_opt = {fn_encode: pub_opt};}
1345
1243
 
1346
- let {msg} = pkt;
1347
- switch (typeof msg) {
1348
- case 'function':
1349
- pub_opt = {...pub_opt, fn_encode: msg};
1350
- // flow into 'undefined' case
1351
- case 'undefined':
1352
- // return a single-value closure to publish packets
1353
- return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1244
+ let msg = pkt.msg, fn_encode = pub_opt?.fn_encode;
1245
+ if (null == msg || _isfn(msg)) {
1246
+ // when msg is a function, return closure using fn_encode
1247
+ if (msg) {pub_opt = {...pub_opt, fn_encode: msg};}
1248
+ // return a single-value closure to publish packets
1249
+ return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
1354
1250
 
1355
1251
  // Encode payload from msg; fn_encode allows alternative to JSON.stringify
1356
- let {fn_encode} = pub_opt || {};
1357
1252
  pkt.payload = fn_encode
1358
1253
  ? await fn_encode(msg)
1359
1254
  : JSON.stringify(msg);}
@@ -1365,31 +1260,31 @@ class MQTTBase {
1365
1260
  pkt = pub_opt.xform(pkt) || pkt;} }
1366
1261
 
1367
1262
  return this._send('publish', pkt,
1368
- pkt.qos ? pkt : void 0 ) }// key
1263
+ pkt.qos ? pkt : null ) }// key
1369
1264
 
1370
1265
 
1371
1266
  // Internal API
1372
1267
 
1373
- /* async _send(type, pkt) -- provided by _conn_ and transport */
1268
+ /* async _send0(type, pkt) -- provided by conn and transport */
1269
+ /* async _send(type, pkt) -- provided by conn and transport */
1374
1270
 
1375
1271
  _init_dispatch(opt) {
1376
1272
  this.constructor?._once_();
1377
1273
  let target ={__proto__: opt.on_mqtt_type};
1378
1274
  target.mqtt_publish ||=
1379
1275
  this._init_router?.(opt, this, target);
1380
- return _mqtt_dispatch(this, target)}
1276
+ return _mqtt_dispatch(opt, target)}
1381
1277
 
1382
1278
  static _aliases() {
1383
1279
  return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
1384
1280
 
1385
- static _once_(self=this) {
1386
- self._once_ = _=>0;
1387
- let p = self.prototype;
1281
+ static _once_(klass=this) {
1282
+ klass._once_ = _=>0;
1283
+ var alias, name, p = klass.prototype;
1388
1284
  p.MQTTError = MQTTError;
1389
- for (let alias of self._aliases().split(/\s+/)) {
1390
- alias = alias.split(':');
1391
- let fn = alias[1] && p[alias[1]];
1392
- if (fn) {p[alias[0]] = fn;} } } }
1285
+ for (alias of klass._aliases().split(/\s+/)) {
1286
+ [alias, name] = alias.split(':');
1287
+ p[alias] = p[name];} } }
1393
1288
 
1394
1289
 
1395
1290
  function _as_topics(pkt, ex, topic_prefix) {
@@ -1414,43 +1309,177 @@ function _as_topics(pkt, ex, topic_prefix) {
1414
1309
  pkt.topics = pkt.topics.map(_prefix_topics);}
1415
1310
  return pkt}
1416
1311
 
1417
- const pkt_api = {
1418
- utf8(u8) { return new TextDecoder('utf-8').decode(u8 || this.payload ) },
1419
- json(u8) { return JSON.parse( this.utf8(u8) || null ) },
1420
- text(u8) { return this.utf8(u8) },
1421
- };
1312
+ const _defer_obj = o =>(
1313
+ o.p = new Promise((a,e) => { o.a=a; o.e=e; })
1314
+ , o);
1315
+
1316
+ function _dfn_reset(client, attr, fn_after) {
1317
+ // a resetable deferred for a function
1318
+ let self = {set}, afn = async (...args) => (await self.p)(...args);
1319
+ return set()
1320
+
1321
+ function set() {
1322
+ if (afn !== client[attr]) {
1323
+ _defer_obj(self).p.then(fn_after, _=>0);
1324
+ client[attr] = afn;}
1325
+ return self} }
1326
+
1327
+ function _mqtt_conn(opt, client, [on_mqtt, pkt_future]) {
1328
+ let _abort;
1329
+ let _dfn_send0 = _dfn_reset(client, '_send0', // client._send0 getter/setter
1330
+ _=> client.conn_emit('on_live', conn.has_connected));
1331
+ let _dfn_ready = _dfn_reset(client, '_send', // client._send getter/setter
1332
+ _=> client.conn_emit('on_ready'));
1333
+ let _keep_alive_ival = _interval (() =>client._send0('pingreq') );// resettable interval for keep_alive ping
1334
+
1335
+ let conn = Object.create({
1336
+ ping: (td=conn.keep_alive) => _keep_alive_ival(td)
1337
+
1338
+ , on_conn(pkt, response) {
1339
+ conn.has_connected = true;
1340
+ conn.keep_alive = opt.keep_alive || response[0].props?.server_keep_alive || pkt.keep_alive;
1341
+ client.conn_emit('on_conn');
1342
+ return opt.use_auth
1343
+ ? response // wait on enhanced authentication step
1344
+ : conn.on_auth(null, response) }// otherwise, connect is also auth
1345
+
1346
+ , on_auth(pkt, response) {
1347
+ _dfn_ready.a(_dfn_send0.p);
1348
+ if (0 != opt.keep_alive) {
1349
+ conn.ping();}
1350
+ client.conn_emit('on_auth', !pkt);
1351
+ return response}
1352
+
1353
+ , on_dis(pkt, response) {
1354
+ conn.reset(false);
1355
+ return response}
1356
+
1357
+ , reset(err) {
1358
+ if (err) {
1359
+ _dfn_send0.e(err); }// send error to uses of _send0 (connect, auth)
1360
+ _abort.e(err); // abort in-progress connections
1361
+
1362
+ delete conn.is_set;
1363
+ conn.ready = handshake();
1364
+ client.conn_emit('on_disconnect', false===err, err);}
1365
+
1366
+ , abort() {
1367
+ _dfn_ready.e(err); // abort all messages awaiting ready state
1368
+ return conn.reset(err)}
1369
+
1370
+ , async setup(gate, send_u8_pkt, init_msg_loop) {
1371
+ if (conn.is_set) {
1372
+ throw new Error() }// already in-progress
1373
+
1374
+ conn.is_set = true;
1375
+ await gate;
1376
+
1377
+ // setup send/recv MQTT parsing context
1378
+ let mqtt_ctx = client.mqtt_ctx.mqtt_stream();
1379
+
1380
+ {// setup inbound message loop
1381
+ let sess_ctx = {mqtt: client}; // mutable session context
1382
+ let on_mqtt_chunk = u8 => on_mqtt(mqtt_ctx.decode(u8), sess_ctx);
1383
+ init_msg_loop(on_mqtt_chunk, conn);}
1384
+
1385
+ // setup outbound message path and transport connection
1386
+ send_u8_pkt = await send_u8_pkt;
1387
+ _dfn_send0.a(
1388
+ async (type, pkt, key) => {
1389
+ let res = undefined !== key
1390
+ ? pkt_future(key) : true;
1391
+
1392
+ await send_u8_pkt(
1393
+ mqtt_ctx.encode_pkt(type, pkt));
1394
+ return res} ); } });
1395
+
1396
+ conn.ready = handshake();
1397
+ return conn
1398
+
1399
+ async function handshake() {
1400
+ _abort = _defer_obj({});
1401
+
1402
+ _keep_alive_ival(0); // clearInterval on keep alive ping
1403
+ _dfn_send0.set(); // reset client._send0 if necessary
1404
+ _dfn_ready.set(); // reset client._send if necessary
1405
+
1406
+ try {
1407
+ // set client._send0 as passtrhough after transport connection
1408
+ client._send0 = await Promise.race([_dfn_send0.p, _abort.p]);
1409
+
1410
+ // set client._send as passtrhough after ready
1411
+ client._send = await Promise.race([_dfn_ready.p, _abort.p]);
1412
+ return true}
1413
+ catch (err) {
1414
+ return false} } }
1415
+
1416
+ const pkt_api ={
1417
+ utf8(u8) {return new TextDecoder('utf-8').decode(u8 || this.payload )}
1418
+ , json(u8) {return JSON.parse( this.utf8(u8) || null )}
1419
+ , text(u8) {return this.utf8(u8)} };
1420
+
1421
+ const opt_default ={
1422
+ sess_stg: globalThis.sessionStorage};
1422
1423
 
1423
1424
  class MQTTCore extends MQTTBase {
1425
+ constructor(opt) {
1426
+ super();
1427
+ this.with(opt);
1428
+ opt ={...opt_default, ...opt};
1429
+ // settings for MQTTCore
1430
+ this.sess_stg = opt.sess_stg;
1431
+ // setup connection and dispatch
1432
+ this.conn = _mqtt_conn(opt, this,
1433
+ this._init_dispatch(opt)); }
1434
+
1435
+ with(fns_ns) {
1436
+ for (let [k,v] of Object.entries(fns_ns)) {
1437
+ if (_isfn(v)) {this[k] = v;} }
1438
+ return this}
1439
+
1440
+
1424
1441
  static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
1425
- let self = class extends this {};
1426
- self.prototype.mqtt_ctx =
1442
+ let klass = class extends this {};
1443
+ klass.prototype.mqtt_ctx =
1427
1444
  mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
1428
- return self}
1445
+ return klass}
1446
+
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
+ log_conn(evt, arg, err_arg) {
1461
+ console.info('[[u8-mqtt conn: %s]]', evt, arg, err_arg); }
1429
1462
 
1430
1463
 
1431
1464
  // automatic Client Id for connect()
1432
1465
  init_client_id(parts=['u8-mqtt--','']) {
1433
- let sess_stg=this.sess_stg;
1466
+ let sess_stg = this.sess_stg;
1434
1467
  let key, cid = sess_stg?.getItem(key=parts.join(' '));
1435
1468
  if (! cid) {
1436
1469
  cid = parts.join(Math.random().toString(36).slice(2));
1437
1470
  sess_stg?.setItem(key, cid);}
1438
1471
  return cid}
1439
1472
 
1440
- get sess_stg() {return globalThis.sessionStorage}
1441
-
1442
-
1443
- //on_error(err, evt) ::
1444
- // console.warn @ '[[u8-mqtt error: %s]]', evt, err
1445
-
1446
- //log_conn(evt, arg, err_arg) ::
1447
- // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1448
1473
 
1449
1474
  on_live(client, is_reconnect) {
1450
1475
  if (is_reconnect) {
1451
1476
  return client.connect()} }
1452
1477
 
1453
- //on_reconnect(client) ::
1478
+ // on_ready(client) ::
1479
+ // on_reconnect(client) ::
1480
+ on_disconnect(client, intentional) {
1481
+ if (! intentional) {
1482
+ return client.on_reconnect?.()} }
1454
1483
 
1455
1484
  _use_conn(fn_reconnect) {
1456
1485
  return (this.reconnect = fn_reconnect)?.()}
@@ -1462,27 +1491,20 @@ class MQTTCore extends MQTTBase {
1462
1491
  .then(this.reconnect)
1463
1492
  .then(opt.reconnect, opt.error);} }) }
1464
1493
 
1465
- on_disconnect(client, intentional) {
1466
- if (! intentional) {
1467
- return client.on_reconnect?.()} }
1468
-
1469
1494
  delay(ms) {
1470
1495
  return new Promise(done => setTimeout(done, ms)) }
1471
1496
 
1472
1497
  with_async_iter(async_iter, write_u8_pkt) {
1473
- let on_mqtt_chunk = this._conn_.set(
1474
- this.mqtt_ctx,
1475
- write_u8_pkt);
1476
-
1477
- this._msg_loop = ((async () => {
1478
- try {
1479
- async_iter = await async_iter;
1480
- for await (let chunk of async_iter)
1481
- on_mqtt_chunk(chunk);
1482
- this._conn_.reset();}
1483
- catch (err) {
1484
- this._conn_.reset(err);} })());
1485
-
1498
+ this.conn.setup(async_iter,
1499
+ write_u8_pkt,
1500
+ async (on_mqtt_chunk, conn) => {
1501
+ try {
1502
+ async_iter = await async_iter;
1503
+ for await (let chunk of async_iter)
1504
+ on_mqtt_chunk(chunk);
1505
+ conn.reset();}
1506
+ catch (err) {
1507
+ conn.reset(err);} } );
1486
1508
  return this}
1487
1509
 
1488
1510
 
@@ -1501,11 +1523,11 @@ class MQTTCore extends MQTTBase {
1501
1523
  this.with_deno_iter(
1502
1524
  Deno.connectTls(opt)) ) }
1503
1525
 
1504
- with_deno_iter(conn) {
1526
+ with_deno_iter(transport) {
1505
1527
  return this.with_async_iter(
1506
- conn.then(Deno.iter),
1507
- async u8_pkt =>
1508
- (await conn).write(u8_pkt)) }
1528
+ transport.then(Deno.iter),
1529
+ transport.then(write_stream =>
1530
+ u8_pkt => write_stream.write(u8_pkt)) ) }
1509
1531
 
1510
1532
  _conn_opt([a0, a1, a2]) {
1511
1533
  // (port, hostname, options) or (url, options)
@@ -1553,33 +1575,30 @@ class MQTTCore extends MQTTBase {
1553
1575
 
1554
1576
  websock.binaryType = 'arraybuffer';
1555
1577
 
1556
- let ready, {readyState} = websock;
1578
+ let ws_ready, readyState = websock.readyState;
1557
1579
  if (1 !== readyState) {
1558
1580
  if (0 !== readyState) {
1559
- throw new Error('Invalid WebSocket readyState') }
1560
-
1561
- ready = new Promise(fn => websock.onopen = fn); }
1581
+ throw new Error('WS readyState') }
1562
1582
 
1583
+ ws_ready = new Promise(ready => websock.onopen = ready); }
1563
1584
 
1564
- let {_conn_} = this;
1565
- let on_mqtt_chunk = _conn_.set(
1566
- this.mqtt_ctx,
1567
- async u8_pkt =>(
1568
- await ready
1569
- , websock.send(u8_pkt)) );
1585
+ this.conn.setup(ws_ready,
1586
+ u8_pkt => websock.send(u8_pkt),
1587
+ (on_mqtt_chunk, conn) => {
1588
+ websock.onmessage = evt =>(
1589
+ on_mqtt_chunk(new Uint8Array(evt.data)) );
1570
1590
 
1571
- websock.onmessage = evt =>(on_mqtt_chunk(new Uint8Array(evt.data)));
1572
- websock.onclose = evt => {
1573
- if (! evt.wasClean) {
1574
- var err = new Error('websocket connection close');
1575
- err.code = evt.code;
1576
- err.reason = evt.reason;}
1591
+ websock.onclose = evt => {
1592
+ if (! evt.wasClean) {
1593
+ var err = new Error('websocket close');
1594
+ err.code = evt.code;
1595
+ err.reason = evt.reason;}
1577
1596
 
1578
- _conn_.reset(err);};
1597
+ conn.reset(err);}; } );
1579
1598
 
1580
1599
  return this} }
1581
1600
 
1582
- const version = '0.5.2-deno';
1601
+ const version = '0.6.0-deno';
1583
1602
 
1584
1603
  const MQTTClient_v4 = /* #__PURE__ */
1585
1604
  with_topic_path_router(
@@ -1595,5 +1614,5 @@ const mqtt_v4 = opt =>
1595
1614
  const mqtt_v5 = opt =>
1596
1615
  new MQTTClient_v5(opt);
1597
1616
 
1598
- export { MQTTBase, MQTTClient_v4, MQTTClient_v5, MQTTCore, MQTTError, _mqtt_cmdid_dispatch, _mqtt_conn, _mqtt_dispatch, ao_defer_v, as_topic_path, mqtt_v4 as default, mqtt_v4, mqtt_v5, version, with_topic_path_router, with_topic_router };
1617
+ export { MQTTBase, MQTTClient_v4, MQTTClient_v5, MQTTCore, MQTTError, _mqtt_cmdids, _mqtt_conn, _mqtt_dispatch, as_topic_path, mqtt_v4 as default, mqtt_v4, mqtt_v5, version, with_topic_path_router, with_topic_router };
1599
1618
  //# sourceMappingURL=index.js.map