@takosjp/yurucommu-api 3.2.1 → 3.3.0
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/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +497 -0
- package/dist/lib/rtc-client.d.ts +84 -0
- package/dist/lib/rtc-client.d.ts.map +1 -0
- package/dist/types/call.d.ts +199 -0
- package/dist/types/call.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1355,6 +1355,402 @@ async function reportContent(input) {
|
|
|
1355
1355
|
});
|
|
1356
1356
|
await assertOk(res, "Failed to submit report");
|
|
1357
1357
|
}
|
|
1358
|
+
// src/lib/rtc-client.ts
|
|
1359
|
+
var CANDIDATE_FLUSH_MS = 200;
|
|
1360
|
+
|
|
1361
|
+
class CallClient {
|
|
1362
|
+
options;
|
|
1363
|
+
ws = null;
|
|
1364
|
+
wantConnected = false;
|
|
1365
|
+
backoff = 500;
|
|
1366
|
+
reconnectTimer = null;
|
|
1367
|
+
listeners = new Map;
|
|
1368
|
+
call = null;
|
|
1369
|
+
localStream = null;
|
|
1370
|
+
state = "idle";
|
|
1371
|
+
constructor(options = {}) {
|
|
1372
|
+
this.options = options;
|
|
1373
|
+
}
|
|
1374
|
+
on(event, listener) {
|
|
1375
|
+
let set = this.listeners.get(event);
|
|
1376
|
+
if (!set) {
|
|
1377
|
+
set = new Set;
|
|
1378
|
+
this.listeners.set(event, set);
|
|
1379
|
+
}
|
|
1380
|
+
set.add(listener);
|
|
1381
|
+
return () => set?.delete(listener);
|
|
1382
|
+
}
|
|
1383
|
+
emit(event, ...args) {
|
|
1384
|
+
for (const l of this.listeners.get(event) ?? []) {
|
|
1385
|
+
l(...args);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
setState(state) {
|
|
1389
|
+
if (this.state === state)
|
|
1390
|
+
return;
|
|
1391
|
+
this.state = state;
|
|
1392
|
+
this.emit("state", state);
|
|
1393
|
+
}
|
|
1394
|
+
getState() {
|
|
1395
|
+
return this.state;
|
|
1396
|
+
}
|
|
1397
|
+
origin() {
|
|
1398
|
+
return this.options.origin ?? (typeof location !== "undefined" ? location.origin : "");
|
|
1399
|
+
}
|
|
1400
|
+
socketUrl() {
|
|
1401
|
+
const o = this.origin();
|
|
1402
|
+
return `${o.replace(/^http/, "ws")}/api/rtc/socket`;
|
|
1403
|
+
}
|
|
1404
|
+
connect() {
|
|
1405
|
+
this.wantConnected = true;
|
|
1406
|
+
this.openSocket();
|
|
1407
|
+
}
|
|
1408
|
+
disconnect() {
|
|
1409
|
+
this.wantConnected = false;
|
|
1410
|
+
if (this.reconnectTimer)
|
|
1411
|
+
clearTimeout(this.reconnectTimer);
|
|
1412
|
+
this.reconnectTimer = null;
|
|
1413
|
+
this.ws?.close();
|
|
1414
|
+
this.ws = null;
|
|
1415
|
+
}
|
|
1416
|
+
openSocket() {
|
|
1417
|
+
if (this.ws && this.ws.readyState <= WebSocket.OPEN)
|
|
1418
|
+
return;
|
|
1419
|
+
let socket;
|
|
1420
|
+
try {
|
|
1421
|
+
socket = new WebSocket(this.socketUrl());
|
|
1422
|
+
} catch (err) {
|
|
1423
|
+
this.emit("error", "socket_open_failed", String(err));
|
|
1424
|
+
this.scheduleReconnect();
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1427
|
+
this.ws = socket;
|
|
1428
|
+
socket.onopen = () => {
|
|
1429
|
+
this.backoff = 500;
|
|
1430
|
+
this.send({ t: "hello" });
|
|
1431
|
+
if (this.call)
|
|
1432
|
+
this.send({ t: "resume", callId: this.call.callId });
|
|
1433
|
+
};
|
|
1434
|
+
socket.onmessage = (ev) => {
|
|
1435
|
+
this.onFrame(ev.data);
|
|
1436
|
+
};
|
|
1437
|
+
socket.onclose = () => {
|
|
1438
|
+
if (this.ws === socket)
|
|
1439
|
+
this.ws = null;
|
|
1440
|
+
if (this.wantConnected)
|
|
1441
|
+
this.scheduleReconnect();
|
|
1442
|
+
};
|
|
1443
|
+
socket.onerror = () => {
|
|
1444
|
+
socket.close();
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
scheduleReconnect() {
|
|
1448
|
+
if (!this.wantConnected || this.reconnectTimer)
|
|
1449
|
+
return;
|
|
1450
|
+
const max = this.options.maxBackoffMs ?? 15000;
|
|
1451
|
+
const delay = Math.min(this.backoff, max);
|
|
1452
|
+
this.backoff = Math.min(this.backoff * 2, max);
|
|
1453
|
+
this.reconnectTimer = setTimeout(() => {
|
|
1454
|
+
this.reconnectTimer = null;
|
|
1455
|
+
this.openSocket();
|
|
1456
|
+
}, delay);
|
|
1457
|
+
}
|
|
1458
|
+
send(frame) {
|
|
1459
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
1460
|
+
this.ws.send(JSON.stringify(frame));
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
async startCall(peer, media) {
|
|
1464
|
+
if (this.call)
|
|
1465
|
+
throw new Error("already in a call");
|
|
1466
|
+
const res = await fetch(`${this.origin()}/api/rtc/calls`, {
|
|
1467
|
+
method: "POST",
|
|
1468
|
+
credentials: "include",
|
|
1469
|
+
headers: { "Content-Type": "application/json" },
|
|
1470
|
+
body: JSON.stringify({ to: peer, media })
|
|
1471
|
+
});
|
|
1472
|
+
if (!res.ok) {
|
|
1473
|
+
const code = res.status === 403 ? "blocked" : "start_failed";
|
|
1474
|
+
this.emit("error", code);
|
|
1475
|
+
throw new Error(code);
|
|
1476
|
+
}
|
|
1477
|
+
const data = await res.json();
|
|
1478
|
+
this.call = this.newCall(data.callId, peer, media, "caller", data.iceServers);
|
|
1479
|
+
this.setState("calling");
|
|
1480
|
+
this.connect();
|
|
1481
|
+
this.send({ t: "invite", callId: data.callId, to: peer, media });
|
|
1482
|
+
await this.setupMedia(this.call);
|
|
1483
|
+
await this.makeOffer(this.call);
|
|
1484
|
+
}
|
|
1485
|
+
async accept() {
|
|
1486
|
+
const call = this.call;
|
|
1487
|
+
if (!call || call.role !== "callee")
|
|
1488
|
+
return;
|
|
1489
|
+
this.setState("connecting");
|
|
1490
|
+
this.send({ t: "accept", callId: call.callId });
|
|
1491
|
+
await this.setupMedia(call);
|
|
1492
|
+
if (call.pc && call.remoteDescriptionSet) {
|
|
1493
|
+
const answer = await call.pc.createAnswer();
|
|
1494
|
+
await call.pc.setLocalDescription(answer);
|
|
1495
|
+
this.send({ t: "answer", callId: call.callId, sdp: answer.sdp ?? "" });
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
reject(reason = "declined") {
|
|
1499
|
+
const call = this.call;
|
|
1500
|
+
if (!call)
|
|
1501
|
+
return;
|
|
1502
|
+
this.send({ t: "reject", callId: call.callId, reason });
|
|
1503
|
+
this.teardown("ended");
|
|
1504
|
+
}
|
|
1505
|
+
hangup(reason = "hangup") {
|
|
1506
|
+
const call = this.call;
|
|
1507
|
+
if (!call)
|
|
1508
|
+
return;
|
|
1509
|
+
this.send({ t: "hangup", callId: call.callId, reason });
|
|
1510
|
+
this.teardown("ended");
|
|
1511
|
+
}
|
|
1512
|
+
setMuted(muted) {
|
|
1513
|
+
for (const track of this.localStream?.getAudioTracks() ?? []) {
|
|
1514
|
+
track.enabled = !muted;
|
|
1515
|
+
}
|
|
1516
|
+
this.emit("muted", muted);
|
|
1517
|
+
}
|
|
1518
|
+
setCameraEnabled(enabled) {
|
|
1519
|
+
for (const track of this.localStream?.getVideoTracks() ?? []) {
|
|
1520
|
+
track.enabled = enabled;
|
|
1521
|
+
}
|
|
1522
|
+
this.emit("cameraoff", !enabled);
|
|
1523
|
+
}
|
|
1524
|
+
async onFrame(raw) {
|
|
1525
|
+
if (typeof raw !== "string")
|
|
1526
|
+
return;
|
|
1527
|
+
let frame;
|
|
1528
|
+
try {
|
|
1529
|
+
frame = JSON.parse(raw);
|
|
1530
|
+
} catch {
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
switch (frame.t) {
|
|
1534
|
+
case "ready":
|
|
1535
|
+
case "pong":
|
|
1536
|
+
return;
|
|
1537
|
+
case "ringing":
|
|
1538
|
+
this.onRinging(frame);
|
|
1539
|
+
return;
|
|
1540
|
+
case "ice-servers":
|
|
1541
|
+
if (this.call && this.call.callId === frame.callId) {
|
|
1542
|
+
this.call.iceServers = frame.iceServers;
|
|
1543
|
+
}
|
|
1544
|
+
return;
|
|
1545
|
+
case "offer":
|
|
1546
|
+
await this.onRemoteOffer(frame);
|
|
1547
|
+
return;
|
|
1548
|
+
case "answer":
|
|
1549
|
+
await this.onRemoteAnswer(frame);
|
|
1550
|
+
return;
|
|
1551
|
+
case "candidates":
|
|
1552
|
+
await this.onRemoteCandidates(frame);
|
|
1553
|
+
return;
|
|
1554
|
+
case "peer-accepted":
|
|
1555
|
+
if (this.state === "calling")
|
|
1556
|
+
this.setState("connecting");
|
|
1557
|
+
return;
|
|
1558
|
+
case "peer-rejected":
|
|
1559
|
+
this.emit("error", "declined", frame.reason);
|
|
1560
|
+
this.teardown("ended");
|
|
1561
|
+
return;
|
|
1562
|
+
case "peer-hangup":
|
|
1563
|
+
this.teardown("ended");
|
|
1564
|
+
return;
|
|
1565
|
+
case "call-state":
|
|
1566
|
+
this.onCallState(frame);
|
|
1567
|
+
return;
|
|
1568
|
+
case "error":
|
|
1569
|
+
this.emit("error", frame.code, frame.message);
|
|
1570
|
+
if (frame.code === "peer_unreachable")
|
|
1571
|
+
this.teardown("ended");
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
onRinging(frame) {
|
|
1576
|
+
if (this.call) {
|
|
1577
|
+
this.send({ t: "reject", callId: frame.callId, reason: "busy" });
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
this.call = this.newCall(frame.callId, frame.from, frame.media, "callee", []);
|
|
1581
|
+
this.setState("incoming");
|
|
1582
|
+
this.emit("incoming", {
|
|
1583
|
+
callId: frame.callId,
|
|
1584
|
+
from: frame.from,
|
|
1585
|
+
media: frame.media
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
onCallState(frame) {
|
|
1589
|
+
if (!this.call || this.call.callId !== frame.callId)
|
|
1590
|
+
return;
|
|
1591
|
+
if (frame.state === "missed" || frame.state === "cancelled" || frame.state === "rejected" || frame.state === "failed" || frame.state === "ended") {
|
|
1592
|
+
this.teardown("ended");
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
async onRemoteOffer(frame) {
|
|
1596
|
+
let call = this.call;
|
|
1597
|
+
if (!call || call.callId !== frame.callId) {
|
|
1598
|
+
call = this.newCall(frame.callId, "unknown", frame.media ?? { audio: true, video: false }, "callee", []);
|
|
1599
|
+
this.call = call;
|
|
1600
|
+
this.setState("incoming");
|
|
1601
|
+
}
|
|
1602
|
+
if (!call.pc)
|
|
1603
|
+
this.buildPeerConnection(call);
|
|
1604
|
+
await call.pc?.setRemoteDescription({ type: "offer", sdp: frame.sdp });
|
|
1605
|
+
call.remoteDescriptionSet = true;
|
|
1606
|
+
await this.drainRemoteCandidates(call);
|
|
1607
|
+
}
|
|
1608
|
+
async onRemoteAnswer(frame) {
|
|
1609
|
+
const call = this.call;
|
|
1610
|
+
if (!call || call.callId !== frame.callId || !call.pc)
|
|
1611
|
+
return;
|
|
1612
|
+
await call.pc.setRemoteDescription({ type: "answer", sdp: frame.sdp });
|
|
1613
|
+
call.remoteDescriptionSet = true;
|
|
1614
|
+
await this.drainRemoteCandidates(call);
|
|
1615
|
+
}
|
|
1616
|
+
async onRemoteCandidates(frame) {
|
|
1617
|
+
const call = this.call;
|
|
1618
|
+
if (!call || call.callId !== frame.callId)
|
|
1619
|
+
return;
|
|
1620
|
+
for (const cand of frame.candidates) {
|
|
1621
|
+
if (call.pc && call.remoteDescriptionSet) {
|
|
1622
|
+
try {
|
|
1623
|
+
await call.pc.addIceCandidate(cand);
|
|
1624
|
+
} catch {}
|
|
1625
|
+
} else {
|
|
1626
|
+
call.pendingRemoteCandidates.push(cand);
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
async drainRemoteCandidates(call) {
|
|
1631
|
+
if (!call.pc)
|
|
1632
|
+
return;
|
|
1633
|
+
const pending = call.pendingRemoteCandidates.splice(0);
|
|
1634
|
+
for (const cand of pending) {
|
|
1635
|
+
try {
|
|
1636
|
+
await call.pc.addIceCandidate(cand);
|
|
1637
|
+
} catch {}
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
async setupMedia(call) {
|
|
1641
|
+
if (!this.localStream) {
|
|
1642
|
+
try {
|
|
1643
|
+
this.localStream = await navigator.mediaDevices.getUserMedia({
|
|
1644
|
+
audio: call.media.audio,
|
|
1645
|
+
video: call.media.video
|
|
1646
|
+
});
|
|
1647
|
+
} catch (err) {
|
|
1648
|
+
this.emit("error", "media_denied", String(err));
|
|
1649
|
+
this.hangup("media_denied");
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
this.emit("localstream", this.localStream);
|
|
1653
|
+
}
|
|
1654
|
+
if (!call.pc)
|
|
1655
|
+
this.buildPeerConnection(call);
|
|
1656
|
+
for (const track of this.localStream.getTracks()) {
|
|
1657
|
+
call.pc?.addTrack(track, this.localStream);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
buildPeerConnection(call) {
|
|
1661
|
+
const pc = new RTCPeerConnection({
|
|
1662
|
+
iceServers: call.iceServers.map((s) => ({
|
|
1663
|
+
urls: s.urls,
|
|
1664
|
+
username: s.username,
|
|
1665
|
+
credential: s.credential
|
|
1666
|
+
}))
|
|
1667
|
+
});
|
|
1668
|
+
pc.onicecandidate = (ev) => {
|
|
1669
|
+
if (ev.candidate)
|
|
1670
|
+
this.bufferCandidate(call, ev.candidate.toJSON());
|
|
1671
|
+
else
|
|
1672
|
+
this.flushCandidates(call);
|
|
1673
|
+
};
|
|
1674
|
+
pc.ontrack = (ev) => {
|
|
1675
|
+
this.emit("remotestream", ev.streams[0] ?? null);
|
|
1676
|
+
};
|
|
1677
|
+
pc.onconnectionstatechange = () => {
|
|
1678
|
+
if (pc.connectionState === "connected")
|
|
1679
|
+
this.setState("connected");
|
|
1680
|
+
else if (pc.connectionState === "failed" || pc.connectionState === "closed") {
|
|
1681
|
+
this.teardown("ended");
|
|
1682
|
+
}
|
|
1683
|
+
};
|
|
1684
|
+
call.pc = pc;
|
|
1685
|
+
}
|
|
1686
|
+
async makeOffer(call) {
|
|
1687
|
+
if (!call.pc)
|
|
1688
|
+
this.buildPeerConnection(call);
|
|
1689
|
+
const offer = await call.pc.createOffer();
|
|
1690
|
+
await call.pc.setLocalDescription(offer);
|
|
1691
|
+
this.send({ t: "offer", callId: call.callId, sdp: offer.sdp ?? "" });
|
|
1692
|
+
}
|
|
1693
|
+
bufferCandidate(call, cand) {
|
|
1694
|
+
call.candidateBuffer.push(cand);
|
|
1695
|
+
if (call.flushTimer)
|
|
1696
|
+
return;
|
|
1697
|
+
call.flushTimer = setTimeout(() => this.flushCandidates(call), CANDIDATE_FLUSH_MS);
|
|
1698
|
+
}
|
|
1699
|
+
flushCandidates(call) {
|
|
1700
|
+
if (call.flushTimer) {
|
|
1701
|
+
clearTimeout(call.flushTimer);
|
|
1702
|
+
call.flushTimer = null;
|
|
1703
|
+
}
|
|
1704
|
+
if (call.candidateBuffer.length === 0)
|
|
1705
|
+
return;
|
|
1706
|
+
const candidates = call.candidateBuffer.splice(0).map((c) => ({
|
|
1707
|
+
candidate: c.candidate ?? "",
|
|
1708
|
+
sdpMid: c.sdpMid ?? null,
|
|
1709
|
+
sdpMLineIndex: c.sdpMLineIndex ?? null,
|
|
1710
|
+
usernameFragment: c.usernameFragment ?? null
|
|
1711
|
+
}));
|
|
1712
|
+
this.send({ t: "candidates", callId: call.callId, candidates });
|
|
1713
|
+
}
|
|
1714
|
+
newCall(callId, peer, media, role, iceServers) {
|
|
1715
|
+
return {
|
|
1716
|
+
callId,
|
|
1717
|
+
peer,
|
|
1718
|
+
media,
|
|
1719
|
+
role,
|
|
1720
|
+
pc: null,
|
|
1721
|
+
iceServers,
|
|
1722
|
+
pendingRemoteCandidates: [],
|
|
1723
|
+
candidateBuffer: [],
|
|
1724
|
+
flushTimer: null,
|
|
1725
|
+
remoteDescriptionSet: false
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
teardown(finalState) {
|
|
1729
|
+
const call = this.call;
|
|
1730
|
+
if (call) {
|
|
1731
|
+
if (call.flushTimer)
|
|
1732
|
+
clearTimeout(call.flushTimer);
|
|
1733
|
+
call.pc?.getSenders().forEach((s) => s.track?.stop());
|
|
1734
|
+
try {
|
|
1735
|
+
call.pc?.close();
|
|
1736
|
+
} catch {}
|
|
1737
|
+
}
|
|
1738
|
+
if (this.localStream) {
|
|
1739
|
+
this.localStream.getTracks().forEach((t) => t.stop());
|
|
1740
|
+
this.localStream = null;
|
|
1741
|
+
this.emit("localstream", null);
|
|
1742
|
+
}
|
|
1743
|
+
this.emit("remotestream", null);
|
|
1744
|
+
this.call = null;
|
|
1745
|
+
this.setState(finalState);
|
|
1746
|
+
if (finalState === "ended") {
|
|
1747
|
+
setTimeout(() => {
|
|
1748
|
+
if (!this.call)
|
|
1749
|
+
this.setState("idle");
|
|
1750
|
+
}, 400);
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1358
1754
|
// src/social-server.ts
|
|
1359
1755
|
async function fetchCurrentActor() {
|
|
1360
1756
|
const res = await apiFetch("/api/auth/me");
|
|
@@ -1369,6 +1765,101 @@ async function fetchSocialServerDiscovery() {
|
|
|
1369
1765
|
await assertOk(res, "Failed to load social server discovery");
|
|
1370
1766
|
return await res.json();
|
|
1371
1767
|
}
|
|
1768
|
+
// src/types/call.ts
|
|
1769
|
+
var RTC_SIGNAL_ENVELOPE_VERSION = 1;
|
|
1770
|
+
var TERMINAL_CALL_STATES = [
|
|
1771
|
+
"ended",
|
|
1772
|
+
"rejected",
|
|
1773
|
+
"missed",
|
|
1774
|
+
"failed",
|
|
1775
|
+
"cancelled"
|
|
1776
|
+
];
|
|
1777
|
+
function isTerminalCallState(state) {
|
|
1778
|
+
return TERMINAL_CALL_STATES.includes(state);
|
|
1779
|
+
}
|
|
1780
|
+
var SIGNAL_TYPES = [
|
|
1781
|
+
"offer",
|
|
1782
|
+
"answer",
|
|
1783
|
+
"candidate",
|
|
1784
|
+
"accept",
|
|
1785
|
+
"reject",
|
|
1786
|
+
"hangup",
|
|
1787
|
+
"cancel"
|
|
1788
|
+
];
|
|
1789
|
+
function isPlainObject(value) {
|
|
1790
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1791
|
+
}
|
|
1792
|
+
function isCallMediaKind(value) {
|
|
1793
|
+
return isPlainObject(value) && typeof value.audio === "boolean" && typeof value.video === "boolean";
|
|
1794
|
+
}
|
|
1795
|
+
function parseCandidates(value) {
|
|
1796
|
+
if (value === undefined)
|
|
1797
|
+
return;
|
|
1798
|
+
if (!Array.isArray(value))
|
|
1799
|
+
return;
|
|
1800
|
+
const out = [];
|
|
1801
|
+
for (const raw of value) {
|
|
1802
|
+
if (!isPlainObject(raw) || typeof raw.candidate !== "string")
|
|
1803
|
+
continue;
|
|
1804
|
+
out.push({
|
|
1805
|
+
candidate: raw.candidate,
|
|
1806
|
+
sdpMid: typeof raw.sdpMid === "string" ? raw.sdpMid : null,
|
|
1807
|
+
sdpMLineIndex: typeof raw.sdpMLineIndex === "number" ? raw.sdpMLineIndex : null,
|
|
1808
|
+
usernameFragment: typeof raw.usernameFragment === "string" ? raw.usernameFragment : null
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
return out;
|
|
1812
|
+
}
|
|
1813
|
+
function parseSfuFocus(value) {
|
|
1814
|
+
if (value === undefined)
|
|
1815
|
+
return;
|
|
1816
|
+
if (value === null)
|
|
1817
|
+
return null;
|
|
1818
|
+
if (!isPlainObject(value))
|
|
1819
|
+
return;
|
|
1820
|
+
if (typeof value.kind !== "string" || typeof value.url !== "string") {
|
|
1821
|
+
return;
|
|
1822
|
+
}
|
|
1823
|
+
return {
|
|
1824
|
+
kind: value.kind,
|
|
1825
|
+
url: value.url,
|
|
1826
|
+
token: typeof value.token === "string" ? value.token : undefined,
|
|
1827
|
+
room: typeof value.room === "string" ? value.room : undefined
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
function parseRtcSignalEnvelope(input) {
|
|
1831
|
+
if (!isPlainObject(input))
|
|
1832
|
+
return null;
|
|
1833
|
+
if (input.v !== RTC_SIGNAL_ENVELOPE_VERSION)
|
|
1834
|
+
return null;
|
|
1835
|
+
const { callId, from, to, type, ts, ttlMs } = input;
|
|
1836
|
+
if (typeof callId !== "string" || callId.length === 0 || callId.length > 200 || typeof from !== "string" || from.length === 0 || typeof to !== "string" || to.length === 0 || typeof type !== "string" || !SIGNAL_TYPES.includes(type) || typeof ts !== "number" || !Number.isFinite(ts) || typeof ttlMs !== "number" || !Number.isFinite(ttlMs) || ttlMs < 0) {
|
|
1837
|
+
return null;
|
|
1838
|
+
}
|
|
1839
|
+
const sdp = typeof input.sdp === "string" ? input.sdp : undefined;
|
|
1840
|
+
if (sdp !== undefined && sdp.length > 1e5)
|
|
1841
|
+
return null;
|
|
1842
|
+
return {
|
|
1843
|
+
v: RTC_SIGNAL_ENVELOPE_VERSION,
|
|
1844
|
+
callId,
|
|
1845
|
+
from,
|
|
1846
|
+
to,
|
|
1847
|
+
type,
|
|
1848
|
+
media: isCallMediaKind(input.media) ? input.media : undefined,
|
|
1849
|
+
sdp,
|
|
1850
|
+
candidates: parseCandidates(input.candidates),
|
|
1851
|
+
sfuFocus: parseSfuFocus(input.sfuFocus),
|
|
1852
|
+
reason: typeof input.reason === "string" ? input.reason.slice(0, 200) : undefined,
|
|
1853
|
+
ts,
|
|
1854
|
+
ttlMs
|
|
1855
|
+
};
|
|
1856
|
+
}
|
|
1857
|
+
function isEnvelopeFresh(envelope, now) {
|
|
1858
|
+
const skewToleranceMs = 30000;
|
|
1859
|
+
if (envelope.ts - now > skewToleranceMs)
|
|
1860
|
+
return false;
|
|
1861
|
+
return now - envelope.ts <= envelope.ttlMs;
|
|
1862
|
+
}
|
|
1372
1863
|
export {
|
|
1373
1864
|
voteOnStory,
|
|
1374
1865
|
validateFile,
|
|
@@ -1412,6 +1903,7 @@ export {
|
|
|
1412
1903
|
rejectCommunityJoinRequest,
|
|
1413
1904
|
registerNotificationPusher,
|
|
1414
1905
|
refreshBrowserNotificationPush,
|
|
1906
|
+
parseRtcSignalEnvelope,
|
|
1415
1907
|
normalizeStory,
|
|
1416
1908
|
normalizePost,
|
|
1417
1909
|
normalizeNotification,
|
|
@@ -1433,6 +1925,8 @@ export {
|
|
|
1433
1925
|
likePost,
|
|
1434
1926
|
leaveCommunity,
|
|
1435
1927
|
joinCommunity,
|
|
1928
|
+
isTerminalCallState,
|
|
1929
|
+
isEnvelopeFresh,
|
|
1436
1930
|
getYurucommuApiTransport,
|
|
1437
1931
|
getStoryViewers,
|
|
1438
1932
|
getBrowserNotificationPushState,
|
|
@@ -1508,6 +2002,9 @@ export {
|
|
|
1508
2002
|
allowedMimeTypes,
|
|
1509
2003
|
acceptFollowRequest,
|
|
1510
2004
|
acceptCommunityJoinRequest,
|
|
2005
|
+
TERMINAL_CALL_STATES,
|
|
2006
|
+
RTC_SIGNAL_ENVELOPE_VERSION,
|
|
1511
2007
|
FileValidationError,
|
|
2008
|
+
CallClient,
|
|
1512
2009
|
ApiError
|
|
1513
2010
|
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CallClient — framework-agnostic browser WebRTC engine for the call feature.
|
|
3
|
+
*
|
|
4
|
+
* Owns the WebSocket to the per-user signaling hub (`/api/rtc/socket`), the
|
|
5
|
+
* `RTCPeerConnection` lifecycle, `getUserMedia`, half-trickle ICE, and the call
|
|
6
|
+
* state machine as the browser sees it. Both products (yurucommu / yurumeet)
|
|
7
|
+
* wrap this with their own reactive state layer (SolidJS signals / jotai) — no
|
|
8
|
+
* WebRTC logic is duplicated per product, and the wire protocol is single-
|
|
9
|
+
* sourced with the backend via `./types/call.ts`.
|
|
10
|
+
*
|
|
11
|
+
* 1:1 only for now: the caller offers, the callee answers, media is P2P over the
|
|
12
|
+
* server-provided ICE (STUN/TURN) servers. Camera on/off toggles the existing
|
|
13
|
+
* video track (no mid-call renegotiation, so no glare at the PC level).
|
|
14
|
+
*/
|
|
15
|
+
import type { CallMediaKind } from "../types/call.js";
|
|
16
|
+
export type CallUiState = "idle" | "calling" | "incoming" | "connecting" | "connected" | "ended";
|
|
17
|
+
export interface IncomingCallInfo {
|
|
18
|
+
callId: string;
|
|
19
|
+
from: string;
|
|
20
|
+
media: CallMediaKind;
|
|
21
|
+
}
|
|
22
|
+
export interface CallClientEvents {
|
|
23
|
+
state: (state: CallUiState) => void;
|
|
24
|
+
incoming: (info: IncomingCallInfo) => void;
|
|
25
|
+
localstream: (stream: MediaStream | null) => void;
|
|
26
|
+
remotestream: (stream: MediaStream | null) => void;
|
|
27
|
+
muted: (muted: boolean) => void;
|
|
28
|
+
cameraoff: (off: boolean) => void;
|
|
29
|
+
error: (code: string, message?: string) => void;
|
|
30
|
+
}
|
|
31
|
+
export interface CallClientOptions {
|
|
32
|
+
/** Origin the app is served from (defaults to the page origin). */
|
|
33
|
+
origin?: string;
|
|
34
|
+
/** WebSocket reconnect backoff ceiling (ms). */
|
|
35
|
+
maxBackoffMs?: number;
|
|
36
|
+
}
|
|
37
|
+
export declare class CallClient {
|
|
38
|
+
private readonly options;
|
|
39
|
+
private ws;
|
|
40
|
+
private wantConnected;
|
|
41
|
+
private backoff;
|
|
42
|
+
private reconnectTimer;
|
|
43
|
+
private readonly listeners;
|
|
44
|
+
private call;
|
|
45
|
+
private localStream;
|
|
46
|
+
private state;
|
|
47
|
+
constructor(options?: CallClientOptions);
|
|
48
|
+
on<K extends keyof CallClientEvents>(event: K, listener: CallClientEvents[K]): () => void;
|
|
49
|
+
private emit;
|
|
50
|
+
private setState;
|
|
51
|
+
getState(): CallUiState;
|
|
52
|
+
private origin;
|
|
53
|
+
private socketUrl;
|
|
54
|
+
connect(): void;
|
|
55
|
+
disconnect(): void;
|
|
56
|
+
private openSocket;
|
|
57
|
+
private scheduleReconnect;
|
|
58
|
+
private send;
|
|
59
|
+
/** Place an outgoing call. Fetches a callId + ICE, then rings the peer. */
|
|
60
|
+
startCall(peer: string, media: CallMediaKind): Promise<void>;
|
|
61
|
+
/** Accept the current incoming call. */
|
|
62
|
+
accept(): Promise<void>;
|
|
63
|
+
/** Decline the current incoming call. */
|
|
64
|
+
reject(reason?: string): void;
|
|
65
|
+
/** Hang up / cancel the active call. */
|
|
66
|
+
hangup(reason?: string): void;
|
|
67
|
+
setMuted(muted: boolean): void;
|
|
68
|
+
setCameraEnabled(enabled: boolean): void;
|
|
69
|
+
private onFrame;
|
|
70
|
+
private onRinging;
|
|
71
|
+
private onCallState;
|
|
72
|
+
private onRemoteOffer;
|
|
73
|
+
private onRemoteAnswer;
|
|
74
|
+
private onRemoteCandidates;
|
|
75
|
+
private drainRemoteCandidates;
|
|
76
|
+
private setupMedia;
|
|
77
|
+
private buildPeerConnection;
|
|
78
|
+
private makeOffer;
|
|
79
|
+
private bufferCandidate;
|
|
80
|
+
private flushCandidates;
|
|
81
|
+
private newCall;
|
|
82
|
+
private teardown;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=rtc-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rtc-client.d.ts","sourceRoot":"","sources":["../../src/lib/rtc-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,aAAa,EAId,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,SAAS,GACT,UAAU,GACV,YAAY,GACZ,WAAW,GACX,OAAO,CAAC;AAEZ,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACpC,QAAQ,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC3C,WAAW,EAAE,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAClD,YAAY,EAAE,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IACnD,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAChC,SAAS,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAmBD,qBAAa,UAAU;IAUT,OAAO,CAAC,QAAQ,CAAC,OAAO;IATpC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAO;IACtB,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoD;IAC9E,OAAO,CAAC,IAAI,CAA2B;IACvC,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,KAAK,CAAuB;gBAEP,OAAO,GAAE,iBAAsB;IAG5D,EAAE,CAAC,CAAC,SAAS,MAAM,gBAAgB,EACjC,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC5B,MAAM,IAAI;IAUb,OAAO,CAAC,IAAI;IASZ,OAAO,CAAC,QAAQ;IAMhB,QAAQ,IAAI,WAAW;IAKvB,OAAO,CAAC,MAAM;IAOd,OAAO,CAAC,SAAS;IAKjB,OAAO,IAAI,IAAI;IAKf,UAAU,IAAI,IAAI;IAQlB,OAAO,CAAC,UAAU;IA4BlB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,IAAI;IAQZ,2EAA2E;IACrE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BlE,wCAAwC;IAClC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAc7B,yCAAyC;IACzC,MAAM,CAAC,MAAM,SAAa,GAAG,IAAI;IAOjC,wCAAwC;IACxC,MAAM,CAAC,MAAM,SAAW,GAAG,IAAI;IAO/B,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAO9B,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;YAQ1B,OAAO;IAiDrB,OAAO,CAAC,SAAS;IAuBjB,OAAO,CAAC,WAAW;YAeL,aAAa;YAsBb,cAAc;YAUd,kBAAkB;YAkBlB,qBAAqB;YAarB,UAAU;IAoBxB,OAAO,CAAC,mBAAmB;YA2Bb,SAAS;IAOvB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,OAAO;IAqBf,OAAO,CAAC,QAAQ;CA0BjB"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Call signaling wire contract (voice + video).
|
|
3
|
+
*
|
|
4
|
+
* SINGLE SOURCE OF TRUTH shared by:
|
|
5
|
+
* - the backend cross-instance signaling ingest (`/ap/rtc/signal`) + the
|
|
6
|
+
* Signaling Durable Object (server-to-server + browser fan-out), and
|
|
7
|
+
* - the browser `CallClient` (`../lib/rtc-client.ts`).
|
|
8
|
+
*
|
|
9
|
+
* Kept deliberately DOM-structural (no `RTCIceCandidateInit` / `RTCIceServer`
|
|
10
|
+
* imports) so the same file type-checks in the server context (which never runs
|
|
11
|
+
* the browser WebRTC APIs) and the browser bundle. The `CallClient` maps these
|
|
12
|
+
* structural shapes to/from the real DOM `RTCSessionDescriptionInit` /
|
|
13
|
+
* `RTCIceCandidateInit` / `RTCIceServer`, which are structurally compatible.
|
|
14
|
+
*
|
|
15
|
+
* Design: signaling travels over federation (server-to-server, HTTP-Signature
|
|
16
|
+
* authenticated) as `RtcSignalEnvelopeV1`; media is P2P WebRTC + STUN/TURN for
|
|
17
|
+
* 1:1 (`sfuFocus: null`) and a pluggable WHIP/WHEP SFU focus for group calls.
|
|
18
|
+
*/
|
|
19
|
+
export declare const RTC_SIGNAL_ENVELOPE_VERSION: 1;
|
|
20
|
+
/** Which media tracks a call carries. `video:false` => audio-only call. */
|
|
21
|
+
export interface CallMediaKind {
|
|
22
|
+
audio: boolean;
|
|
23
|
+
video: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** Cross-instance signaling message kinds (Matrix-VoIP inspired). */
|
|
26
|
+
export type RtcSignalType = "offer" | "answer" | "candidate" | "accept" | "reject" | "hangup" | "cancel";
|
|
27
|
+
/**
|
|
28
|
+
* Selected SFU focus for a group call. `null`/absent means pure P2P (1:1).
|
|
29
|
+
* `kind` names the adapter (`whip` / `livekit` / `cloudflare-realtime` / ...);
|
|
30
|
+
* the client talks WHIP/WHEP so the SFU backend stays vendor-neutral.
|
|
31
|
+
*/
|
|
32
|
+
export interface SfuFocus {
|
|
33
|
+
kind: string;
|
|
34
|
+
/** WHIP (publish) / WHEP (subscribe) endpoint base, or SFU signaling URL. */
|
|
35
|
+
url: string;
|
|
36
|
+
/** Short-lived join token when the adapter requires one. */
|
|
37
|
+
token?: string;
|
|
38
|
+
room?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Structural mirror of `RTCIceCandidateInit` (no DOM dependency). */
|
|
41
|
+
export interface CallIceCandidate {
|
|
42
|
+
candidate: string;
|
|
43
|
+
sdpMid?: string | null;
|
|
44
|
+
sdpMLineIndex?: number | null;
|
|
45
|
+
usernameFragment?: string | null;
|
|
46
|
+
}
|
|
47
|
+
/** Structural mirror of `RTCIceServer` (no DOM dependency). */
|
|
48
|
+
export interface IceServerConfig {
|
|
49
|
+
urls: string | string[];
|
|
50
|
+
username?: string;
|
|
51
|
+
credential?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Server-to-server signaling envelope. Delivered by the sending instance to the
|
|
55
|
+
* recipient instance's `/ap/rtc/signal` endpoint, signed with the sender actor's
|
|
56
|
+
* HTTP Signature key (keyId-owner === `from`). `callId` doubles as the anti-
|
|
57
|
+
* replay nonce; `ts`/`ttlMs` bound its freshness (the DO drops stale frames).
|
|
58
|
+
*/
|
|
59
|
+
export interface RtcSignalEnvelopeV1 {
|
|
60
|
+
v: typeof RTC_SIGNAL_ENVELOPE_VERSION;
|
|
61
|
+
callId: string;
|
|
62
|
+
from: string;
|
|
63
|
+
to: string;
|
|
64
|
+
type: RtcSignalType;
|
|
65
|
+
media?: CallMediaKind;
|
|
66
|
+
/** SDP for `offer` / `answer`. */
|
|
67
|
+
sdp?: string;
|
|
68
|
+
/** Half-trickle ICE bundle for `offer` / `answer` / `candidate`. */
|
|
69
|
+
candidates?: CallIceCandidate[];
|
|
70
|
+
sfuFocus?: SfuFocus | null;
|
|
71
|
+
/** Free-text end/reject reason (`busy`, `declined`, `timeout`, ...). */
|
|
72
|
+
reason?: string;
|
|
73
|
+
ts: number;
|
|
74
|
+
ttlMs: number;
|
|
75
|
+
}
|
|
76
|
+
/** Lifecycle of a single call, mirrored client-side and in `call_sessions`. */
|
|
77
|
+
export type CallState = "idle" | "ringing" | "connecting" | "connected" | "ended" | "rejected" | "missed" | "failed" | "cancelled";
|
|
78
|
+
export type CallDirection = "incoming" | "outgoing";
|
|
79
|
+
/** Terminal states — a call in one of these is over and not resumable. */
|
|
80
|
+
export declare const TERMINAL_CALL_STATES: readonly CallState[];
|
|
81
|
+
export declare function isTerminalCallState(state: CallState): boolean;
|
|
82
|
+
/** Frames the browser sends up to its own instance's Signaling DO. */
|
|
83
|
+
export type ClientToHubFrame = {
|
|
84
|
+
t: "hello";
|
|
85
|
+
} | {
|
|
86
|
+
t: "invite";
|
|
87
|
+
callId: string;
|
|
88
|
+
to: string;
|
|
89
|
+
media: CallMediaKind;
|
|
90
|
+
} | {
|
|
91
|
+
t: "offer";
|
|
92
|
+
callId: string;
|
|
93
|
+
sdp: string;
|
|
94
|
+
} | {
|
|
95
|
+
t: "answer";
|
|
96
|
+
callId: string;
|
|
97
|
+
sdp: string;
|
|
98
|
+
} | {
|
|
99
|
+
t: "candidates";
|
|
100
|
+
callId: string;
|
|
101
|
+
candidates: CallIceCandidate[];
|
|
102
|
+
} | {
|
|
103
|
+
t: "accept";
|
|
104
|
+
callId: string;
|
|
105
|
+
} | {
|
|
106
|
+
t: "reject";
|
|
107
|
+
callId: string;
|
|
108
|
+
reason?: string;
|
|
109
|
+
} | {
|
|
110
|
+
t: "hangup";
|
|
111
|
+
callId: string;
|
|
112
|
+
reason?: string;
|
|
113
|
+
} | {
|
|
114
|
+
t: "resume";
|
|
115
|
+
callId: string;
|
|
116
|
+
} | {
|
|
117
|
+
t: "ping";
|
|
118
|
+
};
|
|
119
|
+
/** Frames the Signaling DO pushes down to the browser. */
|
|
120
|
+
export type HubToClientFrame = {
|
|
121
|
+
t: "ready";
|
|
122
|
+
} | {
|
|
123
|
+
t: "ringing";
|
|
124
|
+
callId: string;
|
|
125
|
+
from: string;
|
|
126
|
+
media: CallMediaKind;
|
|
127
|
+
} | {
|
|
128
|
+
t: "offer";
|
|
129
|
+
callId: string;
|
|
130
|
+
sdp: string;
|
|
131
|
+
media?: CallMediaKind;
|
|
132
|
+
} | {
|
|
133
|
+
t: "answer";
|
|
134
|
+
callId: string;
|
|
135
|
+
sdp: string;
|
|
136
|
+
} | {
|
|
137
|
+
t: "candidates";
|
|
138
|
+
callId: string;
|
|
139
|
+
candidates: CallIceCandidate[];
|
|
140
|
+
} | {
|
|
141
|
+
t: "peer-accepted";
|
|
142
|
+
callId: string;
|
|
143
|
+
} | {
|
|
144
|
+
t: "peer-rejected";
|
|
145
|
+
callId: string;
|
|
146
|
+
reason?: string;
|
|
147
|
+
} | {
|
|
148
|
+
t: "peer-hangup";
|
|
149
|
+
callId: string;
|
|
150
|
+
reason?: string;
|
|
151
|
+
} | {
|
|
152
|
+
t: "ice-servers";
|
|
153
|
+
callId: string;
|
|
154
|
+
iceServers: IceServerConfig[];
|
|
155
|
+
sfuFocus?: SfuFocus | null;
|
|
156
|
+
} | {
|
|
157
|
+
t: "call-state";
|
|
158
|
+
callId: string;
|
|
159
|
+
state: CallState;
|
|
160
|
+
} | {
|
|
161
|
+
t: "pong";
|
|
162
|
+
} | {
|
|
163
|
+
t: "error";
|
|
164
|
+
code: string;
|
|
165
|
+
message?: string;
|
|
166
|
+
};
|
|
167
|
+
export interface StartCallRequest {
|
|
168
|
+
to: string;
|
|
169
|
+
media: CallMediaKind;
|
|
170
|
+
}
|
|
171
|
+
export interface StartCallResponse {
|
|
172
|
+
callId: string;
|
|
173
|
+
iceServers: IceServerConfig[];
|
|
174
|
+
sfuFocus?: SfuFocus | null;
|
|
175
|
+
}
|
|
176
|
+
export interface IceServersResponse {
|
|
177
|
+
iceServers: IceServerConfig[];
|
|
178
|
+
}
|
|
179
|
+
export interface CallSessionSummary {
|
|
180
|
+
id: string;
|
|
181
|
+
peer: string;
|
|
182
|
+
direction: CallDirection;
|
|
183
|
+
state: CallState;
|
|
184
|
+
media: CallMediaKind;
|
|
185
|
+
createdAt: string;
|
|
186
|
+
connectedAt?: string | null;
|
|
187
|
+
endedAt?: string | null;
|
|
188
|
+
endReason?: string | null;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Parse + validate an inbound cross-instance signaling envelope. Returns the
|
|
192
|
+
* normalized envelope or `null` when the shape is invalid. Callers additionally
|
|
193
|
+
* enforce that the HTTP-Signature signer equals `from` and that the recipient
|
|
194
|
+
* (`to`) is a local actor.
|
|
195
|
+
*/
|
|
196
|
+
export declare function parseRtcSignalEnvelope(input: unknown): RtcSignalEnvelopeV1 | null;
|
|
197
|
+
/** True when the envelope is still within its freshness window. */
|
|
198
|
+
export declare function isEnvelopeFresh(envelope: RtcSignalEnvelopeV1, now: number): boolean;
|
|
199
|
+
//# sourceMappingURL=call.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call.d.ts","sourceRoot":"","sources":["../../src/types/call.ts"],"names":[],"mappings":"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"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|