feat(framework): config 启动编排 resolveBootstrap(身份合并+服务器决策+降级)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 16:44:43 +08:00
co-authored by Claude Opus 4.8
parent 6333ddc8e4
commit c87d204f28
2 changed files with 121 additions and 0 deletions
@@ -0,0 +1,61 @@
import type { ChannelIdentity } from './identity.ts';
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, DEFAULT_GAMESERVER, type DebugProfile } from './profiles.ts';
import { resolveServers, type ConfigFetcher, type RemoteConfig, ConfigFetchError } from './remote-config.ts';
export interface BootstrapOptions {
win: NativeHost; // 原生注入宿主(≈ window)
search: string; // location.search
fetcher: ConfigFetcher; // 远程配置抓取
isNative: boolean; // 环境判定(URL 含 index.html → true,由调用方算)
fallbackServers?: string[]; // 远程失败/无地址时降级
cacheBust?: () => string; // 防缓存串注入(默认 Date.now)
}
export interface BootstrapResult {
identity: ChannelIdentity;
servers: string[];
rawConfig: RemoteConfig | null;
profile: DebugProfile;
}
/** 启动编排:解析身份 + 决定服务器,产出可喂 NetClient 的结果。 */
export async function resolveBootstrap(opts: BootstrapOptions): Promise<BootstrapResult> {
const profile = resolveActiveProfile(opts.search);
const runtime = opts.isNative ? nativeSettingsSource(opts.win) : queryStringSource(opts.search);
const identity = resolveIdentity([
() => BUILD_IDENTITY,
runtime,
() => profile.identity ?? {},
queryStringSource(opts.search), // 显式 URL 覆盖(最高)
]);
// 服务器决策:profile 显式 server 直连捷径
if (profile.server) {
return { identity, servers: [profile.server], rawConfig: null, profile };
}
// 否则远程配置
const base = profile.gameserver ?? DEFAULT_GAMESERVER;
const bust = opts.cacheBust ? opts.cacheBust() : String(Date.now());
const url = base + '?' + bust; // 防缓存,等价原 min_timestamp/ifast_random
let config: RemoteConfig;
try {
config = await opts.fetcher.fetch(url);
} catch {
if (opts.fallbackServers && opts.fallbackServers.length) {
return { identity, servers: opts.fallbackServers, rawConfig: null, profile };
}
throw new ConfigFetchError(`远程配置抓取失败: ${url}`);
}
let servers = resolveServers(config, identity);
if (!servers.length && opts.fallbackServers && opts.fallbackServers.length) {
servers = opts.fallbackServers;
}
return { identity, servers, rawConfig: config, profile };
}