@ptner/mqtt-trans-client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # @ai-bus/mqtt-trans-client
2
+
3
+ 基于 mqtt.js 的 Node.js MQTT 客户端,支持 MQTT TCP、TLS、WebSocket 和安全 WebSocket URL。
4
+
5
+ ```ts
6
+ import { MqttTransClient } from "@ai-bus/mqtt-trans-client";
7
+
8
+ const client = new MqttTransClient({
9
+ url: "wss://device.example/mqtt",
10
+ clientId: "controller-1",
11
+ reconnectPeriod: 1_000,
12
+ onError(error) {
13
+ console.error(error);
14
+ },
15
+ });
16
+
17
+ await client.subscribe("devices/+/status", (topic, payload) => {
18
+ console.log(topic, payload.toString());
19
+ }, { qos: 1 });
20
+
21
+ await client.publish("devices/kitchen/command", "start", { qos: 1 });
22
+ await client.close();
23
+ ```
24
+
25
+ 连接选项、认证、遗嘱、QoS、重连与 TLS 参数沿用 mqtt.js 的 `IClientOptions`。一个过滤器可注册多个处理器;移除最后一个处理器时客户端会向 broker 取消该订阅。
@@ -0,0 +1,26 @@
1
+ import { type IClientOptions, type IClientPublishOptions, type IClientSubscribeOptions, type IPublishPacket, type ISubscriptionGrant, type MqttClient } from "mqtt";
2
+ export type MqttMessageHandler = (topic: string, payload: Buffer, packet: IPublishPacket) => void | Promise<void>;
3
+ export interface MqttTransClientOptions extends IClientOptions {
4
+ url: string;
5
+ onError?: (error: Error) => void;
6
+ onReconnect?: () => void;
7
+ onOffline?: () => void;
8
+ }
9
+ export declare class MqttTransClient {
10
+ private readonly options;
11
+ private readonly handlers;
12
+ private clientValue;
13
+ private connectPromise;
14
+ private closePromise;
15
+ constructor(options: MqttTransClientOptions);
16
+ get connected(): boolean;
17
+ get client(): MqttClient | undefined;
18
+ connect(): Promise<void>;
19
+ publish(topic: string, payload: string | Buffer | Uint8Array, options?: IClientPublishOptions): Promise<void>;
20
+ subscribe(filter: string, handler: MqttMessageHandler, options?: IClientSubscribeOptions): Promise<ISubscriptionGrant[]>;
21
+ unsubscribe(filter: string, handler?: MqttMessageHandler): Promise<void>;
22
+ close(force?: boolean): Promise<void>;
23
+ private connectInternal;
24
+ private closeInternal;
25
+ }
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,UAAU,EAChB,MAAM,MAAM,CAAC;AAEd,MAAM,MAAM,kBAAkB,GAAG,CAC/B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,cAAc,KACnB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,WAAW,sBAAuB,SAAQ,cAAc;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,qBAAa,eAAe;IAMd,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8C;IACvE,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,YAAY,CAA4B;gBAEnB,OAAO,EAAE,sBAAsB;IAE5D,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,MAAM,IAAI,UAAU,GAAG,SAAS,CAEnC;IAED,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMlB,OAAO,CACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,EACrC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC;IAQV,SAAS,CACb,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,kBAAkB,EAC3B,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAiB1B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAU9E,KAAK,CAAC,KAAK,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnC,OAAO,CAAC,eAAe;YAuCT,aAAa;CAM5B"}
package/dist/index.js ADDED
@@ -0,0 +1,126 @@
1
+ import { connect as createMqttClient, } from "mqtt";
2
+ export class MqttTransClient {
3
+ options;
4
+ handlers = new Map();
5
+ clientValue;
6
+ connectPromise;
7
+ closePromise;
8
+ constructor(options) {
9
+ this.options = options;
10
+ }
11
+ get connected() {
12
+ return this.clientValue?.connected ?? false;
13
+ }
14
+ get client() {
15
+ return this.clientValue;
16
+ }
17
+ connect() {
18
+ if (this.closePromise)
19
+ throw new Error("Cannot connect a closed MQTT client");
20
+ this.connectPromise ??= this.connectInternal();
21
+ return this.connectPromise;
22
+ }
23
+ async publish(topic, payload, options) {
24
+ await this.connect();
25
+ const mqttPayload = payload instanceof Uint8Array && !Buffer.isBuffer(payload)
26
+ ? Buffer.from(payload)
27
+ : payload;
28
+ await this.clientValue.publishAsync(topic, mqttPayload, options);
29
+ }
30
+ async subscribe(filter, handler, options) {
31
+ await this.connect();
32
+ const handlers = this.handlers.get(filter) ?? new Set();
33
+ const isFirstHandler = handlers.size === 0;
34
+ handlers.add(handler);
35
+ this.handlers.set(filter, handlers);
36
+ try {
37
+ return isFirstHandler
38
+ ? await this.clientValue.subscribeAsync(filter, options)
39
+ : [];
40
+ }
41
+ catch (error) {
42
+ handlers.delete(handler);
43
+ if (handlers.size === 0)
44
+ this.handlers.delete(filter);
45
+ throw error;
46
+ }
47
+ }
48
+ async unsubscribe(filter, handler) {
49
+ const handlers = this.handlers.get(filter);
50
+ if (!handlers)
51
+ return;
52
+ if (handler)
53
+ handlers.delete(handler);
54
+ else
55
+ handlers.clear();
56
+ if (handlers.size > 0)
57
+ return;
58
+ this.handlers.delete(filter);
59
+ if (this.clientValue?.connected)
60
+ await this.clientValue.unsubscribeAsync(filter);
61
+ }
62
+ close(force = false) {
63
+ this.closePromise ??= this.closeInternal(force);
64
+ return this.closePromise;
65
+ }
66
+ connectInternal() {
67
+ const { url, onError, onReconnect, onOffline, ...clientOptions } = this.options;
68
+ const client = createMqttClient(url, clientOptions);
69
+ this.clientValue = client;
70
+ client.on("message", (topic, payload, packet) => {
71
+ for (const [filter, handlers] of this.handlers) {
72
+ if (!topicMatches(filter, topic))
73
+ continue;
74
+ for (const handler of handlers) {
75
+ Promise.resolve(handler(topic, payload, packet)).catch((error) => {
76
+ onError?.(toError(error));
77
+ });
78
+ }
79
+ }
80
+ });
81
+ client.on("error", (error) => onError?.(error));
82
+ client.on("reconnect", () => onReconnect?.());
83
+ client.on("offline", () => onOffline?.());
84
+ if (client.connected)
85
+ return Promise.resolve();
86
+ return new Promise((resolve, reject) => {
87
+ const handleConnect = () => {
88
+ client.off("error", handleInitialError);
89
+ resolve();
90
+ };
91
+ const handleInitialError = (error) => {
92
+ client.off("connect", handleConnect);
93
+ reject(error);
94
+ };
95
+ client.once("connect", handleConnect);
96
+ client.once("error", handleInitialError);
97
+ });
98
+ }
99
+ async closeInternal(force) {
100
+ const client = this.clientValue;
101
+ this.handlers.clear();
102
+ if (client && !client.disconnected)
103
+ await client.endAsync(force);
104
+ this.clientValue = undefined;
105
+ }
106
+ }
107
+ function topicMatches(filter, topic) {
108
+ if (topic.startsWith("$") && !filter.startsWith("$"))
109
+ return false;
110
+ const filterLevels = filter.split("/");
111
+ const topicLevels = topic.split("/");
112
+ for (let index = 0; index < filterLevels.length; index += 1) {
113
+ const filterLevel = filterLevels[index];
114
+ if (filterLevel === "#")
115
+ return index === filterLevels.length - 1;
116
+ if (index >= topicLevels.length)
117
+ return false;
118
+ if (filterLevel !== "+" && filterLevel !== topicLevels[index])
119
+ return false;
120
+ }
121
+ return filterLevels.length === topicLevels.length;
122
+ }
123
+ function toError(value) {
124
+ return value instanceof Error ? value : new Error(String(value));
125
+ }
126
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@ptner/mqtt-trans-client",
3
+ "version": "0.1.0",
4
+ "description": "MQTT and MQTT-over-WebSocket client for Node.js",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json",
20
+ "test": "node --import tsx --test test/**/*.test.ts",
21
+ "check": "npm run build && npm test"
22
+ },
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
26
+ "dependencies": {
27
+ "mqtt": "^5.10.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.0.0",
31
+ "tsx": "^4.19.0",
32
+ "typescript": "^5.7.0"
33
+ },
34
+ "license": "MIT"
35
+ }