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