websocket-mqtt 0.0.3 → 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/README.md CHANGED
@@ -1 +1,38 @@
1
1
  # websocket-mqtt
2
+
3
+ A lightweight zero-dependency MQTT WebSocket implementation.
4
+
5
+ ## Features
6
+
7
+ - Zero-dependency
8
+ - Last-will support
9
+ - QoS support
10
+ - MQTT 3.1.1 support
11
+
12
+ ## Installation
13
+
14
+ ```
15
+ pnpm install websocket-mqtt
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```typescript
21
+ import { createMqtt } from "websocket-mqtt";
22
+
23
+ const client = createMqtt({
24
+ url: "wss://example.com/mqtt",
25
+ });
26
+
27
+ await client.connect();
28
+
29
+ client.on("message", (topic, payload) => {
30
+ console.log(topic, payload);
31
+ });
32
+
33
+ await client.subscribe("topic");
34
+
35
+ await client.publish("topic", "message");
36
+
37
+ await client.close();
38
+ ```
package/dist/base.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { Connection, ConnectionOptions } from "./connection.js";
2
+ import { type ConnackPacket, type PublishPacket, type QoSLevel } from "./packets/index.js";
3
+ import { type EventEmitter } from "./utils/events.js";
4
+ export type MqttEvents = {
5
+ connect: [packet: ConnackPacket];
6
+ message: [topic: string, payload: Uint8Array, packet: PublishPacket];
7
+ error: [error: unknown];
8
+ close: [];
9
+ };
10
+ export type MqttOptions = {
11
+ url: string;
12
+ } & ConnectionOptions;
13
+ export type MqttClient = EventEmitter<MqttEvents> & {
14
+ connection: Connection;
15
+ isConnected: () => boolean;
16
+ publish: (topic: string, payload: string | Uint8Array, options?: {
17
+ qos?: QoSLevel;
18
+ retain?: boolean;
19
+ }) => Promise<void>;
20
+ subscribe: (topic: string, qos?: QoSLevel) => Promise<number[]>;
21
+ connect: () => Promise<void>;
22
+ close: () => void;
23
+ };
24
+ export type MqttClientInternalRequest<K> = {
25
+ resolve: (value: K) => void;
26
+ reject: (err: Error) => void;
27
+ };
28
+ export type MqttClientInternalPending = {
29
+ sub: Map<number, MqttClientInternalRequest<number[]>>;
30
+ pub: Map<number, MqttClientInternalRequest<void>>;
31
+ };
32
+ export declare const createMqtt: (options: MqttOptions) => MqttClient;
33
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../src/base.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,iBAAiB,EAElB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,aAAa,EAElB,KAAK,QAAQ,EACd,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAG1E,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACjC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IACrE,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACxB,KAAK,EAAE,EAAE,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,iBAAiB,CAAC;AAEtB,MAAM,MAAM,UAAU,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG;IAClD,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,EAAE,MAAM,OAAO,CAAC;IAC3B,OAAO,EAAE,CACP,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GAAG,UAAU,EAC5B,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,QAAQ,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAC3C,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,QAAQ,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI;IACzC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,MAAM,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAC9B,CAAC;AACF,MAAM,MAAM,yBAAyB,GAAG;IACtC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,yBAAyB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACtD,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC;CACnD,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,SAAS,WAAW,KAAG,UAqIjD,CAAC"}
package/dist/base.js ADDED
@@ -0,0 +1,114 @@
1
+ import { createConnection, } from "./connection.js";
2
+ import { PacketType, QoS, } from "./packets/index.js";
3
+ import { toUint8Array } from "./utils/buffer.js";
4
+ import { createEventEmitter } from "./utils/events.js";
5
+ import { createLogger } from "./utils/logger.js";
6
+ export const createMqtt = (options) => {
7
+ const pending = {
8
+ sub: new Map(),
9
+ pub: new Map(),
10
+ };
11
+ const events = createEventEmitter();
12
+ const log = createLogger(options);
13
+ const connection = createConnection(options);
14
+ connection.on("connect", (packet) => {
15
+ events.emit("connect", packet);
16
+ });
17
+ connection.on("message", (topic, payload, packet) => {
18
+ events.emit("message", topic, payload, packet);
19
+ });
20
+ connection.on("packet", (packet) => {
21
+ switch (packet.type) {
22
+ case PacketType.SUBACK: {
23
+ log("SUBACK", packet);
24
+ const request = pending.sub.get(packet.messageId);
25
+ if (request) {
26
+ pending.sub.delete(packet.messageId);
27
+ const hasFailure = packet.granted.some((qos) => qos === 0x80);
28
+ if (hasFailure) {
29
+ request.reject(new Error("Subscription failed"));
30
+ }
31
+ else {
32
+ request.resolve(packet.granted);
33
+ }
34
+ }
35
+ break;
36
+ }
37
+ case PacketType.PUBACK: {
38
+ log("PUBACK", packet);
39
+ const request = pending.pub.get(packet.messageId);
40
+ if (request) {
41
+ pending.pub.delete(packet.messageId);
42
+ request.resolve();
43
+ }
44
+ break;
45
+ }
46
+ }
47
+ });
48
+ connection.on("error", (err) => {
49
+ console.error("error", err);
50
+ events.emit("error", err);
51
+ });
52
+ connection.on("close", () => {
53
+ for (const request of pending.sub.values()) {
54
+ request.reject(new Error("Connection closed"));
55
+ }
56
+ pending.sub.clear();
57
+ for (const request of pending.pub.values()) {
58
+ request.reject(new Error("Connection closed"));
59
+ }
60
+ pending.pub.clear();
61
+ events.emit("close");
62
+ });
63
+ const publish = (topic, payload, options) => {
64
+ const qos = options?.qos ?? QoS.AT_MOST_ONCE;
65
+ const retain = options?.retain ?? false;
66
+ const messageId = qos > 0 ? connection.nextMessageId() : undefined;
67
+ connection.send({
68
+ type: PacketType.PUBLISH,
69
+ topic,
70
+ payload: toUint8Array(payload),
71
+ qos,
72
+ retain,
73
+ dup: false,
74
+ messageId,
75
+ });
76
+ if (qos === 0) {
77
+ return Promise.resolve();
78
+ }
79
+ return new Promise((resolve, reject) => {
80
+ if (messageId)
81
+ pending.pub.set(messageId, { resolve, reject });
82
+ });
83
+ };
84
+ const subscribe = (topic, qos = QoS.AT_MOST_ONCE) => {
85
+ return new Promise((resolve, reject) => {
86
+ const messageId = connection.nextMessageId();
87
+ pending.sub.set(messageId, { resolve, reject });
88
+ connection.send({
89
+ type: PacketType.SUBSCRIBE,
90
+ messageId,
91
+ subscriptions: [{ topic, qos }],
92
+ });
93
+ });
94
+ };
95
+ const connect = () => {
96
+ return new Promise((resolve, reject) => {
97
+ connection.open();
98
+ connection.on("connect", () => resolve());
99
+ connection.on("error", (err) => reject(err));
100
+ });
101
+ };
102
+ return {
103
+ ...events,
104
+ publish,
105
+ subscribe,
106
+ connection,
107
+ isConnected: () => connection.isConnected(),
108
+ connect,
109
+ close: () => {
110
+ log("closing from up above");
111
+ connection.close();
112
+ },
113
+ };
114
+ };
@@ -0,0 +1,44 @@
1
+ import { ConnackPacket, IncomingPacket, OutgoingPacket, PublishPacket, QoSLevel } from "./packets/index.js";
2
+ import { type LogOptions } from "./utils/logger.js";
3
+ export type ConnectionOptions = {
4
+ url: string;
5
+ clientId?: string;
6
+ username?: string;
7
+ password?: string;
8
+ keepalive?: number;
9
+ clean?: boolean;
10
+ will?: {
11
+ topic: string;
12
+ payload: string | Uint8Array;
13
+ qos?: QoSLevel;
14
+ retain?: boolean;
15
+ };
16
+ signal?: AbortSignal;
17
+ } & LogOptions;
18
+ export type HandlePacketFunction = (packet: IncomingPacket) => void;
19
+ export type Connection = {
20
+ open: () => void;
21
+ close: () => void;
22
+ send: (packet: OutgoingPacket) => void;
23
+ nextMessageId: () => number;
24
+ isConnected: () => boolean;
25
+ };
26
+ export type ConnectionEvents = {
27
+ connect: [packet: ConnackPacket];
28
+ message: [topic: string, payload: Uint8Array, packet: PublishPacket];
29
+ packet: [packet: IncomingPacket];
30
+ error: [error: unknown];
31
+ close: [];
32
+ };
33
+ export declare const DEFAULT_CLIENT_ID = "websocket_mqtt_";
34
+ export declare const createConnection: (options: ConnectionOptions) => {
35
+ on<K extends keyof ConnectionEvents>(event: K, listener: import("./utils/events.js").EventListener<ConnectionEvents, K>): void;
36
+ off<K extends keyof ConnectionEvents>(event: K, listener: import("./utils/events.js").EventListener<ConnectionEvents, K>): void;
37
+ emit<K extends keyof ConnectionEvents>(event: K, ...args: ConnectionEvents[K]): void;
38
+ close: () => void;
39
+ open: () => void;
40
+ send: (packet: OutgoingPacket) => void;
41
+ nextMessageId: () => number;
42
+ isConnected: () => boolean;
43
+ };
44
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,aAAa,EAGb,cAAc,EACd,cAAc,EAEd,aAAa,EAEb,QAAQ,EACT,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAElE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;QAC7B,GAAG,CAAC,EAAE,QAAQ,CAAC;QACf,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;IACF,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,UAAU,CAAC;AAEf,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;AAEpE,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACvC,aAAa,EAAE,MAAM,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,OAAO,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACjC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IACrE,MAAM,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACjC,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACxB,KAAK,EAAE,EAAE,CAAC;CACX,CAAC;AAEF,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAEnD,eAAO,MAAM,gBAAgB,GAAI,SAAS,iBAAiB;;;;;;mBA6CnC,cAAc,KAAG,IAAI;yBApBjB,MAAM;;CAsJjC,CAAC"}
@@ -0,0 +1,153 @@
1
+ import { encodeConnect, encodeDisconnect, encodePacket, encodePingreq, } from "./packets/encode.js";
2
+ import { decodeAll, DEFAULT_KEEPALIVE_SECONDS, PacketType, QoS, } from "./packets/index.js";
3
+ import { toUint8Array } from "./utils/buffer.js";
4
+ import { createEventEmitter } from "./utils/events.js";
5
+ import { createLogger } from "./utils/logger.js";
6
+ export const DEFAULT_CLIENT_ID = "websocket_mqtt_";
7
+ export const createConnection = (options) => {
8
+ const events = createEventEmitter();
9
+ const log = createLogger(options);
10
+ let lastMessageId = 1;
11
+ let connected = false;
12
+ let pingInterval = null;
13
+ let ws = null;
14
+ const { clientId = DEFAULT_CLIENT_ID + Math.random().toString(36).substring(2, 15), username, password, keepalive = DEFAULT_KEEPALIVE_SECONDS, clean = true, url, } = options;
15
+ const will = options.will
16
+ ? {
17
+ topic: options.will.topic,
18
+ payload: toUint8Array(options.will.payload),
19
+ qos: options.will.qos ?? QoS.AT_MOST_ONCE,
20
+ retain: options.will.retain ?? false,
21
+ }
22
+ : undefined;
23
+ const nextMessageId = () => {
24
+ const messageId = lastMessageId;
25
+ lastMessageId = (messageId % 65535) + 1;
26
+ return lastMessageId;
27
+ };
28
+ const startPingInterval = () => {
29
+ log("starting ping interval");
30
+ if (keepalive > 0) {
31
+ const pingIntervalMs = (keepalive * 1000) / 2;
32
+ pingInterval = setInterval(() => {
33
+ sendRaw(encodePingreq());
34
+ }, pingIntervalMs);
35
+ }
36
+ };
37
+ const send = (packet) => {
38
+ log("sending packet", packet);
39
+ sendRaw(encodePacket(packet));
40
+ };
41
+ const sendRaw = (data) => {
42
+ if (ws && ws.readyState === WebSocket.OPEN) {
43
+ log("sending raw data", data);
44
+ ws.send(data);
45
+ }
46
+ else {
47
+ log("not sending raw data", data);
48
+ }
49
+ };
50
+ const close = () => {
51
+ log("closing connection");
52
+ if (connected && ws) {
53
+ sendRaw(encodeDisconnect());
54
+ }
55
+ ws?.close();
56
+ connected = false;
57
+ if (pingInterval) {
58
+ clearInterval(pingInterval);
59
+ pingInterval = null;
60
+ }
61
+ };
62
+ const open = () => {
63
+ ws = new WebSocket(url, ["mqtt"]);
64
+ ws.binaryType = "arraybuffer";
65
+ ws.onopen = () => {
66
+ // sendConnect();
67
+ const packet = encodeConnect({
68
+ type: PacketType.CONNECT,
69
+ clientId,
70
+ username,
71
+ password,
72
+ keepalive,
73
+ clean,
74
+ will,
75
+ });
76
+ sendRaw(packet);
77
+ };
78
+ let receiveBuffer = new Uint8Array(0);
79
+ const handlePacket2 = (packet) => {
80
+ log("handlePacket2", packet);
81
+ switch (packet.type) {
82
+ case PacketType.CONNACK:
83
+ if (packet.returnCode === 0) {
84
+ connected = true;
85
+ startPingInterval();
86
+ events.emit("connect", packet);
87
+ log("connected");
88
+ }
89
+ else {
90
+ const message = getConnackErrorMessage(packet.returnCode);
91
+ console.error(message);
92
+ events.emit("error", new Error(message));
93
+ close();
94
+ }
95
+ break;
96
+ case PacketType.PUBLISH:
97
+ log("received a publish packet");
98
+ if (packet.qos === 1 && packet.messageId !== undefined) {
99
+ send({ type: PacketType.PUBACK, messageId: packet.messageId });
100
+ }
101
+ events.emit("message", packet.topic, packet.payload, packet);
102
+ break;
103
+ case PacketType.PINGRESP:
104
+ log("PINGRESP");
105
+ break;
106
+ default:
107
+ log("received a default packet");
108
+ events.emit("packet", packet);
109
+ break;
110
+ }
111
+ };
112
+ ws.onmessage = (event) => {
113
+ log("onmessage");
114
+ const data = new Uint8Array(event.data);
115
+ const combined = new Uint8Array(receiveBuffer.length + data.length);
116
+ combined.set(receiveBuffer);
117
+ combined.set(data, receiveBuffer.length);
118
+ receiveBuffer = combined;
119
+ const { packets, remaining } = decodeAll(receiveBuffer);
120
+ receiveBuffer = remaining;
121
+ log("packets to process", packets.length);
122
+ for (const packet of packets) {
123
+ log("packet", packet);
124
+ handlePacket2(packet);
125
+ }
126
+ };
127
+ ws.onerror = (event) => {
128
+ events.emit("error", event);
129
+ };
130
+ ws.onclose = () => {
131
+ log("ws:onclose");
132
+ close();
133
+ };
134
+ };
135
+ return {
136
+ close,
137
+ open,
138
+ send,
139
+ nextMessageId,
140
+ isConnected: () => connected,
141
+ ...events,
142
+ };
143
+ };
144
+ const getConnackErrorMessage = (returnCode) => {
145
+ const errorMessages = {
146
+ 1: "Connection refused: unacceptable protocol version",
147
+ 2: "Connection refused: identifier rejected",
148
+ 3: "Connection refused: server unavailable",
149
+ 4: "Connection refused: bad username or password",
150
+ 5: "Connection refused: not authorized",
151
+ };
152
+ return errorMessages[returnCode] || `Connection refused: ${returnCode}`;
153
+ };
package/dist/index.d.ts CHANGED
@@ -1,20 +1,4 @@
1
- import { type MqttClient } from "./client";
2
- export { createMqttClient, type MqttClient } from "./client";
3
- export type { ConnectionOptions, MqttPacket, QoSLevel } from "./packets";
4
- export { QoS } from "./packets";
5
- export interface ConnectOptions {
6
- clientId?: string;
7
- username?: string;
8
- password?: string;
9
- keepalive?: number;
10
- clean?: boolean;
11
- will?: {
12
- topic: string;
13
- payload: string | Uint8Array;
14
- qos?: 0 | 1 | 2;
15
- retain?: boolean;
16
- };
17
- }
18
- export declare function connect(url: string, options?: ConnectOptions): MqttClient;
19
- export declare function connectAsync(url: string, options?: ConnectOptions): Promise<MqttClient>;
1
+ export * from "./base.js";
2
+ export type { MqttPacket, QoSLevel } from "./packets/index.js";
3
+ export { QoS } from "./packets/index.js";
20
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAE7D,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAC7D,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACzE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEhC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;QAC7B,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChB,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;CACH;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,UAAU,CAM7E;AAED,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC,CAkBrB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -1,24 +1,2 @@
1
- import { createMqttClient } from "./client";
2
- export { createMqttClient } from "./client";
3
- export { QoS } from "./packets";
4
- export function connect(url, options = {}) {
5
- const client = createMqttClient({ url, ...options });
6
- client.connect();
7
- return client;
8
- }
9
- export function connectAsync(url, options = {}) {
10
- return new Promise((resolve, reject) => {
11
- const client = createMqttClient({ url, ...options });
12
- const onConnect = () => {
13
- client.off("error", onError);
14
- resolve(client);
15
- };
16
- const onError = (err) => {
17
- client.off("connect", onConnect);
18
- reject(err);
19
- };
20
- client.on("connect", onConnect);
21
- client.on("error", onError);
22
- client.connect();
23
- });
24
- }
1
+ export * from "./base.js";
2
+ export { QoS } from "./packets/index.js";
@@ -1,8 +1,9 @@
1
- import type { ConnectPacket, PubackPacket, PublishPacket, SubscribePacket } from "./types";
1
+ import type { ConnectPacket, OutgoingPacket, PubackPacket, PublishPacket, SubscribePacket } from "./types";
2
2
  export declare function encodeConnect(packet: ConnectPacket): Uint8Array;
3
3
  export declare function encodeSubscribe(packet: SubscribePacket): Uint8Array;
4
4
  export declare function encodePublish(packet: PublishPacket): Uint8Array;
5
5
  export declare function encodePuback(packet: PubackPacket): Uint8Array;
6
6
  export declare function encodePingreq(): Uint8Array;
7
7
  export declare function encodeDisconnect(): Uint8Array;
8
+ export declare const encodePacket: (packet: OutgoingPacket) => Uint8Array;
8
9
  //# sourceMappingURL=encode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../src/packets/encode.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,eAAe,EAChB,MAAM,SAAS,CAAC;AAoBjB,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,UAAU,CAqC/D;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,UAAU,CASnE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,UAAU,CAkB/D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,UAAU,CAI7D;AAED,wBAAgB,aAAa,IAAI,UAAU,CAE1C;AAED,wBAAgB,gBAAgB,IAAI,UAAU,CAE7C"}
1
+ {"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../src/packets/encode.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,eAAe,EAChB,MAAM,SAAS,CAAC;AAoBjB,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,UAAU,CAqC/D;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,UAAU,CASnE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,UAAU,CAkB/D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,UAAU,CAI7D;AAED,wBAAgB,aAAa,IAAI,UAAU,CAE1C;AAED,wBAAgB,gBAAgB,IAAI,UAAU,CAE7C;AAED,eAAO,MAAM,YAAY,GAAI,QAAQ,cAAc,KAAG,UAiBrD,CAAC"}
@@ -77,3 +77,21 @@ export function encodePingreq() {
77
77
  export function encodeDisconnect() {
78
78
  return new Uint8Array([PacketType.DISCONNECT << 4, 0]);
79
79
  }
80
+ export const encodePacket = (packet) => {
81
+ switch (packet.type) {
82
+ case PacketType.CONNECT:
83
+ return encodeConnect(packet);
84
+ case PacketType.SUBSCRIBE:
85
+ return encodeSubscribe(packet);
86
+ case PacketType.PUBLISH:
87
+ return encodePublish(packet);
88
+ case PacketType.PUBACK:
89
+ return encodePuback(packet);
90
+ // case PacketType.DISCONNECT:
91
+ // return encodeDisconnect();
92
+ // case PacketType.PINGREQ:
93
+ // return encodePingreq();
94
+ default:
95
+ throw new Error(`Unknown packet type: ${packet["type"]}`);
96
+ }
97
+ };
@@ -1,17 +1,4 @@
1
1
  import type { QoSLevel } from "./constants";
2
- export type ConnectionOptions = {
3
- clientId?: string;
4
- username?: string;
5
- password?: string;
6
- keepalive?: number;
7
- clean?: boolean;
8
- will?: {
9
- topic: string;
10
- payload: string | Uint8Array;
11
- qos?: QoSLevel;
12
- retain?: boolean;
13
- };
14
- };
15
2
  export type BasePacket = {
16
3
  type: number;
17
4
  };
@@ -71,4 +58,5 @@ export type DisconnectPacket = BasePacket & {
71
58
  };
72
59
  export type MqttPacket = ConnectPacket | ConnackPacket | PublishPacket | PubackPacket | SubscribePacket | SubackPacket | PingreqPacket | PingrespPacket | DisconnectPacket;
73
60
  export type IncomingPacket = ConnackPacket | PublishPacket | PubackPacket | SubackPacket | PingrespPacket;
61
+ export type OutgoingPacket = ConnectPacket | SubscribePacket | PublishPacket | PubackPacket | DisconnectPacket | PingreqPacket;
74
62
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/packets/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;QAC7B,GAAG,CAAC,EAAE,QAAQ,CAAC;QACf,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAGF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG;IACvC,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,UAAU,CAAC;QACpB,GAAG,EAAE,QAAQ,CAAC;QACd,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;CACH,CAAC;AAGF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG;IACvC,IAAI,EAAE,CAAC,CAAC;IACR,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAGF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG;IACvC,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,GAAG,EAAE,QAAQ,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG;IACtC,IAAI,EAAE,CAAC,CAAC;IACR,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAGF,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG;IACzC,IAAI,EAAE,CAAC,CAAC;IACR,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,KAAK,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,QAAQ,CAAC;KACf,CAAC,CAAC;CACJ,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG;IACtC,IAAI,EAAE,CAAC,CAAC;IACR,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAGF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG;IACvC,IAAI,EAAE,EAAE,CAAC;CACV,CAAC;AAGF,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG;IACxC,IAAI,EAAE,EAAE,CAAC;CACV,CAAC;AAGF,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAC1C,IAAI,EAAE,EAAE,CAAC;CACV,CAAC;AAEF,MAAM,MAAM,UAAU,GAClB,aAAa,GACb,aAAa,GACb,aAAa,GACb,YAAY,GACZ,eAAe,GACf,YAAY,GACZ,aAAa,GACb,cAAc,GACd,gBAAgB,CAAC;AAErB,MAAM,MAAM,cAAc,GACtB,aAAa,GACb,aAAa,GACb,YAAY,GACZ,YAAY,GACZ,cAAc,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/packets/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAGF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG;IACvC,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,UAAU,CAAC;QACpB,GAAG,EAAE,QAAQ,CAAC;QACd,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;CACH,CAAC;AAGF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG;IACvC,IAAI,EAAE,CAAC,CAAC;IACR,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAGF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG;IACvC,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,GAAG,EAAE,QAAQ,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG;IACtC,IAAI,EAAE,CAAC,CAAC;IACR,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAGF,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG;IACzC,IAAI,EAAE,CAAC,CAAC;IACR,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,KAAK,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,QAAQ,CAAC;KACf,CAAC,CAAC;CACJ,CAAC;AAGF,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG;IACtC,IAAI,EAAE,CAAC,CAAC;IACR,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAGF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG;IACvC,IAAI,EAAE,EAAE,CAAC;CACV,CAAC;AAGF,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG;IACxC,IAAI,EAAE,EAAE,CAAC;CACV,CAAC;AAGF,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAC1C,IAAI,EAAE,EAAE,CAAC;CACV,CAAC;AAEF,MAAM,MAAM,UAAU,GAClB,aAAa,GACb,aAAa,GACb,aAAa,GACb,YAAY,GACZ,eAAe,GACf,YAAY,GACZ,aAAa,GACb,cAAc,GACd,gBAAgB,CAAC;AAErB,MAAM,MAAM,cAAc,GACtB,aAAa,GACb,aAAa,GACb,YAAY,GACZ,YAAY,GACZ,cAAc,CAAC;AAEnB,MAAM,MAAM,cAAc,GACtB,aAAa,GACb,eAAe,GACf,aAAa,GACb,YAAY,GACZ,gBAAgB,GAChB,aAAa,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/utils/events.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AAEjD,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,CACjE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KACV,IAAI,CAAC;AAEV,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,QAAQ,IAAI;IAC7C,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACrE,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACtE,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CACxD,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,QAAQ,OAAK,YAAY,CAAC,CAAC,CAsBvE,CAAC"}
@@ -0,0 +1,6 @@
1
+ export type LogOptions = {
2
+ debug?: boolean;
3
+ };
4
+ export type Logger = (...args: unknown[]) => void;
5
+ export declare const createLogger: (options?: LogOptions) => Logger;
6
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;AAElD,eAAO,MAAM,YAAY,GACtB,UAAU,UAAU,KAAG,MAKvB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export const createLogger = (options) => (...args) => {
2
+ if (options?.debug) {
3
+ console.log(...args);
4
+ }
5
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "websocket-mqtt",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "A lightweight MQTT WebSocket client library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/dist/client.d.ts DELETED
@@ -1,29 +0,0 @@
1
- import { EventEmitter } from "./events";
2
- import { type ConnackPacket, type ConnectionOptions, type PublishPacket, type QoSLevel } from "./packets";
3
- export type MqttClientOptions = ConnectionOptions & {
4
- url: string;
5
- };
6
- export type MqttClientEvents = {
7
- connect: [packet: ConnackPacket];
8
- message: [topic: string, payload: Uint8Array, packet: PublishPacket];
9
- error: [error: unknown];
10
- close: [];
11
- };
12
- export type MqttClient = EventEmitter<MqttClientEvents>;
13
- export declare function createMqttClient(options: MqttClientOptions): {
14
- connect: () => void;
15
- subscribe: (topic: string, qos?: QoSLevel) => void;
16
- subscribeAsync: (topic: string, qos?: QoSLevel) => Promise<number[]>;
17
- publish: (topic: string, payload: string | Uint8Array, options?: {
18
- qos?: QoSLevel;
19
- retain?: boolean;
20
- }) => void;
21
- publishAsync: (topic: string, payload: string | Uint8Array, options?: {
22
- qos?: QoSLevel;
23
- retain?: boolean;
24
- }) => Promise<void>;
25
- end: () => void;
26
- endAsync: () => Promise<void>;
27
- readonly isConnected: boolean;
28
- } & EventEmitter<MqttClientEvents>;
29
- //# sourceMappingURL=client.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,YAAY,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAWtB,KAAK,aAAa,EAElB,KAAK,QAAQ,EAEd,MAAM,WAAW,CAAC;AAGnB,MAAM,MAAM,iBAAiB,GAAG,iBAAiB,GAAG;IAClD,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACjC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IACrE,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACxB,KAAK,EAAE,EAAE,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;AAExD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB;mBA2LrC,IAAI;uBAsBE,MAAM,QAAO,QAAQ,KAAsB,IAAI;4BAOhE,MAAM,QACR,QAAQ,KACZ,OAAO,CAAC,MAAM,EAAE,CAAC;qBAiBX,MAAM,WACJ,MAAM,GAAG,UAAU,YAClB;QAAE,GAAG,CAAC,EAAE,QAAQ,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAC7C,IAAI;0BAOE,MAAM,WACJ,MAAM,GAAG,UAAU,YAClB;QAAE,GAAG,CAAC,EAAE,QAAQ,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAC7C,OAAO,CAAC,IAAI,CAAC;eA0BA,IAAI;oBASC,OAAO,CAAC,IAAI,CAAC;;mCAgCnC"}
package/dist/client.js DELETED
@@ -1,246 +0,0 @@
1
- import { createEventEmitter } from "./events";
2
- import { decodeAll, DEFAULT_KEEPALIVE_SECONDS, encodeConnect, encodeDisconnect, encodePingreq, encodePuback, encodePublish, encodeSubscribe, PacketType, QoS, } from "./packets";
3
- import { toUint8Array } from "./utils/buffer";
4
- export function createMqttClient(options) {
5
- let ws = null;
6
- let connected = false;
7
- let messageIdCounter = 1;
8
- let receiveBuffer = new Uint8Array(0);
9
- let pingInterval = null;
10
- const events = createEventEmitter();
11
- const pendingSubscribes = new Map();
12
- const pendingPublishes = new Map();
13
- const mergedOptions = {
14
- keepalive: DEFAULT_KEEPALIVE_SECONDS,
15
- clean: true,
16
- clientId: options.clientId || `mqtt_${Math.random().toString(36).slice(2, 10)}`,
17
- ...options,
18
- };
19
- function send(data) {
20
- if (ws && ws.readyState === WebSocket.OPEN) {
21
- ws.send(data);
22
- }
23
- }
24
- function cleanup() {
25
- connected = false;
26
- if (pingInterval) {
27
- clearInterval(pingInterval);
28
- pingInterval = null;
29
- }
30
- const error = new Error("Connection closed");
31
- pendingSubscribes.forEach((pending) => pending.reject(error));
32
- pendingSubscribes.clear();
33
- pendingPublishes.forEach((pending) => pending.reject(error));
34
- pendingPublishes.clear();
35
- }
36
- function nextMessageId() {
37
- const messageId = messageIdCounter;
38
- messageIdCounter = (messageIdCounter % 65535) + 1;
39
- return messageId;
40
- }
41
- function startPingInterval() {
42
- if (mergedOptions.keepalive && mergedOptions.keepalive > 0) {
43
- const pingIntervalMs = (mergedOptions.keepalive * 1000) / 2;
44
- pingInterval = setInterval(() => {
45
- if (connected) {
46
- send(encodePingreq());
47
- }
48
- }, pingIntervalMs);
49
- }
50
- }
51
- function getConnackErrorMessage(returnCode) {
52
- const errorMessages = {
53
- 1: "Connection refused: unacceptable protocol version",
54
- 2: "Connection refused: identifier rejected",
55
- 3: "Connection refused: server unavailable",
56
- 4: "Connection refused: bad username or password",
57
- 5: "Connection refused: not authorized",
58
- };
59
- return errorMessages[returnCode] || `Connection refused: ${returnCode}`;
60
- }
61
- function handleConnack(packet) {
62
- if (packet.returnCode === 0) {
63
- connected = true;
64
- startPingInterval();
65
- events.emit("connect", packet);
66
- }
67
- else {
68
- const message = getConnackErrorMessage(packet.returnCode);
69
- events.emit("error", new Error(message));
70
- end();
71
- }
72
- }
73
- function handleSuback(packet) {
74
- const pending = pendingSubscribes.get(packet.messageId);
75
- if (pending) {
76
- pendingSubscribes.delete(packet.messageId);
77
- const hasFailure = packet.granted.some((qos) => qos === 0x80);
78
- if (hasFailure) {
79
- pending.reject(new Error("Subscription failed"));
80
- }
81
- else {
82
- pending.resolve(packet.granted);
83
- }
84
- }
85
- }
86
- function handlePublish(packet) {
87
- if (packet.qos === 1 && packet.messageId !== undefined) {
88
- send(encodePuback({
89
- type: PacketType.PUBACK,
90
- messageId: packet.messageId,
91
- }));
92
- }
93
- events.emit("message", packet.topic, packet.payload, packet);
94
- }
95
- function handlePuback(packet) {
96
- const pending = pendingPublishes.get(packet.messageId);
97
- if (pending) {
98
- pendingPublishes.delete(packet.messageId);
99
- pending.resolve();
100
- }
101
- }
102
- function handlePacket(packet) {
103
- switch (packet.type) {
104
- case PacketType.CONNACK:
105
- handleConnack(packet);
106
- break;
107
- case PacketType.SUBACK:
108
- handleSuback(packet);
109
- break;
110
- case PacketType.PUBLISH:
111
- handlePublish(packet);
112
- break;
113
- case PacketType.PUBACK:
114
- handlePuback(packet);
115
- break;
116
- case PacketType.PINGRESP:
117
- break;
118
- }
119
- }
120
- function handleData(data) {
121
- const combined = new Uint8Array(receiveBuffer.length + data.length);
122
- combined.set(receiveBuffer);
123
- combined.set(data, receiveBuffer.length);
124
- receiveBuffer = combined;
125
- const { packets, remaining } = decodeAll(receiveBuffer);
126
- receiveBuffer = remaining;
127
- for (const packet of packets) {
128
- handlePacket(packet);
129
- }
130
- }
131
- function sendConnect() {
132
- const packet = encodeConnect({
133
- type: PacketType.CONNECT,
134
- clientId: mergedOptions.clientId,
135
- username: mergedOptions.username,
136
- password: mergedOptions.password,
137
- keepalive: mergedOptions.keepalive,
138
- clean: mergedOptions.clean,
139
- will: mergedOptions.will
140
- ? {
141
- topic: mergedOptions.will.topic,
142
- payload: toUint8Array(mergedOptions.will.payload),
143
- qos: mergedOptions.will.qos ?? QoS.AT_MOST_ONCE,
144
- retain: mergedOptions.will.retain ?? false,
145
- }
146
- : undefined,
147
- });
148
- send(packet);
149
- }
150
- // Public API functions
151
- function connect() {
152
- ws = new WebSocket(mergedOptions.url, ["mqtt"]);
153
- ws.binaryType = "arraybuffer";
154
- ws.onopen = () => {
155
- sendConnect();
156
- };
157
- ws.onmessage = (event) => {
158
- handleData(new Uint8Array(event.data));
159
- };
160
- ws.onerror = (event) => {
161
- events.emit("error", event);
162
- };
163
- ws.onclose = () => {
164
- cleanup();
165
- events.emit("close");
166
- };
167
- }
168
- function subscribe(topic, qos = QoS.AT_MOST_ONCE) {
169
- subscribeAsync(topic, qos).catch((err) => {
170
- events.emit("error", err);
171
- });
172
- }
173
- function subscribeAsync(topic, qos = QoS.AT_MOST_ONCE) {
174
- return new Promise((resolve, reject) => {
175
- const messageId = nextMessageId();
176
- pendingSubscribes.set(messageId, { resolve, reject });
177
- const packet = encodeSubscribe({
178
- type: PacketType.SUBSCRIBE,
179
- messageId,
180
- subscriptions: [{ topic, qos }],
181
- });
182
- send(packet);
183
- });
184
- }
185
- function publish(topic, payload, options) {
186
- publishAsync(topic, payload, options).catch((err) => {
187
- events.emit("error", err);
188
- });
189
- }
190
- function publishAsync(topic, payload, options) {
191
- const qos = options?.qos ?? QoS.AT_MOST_ONCE;
192
- const retain = options?.retain ?? false;
193
- const messageId = qos > 0 ? nextMessageId() : undefined;
194
- const packet = encodePublish({
195
- type: PacketType.PUBLISH,
196
- topic,
197
- payload: toUint8Array(payload),
198
- qos,
199
- retain,
200
- dup: false,
201
- messageId,
202
- });
203
- send(packet);
204
- if (qos === 0) {
205
- return Promise.resolve();
206
- }
207
- return new Promise((resolve, reject) => {
208
- pendingPublishes.set(messageId, { resolve, reject });
209
- });
210
- }
211
- function end() {
212
- if (connected && ws) {
213
- send(encodeDisconnect());
214
- }
215
- ws?.close();
216
- cleanup();
217
- }
218
- function endAsync() {
219
- return new Promise((resolve) => {
220
- const onClose = () => {
221
- events.off("close", onClose);
222
- resolve();
223
- };
224
- events.on("close", onClose);
225
- end();
226
- if (!ws || ws.readyState === WebSocket.CLOSED) {
227
- events.off("close", onClose);
228
- resolve();
229
- }
230
- });
231
- }
232
- // Return public API
233
- const client = {
234
- connect,
235
- subscribe,
236
- subscribeAsync,
237
- publish,
238
- publishAsync,
239
- end,
240
- endAsync,
241
- get isConnected() {
242
- return connected;
243
- },
244
- };
245
- return Object.assign(client, events);
246
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AAEjD,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,CACjE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KACV,IAAI,CAAC;AAEV,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,QAAQ,IAAI;IAC7C,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACrE,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACtE,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CACxD,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,QAAQ,OAAK,YAAY,CAAC,CAAC,CAsBvE,CAAC"}
File without changes
File without changes