mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-09 10:19:00 +08:00
Merge pull request #343 from GeWuYou/feat/ai-first-config
Feat/添加数组形状关键字的验证与拒绝机制
This commit is contained in:
commit
3fbc563d59
@ -21,3 +21,4 @@
|
||||
GF_ConfigSchema_014 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
|
||||
GF_ConfigSchema_015 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
|
||||
GF_ConfigSchema_016 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
|
||||
GF_ConfigSchema_017 | GFramework.SourceGenerators.Config | Error | ConfigSchemaDiagnostics
|
||||
|
||||
@ -237,6 +237,7 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
||||
return TryValidateStringFormatMetadataRecursively(filePath, "<root>", root, out diagnostic) &&
|
||||
TryValidateUnsupportedCombinatorKeywordsRecursively(filePath, "<root>", root, out diagnostic) &&
|
||||
TryValidateUnsupportedOpenObjectKeywordsRecursively(filePath, "<root>", root, out diagnostic) &&
|
||||
TryValidateUnsupportedArrayShapeKeywordsRecursively(filePath, "<root>", root, out diagnostic) &&
|
||||
TryValidateDependentRequiredMetadataRecursively(filePath, "<root>", root, out diagnostic) &&
|
||||
TryValidateDependentSchemasMetadataRecursively(filePath, "<root>", root, out diagnostic) &&
|
||||
TryValidateAllOfMetadataRecursively(filePath, "<root>", root, out diagnostic) &&
|
||||
@ -916,6 +917,40 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
||||
out diagnostic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 递归拒绝当前共享子集尚未支持的数组形状关键字。
|
||||
/// 当前配置系统只接受单个 object-typed <c>items</c> schema,
|
||||
/// 并继续拒绝 tuple / open-array 关键字,避免生成器对数组元素形状
|
||||
/// 比运行时与工具链更宽松。
|
||||
/// </summary>
|
||||
/// <param name="filePath">Schema 文件路径。</param>
|
||||
/// <param name="displayPath">逻辑字段路径。</param>
|
||||
/// <param name="element">当前 schema 节点。</param>
|
||||
/// <param name="diagnostic">失败时返回的诊断。</param>
|
||||
/// <returns>当前节点树是否未声明不支持的数组形状关键字。</returns>
|
||||
private static bool TryValidateUnsupportedArrayShapeKeywordsRecursively(
|
||||
string filePath,
|
||||
string displayPath,
|
||||
JsonElement element,
|
||||
out Diagnostic? diagnostic)
|
||||
{
|
||||
return TryTraverseSchemaRecursively(
|
||||
filePath,
|
||||
displayPath,
|
||||
element,
|
||||
static (currentFilePath, currentDisplayPath, currentElement, _) =>
|
||||
{
|
||||
return TryValidateUnsupportedArrayShapeKeywords(
|
||||
currentFilePath,
|
||||
currentDisplayPath,
|
||||
currentElement,
|
||||
out var currentDiagnostic)
|
||||
? (true, (Diagnostic?)null)
|
||||
: (false, currentDiagnostic);
|
||||
},
|
||||
out diagnostic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证当前节点是否声明了会改变生成类型形状的未支持组合关键字。
|
||||
/// </summary>
|
||||
@ -976,6 +1011,36 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证当前节点是否声明了当前共享子集尚未支持的数组形状关键字。
|
||||
/// </summary>
|
||||
/// <param name="filePath">Schema 文件路径。</param>
|
||||
/// <param name="displayPath">逻辑字段路径。</param>
|
||||
/// <param name="element">当前 schema 节点。</param>
|
||||
/// <param name="diagnostic">失败时返回的诊断。</param>
|
||||
/// <returns>未声明不支持关键字时返回 <see langword="true" />。</returns>
|
||||
private static bool TryValidateUnsupportedArrayShapeKeywords(
|
||||
string filePath,
|
||||
string displayPath,
|
||||
JsonElement element,
|
||||
out Diagnostic? diagnostic)
|
||||
{
|
||||
diagnostic = null;
|
||||
if (TryGetUnsupportedArrayShapeKeywordName(element) is not { } keywordName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
diagnostic = Diagnostic.Create(
|
||||
ConfigSchemaDiagnostics.UnsupportedArrayShapeKeyword,
|
||||
CreateFileLocation(filePath),
|
||||
Path.GetFileName(filePath),
|
||||
displayPath,
|
||||
keywordName,
|
||||
"The current config schema subset only accepts one object-valued 'items' schema and rejects tuple or open-array keywords that can change item shape across Runtime, Generator, and Tooling.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回当前节点声明的首个未支持组合关键字。
|
||||
/// </summary>
|
||||
@ -1007,6 +1072,19 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
||||
null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回当前节点声明的首个未支持数组形状关键字。
|
||||
/// </summary>
|
||||
/// <param name="element">当前 schema 节点。</param>
|
||||
/// <returns>命中的关键字名称;未声明时返回空。</returns>
|
||||
private static string? TryGetUnsupportedArrayShapeKeywordName(JsonElement element)
|
||||
{
|
||||
return element.TryGetProperty("prefixItems", out _) ? "prefixItems" :
|
||||
element.TryGetProperty("additionalItems", out _) ? "additionalItems" :
|
||||
element.TryGetProperty("unevaluatedItems", out _) ? "unevaluatedItems" :
|
||||
null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 以统一顺序递归遍历 schema 树,并把每个节点交给调用方提供的校验逻辑。
|
||||
/// 该遍历覆盖对象属性、<c>dependentSchemas</c> / <c>allOf</c> /
|
||||
|
||||
@ -187,4 +187,15 @@ public static class ConfigSchemaDiagnostics
|
||||
SourceGeneratorsConfigCategory,
|
||||
DiagnosticSeverity.Error,
|
||||
true);
|
||||
|
||||
/// <summary>
|
||||
/// schema 节点声明了当前共享子集尚未支持的数组形状关键字。
|
||||
/// </summary>
|
||||
public static readonly DiagnosticDescriptor UnsupportedArrayShapeKeyword = new(
|
||||
"GF_ConfigSchema_017",
|
||||
"Config schema uses an unsupported array-shape keyword",
|
||||
"Property '{1}' in schema file '{0}' uses unsupported array-shape keyword '{2}': {3}",
|
||||
SourceGeneratorsConfigCategory,
|
||||
DiagnosticSeverity.Error,
|
||||
true);
|
||||
}
|
||||
|
||||
@ -1636,6 +1636,68 @@ public class YamlConfigLoaderTests
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证数组字段声明 tuple / open-array 关键字时,会在 schema 解析阶段被显式拒绝。
|
||||
/// </summary>
|
||||
/// <param name="keywordName">待验证的数组形状关键字名称。</param>
|
||||
/// <param name="keywordValueJson">用于拼接测试 schema 的关键字值 JSON 片段。</param>
|
||||
[TestCase("prefixItems", """
|
||||
[
|
||||
{ "type": "integer" }
|
||||
]
|
||||
""")]
|
||||
[TestCase("additionalItems", "false")]
|
||||
[TestCase("unevaluatedItems", "false")]
|
||||
public void LoadAsync_Should_Throw_When_Array_Schema_Declares_Unsupported_ArrayShape_Keyword(
|
||||
string keywordName,
|
||||
string keywordValueJson)
|
||||
{
|
||||
CreateConfigFile(
|
||||
"monster/slime.yaml",
|
||||
"""
|
||||
id: 1
|
||||
name: Slime
|
||||
dropRates:
|
||||
- 5
|
||||
""");
|
||||
CreateSchemaFile(
|
||||
"schemas/monster.schema.json",
|
||||
$$"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["id", "name", "dropRates"],
|
||||
"properties": {
|
||||
"id": { "type": "integer" },
|
||||
"name": { "type": "string" },
|
||||
"dropRates": {
|
||||
"type": "array",
|
||||
"{{keywordName}}": {{keywordValueJson}},
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
var loader = new YamlConfigLoader(_rootPath)
|
||||
.RegisterTable<int, MonsterConfigIntegerArrayStub>("monster", "monster", "schemas/monster.schema.json",
|
||||
static config => config.Id);
|
||||
var registry = new ConfigRegistry();
|
||||
|
||||
var exception = Assert.ThrowsAsync<ConfigLoadException>(() => loader.LoadAsync(registry));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(exception, Is.Not.Null);
|
||||
Assert.That(exception!.Diagnostic.FailureKind, Is.EqualTo(ConfigLoadFailureKind.SchemaUnsupported));
|
||||
Assert.That(exception.Diagnostic.DisplayPath, Is.EqualTo("dropRates"));
|
||||
Assert.That(exception.Message, Does.Contain($"unsupported '{keywordName}' metadata"));
|
||||
Assert.That(exception.Message, Does.Contain("only accepts one object-valued 'items' schema"));
|
||||
Assert.That(registry.Count, Is.EqualTo(0));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证对象数组的 <c>contains</c> 试匹配会按声明属性子集工作,而不会因额外字段误判为不匹配。
|
||||
/// </summary>
|
||||
|
||||
@ -326,6 +326,7 @@ internal static partial class YamlConfigSchemaValidator
|
||||
{
|
||||
ValidateUnsupportedCombinatorKeywords(tableName, schemaPath, propertyPath, element);
|
||||
ValidateUnsupportedOpenObjectKeywords(tableName, schemaPath, propertyPath, element);
|
||||
ValidateUnsupportedArrayShapeKeywords(tableName, schemaPath, propertyPath, element);
|
||||
var typeName = ResolveNodeTypeName(tableName, schemaPath, propertyPath, element);
|
||||
var referenceTableName = TryGetReferenceTableName(tableName, schemaPath, propertyPath, element);
|
||||
ValidateObjectOnlyKeywords(tableName, schemaPath, propertyPath, element, typeName);
|
||||
@ -401,6 +402,36 @@ internal static partial class YamlConfigSchemaValidator
|
||||
displayPath: GetDiagnosticPath(propertyPath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显式拒绝当前共享子集中尚未支持的数组形状关键字。
|
||||
/// 当前配置系统只接受单个 object-valued <c>items</c> schema,
|
||||
/// 并继续拒绝 tuple / open-array 关键字,避免 Runtime / Generator / Tooling
|
||||
/// 对数组元素形状产生静默漂移。
|
||||
/// </summary>
|
||||
/// <param name="tableName">所属配置表名称。</param>
|
||||
/// <param name="schemaPath">Schema 文件路径。</param>
|
||||
/// <param name="propertyPath">当前节点的逻辑属性路径。</param>
|
||||
/// <param name="element">当前 schema 节点。</param>
|
||||
private static void ValidateUnsupportedArrayShapeKeywords(
|
||||
string tableName,
|
||||
string schemaPath,
|
||||
string propertyPath,
|
||||
JsonElement element)
|
||||
{
|
||||
if (TryGetUnsupportedArrayShapeKeywordName(element) is not { } keywordName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw ConfigLoadExceptionFactory.Create(
|
||||
ConfigLoadFailureKind.SchemaUnsupported,
|
||||
tableName,
|
||||
$"Property '{propertyPath}' in schema file '{schemaPath}' uses unsupported '{keywordName}' metadata. " +
|
||||
"The current config schema subset only accepts one object-valued 'items' schema and rejects tuple or open-array keywords that can change item shape across Runtime, Generator, and Tooling.",
|
||||
schemaPath: schemaPath,
|
||||
displayPath: GetDiagnosticPath(propertyPath));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回当前节点声明的首个未支持组合关键字。
|
||||
/// </summary>
|
||||
@ -432,6 +463,19 @@ internal static partial class YamlConfigSchemaValidator
|
||||
null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回当前节点声明的首个未支持数组形状关键字。
|
||||
/// </summary>
|
||||
/// <param name="element">当前 schema 节点。</param>
|
||||
/// <returns>命中的关键字名称;未声明时返回空。</returns>
|
||||
private static string? TryGetUnsupportedArrayShapeKeywordName(JsonElement element)
|
||||
{
|
||||
return element.TryGetProperty("prefixItems", out _) ? "prefixItems" :
|
||||
element.TryGetProperty("additionalItems", out _) ? "additionalItems" :
|
||||
element.TryGetProperty("unevaluatedItems", out _) ? "unevaluatedItems" :
|
||||
null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析 schema 节点声明的类型名称,并在缺失或类型错误时立刻给出定位清晰的诊断。
|
||||
/// </summary>
|
||||
|
||||
@ -2017,6 +2017,56 @@ public class SchemaConfigGeneratorTests
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证生成器会显式拒绝当前共享子集尚未支持的数组形状关键字。
|
||||
/// </summary>
|
||||
/// <param name="keywordName">待验证的数组形状关键字名称。</param>
|
||||
/// <param name="keywordValueJson">用于拼接测试 schema 的关键字值 JSON 片段。</param>
|
||||
[TestCase("prefixItems", """
|
||||
[
|
||||
{ "type": "integer" }
|
||||
]
|
||||
""")]
|
||||
[TestCase("additionalItems", "false")]
|
||||
[TestCase("unevaluatedItems", "false")]
|
||||
public void Run_Should_Report_Diagnostic_When_Array_Schema_Declares_Unsupported_ArrayShape_Keyword(
|
||||
string keywordName,
|
||||
string keywordValueJson)
|
||||
{
|
||||
const string source = DummySource;
|
||||
var schema = $$"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["id", "dropRates"],
|
||||
"properties": {
|
||||
"id": { "type": "integer" },
|
||||
"dropRates": {
|
||||
"type": "array",
|
||||
"{{keywordName}}": {{keywordValueJson}},
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
var result = SchemaGeneratorTestDriver.Run(
|
||||
source,
|
||||
("monster.schema.json", schema));
|
||||
|
||||
var diagnostic = result.Results.Single().Diagnostics.Single();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(diagnostic.Id, Is.EqualTo("GF_ConfigSchema_017"));
|
||||
Assert.That(diagnostic.Severity, Is.EqualTo(DiagnosticSeverity.Error));
|
||||
Assert.That(diagnostic.GetMessage(), Does.Contain("dropRates"));
|
||||
Assert.That(diagnostic.GetMessage(), Does.Contain(keywordName));
|
||||
Assert.That(diagnostic.GetMessage(), Does.Contain("only accepts one object-valued 'items' schema"));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 <c>then</c> 子 schema 内的非法 <c>format</c> 也会在生成阶段直接给出诊断。
|
||||
/// </summary>
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
- 已完成 object-focused `if` / `then` / `else`,继续评估下一批仍不改变生成类型形状的共享关键字
|
||||
- 已明确将 `oneOf` / `anyOf` 归类为当前不支持的组合关键字,并在 Runtime / Generator / Tooling 三端显式拒绝,避免静默接受导致形状漂移
|
||||
- 已把开放对象关键字边界收紧为只接受 `additionalProperties: false`,并在 Runtime / Generator / Tooling 三端显式拒绝 `patternProperties`、`propertyNames`、`unevaluatedProperties`
|
||||
- 已把数组形状关键字边界收紧为只接受单个 object-valued `items` schema,并在 Runtime / Generator / Tooling 三端显式拒绝 `prefixItems`、`additionalItems`、`unevaluatedItems`
|
||||
- 已完成 PR #262 的 CodeRabbit follow-up,补齐 latest review body 中 folded `Nitpick comments` 的 skill 解析并按建议收口 Tooling / Tests
|
||||
- 先以 Runtime / Generator / Tooling 三端一致语义为前提筛选下一项,而不是盲目扩全量 JSON Schema
|
||||
- Tooling / Docs 后续改为非阻塞并行 lane;active 入口只保留主线恢复点,把批处理细节下沉到 backlog 文件
|
||||
@ -23,6 +24,8 @@
|
||||
- 缓解措施:`oneOf` / `anyOf` 已改为三端显式拒绝;后续仅继续评估不会引入联合形状、属性合并或分支生成漂移的关键字子集
|
||||
- 开放对象形状风险:如果某一端静默接受 `patternProperties`、`propertyNames`、`unevaluatedProperties` 等关键字,会重新打开对象形状并造成契约漂移
|
||||
- 缓解措施:当前三端已统一把开放对象边界收紧为只接受 `additionalProperties: false`,其余开放对象关键字直接报错
|
||||
- 数组形状漂移风险:如果某一端静默接受 `prefixItems`、`additionalItems`、`unevaluatedItems` 等关键字,会让 tuple / open-array 设计在三端表现不一致
|
||||
- 缓解措施:当前三端已统一把数组形状边界收紧为只接受单个 object-valued `items` schema,其余数组形状关键字直接报错
|
||||
- 工具链验证风险:VS Code 与 CI / 发布管道验证覆盖不足
|
||||
- 缓解措施:继续为新增共享关键字补齐三端测试覆盖,优先保证 C# Runtime 与 Generator 回归通过,并记录 JS 测试与构建验证
|
||||
- PR review 信号漂移风险:CodeRabbit 可能把建议折叠在 latest review body,而不是 issue comments
|
||||
@ -46,6 +49,9 @@
|
||||
- 已明确拒绝会重新打开对象形状的开放对象关键字:
|
||||
- 当前只接受 `additionalProperties: false`
|
||||
- `patternProperties`、`propertyNames`、`unevaluatedProperties` 当前会在 Runtime / Generator / Tooling 三端直接报错,而不是静默忽略
|
||||
- 已明确拒绝会改变数组元素形状的数组关键字:
|
||||
- 当前只接受单个 object-valued `items` schema
|
||||
- `prefixItems`、`additionalItems`、`unevaluatedItems` 当前会在 Runtime / Generator / Tooling 三端直接报错,而不是静默忽略
|
||||
- `if` / `then` / `else` 已按“不改变生成类型形状”的边界落地:
|
||||
- 只允许 object 节点上的 object-typed inline schema
|
||||
- `if` 必填,且必须至少伴随 `then` 或 `else` 之一
|
||||
@ -94,6 +100,9 @@
|
||||
- 最近验证摘要:`2026-04-30` 已完成 Tooling / Docs reader-facing 收口与工具 parser 边界收紧,详细命令、批次背景与验证结果保留在 trace 的 `2026-04-30` 分阶段记录中
|
||||
- 最近验证摘要:`2026-05-06` 已完成开放对象关键字边界收口;Runtime / Generator / Tooling 现统一拒绝 `patternProperties`、`propertyNames`、`unevaluatedProperties`,并保留 `additionalProperties: false` 作为唯一共享闭合对象入口;详细命令与批次背景保留在 trace 的 `2026-05-06` 记录中
|
||||
- 最近验证摘要:`2026-05-06` 已按 PR `#325` latest review follow-up 移除三端开放对象校验中的不可达 `additionalProperties: false` 放行分支,补齐 Tooling 正向回归,并同步拆分 reader-facing docs 对开放对象边界的表述;细节与验证命令保留在 trace 的 `2026-05-06` 追加记录中
|
||||
- 最近验证摘要:`2026-05-06` 已核对 `extension.js` 的对象数组编辑能力与 reader-facing 文档,确认表单当前支持对象数组项内部继续嵌套的对象数组;`tools/gframework-config-tool/README.md` 与 `docs/zh-CN/game/config-tool.md` 已同步收紧回退条件,避免把“仍在共享子集内的嵌套对象数组”误写成默认只能回退 raw YAML;细节与验证命令保留在 trace 的 `2026-05-06` 追加记录中
|
||||
- 最近验证摘要:`2026-05-08` 已把数组形状边界收紧为只接受单个 object-valued `items` schema;Runtime / Generator / Tooling 现统一拒绝 `prefixItems`、`additionalItems`、`unevaluatedItems`,并补齐三端回归测试与 reader-facing 文档说明;细节与验证命令保留在 trace 的 `2026-05-08` 记录中
|
||||
- 最近验证摘要:`2026-05-09` 已按 PR `#343` latest unresolved review threads 补齐两个 public 参数化测试的 XML `<param>` 注释,并为 `2026-05-06` Tooling 文档批次标题追加上下文后缀以消除 `MD024`;细节与定向验证命令保留在 trace 的 `2026-05-09` 记录中
|
||||
- PR `#306` follow-up 摘要:已按 latest open review threads 补齐 Generator `anyOf` 对称回归、Tooling schema type 白名单、object-array 直系收集边界,以及 reader-facing docs 的显式 `additionalProperties: false` / adoption guidance 说明;细节和验证命令保留在 trace 的 `2026-04-30` 新增阶段记录中
|
||||
- PR review 跟进指针:当前分支的 latest review follow-up 与后续本地核验结论以 `ai-first-config-system-trace.md` 为准,active tracking 不再重复展开逐条命令历史
|
||||
|
||||
|
||||
@ -145,7 +145,7 @@
|
||||
- 指向真正承载并行批次细节的 backlog 文件
|
||||
- 本轮不新增代码范围、测试范围或文档范围,只整理 public `ai-plan/**` 的恢复入口表达,避免把治理噪音带回 reader-facing docs
|
||||
|
||||
### 关键决定
|
||||
### 关键决定(Tooling 文档与实际编辑边界对齐)
|
||||
|
||||
- `C# Runtime + Source Generator + Consumer DX` 仍是默认恢复主线
|
||||
- Tooling / Docs 可以并发推进,但后续 batch 应直接以 `ai-first-config-system-csharp-experience-next.md` 为入口,而不是继续扩写 active tracking / trace
|
||||
@ -242,19 +242,19 @@
|
||||
- `patternProperties`、`propertyNames`、`unevaluatedProperties` 当前改为三端直接失败
|
||||
- reader-facing docs 也已同步更新,避免采用文档继续把这类关键字描述成“也许工具没做但运行时可能支持”的灰区
|
||||
|
||||
### 关键决定
|
||||
### 关键决定(Tooling 文档与实际编辑边界对齐)
|
||||
|
||||
- `additionalProperties: false` 仍是唯一共享支持的开放对象相关关键字形状
|
||||
- 任何会重新引入动态字段集的开放对象关键字,都视为当前主线之外的设计,而不是后续工具增强项
|
||||
- 本轮继续保持主线为 `C# Runtime + Source Generator + Consumer DX`,没有把工作重心切回复杂表单或宿主验证
|
||||
|
||||
### Stop Condition
|
||||
### Stop Condition(Tooling 文档与实际编辑边界对齐)
|
||||
|
||||
- Batch baseline:`origin/main` (`a8c6c11e`, `2026-05-05 13:14:24 +0800`)
|
||||
- Primary metric:branch diff vs `origin/main` changed files,阈值 `50`
|
||||
- 本轮执行时的 branch diff 指标仍为 `0`,说明当前批次尚未把 `HEAD` 推进到接近阈值;reviewability headroom 充足
|
||||
|
||||
### 验证
|
||||
### 验证(Tooling 文档与实际编辑边界对齐)
|
||||
|
||||
- 2026-05-06:`bun run test`(`tools/gframework-config-tool`)
|
||||
- 结果:通过(133 tests)
|
||||
@ -275,7 +275,7 @@
|
||||
- 2026-05-06:`git diff --check`
|
||||
- 结果:通过
|
||||
|
||||
### 下一步
|
||||
### 下一步(Tooling 文档与实际编辑边界对齐)
|
||||
|
||||
1. 继续盘点下一批不会改变生成类型形状、也不会重新打开对象形状的共享关键字
|
||||
2. Tooling / Docs 如继续并发推进,优先补真实采用示例,不再重复扩写开放对象边界清单
|
||||
@ -324,3 +324,103 @@
|
||||
1. 执行本轮受影响 Tooling / Runtime / Generator 定向验证,并确认没有新增 warning 或格式漂移
|
||||
2. 若验证通过,重新抓取 PR `#325` review 状态,区分哪些 open threads 会随推送自动折叠
|
||||
3. 继续把 PR review follow-up 约束在“latest unresolved thread + 本地仍成立问题”,不回头追旧 summary 噪音
|
||||
|
||||
### 阶段:Tooling 文档与实际编辑边界对齐(AI-FIRST-CONFIG-RP-003)
|
||||
|
||||
- 已重新核对 `tools/gframework-config-tool/src/extension.js` 的对象数组表单能力,并确认当前实现不只支持对象数组本身
|
||||
- 当前表单还支持“对象数组项内部继续嵌套的对象数组”,前提是内层条目仍保持共享子集允许的对象 / 标量字段 / 标量数组 / 嵌套对象形状
|
||||
- 本轮不扩 Runtime / Generator / Tooling 的 schema 契约,只修正 reader-facing docs 漂移:
|
||||
- `tools/gframework-config-tool/README.md` 不再把“更深层嵌套编辑”笼统描述成默认回退 raw YAML
|
||||
- `docs/zh-CN/game/config-tool.md` 改为明确:只有当对象数组继续嵌套后的结构超出当前共享子集,才需要回到 raw YAML
|
||||
|
||||
### 关键决定(Tooling 文档与实际编辑边界对齐)
|
||||
|
||||
- 这轮批次继续遵守“先核对共享契约,再改文档”的 lane 规则,没有为追求批量推进而硬扩一个收益不明确的新关键字
|
||||
- Tooling 的 reader-facing 说明要以 `extension.js` 当前真实能力为准,避免把已经支持的对象数组路径继续描述成工具缺口
|
||||
- raw YAML 回退条件保留,但需要收敛为“超出共享子集或当前编辑器边界”而不是“只要更深层对象数组就默认回退”
|
||||
|
||||
### Stop Condition(Tooling 文档与实际编辑边界对齐)
|
||||
|
||||
- Batch baseline:`origin/main` (`c01abac0`, `2026-05-06 09:40:08 +0800`)
|
||||
- Primary metric:branch diff vs `origin/main` changed files,阈值 `30`
|
||||
- 本轮开始前 branch diff 指标为 `0` files / `0` changed lines;本批次只触碰 reader-facing docs 与 active `ai-plan`,预计仍远低于阈值
|
||||
|
||||
### 验证(Tooling 文档与实际编辑边界对齐)
|
||||
|
||||
- 2026-05-06:`rg -n "nested object arrays|回退到 raw YAML|更深层对象数组"`(文档 + `extension.js`)
|
||||
- 结果:通过
|
||||
- 备注:确认 `README` / 中文工具文档存在旧边界表述,而 `extension.js` 已支持对象数组项内部继续嵌套的对象数组编辑
|
||||
- 2026-05-06:`git diff --check -- tools/gframework-config-tool/README.md docs/zh-CN/game/config-tool.md ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md`
|
||||
- 结果:通过
|
||||
- 2026-05-06:`python3 scripts/license-header.py --check --paths tools/gframework-config-tool/README.md docs/zh-CN/game/config-tool.md ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md`
|
||||
- 结果:通过
|
||||
- 2026-05-06:`dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
|
||||
- 结果:通过(0 warnings, 0 errors)
|
||||
|
||||
### 下一步(Tooling 文档与实际编辑边界对齐)
|
||||
|
||||
1. 继续优先找“实现已存在但 reader-facing 表述漂移”的低风险 lane,避免在批处理模式下引入收益不明的新 schema contract
|
||||
2. 若下一轮回到主线代码批次,再继续盘点不会改变生成类型形状的共享关键字,而不是重复刷新同一组 Tooling 边界说明
|
||||
|
||||
## 2026-05-08
|
||||
|
||||
### 阶段:数组形状关键字边界收口(AI-FIRST-CONFIG-RP-003)
|
||||
|
||||
- 已在 Runtime、Source Generator 与 VS Code Tooling 三端统一收紧数组形状关键字边界
|
||||
- 本轮不是扩 JSON Schema 能力,而是避免某一端静默接受 tuple / open-array 设计:
|
||||
- 当前共享子集只接受单个 object-valued `items` schema
|
||||
- `prefixItems`、`additionalItems`、`unevaluatedItems` 现在会在三端直接失败,而不是静默忽略
|
||||
- reader-facing docs 也已同步补齐数组形状边界,避免把“标准 JSON Schema 的 tuple/open-array 语义”误读成当前配置系统的隐藏支持范围
|
||||
|
||||
### 验证
|
||||
|
||||
- 2026-05-08:`bun run test`(`tools/gframework-config-tool`)
|
||||
- 目标:验证工具端会拒绝 `prefixItems`、`additionalItems`、`unevaluatedItems`
|
||||
- 2026-05-08:`dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~SchemaConfigGeneratorTests"`
|
||||
- 目标:验证生成器新增 `GF_ConfigSchema_017`
|
||||
- 2026-05-08:`dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests"`
|
||||
- 目标:验证运行时会拒绝不在共享子集内的数组形状关键字
|
||||
- 2026-05-08:`dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
|
||||
- 目标:验证运行时模块 Release 构建
|
||||
- 2026-05-08:`dotnet build GFramework.Game.SourceGenerators/GFramework.Game.SourceGenerators.csproj -c Release`
|
||||
- 目标:验证生成器模块 Release 构建
|
||||
|
||||
### 下一步
|
||||
|
||||
1. 若本轮验证通过,继续回到“不会改变生成类型形状”的下一批共享关键字盘点
|
||||
2. 继续优先寻找“静默接受但主线不支持”的边界收口项,而不是先扩更复杂的组合语义
|
||||
|
||||
## 2026-05-09
|
||||
|
||||
### 阶段:PR #343 review follow-up(AI-FIRST-CONFIG-RP-003)
|
||||
|
||||
- 已用 `gframework-pr-review` 重新抓取当前分支 PR `#343` 的 latest unresolved review threads
|
||||
- 本轮只处理本地仍成立的 3 条 open threads:
|
||||
- `GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs` 的 public 参数化测试补齐 XML `<param>` 注释
|
||||
- `GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs` 的 public 参数化测试补齐 XML `<param>` 注释
|
||||
- `ai-first-config-system-trace.md` 为 `2026-05-06` Tooling 文档批次的重复三级标题加上上下文后缀,消除 `MD024`
|
||||
- 其余 PR 信号已核对:
|
||||
- 当前没有 failed checks
|
||||
- MegaLinter 仅报告 1 条 `dotnet-format` 相关问题;本轮先按 latest open review threads 收口本地仍成立项
|
||||
- GitHub Test Reporter 显示 `2336` tests passed、`0` failed
|
||||
|
||||
### 验证(PR #343 review follow-up)
|
||||
|
||||
- 2026-05-09:`python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/current-pr-review.json`
|
||||
- 结果:通过
|
||||
- 备注:确认 PR `#343` 当前仍有 3 条 latest unresolved CodeRabbit threads,且都可在本地直接复现
|
||||
- 2026-05-09:`git diff --check -- GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs ai-plan/public/ai-first-config-system/todos/ai-first-config-system-tracking.md ai-plan/public/ai-first-config-system/traces/ai-first-config-system-trace.md`
|
||||
- 结果:通过
|
||||
- 2026-05-09:`python3 scripts/license-header.py --check --paths GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorTests.cs`
|
||||
- 结果:通过
|
||||
- 2026-05-09:`dotnet build GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release`
|
||||
- 结果:通过(0 warnings, 0 errors)
|
||||
- 备注:沙箱内并行 restore 命中了 `.nuget.g.props` 已存在的环境型冲突;已按仓库规则在沙箱外重跑原命令,并以外部结果作为准确信号
|
||||
- 2026-05-09:`dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release`
|
||||
- 结果:通过(0 warnings, 0 errors)
|
||||
- 备注:已在沙箱外串行重跑原命令,确认本轮 PR review 修复未引入构建问题
|
||||
|
||||
### 下一步(PR #343 review follow-up)
|
||||
|
||||
1. 若本轮定向校验通过,重新抓取 PR `#343` review 状态,确认这 3 条 open threads 是否已具备自动折叠条件
|
||||
2. 若 PR 仍残留 review 信号,继续只处理 latest unresolved thread 中本地仍成立的问题,不回头追旧 summary 噪音
|
||||
|
||||
@ -17,7 +17,7 @@ description: 说明 GFramework.Game 配置系统的定位、目录约定、生
|
||||
- JSON Schema 作为结构描述
|
||||
- 一对象一文件的目录组织
|
||||
- 运行时只读查询
|
||||
- Runtime / Generator / Tooling 共享支持 `enum`、`const`、`not`、`minimum`、`maximum`、`exclusiveMinimum`、`exclusiveMaximum`、`multipleOf`、`minLength`、`maxLength`、`pattern`、`format`(当前稳定子集:`date`、`date-time`、`duration`、`email`、`time`、`uri`、`uuid`)、`minItems`、`maxItems`、`uniqueItems`、`contains`、`minContains`、`maxContains`、`minProperties`、`maxProperties`、`dependentRequired`、`dependentSchemas`、`allOf`、object-focused `if` / `then` / `else`,以及闭合对象边界 `additionalProperties: false`
|
||||
- Runtime / Generator / Tooling 共享支持 `enum`、`const`、`not`、`minimum`、`maximum`、`exclusiveMinimum`、`exclusiveMaximum`、`multipleOf`、`minLength`、`maxLength`、`pattern`、`format`(当前稳定子集:`date`、`date-time`、`duration`、`email`、`time`、`uri`、`uuid`)、`minItems`、`maxItems`、`uniqueItems`、`contains`、`minContains`、`maxContains`、`minProperties`、`maxProperties`、`dependentRequired`、`dependentSchemas`、`allOf`、object-focused `if` / `then` / `else`,以及闭合对象边界 `additionalProperties: false`;数组形状当前只接受单个 object-valued `items` schema
|
||||
- Source Generator 生成配置类型、表包装、单表注册/访问辅助,以及项目级聚合注册目录
|
||||
- VS Code 插件提供配置浏览、raw 编辑、schema 打开、递归轻量校验和嵌套对象表单入口
|
||||
|
||||
@ -813,13 +813,14 @@ if (MonsterConfigBindings.References.TryGetByDisplayPath("dropItems", out var re
|
||||
- `allOf`:供运行时校验、VS Code 校验、对象 section 表单 hint 和生成代码 XML 文档复用;当前只接受 object 节点上的 object-typed inline schema 数组,并按 focused constraint block 语义把每个条目叠加到当前对象上,不做属性合并,也不改变生成类型形状
|
||||
- `if` / `then` / `else`:供运行时校验、VS Code 校验、对象 section 表单 hint 和生成代码 XML 文档复用;当前只接受 object 节点上的 object-typed inline schema,`if` 必填且必须至少配合 `then` 或 `else` 之一使用,分支只能约束父对象已声明的字段,不做属性合并,也不改变生成类型形状;条件匹配本身沿用 `dependentSchemas` / `allOf` 的 focused matcher 语义,允许对象保留未在条件块中声明的额外同级字段
|
||||
- `additionalProperties`:当前共享支持边界只接受 `additionalProperties: false`;它用于声明对象是闭合的,运行时、生成器和工具都会据此拒绝未声明字段。其他 `additionalProperties` 形态,以及 `patternProperties`、`propertyNames`、`unevaluatedProperties` 这类会重新打开对象形状的关键字,当前都不属于共享支持子集,会在解析或生成阶段直接被拒绝
|
||||
- 数组形状关键字:当前共享支持边界只接受单个 object-valued `items` schema;`prefixItems`、`additionalItems`、`unevaluatedItems` 这类 tuple / open-array 关键字当前都不属于共享支持子集,会在解析或生成阶段直接被拒绝,避免数组元素形状在三端静默漂移
|
||||
- `oneOf` / `anyOf`:当前不属于共享支持子集;Runtime / Generator / Tooling 会在解析或生成阶段直接拒绝,避免静默接受会改变生成类型形状的组合关键字
|
||||
|
||||
如果你的 schema 需要超出这些边界的复杂 shape,推荐采用下面的回退顺序:
|
||||
|
||||
1. 先在 raw YAML 与 schema 文件中直接编辑,而不是强行依赖表单入口
|
||||
2. 再核对该 shape 是否仍符合这里列出的共享支持子集
|
||||
3. 如果它依赖 `oneOf` / `anyOf`、非 `false` 的 `additionalProperties`、`patternProperties` / `propertyNames` / `unevaluatedProperties` 这类开放对象关键字、会向父对象注入新字段的 `allOf` / `dependentSchemas` / `if` 分支,或者更异构的深层数组结构,就应当把它视为当前版本之外的设计,而不是工具层遗漏的“隐藏能力”
|
||||
3. 如果它依赖 `oneOf` / `anyOf`、非 `false` 的 `additionalProperties`、`patternProperties` / `propertyNames` / `unevaluatedProperties` 这类开放对象关键字、`prefixItems` / `additionalItems` / `unevaluatedItems` 这类 tuple / open-array 关键字、会向父对象注入新字段的 `allOf` / `dependentSchemas` / `if` 分支,或者更异构的深层数组结构,就应当把它视为当前版本之外的设计,而不是工具层遗漏的“隐藏能力”
|
||||
|
||||
`allOf` 的最小可工作示例如下。关键点是:字段形状先在父对象 `properties` 中声明,再用 `allOf` 叠加 `required` 或更细的字段约束;`allOf` 条目不会把新字段并回父对象。
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ description: 说明 GFramework AI-First 配置工作流对应的 VS Code 工具
|
||||
|
||||
- 项目不使用 `GFramework.Game` 的配置工作流
|
||||
- 需要完整 JSON Schema 编辑器,而不是当前仓库落地的稳定子集
|
||||
- 需要在编辑器里处理更深层对象数组嵌套,且不接受回退到 raw YAML
|
||||
- 需要完整放宽到当前共享支持子集之外的更异构数组结构,且不接受回退到 raw YAML
|
||||
|
||||
## 工作区约定
|
||||
|
||||
@ -83,7 +83,7 @@ Explorer + 表单预览。
|
||||
- 对空配置文件提供基于 schema 的示例 YAML 初始化入口
|
||||
- 对同一配置域内的多份 YAML 文件执行批量字段更新
|
||||
- 在表单入口中显示 `title / description / default / const / enum / x-gframework-ref-table(UI 中显示为 ref-table) / multipleOf / pattern / format / uniqueItems / contains / minContains / maxContains / minProperties / maxProperties / dependentRequired / dependentSchemas / allOf / if / then / else` 元数据;批量编辑入口当前只暴露顶层可批量改写字段所需的基础信息
|
||||
- 对 `additionalProperties: false` 提供闭合对象边界校验,并在遇到 `oneOf` / `anyOf` 或其他当前未收口的组合形状时明确提示该 schema 不属于当前工具支持子集
|
||||
- 对 `additionalProperties: false` 提供闭合对象边界校验,并在遇到 `oneOf` / `anyOf`、`prefixItems` / `additionalItems` / `unevaluatedItems` 或其他当前未收口的组合 / 数组形状时明确提示该 schema 不属于当前工具支持子集
|
||||
|
||||
当前表单入口适合编辑嵌套对象中的标量字段、标量数组,以及对象数组中的对象项。
|
||||
|
||||
@ -195,6 +195,7 @@ rewardTableId: starter-reward
|
||||
|
||||
- `additionalProperties` 是否显式设置为 `false`;省略或 `true` 不属于当前共享支持子集
|
||||
- schema 是否依赖 `oneOf` / `anyOf`;这些组合关键字会被 Runtime / Generator / Tooling 直接拒绝
|
||||
- schema 是否依赖 `prefixItems` / `additionalItems` / `unevaluatedItems`;当前数组子集只接受单个 object-valued `items` schema
|
||||
- 对象数组里是否混入标量项,或是否存在更深、更异构的数组结构
|
||||
- Runtime / Source Generator 是否已经接受这份 schema,而不是只有编辑器里“暂时看起来能写”
|
||||
|
||||
@ -230,15 +231,16 @@ rewardTableId: starter-reward
|
||||
- `contains` / `minContains` / `maxContains`
|
||||
- `additionalProperties: false`
|
||||
|
||||
如果你进入更深层对象数组嵌套,当前更稳妥的做法通常是:
|
||||
如果你进入对象数组里的继续嵌套对象数组,当前表单通常仍可继续处理;更稳妥的顺序是:
|
||||
|
||||
1. 用 Explorer 找到目标文件
|
||||
2. 先看表单预览确认字段结构
|
||||
3. 再回到 raw YAML 完成最终编辑
|
||||
2. 先看表单预览确认当前层级仍保持对象 / 标量字段 / 标量数组 / 嵌套对象的共享子集形状
|
||||
3. 只有在结构开始变得更异构,或已经超出当前共享支持子集时,再回到 raw YAML 完成最终编辑
|
||||
|
||||
以下 shape 目前也建议直接回退到 raw YAML,并同时检查 schema 是否仍在当前共享支持子集内:
|
||||
|
||||
- 需要表达 `oneOf` / `anyOf` 这类会改变生成类型形状的组合关键字
|
||||
- 需要表达 `prefixItems` / `additionalItems` / `unevaluatedItems` 这类 tuple / open-array 关键字
|
||||
- 需要 `additionalProperties` 的其他形态,而不是当前明确支持的 `additionalProperties: false`
|
||||
- 需要在 `allOf`、`dependentSchemas`、`if` / `then` / `else` 中引入父对象未声明的新字段
|
||||
- 需要比当前对象数组编辑器更深、更异构的数组结构
|
||||
@ -265,9 +267,9 @@ rewardTableId: starter-reward
|
||||
|
||||
- 工作区默认只取第一个 workspace folder
|
||||
- 校验聚焦仓库当前支持的 schema 子集
|
||||
- 表单预览支持对象数组,但更深的嵌套对象数组仍可能需要回退到 raw YAML
|
||||
- 表单预览支持对象数组,以及对象数组项内部继续嵌套的对象数组;只有当内层结构超出共享子集时才需要回退到 raw YAML
|
||||
- 批量编辑当前聚焦顶层标量和顶层标量数组字段
|
||||
- 共享约束里只支持闭合对象边界 `additionalProperties: false`;`oneOf` / `anyOf` 等改变生成形状的组合关键字会被明确拒绝
|
||||
- 共享约束里只支持闭合对象边界 `additionalProperties: false`,数组形状里只支持单个 object-valued `items` schema;`oneOf` / `anyOf` 与 `prefixItems` / `additionalItems` / `unevaluatedItems` 这类会改变生成形状的关键字会被明确拒绝
|
||||
|
||||
因此,最稳妥的理解方式是:
|
||||
|
||||
|
||||
@ -80,6 +80,8 @@ The extension currently validates the repository's current schema subset:
|
||||
object-focused `if` / `then` / `else`
|
||||
- closed-object validation through `additionalProperties: false`
|
||||
- explicit rejection for unsupported combinators such as `oneOf` and `anyOf`, instead of silently ignoring them
|
||||
- explicit rejection for unsupported array-shape keywords such as `prefixItems`, `additionalItems`, and
|
||||
`unevaluatedItems`, so tuple or open-array item shapes do not drift away from Runtime / Generator
|
||||
|
||||
## Contract Boundary
|
||||
|
||||
@ -106,8 +108,8 @@ This extension is an editor-side helper. It does not define the runtime contract
|
||||
project-specific paths relative to the first workspace folder.
|
||||
3. Open the `GFramework Config` explorer view and select a config file or domain.
|
||||
4. Run validation first to confirm the current YAML files still match the supported schema subset.
|
||||
5. Open the lightweight form preview or domain batch editing actions, then fall back to raw YAML for deeper nested edits
|
||||
when needed.
|
||||
5. Open the lightweight form preview or domain batch editing actions, then fall back to raw YAML only when the current
|
||||
path exceeds the supported object-array editor boundary or leaves the shared schema subset.
|
||||
|
||||
Minimal adoption checklist:
|
||||
|
||||
@ -117,6 +119,8 @@ Minimal adoption checklist:
|
||||
- Use `x-gframework-ref-table` only on fields that should link to another config domain or reference file
|
||||
- Keep `additionalProperties` explicitly set to `false` when you need closed-object validation; omitting it, setting
|
||||
it to `true`, or mixing in `patternProperties`, `propertyNames`, or `unevaluatedProperties` is outside the supported subset
|
||||
- Keep arrays on one object-valued `items` schema; tuple or open-array keywords such as `prefixItems`,
|
||||
`additionalItems`, and `unevaluatedItems` are outside the supported subset
|
||||
|
||||
Use raw YAML directly when you need:
|
||||
|
||||
@ -126,6 +130,7 @@ Use raw YAML directly when you need:
|
||||
- `contains` / `minContains` / `maxContains` when the structure is easier to reason about directly in YAML
|
||||
- schema designs outside the current shared subset, including `oneOf`, `anyOf`, non-`false` `additionalProperties`, or
|
||||
other open-object keywords such as `patternProperties`, `propertyNames`, and `unevaluatedProperties`
|
||||
- tuple or open-array designs that depend on `prefixItems`, `additionalItems`, or `unevaluatedItems`
|
||||
|
||||
## Documentation
|
||||
|
||||
@ -136,12 +141,14 @@ Use raw YAML directly when you need:
|
||||
|
||||
- Multi-root workspaces use the first workspace folder
|
||||
- Validation only covers the repository's current schema subset
|
||||
- Form preview supports nested objects and object-array editing, but deeper nested object arrays inside array items still
|
||||
fall back to raw YAML
|
||||
- Form preview supports nested objects, object arrays, and nested object arrays inside object-array items as long as
|
||||
those nested items still stay within the shared subset's object/scalar/array shape
|
||||
- Batch editing remains limited to top-level scalar fields and top-level scalar arrays
|
||||
- Closed-object support is limited to `additionalProperties: false`; open-object keywords such as
|
||||
`patternProperties`, `propertyNames`, and `unevaluatedProperties` are rejected on purpose, as are unsupported
|
||||
combinators such as `oneOf` / `anyOf`
|
||||
- Array-shape support is limited to one object-valued `items` schema; tuple or open-array keywords such as
|
||||
`prefixItems`, `additionalItems`, and `unevaluatedItems` are rejected on purpose
|
||||
|
||||
## Local Testing
|
||||
|
||||
|
||||
@ -1107,6 +1107,7 @@ function parseSchemaNode(rawNode, displayPath) {
|
||||
}
|
||||
|
||||
validateUnsupportedOpenObjectKeyword(value, displayPath);
|
||||
validateUnsupportedArrayShapeKeyword(value, displayPath);
|
||||
|
||||
const type = resolveSupportedSchemaType(value.type, displayPath);
|
||||
const patternMetadata = normalizeSchemaPattern(value.pattern, displayPath);
|
||||
@ -1290,6 +1291,24 @@ function validateUnsupportedOpenObjectKeyword(schemaNode, displayPath) {
|
||||
"The current config schema subset only accepts 'additionalProperties: false' and rejects keywords that reopen object shapes so fields remain closed and strongly typed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject tuple-array and open-array keywords that would let tooling accept item
|
||||
* shapes outside the Runtime / Generator shared subset.
|
||||
*
|
||||
* @param {Record<string, unknown>} schemaNode Raw schema object.
|
||||
* @param {string} displayPath Logical property path.
|
||||
*/
|
||||
function validateUnsupportedArrayShapeKeyword(schemaNode, displayPath) {
|
||||
const unsupportedKeyword = getUnsupportedArrayShapeKeywordName(schemaNode);
|
||||
if (!unsupportedKeyword) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Schema property '${displayPath}' uses unsupported '${unsupportedKeyword}' metadata. ` +
|
||||
"The current config schema subset only accepts one object-valued 'items' schema and rejects tuple or open-array keywords that can change item shape across Runtime, Generator, and Tooling.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse one required array child schema while keeping tooling errors aligned
|
||||
* with the Runtime and Source Generator contracts.
|
||||
@ -1406,6 +1425,29 @@ function getUnsupportedOpenObjectKeywordName(schemaNode) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first array-shape keyword that the current shared schema subset
|
||||
* intentionally rejects to keep array item contracts aligned.
|
||||
*
|
||||
* @param {Record<string, unknown>} schemaNode Raw schema object.
|
||||
* @returns {string | undefined} Unsupported keyword name when present.
|
||||
*/
|
||||
function getUnsupportedArrayShapeKeywordName(schemaNode) {
|
||||
if (Object.prototype.hasOwnProperty.call(schemaNode, "prefixItems")) {
|
||||
return "prefixItems";
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(schemaNode, "additionalItems")) {
|
||||
return "additionalItems";
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(schemaNode, "unevaluatedItems")) {
|
||||
return "unevaluatedItems";
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse one optional `not` sub-schema and keep path formatting aligned with
|
||||
* the runtime/generator diagnostics.
|
||||
|
||||
@ -302,6 +302,61 @@ test("parseSchemaContent should reject unsupported open-object keywords", () =>
|
||||
/unsupported 'unevaluatedProperties' metadata/u);
|
||||
});
|
||||
|
||||
test("parseSchemaContent should reject unsupported array-shape keywords", () => {
|
||||
assert.throws(
|
||||
() => parseSchemaContent(`
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"dropRates": {
|
||||
"type": "array",
|
||||
"prefixItems": [
|
||||
{ "type": "integer" }
|
||||
],
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
/unsupported 'prefixItems' metadata/u);
|
||||
|
||||
assert.throws(
|
||||
() => parseSchemaContent(`
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"dropRates": {
|
||||
"type": "array",
|
||||
"additionalItems": false,
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
/unsupported 'additionalItems' metadata/u);
|
||||
|
||||
assert.throws(
|
||||
() => parseSchemaContent(`
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"dropRates": {
|
||||
"type": "array",
|
||||
"unevaluatedItems": false,
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`),
|
||||
/unsupported 'unevaluatedItems' metadata/u);
|
||||
});
|
||||
|
||||
test("parseSchemaContent should reject unsupported explicit schema types", () => {
|
||||
assert.throws(
|
||||
() => parseSchemaContent(`
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user