egos-transfer 0.2.6 → 0.2.8
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/dist/connection-manager.d.ts +47 -0
- package/dist/connection-manager.js +210 -0
- package/dist/file-transfer.js +0 -1
- package/dist/index.d.ts +2 -77
- package/dist/index.js +2 -495
- package/dist/interfaces.d.ts +6 -0
- package/dist/interfaces.js +6 -0
- package/dist/peer-transfer.d.ts +95 -0
- package/dist/peer-transfer.js +584 -0
- package/dist/server.d.ts +0 -4
- package/package.json +1 -1
- package/src/connection-manager.ts +8 -5
- package/src/peer-transfer.ts +29 -12
package/dist/index.js
CHANGED
|
@@ -13,502 +13,8 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
13
13
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
17
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
18
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
19
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
20
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
21
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
22
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports
|
|
30
|
-
const constant_1 = require("./constant");
|
|
31
|
-
const peerjs_1 = require("peerjs");
|
|
32
|
-
const entity_1 = require("./entity");
|
|
33
|
-
const router_1 = require("./router");
|
|
34
|
-
const axios_1 = __importDefault(require("axios"));
|
|
35
|
-
const delay_1 = __importDefault(require("delay"));
|
|
36
|
-
const MAX_RECONNECT_BACKOFF = 30000;
|
|
37
|
-
const BASE_RECONNECT_BACKOFF = 1000;
|
|
38
|
-
class PeerTransfer {
|
|
39
|
-
constructor(deviceId, config) {
|
|
40
|
-
this.connectPool = new Map();
|
|
41
|
-
this.requests = new Map();
|
|
42
|
-
this.router = new router_1.PeerRouter();
|
|
43
|
-
this.reconnectAttempt = 0;
|
|
44
|
-
this.destroyed = false;
|
|
45
|
-
/** connectionId → Set<requestId> — settle in-flight requests on connection drop */
|
|
46
|
-
this.connRequests = new Map();
|
|
47
|
-
this._reconnecting = false;
|
|
48
|
-
this.peerConfig = Object.assign(Object.assign({}, config), { key: String(Date.now()) });
|
|
49
|
-
this.peerId = deviceId;
|
|
50
|
-
this.deviceId = deviceId;
|
|
51
|
-
}
|
|
52
|
-
// ── Peer lifecycle ──────────────────────────────────────────────
|
|
53
|
-
createPeer() {
|
|
54
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
-
var _a;
|
|
56
|
-
if (this.destroyed)
|
|
57
|
-
throw new Error('destroyed');
|
|
58
|
-
if ((_a = this.peer) === null || _a === void 0 ? void 0 : _a.open)
|
|
59
|
-
return this.peer;
|
|
60
|
-
this.destroyPeer();
|
|
61
|
-
yield this.reloadConfig();
|
|
62
|
-
const peer = new peerjs_1.Peer(this.peerId, Object.assign(Object.assign({}, this.peerConfig), { pingInterval: 9000 }));
|
|
63
|
-
this.peer = peer;
|
|
64
|
-
yield this.waitForOpen(peer);
|
|
65
|
-
peer.on('close', () => {
|
|
66
|
-
if (!this.destroyed)
|
|
67
|
-
this.reconnect();
|
|
68
|
-
});
|
|
69
|
-
peer.on('error', (err) => {
|
|
70
|
-
if (this.destroyed)
|
|
71
|
-
return;
|
|
72
|
-
if (err.type === 'network' || err.type === 'server-error') {
|
|
73
|
-
this.reconnect();
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
peer.on('connection', (conn) => {
|
|
77
|
-
conn.label = 'incoming';
|
|
78
|
-
this.onConnect(conn);
|
|
79
|
-
});
|
|
80
|
-
const socket = peer.socket;
|
|
81
|
-
socket === null || socket === void 0 ? void 0 : socket.on('message', (message) => {
|
|
82
|
-
this.handleSocketMessage(message);
|
|
83
|
-
});
|
|
84
|
-
return peer;
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
waitForOpen(peer) {
|
|
88
|
-
return new Promise((resolve) => {
|
|
89
|
-
const timer = setTimeout(() => resolve(), constant_1.PEER_OPEN_TIMEOUT);
|
|
90
|
-
peer.once('open', () => {
|
|
91
|
-
clearTimeout(timer);
|
|
92
|
-
this.reconnectAttempt = 0;
|
|
93
|
-
resolve();
|
|
94
|
-
});
|
|
95
|
-
peer.once('error', (err) => {
|
|
96
|
-
if (err.message === constant_1.CONNECTION_DUPLICATED)
|
|
97
|
-
return; // wait for open
|
|
98
|
-
clearTimeout(timer);
|
|
99
|
-
if (err.message === constant_1.CONNECTION_PEER_CONFLICT) {
|
|
100
|
-
resolve();
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (err.message === constant_1.CONNECTION_LIMIT_EXCEED) {
|
|
104
|
-
this.onConnectLimit();
|
|
105
|
-
resolve();
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
if (err.message === constant_1.UNAUTHORIZED) {
|
|
109
|
-
this.requireAuth();
|
|
110
|
-
resolve();
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
resolve();
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
handleSocketMessage(message) {
|
|
118
|
-
var _a;
|
|
119
|
-
switch (message.type) {
|
|
120
|
-
case 'ONLINE':
|
|
121
|
-
this.online(message.payload.deviceId);
|
|
122
|
-
break;
|
|
123
|
-
case 'OFFLINE':
|
|
124
|
-
this.ping(message.payload.deviceId);
|
|
125
|
-
break;
|
|
126
|
-
case 'ERROR':
|
|
127
|
-
if (((_a = message.payload) === null || _a === void 0 ? void 0 : _a.msg) === constant_1.CONNECTION_PEER_CONFLICT) {
|
|
128
|
-
this.getStore().dispatch({
|
|
129
|
-
type: 'global/updateNetworkState',
|
|
130
|
-
payload: { error: true, code: 'PEER_CONNECTION_CONFLICT', message: 'peer conflict' },
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
// ── Connection management ───────────────────────────────────────
|
|
137
|
-
connectToPeer(peerId) {
|
|
138
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
139
|
-
if (peerId === this.deviceId)
|
|
140
|
-
return;
|
|
141
|
-
const existing = this.connectPool.get(peerId);
|
|
142
|
-
if (this.isValidConnection(existing))
|
|
143
|
-
return existing;
|
|
144
|
-
if (existing)
|
|
145
|
-
this.removeConnection(peerId, existing.connectionId);
|
|
146
|
-
return this.createConnection(peerId);
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
createConnection(peerId) {
|
|
150
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
-
var _a, _b;
|
|
152
|
-
for (let retry = 0; retry < 3; retry++) {
|
|
153
|
-
if (!((_a = this.peer) === null || _a === void 0 ? void 0 : _a.open)) {
|
|
154
|
-
try {
|
|
155
|
-
yield this.createPeer();
|
|
156
|
-
}
|
|
157
|
-
catch ( /* fall through */_c) { /* fall through */ }
|
|
158
|
-
}
|
|
159
|
-
if (!((_b = this.peer) === null || _b === void 0 ? void 0 : _b.open))
|
|
160
|
-
continue;
|
|
161
|
-
const pooled = this.connectPool.get(peerId);
|
|
162
|
-
if (this.isValidConnection(pooled))
|
|
163
|
-
return pooled;
|
|
164
|
-
const conn = this.peer.connect(peerId, {
|
|
165
|
-
label: 'outgoing',
|
|
166
|
-
metadata: { time: Date.now() },
|
|
167
|
-
});
|
|
168
|
-
if (!conn)
|
|
169
|
-
continue;
|
|
170
|
-
const result = yield this.onConnect(conn).catch(() => undefined);
|
|
171
|
-
if (result)
|
|
172
|
-
return result;
|
|
173
|
-
const pooled2 = this.connectPool.get(peerId);
|
|
174
|
-
if (this.isValidConnection(pooled2))
|
|
175
|
-
return pooled2;
|
|
176
|
-
}
|
|
177
|
-
return undefined;
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
onConnect(conn) {
|
|
181
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
-
if (!conn)
|
|
183
|
-
return;
|
|
184
|
-
const existing = this.connectPool.get(conn.peer);
|
|
185
|
-
if (existing) {
|
|
186
|
-
if (existing.connectionId > conn.connectionId) {
|
|
187
|
-
this.removeConnection(conn.peer, conn.connectionId);
|
|
188
|
-
if (this.isValidConnection(existing))
|
|
189
|
-
return existing;
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
this.removeConnection(existing.peer, existing.connectionId);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
if (conn.open) {
|
|
196
|
-
this.setupConnection(conn);
|
|
197
|
-
return conn;
|
|
198
|
-
}
|
|
199
|
-
return new Promise((resolve) => {
|
|
200
|
-
const timer = setTimeout(() => {
|
|
201
|
-
conn.off('open', onOpen);
|
|
202
|
-
conn.off('error', onError);
|
|
203
|
-
conn.off('close', onClose);
|
|
204
|
-
resolve(this.isValidConnection(conn) ? this.setupConnection(conn) : undefined);
|
|
205
|
-
}, constant_1.CONNECTION_TIMEOUT);
|
|
206
|
-
const onOpen = () => {
|
|
207
|
-
clearTimeout(timer);
|
|
208
|
-
conn.off('error', onError);
|
|
209
|
-
conn.off('close', onClose);
|
|
210
|
-
resolve(this.setupConnection(conn));
|
|
211
|
-
};
|
|
212
|
-
const onError = () => {
|
|
213
|
-
clearTimeout(timer);
|
|
214
|
-
conn.off('open', onOpen);
|
|
215
|
-
conn.off('close', onClose);
|
|
216
|
-
const pooled = this.connectPool.get(conn.peer);
|
|
217
|
-
resolve(this.isValidConnection(pooled) ? pooled : undefined);
|
|
218
|
-
};
|
|
219
|
-
const onClose = () => {
|
|
220
|
-
clearTimeout(timer);
|
|
221
|
-
conn.off('open', onOpen);
|
|
222
|
-
conn.off('error', onError);
|
|
223
|
-
resolve(undefined);
|
|
224
|
-
};
|
|
225
|
-
conn.once('open', onOpen);
|
|
226
|
-
conn.once('error', onError);
|
|
227
|
-
conn.once('close', onClose);
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
setupConnection(conn) {
|
|
232
|
-
this.connectPool.set(conn.peer, conn);
|
|
233
|
-
this.bindConnectionData(conn);
|
|
234
|
-
conn.on('close', () => this.onConnClosed(conn));
|
|
235
|
-
conn.on('error', () => this.onConnClosed(conn));
|
|
236
|
-
return conn;
|
|
237
|
-
}
|
|
238
|
-
onConnClosed(conn) {
|
|
239
|
-
this.connectPool.delete(conn.peer);
|
|
240
|
-
const reqIds = this.connRequests.get(conn.connectionId);
|
|
241
|
-
if (reqIds) {
|
|
242
|
-
for (const reqId of reqIds) {
|
|
243
|
-
const cb = this.requests.get(reqId);
|
|
244
|
-
if (cb) {
|
|
245
|
-
cb({ error: { message: 'connection closed', code: 'CONN_CLOSED' } });
|
|
246
|
-
this.requests.delete(reqId);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
this.connRequests.delete(conn.connectionId);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
removeConnection(peerId, connectionId) {
|
|
253
|
-
var _a, _b, _c;
|
|
254
|
-
const conn = this.connectPool.get(peerId);
|
|
255
|
-
if (conn) {
|
|
256
|
-
if (connectionId && conn.connectionId !== connectionId)
|
|
257
|
-
return;
|
|
258
|
-
this.connectPool.delete(peerId);
|
|
259
|
-
this.connRequests.delete(conn.connectionId);
|
|
260
|
-
conn.removeAllListeners();
|
|
261
|
-
conn.close();
|
|
262
|
-
(_a = this.peer) === null || _a === void 0 ? void 0 : _a._removeConnection(conn);
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
const connections = ((_b = this.peer) === null || _b === void 0 ? void 0 : _b.connections) || {};
|
|
266
|
-
for (const c of connections[peerId] || []) {
|
|
267
|
-
c.removeAllListeners();
|
|
268
|
-
c.close();
|
|
269
|
-
(_c = this.peer) === null || _c === void 0 ? void 0 : _c._removeConnection(c);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
isValidConnection(conn) {
|
|
274
|
-
if (!(conn === null || conn === void 0 ? void 0 : conn.open))
|
|
275
|
-
return false;
|
|
276
|
-
if (!(conn === null || conn === void 0 ? void 0 : conn.peerConnection))
|
|
277
|
-
return false;
|
|
278
|
-
return ['connected', 'completed'].includes(conn.peerConnection.connectionState);
|
|
279
|
-
}
|
|
280
|
-
bindConnectionData(conn) {
|
|
281
|
-
conn.on('data', (message) => {
|
|
282
|
-
switch (message.scope) {
|
|
283
|
-
case entity_1.PeerScope.REQUEST:
|
|
284
|
-
return this.onRequest(conn, message);
|
|
285
|
-
case entity_1.PeerScope.RESPONSE:
|
|
286
|
-
return this.onResponse(conn, message);
|
|
287
|
-
default:
|
|
288
|
-
return this.responseFail(conn, message, 'unknown scope');
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
// ── Request / Reply ─────────────────────────────────────────────
|
|
293
|
-
request(peerId, payload) {
|
|
294
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
295
|
-
var _a;
|
|
296
|
-
const requestId = payload.requestId || (yield this.genRequestId());
|
|
297
|
-
let res;
|
|
298
|
-
for (let attempt = 0; attempt <= 2; attempt++) {
|
|
299
|
-
res = yield this.doRequestOnce(peerId, payload, requestId);
|
|
300
|
-
const code = (_a = res.error) === null || _a === void 0 ? void 0 : _a.code;
|
|
301
|
-
if (!res.error || (code !== 'CONN_CLOSED' && code !== 'CONN_FAILED'))
|
|
302
|
-
return res;
|
|
303
|
-
}
|
|
304
|
-
return res;
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
doRequestOnce(peerId, payload, requestId) {
|
|
308
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
309
|
-
if (this.destroyed) {
|
|
310
|
-
return Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'destroyed', code: 'CONN_CLOSED' } });
|
|
311
|
-
}
|
|
312
|
-
const conn = yield this.connectToPeer(peerId);
|
|
313
|
-
if (!this.isValidConnection(conn)) {
|
|
314
|
-
return Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'connection failed', code: 'CONN_FAILED' } });
|
|
315
|
-
}
|
|
316
|
-
return new Promise((resolve) => {
|
|
317
|
-
let timeout = null;
|
|
318
|
-
const cleanup = () => {
|
|
319
|
-
if (timeout) {
|
|
320
|
-
clearTimeout(timeout);
|
|
321
|
-
timeout = null;
|
|
322
|
-
}
|
|
323
|
-
this.requests.delete(requestId);
|
|
324
|
-
const reqs = this.connRequests.get(conn.connectionId);
|
|
325
|
-
if (reqs) {
|
|
326
|
-
reqs.delete(requestId);
|
|
327
|
-
if (reqs.size === 0)
|
|
328
|
-
this.connRequests.delete(conn.connectionId);
|
|
329
|
-
}
|
|
330
|
-
};
|
|
331
|
-
timeout = setTimeout(() => {
|
|
332
|
-
cleanup();
|
|
333
|
-
this.removeConnection(conn.peer, conn.connectionId);
|
|
334
|
-
resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'timeout', code: 'TIMEOUT' } }));
|
|
335
|
-
}, payload.timeout || entity_1.REQUEST_TIMEOUT);
|
|
336
|
-
let reqs = this.connRequests.get(conn.connectionId);
|
|
337
|
-
if (!reqs) {
|
|
338
|
-
reqs = new Set();
|
|
339
|
-
this.connRequests.set(conn.connectionId, reqs);
|
|
340
|
-
}
|
|
341
|
-
reqs.add(requestId);
|
|
342
|
-
this.requests.set(requestId, (message) => {
|
|
343
|
-
cleanup();
|
|
344
|
-
resolve(message);
|
|
345
|
-
});
|
|
346
|
-
this.send(conn, Object.assign(Object.assign({}, payload), { requestId })).catch(() => {
|
|
347
|
-
cleanup();
|
|
348
|
-
resolve(Object.assign(Object.assign({}, payload), { body: {}, error: { message: 'send failed', code: 'CONN_CLOSED' } }));
|
|
349
|
-
});
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
reply(conn, oldMessage, message) {
|
|
354
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
355
|
-
const data = Object.assign(Object.assign(Object.assign({}, this.inheritMessage(oldMessage)), message), { dest: conn.peer, scope: entity_1.PeerScope.RESPONSE, updatedAt: Date.now() });
|
|
356
|
-
if (!this.isValidConnection(conn)) {
|
|
357
|
-
const c = yield this.connectToPeer(conn.peer);
|
|
358
|
-
return c === null || c === void 0 ? void 0 : c.send(data);
|
|
359
|
-
}
|
|
360
|
-
return conn.send(data);
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
send(conn, message) {
|
|
364
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
365
|
-
const internalApi = yield this.getApi(conn.peer);
|
|
366
|
-
let connection = conn;
|
|
367
|
-
if (!this.isValidConnection(conn)) {
|
|
368
|
-
const c = yield this.connectToPeer(conn.peer);
|
|
369
|
-
if (!c)
|
|
370
|
-
return this.responseFail(conn, message, 'send with invalid connection');
|
|
371
|
-
connection = c;
|
|
372
|
-
}
|
|
373
|
-
const requestId = message.requestId || (yield this.genRequestId());
|
|
374
|
-
return connection.send(Object.assign(Object.assign({}, message), { requestId, headers: Object.assign(Object.assign({}, message.headers), { ['x-device-token']: internalApi === null || internalApi === void 0 ? void 0 : internalApi.token }), scope: entity_1.PeerScope.REQUEST, action: entity_1.PeerAction.SEND, src: this.peerId, dest: conn.peer, createdAt: Date.now() }), false);
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
// ── Ping ────────────────────────────────────────────────────────
|
|
378
|
-
ping(peerId) {
|
|
379
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
380
|
-
if (this.deviceId === peerId)
|
|
381
|
-
return false;
|
|
382
|
-
const conn = this.connectPool.get(peerId);
|
|
383
|
-
if (!this.isValidConnection(conn)) {
|
|
384
|
-
this.removeConnection(peerId, conn === null || conn === void 0 ? void 0 : conn.connectionId);
|
|
385
|
-
return false;
|
|
386
|
-
}
|
|
387
|
-
try {
|
|
388
|
-
const requestId = yield this.genRequestId();
|
|
389
|
-
return new Promise((resolve) => {
|
|
390
|
-
const timer = setTimeout(() => { this.requests.delete(requestId); resolve(false); }, constant_1.PING_TIMEOUT);
|
|
391
|
-
this.requests.set(requestId, () => { clearTimeout(timer); resolve(true); });
|
|
392
|
-
conn.send({
|
|
393
|
-
route: entity_1.PeerRoutes.HEARTBEAT, method: 'get', body: {}, requestId,
|
|
394
|
-
scope: entity_1.PeerScope.REQUEST, action: entity_1.PeerAction.SEND,
|
|
395
|
-
src: this.peerId, dest: peerId, headers: {}, createdAt: Date.now(),
|
|
396
|
-
});
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
catch (_a) {
|
|
400
|
-
return false;
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
// ── Server helpers ──────────────────────────────────────────────
|
|
405
|
-
getApiHost() {
|
|
406
|
-
var _a, _b, _c;
|
|
407
|
-
const protocol = ((_a = this.peerConfig) === null || _a === void 0 ? void 0 : _a.secure) ? 'https' : 'http';
|
|
408
|
-
const host = ((_b = this.peerConfig) === null || _b === void 0 ? void 0 : _b.host) || 'localhost';
|
|
409
|
-
const port = ((_c = this.peerConfig) === null || _c === void 0 ? void 0 : _c.port) || 9000;
|
|
410
|
-
return `${protocol}://${host}:${port}`;
|
|
411
|
-
}
|
|
412
|
-
getDeviceStatus(peerId) {
|
|
413
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
414
|
-
try {
|
|
415
|
-
const res = yield axios_1.default.get(`${this.getApiHost()}/api/v1/devices/${peerId}/status`);
|
|
416
|
-
if (res.status < 400)
|
|
417
|
-
return res.data.data;
|
|
418
|
-
}
|
|
419
|
-
catch ( /* ignore */_a) { /* ignore */ }
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
// ── Reconnection ────────────────────────────────────────────────
|
|
423
|
-
reconnect() {
|
|
424
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
425
|
-
if (this.destroyed || this._reconnecting)
|
|
426
|
-
return;
|
|
427
|
-
this._reconnecting = true;
|
|
428
|
-
try {
|
|
429
|
-
this.reconnectAttempt++;
|
|
430
|
-
const backoff = Math.min(BASE_RECONNECT_BACKOFF * Math.pow(2, this.reconnectAttempt - 1), MAX_RECONNECT_BACKOFF);
|
|
431
|
-
yield (0, delay_1.default)(backoff);
|
|
432
|
-
yield this.reloadConfig();
|
|
433
|
-
return yield this.createPeer();
|
|
434
|
-
}
|
|
435
|
-
catch (_a) {
|
|
436
|
-
return this.createPeer();
|
|
437
|
-
}
|
|
438
|
-
finally {
|
|
439
|
-
this._reconnecting = false;
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
// ── Message helpers ─────────────────────────────────────────────
|
|
444
|
-
inheritMessage(receiveMsg) {
|
|
445
|
-
return {
|
|
446
|
-
src: this.peerId,
|
|
447
|
-
id: receiveMsg.id,
|
|
448
|
-
requestId: receiveMsg.requestId,
|
|
449
|
-
route: receiveMsg.route,
|
|
450
|
-
createdAt: receiveMsg.createdAt,
|
|
451
|
-
action: entity_1.PeerAction.RECEIVE,
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
// ── Router integration ──────────────────────────────────────────
|
|
455
|
-
onRequest(conn, message) {
|
|
456
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
457
|
-
try {
|
|
458
|
-
yield this.router.run(conn, message);
|
|
459
|
-
}
|
|
460
|
-
catch (err) {
|
|
461
|
-
this.responseFail(conn, message, { message: err.message, code: err.code });
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
onResponse(_conn, message) {
|
|
466
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
467
|
-
const cb = this.requests.get(message.requestId);
|
|
468
|
-
if (cb) {
|
|
469
|
-
cb.apply(this, [message]);
|
|
470
|
-
this.requests.delete(message.requestId);
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
responseOk(conn, message) {
|
|
475
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
476
|
-
return this.reply(conn, message, { body: { ok: true } });
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
responseFail(conn, message, err) {
|
|
480
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
481
|
-
return this.reply(conn, message, { body: {}, error: err || 'service error' });
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
use(handler, prefix = '') {
|
|
485
|
-
this.router.use(handler, prefix);
|
|
486
|
-
}
|
|
487
|
-
addRoute(route) {
|
|
488
|
-
this.router.add(route);
|
|
489
|
-
}
|
|
490
|
-
// ── Lifecycle ───────────────────────────────────────────────────
|
|
491
|
-
clearInstance() {
|
|
492
|
-
this.destroyed = true;
|
|
493
|
-
for (const peerId of Array.from(this.connectPool.keys())) {
|
|
494
|
-
this.removeConnection(peerId);
|
|
495
|
-
}
|
|
496
|
-
this.requests.clear();
|
|
497
|
-
this.connRequests.clear();
|
|
498
|
-
this.destroyPeer();
|
|
499
|
-
}
|
|
500
|
-
destroy() {
|
|
501
|
-
this.clearInstance();
|
|
502
|
-
this.onDestroy();
|
|
503
|
-
}
|
|
504
|
-
destroyPeer() {
|
|
505
|
-
var _a, _b;
|
|
506
|
-
(_a = this.peer) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
507
|
-
(_b = this.peer) === null || _b === void 0 ? void 0 : _b.removeAllListeners();
|
|
508
|
-
this.peer = undefined;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
exports.PeerTransfer = PeerTransfer;
|
|
17
|
+
__exportStar(require("./peer-transfer"), exports);
|
|
512
18
|
__exportStar(require("./interfaces"), exports);
|
|
513
19
|
__exportStar(require("./handler"), exports);
|
|
514
20
|
__exportStar(require("./file-transfer"), exports);
|
|
@@ -518,3 +24,4 @@ __exportStar(require("./locker"), exports);
|
|
|
518
24
|
__exportStar(require("./entity"), exports);
|
|
519
25
|
__exportStar(require("./http-transformer"), exports);
|
|
520
26
|
__exportStar(require("./http-transfer"), exports);
|
|
27
|
+
__exportStar(require("./connection-manager"), exports);
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -158,6 +158,10 @@ export interface MiddlewareItem {
|
|
|
158
158
|
async?: boolean;
|
|
159
159
|
errorHandler?: (error: Error) => void;
|
|
160
160
|
}
|
|
161
|
+
export declare enum MessageRole {
|
|
162
|
+
Agent = "agent",
|
|
163
|
+
User = "user"
|
|
164
|
+
}
|
|
161
165
|
export type DataMessage = {
|
|
162
166
|
id: string;
|
|
163
167
|
src: string;
|
|
@@ -173,6 +177,8 @@ export type DataMessage = {
|
|
|
173
177
|
priority?: TransferPriority;
|
|
174
178
|
retryCount?: number;
|
|
175
179
|
metadata?: Record<string, any>;
|
|
180
|
+
sessionId?: string;
|
|
181
|
+
role?: MessageRole;
|
|
176
182
|
};
|
|
177
183
|
export interface MetaData {
|
|
178
184
|
size: number;
|
package/dist/interfaces.js
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MessageRole = void 0;
|
|
4
|
+
var MessageRole;
|
|
5
|
+
(function (MessageRole) {
|
|
6
|
+
MessageRole["Agent"] = "agent";
|
|
7
|
+
MessageRole["User"] = "user";
|
|
8
|
+
})(MessageRole || (exports.MessageRole = MessageRole = {}));
|
|
@@ -0,0 +1,95 @@
|
|
|
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 { ConnectionManager } from './connection-manager';
|
|
5
|
+
import { PeerRouter } from './router';
|
|
6
|
+
export interface ContentPayload {
|
|
7
|
+
file: string;
|
|
8
|
+
parent: string;
|
|
9
|
+
}
|
|
10
|
+
type ResponseCb<T = any> = (args: T) => void;
|
|
11
|
+
type PeerTransferOptions = PeerOptions & {
|
|
12
|
+
turnExpiredAt: number;
|
|
13
|
+
};
|
|
14
|
+
export declare const PEER_CLOSE_RECONNECT_TIMEOUT = 3000;
|
|
15
|
+
export declare abstract class PeerTransfer {
|
|
16
|
+
protected peer: Peer | undefined;
|
|
17
|
+
protected connectionManager: ConnectionManager;
|
|
18
|
+
protected peerConfig: PeerTransferOptions | undefined;
|
|
19
|
+
readonly deviceId: string;
|
|
20
|
+
readonly peerId: string;
|
|
21
|
+
protected requests: Map<string, ResponseCb<any>>;
|
|
22
|
+
protected router: PeerRouter;
|
|
23
|
+
protected reconnectAttempt: number;
|
|
24
|
+
protected destroyed: boolean;
|
|
25
|
+
/** connectionId → Set<requestId> — settle in-flight requests on connection drop */
|
|
26
|
+
private connRequests;
|
|
27
|
+
private _reconnecting;
|
|
28
|
+
/** 退出标志:为 true 时禁止自动重连,直到 createPeer 被调用 */
|
|
29
|
+
private _exited;
|
|
30
|
+
/** 空闲重连定时器:超时无消息活动则重建 Peer */
|
|
31
|
+
private idleTimer;
|
|
32
|
+
/** 心跳超时定时器:30 秒未收到 HEARTBEAT 则重连 */
|
|
33
|
+
private heartbeatTimer;
|
|
34
|
+
constructor(deviceId: string, config: PeerTransferOptions);
|
|
35
|
+
abstract getStore(): any;
|
|
36
|
+
abstract online(deviceId: string): void;
|
|
37
|
+
abstract offline(deviceId: string): void;
|
|
38
|
+
abstract genRequestId(): Promise<string>;
|
|
39
|
+
abstract getApi(peerId: string): Promise<InternalApi | undefined>;
|
|
40
|
+
abstract discoverDeviceById(deviceId: string): Promise<void>;
|
|
41
|
+
abstract onDestroy(): void;
|
|
42
|
+
abstract requireAuth(): Promise<void>;
|
|
43
|
+
abstract onConnectLimit(): void;
|
|
44
|
+
abstract reloadConfig(config?: Record<string, any>): Promise<void>;
|
|
45
|
+
createPeer(force?: boolean): Promise<Peer>;
|
|
46
|
+
private waitForOpen;
|
|
47
|
+
handleSocketMessage(message: IMessage): void;
|
|
48
|
+
connectToPeer(peerId: string): Promise<DataConnection | undefined>;
|
|
49
|
+
private createConnection;
|
|
50
|
+
onConnect(conn: DataConnection): Promise<DataConnection | undefined>;
|
|
51
|
+
private setupConnection;
|
|
52
|
+
private onConnClosed;
|
|
53
|
+
removeConnection(peerId: string, connectionId?: string): void;
|
|
54
|
+
isValidConnection(conn?: DataConnection): boolean;
|
|
55
|
+
request(peerId: string, payload: PeerRequestPayload): Promise<PeerMessage>;
|
|
56
|
+
private doRequestOnce;
|
|
57
|
+
reply(conn: DataConnection, oldMessage: PeerMessage, message: {
|
|
58
|
+
body?: Record<string, any>;
|
|
59
|
+
headers?: Record<string, string | number>;
|
|
60
|
+
error?: string | Record<string, any>;
|
|
61
|
+
}): Promise<void>;
|
|
62
|
+
send(conn: DataConnection, message: PeerRequestPayload): Promise<void>;
|
|
63
|
+
ping(peerId: string): Promise<boolean>;
|
|
64
|
+
getApiHost(): string;
|
|
65
|
+
getDeviceStatus(peerId: string): Promise<IDeviceStatus | undefined>;
|
|
66
|
+
reconnect(force?: boolean): Promise<Peer | undefined>;
|
|
67
|
+
inheritMessage(receiveMsg: PeerMessage): {
|
|
68
|
+
src: string;
|
|
69
|
+
id: string;
|
|
70
|
+
requestId: string;
|
|
71
|
+
route: string;
|
|
72
|
+
createdAt: string | number;
|
|
73
|
+
action: PeerAction;
|
|
74
|
+
};
|
|
75
|
+
onRequest(conn: DataConnection, message: PeerMessage): Promise<void>;
|
|
76
|
+
onResponse(_conn: DataConnection, message: PeerMessage): Promise<void>;
|
|
77
|
+
responseOk(conn: DataConnection, message: PeerMessage): Promise<void>;
|
|
78
|
+
responseFail(conn: DataConnection, message: PeerMessage, err: string | Record<string, any>): Promise<void>;
|
|
79
|
+
use(handler: RequestCallbackFn, prefix?: string): void;
|
|
80
|
+
addRoute(route: Omit<RouteItem, 'scope'>): void;
|
|
81
|
+
private resetIdleTimer;
|
|
82
|
+
private clearIdleTimer;
|
|
83
|
+
private resetHeartbeatTimer;
|
|
84
|
+
private clearHeartbeatTimer;
|
|
85
|
+
clearInstance(): void;
|
|
86
|
+
destroy(): void;
|
|
87
|
+
/**
|
|
88
|
+
* 退出:关闭 peer 和所有连接,停止自动重连。
|
|
89
|
+
* 与 destroy stop 后仍可调用 createPeer 恢复工作。
|
|
90
|
+
*/
|
|
91
|
+
stop(): void;
|
|
92
|
+
private destroyPeer;
|
|
93
|
+
askReload(peerId: string): void;
|
|
94
|
+
}
|
|
95
|
+
export {};
|