u8-mqtt 0.5.3 → 0.6.1
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 +1 -1
- package/cjs/basic-v4.cjs +283 -277
- package/cjs/basic-v4.cjs.map +1 -1
- package/cjs/basic-v5.cjs +297 -281
- package/cjs/basic-v5.cjs.map +1 -1
- package/cjs/full-v4.cjs +295 -289
- package/cjs/full-v4.cjs.map +1 -1
- package/cjs/full-v5.cjs +309 -293
- package/cjs/full-v5.cjs.map +1 -1
- package/cjs/index.cjs +310 -295
- package/cjs/index.cjs.map +1 -1
- package/cjs/v4.cjs +295 -289
- package/cjs/v4.cjs.map +1 -1
- package/cjs/v5.cjs +309 -293
- 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 +33 -61
- package/code/core.jsy +73 -51
- package/code/router_path.jsy +2 -1
- package/esm/basic-v4.js +283 -277
- package/esm/basic-v4.js.map +1 -1
- package/esm/basic-v5.js +297 -281
- package/esm/basic-v5.js.map +1 -1
- package/esm/deno/basic-v4.js +287 -281
- package/esm/deno/basic-v4.js.map +1 -1
- package/esm/deno/basic-v5.js +301 -285
- package/esm/deno/basic-v5.js.map +1 -1
- package/esm/deno/full-v4.js +299 -293
- package/esm/deno/full-v4.js.map +1 -1
- package/esm/deno/full-v5.js +313 -297
- package/esm/deno/full-v5.js.map +1 -1
- package/esm/deno/index.js +314 -298
- package/esm/deno/index.js.map +1 -1
- package/esm/deno/v4.js +299 -293
- package/esm/deno/v4.js.map +1 -1
- package/esm/deno/v5.js +313 -297
- package/esm/deno/v5.js.map +1 -1
- package/esm/full-v4.js +295 -289
- package/esm/full-v4.js.map +1 -1
- package/esm/full-v5.js +309 -293
- package/esm/full-v5.js.map +1 -1
- package/esm/index.js +310 -294
- package/esm/index.js.map +1 -1
- package/esm/node/basic-v4.js +283 -277
- package/esm/node/basic-v4.js.map +1 -1
- package/esm/node/basic-v4.mjs +283 -277
- package/esm/node/basic-v4.mjs.map +1 -1
- package/esm/node/basic-v5.js +297 -281
- package/esm/node/basic-v5.js.map +1 -1
- package/esm/node/basic-v5.mjs +297 -281
- package/esm/node/basic-v5.mjs.map +1 -1
- package/esm/node/full-v4.js +295 -289
- package/esm/node/full-v4.js.map +1 -1
- package/esm/node/full-v4.mjs +295 -289
- package/esm/node/full-v4.mjs.map +1 -1
- package/esm/node/full-v5.js +309 -293
- package/esm/node/full-v5.js.map +1 -1
- package/esm/node/full-v5.mjs +309 -293
- package/esm/node/full-v5.mjs.map +1 -1
- package/esm/node/index.js +310 -294
- package/esm/node/index.js.map +1 -1
- package/esm/node/index.mjs +310 -294
- package/esm/node/index.mjs.map +1 -1
- package/esm/node/v4.js +295 -289
- package/esm/node/v4.js.map +1 -1
- package/esm/node/v4.mjs +295 -289
- package/esm/node/v4.mjs.map +1 -1
- package/esm/node/v5.js +309 -293
- package/esm/node/v5.js.map +1 -1
- package/esm/node/v5.mjs +309 -293
- package/esm/node/v5.mjs.map +1 -1
- package/esm/v4.js +295 -289
- package/esm/v4.js.map +1 -1
- package/esm/v5.js +309 -293
- package/esm/v5.js.map +1 -1
- package/esm/web/basic-v4.js +283 -277
- 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 +297 -281
- 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 +295 -289
- package/esm/web/full-v4.js.map +1 -1
- package/esm/web/full-v4.min.js +1 -1
- 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 +309 -293
- package/esm/web/full-v5.js.map +1 -1
- package/esm/web/full-v5.min.js +1 -1
- 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 +310 -294
- 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 +295 -289
- 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 +309 -293
- 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/deno/full-v4.js
CHANGED
|
@@ -721,6 +721,23 @@ function parse(str, loose) {
|
|
|
721
721
|
};
|
|
722
722
|
}
|
|
723
723
|
|
|
724
|
+
const _isfn = v => typeof v === 'function';
|
|
725
|
+
const _isstr = v => typeof v === 'string';
|
|
726
|
+
|
|
727
|
+
function _interval(fn_callback) {
|
|
728
|
+
let tid;
|
|
729
|
+
return (( td ) => {
|
|
730
|
+
tid = clearInterval(tid);
|
|
731
|
+
if (td) {
|
|
732
|
+
tid = setInterval(fn_callback, 1000 * td);
|
|
733
|
+
|
|
734
|
+
// ensure the interval allows the Deno event loop to exit
|
|
735
|
+
Deno.unrefTimer(tid);
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
return true} }) }
|
|
740
|
+
|
|
724
741
|
/*
|
|
725
742
|
class AbstractTopicRouter ::
|
|
726
743
|
async invoke(pkt, ctx) ::
|
|
@@ -811,7 +828,7 @@ function mqtt_topic_path_router() {
|
|
|
811
828
|
let fn = args.pop();
|
|
812
829
|
let priority = args.pop();
|
|
813
830
|
|
|
814
|
-
if (
|
|
831
|
+
if (! _isfn(fn)) {
|
|
815
832
|
if (fn) {throw new TypeError()}
|
|
816
833
|
fn = _ignore;}
|
|
817
834
|
|
|
@@ -976,238 +993,108 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
|
|
|
976
993
|
}
|
|
977
994
|
}
|
|
978
995
|
|
|
979
|
-
function
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
,
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
_q_ready = ao_defer_v();
|
|
1025
|
-
client.conn_emit('on_ready');}
|
|
1026
|
-
|
|
1027
|
-
return res}
|
|
1028
|
-
|
|
1029
|
-
, is_set: (() =>!! _send_mqtt_pkt)
|
|
1030
|
-
, set(mqtt_ctx, send_u8_pkt) {
|
|
1031
|
-
if (_send_mqtt_pkt) {
|
|
1032
|
-
throw new Error('Already connected')}
|
|
1033
|
-
|
|
1034
|
-
mqtt_ctx = mqtt_ctx.mqtt_stream();
|
|
1035
|
-
let sess_ctx = {mqtt: client};
|
|
1036
|
-
let on_mqtt_chunk = u8_buf =>
|
|
1037
|
-
on_mqtt(mqtt_ctx.decode(u8_buf), sess_ctx);
|
|
1038
|
-
|
|
1039
|
-
_send_mqtt_pkt = async (type, pkt, key) => {
|
|
1040
|
-
let res = undefined !== key
|
|
1041
|
-
? pkt_future(key) : true;
|
|
1042
|
-
|
|
1043
|
-
await send_u8_pkt(
|
|
1044
|
-
mqtt_ctx.encode_pkt(type, pkt));
|
|
996
|
+
async function _mqtt_cmd_evt(target, answer, pkt, ctx) {
|
|
997
|
+
/* target : on_mqtt_type = {
|
|
998
|
+
mqtt_pkt(pkt, ctx) {}, // generic
|
|
999
|
+
|
|
1000
|
+
mqtt_auth(pkt, ctx) {},
|
|
1001
|
+
mqtt_connect(pkt, ctx) {},
|
|
1002
|
+
mqtt_connack(pkt, ctx) {},
|
|
1003
|
+
mqtt_disconnect(pkt, ctx) {},
|
|
1004
|
+
|
|
1005
|
+
mqtt_publish(pkt, ctx) {},
|
|
1006
|
+
mqtt_subscribe(pkt, ctx) {},
|
|
1007
|
+
mqtt_unsubscribe(pkt, ctx) {},
|
|
1008
|
+
|
|
1009
|
+
mqtt_pingreq(pkt, ctx) {},
|
|
1010
|
+
mqtt_pingresp(pkt, ctx) {},
|
|
1011
|
+
} */
|
|
1012
|
+
|
|
1013
|
+
let pkt_fn = target[`mqtt_${pkt.type}`] || target.mqtt_pkt;
|
|
1014
|
+
await pkt_fn?.call(target, pkt, ctx);}
|
|
1015
|
+
|
|
1016
|
+
function _mqtt_cmd_type(target, answer, pkt, ctx) {
|
|
1017
|
+
answer(pkt.type, pkt);
|
|
1018
|
+
_mqtt_cmd_evt(target, answer, pkt, ctx);}
|
|
1019
|
+
|
|
1020
|
+
function _mqtt_cmd_id(target, answer, pkt) {
|
|
1021
|
+
answer(pkt.pkt_id, pkt);}
|
|
1022
|
+
|
|
1023
|
+
|
|
1024
|
+
const _mqtt_cmdids =[
|
|
1025
|
+
_ => {} // 0x0 reserved
|
|
1026
|
+
, _mqtt_cmd_evt // 0x1 connect
|
|
1027
|
+
, _mqtt_cmd_type // 0x2 connack
|
|
1028
|
+
, _mqtt_cmd_evt // 0x3 publish
|
|
1029
|
+
, _mqtt_cmd_id // 0x4 puback
|
|
1030
|
+
, _mqtt_cmd_id // 0x5 pubrec
|
|
1031
|
+
, _mqtt_cmd_id // 0x6 pubrel
|
|
1032
|
+
, _mqtt_cmd_id // 0x7 pubcomp
|
|
1033
|
+
, _mqtt_cmd_evt // 0x8 subscribe
|
|
1034
|
+
, _mqtt_cmd_id // 0x9 suback
|
|
1035
|
+
, _mqtt_cmd_evt // 0xa unsubscribe
|
|
1036
|
+
, _mqtt_cmd_id // 0xb unsuback
|
|
1037
|
+
, _mqtt_cmd_type // 0xc pingreq
|
|
1038
|
+
, _mqtt_cmd_type // 0xd pingresp
|
|
1039
|
+
, _mqtt_cmd_evt // 0xe disconnect
|
|
1040
|
+
, _mqtt_cmd_type ];// 0xf auth
|
|
1045
1041
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
// call client.on_live in next promise microtask
|
|
1051
|
-
client.conn_emit('on_live', _has_connected);
|
|
1052
|
-
return on_mqtt_chunk} } }
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
function _ping_interval(send_ping) {
|
|
1056
|
-
let tid;
|
|
1057
|
-
return (( td ) => {
|
|
1058
|
-
tid = clearInterval(tid);
|
|
1059
|
-
if (td) {
|
|
1060
|
-
tid = setInterval(send_ping, 1000 * td);
|
|
1061
|
-
|
|
1062
|
-
// ensure the interval allows the Deno event loop to exit
|
|
1063
|
-
Deno.unrefTimer(tid);
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
return true} }) }
|
|
1042
|
+
function _mqtt_dispatch(opt, target) {
|
|
1043
|
+
let hashbelt=[], rotate_ts=0;
|
|
1044
|
+
// default rotate at 1s across 5 buckets
|
|
1045
|
+
let { td: rotate_td=1000, n: rotate_n=5 } = opt?.rotate || {};
|
|
1068
1046
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1047
|
+
// Promise / future scaffolding
|
|
1048
|
+
let _pkt_id=100, _ftr_key; // use _ftr_key to reuse _by_key closure
|
|
1049
|
+
let _ftr_by_key = fn_answer => hashbelt[0].set(_ftr_key, fn_answer);
|
|
1072
1050
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1051
|
+
on_mqtt([]); // init hashbelt and rotate_ts
|
|
1052
|
+
return [on_mqtt, pkt_future]
|
|
1075
1053
|
|
|
1076
|
-
let _tmp_; // use _tmp_ to reuse _by_key closure
|
|
1077
|
-
let _by_key = answer_monad =>
|
|
1078
|
-
hashbelt[0].set(_tmp_, answer_monad);
|
|
1079
1054
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
_tmp_ = pkt_or_key.pkt_id = _pkt_id;}
|
|
1055
|
+
function pkt_future(pkt_or_key) {
|
|
1056
|
+
if (! _isstr(pkt_or_key)) {
|
|
1057
|
+
_pkt_id = (_pkt_id + 1) & 0xffff; // 16-bit unsigned short
|
|
1058
|
+
_ftr_key = pkt_or_key.pkt_id = _pkt_id;}
|
|
1059
|
+
else _ftr_key = pkt_or_key;
|
|
1086
1060
|
|
|
1087
|
-
|
|
1061
|
+
return new Promise(_ftr_by_key)}
|
|
1088
1062
|
|
|
1089
|
-
|
|
1090
|
-
for (let map of
|
|
1091
|
-
let
|
|
1092
|
-
if (
|
|
1063
|
+
function answer(key, pkt) {
|
|
1064
|
+
for (let map of hashbelt) {
|
|
1065
|
+
let fn_answer = map.get(key);
|
|
1066
|
+
if (fn_answer) {
|
|
1093
1067
|
map.delete(key);
|
|
1094
1068
|
|
|
1095
|
-
|
|
1069
|
+
fn_answer([pkt, /*err*/]); // option/maybe monad
|
|
1096
1070
|
return true} }
|
|
1097
1071
|
return false}
|
|
1098
1072
|
|
|
1099
|
-
, rotate_belt(n) {
|
|
1100
|
-
let {hashbelt} = this;
|
|
1101
|
-
hashbelt.unshift(new Map());
|
|
1102
|
-
for (let old of hashbelt.splice(n || 5)) {
|
|
1103
|
-
for (let answer_monad of old.values()) {
|
|
1104
|
-
answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
|
|
1105
|
-
|
|
1106
|
-
, cmdids: ((() => {
|
|
1107
|
-
return [
|
|
1108
|
-
(() =>{} )// 0x0 reserved
|
|
1109
|
-
, by_evt // 0x1 connect
|
|
1110
|
-
, by_type // 0x2 connack
|
|
1111
|
-
, by_evt // 0x3 publish
|
|
1112
|
-
, by_id // 0x4 puback
|
|
1113
|
-
, by_id // 0x5 pubrec
|
|
1114
|
-
, by_id // 0x6 pubrel
|
|
1115
|
-
, by_id // 0x7 pubcomp
|
|
1116
|
-
, by_evt // 0x8 subscribe
|
|
1117
|
-
, by_id // 0x9 suback
|
|
1118
|
-
, by_evt // 0xa unsubscribe
|
|
1119
|
-
, by_id // 0xb unsuback
|
|
1120
|
-
, by_type // 0xc pingreq
|
|
1121
|
-
, by_type // 0xd pingresp
|
|
1122
|
-
, by_evt // 0xe disconnect
|
|
1123
|
-
, by_type ]// 0xf auth
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
function by_id(disp, pkt) {
|
|
1127
|
-
disp.answer(pkt.pkt_id, pkt); }
|
|
1128
|
-
|
|
1129
|
-
function by_type(disp, pkt, ctx) {
|
|
1130
|
-
disp.answer(pkt.type, pkt);
|
|
1131
|
-
by_evt(disp, pkt, ctx);}
|
|
1132
|
-
|
|
1133
|
-
async function by_evt({target}, pkt, ctx) {
|
|
1134
|
-
let fn = target[`mqtt_${pkt.type}`]
|
|
1135
|
-
|| target.mqtt_pkt;
|
|
1136
|
-
|
|
1137
|
-
await fn?.call(target, pkt, ctx);} })()) };
|
|
1138
|
-
|
|
1139
|
-
/*
|
|
1140
|
-
on_mqtt_type = {
|
|
1141
|
-
mqtt_auth(pkt, ctx) ::
|
|
1142
|
-
mqtt_connect(pkt, ctx) ::
|
|
1143
|
-
mqtt_connack(pkt, ctx) ::
|
|
1144
|
-
mqtt_disconnect(pkt, ctx) ::
|
|
1145
|
-
|
|
1146
|
-
mqtt_publish(pkt, ctx)
|
|
1147
|
-
mqtt_subscribe(pkt, ctx) ::
|
|
1148
|
-
mqtt_unsubscribe(pkt, ctx) ::
|
|
1149
|
-
|
|
1150
|
-
mqtt_pingreq(pkt, ctx) ::
|
|
1151
|
-
mqtt_pingresp(pkt, ctx) ::
|
|
1152
|
-
}
|
|
1153
|
-
*/
|
|
1154
|
-
|
|
1155
|
-
function _mqtt_dispatch(opt, target) {
|
|
1156
|
-
let _disp_ = _mqtt_cmdid_dispatch.create(target);
|
|
1157
|
-
let { cmdids } = _disp_;
|
|
1158
|
-
|
|
1159
|
-
// default rotate at 1s across 5 buckets
|
|
1160
|
-
let { td: rotate_td=1000, n: rotate_n=5 } =
|
|
1161
|
-
opt && opt.rotate || {};
|
|
1162
|
-
|
|
1163
|
-
let rotate_ts = rotate_td + Date.now();
|
|
1164
|
-
|
|
1165
|
-
return [on_mqtt,
|
|
1166
|
-
_disp_.bind_pkt_future()]
|
|
1167
|
-
|
|
1168
1073
|
function on_mqtt(pkt_list, ctx) {
|
|
1169
1074
|
for (let pkt of pkt_list) {
|
|
1170
|
-
|
|
1075
|
+
_mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
|
|
1171
1076
|
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1077
|
+
// rotate after rotate_ts
|
|
1078
|
+
let now = Date.now();
|
|
1079
|
+
if (now > rotate_ts) {
|
|
1080
|
+
rotate_ts = rotate_td + now;
|
|
1081
|
+
hashbelt.unshift(new Map());
|
|
1082
|
+
while (hashbelt.length > rotate_n) {
|
|
1083
|
+
for (let fn_answer of hashbelt.pop().values()) {
|
|
1084
|
+
fn_answer([/*pkt*/, 'expired']); } } } } }// option/maybe monad
|
|
1175
1085
|
|
|
1176
1086
|
class MQTTError extends Error {
|
|
1177
1087
|
constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
|
|
1088
|
+
// use hex-encoded reasons to match MQTT spec documentation
|
|
1178
1089
|
super(`[0x${reason.toString(16)}] ${reason.reason}`);
|
|
1179
1090
|
this.mqtt_pkt = mqtt_pkt;
|
|
1180
1091
|
this.reason = reason;} }
|
|
1181
1092
|
|
|
1182
1093
|
class MQTTBase {
|
|
1183
|
-
constructor(opt={}) {
|
|
1184
|
-
this.with(opt);
|
|
1185
|
-
this._conn_ = _mqtt_conn(this,
|
|
1186
|
-
this._init_dispatch(opt, this)); }
|
|
1187
|
-
|
|
1188
|
-
with(fns_ns) {
|
|
1189
|
-
for (let [k,v] of Object.entries(fns_ns)) {
|
|
1190
|
-
if ('function' === typeof v) {this[k] = v;} }
|
|
1191
|
-
return this}
|
|
1192
|
-
|
|
1193
|
-
async conn_emit(evt, arg, err_arg) {
|
|
1194
|
-
this.log_conn?.(evt, arg, err_arg);
|
|
1195
|
-
try {
|
|
1196
|
-
let fn_evt = this[await evt]; // microtask break using `await evt`
|
|
1197
|
-
if (fn_evt) {
|
|
1198
|
-
await fn_evt.call(this, this, arg, err_arg);}
|
|
1199
|
-
else if (err_arg) {throw err_arg} }
|
|
1200
|
-
catch (err) {
|
|
1201
|
-
this.on_error(err, evt);} }
|
|
1202
|
-
|
|
1203
|
-
on_error(err, evt) {
|
|
1204
|
-
console.warn('[[u8-mqtt error: %s]]', evt, err); }
|
|
1205
|
-
|
|
1206
1094
|
// Handshaking Packets
|
|
1207
|
-
|
|
1208
1095
|
async connect(pkt={}) {
|
|
1209
1096
|
let cid = pkt.client_id;
|
|
1210
|
-
if (
|
|
1097
|
+
if (! _isstr(cid)) {
|
|
1211
1098
|
// see init_client_id implementation in core.jsy
|
|
1212
1099
|
pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
|
|
1213
1100
|
this.client_id = cid;
|
|
@@ -1215,23 +1102,20 @@ class MQTTBase {
|
|
|
1215
1102
|
if (null == pkt.keep_alive) {
|
|
1216
1103
|
pkt.keep_alive = 60;}
|
|
1217
1104
|
|
|
1218
|
-
let
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
throw new this.MQTTError(res[0])}
|
|
1223
|
-
|
|
1224
|
-
// TODO: merge with server's keep_alive frequency
|
|
1225
|
-
this._conn_.ping(pkt.keep_alive);
|
|
1226
|
-
return res}
|
|
1105
|
+
let response = await this._send0('connect', pkt, 'connack');
|
|
1106
|
+
if (0 != response[0].reason) {// compare to 0 to coerce to number
|
|
1107
|
+
throw new this.MQTTError(response[0])}
|
|
1108
|
+
return this.conn.on_conn(pkt, response)}
|
|
1227
1109
|
|
|
1228
1110
|
async disconnect(pkt={}) {
|
|
1229
|
-
let
|
|
1230
|
-
this.
|
|
1231
|
-
return res}
|
|
1111
|
+
let response = await this._send0('disconnect', pkt);
|
|
1112
|
+
return this.conn.on_dis(pkt, response)}
|
|
1232
1113
|
|
|
1233
|
-
auth(pkt={}) {
|
|
1234
|
-
|
|
1114
|
+
async auth(pkt={}) {
|
|
1115
|
+
let response = await this._send0('auth', pkt, 'auth');
|
|
1116
|
+
if (response[0].reason) {
|
|
1117
|
+
throw new this.MQTTError(response[0])}
|
|
1118
|
+
return this.conn.on_auth(pkt, response)}
|
|
1235
1119
|
|
|
1236
1120
|
ping() {return this._send('pingreq', null, 'pingresp')}
|
|
1237
1121
|
puback({pkt_id}) {return this._send('puback', {pkt_id})}
|
|
@@ -1268,20 +1152,18 @@ class MQTTBase {
|
|
|
1268
1152
|
// alias: publish -- because 'pub' is shorter for semantic aliases above
|
|
1269
1153
|
async pub(pkt, pub_opt) {
|
|
1270
1154
|
if (undefined === pkt.payload) {
|
|
1271
|
-
if (
|
|
1155
|
+
if (_isfn(pub_opt)) {
|
|
1156
|
+
// pub_opt as a function is fn_encode value
|
|
1272
1157
|
pub_opt = {fn_encode: pub_opt};}
|
|
1273
1158
|
|
|
1274
|
-
let
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
// return a single-value closure to publish packets
|
|
1281
|
-
return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
|
|
1159
|
+
let msg = pkt.msg, fn_encode = pub_opt?.fn_encode;
|
|
1160
|
+
if (null == msg || _isfn(msg)) {
|
|
1161
|
+
// when msg is a function, return closure using fn_encode
|
|
1162
|
+
if (msg) {pub_opt = {...pub_opt, fn_encode: msg};}
|
|
1163
|
+
// return a single-value closure to publish packets
|
|
1164
|
+
return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
|
|
1282
1165
|
|
|
1283
1166
|
// Encode payload from msg; fn_encode allows alternative to JSON.stringify
|
|
1284
|
-
let {fn_encode} = pub_opt || {};
|
|
1285
1167
|
pkt.payload = fn_encode
|
|
1286
1168
|
? await fn_encode(msg)
|
|
1287
1169
|
: JSON.stringify(msg);}
|
|
@@ -1293,31 +1175,31 @@ class MQTTBase {
|
|
|
1293
1175
|
pkt = pub_opt.xform(pkt) || pkt;} }
|
|
1294
1176
|
|
|
1295
1177
|
return this._send('publish', pkt,
|
|
1296
|
-
pkt.qos ? pkt :
|
|
1178
|
+
pkt.qos ? pkt : null ) }// key
|
|
1297
1179
|
|
|
1298
1180
|
|
|
1299
1181
|
// Internal API
|
|
1300
1182
|
|
|
1301
|
-
/* async
|
|
1183
|
+
/* async _send0(type, pkt) -- provided by conn and transport */
|
|
1184
|
+
/* async _send(type, pkt) -- provided by conn and transport */
|
|
1302
1185
|
|
|
1303
1186
|
_init_dispatch(opt) {
|
|
1304
1187
|
this.constructor?._once_();
|
|
1305
1188
|
let target ={__proto__: opt.on_mqtt_type};
|
|
1306
1189
|
target.mqtt_publish ||=
|
|
1307
1190
|
this._init_router?.(opt, this, target);
|
|
1308
|
-
return _mqtt_dispatch(
|
|
1191
|
+
return _mqtt_dispatch(opt, target)}
|
|
1309
1192
|
|
|
1310
1193
|
static _aliases() {
|
|
1311
1194
|
return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
|
|
1312
1195
|
|
|
1313
|
-
static _once_(
|
|
1314
|
-
|
|
1315
|
-
|
|
1196
|
+
static _once_(klass=this) {
|
|
1197
|
+
klass._once_ = _=>0;
|
|
1198
|
+
var alias, name, p = klass.prototype;
|
|
1316
1199
|
p.MQTTError = MQTTError;
|
|
1317
|
-
for (
|
|
1318
|
-
alias = alias.split(':');
|
|
1319
|
-
|
|
1320
|
-
if (fn) {p[alias[0]] = fn;} } } }
|
|
1200
|
+
for (alias of klass._aliases().split(/\s+/)) {
|
|
1201
|
+
[alias, name] = alias.split(':');
|
|
1202
|
+
p[alias] = p[name];} } }
|
|
1321
1203
|
|
|
1322
1204
|
|
|
1323
1205
|
function _as_topics(pkt, ex, topic_prefix) {
|
|
@@ -1342,37 +1224,167 @@ function _as_topics(pkt, ex, topic_prefix) {
|
|
|
1342
1224
|
pkt.topics = pkt.topics.map(_prefix_topics);}
|
|
1343
1225
|
return pkt}
|
|
1344
1226
|
|
|
1345
|
-
const
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1227
|
+
const _defer_obj = o =>(
|
|
1228
|
+
o.p = new Promise((a,e) => { o.a=a; o.e=e; })
|
|
1229
|
+
, o);
|
|
1230
|
+
|
|
1231
|
+
function _dfn_reset(client, attr, fn_after) {
|
|
1232
|
+
// a resetable deferred for a function
|
|
1233
|
+
let self = {set}, afn = async (...args) => (await self.p)(...args);
|
|
1234
|
+
return set()
|
|
1235
|
+
|
|
1236
|
+
function set() {
|
|
1237
|
+
if (afn !== client[attr]) {
|
|
1238
|
+
_defer_obj(self).p.then(fn_after, _=>0);
|
|
1239
|
+
client[attr] = afn;}
|
|
1240
|
+
return self} }
|
|
1241
|
+
|
|
1242
|
+
function _mqtt_conn(opt, client, [on_mqtt, pkt_future]) {
|
|
1243
|
+
let _abort;
|
|
1244
|
+
let _dfn_send0 = _dfn_reset(client, '_send0', // client._send0 getter/setter
|
|
1245
|
+
_=> client.conn_emit('on_live', conn.has_connected));
|
|
1246
|
+
let _dfn_ready = _dfn_reset(client, '_send', // client._send getter/setter
|
|
1247
|
+
_=> client.conn_emit('on_ready'));
|
|
1248
|
+
let _keep_alive_ival = _interval (() =>client._send0('pingreq') );// resettable interval for keep_alive ping
|
|
1249
|
+
|
|
1250
|
+
let conn = Object.create({
|
|
1251
|
+
ping: (td=conn.keep_alive) => _keep_alive_ival(td)
|
|
1252
|
+
|
|
1253
|
+
, on_conn(pkt, response) {
|
|
1254
|
+
conn.has_connected = true;
|
|
1255
|
+
conn.keep_alive = opt.keep_alive || response[0].props?.server_keep_alive || pkt.keep_alive;
|
|
1256
|
+
client.conn_emit('on_conn');
|
|
1257
|
+
return opt.use_auth
|
|
1258
|
+
? response // wait on enhanced authentication step
|
|
1259
|
+
: conn.on_auth(null, response) }// otherwise, connect is also auth
|
|
1260
|
+
|
|
1261
|
+
, on_auth(pkt, response) {
|
|
1262
|
+
_dfn_ready.a(_dfn_send0.p);
|
|
1263
|
+
if (0 != opt.keep_alive) {
|
|
1264
|
+
conn.ping();}
|
|
1265
|
+
client.conn_emit('on_auth', !pkt);
|
|
1266
|
+
return response}
|
|
1267
|
+
|
|
1268
|
+
, on_dis(pkt, response) {
|
|
1269
|
+
conn.reset(false);
|
|
1270
|
+
return response}
|
|
1271
|
+
|
|
1272
|
+
, reset(err) {
|
|
1273
|
+
if (err) {
|
|
1274
|
+
_dfn_send0.e(err); }// send error to uses of _send0 (connect, auth)
|
|
1275
|
+
_abort.e(err); // abort in-progress connections
|
|
1276
|
+
|
|
1277
|
+
delete conn.is_set;
|
|
1278
|
+
conn.ready = handshake();
|
|
1279
|
+
client.conn_emit('on_disconnect', false===err, err);}
|
|
1280
|
+
|
|
1281
|
+
, abort() {
|
|
1282
|
+
_dfn_ready.e(err); // abort all messages awaiting ready state
|
|
1283
|
+
return conn.reset(err)}
|
|
1284
|
+
|
|
1285
|
+
, async setup(gate, send_u8_pkt, init_msg_loop) {
|
|
1286
|
+
if (conn.is_set) {
|
|
1287
|
+
throw new Error() }// already in-progress
|
|
1288
|
+
|
|
1289
|
+
conn.is_set = true;
|
|
1290
|
+
await gate;
|
|
1291
|
+
|
|
1292
|
+
// setup send/recv MQTT parsing context
|
|
1293
|
+
let mqtt_ctx = client.mqtt_ctx.mqtt_stream();
|
|
1294
|
+
|
|
1295
|
+
{// setup inbound message loop
|
|
1296
|
+
let sess_ctx = {mqtt: client}; // mutable session context
|
|
1297
|
+
let on_mqtt_chunk = u8 => on_mqtt(mqtt_ctx.decode(u8), sess_ctx);
|
|
1298
|
+
init_msg_loop(on_mqtt_chunk, conn);}
|
|
1299
|
+
|
|
1300
|
+
// setup outbound message path and transport connection
|
|
1301
|
+
send_u8_pkt = await send_u8_pkt;
|
|
1302
|
+
_dfn_send0.a(
|
|
1303
|
+
async (type, pkt, key) => {
|
|
1304
|
+
let res = undefined !== key
|
|
1305
|
+
? pkt_future(key) : true;
|
|
1306
|
+
|
|
1307
|
+
await send_u8_pkt(
|
|
1308
|
+
mqtt_ctx.encode_pkt(type, pkt));
|
|
1309
|
+
return res} ); } });
|
|
1310
|
+
|
|
1311
|
+
conn.ready = handshake();
|
|
1312
|
+
return conn
|
|
1313
|
+
|
|
1314
|
+
async function handshake() {
|
|
1315
|
+
_abort = _defer_obj({});
|
|
1316
|
+
|
|
1317
|
+
_keep_alive_ival(0); // clearInterval on keep alive ping
|
|
1318
|
+
_dfn_send0.set(); // reset client._send0 if necessary
|
|
1319
|
+
_dfn_ready.set(); // reset client._send if necessary
|
|
1320
|
+
|
|
1321
|
+
try {
|
|
1322
|
+
// set client._send0 as passtrhough after transport connection
|
|
1323
|
+
client._send0 = await Promise.race([_dfn_send0.p, _abort.p]);
|
|
1324
|
+
|
|
1325
|
+
// set client._send as passtrhough after ready
|
|
1326
|
+
client._send = await Promise.race([_dfn_ready.p, _abort.p]);
|
|
1327
|
+
return true}
|
|
1328
|
+
catch (err) {
|
|
1329
|
+
return false} } }
|
|
1330
|
+
|
|
1331
|
+
const pkt_api ={
|
|
1332
|
+
utf8(u8) {return new TextDecoder('utf-8').decode(u8 || this.payload )}
|
|
1333
|
+
, json(u8) {return JSON.parse( this.utf8(u8) || null )}
|
|
1334
|
+
, text(u8) {return this.utf8(u8)} };
|
|
1335
|
+
|
|
1336
|
+
const opt_default ={
|
|
1337
|
+
sess_stg: globalThis.sessionStorage};
|
|
1350
1338
|
|
|
1351
1339
|
class MQTTCore extends MQTTBase {
|
|
1340
|
+
constructor(opt) {
|
|
1341
|
+
opt = {...opt_default, ...opt};
|
|
1342
|
+
super();
|
|
1343
|
+
this.with(opt);
|
|
1344
|
+
// settings for MQTTCore
|
|
1345
|
+
this.sess_stg = opt.sess_stg;
|
|
1346
|
+
// setup connection and dispatch
|
|
1347
|
+
this.conn = _mqtt_conn(opt, this,
|
|
1348
|
+
this._init_dispatch(opt)); }
|
|
1349
|
+
|
|
1350
|
+
with(fns_ns) {
|
|
1351
|
+
for (let [k,v] of Object.entries(fns_ns)) {
|
|
1352
|
+
if (_isfn(v)) {this[k] = v;} }
|
|
1353
|
+
return this}
|
|
1354
|
+
|
|
1355
|
+
|
|
1352
1356
|
static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
|
|
1353
|
-
let
|
|
1354
|
-
|
|
1357
|
+
let klass = class extends this {};
|
|
1358
|
+
klass.prototype.mqtt_ctx =
|
|
1355
1359
|
mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
|
|
1356
|
-
return
|
|
1360
|
+
return klass}
|
|
1361
|
+
|
|
1362
|
+
|
|
1363
|
+
async conn_emit(evt, arg, err_arg) {
|
|
1364
|
+
this.log_conn?.(evt, arg, err_arg);
|
|
1365
|
+
try {
|
|
1366
|
+
let fn_evt = this[await evt]; // microtask break using `await evt`
|
|
1367
|
+
if (fn_evt) {
|
|
1368
|
+
await fn_evt.call(this, this, arg, err_arg);}
|
|
1369
|
+
else if (err_arg) {throw err_arg} }
|
|
1370
|
+
catch (err) {
|
|
1371
|
+
this.on_error(err, evt);} }
|
|
1372
|
+
|
|
1373
|
+
on_error(err, evt) {
|
|
1374
|
+
console.warn('[[u8-mqtt error: %s]]', evt, err); }
|
|
1375
|
+
log_conn(evt, arg, err_arg) {
|
|
1376
|
+
console.info('[[u8-mqtt conn: %s]]', evt, arg, err_arg); }
|
|
1357
1377
|
|
|
1358
1378
|
|
|
1359
1379
|
// automatic Client Id for connect()
|
|
1360
1380
|
init_client_id(parts=['u8-mqtt--','']) {
|
|
1361
|
-
let sess_stg=this.sess_stg;
|
|
1381
|
+
let sess_stg = this.sess_stg;
|
|
1362
1382
|
let key, cid = sess_stg?.getItem(key=parts.join(' '));
|
|
1363
1383
|
if (! cid) {
|
|
1364
1384
|
cid = parts.join(Math.random().toString(36).slice(2));
|
|
1365
1385
|
sess_stg?.setItem(key, cid);}
|
|
1366
1386
|
return cid}
|
|
1367
1387
|
|
|
1368
|
-
get sess_stg() {return globalThis.sessionStorage}
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
//on_error(err, evt) ::
|
|
1372
|
-
// console.warn @ '[[u8-mqtt error: %s]]', evt, err
|
|
1373
|
-
|
|
1374
|
-
//log_conn(evt, arg, err_arg) ::
|
|
1375
|
-
// console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
|
|
1376
1388
|
|
|
1377
1389
|
on_live(client, is_reconnect) {
|
|
1378
1390
|
if (is_reconnect) {
|
|
@@ -1398,19 +1410,16 @@ class MQTTCore extends MQTTBase {
|
|
|
1398
1410
|
return new Promise(done => setTimeout(done, ms)) }
|
|
1399
1411
|
|
|
1400
1412
|
with_async_iter(async_iter, write_u8_pkt) {
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
catch (err) {
|
|
1412
|
-
this._conn_.reset(err);} })());
|
|
1413
|
-
|
|
1413
|
+
this.conn.setup(async_iter,
|
|
1414
|
+
write_u8_pkt,
|
|
1415
|
+
async (on_mqtt_chunk, conn) => {
|
|
1416
|
+
try {
|
|
1417
|
+
async_iter = await async_iter;
|
|
1418
|
+
for await (let chunk of async_iter)
|
|
1419
|
+
on_mqtt_chunk(chunk);
|
|
1420
|
+
conn.reset();}
|
|
1421
|
+
catch (err) {
|
|
1422
|
+
conn.reset(err);} } );
|
|
1414
1423
|
return this}
|
|
1415
1424
|
|
|
1416
1425
|
|
|
@@ -1429,11 +1438,11 @@ class MQTTCore extends MQTTBase {
|
|
|
1429
1438
|
this.with_deno_iter(
|
|
1430
1439
|
Deno.connectTls(opt)) ) }
|
|
1431
1440
|
|
|
1432
|
-
with_deno_iter(
|
|
1441
|
+
with_deno_iter(transport) {
|
|
1433
1442
|
return this.with_async_iter(
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1443
|
+
transport.then(Deno.iter),
|
|
1444
|
+
transport.then(write_stream =>
|
|
1445
|
+
u8_pkt => write_stream.write(u8_pkt)) ) }
|
|
1437
1446
|
|
|
1438
1447
|
_conn_opt([a0, a1, a2]) {
|
|
1439
1448
|
// (port, hostname, options) or (url, options)
|
|
@@ -1481,33 +1490,30 @@ class MQTTCore extends MQTTBase {
|
|
|
1481
1490
|
|
|
1482
1491
|
websock.binaryType = 'arraybuffer';
|
|
1483
1492
|
|
|
1484
|
-
let
|
|
1493
|
+
let ws_ready, readyState = websock.readyState;
|
|
1485
1494
|
if (1 !== readyState) {
|
|
1486
1495
|
if (0 !== readyState) {
|
|
1487
|
-
throw new Error('
|
|
1488
|
-
|
|
1489
|
-
ready = new Promise(fn => websock.onopen = fn); }
|
|
1496
|
+
throw new Error('WS readyState') }
|
|
1490
1497
|
|
|
1498
|
+
ws_ready = new Promise(ready => websock.onopen = ready); }
|
|
1491
1499
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
, websock.send(u8_pkt)) );
|
|
1500
|
+
this.conn.setup(ws_ready,
|
|
1501
|
+
u8_pkt => websock.send(u8_pkt),
|
|
1502
|
+
(on_mqtt_chunk, conn) => {
|
|
1503
|
+
websock.onmessage = evt =>(
|
|
1504
|
+
on_mqtt_chunk(new Uint8Array(evt.data)) );
|
|
1498
1505
|
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
err.reason = evt.reason;}
|
|
1506
|
+
websock.onclose = evt => {
|
|
1507
|
+
if (! evt.wasClean) {
|
|
1508
|
+
var err = new Error('websocket close');
|
|
1509
|
+
err.code = evt.code;
|
|
1510
|
+
err.reason = evt.reason;}
|
|
1505
1511
|
|
|
1506
|
-
|
|
1512
|
+
conn.reset(err);}; } );
|
|
1507
1513
|
|
|
1508
1514
|
return this} }
|
|
1509
1515
|
|
|
1510
|
-
const version = '0.
|
|
1516
|
+
const version = '0.6.1-deno';
|
|
1511
1517
|
|
|
1512
1518
|
const MQTTClient_v4 = /* #__PURE__ */
|
|
1513
1519
|
with_topic_path_router(
|