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
package/esm/deno/v5.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
 
@@ -1051,238 +1078,108 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
1051
1078
  }
1052
1079
  }
1053
1080
 
1054
- function ao_defer_ctx(as_res = (...args) => args) {
1055
- let y,n,_pset = (a,b) => { y=a, n=b; };
1056
- return p =>(
1057
- p = new Promise(_pset)
1058
- , as_res(p, y, n)) }
1059
-
1060
- const ao_defer_v = /* #__PURE__ */
1061
- ao_defer_ctx();
1062
-
1063
- Promise.resolve({type:'init'});
1064
-
1065
- function _mqtt_conn(client, [on_mqtt, pkt_future]) {
1066
- let _q_init = ao_defer_v(), _q_ready = ao_defer_v();
1067
- let _send_ready = async (...args) => (await _q_ready[0])(...args);
1068
- let _send_mqtt_pkt, _has_connected;
1069
- client._send = _send_ready;
1070
-
1071
- return {
1072
- async when_ready() {await _q_ready[0];}
1073
-
1074
- , ping: _ping_interval (() =>_send_mqtt_pkt?.('pingreq'))
1075
-
1076
- , reset(err) {
1077
- if (! _send_mqtt_pkt) {return}
1078
-
1079
- if (err) {
1080
- _q_init[2](err);}
1081
-
1082
- _send_mqtt_pkt = null;
1083
- _q_init = ao_defer_v();
1084
- client._send = _send_ready;
1085
-
1086
- // call client.on_conn_reset in next promise microtask
1087
- client.conn_emit('on_disconnect', false===err, err);}
1088
-
1089
- , async send_connect(... args) {
1090
- if (! _send_mqtt_pkt) {
1091
- await _q_init[0]; }// _send_mqtt_pkt is set before fulfilled
1092
-
1093
- // await connack response
1094
- let res = await _send_mqtt_pkt(...args);
1095
- if (0 == res[0].reason) {
1096
- _has_connected = true;
1097
- // resolve _q_ready[0] with _send_mqtt_pkt closure
1098
- _q_ready[1](client._send = _send_mqtt_pkt);
1099
- _q_ready = ao_defer_v();
1100
- client.conn_emit('on_ready');}
1101
-
1102
- return res}
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
1103
1126
 
1104
- , is_set: (() =>!! _send_mqtt_pkt)
1105
- , set(mqtt_ctx, send_u8_pkt) {
1106
- if (_send_mqtt_pkt) {
1107
- throw new Error('Already connected')}
1108
-
1109
- mqtt_ctx = mqtt_ctx.mqtt_stream();
1110
- let sess_ctx = {mqtt: client};
1111
- let on_mqtt_chunk = u8_buf =>
1112
- on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
1113
-
1114
- _send_mqtt_pkt = async (type, pkt, key) => {
1115
- let res = undefined !== key
1116
- ? pkt_future(key) : true;
1117
-
1118
- await send_u8_pkt(
1119
- mqtt_ctx.encode_pkt(type, pkt));
1120
-
1121
- return res};
1122
-
1123
- _q_init[1](_send_mqtt_pkt); // resolve _q_init with _send_mqtt_pkt closure
1124
-
1125
- // call client.on_live in next promise microtask
1126
- client.conn_emit('on_live', _has_connected);
1127
- return on_mqtt_chunk} } }
1128
-
1129
-
1130
- function _ping_interval(send_ping) {
1131
- let tid;
1132
- return (( td ) => {
1133
- tid = clearInterval(tid);
1134
- if (td) {
1135
- tid = setInterval(send_ping, 1000 * td);
1136
-
1137
- // ensure the interval allows the Deno event loop to exit
1138
- Deno.unrefTimer(tid);
1139
-
1140
-
1141
-
1142
- 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 || {};
1143
1131
 
1144
- const _mqtt_cmdid_dispatch ={
1145
- create(target) {
1146
- 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);
1147
1135
 
1148
- , bind_pkt_future(_pkt_id=100) {
1149
- let {hashbelt} = this;
1136
+ on_mqtt([]); // init hashbelt and rotate_ts
1137
+ return [on_mqtt, pkt_future]
1150
1138
 
1151
- let _tmp_; // use _tmp_ to reuse _by_key closure
1152
- let _by_key = answer_monad =>
1153
- hashbelt[0].set(_tmp_, answer_monad);
1154
1139
 
1155
- return (( pkt_or_key ) => {
1156
- if ('string' === typeof pkt_or_key) {
1157
- _tmp_ = pkt_or_key;}
1158
- else {
1159
- _pkt_id = (_pkt_id + 1) & 0xffff;
1160
- _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;
1161
1145
 
1162
- return new Promise(_by_key)}) }
1146
+ return new Promise(_ftr_by_key)}
1163
1147
 
1164
- , answer(key, pkt) {
1165
- for (let map of this.hashbelt) {
1166
- let answer_monad = map.get(key);
1167
- 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) {
1168
1152
  map.delete(key);
1169
1153
 
1170
- answer_monad([pkt, /*err*/]); // option/maybe monad
1154
+ fn_answer([pkt, /*err*/]); // option/maybe monad
1171
1155
  return true} }
1172
1156
  return false}
1173
1157
 
1174
- , rotate_belt(n) {
1175
- let {hashbelt} = this;
1176
- hashbelt.unshift(new Map());
1177
- for (let old of hashbelt.splice(n || 5)) {
1178
- for (let answer_monad of old.values()) {
1179
- answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
1180
-
1181
- , cmdids: ((() => {
1182
- return [
1183
- (() =>{} )// 0x0 reserved
1184
- , by_evt // 0x1 connect
1185
- , by_type // 0x2 connack
1186
- , by_evt // 0x3 publish
1187
- , by_id // 0x4 puback
1188
- , by_id // 0x5 pubrec
1189
- , by_id // 0x6 pubrel
1190
- , by_id // 0x7 pubcomp
1191
- , by_evt // 0x8 subscribe
1192
- , by_id // 0x9 suback
1193
- , by_evt // 0xa unsubscribe
1194
- , by_id // 0xb unsuback
1195
- , by_type // 0xc pingreq
1196
- , by_type // 0xd pingresp
1197
- , by_evt // 0xe disconnect
1198
- , by_type ]// 0xf auth
1199
-
1200
-
1201
- function by_id(disp, pkt) {
1202
- disp.answer(pkt.pkt_id, pkt); }
1203
-
1204
- function by_type(disp, pkt, ctx) {
1205
- disp.answer(pkt.type, pkt);
1206
- by_evt(disp, pkt, ctx);}
1207
-
1208
- async function by_evt({target}, pkt, ctx) {
1209
- let fn = target[`mqtt_${pkt.type}`]
1210
- || target.mqtt_pkt;
1211
-
1212
- await fn?.call(target, pkt, ctx);} })()) };
1213
-
1214
- /*
1215
- on_mqtt_type = {
1216
- mqtt_auth(pkt, ctx) ::
1217
- mqtt_connect(pkt, ctx) ::
1218
- mqtt_connack(pkt, ctx) ::
1219
- mqtt_disconnect(pkt, ctx) ::
1220
-
1221
- mqtt_publish(pkt, ctx)
1222
- mqtt_subscribe(pkt, ctx) ::
1223
- mqtt_unsubscribe(pkt, ctx) ::
1224
-
1225
- mqtt_pingreq(pkt, ctx) ::
1226
- mqtt_pingresp(pkt, ctx) ::
1227
- }
1228
- */
1229
-
1230
- function _mqtt_dispatch(opt, target) {
1231
- let _disp_ = _mqtt_cmdid_dispatch.create(target);
1232
- let { cmdids } = _disp_;
1233
-
1234
- // default rotate at 1s across 5 buckets
1235
- let { td: rotate_td=1000, n: rotate_n=5 } =
1236
- opt && opt.rotate || {};
1237
-
1238
- let rotate_ts = rotate_td + Date.now();
1239
-
1240
- return [on_mqtt,
1241
- _disp_.bind_pkt_future()]
1242
-
1243
1158
  function on_mqtt(pkt_list, ctx) {
1244
1159
  for (let pkt of pkt_list) {
1245
- cmdids[pkt.id](_disp_, pkt, ctx); }
1160
+ _mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
1246
1161
 
1247
- if (Date.now() > rotate_ts) {
1248
- _disp_.rotate_belt(rotate_n);
1249
- 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
1250
1170
 
1251
1171
  class MQTTError extends Error {
1252
1172
  constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
1173
+ // use hex-encoded reasons to match MQTT spec documentation
1253
1174
  super(`[0x${reason.toString(16)}] ${reason.reason}`);
1254
1175
  this.mqtt_pkt = mqtt_pkt;
1255
1176
  this.reason = reason;} }
1256
1177
 
1257
1178
  class MQTTBase {
1258
- constructor(opt={}) {
1259
- this.with(opt);
1260
- this._conn_ = _mqtt_conn(this,
1261
- this._init_dispatch(opt, this)); }
1262
-
1263
- with(fns_ns) {
1264
- for (let [k,v] of Object.entries(fns_ns)) {
1265
- if ('function' === typeof v) {this[k] = v;} }
1266
- return this}
1267
-
1268
- async conn_emit(evt, arg, err_arg) {
1269
- this.log_conn?.(evt, arg, err_arg);
1270
- try {
1271
- let fn_evt = this[await evt]; // microtask break using `await evt`
1272
- if (fn_evt) {
1273
- await fn_evt.call(this, this, arg, err_arg);}
1274
- else if (err_arg) {throw err_arg} }
1275
- catch (err) {
1276
- this.on_error(err, evt);} }
1277
-
1278
- on_error(err, evt) {
1279
- console.warn('[[u8-mqtt error: %s]]', evt, err); }
1280
-
1281
1179
  // Handshaking Packets
1282
-
1283
1180
  async connect(pkt={}) {
1284
1181
  let cid = pkt.client_id;
1285
- if ('string' !== typeof cid) {
1182
+ if (! _isstr(cid)) {
1286
1183
  // see init_client_id implementation in core.jsy
1287
1184
  pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
1288
1185
  this.client_id = cid;
@@ -1290,23 +1187,20 @@ class MQTTBase {
1290
1187
  if (null == pkt.keep_alive) {
1291
1188
  pkt.keep_alive = 60;}
1292
1189
 
1293
- let res = await this._conn_
1294
- .send_connect('connect', pkt, 'connack');
1295
-
1296
- if (0 != res[0].reason) {
1297
- throw new this.MQTTError(res[0])}
1298
-
1299
- // TODO: merge with server's keep_alive frequency
1300
- this._conn_.ping(pkt.keep_alive);
1301
- 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)}
1302
1194
 
1303
1195
  async disconnect(pkt={}) {
1304
- let res = await this._send('disconnect', pkt);
1305
- this._conn_.reset(false);
1306
- return res}
1196
+ let response = await this._send0('disconnect', pkt);
1197
+ return this.conn.on_dis(pkt, response)}
1307
1198
 
1308
- auth(pkt={}) {
1309
- 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)}
1310
1204
 
1311
1205
  ping() {return this._send('pingreq', null, 'pingresp')}
1312
1206
  puback({pkt_id}) {return this._send('puback', {pkt_id})}
@@ -1343,20 +1237,18 @@ class MQTTBase {
1343
1237
  // alias: publish -- because 'pub' is shorter for semantic aliases above
1344
1238
  async pub(pkt, pub_opt) {
1345
1239
  if (undefined === pkt.payload) {
1346
- if ('function' === typeof pub_opt) {
1240
+ if (_isfn(pub_opt)) {
1241
+ // pub_opt as a function is fn_encode value
1347
1242
  pub_opt = {fn_encode: pub_opt};}
1348
1243
 
1349
- let {msg} = pkt;
1350
- switch (typeof msg) {
1351
- case 'function':
1352
- pub_opt = {...pub_opt, fn_encode: msg};
1353
- // flow into 'undefined' case
1354
- case 'undefined':
1355
- // return a single-value closure to publish packets
1356
- 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)}
1357
1250
 
1358
1251
  // Encode payload from msg; fn_encode allows alternative to JSON.stringify
1359
- let {fn_encode} = pub_opt || {};
1360
1252
  pkt.payload = fn_encode
1361
1253
  ? await fn_encode(msg)
1362
1254
  : JSON.stringify(msg);}
@@ -1368,31 +1260,31 @@ class MQTTBase {
1368
1260
  pkt = pub_opt.xform(pkt) || pkt;} }
1369
1261
 
1370
1262
  return this._send('publish', pkt,
1371
- pkt.qos ? pkt : void 0 ) }// key
1263
+ pkt.qos ? pkt : null ) }// key
1372
1264
 
1373
1265
 
1374
1266
  // Internal API
1375
1267
 
1376
- /* 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 */
1377
1270
 
1378
1271
  _init_dispatch(opt) {
1379
1272
  this.constructor?._once_();
1380
1273
  let target ={__proto__: opt.on_mqtt_type};
1381
1274
  target.mqtt_publish ||=
1382
1275
  this._init_router?.(opt, this, target);
1383
- return _mqtt_dispatch(this, target)}
1276
+ return _mqtt_dispatch(opt, target)}
1384
1277
 
1385
1278
  static _aliases() {
1386
1279
  return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
1387
1280
 
1388
- static _once_(self=this) {
1389
- self._once_ = _=>0;
1390
- let p = self.prototype;
1281
+ static _once_(klass=this) {
1282
+ klass._once_ = _=>0;
1283
+ var alias, name, p = klass.prototype;
1391
1284
  p.MQTTError = MQTTError;
1392
- for (let alias of self._aliases().split(/\s+/)) {
1393
- alias = alias.split(':');
1394
- let fn = alias[1] && p[alias[1]];
1395
- if (fn) {p[alias[0]] = fn;} } } }
1285
+ for (alias of klass._aliases().split(/\s+/)) {
1286
+ [alias, name] = alias.split(':');
1287
+ p[alias] = p[name];} } }
1396
1288
 
1397
1289
 
1398
1290
  function _as_topics(pkt, ex, topic_prefix) {
@@ -1417,37 +1309,167 @@ function _as_topics(pkt, ex, topic_prefix) {
1417
1309
  pkt.topics = pkt.topics.map(_prefix_topics);}
1418
1310
  return pkt}
1419
1311
 
1420
- const pkt_api = {
1421
- utf8(u8) { return new TextDecoder('utf-8').decode(u8 || this.payload ) },
1422
- json(u8) { return JSON.parse( this.utf8(u8) || null ) },
1423
- text(u8) { return this.utf8(u8) },
1424
- };
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};
1425
1423
 
1426
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
+
1427
1441
  static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
1428
- let self = class extends this {};
1429
- self.prototype.mqtt_ctx =
1442
+ let klass = class extends this {};
1443
+ klass.prototype.mqtt_ctx =
1430
1444
  mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
1431
- 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); }
1432
1462
 
1433
1463
 
1434
1464
  // automatic Client Id for connect()
1435
1465
  init_client_id(parts=['u8-mqtt--','']) {
1436
- let sess_stg=this.sess_stg;
1466
+ let sess_stg = this.sess_stg;
1437
1467
  let key, cid = sess_stg?.getItem(key=parts.join(' '));
1438
1468
  if (! cid) {
1439
1469
  cid = parts.join(Math.random().toString(36).slice(2));
1440
1470
  sess_stg?.setItem(key, cid);}
1441
1471
  return cid}
1442
1472
 
1443
- get sess_stg() {return globalThis.sessionStorage}
1444
-
1445
-
1446
- //on_error(err, evt) ::
1447
- // console.warn @ '[[u8-mqtt error: %s]]', evt, err
1448
-
1449
- //log_conn(evt, arg, err_arg) ::
1450
- // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
1451
1473
 
1452
1474
  on_live(client, is_reconnect) {
1453
1475
  if (is_reconnect) {
@@ -1473,19 +1495,16 @@ class MQTTCore extends MQTTBase {
1473
1495
  return new Promise(done => setTimeout(done, ms)) }
1474
1496
 
1475
1497
  with_async_iter(async_iter, write_u8_pkt) {
1476
- let on_mqtt_chunk = this._conn_.set(
1477
- this.mqtt_ctx,
1478
- write_u8_pkt);
1479
-
1480
- this._msg_loop = ((async () => {
1481
- try {
1482
- async_iter = await async_iter;
1483
- for await (let chunk of async_iter)
1484
- on_mqtt_chunk(chunk);
1485
- this._conn_.reset();}
1486
- catch (err) {
1487
- this._conn_.reset(err);} })());
1488
-
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);} } );
1489
1508
  return this}
1490
1509
 
1491
1510
 
@@ -1504,11 +1523,11 @@ class MQTTCore extends MQTTBase {
1504
1523
  this.with_deno_iter(
1505
1524
  Deno.connectTls(opt)) ) }
1506
1525
 
1507
- with_deno_iter(conn) {
1526
+ with_deno_iter(transport) {
1508
1527
  return this.with_async_iter(
1509
- conn.then(Deno.iter),
1510
- async u8_pkt =>
1511
- (await conn).write(u8_pkt)) }
1528
+ transport.then(Deno.iter),
1529
+ transport.then(write_stream =>
1530
+ u8_pkt => write_stream.write(u8_pkt)) ) }
1512
1531
 
1513
1532
  _conn_opt([a0, a1, a2]) {
1514
1533
  // (port, hostname, options) or (url, options)
@@ -1556,33 +1575,30 @@ class MQTTCore extends MQTTBase {
1556
1575
 
1557
1576
  websock.binaryType = 'arraybuffer';
1558
1577
 
1559
- let ready, {readyState} = websock;
1578
+ let ws_ready, readyState = websock.readyState;
1560
1579
  if (1 !== readyState) {
1561
1580
  if (0 !== readyState) {
1562
- throw new Error('Invalid WebSocket readyState') }
1563
-
1564
- ready = new Promise(fn => websock.onopen = fn); }
1581
+ throw new Error('WS readyState') }
1565
1582
 
1583
+ ws_ready = new Promise(ready => websock.onopen = ready); }
1566
1584
 
1567
- let {_conn_} = this;
1568
- let on_mqtt_chunk = _conn_.set(
1569
- this.mqtt_ctx,
1570
- async u8_pkt =>(
1571
- await ready
1572
- , 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)) );
1573
1590
 
1574
- websock.onmessage = evt =>(on_mqtt_chunk(new Uint8Array(evt.data)));
1575
- websock.onclose = evt => {
1576
- if (! evt.wasClean) {
1577
- var err = new Error('websocket connection close');
1578
- err.code = evt.code;
1579
- 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;}
1580
1596
 
1581
- _conn_.reset(err);};
1597
+ conn.reset(err);}; } );
1582
1598
 
1583
1599
  return this} }
1584
1600
 
1585
- const version = '0.5.3-deno';
1601
+ const version = '0.6.0-deno';
1586
1602
 
1587
1603
  const MQTTClient_v4 = /* #__PURE__ */
1588
1604
  with_topic_path_router(