@jiangtaste/baiwei-sdk 1.0.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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +360 -0
  3. package/dist/core/BaiweiClient.d.ts +38 -0
  4. package/dist/core/BaiweiClient.js +94 -0
  5. package/dist/core/BaiweiSession.d.ts +56 -0
  6. package/dist/core/BaiweiSession.js +229 -0
  7. package/dist/core/Client.d.ts +74 -0
  8. package/dist/core/Client.js +127 -0
  9. package/dist/core/MessageAssembler.d.ts +17 -0
  10. package/dist/core/MessageAssembler.js +76 -0
  11. package/dist/core/PendingRequestStore.d.ts +18 -0
  12. package/dist/core/PendingRequestStore.js +33 -0
  13. package/dist/core/Session.d.ts +43 -0
  14. package/dist/core/Session.js +330 -0
  15. package/dist/core/SessionConnector.d.ts +14 -0
  16. package/dist/core/SessionConnector.js +62 -0
  17. package/dist/index.d.ts +11 -0
  18. package/dist/index.js +27 -0
  19. package/dist/services/BaseService.d.ts +11 -0
  20. package/dist/services/BaseService.js +17 -0
  21. package/dist/services/ControlService.d.ts +33 -0
  22. package/dist/services/ControlService.js +67 -0
  23. package/dist/services/DeviceService.d.ts +26 -0
  24. package/dist/services/DeviceService.js +51 -0
  25. package/dist/services/GatewayService.d.ts +11 -0
  26. package/dist/services/GatewayService.js +33 -0
  27. package/dist/services/RoomService.d.ts +11 -0
  28. package/dist/services/RoomService.js +27 -0
  29. package/dist/services/SceneService.d.ts +12 -0
  30. package/dist/services/SceneService.js +39 -0
  31. package/dist/services/UserService.d.ts +21 -0
  32. package/dist/services/UserService.js +70 -0
  33. package/dist/transport/TcpClient.d.ts +38 -0
  34. package/dist/transport/TcpClient.js +315 -0
  35. package/dist/types/device-catalog.d.ts +42 -0
  36. package/dist/types/device-catalog.js +16 -0
  37. package/dist/types/device-state.d.ts +56 -0
  38. package/dist/types/device-state.js +2 -0
  39. package/dist/types/index.d.ts +8 -0
  40. package/dist/types/index.js +24 -0
  41. package/dist/types/messages.d.ts +53 -0
  42. package/dist/types/messages.js +32 -0
  43. package/dist/types/options.d.ts +27 -0
  44. package/dist/types/options.js +7 -0
  45. package/dist/types/room.d.ts +9 -0
  46. package/dist/types/room.js +2 -0
  47. package/dist/types/scene.d.ts +22 -0
  48. package/dist/types/scene.js +2 -0
  49. package/dist/types/user.d.ts +17 -0
  50. package/dist/types/user.js +2 -0
  51. package/dist/types.d.ts +212 -0
  52. package/dist/types.js +50 -0
  53. package/dist/utils/IdGenerator.d.ts +19 -0
  54. package/dist/utils/IdGenerator.js +49 -0
  55. package/dist/utils/MessageIdGenerator.d.ts +4 -0
  56. package/dist/utils/MessageIdGenerator.js +12 -0
  57. package/dist/utils/logger.d.ts +33 -0
  58. package/dist/utils/logger.js +115 -0
  59. package/dist/utils/time.d.ts +2 -0
  60. package/dist/utils/time.js +14 -0
  61. package/package.json +45 -0
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BaiweiSession = void 0;
7
+ const events_1 = __importDefault(require("events"));
8
+ const MessageAssembler_1 = require("./MessageAssembler");
9
+ const PendingRequestStore_1 = require("./PendingRequestStore");
10
+ const SessionConnector_1 = require("./SessionConnector");
11
+ const TcpClient_1 = require("../transport/TcpClient");
12
+ const types_1 = require("../types");
13
+ const MessageIdGenerator_1 = require("../utils/MessageIdGenerator");
14
+ const logger_1 = require("../utils/logger");
15
+ /**
16
+ * 会话编排层。
17
+ * 负责连接、请求派发、响应匹配、分片合并和 report 转发。
18
+ */
19
+ class BaiweiSession extends events_1.default {
20
+ tcp;
21
+ options;
22
+ log;
23
+ listenersAttached = false;
24
+ token;
25
+ connecting;
26
+ onTcpMessage = (message) => this.onMessage(message);
27
+ onTcpConnected = () => this.emit("connected");
28
+ onTcpDisconnect = (reason) => this.handleDisconnect(reason);
29
+ onTcpError = (err) => {
30
+ if (this.connecting) {
31
+ return;
32
+ }
33
+ this.emit("error", err);
34
+ };
35
+ onTcpProtocolError = (err) => this.emit("error", err);
36
+ idGenerator = new MessageIdGenerator_1.MessageIdGenerator();
37
+ pending = new PendingRequestStore_1.PendingRequestStore();
38
+ assembler;
39
+ connector;
40
+ constructor(options) {
41
+ super();
42
+ this.log = (0, logger_1.createLogger)(options.logger);
43
+ this.options = options;
44
+ this.tcp = new TcpClient_1.TcpClient({ ...this.options });
45
+ this.assembler = new MessageAssembler_1.MessageAssembler(this.options.timeoutMs, (msgId, label) => this.emitError(`Partial payload expired: msgId=${msgId}, ${label}`));
46
+ this.connector = new SessionConnector_1.SessionConnector(this.tcp, this.log, (error) => {
47
+ this.emit("error", error);
48
+ });
49
+ this.bindTcpListeners();
50
+ }
51
+ /** 返回 TCP 层当前是否可用。 */
52
+ isConnected() {
53
+ return this.tcp.isConnected();
54
+ }
55
+ /** 幂等连接:重复调用时复用同一个连接过程。 */
56
+ connect() {
57
+ if (this.isConnected()) {
58
+ return Promise.resolve();
59
+ }
60
+ if (this.connecting) {
61
+ return this.connecting;
62
+ }
63
+ this.connecting = this.connector.connect().finally(() => {
64
+ this.connecting = undefined;
65
+ });
66
+ return this.connecting;
67
+ }
68
+ /** 登录成功后保存 token,后续请求会自动带上。 */
69
+ updateToken(token) {
70
+ this.token = token;
71
+ }
72
+ disconnect() {
73
+ if (!this.isConnected() && !this.connecting) {
74
+ this.clearRuntimeState(new Error("Session disconnected"));
75
+ return;
76
+ }
77
+ this.connecting = undefined;
78
+ this.tcp.disconnect();
79
+ }
80
+ /** 构造协议请求并注册 pending,等待后续 response 匹配回填。 */
81
+ request(req) {
82
+ const msgId = this.idGenerator.next();
83
+ const message = this.createMessage(req, msgId);
84
+ return new Promise(async (resolve, reject) => {
85
+ const timeoutTimer = setTimeout(() => {
86
+ this.pending.delete(msgId);
87
+ const error = new Error(`Request timeout: msgClass/msgName=${req.msgClass}/${req.msgName}, msgId=${msgId}`);
88
+ this.log.info("TCP request timeout:", error);
89
+ reject(error);
90
+ }, this.options.timeoutMs);
91
+ this.pending.set(msgId, {
92
+ resolve: (value) => resolve(value),
93
+ reject,
94
+ timeoutTimer,
95
+ });
96
+ try {
97
+ await this.tcp.send(message);
98
+ }
99
+ catch (error) {
100
+ this.pending.delete(msgId);
101
+ this.log.info("Send failed:", error);
102
+ reject(error);
103
+ }
104
+ });
105
+ }
106
+ /** 把 SDK 内部请求结构转换成网关协议报文。 */
107
+ createMessage(req, msgId) {
108
+ return {
109
+ api_version: "0.1",
110
+ appId: types_1.DEFAULT_APP_ID,
111
+ from: this.options.clientId,
112
+ to: this.options.gatewaySN,
113
+ msg_id: msgId,
114
+ msg_class: req.msgClass,
115
+ msg_name: req.msgName,
116
+ msg_type: req.msgType ?? types_1.MsgType.GET,
117
+ token: this.token,
118
+ ...req.payload,
119
+ };
120
+ }
121
+ handleDisconnect(reason) {
122
+ this.connecting = undefined;
123
+ this.clearRuntimeState(reason);
124
+ this.emit("disconnect", reason);
125
+ }
126
+ /** 只在构造时绑定一次底层事件,避免重复注册。 */
127
+ bindTcpListeners() {
128
+ if (this.listenersAttached) {
129
+ return;
130
+ }
131
+ this.tcp.on("connected", this.onTcpConnected);
132
+ this.tcp.on("message", this.onTcpMessage);
133
+ this.tcp.on("disconnected", this.onTcpDisconnect);
134
+ this.tcp.on("error", this.onTcpError);
135
+ this.tcp.on("protocol_error", this.onTcpProtocolError);
136
+ this.listenersAttached = true;
137
+ }
138
+ /** 断线时统一清理请求与分片状态,避免悬空定时器和脏数据。 */
139
+ clearRuntimeState(reason) {
140
+ this.pending.rejectAll(reason);
141
+ this.assembler.clearAll();
142
+ }
143
+ /** 统一处理来自 TCP 层的完整 JSON 消息。 */
144
+ onMessage(message) {
145
+ const { api_version, from, to, msg_id, msg_class, msg_name, msg_type, end = 1, status = 0, ...payload } = message;
146
+ this.log.info(`API_VERSION: ${api_version}, FROM: ${from}, TO: ${to}, MSG_TYPE: ${msg_type}, MSG_ID: ${msg_id}, MSG_CLASS: ${msg_class}, MSG_NAME: ${msg_name}, END: ${end}`);
147
+ if (!this.validateMessage(message, from, msg_id)) {
148
+ return;
149
+ }
150
+ if (status !== 0) {
151
+ this.handleGatewayError(msg_id, status, msg_class, msg_name);
152
+ return;
153
+ }
154
+ const finalPayload = this.handleFragmentation(msg_id, payload, end, msg_class, msg_name);
155
+ if (!finalPayload) {
156
+ return;
157
+ }
158
+ const res = {
159
+ msgId: msg_id,
160
+ msgClass: msg_class,
161
+ msgName: msg_name,
162
+ msgType: msg_type,
163
+ payload: finalPayload,
164
+ };
165
+ this.log.info("FULL MESSAGE =====>", JSON.stringify(res));
166
+ this.handleMessageResponse(res);
167
+ }
168
+ /** 只接受来自目标网关且带有合法 msg_id 的消息。 */
169
+ validateMessage(message, from, msgId) {
170
+ if (from !== this.options.gatewaySN) {
171
+ this.emitError(`Invalid gatewaySN, got ${from}, expected: ${this.options.gatewaySN}`);
172
+ return false;
173
+ }
174
+ if (!msgId) {
175
+ this.emitError(`Invalid msgId, got ${msgId}`);
176
+ return false;
177
+ }
178
+ return true;
179
+ }
180
+ /** 网关返回非 0 状态码时,优先回填给对应请求。 */
181
+ handleGatewayError(msgId, status, msgClass, msgName) {
182
+ const pending = this.pending.get(msgId);
183
+ if (pending) {
184
+ this.pending.delete(msgId);
185
+ pending.reject(new Error(`Gateway status=${status}, msgClass/msgName=${msgClass}/${msgName}, msgId=${msgId}`));
186
+ }
187
+ else {
188
+ this.emitError(`Gateway status=${status}, unmatched msgId=${msgId}, msgClass/msgName=${msgClass}/${msgName}`);
189
+ }
190
+ }
191
+ /** 分片逻辑委托给 assembler,session 只保留编排职责。 */
192
+ handleFragmentation(msgId, payload, end, msgClass, msgName) {
193
+ return this.assembler.append(msgId, payload, end, `msgClass/msgName=${msgClass}/${msgName}`);
194
+ }
195
+ /** 按消息类型分发到 response/report 两条处理链。 */
196
+ handleMessageResponse(res) {
197
+ switch (res.msgType) {
198
+ case types_1.MsgType.RESPONSE:
199
+ this.handleResponse(res);
200
+ break;
201
+ case types_1.MsgType.REPORT:
202
+ this.handleReport(res);
203
+ break;
204
+ default:
205
+ this.log.warn(`Unknown msg_type: ${res.msgType} for msgId: ${res.msgId}`);
206
+ return;
207
+ }
208
+ }
209
+ /** response 通过 msgId 命中 pending 请求并完成 Promise。 */
210
+ handleResponse(res) {
211
+ const { msgId: msg_id, payload } = res;
212
+ const pending = this.pending.get(msg_id);
213
+ if (!pending)
214
+ return;
215
+ this.pending.delete(msg_id);
216
+ this.log.info("RESPONSE =====>", JSON.stringify(payload));
217
+ pending.resolve(payload);
218
+ }
219
+ /** report 不做状态缓存,保持原样向上层抛出。 */
220
+ handleReport(res) {
221
+ this.log.info("REPORT =====>", JSON.stringify(res.payload));
222
+ this.emit("report", res);
223
+ }
224
+ /** 统一发出 Error 对象,避免到处重复 new Error。 */
225
+ emitError(message) {
226
+ this.emit("error", new Error(message));
227
+ }
228
+ }
229
+ exports.BaiweiSession = BaiweiSession;
@@ -0,0 +1,74 @@
1
+ import { EventEmitter } from "events";
2
+ import { ControlService } from "../services/ControlService";
3
+ import { DeviceService } from "../services/DeviceService";
4
+ import { GatewayService } from "../services/GatewayService";
5
+ import { RoomService } from "../services/RoomService";
6
+ import { SceneService } from "../services/SceneService";
7
+ import { UserService } from "../services/UserService";
8
+ import { BaiweiClientOptions, MessageRequest } from "../types";
9
+ export declare class BaiweiClient extends EventEmitter {
10
+ readonly options: BaiweiClientOptions;
11
+ private readonly session;
12
+ private log;
13
+ private readonly onSessionConnected;
14
+ private readonly onSessionDisconnect;
15
+ private readonly onSessionReport;
16
+ private readonly onSessionError;
17
+ readonly userService: UserService;
18
+ readonly deviceService: DeviceService;
19
+ readonly gatewayService: GatewayService;
20
+ readonly controlService: ControlService;
21
+ readonly sceneService: SceneService;
22
+ readonly roomService: RoomService;
23
+ constructor(options: BaiweiClientOptions);
24
+ /**
25
+ * Connect to the gateway.
26
+ *
27
+ * @throws {Error} If connection fails or times out
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * // Recommended: Always use try-catch or .catch()
32
+ * try {
33
+ * await client.connect();
34
+ * } catch (error) {
35
+ * console.error('Connection failed:', error);
36
+ * // Handle error gracefully
37
+ * }
38
+ *
39
+ * // Or use .catch()
40
+ * client.connect().catch((error) => {
41
+ * console.error('Connection failed:', error);
42
+ * });
43
+ * ```
44
+ */
45
+ /**
46
+ * Check if the client is currently connected.
47
+ */
48
+ isConnected(): boolean;
49
+ connect(): Promise<void>;
50
+ /**
51
+ * Safely connect to the gateway without throwing errors.
52
+ * Errors are emitted as 'error' events instead.
53
+ *
54
+ * @returns {Promise<boolean>} true if connected successfully, false otherwise
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // Use this if you don't want to handle exceptions
59
+ * const connected = await client.connectSafe();
60
+ * if (connected) {
61
+ * console.log('Connected successfully');
62
+ * } else {
63
+ * console.log('Connection failed, check error event');
64
+ * }
65
+ *
66
+ * client.on('error', (error) => {
67
+ * console.error('Connection error:', error);
68
+ * });
69
+ * ```
70
+ */
71
+ connectSafe(): Promise<boolean>;
72
+ disconnect(): void;
73
+ request<T>(message: MessageRequest): Promise<T>;
74
+ }
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaiweiClient = void 0;
4
+ const events_1 = require("events");
5
+ const ControlService_1 = require("../services/ControlService");
6
+ const DeviceService_1 = require("../services/DeviceService");
7
+ const GatewayService_1 = require("../services/GatewayService");
8
+ const RoomService_1 = require("../services/RoomService");
9
+ const SceneService_1 = require("../services/SceneService");
10
+ const UserService_1 = require("../services/UserService");
11
+ const logger_1 = require("../utils/logger");
12
+ const Session_1 = require("./Session");
13
+ class BaiweiClient extends events_1.EventEmitter {
14
+ options;
15
+ session;
16
+ log;
17
+ onSessionConnected = () => this.emit("connected");
18
+ onSessionDisconnect = (reason) => this.emit("disconnect", reason);
19
+ onSessionReport = (message) => this.emit("report", message);
20
+ onSessionError = (error) => {
21
+ if (this.listenerCount("error") > 0) {
22
+ this.emit("error", error);
23
+ }
24
+ };
25
+ userService;
26
+ deviceService;
27
+ gatewayService;
28
+ controlService;
29
+ sceneService;
30
+ roomService;
31
+ constructor(options) {
32
+ super();
33
+ this.options = options;
34
+ this.log = (0, logger_1.createLogger)(options.logger, { prefix: "Baiwei" });
35
+ const session = new Session_1.BaiweiSession({
36
+ ...options,
37
+ clientId: options.clientId ?? "baiwei-sdk",
38
+ timeoutMs: options.timeoutMs ?? 5_000,
39
+ logger: this.log,
40
+ });
41
+ this.userService = new UserService_1.UserService(session);
42
+ this.deviceService = new DeviceService_1.DeviceService(session);
43
+ this.gatewayService = new GatewayService_1.GatewayService(session);
44
+ this.controlService = new ControlService_1.ControlService(session);
45
+ this.sceneService = new SceneService_1.SceneService(session);
46
+ this.roomService = new RoomService_1.RoomService(session);
47
+ session.on("connected", this.onSessionConnected);
48
+ session.on("disconnect", this.onSessionDisconnect);
49
+ session.on("report", this.onSessionReport);
50
+ session.on("error", this.onSessionError);
51
+ this.session = session;
52
+ }
53
+ /**
54
+ * Connect to the gateway.
55
+ *
56
+ * @throws {Error} If connection fails or times out
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * // Recommended: Always use try-catch or .catch()
61
+ * try {
62
+ * await client.connect();
63
+ * } catch (error) {
64
+ * console.error('Connection failed:', error);
65
+ * // Handle error gracefully
66
+ * }
67
+ *
68
+ * // Or use .catch()
69
+ * client.connect().catch((error) => {
70
+ * console.error('Connection failed:', error);
71
+ * });
72
+ * ```
73
+ */
74
+ /**
75
+ * Check if the client is currently connected.
76
+ */
77
+ isConnected() {
78
+ return this.session.isConnected();
79
+ }
80
+ async connect() {
81
+ try {
82
+ await this.session.connect();
83
+ }
84
+ catch (error) {
85
+ const errorMessage = error instanceof Error ? error.message : String(error);
86
+ this.log.error("Failed to connect to gateway:", errorMessage);
87
+ throw error;
88
+ }
89
+ }
90
+ /**
91
+ * Safely connect to the gateway without throwing errors.
92
+ * Errors are emitted as 'error' events instead.
93
+ *
94
+ * @returns {Promise<boolean>} true if connected successfully, false otherwise
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * // Use this if you don't want to handle exceptions
99
+ * const connected = await client.connectSafe();
100
+ * if (connected) {
101
+ * console.log('Connected successfully');
102
+ * } else {
103
+ * console.log('Connection failed, check error event');
104
+ * }
105
+ *
106
+ * client.on('error', (error) => {
107
+ * console.error('Connection error:', error);
108
+ * });
109
+ * ```
110
+ */
111
+ async connectSafe() {
112
+ try {
113
+ await this.connect();
114
+ return true;
115
+ }
116
+ catch {
117
+ return false;
118
+ }
119
+ }
120
+ disconnect() {
121
+ this.session.disconnect();
122
+ }
123
+ request(message) {
124
+ return this.session.request(message);
125
+ }
126
+ }
127
+ exports.BaiweiClient = BaiweiClient;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 负责把同一 msgId 的多段 payload 合并为完整消息。
3
+ * 这里不关心协议语义,只处理“分片”和“过期”。
4
+ */
5
+ export declare class MessageAssembler {
6
+ private readonly timeoutMs;
7
+ private readonly onExpired;
8
+ private readonly partialPayloads;
9
+ constructor(timeoutMs: number, onExpired: (msgId: string, label: string) => void);
10
+ /** `end === 1` 表示最后一片,否则继续缓存并等待后续分片。 */
11
+ append(msgId: string, payload: Record<string, unknown>, end: number, label: string): Record<string, unknown> | null;
12
+ /** 断线或重置时统一清掉所有分片缓存。 */
13
+ clearAll(): void;
14
+ private clear;
15
+ /** 递归合并对象,数组则做追加,适配网关分片返回的常见结构。 */
16
+ private merge;
17
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MessageAssembler = void 0;
4
+ /**
5
+ * 负责把同一 msgId 的多段 payload 合并为完整消息。
6
+ * 这里不关心协议语义,只处理“分片”和“过期”。
7
+ */
8
+ class MessageAssembler {
9
+ timeoutMs;
10
+ onExpired;
11
+ partialPayloads = new Map();
12
+ constructor(timeoutMs, onExpired) {
13
+ this.timeoutMs = timeoutMs;
14
+ this.onExpired = onExpired;
15
+ }
16
+ /** `end === 1` 表示最后一片,否则继续缓存并等待后续分片。 */
17
+ append(msgId, payload, end, label) {
18
+ if (end === 1) {
19
+ const prev = this.partialPayloads.get(msgId);
20
+ if (!prev) {
21
+ return payload;
22
+ }
23
+ this.clear(msgId, prev);
24
+ return this.merge(prev.payload, payload);
25
+ }
26
+ const prev = this.partialPayloads.get(msgId);
27
+ if (prev) {
28
+ prev.payload = this.merge(prev.payload, payload);
29
+ return null;
30
+ }
31
+ const timer = setTimeout(() => {
32
+ const entry = this.partialPayloads.get(msgId);
33
+ if (!entry) {
34
+ return;
35
+ }
36
+ this.clear(msgId, entry);
37
+ this.onExpired(msgId, label);
38
+ }, this.timeoutMs);
39
+ this.partialPayloads.set(msgId, { payload, timer });
40
+ return null;
41
+ }
42
+ /** 断线或重置时统一清掉所有分片缓存。 */
43
+ clearAll() {
44
+ for (const [msgId, entry] of this.partialPayloads.entries()) {
45
+ this.clear(msgId, entry);
46
+ }
47
+ }
48
+ clear(msgId, entry) {
49
+ clearTimeout(entry.timer);
50
+ this.partialPayloads.delete(msgId);
51
+ }
52
+ /** 递归合并对象,数组则做追加,适配网关分片返回的常见结构。 */
53
+ merge(prev, curr) {
54
+ const result = { ...prev };
55
+ for (const key of Object.keys(curr)) {
56
+ const prevValue = result[key];
57
+ const currValue = curr[key];
58
+ if (prevValue &&
59
+ currValue &&
60
+ typeof prevValue === "object" &&
61
+ typeof currValue === "object" &&
62
+ !Array.isArray(prevValue) &&
63
+ !Array.isArray(currValue)) {
64
+ result[key] = this.merge(prevValue, currValue);
65
+ continue;
66
+ }
67
+ if (Array.isArray(prevValue) && Array.isArray(currValue)) {
68
+ result[key] = [...prevValue, ...currValue];
69
+ continue;
70
+ }
71
+ result[key] = currValue;
72
+ }
73
+ return result;
74
+ }
75
+ }
76
+ exports.MessageAssembler = MessageAssembler;
@@ -0,0 +1,18 @@
1
+ interface PendingRequest<T = unknown> {
2
+ resolve: (value: T) => void;
3
+ reject: (reason?: unknown) => void;
4
+ timeoutTimer: NodeJS.Timeout;
5
+ }
6
+ /**
7
+ * 维护“请求 msgId -> Promise 回调”的映射。
8
+ * session 只通过它做增删查和批量拒绝。
9
+ */
10
+ export declare class PendingRequestStore {
11
+ private readonly pending;
12
+ set<T>(msgId: string, entry: PendingRequest<T>): void;
13
+ get<T>(msgId: string): PendingRequest<T> | undefined;
14
+ delete(msgId: string): void;
15
+ /** 断线时批量拒绝所有未完成请求,避免上层永久等待。 */
16
+ rejectAll(reason: unknown): void;
17
+ }
18
+ export {};
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PendingRequestStore = void 0;
4
+ /**
5
+ * 维护“请求 msgId -> Promise 回调”的映射。
6
+ * session 只通过它做增删查和批量拒绝。
7
+ */
8
+ class PendingRequestStore {
9
+ pending = new Map();
10
+ set(msgId, entry) {
11
+ this.pending.set(msgId, entry);
12
+ }
13
+ get(msgId) {
14
+ return this.pending.get(msgId);
15
+ }
16
+ delete(msgId) {
17
+ const pending = this.pending.get(msgId);
18
+ if (!pending) {
19
+ return;
20
+ }
21
+ clearTimeout(pending.timeoutTimer);
22
+ this.pending.delete(msgId);
23
+ }
24
+ /** 断线时批量拒绝所有未完成请求,避免上层永久等待。 */
25
+ rejectAll(reason) {
26
+ for (const [msgId, pending] of this.pending.entries()) {
27
+ clearTimeout(pending.timeoutTimer);
28
+ pending.reject(new Error(`TCP disconnected. msgId=${msgId}. reason=${String(reason ?? "")}`));
29
+ }
30
+ this.pending.clear();
31
+ }
32
+ }
33
+ exports.PendingRequestStore = PendingRequestStore;
@@ -0,0 +1,43 @@
1
+ import EventEmitter from "events";
2
+ import { BaiweiSessionOptions, MessageRequest } from "../types";
3
+ export declare class BaiweiSession extends EventEmitter {
4
+ private tcp;
5
+ private options;
6
+ private log;
7
+ private listenersAttached;
8
+ private token?;
9
+ private connecting?;
10
+ private onTcpMessage;
11
+ private onTcpConnected;
12
+ private onTcpDisconnect;
13
+ private onTcpError;
14
+ private onTcpProtocolError;
15
+ private idGenerator;
16
+ private pending;
17
+ private partialPayloads;
18
+ constructor(options: BaiweiSessionOptions);
19
+ /**
20
+ * Check if the session is currently connected.
21
+ */
22
+ isConnected(): boolean;
23
+ /**
24
+ * Establish TCP connection.
25
+ * Returns existing promise if connection is already in progress.
26
+ * If already connected, returns a resolved promise.
27
+ */
28
+ connect(): Promise<void>;
29
+ updateToken(token?: string): void;
30
+ disconnect(): void;
31
+ request<T>(req: MessageRequest): Promise<T>;
32
+ private handleDisconnect;
33
+ private bindTcpListeners;
34
+ private clearRuntimeState;
35
+ private onMessage;
36
+ private validateMessage;
37
+ private handleGatewayError;
38
+ private handleFragmentation;
39
+ private mergePayload;
40
+ private handleMessageResponse;
41
+ private handleResponse;
42
+ private handleReport;
43
+ }