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