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.
Files changed (2) hide show
  1. package/dist/mqtt.client.js +457 -403
  2. package/package.json +72 -72
@@ -1,423 +1,477 @@
1
- 'use strict';
2
- Object.defineProperty(exports, '__esModule', { value: true });
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MqttClient = void 0;
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');
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
- 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})`);
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
- 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)(packetLogger),
80
- },
81
- options.writeMap,
82
- );
83
- }
84
- async _connect(options) {
85
- this.expectCreated();
86
- this.mqttDebug(`Connecting using transport "${this.transport.constructor.name}"`);
87
- this.connectResolver = options;
88
- this.setConnecting();
89
- try {
90
- await this.transport.connect();
91
- } catch (e) {
92
- const err = tryMakeError(e);
93
- this.mqttDebug(`Transport connect error ("${this.transport.constructor.name}")`, err.message);
94
- const shouldReconnect = this.reconnectStrategy?.check();
95
- await this.setDisconnected(err);
96
- if (shouldReconnect) {
97
- return;
98
- } else {
99
- throw e;
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
- this.createPipeline();
103
- return this.registerClient(await this.resolveConnectOptions());
104
- }
105
- async connect(options) {
106
- try {
107
- await this._connect(options);
108
- } catch (e) {
109
- const err = tryMakeError(e);
110
- this.mqttDebug(`Connection error`, err);
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
- createPipeline() {
115
- if (!this.transport.duplex) throw new errors_1.IllegalStateError('Expected transport to expose a Duplex.');
116
- this.pipeline = (0, stream_1.pipeline)(
117
- this.transport.duplex,
118
- this.transformer,
119
- async source => {
120
- for await (const chunk of source) {
121
- if (!chunk.type) {
122
- throw new Error('Chunk is not a MqttPacket');
123
- }
124
- await this.handlePacket(chunk);
125
- }
126
- return 'Source drained';
127
- } /* bad definitions */,
128
- err => {
129
- if (err) this.emitError(tryMakeError(err));
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
- listenSubscribe(options, handlerFn) {
157
- const listener = typeof options === 'string' ? { topic: options } : options;
158
- return this.subscribe({
159
- ...listener.subscriptionInfo,
160
- topic: listener.topic.replace(/\/:[A-Za-z-_0-9]+/g, '/+'),
161
- }).then(() => this.listen(listener, handlerFn));
162
- }
163
- listen(options, handlerFn) {
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
- }
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
- this.clearFinishedFlows();
227
- return result;
228
- }
229
- clearFinishedFlows() {
230
- this.activeFlows = this.activeFlows.filter(flow => !flow.finished);
231
- }
232
- stopExecutingFlows(error) {
233
- for (const flow of this.activeFlows) {
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
- flow.finished = true;
236
- }
237
- this.activeFlows = [];
238
- }
239
- getFlowById(id) {
240
- return this.activeFlows.find(f => f.flowId === id);
241
- }
242
- registerClient(options) {
243
- const flow = this.getConnectFlow(options);
244
- const connectPromiseFlow = this.startFlow(flow);
245
- if (typeof options.connectDelay !== 'undefined') {
246
- const timerId = this.executeDelayed(options.connectDelay ?? 2000, () => {
247
- const flow = this.getFlowById(connectPromiseFlow.flowId);
248
- if (!flow) {
249
- // there's no flow anymore
250
- this.stopExecuting(timerId);
251
- return;
252
- }
253
- const packet = flow.callbacks.start();
254
- if (packet) this.sendData(this.writer.write(packet.type, packet.options));
255
- });
256
- connectPromiseFlow
257
- .finally(() => this.stopExecuting(timerId))
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
- 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
- });
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
- 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
- ),
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
- .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();
373
-
374
- try {
375
- this.stopExecutingFlows(new errors_1.AbortError('Resetting'));
376
- } catch (e) {
377
- this.emitWarning(tryMakeError(e));
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
- if (this.keepAliveTimer) {
381
- clearInterval(this.keepAliveTimer);
382
- this.keepAliveTimer = undefined;
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
- try {
403
- this.stopExecutingFlows(new errors_1.AbortError('Client disconnected.'));
404
- } catch (e) {
405
- this.emitWarning(tryMakeError(e));
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
- this.emitDisconnect({ reason, reconnect: willReconnect });
409
- if (this.transport.active) {
410
- await new Promise(resolve => this.transport.duplex?.end(resolve) ?? /* never */ resolve());
411
- if (this.transport.duplex && !this.transport.duplex.writableEnded) {
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
- "name": "abmqtt-dist",
3
- "version": "0.0.4",
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"
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
  }