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/v5.js
CHANGED
|
@@ -156,10 +156,18 @@ let mqtt_reader_v5$1 = class mqtt_reader_v5 extends mqtt_reader_v4 {
|
|
|
156
156
|
|
|
157
157
|
let res={}, fork = this.of(buf.subarray(vi, step.k|0));
|
|
158
158
|
while (fork.has_more()) {
|
|
159
|
-
let pt = mqtt_props.get(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
let v, pk = fork.u8(), pt = mqtt_props.get( pk );
|
|
160
|
+
|
|
161
|
+
if (!pt) {
|
|
162
|
+
res.error = `Unknown mqtt_prop enum ${pk}`;
|
|
163
|
+
return res
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
v = fork[pt.type]();
|
|
167
|
+
if (pt.op) // accumulate operation
|
|
168
|
+
v = fork[pt.op](res[pt.name], v);
|
|
169
|
+
|
|
170
|
+
res[pt.name] = v;
|
|
163
171
|
}
|
|
164
172
|
return res
|
|
165
173
|
}
|
|
@@ -293,6 +301,8 @@ class mqtt_writer_v5 extends mqtt_writer_v4 {
|
|
|
293
301
|
let fork = this.of();
|
|
294
302
|
for (let [name, value] of props) {
|
|
295
303
|
let pt = mqtt_props.get(name);
|
|
304
|
+
if (!pt)
|
|
305
|
+
throw new Error(`Unknown mqtt_prop "${name}"`)
|
|
296
306
|
fork[pt.op || 'one'](value, pt);
|
|
297
307
|
}
|
|
298
308
|
this.push(fork.pack());
|
|
@@ -796,6 +806,23 @@ function parse(str, loose) {
|
|
|
796
806
|
};
|
|
797
807
|
}
|
|
798
808
|
|
|
809
|
+
const _isfn = v => typeof v === 'function';
|
|
810
|
+
const _isstr = v => typeof v === 'string';
|
|
811
|
+
|
|
812
|
+
function _interval(fn_callback) {
|
|
813
|
+
let tid;
|
|
814
|
+
return (( td ) => {
|
|
815
|
+
tid = clearInterval(tid);
|
|
816
|
+
if (td) {
|
|
817
|
+
tid = setInterval(fn_callback, 1000 * td);
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
// ensure the interval allows the NodeJS event loop to exit
|
|
823
|
+
tid.unref?.();
|
|
824
|
+
return true} }) }
|
|
825
|
+
|
|
799
826
|
/*
|
|
800
827
|
class AbstractTopicRouter ::
|
|
801
828
|
async invoke(pkt, ctx) ::
|
|
@@ -886,7 +913,7 @@ function mqtt_topic_path_router() {
|
|
|
886
913
|
let fn = args.pop();
|
|
887
914
|
let priority = args.pop();
|
|
888
915
|
|
|
889
|
-
if (
|
|
916
|
+
if (! _isfn(fn)) {
|
|
890
917
|
if (fn) {throw new TypeError()}
|
|
891
918
|
fn = _ignore;}
|
|
892
919
|
|
|
@@ -1036,6 +1063,9 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
|
|
|
1036
1063
|
return _encode_by_type[type]( mqtt_level, pkt ) },
|
|
1037
1064
|
|
|
1038
1065
|
decode_pkt(b0, u8_body) {
|
|
1066
|
+
if (b0.map) // Uint8Array in first arg
|
|
1067
|
+
return mqtt_raw_dispatch(this)(b0)[0]
|
|
1068
|
+
|
|
1039
1069
|
let fn_decode = _decode_by_id[b0>>>4] || _decode_by_id[0];
|
|
1040
1070
|
return fn_decode?.({__proto__: this.pkt_ctx, b0}, u8_body) },
|
|
1041
1071
|
|
|
@@ -1048,262 +1078,129 @@ function mqtt_pkt_ctx(mqtt_level, opts, pkt_ctx) {
|
|
|
1048
1078
|
}
|
|
1049
1079
|
}
|
|
1050
1080
|
|
|
1051
|
-
function
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
,
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
_q_ready = ao_defer_v();
|
|
1097
|
-
client.conn_emit('on_ready');}
|
|
1098
|
-
|
|
1099
|
-
return res}
|
|
1100
|
-
|
|
1101
|
-
, is_set: (() =>!! _send_mqtt_pkt)
|
|
1102
|
-
, set(mqtt_ctx, send_u8_pkt) {
|
|
1103
|
-
if (_send_mqtt_pkt) {
|
|
1104
|
-
throw new Error('Already connected')}
|
|
1081
|
+
async function _mqtt_cmd_evt(target, answer, pkt, ctx) {
|
|
1082
|
+
/* target : on_mqtt_type = {
|
|
1083
|
+
mqtt_pkt(pkt, ctx) {}, // generic
|
|
1084
|
+
|
|
1085
|
+
mqtt_auth(pkt, ctx) {},
|
|
1086
|
+
mqtt_connect(pkt, ctx) {},
|
|
1087
|
+
mqtt_connack(pkt, ctx) {},
|
|
1088
|
+
mqtt_disconnect(pkt, ctx) {},
|
|
1089
|
+
|
|
1090
|
+
mqtt_publish(pkt, ctx) {},
|
|
1091
|
+
mqtt_subscribe(pkt, ctx) {},
|
|
1092
|
+
mqtt_unsubscribe(pkt, ctx) {},
|
|
1093
|
+
|
|
1094
|
+
mqtt_pingreq(pkt, ctx) {},
|
|
1095
|
+
mqtt_pingresp(pkt, ctx) {},
|
|
1096
|
+
} */
|
|
1097
|
+
|
|
1098
|
+
let pkt_fn = target[`mqtt_${pkt.type}`] || target.mqtt_pkt;
|
|
1099
|
+
await pkt_fn?.call(target, pkt, ctx);}
|
|
1100
|
+
|
|
1101
|
+
function _mqtt_cmd_type(target, answer, pkt, ctx) {
|
|
1102
|
+
answer(pkt.type, pkt);
|
|
1103
|
+
_mqtt_cmd_evt(target, answer, pkt, ctx);}
|
|
1104
|
+
|
|
1105
|
+
function _mqtt_cmd_id(target, answer, pkt) {
|
|
1106
|
+
answer(pkt.pkt_id, pkt);}
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
const _mqtt_cmdids =[
|
|
1110
|
+
_ => {} // 0x0 reserved
|
|
1111
|
+
, _mqtt_cmd_evt // 0x1 connect
|
|
1112
|
+
, _mqtt_cmd_type // 0x2 connack
|
|
1113
|
+
, _mqtt_cmd_evt // 0x3 publish
|
|
1114
|
+
, _mqtt_cmd_id // 0x4 puback
|
|
1115
|
+
, _mqtt_cmd_id // 0x5 pubrec
|
|
1116
|
+
, _mqtt_cmd_id // 0x6 pubrel
|
|
1117
|
+
, _mqtt_cmd_id // 0x7 pubcomp
|
|
1118
|
+
, _mqtt_cmd_evt // 0x8 subscribe
|
|
1119
|
+
, _mqtt_cmd_id // 0x9 suback
|
|
1120
|
+
, _mqtt_cmd_evt // 0xa unsubscribe
|
|
1121
|
+
, _mqtt_cmd_id // 0xb unsuback
|
|
1122
|
+
, _mqtt_cmd_type // 0xc pingreq
|
|
1123
|
+
, _mqtt_cmd_type // 0xd pingresp
|
|
1124
|
+
, _mqtt_cmd_evt // 0xe disconnect
|
|
1125
|
+
, _mqtt_cmd_type ];// 0xf auth
|
|
1105
1126
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
_send_mqtt_pkt = async (type, pkt, key) => {
|
|
1112
|
-
let res = undefined !== key
|
|
1113
|
-
? pkt_future(key) : true;
|
|
1114
|
-
|
|
1115
|
-
await send_u8_pkt(
|
|
1116
|
-
mqtt_ctx.encode_pkt(type, pkt));
|
|
1117
|
-
|
|
1118
|
-
return res};
|
|
1119
|
-
|
|
1120
|
-
_q_init[1](_send_mqtt_pkt); // resolve _q_init with _send_mqtt_pkt closure
|
|
1121
|
-
|
|
1122
|
-
// call client.on_live in next promise microtask
|
|
1123
|
-
client.conn_emit('on_live', _has_connected);
|
|
1124
|
-
return on_mqtt_chunk} } }
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
function _ping_interval(send_ping) {
|
|
1128
|
-
let tid;
|
|
1129
|
-
return (( td ) => {
|
|
1130
|
-
tid = clearInterval(tid);
|
|
1131
|
-
if (td) {
|
|
1132
|
-
tid = setInterval(send_ping, 1000 * td);
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
// ensure the interval allows the NodeJS event loop to exit
|
|
1138
|
-
tid.unref?.();
|
|
1139
|
-
return true} }) }
|
|
1127
|
+
function _mqtt_dispatch(opt, target) {
|
|
1128
|
+
let hashbelt=[], rotate_ts=0;
|
|
1129
|
+
// default rotate at 1s across 5 buckets
|
|
1130
|
+
let { td: rotate_td=1000, n: rotate_n=5 } = opt?.rotate || {};
|
|
1140
1131
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1132
|
+
// Promise / future scaffolding
|
|
1133
|
+
let _pkt_id=100, _ftr_key; // use _ftr_key to reuse _by_key closure
|
|
1134
|
+
let _ftr_by_key = fn_answer => hashbelt[0].set(_ftr_key, fn_answer);
|
|
1144
1135
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1136
|
+
on_mqtt([]); // init hashbelt and rotate_ts
|
|
1137
|
+
return [on_mqtt, pkt_future]
|
|
1147
1138
|
|
|
1148
|
-
let _tmp_; // use _tmp_ to reuse _by_key closure
|
|
1149
|
-
let _by_key = answer_monad =>
|
|
1150
|
-
hashbelt[0].set(_tmp_, answer_monad);
|
|
1151
1139
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
_tmp_ = pkt_or_key.pkt_id = _pkt_id;}
|
|
1140
|
+
function pkt_future(pkt_or_key) {
|
|
1141
|
+
if (! _isstr(pkt_or_key)) {
|
|
1142
|
+
_pkt_id = (_pkt_id + 1) & 0xffff; // 16-bit unsigned short
|
|
1143
|
+
_ftr_key = pkt_or_key.pkt_id = _pkt_id;}
|
|
1144
|
+
else _ftr_key = pkt_or_key;
|
|
1158
1145
|
|
|
1159
|
-
|
|
1146
|
+
return new Promise(_ftr_by_key)}
|
|
1160
1147
|
|
|
1161
|
-
|
|
1162
|
-
for (let map of
|
|
1163
|
-
let
|
|
1164
|
-
if (
|
|
1148
|
+
function answer(key, pkt) {
|
|
1149
|
+
for (let map of hashbelt) {
|
|
1150
|
+
let fn_answer = map.get(key);
|
|
1151
|
+
if (fn_answer) {
|
|
1165
1152
|
map.delete(key);
|
|
1166
1153
|
|
|
1167
|
-
|
|
1154
|
+
fn_answer([pkt, /*err*/]); // option/maybe monad
|
|
1168
1155
|
return true} }
|
|
1169
1156
|
return false}
|
|
1170
1157
|
|
|
1171
|
-
, rotate_belt(n) {
|
|
1172
|
-
let {hashbelt} = this;
|
|
1173
|
-
hashbelt.unshift(new Map());
|
|
1174
|
-
for (let old of hashbelt.splice(n || 5)) {
|
|
1175
|
-
for (let answer_monad of old.values()) {
|
|
1176
|
-
answer_monad([/*pkt*/, 'expired']); } } }// option/maybe monad
|
|
1177
|
-
|
|
1178
|
-
, cmdids: ((() => {
|
|
1179
|
-
return [
|
|
1180
|
-
(() =>{} )// 0x0 reserved
|
|
1181
|
-
, by_evt // 0x1 connect
|
|
1182
|
-
, by_type // 0x2 connack
|
|
1183
|
-
, by_evt // 0x3 publish
|
|
1184
|
-
, by_id // 0x4 puback
|
|
1185
|
-
, by_id // 0x5 pubrec
|
|
1186
|
-
, by_id // 0x6 pubrel
|
|
1187
|
-
, by_id // 0x7 pubcomp
|
|
1188
|
-
, by_evt // 0x8 subscribe
|
|
1189
|
-
, by_id // 0x9 suback
|
|
1190
|
-
, by_evt // 0xa unsubscribe
|
|
1191
|
-
, by_id // 0xb unsuback
|
|
1192
|
-
, by_type // 0xc pingreq
|
|
1193
|
-
, by_type // 0xd pingresp
|
|
1194
|
-
, by_evt // 0xe disconnect
|
|
1195
|
-
, by_type ]// 0xf auth
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
function by_id(disp, pkt) {
|
|
1199
|
-
disp.answer(pkt.pkt_id, pkt); }
|
|
1200
|
-
|
|
1201
|
-
function by_type(disp, pkt, ctx) {
|
|
1202
|
-
disp.answer(pkt.type, pkt);
|
|
1203
|
-
by_evt(disp, pkt, ctx);}
|
|
1204
|
-
|
|
1205
|
-
async function by_evt({target}, pkt, ctx) {
|
|
1206
|
-
let fn = target[`mqtt_${pkt.type}`]
|
|
1207
|
-
|| target.mqtt_pkt;
|
|
1208
|
-
|
|
1209
|
-
await fn?.call(target, pkt, ctx);} })()) };
|
|
1210
|
-
|
|
1211
|
-
/*
|
|
1212
|
-
on_mqtt_type = {
|
|
1213
|
-
mqtt_auth(pkt, ctx) ::
|
|
1214
|
-
mqtt_connect(pkt, ctx) ::
|
|
1215
|
-
mqtt_connack(pkt, ctx) ::
|
|
1216
|
-
mqtt_disconnect(pkt, ctx) ::
|
|
1217
|
-
|
|
1218
|
-
mqtt_publish(pkt, ctx)
|
|
1219
|
-
mqtt_subscribe(pkt, ctx) ::
|
|
1220
|
-
mqtt_unsubscribe(pkt, ctx) ::
|
|
1221
|
-
|
|
1222
|
-
mqtt_pingreq(pkt, ctx) ::
|
|
1223
|
-
mqtt_pingresp(pkt, ctx) ::
|
|
1224
|
-
}
|
|
1225
|
-
*/
|
|
1226
|
-
|
|
1227
|
-
function _mqtt_dispatch(opt, target) {
|
|
1228
|
-
let _disp_ = _mqtt_cmdid_dispatch.create(target);
|
|
1229
|
-
let { cmdids } = _disp_;
|
|
1230
|
-
|
|
1231
|
-
// default rotate at 1s across 5 buckets
|
|
1232
|
-
let { td: rotate_td=1000, n: rotate_n=5 } =
|
|
1233
|
-
opt && opt.rotate || {};
|
|
1234
|
-
|
|
1235
|
-
let rotate_ts = rotate_td + Date.now();
|
|
1236
|
-
|
|
1237
|
-
return [on_mqtt,
|
|
1238
|
-
_disp_.bind_pkt_future()]
|
|
1239
|
-
|
|
1240
1158
|
function on_mqtt(pkt_list, ctx) {
|
|
1241
1159
|
for (let pkt of pkt_list) {
|
|
1242
|
-
|
|
1160
|
+
_mqtt_cmdids[pkt.id](target, answer, pkt, ctx);}
|
|
1243
1161
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1162
|
+
// rotate after rotate_ts
|
|
1163
|
+
let now = Date.now();
|
|
1164
|
+
if (now > rotate_ts) {
|
|
1165
|
+
rotate_ts = rotate_td + now;
|
|
1166
|
+
hashbelt.unshift(new Map());
|
|
1167
|
+
while (hashbelt.length > rotate_n) {
|
|
1168
|
+
for (let fn_answer of hashbelt.pop().values()) {
|
|
1169
|
+
fn_answer([/*pkt*/, 'expired']); } } } } }// option/maybe monad
|
|
1247
1170
|
|
|
1248
1171
|
class MQTTError extends Error {
|
|
1249
1172
|
constructor(mqtt_pkt, reason=mqtt_pkt.reason) {
|
|
1173
|
+
// use hex-encoded reasons to match MQTT spec documentation
|
|
1250
1174
|
super(`[0x${reason.toString(16)}] ${reason.reason}`);
|
|
1251
1175
|
this.mqtt_pkt = mqtt_pkt;
|
|
1252
1176
|
this.reason = reason;} }
|
|
1253
1177
|
|
|
1254
1178
|
class MQTTBase {
|
|
1255
|
-
constructor(opt={}) {
|
|
1256
|
-
this.with(opt);
|
|
1257
|
-
this._conn_ = _mqtt_conn(this,
|
|
1258
|
-
this._init_dispatch(opt, this)); }
|
|
1259
|
-
|
|
1260
|
-
with(fns_ns) {
|
|
1261
|
-
for (let [k,v] of Object.entries(fns_ns)) {
|
|
1262
|
-
if ('function' === typeof v) {this[k] = v;} }
|
|
1263
|
-
return this}
|
|
1264
|
-
|
|
1265
|
-
async conn_emit(evt, arg, err_arg) {
|
|
1266
|
-
this.log_conn?.(evt, arg, err_arg);
|
|
1267
|
-
try {
|
|
1268
|
-
let fn_evt = this[await evt]; // microtask break using `await evt`
|
|
1269
|
-
if (fn_evt) {
|
|
1270
|
-
await fn_evt.call(this, this, arg, err_arg);}
|
|
1271
|
-
else if (err_arg) {throw err_arg} }
|
|
1272
|
-
catch (err) {
|
|
1273
|
-
this.on_error(err, evt);} }
|
|
1274
|
-
|
|
1275
|
-
on_error(err, evt) {
|
|
1276
|
-
console.warn('[[u8-mqtt error: %s]]', evt, err); }
|
|
1277
|
-
|
|
1278
1179
|
// Handshaking Packets
|
|
1279
|
-
|
|
1280
1180
|
async connect(pkt={}) {
|
|
1281
|
-
let cid = pkt.client_id
|
|
1282
|
-
if (
|
|
1181
|
+
let cid = pkt.client_id;
|
|
1182
|
+
if (! _isstr(cid)) {
|
|
1283
1183
|
// see init_client_id implementation in core.jsy
|
|
1284
|
-
pkt.client_id = cid = this.init_client_id(cid);}
|
|
1184
|
+
pkt.client_id = cid = this.client_id || this.init_client_id(cid);}
|
|
1285
1185
|
this.client_id = cid;
|
|
1286
1186
|
|
|
1287
1187
|
if (null == pkt.keep_alive) {
|
|
1288
1188
|
pkt.keep_alive = 60;}
|
|
1289
1189
|
|
|
1290
|
-
let
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
throw new this.MQTTError(res[0])}
|
|
1295
|
-
|
|
1296
|
-
// TODO: merge with server's keep_alive frequency
|
|
1297
|
-
this._conn_.ping(pkt.keep_alive);
|
|
1298
|
-
return res}
|
|
1190
|
+
let response = await this._send0('connect', pkt, 'connack');
|
|
1191
|
+
if (0 != response[0].reason) {// compare to 0 to coerce to number
|
|
1192
|
+
throw new this.MQTTError(response[0])}
|
|
1193
|
+
return this.conn.on_conn(pkt, response)}
|
|
1299
1194
|
|
|
1300
1195
|
async disconnect(pkt={}) {
|
|
1301
|
-
let
|
|
1302
|
-
this.
|
|
1303
|
-
return res}
|
|
1196
|
+
let response = await this._send0('disconnect', pkt);
|
|
1197
|
+
return this.conn.on_dis(pkt, response)}
|
|
1304
1198
|
|
|
1305
|
-
auth(pkt={}) {
|
|
1306
|
-
|
|
1199
|
+
async auth(pkt={}) {
|
|
1200
|
+
let response = await this._send0('auth', pkt, 'auth');
|
|
1201
|
+
if (response[0].reason) {
|
|
1202
|
+
throw new this.MQTTError(response[0])}
|
|
1203
|
+
return this.conn.on_auth(pkt, response)}
|
|
1307
1204
|
|
|
1308
1205
|
ping() {return this._send('pingreq', null, 'pingresp')}
|
|
1309
1206
|
puback({pkt_id}) {return this._send('puback', {pkt_id})}
|
|
@@ -1340,20 +1237,18 @@ class MQTTBase {
|
|
|
1340
1237
|
// alias: publish -- because 'pub' is shorter for semantic aliases above
|
|
1341
1238
|
async pub(pkt, pub_opt) {
|
|
1342
1239
|
if (undefined === pkt.payload) {
|
|
1343
|
-
if (
|
|
1240
|
+
if (_isfn(pub_opt)) {
|
|
1241
|
+
// pub_opt as a function is fn_encode value
|
|
1344
1242
|
pub_opt = {fn_encode: pub_opt};}
|
|
1345
1243
|
|
|
1346
|
-
let
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
// return a single-value closure to publish packets
|
|
1353
|
-
return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
|
|
1244
|
+
let msg = pkt.msg, fn_encode = pub_opt?.fn_encode;
|
|
1245
|
+
if (null == msg || _isfn(msg)) {
|
|
1246
|
+
// when msg is a function, return closure using fn_encode
|
|
1247
|
+
if (msg) {pub_opt = {...pub_opt, fn_encode: msg};}
|
|
1248
|
+
// return a single-value closure to publish packets
|
|
1249
|
+
return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)}
|
|
1354
1250
|
|
|
1355
1251
|
// Encode payload from msg; fn_encode allows alternative to JSON.stringify
|
|
1356
|
-
let {fn_encode} = pub_opt || {};
|
|
1357
1252
|
pkt.payload = fn_encode
|
|
1358
1253
|
? await fn_encode(msg)
|
|
1359
1254
|
: JSON.stringify(msg);}
|
|
@@ -1365,31 +1260,31 @@ class MQTTBase {
|
|
|
1365
1260
|
pkt = pub_opt.xform(pkt) || pkt;} }
|
|
1366
1261
|
|
|
1367
1262
|
return this._send('publish', pkt,
|
|
1368
|
-
pkt.qos ? pkt :
|
|
1263
|
+
pkt.qos ? pkt : null ) }// key
|
|
1369
1264
|
|
|
1370
1265
|
|
|
1371
1266
|
// Internal API
|
|
1372
1267
|
|
|
1373
|
-
/* async
|
|
1268
|
+
/* async _send0(type, pkt) -- provided by conn and transport */
|
|
1269
|
+
/* async _send(type, pkt) -- provided by conn and transport */
|
|
1374
1270
|
|
|
1375
1271
|
_init_dispatch(opt) {
|
|
1376
1272
|
this.constructor?._once_();
|
|
1377
1273
|
let target ={__proto__: opt.on_mqtt_type};
|
|
1378
1274
|
target.mqtt_publish ||=
|
|
1379
1275
|
this._init_router?.(opt, this, target);
|
|
1380
|
-
return _mqtt_dispatch(
|
|
1276
|
+
return _mqtt_dispatch(opt, target)}
|
|
1381
1277
|
|
|
1382
1278
|
static _aliases() {
|
|
1383
1279
|
return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'}
|
|
1384
1280
|
|
|
1385
|
-
static _once_(
|
|
1386
|
-
|
|
1387
|
-
|
|
1281
|
+
static _once_(klass=this) {
|
|
1282
|
+
klass._once_ = _=>0;
|
|
1283
|
+
var alias, name, p = klass.prototype;
|
|
1388
1284
|
p.MQTTError = MQTTError;
|
|
1389
|
-
for (
|
|
1390
|
-
alias = alias.split(':');
|
|
1391
|
-
|
|
1392
|
-
if (fn) {p[alias[0]] = fn;} } } }
|
|
1285
|
+
for (alias of klass._aliases().split(/\s+/)) {
|
|
1286
|
+
[alias, name] = alias.split(':');
|
|
1287
|
+
p[alias] = p[name];} } }
|
|
1393
1288
|
|
|
1394
1289
|
|
|
1395
1290
|
function _as_topics(pkt, ex, topic_prefix) {
|
|
@@ -1414,43 +1309,177 @@ function _as_topics(pkt, ex, topic_prefix) {
|
|
|
1414
1309
|
pkt.topics = pkt.topics.map(_prefix_topics);}
|
|
1415
1310
|
return pkt}
|
|
1416
1311
|
|
|
1417
|
-
const
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1312
|
+
const _defer_obj = o =>(
|
|
1313
|
+
o.p = new Promise((a,e) => { o.a=a; o.e=e; })
|
|
1314
|
+
, o);
|
|
1315
|
+
|
|
1316
|
+
function _dfn_reset(client, attr, fn_after) {
|
|
1317
|
+
// a resetable deferred for a function
|
|
1318
|
+
let self = {set}, afn = async (...args) => (await self.p)(...args);
|
|
1319
|
+
return set()
|
|
1320
|
+
|
|
1321
|
+
function set() {
|
|
1322
|
+
if (afn !== client[attr]) {
|
|
1323
|
+
_defer_obj(self).p.then(fn_after, _=>0);
|
|
1324
|
+
client[attr] = afn;}
|
|
1325
|
+
return self} }
|
|
1326
|
+
|
|
1327
|
+
function _mqtt_conn(opt, client, [on_mqtt, pkt_future]) {
|
|
1328
|
+
let _abort;
|
|
1329
|
+
let _dfn_send0 = _dfn_reset(client, '_send0', // client._send0 getter/setter
|
|
1330
|
+
_=> client.conn_emit('on_live', conn.has_connected));
|
|
1331
|
+
let _dfn_ready = _dfn_reset(client, '_send', // client._send getter/setter
|
|
1332
|
+
_=> client.conn_emit('on_ready'));
|
|
1333
|
+
let _keep_alive_ival = _interval (() =>client._send0('pingreq') );// resettable interval for keep_alive ping
|
|
1334
|
+
|
|
1335
|
+
let conn = Object.create({
|
|
1336
|
+
ping: (td=conn.keep_alive) => _keep_alive_ival(td)
|
|
1337
|
+
|
|
1338
|
+
, on_conn(pkt, response) {
|
|
1339
|
+
conn.has_connected = true;
|
|
1340
|
+
conn.keep_alive = opt.keep_alive || response[0].props?.server_keep_alive || pkt.keep_alive;
|
|
1341
|
+
client.conn_emit('on_conn');
|
|
1342
|
+
return opt.use_auth
|
|
1343
|
+
? response // wait on enhanced authentication step
|
|
1344
|
+
: conn.on_auth(null, response) }// otherwise, connect is also auth
|
|
1345
|
+
|
|
1346
|
+
, on_auth(pkt, response) {
|
|
1347
|
+
_dfn_ready.a(_dfn_send0.p);
|
|
1348
|
+
if (0 != opt.keep_alive) {
|
|
1349
|
+
conn.ping();}
|
|
1350
|
+
client.conn_emit('on_auth', !pkt);
|
|
1351
|
+
return response}
|
|
1352
|
+
|
|
1353
|
+
, on_dis(pkt, response) {
|
|
1354
|
+
conn.reset(false);
|
|
1355
|
+
return response}
|
|
1356
|
+
|
|
1357
|
+
, reset(err) {
|
|
1358
|
+
if (err) {
|
|
1359
|
+
_dfn_send0.e(err); }// send error to uses of _send0 (connect, auth)
|
|
1360
|
+
_abort.e(err); // abort in-progress connections
|
|
1361
|
+
|
|
1362
|
+
delete conn.is_set;
|
|
1363
|
+
conn.ready = handshake();
|
|
1364
|
+
client.conn_emit('on_disconnect', false===err, err);}
|
|
1365
|
+
|
|
1366
|
+
, abort() {
|
|
1367
|
+
_dfn_ready.e(err); // abort all messages awaiting ready state
|
|
1368
|
+
return conn.reset(err)}
|
|
1369
|
+
|
|
1370
|
+
, async setup(gate, send_u8_pkt, init_msg_loop) {
|
|
1371
|
+
if (conn.is_set) {
|
|
1372
|
+
throw new Error() }// already in-progress
|
|
1373
|
+
|
|
1374
|
+
conn.is_set = true;
|
|
1375
|
+
await gate;
|
|
1376
|
+
|
|
1377
|
+
// setup send/recv MQTT parsing context
|
|
1378
|
+
let mqtt_ctx = client.mqtt_ctx.mqtt_stream();
|
|
1379
|
+
|
|
1380
|
+
{// setup inbound message loop
|
|
1381
|
+
let sess_ctx = {mqtt: client}; // mutable session context
|
|
1382
|
+
let on_mqtt_chunk = u8 => on_mqtt(mqtt_ctx.decode(u8), sess_ctx);
|
|
1383
|
+
init_msg_loop(on_mqtt_chunk, conn);}
|
|
1384
|
+
|
|
1385
|
+
// setup outbound message path and transport connection
|
|
1386
|
+
send_u8_pkt = await send_u8_pkt;
|
|
1387
|
+
_dfn_send0.a(
|
|
1388
|
+
async (type, pkt, key) => {
|
|
1389
|
+
let res = undefined !== key
|
|
1390
|
+
? pkt_future(key) : true;
|
|
1391
|
+
|
|
1392
|
+
await send_u8_pkt(
|
|
1393
|
+
mqtt_ctx.encode_pkt(type, pkt));
|
|
1394
|
+
return res} ); } });
|
|
1395
|
+
|
|
1396
|
+
conn.ready = handshake();
|
|
1397
|
+
return conn
|
|
1398
|
+
|
|
1399
|
+
async function handshake() {
|
|
1400
|
+
_abort = _defer_obj({});
|
|
1401
|
+
|
|
1402
|
+
_keep_alive_ival(0); // clearInterval on keep alive ping
|
|
1403
|
+
_dfn_send0.set(); // reset client._send0 if necessary
|
|
1404
|
+
_dfn_ready.set(); // reset client._send if necessary
|
|
1405
|
+
|
|
1406
|
+
try {
|
|
1407
|
+
// set client._send0 as passtrhough after transport connection
|
|
1408
|
+
client._send0 = await Promise.race([_dfn_send0.p, _abort.p]);
|
|
1409
|
+
|
|
1410
|
+
// set client._send as passtrhough after ready
|
|
1411
|
+
client._send = await Promise.race([_dfn_ready.p, _abort.p]);
|
|
1412
|
+
return true}
|
|
1413
|
+
catch (err) {
|
|
1414
|
+
return false} } }
|
|
1415
|
+
|
|
1416
|
+
const pkt_api ={
|
|
1417
|
+
utf8(u8) {return new TextDecoder('utf-8').decode(u8 || this.payload )}
|
|
1418
|
+
, json(u8) {return JSON.parse( this.utf8(u8) || null )}
|
|
1419
|
+
, text(u8) {return this.utf8(u8)} };
|
|
1420
|
+
|
|
1421
|
+
const opt_default ={
|
|
1422
|
+
sess_stg: globalThis.sessionStorage};
|
|
1422
1423
|
|
|
1423
1424
|
class MQTTCore extends MQTTBase {
|
|
1425
|
+
constructor(opt) {
|
|
1426
|
+
super();
|
|
1427
|
+
this.with(opt);
|
|
1428
|
+
opt ={...opt_default, ...opt};
|
|
1429
|
+
// settings for MQTTCore
|
|
1430
|
+
this.sess_stg = opt.sess_stg;
|
|
1431
|
+
// setup connection and dispatch
|
|
1432
|
+
this.conn = _mqtt_conn(opt, this,
|
|
1433
|
+
this._init_dispatch(opt)); }
|
|
1434
|
+
|
|
1435
|
+
with(fns_ns) {
|
|
1436
|
+
for (let [k,v] of Object.entries(fns_ns)) {
|
|
1437
|
+
if (_isfn(v)) {this[k] = v;} }
|
|
1438
|
+
return this}
|
|
1439
|
+
|
|
1440
|
+
|
|
1424
1441
|
static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) {
|
|
1425
|
-
let
|
|
1426
|
-
|
|
1442
|
+
let klass = class extends this {};
|
|
1443
|
+
klass.prototype.mqtt_ctx =
|
|
1427
1444
|
mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx);
|
|
1428
|
-
return
|
|
1445
|
+
return klass}
|
|
1446
|
+
|
|
1447
|
+
|
|
1448
|
+
async conn_emit(evt, arg, err_arg) {
|
|
1449
|
+
this.log_conn?.(evt, arg, err_arg);
|
|
1450
|
+
try {
|
|
1451
|
+
let fn_evt = this[await evt]; // microtask break using `await evt`
|
|
1452
|
+
if (fn_evt) {
|
|
1453
|
+
await fn_evt.call(this, this, arg, err_arg);}
|
|
1454
|
+
else if (err_arg) {throw err_arg} }
|
|
1455
|
+
catch (err) {
|
|
1456
|
+
this.on_error(err, evt);} }
|
|
1457
|
+
|
|
1458
|
+
on_error(err, evt) {
|
|
1459
|
+
console.warn('[[u8-mqtt error: %s]]', evt, err); }
|
|
1460
|
+
log_conn(evt, arg, err_arg) {
|
|
1461
|
+
console.info('[[u8-mqtt conn: %s]]', evt, arg, err_arg); }
|
|
1429
1462
|
|
|
1430
1463
|
|
|
1431
1464
|
// automatic Client Id for connect()
|
|
1432
1465
|
init_client_id(parts=['u8-mqtt--','']) {
|
|
1433
|
-
let sess_stg=this.sess_stg;
|
|
1466
|
+
let sess_stg = this.sess_stg;
|
|
1434
1467
|
let key, cid = sess_stg?.getItem(key=parts.join(' '));
|
|
1435
1468
|
if (! cid) {
|
|
1436
1469
|
cid = parts.join(Math.random().toString(36).slice(2));
|
|
1437
1470
|
sess_stg?.setItem(key, cid);}
|
|
1438
1471
|
return cid}
|
|
1439
1472
|
|
|
1440
|
-
get sess_stg() {return globalThis.sessionStorage}
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
//on_error(err, evt) ::
|
|
1444
|
-
// console.warn @ '[[u8-mqtt error: %s]]', evt, err
|
|
1445
|
-
|
|
1446
|
-
//log_conn(evt, arg, err_arg) ::
|
|
1447
|
-
// console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
|
|
1448
1473
|
|
|
1449
1474
|
on_live(client, is_reconnect) {
|
|
1450
1475
|
if (is_reconnect) {
|
|
1451
1476
|
return client.connect()} }
|
|
1452
1477
|
|
|
1453
|
-
//
|
|
1478
|
+
// on_ready(client) ::
|
|
1479
|
+
// on_reconnect(client) ::
|
|
1480
|
+
on_disconnect(client, intentional) {
|
|
1481
|
+
if (! intentional) {
|
|
1482
|
+
return client.on_reconnect?.()} }
|
|
1454
1483
|
|
|
1455
1484
|
_use_conn(fn_reconnect) {
|
|
1456
1485
|
return (this.reconnect = fn_reconnect)?.()}
|
|
@@ -1462,27 +1491,20 @@ class MQTTCore extends MQTTBase {
|
|
|
1462
1491
|
.then(this.reconnect)
|
|
1463
1492
|
.then(opt.reconnect, opt.error);} }) }
|
|
1464
1493
|
|
|
1465
|
-
on_disconnect(client, intentional) {
|
|
1466
|
-
if (! intentional) {
|
|
1467
|
-
return client.on_reconnect?.()} }
|
|
1468
|
-
|
|
1469
1494
|
delay(ms) {
|
|
1470
1495
|
return new Promise(done => setTimeout(done, ms)) }
|
|
1471
1496
|
|
|
1472
1497
|
with_async_iter(async_iter, write_u8_pkt) {
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
catch (err) {
|
|
1484
|
-
this._conn_.reset(err);} })());
|
|
1485
|
-
|
|
1498
|
+
this.conn.setup(async_iter,
|
|
1499
|
+
write_u8_pkt,
|
|
1500
|
+
async (on_mqtt_chunk, conn) => {
|
|
1501
|
+
try {
|
|
1502
|
+
async_iter = await async_iter;
|
|
1503
|
+
for await (let chunk of async_iter)
|
|
1504
|
+
on_mqtt_chunk(chunk);
|
|
1505
|
+
conn.reset();}
|
|
1506
|
+
catch (err) {
|
|
1507
|
+
conn.reset(err);} } );
|
|
1486
1508
|
return this}
|
|
1487
1509
|
|
|
1488
1510
|
|
|
@@ -1553,33 +1575,30 @@ class MQTTCore extends MQTTBase {
|
|
|
1553
1575
|
|
|
1554
1576
|
websock.binaryType = 'arraybuffer';
|
|
1555
1577
|
|
|
1556
|
-
let
|
|
1578
|
+
let ws_ready, readyState = websock.readyState;
|
|
1557
1579
|
if (1 !== readyState) {
|
|
1558
1580
|
if (0 !== readyState) {
|
|
1559
|
-
throw new Error('
|
|
1560
|
-
|
|
1561
|
-
ready = new Promise(fn => websock.onopen = fn); }
|
|
1581
|
+
throw new Error('WS readyState') }
|
|
1562
1582
|
|
|
1583
|
+
ws_ready = new Promise(ready => websock.onopen = ready); }
|
|
1563
1584
|
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
, websock.send(u8_pkt)) );
|
|
1585
|
+
this.conn.setup(ws_ready,
|
|
1586
|
+
u8_pkt => websock.send(u8_pkt),
|
|
1587
|
+
(on_mqtt_chunk, conn) => {
|
|
1588
|
+
websock.onmessage = evt =>(
|
|
1589
|
+
on_mqtt_chunk(new Uint8Array(evt.data)) );
|
|
1570
1590
|
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
err.reason = evt.reason;}
|
|
1591
|
+
websock.onclose = evt => {
|
|
1592
|
+
if (! evt.wasClean) {
|
|
1593
|
+
var err = new Error('websocket close');
|
|
1594
|
+
err.code = evt.code;
|
|
1595
|
+
err.reason = evt.reason;}
|
|
1577
1596
|
|
|
1578
|
-
|
|
1597
|
+
conn.reset(err);}; } );
|
|
1579
1598
|
|
|
1580
1599
|
return this} }
|
|
1581
1600
|
|
|
1582
|
-
const version = '0.
|
|
1601
|
+
const version = '0.6.0-web';
|
|
1583
1602
|
|
|
1584
1603
|
const MQTTClient_v4 = /* #__PURE__ */
|
|
1585
1604
|
with_topic_path_router(
|