@decentnetwork/lan 0.1.282 → 0.1.284
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/daemon/server.js +53 -65
- package/dist/ui/desktop/app.js +1 -1
- package/package.json +1 -1
package/dist/daemon/server.js
CHANGED
|
@@ -393,7 +393,11 @@ export class DaemonServer {
|
|
|
393
393
|
this.ipcEvents.setMaxListeners(50);
|
|
394
394
|
this.ipcServer = new IpcServer(ipcSocketPath(this.config.carrier.dataDir), {
|
|
395
395
|
friendRequest: async (address, hello) => {
|
|
396
|
-
|
|
396
|
+
// sendFriendRequest waits for our own announce to store and then
|
|
397
|
+
// walks the DHT toward the target — tens of seconds is normal, and
|
|
398
|
+
// none of it changes what the caller should do. Awaiting it made
|
|
399
|
+
// "add" look like a dead button for the whole walk.
|
|
400
|
+
void this.peerManager.sendFriendRequest(address, hello).catch((e) => this.logger.warn(`friend-request to ${String(address).slice(0, 8)} failed: ${e.message}`));
|
|
397
401
|
},
|
|
398
402
|
friendsPending: async () => {
|
|
399
403
|
const entries = this.pendingFriends?.list() ?? [];
|
|
@@ -412,7 +416,14 @@ export class DaemonServer {
|
|
|
412
416
|
const entry = this.pendingFriends?.remove(userid);
|
|
413
417
|
if (!entry)
|
|
414
418
|
throw new Error(`No pending friend-request for userid ${userid}`);
|
|
415
|
-
|
|
419
|
+
// Answer as soon as the entry is gone from pending — that is the part
|
|
420
|
+
// the UI renders. The node call is effectively local (its session
|
|
421
|
+
// attempt is already fire-and-forget), so waiting on it only delayed
|
|
422
|
+
// the click. Put the entry back if it does refuse.
|
|
423
|
+
void this.peerManager.acceptFriendRequest(entry.pubkey).catch((e) => {
|
|
424
|
+
this.pendingFriends?.add(entry);
|
|
425
|
+
this.logger.warn(`accept ${userid.slice(0, 8)} failed: ${e.message}`);
|
|
426
|
+
});
|
|
416
427
|
},
|
|
417
428
|
friendsReject: async (userid) => {
|
|
418
429
|
const entry = this.pendingFriends?.remove(userid);
|
|
@@ -423,23 +434,37 @@ export class DaemonServer {
|
|
|
423
434
|
chatSend: async (userid, text) => {
|
|
424
435
|
if (!text)
|
|
425
436
|
return;
|
|
426
|
-
//
|
|
427
|
-
//
|
|
428
|
-
//
|
|
429
|
-
|
|
437
|
+
// Record the message BEFORE any network attempt, and return without
|
|
438
|
+
// waiting for the wire.
|
|
439
|
+
//
|
|
440
|
+
// The offline branch already did this. The ONLINE branch awaited
|
|
441
|
+
// sendText and only logged afterwards — and sendText is not quick:
|
|
442
|
+
// with the ack path it retries for up to 15s. So a message to an
|
|
443
|
+
// ONLINE friend did not appear in the sender's own thread until
|
|
444
|
+
// delivery finished, while a message to an offline one appeared at
|
|
445
|
+
// once. The faster the peer, the less you noticed; the slower, the
|
|
446
|
+
// more it looked like the app had ignored you.
|
|
447
|
+
//
|
|
448
|
+
// Same rule the embedded host and the browser client already follow:
|
|
449
|
+
// a delivery failure is a STATE on the message, never its absence.
|
|
450
|
+
const msg = this.messageStore?.append(userid, "out", text, Date.now(), "queued");
|
|
451
|
+
this.friendMeta?.ensure(userid);
|
|
452
|
+
this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
|
|
453
|
+
if (!msg || !this.peerManager?.isFriendOnline(userid)) {
|
|
454
|
+
this.logger.info(`Queued text for ${userid.slice(0, 8)} (delivers on reconnect)`);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
// Deliver in the background; the reconnect flush covers a failure.
|
|
458
|
+
void (async () => {
|
|
430
459
|
try {
|
|
431
460
|
await this.peerManager.sendText(userid, text);
|
|
432
|
-
this.
|
|
433
|
-
return;
|
|
461
|
+
this.messageStore?.setStatus(userid, msg.id, undefined); // delivered
|
|
434
462
|
}
|
|
435
463
|
catch (e) {
|
|
436
|
-
this.logger.warn(`sendText to ${userid.slice(0, 8)} failed,
|
|
464
|
+
this.logger.warn(`sendText to ${userid.slice(0, 8)} failed, stays queued: ${e.message}`);
|
|
437
465
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
this.friendMeta?.ensure(userid);
|
|
441
|
-
this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
|
|
442
|
-
this.logger.info(`Queued text for offline ${userid.slice(0, 8)} (delivers on reconnect)`);
|
|
466
|
+
this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
|
|
467
|
+
})();
|
|
443
468
|
},
|
|
444
469
|
chatLogLocal: async (userid, dir, text) => {
|
|
445
470
|
if (!text)
|
|
@@ -564,57 +589,20 @@ export class DaemonServer {
|
|
|
564
589
|
this.logger.info(`Queued file "${name}" (${data.length}B) for offline ${userid.slice(0, 8)} (delivers on reconnect)`);
|
|
565
590
|
return { queued: true, name: safe, size: data.length };
|
|
566
591
|
}
|
|
567
|
-
// NATIVE friends (iOS/Android/C Carrier)
|
|
568
|
-
//
|
|
569
|
-
//
|
|
570
|
-
//
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
//
|
|
575
|
-
//
|
|
576
|
-
//
|
|
577
|
-
//
|
|
578
|
-
//
|
|
579
|
-
//
|
|
580
|
-
//
|
|
581
|
-
const INLINE_NATIVE_MAX = 256 * 1024;
|
|
582
|
-
if (this.peerManager?.isNativeFriend(userid) && data.length <= INLINE_NATIVE_MAX) {
|
|
583
|
-
// Append the chip as "sending" FIRST, then let the SDK's delivery
|
|
584
|
-
// verdict decide what it becomes. Marking "sent" the moment
|
|
585
|
-
// sendInlineFile resolved was a lie — that only meant the bytes
|
|
586
|
-
// left this machine; a half-open session swallowed them silently
|
|
587
|
-
// while the sender stared at a checkmark (INBOX 2026-08-21 A).
|
|
588
|
-
const safeName = sanitizeFileName(name);
|
|
589
|
-
const chip = this.messageStore?.appendFile(userid, "out", { name: safeName, size: data.length, status: "sending", sent: 0 });
|
|
590
|
-
this.friendMeta?.ensure(userid);
|
|
591
|
-
this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
|
|
592
|
-
// Sends are NON-BLOCKING (the 2026-08-05 rule): the delivery wait
|
|
593
|
-
// scales with size (up to 180s), and holding the HTTP request open
|
|
594
|
-
// that long froze the UI on an 8MB inline send. Return now; the
|
|
595
|
-
// verdict patches the chip and the event stream repaints it.
|
|
596
|
-
const bytes = new Uint8Array(data);
|
|
597
|
-
void (async () => {
|
|
598
|
-
let delivery;
|
|
599
|
-
try {
|
|
600
|
-
delivery = await this.peerManager.sendInlineFile(userid, bytes, name);
|
|
601
|
-
}
|
|
602
|
-
catch (e) {
|
|
603
|
-
if (chip)
|
|
604
|
-
this.messageStore?.patchFile(userid, chip.id, { status: "failed" });
|
|
605
|
-
this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
|
|
606
|
-
this.logger.warn(`Inline file "${name}" to native ${userid.slice(0, 8)} failed to send: ${e.message}`);
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
const status = delivery === "acked" ? "sent" : delivery === "offline" ? "queued" : "failed";
|
|
610
|
-
if (chip)
|
|
611
|
-
this.messageStore?.patchFile(userid, chip.id, { status, sent: delivery === "acked" ? data.length : 0 });
|
|
612
|
-
this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
|
|
613
|
-
this.logger.info(`Inline file "${name}" (${data.length}B) to native ${userid.slice(0, 8)}: delivery=${delivery} → ${status}` +
|
|
614
|
-
(delivery === "accepted" ? " (unconfirmed — the bytes went out and may have arrived, but the peer never acknowledged)" : ""));
|
|
615
|
-
})();
|
|
616
|
-
return { inline: true, name, size: data.length, pending: true };
|
|
617
|
-
}
|
|
592
|
+
// NATIVE friends (iOS/Android/C Carrier) stream like everyone else:
|
|
593
|
+
// sendFile below reaches them as DNFT1 frames (peer >= 0.1.141 —
|
|
594
|
+
// offset acks, FEC, resume; verified 100MB JS→iPad). There is NO
|
|
595
|
+
// inline shortcut any more, at any size. The inline envelope has no
|
|
596
|
+
// usable confirmation against a native peer — protoVersion is
|
|
597
|
+
// structurally 0 (no text-ack), the bare FileModel carries no
|
|
598
|
+
// deliveryId to ack, and the transport-ack signal (our send window
|
|
599
|
+
// draining) was FIELD-FALSIFIED at every scale: a 282-BYTE
|
|
600
|
+
// single-fragment PNG sat unconfirmed for 20s on the same session
|
|
601
|
+
// that streamed 100MB with 0.0% loss moments earlier. Routing small
|
|
602
|
+
// files inline just manufactured a daily stream of failed chips for
|
|
603
|
+
// images that had actually arrived. DNFT1's contiguous ack is the
|
|
604
|
+
// only true delivery signal a native can give us; the cost is one
|
|
605
|
+
// offer round trip.
|
|
618
606
|
// Add the "out" chip immediately as STATUS=sending (not "sent" — the
|
|
619
607
|
// transfer is reliable+acked, so it only flips to "sent" once the
|
|
620
608
|
// receiver confirms every byte). Progress/complete/cancel events patch
|
package/dist/ui/desktop/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
window.__DK_UI_VERSION="0.1.
|
|
1
|
+
window.__DK_UI_VERSION="0.1.284";
|
|
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.
|
|
3
|
+
"version": "0.1.284",
|
|
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",
|