abmqtt-dist 0.0.4 → 0.0.6
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/dist/mqtt.client.js +452 -402
- package/package.json +72 -72
package/dist/mqtt.client.js
CHANGED
|
@@ -1,423 +1,473 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports,
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MqttClient = void 0;
|
|
4
|
-
const flow_1 = require(
|
|
5
|
-
const mqtt_parser_1 = require(
|
|
6
|
-
const transport_1 = require(
|
|
7
|
-
const packets_1 = require(
|
|
8
|
-
const errors_1 = require(
|
|
9
|
-
const stream_1 = require(
|
|
10
|
-
const mqtt_constants_1 = require(
|
|
11
|
-
const mqtt_base_client_1 = require(
|
|
12
|
-
const mqtt_listener_1 = require(
|
|
13
|
-
const mqtt_utilities_1 = require(
|
|
14
|
-
const debug = require(
|
|
15
|
-
const reconnect_strategy_1 = require(
|
|
4
|
+
const flow_1 = require("./flow");
|
|
5
|
+
const mqtt_parser_1 = require("./mqtt.parser");
|
|
6
|
+
const transport_1 = require("./transport");
|
|
7
|
+
const packets_1 = require("./packets");
|
|
8
|
+
const errors_1 = require("./errors");
|
|
9
|
+
const stream_1 = require("stream");
|
|
10
|
+
const mqtt_constants_1 = require("./mqtt.constants");
|
|
11
|
+
const mqtt_base_client_1 = require("./mqtt.base-client");
|
|
12
|
+
const mqtt_listener_1 = require("./mqtt.listener");
|
|
13
|
+
const mqtt_utilities_1 = require("./mqtt.utilities");
|
|
14
|
+
const debug = require("debug");
|
|
15
|
+
const reconnect_strategy_1 = require("./reconnect-strategy");
|
|
16
16
|
function tryMakeError(e) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
if (e && e instanceof Error) {
|
|
18
|
+
return e;
|
|
19
|
+
}
|
|
20
|
+
if (typeof e === "string") {
|
|
21
|
+
return new Error(e);
|
|
22
|
+
}
|
|
23
|
+
return new Error(`unknown error (received: ${e})`);
|
|
24
24
|
}
|
|
25
25
|
class MqttClient extends mqtt_base_client_1.MqttBaseClient {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
},
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
publish(message) {
|
|
139
|
-
return this.startFlow((0, flow_1.outgoingPublishFlow)(message));
|
|
140
|
-
}
|
|
141
|
-
subscribe(subscription) {
|
|
142
|
-
return this.startFlow((0, flow_1.outgoingSubscribeFlow)(subscription));
|
|
143
|
-
}
|
|
144
|
-
unsubscribe(subscription) {
|
|
145
|
-
return this.startFlow((0, flow_1.outgoingUnsubscribeFlow)(subscription));
|
|
146
|
-
}
|
|
147
|
-
async disconnect(force = false) {
|
|
148
|
-
if (!force) {
|
|
149
|
-
return this.startFlow((0, flow_1.outgoingDisconnectFlow)()).then(async () => {
|
|
150
|
-
await this.setDisconnected('Soft disconnect');
|
|
151
|
-
});
|
|
152
|
-
} else {
|
|
153
|
-
await this.setDisconnected('Forced disconnect');
|
|
26
|
+
get keepAlive() {
|
|
27
|
+
return this.connectOptions?.keepAlive ?? 0;
|
|
28
|
+
}
|
|
29
|
+
set keepAlive(value) {
|
|
30
|
+
if (this.connectOptions) {
|
|
31
|
+
this.connectOptions.keepAlive = value;
|
|
32
|
+
if (value) {
|
|
33
|
+
this.updateKeepAlive(value);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
constructor(options) {
|
|
38
|
+
super();
|
|
39
|
+
this.mqttDebug = debug("mqtt:client");
|
|
40
|
+
this.receiveDebug = this.mqttDebug.extend("packet");
|
|
41
|
+
this.pingDebug = this.mqttDebug.extend("ping");
|
|
42
|
+
// wrapper functions
|
|
43
|
+
this.executePeriodically = (ms, cb) => setInterval(cb, ms);
|
|
44
|
+
this.stopExecuting = (ref) => clearInterval(ref);
|
|
45
|
+
this.executeDelayed = (ms, cb) => setTimeout(cb, ms);
|
|
46
|
+
this.flowCounter = (0, mqtt_utilities_1.createFlowCounter)();
|
|
47
|
+
this.activeFlows = [];
|
|
48
|
+
this.messageListener = new mqtt_listener_1.MqttListener();
|
|
49
|
+
if (typeof options.autoReconnect === "boolean") {
|
|
50
|
+
this.reconnectStrategy = options.autoReconnect
|
|
51
|
+
? new reconnect_strategy_1.MqttsReconnectStrategyDefault()
|
|
52
|
+
: new reconnect_strategy_1.MqttsReconnectStrategyDefault(0);
|
|
53
|
+
} else {
|
|
54
|
+
this.reconnectStrategy = options.autoReconnect;
|
|
55
|
+
}
|
|
56
|
+
this.transport =
|
|
57
|
+
options.transport ??
|
|
58
|
+
new transport_1.TlsTransport({
|
|
59
|
+
host: options.host,
|
|
60
|
+
port: options.port,
|
|
61
|
+
additionalOptions: {
|
|
62
|
+
enableTrace: options.enableTrace,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
this.createTransformer =
|
|
66
|
+
options.createTransformer ??
|
|
67
|
+
(() =>
|
|
68
|
+
new mqtt_parser_1.MqttTransformer({
|
|
69
|
+
debug: this.mqttDebug.extend("transformer"),
|
|
70
|
+
mapping: options.readMap ?? packets_1.DefaultPacketReadMap,
|
|
71
|
+
}));
|
|
72
|
+
this.transformer = this.createTransformer();
|
|
73
|
+
this.transformer.options.debug ??= this.mqttDebug.extend("transformer");
|
|
74
|
+
const packetLogger = this.mqttDebug.extend("write");
|
|
75
|
+
this.writer =
|
|
76
|
+
options.packetWriter ??
|
|
77
|
+
new packets_1.PacketWriter(
|
|
78
|
+
{
|
|
79
|
+
logPacketWrite: (0, mqtt_utilities_1.createDefaultPacketLogger)(
|
|
80
|
+
packetLogger
|
|
81
|
+
),
|
|
82
|
+
},
|
|
83
|
+
options.writeMap
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
async _connect(options) {
|
|
87
|
+
this.expectCreated();
|
|
88
|
+
this.mqttDebug(
|
|
89
|
+
`Connecting using transport "${this.transport.constructor.name}"`
|
|
90
|
+
);
|
|
91
|
+
this.connectResolver = options;
|
|
92
|
+
this.setConnecting();
|
|
93
|
+
try {
|
|
94
|
+
await this.transport.connect();
|
|
95
|
+
} catch (e) {
|
|
96
|
+
const err = tryMakeError(e);
|
|
97
|
+
this.mqttDebug(
|
|
98
|
+
`Transport connect error ("${this.transport.constructor.name}")`,
|
|
99
|
+
err.message
|
|
100
|
+
);
|
|
101
|
+
const shouldReconnect = this.reconnectStrategy?.check();
|
|
102
|
+
await this.setDisconnected(err);
|
|
103
|
+
if (shouldReconnect) {
|
|
104
|
+
return;
|
|
105
|
+
} else {
|
|
106
|
+
throw e;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
this.createPipeline();
|
|
110
|
+
return this.registerClient(await this.resolveConnectOptions());
|
|
111
|
+
}
|
|
112
|
+
async connect(options) {
|
|
113
|
+
try {
|
|
114
|
+
await this._connect(options);
|
|
115
|
+
} catch (e) {
|
|
116
|
+
const err = tryMakeError(e);
|
|
117
|
+
this.mqttDebug(`Connection error`, err);
|
|
118
|
+
this.emitError(err);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
createPipeline() {
|
|
122
|
+
if (!this.transport.duplex)
|
|
123
|
+
throw new errors_1.IllegalStateError(
|
|
124
|
+
"Expected transport to expose a Duplex."
|
|
125
|
+
);
|
|
126
|
+
this.pipeline = (0, stream_1.pipeline)(
|
|
127
|
+
this.transport.duplex,
|
|
128
|
+
this.transformer,
|
|
129
|
+
async (source) => {
|
|
130
|
+
for await (const chunk of source) {
|
|
131
|
+
if (!chunk.type) {
|
|
132
|
+
throw new Error("Chunk is not a MqttPacket");
|
|
133
|
+
}
|
|
134
|
+
await this.handlePacket(chunk);
|
|
154
135
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const listener = typeof options === 'string' ? { topic: options } : options;
|
|
165
|
-
const [topicFilter, paramMatcher] = (0, mqtt_utilities_1.toMqttTopicFilter)(listener.topic);
|
|
166
|
-
return this.messageListener.addHandler({
|
|
167
|
-
topicFilter,
|
|
168
|
-
handle: handlerFn,
|
|
169
|
-
transformer: listener.transformer,
|
|
170
|
-
validator: listener.validator,
|
|
171
|
-
paramMatcher,
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
startFlow(flow) {
|
|
175
|
-
const flowId = this.flowCounter.next();
|
|
176
|
-
const promise = new Promise((resolve, reject) => {
|
|
177
|
-
const data = {
|
|
178
|
-
resolvers: { resolve, reject },
|
|
179
|
-
finished: false,
|
|
180
|
-
callbacks: flow(
|
|
181
|
-
value => {
|
|
182
|
-
data.finished = true;
|
|
183
|
-
resolve(value);
|
|
184
|
-
},
|
|
185
|
-
err => {
|
|
186
|
-
data.finished = true;
|
|
187
|
-
reject(err);
|
|
188
|
-
},
|
|
189
|
-
),
|
|
190
|
-
flowId,
|
|
191
|
-
flowFunc: flow,
|
|
192
|
-
};
|
|
193
|
-
const first = data.callbacks.start();
|
|
194
|
-
if (first) this.sendData(this.writer.write(first.type, first.options));
|
|
195
|
-
if (!data.finished) {
|
|
196
|
-
this.activeFlows.push(data);
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
promise.flowId = flowId;
|
|
200
|
-
return promise;
|
|
201
|
-
}
|
|
202
|
-
stopFlow(flowId, rejection) {
|
|
203
|
-
const flow = this.getFlowById(flowId);
|
|
204
|
-
if (!flow) return false;
|
|
205
|
-
this.activeFlows = this.activeFlows.filter(f => f.flowId !== flowId);
|
|
206
|
-
flow.finished = true;
|
|
207
|
-
flow.resolvers.reject(rejection ?? new errors_1.FlowStoppedError());
|
|
208
|
-
return true;
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Run the accept and next function of all active flows
|
|
212
|
-
* @param packet
|
|
213
|
-
* @returns true if a flow has been found
|
|
214
|
-
*/
|
|
215
|
-
continueFlows(packet) {
|
|
216
|
-
let result = false;
|
|
217
|
-
for (const flow of this.activeFlows) {
|
|
218
|
-
if (flow.callbacks.accept?.(packet.data)) {
|
|
219
|
-
const next = flow.callbacks.next?.(packet.data);
|
|
220
|
-
if (next) {
|
|
221
|
-
this.sendData(this.writer.write(next.type, next.options));
|
|
222
|
-
}
|
|
223
|
-
result = true;
|
|
224
|
-
}
|
|
136
|
+
return "Source drained";
|
|
137
|
+
} /* bad definitions */,
|
|
138
|
+
(err) => {
|
|
139
|
+
if (err) this.emitError(tryMakeError(err));
|
|
140
|
+
if (!this.disconnected) {
|
|
141
|
+
(err
|
|
142
|
+
? this.setDisconnected(tryMakeError(err))
|
|
143
|
+
: this.setDisconnected("Pipeline finished")
|
|
144
|
+
).catch((e) => this.emitWarning(e));
|
|
225
145
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
publish(message) {
|
|
150
|
+
return this.startFlow((0, flow_1.outgoingPublishFlow)(message));
|
|
151
|
+
}
|
|
152
|
+
subscribe(subscription) {
|
|
153
|
+
return this.startFlow((0, flow_1.outgoingSubscribeFlow)(subscription));
|
|
154
|
+
}
|
|
155
|
+
unsubscribe(subscription) {
|
|
156
|
+
return this.startFlow((0, flow_1.outgoingUnsubscribeFlow)(subscription));
|
|
157
|
+
}
|
|
158
|
+
async disconnect(force = false) {
|
|
159
|
+
if (!force) {
|
|
160
|
+
return this.startFlow((0, flow_1.outgoingDisconnectFlow)()).then(
|
|
161
|
+
async () => {
|
|
162
|
+
await this.setDisconnected("Soft disconnect");
|
|
236
163
|
}
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
164
|
+
);
|
|
165
|
+
} else {
|
|
166
|
+
await this.setDisconnected("Forced disconnect");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
listenSubscribe(options, handlerFn) {
|
|
170
|
+
const listener = typeof options === "string" ? { topic: options } : options;
|
|
171
|
+
return this.subscribe({
|
|
172
|
+
...listener.subscriptionInfo,
|
|
173
|
+
topic: listener.topic.replace(/\/:[A-Za-z-_0-9]+/g, "/+"),
|
|
174
|
+
}).then(() => this.listen(listener, handlerFn));
|
|
175
|
+
}
|
|
176
|
+
listen(options, handlerFn) {
|
|
177
|
+
const listener = typeof options === "string" ? { topic: options } : options;
|
|
178
|
+
const [topicFilter, paramMatcher] = (0, mqtt_utilities_1.toMqttTopicFilter)(
|
|
179
|
+
listener.topic
|
|
180
|
+
);
|
|
181
|
+
return this.messageListener.addHandler({
|
|
182
|
+
topicFilter,
|
|
183
|
+
handle: handlerFn,
|
|
184
|
+
transformer: listener.transformer,
|
|
185
|
+
validator: listener.validator,
|
|
186
|
+
paramMatcher,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
startFlow(flow) {
|
|
190
|
+
const flowId = this.flowCounter.next();
|
|
191
|
+
const promise = new Promise((resolve, reject) => {
|
|
192
|
+
const data = {
|
|
193
|
+
resolvers: { resolve, reject },
|
|
194
|
+
finished: false,
|
|
195
|
+
callbacks: flow(
|
|
196
|
+
(value) => {
|
|
197
|
+
data.finished = true;
|
|
198
|
+
resolve(value);
|
|
199
|
+
},
|
|
200
|
+
(err) => {
|
|
201
|
+
data.finished = true;
|
|
202
|
+
reject(err);
|
|
203
|
+
}
|
|
204
|
+
),
|
|
205
|
+
flowId,
|
|
206
|
+
flowFunc: flow,
|
|
207
|
+
};
|
|
208
|
+
const first = data.callbacks.start();
|
|
209
|
+
if (first) this.sendData(this.writer.write(first.type, first.options));
|
|
210
|
+
if (!data.finished) {
|
|
211
|
+
this.activeFlows.push(data);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
promise.flowId = flowId;
|
|
215
|
+
return promise;
|
|
216
|
+
}
|
|
217
|
+
stopFlow(flowId, rejection) {
|
|
218
|
+
const flow = this.getFlowById(flowId);
|
|
219
|
+
if (!flow) return false;
|
|
220
|
+
this.activeFlows = this.activeFlows.filter((f) => f.flowId !== flowId);
|
|
221
|
+
flow.finished = true;
|
|
222
|
+
flow.resolvers.reject(rejection ?? new errors_1.FlowStoppedError());
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Run the accept and next function of all active flows
|
|
227
|
+
* @param packet
|
|
228
|
+
* @returns true if a flow has been found
|
|
229
|
+
*/
|
|
230
|
+
continueFlows(packet) {
|
|
231
|
+
let result = false;
|
|
232
|
+
for (const flow of this.activeFlows) {
|
|
233
|
+
if (flow.callbacks.accept?.(packet.data)) {
|
|
234
|
+
const next = flow.callbacks.next?.(packet.data);
|
|
235
|
+
if (next) {
|
|
236
|
+
this.sendData(this.writer.write(next.type, next.options));
|
|
260
237
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
getConnectFlow(options) {
|
|
271
|
-
// assume the defaults are used
|
|
272
|
-
return (0, flow_1.outgoingConnectFlow)(options);
|
|
273
|
-
}
|
|
274
|
-
updateKeepAlive(value) {
|
|
275
|
-
value = Math.max(value - 0.5, 1);
|
|
276
|
-
if (this.keepAliveTimer) {
|
|
277
|
-
this.stopExecuting(this.keepAliveTimer);
|
|
278
|
-
}
|
|
279
|
-
this.mqttDebug(`Starting keep-alive-ping {delay: ${value}}`);
|
|
280
|
-
this.keepAliveTimer = this.executePeriodically(value * 1000, () => {
|
|
281
|
-
// assume the defaults are used
|
|
282
|
-
this.startFlow((0, flow_1.outgoingPingFlow)())
|
|
283
|
-
.then(() => this.pingDebug(`PingPong @ ${Date.now()}`))
|
|
284
|
-
.catch(e => {
|
|
285
|
-
this.emitWarning(e);
|
|
286
|
-
this.pingDebug(`PingPong failed. (${e.message})`);
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
sendData(data) {
|
|
291
|
-
if (!this.transport.duplex) {
|
|
292
|
-
this.emitError(new errors_1.IllegalStateError('Expected a duplex - was undefined'));
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
this.transport.duplex.write(data);
|
|
296
|
-
}
|
|
297
|
-
async handlePacket(packet) {
|
|
298
|
-
this.logReceivedPacket(packet);
|
|
299
|
-
this.emit(mqtt_constants_1.PacketType[packet.type].toUpperCase(), packet.data);
|
|
300
|
-
let forceCheckFlows = false;
|
|
301
|
-
// The following "type assertions" are valid as clients extending MqttClient have to implement their own methods
|
|
302
|
-
switch (packet.type) {
|
|
303
|
-
case mqtt_constants_1.PacketType.ConnAck: {
|
|
304
|
-
this.onConnAck(packet.data);
|
|
305
|
-
break;
|
|
306
|
-
}
|
|
307
|
-
case mqtt_constants_1.PacketType.Publish: {
|
|
308
|
-
this.onPublish(packet.data);
|
|
309
|
-
break;
|
|
310
|
-
}
|
|
311
|
-
case mqtt_constants_1.PacketType.PingReq: {
|
|
312
|
-
this.onPingReq();
|
|
313
|
-
break;
|
|
314
|
-
}
|
|
315
|
-
case mqtt_constants_1.PacketType.Disconnect: {
|
|
316
|
-
this.setDisconnected('disconnect packet received').catch(e => this.emitWarning(e));
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
default:
|
|
320
|
-
forceCheckFlows = true;
|
|
321
|
-
}
|
|
322
|
-
if (!this.continueFlows(packet) && forceCheckFlows) {
|
|
323
|
-
this.emitWarning(new errors_1.UnexpectedPacketError((0, mqtt_constants_1.packetTypeToString)(packet.type)));
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
onConnAck(connAck) {
|
|
327
|
-
if (connAck.isSuccess) {
|
|
328
|
-
this.setReady();
|
|
329
|
-
this.emitConnect(connAck);
|
|
330
|
-
if (this.connectOptions?.keepAlive) {
|
|
331
|
-
this.updateKeepAlive(this.connectOptions.keepAlive);
|
|
332
|
-
}
|
|
333
|
-
this.reconnectStrategy?.reset();
|
|
334
|
-
} else {
|
|
335
|
-
const error = new errors_1.ConnectError(connAck.errorName);
|
|
336
|
-
this.setDisconnected(error).catch(e => this.emitWarning(e));
|
|
337
|
-
this.emitError(error);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
onPublish(publish) {
|
|
341
|
-
this.startFlow(
|
|
342
|
-
(0, flow_1.incomingPublishFlow)(
|
|
343
|
-
{
|
|
344
|
-
topic: publish.topic,
|
|
345
|
-
payload: publish.payload,
|
|
346
|
-
qosLevel: publish.qos,
|
|
347
|
-
retained: publish.retain,
|
|
348
|
-
duplicate: publish.duplicate,
|
|
349
|
-
},
|
|
350
|
-
publish.identifier ?? undefined,
|
|
351
|
-
),
|
|
352
|
-
)
|
|
353
|
-
.then(async m => {
|
|
354
|
-
this.emitMessage(m);
|
|
355
|
-
await this.messageListener.handleMessage(m);
|
|
356
|
-
})
|
|
357
|
-
.catch(e => this.emitWarning(e));
|
|
358
|
-
}
|
|
359
|
-
onPingReq() {
|
|
360
|
-
this.startFlow((0, flow_1.incomingPingFlow)())
|
|
361
|
-
.then(() => this.pingDebug(`Server-PingPong @ ${Date.now()}`))
|
|
362
|
-
.catch(e => {
|
|
363
|
-
this.emitWarning(e);
|
|
364
|
-
this.pingDebug(`Server-PingPong failed. (${e.message})`);
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
logReceivedPacket(packet) {
|
|
368
|
-
if (packet.type !== mqtt_constants_1.PacketType.PingReq && packet.type !== mqtt_constants_1.PacketType.PingResp)
|
|
369
|
-
this.receiveDebug(`Received ${(0, mqtt_utilities_1.stringifyObject)(packet.data)}`);
|
|
370
|
-
}
|
|
371
|
-
reset() {
|
|
372
|
-
super.reset();
|
|
238
|
+
result = true;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
this.clearFinishedFlows();
|
|
242
|
+
return result;
|
|
243
|
+
}
|
|
244
|
+
clearFinishedFlows() {
|
|
245
|
+
this.activeFlows = this.activeFlows.filter((flow) => !flow.finished);
|
|
246
|
+
}
|
|
373
247
|
|
|
248
|
+
stopExecutingFlows(error) {
|
|
249
|
+
const flows = [...this.activeFlows];
|
|
250
|
+
this.activeFlows = [];
|
|
251
|
+
|
|
252
|
+
flows.forEach((flow) => {
|
|
253
|
+
flow.finished = true;
|
|
254
|
+
|
|
255
|
+
(async () => {
|
|
374
256
|
try {
|
|
375
|
-
|
|
257
|
+
flow.resolvers.reject(error);
|
|
376
258
|
} catch (e) {
|
|
377
|
-
|
|
259
|
+
this.mqttDebug?.("Flow rejection error during cleanup:", e);
|
|
378
260
|
}
|
|
261
|
+
})().catch(() => {});
|
|
262
|
+
});
|
|
263
|
+
}
|
|
379
264
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
265
|
+
getFlowById(id) {
|
|
266
|
+
return this.activeFlows.find((f) => f.flowId === id);
|
|
267
|
+
}
|
|
268
|
+
registerClient(options) {
|
|
269
|
+
const flow = this.getConnectFlow(options);
|
|
270
|
+
const connectPromiseFlow = this.startFlow(flow);
|
|
271
|
+
if (typeof options.connectDelay !== "undefined") {
|
|
272
|
+
const timerId = this.executeDelayed(options.connectDelay ?? 2000, () => {
|
|
273
|
+
const flow = this.getFlowById(connectPromiseFlow.flowId);
|
|
274
|
+
if (!flow) {
|
|
275
|
+
// there's no flow anymore
|
|
276
|
+
this.stopExecuting(timerId);
|
|
277
|
+
return;
|
|
383
278
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
279
|
+
const packet = flow.callbacks.start();
|
|
280
|
+
if (packet)
|
|
281
|
+
this.sendData(this.writer.write(packet.type, packet.options));
|
|
282
|
+
});
|
|
283
|
+
connectPromiseFlow
|
|
284
|
+
.finally(() => this.stopExecuting(timerId))
|
|
285
|
+
// not sure why this is necessary, but it's there so no unhandledRejection is thrown
|
|
286
|
+
.catch(() => undefined);
|
|
287
|
+
}
|
|
288
|
+
options.signal?.addEventListener("abort", () => {
|
|
289
|
+
try {
|
|
290
|
+
this.stopFlow(
|
|
291
|
+
connectPromiseFlow.flowId,
|
|
292
|
+
new errors_1.AbortError("Connecting aborted")
|
|
293
|
+
);
|
|
294
|
+
} catch (e) {
|
|
295
|
+
this.emitWarning(tryMakeError(e));
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
return connectPromiseFlow;
|
|
299
|
+
}
|
|
300
|
+
getConnectFlow(options) {
|
|
301
|
+
// assume the defaults are used
|
|
302
|
+
return (0, flow_1.outgoingConnectFlow)(options);
|
|
303
|
+
}
|
|
304
|
+
updateKeepAlive(value) {
|
|
305
|
+
value = Math.max(value - 0.5, 1);
|
|
306
|
+
if (this.keepAliveTimer) {
|
|
307
|
+
this.stopExecuting(this.keepAliveTimer);
|
|
308
|
+
}
|
|
309
|
+
this.mqttDebug(`Starting keep-alive-ping {delay: ${value}}`);
|
|
310
|
+
this.keepAliveTimer = this.executePeriodically(value * 1000, () => {
|
|
311
|
+
// assume the defaults are used
|
|
312
|
+
this.startFlow((0, flow_1.outgoingPingFlow)())
|
|
313
|
+
.then(() => this.pingDebug(`PingPong @ ${Date.now()}`))
|
|
314
|
+
.catch((e) => {
|
|
315
|
+
this.emitWarning(e);
|
|
316
|
+
this.pingDebug(`PingPong failed. (${e.message})`);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
sendData(data) {
|
|
321
|
+
if (!this.transport.duplex) {
|
|
322
|
+
this.emitError(
|
|
323
|
+
new errors_1.IllegalStateError("Expected a duplex - was undefined")
|
|
324
|
+
);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
this.transport.duplex.write(data);
|
|
328
|
+
}
|
|
329
|
+
async handlePacket(packet) {
|
|
330
|
+
this.logReceivedPacket(packet);
|
|
331
|
+
this.emit(
|
|
332
|
+
mqtt_constants_1.PacketType[packet.type].toUpperCase(),
|
|
333
|
+
packet.data
|
|
334
|
+
);
|
|
335
|
+
let forceCheckFlows = false;
|
|
336
|
+
// The following "type assertions" are valid as clients extending MqttClient have to implement their own methods
|
|
337
|
+
switch (packet.type) {
|
|
338
|
+
case mqtt_constants_1.PacketType.ConnAck: {
|
|
339
|
+
this.onConnAck(packet.data);
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
case mqtt_constants_1.PacketType.Publish: {
|
|
343
|
+
this.onPublish(packet.data);
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
case mqtt_constants_1.PacketType.PingReq: {
|
|
347
|
+
this.onPingReq();
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
case mqtt_constants_1.PacketType.Disconnect: {
|
|
351
|
+
this.setDisconnected("disconnect packet received").catch((e) =>
|
|
352
|
+
this.emitWarning(e)
|
|
353
|
+
);
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
default:
|
|
357
|
+
forceCheckFlows = true;
|
|
358
|
+
}
|
|
359
|
+
if (!this.continueFlows(packet) && forceCheckFlows) {
|
|
360
|
+
this.emitWarning(
|
|
361
|
+
new errors_1.UnexpectedPacketError(
|
|
362
|
+
(0, mqtt_constants_1.packetTypeToString)(packet.type)
|
|
363
|
+
)
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
onConnAck(connAck) {
|
|
368
|
+
if (connAck.isSuccess) {
|
|
369
|
+
this.setReady();
|
|
370
|
+
this.emitConnect(connAck);
|
|
371
|
+
if (this.connectOptions?.keepAlive) {
|
|
372
|
+
this.updateKeepAlive(this.connectOptions.keepAlive);
|
|
373
|
+
}
|
|
374
|
+
this.reconnectStrategy?.reset();
|
|
375
|
+
} else {
|
|
376
|
+
const error = new errors_1.ConnectError(connAck.errorName);
|
|
377
|
+
this.setDisconnected(error).catch((e) => this.emitWarning(e));
|
|
378
|
+
this.emitError(error);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
onPublish(publish) {
|
|
382
|
+
this.startFlow(
|
|
383
|
+
(0, flow_1.incomingPublishFlow)(
|
|
384
|
+
{
|
|
385
|
+
topic: publish.topic,
|
|
386
|
+
payload: publish.payload,
|
|
387
|
+
qosLevel: publish.qos,
|
|
388
|
+
retained: publish.retain,
|
|
389
|
+
duplicate: publish.duplicate,
|
|
390
|
+
},
|
|
391
|
+
publish.identifier ?? undefined
|
|
392
|
+
)
|
|
393
|
+
)
|
|
394
|
+
.then(async (m) => {
|
|
395
|
+
this.emitMessage(m);
|
|
396
|
+
await this.messageListener.handleMessage(m);
|
|
397
|
+
})
|
|
398
|
+
.catch((e) => this.emitWarning(e));
|
|
399
|
+
}
|
|
400
|
+
onPingReq() {
|
|
401
|
+
this.startFlow((0, flow_1.incomingPingFlow)())
|
|
402
|
+
.then(() => this.pingDebug(`Server-PingPong @ ${Date.now()}`))
|
|
403
|
+
.catch((e) => {
|
|
404
|
+
this.emitWarning(e);
|
|
405
|
+
this.pingDebug(`Server-PingPong failed. (${e.message})`);
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
logReceivedPacket(packet) {
|
|
409
|
+
if (
|
|
410
|
+
packet.type !== mqtt_constants_1.PacketType.PingReq &&
|
|
411
|
+
packet.type !== mqtt_constants_1.PacketType.PingResp
|
|
412
|
+
)
|
|
413
|
+
this.receiveDebug(
|
|
414
|
+
`Received ${(0, mqtt_utilities_1.stringifyObject)(packet.data)}`
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
reset() {
|
|
418
|
+
super.reset();
|
|
419
|
+
|
|
420
|
+
try {
|
|
421
|
+
this.stopExecutingFlows(new errors_1.AbortError("Resetting"));
|
|
422
|
+
} catch (e) {
|
|
423
|
+
this.emitWarning(tryMakeError(e));
|
|
396
424
|
}
|
|
397
|
-
async setDisconnected(reason) {
|
|
398
|
-
const willReconnect = this.reconnectStrategy?.check(reason) ?? false;
|
|
399
|
-
this.mqttDebug(`Disconnected. Will reconnect: ${willReconnect}`);
|
|
400
|
-
this._setDisconnected();
|
|
401
425
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
426
|
+
if (this.keepAliveTimer) {
|
|
427
|
+
clearInterval(this.keepAliveTimer);
|
|
428
|
+
this.keepAliveTimer = undefined;
|
|
429
|
+
}
|
|
430
|
+
this.transformer.reset();
|
|
431
|
+
}
|
|
432
|
+
setReady() {
|
|
433
|
+
super.setReady();
|
|
434
|
+
this.mqttDebug("Ready!");
|
|
435
|
+
}
|
|
436
|
+
async reconnect() {
|
|
437
|
+
this.transport.reset();
|
|
438
|
+
this.transformer = this.createTransformer();
|
|
439
|
+
this.transformer.options.debug =
|
|
440
|
+
this.transformer.options.debug ?? this.mqttDebug.extend("transformer");
|
|
441
|
+
await this.reconnectStrategy?.wait();
|
|
442
|
+
await this.connect();
|
|
443
|
+
}
|
|
444
|
+
async setDisconnected(reason) {
|
|
445
|
+
const willReconnect = this.reconnectStrategy?.check(reason) ?? false;
|
|
446
|
+
this.mqttDebug(`Disconnected. Will reconnect: ${willReconnect}`);
|
|
447
|
+
this._setDisconnected();
|
|
407
448
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
this.transport.duplex.destroy(new Error('force destroy'));
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
this.stopExecuting(this.keepAliveTimer);
|
|
416
|
-
this.reset();
|
|
417
|
-
if (willReconnect) {
|
|
418
|
-
await this.reconnect();
|
|
419
|
-
}
|
|
449
|
+
try {
|
|
450
|
+
this.stopExecutingFlows(new errors_1.AbortError("Client disconnected."));
|
|
451
|
+
} catch (e) {
|
|
452
|
+
this.emitWarning(tryMakeError(e));
|
|
420
453
|
}
|
|
454
|
+
|
|
455
|
+
this.emitDisconnect({ reason, reconnect: willReconnect });
|
|
456
|
+
if (this.transport.active) {
|
|
457
|
+
await new Promise(
|
|
458
|
+
(resolve) =>
|
|
459
|
+
this.transport.duplex?.end(resolve) ?? /* never */ resolve()
|
|
460
|
+
);
|
|
461
|
+
if (this.transport.duplex && !this.transport.duplex.writableEnded) {
|
|
462
|
+
this.transport.duplex.destroy(new Error("force destroy"));
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
this.stopExecuting(this.keepAliveTimer);
|
|
466
|
+
this.reset();
|
|
467
|
+
if (willReconnect) {
|
|
468
|
+
await this.reconnect();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
421
471
|
}
|
|
422
472
|
exports.MqttClient = MqttClient;
|
|
423
473
|
//# sourceMappingURL=mqtt.client.js.map
|
package/package.json
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
2
|
+
"name": "abmqtt-dist",
|
|
3
|
+
"version": "0.0.6",
|
|
4
|
+
"description": "MQTT client in Typescript",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=18.0.0"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/ozanbaskan/mqtts"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/ozanbaskan/mqtts/issues"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "jest",
|
|
22
|
+
"build": "",
|
|
23
|
+
"prepare": "npm run build",
|
|
24
|
+
"lint-fix": "eslint --fix --ext .js,.ts src/**",
|
|
25
|
+
"lint": "eslint --ext .js,.ts src/**",
|
|
26
|
+
"format": "prettier -w . --loglevel warn",
|
|
27
|
+
"check-format": "prettier -c .",
|
|
28
|
+
"typecheck": "tsc -p tsconfig.build.json --noEmit",
|
|
29
|
+
"precommit": "pnpm run typecheck && pnpm run lint && pnpm run format"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@types/ws": "^8.5.10",
|
|
33
|
+
"debug": "^4.3.4",
|
|
34
|
+
"eventemitter3": "^5.0.1",
|
|
35
|
+
"socks": "^2.8.1",
|
|
36
|
+
"ts-custom-error": "^3.3.1",
|
|
37
|
+
"ts-xor": "^1.3.0",
|
|
38
|
+
"ws": "^8.16.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@swc/core": "^1.4.12",
|
|
42
|
+
"@swc/jest": "^0.2.36",
|
|
43
|
+
"@types/chai": "^4.3.14",
|
|
44
|
+
"@types/chai-as-promised": "^7.1.8",
|
|
45
|
+
"@types/debug": "^4.1.12",
|
|
46
|
+
"@types/duplexify": "^3.6.4",
|
|
47
|
+
"@types/jest": "^29.5.12",
|
|
48
|
+
"@types/node": "^18.11.9",
|
|
49
|
+
"@types/sinon": "^17.0.3",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^7.5.0",
|
|
51
|
+
"@typescript-eslint/parser": "^7.5.0",
|
|
52
|
+
"chai": "^4.4.1 < 5",
|
|
53
|
+
"chai-as-promised": "^7.1.1",
|
|
54
|
+
"duplexify": "^4.1.3",
|
|
55
|
+
"eslint": "^8.57.0",
|
|
56
|
+
"jest": "^29.7.0",
|
|
57
|
+
"prettier": "^3.2.5",
|
|
58
|
+
"rimraf": "^5.0.5",
|
|
59
|
+
"sinon": "^17.0.1",
|
|
60
|
+
"ts-jest": "^29.1.2",
|
|
61
|
+
"typescript": "^5.4.4"
|
|
62
|
+
},
|
|
63
|
+
"keywords": [
|
|
64
|
+
"MQTT",
|
|
65
|
+
"subscribe",
|
|
66
|
+
"iot",
|
|
67
|
+
"tls"
|
|
68
|
+
],
|
|
69
|
+
"author": {
|
|
70
|
+
"name": "Ozan Baskan",
|
|
71
|
+
"email": "ozanbaskan7@gmail.com"
|
|
72
|
+
},
|
|
73
|
+
"license": "MIT"
|
|
74
74
|
}
|