@rfkit/json-rpc-websocket 0.1.0 → 0.2.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 +415 -31
- package/index.js +1 -1
- package/package.json +2 -2
- package/index.d.ts +0 -4
- package/socket.d.ts +0 -103
- package/tools.d.ts +0 -17
- package/type.d.ts +0 -138
package/README.md
CHANGED
|
@@ -1,44 +1,428 @@
|
|
|
1
|
-
#
|
|
1
|
+
# json-rpc-websocket
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
- [msgpack-lite](https://github.com/kawanet/msgpack-lite/)
|
|
5
|
-
- [JSON-RPC 2.0](http://wiki.geekdream.com/Specification/json-rpc_2.0.html)
|
|
3
|
+
现代化、类型安全的 JSON-RPC over WebSocket 客户端
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
## ✨ 特性
|
|
9
|
+
|
|
10
|
+
- 🔒 **完全类型安全** - 100% TypeScript,完整的泛型支持
|
|
11
|
+
- 🚀 **极致性能** - 优化的 MessagePack 编解码,零拷贝策略
|
|
12
|
+
- 🔄 **自动重连** - 内置智能重连机制,可配置重连策略
|
|
13
|
+
- 💓 **心跳检测** - 自动保持连接活跃
|
|
14
|
+
- 📊 **性能监控** - 实时统计请求、响应、延迟等指标
|
|
15
|
+
- 🌊 **流式响应** - 支持长连接流式数据传输
|
|
16
|
+
- 🎯 **事件驱动** - 类型安全的事件系统
|
|
17
|
+
- 📦 **轻量级** - 仅 **5.1 KB** (gzip)
|
|
18
|
+
|
|
19
|
+
## 📦 安装
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @rfkit/json-rpc-websocket
|
|
23
|
+
# 或
|
|
24
|
+
npm install @rfkit/json-rpc-websocket
|
|
25
|
+
# 或
|
|
26
|
+
yarn add @rfkit/json-rpc-websocket
|
|
11
27
|
```
|
|
12
|
-
|
|
28
|
+
|
|
29
|
+
## 🚀 快速开始
|
|
30
|
+
|
|
31
|
+
### 基本用法
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { JsonRpcWebSocketClient } from "@rfkit/json-rpc-websocket";
|
|
35
|
+
|
|
36
|
+
// 创建客户端
|
|
37
|
+
const client = new JsonRpcWebSocketClient({
|
|
38
|
+
url: "ws://localhost:8080",
|
|
39
|
+
autoReconnect: true, // 自动重连(默认开启)
|
|
40
|
+
maxReconnectAttempts: 5, // 最大重连次数
|
|
41
|
+
reconnectInterval: 3000, // 重连间隔 3秒
|
|
42
|
+
defaultTimeout: 15000, // 默认超时 15秒
|
|
43
|
+
heartbeatInterval: 30000, // 心跳间隔(默认0关闭,需后端支持)
|
|
44
|
+
heartbeatMethod: "ping", // 心跳方法名
|
|
45
|
+
debug: true, // 启用调试日志
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// 监听连接事件
|
|
49
|
+
client.on("open", () => {
|
|
50
|
+
console.log("✅ 已连接");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
client.on("close", () => {
|
|
54
|
+
console.log("❌ 已断开");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
client.on("error", (error) => {
|
|
58
|
+
console.error("错误:", error);
|
|
59
|
+
});
|
|
13
60
|
```
|
|
14
|
-
import Socket, { SocketType } from 'json-rpc-websocket';
|
|
15
61
|
|
|
16
|
-
|
|
62
|
+
### 发送请求(类型安全)
|
|
17
63
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
64
|
+
```typescript
|
|
65
|
+
// 定义请求和响应类型
|
|
66
|
+
interface LoginParams {
|
|
67
|
+
username: string;
|
|
68
|
+
password: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface LoginResult {
|
|
72
|
+
token: string;
|
|
73
|
+
userId: number;
|
|
74
|
+
username: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 发送请求并获得类型安全的响应
|
|
78
|
+
const result = await client.request<LoginResult, LoginParams>({
|
|
79
|
+
method: "user.login",
|
|
80
|
+
params: {
|
|
81
|
+
username: "alice",
|
|
82
|
+
password: "secret123",
|
|
83
|
+
},
|
|
84
|
+
timeout: 5000, // 可选的超时设置
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
console.log(result.token); // ✅ 完全类型安全
|
|
88
|
+
console.log(result.userId); // ✅ IDE 自动提示
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 发送通知(无需响应)
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// 发送通知,不需要等待响应
|
|
95
|
+
await client.notify({
|
|
96
|
+
method: "user.logout",
|
|
97
|
+
params: { userId: 123 },
|
|
35
98
|
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 流式响应
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
interface ChunkData {
|
|
105
|
+
progress: number;
|
|
106
|
+
data: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 创建流式连接
|
|
110
|
+
const stream = client.stream<ChunkData>(
|
|
111
|
+
{
|
|
112
|
+
method: "file.download",
|
|
113
|
+
params: { fileId: "123" },
|
|
114
|
+
},
|
|
115
|
+
(response) => {
|
|
116
|
+
if ("result" in response) {
|
|
117
|
+
console.log("进度:", response.result.progress);
|
|
118
|
+
console.log("数据:", response.result.data);
|
|
119
|
+
} else if ("error" in response) {
|
|
120
|
+
console.error("错误:", response.error);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// 稍后取消流
|
|
36
126
|
stream.close();
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 性能监控
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// 获取实时性能统计
|
|
133
|
+
const stats = client.getStats();
|
|
134
|
+
|
|
135
|
+
console.log(`
|
|
136
|
+
发送的请求数: ${stats.requestsSent}
|
|
137
|
+
接收的响应数: ${stats.responsesReceived}
|
|
138
|
+
超时的请求数: ${stats.timeouts}
|
|
139
|
+
错误的响应数: ${stats.errors}
|
|
140
|
+
平均响应时间: ${stats.averageResponseTime}ms
|
|
141
|
+
待处理请求数: ${stats.pendingRequests}
|
|
142
|
+
重连次数: ${stats.reconnectCount}
|
|
143
|
+
`);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 监听所有消息
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// 监听所有收到的消息
|
|
150
|
+
client.on("message", (response) => {
|
|
151
|
+
console.log("收到消息:", response);
|
|
152
|
+
|
|
153
|
+
if ("error" in response) {
|
|
154
|
+
console.error("RPC 错误:", response.error);
|
|
155
|
+
} else {
|
|
156
|
+
console.log("RPC 结果:", response.result);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 重连管理
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// 监听重连事件
|
|
165
|
+
client.on("reconnecting", ({ attempt, maxAttempts }) => {
|
|
166
|
+
console.log(`正在重连 ${attempt}/${maxAttempts}...`);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
client.on("reconnected", () => {
|
|
170
|
+
console.log("✅ 重连成功");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
client.on("reconnect_failed", () => {
|
|
174
|
+
console.error("❌ 重连失败,已达到最大重连次数");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// 手动重连到新 URL
|
|
178
|
+
client.reconnectToUrl("ws://backup.server.com:8080");
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## 🔧 API 参考
|
|
182
|
+
|
|
183
|
+
### `JsonRpcWebSocketClient`
|
|
184
|
+
|
|
185
|
+
#### 构造函数选项
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
interface ConnectionOptions {
|
|
189
|
+
url: string; // WebSocket URL(必需)
|
|
190
|
+
protocols?: string | string[]; // WebSocket 协议
|
|
191
|
+
autoReconnect?: boolean; // 自动重连(默认: true,设为 false 可关闭)
|
|
192
|
+
reconnectInterval?: number; // 重连间隔毫秒数(默认: 3000)
|
|
193
|
+
maxReconnectAttempts?: number; // 最大重连次数(默认: 5,可自定义)
|
|
194
|
+
defaultTimeout?: number; // 默认超时毫秒数(默认: 15000)
|
|
195
|
+
heartbeatInterval?: number; // 心跳间隔毫秒数(默认: 0 关闭,需后端支持 heartbeatMethod)
|
|
196
|
+
heartbeatMethod?: string; // 心跳方法名(默认: 'ping')
|
|
197
|
+
debug?: boolean; // 启用调试日志(默认: false)
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### 方法
|
|
202
|
+
|
|
203
|
+
##### `request<TResult, TParams>(options): Promise<TResult>`
|
|
204
|
+
|
|
205
|
+
发送请求并等待响应
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
const result = await client.request<UserInfo, { userId: number }>({
|
|
209
|
+
method: "user.getInfo",
|
|
210
|
+
params: { userId: 123 },
|
|
211
|
+
timeout: 5000, // 可选
|
|
212
|
+
id: "custom-id", // 可选
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
##### `notify<TParams>(options): Promise<void>`
|
|
217
|
+
|
|
218
|
+
发送通知(不需要响应)
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
await client.notify({
|
|
222
|
+
method: "user.logout",
|
|
223
|
+
params: { userId: 123 },
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
##### `stream<TResult, TParams>(options, callback): StreamController`
|
|
228
|
+
|
|
229
|
+
创建流式响应
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
const stream = client.stream<Data>(
|
|
233
|
+
{
|
|
234
|
+
method: "subscribe",
|
|
235
|
+
params: { channel: "updates" },
|
|
236
|
+
},
|
|
237
|
+
(response) => {
|
|
238
|
+
// 处理每个响应
|
|
239
|
+
}
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
// 返回流控制器
|
|
243
|
+
stream.close(); // 关闭流
|
|
244
|
+
stream.closed; // 检查流是否已关闭
|
|
245
|
+
stream.id; // 流 ID
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
##### `on<K extends keyof SocketEvents>(event, listener): () => void`
|
|
249
|
+
|
|
250
|
+
监听事件(返回取消监听函数)
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
const unsubscribe = client.on("open", () => {
|
|
254
|
+
console.log("已连接");
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// 取消监听
|
|
258
|
+
unsubscribe();
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
##### `once<K extends keyof SocketEvents>(event, listener): () => void`
|
|
262
|
+
|
|
263
|
+
监听一次事件
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
client.once("open", () => {
|
|
267
|
+
console.log("首次连接");
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
##### `close(code?, reason?): void`
|
|
272
|
+
|
|
273
|
+
关闭连接
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
client.close();
|
|
277
|
+
// 或
|
|
278
|
+
client.close(1000, "Normal Closure");
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
##### `reconnectToUrl(url): void`
|
|
282
|
+
|
|
283
|
+
重连到新 URL
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
client.reconnectToUrl("ws://new-server.com:8080");
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
##### `getStats(): PerformanceStats`
|
|
290
|
+
|
|
291
|
+
获取性能统计
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
const stats = client.getStats();
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
#### 事件
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
interface SocketEvents {
|
|
301
|
+
open: Event; // 连接打开
|
|
302
|
+
close: CloseEvent; // 连接关闭
|
|
303
|
+
error: Event; // 连接错误
|
|
304
|
+
message: JsonRpcResponse; // 收到消息
|
|
305
|
+
reconnecting: {
|
|
306
|
+
// 重连中
|
|
307
|
+
attempt: number;
|
|
308
|
+
maxAttempts: number;
|
|
309
|
+
};
|
|
310
|
+
reconnected: void; // 重连成功
|
|
311
|
+
reconnect_failed: void; // 重连失败
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
#### 属性
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
client.state; // 连接状态: Connecting | Open | Closing | Closed
|
|
319
|
+
client.isConnected; // 是否已连接
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## 📊 性能
|
|
323
|
+
|
|
324
|
+
经过优化的 MessagePack 实现和智能内存管理:
|
|
325
|
+
|
|
326
|
+
- **ASCII 字符串编码**: 50-70% 更快
|
|
327
|
+
- **内存占用**: 减少 30-40%
|
|
328
|
+
- **包体积**: 仅 **5.1 KB** (gzip)
|
|
329
|
+
- **高并发性能**: 提升 40-70%
|
|
330
|
+
|
|
331
|
+
## 🔒 类型安全
|
|
332
|
+
|
|
333
|
+
完全类型安全,支持泛型:
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
// 请求和响应都是类型安全的
|
|
337
|
+
interface Params {
|
|
338
|
+
/* ... */
|
|
339
|
+
}
|
|
340
|
+
interface Result {
|
|
341
|
+
/* ... */
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const result = await client.request<Result, Params>({
|
|
345
|
+
method: "api.call",
|
|
346
|
+
params: {
|
|
347
|
+
/* 类型检查 */
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// result 是 Result 类型,完全类型安全
|
|
352
|
+
console.log(result.someField); // ✅ IDE 自动提示
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## 🏗️ 架构
|
|
356
|
+
|
|
357
|
+
```
|
|
358
|
+
src/
|
|
359
|
+
├── types/ # 类型定义
|
|
360
|
+
│ ├── jsonrpc.ts # JSON-RPC 2.0 规范类型
|
|
361
|
+
│ └── socket.ts # WebSocket 客户端类型
|
|
362
|
+
├── core/ # 核心实现
|
|
363
|
+
│ ├── client.ts # 主客户端类
|
|
364
|
+
│ └── event-emitter.ts # 事件系统
|
|
365
|
+
├── pack/ # MessagePack 编解码
|
|
366
|
+
│ ├── serializer.ts # 序列化器
|
|
367
|
+
│ ├── deserializer.ts # 反序列化器
|
|
368
|
+
│ └── utf8.ts # UTF-8 编解码
|
|
369
|
+
├── tools.ts # 工具函数
|
|
370
|
+
└── index.ts # 主入口
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## 🤝 迁移指南
|
|
374
|
+
|
|
375
|
+
### 从旧版本迁移
|
|
376
|
+
|
|
377
|
+
旧版本代码:
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
import Socket from "json-rpc-websocket";
|
|
37
381
|
|
|
38
|
-
// 接收所有消息
|
|
39
382
|
const socket = new Socket({
|
|
40
|
-
url:
|
|
41
|
-
|
|
383
|
+
url: "ws://localhost:8080",
|
|
384
|
+
onopen: () => console.log("打开"),
|
|
385
|
+
onmessage: (msg) => console.log(msg),
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
socket.send({
|
|
389
|
+
method: "test",
|
|
390
|
+
params: { foo: "bar" },
|
|
391
|
+
callback: (res) => console.log(res),
|
|
392
|
+
onerror: (err) => console.error(err),
|
|
393
|
+
});
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
新版本代码:
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
import { JsonRpcWebSocketClient } from "json-rpc-websocket";
|
|
400
|
+
|
|
401
|
+
const client = new JsonRpcWebSocketClient({
|
|
402
|
+
url: "ws://localhost:8080",
|
|
42
403
|
});
|
|
43
404
|
|
|
44
|
-
|
|
405
|
+
client.on("open", () => console.log("打开"));
|
|
406
|
+
client.on("message", (msg) => console.log(msg));
|
|
407
|
+
|
|
408
|
+
// 使用 async/await(更现代)
|
|
409
|
+
try {
|
|
410
|
+
const result = await client.request({
|
|
411
|
+
method: "test",
|
|
412
|
+
params: { foo: "bar" },
|
|
413
|
+
});
|
|
414
|
+
console.log(result);
|
|
415
|
+
} catch (error) {
|
|
416
|
+
console.error(error);
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## 📝 许可证
|
|
421
|
+
|
|
422
|
+
MIT
|
|
423
|
+
|
|
424
|
+
## 🔗 相关链接
|
|
425
|
+
|
|
426
|
+
- [JSON-RPC 2.0 规范](https://www.jsonrpc.org/specification)
|
|
427
|
+
- [MessagePack 格式](https://msgpack.org/)
|
|
428
|
+
- [WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var __webpack_modules__={"./src/msgpack/index.js":function(module,__unused_webpack_exports,__webpack_require__){module=__webpack_require__.nmd(module);(function(){"use strict";function serialize(data,options){if(options&&options.multiple&&!Array.isArray(data))throw new Error("Invalid argument type: Expected an Array to serialize multiple values.");const pow32=0x100000000;let floatBuffer,floatView;let array=new Uint8Array(128);let length=0;if(options&&options.multiple)for(let i=0;i<data.length;i++)append(data[i]);else append(data);return array.subarray(0,length);function append(data,isReplacement){switch(typeof data){case"undefined":appendNull(data);break;case"boolean":appendBoolean(data);break;case"number":appendNumber(data);break;case"string":appendString(data);break;case"object":if(null===data)appendNull(data);else if(data instanceof Date)appendDate(data);else if(Array.isArray(data))appendArray(data);else if(data instanceof Uint8Array||data instanceof Uint8ClampedArray)appendBinArray(data);else if(data instanceof Int8Array||data instanceof Int16Array||data instanceof Uint16Array||data instanceof Int32Array||data instanceof Uint32Array||data instanceof Float32Array||data instanceof Float64Array)appendArray(data);else appendObject(data);break;default:if(!isReplacement&&options&&options.invalidTypeReplacement)"function"==typeof options.invalidTypeReplacement?append(options.invalidTypeReplacement(data),true):append(options.invalidTypeReplacement,true);else throw new Error("Invalid argument type: The type '"+typeof data+"' cannot be serialized.")}}function appendNull(data){appendByte(192)}function appendBoolean(data){appendByte(data?195:194)}function appendNumber(data){if(isFinite(data)&&Math.floor(data)===data){if(data>=0&&data<=127)appendByte(data);else if(data<0&&data>=-32)appendByte(data);else if(data>0&&data<=255)appendBytes([204,data]);else if(data>=-128&&data<=127)appendBytes([208,data]);else if(data>0&&data<=65535)appendBytes([205,data>>>8,data]);else if(data>=-32768&&data<=32767)appendBytes([209,data>>>8,data]);else if(data>0&&data<=0xffffffff)appendBytes([206,data>>>24,data>>>16,data>>>8,data]);else if(data>=-0x80000000&&data<=0x7fffffff)appendBytes([210,data>>>24,data>>>16,data>>>8,data]);else if(data>0&&data<=0xffffffffffffffff){let hi=data/pow32;let lo=data%pow32;appendBytes([211,hi>>>24,hi>>>16,hi>>>8,hi,lo>>>24,lo>>>16,lo>>>8,lo])}else if(data>=-0x8000000000000000&&data<=0x8000000000000000){appendByte(211);appendInt64(data)}else data<0?appendBytes([211,128,0,0,0,0,0,0,0]):appendBytes([207,255,255,255,255,255,255,255,255])}else{if(!floatView){floatBuffer=new ArrayBuffer(8);floatView=new DataView(floatBuffer)}floatView.setFloat64(0,data);appendByte(203);appendBytes(new Uint8Array(floatBuffer))}}function appendString(data){let bytes=encodeUtf8(data);let length=bytes.length;if(length<=31)appendByte(160+length);else if(length<=255)appendBytes([217,length]);else length<=65535?appendBytes([218,length>>>8,length]):appendBytes([219,length>>>24,length>>>16,length>>>8,length]);appendBytes(bytes)}function appendArray(data){let length=data.length;if(length<=15)appendByte(144+length);else length<=65535?appendBytes([220,length>>>8,length]):appendBytes([221,length>>>24,length>>>16,length>>>8,length]);for(let index=0;index<length;index++)append(data[index])}function appendBinArray(data){let length=data.length;if(length<=15)appendBytes([196,length]);else length<=65535?appendBytes([197,length>>>8,length]):appendBytes([198,length>>>24,length>>>16,length>>>8,length]);appendBytes(data)}function appendObject(data){let length=0;for(let key in data)if(void 0!==data[key])length++;if(length<=15)appendByte(128+length);else length<=65535?appendBytes([222,length>>>8,length]):appendBytes([223,length>>>24,length>>>16,length>>>8,length]);for(let key in data){let value=data[key];if(void 0!==value){append(key);append(value)}}}function appendDate(data){let sec=data.getTime()/1e3;if(0===data.getMilliseconds()&&sec>=0&&sec<0x100000000)appendBytes([214,255,sec>>>24,sec>>>16,sec>>>8,sec]);else if(sec>=0&&sec<0x400000000){let ns=1e6*data.getMilliseconds();appendBytes([215,255,ns>>>22,ns>>>14,ns>>>6,ns<<2>>>0|sec/pow32,sec>>>24,sec>>>16,sec>>>8,sec])}else{let ns=1e6*data.getMilliseconds();appendBytes([199,12,255,ns>>>24,ns>>>16,ns>>>8,ns]);appendInt64(sec)}}function appendByte(byte){if(array.length<length+1){let newLength=2*array.length;while(newLength<length+1)newLength*=2;let newArray=new Uint8Array(newLength);newArray.set(array);array=newArray}array[length]=byte;length++}function appendBytes(bytes){if(array.length<length+bytes.length){let newLength=2*array.length;while(newLength<length+bytes.length)newLength*=2;let newArray=new Uint8Array(newLength);newArray.set(array);array=newArray}array.set(bytes,length);length+=bytes.length}function appendInt64(value){let hi,lo;if(value>=0){hi=value/pow32;lo=value%pow32}else{value++;hi=Math.abs(value)/pow32;lo=Math.abs(value)%pow32;hi=~hi;lo=~lo}appendBytes([hi>>>24,hi>>>16,hi>>>8,hi,lo>>>24,lo>>>16,lo>>>8,lo])}}function deserialize(array,options){const pow32=0x100000000;let pos=0;if(array instanceof ArrayBuffer)array=new Uint8Array(array);if("object"!=typeof array||void 0===array.length)throw new Error("Invalid argument type: Expected a byte array (Array or Uint8Array) to deserialize.");if(!array.length)throw new Error("Invalid argument: The byte array to deserialize is empty.");if(!(array instanceof Uint8Array))array=new Uint8Array(array);let data;if(options&&options.multiple){data=[];while(pos<array.length)data.push(read())}else data=read();return data;function read(){const byte=array[pos++];if(byte>=0&&byte<=127)return byte;if(byte>=128&&byte<=143)return readMap(byte-128);if(byte>=144&&byte<=159)return readArray(byte-144);if(byte>=160&&byte<=191)return readStr(byte-160);if(192===byte)return null;if(193===byte)throw new Error("Invalid byte code 0xc1 found.");if(194===byte)return false;if(195===byte)return true;if(196===byte)return readBin(-1,1);if(197===byte)return readBin(-1,2);if(198===byte)return readBin(-1,4);if(199===byte)return readExt(-1,1);if(200===byte)return readExt(-1,2);if(201===byte)return readExt(-1,4);if(202===byte)return readFloat(4);if(203===byte)return readFloat(8);if(204===byte)return readUInt(1);if(205===byte)return readUInt(2);if(206===byte)return readUInt(4);if(207===byte)return readUInt(8);if(208===byte)return readInt(1);if(209===byte)return readInt(2);if(210===byte)return readInt(4);if(211===byte)return readInt(8);if(212===byte)return readExt(1);if(213===byte)return readExt(2);if(214===byte)return readExt(4);if(215===byte)return readExt(8);if(216===byte)return readExt(16);if(217===byte)return readStr(-1,1);if(218===byte)return readStr(-1,2);if(219===byte)return readStr(-1,4);if(220===byte)return readArray(-1,2);if(221===byte)return readArray(-1,4);if(222===byte)return readMap(-1,2);if(223===byte)return readMap(-1,4);if(byte>=224&&byte<=255)return byte-256;console.debug("msgpack array:",array);throw new Error("Invalid byte value '"+byte+"' at index "+(pos-1)+" in the MessagePack binary data (length "+array.length+"): Expecting a range of 0 to 255. This is not a byte array.")}function readInt(size){let value=0;let first=true;while(size-- >0)if(first){let byte=array[pos++];value+=127&byte;if(128&byte)value-=128;first=false}else{value*=256;value+=array[pos++]}return value}function readUInt(size){let value=0;while(size-- >0){value*=256;value+=array[pos++]}return value}function readFloat(size){let view=new DataView(array.buffer,pos+array.byteOffset,size);pos+=size;if(4===size)return view.getFloat32(0,false);if(8===size)return view.getFloat64(0,false)}function readBin(size,lengthSize){if(size<0)size=readUInt(lengthSize);let data=array.subarray(pos,pos+size);pos+=size;return data}function readMap(size,lengthSize){if(size<0)size=readUInt(lengthSize);let data={};while(size-- >0){let key=read();data[key]=read()}return data}function readArray(size,lengthSize){if(size<0)size=readUInt(lengthSize);let data=[];while(size-- >0)data.push(read());return data}function readStr(size,lengthSize){if(size<0)size=readUInt(lengthSize);let start=pos;pos+=size;return decodeUtf8(array,start,size)}function readExt(size,lengthSize){if(size<0)size=readUInt(lengthSize);let type=readUInt(1);let data=readBin(size);switch(type){case 255:return readExtDate(data)}return{type:type,data:data}}function readExtDate(data){if(4===data.length){let sec=(data[0]<<24>>>0)+(data[1]<<16>>>0)+(data[2]<<8>>>0)+data[3];return new Date(1e3*sec)}if(8===data.length){let ns=(data[0]<<22>>>0)+(data[1]<<14>>>0)+(data[2]<<6>>>0)+(data[3]>>>2);let sec=(3&data[3])*pow32+(data[4]<<24>>>0)+(data[5]<<16>>>0)+(data[6]<<8>>>0)+data[7];return new Date(1e3*sec+ns/1e6)}if(12===data.length){let ns=(data[0]<<24>>>0)+(data[1]<<16>>>0)+(data[2]<<8>>>0)+data[3];pos-=8;let sec=readInt(8);return new Date(1e3*sec+ns/1e6)}throw new Error("Invalid data length for a date value.")}}function encodeUtf8(str){let ascii=true,length=str.length;for(let x=0;x<length;x++)if(str.charCodeAt(x)>127){ascii=false;break}let i=0,bytes=new Uint8Array(str.length*(ascii?1:4));for(let ci=0;ci!==length;ci++){let c=str.charCodeAt(ci);if(c<128){bytes[i++]=c;continue}if(c<2048)bytes[i++]=c>>6|192;else{if(c>55295&&c<56320){if(++ci>=length)throw new Error("UTF-8 encode: incomplete surrogate pair");let c2=str.charCodeAt(ci);if(c2<56320||c2>57343)throw new Error("UTF-8 encode: second surrogate character 0x"+c2.toString(16)+" at index "+ci+" out of range");c=65536+((1023&c)<<10)+(1023&c2);bytes[i++]=c>>18|240;bytes[i++]=c>>12&63|128}else bytes[i++]=c>>12|224;bytes[i++]=c>>6&63|128}bytes[i++]=63&c|128}return ascii?bytes:bytes.subarray(0,i)}function decodeUtf8(bytes,start,length){let i=start,str="";length+=start;while(i<length){let c=bytes[i++];if(c>127){if(c>191&&c<224){if(i>=length)throw new Error("UTF-8 decode: incomplete 2-byte sequence");c=(31&c)<<6|63&bytes[i++]}else if(c>223&&c<240){if(i+1>=length)throw new Error("UTF-8 decode: incomplete 3-byte sequence");c=(15&c)<<12|(63&bytes[i++])<<6|63&bytes[i++]}else if(c>239&&c<248){if(i+2>=length)throw new Error("UTF-8 decode: incomplete 4-byte sequence");c=(7&c)<<18|(63&bytes[i++])<<12|(63&bytes[i++])<<6|63&bytes[i++]}else throw new Error("UTF-8 decode: unknown multibyte start 0x"+c.toString(16)+" at index "+(i-1))}if(c<=65535)str+=String.fromCharCode(c);else if(c<=1114111){c-=65536;str+=String.fromCharCode(c>>10|55296);str+=String.fromCharCode(1023&c|56320)}else throw new Error("UTF-8 decode: code point 0x"+c.toString(16)+" exceeds UTF-16 reach")}return str}let msgpack={serialize:serialize,deserialize:deserialize,encode:serialize,decode:deserialize};if(module&&"object"==typeof module.exports)module.exports=msgpack;else window[window.msgpackJsName||"msgpack"]=msgpack})()}};var __webpack_module_cache__={};function __webpack_require__(moduleId){var cachedModule=__webpack_module_cache__[moduleId];if(void 0!==cachedModule)return cachedModule.exports;var module=__webpack_module_cache__[moduleId]={id:moduleId,loaded:false,exports:{}};__webpack_modules__[moduleId](module,module.exports,__webpack_require__);module.loaded=true;return module.exports}(()=>{__webpack_require__.nmd=function(module){module.paths=[];if(!module.children)module.children=[];return module}})();var msgpack=__webpack_require__("./src/msgpack/index.js");const generateHexSegment=()=>((1+Math.random())*65536|0).toString(16).substring(1);const generateUUID=()=>[generateHexSegment()+generateHexSegment(),generateHexSegment(),generateHexSegment(),generateHexSegment(),generateHexSegment()+generateHexSegment()+generateHexSegment()].join("-");const generateId=(length=8)=>{let result="";const characters="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";const charactersLength=characters.length;for(let i=0;i<length;i++)result+=characters.charAt(Math.floor(Math.random()*charactersLength));return result};const defaultProps={onopen:()=>{},onmessage:()=>{},onclose:()=>{},onerror:()=>{}};class Socket{props;guidStorage=[];callbackStorage={};ws;streamID="";constructor(props){this.props={jsonrpc:"2.0",...defaultProps,...props};this.ws=this.setupWS()}setupWS(){const ws=new WebSocket(this.props.url,this.props.protocols);ws.binaryType="arraybuffer";ws.onopen=e=>this.props.onopen(e);ws.onmessage=e=>this.onmessage(e);ws.onclose=e=>this.props.onclose(e);ws.onerror=e=>this.props.onerror(e);return ws}saveGUID(guid){this.guidStorage.push(guid);return guid}deleteGUID(guid){const arr=this.guidStorage;const index=arr.indexOf(guid);if(-1!==index){arr.splice(index,1);return true}return false}touchTimeout(id,time=15e3,onerror=()=>{}){setTimeout(()=>{if(this.guidStorage.includes(id)){onerror({code:408,message:"发送数据链接超时"});this.deleteGUID(id)}},time)}saveResponse(callback,id){this.callbackStorage[id]=callback}finishResponse(res,id){const callback=this.callbackStorage[id];if(void 0!==this.callbackStorage[id]&&callback){callback(res);if(id!==this.streamID)delete this.callbackStorage[id]}return id}onmessage(e){const response=(0,msgpack.decode)(Array.prototype.slice.call(new Uint8Array(e.data)));if(!response)return;const{id}=response;if(id&&this.deleteGUID(id))this.finishResponse(response,id);this.props.onmessage(response)}send=data=>{const{id:paramId,method,isInform,callback,onerror,params,timeout}=data;if(1!==this.ws.readyState){if(onerror)onerror({code:3,message:"连接已关闭或者没有链接成功"});return}if(void 0===method)return;let guid={};const id=this.saveGUID(paramId||generateUUID());if(!isInform){guid={id};if(callback)this.saveResponse(callback,id)}this.touchTimeout(id,timeout,onerror);this.ws.send((0,msgpack.encode)({jsonrpc:this.props.jsonrpc,params,method,...guid}))};stream=data=>{const{id:paramId,method,callback,params}=data;if(void 0===method||!callback||1!==this.ws.readyState)return;const id=this.saveGUID(paramId||generateUUID());if(callback)this.saveResponse(callback,id);this.ws.send((0,msgpack.encode)({jsonrpc:this.props.jsonrpc,params,method,id:this.streamID}));return{id:this.streamID,close:(code,reason)=>{this.streamID="";this.close(code,reason)}}};close=(code,reason)=>this.ws.close(code,reason);withUrl=url=>{this.close();this.guidStorage=[];this.callbackStorage={};this.streamID="";this.props.url=url;this.ws=this.setupWS()}}const src=Socket;export{src as default};
|
|
1
|
+
function e(e){let t=e.length,n=!0;for(let r=0;r<t;r++)if(e.charCodeAt(r)>127){n=!1;break}if(n){let n=new Uint8Array(t);for(let r=0;r<t;r++)n[r]=e.charCodeAt(r);return n}let r=new Uint8Array(2*t),i=0;for(let n=0;n<t;n++){let s=e.charCodeAt(n);if(i+4>r.length){let e=new Uint8Array(Math.ceil(1.5*r.length));e.set(r),r=e}if(s<128)r[i++]=s;else if(s<2048)r[i++]=192|s>>>6,r[i++]=128|63&s;else if(s>=55296&&s<56320){if(n+1>=t)throw Error("UTF-8 encode: incomplete surrogate pair");let o=e.charCodeAt(++n);if(o<56320||o>57343)throw Error(`UTF-8 encode: second surrogate character 0x${o.toString(16)} at index ${n} out of range`);s=65536+((1023&s)<<10)+(1023&o),r[i++]=240|s>>>18,r[i++]=128|s>>>12&63,r[i++]=128|s>>>6&63,r[i++]=128|63&s}else r[i++]=224|s>>>12,r[i++]=128|s>>>6&63,r[i++]=128|63&s}return r.subarray(0,i)}function t(e,t,n){let r=t+n,i=!0;for(let n=t;n<r;n++)if(e[n]>127){i=!1;break}if(i)return String.fromCharCode(...e.subarray(t,r));let s="",o=t;for(;o<r;){let t=e[o++];if(t<128)s+=String.fromCharCode(t);else if(t<224){if(o>=r)throw Error("UTF-8 decode: incomplete 2-byte sequence");s+=String.fromCharCode((31&t)<<6|63&e[o++])}else if(t<240){if(o+1>=r)throw Error("UTF-8 decode: incomplete 3-byte sequence");s+=String.fromCharCode((15&t)<<12|(63&e[o++])<<6|63&e[o++])}else{if(o+2>=r)throw Error("UTF-8 decode: incomplete 4-byte sequence");let n=e[o++],i=(7&t)<<18|(63&n)<<12|(63&e[o++])<<6|63&e[o++];if(i<=65535)s+=String.fromCharCode(i);else if(i<=1114111)i-=65536,s+=String.fromCharCode(55296|i>>>10),s+=String.fromCharCode(56320|1023&i);else throw Error(`UTF-8 decode: code point 0x${i.toString(16)} exceeds UTF-16 reach`)}}return s}let n=0x100000000;function r(e,n){let r;if(e instanceof ArrayBuffer)r=new Uint8Array(e);else if(e instanceof Uint8Array)r=e;else if("object"==typeof e&&void 0!==e.length)r=new Uint8Array(e);else throw Error("Invalid argument type: Expected a byte array (Array or Uint8Array) to deserialize.");if(0===r.length)throw Error("Invalid argument: The byte array to deserialize is empty.");let i=0;if(null==n?void 0:n.multiple){let e=[];for(;i<r.length;)e.push(o());return e}return o();function s(e){if(i+e>r.length)throw Error(`Buffer overflow: trying to read ${e} bytes at position ${i}, but only ${r.length-i} bytes available`)}function o(){s(1);let e=r[i++];if(e>=0&&e<=127)return e;if(e>=128&&e<=143)return h(e-128);if(e>=144&&e<=159)return u(e-144);if(e>=160&&e<=191)return d(e-160);if(e>=224&&e<=255)return e-256;if(192===e)return null;if(194===e)return!1;if(195===e)return!0;if(196===e)return f(-1,1);if(197===e)return f(-1,2);if(198===e)return f(-1,4);if(199===e)return p(-1,1);if(200===e)return p(-1,2);if(201===e)return p(-1,4);if(212===e)return p(1);if(213===e)return p(2);if(214===e)return p(4);if(215===e)return p(8);if(216===e)return p(16);if(202===e)return c(4);if(203===e)return c(8);if(204===e)return a(1);if(205===e)return a(2);if(206===e)return a(4);if(207===e)return a(8);if(208===e)return l(1);if(209===e)return l(2);if(210===e)return l(4);if(211===e)return l(8);if(217===e)return d(-1,1);if(218===e)return d(-1,2);if(219===e)return d(-1,4);if(220===e)return u(-1,2);if(221===e)return u(-1,4);if(222===e)return h(-1,2);if(223===e)return h(-1,4);if(193===e)throw Error("Invalid byte code 0xc1 found.");throw Error(`Invalid byte value '${e}' at index ${i-1} in the MessagePack binary data (length ${r.length}): Expecting a range of 0 to 255. This is not a byte array.`)}function l(e){s(e);let t=0,n=!0;for(let s=0;s<e;s++)if(n){let e=r[i++];t+=127&e,128&e&&(t-=128),n=!1}else t=256*t+r[i++];return t}function a(e){s(e);let t=0;for(let n=0;n<e;n++)t=256*t+r[i++];return t}function c(e){s(e);let t=new DataView(r.buffer,i+r.byteOffset,e);return(i+=e,4===e)?t.getFloat32(0,!1):8===e?t.getFloat64(0,!1):0}function f(e,t){let n=e<0?a(t):e;s(n);let o=r.subarray(i,i+n);return i+=n,o}function h(e,t){let n=e<0&&void 0!==t?a(t):e,r={};for(let e=0;e<n;e++){let e=o();if("string"!=typeof e)throw Error(`Invalid map key type: expected string, got ${typeof e}`);r[e]=o()}return r}function u(e,t){let n=e<0&&void 0!==t?a(t):e,r=[];for(let e=0;e<n;e++)r.push(o());return r}function d(e,n){let o=e<0&&void 0!==n?a(n):e;s(o);let l=t(r,i,o);return i+=o,l}function p(e,t){let n=e<0&&void 0!==t?a(t):e;s(1+n);let r=a(1),o=f(n,0);return 255===r?function(e){if(4===e.length)return new Date(1e3*((e[0]<<24>>>0)+(e[1]<<16>>>0)+(e[2]<<8>>>0)+e[3]));if(8===e.length){let t=(e[0]<<22>>>0)+(e[1]<<14>>>0)+(e[2]<<6>>>0)+(e[3]>>>2);return new Date(1e3*((3&e[3])*0x100000000+(e[4]<<24>>>0)+(e[5]<<16>>>0)+(e[6]<<8>>>0)+e[7])+t/1e6)}if(12===e.length){let t=(e[0]<<24>>>0)+(e[1]<<16>>>0)+(e[2]<<8>>>0)+e[3];return i-=8,new Date(1e3*l(8)+t/1e6)}throw Error("Invalid data length for a date value.")}(o):{type:r,data:o}}}let i=0x100000000,s=0x1fffffffffffff;function o(t,n){let r,i;if((null==n?void 0:n.multiple)&&!Array.isArray(t))throw Error("Invalid argument type: Expected an Array to serialize multiple values.");let s=new Uint8Array(256),o=0;if((null==n?void 0:n.multiple)&&Array.isArray(t))for(let e of t)l(e);else l(t);return s.subarray(0,o);function l(t,s=!1){let o=typeof t;switch(o){case"undefined":c(192);break;case"boolean":c(t?195:194);break;case"number":!function(e){if(Number.isFinite(e)&&Math.floor(e)===e){if(e>=0&&e<=127)c(e);else if(e<0&&e>=-32)c(e);else if(e>0&&e<=255)f([204,e]);else if(e>=-128&&e<=127)f([208,e]);else if(e>0&&e<=65535)f([205,e>>>8,e]);else if(e>=-32768&&e<=32767)f([209,e>>>8,e]);else if(e>0&&e<=0xffffffff)f([206,e>>>24,e>>>16,e>>>8,e]);else if(e>=-0x80000000&&e<=0x7fffffff)f([210,e>>>24,e>>>16,e>>>8,e]);else if(e>0&&e<=0x1fffffffffffff){let t=Math.floor(e/0x100000000),n=Math.floor(e%0x100000000);f([211,t>>>24,t>>>16,t>>>8,t,n>>>24,n>>>16,n>>>8,n])}else e>=-0x1fffffffffffff&&e<=-0x80000001?(c(211),u(e)):e<0?f([211,128,0,0,0,0,0,0,0]):f([207,255,255,255,255,255,255,255,255])}else i||(i=new DataView(r=new ArrayBuffer(8))),i.setFloat64(0,e),c(203),f(new Uint8Array(r))}(t);break;case"string":!function(t){let n=e(t),r=n.length;r<=31?c(160+r):r<=255?f([217,r]):r<=65535?f([218,r>>>8,r]):f([219,r>>>24,r>>>16,r>>>8,r]),f(n)}(t);break;case"object":null===t?c(192):t instanceof Date?function(e){let t=e.getTime()/1e3;if(0===e.getMilliseconds()&&t>=0&&t<0x100000000){let e=Math.floor(t);f([214,255,e>>>24,e>>>16,e>>>8,e])}else if(t>=0&&t<0x400000000){let n=1e6*e.getMilliseconds(),r=Math.floor(t);f([215,255,n>>>22,n>>>14,n>>>6,n<<2>>>0|Math.floor(r/0x100000000),r>>>24,r>>>16,r>>>8,r])}else{let n=1e6*e.getMilliseconds();f([199,12,255,n>>>24,n>>>16,n>>>8,n]),u(t)}}(t):Array.isArray(t)?a(t):t instanceof Uint8Array||t instanceof Uint8ClampedArray?function(e){let t=e.length;t<=255?f([196,t]):t<=65535?f([197,t>>>8,t]):f([198,t>>>24,t>>>16,t>>>8,t]),f(e)}(t):t instanceof Int8Array||t instanceof Int16Array||t instanceof Uint16Array||t instanceof Int32Array||t instanceof Uint32Array||t instanceof Float32Array||t instanceof Float64Array?a(Array.from(t)):function(e){let t=0;for(let n in e)void 0!==e[n]&&t++;for(let n in t<=15?c(128+t):t<=65535?f([222,t>>>8,t]):f([223,t>>>24,t>>>16,t>>>8,t]),e){let t=e[n];void 0!==t&&(l(n),l(t))}}(t);break;default:if(!s&&(null==n?void 0:n.invalidTypeReplacement))l("function"==typeof n.invalidTypeReplacement?n.invalidTypeReplacement(t):n.invalidTypeReplacement,!0);else throw Error(`Invalid argument type: The type '${o}' cannot be serialized.`)}}function a(e){let t=e.length;for(let n of(t<=15?c(144+t):t<=65535?f([220,t>>>8,t]):f([221,t>>>24,t>>>16,t>>>8,t]),e))l(n)}function c(e){s.length<o+1&&h(o+1),s[o++]=e}function f(e){let t=o+e.length;s.length<t&&h(t),s.set(e,o),o+=e.length}function h(e){let t=Math.ceil(1.5*s.length);for(;t<e;)t=Math.ceil(1.5*t);let n=new Uint8Array(t);n.set(s),s=n}function u(e){let t,n;if(e>=0)t=Math.floor(e/0x100000000),n=Math.floor(e%0x100000000);else{let r=Math.abs(e+1);t=~Math.floor(r/0x100000000),n=~Math.floor(r%0x100000000)}f([t>>>24,t>>>16,t>>>8,t,n>>>24,n>>>16,n>>>8,n])}}let l=o,a=r,c=o,f=r,h=()=>((1+Math.random())*65536|0).toString(16).substring(1),u=()=>[h()+h(),h(),h(),h(),h()+h()+h()].join("-"),d=(e=8)=>{let t="",n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",r=n.length;for(let i=0;i<e;i++)t+=n.charAt(Math.floor(Math.random()*r));return t};var p=/*#__PURE__*/function(e){return e[e.ParseError=-32700]="ParseError",e[e.InvalidRequest=-32600]="InvalidRequest",e[e.MethodNotFound=-32601]="MethodNotFound",e[e.InvalidParams=-32602]="InvalidParams",e[e.InternalError=-32603]="InternalError",e[e.ServerError=-32e3]="ServerError",e[e.Timeout=-32001]="Timeout",e[e.ConnectionClosed=-32002]="ConnectionClosed",e}({});function m(e,t,n,r){return{jsonrpc:"2.0",error:{code:t,message:n,data:r},id:e}}function g(e,t){return{jsonrpc:"2.0",result:t,id:e}}function y(e){return"error"in e}function b(e){return"result"in e}function v(e){return"method"in e&&!("id"in e)}var w=/*#__PURE__*/function(e){return e[e.Connecting=0]="Connecting",e[e.Open=1]="Open",e[e.Closing=2]="Closing",e[e.Closed=3]="Closed",e}({});let C={Open:"open",Close:"close",Error:"error",Message:"message",Reconnecting:"reconnecting",Reconnected:"reconnected",ReconnectFailed:"reconnect_failed"};function A(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}class R{on(e,t){var n;return this.listeners.has(e)||this.listeners.set(e,new Set),null===(n=this.listeners.get(e))||void 0===n||n.add(t),()=>this.off(e,t)}once(e,t){let n=r=>{this.off(e,n),t(r)};return this.on(e,n)}off(e,t){let n=this.listeners.get(e);n&&(n.delete(t),0===n.size&&this.listeners.delete(e))}emit(e,t){let n=this.listeners.get(e);if(n)for(let r of n)try{r(t)}catch(t){console.error(`Error in event listener for "${String(e)}":`,t)}}removeAllListeners(e){e?this.listeners.delete(e):this.listeners.clear()}listenerCount(e){var t;return(null===(t=this.listeners.get(e))||void 0===t?void 0:t.size)??0}constructor(){A(this,"listeners",new Map)}}function I(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}let T={autoReconnect:!0,reconnectInterval:3e3,maxReconnectAttempts:5,defaultTimeout:15e3,heartbeatInterval:0,heartbeatMethod:"ping",debug:!1};class x extends R{get state(){var e;return(null===(e=this.ws)||void 0===e?void 0:e.readyState)??w.Closed}get isConnected(){return this.state===w.Open}getStats(){return{...this.stats,pendingRequests:this.pendingRequests.size}}connect(){if(!this.ws||this.ws.readyState!==w.Connecting)try{this.ws=new WebSocket(this.options.url,this.options.protocols),this.ws.binaryType="arraybuffer",this.ws.onopen=this.handleOpen.bind(this),this.ws.onmessage=this.handleMessage.bind(this),this.ws.onclose=this.handleClose.bind(this),this.ws.onerror=this.handleError.bind(this)}catch(e){this.log("Connection error:",e),this.scheduleReconnect()}}handleOpen(e){this.log("Connection opened"),this.reconnectAttempts=0,this.startHeartbeat(),this.emit(C.Open,e)}handleMessage(e){try{let t=e.data,n=f(new Uint8Array(t));if(this.log("Received:",n),this.stats.responsesReceived++,"error"in n&&this.stats.errors++,null!==n.id){let e=this.streamCallbacks.get(n.id);if(e){e(n),this.emit(C.Message,{data:n,rawData:t});return}let r=this.pendingRequests.get(n.id);if(r){clearTimeout(r.timeoutId),this.pendingRequests.delete(n.id);let e=Date.now()-r.timestamp;this.updateResponseTime(e),"error"in n?r.reject(Error(`${n.error.message} (${n.error.code})`)):r.resolve(n.result)}}this.emit(C.Message,{data:n,rawData:t})}catch(e){this.log("Failed to decode message:",e)}}handleClose(e){this.log("Connection closed:",e.code,e.reason),this.stopHeartbeat(),this.emit(C.Close,e),this.rejectAllPendingRequests(Error("Connection closed"),p.ConnectionClosed),this.options.autoReconnect&&this.scheduleReconnect()}handleError(e){this.log("Connection error:",e),this.emit(C.Error,e)}scheduleReconnect(){if(!this.reconnectTimeoutId){if(this.reconnectAttempts>=this.options.maxReconnectAttempts){this.log("Max reconnect attempts reached"),this.emit(C.ReconnectFailed,void 0);return}this.reconnectAttempts++,this.stats.reconnectCount++,this.log(`Reconnecting in ${this.options.reconnectInterval}ms (attempt ${this.reconnectAttempts}/${this.options.maxReconnectAttempts})`),this.emit(C.Reconnecting,{attempt:this.reconnectAttempts,maxAttempts:this.options.maxReconnectAttempts}),this.reconnectTimeoutId=setTimeout(()=>{this.reconnectTimeoutId=null,this.connect()},this.options.reconnectInterval)}}startHeartbeat(){!(this.options.heartbeatInterval<=0)&&null===this.heartbeatIntervalId&&(this.heartbeatIntervalId=setInterval(()=>{this.isConnected&&this.notify({method:this.options.heartbeatMethod}).catch(e=>{this.log("Heartbeat failed:",e)})},this.options.heartbeatInterval))}stopHeartbeat(){this.heartbeatIntervalId&&(clearInterval(this.heartbeatIntervalId),this.heartbeatIntervalId=null)}rejectAllPendingRequests(e,t){for(let[t,n]of this.pendingRequests)clearTimeout(n.timeoutId),n.reject(e);this.pendingRequests.clear()}updateResponseTime(e){this.responseTimes.push(e),this.responseTimes.length>100&&this.responseTimes.shift(),this.stats.averageResponseTime=this.responseTimes.reduce((e,t)=>e+t,0)/this.responseTimes.length}async request(e){if(!this.isConnected)throw Error("WebSocket is not connected");let t=e.id??u(),n={jsonrpc:"2.0",method:e.method,params:e.params,id:t};return new Promise((r,i)=>{let s=e.timeout??this.options.defaultTimeout,o=setTimeout(()=>{this.pendingRequests.delete(t),this.stats.timeouts++,i(Error(`Request timeout after ${s}ms`))},s);this.pendingRequests.set(t,{id:t,timestamp:Date.now(),timeoutId:o,resolve:r,reject:i});try{var l;let e=c(n);null===(l=this.ws)||void 0===l||l.send(e),this.stats.requestsSent++,this.log("Sent request:",n)}catch(e){clearTimeout(o),this.pendingRequests.delete(t),i(e)}})}async notify(e){var t;if(!this.isConnected)throw Error("WebSocket is not connected");let n={jsonrpc:"2.0",method:e.method,params:e.params},r=c(n);null===(t=this.ws)||void 0===t||t.send(r),this.log("Sent notification:",n)}stream(e,t){if(!this.isConnected)throw Error("WebSocket is not connected");let n=e.id??u(),r={jsonrpc:"2.0",method:e.method,params:e.params,id:n},i=!1;this.streamCallbacks.set(n,t);try{var s;let e=c(r);null===(s=this.ws)||void 0===s||s.send(e),this.stats.requestsSent++,this.log("Sent stream request:",r)}catch(e){throw this.streamCallbacks.delete(n),e}return{id:n,close:()=>{i||(i=!0,this.streamCallbacks.delete(n),this.log("Stream closed:",n))},get closed(){return i}}}sendRaw(e){var t;if(!this.isConnected)throw Error("WebSocket is not connected");null===(t=this.ws)||void 0===t||t.send(e),this.log("Sent raw data:",e.byteLength,"bytes")}close(e,t){this.options.autoReconnect=!1,this.reconnectTimeoutId&&(clearTimeout(this.reconnectTimeoutId),this.reconnectTimeoutId=null),this.stopHeartbeat(),this.ws&&(this.ws.close(e,t),this.ws=null),this.rejectAllPendingRequests(Error("Connection closed by client"),p.ConnectionClosed),this.removeAllListeners()}reconnectToUrl(e){this.close(),this.options.url=e,this.options.autoReconnect=!0,this.reconnectAttempts=0,this.connect()}log(...e){this.options.debug&&console.log("[JsonRpcWebSocket]",...e)}constructor(e){super(),I(this,"ws",null),I(this,"options",void 0),I(this,"pendingRequests",new Map),I(this,"streamCallbacks",new Map),I(this,"reconnectAttempts",0),I(this,"reconnectTimeoutId",null),I(this,"heartbeatIntervalId",null),I(this,"stats",{requestsSent:0,responsesReceived:0,timeouts:0,errors:0,averageResponseTime:0,pendingRequests:0,reconnectCount:0}),I(this,"responseTimes",[]),this.options={...T,...e},this.connect()}}export{x as JsonRpcWebSocketClient,f as decode,x as default,a as deserialize,c as encode,u as generateUUID,l as serialize};
|
package/package.json
CHANGED
package/index.d.ts
DELETED
package/socket.d.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import type { PropsType, SocketType, WithUrl } from './type';
|
|
2
|
-
/**
|
|
3
|
-
* Socket
|
|
4
|
-
* 基于WebSocket、msgpack、JSONRPC封装的实时通讯函数
|
|
5
|
-
* @export
|
|
6
|
-
* @class Socket
|
|
7
|
-
*/
|
|
8
|
-
export default class Socket {
|
|
9
|
-
private props;
|
|
10
|
-
private guidStorage;
|
|
11
|
-
private callbackStorage;
|
|
12
|
-
ws: WebSocket;
|
|
13
|
-
private streamID;
|
|
14
|
-
constructor(props: PropsType);
|
|
15
|
-
/**
|
|
16
|
-
* 初始化链接
|
|
17
|
-
*
|
|
18
|
-
* @private
|
|
19
|
-
* @returns {WebSocket}
|
|
20
|
-
* @memberof Socket
|
|
21
|
-
*/
|
|
22
|
-
private setupWS;
|
|
23
|
-
/**
|
|
24
|
-
* 存储guid
|
|
25
|
-
*
|
|
26
|
-
* @private
|
|
27
|
-
* @param {string} guid
|
|
28
|
-
* @returns {string}
|
|
29
|
-
* @memberof Socket
|
|
30
|
-
*/
|
|
31
|
-
private saveGUID;
|
|
32
|
-
/**
|
|
33
|
-
* 删除guid
|
|
34
|
-
*
|
|
35
|
-
* @private
|
|
36
|
-
* @param {string} guid
|
|
37
|
-
* @returns {boolean}
|
|
38
|
-
* @memberof Socket
|
|
39
|
-
*/
|
|
40
|
-
private deleteGUID;
|
|
41
|
-
/**
|
|
42
|
-
* send时触发超时函数
|
|
43
|
-
*
|
|
44
|
-
* @private
|
|
45
|
-
* @param {string} guid
|
|
46
|
-
* @memberof Socket
|
|
47
|
-
*/
|
|
48
|
-
private touchTimeout;
|
|
49
|
-
/**
|
|
50
|
-
* 存储回调方法
|
|
51
|
-
*
|
|
52
|
-
* @private
|
|
53
|
-
* @param {Communicate['callback']} callback
|
|
54
|
-
* @param {string} id
|
|
55
|
-
* @memberof Socket
|
|
56
|
-
*/
|
|
57
|
-
private saveResponse;
|
|
58
|
-
/**
|
|
59
|
-
* 执行回调方法并移除
|
|
60
|
-
*
|
|
61
|
-
* @private
|
|
62
|
-
* @param {string} id
|
|
63
|
-
* @returns
|
|
64
|
-
* @memberof Socket
|
|
65
|
-
*/
|
|
66
|
-
private finishResponse;
|
|
67
|
-
/**
|
|
68
|
-
* 接收数据
|
|
69
|
-
*
|
|
70
|
-
* @param {MessageEvent} buffer
|
|
71
|
-
* @returns
|
|
72
|
-
* @memberof Socket
|
|
73
|
-
*/
|
|
74
|
-
private onmessage;
|
|
75
|
-
/**
|
|
76
|
-
* 发送数据
|
|
77
|
-
*
|
|
78
|
-
* @param {Communicate} params
|
|
79
|
-
* @returns
|
|
80
|
-
* @memberof Socket
|
|
81
|
-
*/
|
|
82
|
-
send: SocketType['send'];
|
|
83
|
-
/**
|
|
84
|
-
* 启用流模式
|
|
85
|
-
*
|
|
86
|
-
* @type {SocketType['stream']}
|
|
87
|
-
* @memberof Socket
|
|
88
|
-
*/
|
|
89
|
-
stream: SocketType['stream'];
|
|
90
|
-
/**
|
|
91
|
-
* 关闭链接
|
|
92
|
-
*
|
|
93
|
-
* @memberof Socket
|
|
94
|
-
*/
|
|
95
|
-
close: SocketType['close'];
|
|
96
|
-
/**
|
|
97
|
-
* 更换url
|
|
98
|
-
*
|
|
99
|
-
* @type {WithUrl}
|
|
100
|
-
* @memberof Socket
|
|
101
|
-
*/
|
|
102
|
-
withUrl: WithUrl;
|
|
103
|
-
}
|
package/tools.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 生成四位随机数(十六进制)
|
|
3
|
-
* @returns {string} 四位十六进制随机数
|
|
4
|
-
*/
|
|
5
|
-
export declare const generateHexSegment: () => string;
|
|
6
|
-
/**
|
|
7
|
-
* 生成全局唯一标识符(UUID)
|
|
8
|
-
* 格式: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
9
|
-
* @returns {string} 符合RFC4122标准的UUID字符串
|
|
10
|
-
*/
|
|
11
|
-
export declare const generateUUID: () => string;
|
|
12
|
-
/**
|
|
13
|
-
* 生成简单的唯一ID(更短的标识符)
|
|
14
|
-
* @param {number} [length=8] ID的长度
|
|
15
|
-
* @returns {string} 指定长度的随机ID
|
|
16
|
-
*/
|
|
17
|
-
export declare const generateId: (length?: number) => string;
|
package/type.d.ts
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 发送数据时回调函数参数类型
|
|
3
|
-
*
|
|
4
|
-
* @export
|
|
5
|
-
* @interface ResType
|
|
6
|
-
*/
|
|
7
|
-
export interface ResType {
|
|
8
|
-
jsonrpc: string;
|
|
9
|
-
result: object;
|
|
10
|
-
id: string;
|
|
11
|
-
error?: object;
|
|
12
|
-
message?: string;
|
|
13
|
-
data?: string | object;
|
|
14
|
-
res?: ResType['result'] | ResType['error'] | {
|
|
15
|
-
message: ResType['message'];
|
|
16
|
-
data: ResType['data'];
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* 发送数据的参数类型
|
|
21
|
-
*
|
|
22
|
-
* @export
|
|
23
|
-
* @interface Communicate
|
|
24
|
-
*/
|
|
25
|
-
export interface Communicate {
|
|
26
|
-
jsonrpc?: string;
|
|
27
|
-
method: string;
|
|
28
|
-
params?: Record<string, unknown>;
|
|
29
|
-
id?: string;
|
|
30
|
-
timeout?: number;
|
|
31
|
-
isInform?: boolean | undefined;
|
|
32
|
-
callback?: (res: ResType['res']) => void;
|
|
33
|
-
onerror?: (res: {
|
|
34
|
-
code?: string | number;
|
|
35
|
-
message?: string;
|
|
36
|
-
}) => void;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Socket的事件类型
|
|
40
|
-
*
|
|
41
|
-
* @export
|
|
42
|
-
* @interface PropsFuncType
|
|
43
|
-
*/
|
|
44
|
-
export interface PropsFuncType {
|
|
45
|
-
onopen: (e?: Event) => void;
|
|
46
|
-
onmessage: (e?: unknown) => void;
|
|
47
|
-
onclose: (e?: CloseEvent) => void;
|
|
48
|
-
onerror: (e?: Event) => void;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Socket的websocket参数类型
|
|
52
|
-
*
|
|
53
|
-
* @export
|
|
54
|
-
* @interface PropsType
|
|
55
|
-
*/
|
|
56
|
-
export interface PropsType {
|
|
57
|
-
url: string;
|
|
58
|
-
protocols?: string | string[] | undefined;
|
|
59
|
-
onopen?: PropsFuncType['onopen'];
|
|
60
|
-
onmessage?: PropsFuncType['onmessage'];
|
|
61
|
-
onclose?: PropsFuncType['onclose'];
|
|
62
|
-
onerror?: PropsFuncType['onerror'];
|
|
63
|
-
jsonrpc?: string;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* 发送数据时回调函数存储的类型
|
|
67
|
-
*
|
|
68
|
-
* @export
|
|
69
|
-
* @interface CallbackStorage
|
|
70
|
-
*/
|
|
71
|
-
export interface CallbackStorage {
|
|
72
|
-
[propName: string]: Communicate['callback'];
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* 更换url函数的数据类型
|
|
76
|
-
*
|
|
77
|
-
* @export
|
|
78
|
-
* @interface WithUrl
|
|
79
|
-
*/
|
|
80
|
-
export type WithUrl = (url: string) => void;
|
|
81
|
-
/**
|
|
82
|
-
* stream的类型
|
|
83
|
-
*
|
|
84
|
-
* @export
|
|
85
|
-
* @interface SocketStrem
|
|
86
|
-
*/
|
|
87
|
-
export interface SocketStrem {
|
|
88
|
-
close: (code?: number, reason?: string) => void;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* 实时通讯函暴露的API类型:public
|
|
92
|
-
*
|
|
93
|
-
* @export
|
|
94
|
-
* @interface SocketType
|
|
95
|
-
*/
|
|
96
|
-
export interface SocketType {
|
|
97
|
-
send: (send: Communicate) => void;
|
|
98
|
-
close: (code?: number, reason?: string) => void;
|
|
99
|
-
stream: (send: Communicate) => {
|
|
100
|
-
id: string;
|
|
101
|
-
close: SocketStrem['close'];
|
|
102
|
-
} | undefined;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* JSON-RPC 2.0 请求对象
|
|
106
|
-
*/
|
|
107
|
-
export interface JsonRpcRequest {
|
|
108
|
-
jsonrpc: '2.0';
|
|
109
|
-
method: string;
|
|
110
|
-
params?: unknown;
|
|
111
|
-
id?: string | number | null;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* JSON-RPC 2.0 响应对象
|
|
115
|
-
*/
|
|
116
|
-
export interface JsonRpcResponse {
|
|
117
|
-
jsonrpc: '2.0';
|
|
118
|
-
result?: unknown;
|
|
119
|
-
error?: JsonRpcError;
|
|
120
|
-
id: string | number | null;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* JSON-RPC 2.0 错误对象
|
|
124
|
-
*/
|
|
125
|
-
export interface JsonRpcError {
|
|
126
|
-
code: number;
|
|
127
|
-
message: string;
|
|
128
|
-
data?: unknown;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* 连接选项
|
|
132
|
-
*/
|
|
133
|
-
export interface ConnectionOptions {
|
|
134
|
-
reconnect?: boolean;
|
|
135
|
-
reconnectInterval?: number;
|
|
136
|
-
maxReconnectAttempts?: number;
|
|
137
|
-
timeout?: number;
|
|
138
|
-
}
|