@mentra/cloud-client 3.2.1-beta.502 → 3.2.1-beta.508
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentra/cloud-client",
|
|
3
|
-
"version": "3.2.1-beta.
|
|
3
|
+
"version": "3.2.1-beta.508",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"exports": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"test": "bun test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@mentra/cloud-protocol": "3.2.1-beta.
|
|
15
|
+
"@mentra/cloud-protocol": "3.2.1-beta.508",
|
|
16
16
|
"tweetnacl": "^1.0.3"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
@@ -78,6 +78,13 @@ export class UdpAudio {
|
|
|
78
78
|
return this.session?.sessionTag ?? null;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
/** Refresh a dead UDP route without changing the session key, tag or sequence. */
|
|
82
|
+
resetSocket(): void {
|
|
83
|
+
if (!this.session) return;
|
|
84
|
+
this.session.socket.close();
|
|
85
|
+
this.session.socket = this.udpFactory();
|
|
86
|
+
}
|
|
87
|
+
|
|
81
88
|
/**
|
|
82
89
|
* Open the UDP socket and load the session's routing + encryption material
|
|
83
90
|
* from `connection.ack.audio`.
|
|
@@ -162,6 +162,7 @@ export class Runtime implements RuntimeModule {
|
|
|
162
162
|
private udpProbeTimer: unknown | null = null;
|
|
163
163
|
private udpProbeStartedAt = 0;
|
|
164
164
|
private lastUdpAckAt = 0;
|
|
165
|
+
private readonly pendingUdpProbes = new Map<string, number>();
|
|
165
166
|
|
|
166
167
|
/**
|
|
167
168
|
* Whether the inbound-message routing has been wired to the connection yet.
|
|
@@ -393,7 +394,9 @@ export class Runtime implements RuntimeModule {
|
|
|
393
394
|
private configureAudio(ack: ConnectionAck): void {
|
|
394
395
|
if (ack.audio) {
|
|
395
396
|
this.audio.configure(ack.audio);
|
|
396
|
-
|
|
397
|
+
// Configured credentials do not prove DNS/connect or UDP delivery works.
|
|
398
|
+
// Use the already-open WS while probes establish the new UDP route.
|
|
399
|
+
this.updateStatus({ audioTransport: this.connection.isOpen ? "ws" : "none" });
|
|
397
400
|
this.startUdpLiveness();
|
|
398
401
|
return;
|
|
399
402
|
}
|
|
@@ -536,8 +539,8 @@ export class Runtime implements RuntimeModule {
|
|
|
536
539
|
this.lastUdpAckAt = 0;
|
|
537
540
|
this.sendUdpProbe();
|
|
538
541
|
this.udpProbeTimer = this.timers.setInterval(() => {
|
|
539
|
-
this.sendUdpProbe();
|
|
540
542
|
this.checkUdpLiveness();
|
|
543
|
+
this.sendUdpProbe();
|
|
541
544
|
}, UDP_PROBE_INTERVAL_MS);
|
|
542
545
|
}
|
|
543
546
|
|
|
@@ -548,12 +551,18 @@ export class Runtime implements RuntimeModule {
|
|
|
548
551
|
}
|
|
549
552
|
this.udpProbeStartedAt = 0;
|
|
550
553
|
this.lastUdpAckAt = 0;
|
|
554
|
+
this.pendingUdpProbes.clear();
|
|
551
555
|
}
|
|
552
556
|
|
|
553
557
|
private sendUdpProbe(): void {
|
|
554
558
|
const tag = this.audio.sessionTag;
|
|
555
559
|
if (tag === null) return;
|
|
556
|
-
const
|
|
560
|
+
const now = Date.now();
|
|
561
|
+
for (const [id, sentAt] of this.pendingUdpProbes) {
|
|
562
|
+
if (now - sentAt >= UDP_LIVENESS_TIMEOUT_MS) this.pendingUdpProbes.delete(id);
|
|
563
|
+
}
|
|
564
|
+
const probeId = `${tag}-${now}-${Math.floor(Math.random() * 1_000_000)}`;
|
|
565
|
+
this.pendingUdpProbes.set(probeId, now);
|
|
557
566
|
this.audio.sendProbe(probeId);
|
|
558
567
|
}
|
|
559
568
|
|
|
@@ -562,6 +571,14 @@ export class Runtime implements RuntimeModule {
|
|
|
562
571
|
const since = this.lastUdpAckAt || this.udpProbeStartedAt;
|
|
563
572
|
if (since === 0 || Date.now() - since < UDP_LIVENESS_TIMEOUT_MS) return;
|
|
564
573
|
this.updateStatus({ audioTransport: this.connection.isOpen ? "ws" : "none" });
|
|
574
|
+
// Connected UDP sockets retain their resolved peer and route. Recreate one
|
|
575
|
+
// after a missed liveness window so DNS/network changes can recover even
|
|
576
|
+
// when the WebSocket stayed open. Only this replacement's probes may restore
|
|
577
|
+
// UDP: an old socket's delayed ack does not establish the new route works.
|
|
578
|
+
this.pendingUdpProbes.clear();
|
|
579
|
+
this.audio.resetSocket();
|
|
580
|
+
this.lastUdpAckAt = 0;
|
|
581
|
+
this.udpProbeStartedAt = Date.now();
|
|
565
582
|
}
|
|
566
583
|
|
|
567
584
|
private handleUdpLivenessAck(payload: {
|
|
@@ -571,6 +588,9 @@ export class Runtime implements RuntimeModule {
|
|
|
571
588
|
receivedAt: number;
|
|
572
589
|
}): void {
|
|
573
590
|
if (payload.sessionTag !== this.audio.sessionTag) return;
|
|
591
|
+
const sentAt = this.pendingUdpProbes.get(payload.probeId);
|
|
592
|
+
this.pendingUdpProbes.delete(payload.probeId);
|
|
593
|
+
if (sentAt === undefined || Date.now() - sentAt >= UDP_LIVENESS_TIMEOUT_MS) return;
|
|
574
594
|
this.lastUdpAckAt = Date.now();
|
|
575
595
|
if (this.status.status === "connected") {
|
|
576
596
|
this.updateStatus({ audioTransport: "udp" });
|