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:
@@ -3,7 +3,7 @@ import { resolveIdentity } from './identity.ts';
|
||||
import { BUILD_IDENTITY } from './sources/defaults.ts';
|
||||
import { queryStringSource } from './sources/query-string.ts';
|
||||
import { nativeSettingsSource, type NativeHost } from './sources/native-settings.ts';
|
||||
import { resolveActiveProfile, PROFILES, DEFAULT_GAMESERVER, type DebugProfile } from './profiles.ts';
|
||||
import { resolveActiveProfile, PROFILES, type DebugProfile } from './profiles.ts';
|
||||
import { resolveServers, type ConfigFetcher, type RemoteConfig, ConfigFetchError } from './remote-config.ts';
|
||||
import type { RuntimeMode } from './runtime-mode.ts';
|
||||
|
||||
@@ -13,7 +13,6 @@ export interface BootstrapOptions {
|
||||
search: string; // location.search
|
||||
fetcher: ConfigFetcher; // 远程配置抓取
|
||||
isNative: boolean; // 环境判定(URL 含 index.html → true,由调用方算)
|
||||
fallbackServers?: string[]; // 远程失败/无地址时降级
|
||||
cacheBust?: () => string; // 防缓存串注入(默认 Date.now)
|
||||
}
|
||||
|
||||
@@ -26,7 +25,11 @@ export interface BootstrapResult {
|
||||
profile: DebugProfile;
|
||||
}
|
||||
|
||||
/** 启动编排:按运行模式解析身份 + 决定服务器,产出可喂 NetClient 的结果。 */
|
||||
/**
|
||||
* 启动编排:按运行模式解析身份 + 决定服务器,产出可喂 NetClient 的结果。
|
||||
* 数据源权威唯一、下游不兜底(CLAUDE.md 第二准则):服务器地址只来自 profile.server(直连)
|
||||
* 或远程配置解析;取不到一律显式抛错暴露,不猜默认、不降级。
|
||||
*/
|
||||
export async function resolveBootstrap(opts: BootstrapOptions): Promise<BootstrapResult> {
|
||||
const isDebug = opts.mode === 'debug';
|
||||
|
||||
@@ -46,29 +49,29 @@ export async function resolveBootstrap(opts: BootstrapOptions): Promise<Bootstra
|
||||
|
||||
const resultBase = { mode: opts.mode, isDebugger: isDebug, identity, profile };
|
||||
|
||||
// 服务器决策:profile 显式 server 直连捷径(仅 debug 的 local/lab 等档位会有)
|
||||
// 服务器决策:profile 显式 server 直连捷径
|
||||
if (profile.server) {
|
||||
return { ...resultBase, servers: [profile.server], rawConfig: null };
|
||||
}
|
||||
|
||||
// 否则远程配置
|
||||
const gameserver = profile.gameserver ?? DEFAULT_GAMESERVER;
|
||||
// profile 是服务器地址的权威来源:无 server 则必须有 gameserver,缺则配置错误,显式暴露。
|
||||
if (!profile.gameserver) {
|
||||
throw new ConfigFetchError(`profile '${profile.name}' 未配置 server 或 gameserver`);
|
||||
}
|
||||
|
||||
const bust = opts.cacheBust ? opts.cacheBust() : String(Date.now());
|
||||
const url = gameserver + '?' + bust; // 防缓存,等价原 min_timestamp/ifast_random
|
||||
const url = profile.gameserver + '?' + bust; // 防缓存,等价原 min_timestamp/ifast_random
|
||||
|
||||
let config: RemoteConfig;
|
||||
try {
|
||||
config = await opts.fetcher.fetch(url);
|
||||
} catch {
|
||||
if (opts.fallbackServers && opts.fallbackServers.length) {
|
||||
return { ...resultBase, servers: opts.fallbackServers, rawConfig: null };
|
||||
}
|
||||
throw new ConfigFetchError(`远程配置抓取失败: ${url}`);
|
||||
}
|
||||
|
||||
let servers = resolveServers(config, identity);
|
||||
if (!servers.length && opts.fallbackServers && opts.fallbackServers.length) {
|
||||
servers = opts.fallbackServers;
|
||||
const servers = resolveServers(config, identity);
|
||||
if (!servers.length) {
|
||||
throw new ConfigFetchError(`远程配置未给出可用服务器地址: ${url}`);
|
||||
}
|
||||
return { ...resultBase, servers, rawConfig: config };
|
||||
}
|
||||
|
||||
@@ -47,18 +47,21 @@ test('debug + 原生模式:identity 来自 window.settings', async () => {
|
||||
assert.equal(r.identity.marketid, 3);
|
||||
});
|
||||
|
||||
test('debug + 远程失败 + 有 fallbackServers → 降级使用', async () => {
|
||||
const r = await resolveBootstrap({ mode: 'debug', win: {}, search: '', fetcher: throwingFetcher, isNative: false, fallbackServers: ['ws://fb:1'] });
|
||||
assert.deepEqual(r.servers, ['ws://fb:1']);
|
||||
});
|
||||
|
||||
test('debug + 远程失败 + 无 fallback → 抛 ConfigFetchError', async () => {
|
||||
test('远程抓取失败 → 抛 ConfigFetchError(不兜底)', async () => {
|
||||
await assert.rejects(
|
||||
resolveBootstrap({ mode: 'debug', win: {}, search: '', fetcher: throwingFetcher, isNative: false }),
|
||||
/远程配置抓取失败/,
|
||||
);
|
||||
});
|
||||
|
||||
test('远程配置未给出可用服务器地址 → 抛错(不兜底)', async () => {
|
||||
const emptyFetcher: ConfigFetcher = { fetch: async () => ({ data: {} }) };
|
||||
await assert.rejects(
|
||||
resolveBootstrap({ mode: 'debug', win: {}, search: '', fetcher: emptyFetcher, isNative: false }),
|
||||
/未给出可用服务器/,
|
||||
);
|
||||
});
|
||||
|
||||
test('debug 结果含 mode/isDebugger', async () => {
|
||||
const fetcher = fetcherReturning(REMOTE);
|
||||
const r = await resolveBootstrap({ mode: 'debug', win: {}, search: '', fetcher, isNative: false });
|
||||
|
||||
Reference in New Issue
Block a user