Release 0.2.0

Complete OpenAPI 3.0/3.1 generation, diagnostics, lifecycle safety, enum and naming mappings, bilingual documentation, examples, and pub.dev release validation.
This commit is contained in:
mr.koi
2026-07-31 16:47:39 +08:00
parent ea93f09154
commit 454e6c51d7
56 changed files with 14506 additions and 491 deletions
+154
View File
@@ -0,0 +1,154 @@
# 架构
[English](../ARCHITECTURE.md) | [简体中文](ARCHITECTURE.md)
生成流水线有意拆分为多个阶段:
```text
配置
-> source loader
-> external/local $ref resolver
-> multi-source merger
-> validation + parser
-> transport/schema IR
-> preset + Dart renderer
-> manifest lifecycle
-> fix / format / build_runner
```
核心不变量是:加载阶段产生自包含且依赖可追踪的文档;parser 表达 OpenAPI
语义;renderer 不重新解释规范;lifecycle 只管理 manifest 中声明的文件。
## 配置
`lib/src/config/config.dart`:
- 查找 `koi_swagger_parser.yaml` 和兼容的 `generator_config.yaml` 名称;
- 相对 source/output 路径以配置文件目录解析;
- 从 `--cwd` 或显式配置目录确定宿主项目命令目录;
- 在转换 YAML scalar 前完成类型校验;
- 解析 enum、命名、type/import mapping 和 filter;
- 校验不支持的组合、mapping collision、正则和 enum mapping;
- 支持从旧 `base_result_import` 迁移。
未知 key 产生 warning;非法类型和不完整组合产生 error。详见
[配置参考](CONFIGURATION.md)。
## Loader、resolver 与 merger
对每个 root source,`OpenApiReferenceResolver`:
1. 规范化本地文件、file URL 或 HTTP(S) source;
2. 缓存每个加载的文档;
3. 解析 local、relative 和 remote `$ref`;
4. 把外部 component bundle 到根文档;
5. 为 component 冲突分配确定性名称;
6. 记录依赖并诊断无法支持的跨文档循环。
`OpenApiDocumentMerger` 再按配置顺序合并 root:
- 重复的 operation method/path 是 error;
- 同名但内容不同的 component 会确定性改名,并重写该 source 的 ref;
- 后输入不会静默覆盖冲突;
- 版本、info 和 path parameter 冲突会被报告。
parser 原则上只应收到根文档 JSON Pointer。仍然存在的 external ref 会被视为
resolver error。
## Validation 与 parser
`OpenApiDocumentParser` 负责:
- OpenAPI 3.0/3.1 版本和关键结构校验;
- operation、path、tag 和 model filter;
- parameter location、style、explode、`allowReserved` 和 `content`;
- request media type,以及 response status/content/header;
- server、security scheme 和 security requirement;
- required、nullable、readOnly/writeOnly 和 `additionalProperties` 语义;
- enum 配置/vendor extension 优先级;
- `allOf`、discriminator union 和已支持的 generic union;
- custom mapping 和 Dart 名称冲突。
无法用可靠 wire 语义渲染的结构不能静默退化为 `dynamic`。安全但有信息损失的
继续处理会产生 warning,例如不同成功 JSON body type 可能统一为 `Object?`。
XML、Protobuf 等没有已支持 decoder 的 media type 会产生 error。
## 中间表示
`lib/src/ir/ir.dart` 是 parser/renderer 契约,主要节点包括:
- `ApiOperation`:method、path、tag、version、server 和 security;
- `ApiParameter`:Dart/wire name、location、style、explode 和
`allowReserved`;
- `ApiRequestBody`:JSON、multipart、form-urlencoded 和 binary request;
- `ApiResponse`:status/default、body kind/type、content type 和 header;
- `ModelSpec`:object、enum、union model,以及 usage category 和 extras;
- `ModelField`:required/nullability/read-write flag 和 constraint;
- `DartType`:named、list、map、object、dynamic 和 void type graph。
model 按 request、result、shared、enum 分类。包含 readOnly/writeOnly 属性的
component 可投影为 request/response variant,避免 request 强制只读值,也避免
response 暴露 write-only secret。
generic `oneOf` 要求恰好一个 decoder 匹配。generic `anyOf` 保留 raw value 和
全部匹配 case。object 分支在 decode 前会用 required key 和 enum constraint
预筛。
## Preset 与 renderer
当前 `koi_flutter` preset 面向 Dio、Retrofit、Freezed 和 json_serializable。
renderer:
- 对 Retrofit 可表达的 parameter 形式使用 Retrofit annotation;
- 当已支持的 style、cookie 或 `allowReserved` 组合需要自定义序列化时,生成
typed public adapter 和 private Retrofit delegate;
- 渲染 Freezed object、enum 和 union model;
- 为根级 `properties` + `additionalProperties` 渲染自定义 map 读写 helper;
- 收集 model dependency 和 external type import;
- 按 version/tag 分组 API,并输出 client/index 文件。
renderer 不加载 ref,也不判断 OpenAPI 结构是否合法。
## 生成生命周期
落盘前,`GenerationLifecycle` 计算:
- canonical merged spec SHA-256;
- normalized config SHA-256;
- 每个渲染 path/content SHA-256。
manifest 记录 schema/generator version、source root、dependency 和每个主生成
文件的实际 hash。写入使用操作系统 exclusive lock 和同目录临时文件 rename。
安全不变量:
1. renderer 输出必须位于配置的 API/model 根;
2. stale candidate 必须来自旧 manifest;
3. stale path 仍必须位于当前 managed root;
4. 删除前当前 hash 必须等于旧 manifest hash;
5. 损坏或未知版本 manifest 不参与删除;
6. fix/format/build 失败时不写 success manifest;
7. watch 在释放生成锁后启动。
`.g.dart` 和 `.freezed.dart` 由 build_runner 管理,不进入本生成器的 stale
manifest。
## Response wrapper
parser 不硬编码 `BaseResult`:
- `passthrough` 返回 OpenAPI response schema;
- `external_generic` 检测配置的成功 envelope,并返回
`ExternalWrapper<Inner>`。
`default` response 仍是错误响应。`response_mode: http_response` 保留 Dio
status/header。错误 response model 仍保留在 IR 和生成 model 集合中。
## 扩展边界
IR/preset/renderer 分层为未来的版本化 renderer interface、template override、
semantic diff 和可选 runtime adapter 留出空间。这些是设计方向,不是当前
公开版本的功能。当前实现边界见[兼容性](COMPATIBILITY.md)。
更多生态对比属于项目内部调研,不随发布 package 交付。
+67
View File
@@ -0,0 +1,67 @@
# 更新日志
本文件记录此 package 的重要变更。
[English](../../CHANGELOG.md) | [简体中文](CHANGELOG.md)
## 0.2.0 - 2026-07-31
### 破坏性变更
- 主 library 不再导出 `DartType`、`ModelSpec`、`ApiOperation`、
`GeneratedFile`、`GeneratedBundle` 等 parser IR 实现类型;请改用
`KoiSwaggerParser` 及其文档化的 `GenerationResult`;
- 不再导出 `KoiSwaggerConfigLoader`;程序化加载配置请改用
`KoiSwaggerConfig.load()`。
### 新增
- 结构化配置、OpenAPI、过滤和生命周期诊断;error 不再静默生成已知错误代码;
- local、relative、file 和 HTTP(S) `$ref` 解析,包括缓存、循环检测、依赖
追踪、外部 component bundling 和确定性多 source 冲突处理;
- `enum_mappings`、兼容字段 `enum_key_mappings`、常见 `x-enum` 扩展、描述和
可选 unknown enum fallback;
- model、property、parameter、operation、schema、type 和 import 映射及冲突校验;
- OpenAPI path、query、header、cookie 参数序列化,包括已支持的 matrix、label、
form、simple、spaceDelimited、pipeDelimited、deepObject、explode 和
`allowReserved` 组合;
- JSON、text、multipart、form-urlencoded 和 binary upload,以及 bytes/stream
download;
- 多成功响应、204、`2XX`、`default`、错误模型、响应头和 `HttpResponse<T>` 模式;
- required、nullable、absent 语义、readOnly/writeOnly 投影,以及根级
`properties` + `additionalProperties` round trip;
- `allOf`、discriminator union,以及已支持的 primitive、container、object
`oneOf`/`anyOf`;
- server variables、security schemes 和 operation OR/AND security metadata;
- tag、path、operation、model 的精确、glob 和正则过滤;
- 带锁 manifest、spec/config/content SHA-256、未变化跳过、安全 stale cleanup、
原子写入和 dry-run 变更计划;
- 内置 build_runner build/watch 集成、reviewed golden、OpenAPI 3.0/3.1 fixture
和临时生成包验证;
- 英文 canonical 文档与完整简体中文镜像,以及配置、兼容性、诊断和迁移参考。
### 变更
- `GenerationResult.writtenFiles` 现在只包含实际写入文件;`generatedFiles` 包含
完整目标集合;dry-run 使用 `wouldWriteFiles` 和 `wouldDeleteFiles`;
- 显式传入配置文件且未设置 `workingDirectory`/`--cwd` 时,配置文件目录成为
宿主项目工作目录;
- `default` response 不再视为成功;XML、Protobuf 和没有安全 decoder 的其他
response media type 会产生诊断;
- `dart fix` 和 `dart format` 仅处理本轮改变的主生成文件。
- 所有非测试 Dart 声明均在不使用文件级排除的情况下满足
`public_member_api_docs`,包括内部 IR 和 lifecycle 类型。
### 修复
- 使 `formatting.run_dart_fix` 和 `output.extract_version_from_url` 真正生效;
- 避免 Dio/Retrofit `Headers` import 冲突、Freezed custom `toJson` 缺失,以及
parameter、field、method mapping 碰撞。
- lifecycle manifest 记录的生成器版本已与 package `0.2.0` 同步。
## 0.1.0 - 2026-06-02
- 首次发布 `koi_swagger_parser`;
- 支持常见 OpenAPI 3.1/FastAPI 解析、Koi 风格 Retrofit API 生成和 Freezed
模型生成;
- 支持兼容读取旧版 `generator_config.yaml`。
+113
View File
@@ -0,0 +1,113 @@
# 兼容性
[English](../COMPATIBILITY.md) | [简体中文](COMPATIBILITY.md)
本矩阵只描述当前实现和自动化测试已覆盖的行为。
- **支持**:已经实现并测试;
- **部分支持**:仅支持“边界”列出的子集;
- **不支持**:生成前产生诊断,或 warning 后明确忽略。
## 文档、引用与合并
| 能力 | 状态 | 边界 |
|---|---|---|
| OpenAPI 3.0.x / 3.1.x | 支持 | JSON/YAML;客户端生成所需常见 Schema Object 子集 |
| Swagger/OpenAPI 2.0 | 不支持 | `openapi.unsupported_version` error |
| 本地 `$ref` | 支持 | 各 component 分组的 JSON Pointer |
| 相对文件 / file URL `$ref` | 支持 | 相对当前引用文档解析 |
| HTTP(S) `$ref` | 支持 | 单次运行缓存、依赖追踪和循环诊断 |
| 外部 component 重名 | 支持 | 确定性改名并 warning |
| 多 root source | 支持 | 确定性 merge;component/operation 冲突诊断 |
| unresolved `$ref` | 支持诊断 | error,不静默转换成 `dynamic` |
| callbacks / webhooks | 不支持 | warning,不生成反向调用 client |
| Link Object | 不支持 | `openapi.links_not_supported` warning |
当前不是完整 JSON Schema 2020-12 实现。`$defs` 作为生成根、
`if`/`then`/`else`、`not`、`contains`、`dependentSchemas` 和 unevaluated
property/item 语义不在当前契约中。
## Schema 与 model
| 能力 | 状态 | 边界 |
|---|---|---|
| object / array / map | 支持 | 递归容器和 inline schema |
| `properties` + `additionalProperties` | 支持 | extra entry 在 JSON 根 round trip |
| string/number/integer/bool/date/date-time | 支持 | format 可由 type mapping 覆盖 |
| enum | 支持 | string、number、boolean wire value;inline/component |
| enum 成员名映射 | 支持 | 配置及常见 `x-enum` extension |
| enum unknown fallback | 支持 | 可配置 `JsonKey.unknownEnumValue` 输出 |
| required / nullable / absent | 支持 | 反序列化区分缺失和不允许 null;普通 Freezed 字段不保留 presence 历史 |
| readOnly / writeOnly | 支持 | 必要时生成 request/response directional variant |
| `allOf` | 支持 | 合并 object property、required 和 additional property |
| discriminator `oneOf`/`anyOf` | 支持 | local 或已归一化 object branch |
| 无 discriminator `oneOf`/`anyOf` | 部分支持 | primitive/container/object;重叠 `oneOf` 匹配会拒绝 |
| custom schema/type/format | 支持 | external import;宿主代码负责 JSON 转换契约 |
| constraint metadata | 部分支持 | 保留 description/doc comment;不生成 runtime validator |
| presence/dirty tracking | 不支持 | 无 Kiota 风格 backing store |
## HTTP wire 语义
| 能力 | 状态 | 边界 |
|---|---|---|
| path/query/header/cookie parameter | 支持 | type、required/default 和 wire name |
| matrix / label / simple | 支持 | 校验 location/style 组合 |
| form / spaceDelimited / pipeDelimited / deepObject | 支持 | 校验 query/cookie 组合 |
| explode / `allowReserved` | 支持 | Retrofit 无法表达时生成 transport adapter |
| Parameter Object `content` | 部分支持 | 必须恰有一个 media type |
| JSON / `+json` request body | 支持 | required body 和 null-to-absent |
| text request body | 支持 | typed `@Body` |
| `multipart/form-data` | 支持 | binary、primitive、array 和 structured JSON part |
| `application/x-www-form-urlencoded` | 支持 | typed field;复杂 encoding 使用 adapter |
| binary upload | 支持 | `Uint8List` |
| binary download | 支持 | `Uint8List` 或 Dio `ResponseBody` stream |
| XML / Protobuf / 任意 custom codec | 不支持 | content-type error,不生成不安全 decoder |
## Response、server 与 security
| 能力 | 状态 | 边界 |
|---|---|---|
| 多个 2xx / 204 / `2XX` | 支持 | 确定性 primary response;不同 JSON type 可 warning 后变成 `Object?` |
| `default` / 4xx / 5xx | 支持 | 保留 error model/IR metadata;不作为 success |
| response header | 支持 | status、type、required flag 和 description |
| raw response metadata | 支持 | `response_mode: http_response` |
| binary + JSON 混合 success media | 支持诊断 | 单个 Retrofit 方法无法安全 decode 时 error |
| root/path/operation server | 支持 | server variable default/enum 校验;preferred server 成为 client default |
| 多 server runtime 切换 | 部分支持 | 保留 metadata;不生成环境选择 UI |
| API key / Bearer / Basic | 支持 metadata | 生成 scheme/operation requirement;凭证由宿主 interceptor/provider 提供 |
| OAuth2 / OpenID / mutual TLS | 部分支持 | 仅 scheme/scope metadata;不生成完整授权流程 |
| security OR/AND requirement | 支持 | list 项是 OR;同一 map 项中的 scheme 是 AND |
## 生成与生命周期
| 能力 | 状态 | 边界 |
|---|---|---|
| Retrofit + Freezed | 支持 | 当前版本唯一完整 renderer |
| passthrough / external generic wrapper | 支持 | 可配置标准 envelope 检测 |
| model/property/parameter/operation mapping | 支持 | collision 校验和安全 identifier |
| schema/type/import mapping | 支持 | 递归 Dart `List`/`Map` type expression |
| tag/path/operation/model filter | 支持 | exact、glob 和 `regex:` |
| dry-run | 支持 | 报告 would-write/would-delete |
| Dart fix / format | 支持 | 仅本轮改变的主生成文件 |
| build_runner build/watch | 支持 | 在配置的宿主项目目录执行 |
| unchanged skip | 支持 | spec/config/content 和实际文件 SHA-256 |
| stale cleanup | 支持 | manifest ownership、root containment、用户修改保护 |
| concurrent generation lock | 支持 | manifest 旁的操作系统 exclusive lock |
| external template / plugin renderer | 不支持 | IR/preset 已分层,但无公开 renderer SPI |
| 完整 SDK package scaffold | 不支持 | 在已有项目内生成 API/model |
## 验证覆盖
仓库测试覆盖:
- parser、IR、renderer 和 lifecycle 阶段;
- OpenAPI 3.0 YAML、OpenAPI 3.1 JSON/FastAPI fixture;
- enum/命名 mapping、parameter wire style、response、schema 语义、security
和 external/cyclic ref;
- reviewed SHA-256 golden;
- 生成到临时 Dart package,随后运行 build_runner、analysis 和 runtime test。
矩阵中的“支持”不代表超出所列边界的完整 OpenAPI/JSON Schema conformance。
引入新的 schema corpus 时,应先用最小 fixture 和 dry-run 验证。
诊断含义和处理方式见[诊断参考](DIAGNOSTICS.md)。
+254
View File
@@ -0,0 +1,254 @@
# 配置参考
[English](../CONFIGURATION.md) | [简体中文](CONFIGURATION.md)
`koi_swagger_parser` 使用 YAML 配置。完整注释示例见
[koi_swagger_parser.example.yaml](../../koi_swagger_parser.example.yaml)。
未知字段会产生 `config.unknown_key`;非法 scalar 类型和非法组合会在 OpenAPI
解析前报错。
## 查找顺序与路径语义
未传入 `--config` 时,加载器按以下顺序在工作目录查找:
1. `koi_swagger_parser.yaml`
2. `koi_swagger_parser.yml`
3. `generator_config.yaml`
4. `generator_config.yml`
所有相对 OpenAPI 文件 source、输出目录和显式 manifest 路径都以所选配置文件的
目录为基准。未找到配置文件时,以命令调用目录为基准。
显式传入配置文件且未指定 `--cwd` 时,其目录也会成为 build_runner 使用的宿主
项目工作目录。`--cwd` 可覆盖命令工作目录,但不会重新解释配置中的相对路径。
HTTP(S) source URL 保持 URL 语义。
## 顶层字段
| 字段 | 类型 | 必填 | 默认值 |
|---|---|---:|---|
| `input` | map | 是 | 无 |
| `output` | map | 否 | 见下文 |
| `generation` | map | 否 | 见下文 |
| `formatting` | map | 否 | 见下文 |
必须至少配置一个启用的 `input.swagger_urls` 项。
## `input`
### `input.swagger_urls`
类型:字符串或 source 对象的列表。默认:空,会报错。
```yaml
input:
swagger_urls:
- ./openapi.yaml
- url: https://example.com/openapi.json
enabled: true
- url: ./experimental.yaml
enabled: false
```
source 对象字段:
| 字段 | 类型 | 必填 | 默认值 | 含义 |
|---|---|---:|---|---|
| `url` | string | 是 | 无 | 本地路径、file URL 或 HTTP(S) URL |
| `enabled` | boolean | 否 | `true` | 是否参与生成 |
source 按配置顺序处理。多个根文档会确定性合并;operation 冲突报错,component
冲突会确定性改名并产生诊断。
## `output`
| 字段 | 类型 | 默认值 | 含义 |
|---|---|---|---|
| `base_dir` | string path | `./lib/src` | 生成代码公共根 |
| `api_dir` | string path | `<base_dir>/api` | API 输出根 |
| `models_dir` | string path | `<base_dir>/api_models` | model 输出根 |
| `included_tags` | string list | 未设置 | 包含匹配 tag 的 operation |
| `excluded_tags` | string list | 未设置 | 排除匹配 tag 的 operation |
| `included_paths` | string list | 未设置 | 包含匹配的 OpenAPI path |
| `excluded_paths` | string list | 未设置 | 排除匹配的 OpenAPI path |
| `included_operations` | string list | 未设置 | 包含匹配 operation |
| `excluded_operations` | string list | 未设置 | 排除匹配 operation |
| `included_models` | string list | 未设置 | 把匹配 schema 加为生成根 |
| `excluded_models` | string list | 未设置 | 排除匹配 model |
| `split_by_tags` | boolean | `true` | 按 operation tag 拆分 API 文件 |
| `extract_version_from_url` | boolean | `false` | 尝试从 source URL 提取分组版本 |
| `clean_stale_files` | boolean | `true` | 安全删除不再生成的自有文件 |
| `skip_if_unchanged` | boolean | `true` | spec、config 和内容未变时跳过写入 |
| `manifest_path` | string path | `<project>/.dart_tool/koi_swagger_parser/manifest.json` | 所有权/hash manifest |
过滤项支持:
- 精确字符串;
- `*`、`?`、`**` glob;
- `regex:<expression>`,使用 Dart 正则语法。
同一值同时匹配 include/exclude 时,exclude 优先;相同字面量同时出现在两边会
产生 warning。若保留的 operation 依赖被排除 model,生成会失败,不会输出无法
编译的客户端。
stale cleanup 仅处理旧 manifest 记录、仍位于当前 API/model 根、且内容仍匹配
旧 hash 的文件。manifest 应放在生成的 API/model 根之外。
## `generation.preset`
| 字段 | 类型 | 默认值 | 可选值 |
|---|---|---|---|
| `name` | string | `koi_flutter` | `koi_flutter` |
当前版本只有一个 renderer preset:Dio + Retrofit API 和
Freezed/json_serializable model。
## `generation.api`
| 字段 | 类型 | 默认值 | 可选值/含义 |
|---|---|---|---|
| `enabled` | boolean | `true` | 生成 API |
| `use_retrofit` | boolean | `true` | 启用 API 时必须为 `true` |
| `cancel_token_support` | boolean | `false` | 添加 Dio `CancelToken` 参数 |
| `response_mode` | string | `body` | `body`、`http_response` |
| `binary_response_mode` | string | `bytes` | `bytes`、`stream` |
`generation.api.enabled: true` 要求 `generation.models.enabled: true`,因为
当前 Retrofit renderer 会导入生成的 model index。
### `generation.api.client`
| 字段 | 类型 | 默认值 | 含义 |
|---|---|---|---|
| `class_name` | string | `ApiClient` | 生成的 client class 名 |
| `file_name` | string | `api_client` | 生成的 client 文件名 |
值会转换为安全的 Dart identifier/file name。
### `generation.api.response_wrapper`
| 字段 | 类型 | 默认值 | 可选值/含义 |
|---|---|---|---|
| `mode` | string | `passthrough` | `passthrough`、`external_generic` |
| `success_data_field` | string | `data` | envelope 中的成功 payload 字段 |
| `detect_standard_envelope` | boolean | `true` | 检测配置的 code/message/data 风格 envelope |
| `external_generic.import` | string | 无 | `external_generic` 模式必填 |
| `external_generic.class_name` | string | `BaseResult` | 外部 generic wrapper 类型 |
```yaml
generation:
api:
response_wrapper:
mode: external_generic
success_data_field: data
detect_standard_envelope: true
external_generic:
import: package:your_network/your_network.dart
class_name: BaseResult
```
兼容字段 `generation.api.base_result_import` 会在未显式设置 mode 时选择
`external_generic`,class name 为 `BaseResult`。新配置应优先使用结构化写法。
## `generation.models`
| 字段 | 类型 | 默认值 | 含义 |
|---|---|---|---|
| `enabled` | boolean | `true` | 生成 model |
| `class_prefix` | string | `Koi` | 生成 model 名称前缀 |
| `default_non_nullable_list_to_empty` | boolean | `true` | 符合条件的非空 list 默认使用空 list |
| `enum_unknown_fallback` | boolean | `false` | 生成并使用 `unknown` enum 成员 |
| `enum_unknown_default_case` | boolean | `false` | 上一字段的兼容别名 |
### `generation.models.json_serializable`
| 字段 | 类型 | 默认值 |
|---|---|---|
| `checked` | boolean | `true` |
| `explicit_to_json` | boolean | `true` |
| `include_if_null` | boolean | `false` |
这些值会在适用场景传给生成的 json_serializable annotation。
### Enum 映射
支持 list 和 map 两种写法:
```yaml
generation:
models:
enum_mappings:
OrderStatus:
- value: pending
name: waitingForPayment
description: 等待付款
- value: done
name: completed
Priority:
1: low
2:
name: high
description: 需要关注
```
外层 key 是 OpenAPI enum/schema 名称。wire `value` 可以是 string、number 或
boolean;`name` 必须是非空 Dart 成员名;`description` 可选。
兼容字段 `enum_key_mappings` 接受同样结构。两个字段映射同一 enum value 时,
`enum_mappings` 优先并产生 warning。vendor extension 优先级见
[README 的 Enum 映射](../../README.zh-CN.md#enum-映射)。
## `generation.mappings`
所有 mapping 段默认都是空 map。
| 字段 | 结构 | 含义 |
|---|---|---|
| `models` | `schema: DartName` | 重命名生成的 component model |
| `properties` | `schema: {wireProperty: dartField}` | 重命名指定 schema 的属性 |
| `parameters` | `operation: {wireParameter: dartParameter}` | 重命名指定 operation 的参数 |
| `operations` | `operation: dartMethod` | 重命名 operation |
| `schemas` | `schema: type-or-object` | 使用外部 Dart 类型,不生成该 schema |
| `types` | `openapiType[:format]: type-or-object` | 映射 OpenAPI scalar/type pattern |
| `imports` | `DartType: packageImport` | 为映射后的 Dart 类型提供 import |
operation key 可以是 `operationId` 或 `METHOD /path`。type mapping 先尝试
`type:format`,再尝试 `type`。
type-or-object 可以是 Dart 类型字符串,也可以是:
```yaml
type: UuidValue
import: package:uuid/uuid_value.dart
```
同一 mapping scope 内,两个 source name 不能指向同一个 Dart 名。相同 schema
不能同时出现在 `models` 和 `schemas`。同一个 Dart 类型不能解析出冲突 import。
这些组合都会报配置错误。
## `formatting`
| 字段 | 类型 | 默认值 | 可选值/含义 |
|---|---|---|---|
| `run_dart_fix` | boolean | `false` | 对本轮改变的主生成文件执行 `dart fix --apply` |
| `run_dart_format` | boolean | `true` | 格式化本轮改变的主生成文件 |
| `run_build_runner` | boolean | `false` | 主文件写入后运行 build_runner |
| `build_runner_mode` | string | `build` | `build`、`watch` |
| `delete_conflicting_outputs` | boolean | `true` | 向 build_runner 传入 `--delete-conflicting-outputs` |
CLI `--watch` 会在非 dry-run 生成后启动 build_runner watch,不受配置 mode
限制。watch 是长驻流程,适合交互式开发,不适合一次性 CI job。
## 组合规则
加载器会强制以下关系:
- API 和 model 不能同时关闭;
- API 当前依赖 model;
- API 当前要求 `use_retrofit: true`;
- `external_generic` response wrapper 需要非空 import;
- 未知 preset、enum/response/build-runner mode 会报错;
- 非法 filter 正则会报错;
- mapping target/import 冲突会报错。
稳定 code 和处理方式见[诊断参考](DIAGNOSTICS.md)。
+150
View File
@@ -0,0 +1,150 @@
# 诊断
[English](../DIAGNOSTICS.md) | [简体中文](DIAGNOSTICS.md)
配置、加载、解析、渲染和生命周期问题统一表示为 `KoiDiagnostic`:
```dart
class KoiDiagnostic {
final String code;
final DiagnosticSeverity severity; // info | warning | error
final String message;
final String? source;
final String? location;
}
```
`code` 是稳定的机器可读标识;message 文案可能随版本改进。`source` 表示配置
路径、文档 URL/文件或生成文件;`location` 通常是 source 内的 JSONPath、
JSON Pointer 或其他逻辑位置。
## Severity 行为
- `info`:正常决策,例如跳过未变化的生成;
- `warning`:输出仍然可靠,但发生改名、安全降级或明确省略的非阻塞能力;
- `error`:继续执行可能产生无效客户端,生成器抛出
`KoiDiagnosticException`。
CLI 默认输出 warning/error;`--verbose` 还会输出 info 和失败堆栈。
library 调用方可检查诊断:
```dart
try {
final result = await const KoiSwaggerParser().generate();
for (final item in result.diagnostics) {
print(item.format());
}
} on KoiDiagnosticException catch (error) {
for (final item in error.diagnostics) {
print(item.format());
}
}
```
自动化应匹配 `code`,不要匹配 message 文本。需要区分上下文时再断言 `source`
或 `location`。
## 常见 code
### 配置
| Code | 含义和处理 |
|---|---|
| `config.missing_sources` | 添加至少一个启用的 `input.swagger_urls`。 |
| `config.unknown_key` | 修正拼写或删除当前版本不认识的字段;warning。 |
| `config.invalid_section` | 把 scalar/list 改成预期 YAML map。 |
| `config.invalid_string` / `config.invalid_boolean` | 修正 YAML scalar 类型。 |
| `config.invalid_string_list` | include/exclude filter 必须是字符串列表。 |
| `config.invalid_filter_regex` | 修正 `regex:` 后的 Dart 表达式。 |
| `config.no_outputs_enabled` | API/model 不能同时关闭。 |
| `config.api_requires_models` | 当前 API renderer 依赖生成 model。 |
| `config.unsupported_api_renderer` | 保持 `use_retrofit: true`;目前没有其他公开 API renderer。 |
| `config.external_wrapper_import_required` | 添加 `external_generic.import`。 |
| `config.mapping_target_collision` | 为 mapping source 设置不同的 Dart 目标名。 |
| `config.import_mapping_conflict` | 同一映射 Dart 类型只能使用一个 import。 |
| `config.conflicting_schema_mapping` | 同一 schema 不能既重命名又由外部类型替换。 |
| `config.ambiguous_enum_mapping` | 使用类型明确、无歧义的 enum wire value。 |
### 文档与引用
| Code | 含义和处理 |
|---|---|
| `openapi.missing_version` / `openapi.unsupported_version` | 提供 OpenAPI 3.0.x/3.1.x 文档。 |
| `openapi.invalid_info` / `openapi.invalid_paths` | 修正文档对应关键结构。 |
| `openapi.source_load_failed` / `openapi.source_http_error` | 检查文件路径、URL、访问权限和响应状态。 |
| `openapi.unresolved_ref` | 本地 JSON Pointer 不存在。 |
| `openapi.unresolved_external_ref` | 外部资源/fragment 不存在或无法加载。 |
| `openapi.external_ref_not_bundled` | ref 归一化失败;检查前序 source/resolver 诊断。 |
| `openapi.external_component_renamed` | 外部 component 冲突已确定性改名;warning。 |
| `openapi.multisource_component_renamed` | root 间 component 冲突已确定性改名;warning。 |
| `openapi.multisource_operation_conflict` | 两个 root 定义相同 method/path;删除或拆分其中一个。 |
| `openapi.callbacks_not_supported` / `openapi.webhooks_not_supported` | 不生成反向调用 client;warning。 |
| `openapi.links_not_supported` | 不生成 Link Object 调用关系;warning。 |
### Parameter、request 与 response
| Code | 含义和处理 |
|---|---|
| `openapi.invalid_parameter_style` | 使用与 parameter location 匹配的 style。 |
| `openapi.deep_object_requires_object` | deepObject parameter 必须使用 object schema。 |
| `openapi.delimited_parameter_requires_array` | space/pipe-delimited parameter 必须使用 array schema。 |
| `openapi.parameter_content_count` | Parameter Object `content` 必须恰有一个 media type。 |
| `openapi.request_body_schema_missing` | 为选中的 request media type 添加 schema。 |
| `openapi.unsupported_request_content_type` | 使用已支持 request codec 或手写 endpoint。 |
| `openapi.unsupported_response_content_type` | 使用已支持 response codec 或手写 endpoint。 |
| `openapi.success_response_missing` | 添加显式 2xx response;`default` 不是 success。 |
| `openapi.multiple_success_response_types` | 多个成功 JSON body type 统一为 `Object?`;warning。 |
| `openapi.mixed_success_media_types` | 单个生成方法无法安全 decode binary/stream + JSON success。 |
| `openapi.path_parameter_forced_required` | 声明为 optional 的 path parameter 被强制为 required;warning。 |
### Schema 与命名
| Code | 含义和处理 |
|---|---|
| `openapi.property_read_write_only` | property 不能同时 read-only/write-only。 |
| `openapi.invalid_additional_properties` | 使用 boolean 或 Schema Object。 |
| `openapi.property_name_collision` | 自动 suffix 避免了 Dart field collision;warning。 |
| `openapi.operation_name_collision` | 自动 suffix 避免了 method collision;warning。 |
| `openapi.operation_argument_name_collision` | parameter 与 body、cancel token 或其他参数冲突。 |
| `openapi.duplicate_enum_member_name` | 多个 wire value 映射到同名,已添加 suffix。 |
| `openapi.enum_member_name_sanitized` | enum member name 已转换为合法 Dart identifier。 |
| `filter.required_model_excluded` | 选中 API 依赖被排除 model;包含该 model 或排除调用方。 |
| `filter.model_pattern_unmatched` | `included_models` pattern 没有匹配 schema。 |
### Server 与 security
| Code | 含义和处理 |
|---|---|
| `openapi.server_url_missing` | 添加非空 server URL。 |
| `openapi.server_variable_default_missing` | 为每个 server variable 提供 default。 |
| `openapi.server_variable_default_invalid` | 让 default 属于声明的 enum value。 |
| `openapi.server_variable_unresolved` | 定义 server URL 中的每个 variable。 |
| `openapi.unknown_security_scheme` | 在 components 中定义被引用 scheme。 |
| `openapi.unsupported_security_scheme` | scheme 只在文档边界内保留或被拒绝。 |
| `openapi.http_auth_requires_custom_interceptor` | 在宿主 Dio 层实现凭证注入;warning。 |
### 生成生命周期
| Code | 含义和处理 |
|---|---|
| `lifecycle.unchanged_skipped` | hash/file 一致,已安全跳过生成。 |
| `lifecycle.manifest_invalid` | 旧 manifest 损坏;重建输出但不信任其删除列表。 |
| `lifecycle.manifest_version_ignored` | manifest schema 不兼容,其删除列表被忽略。 |
| `lifecycle.output_outside_managed_roots` | renderer 输出越过 API/model 根,已拒绝。 |
| `lifecycle.duplicate_output_path` | 两个渲染文件以不同内容指向同一路径。 |
| `lifecycle.stale_path_outside_roots` | 旧路径位于当前 root 外,因此保留。 |
| `lifecycle.stale_file_modified` | stale 生成文件已被编辑,保留供人工处理。 |
| `lifecycle.generated_file_missing` | 主生成文件在 fix/format/build 期间消失。 |
| `lifecycle.control_file_not_stale` | lifecycle 控制文件永不作为 stale 输出处理。 |
## 设计原则
1. unresolved ref、非法 wire style 和不支持 codec 不会静默变成 `dynamic`;
2. 安全且可编译的自动改名可以 warning 后继续;
3. 不会破坏其他输出的不支持结构会 warning 后明确省略;
4. stale cleanup 在 path containment 或 hash ownership 不确定时保留文件;
5. 可用时诊断会带 component、operation、parameter 或文件上下文。
minor release 可以增加新 code。自动化应把未知 warning 视为需复核,把未知 error
视为阻塞。
+215
View File
@@ -0,0 +1,215 @@
# 从 swagger_generator_flutter 迁移
[English](../MIGRATION.md) | [简体中文](MIGRATION.md)
本指南面向使用 `swagger_generator_flutter` 的 `generator_config.yaml` 惯例。
迁移应在消费方 Dart/Flutter 仓库中进行,不依赖任何本机绝对路径。
## 建议顺序
1. 在独立分支保留旧生成结果;
2. 复制 [koi_swagger_parser.example.yaml](../../koi_swagger_parser.example.yaml),
第一轮只迁移 source、输出根和 response wrapper;
3. 第一次设置 `clean_stale_files: false` 并执行 `--dry-run`;
4. 生成到临时目录,对比 API signature、JSON key 和 import;
5. 迁移 enum、命名和 custom type mapping;
6. 在宿主项目运行 build_runner、`dart analyze` 和测试;
7. 审核输出后切换正式目录,并启用 `clean_stale_files: true`。
## 可直接沿用
| 旧字段 | 新字段或行为 |
|---|---|
| `input.swagger_urls` | 同名;支持 string/object、`enabled`、本地路径和 HTTP(S) source |
| `output.base_dir/api_dir/models_dir` | 同名 |
| `output.included_tags/excluded_tags` | 同名,并支持 glob/正则 |
| `output.split_by_tags` | 同名 |
| `generation.api.client.class_name/file_name` | 同名 |
| `generation.api.cancel_token_support` | 同名 |
| `generation.api.base_result_import` | 兼容输入;选择 `external_generic` + `BaseResult` |
| `generation.models.class_prefix` | 同名 |
| `generation.models.json_serializable.*` | 同名 |
| `generation.models.enum_key_mappings` | 仍然支持;建议迁移到 `enum_mappings` |
| `formatting.run_dart_format` | 同名 |
已有 enum mapping 不需要立即重写:
```yaml
generation:
models:
enum_key_mappings:
TaskType:
- value: 1
name: spotCheck
description: 抽查
```
两个字段映射同一 wire value 时,`enum_mappings` 优先。生成器也识别
`x-enumNames`、`x-enum-varnames` 和 `x-enum-values`。
## 需要改写
### Response wrapper
旧写法:
```yaml
generation:
api:
base_result_type: BaseResult
base_result_import: package:app/network/base_result.dart
```
推荐写法:
```yaml
generation:
api:
response_wrapper:
mode: external_generic
detect_standard_envelope: true
success_data_field: data
external_generic:
import: package:app/network/base_result.dart
class_name: BaseResult
```
`base_page_result_type/base_page_result_import` 没有自动一对一迁移。优先在
OpenAPI 中准确描述分页 schema;复用已有类型时使用
`generation.mappings.schemas`,或用 `passthrough` 保留服务端 envelope。
### 类型与 import mapping
旧顶层 `type_mapping` 和 `imports` 是全局配置。新配置把可校验 mapping 集中到
`generation.mappings`:
```yaml
generation:
mappings:
schemas:
Money:
type: Money
import: package:money2/money2.dart
types:
string:uuid:
type: UuidValue
import: package:uuid/uuid_value.dart
string:date-time: DateTime
imports:
UuidValue: package:uuid/uuid_value.dart
```
type 查找先尝试 `type:format`,再尝试 `type`。`schemas` 表示 schema 完全由
external type 提供,不再生成对应 Dart model。
### ignored directory/file
旧 `ignored_directories`/`ignored_files` 在渲染后跳过文件,可能留下引用缺失
model 的 API。应改为源语义 filter:
```yaml
output:
excluded_paths:
- /api/v1/legacy/**
excluded_operations:
- regex:^internal
excluded_models:
- Legacy*
```
保留的 operation 若依赖被排除 model,生成会以
`filter.required_model_excluded` 失败。没有任意丢弃渲染文件的设置。
### 版本提取
`/api/v1/`、`/v2/` 等 path 会自动分组。若还要尝试从 source URL 提取:
```yaml
output:
extract_version_from_url: true
```
旧 `generation.api.version_extraction.pattern/default_version` 没有一对一配置。
URL 提取失败会产生诊断并回到基于 path 的分组。
### build_runner
旧流程通常单独执行 build_runner,现在可以并入生成:
```yaml
formatting:
run_dart_format: true
run_build_runner: true
build_runner_mode: build
delete_conflicting_outputs: true
```
交互式开发可用:
```bash
dart run koi_swagger_parser generate --watch
```
watch 是长驻模式,不适合一次性 CI job。
## 不再读取的旧设置
这些字段会产生 `config.unknown_key` warning:
- `generator.name/version/author/copyright`;
- `input.validate_schema/strict_mode`;
- `output.api_file_suffix/model_file_suffix`;
- `generation.api.use_dio/parser/method_naming`;
- `generation.models.class_naming/field_naming/use_const_constructor`;
- 顶层 `validation.on_error/on_warning`;
- 顶层通用 import 排序/分组设置。
新生成器对会破坏 wire semantics 或编译的情况使用结构化 error,不提供
warn/ignore override。callbacks、links 等可安全省略的不支持结构使用 warning。
## 需审核的生成代码差异
- readOnly/writeOnly schema 可拆为 `FooRequest`/`FooResponse`;
- optional 但 schema 不允许 null 的字段仍用 nullable Dart argument 表示 absent,
同时使用 `disallowNullValue`;显式 JSON null 会被拒绝;
- 根 object 同时有 `properties` 和 `additionalProperties` 时,extra 会 flatten
到 JSON 根;
- `default` response 永不作为 success;
- 复杂 parameter style 可产生 public transport adapter + private Retrofit
delegate;
- API method 可返回 `HttpResponse<T>`,binary 输出可使用 bytes/stream;
- enum 包含 wire conversion,unknown fallback 使用内部 sentinel wire value;
- stale file 只通过 manifest ownership 删除;用户修改的 stale file 会保留。
## CLI 对照
旧:
```bash
dart run swagger_generator_flutter generate --all
```
新:
```bash
dart run koi_swagger_parser generate --all
dart run koi_swagger_parser generate --dry-run --verbose
```
旧 `--api`、`--models`、`--docs` 和 CLI tag override 不再保留。使用
`generation.*.enabled` 和 output filter。当前生成器不输出独立 API 文档站点。
## 验收清单
- dry-run 无 error 诊断;
- enum wire value 和 Dart member name 与旧契约一致;
- request/response directional model 包含预期字段;
- cookie、query、path 序列化产生等价请求;
- error/default response 不被 decode 成 success;
- external custom type 的 import 和 JSON conversion 正确;
- `dart run build_runner build --delete-conflicting-outputs` 成功;
- `dart analyze` 和宿主项目测试通过;
- 第二次生成报告 `lifecycle.unchanged_skipped`;
- 移除 operation 后只删除 manifest 拥有且未修改的文件。
对每个可用后端 fixture,除了对比生成源码,还应对比 runtime serialization。