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