virlen-remote 0.1.0 → 0.1.2

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/CHANGELOG.md CHANGED
@@ -3,6 +3,36 @@
3
3
  本文件记录对外可见的变更(协议 / 导出面 / 行为)。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
4
4
  版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
5
5
 
6
+ ## [0.1.2] - 2026-09-28
7
+
8
+ ### Changed
9
+
10
+ - `host.event.connection.changed` 的 `degraded` 字段改为**可选**,并在协议表中标注为「预留,当前不发送」:
11
+ 该字段此前是**必填却无任何定义 / 无任何消费方**的悬空字段,强类型 `emit` 会强迫未来多端的发送方
12
+ 编造一个值。当前实现只区分 `path`(直连 / 中继),没有独立可观测的「降级」判据,故转为可选,
13
+ 并在注释里明确:消费方**不得**用 `path === 'relay'` 反推它。
14
+
15
+ ### Notes
16
+
17
+ - 兼容性:对**消费方**无破坏(可选字段可缺席);对发送方为**放宽**(不再强制提供)。
18
+ - 无运行时行为变更。
19
+
20
+ ## [0.1.1] - 2026-09-28
21
+
22
+ ### Added
23
+
24
+ - **链路类型判定**(`classifyLinkKind` / `probeLinkKind` / `LinkKindWatcher` / `LINK_KIND_POLL_MS`):
25
+ 从 `RTCPeerConnection.getStats()` 的**所选候选对**判定本次连接是 P2P 直连还是 TURN 中继
26
+ (`host` / `srflx` / `prflx` → 直连;任一端 `relay` → 中继;拿不到结论 → `unknown`,**不猜**)。
27
+ 这是**两端共用的唯一一份口径**:此前电脑端(virlen-app)与手机端(virlen-mobile)各持一份副本,
28
+ 存在「手机说直连、电脑说中继」的漂移风险,本版收敛到本包。附带导出 `pickCandidatePair` /
29
+ `findCandidate`,供消费方按**同一次挑选**提取其它 stats 字段(rtt / 协议等)。
30
+
31
+ ### Notes
32
+
33
+ - 无破坏性变更;0.1.0 的导出面保持不变。
34
+ - 判定逻辑为**纯函数**(喂 `Record<string, unknown>[]` 即可),不依赖 DOM 类型。
35
+
6
36
  ## [0.1.0] - 2026-09-30
7
37
 
8
38
  首次发布。内容为 Virlen「手机控制」M0–M6 期间沉淀的协议栈,两端(virlen-app / virlen-mobile)此前以源码方式共用,
@@ -38,3 +68,5 @@
38
68
  真值判断不收敛联合类型。
39
69
 
40
70
  [0.1.0]: https://github.com/WeiChens/virlen-remote/releases/tag/v0.1.0
71
+ [0.1.1]: https://github.com/WeiChens/virlen-remote/releases/tag/v0.1.1
72
+ [0.1.2]: https://github.com/WeiChens/virlen-remote/releases/tag/v0.1.2
package/dist/index.d.ts CHANGED
@@ -39,6 +39,8 @@ export { SseSignalingClient, fetchRoomStatus, fetchHostOnlineMap } from './trans
39
39
  export type { SignalingChannel, SignalingRole, SseSignalingOptions, EventSourceLike, KickedInfo, FetchRoomStatusOptions, } from './transport/signaling.js';
40
40
  export { RtcTransport } from './transport/rtc.js';
41
41
  export type { RtcTransportOptions } from './transport/rtc.js';
42
+ export { LINK_KIND_POLL_MS, classifyLinkKind, probeLinkKind, pickCandidatePair, findCandidate, LinkKindWatcher, } from './transport/link-kind.js';
43
+ export type { LinkKind, LinkStatsEntry, StatsReportLike, StatsProvider, } from './transport/link-kind.js';
42
44
  export { ICE_CONFIG_VERSION, ICE_API_PATH, ICE_CUSTOM_STORAGE_KEY, ICE_REMOTE_STORAGE_KEY, ICE_CACHE_TTL_MS, ICE_FETCH_TIMEOUT_MS, sanitizeIceServers, parseIceText, formatIceServers, describeIceSource, fetchIceServers, resolveIceServers, readCustomIceText, writeCustomIceText, } from './transport/ice.js';
43
45
  export type { IceServerInit, IceSource, IceStoragePort, ResolvedIceServers, ParseIceResult, FetchIceOptions, ResolveIceOptions, } from './transport/ice.js';
44
46
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -29,6 +29,8 @@ export { MemoryTransport, createMemoryPair } from './transport/memory.js';
29
29
  export { BroadcastTransport, createBroadcastPair } from './transport/broadcast.js';
30
30
  export { SseSignalingClient, fetchRoomStatus, fetchHostOnlineMap } from './transport/signaling.js';
31
31
  export { RtcTransport } from './transport/rtc.js';
32
+ // ── RTC 链路类型判定(直连 / 中继,两端同一份口径)──
33
+ export { LINK_KIND_POLL_MS, classifyLinkKind, probeLinkKind, pickCandidatePair, findCandidate, LinkKindWatcher, } from './transport/link-kind.js';
32
34
  // ── ICE 配置(M7,§31):客户端不再内置任何 TURN 凭证 ──
33
35
  export { ICE_CONFIG_VERSION, ICE_API_PATH, ICE_CUSTOM_STORAGE_KEY, ICE_REMOTE_STORAGE_KEY, ICE_CACHE_TTL_MS, ICE_FETCH_TIMEOUT_MS, sanitizeIceServers, parseIceText, formatIceServers, describeIceSource, fetchIceServers, resolveIceServers, readCustomIceText, writeCustomIceText, } from './transport/ice.js';
34
36
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,iBAAiB;AACjB,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,yBAAyB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACtG,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAGnG,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAG9D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,yBAAyB;AACzB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,SAAS,EACT,YAAY,EACZ,aAAa,EACb,WAAW,EACX,OAAO,EACP,eAAe,EACf,UAAU,EACV,cAAc,EACd,UAAU,EACV,UAAU,EACV,sBAAsB,GACvB,MAAM,qBAAqB,CAAA;AAG5B,yBAAyB;AACzB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,GACd,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAG3F,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAEnE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAU9C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjF,OAAO,EAAE,6BAA6B,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAsClF,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAItD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAEtE,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAS/F,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAG9C,yCAAyC;AACzC,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,EACtB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,iBAAiB;AACjB,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,yBAAyB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACtG,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAGnG,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAG9D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,yBAAyB;AACzB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,SAAS,EACT,YAAY,EACZ,aAAa,EACb,WAAW,EACX,OAAO,EACP,eAAe,EACf,UAAU,EACV,cAAc,EACd,UAAU,EACV,UAAU,EACV,sBAAsB,GACvB,MAAM,qBAAqB,CAAA;AAG5B,yBAAyB;AACzB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,GACd,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAG3F,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAEnE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAU9C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjF,OAAO,EAAE,6BAA6B,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAsClF,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAItD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAEtE,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAS/F,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAG9C,mCAAmC;AACnC,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,eAAe,GAChB,MAAM,uBAAuB,CAAA;AAQ9B,yCAAyC;AACzC,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,EACtB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,iBAAiB,CAAA"}
@@ -537,9 +537,26 @@ export interface HostEvents {
537
537
  'host.event.session.messages.reset': {
538
538
  sessionId: string;
539
539
  };
540
+ /**
541
+ * 本机(电脑)视角的链路通讯类型 —— 「这次连接到底是直连还是走了 TURN 中继」。
542
+ *
543
+ * 同一事实在本端也成立(手机端同样有一条所选候选对,可自行判定),本事件的用途是让手机
544
+ * **拿电脑视角交叉校验**:两端口径已收敛到共享包的 `classifyLinkKind`(见 `HostEvents` 之外
545
+ * 的 `virlen-remote` 导出),若仍不一致,说明有一端的 stats 读取出了问题。
546
+ *
547
+ * 只在结论**确定**(`direct` / `relay`)时发送:链路刚建立 / 正在重协商时电脑端算不出结论,
548
+ * 那属于「没有结论」,**不发事件**(而不是发一个猜测);`unknown` 不在本事件的取值域内。
549
+ */
540
550
  'host.event.connection.changed': {
541
551
  path: 'direct' | 'relay';
542
- degraded: boolean;
552
+ /**
553
+ * ⚠️ **预留字段,当前不发送**(`virlen-remote@0.1.2` 起转为可选)。
554
+ *
555
+ * 语义(若将来启用):链路虽可用但已**降级**(例如被迫走中继、或质量明显下滑)。
556
+ * 当前实现只区分 `path`,没有任何独立可观测的「降级」判据,故先留空 —— 与其用一个尚未
557
+ * 定义的口径误导消费方,不如让它缺席。消费方**不得**用 `path === 'relay'` 反推它。
558
+ */
559
+ degraded?: boolean;
543
560
  };
544
561
  }
545
562
  export interface MobileEvents {
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/protocol/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/protocol/api.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/protocol/api.ts"],"names":[],"mappings":"AA+MA,uCAAuC;AAEvC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAO,CAAA;AAEpD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAA;AA8QrC,MAAM,UAAU,YAAY,CAAI,QAAkB;IAChD,OAAO;QACL,IAAI,CAA6B,MAAS,EAAE,MAAsB,EAAE,SAAkB;YACpF,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAA4B,CAAA;QACtH,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAI,QAAkB;IACpD,OAAO;QACL,SAAS,CAA6B,KAAQ,EAAE,QAAiC;YAC/E,OAAO,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,QAAsC,CAAC,CAAA;QAC1E,CAAC;KACF,CAAA;AACH,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,gBAAgB,CAAI,QAAkB,EAAE,IAAoB;IAC1E,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,EAAE,GAAG,IAAI,CAAC,MAA0B,CAAmD,CAAA;QAC7F,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;IAC5E,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/protocol/api.ts"],"names":[],"mappings":"AA+MA,uCAAuC;AAEvC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAO,CAAA;AAEpD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAA;AAkSrC,MAAM,UAAU,YAAY,CAAI,QAAkB;IAChD,OAAO;QACL,IAAI,CAA6B,MAAS,EAAE,MAAsB,EAAE,SAAkB;YACpF,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAA4B,CAAA;QACtH,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAI,QAAkB;IACpD,OAAO;QACL,SAAS,CAA6B,KAAQ,EAAE,QAAiC;YAC/E,OAAO,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,QAAsC,CAAC,CAAA;QAC1E,CAAC;KACF,CAAA;AACH,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,gBAAgB,CAAI,QAAkB,EAAE,IAAoB;IAC1E,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,EAAE,GAAG,IAAI,CAAC,MAA0B,CAAmD,CAAA;QAC7F,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;IAC5E,CAAC;AACH,CAAC"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * 链路通讯类型 —— 「这次连接到底是**直连**还是**走了中继**」。
3
+ *
4
+ * ## 为什么放进共享包
5
+ *
6
+ * 这个判定**只存在于本机 PC 的 ICE 候选对里**,不信令、不落盘、重连就变 —— 所以两端各自读、
7
+ * 各自算。但「算」的口径必须**只有一份**:电脑端(`virlen-app/src/bridge/link-kind.ts`)
8
+ * 曾经与手机端(`virlen-mobile/src/lib/rtc-stats.ts`)各持一份副本,一旦漂移就会出现
9
+ * 「手机说直连、电脑说中继」——两台设备互相打脸,而用户无从判断该信谁。
10
+ * 本文件即那份唯一实现:两端都从这里引入,改一处两端同时生效(编译期就能发现不兼容)。
11
+ *
12
+ * 桌面设置页「已连接」旁边那枚胶囊、手机「通讯状态面板」、以及协议事件
13
+ * `host.event.connection.changed`(电脑视角下发),都应读同一个结论。
14
+ *
15
+ * ## 判定口径(取 `RTCPeerConnection.getStats()` 的**所选候选对**)
16
+ *
17
+ * - 候选对任一端 `candidateType === 'relay'` → **中继**(字节确实过了 TURN 服务器);
18
+ * - `host`(同网段)/ `srflx`(STUN 打洞后的公网映射)/ `prflx`(对端反射)→ **直连**;
19
+ * - 拿不到结论(stats 还没填好、环境不支持)→ `unknown` —— **不猜**,宁可不显示。
20
+ *
21
+ * ⚠️ `classifyLinkKind` 是**纯函数**(喂普通对象数组即可),因为真实 WebRTC 跑不进 CI;
22
+ * 真机上的正确性靠「口径钉在单测里」兜住。
23
+ *
24
+ * ## 为什么还要 `LinkKindWatcher`(巡检)
25
+ *
26
+ * 「连上了」不是一次就问完的事:**所选候选对随时会换** ——
27
+ * - 刚打通那几秒:先成的是 relay 那条(打洞还没探完)→ 随后直连候选对胜出**换成 P2P**;
28
+ * - 之后任何一环变网(手机 WiFi → 蜂窝):ICE 重新协商,直连可能又退回中继。
29
+ *
30
+ * 而 `iceconnectionstatechange` **只在状态变时触发**:重新提名 / 重协商后换候选对,状态可以一直是
31
+ * `completed` —— 那一刻一个事件都不会来,胶囊就会一直停在「TURN 中继」上骗人。
32
+ *
33
+ * `RTCIceTransport.onselectedcandidatepairchange` 是规范里专为此存在的精确信号,但它挂在
34
+ * `pc.sctp.transport.iceTransport` 上、还得等 SCTP 建好(数据通道这条路上不一定拿得到),
35
+ * 所以采用「**定时问一次**」兜底:一次 `getStats()` 只有几毫秒,换来的是**类型永远跟得上现实**。
36
+ */
37
+ /** 通讯类型:`direct` = P2P 直连;`relay` = TURN 中继;`unknown` = 没拿到结论。 */
38
+ export type LinkKind = 'direct' | 'relay' | 'unknown';
39
+ /** 一条 stats 记录(`RTCStatsReport` 的 value;只用到少数几个字段)。 */
40
+ export type LinkStatsEntry = Record<string, unknown>;
41
+ /** `RTCStatsReport` 的最小形状(测试可喂 Map / 假对象)。 */
42
+ export interface StatsReportLike {
43
+ forEach(callback: (entry: unknown) => void): void;
44
+ }
45
+ /** `RTCPeerConnection.getStats()` 的最小形状。 */
46
+ export interface StatsProvider {
47
+ getStats(): Promise<StatsReportLike>;
48
+ }
49
+ /**
50
+ * 巡检间隔(毫秒)。
51
+ *
52
+ * 取 3s 而不是更短:变化只影响一枚胶囊,用户感知不到亚秒差异;而 3s 一次 `getStats()` 对一条
53
+ * 数据通道而言开销可忽略(比固定更频繁地问更划算的是「只在链路开着时问」)。
54
+ */
55
+ export declare const LINK_KIND_POLL_MS = 3000;
56
+ /**
57
+ * 从 stats 全集里判定通讯类型。
58
+ *
59
+ * 顺序刻意如此:
60
+ * 1. `transport.selectedCandidatePairId` —— 标准里**明确**指出「正在用哪条候选对」,最可信;
61
+ * 2. 退而求其次:`nominated` 且 `succeeded` 的候选对(老实现不一定给 `transport` 记录);
62
+ * 3. 再退:任意 `succeeded` 的候选对;
63
+ * 4. 都没有 → `unknown`。
64
+ */
65
+ export declare function classifyLinkKind(stats: Iterable<LinkStatsEntry>): LinkKind;
66
+ /**
67
+ * 挑出「正在用哪条候选对」:标准路径(`transport.selectedCandidatePairId`)优先,退而求其次取
68
+ * `nominated` + `succeeded` 的那条,再退取任意 `succeeded` 的候选对。
69
+ *
70
+ * 单独导出是因为消费方(手机端样本提取)也要拿它读 rtt / 协议等字段 —— 与判定走**同一次挑选**,
71
+ * 否则判定与显示可能取自不同的候选对。
72
+ */
73
+ export declare function pickCandidatePair(list: LinkStatsEntry[]): LinkStatsEntry | undefined;
74
+ /**
75
+ * 按 id 找候选记录。
76
+ *
77
+ * `localcandidate` / `remotecandidate` 是 2014 版规范里的类型名(部分老实现仍在用),
78
+ * 一起认下来 —— 认不出的代价是「明明是直连却显示未知」。
79
+ */
80
+ export declare function findCandidate(list: LinkStatsEntry[], id: unknown, side: 'local' | 'remote'): LinkStatsEntry | undefined;
81
+ /**
82
+ * 采一次样(`getStats()` 是异步的)。
83
+ *
84
+ * ⚠️ **不吞异常**:调用方需要知道「问不到」,那是 `unknown` 而不是直连。
85
+ */
86
+ export declare function probeLinkKind(pc: StatsProvider): Promise<LinkKind>;
87
+ /**
88
+ * 通讯类型巡检器 —— 链路开着期间定时问一次「现在是直连还是中继」,变了就上报。
89
+ *
90
+ * 生命周期与链路绑定:`watch(pc)` 开始、`stop()` 结束(换链路 / 断链 / 停用都要 stop)。
91
+ * 不碰 UI、不碰 store,只负责「问」与「去重」,因此能用假 PC + 假时钟完整覆盖。
92
+ */
93
+ export declare class LinkKindWatcher {
94
+ private readonly onChange;
95
+ private readonly intervalMs;
96
+ private timer;
97
+ private pc;
98
+ private _kind;
99
+ constructor(onChange: (kind: LinkKind) => void, intervalMs?: number);
100
+ /** 当前结论(`unknown` = 没结论,或巡检已停)。 */
101
+ get kind(): LinkKind;
102
+ /** 开始巡检(换链路时直接再调一次,旧的会先停)。不会等一个间隔:立刻先问一次。 */
103
+ watch(pc: StatsProvider): void;
104
+ /**
105
+ * 停:链路没了 → 结论跟着作废(会广播 `unknown`,UI 据此收起胶囊)。
106
+ *
107
+ * 「换个链路」也是这个动作(`watch` 内部先调它),所以不会残留上一条链路的结论。
108
+ */
109
+ stop(): void;
110
+ /**
111
+ * 探一次并上报。
112
+ *
113
+ * ⚠️ 两个刻意行为:
114
+ * - 链路**还开着**时读到 `unknown`(stats 还没填好 / 正在重协商)→ **保留上次结论**:
115
+ * 一次读不到不等于「不知道怎么连的」,而在直连/中继之间闪一下比稍旧更难受;
116
+ * - `await` 期间链路可能已换/已停 → 认准「还是这个 PC」再写回。
117
+ */
118
+ poll(): Promise<void>;
119
+ private publish;
120
+ }
121
+ //# sourceMappingURL=link-kind.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-kind.d.ts","sourceRoot":"","sources":["../../src/transport/link-kind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,kEAAkE;AAClE,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAA;AAErD,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEpD,8CAA8C;AAC9C,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAA;CAClD;AAED,4CAA4C;AAC5C,MAAM,WAAW,aAAa;IAC5B,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC,CAAA;CACrC;AAKD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAA;AAErC;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,GAAG,QAAQ,CAiB1E;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,cAAc,GAAG,SAAS,CAEpF;AAkBD;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,cAAc,EAAE,EACtB,EAAE,EAAE,OAAO,EACX,IAAI,EAAE,OAAO,GAAG,QAAQ,GACvB,cAAc,GAAG,SAAS,CAK5B;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAOxE;AAED;;;;;GAKG;AACH,qBAAa,eAAe;IAMxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAN7B,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,EAAE,CAA6B;IACvC,OAAO,CAAC,KAAK,CAAsB;IAEnC,YACmB,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,EAClC,UAAU,GAAE,MAA0B,EACrD;IAEJ,mCAAmC;IACnC,IAAI,IAAI,IAAI,QAAQ,CAEnB;IAED,6CAA6C;IAC7C,KAAK,CAAC,EAAE,EAAE,aAAa,GAAG,IAAI,CAK7B;IAED;;;;OAIG;IACH,IAAI,IAAI,IAAI,CAOX;IAED;;;;;;;OAOG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAW1B;IAED,OAAO,CAAC,OAAO;CAOhB"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * 链路通讯类型 —— 「这次连接到底是**直连**还是**走了中继**」。
3
+ *
4
+ * ## 为什么放进共享包
5
+ *
6
+ * 这个判定**只存在于本机 PC 的 ICE 候选对里**,不信令、不落盘、重连就变 —— 所以两端各自读、
7
+ * 各自算。但「算」的口径必须**只有一份**:电脑端(`virlen-app/src/bridge/link-kind.ts`)
8
+ * 曾经与手机端(`virlen-mobile/src/lib/rtc-stats.ts`)各持一份副本,一旦漂移就会出现
9
+ * 「手机说直连、电脑说中继」——两台设备互相打脸,而用户无从判断该信谁。
10
+ * 本文件即那份唯一实现:两端都从这里引入,改一处两端同时生效(编译期就能发现不兼容)。
11
+ *
12
+ * 桌面设置页「已连接」旁边那枚胶囊、手机「通讯状态面板」、以及协议事件
13
+ * `host.event.connection.changed`(电脑视角下发),都应读同一个结论。
14
+ *
15
+ * ## 判定口径(取 `RTCPeerConnection.getStats()` 的**所选候选对**)
16
+ *
17
+ * - 候选对任一端 `candidateType === 'relay'` → **中继**(字节确实过了 TURN 服务器);
18
+ * - `host`(同网段)/ `srflx`(STUN 打洞后的公网映射)/ `prflx`(对端反射)→ **直连**;
19
+ * - 拿不到结论(stats 还没填好、环境不支持)→ `unknown` —— **不猜**,宁可不显示。
20
+ *
21
+ * ⚠️ `classifyLinkKind` 是**纯函数**(喂普通对象数组即可),因为真实 WebRTC 跑不进 CI;
22
+ * 真机上的正确性靠「口径钉在单测里」兜住。
23
+ *
24
+ * ## 为什么还要 `LinkKindWatcher`(巡检)
25
+ *
26
+ * 「连上了」不是一次就问完的事:**所选候选对随时会换** ——
27
+ * - 刚打通那几秒:先成的是 relay 那条(打洞还没探完)→ 随后直连候选对胜出**换成 P2P**;
28
+ * - 之后任何一环变网(手机 WiFi → 蜂窝):ICE 重新协商,直连可能又退回中继。
29
+ *
30
+ * 而 `iceconnectionstatechange` **只在状态变时触发**:重新提名 / 重协商后换候选对,状态可以一直是
31
+ * `completed` —— 那一刻一个事件都不会来,胶囊就会一直停在「TURN 中继」上骗人。
32
+ *
33
+ * `RTCIceTransport.onselectedcandidatepairchange` 是规范里专为此存在的精确信号,但它挂在
34
+ * `pc.sctp.transport.iceTransport` 上、还得等 SCTP 建好(数据通道这条路上不一定拿得到),
35
+ * 所以采用「**定时问一次**」兜底:一次 `getStats()` 只有几毫秒,换来的是**类型永远跟得上现实**。
36
+ */
37
+ /** 直连的候选类型:本机候选(同网段)/ 服务器反射(打洞成功)/ 对端反射。 */
38
+ const DIRECT_TYPES = ['host', 'srflx', 'prflx'];
39
+ /**
40
+ * 巡检间隔(毫秒)。
41
+ *
42
+ * 取 3s 而不是更短:变化只影响一枚胶囊,用户感知不到亚秒差异;而 3s 一次 `getStats()` 对一条
43
+ * 数据通道而言开销可忽略(比固定更频繁地问更划算的是「只在链路开着时问」)。
44
+ */
45
+ export const LINK_KIND_POLL_MS = 3000;
46
+ /**
47
+ * 从 stats 全集里判定通讯类型。
48
+ *
49
+ * 顺序刻意如此:
50
+ * 1. `transport.selectedCandidatePairId` —— 标准里**明确**指出「正在用哪条候选对」,最可信;
51
+ * 2. 退而求其次:`nominated` 且 `succeeded` 的候选对(老实现不一定给 `transport` 记录);
52
+ * 3. 再退:任意 `succeeded` 的候选对;
53
+ * 4. 都没有 → `unknown`。
54
+ */
55
+ export function classifyLinkKind(stats) {
56
+ const list = [...stats];
57
+ const pair = pickCandidatePair(list);
58
+ if (!pair)
59
+ return 'unknown';
60
+ // 还没定型的候选对(in-progress / failed)不算结论:此刻「怎么连的」还没确定
61
+ const state = pair['state'];
62
+ if (typeof state === 'string' && state !== 'succeeded')
63
+ return 'unknown';
64
+ const local = findCandidate(list, pair['localCandidateId'], 'local');
65
+ const remote = findCandidate(list, pair['remoteCandidateId'], 'remote');
66
+ const types = [local, remote]
67
+ .map((c) => c?.['candidateType'])
68
+ .filter((t) => typeof t === 'string');
69
+ if (types.includes('relay'))
70
+ return 'relay';
71
+ if (types.some((t) => DIRECT_TYPES.includes(t)))
72
+ return 'direct';
73
+ return 'unknown';
74
+ }
75
+ /**
76
+ * 挑出「正在用哪条候选对」:标准路径(`transport.selectedCandidatePairId`)优先,退而求其次取
77
+ * `nominated` + `succeeded` 的那条,再退取任意 `succeeded` 的候选对。
78
+ *
79
+ * 单独导出是因为消费方(手机端样本提取)也要拿它读 rtt / 协议等字段 —— 与判定走**同一次挑选**,
80
+ * 否则判定与显示可能取自不同的候选对。
81
+ */
82
+ export function pickCandidatePair(list) {
83
+ return selectedPair(list) ?? succeededPair(list);
84
+ }
85
+ /** 标准路径:`transport` 记录里明确指出的那条候选对。 */
86
+ function selectedPair(list) {
87
+ const transport = list.find((s) => s['type'] === 'transport' && typeof s['selectedCandidatePairId'] === 'string');
88
+ const id = transport?.['selectedCandidatePairId'];
89
+ if (typeof id !== 'string')
90
+ return undefined;
91
+ return list.find((s) => s['type'] === 'candidate-pair' && s['id'] === id);
92
+ }
93
+ /** 兜底路径:`nominated`(已被选中的那条)+ `succeeded`;没有 nominated 就取第一条 succeeded。 */
94
+ function succeededPair(list) {
95
+ const pairs = list.filter((s) => s['type'] === 'candidate-pair' && s['state'] === 'succeeded');
96
+ return pairs.find((p) => p['nominated'] === true) ?? pairs[0];
97
+ }
98
+ /**
99
+ * 按 id 找候选记录。
100
+ *
101
+ * `localcandidate` / `remotecandidate` 是 2014 版规范里的类型名(部分老实现仍在用),
102
+ * 一起认下来 —— 认不出的代价是「明明是直连却显示未知」。
103
+ */
104
+ export function findCandidate(list, id, side) {
105
+ if (typeof id !== 'string')
106
+ return undefined;
107
+ const kinds = side === 'local' ? ['local-candidate', 'localcandidate'] : ['remote-candidate', 'remotecandidate'];
108
+ return list.find((s) => s['id'] === id && kinds.includes(s['type']));
109
+ }
110
+ /**
111
+ * 采一次样(`getStats()` 是异步的)。
112
+ *
113
+ * ⚠️ **不吞异常**:调用方需要知道「问不到」,那是 `unknown` 而不是直连。
114
+ */
115
+ export async function probeLinkKind(pc) {
116
+ const report = await pc.getStats();
117
+ const list = [];
118
+ report.forEach((entry) => {
119
+ if (entry && typeof entry === 'object')
120
+ list.push(entry);
121
+ });
122
+ return classifyLinkKind(list);
123
+ }
124
+ /**
125
+ * 通讯类型巡检器 —— 链路开着期间定时问一次「现在是直连还是中继」,变了就上报。
126
+ *
127
+ * 生命周期与链路绑定:`watch(pc)` 开始、`stop()` 结束(换链路 / 断链 / 停用都要 stop)。
128
+ * 不碰 UI、不碰 store,只负责「问」与「去重」,因此能用假 PC + 假时钟完整覆盖。
129
+ */
130
+ export class LinkKindWatcher {
131
+ constructor(onChange, intervalMs = LINK_KIND_POLL_MS) {
132
+ this.onChange = onChange;
133
+ this.intervalMs = intervalMs;
134
+ this.timer = null;
135
+ this.pc = null;
136
+ this._kind = 'unknown';
137
+ }
138
+ /** 当前结论(`unknown` = 没结论,或巡检已停)。 */
139
+ get kind() {
140
+ return this._kind;
141
+ }
142
+ /** 开始巡检(换链路时直接再调一次,旧的会先停)。不会等一个间隔:立刻先问一次。 */
143
+ watch(pc) {
144
+ this.stop();
145
+ this.pc = pc;
146
+ void this.poll();
147
+ this.timer = setInterval(() => void this.poll(), this.intervalMs);
148
+ }
149
+ /**
150
+ * 停:链路没了 → 结论跟着作废(会广播 `unknown`,UI 据此收起胶囊)。
151
+ *
152
+ * 「换个链路」也是这个动作(`watch` 内部先调它),所以不会残留上一条链路的结论。
153
+ */
154
+ stop() {
155
+ if (this.timer) {
156
+ clearInterval(this.timer);
157
+ this.timer = null;
158
+ }
159
+ this.pc = null;
160
+ this.publish('unknown');
161
+ }
162
+ /**
163
+ * 探一次并上报。
164
+ *
165
+ * ⚠️ 两个刻意行为:
166
+ * - 链路**还开着**时读到 `unknown`(stats 还没填好 / 正在重协商)→ **保留上次结论**:
167
+ * 一次读不到不等于「不知道怎么连的」,而在直连/中继之间闪一下比稍旧更难受;
168
+ * - `await` 期间链路可能已换/已停 → 认准「还是这个 PC」再写回。
169
+ */
170
+ async poll() {
171
+ const pc = this.pc;
172
+ if (!pc)
173
+ return;
174
+ let kind = 'unknown';
175
+ try {
176
+ kind = await probeLinkKind(pc);
177
+ }
178
+ catch {
179
+ /* `getStats()` 失败 = 拿不到结论,不等于「直连」 */
180
+ }
181
+ if (this.pc !== pc)
182
+ return;
183
+ this.publish(kind);
184
+ }
185
+ publish(kind) {
186
+ // 开着链路时的 `unknown` = 这一次没读到(见 `poll` 的说明)→ 保留上次结论
187
+ if (kind === 'unknown' && this.pc)
188
+ return;
189
+ if (kind === this._kind)
190
+ return;
191
+ this._kind = kind;
192
+ this.onChange(kind);
193
+ }
194
+ }
195
+ //# sourceMappingURL=link-kind.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-kind.js","sourceRoot":"","sources":["../../src/transport/link-kind.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "virlen-remote",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Virlen 手机控制 · 协议栈共享包 —— 帧 / RPC / 错误 / 能力协商 / 设备身份与授权凭证 / 配对载荷 / ICE 解析 / Transport(WebRTC / 内存 / Broadcast)",
5
5
  "license": "MIT",
6
6
  "author": "WeiChens",
package/src/index.ts CHANGED
@@ -127,6 +127,22 @@ export type {
127
127
  export { RtcTransport } from './transport/rtc'
128
128
  export type { RtcTransportOptions } from './transport/rtc'
129
129
 
130
+ // ── RTC 链路类型判定(直连 / 中继,两端同一份口径)──
131
+ export {
132
+ LINK_KIND_POLL_MS,
133
+ classifyLinkKind,
134
+ probeLinkKind,
135
+ pickCandidatePair,
136
+ findCandidate,
137
+ LinkKindWatcher,
138
+ } from './transport/link-kind'
139
+ export type {
140
+ LinkKind,
141
+ LinkStatsEntry,
142
+ StatsReportLike,
143
+ StatsProvider,
144
+ } from './transport/link-kind'
145
+
130
146
  // ── ICE 配置(M7,§31):客户端不再内置任何 TURN 凭证 ──
131
147
  export {
132
148
  ICE_CONFIG_VERSION,
@@ -464,7 +464,27 @@ export interface HostEvents {
464
464
  * 为什么必需:此前删除的消息只是从电脑侧快照里消失(不发事件),手机端会一直显示幽灵消息。
465
465
  */
466
466
  'host.event.session.messages.reset': { sessionId: string }
467
- 'host.event.connection.changed': { path: 'direct' | 'relay'; degraded: boolean }
467
+ /**
468
+ * 本机(电脑)视角的链路通讯类型 —— 「这次连接到底是直连还是走了 TURN 中继」。
469
+ *
470
+ * 同一事实在本端也成立(手机端同样有一条所选候选对,可自行判定),本事件的用途是让手机
471
+ * **拿电脑视角交叉校验**:两端口径已收敛到共享包的 `classifyLinkKind`(见 `HostEvents` 之外
472
+ * 的 `virlen-remote` 导出),若仍不一致,说明有一端的 stats 读取出了问题。
473
+ *
474
+ * 只在结论**确定**(`direct` / `relay`)时发送:链路刚建立 / 正在重协商时电脑端算不出结论,
475
+ * 那属于「没有结论」,**不发事件**(而不是发一个猜测);`unknown` 不在本事件的取值域内。
476
+ */
477
+ 'host.event.connection.changed': {
478
+ path: 'direct' | 'relay'
479
+ /**
480
+ * ⚠️ **预留字段,当前不发送**(`virlen-remote@0.1.2` 起转为可选)。
481
+ *
482
+ * 语义(若将来启用):链路虽可用但已**降级**(例如被迫走中继、或质量明显下滑)。
483
+ * 当前实现只区分 `path`,没有任何独立可观测的「降级」判据,故先留空 —— 与其用一个尚未
484
+ * 定义的口径误导消费方,不如让它缺席。消费方**不得**用 `path === 'relay'` 反推它。
485
+ */
486
+ degraded?: boolean
487
+ }
468
488
  }
469
489
 
470
490
  export interface MobileEvents {
@@ -0,0 +1,222 @@
1
+ /**
2
+ * 链路通讯类型 —— 「这次连接到底是**直连**还是**走了中继**」。
3
+ *
4
+ * ## 为什么放进共享包
5
+ *
6
+ * 这个判定**只存在于本机 PC 的 ICE 候选对里**,不信令、不落盘、重连就变 —— 所以两端各自读、
7
+ * 各自算。但「算」的口径必须**只有一份**:电脑端(`virlen-app/src/bridge/link-kind.ts`)
8
+ * 曾经与手机端(`virlen-mobile/src/lib/rtc-stats.ts`)各持一份副本,一旦漂移就会出现
9
+ * 「手机说直连、电脑说中继」——两台设备互相打脸,而用户无从判断该信谁。
10
+ * 本文件即那份唯一实现:两端都从这里引入,改一处两端同时生效(编译期就能发现不兼容)。
11
+ *
12
+ * 桌面设置页「已连接」旁边那枚胶囊、手机「通讯状态面板」、以及协议事件
13
+ * `host.event.connection.changed`(电脑视角下发),都应读同一个结论。
14
+ *
15
+ * ## 判定口径(取 `RTCPeerConnection.getStats()` 的**所选候选对**)
16
+ *
17
+ * - 候选对任一端 `candidateType === 'relay'` → **中继**(字节确实过了 TURN 服务器);
18
+ * - `host`(同网段)/ `srflx`(STUN 打洞后的公网映射)/ `prflx`(对端反射)→ **直连**;
19
+ * - 拿不到结论(stats 还没填好、环境不支持)→ `unknown` —— **不猜**,宁可不显示。
20
+ *
21
+ * ⚠️ `classifyLinkKind` 是**纯函数**(喂普通对象数组即可),因为真实 WebRTC 跑不进 CI;
22
+ * 真机上的正确性靠「口径钉在单测里」兜住。
23
+ *
24
+ * ## 为什么还要 `LinkKindWatcher`(巡检)
25
+ *
26
+ * 「连上了」不是一次就问完的事:**所选候选对随时会换** ——
27
+ * - 刚打通那几秒:先成的是 relay 那条(打洞还没探完)→ 随后直连候选对胜出**换成 P2P**;
28
+ * - 之后任何一环变网(手机 WiFi → 蜂窝):ICE 重新协商,直连可能又退回中继。
29
+ *
30
+ * 而 `iceconnectionstatechange` **只在状态变时触发**:重新提名 / 重协商后换候选对,状态可以一直是
31
+ * `completed` —— 那一刻一个事件都不会来,胶囊就会一直停在「TURN 中继」上骗人。
32
+ *
33
+ * `RTCIceTransport.onselectedcandidatepairchange` 是规范里专为此存在的精确信号,但它挂在
34
+ * `pc.sctp.transport.iceTransport` 上、还得等 SCTP 建好(数据通道这条路上不一定拿得到),
35
+ * 所以采用「**定时问一次**」兜底:一次 `getStats()` 只有几毫秒,换来的是**类型永远跟得上现实**。
36
+ */
37
+
38
+ /** 通讯类型:`direct` = P2P 直连;`relay` = TURN 中继;`unknown` = 没拿到结论。 */
39
+ export type LinkKind = 'direct' | 'relay' | 'unknown'
40
+
41
+ /** 一条 stats 记录(`RTCStatsReport` 的 value;只用到少数几个字段)。 */
42
+ export type LinkStatsEntry = Record<string, unknown>
43
+
44
+ /** `RTCStatsReport` 的最小形状(测试可喂 Map / 假对象)。 */
45
+ export interface StatsReportLike {
46
+ forEach(callback: (entry: unknown) => void): void
47
+ }
48
+
49
+ /** `RTCPeerConnection.getStats()` 的最小形状。 */
50
+ export interface StatsProvider {
51
+ getStats(): Promise<StatsReportLike>
52
+ }
53
+
54
+ /** 直连的候选类型:本机候选(同网段)/ 服务器反射(打洞成功)/ 对端反射。 */
55
+ const DIRECT_TYPES = ['host', 'srflx', 'prflx']
56
+
57
+ /**
58
+ * 巡检间隔(毫秒)。
59
+ *
60
+ * 取 3s 而不是更短:变化只影响一枚胶囊,用户感知不到亚秒差异;而 3s 一次 `getStats()` 对一条
61
+ * 数据通道而言开销可忽略(比固定更频繁地问更划算的是「只在链路开着时问」)。
62
+ */
63
+ export const LINK_KIND_POLL_MS = 3000
64
+
65
+ /**
66
+ * 从 stats 全集里判定通讯类型。
67
+ *
68
+ * 顺序刻意如此:
69
+ * 1. `transport.selectedCandidatePairId` —— 标准里**明确**指出「正在用哪条候选对」,最可信;
70
+ * 2. 退而求其次:`nominated` 且 `succeeded` 的候选对(老实现不一定给 `transport` 记录);
71
+ * 3. 再退:任意 `succeeded` 的候选对;
72
+ * 4. 都没有 → `unknown`。
73
+ */
74
+ export function classifyLinkKind(stats: Iterable<LinkStatsEntry>): LinkKind {
75
+ const list = [...stats]
76
+ const pair = pickCandidatePair(list)
77
+ if (!pair) return 'unknown'
78
+ // 还没定型的候选对(in-progress / failed)不算结论:此刻「怎么连的」还没确定
79
+ const state = pair['state']
80
+ if (typeof state === 'string' && state !== 'succeeded') return 'unknown'
81
+
82
+ const local = findCandidate(list, pair['localCandidateId'], 'local')
83
+ const remote = findCandidate(list, pair['remoteCandidateId'], 'remote')
84
+ const types = [local, remote]
85
+ .map((c) => c?.['candidateType'])
86
+ .filter((t): t is string => typeof t === 'string')
87
+
88
+ if (types.includes('relay')) return 'relay'
89
+ if (types.some((t) => DIRECT_TYPES.includes(t))) return 'direct'
90
+ return 'unknown'
91
+ }
92
+
93
+ /**
94
+ * 挑出「正在用哪条候选对」:标准路径(`transport.selectedCandidatePairId`)优先,退而求其次取
95
+ * `nominated` + `succeeded` 的那条,再退取任意 `succeeded` 的候选对。
96
+ *
97
+ * 单独导出是因为消费方(手机端样本提取)也要拿它读 rtt / 协议等字段 —— 与判定走**同一次挑选**,
98
+ * 否则判定与显示可能取自不同的候选对。
99
+ */
100
+ export function pickCandidatePair(list: LinkStatsEntry[]): LinkStatsEntry | undefined {
101
+ return selectedPair(list) ?? succeededPair(list)
102
+ }
103
+
104
+ /** 标准路径:`transport` 记录里明确指出的那条候选对。 */
105
+ function selectedPair(list: LinkStatsEntry[]): LinkStatsEntry | undefined {
106
+ const transport = list.find(
107
+ (s) => s['type'] === 'transport' && typeof s['selectedCandidatePairId'] === 'string',
108
+ )
109
+ const id = transport?.['selectedCandidatePairId']
110
+ if (typeof id !== 'string') return undefined
111
+ return list.find((s) => s['type'] === 'candidate-pair' && s['id'] === id)
112
+ }
113
+
114
+ /** 兜底路径:`nominated`(已被选中的那条)+ `succeeded`;没有 nominated 就取第一条 succeeded。 */
115
+ function succeededPair(list: LinkStatsEntry[]): LinkStatsEntry | undefined {
116
+ const pairs = list.filter((s) => s['type'] === 'candidate-pair' && s['state'] === 'succeeded')
117
+ return pairs.find((p) => p['nominated'] === true) ?? pairs[0]
118
+ }
119
+
120
+ /**
121
+ * 按 id 找候选记录。
122
+ *
123
+ * `localcandidate` / `remotecandidate` 是 2014 版规范里的类型名(部分老实现仍在用),
124
+ * 一起认下来 —— 认不出的代价是「明明是直连却显示未知」。
125
+ */
126
+ export function findCandidate(
127
+ list: LinkStatsEntry[],
128
+ id: unknown,
129
+ side: 'local' | 'remote',
130
+ ): LinkStatsEntry | undefined {
131
+ if (typeof id !== 'string') return undefined
132
+ const kinds =
133
+ side === 'local' ? ['local-candidate', 'localcandidate'] : ['remote-candidate', 'remotecandidate']
134
+ return list.find((s) => s['id'] === id && kinds.includes(s['type'] as string))
135
+ }
136
+
137
+ /**
138
+ * 采一次样(`getStats()` 是异步的)。
139
+ *
140
+ * ⚠️ **不吞异常**:调用方需要知道「问不到」,那是 `unknown` 而不是直连。
141
+ */
142
+ export async function probeLinkKind(pc: StatsProvider): Promise<LinkKind> {
143
+ const report = await pc.getStats()
144
+ const list: LinkStatsEntry[] = []
145
+ report.forEach((entry) => {
146
+ if (entry && typeof entry === 'object') list.push(entry as LinkStatsEntry)
147
+ })
148
+ return classifyLinkKind(list)
149
+ }
150
+
151
+ /**
152
+ * 通讯类型巡检器 —— 链路开着期间定时问一次「现在是直连还是中继」,变了就上报。
153
+ *
154
+ * 生命周期与链路绑定:`watch(pc)` 开始、`stop()` 结束(换链路 / 断链 / 停用都要 stop)。
155
+ * 不碰 UI、不碰 store,只负责「问」与「去重」,因此能用假 PC + 假时钟完整覆盖。
156
+ */
157
+ export class LinkKindWatcher {
158
+ private timer: ReturnType<typeof setInterval> | null = null
159
+ private pc: StatsProvider | null = null
160
+ private _kind: LinkKind = 'unknown'
161
+
162
+ constructor(
163
+ private readonly onChange: (kind: LinkKind) => void,
164
+ private readonly intervalMs: number = LINK_KIND_POLL_MS,
165
+ ) {}
166
+
167
+ /** 当前结论(`unknown` = 没结论,或巡检已停)。 */
168
+ get kind(): LinkKind {
169
+ return this._kind
170
+ }
171
+
172
+ /** 开始巡检(换链路时直接再调一次,旧的会先停)。不会等一个间隔:立刻先问一次。 */
173
+ watch(pc: StatsProvider): void {
174
+ this.stop()
175
+ this.pc = pc
176
+ void this.poll()
177
+ this.timer = setInterval(() => void this.poll(), this.intervalMs)
178
+ }
179
+
180
+ /**
181
+ * 停:链路没了 → 结论跟着作废(会广播 `unknown`,UI 据此收起胶囊)。
182
+ *
183
+ * 「换个链路」也是这个动作(`watch` 内部先调它),所以不会残留上一条链路的结论。
184
+ */
185
+ stop(): void {
186
+ if (this.timer) {
187
+ clearInterval(this.timer)
188
+ this.timer = null
189
+ }
190
+ this.pc = null
191
+ this.publish('unknown')
192
+ }
193
+
194
+ /**
195
+ * 探一次并上报。
196
+ *
197
+ * ⚠️ 两个刻意行为:
198
+ * - 链路**还开着**时读到 `unknown`(stats 还没填好 / 正在重协商)→ **保留上次结论**:
199
+ * 一次读不到不等于「不知道怎么连的」,而在直连/中继之间闪一下比稍旧更难受;
200
+ * - `await` 期间链路可能已换/已停 → 认准「还是这个 PC」再写回。
201
+ */
202
+ async poll(): Promise<void> {
203
+ const pc = this.pc
204
+ if (!pc) return
205
+ let kind: LinkKind = 'unknown'
206
+ try {
207
+ kind = await probeLinkKind(pc)
208
+ } catch {
209
+ /* `getStats()` 失败 = 拿不到结论,不等于「直连」 */
210
+ }
211
+ if (this.pc !== pc) return
212
+ this.publish(kind)
213
+ }
214
+
215
+ private publish(kind: LinkKind): void {
216
+ // 开着链路时的 `unknown` = 这一次没读到(见 `poll` 的说明)→ 保留上次结论
217
+ if (kind === 'unknown' && this.pc) return
218
+ if (kind === this._kind) return
219
+ this._kind = kind
220
+ this.onChange(kind)
221
+ }
222
+ }