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/node/v4.js
CHANGED
|
@@ -543,6 +543,23 @@ function parse(str, loose) {
|
|
|
543
543
|
};
|
|
544
544
|
}
|
|
545
545
|
|
|
546
|
+
const _isfn = v => typeof v === 'function';
|
|
547
|
+
const _isstr = v => typeof v === 'string';
|
|
548
|
+
|
|
549
|
+
function _interval(fn_callback) {
|
|
550
|
+
let tid;
|
|
551
|
+
return (( td ) => {
|
|
552
|
+
tid = clearInterval(tid);
|
|
553
|
+
if (td) {
|
|
554
|
+
tid = setInterval(fn_callback, 1000 * td);
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
// ensure the interval allows the NodeJS event loop to exit
|
|
560
|
+
tid.unref?.();
|
|
561
|
+
return true} }) }
|
|
562
|
+
|
|
546
563
|
/*
|
|
547
564
|
class AbstractTopicRouter ::
|
|
548
565
|
async invoke(pkt, ctx) ::
|
|
@@ -633,7 +650,7 @@ function mqtt_topic_path_router() {
|
|
|
633
650
|
let fn = args.pop();
|
|
634
651
|
let priority = args.pop();
|
|
635
652
|
|
|
636
|
-
if (
|
|
653
|
+
if (! _isfn(fn)) {
|
|
637
654
|
if (fn) {throw new TypeError()}
|
|
638
655
|
fn = _ignore;}
|
|
639
656
|
|
|
@@ -783,6 +800,9 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
|
|
|
783
800
|
return _encode_by_type[type]( mqtt_level, pkt ) },
|
|
784
801
|
|
|
785
802
|
decode_pkt(b0, u8_body) {
|
|
803
|
+
if (b0.map) // Uint8Array in first arg
|
|
804
|
+
return mqtt_raw_dispatch(this)(b0)[0]
|
|
805
|
+
|
|
786
806
|
let fn_decode = _decode_by_id[b0>>>4] || _decode_by_id[0];
|
|
787
807
|
return fn_decode?.({__proto__: this.pkt_ctx, b0}, u8_body) },
|
|
788
808
|
|
|
@@ -795,262 +815,129 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
|
|
|
795
815
|
}
|
|
796
816
|
}
|
|
797
817
|
|
|
798
|
-
function
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
,
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
_q_ready = ao_defer_v();
|
|
844
|
-
client.conn_emit('on_ready');}
|
|
845
|
-
|
|
846
|
-
return res}
|
|
847
|
-
|
|
848
|
-
, is_set: (() =>!! _send_mqtt_pkt)
|
|
849
|
-
, set(mqtt_ctx, send_u8_pkt) {
|
|
850
|
-
if (_send_mqtt_pkt) {
|
|
851
|
-
throw new Error('Already connected')}
|
|
852
|
-
|
|
853
|
-
mqtt_ctx = mqtt_ctx.mqtt_stream();
|
|
854
|
-
let sess_ctx = {mqtt: client};
|
|
855
|
-
let on_mqtt_chunk = u8_buf =>
|
|
856
|
-
on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
|
|
857
|
-
|
|
858
|
-
_send_mqtt_pkt = async (type, pkt, key) => {
|
|
859
|
-
let res = undefined !== key
|
|
860
|
-
? pkt_future(key) : true;
|
|
861
|
-
|
|
862
|
-
await send_u8_pkt(
|
|
863
|
-
mqtt_ctx.encode_pkt(type, pkt));
|
|
864
|
-
|
|
865
|
-
return res};
|
|
818
|
+
async function _mqtt_cmd_evt(target, answer, pkt, ctx) {
|
|
819
|
+
/* target : on_mqtt_type = {
|
|
820
|
+
mqtt_pkt(pkt, ctx) {}, // generic
|
|
821
|
+
|
|
822
|
+
mqtt_auth(pkt, ctx) {},
|
|
823
|
+
mqtt_connect(pkt, ctx) {},
|
|
824
|
+
mqtt_connack(pkt, ctx) {},
|
|
825
|
+
mqtt_disconnect(pkt, ctx) {},
|
|
826
|
+
|
|
827
|
+
mqtt_publish(pkt, ctx) {},
|
|
828
|
+
mqtt_subscribe(pkt, ctx) {},
|
|
829
|
+
mqtt_unsubscribe(pkt, ctx) {},
|
|
830
|
+
|
|
831
|
+
mqtt_pingreq(pkt, ctx) {},
|
|
832
|
+
mqtt_pingresp(pkt, ctx) {},
|
|
833
|
+
} */
|
|
834
|
+
|
|
835
|
+
let pkt_fn = target[`mqtt_${pkt.type}`] || target.mqtt_pkt;
|
|
836
|
+
await pkt_fn?.call(target, pkt, ctx);}
|
|
837
|
+
|
|
838
|
+
function _mqtt_cmd_type(target, answer, pkt, ctx) {
|
|
839
|
+
answer(pkt.type, pkt);
|
|
840
|
+
_mqtt_cmd_evt(target, answer, pkt, ctx);}
|
|
841
|
+
|
|
842
|
+
function _mqtt_cmd_id(target, answer, pkt) {
|
|
843
|
+
answer(pkt.pkt_id, pkt);}
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
const _mqtt_cmdids =[
|
|
847
|
+
_ => {} // 0x0 reserved
|
|
848
|
+
, _mqtt_cmd_evt // 0x1 connect
|
|
849
|
+
, _mqtt_cmd_type // 0x2 connack
|
|
850
|
+
, _mqtt_cmd_evt // 0x3 publish
|
|
851
|
+
, _mqtt_cmd_id // 0x4 puback
|
|
852
|
+
, _mqtt_cmd_id // 0x5 pubrec
|
|
853
|
+
, _mqtt_cmd_id // 0x6 pubrel
|
|
854
|
+
, _mqtt_cmd_id // 0x7 pubcomp
|
|
855
|
+
, _mqtt_cmd_evt // 0x8 subscribe
|
|
856
|
+
, _mqtt_cmd_id // 0x9 suback
|
|
857
|
+
, _mqtt_cmd_evt // 0xa unsubscribe
|
|
858
|
+
, _mqtt_cmd_id // 0xb unsuback
|
|
859
|
+
, _mqtt_cmd_type // 0xc pingreq
|
|
860
|
+
, _mqtt_cmd_type // 0xd pingresp
|
|
861
|
+
, _mqtt_cmd_evt // 0xe disconnect
|
|
862
|
+
, _mqtt_cmd_type ];// 0xf auth
|
|
866
863
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
return on_mqtt_chunk} } }
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
function _ping_interval(send_ping) {
|
|
875
|
-
let tid;
|
|
876
|
-
return (( td ) => {
|
|
877
|
-
tid = clearInterval(tid);
|
|
878
|
-
if (td) {
|
|
879
|
-
tid = setInterval(send_ping, 1000 * td);
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
// ensure the interval allows the NodeJS event loop to exit
|
|
885
|
-
tid.unref?.();
|
|
886
|
-
return true} }) }
|
|
864
|
+
function _mqtt_dispatch(opt, target) {
|
|
865
|
+
let hashbelt=[], rotate_ts=0;
|
|
866
|
+
// default rotate at 1s across 5 buckets
|
|
867
|
+
let { td: rotate_td=1000, n: rotate_n=5 } = opt?.rotate || {};
|
|
887
868
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
869
|
+
// Promise / future scaffolding
|
|
870
|
+
let _pkt_id=100, _ftr_key; // use _ftr_key to reuse _by_key closure
|
|
871
|
+
let _ftr_by_key = fn_answer => hashbelt[0].set(_ftr_key, fn_answer);
|
|
891
872
|
|
|
892
|
-
|
|
893
|
-
|
|
873
|
+
on_mqtt([]); // init hashbelt and rotate_ts
|
|
874
|
+
return [on_mqtt, pkt_future]
|
|
894
875
|
|
|
895
|
-
let _tmp_; // use _tmp_ to reuse _by_key closure
|
|
896
|
-
let _by_key = answer_monad =>
|
|
897
|
-
hashbelt[0].set(_tmp_, answer_monad);
|
|
898
876
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
_tmp_ = pkt_or_key.pkt_id = _pkt_id;}
|
|
877
|
+
function pkt_future(pkt_or_key) {
|
|
878
|
+
if (! _isstr(pkt_or_key)) {
|
|
879
|
+
_pkt_id = (_pkt_id + 1) & 0xffff; // 16-bit unsigned short
|
|
880
|
+
_ftr_key = pkt_or_key.pkt_id = _pkt_id;}
|
|
881
|
+
else _ftr_key = pkt_or_key;
|
|
905
882
|
|
|
906
|
-
|
|
883
|
+
return new Promise(_ftr_by_key)}
|
|
907
884
|
|
|
908
|
-
|
|
909
|
-
for (let map of
|
|
910
|
-
let
|
|
911
|
-
if (
|
|
885
|
+
function answer(key, pkt) {
|
|
886
|
+
for (let map of hashbelt) {
|
|
887
|
+
let fn_answer = map.get(key);
|
|
888
|
+
if (fn_answer) {
|
|
912
889
|
map.delete(key);
|
|
913
890
|
|
|
914
|
-
|
|
891
|
+
fn_answer([pkt, /*err*/]); // option/maybe monad
|
|
915
892
|
return true} }
|
|
916
893
|
return false}
|
|
917
894
|
|
|
918
|
-
, rotate_belt(n) {
|
|
919
|
-
let {hashbelt} = this;
|
|
920
|
-
hashbelt.unshift(new Map());
|
|
921
|
-
for (let old of hashbelt.splice(n || 5)) {
|
|
922
|
-
for (let answer_monad of old.values()) {
|
|
923
|
-
answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
|
|
924
|
-
|
|
925
|
-
, cmdids: ((() => {
|
|
926
|
-
return [
|
|
927
|
-
(() =>{} )// 0x0 reserved
|
|
928
|
-
, by_evt // 0x1 connect
|
|
929
|
-
, by_type // 0x2 connack
|
|
930
|
-
, by_evt // 0x3 publish
|
|
931
|
-
, by_id // 0x4 puback
|
|
932
|
-
, by_id // 0x5 pubrec
|
|
933
|
-
, by_id // 0x6 pubrel
|
|
934
|
-
, by_id // 0x7 pubcomp
|
|
935
|
-
, by_evt // 0x8 subscribe
|
|
936
|
-
, by_id // 0x9 suback
|
|
937
|
-
, by_evt // 0xa unsubscribe
|
|
938
|
-
, by_id // 0xb unsuback
|
|
939
|
-
, by_type // 0xc pingreq
|
|
940
|
-
, by_type // 0xd pingresp
|
|
941
|
-
, by_evt // 0xe disconnect
|
|
942
|
-
, by_type ]// 0xf auth
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
function by_id(disp, pkt) {
|
|
946
|
-
disp.answer(pkt.pkt_id, pkt); }
|
|
947
|
-
|
|
948
|
-
function by_type(disp, pkt, ctx) {
|
|
949
|
-
disp.answer(pkt.type, pkt);
|
|
950
|
-
by_evt(disp, pkt, ctx);}
|
|
951
|
-
|
|
952
|
-
async function by_evt({target}, pkt, ctx) {
|
|
953
|
-
let fn = target[`mqtt_${pkt.type}`]
|
|
954
|
-
|| target.mqtt_pkt;
|
|
955
|
-
|
|
956
|
-
await fn?.call(target, pkt, ctx);} })()) };
|
|
957
|
-
|
|
958
|
-
/*
|
|
959
|
-
on_mqtt_type = {
|
|
960
|
-
mqtt_auth(pkt, ctx) ::
|
|
961
|
-
mqtt_connect(pkt, ctx) ::
|
|
962
|
-
mqtt_connack(pkt, ctx) ::
|
|
963
|
-
mqtt_disconnect(pkt, ctx) ::
|
|
964
|
-
|
|
965
|
-
mqtt_publish(pkt, ctx)
|
|
966
|
-
mqtt_subscribe(pkt, ctx) ::
|
|
967
|
-
mqtt_unsubscribe(pkt, ctx) ::
|
|
968
|
-
|
|
969
|
-
mqtt_pingreq(pkt, ctx) ::
|
|
970
|
-
mqtt_pingresp(pkt, ctx) ::
|
|
971
|
-
}
|
|
972
|
-
*/
|
|
973
|
-
|
|
974
|
-
function _mqtt_dispatch(opt, target) {
|
|
975
|
-
let _disp_ = _mqtt_cmdid_dispatch.create(target);
|
|
976
|
-
let { cmdids } = _disp_;
|
|
977
|
-
|
|
978
|
-
// default rotate at 1s across 5 buckets
|
|
979
|
-
let { td: rotate_td=1000, n: rotate_n=5 } =
|
|
980
|
-
opt && opt.rotate || {};
|
|
981
|
-
|
|
982
|
-
let rotate_ts = rotate_td + Date.now();
|
|
983
|
-
|
|
984
|
-
return [on_mqtt,
|
|
985
|
-
_disp_.bind_pkt_future()]
|
|
986
|
-
|
|
987
895
|
function on_mqtt(pkt_list, ctx) {
|
|
988
896
|
for (let pkt of pkt_list) {
|
|
989
|
-
|
|
897
|
+
_mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
|
|
990
898
|
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
899
|
+
// rotate after rotate_ts
|
|
900
|
+
let now = Date.now();
|
|
901
|
+
if (now > rotate_ts) {
|
|
902
|
+
rotate_ts = rotate_td + now;
|
|
903
|
+
hashbelt.unshift(new Map());
|
|
904
|
+
while (hashbelt.length > rotate_n) {
|
|
905
|
+
for (let fn_answer of hashbelt.pop().values()) {
|
|
906
|
+
fn_answer([/*pkt*/, 'expired']); } } } } }// option/maybe monad
|
|
994
907
|
|
|
995
908
|
class MQTTError extends Error {
|
|
996
909
|
constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
|
|
910
|
+
// use hex-encoded reasons to match MQTT spec documentation
|
|
997
911
|
super(`[0x${reason.toString(16)}] ${reason.reason}`);
|
|
998
912
|
this.mqtt_pkt = mqtt_pkt;
|
|
999
913
|
this.reason = reason;} }
|
|
1000
914
|
|
|
1001
915
|
class MQTTBase {
|
|
1002
|
-
constructor(opt={}) {
|
|
1003
|
-
this.with(opt);
|
|
1004
|
-
this._conn_ = _mqtt_conn(this,
|
|
1005
|
-
this._init_dispatch(opt, this)); }
|
|
1006
|
-
|
|
1007
|
-
with(fns_ns) {
|
|
1008
|
-
for (let [k,v] of Object.entries(fns_ns)) {
|
|
1009
|
-
if ('function' === typeof v) {this[k] = v;} }
|
|
1010
|
-
return this}
|
|
1011
|
-
|
|
1012
|
-
async conn_emit(evt, arg, err_arg) {
|
|
1013
|
-
this.log_conn?.(evt, arg, err_arg);
|
|
1014
|
-
try {
|
|
1015
|
-
let fn_evt = this[await evt]; // microtask break using `await evt`
|
|
1016
|
-
if (fn_evt) {
|
|
1017
|
-
await fn_evt.call(this, this, arg, err_arg);}
|
|
1018
|
-
else if (err_arg) {throw err_arg} }
|
|
1019
|
-
catch (err) {
|
|
1020
|
-
this.on_error(err, evt);} }
|
|
1021
|
-
|
|
1022
|
-
on_error(err, evt) {
|
|
1023
|
-
console.warn('[[u8-mqtt error: %s]]', evt, err); }
|
|
1024
|
-
|
|
1025
916
|
// Handshaking Packets
|
|
1026
|
-
|
|
1027
917
|
async connect(pkt={}) {
|
|
1028
|
-
let cid = pkt.client_id
|
|
1029
|
-
if (
|
|
918
|
+
let cid = pkt.client_id;
|
|
919
|
+
if (! _isstr(cid)) {
|
|
1030
920
|
// see init_client_id implementation in core.jsy
|
|
1031
|
-
pkt.client_id = cid = this.init_client_id(cid);}
|
|
921
|
+
pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
|
|
1032
922
|
this.client_id = cid;
|
|
1033
923
|
|
|
1034
924
|
if (null == pkt.keep_alive) {
|
|
1035
925
|
pkt.keep_alive = 60;}
|
|
1036
926
|
|
|
1037
|
-
let
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
throw new this.MQTTError(res[0])}
|
|
1042
|
-
|
|
1043
|
-
// TODO: merge with server's keep_alive frequency
|
|
1044
|
-
this._conn_.ping(pkt.keep_alive);
|
|
1045
|
-
return res}
|
|
927
|
+
let response = await this._send0('connect', pkt, 'connack');
|
|
928
|
+
if (0 != response[0].reason) {// compare to 0 to coerce to number
|
|
929
|
+
throw new this.MQTTError(response[0])}
|
|
930
|
+
return this.conn.on_conn(pkt, response)}
|
|
1046
931
|
|
|
1047
932
|
async disconnect(pkt={}) {
|
|
1048
|
-
let
|
|
1049
|
-
this.
|
|
1050
|
-
return res}
|
|
933
|
+
let response = await this._send0('disconnect', pkt);
|
|
934
|
+
return this.conn.on_dis(pkt, response)}
|
|
1051
935
|
|
|
1052
|
-
auth(pkt={}) {
|
|
1053
|
-
|
|
936
|
+
async auth(pkt={}) {
|
|
937
|
+
let response = await this._send0('auth', pkt, 'auth');
|
|
938
|
+
if (response[0].reason) {
|
|
939
|
+
throw new this.MQTTError(response[0])}
|
|
940
|
+
return this.conn.on_auth(pkt, response)}
|
|
1054
941
|
|
|
1055
942
|
ping() {return this._send('pingreq', null, 'pingresp')}
|
|
1056
943
|
puback({pkt_id}) {return this._send('puback', {pkt_id})}
|
|
@@ -1087,20 +974,18 @@ class MQTTBase {
|
|
|
1087
974
|
// alias: publish -- because 'pub' is shorter for semantic aliases above
|
|
1088
975
|
async pub(pkt, pub_opt) {
|
|
1089
976
|
if (undefined === pkt.payload) {
|
|
1090
|
-
if (
|
|
977
|
+
if (_isfn(pub_opt)) {
|
|
978
|
+
// pub_opt as a function is fn_encode value
|
|
1091
979
|
pub_opt = {fn_encode: pub_opt};}
|
|
1092
980
|
|
|
1093
|
-
let
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
// return a single-value closure to publish packets
|
|
1100
|
-
return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
|
|
981
|
+
let msg = pkt.msg, fn_encode = pub_opt?.fn_encode;
|
|
982
|
+
if (null == msg || _isfn(msg)) {
|
|
983
|
+
// when msg is a function, return closure using fn_encode
|
|
984
|
+
if (msg) {pub_opt = {...pub_opt, fn_encode: msg};}
|
|
985
|
+
// return a single-value closure to publish packets
|
|
986
|
+
return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
|
|
1101
987
|
|
|
1102
988
|
// Encode payload from msg; fn_encode allows alternative to JSON.stringify
|
|
1103
|
-
let {fn_encode} = pub_opt || {};
|
|
1104
989
|
pkt.payload = fn_encode
|
|
1105
990
|
? await fn_encode(msg)
|
|
1106
991
|
: JSON.stringify(msg);}
|
|
@@ -1112,31 +997,31 @@ class MQTTBase {
|
|
|
1112
997
|
pkt = pub_opt.xform(pkt) || pkt;} }
|
|
1113
998
|
|
|
1114
999
|
return this._send('publish', pkt,
|
|
1115
|
-
pkt.qos ? pkt :
|
|
1000
|
+
pkt.qos ? pkt : null ) }// key
|
|
1116
1001
|
|
|
1117
1002
|
|
|
1118
1003
|
// Internal API
|
|
1119
1004
|
|
|
1120
|
-
/* async
|
|
1005
|
+
/* async _send0(type, pkt) -- provided by conn and transport */
|
|
1006
|
+
/* async _send(type, pkt) -- provided by conn and transport */
|
|
1121
1007
|
|
|
1122
1008
|
_init_dispatch(opt) {
|
|
1123
1009
|
this.constructor?._once_();
|
|
1124
1010
|
let target ={__proto__: opt.on_mqtt_type};
|
|
1125
1011
|
target.mqtt_publish ||=
|
|
1126
1012
|
this._init_router?.(opt, this, target);
|
|
1127
|
-
return _mqtt_dispatch(
|
|
1013
|
+
return _mqtt_dispatch(opt, target)}
|
|
1128
1014
|
|
|
1129
1015
|
static _aliases() {
|
|
1130
1016
|
return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
|
|
1131
1017
|
|
|
1132
|
-
static _once_(
|
|
1133
|
-
|
|
1134
|
-
|
|
1018
|
+
static _once_(klass=this) {
|
|
1019
|
+
klass._once_ = _=>0;
|
|
1020
|
+
var alias, name, p = klass.prototype;
|
|
1135
1021
|
p.MQTTError = MQTTError;
|
|
1136
|
-
for (
|
|
1137
|
-
alias = alias.split(':');
|
|
1138
|
-
|
|
1139
|
-
if (fn) {p[alias[0]] = fn;} } } }
|
|
1022
|
+
for (alias of klass._aliases().split(/\s+/)) {
|
|
1023
|
+
[alias, name] = alias.split(':');
|
|
1024
|
+
p[alias] = p[name];} } }
|
|
1140
1025
|
|
|
1141
1026
|
|
|
1142
1027
|
function _as_topics(pkt, ex, topic_prefix) {
|
|
@@ -1161,43 +1046,177 @@ function _as_topics(pkt, ex, topic_prefix) {
|
|
|
1161
1046
|
pkt.topics = pkt.topics.map(_prefix_topics);}
|
|
1162
1047
|
return pkt}
|
|
1163
1048
|
|
|
1164
|
-
const
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1049
|
+
const _defer_obj = o =>(
|
|
1050
|
+
o.p = new Promise((a,e) => { o.a=a; o.e=e; })
|
|
1051
|
+
, o);
|
|
1052
|
+
|
|
1053
|
+
function _dfn_reset(client, attr, fn_after) {
|
|
1054
|
+
// a resetable deferred for a function
|
|
1055
|
+
let self = {set}, afn = async (...args) => (await self.p)(...args);
|
|
1056
|
+
return set()
|
|
1057
|
+
|
|
1058
|
+
function set() {
|
|
1059
|
+
if (afn !== client[attr]) {
|
|
1060
|
+
_defer_obj(self).p.then(fn_after, _=>0);
|
|
1061
|
+
client[attr] = afn;}
|
|
1062
|
+
return self} }
|
|
1063
|
+
|
|
1064
|
+
function _mqtt_conn(opt, client, [on_mqtt, pkt_future]) {
|
|
1065
|
+
let _abort;
|
|
1066
|
+
let _dfn_send0 = _dfn_reset(client, '_send0', // client._send0 getter/setter
|
|
1067
|
+
_=> client.conn_emit('on_live', conn.has_connected));
|
|
1068
|
+
let _dfn_ready = _dfn_reset(client, '_send', // client._send getter/setter
|
|
1069
|
+
_=> client.conn_emit('on_ready'));
|
|
1070
|
+
let _keep_alive_ival = _interval (() =>client._send0('pingreq') );// resettable interval for keep_alive ping
|
|
1071
|
+
|
|
1072
|
+
let conn = Object.create({
|
|
1073
|
+
ping: (td=conn.keep_alive) => _keep_alive_ival(td)
|
|
1074
|
+
|
|
1075
|
+
, on_conn(pkt, response) {
|
|
1076
|
+
conn.has_connected = true;
|
|
1077
|
+
conn.keep_alive = opt.keep_alive || response[0].props?.server_keep_alive || pkt.keep_alive;
|
|
1078
|
+
client.conn_emit('on_conn');
|
|
1079
|
+
return opt.use_auth
|
|
1080
|
+
? response // wait on enhanced authentication step
|
|
1081
|
+
: conn.on_auth(null, response) }// otherwise, connect is also auth
|
|
1082
|
+
|
|
1083
|
+
, on_auth(pkt, response) {
|
|
1084
|
+
_dfn_ready.a(_dfn_send0.p);
|
|
1085
|
+
if (0 != opt.keep_alive) {
|
|
1086
|
+
conn.ping();}
|
|
1087
|
+
client.conn_emit('on_auth', !pkt);
|
|
1088
|
+
return response}
|
|
1089
|
+
|
|
1090
|
+
, on_dis(pkt, response) {
|
|
1091
|
+
conn.reset(false);
|
|
1092
|
+
return response}
|
|
1093
|
+
|
|
1094
|
+
, reset(err) {
|
|
1095
|
+
if (err) {
|
|
1096
|
+
_dfn_send0.e(err); }// send error to uses of _send0 (connect, auth)
|
|
1097
|
+
_abort.e(err); // abort in-progress connections
|
|
1098
|
+
|
|
1099
|
+
delete conn.is_set;
|
|
1100
|
+
conn.ready = handshake();
|
|
1101
|
+
client.conn_emit('on_disconnect', false===err, err);}
|
|
1102
|
+
|
|
1103
|
+
, abort() {
|
|
1104
|
+
_dfn_ready.e(err); // abort all messages awaiting ready state
|
|
1105
|
+
return conn.reset(err)}
|
|
1106
|
+
|
|
1107
|
+
, async setup(gate, send_u8_pkt, init_msg_loop) {
|
|
1108
|
+
if (conn.is_set) {
|
|
1109
|
+
throw new Error() }// already in-progress
|
|
1110
|
+
|
|
1111
|
+
conn.is_set = true;
|
|
1112
|
+
await gate;
|
|
1113
|
+
|
|
1114
|
+
// setup send/recv MQTT parsing context
|
|
1115
|
+
let mqtt_ctx = client.mqtt_ctx.mqtt_stream();
|
|
1116
|
+
|
|
1117
|
+
{// setup inbound message loop
|
|
1118
|
+
let sess_ctx = {mqtt: client}; // mutable session context
|
|
1119
|
+
let on_mqtt_chunk = u8 => on_mqtt(mqtt_ctx.decode(u8), sess_ctx);
|
|
1120
|
+
init_msg_loop(on_mqtt_chunk, conn);}
|
|
1121
|
+
|
|
1122
|
+
// setup outbound message path and transport connection
|
|
1123
|
+
send_u8_pkt = await send_u8_pkt;
|
|
1124
|
+
_dfn_send0.a(
|
|
1125
|
+
async (type, pkt, key) => {
|
|
1126
|
+
let res = undefined !== key
|
|
1127
|
+
? pkt_future(key) : true;
|
|
1128
|
+
|
|
1129
|
+
await send_u8_pkt(
|
|
1130
|
+
mqtt_ctx.encode_pkt(type, pkt));
|
|
1131
|
+
return res} ); } });
|
|
1132
|
+
|
|
1133
|
+
conn.ready = handshake();
|
|
1134
|
+
return conn
|
|
1135
|
+
|
|
1136
|
+
async function handshake() {
|
|
1137
|
+
_abort = _defer_obj({});
|
|
1138
|
+
|
|
1139
|
+
_keep_alive_ival(0); // clearInterval on keep alive ping
|
|
1140
|
+
_dfn_send0.set(); // reset client._send0 if necessary
|
|
1141
|
+
_dfn_ready.set(); // reset client._send if necessary
|
|
1142
|
+
|
|
1143
|
+
try {
|
|
1144
|
+
// set client._send0 as passtrhough after transport connection
|
|
1145
|
+
client._send0 = await Promise.race([_dfn_send0.p, _abort.p]);
|
|
1146
|
+
|
|
1147
|
+
// set client._send as passtrhough after ready
|
|
1148
|
+
client._send = await Promise.race([_dfn_ready.p, _abort.p]);
|
|
1149
|
+
return true}
|
|
1150
|
+
catch (err) {
|
|
1151
|
+
return false} } }
|
|
1152
|
+
|
|
1153
|
+
const pkt_api ={
|
|
1154
|
+
utf8(u8) {return new TextDecoder('utf-8').decode(u8 || this.payload )}
|
|
1155
|
+
, json(u8) {return JSON.parse( this.utf8(u8) || null )}
|
|
1156
|
+
, text(u8) {return this.utf8(u8)} };
|
|
1157
|
+
|
|
1158
|
+
const opt_default ={
|
|
1159
|
+
sess_stg: globalThis.sessionStorage};
|
|
1169
1160
|
|
|
1170
1161
|
class MQTTCore extends MQTTBase {
|
|
1162
|
+
constructor(opt) {
|
|
1163
|
+
super();
|
|
1164
|
+
this.with(opt);
|
|
1165
|
+
opt ={...opt_default, ...opt};
|
|
1166
|
+
// settings for MQTTCore
|
|
1167
|
+
this.sess_stg = opt.sess_stg;
|
|
1168
|
+
// setup connection and dispatch
|
|
1169
|
+
this.conn = _mqtt_conn(opt, this,
|
|
1170
|
+
this._init_dispatch(opt)); }
|
|
1171
|
+
|
|
1172
|
+
with(fns_ns) {
|
|
1173
|
+
for (let [k,v] of Object.entries(fns_ns)) {
|
|
1174
|
+
if (_isfn(v)) {this[k] = v;} }
|
|
1175
|
+
return this}
|
|
1176
|
+
|
|
1177
|
+
|
|
1171
1178
|
static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
|
|
1172
|
-
let
|
|
1173
|
-
|
|
1179
|
+
let klass = class extends this {};
|
|
1180
|
+
klass.prototype.mqtt_ctx =
|
|
1174
1181
|
mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
|
|
1175
|
-
return
|
|
1182
|
+
return klass}
|
|
1183
|
+
|
|
1184
|
+
|
|
1185
|
+
async conn_emit(evt, arg, err_arg) {
|
|
1186
|
+
this.log_conn?.(evt, arg, err_arg);
|
|
1187
|
+
try {
|
|
1188
|
+
let fn_evt = this[await evt]; // microtask break using `await evt`
|
|
1189
|
+
if (fn_evt) {
|
|
1190
|
+
await fn_evt.call(this, this, arg, err_arg);}
|
|
1191
|
+
else if (err_arg) {throw err_arg} }
|
|
1192
|
+
catch (err) {
|
|
1193
|
+
this.on_error(err, evt);} }
|
|
1194
|
+
|
|
1195
|
+
on_error(err, evt) {
|
|
1196
|
+
console.warn('[[u8-mqtt error: %s]]', evt, err); }
|
|
1197
|
+
log_conn(evt, arg, err_arg) {
|
|
1198
|
+
console.info('[[u8-mqtt conn: %s]]', evt, arg, err_arg); }
|
|
1176
1199
|
|
|
1177
1200
|
|
|
1178
1201
|
// automatic Client Id for connect()
|
|
1179
1202
|
init_client_id(parts=['u8-mqtt--','']) {
|
|
1180
|
-
let sess_stg=this.sess_stg;
|
|
1203
|
+
let sess_stg = this.sess_stg;
|
|
1181
1204
|
let key, cid = sess_stg?.getItem(key=parts.join(' '));
|
|
1182
1205
|
if (! cid) {
|
|
1183
1206
|
cid = parts.join(Math.random().toString(36).slice(2));
|
|
1184
1207
|
sess_stg?.setItem(key, cid);}
|
|
1185
1208
|
return cid}
|
|
1186
1209
|
|
|
1187
|
-
get sess_stg() {return globalThis.sessionStorage}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
//on_error(err, evt) ::
|
|
1191
|
-
// console.warn @ '[[u8-mqtt error: %s]]', evt, err
|
|
1192
|
-
|
|
1193
|
-
//log_conn(evt, arg, err_arg) ::
|
|
1194
|
-
// console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
|
|
1195
1210
|
|
|
1196
1211
|
on_live(client, is_reconnect) {
|
|
1197
1212
|
if (is_reconnect) {
|
|
1198
1213
|
return client.connect()} }
|
|
1199
1214
|
|
|
1200
|
-
//
|
|
1215
|
+
// on_ready(client) ::
|
|
1216
|
+
// on_reconnect(client) ::
|
|
1217
|
+
on_disconnect(client, intentional) {
|
|
1218
|
+
if (! intentional) {
|
|
1219
|
+
return client.on_reconnect?.()} }
|
|
1201
1220
|
|
|
1202
1221
|
_use_conn(fn_reconnect) {
|
|
1203
1222
|
return (this.reconnect = fn_reconnect)?.()}
|
|
@@ -1209,27 +1228,20 @@ class MQTTCore extends MQTTBase {
|
|
|
1209
1228
|
.then(this.reconnect)
|
|
1210
1229
|
.then(opt.reconnect, opt.error);} }) }
|
|
1211
1230
|
|
|
1212
|
-
on_disconnect(client, intentional) {
|
|
1213
|
-
if (! intentional) {
|
|
1214
|
-
return client.on_reconnect?.()} }
|
|
1215
|
-
|
|
1216
1231
|
delay(ms) {
|
|
1217
1232
|
return new Promise(done => setTimeout(done, ms)) }
|
|
1218
1233
|
|
|
1219
1234
|
with_async_iter(async_iter, write_u8_pkt) {
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
catch (err) {
|
|
1231
|
-
this._conn_.reset(err);} })());
|
|
1232
|
-
|
|
1235
|
+
this.conn.setup(async_iter,
|
|
1236
|
+
write_u8_pkt,
|
|
1237
|
+
async (on_mqtt_chunk, conn) => {
|
|
1238
|
+
try {
|
|
1239
|
+
async_iter = await async_iter;
|
|
1240
|
+
for await (let chunk of async_iter)
|
|
1241
|
+
on_mqtt_chunk(chunk);
|
|
1242
|
+
conn.reset();}
|
|
1243
|
+
catch (err) {
|
|
1244
|
+
conn.reset(err);} } );
|
|
1233
1245
|
return this}
|
|
1234
1246
|
|
|
1235
1247
|
|
|
@@ -1300,33 +1312,30 @@ class MQTTCore extends MQTTBase {
|
|
|
1300
1312
|
|
|
1301
1313
|
websock.binaryType = 'arraybuffer';
|
|
1302
1314
|
|
|
1303
|
-
let
|
|
1315
|
+
let ws_ready, readyState = websock.readyState;
|
|
1304
1316
|
if (1 !== readyState) {
|
|
1305
1317
|
if (0 !== readyState) {
|
|
1306
|
-
throw new Error('
|
|
1307
|
-
|
|
1308
|
-
ready = new Promise(fn => websock.onopen = fn); }
|
|
1318
|
+
throw new Error('WS readyState') }
|
|
1309
1319
|
|
|
1320
|
+
ws_ready = new Promise(ready => websock.onopen = ready); }
|
|
1310
1321
|
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
, websock.send(u8_pkt)) );
|
|
1322
|
+
this.conn.setup(ws_ready,
|
|
1323
|
+
u8_pkt => websock.send(u8_pkt),
|
|
1324
|
+
(on_mqtt_chunk, conn) => {
|
|
1325
|
+
websock.onmessage = evt =>(
|
|
1326
|
+
on_mqtt_chunk(new Uint8Array(evt.data)) );
|
|
1317
1327
|
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
err.reason = evt.reason;}
|
|
1328
|
+
websock.onclose = evt => {
|
|
1329
|
+
if (! evt.wasClean) {
|
|
1330
|
+
var err = new Error('websocket close');
|
|
1331
|
+
err.code = evt.code;
|
|
1332
|
+
err.reason = evt.reason;}
|
|
1324
1333
|
|
|
1325
|
-
|
|
1334
|
+
conn.reset(err);}; } );
|
|
1326
1335
|
|
|
1327
1336
|
return this} }
|
|
1328
1337
|
|
|
1329
|
-
const version = '0.
|
|
1338
|
+
const version = '0.6.0-node';
|
|
1330
1339
|
|
|
1331
1340
|
const MQTTClient_v4 = /* #__PURE__ */
|
|
1332
1341
|
with_topic_path_router(
|