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:
+132
-74
@@ -1,114 +1,172 @@
|
||||
# Architecture
|
||||
|
||||
`koi_swagger_parser` 采用“加载 -> 解析 -> IR -> 渲染 -> 落盘”的分层设计,避免把 OpenAPI 细节、Koi 业务约定、具体 Dart 输出模板揉在一起。
|
||||
[English](ARCHITECTURE.md) | [Chinese (Simplified)](zh-CN/ARCHITECTURE.md)
|
||||
|
||||
## 1. 分层
|
||||
The generation pipeline is deliberately split into stages:
|
||||
|
||||
### config
|
||||
```text
|
||||
configuration
|
||||
-> source loader
|
||||
-> external/local $ref resolver
|
||||
-> multi-source merger
|
||||
-> validation + parser
|
||||
-> transport/schema IR
|
||||
-> preset + Dart renderer
|
||||
-> manifest lifecycle
|
||||
-> fix / format / build_runner
|
||||
```
|
||||
|
||||
负责读取与归一化配置:
|
||||
The main invariant is that loading produces a self-contained document with
|
||||
traceable dependencies; the parser represents OpenAPI semantics; the renderer
|
||||
does not reinterpret the specification; and the lifecycle manages only files
|
||||
declared in its manifest.
|
||||
|
||||
- 新配置:`koi_swagger_parser.yaml`
|
||||
- 兼容配置:`generator_config.yaml`
|
||||
- 路径归一化、默认值、旧字段兼容映射
|
||||
- preset 解析(当前内置 `koi_flutter`)
|
||||
## Configuration
|
||||
|
||||
### openapi
|
||||
`lib/src/config/config.dart`:
|
||||
|
||||
负责:
|
||||
- discovers `koi_swagger_parser.yaml` and compatible
|
||||
`generator_config.yaml` names;
|
||||
- resolves source and output paths against the configuration directory;
|
||||
- chooses the host-project command directory from `--cwd` or the explicit
|
||||
configuration directory;
|
||||
- validates YAML scalar types before casting;
|
||||
- parses enum, naming, type/import mappings, and filters;
|
||||
- validates unsupported combinations, mapping collisions, regular
|
||||
expressions, and enum mappings;
|
||||
- supports migration from the legacy `base_result_import` setting.
|
||||
|
||||
- 本地/远程文档加载
|
||||
- 多 source 深合并
|
||||
- schema/ref 解析
|
||||
- OpenAPI 3.1 nullable / anyOf / oneOf / additionalProperties / allOf 归一化
|
||||
- multipart request body 与 discriminator union 识别
|
||||
Unknown keys produce warnings. Invalid types and incomplete combinations
|
||||
produce errors. See [Configuration](CONFIGURATION.md).
|
||||
|
||||
### ir
|
||||
## Loader, resolver, and merger
|
||||
|
||||
产出与 OpenAPI 原文解耦的中间表示:
|
||||
For each root source, `OpenApiReferenceResolver`:
|
||||
|
||||
- API operation
|
||||
- request body / parameter
|
||||
- model / enum
|
||||
- Dart type graph
|
||||
1. normalizes a local file, file URL, or HTTP(S) source;
|
||||
2. caches each loaded document;
|
||||
3. resolves local, relative, and remote `$ref` values;
|
||||
4. bundles external components into the root document;
|
||||
5. assigns deterministic names to component conflicts;
|
||||
6. records dependencies and diagnoses unsupported cross-document cycles.
|
||||
|
||||
IR 是后续接入 `koi_blueprint_flutter` preset 的关键。
|
||||
`OpenApiDocumentMerger` then merges roots in configuration order:
|
||||
|
||||
### generator
|
||||
- a duplicate operation method/path is an error;
|
||||
- components with the same name but different content are renamed
|
||||
deterministically, and references from that source are rewritten;
|
||||
- later inputs never silently overwrite conflicts;
|
||||
- version, info, and path-parameter conflicts are reported.
|
||||
|
||||
只关心如何把 IR 渲染成 Dart 文件:
|
||||
The parser should receive only root-document JSON Pointers. A remaining
|
||||
external reference is treated as a resolver error.
|
||||
|
||||
- Retrofit API
|
||||
- Freezed model
|
||||
- enums
|
||||
- index/client 聚合文件
|
||||
## Validation and parsing
|
||||
|
||||
这一层不直接处理 OpenAPI 细节。
|
||||
`OpenApiDocumentParser` handles:
|
||||
|
||||
### preset
|
||||
- OpenAPI 3.0/3.1 version and core structure checks;
|
||||
- operation, path, tag, and model filters;
|
||||
- parameter location, style, explode, `allowReserved`, and `content`;
|
||||
- request media types and response status, content, and headers;
|
||||
- servers, security schemes, and security requirements;
|
||||
- required, nullable, read-only/write-only, and `additionalProperties`
|
||||
semantics;
|
||||
- enum configuration/vendor-extension precedence;
|
||||
- `allOf`, discriminated unions, and supported generic unions;
|
||||
- custom mappings and Dart name collisions.
|
||||
|
||||
用于把 parser core 的 IR 和具体客户端技术栈解耦。当前先提供一个很薄的 preset 层:
|
||||
A structure that cannot be rendered with reliable wire semantics must not
|
||||
silently become `dynamic`. A safe but lossy continuation produces a warning;
|
||||
for example, different successful JSON body types may converge on `Object?`.
|
||||
A media type without a supported decoder, such as XML or Protobuf, produces an
|
||||
error.
|
||||
|
||||
- `koi_flutter`:默认面向 Retrofit + Dio + Freezed
|
||||
- 当前主要控制 multipart 二进制字段该生成为 `MultipartFile`、`File`、`Uint8List` 还是 `String`
|
||||
- 后续可以继续扩展 blueprint preset、网络层 preset、命名/包装策略 preset
|
||||
## Intermediate representation
|
||||
|
||||
## 2. Response Wrapper 设计
|
||||
`lib/src/ir/ir.dart` is the parser/renderer contract. Its main nodes include:
|
||||
|
||||
parser core 不直接写死 `BaseResult`。
|
||||
- `ApiOperation`: method, path, tag, version, server, and security data;
|
||||
- `ApiParameter`: Dart/wire name, location, style, explode, and
|
||||
`allowReserved`;
|
||||
- `ApiRequestBody`: JSON, multipart, form-urlencoded, and binary requests;
|
||||
- `ApiResponse`: status/default, body kind/type, content type, and headers;
|
||||
- `ModelSpec`: object, enum, and union models with usage category and extras;
|
||||
- `ModelField`: required/nullability/read-write flags and constraints;
|
||||
- `DartType`: named, list, map, object, dynamic, and void type graphs.
|
||||
|
||||
当前支持两种模式:
|
||||
Models are grouped as request, result, shared, or enum models. A component with
|
||||
read-only/write-only properties can be projected into request and response
|
||||
variants so that read-only values are not required in requests and
|
||||
write-only secrets are not exposed in responses.
|
||||
|
||||
- `passthrough`
|
||||
- 返回真实 OpenAPI response schema
|
||||
- 适合你希望保留服务端 envelope 模型的场景
|
||||
- `external_generic`
|
||||
- 识别标准成功 envelope(默认 `code/message/data`)
|
||||
- 将 `data` 提取为 inner type
|
||||
- API 返回 `ExternalWrapper<Inner>`,例如 `BaseResult<KoiStoreResponse>`
|
||||
A generic `oneOf` requires exactly one decoder match. A generic `anyOf` retains
|
||||
the raw value and all matching cases. Object branches are prefiltered by
|
||||
required keys and enum constraints before decoding.
|
||||
|
||||
这样做的意义是:
|
||||
## Preset and renderer
|
||||
|
||||
- parser core 保持通用
|
||||
- Koi 网络层包裹策略可配置
|
||||
- 未来可以挂更多 wrapper preset,而不是继续堆服务端补丁
|
||||
The current `koi_flutter` preset targets Dio, Retrofit, Freezed, and
|
||||
json_serializable.
|
||||
|
||||
## 3. 模型分组
|
||||
The renderer:
|
||||
|
||||
模型按使用语义归档:
|
||||
- uses Retrofit annotations for parameter forms Retrofit can represent;
|
||||
- emits a typed public adapter and private Retrofit delegate when a supported
|
||||
style, cookie, or `allowReserved` combination needs custom serialization;
|
||||
- renders Freezed object, enum, and union models;
|
||||
- renders custom root-map read/write helpers for
|
||||
`properties` plus `additionalProperties`;
|
||||
- collects model dependencies and external type imports;
|
||||
- groups API files by version and tag and emits client/index files.
|
||||
|
||||
- `request/`:只在请求体根节点使用
|
||||
- `result/`:只在成功响应根节点使用
|
||||
- `shared/`:被请求与响应共同依赖,或用于错误响应
|
||||
- `enums/`:枚举单独分组
|
||||
The renderer does not load references or decide whether an OpenAPI structure
|
||||
is valid.
|
||||
|
||||
这比“按 swagger component 平铺”更符合 Koi 项目结构,也更利于未来蓝图模板整理。
|
||||
## Generation lifecycle
|
||||
|
||||
## 4. 命名原则
|
||||
Before writing, `GenerationLifecycle` calculates:
|
||||
|
||||
- 类名统一 PascalCase。
|
||||
- 字段统一 camelCase,并保留 `@JsonKey(name: ...)` 对应 wire name。
|
||||
- 文件名统一 snake_case。
|
||||
- API 默认按 `version + tag` 分文件。
|
||||
- the canonical merged-spec SHA-256;
|
||||
- the normalized configuration SHA-256;
|
||||
- each rendered path and content SHA-256.
|
||||
|
||||
命名转换统一收口到 util 层,避免每个阶段各写一套字符串处理逻辑。
|
||||
The manifest records its schema/generator version, source roots, dependencies,
|
||||
and the actual hash of each primary generated file. Writes use an operating
|
||||
system exclusive lock and same-directory temporary-file rename.
|
||||
|
||||
## 5. 为什么新建项目而不是继续修补旧生成器
|
||||
Safety invariants:
|
||||
|
||||
旧生成器的问题不是单点 bug,而是结构性耦合:
|
||||
1. Renderer output must stay inside the configured API/model roots.
|
||||
2. A stale candidate must come from the previous manifest.
|
||||
3. Its path must still be inside the current managed roots.
|
||||
4. Its current hash must equal the previous manifest hash before deletion.
|
||||
5. A corrupt or unknown-version manifest is never trusted for deletion.
|
||||
6. A failed fix, format, or build step does not write a success manifest.
|
||||
7. Watch mode starts after the generation lock is released.
|
||||
|
||||
- OpenAPI 解析和业务输出绑定太深。
|
||||
- nullable / anyOf / wrapper 规则分散在多个模板与解析阶段。
|
||||
- 很难为未来 `koi_blueprint_flutter` 抽出复用 IR。
|
||||
build_runner owns `.g.dart` and `.freezed.dart` files; they are not included in
|
||||
this generator's stale-file manifest.
|
||||
|
||||
所以这里直接重新收口:
|
||||
## Response wrappers
|
||||
|
||||
- parser core 面向 OpenAPI 语义
|
||||
- Koi 输出作为 renderer/preset
|
||||
- 兼容旧配置,但不延续旧架构负担
|
||||
The parser does not hard-code `BaseResult`:
|
||||
|
||||
## 6. 近期路线
|
||||
- `passthrough` returns the OpenAPI response schema;
|
||||
- `external_generic` detects configured success envelopes and returns
|
||||
`ExternalWrapper<Inner>`.
|
||||
|
||||
- 第一阶段:打通 Koi/FastAPI 场景(已完成)
|
||||
- 第二阶段:补齐 multipart、discriminator union、preset 基础层(当前版本)
|
||||
- 第三阶段:扩更强 union fallback、更多 preset,并服务 `koi_blueprint_flutter`
|
||||
A `default` response remains an error response. `response_mode:
|
||||
http_response` retains Dio status and headers. Error response models remain in
|
||||
the IR and generated model set.
|
||||
|
||||
## Extension boundaries
|
||||
|
||||
The IR/preset/renderer split leaves room for a versioned renderer interface,
|
||||
template overrides, semantic-diff tooling, and optional runtime adapters.
|
||||
Those are design directions, not features of the current public release. The
|
||||
current implementation boundaries are documented in
|
||||
[Compatibility](COMPATIBILITY.md).
|
||||
|
||||
Additional ecosystem comparisons are kept as internal project research and
|
||||
are not part of the published package.
|
||||
|
||||
Reference in New Issue
Block a user