refactor(framework): 移除 resolveBootstrap 下游兜底,立第二准则

- CLAUDE.md 新增「第二准则:数据源权威、唯一,下游不兜底」
- bootstrap 删除 fallbackServers 选项与 ?? DEFAULT_GAMESERVER 猜默认
- 无 server/gameserver、远程失败、解析不出地址 → 一律抛 ConfigFetchError 显式暴露
- 同步 guide + 两篇 spec(删 fallback/降级措辞)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 18:59:15 +08:00
co-authored by Claude Opus 4.8
parent 39bb964a49
commit 51a4ee57ac
6 changed files with 42 additions and 29 deletions
+5 -7
View File
@@ -232,8 +232,8 @@ export const PROFILES: Record<string, DebugProfile> = {
`resolveBootstrap` 决定 `servers`(一个 `ws://` 地址数组,交给 `NetClient` 轮询)的顺序:
1. **当前 profile 有 `server`** → 直接用它,**跳过远程配置抓取**(`local`/`lab` 这类直连档位走这条)。
2. **否则**抓远程配置文件(profile 的 `gameserver`,没有就用 `DEFAULT_GAMESERVER`),用 `getParam` 取出连接参数,`resolveServers` 组装候选列表。
3. 远程抓取失败或解析不出地址时:若调用方传了 `fallbackServers` 则降级使用;否则抛 `ConfigFetchError`(交由上层重试,重连本身归 `NetClient`)。
2. **否则**抓 profile 的 `gameserver` 远程配置文件,用 `getParam` 取出连接参数,`resolveServers` 组装候选列表。
3. **不兜底**(CLAUDE.md 第二准则):profile 既无 `server` 又无 `gameserver`、远程抓取失败、或解析不出任何地址——一律抛 `ConfigFetchError` **显式暴露**,不猜默认、不降级。重试/重连归 `NetClient`,不在本层造兜底轮子。
`resolveServers` 取的连接参数键(**逐字一致**原工程,按此优先级):
@@ -308,7 +308,6 @@ interface BootstrapOptions {
search: string; // location.search(如 '?profile=local')
fetcher: ConfigFetcher; // 远程配置抓取;生产传 new HttpConfigFetcher()
isNative: boolean; // 环境判定:原生 WebView=true,H5/浏览器=false(由调用方算)
fallbackServers?: string[];// 远程失败/无地址时降级
cacheBust?: () => string; // 防缓存串注入(默认 String(Date.now()))
}
@@ -318,7 +317,7 @@ interface BootstrapResult {
rawConfig: RemoteConfig | null; // 直连档位为 null
profile: DebugProfile;
mode: 'debug' | 'release'; // 透传入参 mode
isDebugger: boolean; // 是否开启收发包日志(debug 模式 + profile.isDebugger)
isDebugger: boolean; // 是否开启收发包日志(= mode === 'debug')
}
function resolveBootstrap(opts: BootstrapOptions): Promise<BootstrapResult>;
@@ -344,7 +343,6 @@ async function startup() {
search: location.search,
fetcher: new HttpConfigFetcher(),
isNative: href.indexOf('index.html') > -1, // 等价旧 Logic.isH5Version() 反向
fallbackServers: [], // 可选:填应急地址
});
const net = new NetClient({
@@ -435,9 +433,9 @@ npm run typecheck:framework # 类型检查(tsc --noEmit)
在 `PROFILES` 里加一个档位(见 [§5](#5-调试-profiles) 的 `lab` 例子),用 `?profile=lab`。
### E. 远程配置服挂了也要能调
### E. 想绕开远程配置服直连某地址
给 `resolveBootstrap` 传 `fallbackServers: ['ws://<应急地址>']`,抓取失败时自动降级。
加一个带 `server` 的 profile(同 D 的 `lab` 例子)用 `?profile=lab` 直连——这是把"应急地址"写进**权威来源**(profile),而不是让 `resolveBootstrap` 兜底。远程配置服真的挂了,就是显式 `ConfigFetchError`,去修配置服或临时切到直连 profile,不在代码里降级(CLAUDE.md 第二准则)。
---
@@ -93,7 +93,6 @@ export interface BootstrapOptions {
search: string; // location.search(测试可注入)
fetcher: ConfigFetcher; // 远程配置抓取(测试注入 fake)
isNative: boolean; // 环境判定结果(URL 含 index.html → true)
fallbackServers?: string[]; // 远程失败时降级
}
export interface BootstrapResult {
identity: ChannelIdentity;
@@ -134,7 +133,7 @@ export function resolveActiveProfile(search: string): DebugProfile;
## 6. 错误处理
- 远程抓取失败:无 `fallbackServers` 时抛 `ConfigFetchError`,交调用方重试(重连归 net,不在本层重复造轮子);有 fallback 则降级使用;
- 远程抓取失败 / 解析不出任何地址 / profile 既无 server 又无 gameserver:一律抛 `ConfigFetchError` **显式暴露**,不猜默认、不降级(CLAUDE.md 第二准则)。重试/重连归 net,不在本层造兜底;
- `getParam`:某层匹配不到 → 返回已找到的最具体值或顶层 `data[key]`,最终可能 `null`(忠实原语义);
- 原生 settings 缺失:`try/catch` → source 返回 `{}`(不覆盖),等价原 `try/catch return ""`;
- 远程 txt JSON 解析失败:抛 `ConfigParseError`。
@@ -57,7 +57,6 @@ export interface BootstrapOptions {
search: string;
fetcher: ConfigFetcher;
isNative: boolean;
fallbackServers?: string[];
cacheBust?: () => string;
}