@decentnetwork/lan 0.1.221 → 0.1.223

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.
@@ -1848,8 +1848,8 @@ function ProfileTab({ T, me, onEdit }) {
1848
1848
  }
1849
1849
  Object.assign(window, { ProfileTab });
1850
1850
  const DK_FILE_RTC_KIND = "file";
1851
- const DK_FILE_RTC_CHUNK = 64 * 1024;
1852
- const DK_FILE_RTC_OPEN_TIMEOUT_MS = 8e3;
1851
+ const DK_FILE_RTC_CHUNK = 16 * 1024;
1852
+ const DK_FILE_RTC_OPEN_TIMEOUT_MS = 2e4;
1853
1853
  const DK_FILE_RTC_SIGNAL_TTL_MS = 10 * 60 * 1e3;
1854
1854
  function dkFileRtcId() {
1855
1855
  if (window.crypto && window.crypto.randomUUID) return window.crypto.randomUUID();
@@ -1858,12 +1858,17 @@ function dkFileRtcId() {
1858
1858
  function dkFileRtcEnvelope(fileId, type, extra) {
1859
1859
  return Object.assign({ dkRtc: 1, kind: DK_FILE_RTC_KIND, fileId, type, ts: Date.now() }, extra || {});
1860
1860
  }
1861
- function dkWaitForOpen(dc, timeoutMs) {
1861
+ function dkRtcState(pc) {
1862
+ if (!pc) return "";
1863
+ return "ice=" + pc.iceConnectionState + " conn=" + pc.connectionState + " gather=" + pc.iceGatheringState;
1864
+ }
1865
+ function dkWaitForOpen(dc, timeoutMs, describeState) {
1862
1866
  if (dc.readyState === "open") return Promise.resolve();
1863
1867
  return new Promise((resolve, reject) => {
1864
1868
  const timer = setTimeout(() => {
1865
1869
  cleanup();
1866
- reject(new Error("WebRTC data channel open timed out"));
1870
+ const s = describeState ? describeState() : "";
1871
+ reject(new Error("WebRTC data channel open timed out" + (s ? " (" + s + ")" : "")));
1867
1872
  }, timeoutMs);
1868
1873
  const cleanup = () => {
1869
1874
  clearTimeout(timer);
@@ -1877,11 +1882,13 @@ function dkWaitForOpen(dc, timeoutMs) {
1877
1882
  };
1878
1883
  const onError = () => {
1879
1884
  cleanup();
1880
- reject(new Error("WebRTC data channel failed"));
1885
+ const s = describeState ? describeState() : "";
1886
+ reject(new Error("WebRTC data channel failed" + (s ? " (" + s + ")" : "")));
1881
1887
  };
1882
1888
  const onClose = () => {
1883
1889
  cleanup();
1884
- reject(new Error("WebRTC data channel closed"));
1890
+ const s = describeState ? describeState() : "";
1891
+ reject(new Error("WebRTC data channel closed before open" + (s ? " (" + s + ")" : "")));
1885
1892
  };
1886
1893
  dc.addEventListener("open", onOpen);
1887
1894
  dc.addEventListener("error", onError);
@@ -1971,8 +1978,13 @@ function useRtcFileController(selfId, onReceivedText) {
1971
1978
  let sess = peersRef.current.get(fileId);
1972
1979
  try {
1973
1980
  if (env.type === "offer") {
1974
- if (sess) cleanup(fileId);
1981
+ if (sess && sess.pc && sess.pc.signalingState !== "closed") {
1982
+ console.warn("[file-rtc] duplicate offer ignored for " + fileId);
1983
+ return;
1984
+ }
1975
1985
  const pc = new RTCPeerConnection({ iceServers: CALL_ICE_SERVERS });
1986
+ pc.oniceconnectionstatechange = () => console.warn("[file-rtc] receiver ice=" + pc.iceConnectionState + " conn=" + pc.connectionState);
1987
+ pc.onconnectionstatechange = () => console.warn("[file-rtc] receiver conn=" + pc.connectionState + " ice=" + pc.iceConnectionState);
1976
1988
  pc.onicecandidate = (ev) => {
1977
1989
  if (ev.candidate) sendSignal(peerId, fileId, "candidate", { candidate: ev.candidate.toJSON() }).catch(() => {
1978
1990
  });
@@ -1993,6 +2005,10 @@ function useRtcFileController(selfId, onReceivedText) {
1993
2005
  await pc.setLocalDescription(answer);
1994
2006
  await sendSignal(peerId, fileId, "answer", { sdp: answer.sdp });
1995
2007
  } else if (env.type === "answer" && sess) {
2008
+ if (sess.pc.signalingState !== "have-local-offer") {
2009
+ console.warn("[file-rtc] stale answer ignored for " + fileId + " state=" + sess.pc.signalingState);
2010
+ return;
2011
+ }
1996
2012
  await sess.pc.setRemoteDescription({ type: "answer", sdp: env.sdp });
1997
2013
  if (sess.pendingCandidates) {
1998
2014
  for (const c of sess.pendingCandidates.splice(0)) {
@@ -2048,6 +2064,8 @@ function useRtcFileController(selfId, onReceivedText) {
2048
2064
  };
2049
2065
  const sendSignal = (type, extra) => bus.send(peerId, dkFileRtcEnvelope(fileId, type, extra));
2050
2066
  try {
2067
+ pc.oniceconnectionstatechange = () => console.warn("[file-rtc] sender ice=" + pc.iceConnectionState + " conn=" + pc.connectionState);
2068
+ pc.onconnectionstatechange = () => console.warn("[file-rtc] sender conn=" + pc.connectionState + " ice=" + pc.iceConnectionState);
2051
2069
  pc.onicecandidate = (ev) => {
2052
2070
  if (ev.candidate) sendSignal("candidate", { candidate: ev.candidate.toJSON() }).catch(() => {
2053
2071
  });
@@ -2058,7 +2076,7 @@ function useRtcFileController(selfId, onReceivedText) {
2058
2076
  sdp: offer.sdp,
2059
2077
  meta: { name: file.name, size: file.size, mime: file.type || "application/octet-stream" }
2060
2078
  });
2061
- await dkWaitForOpen(dc, DK_FILE_RTC_OPEN_TIMEOUT_MS);
2079
+ await dkWaitForOpen(dc, DK_FILE_RTC_OPEN_TIMEOUT_MS, () => dkRtcState(pc));
2062
2080
  dc.send(JSON.stringify({ type: "meta", name: file.name, size: file.size, mime: file.type || "application/octet-stream" }));
2063
2081
  let sent = 0;
2064
2082
  const highWater = 4 * 1024 * 1024;
@@ -2104,7 +2122,8 @@ function RtcFileInbox({ T, peers, ctl }) {
2104
2122
  Object.assign(window, { useRtcFileController, RtcFileInbox });
2105
2123
  const CALL_ICE_SERVERS = [
2106
2124
  { urls: "stun:stun.l.google.com:19302" },
2107
- { urls: "turn:tokyo.fi.chat:3478", username: "allcom", credential: "allcompass" }
2125
+ { urls: "turn:tokyo.fi.chat:3478", username: "allcom", credential: "allcompass" },
2126
+ { urls: "turn:tokyo.fi.chat:3478?transport=tcp", username: "allcom", credential: "allcompass" }
2108
2127
  ];
2109
2128
  function useCallController(selfId) {
2110
2129
  const [incoming, setIncoming] = React.useState(null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/lan",
3
- "version": "0.1.221",
3
+ "version": "0.1.223",
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",