feat(config): 添加配置验证功能模块

- 实现配置架构解析器,支持对象、数组和标量类型的递归验证
- 添加YAML文档解析功能,包括注释提取和路径映射
- 集成配置验证诊断系统,支持多种数据类型约束检查
- 实现批量编辑器的可编辑字段收集功能
- 添加表单更新应用逻辑,支持标量和数组值的安全更新
- 集成数值约束验证,包括最小值、最大值和倍数检查
- 实现字符串长度和正则表达式模式验证
- 添加枚举值匹配和唯一性约束检查
- 实现配置架构注释提取和样本YAML生成功能
- 支持配置架构默认值和常量值处理
This commit is contained in:
GeWuYou 2026-04-10 20:09:12 +08:00
parent 925af56b1c
commit dca304afeb
2 changed files with 99 additions and 0 deletions

View File

@ -897,6 +897,11 @@ function parseSchemaNode(rawNode, displayPath) {
const containsNode = value.contains && typeof value.contains === "object"
? parseSchemaNode(value.contains, joinArrayTemplatePath(displayPath))
: undefined;
if (!containsNode &&
(typeof metadata.minContains === "number" || typeof metadata.maxContains === "number")) {
throw new Error(`Schema property '${displayPath}' declares 'minContains' or 'maxContains' without 'contains'.`);
}
if (containsNode && containsNode.type === "array") {
throw new Error(`Schema property '${displayPath}' uses unsupported nested array 'contains' schemas.`);
}

View File

@ -863,6 +863,64 @@ dropRates:
assert.match(diagnostics[0].message, /at most 1 items matching the 'contains' schema|最多只能包含 1 个匹配 contains 条件的元素/u);
});
test("validateParsedConfig should accept satisfied contains constraints", () => {
const schemaWithRange = parseSchemaContent(`
{
"type": "object",
"properties": {
"dropRates": {
"type": "array",
"minContains": 2,
"maxContains": 3,
"contains": {
"type": "integer",
"const": 5
},
"items": {
"type": "integer"
}
}
}
}
`);
const yamlWithinRange = parseTopLevelYaml(`
dropRates:
- 0
- 5
- 5
- 10
`);
assert.deepEqual(validateParsedConfig(schemaWithRange, yamlWithinRange), []);
const schemaWithDefaultMinContains = parseSchemaContent(`
{
"type": "object",
"properties": {
"dropRates": {
"type": "array",
"contains": {
"type": "integer",
"const": 5
},
"items": {
"type": "integer"
}
}
}
}
`);
const yamlSatisfyingDefaultMinContains = parseTopLevelYaml(`
dropRates:
- 1
- 2
- 5
- 3
`);
assert.deepEqual(validateParsedConfig(schemaWithDefaultMinContains, yamlSatisfyingDefaultMinContains), []);
});
test("validateParsedConfig should accept large decimal multiples without floating-point drift", () => {
const schema = parseSchemaContent(`
{
@ -1180,6 +1238,42 @@ test("parseSchemaContent should reject nested-array contains schemas", () => {
/unsupported nested array 'contains' schemas/u);
});
test("parseSchemaContent should reject minContains and maxContains without contains", () => {
assert.throws(
() => parseSchemaContent(`
{
"type": "object",
"properties": {
"dropRates": {
"type": "array",
"minContains": 1,
"items": {
"type": "integer"
}
}
}
}
`),
/'minContains' or 'maxContains' without 'contains'/u);
assert.throws(
() => parseSchemaContent(`
{
"type": "object",
"properties": {
"dropRates": {
"type": "array",
"maxContains": 1,
"items": {
"type": "integer"
}
}
}
}
`),
/'minContains' or 'maxContains' without 'contains'/u);
});
test("parseSchemaContent should reject contains schemas where default minContains exceeds maxContains", () => {
assert.throws(
() => parseSchemaContent(`