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