feat: 引入分层配置生命周期,支持变量引用和 JSON Schema 校验
- 新增 src/server/config/ 模块(types、issues、variables、normalizer、schema)
- 配置布局从 server.host/server.port 切换为 server.listen.host/server.listen.port
- 移除 HOST/PORT 隐式环境变量覆盖,改为 YAML 显式 ${KEY} 变量引用
- 支持 ${KEY}、${KEY|default}、${KEY|}、$${KEY} 变量语法
- 使用 @sinclair/typebox + ajv 实现运行时严格契约校验和 JSON Schema 导出
- 新增 scripts/generate-config-schema.ts 和 config.schema.json
- 新增 bun run schema / schema:check 命令,check 先执行 schema:check
- 更新 README.md 和 DEVELOPMENT.md 匹配新配置体系
- 新增变量解析、schema 校验和 schema 同步测试
This commit is contained in:
43
src/server/config/issues.ts
Normal file
43
src/server/config/issues.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
export interface ConfigValidationIssue {
|
||||
code: string;
|
||||
message: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export function dedupeIssues(issues: ConfigValidationIssue[]): ConfigValidationIssue[] {
|
||||
const seen = new Set<string>();
|
||||
const result: ConfigValidationIssue[] = [];
|
||||
for (const item of issues) {
|
||||
const key = `${item.code}:${item.path}:${item.message}`;
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
result.push(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function formatConfigIssues(issues: ConfigValidationIssue[]): string {
|
||||
return issues.map(formatConfigIssue).join("\n");
|
||||
}
|
||||
|
||||
export function issue(code: string, path: string, message: string): ConfigValidationIssue {
|
||||
return { code, message, path };
|
||||
}
|
||||
|
||||
export function joinPath(base: string, key: string): string {
|
||||
if (base === "") return key;
|
||||
if (key.startsWith("[")) return `${base}${key}`;
|
||||
return `${base}.${key}`;
|
||||
}
|
||||
|
||||
export function renderPath(path: string): string {
|
||||
return path === "" ? "配置文件" : path;
|
||||
}
|
||||
|
||||
export function throwConfigIssues(issues: ConfigValidationIssue[]): never {
|
||||
throw new Error(formatConfigIssues(issues));
|
||||
}
|
||||
|
||||
function formatConfigIssue(i: ConfigValidationIssue): string {
|
||||
return `${renderPath(i.path)} ${i.message}`;
|
||||
}
|
||||
Reference in New Issue
Block a user