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