@decentnetwork/lan 0.1.281 → 0.1.282
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/ipc.d.ts +3 -0
- package/dist/daemon/ipc.js +27 -1
- package/dist/daemon/server.js +19 -16
- package/dist/ui/desktop/app.js +1 -1
- package/package.json +1 -1
package/dist/daemon/ipc.d.ts
CHANGED
|
@@ -169,6 +169,9 @@ export declare class IpcServer {
|
|
|
169
169
|
private socketPath;
|
|
170
170
|
private handlers;
|
|
171
171
|
private server?;
|
|
172
|
+
/** Live client connections, so stop() can end them instead of waiting on
|
|
173
|
+
* a subscriber that never hangs up. */
|
|
174
|
+
private openSockets;
|
|
172
175
|
private logger;
|
|
173
176
|
constructor(socketPath: string, handlers: IpcHandlers);
|
|
174
177
|
start(): Promise<void>;
|
package/dist/daemon/ipc.js
CHANGED
|
@@ -61,6 +61,9 @@ export class IpcServer {
|
|
|
61
61
|
socketPath;
|
|
62
62
|
handlers;
|
|
63
63
|
server;
|
|
64
|
+
/** Live client connections, so stop() can end them instead of waiting on
|
|
65
|
+
* a subscriber that never hangs up. */
|
|
66
|
+
openSockets = new Set();
|
|
64
67
|
logger;
|
|
65
68
|
constructor(socketPath, handlers) {
|
|
66
69
|
this.socketPath = socketPath;
|
|
@@ -79,7 +82,14 @@ export class IpcServer {
|
|
|
79
82
|
this.logger.warn(`Could not remove stale socket ${this.socketPath}: ${err}`);
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
|
-
this.server = createServer((sock) =>
|
|
85
|
+
this.server = createServer((sock) => {
|
|
86
|
+
// Track every live connection so stop() can force them shut. Without
|
|
87
|
+
// this, server.close() waits for all of them and a single long-lived
|
|
88
|
+
// subscriber deadlocks shutdown (see stop()).
|
|
89
|
+
this.openSockets.add(sock);
|
|
90
|
+
sock.once("close", () => this.openSockets.delete(sock));
|
|
91
|
+
this.handleConnection(sock);
|
|
92
|
+
});
|
|
83
93
|
await new Promise((resolve, reject) => {
|
|
84
94
|
this.server.once("error", reject);
|
|
85
95
|
this.server.listen(this.socketPath, () => {
|
|
@@ -103,6 +113,22 @@ export class IpcServer {
|
|
|
103
113
|
async stop() {
|
|
104
114
|
if (!this.server)
|
|
105
115
|
return;
|
|
116
|
+
// server.close() only fires its callback once every EXISTING connection
|
|
117
|
+
// has closed — it stops new ones, it does not end open ones. Event
|
|
118
|
+
// subscribers ({op:"subscribe"}) hold their connection open by design and
|
|
119
|
+
// never close it, so waiting on that callback is an unbounded wait on a
|
|
120
|
+
// peer that is never going to hang up.
|
|
121
|
+
//
|
|
122
|
+
// That is not theoretical: a daemon logged "Stopping daemon" and then sat
|
|
123
|
+
// in teardown for 24h46m, finishing 300ms after an unrelated `pm2 restart`
|
|
124
|
+
// happened to drop the subscriber. The same bug is behind the SIGTERM
|
|
125
|
+
// hangs that ran out systemd's TimeoutStopSec — every journal entry with
|
|
126
|
+
// "Shutting down..." and no matching "Daemon stopped" is this.
|
|
127
|
+
//
|
|
128
|
+
// We are shutting down, so end them ourselves and let close() complete.
|
|
129
|
+
for (const sock of this.openSockets)
|
|
130
|
+
sock.destroy();
|
|
131
|
+
this.openSockets.clear();
|
|
106
132
|
await new Promise((resolve) => {
|
|
107
133
|
this.server.close(() => resolve());
|
|
108
134
|
});
|
package/dist/daemon/server.js
CHANGED
|
@@ -564,20 +564,22 @@ export class DaemonServer {
|
|
|
564
564
|
this.logger.info(`Queued file "${name}" (${data.length}B) for offline ${userid.slice(0, 8)} (delivers on reconnect)`);
|
|
565
565
|
return { queued: true, name: safe, size: data.length };
|
|
566
566
|
}
|
|
567
|
-
// NATIVE friends (iOS/Android/C Carrier):
|
|
568
|
-
// FileModel
|
|
569
|
-
//
|
|
570
|
-
//
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
567
|
+
// NATIVE friends (iOS/Android/C Carrier): only TINY files take the
|
|
568
|
+
// inline FileModel envelope — a thumbnail/voice-note shortcut, one
|
|
569
|
+
// blob, one round trip. Everything else streams via sendFile, which
|
|
570
|
+
// peer >= 0.1.141 carries to natives as DNFT1 frames (offset acks,
|
|
571
|
+
// FEC, resume — verified 100MB JS→iPad).
|
|
572
|
+
//
|
|
573
|
+
// The gate was 11MB, then 2.5MB; both sat inside a band where the
|
|
574
|
+
// envelope's ONLY confirmation signal — the toxcore send window
|
|
575
|
+
// draining — is structurally unreliable: an 8.5MB envelope is ~11k
|
|
576
|
+
// bulkmsg fragments on a channel with no congestion control, and
|
|
577
|
+
// three live 8.5MB sends all timed out unconfirmed at 180s while a
|
|
578
|
+
// 5.2MB died silently in the native kernel. At 256KB (~350
|
|
579
|
+
// fragments) the window drains in seconds and the transport ack
|
|
580
|
+
// actually means something.
|
|
581
|
+
const INLINE_NATIVE_MAX = 256 * 1024;
|
|
582
|
+
if (this.peerManager?.isNativeFriend(userid) && data.length <= INLINE_NATIVE_MAX) {
|
|
581
583
|
// Append the chip as "sending" FIRST, then let the SDK's delivery
|
|
582
584
|
// verdict decide what it becomes. Marking "sent" the moment
|
|
583
585
|
// sendInlineFile resolved was a lie — that only meant the bytes
|
|
@@ -601,14 +603,15 @@ export class DaemonServer {
|
|
|
601
603
|
if (chip)
|
|
602
604
|
this.messageStore?.patchFile(userid, chip.id, { status: "failed" });
|
|
603
605
|
this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
|
|
604
|
-
this.logger.warn(`Inline file "${name}" to native ${userid.slice(0, 8)} failed: ${e.message}`);
|
|
606
|
+
this.logger.warn(`Inline file "${name}" to native ${userid.slice(0, 8)} failed to send: ${e.message}`);
|
|
605
607
|
return;
|
|
606
608
|
}
|
|
607
609
|
const status = delivery === "acked" ? "sent" : delivery === "offline" ? "queued" : "failed";
|
|
608
610
|
if (chip)
|
|
609
611
|
this.messageStore?.patchFile(userid, chip.id, { status, sent: delivery === "acked" ? data.length : 0 });
|
|
610
612
|
this.ipcEvents.emit("event", { type: "chat", userid, dir: "out" });
|
|
611
|
-
this.logger.info(`Inline file "${name}" (${data.length}B) to native ${userid.slice(0, 8)}: delivery=${delivery} → ${status}`
|
|
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)" : ""));
|
|
612
615
|
})();
|
|
613
616
|
return { inline: true, name, size: data.length, pending: true };
|
|
614
617
|
}
|
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.282";
|
|
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.282",
|
|
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",
|