@wukong-kit/network 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 +150 -0
- package/dist/WKNetworkManager.d.ts +88 -0
- package/dist/WKNetworkManager.d.ts.map +1 -0
- package/dist/WKNetworkManager.js +383 -0
- package/dist/WKNetworkManager.js.map +1 -0
- package/dist/WKNetworkProtocol.d.ts +32 -0
- package/dist/WKNetworkProtocol.d.ts.map +1 -0
- package/dist/WKNetworkProtocol.js +60 -0
- package/dist/WKNetworkProtocol.js.map +1 -0
- package/dist/WKNetworkQueue.d.ts +34 -0
- package/dist/WKNetworkQueue.d.ts.map +1 -0
- package/dist/WKNetworkQueue.js +58 -0
- package/dist/WKNetworkQueue.js.map +1 -0
- package/dist/WKWebSocket.d.ts +28 -0
- package/dist/WKWebSocket.d.ts.map +1 -0
- package/dist/WKWebSocket.js +95 -0
- package/dist/WKWebSocket.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +25 -0
- package/dist/types.js.map +1 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# @wukong-kit/network
|
|
2
|
+
|
|
3
|
+
WebSocket-based network layer for Wukong Kit.
|
|
4
|
+
|
|
5
|
+
Built-in JSON protocol, request/response pattern, automatic reconnect with exponential backoff, heartbeat keepalive, and offline message queuing.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **WebSocket transport** — single connection, URL token auth
|
|
10
|
+
- **Request / Response** — `request(route, body) → Promise<T>` with timeout
|
|
11
|
+
- **Event-driven push** — all server pushes via `WKEventBus`
|
|
12
|
+
- **Auto-reconnect** — exponential backoff (1s → 2s → 4s → ... → 30s)
|
|
13
|
+
- **Retry callback** — 3 automatic retries, then delegate to your handler
|
|
14
|
+
- **Heartbeat** — configurable interval & timeout, auto-reconnect on timeout
|
|
15
|
+
- **Offline queue** — buffer messages when disconnected, flush on reconnect
|
|
16
|
+
- **Pluggable protocol** — default JSON, swap to Protobuf later via `WKProtocol`
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @wukong-kit/network
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Basic setup
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { WKNetworkManager, networkManager } from '@wukong-kit/network';
|
|
30
|
+
|
|
31
|
+
networkManager.setConfig({
|
|
32
|
+
serverUrl: 'wss://game.example.com/ws',
|
|
33
|
+
authToken: 'your-auth-token',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
networkManager.connect();
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Request / Response
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
try {
|
|
43
|
+
const data = await networkManager.request('player.login', {
|
|
44
|
+
uid: 10001,
|
|
45
|
+
password: '***',
|
|
46
|
+
});
|
|
47
|
+
console.log('Login success:', data);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error('Login failed:', err);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Fire-and-forget
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
networkManager.send('player.move', { x: 10, y: 20 });
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Listen for pushes (事件模式)
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { WKEvent } from '@wukong-kit/core';
|
|
63
|
+
|
|
64
|
+
// 监听具体路由
|
|
65
|
+
WKEvent.on('wk:net:push:room.match', (data) => {
|
|
66
|
+
console.log('Matched!', data);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// 监听所有推送
|
|
70
|
+
WKEvent.on('wk:net:push', (message) => {
|
|
71
|
+
console.log('Push:', message.route, message.body);
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Connection events
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
WKEvent.on('wk:net:connected', () => { /* ... */ });
|
|
79
|
+
WKEvent.on('wk:net:disconnected', ({ code, reason }) => { /* ... */ });
|
|
80
|
+
WKEvent.on('wk:net:reconnecting', ({ attempt, delay }) => { /* ... */ });
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Retry handler
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
networkManager.setRetryHandler((attempts, retry, abort) => {
|
|
87
|
+
// 弹窗询问玩家
|
|
88
|
+
showConfirmDialog({
|
|
89
|
+
title: '连接失败',
|
|
90
|
+
message: `已重连 ${attempts} 次失败,是否继续?`,
|
|
91
|
+
onConfirm: retry,
|
|
92
|
+
onCancel: abort,
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Custom protocol (advanced)
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { WKProtocolRegistry, WKBaseProtocol } from '@wukong-kit/network';
|
|
101
|
+
|
|
102
|
+
class MyProtobufProtocol extends WKBaseProtocol {
|
|
103
|
+
encode(msg) { /* ... */ }
|
|
104
|
+
decode(data) { /* ... */ }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const registry = new WKProtocolRegistry();
|
|
108
|
+
registry.register('protobuf', new MyProtobufProtocol());
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Configuration reference
|
|
112
|
+
|
|
113
|
+
| Option | Default | Description |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| `serverUrl` | `''` | WebSocket server URL (required) |
|
|
116
|
+
| `authToken` | `''` | Token appended as `?token=xxx` |
|
|
117
|
+
| `defaultTimeout` | `30000` | Request timeout (ms) |
|
|
118
|
+
| `heartbeatInterval` | `10000` | Ping interval (ms) |
|
|
119
|
+
| `heartbeatTimeout` | `5000` | Pong wait before reconnecting (ms) |
|
|
120
|
+
| `reconnectMaxAttempts` | `3` | Automatic retry count before callback |
|
|
121
|
+
| `reconnectBaseDelay` | `1000` | Initial reconnect delay (ms) |
|
|
122
|
+
| `reconnectMaxDelay` | `30000` | Max reconnect delay (ms) |
|
|
123
|
+
|
|
124
|
+
## Message envelope
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
interface WKMessage {
|
|
128
|
+
route: string; // "player.login"
|
|
129
|
+
seq: number; // auto-increment
|
|
130
|
+
body: unknown; // your payload
|
|
131
|
+
timestamp: number; // ms
|
|
132
|
+
ack: boolean; // expects response
|
|
133
|
+
ackSeq?: number; // response to which seq
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Event reference
|
|
138
|
+
|
|
139
|
+
| Event | Payload | Description |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| `wk:net:connected` | `{ url }` | Connection established |
|
|
142
|
+
| `wk:net:disconnected` | `{ code, reason, wasConnected }` | Connection closed |
|
|
143
|
+
| `wk:net:reconnecting` | `{ attempt, delay }` | Auto-reconnect countdown |
|
|
144
|
+
| `wk:net:push` | `WKMessage` | Raw server push |
|
|
145
|
+
| `wk:net:push:${route}` | `body` | Route-specific push |
|
|
146
|
+
| `wk:net:error` | `{ message, code? }` | Network error |
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wukong-kit/network — Network Manager
|
|
3
|
+
*
|
|
4
|
+
* 核心单例,管理 WebSocket 连接生命周期、消息收发、
|
|
5
|
+
* 自动重连、心跳保活、请求-应答匹配、离线缓冲。
|
|
6
|
+
*
|
|
7
|
+
* 使用方式:
|
|
8
|
+
* ```ts
|
|
9
|
+
* const net = WKNetworkManager.get();
|
|
10
|
+
* net.setConfig({ serverUrl: 'wss://...', authToken: 'xxx' });
|
|
11
|
+
* net.connect();
|
|
12
|
+
* const data = await net.request('player.login', { uid: 1 });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import { WKSingleton } from '@wukong-kit/core';
|
|
16
|
+
import { WKNetworkStatus } from './types.js';
|
|
17
|
+
import type { WKNetworkConfig, WKRetryHandler } from './types.js';
|
|
18
|
+
export declare class WKNetworkManager extends WKSingleton {
|
|
19
|
+
private readonly ws;
|
|
20
|
+
private readonly protocolRegistry;
|
|
21
|
+
private readonly queue;
|
|
22
|
+
private config;
|
|
23
|
+
private _connected;
|
|
24
|
+
private reconnectAttempt;
|
|
25
|
+
private reconnectTimer;
|
|
26
|
+
private heartbeatTimer;
|
|
27
|
+
private heartbeatPending;
|
|
28
|
+
private readonly pendingRequests;
|
|
29
|
+
private retryHandler;
|
|
30
|
+
private explicitDisconnect;
|
|
31
|
+
/** 当前连接状态 */
|
|
32
|
+
get status(): WKNetworkStatus;
|
|
33
|
+
/** 是否已连接 */
|
|
34
|
+
get connected(): boolean;
|
|
35
|
+
/** 离线队列当前长度 */
|
|
36
|
+
get queueLength(): number;
|
|
37
|
+
setConfig(config: WKNetworkConfig): void;
|
|
38
|
+
/** 更新单项配置 */
|
|
39
|
+
updateConfig(partial: Partial<WKNetworkConfig>): void;
|
|
40
|
+
connect(): void;
|
|
41
|
+
/**
|
|
42
|
+
* 优雅断开连接。
|
|
43
|
+
* @param code 关闭码,默认 1000
|
|
44
|
+
* @param reason 关闭原因
|
|
45
|
+
*/
|
|
46
|
+
disconnect(code?: number, reason?: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* 发送消息并等待响应。
|
|
49
|
+
* @param route 消息路由
|
|
50
|
+
* @param body 消息体
|
|
51
|
+
* @param timeoutMs 超时(ms),默认使用 config.defaultTimeout
|
|
52
|
+
* @returns Promise 解析为响应 body
|
|
53
|
+
*/
|
|
54
|
+
request<T = unknown>(route: string, body: unknown, timeoutMs?: number): Promise<T>;
|
|
55
|
+
/**
|
|
56
|
+
* 发送消息(不等待响应)。
|
|
57
|
+
*/
|
|
58
|
+
send(route: string, body: unknown): void;
|
|
59
|
+
/**
|
|
60
|
+
* 注册重试回调。
|
|
61
|
+
* 自动重连失败超过 maxAttempts 次后调用。
|
|
62
|
+
* 回调接收 (attempts, retry, abort):
|
|
63
|
+
* - attempts: 已失败次数
|
|
64
|
+
* - retry: 调用则再试一次
|
|
65
|
+
* - abort: 放弃连接
|
|
66
|
+
*/
|
|
67
|
+
setRetryHandler(handler: WKRetryHandler | null): void;
|
|
68
|
+
private _doConnect;
|
|
69
|
+
private _onOpen;
|
|
70
|
+
private _onClose;
|
|
71
|
+
private _onMessage;
|
|
72
|
+
private _sendOrQueue;
|
|
73
|
+
private _sendMessage;
|
|
74
|
+
private _flushQueue;
|
|
75
|
+
private _startHeartbeat;
|
|
76
|
+
private _clearHeartbeat;
|
|
77
|
+
private _onHeartbeatTimeout;
|
|
78
|
+
private _scheduleReconnect;
|
|
79
|
+
/** 指数退避 delay 计算 */
|
|
80
|
+
private _reconnectDelay;
|
|
81
|
+
private _onReconnectExhausted;
|
|
82
|
+
private _clearReconnectTimer;
|
|
83
|
+
private _rejectAllPending;
|
|
84
|
+
dispose(): void;
|
|
85
|
+
}
|
|
86
|
+
/** 全局快捷引用 */
|
|
87
|
+
export declare const networkManager: WKNetworkManager;
|
|
88
|
+
//# sourceMappingURL=WKNetworkManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WKNetworkManager.d.ts","sourceRoot":"","sources":["../src/WKNetworkManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,WAAW,EAAqB,MAAM,kBAAkB,CAAC;AAIlE,OAAO,EACL,eAAe,EAEhB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EACV,eAAe,EAEf,cAAc,EAEf,MAAM,YAAY,CAAC;AA2BpB,qBAAa,gBAAiB,SAAQ,WAAW;IAE/C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAqB;IACxC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA4B;IAC7D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwB;IAG9C,OAAO,CAAC,MAAM,CAAoD;IAClE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqC;IACrE,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,kBAAkB,CAAS;IAEnC,aAAa;IACb,IAAI,MAAM,IAAI,eAAe,CAE5B;IAED,YAAY;IACZ,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,eAAe;IACf,IAAI,WAAW,IAAI,MAAM,CAExB;IAID,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAIxC,aAAa;IACb,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI;IAMrD,OAAO,IAAI,IAAI;IAcf;;;;OAIG;IACH,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAWhD;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAwBlF;;OAEG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;IAcxC;;;;;;;OAOG;IACH,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI;IAMrD,OAAO,CAAC,UAAU;IAqBlB,OAAO,CAAC,OAAO;IAef,OAAO,CAAC,QAAQ;IAkBhB,OAAO,CAAC,UAAU;IAkClB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,kBAAkB;IA2B1B,oBAAoB;IACpB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,qBAAqB;IAsB7B,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,iBAAiB;IAUzB,OAAO,IAAI,IAAI;CAKhB;AAED,aAAa;AACb,eAAO,MAAM,cAAc,kBAAyB,CAAC"}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wukong-kit/network — Network Manager
|
|
3
|
+
*
|
|
4
|
+
* 核心单例,管理 WebSocket 连接生命周期、消息收发、
|
|
5
|
+
* 自动重连、心跳保活、请求-应答匹配、离线缓冲。
|
|
6
|
+
*
|
|
7
|
+
* 使用方式:
|
|
8
|
+
* ```ts
|
|
9
|
+
* const net = WKNetworkManager.get();
|
|
10
|
+
* net.setConfig({ serverUrl: 'wss://...', authToken: 'xxx' });
|
|
11
|
+
* net.connect();
|
|
12
|
+
* const data = await net.request('player.login', { uid: 1 });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import { WKSingleton, WKEvent, WKLogger } from '@wukong-kit/core';
|
|
16
|
+
import { WKNetworkQueue } from './WKNetworkQueue.js';
|
|
17
|
+
import { WKProtocolRegistry } from './WKNetworkProtocol.js';
|
|
18
|
+
import { WKWebSocket } from './WKWebSocket.js';
|
|
19
|
+
import { WKNetworkStatus, WKNetworkEventName, } from './types.js';
|
|
20
|
+
// ─── 默认配置 ──────────────────────────────────────
|
|
21
|
+
const DEFAULT_CONFIG = {
|
|
22
|
+
serverUrl: '',
|
|
23
|
+
authToken: '',
|
|
24
|
+
defaultTimeout: 30000,
|
|
25
|
+
heartbeatInterval: 10000,
|
|
26
|
+
heartbeatTimeout: 5000,
|
|
27
|
+
reconnectMaxAttempts: 3,
|
|
28
|
+
reconnectBaseDelay: 1000,
|
|
29
|
+
reconnectMaxDelay: 30000,
|
|
30
|
+
};
|
|
31
|
+
// ─── 内部路由 ──────────────────────────────────────
|
|
32
|
+
const INTERNAL_ROUTE_PING = '@ping';
|
|
33
|
+
const INTERNAL_ROUTE_PONG = '@pong';
|
|
34
|
+
let _globalSeq = 0;
|
|
35
|
+
function nextSeq() {
|
|
36
|
+
return ++_globalSeq;
|
|
37
|
+
}
|
|
38
|
+
// ─── WKNetworkManager ──────────────────────────────
|
|
39
|
+
export class WKNetworkManager extends WKSingleton {
|
|
40
|
+
constructor() {
|
|
41
|
+
super(...arguments);
|
|
42
|
+
// 子系统
|
|
43
|
+
this.ws = new WKWebSocket();
|
|
44
|
+
this.protocolRegistry = new WKProtocolRegistry();
|
|
45
|
+
this.queue = new WKNetworkQueue();
|
|
46
|
+
// 状态
|
|
47
|
+
this.config = { ...DEFAULT_CONFIG };
|
|
48
|
+
this._connected = false;
|
|
49
|
+
this.reconnectAttempt = 0;
|
|
50
|
+
this.reconnectTimer = null;
|
|
51
|
+
this.heartbeatTimer = null;
|
|
52
|
+
this.heartbeatPending = false;
|
|
53
|
+
this.pendingRequests = new Map();
|
|
54
|
+
this.retryHandler = null;
|
|
55
|
+
this.explicitDisconnect = false;
|
|
56
|
+
}
|
|
57
|
+
/** 当前连接状态 */
|
|
58
|
+
get status() {
|
|
59
|
+
return this.ws.status;
|
|
60
|
+
}
|
|
61
|
+
/** 是否已连接 */
|
|
62
|
+
get connected() {
|
|
63
|
+
return this._connected;
|
|
64
|
+
}
|
|
65
|
+
/** 离线队列当前长度 */
|
|
66
|
+
get queueLength() {
|
|
67
|
+
return this.queue.length;
|
|
68
|
+
}
|
|
69
|
+
// ─── 配置 ───────────────────────────────────
|
|
70
|
+
setConfig(config) {
|
|
71
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
72
|
+
}
|
|
73
|
+
/** 更新单项配置 */
|
|
74
|
+
updateConfig(partial) {
|
|
75
|
+
Object.assign(this.config, partial);
|
|
76
|
+
}
|
|
77
|
+
// ─── 连接 / 断开 ─────────────────────────────
|
|
78
|
+
connect() {
|
|
79
|
+
if (this.ws.status === WKNetworkStatus.CONNECTED) {
|
|
80
|
+
WKLogger.warn('[Network] Already connected');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (this.ws.status === WKNetworkStatus.CONNECTING) {
|
|
84
|
+
WKLogger.warn('[Network] Already connecting');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.explicitDisconnect = false;
|
|
88
|
+
this._doConnect();
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 优雅断开连接。
|
|
92
|
+
* @param code 关闭码,默认 1000
|
|
93
|
+
* @param reason 关闭原因
|
|
94
|
+
*/
|
|
95
|
+
disconnect(code, reason) {
|
|
96
|
+
this.explicitDisconnect = true;
|
|
97
|
+
this._clearReconnectTimer();
|
|
98
|
+
this._clearHeartbeat();
|
|
99
|
+
this._rejectAllPending(new Error('Network disconnected'));
|
|
100
|
+
this.ws.disconnect(code, reason);
|
|
101
|
+
this._connected = false;
|
|
102
|
+
}
|
|
103
|
+
// ─── 发送 ───────────────────────────────────
|
|
104
|
+
/**
|
|
105
|
+
* 发送消息并等待响应。
|
|
106
|
+
* @param route 消息路由
|
|
107
|
+
* @param body 消息体
|
|
108
|
+
* @param timeoutMs 超时(ms),默认使用 config.defaultTimeout
|
|
109
|
+
* @returns Promise 解析为响应 body
|
|
110
|
+
*/
|
|
111
|
+
request(route, body, timeoutMs) {
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
const seq = nextSeq();
|
|
114
|
+
const timeout = timeoutMs ?? this.config.defaultTimeout;
|
|
115
|
+
const timer = setTimeout(() => {
|
|
116
|
+
this.pendingRequests.delete(seq);
|
|
117
|
+
reject(new Error(`Request timeout: ${route} (seq=${seq}, timeout=${timeout}ms)`));
|
|
118
|
+
}, timeout);
|
|
119
|
+
this.pendingRequests.set(seq, { route, resolve: resolve, reject, timer });
|
|
120
|
+
const message = {
|
|
121
|
+
route,
|
|
122
|
+
seq,
|
|
123
|
+
body,
|
|
124
|
+
timestamp: Date.now(),
|
|
125
|
+
ack: true,
|
|
126
|
+
};
|
|
127
|
+
this._sendOrQueue(message);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 发送消息(不等待响应)。
|
|
132
|
+
*/
|
|
133
|
+
send(route, body) {
|
|
134
|
+
const seq = nextSeq();
|
|
135
|
+
const message = {
|
|
136
|
+
route,
|
|
137
|
+
seq,
|
|
138
|
+
body,
|
|
139
|
+
timestamp: Date.now(),
|
|
140
|
+
ack: false,
|
|
141
|
+
};
|
|
142
|
+
this._sendOrQueue(message);
|
|
143
|
+
}
|
|
144
|
+
// ─── 重试处理器 ─────────────────────────────
|
|
145
|
+
/**
|
|
146
|
+
* 注册重试回调。
|
|
147
|
+
* 自动重连失败超过 maxAttempts 次后调用。
|
|
148
|
+
* 回调接收 (attempts, retry, abort):
|
|
149
|
+
* - attempts: 已失败次数
|
|
150
|
+
* - retry: 调用则再试一次
|
|
151
|
+
* - abort: 放弃连接
|
|
152
|
+
*/
|
|
153
|
+
setRetryHandler(handler) {
|
|
154
|
+
this.retryHandler = handler;
|
|
155
|
+
}
|
|
156
|
+
// ─── 内部实现 ───────────────────────────────
|
|
157
|
+
_doConnect() {
|
|
158
|
+
let url = this.config.serverUrl;
|
|
159
|
+
// 拼接 auth token
|
|
160
|
+
if (this.config.authToken) {
|
|
161
|
+
const separator = url.includes('?') ? '&' : '?';
|
|
162
|
+
url = `${url}${separator}token=${encodeURIComponent(this.config.authToken)}`;
|
|
163
|
+
}
|
|
164
|
+
this.ws.connect(url, {
|
|
165
|
+
onOpen: () => this._onOpen(),
|
|
166
|
+
onClose: (code, reason) => this._onClose(code, reason),
|
|
167
|
+
onMessage: (data) => this._onMessage(data),
|
|
168
|
+
onError: (payload) => {
|
|
169
|
+
WKLogger.error('[Network]', payload.message);
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
// ─── 事件处理 ───────────────────────────────
|
|
174
|
+
_onOpen() {
|
|
175
|
+
this._connected = true;
|
|
176
|
+
this.reconnectAttempt = 0;
|
|
177
|
+
this._clearReconnectTimer();
|
|
178
|
+
// 发送队列中的离线消息
|
|
179
|
+
this._flushQueue();
|
|
180
|
+
// 启动心跳
|
|
181
|
+
this._startHeartbeat();
|
|
182
|
+
WKLogger.info('[Network] Connected:', this.config.serverUrl);
|
|
183
|
+
WKEvent.emit(WKNetworkEventName.CONNECTED, { url: this.config.serverUrl });
|
|
184
|
+
}
|
|
185
|
+
_onClose(code, reason) {
|
|
186
|
+
const wasConnected = this._connected;
|
|
187
|
+
this._connected = false;
|
|
188
|
+
this._clearHeartbeat();
|
|
189
|
+
WKLogger.warn(`[Network] Disconnected: code=${code}, reason=${reason}`);
|
|
190
|
+
WKEvent.emit(WKNetworkEventName.DISCONNECTED, { code, reason, wasConnected });
|
|
191
|
+
// 拒绝所有挂起的请求
|
|
192
|
+
this._rejectAllPending(new Error(`Connection closed: ${reason || 'unknown'}`));
|
|
193
|
+
// 自动重连(非主动断开时)
|
|
194
|
+
if (!this.explicitDisconnect) {
|
|
195
|
+
this._scheduleReconnect();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
_onMessage(data) {
|
|
199
|
+
try {
|
|
200
|
+
const protocol = this.protocolRegistry.get('json');
|
|
201
|
+
const message = protocol.decode(data);
|
|
202
|
+
// 内部心跳回复
|
|
203
|
+
if (message.route === INTERNAL_ROUTE_PONG) {
|
|
204
|
+
this.heartbeatPending = false;
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
// 应答消息(匹配 pending request)
|
|
208
|
+
if (message.ack && message.ackSeq !== undefined) {
|
|
209
|
+
const pending = this.pendingRequests.get(message.ackSeq);
|
|
210
|
+
if (pending) {
|
|
211
|
+
clearTimeout(pending.timer);
|
|
212
|
+
this.pendingRequests.delete(message.ackSeq);
|
|
213
|
+
pending.resolve(message.body);
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
// 推送消息 → 事件总线
|
|
218
|
+
WKEvent.emit(WKNetworkEventName.PUSH, message);
|
|
219
|
+
// 同时发具体路由事件,方便精确监听
|
|
220
|
+
WKEvent.emit(`wk:net:push:${message.route}`, message.body);
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
WKLogger.error('[Network] Failed to decode message:', err.message);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// ─── 消息发送(在线发送 / 离线入队)────────
|
|
227
|
+
_sendOrQueue(message) {
|
|
228
|
+
if (this._connected) {
|
|
229
|
+
this._sendMessage(message);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
// 仅缓冲需要 ACK 的消息(fire-and-forget 不缓冲)
|
|
233
|
+
if (message.ack) {
|
|
234
|
+
this.queue.push(message.route, message.body, message.seq, this.config.defaultTimeout);
|
|
235
|
+
WKLogger.debug(`[Network] Queued message (offline): ${message.route}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
_sendMessage(message) {
|
|
240
|
+
const protocol = this.protocolRegistry.get('json');
|
|
241
|
+
const encoded = protocol.encode(message);
|
|
242
|
+
const sent = this.ws.send(encoded);
|
|
243
|
+
if (!sent) {
|
|
244
|
+
WKLogger.error('[Network] Failed to send message:', message.route);
|
|
245
|
+
// 发送失败也入队
|
|
246
|
+
if (message.ack) {
|
|
247
|
+
this.queue.push(message.route, message.body, message.seq, this.config.defaultTimeout);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
_flushQueue() {
|
|
252
|
+
if (this.queue.isEmpty)
|
|
253
|
+
return;
|
|
254
|
+
const messages = this.queue.flushAll();
|
|
255
|
+
WKLogger.info(`[Network] Flushing ${messages.length} queued messages`);
|
|
256
|
+
for (const msg of messages) {
|
|
257
|
+
this._sendMessage({
|
|
258
|
+
route: msg.route,
|
|
259
|
+
seq: msg.seq,
|
|
260
|
+
body: msg.body,
|
|
261
|
+
timestamp: Date.now(),
|
|
262
|
+
ack: true,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// ─── 心跳 ──────────────────────────────────
|
|
267
|
+
_startHeartbeat() {
|
|
268
|
+
this._clearHeartbeat();
|
|
269
|
+
this.heartbeatPending = false;
|
|
270
|
+
this.heartbeatTimer = setInterval(() => {
|
|
271
|
+
if (!this._connected)
|
|
272
|
+
return;
|
|
273
|
+
// 上次心跳还没回复 → 超时
|
|
274
|
+
if (this.heartbeatPending) {
|
|
275
|
+
WKLogger.warn('[Network] Heartbeat timeout, reconnecting...');
|
|
276
|
+
this._onHeartbeatTimeout();
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
this.heartbeatPending = true;
|
|
280
|
+
this._sendMessage({
|
|
281
|
+
route: INTERNAL_ROUTE_PING,
|
|
282
|
+
seq: nextSeq(),
|
|
283
|
+
body: {},
|
|
284
|
+
timestamp: Date.now(),
|
|
285
|
+
ack: false,
|
|
286
|
+
});
|
|
287
|
+
// 单次心跳超时检测
|
|
288
|
+
setTimeout(() => {
|
|
289
|
+
if (this.heartbeatPending && this._connected) {
|
|
290
|
+
WKLogger.warn('[Network] Heartbeat timeout, reconnecting...');
|
|
291
|
+
this._onHeartbeatTimeout();
|
|
292
|
+
}
|
|
293
|
+
}, this.config.heartbeatTimeout);
|
|
294
|
+
}, this.config.heartbeatInterval);
|
|
295
|
+
}
|
|
296
|
+
_clearHeartbeat() {
|
|
297
|
+
if (this.heartbeatTimer !== null) {
|
|
298
|
+
clearInterval(this.heartbeatTimer);
|
|
299
|
+
this.heartbeatTimer = null;
|
|
300
|
+
}
|
|
301
|
+
this.heartbeatPending = false;
|
|
302
|
+
}
|
|
303
|
+
_onHeartbeatTimeout() {
|
|
304
|
+
this._clearHeartbeat();
|
|
305
|
+
this._connected = false;
|
|
306
|
+
// 强制关闭当前连接
|
|
307
|
+
this.ws.disconnect(4001, 'Heartbeat timeout');
|
|
308
|
+
// 拒绝所有挂起请求
|
|
309
|
+
this._rejectAllPending(new Error('Heartbeat timeout'));
|
|
310
|
+
// 触发重连
|
|
311
|
+
this._scheduleReconnect();
|
|
312
|
+
}
|
|
313
|
+
// ─── 重连 ──────────────────────────────────
|
|
314
|
+
_scheduleReconnect() {
|
|
315
|
+
if (this.explicitDisconnect)
|
|
316
|
+
return;
|
|
317
|
+
// 清除已有重连定时器
|
|
318
|
+
this._clearReconnectTimer();
|
|
319
|
+
this.reconnectAttempt++;
|
|
320
|
+
if (this.reconnectAttempt <= this.config.reconnectMaxAttempts) {
|
|
321
|
+
// 自动重试
|
|
322
|
+
const delay = this._reconnectDelay(this.reconnectAttempt);
|
|
323
|
+
WKLogger.info(`[Network] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempt}/${this.config.reconnectMaxAttempts})`);
|
|
324
|
+
WKEvent.emit(WKNetworkEventName.RECONNECTING, {
|
|
325
|
+
attempt: this.reconnectAttempt,
|
|
326
|
+
delay,
|
|
327
|
+
});
|
|
328
|
+
this.reconnectTimer = setTimeout(() => {
|
|
329
|
+
this._doConnect();
|
|
330
|
+
}, delay);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
// 超过最大自动重试次数 → 调用 retry handler
|
|
334
|
+
this._onReconnectExhausted();
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/** 指数退避 delay 计算 */
|
|
338
|
+
_reconnectDelay(attempt) {
|
|
339
|
+
const delay = this.config.reconnectBaseDelay * Math.pow(2, attempt - 1);
|
|
340
|
+
return Math.min(delay, this.config.reconnectMaxDelay);
|
|
341
|
+
}
|
|
342
|
+
_onReconnectExhausted() {
|
|
343
|
+
WKLogger.warn(`[Network] Reconnect exhausted after ${this.reconnectAttempt} attempts`);
|
|
344
|
+
if (this.retryHandler) {
|
|
345
|
+
this.retryHandler(this.reconnectAttempt,
|
|
346
|
+
// retry — 立即再试一次
|
|
347
|
+
() => {
|
|
348
|
+
WKLogger.info('[Network] Manual retry triggered by handler');
|
|
349
|
+
this.reconnectAttempt = 0; // 不重置也行,但为了指数退避从 1 开始,这里重置
|
|
350
|
+
this._scheduleReconnect();
|
|
351
|
+
},
|
|
352
|
+
// abort — 放弃重连
|
|
353
|
+
() => {
|
|
354
|
+
WKLogger.info('[Network] Reconnect aborted by handler');
|
|
355
|
+
this._clearReconnectTimer();
|
|
356
|
+
this.reconnectAttempt = 0;
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
_clearReconnectTimer() {
|
|
361
|
+
if (this.reconnectTimer !== null) {
|
|
362
|
+
clearTimeout(this.reconnectTimer);
|
|
363
|
+
this.reconnectTimer = null;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// ─── 辅助 ──────────────────────────────────
|
|
367
|
+
_rejectAllPending(reason) {
|
|
368
|
+
for (const [seq, pending] of this.pendingRequests) {
|
|
369
|
+
clearTimeout(pending.timer);
|
|
370
|
+
pending.reject(reason);
|
|
371
|
+
}
|
|
372
|
+
this.pendingRequests.clear();
|
|
373
|
+
}
|
|
374
|
+
// ─── WKSingleton ───────────────────────────
|
|
375
|
+
dispose() {
|
|
376
|
+
this.disconnect(1001, 'App shutting down');
|
|
377
|
+
this.retryHandler = null;
|
|
378
|
+
WKNetworkManager.clear();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/** 全局快捷引用 */
|
|
382
|
+
export const networkManager = WKNetworkManager.get();
|
|
383
|
+
//# sourceMappingURL=WKNetworkManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WKNetworkManager.js","sourceRoot":"","sources":["../src/WKNetworkManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wukong-kit/network — Protocol abstraction & JSON implementation
|
|
3
|
+
*/
|
|
4
|
+
import type { WKMessage, WKProtocol } from './types.js';
|
|
5
|
+
export declare abstract class WKBaseProtocol implements WKProtocol {
|
|
6
|
+
abstract encode(message: WKMessage): string | ArrayBuffer;
|
|
7
|
+
abstract decode(data: string | ArrayBuffer): WKMessage;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 内置 JSON 序列化协议。
|
|
11
|
+
* 消息按 JSON.stringify / JSON.parse 编解码。
|
|
12
|
+
*/
|
|
13
|
+
export declare class WKJsonProtocol extends WKBaseProtocol {
|
|
14
|
+
encode(message: WKMessage): string;
|
|
15
|
+
decode(data: string | ArrayBuffer): WKMessage;
|
|
16
|
+
private _bufferToString;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 管理多种协议实现,按名称查找。
|
|
20
|
+
* 内置注册 "json" 协议。
|
|
21
|
+
*/
|
|
22
|
+
export declare class WKProtocolRegistry {
|
|
23
|
+
private readonly protocols;
|
|
24
|
+
constructor();
|
|
25
|
+
/** 注册自定义协议 */
|
|
26
|
+
register(name: string, protocol: WKProtocol): void;
|
|
27
|
+
/** 获取协议 */
|
|
28
|
+
get(name: string): WKProtocol;
|
|
29
|
+
/** 是否已注册 */
|
|
30
|
+
has(name: string): boolean;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=WKNetworkProtocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WKNetworkProtocol.d.ts","sourceRoot":"","sources":["../src/WKNetworkProtocol.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIxD,8BAAsB,cAAe,YAAW,UAAU;IACxD,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,GAAG,WAAW;IACzD,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;CACvD;AAID;;;GAGG;AACH,qBAAa,cAAe,SAAQ,cAAc;IAChD,MAAM,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM;IAIlC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAY7C,OAAO,CAAC,eAAe;CAIxB;AAID;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;;IAM3D,cAAc;IACd,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,GAAG,IAAI;IAOlD,WAAW;IACX,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU;IAQ7B,YAAY;IACZ,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAG3B"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wukong-kit/network — Protocol abstraction & JSON implementation
|
|
3
|
+
*/
|
|
4
|
+
// ─── 抽象基类(方便子类继承)─────────────────────
|
|
5
|
+
export class WKBaseProtocol {
|
|
6
|
+
}
|
|
7
|
+
// ─── JSON 协议(默认实现)────────────────────────
|
|
8
|
+
/**
|
|
9
|
+
* 内置 JSON 序列化协议。
|
|
10
|
+
* 消息按 JSON.stringify / JSON.parse 编解码。
|
|
11
|
+
*/
|
|
12
|
+
export class WKJsonProtocol extends WKBaseProtocol {
|
|
13
|
+
encode(message) {
|
|
14
|
+
return JSON.stringify(message);
|
|
15
|
+
}
|
|
16
|
+
decode(data) {
|
|
17
|
+
const text = typeof data === 'string' ? data : this._bufferToString(data);
|
|
18
|
+
const parsed = JSON.parse(text);
|
|
19
|
+
// 校验必要字段
|
|
20
|
+
if (typeof parsed.route !== 'string' || typeof parsed.seq !== 'number') {
|
|
21
|
+
throw new Error(`Invalid WKMessage: missing route or seq`);
|
|
22
|
+
}
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
_bufferToString(buf) {
|
|
26
|
+
const bytes = new Uint8Array(buf);
|
|
27
|
+
return new TextDecoder().decode(bytes);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// ─── 协议注册中心 ────────────────────────────────
|
|
31
|
+
/**
|
|
32
|
+
* 管理多种协议实现,按名称查找。
|
|
33
|
+
* 内置注册 "json" 协议。
|
|
34
|
+
*/
|
|
35
|
+
export class WKProtocolRegistry {
|
|
36
|
+
constructor() {
|
|
37
|
+
this.protocols = new Map();
|
|
38
|
+
this.protocols.set('json', new WKJsonProtocol());
|
|
39
|
+
}
|
|
40
|
+
/** 注册自定义协议 */
|
|
41
|
+
register(name, protocol) {
|
|
42
|
+
if (this.protocols.has(name)) {
|
|
43
|
+
throw new Error(`Protocol already registered: ${name}`);
|
|
44
|
+
}
|
|
45
|
+
this.protocols.set(name, protocol);
|
|
46
|
+
}
|
|
47
|
+
/** 获取协议 */
|
|
48
|
+
get(name) {
|
|
49
|
+
const protocol = this.protocols.get(name);
|
|
50
|
+
if (!protocol) {
|
|
51
|
+
throw new Error(`Protocol not found: ${name}`);
|
|
52
|
+
}
|
|
53
|
+
return protocol;
|
|
54
|
+
}
|
|
55
|
+
/** 是否已注册 */
|
|
56
|
+
has(name) {
|
|
57
|
+
return this.protocols.has(name);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=WKNetworkProtocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WKNetworkProtocol.js","sourceRoot":"","sources":["../src/WKNetworkProtocol.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wCAAwC;AAExC,MAAM,OAAgB,cAAc;CAGnC;AAED,4CAA4C;AAE5C;;;GAGG;AACH,MAAM,OAAO,cAAe,SAAQ,cAAc;IAChD,MAAM,CAAC,OAAkB;QACvB,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,CAAC,IAA0B;QAC/B,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAc,CAAC;QAE7C,SAAS;QACT,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;YACvE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,eAAe,CAAC,GAAgB;QACtC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;CACF;AAED,8CAA8C;AAE9C;;;GAGG;AACH,MAAM,OAAO,kBAAkB;IAG7B;QAFiB,cAAS,GAAG,IAAI,GAAG,EAAsB,CAAC;QAGzD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,cAAc,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,cAAc;IACd,QAAQ,CAAC,IAAY,EAAE,QAAoB;QACzC,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED,WAAW;IACX,GAAG,CAAC,IAAY;QACd,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,YAAY;IACZ,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;CACF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wukong-kit/network — Offline message queue
|
|
3
|
+
*
|
|
4
|
+
* 断线时缓冲待发消息,恢复后按序重发。
|
|
5
|
+
* 每条消息可选 TTL,超过时间未发送则丢弃。
|
|
6
|
+
*/
|
|
7
|
+
export interface WKQueuedMessage {
|
|
8
|
+
/** 路由 */
|
|
9
|
+
route: string;
|
|
10
|
+
/** 消息体 */
|
|
11
|
+
body: unknown;
|
|
12
|
+
/** 消息序列号 */
|
|
13
|
+
seq: number;
|
|
14
|
+
/** 入队时间戳(ms) */
|
|
15
|
+
timestamp: number;
|
|
16
|
+
/** TTL(ms),超时丢弃,0 表示不过期 */
|
|
17
|
+
ttl: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class WKNetworkQueue {
|
|
20
|
+
private queue;
|
|
21
|
+
/** 当前队列长度 */
|
|
22
|
+
get length(): number;
|
|
23
|
+
/** 是否为空 */
|
|
24
|
+
get isEmpty(): boolean;
|
|
25
|
+
push(route: string, body: unknown, seq: number, ttl?: number): void;
|
|
26
|
+
/**
|
|
27
|
+
* 取出队列中所有未过期的消息,清空队列。
|
|
28
|
+
* 过期的消息被丢弃,不计入返回结果。
|
|
29
|
+
* @returns 未过期的消息列表
|
|
30
|
+
*/
|
|
31
|
+
flushAll(): WKQueuedMessage[];
|
|
32
|
+
clear(): void;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=WKNetworkQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WKNetworkQueue.d.ts","sourceRoot":"","sources":["../src/WKNetworkQueue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,eAAe;IAC9B,SAAS;IACT,KAAK,EAAE,MAAM,CAAC;IACd,UAAU;IACV,IAAI,EAAE,OAAO,CAAC;IACd,YAAY;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAyB;IAEtC,aAAa;IACb,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,WAAW;IACX,IAAI,OAAO,IAAI,OAAO,CAErB;IAID,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,SAAI,GAAG,IAAI;IAY9D;;;;OAIG;IACH,QAAQ,IAAI,eAAe,EAAE;IAwB7B,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wukong-kit/network — Offline message queue
|
|
3
|
+
*
|
|
4
|
+
* 断线时缓冲待发消息,恢复后按序重发。
|
|
5
|
+
* 每条消息可选 TTL,超过时间未发送则丢弃。
|
|
6
|
+
*/
|
|
7
|
+
export class WKNetworkQueue {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.queue = [];
|
|
10
|
+
}
|
|
11
|
+
/** 当前队列长度 */
|
|
12
|
+
get length() {
|
|
13
|
+
return this.queue.length;
|
|
14
|
+
}
|
|
15
|
+
/** 是否为空 */
|
|
16
|
+
get isEmpty() {
|
|
17
|
+
return this.queue.length === 0;
|
|
18
|
+
}
|
|
19
|
+
// ─── 入队 ───────────────────────────────────
|
|
20
|
+
push(route, body, seq, ttl = 0) {
|
|
21
|
+
this.queue.push({
|
|
22
|
+
route,
|
|
23
|
+
body,
|
|
24
|
+
seq,
|
|
25
|
+
timestamp: Date.now(),
|
|
26
|
+
ttl,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// ─── 取出全部(并清除过期消息)──────────────
|
|
30
|
+
/**
|
|
31
|
+
* 取出队列中所有未过期的消息,清空队列。
|
|
32
|
+
* 过期的消息被丢弃,不计入返回结果。
|
|
33
|
+
* @returns 未过期的消息列表
|
|
34
|
+
*/
|
|
35
|
+
flushAll() {
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
const valid = [];
|
|
38
|
+
const expired = [];
|
|
39
|
+
for (const msg of this.queue) {
|
|
40
|
+
if (msg.ttl > 0 && now - msg.timestamp > msg.ttl) {
|
|
41
|
+
expired.push(msg);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
valid.push(msg);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
this.queue = [];
|
|
48
|
+
if (expired.length > 0) {
|
|
49
|
+
console.warn(`[WKNetworkQueue] Discarded ${expired.length} expired messages`);
|
|
50
|
+
}
|
|
51
|
+
return valid;
|
|
52
|
+
}
|
|
53
|
+
// ─── 清空 ───────────────────────────────────
|
|
54
|
+
clear() {
|
|
55
|
+
this.queue.length = 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=WKNetworkQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WKNetworkQueue.js","sourceRoot":"","sources":["../src/WKNetworkQueue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,MAAM,OAAO,cAAc;IAA3B;QACU,UAAK,GAAsB,EAAE,CAAC;IA0DxC,CAAC;IAxDC,aAAa;IACb,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,WAAW;IACX,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,6CAA6C;IAE7C,IAAI,CAAC,KAAa,EAAE,IAAa,EAAE,GAAW,EAAE,GAAG,GAAG,CAAC;QACrD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YACd,KAAK;YACL,IAAI;YACJ,GAAG;YACH,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,GAAG;SACJ,CAAC,CAAC;IACL,CAAC;IAED,kCAAkC;IAElC;;;;OAIG;IACH,QAAQ;QACN,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAsB,EAAE,CAAC;QACpC,MAAM,OAAO,GAAsB,EAAE,CAAC;QAEtC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC;gBACjD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACpB,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAEhB,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,IAAI,CAAC,8BAA8B,OAAO,CAAC,MAAM,mBAAmB,CAAC,CAAC;QAChF,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,6CAA6C;IAE7C,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACxB,CAAC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wukong-kit/network — WebSocket transport layer
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper around the native WebSocket API.
|
|
5
|
+
* Manages connection state (connected/disconnected/connecting).
|
|
6
|
+
* Does NOT handle reconnect or heartbeat — those belong in WKNetworkManager.
|
|
7
|
+
*/
|
|
8
|
+
import { WKNetworkStatus } from './types.js';
|
|
9
|
+
import type { WKNetworkErrorPayload } from './types.js';
|
|
10
|
+
export type WKWebSocketEventHandler = {
|
|
11
|
+
onOpen: () => void;
|
|
12
|
+
onClose: (code: number, reason: string) => void;
|
|
13
|
+
onMessage: (data: string | ArrayBuffer) => void;
|
|
14
|
+
onError: (payload: WKNetworkErrorPayload) => void;
|
|
15
|
+
};
|
|
16
|
+
export declare class WKWebSocket {
|
|
17
|
+
private ws;
|
|
18
|
+
private _status;
|
|
19
|
+
/** 当前 WebSocket URL(含 token) */
|
|
20
|
+
private _url;
|
|
21
|
+
get status(): WKNetworkStatus;
|
|
22
|
+
get url(): string;
|
|
23
|
+
connect(url: string, handlers: WKWebSocketEventHandler): void;
|
|
24
|
+
disconnect(code?: number, reason?: string): void;
|
|
25
|
+
send(data: string | ArrayBuffer): boolean;
|
|
26
|
+
private _setStatus;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=WKWebSocket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WKWebSocket.d.ts","sourceRoot":"","sources":["../src/WKWebSocket.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,KAAK,IAAI,CAAC;IAChD,OAAO,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACnD,CAAC;AAEF,qBAAa,WAAW;IACtB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,OAAO,CAAiD;IAEhE,gCAAgC;IAChC,OAAO,CAAC,IAAI,CAAM;IAElB,IAAI,MAAM,IAAI,eAAe,CAE5B;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IAID,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,uBAAuB,GAAG,IAAI;IA0C7D,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAsBhD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO;IAezC,OAAO,CAAC,UAAU;CAGnB"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wukong-kit/network — WebSocket transport layer
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper around the native WebSocket API.
|
|
5
|
+
* Manages connection state (connected/disconnected/connecting).
|
|
6
|
+
* Does NOT handle reconnect or heartbeat — those belong in WKNetworkManager.
|
|
7
|
+
*/
|
|
8
|
+
import { WKNetworkStatus } from './types.js';
|
|
9
|
+
export class WKWebSocket {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.ws = null;
|
|
12
|
+
this._status = WKNetworkStatus.DISCONNECTED;
|
|
13
|
+
/** 当前 WebSocket URL(含 token) */
|
|
14
|
+
this._url = '';
|
|
15
|
+
}
|
|
16
|
+
get status() {
|
|
17
|
+
return this._status;
|
|
18
|
+
}
|
|
19
|
+
get url() {
|
|
20
|
+
return this._url;
|
|
21
|
+
}
|
|
22
|
+
// ─── 连接 / 断开 ─────────────────────────────
|
|
23
|
+
connect(url, handlers) {
|
|
24
|
+
if (this._status === WKNetworkStatus.CONNECTING || this._status === WKNetworkStatus.CONNECTED) {
|
|
25
|
+
throw new Error(`WebSocket already ${this._status}`);
|
|
26
|
+
}
|
|
27
|
+
this._url = url;
|
|
28
|
+
this._setStatus(WKNetworkStatus.CONNECTING);
|
|
29
|
+
try {
|
|
30
|
+
this.ws = new WebSocket(url);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
this._setStatus(WKNetworkStatus.DISCONNECTED);
|
|
34
|
+
handlers.onError({
|
|
35
|
+
message: `Failed to create WebSocket: ${err.message}`,
|
|
36
|
+
});
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this.ws.binaryType = 'arraybuffer';
|
|
40
|
+
this.ws.onopen = () => {
|
|
41
|
+
this._setStatus(WKNetworkStatus.CONNECTED);
|
|
42
|
+
handlers.onOpen();
|
|
43
|
+
};
|
|
44
|
+
this.ws.onclose = (event) => {
|
|
45
|
+
this._setStatus(WKNetworkStatus.DISCONNECTED);
|
|
46
|
+
handlers.onClose(event.code, event.reason);
|
|
47
|
+
};
|
|
48
|
+
this.ws.onmessage = (event) => {
|
|
49
|
+
handlers.onMessage(event.data);
|
|
50
|
+
};
|
|
51
|
+
this.ws.onerror = () => {
|
|
52
|
+
// onerror 之后通常会触发 onclose,所以这里只抛事件、不改变状态
|
|
53
|
+
handlers.onError({
|
|
54
|
+
message: 'WebSocket error occurred',
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
disconnect(code, reason) {
|
|
59
|
+
if (!this.ws || this._status === WKNetworkStatus.DISCONNECTED)
|
|
60
|
+
return;
|
|
61
|
+
this._setStatus(WKNetworkStatus.DISCONNECTING);
|
|
62
|
+
// 移除事件监听,避免 disconnect 触发的 onclose 被外部处理
|
|
63
|
+
const ws = this.ws;
|
|
64
|
+
ws.onclose = null;
|
|
65
|
+
ws.onerror = null;
|
|
66
|
+
ws.onmessage = null;
|
|
67
|
+
ws.onopen = null;
|
|
68
|
+
try {
|
|
69
|
+
ws.close(code ?? 1000, reason ?? 'Client closed');
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// ignore
|
|
73
|
+
}
|
|
74
|
+
this.ws = null;
|
|
75
|
+
this._setStatus(WKNetworkStatus.DISCONNECTED);
|
|
76
|
+
}
|
|
77
|
+
// ─── 发送 ───────────────────────────────────
|
|
78
|
+
send(data) {
|
|
79
|
+
if (this._status !== WKNetworkStatus.CONNECTED || !this.ws) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
this.ws.send(data);
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// ─── 状态管理 ───────────────────────────────
|
|
91
|
+
_setStatus(status) {
|
|
92
|
+
this._status = status;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=WKWebSocket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WKWebSocket.js","sourceRoot":"","sources":["../src/WKWebSocket.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wukong-kit/network
|
|
3
|
+
*
|
|
4
|
+
* WebSocket-based network layer for Wukong Kit.
|
|
5
|
+
*
|
|
6
|
+
* ## 快速开始
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { WKNetworkManager, networkManager } from '@wukong-kit/network';
|
|
10
|
+
* import { App } from '@wukong-kit/core';
|
|
11
|
+
*
|
|
12
|
+
* // 配置
|
|
13
|
+
* networkManager.setConfig({
|
|
14
|
+
* serverUrl: 'wss://game.example.com/ws',
|
|
15
|
+
* authToken: 'jwt-token-here',
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // 注册重试回调(3次自动失败后弹窗询问)
|
|
19
|
+
* networkManager.setRetryHandler((attempts, retry, abort) => {
|
|
20
|
+
* // 显示确认框,玩家点击 "重试" 调用 retry(),"取消" 调用 abort()
|
|
21
|
+
* retry();
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // 启动 App
|
|
25
|
+
* App.start({ name: 'MyGame' });
|
|
26
|
+
*
|
|
27
|
+
* // 连接服务器
|
|
28
|
+
* networkManager.connect();
|
|
29
|
+
*
|
|
30
|
+
* // 请求-应答
|
|
31
|
+
* const playerData = await networkManager.request('player.login', { uid: 1 });
|
|
32
|
+
*
|
|
33
|
+
* // 发送(不等待)
|
|
34
|
+
* networkManager.send('player.move', { x: 10, y: 20 });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export { WKNetworkManager, networkManager } from './WKNetworkManager.js';
|
|
38
|
+
export { WKWebSocket } from './WKWebSocket.js';
|
|
39
|
+
export type { WKWebSocketEventHandler } from './WKWebSocket.js';
|
|
40
|
+
export { WKNetworkQueue } from './WKNetworkQueue.js';
|
|
41
|
+
export type { WKQueuedMessage } from './WKNetworkQueue.js';
|
|
42
|
+
export { WKBaseProtocol, WKJsonProtocol, WKProtocolRegistry, } from './WKNetworkProtocol.js';
|
|
43
|
+
export { WKNetworkStatus, WKNetworkEventName, } from './types.js';
|
|
44
|
+
export type { WKNetworkConfig, WKMessage, WKProtocol, WKRetryHandler, WKNetworkConnectedPayload, WKNetworkDisconnectedPayload, WKNetworkReconnectingPayload, WKNetworkErrorPayload, } from './types.js';
|
|
45
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACL,cAAc,EACd,cAAc,EACd,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,eAAe,EACf,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,eAAe,EACf,SAAS,EACT,UAAU,EACV,cAAc,EACd,yBAAyB,EACzB,4BAA4B,EAC5B,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wukong-kit/network
|
|
3
|
+
*
|
|
4
|
+
* WebSocket-based network layer for Wukong Kit.
|
|
5
|
+
*
|
|
6
|
+
* ## 快速开始
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { WKNetworkManager, networkManager } from '@wukong-kit/network';
|
|
10
|
+
* import { App } from '@wukong-kit/core';
|
|
11
|
+
*
|
|
12
|
+
* // 配置
|
|
13
|
+
* networkManager.setConfig({
|
|
14
|
+
* serverUrl: 'wss://game.example.com/ws',
|
|
15
|
+
* authToken: 'jwt-token-here',
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* // 注册重试回调(3次自动失败后弹窗询问)
|
|
19
|
+
* networkManager.setRetryHandler((attempts, retry, abort) => {
|
|
20
|
+
* // 显示确认框,玩家点击 "重试" 调用 retry(),"取消" 调用 abort()
|
|
21
|
+
* retry();
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // 启动 App
|
|
25
|
+
* App.start({ name: 'MyGame' });
|
|
26
|
+
*
|
|
27
|
+
* // 连接服务器
|
|
28
|
+
* networkManager.connect();
|
|
29
|
+
*
|
|
30
|
+
* // 请求-应答
|
|
31
|
+
* const playerData = await networkManager.request('player.login', { uid: 1 });
|
|
32
|
+
*
|
|
33
|
+
* // 发送(不等待)
|
|
34
|
+
* networkManager.send('player.move', { x: 10, y: 20 });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export { WKNetworkManager, networkManager } from './WKNetworkManager.js';
|
|
38
|
+
export { WKWebSocket } from './WKWebSocket.js';
|
|
39
|
+
export { WKNetworkQueue } from './WKNetworkQueue.js';
|
|
40
|
+
export { WKBaseProtocol, WKJsonProtocol, WKProtocolRegistry, } from './WKNetworkProtocol.js';
|
|
41
|
+
export { WKNetworkStatus, WKNetworkEventName, } from './types.js';
|
|
42
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EACL,cAAc,EACd,cAAc,EACd,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,eAAe,EACf,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wukong-kit/network — Type definitions
|
|
3
|
+
*/
|
|
4
|
+
export declare enum WKNetworkStatus {
|
|
5
|
+
DISCONNECTED = "disconnected",
|
|
6
|
+
CONNECTING = "connecting",
|
|
7
|
+
CONNECTED = "connected",
|
|
8
|
+
DISCONNECTING = "disconnecting"
|
|
9
|
+
}
|
|
10
|
+
export interface WKMessage {
|
|
11
|
+
/** 路由,e.g. "player.login" */
|
|
12
|
+
route: string;
|
|
13
|
+
/** 递增序列号,用于请求-应答匹配 */
|
|
14
|
+
seq: number;
|
|
15
|
+
/** 业务荷载 */
|
|
16
|
+
body: unknown;
|
|
17
|
+
/** 时间戳(ms) */
|
|
18
|
+
timestamp: number;
|
|
19
|
+
/** 是否需要 ACK / 是否应答消息 */
|
|
20
|
+
ack: boolean;
|
|
21
|
+
/** [应答专用] 对应请求的 seq */
|
|
22
|
+
ackSeq?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface WKNetworkConfig {
|
|
25
|
+
/** WebSocket 服务器地址,e.g. "wss://game.example.com/ws" */
|
|
26
|
+
serverUrl: string;
|
|
27
|
+
/** 鉴权 token,自动拼接到 serverUrl 的 query 参数 ?token=xxx */
|
|
28
|
+
authToken?: string;
|
|
29
|
+
/** 请求超时时间(ms),默认 30000 */
|
|
30
|
+
defaultTimeout?: number;
|
|
31
|
+
/** 心跳间隔(ms),默认 10000 */
|
|
32
|
+
heartbeatInterval?: number;
|
|
33
|
+
/** 心跳超时时间(ms),默认 5000 */
|
|
34
|
+
heartbeatTimeout?: number;
|
|
35
|
+
/** 最大自动重联次数,默认 3,之后触发 retryHandler */
|
|
36
|
+
reconnectMaxAttempts?: number;
|
|
37
|
+
/** 重连基础间隔(ms),默认 1000,指数退避 */
|
|
38
|
+
reconnectBaseDelay?: number;
|
|
39
|
+
/** 重连最大间隔(ms),默认 30000 */
|
|
40
|
+
reconnectMaxDelay?: number;
|
|
41
|
+
}
|
|
42
|
+
export interface WKProtocol {
|
|
43
|
+
/** 消息编码 → 可发送数据 */
|
|
44
|
+
encode(message: WKMessage): string | ArrayBuffer;
|
|
45
|
+
/** 接收数据解码 → 消息 */
|
|
46
|
+
decode(data: string | ArrayBuffer): WKMessage;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 自动重连全部失败后调用。
|
|
50
|
+
* @param attempts 已失败次数(从 3 开始计数)
|
|
51
|
+
* @param retry 调用则立即再试一次
|
|
52
|
+
* @param abort 放弃重连
|
|
53
|
+
*/
|
|
54
|
+
export type WKRetryHandler = (attempts: number, retry: () => void, abort: () => void) => void;
|
|
55
|
+
export declare const WKNetworkEventName: {
|
|
56
|
+
/** 连接已建立 */
|
|
57
|
+
readonly CONNECTED: "wk:net:connected";
|
|
58
|
+
/** 连接已断开 */
|
|
59
|
+
readonly DISCONNECTED: "wk:net:disconnected";
|
|
60
|
+
/** 正在重连 */
|
|
61
|
+
readonly RECONNECTING: "wk:net:reconnecting";
|
|
62
|
+
/** 收到推送消息,payload: WKMessage */
|
|
63
|
+
readonly PUSH: "wk:net:push";
|
|
64
|
+
/** 网络错误 */
|
|
65
|
+
readonly ERROR: "wk:net:error";
|
|
66
|
+
};
|
|
67
|
+
/** 挂起的请求,用于 request/response 匹配 */
|
|
68
|
+
export interface PendingRequest {
|
|
69
|
+
route: string;
|
|
70
|
+
resolve: (data: unknown) => void;
|
|
71
|
+
reject: (reason: Error) => void;
|
|
72
|
+
timer: ReturnType<typeof setTimeout>;
|
|
73
|
+
}
|
|
74
|
+
export interface WKNetworkConnectedPayload {
|
|
75
|
+
url: string;
|
|
76
|
+
}
|
|
77
|
+
export interface WKNetworkDisconnectedPayload {
|
|
78
|
+
code: number;
|
|
79
|
+
reason: string;
|
|
80
|
+
wasConnected: boolean;
|
|
81
|
+
}
|
|
82
|
+
export interface WKNetworkReconnectingPayload {
|
|
83
|
+
attempt: number;
|
|
84
|
+
delay: number;
|
|
85
|
+
}
|
|
86
|
+
export interface WKNetworkErrorPayload {
|
|
87
|
+
message: string;
|
|
88
|
+
code?: number;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,oBAAY,eAAe;IACzB,YAAY,iBAAiB;IAC7B,UAAU,eAAe;IACzB,SAAS,cAAc;IACvB,aAAa,kBAAkB;CAChC;AAID,MAAM,WAAW,SAAS;IACxB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW;IACX,IAAI,EAAE,OAAO,CAAC;IACd,cAAc;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,GAAG,EAAE,OAAO,CAAC;IACb,uBAAuB;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,MAAM,WAAW,eAAe;IAC9B,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wBAAwB;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yBAAyB;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sCAAsC;IACtC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,8BAA8B;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0BAA0B;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAID,MAAM,WAAW,UAAU;IACzB,mBAAmB;IACnB,MAAM,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC;IACjD,kBAAkB;IAClB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;CAC/C;AAID;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,CAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,IAAI,EACjB,KAAK,EAAE,MAAM,IAAI,KACd,IAAI,CAAC;AAIV,eAAO,MAAM,kBAAkB;IAC7B,YAAY;;IAEZ,YAAY;;IAEZ,WAAW;;IAEX,gCAAgC;;IAEhC,WAAW;;CAEH,CAAC;AAIX,mCAAmC;AACnC,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC,MAAM,EAAE,CAAC,MAAM,EAAE,KAAK,KAAK,IAAI,CAAC;IAChC,KAAK,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;CACtC;AAID,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wukong-kit/network — Type definitions
|
|
3
|
+
*/
|
|
4
|
+
// ─── 网络状态 ────────────────────────────────────────
|
|
5
|
+
export var WKNetworkStatus;
|
|
6
|
+
(function (WKNetworkStatus) {
|
|
7
|
+
WKNetworkStatus["DISCONNECTED"] = "disconnected";
|
|
8
|
+
WKNetworkStatus["CONNECTING"] = "connecting";
|
|
9
|
+
WKNetworkStatus["CONNECTED"] = "connected";
|
|
10
|
+
WKNetworkStatus["DISCONNECTING"] = "disconnecting";
|
|
11
|
+
})(WKNetworkStatus || (WKNetworkStatus = {}));
|
|
12
|
+
// ─── 网络事件名 ─────────────────────────────────────
|
|
13
|
+
export const WKNetworkEventName = {
|
|
14
|
+
/** 连接已建立 */
|
|
15
|
+
CONNECTED: 'wk:net:connected',
|
|
16
|
+
/** 连接已断开 */
|
|
17
|
+
DISCONNECTED: 'wk:net:disconnected',
|
|
18
|
+
/** 正在重连 */
|
|
19
|
+
RECONNECTING: 'wk:net:reconnecting',
|
|
20
|
+
/** 收到推送消息,payload: WKMessage */
|
|
21
|
+
PUSH: 'wk:net:push',
|
|
22
|
+
/** 网络错误 */
|
|
23
|
+
ERROR: 'wk:net:error',
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,oDAAoD;AAEpD,MAAM,CAAN,IAAY,eAKX;AALD,WAAY,eAAe;IACzB,gDAA6B,CAAA;IAC7B,4CAAyB,CAAA;IACzB,0CAAuB,CAAA;IACvB,kDAA+B,CAAA;AACjC,CAAC,EALW,eAAe,KAAf,eAAe,QAK1B;AA+DD,kDAAkD;AAElD,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,YAAY;IACZ,SAAS,EAAE,kBAAkB;IAC7B,YAAY;IACZ,YAAY,EAAE,qBAAqB;IACnC,WAAW;IACX,YAAY,EAAE,qBAAqB;IACnC,gCAAgC;IAChC,IAAI,EAAE,aAAa;IACnB,WAAW;IACX,KAAK,EAAE,cAAc;CACb,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wukong-kit/network",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "WebSocket-based network layer for Wukong Kit with JSON protocol, auto-reconnect, and request/response pattern.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"package.json"
|
|
18
|
+
],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@wukong-kit/core": "^0.1.0",
|
|
21
|
+
"wukongtest": "file:../.."
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@cocos/creator-types": "^3.8.3",
|
|
25
|
+
"@types/node": "^25.8.0",
|
|
26
|
+
"typescript": "^5.0.0"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT"
|
|
29
|
+
}
|