Files
mr.koi 454e6c51d7 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.
2026-07-31 16:47:39 +08:00

173 lines
6.7 KiB
Markdown

# Architecture
[English](ARCHITECTURE.md) | [Chinese (Simplified)](zh-CN/ARCHITECTURE.md)
The generation pipeline is deliberately split into stages:
```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.
## Configuration
`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.
Unknown keys produce warnings. Invalid types and incomplete combinations
produce errors. See [Configuration](CONFIGURATION.md).
## Loader, resolver, and merger
For each root source, `OpenApiReferenceResolver`:
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.
`OpenApiDocumentMerger` then merges roots in configuration order:
- 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.
The parser should receive only root-document JSON Pointers. A remaining
external reference is treated as a resolver error.
## Validation and parsing
`OpenApiDocumentParser` handles:
- 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.
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.
## Intermediate representation
`lib/src/ir/ir.dart` is the parser/renderer contract. Its main nodes include:
- `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.
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
The current `koi_flutter` preset targets Dio, Retrofit, Freezed, and
json_serializable.
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.
The renderer does not load references or decide whether an OpenAPI structure
is valid.
## Generation lifecycle
Before writing, `GenerationLifecycle` calculates:
- the canonical merged-spec SHA-256;
- the normalized configuration SHA-256;
- each rendered path and content SHA-256.
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.
Safety invariants:
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.
build_runner owns `.g.dart` and `.freezed.dart` files; they are not included in
this generator's stale-file manifest.
## Response wrappers
The parser does not hard-code `BaseResult`:
- `passthrough` returns the OpenAPI response schema;
- `external_generic` detects configured success envelopes and returns
`ExternalWrapper<Inner>`.
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.