Files
mihomo-patchbay/shared/outbound-chain.ts
T
im0o c5ac59ecf9 按审查结论重构:先补测试网,再搬代码
顺序是刻意的。前端一行测试都没有,直接挪代码挪坏了 bun test 不会红,所以先把纯逻辑
抽到能被测试的位置并补上用例,最后才动组件。测试从 37 条增加到 77 条。

抽出来的四块:
- shared/outbound-chain.ts  「这个端口现在走哪条线」的链路解析,带防环,9 条测试
- web/delay-test.ts          延迟测试失败时说什么,6 个分支原本一条测试都没有
- web/runtime.ts             运行时状态的模块级单例,照 prefs.ts 已有的模式
- web/components/InboundList.vue  入站列表

出站的文本原本散成四份(读一份、显示一份、写回一份、表单又抄一份),加一种出站模式
要同时改四处,漏一个就是显示与写回不一致。合并成 OUTBOUND_PRESETS 一张表。

给认证和透传白名单补了 20 条测试。两者都是安全边界、都是纯函数、都不需要内核在线,
之前却只靠需要活内核的冒烟脚本间接覆盖。其中「形似但不是健康检查的路径」几条确认了
正则没写松。

另外:配置快照不再把整份文件原文下发(前端从不读,白白让响应体积翻倍);
yaml-cosmetics 的测试改走生产路径,之前它自己拼了一遍三步流程,落盘链路漏掉其中一步
测试照样会绿。
2026-08-10 18:21:23 +08:00

64 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 「这个端口现在到底走哪条线」——这个工具要回答的核心问题。
*
* 答案往往不是一跳能到的:一路入站落在 `PROXY` 组上,`PROXY` 选中了 `AUTO`,
* `AUTO` 又按延迟挑了某条线路。只显示第一层等于没回答。
*
* 这里只做纯计算,输入是内核报上来的运行时状态,输出是名字数组。放在 shared 里
* 是为了能被 `bun test` 覆盖——它带防环处理,而防环这种事写在组件的 computed 里
* 就再也没人测得到。
*/
/** 运行时状态里跟解析链路有关的部分。完整定义在 web/api.ts。 */
export interface RuntimeNode {
/** 策略组当前选中的成员。不是策略组就没有这个字段 */
now?: string
/** 策略组的成员列表 */
all?: string[]
}
/**
* 从落脚的出站一路跟着「当前选中」往下走。
*
* 返回的数组第一项是起点,最后一项是走到头的那个名字。起点为 null 时返回空数组。
* 策略组理论上可以互相引用成环,遇到重复的名字就停下——内核自己会拒绝这种配置,
* 但界面不能因为一份坏配置就转到卡死。
*/
export function resolveChain(
runtime: Record<string, RuntimeNode>,
start: string | null,
): string[] {
const hops: string[] = []
let cursor = start
while (cursor && !hops.includes(cursor)) {
hops.push(cursor)
cursor = runtime[cursor]?.now ?? null
}
return hops
}
export interface SwitchableGroup {
group: string
members: string[]
now: string
}
/**
* 链条上最后一个还是策略组的环节。
*
* 那是用户能真正做出选择的地方:往前的环节只是层层转交,往后已经是具体线路了。
* 取最后一个而不是第一个,是因为换最靠近线路的那一层才是「换线路」;换前面的层
* 换的是「走哪套策略」,那是另一回事。
*/
export function lastSwitchableGroup(
runtime: Record<string, RuntimeNode>,
chain: string[],
): SwitchableGroup | null {
for (let i = chain.length - 1; i >= 0; i -= 1) {
const name = chain[i]!
const node = runtime[name]
if (node?.all?.length) return { group: name, members: node.all, now: node.now ?? '' }
}
return null
}