@decentnetwork/peer 0.1.77 → 0.1.79

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.
@@ -54,6 +54,7 @@ export declare class FileTransferManager {
54
54
  }): string | null;
55
55
  accept(friendId: string, fileNumber: number): void;
56
56
  cancel(friendId: string, fileNumber: number, isSending: boolean): void;
57
+ cancelByFileId(friendId: string, fileIdHex: string, isSending: boolean): boolean;
57
58
  handlePacket(friendId: string, packetId: number, payload: Uint8Array): boolean;
58
59
  clearFriend(friendId: string): void;
59
60
  }
@@ -311,6 +311,20 @@ export class FileTransferManager {
311
311
  else
312
312
  this.#endRecv(friendId, fileNumber);
313
313
  }
314
+ // Cancel by content fileId (hex) — the daemon/UI tracks transfers by fileId,
315
+ // not the internal fileNumber, so it can stop a specific in-flight transfer.
316
+ cancelByFileId(friendId, fileIdHex, isSending) {
317
+ const inner = (isSending ? this.#sending : this.#receiving).get(friendId);
318
+ if (!inner)
319
+ return false;
320
+ for (const [fileNumber, st] of inner) {
321
+ if (hex(st.fileId) === fileIdHex) {
322
+ this.cancel(friendId, fileNumber, isSending);
323
+ return true;
324
+ }
325
+ }
326
+ return false;
327
+ }
314
328
  handlePacket(friendId, packetId, payload) {
315
329
  if (packetId === PACKET_ID_FILE_SENDREQUEST) {
316
330
  this.#onSendRequest(friendId, payload);
@@ -751,8 +765,7 @@ export class FileTransferManager {
751
765
  // so a dup-ack burst can't storm). No window backoff: loss ≠ congestion.
752
766
  st.lastResendMs = nowMs();
753
767
  st.lastLossMs = nowMs(); // the path is lossy → turn adaptive FEC on
754
- if (st.nextSend > st.acked)
755
- st.nextSend = st.acked;
768
+ this.#rewindRetransmit(st);
756
769
  }
757
770
  if (st.acked >= st.size) {
758
771
  this.#completeSend(friendId, st);
@@ -760,6 +773,21 @@ export class FileTransferManager {
760
773
  }
761
774
  void this.#pump(friendId, st);
762
775
  }
776
+ // Rewind for a go-back-N retransmit: move the send cursor back to the ack point
777
+ // AND rewind the FEC high-water mark so the re-sent blocks get FRESH parity.
778
+ // Without the parity rewind a lossy TAIL freezes forever — re-sending the
779
+ // region drops new chunks faster than it fills the old holes, and the receiver
780
+ // has no parity to recover them locally (parity was emitted once, long gone).
781
+ // Re-emitting parity lets the receiver FEC-recover the holes, so the tail
782
+ // converges instead of stalling at 93% (observed on 100 MB+ files).
783
+ #rewindRetransmit(st) {
784
+ if (st.nextSend > st.acked)
785
+ st.nextSend = st.acked;
786
+ const ackBlock = Math.floor(st.acked / MAX_FILE_DATA_SIZE / FEC_K);
787
+ if (ackBlock - 1 < st.parityHighBlock)
788
+ st.parityHighBlock = ackBlock - 1;
789
+ st.lastLossMs = nowMs(); // keep FEC ON so the re-emit actually fires
790
+ }
763
791
  // Single send loop. Awaits each send so net_crypto / the UDP socket applies
764
792
  // backpressure (fire-and-forget blasted the whole window into the macOS UDP
765
793
  // buffer at once → systematic overflow/drops the retransmit couldn't dig out
@@ -803,8 +831,7 @@ export class FileTransferManager {
803
831
  st.cwnd = Math.max(CWND_MIN_CHUNKS, Math.floor(st.cwnd / 2));
804
832
  st.lastAckAdvanceMs = nowMs();
805
833
  st.lastResendMs = nowMs();
806
- if (st.nextSend > st.acked)
807
- st.nextSend = st.acked; // go-back-N
834
+ this.#rewindRetransmit(st); // go-back-N + re-emit tail parity
808
835
  void this.#pump(friendId, st);
809
836
  }, WATCHDOG_MS);
810
837
  if (typeof st.timer.unref === "function")
package/dist/peer.d.ts CHANGED
@@ -111,6 +111,8 @@ export declare class Peer {
111
111
  acceptFile(userid: string, fileNumber: number): void;
112
112
  /** Cancel a transfer (isSending = true if we're the sender). */
113
113
  cancelFile(userid: string, fileNumber: number, isSending?: boolean): void;
114
+ /** Cancel an in-flight transfer by its content fileId (hex). Returns true if found. */
115
+ cancelFileById(userid: string, fileId: string, isSending?: boolean): boolean;
114
116
  /** Incoming file offer: { friendId, fileNumber, fileId, name, size, kind }. */
115
117
  onFile(cb: (offer: {
116
118
  friendId: string;
package/dist/peer.js CHANGED
@@ -1032,6 +1032,8 @@ export class Peer {
1032
1032
  acceptFile(userid, fileNumber) { this.#fileTransfer.accept(userid, fileNumber); }
1033
1033
  /** Cancel a transfer (isSending = true if we're the sender). */
1034
1034
  cancelFile(userid, fileNumber, isSending = false) { this.#fileTransfer.cancel(userid, fileNumber, isSending); }
1035
+ /** Cancel an in-flight transfer by its content fileId (hex). Returns true if found. */
1036
+ cancelFileById(userid, fileId, isSending = false) { return this.#fileTransfer.cancelByFileId(userid, fileId, isSending); }
1035
1037
  /** Incoming file offer: { friendId, fileNumber, fileId, name, size, kind }. */
1036
1038
  onFile(cb) { this.#events.on("file-offer", cb); }
1037
1039
  /** Transfer progress: { friendId, fileId, received, total, sending? }. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/peer",
3
- "version": "0.1.77",
3
+ "version": "0.1.79",
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",