@decentnetwork/lan 0.1.278 → 0.1.279

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.
@@ -95,8 +95,11 @@ export declare class PeerManager extends EventEmitter {
95
95
  sendAppPacket(userid: string, id: number, data: Uint8Array): Promise<void>;
96
96
  /** Send a file INLINE over the message channel (FileModel JSON envelope,
97
97
  * bulkmsg-split) — the only file path native iOS/C Carrier clients can
98
- * receive online. Use for native friends; JS friends keep sendFile(). */
99
- sendInlineFile(userid: string, data: Uint8Array, name: string): Promise<void>;
98
+ * receive online. Use for native friends; JS friends keep sendFile().
99
+ * Returns the SDK's delivery verdict: "acked" is the only outcome that
100
+ * justifies a "sent" checkmark. Older SDKs (< 0.1.140) resolve void —
101
+ * treat that as "accepted" (bytes out, delivery unproven). */
102
+ sendInlineFile(userid: string, data: Uint8Array, name: string): Promise<"acked" | "accepted" | "offline">;
100
103
  /**
101
104
  * Send a WebRTC call-signaling payload to a friend over the Carrier
102
105
  * friend-invite channel (the "carrier" extension), the exact transport the
@@ -186,11 +186,15 @@ export class PeerManager extends EventEmitter {
186
186
  }
187
187
  /** Send a file INLINE over the message channel (FileModel JSON envelope,
188
188
  * bulkmsg-split) — the only file path native iOS/C Carrier clients can
189
- * receive online. Use for native friends; JS friends keep sendFile(). */
189
+ * receive online. Use for native friends; JS friends keep sendFile().
190
+ * Returns the SDK's delivery verdict: "acked" is the only outcome that
191
+ * justifies a "sent" checkmark. Older SDKs (< 0.1.140) resolve void —
192
+ * treat that as "accepted" (bytes out, delivery unproven). */
190
193
  async sendInlineFile(userid, data, name) {
191
194
  if (!this.peer)
192
195
  throw new Error("Peer not created. Call create() first.");
193
- await this.peer.sendInlineFile(userid, { name, data });
196
+ const r = (await this.peer.sendInlineFile(userid, { name, data }));
197
+ return r?.delivery ?? "accepted";
194
198
  }
195
199
  /**
196
200
  * Send a WebRTC call-signaling payload to a friend over the Carrier
@@ -578,12 +578,35 @@ export class DaemonServer {
578
578
  throw new Error(`This friend is a native (iOS/Android) client — files are sent inline and limited to ` +
579
579
  `${(INLINE_MAX / 1024 / 1024).toFixed(1)} MB (this is ${(data.length / 1024 / 1024).toFixed(1)} MB).`);
580
580
  }
581
- await this.peerManager.sendInlineFile(userid, new Uint8Array(data), name);
582
- this.logger.info(`Sent inline file "${name}" (${data.length}B) to native peer ${userid.slice(0, 8)}`);
583
- this.messageStore?.appendFile(userid, "out", { name: sanitizeFileName(name), size: data.length, status: "sent", sent: data.length });
581
+ // Append the chip as "sending" FIRST, then let the SDK's delivery
582
+ // verdict decide what it becomes. Marking "sent" the moment
583
+ // sendInlineFile resolved was a lie — that only meant the bytes
584
+ // left this machine; a half-open session swallowed them silently
585
+ // while the sender stared at a checkmark (INBOX 2026-08-21 A).
586
+ const safeName = sanitizeFileName(name);
587
+ const chip = this.messageStore?.appendFile(userid, "out", { name: safeName, size: data.length, status: "sending", sent: 0 });
584
588
  this.friendMeta?.ensure(userid);
585
589
  this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
586
- return { inline: true, name, size: data.length };
590
+ let delivery;
591
+ try {
592
+ delivery = await this.peerManager.sendInlineFile(userid, new Uint8Array(data), name);
593
+ }
594
+ catch (e) {
595
+ if (chip)
596
+ this.messageStore?.patchFile(userid, chip.id, { status: "failed" });
597
+ this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
598
+ throw e;
599
+ }
600
+ const status = delivery === "acked" ? "sent" : delivery === "offline" ? "queued" : "failed";
601
+ if (chip)
602
+ this.messageStore?.patchFile(userid, chip.id, { status, sent: delivery === "acked" ? data.length : 0 });
603
+ this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
604
+ this.logger.info(`Inline file "${name}" (${data.length}B) to native ${userid.slice(0, 8)}: delivery=${delivery} → ${status}`);
605
+ if (delivery === "accepted") {
606
+ throw new Error(`The peer never confirmed receiving "${name}" — it likely did not arrive. ` +
607
+ `Check that their app is in the foreground and try again.`);
608
+ }
609
+ return { inline: true, name, size: data.length, delivery };
587
610
  }
588
611
  // Add the "out" chip immediately as STATUS=sending (not "sent" — the
589
612
  // transfer is reliable+acked, so it only flips to "sent" once the
@@ -1,4 +1,4 @@
1
- window.__DK_UI_VERSION="0.1.278";
1
+ window.__DK_UI_VERSION="0.1.279";
2
2
  const ICON_PATHS = {
3
3
  // ---- tab bar (the four must feel like one set) ----
4
4
  users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/lan",
3
- "version": "0.1.278",
3
+ "version": "0.1.279",
4
4
  "description": "Private virtual LAN for self-hosted services and AI agents, built on Elastos Carrier. NAT-traversal, name service, ACL, all over a peer-to-peer mesh — no public IP required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -84,7 +84,7 @@
84
84
  },
85
85
  "dependencies": {
86
86
  "@decentnetwork/dora": "^0.1.14",
87
- "@decentnetwork/peer": "^0.1.139",
87
+ "@decentnetwork/peer": "^0.1.140",
88
88
  "@decentnetwork/peer-webrtc": "^0.2.10",
89
89
  "ink": "^5.2.1",
90
90
  "js-yaml": "^4.1.0",