egos-transfer 0.2.5 → 0.2.7

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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm run *)"
5
+ ]
6
+ }
7
+ }
@@ -0,0 +1,47 @@
1
+ import { DataConnection, Peer } from 'peerjs';
2
+ export interface ConnectionManagerDeps {
3
+ /** 获取当前 Peer 实例(PeerTransfer 可能异步重建) */
4
+ getPeer: () => Peer | undefined;
5
+ peerId: string;
6
+ ping: (peerId: string) => Promise<boolean>;
7
+ /** 连接关闭时回调,由 PeerTransfer 处理未完成的请求结算 */
8
+ onConnClosed: (conn: DataConnection) => void;
9
+ }
10
+ /**
11
+ * DataConnection 连接池管理器
12
+ * - 管理 DataConnection 的生命周期(注册/移除/有效性检查)
13
+ * - 15 秒定时健康检查:ping 每个连接,不通则丢弃
14
+ * - 监听 connectionstatechange,终态时自动清理
15
+ */
16
+ export declare class ConnectionManager {
17
+ private pool;
18
+ private healthCheckTimer;
19
+ private pingRequests;
20
+ private destroyed;
21
+ private readonly deps;
22
+ constructor(deps: ConnectionManagerDeps);
23
+ get(peerId: string): DataConnection | undefined;
24
+ has(peerId: string): boolean;
25
+ get size(): number;
26
+ getAllPeers(): string[];
27
+ clean(): void;
28
+ /**
29
+ * 注册一个连接:入池、绑定 data/close/error/connectionstatechange 事件
30
+ * @param conn - 已打开的 DataConnection
31
+ * @param onData - 数据事件回调(由 PeerTransfer 提供,路由消息)
32
+ */
33
+ setup(conn: DataConnection, onData: (message: any) => void): DataConnection;
34
+ remove(peerId: string, connectionId?: string): void;
35
+ isValid(conn?: DataConnection): boolean;
36
+ private handleConnClosed;
37
+ startHealthCheck(): void;
38
+ stopHealthCheck(): void;
39
+ private checkAllConnections;
40
+ private pingConnection;
41
+ /**
42
+ * 由 PeerTransfer.onResponse 调用,优先处理 ping 回调
43
+ * @returns true 表示该 requestId 是 health check 并已处理
44
+ */
45
+ tryResolvePing(requestId: string): boolean;
46
+ destroy(): void;
47
+ }
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ConnectionManager = void 0;
13
+ const HEALTH_CHECK_INTERVAL = 15000;
14
+ /**
15
+ * DataConnection 连接池管理器
16
+ * - 管理 DataConnection 的生命周期(注册/移除/有效性检查)
17
+ * - 15 秒定时健康检查:ping 每个连接,不通则丢弃
18
+ * - 监听 connectionstatechange,终态时自动清理
19
+ */
20
+ class ConnectionManager {
21
+ constructor(deps) {
22
+ this.pool = new Map();
23
+ this.healthCheckTimer = null;
24
+ this.pingRequests = new Map();
25
+ this.destroyed = false;
26
+ this.deps = deps;
27
+ }
28
+ // ── Pool access ──────────────────────────────────────────────────
29
+ get(peerId) {
30
+ const conn = this.pool.get(peerId);
31
+ if (!conn) {
32
+ return;
33
+ }
34
+ if (this.isValid(conn)) {
35
+ return conn;
36
+ }
37
+ else {
38
+ this.remove(peerId);
39
+ return;
40
+ }
41
+ }
42
+ has(peerId) {
43
+ return this.pool.has(peerId);
44
+ }
45
+ get size() {
46
+ return this.pool.size;
47
+ }
48
+ getAllPeers() {
49
+ return Array.from(this.pool.keys());
50
+ }
51
+ clean() {
52
+ for (const peerId of this.getAllPeers()) {
53
+ this.remove(peerId);
54
+ }
55
+ }
56
+ // ── Connection lifecycle ─────────────────────────────────────────
57
+ /**
58
+ * 注册一个连接:入池、绑定 data/close/error/connectionstatechange 事件
59
+ * @param conn - 已打开的 DataConnection
60
+ * @param onData - 数据事件回调(由 PeerTransfer 提供,路由消息)
61
+ */
62
+ setup(conn, onData) {
63
+ this.pool.set(conn.peer, conn);
64
+ conn.on('data', onData);
65
+ conn.on('close', () => this.handleConnClosed(conn));
66
+ conn.on('error', () => this.handleConnClosed(conn));
67
+ // 监听底层 RTCPeerConnection 的 connectionstatechange,终态时自动清理
68
+ const pc = conn.peerConnection;
69
+ if (pc) {
70
+ const onStateChange = () => {
71
+ if (pc.connectionState === 'failed' || pc.connectionState === 'closed') {
72
+ this.handleConnClosed(conn);
73
+ }
74
+ };
75
+ pc.addEventListener('connectionstatechange', onStateChange);
76
+ // 将清理函数挂到 conn 上,remove 时一并移除
77
+ conn.__cm_stateChangeCleanup = () => {
78
+ pc.removeEventListener('connectionstatechange', onStateChange);
79
+ };
80
+ }
81
+ return conn;
82
+ }
83
+ remove(peerId, connectionId) {
84
+ var _a, _b;
85
+ const conn = this.pool.get(peerId);
86
+ if (conn) {
87
+ if (connectionId && conn.connectionId !== connectionId)
88
+ return;
89
+ this.pool.delete(peerId);
90
+ // 清理 connectionstatechange listener
91
+ // (conn as any).__cm_stateChangeCleanup?.();
92
+ // conn.removeAllListeners();
93
+ // conn.close();
94
+ // this.deps.getPeer()?._removeConnection(conn);
95
+ }
96
+ else {
97
+ // 清理 Peer 内部残留的 connection 引用
98
+ const connections = ((_a = this.deps.getPeer()) === null || _a === void 0 ? void 0 : _a.connections) || {};
99
+ for (const c of connections[peerId] || []) {
100
+ c.removeAllListeners();
101
+ c.close();
102
+ (_b = this.deps.getPeer()) === null || _b === void 0 ? void 0 : _b._removeConnection(c);
103
+ }
104
+ }
105
+ }
106
+ isValid(conn) {
107
+ if (!(conn === null || conn === void 0 ? void 0 : conn.open))
108
+ return false;
109
+ if (!(conn === null || conn === void 0 ? void 0 : conn.peerConnection))
110
+ return false;
111
+ return ['connected', 'completed'].includes(conn.peerConnection.connectionState);
112
+ }
113
+ handleConnClosed(conn) {
114
+ this.pool.delete(conn.peer);
115
+ this.deps.onConnClosed(conn);
116
+ }
117
+ // ── Health check ─────────────────────────────────────────────────
118
+ startHealthCheck() {
119
+ if (this.destroyed)
120
+ return;
121
+ this.stopHealthCheck();
122
+ this.healthCheckTimer = setInterval(() => {
123
+ this.checkAllConnections();
124
+ }, HEALTH_CHECK_INTERVAL);
125
+ }
126
+ stopHealthCheck() {
127
+ if (this.healthCheckTimer !== null) {
128
+ clearInterval(this.healthCheckTimer);
129
+ this.healthCheckTimer = null;
130
+ }
131
+ }
132
+ checkAllConnections() {
133
+ return __awaiter(this, void 0, void 0, function* () {
134
+ const peers = this.getAllPeers();
135
+ for (const peerId of peers) {
136
+ const conn = this.pool.get(peerId);
137
+ if (!conn)
138
+ continue;
139
+ const alive = yield this.pingConnection(conn);
140
+ if (!alive) {
141
+ this.remove(peerId);
142
+ }
143
+ }
144
+ });
145
+ }
146
+ pingConnection(conn) {
147
+ return __awaiter(this, void 0, void 0, function* () {
148
+ try {
149
+ if (!this.isValid(conn)) {
150
+ return false;
151
+ }
152
+ return yield this.deps.ping(conn.peer);
153
+ // const requestId = await this.deps.genRequestId();
154
+ // return new Promise<boolean>((resolve) => {
155
+ // const timer = setTimeout(() => {
156
+ // this.pingRequests.delete(requestId);
157
+ // resolve(false);
158
+ // }, PING_TIMEOUT);
159
+ // this.pingRequests.set(requestId, () => {
160
+ // clearTimeout(timer);
161
+ // resolve(true);
162
+ // });
163
+ // conn.send({
164
+ // route: PeerRoutes.HEARTBEAT,
165
+ // method: 'get',
166
+ // body: {},
167
+ // requestId,
168
+ // scope: PeerScope.REQUEST,
169
+ // action: PeerAction.SEND,
170
+ // src: this.deps.peerId,
171
+ // dest: conn.peer,
172
+ // headers: {},
173
+ // createdAt: Date.now(),
174
+ // });
175
+ // });
176
+ }
177
+ catch (_a) {
178
+ return false;
179
+ }
180
+ });
181
+ }
182
+ /**
183
+ * 由 PeerTransfer.onResponse 调用,优先处理 ping 回调
184
+ * @returns true 表示该 requestId 是 health check 并已处理
185
+ */
186
+ tryResolvePing(requestId) {
187
+ const cb = this.pingRequests.get(requestId);
188
+ if (cb) {
189
+ cb({});
190
+ this.pingRequests.delete(requestId);
191
+ return true;
192
+ }
193
+ return false;
194
+ }
195
+ // ── Lifecycle ───────────────────────────────────────────────────
196
+ destroy() {
197
+ this.destroyed = true;
198
+ this.stopHealthCheck();
199
+ for (const peerId of this.getAllPeers()) {
200
+ this.remove(peerId);
201
+ }
202
+ this.pool.clear();
203
+ this.pingRequests.clear();
204
+ }
205
+ }
206
+ exports.ConnectionManager = ConnectionManager;
@@ -302,7 +302,6 @@ class FileTransfer {
302
302
  }
303
303
  if (processed.length === totalChunk) {
304
304
  const checksum = yield this.taskService.verifyFile(task.transId);
305
- console.log('file verify', checksum);
306
305
  if (checksum) {
307
306
  yield this.done(task);
308
307
  yield this.updateTask(task.transId, {
package/dist/index.d.ts CHANGED
@@ -1,119 +1,4 @@
1
- import { DataConnection, Peer, PeerOptions } from 'peerjs';
2
- import { IDeviceStatus, IMessage, PeerMessage, PeerRequestPayload, RequestCallbackFn, RouteItem } from './interfaces';
3
- import { InternalApi, PeerAction } from './entity';
4
- import { PeerRouter } from './router';
5
- export interface ContentPayload {
6
- file: string;
7
- parent: string;
8
- }
9
- type ResponseCb<T = any> = (args: T) => void;
10
- type PeerTransferOptions = PeerOptions & {
11
- turnExpiredAt: number;
12
- };
13
- export declare abstract class PeerTransfer {
14
- protected peer: Peer | undefined;
15
- protected connectPool: Map<string, DataConnection>;
16
- static connecting: Map<string, {
17
- promise: Promise<any>;
18
- resolve: (value: any | PromiseLike<any>) => void;
19
- reject: (reason?: any) => void;
20
- }>;
21
- protected peerConfig: PeerTransferOptions | undefined;
22
- readonly deviceId: string;
23
- /** 接入 peerjs 服务端的 peerId,默认与 deviceId 相同 */
24
- readonly peerId: string;
25
- protected requests: Map<string, ResponseCb>;
26
- /** 追踪 addRequest 的延时清理定时器,避免重试时误删新回调 */
27
- protected requestTimers: Map<string, ReturnType<typeof setTimeout>>;
28
- protected timeout: number;
29
- protected router: PeerRouter;
30
- protected booted: Promise<Peer | undefined> | undefined;
31
- protected reconnectingPromise: Promise<Peer | undefined> | undefined;
32
- protected checkerId: ReturnType<typeof setInterval> | null;
33
- isPaused: boolean;
34
- /** 重连尝试次数,成功连接后归零 */
35
- protected reconnectAttempt: number;
36
- /** 标记实例已销毁,防止异步操作继续 */
37
- protected destroyed: boolean;
38
- /** 按 requestId 追踪进行中的请求 */
39
- private inRequest;
40
- constructor(deviceId: string, config: PeerTransferOptions, isRandomPeerId?: boolean);
41
- abstract getStore(): any;
42
- abstract online(deviceId: string): void;
43
- abstract offline(deviceId: string): void;
44
- abstract genRequestId(): Promise<string>;
45
- abstract getApi(peerId: string): Promise<InternalApi | undefined>;
46
- abstract discoverDeviceById(deviceId: string): Promise<void>;
47
- abstract onDestroy(): void;
48
- abstract requireAuth(): Promise<void>;
49
- abstract onConnectLimit(): void;
50
- abstract reloadConfig(config?: Record<string, any>): Promise<void>;
51
- createPeer(force?: boolean): Promise<Peer | undefined>;
52
- initialize(peer: Peer): Promise<Peer>;
53
- handleSocketMessage(message: IMessage): void;
54
- onHeartbeat(message: IMessage): void;
55
- onError(message: IMessage): void;
56
- onOpen(message: IMessage): void;
57
- onDeviceOnline(message: IMessage): Promise<void>;
58
- onDeviceOffline(message: IMessage): void;
59
- removeConnection(peerId: string, connectionId?: string): void;
60
- /**
61
- * 连接关闭时尝试快速重连(5s 超时):
62
- * - 重连成功 → 保留 inRequest 条目,更新 conn 引用
63
- * - 重连失败 → 触发 feedback 并清理 inRequest
64
- */
65
- private handleConnectionClose;
66
- addConnection(conn: DataConnection): void;
67
- checkConnection(): void;
68
- getApiHost(): string;
69
- getDeviceStatus(peerId: string): Promise<IDeviceStatus | undefined>;
70
- connectToPeer(peerId: string): Promise<DataConnection | undefined>;
71
- private createConnection;
72
- /**
73
- * 心跳检测 - 直接 send,不走 request 管线,避免重试放大
74
- */
75
- ping(peerId: string): Promise<boolean>;
76
- onConnect(conn: DataConnection): Promise<DataConnection | undefined>;
77
- /** 绑定 DataConnection 的 data 事件 */
78
- private bindConnectionData;
79
- request(peerId: string, payload: PeerRequestPayload): Promise<PeerMessage>;
80
- /**
81
- * 执行一次请求,返回带错误码的结果供外层判断是否重试
82
- * @param peerId - 目标 peerId
83
- * @param payload - 请求负载
84
- * @param requestId - 请求ID(重试时复用)
85
- * @returns error.code: CONN_CLOSED/CONN_FAILED(可重试)、TIMEOUT(不重试)
86
- */
87
- private doRequestOnce;
88
- reply(conn: DataConnection, oldMessage: PeerMessage, message: {
89
- body?: Record<string, any>;
90
- headers?: Record<string, string | number>;
91
- error?: string | Record<string, any>;
92
- }): Promise<void>;
93
- send(conn: DataConnection, message: PeerRequestPayload): Promise<void>;
94
- inheritMessage(receiveMsg: PeerMessage): {
95
- src: string;
96
- id: string;
97
- requestId: string;
98
- route: string;
99
- createdAt: string | number;
100
- action: PeerAction;
101
- };
102
- reconnect(): Promise<Peer | undefined>;
103
- isValidConnection(conn: DataConnection): boolean;
104
- private doReconnect;
105
- clearInstance(): void;
106
- destroy(): void;
107
- onRequest(conn: DataConnection, message: PeerMessage): Promise<void>;
108
- onResponse(conn: DataConnection, message: PeerMessage): Promise<void>;
109
- responseOk(conn: DataConnection, message: PeerMessage): Promise<void>;
110
- responseFail(conn: DataConnection, message: PeerMessage, err: string | Record<string, any>): Promise<void>;
111
- addRequest(id: string, cb: ResponseCb): void;
112
- use(handler: RequestCallbackFn, prefix?: string): void;
113
- addRoute(route: Omit<RouteItem, 'scope'>): void;
114
- stop(): void;
115
- resume(): Promise<Peer>;
116
- }
1
+ export * from './peer-transfer';
117
2
  export * from './interfaces';
118
3
  export * from './handler';
119
4
  export * from './file-transfer';
@@ -123,3 +8,4 @@ export * from './locker';
123
8
  export * from './entity';
124
9
  export * from './http-transformer';
125
10
  export * from './http-transfer';
11
+ export * from './connection-manager';