@decentnetwork/peer 0.1.110 → 0.1.112
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/compat/filetransfer.js +9 -6
- package/dist/index.d.ts +1 -1
- package/dist/peer.d.ts +14 -2
- package/dist/peer.js +180 -9
- package/dist/types/peer.d.ts +15 -0
- package/package.json +1 -1
|
@@ -785,12 +785,14 @@ export class FileTransferManager {
|
|
|
785
785
|
// One paced round completed (the timed frontier byte's ack landed).
|
|
786
786
|
const rtt = nowT - st.probeSentMs;
|
|
787
787
|
st.probeOffset = -1;
|
|
788
|
+
const pathKindAtProbe = this.pathKind(friendId);
|
|
788
789
|
// A single very-low sample can be a batched/late ACK that makes a probe
|
|
789
790
|
// look newer than it is. But several consecutive low samples mean the
|
|
790
|
-
// path genuinely improved
|
|
791
|
-
//
|
|
792
|
-
//
|
|
793
|
-
//
|
|
791
|
+
// path genuinely improved when we are moving onto a validated LAN path.
|
|
792
|
+
// Do NOT do this on public UDP-direct: cross-border ACK compression can
|
|
793
|
+
// produce several falsely-low samples, and rebasing RTprop there shrinks
|
|
794
|
+
// the BDP window to the floor, collapsing a good remote transfer to
|
|
795
|
+
// tens of KB/s.
|
|
794
796
|
const lowSample = st.srttMs > 0 && rtt > 0 && rtt < st.srttMs * 0.4;
|
|
795
797
|
if (lowSample) {
|
|
796
798
|
st.lowRttCount++;
|
|
@@ -800,7 +802,7 @@ export class FileTransferManager {
|
|
|
800
802
|
st.lowRttCount = 0;
|
|
801
803
|
st.lowRttMinMs = Infinity;
|
|
802
804
|
}
|
|
803
|
-
const promoteLowPath = lowSample && st.lowRttCount >= 3;
|
|
805
|
+
const promoteLowPath = pathKindAtProbe === "lan" && lowSample && st.lowRttCount >= 3;
|
|
804
806
|
const acceptedRtt = promoteLowPath ? st.lowRttMinMs : rtt;
|
|
805
807
|
if (promoteLowPath) {
|
|
806
808
|
st.srttMs = acceptedRtt;
|
|
@@ -823,7 +825,7 @@ export class FileTransferManager {
|
|
|
823
825
|
const roundRate = ((st.acked - st.rateMarkAcked) * 1000) / sampleMs;
|
|
824
826
|
st.rateMarkMs = nowT;
|
|
825
827
|
st.rateMarkAcked = st.acked;
|
|
826
|
-
const pathKind =
|
|
828
|
+
const pathKind = pathKindAtProbe;
|
|
827
829
|
const lanNow = pathKind === "lan";
|
|
828
830
|
const udpDirectNow = pathKind === "udp-direct";
|
|
829
831
|
// On a paced LAN flow, sustainable delivery cannot suddenly be
|
|
@@ -996,6 +998,7 @@ export class FileTransferManager {
|
|
|
996
998
|
` pace=${Math.round(st.paceRateBps)}Bps bw=${Math.round(st.btlBwBps)}Bps` +
|
|
997
999
|
` cwnd=${st.cwnd} phase=${st.bbrPhase} slow=${st.fullBwRounds}` +
|
|
998
1000
|
` loss=${(st.lossEwma * 100).toFixed(1)}% rounds=${st.roundCount}` +
|
|
1001
|
+
` path=${pathKindAtProbe}` +
|
|
999
1002
|
` lanNow=${st.dbgLanNow} lanPath=${st.lanPath ? 1 : 0}` +
|
|
1000
1003
|
` sampleMs=${st.dbgSampleMs} roundRate=${st.dbgRoundRate}` +
|
|
1001
1004
|
` measuredRate=${st.dbgMeasuredRate} rawQueue=${st.dbgRawQueue}` +
|
package/dist/index.d.ts
CHANGED
|
@@ -10,4 +10,4 @@ export { LegacyProtocolNotImplementedError } from "./runtime/errors.js";
|
|
|
10
10
|
export type { CarrierPacket, FriendMessagePacket, FriendRequestPacket, InviteReqPacket, InviteRspPacket } from "./compat/packet.js";
|
|
11
11
|
export type { ToxDhtCryptoRequest } from "./compat/tox-dht-crypto.js";
|
|
12
12
|
export type { CarrierAddressParts } from "./compat/address.js";
|
|
13
|
-
export type { CompatibilityMode, CustomPacketEvent, FriendConnectionEvent, FriendConnectionStatus, FriendInfoEvent, FriendRequest, InlineFileEvent, InviteEvent, InviteResponseEvent, LookupResult, NetworkNode, PeerOptions, TextMessage } from "./types/peer.js";
|
|
13
|
+
export type { CompatibilityMode, CustomPacketEvent, FriendConnectionEvent, FriendConnectionStatus, FriendInfoEvent, FriendRequest, InlineFileEvent, InviteEvent, InviteResponseEvent, LookupResult, NetworkNode, PeerOptions, SendTextUntilAckOptions, TextMessage } from "./types/peer.js";
|
package/dist/peer.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type BootstrapResult } from "./compat/bootstrap.js";
|
|
2
2
|
import type { FriendRecord } from "./store/friends.js";
|
|
3
|
-
import type { CustomPacketEvent, FriendConnectionEvent, FriendRequest, FriendInfoEvent, InlineFileEvent, InviteEvent, InviteResponseEvent, LookupResult, NetworkNode, PeerOptions, TextMessage } from "./types/peer.js";
|
|
3
|
+
import type { CustomPacketEvent, FriendConnectionEvent, FriendRequest, FriendInfoEvent, InlineFileEvent, InviteEvent, InviteResponseEvent, LookupResult, NetworkNode, PeerOptions, SendTextUntilAckOptions, TextMessage } from "./types/peer.js";
|
|
4
4
|
export declare class Peer {
|
|
5
5
|
#private;
|
|
6
6
|
private constructor();
|
|
@@ -84,9 +84,21 @@ export declare class Peer {
|
|
|
84
84
|
*/
|
|
85
85
|
removeFriend(pubkey: string): boolean;
|
|
86
86
|
sendText(pubkey: string, text: string): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Send text and keep retransmitting until the peer explicitly ACKs it.
|
|
89
|
+
*
|
|
90
|
+
* The receiving peer sends that ACK only after every onText handler returns
|
|
91
|
+
* successfully. If a handler persists to an inbox, return its write Promise
|
|
92
|
+
* from the handler; then this method resolves only after that durable write
|
|
93
|
+
* completed on the far side. Pass a stable deliveryId when retrying an item
|
|
94
|
+
* from an application outbox across process restarts.
|
|
95
|
+
*/
|
|
96
|
+
sendTextUntilAck(pubkey: string, text: string, opts?: SendTextUntilAckOptions): Promise<{
|
|
97
|
+
deliveryId: string;
|
|
98
|
+
}>;
|
|
87
99
|
waitForFriendConnected(pubkey: string, timeoutMs?: number): Promise<boolean>;
|
|
88
100
|
onFriendRequest(cb: (req: FriendRequest) => void): void;
|
|
89
|
-
onText(cb: (msg: TextMessage) =>
|
|
101
|
+
onText(cb: (msg: TextMessage) => unknown | Promise<unknown>): void;
|
|
90
102
|
/** Files received inline over (bulk)messages — the iOS/C Carrier apps'
|
|
91
103
|
* native way of sending images/audio online (FileModel JSON envelope). */
|
|
92
104
|
onInlineFile(cb: (evt: InlineFileEvent) => void): void;
|
package/dist/peer.js
CHANGED
|
@@ -168,15 +168,18 @@ const PEER_NICKNAME = process.env.DECENT_PEER_NAME ?? "@decentnetwork/peer";
|
|
|
168
168
|
const PEER_STATUS_MESSAGE = process.env.DECENT_PEER_STATUS_MESSAGE ?? "decent peer";
|
|
169
169
|
const GREETING_TEXT = process.env.DECENT_GREETING_TEXT ?? "";
|
|
170
170
|
// AgentNet wire-protocol version advertised in the userinfo profile (field 7).
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
const AGENTNET_PROTO_VERSION = 1;
|
|
171
|
+
// v1 marks a peer that supports the toxcore file-transfer channel. v2 adds
|
|
172
|
+
// application-level text delivery ACKs: senders keep outbox entries until the
|
|
173
|
+
// receiver's onText handler has returned successfully.
|
|
174
|
+
const AGENTNET_PROTO_VERSION = 2;
|
|
176
175
|
// Peer package version, advertised as the default appVersion when the embedder
|
|
177
176
|
// doesn't override it. Read lazily so a bundler that inlines this file doesn't
|
|
178
177
|
// need the package.json at runtime.
|
|
179
|
-
const PEER_PKG_VERSION = "0.1.
|
|
178
|
+
const PEER_PKG_VERSION = "0.1.112";
|
|
179
|
+
const TEXT_ACK_PREFIX = "\x1eDNPACK1:";
|
|
180
|
+
const TEXT_ACK_TIMEOUT_MS = readEnvInt("DECENT_TEXT_ACK_TIMEOUT_MS", 300_000);
|
|
181
|
+
const TEXT_ACK_RETRY_MS = readEnvInt("DECENT_TEXT_ACK_RETRY_MS", 5_000);
|
|
182
|
+
const TEXT_AUTO_ACK_TIMEOUT_MS = readEnvInt("DECENT_TEXT_AUTO_ACK_TIMEOUT_MS", 15_000);
|
|
180
183
|
// Toxcore Messenger.h packet IDs (live inside encrypted 0x1b crypto data plain payload)
|
|
181
184
|
const PACKET_ID_PADDING = 0;
|
|
182
185
|
const PACKET_ID_REQUEST = 1; // request retransmission of unreceived packets
|
|
@@ -273,6 +276,10 @@ export class Peer {
|
|
|
273
276
|
#nodeBlacklist = new Map();
|
|
274
277
|
#pendingFriendRequests = new Map();
|
|
275
278
|
#friends = new Map();
|
|
279
|
+
#textHandlers = new Set();
|
|
280
|
+
#pendingTextAcks = new Map();
|
|
281
|
+
#deliveredTextIds = new Set();
|
|
282
|
+
#deliveredTextOrder = [];
|
|
276
283
|
#friendStoreFile;
|
|
277
284
|
#persistSeq = 0; // makes atomic friend-store temp filenames unique per write
|
|
278
285
|
#cookieSymmetricKey;
|
|
@@ -1059,6 +1066,16 @@ export class Peer {
|
|
|
1059
1066
|
}
|
|
1060
1067
|
}
|
|
1061
1068
|
async sendText(pubkey, text) {
|
|
1069
|
+
if (text.length > 0 && !text.startsWith(TEXT_ACK_PREFIX) && this.#shouldRequireTextAck(pubkey)) {
|
|
1070
|
+
await this.sendTextUntilAck(pubkey, text, {
|
|
1071
|
+
timeoutMs: TEXT_AUTO_ACK_TIMEOUT_MS,
|
|
1072
|
+
retryIntervalMs: Math.min(TEXT_ACK_RETRY_MS, TEXT_AUTO_ACK_TIMEOUT_MS)
|
|
1073
|
+
});
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
await this.#sendTextPlain(pubkey, text);
|
|
1077
|
+
}
|
|
1078
|
+
async #sendTextPlain(pubkey, text) {
|
|
1062
1079
|
const friend = this.#friends.get(pubkey);
|
|
1063
1080
|
if (!friend) {
|
|
1064
1081
|
throw new Error(`Not a friend: ${pubkey}`);
|
|
@@ -1175,6 +1192,75 @@ export class Peer {
|
|
|
1175
1192
|
}
|
|
1176
1193
|
throw new Error("friend is offline and no express node is configured");
|
|
1177
1194
|
}
|
|
1195
|
+
#shouldRequireTextAck(pubkey) {
|
|
1196
|
+
const friend = this.#friends.get(pubkey);
|
|
1197
|
+
if ((friend?.protoVersion ?? 0) >= 2)
|
|
1198
|
+
return true;
|
|
1199
|
+
// decentlan data-plane peers run with expressControlPlaneOnly so an
|
|
1200
|
+
// accepted local send must not be treated as final delivery. A short ACK
|
|
1201
|
+
// timeout makes the caller keep its outbox item and retry on reconnect.
|
|
1202
|
+
return this.#opts.expressControlPlaneOnly === true;
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* Send text and keep retransmitting until the peer explicitly ACKs it.
|
|
1206
|
+
*
|
|
1207
|
+
* The receiving peer sends that ACK only after every onText handler returns
|
|
1208
|
+
* successfully. If a handler persists to an inbox, return its write Promise
|
|
1209
|
+
* from the handler; then this method resolves only after that durable write
|
|
1210
|
+
* completed on the far side. Pass a stable deliveryId when retrying an item
|
|
1211
|
+
* from an application outbox across process restarts.
|
|
1212
|
+
*/
|
|
1213
|
+
async sendTextUntilAck(pubkey, text, opts = {}) {
|
|
1214
|
+
const deliveryId = opts.deliveryId ?? createTextDeliveryId();
|
|
1215
|
+
const retryIntervalMs = Math.max(250, opts.retryIntervalMs ?? TEXT_ACK_RETRY_MS);
|
|
1216
|
+
const timeoutMs = Math.max(retryIntervalMs, opts.timeoutMs ?? TEXT_ACK_TIMEOUT_MS);
|
|
1217
|
+
const envelope = encodeTextAckEnvelope({ t: "msg", id: deliveryId, text });
|
|
1218
|
+
const started = Date.now();
|
|
1219
|
+
let lastError;
|
|
1220
|
+
while (Date.now() - started < timeoutMs) {
|
|
1221
|
+
const remaining = Math.max(1, timeoutMs - (Date.now() - started));
|
|
1222
|
+
const waitMs = Math.min(retryIntervalMs, remaining);
|
|
1223
|
+
const ackPromise = this.#waitForTextAck(deliveryId, waitMs);
|
|
1224
|
+
try {
|
|
1225
|
+
await this.#sendTextPlain(pubkey, envelope);
|
|
1226
|
+
}
|
|
1227
|
+
catch (error) {
|
|
1228
|
+
this.#cancelTextAckWait(deliveryId);
|
|
1229
|
+
lastError = error;
|
|
1230
|
+
await sleep(waitMs);
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
if (await ackPromise) {
|
|
1234
|
+
return { deliveryId };
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
this.#cancelTextAckWait(deliveryId);
|
|
1238
|
+
throw lastError ?? new Error(`text delivery ACK timed out for ${pubkey}`);
|
|
1239
|
+
}
|
|
1240
|
+
#waitForTextAck(deliveryId, timeoutMs) {
|
|
1241
|
+
return new Promise((resolve) => {
|
|
1242
|
+
const timer = setTimeout(() => {
|
|
1243
|
+
this.#pendingTextAcks.delete(deliveryId);
|
|
1244
|
+
resolve(false);
|
|
1245
|
+
}, timeoutMs);
|
|
1246
|
+
timer.unref?.();
|
|
1247
|
+
this.#pendingTextAcks.set(deliveryId, {
|
|
1248
|
+
resolve: () => {
|
|
1249
|
+
clearTimeout(timer);
|
|
1250
|
+
this.#pendingTextAcks.delete(deliveryId);
|
|
1251
|
+
resolve(true);
|
|
1252
|
+
},
|
|
1253
|
+
reject: () => {
|
|
1254
|
+
clearTimeout(timer);
|
|
1255
|
+
this.#pendingTextAcks.delete(deliveryId);
|
|
1256
|
+
resolve(false);
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
#cancelTextAckWait(deliveryId) {
|
|
1262
|
+
this.#pendingTextAcks.get(deliveryId)?.reject(new Error("text delivery ACK wait cancelled"));
|
|
1263
|
+
}
|
|
1178
1264
|
waitForFriendConnected(pubkey, timeoutMs = 30000) {
|
|
1179
1265
|
return this.#waitForFriendConnected(pubkey, timeoutMs);
|
|
1180
1266
|
}
|
|
@@ -1202,7 +1288,65 @@ export class Peer {
|
|
|
1202
1288
|
this.#events.on("friendRequest", cb);
|
|
1203
1289
|
}
|
|
1204
1290
|
onText(cb) {
|
|
1205
|
-
this.#
|
|
1291
|
+
this.#textHandlers.add(cb);
|
|
1292
|
+
}
|
|
1293
|
+
async #dispatchTextMessage(msg) {
|
|
1294
|
+
const envelope = decodeTextAckEnvelope(msg.text);
|
|
1295
|
+
if (envelope?.t === "ack") {
|
|
1296
|
+
this.#pendingTextAcks.get(envelope.id)?.resolve();
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
let deliveryId;
|
|
1300
|
+
let text = msg.text;
|
|
1301
|
+
if (envelope?.t === "msg") {
|
|
1302
|
+
deliveryId = envelope.id;
|
|
1303
|
+
text = envelope.text;
|
|
1304
|
+
if (this.#deliveredTextIds.has(deliveryId)) {
|
|
1305
|
+
await this.#sendTextAck(msg.pubkey, deliveryId);
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
if (this.#textHandlers.size === 0) {
|
|
1310
|
+
if (deliveryId)
|
|
1311
|
+
this.#debugLog(`text delivery ${deliveryId} from ${msg.pubkey} has no onText handlers; not ACKing`);
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
const ack = deliveryId
|
|
1315
|
+
? async () => { await this.#sendTextAck(msg.pubkey, deliveryId); }
|
|
1316
|
+
: undefined;
|
|
1317
|
+
const delivered = { ...msg, text, deliveryId, ack };
|
|
1318
|
+
try {
|
|
1319
|
+
for (const handler of this.#textHandlers) {
|
|
1320
|
+
await handler(delivered);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
catch (error) {
|
|
1324
|
+
this.#debugLog(`text handler failed for ${msg.pubkey}: ${error.message}`);
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
if (deliveryId) {
|
|
1328
|
+
this.#rememberDeliveredTextId(deliveryId);
|
|
1329
|
+
await ack?.();
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
async #sendTextAck(pubkey, deliveryId) {
|
|
1333
|
+
try {
|
|
1334
|
+
await this.sendText(pubkey, encodeTextAckEnvelope({ t: "ack", id: deliveryId }));
|
|
1335
|
+
}
|
|
1336
|
+
catch (error) {
|
|
1337
|
+
this.#debugLog(`text ACK send failed for ${pubkey}: ${error.message}`);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
#rememberDeliveredTextId(deliveryId) {
|
|
1341
|
+
if (this.#deliveredTextIds.has(deliveryId))
|
|
1342
|
+
return;
|
|
1343
|
+
this.#deliveredTextIds.add(deliveryId);
|
|
1344
|
+
this.#deliveredTextOrder.push(deliveryId);
|
|
1345
|
+
while (this.#deliveredTextOrder.length > 4096) {
|
|
1346
|
+
const old = this.#deliveredTextOrder.shift();
|
|
1347
|
+
if (old)
|
|
1348
|
+
this.#deliveredTextIds.delete(old);
|
|
1349
|
+
}
|
|
1206
1350
|
}
|
|
1207
1351
|
/** Files received inline over (bulk)messages — the iOS/C Carrier apps'
|
|
1208
1352
|
* native way of sending images/audio online (FileModel JSON envelope). */
|
|
@@ -2620,7 +2764,7 @@ export class Peer {
|
|
|
2620
2764
|
// (iOS posts the whole thing as one express MESSAGE packet).
|
|
2621
2765
|
if (this.#tryEmitInlineFile(fromUserId, text, "offline"))
|
|
2622
2766
|
return;
|
|
2623
|
-
this.#
|
|
2767
|
+
void this.#dispatchTextMessage({ pubkey: fromUserId, text, via: "offline" });
|
|
2624
2768
|
}
|
|
2625
2769
|
catch {
|
|
2626
2770
|
// Ignore invalid offline payloads.
|
|
@@ -3607,7 +3751,7 @@ export class Peer {
|
|
|
3607
3751
|
// as files, not as a wall of base64 text.
|
|
3608
3752
|
if (this.#tryEmitInlineFile(friendId, text, "online"))
|
|
3609
3753
|
return;
|
|
3610
|
-
this.#
|
|
3754
|
+
void this.#dispatchTextMessage({
|
|
3611
3755
|
pubkey: friendId,
|
|
3612
3756
|
text,
|
|
3613
3757
|
via: "online"
|
|
@@ -6057,6 +6201,33 @@ function decodeUtf8Best(payload) {
|
|
|
6057
6201
|
return "";
|
|
6058
6202
|
}
|
|
6059
6203
|
}
|
|
6204
|
+
function encodeTextAckEnvelope(envelope) {
|
|
6205
|
+
return TEXT_ACK_PREFIX + Buffer.from(JSON.stringify(envelope), "utf8").toString("base64url");
|
|
6206
|
+
}
|
|
6207
|
+
function decodeTextAckEnvelope(text) {
|
|
6208
|
+
if (!text.startsWith(TEXT_ACK_PREFIX))
|
|
6209
|
+
return undefined;
|
|
6210
|
+
try {
|
|
6211
|
+
const raw = Buffer.from(text.slice(TEXT_ACK_PREFIX.length), "base64url").toString("utf8");
|
|
6212
|
+
const parsed = JSON.parse(raw);
|
|
6213
|
+
if (parsed.t === "ack" && typeof parsed.id === "string" && parsed.id.length > 0) {
|
|
6214
|
+
return { t: "ack", id: parsed.id };
|
|
6215
|
+
}
|
|
6216
|
+
if (parsed.t === "msg" &&
|
|
6217
|
+
typeof parsed.id === "string" &&
|
|
6218
|
+
parsed.id.length > 0 &&
|
|
6219
|
+
typeof parsed.text === "string") {
|
|
6220
|
+
return { t: "msg", id: parsed.id, text: parsed.text };
|
|
6221
|
+
}
|
|
6222
|
+
}
|
|
6223
|
+
catch {
|
|
6224
|
+
return undefined;
|
|
6225
|
+
}
|
|
6226
|
+
return undefined;
|
|
6227
|
+
}
|
|
6228
|
+
function createTextDeliveryId() {
|
|
6229
|
+
return `${Date.now().toString(36)}-${Buffer.from(randomBytes(12)).toString("base64url")}`;
|
|
6230
|
+
}
|
|
6060
6231
|
function tryDecodeCarrierMessagePacket(payload) {
|
|
6061
6232
|
try {
|
|
6062
6233
|
const decoded = decodeCarrierPacket(payload);
|
package/dist/types/peer.d.ts
CHANGED
|
@@ -122,11 +122,26 @@ export type FriendInfoEvent = {
|
|
|
122
122
|
export type TextMessage = {
|
|
123
123
|
pubkey: string;
|
|
124
124
|
text: string;
|
|
125
|
+
/** Stable id for SDK-level acknowledged delivery. Present only for messages
|
|
126
|
+
* sent with sendTextUntilAck(). Receivers can persist this id for dedupe. */
|
|
127
|
+
deliveryId?: string;
|
|
128
|
+
/** Send the delivery ACK. The SDK calls this automatically after all onText
|
|
129
|
+
* handlers return successfully; handlers that need durable inbox semantics
|
|
130
|
+
* should return a Promise that resolves only after the inbox write is done. */
|
|
131
|
+
ack?: () => Promise<void>;
|
|
125
132
|
/** Delivery path: "online" = live net_crypto session (direct/relay), "offline"
|
|
126
133
|
* = express store-and-forward. Lets the UI color the two differently so a user
|
|
127
134
|
* can see when online delivery is failing and only offline messages land. */
|
|
128
135
|
via?: "online" | "offline";
|
|
129
136
|
};
|
|
137
|
+
export type SendTextUntilAckOptions = {
|
|
138
|
+
/** Stable id for retries across a caller-managed outbox. Defaults to a random id. */
|
|
139
|
+
deliveryId?: string;
|
|
140
|
+
/** How long to keep retrying before rejecting. Defaults to 5 minutes. */
|
|
141
|
+
timeoutMs?: number;
|
|
142
|
+
/** Delay between retransmit attempts while no ACK has arrived. Defaults to 5s. */
|
|
143
|
+
retryIntervalMs?: number;
|
|
144
|
+
};
|
|
130
145
|
/**
|
|
131
146
|
* An application-defined custom packet received from a friend, over the
|
|
132
147
|
* toxcore custom packet ranges: lossless `160–191` (reliable, ordered) or
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/peer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.112",
|
|
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",
|