abmqtt-dist 0.0.4 → 0.0.5
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 +457 -403
- package/package.json +72 -72
package/dist/mqtt.client.js
CHANGED
|
@@ -1,423 +1,477 @@
|
|
|
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
|
-
|
|
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);
|
|
101
135
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
this.emitError(err);
|
|
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));
|
|
112
145
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (!this.disconnected) {
|
|
131
|
-
(err ? this.setDisconnected(tryMakeError(err)) : this.setDisconnected('Pipeline finished')).catch(
|
|
132
|
-
e => this.emitWarning(e),
|
|
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');
|
|
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");
|
|
154
163
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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));
|
|
225
237
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
clearFinishedFlows()
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
238
|
+
result = true;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
this.clearFinishedFlows();
|
|
242
|
+
return result;
|
|
243
|
+
}
|
|
244
|
+
clearFinishedFlows() {
|
|
245
|
+
this.activeFlows = this.activeFlows.filter((flow) => !flow.finished);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
stopExecutingFlows(error) {
|
|
249
|
+
const flows = [...this.activeFlows];
|
|
250
|
+
this.activeFlows = [];
|
|
251
|
+
|
|
252
|
+
for (const flow of flows) {
|
|
253
|
+
flow.finished = true;
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
Promise.resolve()
|
|
257
|
+
.then(() => {
|
|
234
258
|
flow.resolvers.reject(error);
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// not sure why this is necessary, but it's there so no unhandledRejection is thrown
|
|
259
|
-
.catch(() => undefined);
|
|
260
|
-
}
|
|
261
|
-
options.signal?.addEventListener('abort', () => {
|
|
262
|
-
try {
|
|
263
|
-
this.stopFlow(connectPromiseFlow.flowId, new errors_1.AbortError('Connecting aborted'));
|
|
264
|
-
} catch (e) {
|
|
265
|
-
this.emitWarning(tryMakeError(e));
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
return connectPromiseFlow;
|
|
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);
|
|
259
|
+
})
|
|
260
|
+
.catch(() => {
|
|
261
|
+
this.mqttDebug?.("Flow rejected during cleanup (expected)");
|
|
262
|
+
});
|
|
263
|
+
} catch (e) {
|
|
264
|
+
this.emitWarning?.(tryMakeError(e));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
getFlowById(id) {
|
|
270
|
+
return this.activeFlows.find((f) => f.flowId === id);
|
|
271
|
+
}
|
|
272
|
+
registerClient(options) {
|
|
273
|
+
const flow = this.getConnectFlow(options);
|
|
274
|
+
const connectPromiseFlow = this.startFlow(flow);
|
|
275
|
+
if (typeof options.connectDelay !== "undefined") {
|
|
276
|
+
const timerId = this.executeDelayed(options.connectDelay ?? 2000, () => {
|
|
277
|
+
const flow = this.getFlowById(connectPromiseFlow.flowId);
|
|
278
|
+
if (!flow) {
|
|
279
|
+
// there's no flow anymore
|
|
280
|
+
this.stopExecuting(timerId);
|
|
281
|
+
return;
|
|
278
282
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
283
|
+
const packet = flow.callbacks.start();
|
|
284
|
+
if (packet)
|
|
285
|
+
this.sendData(this.writer.write(packet.type, packet.options));
|
|
286
|
+
});
|
|
287
|
+
connectPromiseFlow
|
|
288
|
+
.finally(() => this.stopExecuting(timerId))
|
|
289
|
+
// not sure why this is necessary, but it's there so no unhandledRejection is thrown
|
|
290
|
+
.catch(() => undefined);
|
|
291
|
+
}
|
|
292
|
+
options.signal?.addEventListener("abort", () => {
|
|
293
|
+
try {
|
|
294
|
+
this.stopFlow(
|
|
295
|
+
connectPromiseFlow.flowId,
|
|
296
|
+
new errors_1.AbortError("Connecting aborted")
|
|
297
|
+
);
|
|
298
|
+
} catch (e) {
|
|
299
|
+
this.emitWarning(tryMakeError(e));
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
return connectPromiseFlow;
|
|
303
|
+
}
|
|
304
|
+
getConnectFlow(options) {
|
|
305
|
+
// assume the defaults are used
|
|
306
|
+
return (0, flow_1.outgoingConnectFlow)(options);
|
|
307
|
+
}
|
|
308
|
+
updateKeepAlive(value) {
|
|
309
|
+
value = Math.max(value - 0.5, 1);
|
|
310
|
+
if (this.keepAliveTimer) {
|
|
311
|
+
this.stopExecuting(this.keepAliveTimer);
|
|
312
|
+
}
|
|
313
|
+
this.mqttDebug(`Starting keep-alive-ping {delay: ${value}}`);
|
|
314
|
+
this.keepAliveTimer = this.executePeriodically(value * 1000, () => {
|
|
315
|
+
// assume the defaults are used
|
|
316
|
+
this.startFlow((0, flow_1.outgoingPingFlow)())
|
|
317
|
+
.then(() => this.pingDebug(`PingPong @ ${Date.now()}`))
|
|
318
|
+
.catch((e) => {
|
|
319
|
+
this.emitWarning(e);
|
|
320
|
+
this.pingDebug(`PingPong failed. (${e.message})`);
|
|
288
321
|
});
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
sendData(data) {
|
|
325
|
+
if (!this.transport.duplex) {
|
|
326
|
+
this.emitError(
|
|
327
|
+
new errors_1.IllegalStateError("Expected a duplex - was undefined")
|
|
328
|
+
);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
this.transport.duplex.write(data);
|
|
332
|
+
}
|
|
333
|
+
async handlePacket(packet) {
|
|
334
|
+
this.logReceivedPacket(packet);
|
|
335
|
+
this.emit(
|
|
336
|
+
mqtt_constants_1.PacketType[packet.type].toUpperCase(),
|
|
337
|
+
packet.data
|
|
338
|
+
);
|
|
339
|
+
let forceCheckFlows = false;
|
|
340
|
+
// The following "type assertions" are valid as clients extending MqttClient have to implement their own methods
|
|
341
|
+
switch (packet.type) {
|
|
342
|
+
case mqtt_constants_1.PacketType.ConnAck: {
|
|
343
|
+
this.onConnAck(packet.data);
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
case mqtt_constants_1.PacketType.Publish: {
|
|
347
|
+
this.onPublish(packet.data);
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
case mqtt_constants_1.PacketType.PingReq: {
|
|
351
|
+
this.onPingReq();
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
case mqtt_constants_1.PacketType.Disconnect: {
|
|
355
|
+
this.setDisconnected("disconnect packet received").catch((e) =>
|
|
356
|
+
this.emitWarning(e)
|
|
357
|
+
);
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
default:
|
|
361
|
+
forceCheckFlows = true;
|
|
362
|
+
}
|
|
363
|
+
if (!this.continueFlows(packet) && forceCheckFlows) {
|
|
364
|
+
this.emitWarning(
|
|
365
|
+
new errors_1.UnexpectedPacketError(
|
|
366
|
+
(0, mqtt_constants_1.packetTypeToString)(packet.type)
|
|
352
367
|
)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
onConnAck(connAck) {
|
|
372
|
+
if (connAck.isSuccess) {
|
|
373
|
+
this.setReady();
|
|
374
|
+
this.emitConnect(connAck);
|
|
375
|
+
if (this.connectOptions?.keepAlive) {
|
|
376
|
+
this.updateKeepAlive(this.connectOptions.keepAlive);
|
|
377
|
+
}
|
|
378
|
+
this.reconnectStrategy?.reset();
|
|
379
|
+
} else {
|
|
380
|
+
const error = new errors_1.ConnectError(connAck.errorName);
|
|
381
|
+
this.setDisconnected(error).catch((e) => this.emitWarning(e));
|
|
382
|
+
this.emitError(error);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
onPublish(publish) {
|
|
386
|
+
this.startFlow(
|
|
387
|
+
(0, flow_1.incomingPublishFlow)(
|
|
388
|
+
{
|
|
389
|
+
topic: publish.topic,
|
|
390
|
+
payload: publish.payload,
|
|
391
|
+
qosLevel: publish.qos,
|
|
392
|
+
retained: publish.retain,
|
|
393
|
+
duplicate: publish.duplicate,
|
|
394
|
+
},
|
|
395
|
+
publish.identifier ?? undefined
|
|
396
|
+
)
|
|
397
|
+
)
|
|
398
|
+
.then(async (m) => {
|
|
399
|
+
this.emitMessage(m);
|
|
400
|
+
await this.messageListener.handleMessage(m);
|
|
401
|
+
})
|
|
402
|
+
.catch((e) => this.emitWarning(e));
|
|
403
|
+
}
|
|
404
|
+
onPingReq() {
|
|
405
|
+
this.startFlow((0, flow_1.incomingPingFlow)())
|
|
406
|
+
.then(() => this.pingDebug(`Server-PingPong @ ${Date.now()}`))
|
|
407
|
+
.catch((e) => {
|
|
408
|
+
this.emitWarning(e);
|
|
409
|
+
this.pingDebug(`Server-PingPong failed. (${e.message})`);
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
logReceivedPacket(packet) {
|
|
413
|
+
if (
|
|
414
|
+
packet.type !== mqtt_constants_1.PacketType.PingReq &&
|
|
415
|
+
packet.type !== mqtt_constants_1.PacketType.PingResp
|
|
416
|
+
)
|
|
417
|
+
this.receiveDebug(
|
|
418
|
+
`Received ${(0, mqtt_utilities_1.stringifyObject)(packet.data)}`
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
reset() {
|
|
422
|
+
super.reset();
|
|
379
423
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
this.transformer.reset();
|
|
385
|
-
}
|
|
386
|
-
setReady() {
|
|
387
|
-
super.setReady();
|
|
388
|
-
this.mqttDebug('Ready!');
|
|
424
|
+
try {
|
|
425
|
+
this.stopExecutingFlows(new errors_1.AbortError("Resetting"));
|
|
426
|
+
} catch (e) {
|
|
427
|
+
this.emitWarning(tryMakeError(e));
|
|
389
428
|
}
|
|
390
|
-
async reconnect() {
|
|
391
|
-
this.transport.reset();
|
|
392
|
-
this.transformer = this.createTransformer();
|
|
393
|
-
this.transformer.options.debug = this.transformer.options.debug ?? this.mqttDebug.extend('transformer');
|
|
394
|
-
await this.reconnectStrategy?.wait();
|
|
395
|
-
await this.connect();
|
|
396
|
-
}
|
|
397
|
-
async setDisconnected(reason) {
|
|
398
|
-
const willReconnect = this.reconnectStrategy?.check(reason) ?? false;
|
|
399
|
-
this.mqttDebug(`Disconnected. Will reconnect: ${willReconnect}`);
|
|
400
|
-
this._setDisconnected();
|
|
401
429
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
430
|
+
if (this.keepAliveTimer) {
|
|
431
|
+
clearInterval(this.keepAliveTimer);
|
|
432
|
+
this.keepAliveTimer = undefined;
|
|
433
|
+
}
|
|
434
|
+
this.transformer.reset();
|
|
435
|
+
}
|
|
436
|
+
setReady() {
|
|
437
|
+
super.setReady();
|
|
438
|
+
this.mqttDebug("Ready!");
|
|
439
|
+
}
|
|
440
|
+
async reconnect() {
|
|
441
|
+
this.transport.reset();
|
|
442
|
+
this.transformer = this.createTransformer();
|
|
443
|
+
this.transformer.options.debug =
|
|
444
|
+
this.transformer.options.debug ?? this.mqttDebug.extend("transformer");
|
|
445
|
+
await this.reconnectStrategy?.wait();
|
|
446
|
+
await this.connect();
|
|
447
|
+
}
|
|
448
|
+
async setDisconnected(reason) {
|
|
449
|
+
const willReconnect = this.reconnectStrategy?.check(reason) ?? false;
|
|
450
|
+
this.mqttDebug(`Disconnected. Will reconnect: ${willReconnect}`);
|
|
451
|
+
this._setDisconnected();
|
|
407
452
|
|
|
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
|
-
}
|
|
453
|
+
try {
|
|
454
|
+
this.stopExecutingFlows(new errors_1.AbortError("Client disconnected."));
|
|
455
|
+
} catch (e) {
|
|
456
|
+
this.emitWarning(tryMakeError(e));
|
|
420
457
|
}
|
|
458
|
+
|
|
459
|
+
this.emitDisconnect({ reason, reconnect: willReconnect });
|
|
460
|
+
if (this.transport.active) {
|
|
461
|
+
await new Promise(
|
|
462
|
+
(resolve) =>
|
|
463
|
+
this.transport.duplex?.end(resolve) ?? /* never */ resolve()
|
|
464
|
+
);
|
|
465
|
+
if (this.transport.duplex && !this.transport.duplex.writableEnded) {
|
|
466
|
+
this.transport.duplex.destroy(new Error("force destroy"));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
this.stopExecuting(this.keepAliveTimer);
|
|
470
|
+
this.reset();
|
|
471
|
+
if (willReconnect) {
|
|
472
|
+
await this.reconnect();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
421
475
|
}
|
|
422
476
|
exports.MqttClient = MqttClient;
|
|
423
477
|
//# 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.5",
|
|
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
|
}
|