Complete OpenAPI 3.0/3.1 generation, diagnostics, lifecycle safety, enum and naming mappings, bilingual documentation, examples, and pub.dev release validation.
6.7 KiB
Architecture
English | Chinese (Simplified)
The generation pipeline is deliberately split into stages:
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.yamland compatiblegenerator_config.yamlnames; - resolves source and output paths against the configuration directory;
- chooses the host-project command directory from
--cwdor 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_importsetting.
Unknown keys produce warnings. Invalid types and incomplete combinations produce errors. See Configuration.
Loader, resolver, and merger
For each root source, OpenApiReferenceResolver:
- normalizes a local file, file URL, or HTTP(S) source;
- caches each loaded document;
- resolves local, relative, and remote
$refvalues; - bundles external components into the root document;
- assigns deterministic names to component conflicts;
- 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, andcontent; - request media types and response status, content, and headers;
- servers, security schemes, and security requirements;
- required, nullable, read-only/write-only, and
additionalPropertiessemantics; - 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, andallowReserved;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
allowReservedcombination needs custom serialization; - renders Freezed object, enum, and union models;
- renders custom root-map read/write helpers for
propertiesplusadditionalProperties; - 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:
- Renderer output must stay inside the configured API/model roots.
- A stale candidate must come from the previous manifest.
- Its path must still be inside the current managed roots.
- Its current hash must equal the previous manifest hash before deletion.
- A corrupt or unknown-version manifest is never trusted for deletion.
- A failed fix, format, or build step does not write a success manifest.
- 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:
passthroughreturns the OpenAPI response schema;external_genericdetects configured success envelopes and returnsExternalWrapper<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.
Additional ecosystem comparisons are kept as internal project research and are not part of the published package.