@decentnetwork/peer 0.1.157 → 0.1.159
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/peer.d.ts +22 -1
- package/dist/peer.js +63 -12
- package/package.json +1 -1
package/dist/peer.d.ts
CHANGED
|
@@ -97,7 +97,28 @@ export declare class Peer {
|
|
|
97
97
|
routes: number;
|
|
98
98
|
targets: number;
|
|
99
99
|
} | undefined;
|
|
100
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Accept a friend request.
|
|
102
|
+
*
|
|
103
|
+
* `#pendingFriendRequests` is an in-memory cache, not the authority on what
|
|
104
|
+
* was asked. A host that remembers requests across a restart — beagle-web
|
|
105
|
+
* keeps them in IndexedDB, because a request usually arrives while the tab is
|
|
106
|
+
* closed — is doing the right thing, and refusing it here made every restored
|
|
107
|
+
* card permanently un-acceptable: click Accept, the SDK throws "no pending
|
|
108
|
+
* friend request", the card comes back, and the button reads as dead. That is
|
|
109
|
+
* the whole of the reported "Accept does not respond" on app.beagle.chat.
|
|
110
|
+
*
|
|
111
|
+
* Accepting is the host's decision. Take it, and let the host pass back what
|
|
112
|
+
* it remembered so the friend does not arrive nameless — anything missing is
|
|
113
|
+
* filled in by the userinfo exchange once the session comes up.
|
|
114
|
+
*/
|
|
115
|
+
acceptFriendRequest(pubkey: string, opts?: {
|
|
116
|
+
name?: string;
|
|
117
|
+
description?: string;
|
|
118
|
+
hello?: string;
|
|
119
|
+
address?: string;
|
|
120
|
+
nospam?: number;
|
|
121
|
+
}): Promise<void>;
|
|
101
122
|
/** Decline an inbound request.
|
|
102
123
|
*
|
|
103
124
|
* Also drops any friend record and session the request already created.
|
package/dist/peer.js
CHANGED
|
@@ -103,6 +103,10 @@ const FAULT_REQUEST_HANG = process.env.DECENT_FAULT_REQUEST_HANG === "1";
|
|
|
103
103
|
// toxcore expires announce entries on roughly this timescale, so a node that
|
|
104
104
|
// acknowledged a store longer ago than this can no longer be counted on.
|
|
105
105
|
const ANNOUNCE_ENTRY_TTL_MS = readEnvInt("DECENT_ANNOUNCE_ENTRY_TTL_MS", 300_000);
|
|
106
|
+
// How long a partial BULKMSG may go with no new fragment before it is
|
|
107
|
+
// abandoned. Refreshed on every fragment, so it bounds a STALLED transfer, not
|
|
108
|
+
// a large one.
|
|
109
|
+
const BULK_ASSEMBLY_IDLE_MS = readEnvInt("DECENT_BULK_ASSEMBLY_IDLE_MS", 60_000);
|
|
106
110
|
const FAULT_ANNOUNCE_HANG_RUN = readEnvInt("DECENT_FAULT_ANNOUNCE_HANG", 0);
|
|
107
111
|
// Classic-DHT maintenance cadence. Every tick we get_nodes toward our own key
|
|
108
112
|
// (so neighbours store our address and native peers can find our UDP endpoint)
|
|
@@ -1245,20 +1249,49 @@ export class Peer {
|
|
|
1245
1249
|
lastFriendRequestDispatch() {
|
|
1246
1250
|
return this.#lastFriendRequestDispatch;
|
|
1247
1251
|
}
|
|
1248
|
-
|
|
1252
|
+
/**
|
|
1253
|
+
* Accept a friend request.
|
|
1254
|
+
*
|
|
1255
|
+
* `#pendingFriendRequests` is an in-memory cache, not the authority on what
|
|
1256
|
+
* was asked. A host that remembers requests across a restart — beagle-web
|
|
1257
|
+
* keeps them in IndexedDB, because a request usually arrives while the tab is
|
|
1258
|
+
* closed — is doing the right thing, and refusing it here made every restored
|
|
1259
|
+
* card permanently un-acceptable: click Accept, the SDK throws "no pending
|
|
1260
|
+
* friend request", the card comes back, and the button reads as dead. That is
|
|
1261
|
+
* the whole of the reported "Accept does not respond" on app.beagle.chat.
|
|
1262
|
+
*
|
|
1263
|
+
* Accepting is the host's decision. Take it, and let the host pass back what
|
|
1264
|
+
* it remembered so the friend does not arrive nameless — anything missing is
|
|
1265
|
+
* filled in by the userinfo exchange once the session comes up.
|
|
1266
|
+
*/
|
|
1267
|
+
async acceptFriendRequest(pubkey, opts) {
|
|
1249
1268
|
const request = this.#pendingFriendRequests.get(pubkey);
|
|
1250
|
-
if (!request) {
|
|
1251
|
-
|
|
1269
|
+
if (!request && !opts) {
|
|
1270
|
+
// No cached request AND the caller told us nothing: it can still be
|
|
1271
|
+
// accepted, but say so, because it means the host is not passing on what
|
|
1272
|
+
// it knows and the friend will start out anonymous.
|
|
1273
|
+
this.#debugLog(`accepting ${pubkey} with no cached request and no details from the host`);
|
|
1274
|
+
}
|
|
1275
|
+
let key;
|
|
1276
|
+
try {
|
|
1277
|
+
key = base58ToBytes(pubkey);
|
|
1278
|
+
}
|
|
1279
|
+
catch {
|
|
1280
|
+
throw new Error(`Not a valid peer key: ${pubkey}`);
|
|
1252
1281
|
}
|
|
1282
|
+
if (key.length !== 32) {
|
|
1283
|
+
throw new Error(`Not a valid peer key: ${pubkey}`);
|
|
1284
|
+
}
|
|
1285
|
+
const nospam = request?.nospam ?? opts?.nospam ?? 0;
|
|
1253
1286
|
this.#pendingFriendRequests.delete(pubkey);
|
|
1254
1287
|
this.#friends.set(pubkey, {
|
|
1255
1288
|
pubkey,
|
|
1256
|
-
userid: request
|
|
1257
|
-
address: request
|
|
1258
|
-
nospam
|
|
1259
|
-
name: request
|
|
1260
|
-
description: request
|
|
1261
|
-
hello: request
|
|
1289
|
+
userid: request?.userid ?? pubkey,
|
|
1290
|
+
address: request?.address ?? opts?.address ?? carrierAddressFromPublicKey(key, nospam),
|
|
1291
|
+
nospam,
|
|
1292
|
+
name: request?.name ?? opts?.name ?? "",
|
|
1293
|
+
description: request?.description ?? opts?.description ?? "",
|
|
1294
|
+
hello: request?.hello ?? opts?.hello ?? "",
|
|
1262
1295
|
status: "offline",
|
|
1263
1296
|
acceptedAt: Date.now()
|
|
1264
1297
|
});
|
|
@@ -4797,10 +4830,25 @@ export class Peer {
|
|
|
4797
4830
|
* lost fragment just expires this one message after 60s, nothing else). */
|
|
4798
4831
|
#assembleBulkMsg(friendId, frag, packetNumber) {
|
|
4799
4832
|
const now = Date.now();
|
|
4800
|
-
// Lazy expiry sweep
|
|
4833
|
+
// Lazy expiry sweep. The deadline is IDLE time, not total time.
|
|
4834
|
+
//
|
|
4835
|
+
// It used to be set once, at creation, and never extended — so an assembly
|
|
4836
|
+
// was thrown away 60s after its first fragment no matter how well it was
|
|
4837
|
+
// going. A phone photo is several megabytes; base64 makes it larger still,
|
|
4838
|
+
// and over a TCP relay that takes well over a minute. The transfer was
|
|
4839
|
+
// discarded mid-flight, with every fragment still arriving, and nothing was
|
|
4840
|
+
// logged. Small files were unaffected, which is why this survived testing:
|
|
4841
|
+
// a 300 KB file completes in seconds and never reaches the deadline.
|
|
4842
|
+
//
|
|
4843
|
+
// Idle is the right measure — give up when the SENDER stops, not when the
|
|
4844
|
+
// file is big. Unbounded growth is still prevented by the 16 MB size cap
|
|
4845
|
+
// that totalsz is checked against below.
|
|
4801
4846
|
for (const [key, entry] of this.#bulkAssembly) {
|
|
4802
|
-
if (entry.expireAtMs < now)
|
|
4847
|
+
if (entry.expireAtMs < now) {
|
|
4848
|
+
this.#debugLog(`bulkmsg ${key} abandoned: no fragment for ${Math.round(BULK_ASSEMBLY_IDLE_MS / 1000)}s ` +
|
|
4849
|
+
`(had ${entry.got}/${entry.total} bytes)`);
|
|
4803
4850
|
this.#bulkAssembly.delete(key);
|
|
4851
|
+
}
|
|
4804
4852
|
}
|
|
4805
4853
|
const key = `${friendId}:${frag.tid.toString()}`;
|
|
4806
4854
|
// A completed assembly is deleted, so a second copy of the same fragments
|
|
@@ -4823,7 +4871,7 @@ export class Peer {
|
|
|
4823
4871
|
}
|
|
4824
4872
|
let entry = this.#bulkAssembly.get(key);
|
|
4825
4873
|
if (!entry) {
|
|
4826
|
-
entry = { total: 0, frags: new Map(), got: 0, expireAtMs: now +
|
|
4874
|
+
entry = { total: 0, frags: new Map(), got: 0, expireAtMs: now + BULK_ASSEMBLY_IDLE_MS };
|
|
4827
4875
|
this.#bulkAssembly.set(key, entry);
|
|
4828
4876
|
}
|
|
4829
4877
|
// The first fragment carries totalsz; it may arrive out of order.
|
|
@@ -4837,6 +4885,9 @@ export class Peer {
|
|
|
4837
4885
|
}
|
|
4838
4886
|
// Store by packet number; a duplicate (dual-send) fragment is ignored.
|
|
4839
4887
|
if (frag.data.length > 0 && !entry.frags.has(packetNumber)) {
|
|
4888
|
+
// Progress: push the idle deadline out. This is what makes the timeout
|
|
4889
|
+
// mean "the sender went away" instead of "the file was too big".
|
|
4890
|
+
entry.expireAtMs = now + BULK_ASSEMBLY_IDLE_MS;
|
|
4840
4891
|
entry.frags.set(packetNumber, frag.data);
|
|
4841
4892
|
entry.got += frag.data.length;
|
|
4842
4893
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/peer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.159",
|
|
4
4
|
"description": "Pure TypeScript port of Elastos Carrier (toxcore-derived) P2P messaging. DHT, onion routing, TCP relay, FlatBuffers app payloads, Express offline relay. Wire-compatible with iOS Beagle and the Carrier C SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|