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