zopia 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +354 -0
- package/LICENSE +21 -0
- package/README.md +167 -0
- package/bin/zopia.js +20 -0
- package/docs/01-overview.md +94 -0
- package/docs/02-targets.md +55 -0
- package/docs/03-roadmap.md +205 -0
- package/docs/04-architecture.md +345 -0
- package/docs/05-concepts.md +239 -0
- package/docs/06-conversions.md +493 -0
- package/docs/07-api-docs.md +337 -0
- package/docs/08-components.md +223 -0
- package/docs/09-configuration.md +167 -0
- package/docs/10-usage.md +208 -0
- package/docs/11-testing.md +267 -0
- package/docs/12-standards.md +242 -0
- package/docs/README.md +42 -0
- package/docs/publish-workflow.yml.example +48 -0
- package/package.json +77 -0
- package/src/api-docs-navigation.ts +353 -0
- package/src/cli-command.ts +537 -0
- package/src/cli.ts +4 -0
- package/src/config.ts +190 -0
- package/src/conversions/api-docs-facade.ts +42 -0
- package/src/conversions/api-docs-generate.ts +567 -0
- package/src/conversions/api-docs-layout.ts +39 -0
- package/src/conversions/api-docs-plan.ts +130 -0
- package/src/conversions/api-docs-presets.ts +246 -0
- package/src/conversions/json-schema-to-zod.ts +931 -0
- package/src/conversions/manifest-staleness.ts +211 -0
- package/src/conversions/manifest-to-openapi.ts +1861 -0
- package/src/conversions/manifest-writer.ts +778 -0
- package/src/conversions/openapi-contracts.ts +333 -0
- package/src/conversions/openapi-external-ref.ts +233 -0
- package/src/conversions/openapi-ir.ts +74 -0
- package/src/conversions/openapi-ref.ts +38 -0
- package/src/conversions/openapi-to-api-docs-public.ts +466 -0
- package/src/conversions/openapi-to-api-docs.ts +203 -0
- package/src/conversions/openapi.ts +80 -0
- package/src/conversions/reverse-security.ts +68 -0
- package/src/conversions/yaml.ts +876 -0
- package/src/conversions/zod-to-json-schema.ts +536 -0
- package/src/diff.ts +353 -0
- package/src/errors.ts +114 -0
- package/src/index.ts +80 -0
- package/src/validation.ts +299 -0
- package/src/warnings.ts +164 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# ๐๏ธ Architecture
|
|
2
|
+
|
|
3
|
+
This document describes **how zopia v0.1.0 is built**: module layout, conversion
|
|
4
|
+
pipeline, stage-specific representations, reference handling, and the
|
|
5
|
+
error/safety model. Code and documentation change together; disagreement is a
|
|
6
|
+
release-blocking defect under the
|
|
7
|
+
[docs convention](12-standards.md#-docs-convention).
|
|
8
|
+
|
|
9
|
+
## ๐งฉ Module layout
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
. # ๐ฆ repository root
|
|
13
|
+
โโโ bin/zopia.js # โจ๏ธ npm executable; launches the Bun CLI
|
|
14
|
+
โโโ src/
|
|
15
|
+
โ โโโ index.ts # ๐ช public named-export surface
|
|
16
|
+
โ โโโ cli.ts # โจ๏ธ process entry point
|
|
17
|
+
โ โโโ cli-command.ts # strict parser, help, output/exit contract
|
|
18
|
+
โ โโโ config.ts # ๐งพ zopia.config.ts discovery, trusted import, validation (D-19)
|
|
19
|
+
โ โโโ errors.ts # ๐ typed error catalogue
|
|
20
|
+
โ โโโ warnings.ts # โ ๏ธ structured warning pipeline
|
|
21
|
+
โ โโโ validation.ts # ๐งน zopia validate lint batteries (specs + generated trees, S-89)
|
|
22
|
+
โ โโโ diff.ts # ๐ zopia diff semantic spec comparison (S-91)
|
|
23
|
+
โ โโโ conversions/
|
|
24
|
+
โ โโโ zod-to-json-schema.ts # โ Zod โ JSON Schema
|
|
25
|
+
โ โโโ json-schema-to-zod.ts # โก JSON Schema โ Zod
|
|
26
|
+
โ โโโ yaml.ts # owned YAML 1.2 core-schema parser (D-16)
|
|
27
|
+
โ โโโ openapi.ts # dialect/envelope normalization
|
|
28
|
+
โ โโโ openapi-ref.ts # local JSON Pointer resolution
|
|
29
|
+
โ โโโ openapi-external-ref.ts # same-folder external $ref bundling (D-17)
|
|
30
|
+
โ โโโ openapi-to-api-docs.ts # operation collection
|
|
31
|
+
โ โโโ openapi-ir.ts # operation-level generation IR
|
|
32
|
+
โ โโโ openapi-contracts.ts # request/response extraction
|
|
33
|
+
โ โโโ api-docs-layout.ts # directory/flat path mapping
|
|
34
|
+
โ โโโ api-docs-plan.ts # collision-safe file planning
|
|
35
|
+
โ โโโ api-docs-facade.ts # ergonomic access-path helper
|
|
36
|
+
โ โโโ api-docs-generate.ts # โข rendering + guarded writes
|
|
37
|
+
โ โโโ openapi-to-api-docs-public.ts # public Engine โข wrapper
|
|
38
|
+
โ โโโ api-docs-presets.ts # split-generation bucket planner (S-92)
|
|
39
|
+
โ โโโ api-docs-navigation.ts # spec โ tree navigation index (S-93)
|
|
40
|
+
โ โโโ manifest-writer.ts # canonical manifest contract
|
|
41
|
+
โ โโโ manifest-staleness.ts # drift/ownership cleanup
|
|
42
|
+
โ โโโ manifest-to-openapi.ts # โฃ trusted import + reconstruction
|
|
43
|
+
โ โโโ reverse-security.ts # reverse security fallback
|
|
44
|
+
โโโ tests/ # ๐งช focused, integration, contract, round-trip
|
|
45
|
+
โโโ scripts/ # ๐ฃ coverage, golden, package, release gates
|
|
46
|
+
โโโ docs/ # ๐ this documentation
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`km-api@^0.4.1` is consumed from npm as a peer and development dependency
|
|
50
|
+
(D-15). Generated endpoint files and the golden typecheck use that published
|
|
51
|
+
surface directly; there is no vendored copy or package swap remaining.
|
|
52
|
+
|
|
53
|
+
YAML input (v0.2.x, D-16) is parsed by the owned, deterministic parser in
|
|
54
|
+
`src/conversions/yaml.ts` (YAML 1.2 core-schema scalars, block/flow
|
|
55
|
+
collections, quoted and block scalars, anchors/aliases/`<<` merge keys,
|
|
56
|
+
single-document streams). It stays pure (P-3) and adds no runtime dependency
|
|
57
|
+
(D-11); every rejection is a typed `ZOPIA_SPEC_INVALID_YAML`.
|
|
58
|
+
|
|
59
|
+
File-path inputs additionally resolve **same-folder external `$ref`s** before
|
|
60
|
+
normalization (v0.2.x, D-17): `src/conversions/openapi-external-ref.ts` bundles
|
|
61
|
+
references like `other.yaml#/pointer` (plus `other.json`/`other.yml`/`./โฆ`
|
|
62
|
+
spellings and whole-file targets) inline, with each sibling file read once
|
|
63
|
+
(P-1), bundled content deep-cloned, and nested cross-file references resolved
|
|
64
|
+
against their owning file. Targets outside the spec folder (URLs, absolute
|
|
65
|
+
paths, `../`, subdirectories) still fail with `ZOPIA_REF_EXTERNAL`, exactly
|
|
66
|
+
like external references in non-file inputs.
|
|
67
|
+
|
|
68
|
+
> ๐ Production modules use `kebab-case.ts`; `src/index.ts` is the package-root
|
|
69
|
+
> re-export surface. Focused and integration tests live under `tests/`, with
|
|
70
|
+
> dedicated `tests/contract/` and `tests/roundtrip/` suites. See
|
|
71
|
+
> [Standards โ Naming](12-standards.md#-naming).
|
|
72
|
+
|
|
73
|
+
## ๐ The pipeline
|
|
74
|
+
|
|
75
|
+
Conversion work is split between in-memory transforms and explicit adapters.
|
|
76
|
+
Engines โ /โก are in-memory; the public engine โข/โฃ wrappers own documented file
|
|
77
|
+
reads, guarded generated-tree writes, trusted module imports, and warning
|
|
78
|
+
collection. Process arguments/output remain in the CLI.
|
|
79
|
+
|
|
80
|
+
```mermaid
|
|
81
|
+
flowchart TB
|
|
82
|
+
subgraph IN ["โข OpenAPI โ api docs"]
|
|
83
|
+
A["object ยท JSON/YAML text ยท .json/.yaml path"] --> B["normalizeOpenApiDocument()"]
|
|
84
|
+
B --> C["collectOpenApiOperations()"]
|
|
85
|
+
C --> D["buildOpenApiOperationIR() + extractOperationContracts()"]
|
|
86
|
+
D --> E["planApiDocsFiles()"]
|
|
87
|
+
E --> F["render endpoints/components"]
|
|
88
|
+
F --> G["createZopiaManifest()"]
|
|
89
|
+
G --> H["guarded writes + stale-owned cleanup"]
|
|
90
|
+
H --> I["๐ api_docs/**"]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
subgraph OUT ["โฃ api docs โ OpenAPI"]
|
|
94
|
+
J["๐ api_docs/**"] --> K["validate manifest + owned paths"]
|
|
95
|
+
K --> L["trusted import of endpoint/component .ts"]
|
|
96
|
+
L --> M["runtime Zod โ schema serialization"]
|
|
97
|
+
M --> N["apply refs + manifest overlays"]
|
|
98
|
+
N --> O["dialect translation + canonical document"]
|
|
99
|
+
end
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The manifest boundary in `src/conversions/manifest-writer.ts` separates pure,
|
|
103
|
+
detached snapshot construction and canonical validation/serialization from
|
|
104
|
+
atomic filesystem output. Engine โข records source facts that generated Zod or
|
|
105
|
+
km-api values cannot carry; engine โฃ combines those snapshots with imported
|
|
106
|
+
runtime values so developer edits remain authoritative where representable.
|
|
107
|
+
|
|
108
|
+
Round-trip stability does not depend on one repository-wide `ApiModel`. The
|
|
109
|
+
implemented boundaries use a validated document envelope, an operation-level
|
|
110
|
+
IR for generation, normalized operation contracts, and the versioned manifest
|
|
111
|
+
for reverse reconstruction. Each shape is narrower than the stage that consumes
|
|
112
|
+
it, and fixture properties verify their composition (T-11/R-409).
|
|
113
|
+
|
|
114
|
+
## ๐งฌ The internal representations
|
|
115
|
+
|
|
116
|
+
The implementation uses stage-specific public shapes rather than one oversized
|
|
117
|
+
model. The generation path starts with the validated source envelope:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
interface NormalizedOpenApiDocument {
|
|
121
|
+
document: OpenApiDocument;
|
|
122
|
+
version: '2.0' | '3.0' | '3.1';
|
|
123
|
+
title?: string;
|
|
124
|
+
versionString?: string;
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Each collected operation is then narrowed to the data endpoint rendering needs:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
interface OpenApiOperationIR {
|
|
132
|
+
path: string;
|
|
133
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PATCH' | 'TRACE';
|
|
134
|
+
pathShape: string;
|
|
135
|
+
operationId: string;
|
|
136
|
+
summary?: string;
|
|
137
|
+
description?: string;
|
|
138
|
+
tags: string[];
|
|
139
|
+
deprecated: boolean;
|
|
140
|
+
security?: unknown[];
|
|
141
|
+
operation: Record<string, any>;
|
|
142
|
+
parameters: any[];
|
|
143
|
+
document: OpenApiDocument;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface OperationContracts {
|
|
147
|
+
parameters: Array<{
|
|
148
|
+
name: string;
|
|
149
|
+
in: 'path' | 'query' | 'header' | 'cookie';
|
|
150
|
+
required: boolean;
|
|
151
|
+
schema?: unknown;
|
|
152
|
+
}>;
|
|
153
|
+
requestBody?: { contentType: string; schema?: unknown; required: boolean };
|
|
154
|
+
responses: Array<{
|
|
155
|
+
status: string;
|
|
156
|
+
description: string;
|
|
157
|
+
contentType?: string;
|
|
158
|
+
schema?: unknown;
|
|
159
|
+
}>;
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Reverse conversion is anchored by `ZopiaManifest`, not by a hidden in-memory
|
|
164
|
+
model. It records source dialect/frame metadata, generated file ownership,
|
|
165
|
+
operation snapshots, component schemas, source path order/empty Path Items,
|
|
166
|
+
explicit schema-container presence, local-reference placements, and restoration
|
|
167
|
+
overlays. Current writer shapes are validated before serialization;
|
|
168
|
+
the reader retains explicit compatibility allowances for older optional fields.
|
|
169
|
+
|
|
170
|
+
### ๐ Canonical order (determinism, P-1)
|
|
171
|
+
|
|
172
|
+
| ๐ฆ Where | ๐ Order |
|
|
173
|
+
| --- | --- |
|
|
174
|
+
| collected operations | source path order; fixed method order `get, post, put, delete, head, options, patch, trace` |
|
|
175
|
+
| public generated-file result | lexical order by portable relative `path` |
|
|
176
|
+
| runtime schema properties | document order (JSON object key order of the source) |
|
|
177
|
+
| generated TypeScript schema maps/literal-object keys | lexical order (reverse restores source `required` order only while membership is unchanged) |
|
|
178
|
+
| OpenAPI output document | `openapi, info, servers, security, tags, paths, components, externalDocs` |
|
|
179
|
+
| path keys inside `paths` | sorted by path string |
|
|
180
|
+
|
|
181
|
+
> ๐ **Rule R-401** โ anywhere zopia *creates* a list or object, the order above
|
|
182
|
+
> applies. Anywhere zopia *mirrors* source data (runtime properties, params),
|
|
183
|
+
> the source order applies. Generated TypeScript canonicalizes schema-map and
|
|
184
|
+
> literal-object keys so parsing a canonical manifest cannot change the next
|
|
185
|
+
> generated tree. No `Date.now()`, no `Math.random()` anywhere in the package
|
|
186
|
+
> (P-1). Value-level normalizations (Zod sentinel bounds, const-union โ
|
|
187
|
+
> `enum`, the `io` input/output split for `required`/`default`) live with the
|
|
188
|
+
> engines that apply them โ see
|
|
189
|
+
> [Conversions โ R-615 / R-618 / R-654](06-conversions.md).
|
|
190
|
+
|
|
191
|
+
## ๐ The reference graph
|
|
192
|
+
|
|
193
|
+
`$ref` is a graph, and it can cycle (e.g. `Comment.replies โ Comment`):
|
|
194
|
+
|
|
195
|
+
```mermaid
|
|
196
|
+
flowchart LR
|
|
197
|
+
Post["๐ Post"] -->|"$.ref #/components/schemas/Author"| Author["๐ค Author"]
|
|
198
|
+
Author -->|"$.ref #/components/schemas/Address"| Address["๐ Address"]
|
|
199
|
+
Comment["๐ฌ Comment"] -->|"$.ref #/components/schemas/Comment"| Comment
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Implemented flow** (`openapi-ref.ts`, generation, and manifest modules):
|
|
203
|
+
|
|
204
|
+
1. ๐งท **Bundle external refs** (file inputs only, D-17) โ resolve same-folder
|
|
205
|
+
external references inline first so every later step sees one document.
|
|
206
|
+
2. ๐ **Preflight** โ walk source values, reject remaining external refs,
|
|
207
|
+
validate local pointer escapes, and report missing targets with exact
|
|
208
|
+
locations.
|
|
209
|
+
3. ๐ **Resolve operation refs** โ path-item and parameter chains use per-chain
|
|
210
|
+
seen sets, so malformed and circular non-schema references fail explicitly.
|
|
211
|
+
4. ๐งฉ **Collect component dependencies** โ rendering finds schema-component
|
|
212
|
+
targets while excluding literal/example data that merely contains `$ref` text.
|
|
213
|
+
5. ๐งต **Render schemas** โ engine โก's local-definition state and component
|
|
214
|
+
dependency reachability detect recursive edges; self and mutual cycles become
|
|
215
|
+
`z.lazy()` references.
|
|
216
|
+
6. ๐ฆ **Record identity** โ the manifest stores original reference placements;
|
|
217
|
+
reverse conversion combines those records with imported runtime schema
|
|
218
|
+
identity to restore local `$ref`s.
|
|
219
|
+
|
|
220
|
+
> ๐ **Rule R-402** โ circular schema components remain executable through
|
|
221
|
+
> `z.lazy()`, while linear refs become direct references/imports. **Scope:**
|
|
222
|
+
> graph nodes are schema components only. Reusable non-schema objects (Swagger
|
|
223
|
+
> 2.0 global `parameters`/`responses`, OpenAPI 3
|
|
224
|
+
> `components.parameters`/`responses`) *also* get their own component modules in
|
|
225
|
+
> components mode (v0.2.x โ D-18): a module holds only the declaration's derived
|
|
226
|
+
> schema, and in-source declarations plus use-site `$ref` placements restore
|
|
227
|
+
> verbatim on reverse conversion while the declaration refreshes from the
|
|
228
|
+
> current module. Bare `$ref` use sites import from the per-kind barrels; merged
|
|
229
|
+
> `$ref`-sibling forms still resolve at use sites for generated runtime configs.
|
|
230
|
+
|
|
231
|
+
## ๐งฎ Schema reuse within generated files
|
|
232
|
+
|
|
233
|
+
Default mode keeps every endpoint self-contained: each request/parameter/response
|
|
234
|
+
schema occurrence is rendered in place. Engine โก may build a local-definition
|
|
235
|
+
closure inside an expression when resolving `$defs` or inlined component refs;
|
|
236
|
+
it does not hoist structurally identical endpoint contracts into shared top-level
|
|
237
|
+
constants.
|
|
238
|
+
|
|
239
|
+
> ๐ **Rule R-403** โ schema reuse is explicit, not inferred from structural
|
|
240
|
+
> equality. Default mode independently inlines each contract occurrence. With
|
|
241
|
+
> `useComponentAsReference: true`, each referenced component export is imported
|
|
242
|
+
> at most once per endpoint and reused wherever that identity occurs.
|
|
243
|
+
|
|
244
|
+
## ๐ Error model
|
|
245
|
+
|
|
246
|
+
All errors crossing a zopia boundary extend one base class โ **no raw `Error`, no thrown strings** (standards โ Errors). `ZOPIA_ERROR_CODES` is the immutable runtime catalogue and the source of the `ZopiaErrorCode` union; `isZopiaError()` narrows unknown failures and `asZopiaError()` preserves an existing typed error or attaches a lower-level failure as `cause`.
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
export class ZopiaError extends Error {
|
|
250
|
+
/** ๐ Stable machine-readable code, e.g. 'ZOPIA_REF_NOT_FOUND'. */
|
|
251
|
+
readonly code: ZopiaErrorCode;
|
|
252
|
+
/** ๐ JSON-pointer, option name, or file location, when discoverable. */
|
|
253
|
+
readonly at?: string;
|
|
254
|
+
/** ๐ก Actionable, human-readable suggestion (always populated). */
|
|
255
|
+
readonly hint: string;
|
|
256
|
+
/** ๐ Original parser, import, or filesystem failure, when translated. */
|
|
257
|
+
readonly cause?: unknown;
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
| ๐ Code | ๐ Where | ๐ฅ When | ๐ก Hint pattern |
|
|
262
|
+
| --- | --- | --- | --- |
|
|
263
|
+
| `ZOPIA_CONFIG_INVALID` | public options / CLI | an argument, option, or option combination is invalid | "correct the invalid option or argument" |
|
|
264
|
+
| `ZOPIA_DOCS_IMPORT_FAILED` | engine โฃ | generated modules cannot load, export one expected value, or serialize edited runtime schemas | "fix or regenerate the affected generated module" |
|
|
265
|
+
| `ZOPIA_DOCS_MANIFEST_MISMATCH` | engine โฃ preflight | a manifest-owned endpoint/component file is missing, renamed, or not a regular file | "regenerate the tree or restore its generated files" |
|
|
266
|
+
| `ZOPIA_DOCS_MISSING_MANIFEST` | engine โฃ entry | no `.zopia-manifest.json` exists at the selected path | "generate api docs first or pass the manifest path" |
|
|
267
|
+
| `ZOPIA_FS_OUTSIDE_OUTDIR` | generation guard | a generated path escapes `outDir` or traverses an unsafe ancestor | "keep generated paths inside the output directory" |
|
|
268
|
+
| `ZOPIA_FS_WRITE_FAILED` | writers / CLI | output inspection, directory creation, cleanup, or writing fails | "check the output path, permissions, and available disk space" |
|
|
269
|
+
| `ZOPIA_MANIFEST_INVALID` | manifest writer/reader | manifest JSON or metadata violates `zopia:manifest@1` | "regenerate the manifest or fix its invalid metadata" |
|
|
270
|
+
| `ZOPIA_REF_EXTERNAL` | external-ref bundling / reference validation | `$ref` escapes the spec folder, or a non-file input points to another file | "keep external targets next to the spec file" |
|
|
271
|
+
| `ZOPIA_REF_NOT_FOUND` | reference validation | a local `$ref` is malformed, circular where unsupported, or unresolved | "check that the local JSON Pointer target exists" |
|
|
272
|
+
| `ZOPIA_SCHEMA_INVALID` | engines โ /โก | the Zod or JSON Schema input cannot be converted | "provide a valid Zod or JSON Schema value" |
|
|
273
|
+
| `ZOPIA_SPEC_INVALID` | OpenAPI validation | the parsed document violates the supported Swagger/OpenAPI shape | "fix the invalid Swagger/OpenAPI document" |
|
|
274
|
+
| `ZOPIA_SPEC_INVALID_JSON` | JSON entry points | source text is unreadable or not valid JSON | "provide readable, valid JSON" |
|
|
275
|
+
| `ZOPIA_SPEC_INVALID_YAML` | YAML entry points | source text is unreadable, malformed/unsupported YAML, or holds a non-JSON value | "provide readable, valid YAML" |
|
|
276
|
+
| `ZOPIA_SPEC_MISSING_PATHS` | normalizers | the document has no object-valued `paths` | "add a paths object" |
|
|
277
|
+
| `ZOPIA_SPEC_PATH_REF` | operation collection | a path-item reference is invalid or circular | "use a valid local path-item reference" |
|
|
278
|
+
| `ZOPIA_SPEC_UNSUPPORTED_VERSION` | normalization | neither Swagger 2.0 nor OpenAPI 3.0/3.1 is selected | "use Swagger 2.0, OpenAPI 3.0, or OpenAPI 3.1" |
|
|
279
|
+
| `ZOPIA_WARNING_INVALID` | warnings pipeline | a warning iterable/code/location/message is malformed | "provide a valid warning code, location, and message" |
|
|
280
|
+
|
|
281
|
+
> ๐ **Rule R-404** โ every error is thrown as a `ZopiaError` with a stable
|
|
282
|
+
> code, a location (`at`) when discoverable, and a `hint`. Tests assert on
|
|
283
|
+
> `code`, never on message text.
|
|
284
|
+
|
|
285
|
+
## โ ๏ธ Warning model
|
|
286
|
+
|
|
287
|
+
Warnings are non-fatal conversion diagnostics. Every public engine uses the
|
|
288
|
+
same `ZopiaWarning` contract and stable `ZopiaWarningCode` union:
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
interface ZopiaWarning {
|
|
292
|
+
code: ZopiaWarningCode;
|
|
293
|
+
at?: string; // escaped RFC 6901 JSON Pointer, including leading #
|
|
294
|
+
message: string;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
| ๐ Stable code | ๐ Meaning |
|
|
299
|
+
| --- | --- |
|
|
300
|
+
| `ZOPIA_WARN_UNREPRESENTABLE` | a Zod node cannot be represented in the selected schema dialect |
|
|
301
|
+
| `ZOPIA_WARN_INVALID_SCHEMA` | a malformed JSON Schema keyword is ignored or approximated |
|
|
302
|
+
| `ZOPIA_WARN_CUSTOM_FORMAT`, `ZOPIA_WARN_CONTENT_ENCODING`, `ZOPIA_WARN_INT64` | a string/numeric format or encoding has no exact runtime equivalent |
|
|
303
|
+
| `ZOPIA_WARN_LEGACY_EXCLUSIVE_BOUND` | a legacy boolean exclusive bound requires normalization |
|
|
304
|
+
| `ZOPIA_WARN_ONE_OF`, `ZOPIA_WARN_NOT`, `ZOPIA_WARN_UNIQUE_ITEMS`, `ZOPIA_WARN_FROZEN_SUBTREE` | an applicator or refinement needs an approximation or frozen manifest restoration |
|
|
305
|
+
| `ZOPIA_WARN_REF` | a recoverable schema-reference conversion cannot be exact |
|
|
306
|
+
| `ZOPIA_WARN_MULTI_CONTENT`, `ZOPIA_WARN_SERVER_VARIABLES`, `ZOPIA_WARN_WEBHOOKS` | an OpenAPI document fact has no direct generated-code representation |
|
|
307
|
+
| `ZOPIA_WARN_STALE_TREE` | regeneration found source/config drift, missing owned files, manifest disablement, or invalid existing metadata |
|
|
308
|
+
| `ZOPIA_WARN_DEFAULT_INFO`, `ZOPIA_WARN_DEFAULT_SECURITY` | reverse conversion synthesized documented fallback metadata or security |
|
|
309
|
+
| `ZOPIA_WARN_DIALECT_DOWNGRADE` | OpenAPI 3.1-only content is omitted from 3.0 output |
|
|
310
|
+
|
|
311
|
+
The shared collector validates codes, collapses line breaks in messages,
|
|
312
|
+
deduplicates identical diagnostics, and sorts by location, code, then message.
|
|
313
|
+
Nested engine warnings are rebased rather than string-concatenated ad hoc, so
|
|
314
|
+
`at` always identifies the affected source or output node. Engine โ and engine
|
|
315
|
+
โฃ invoke `onWarning` once per normalized warning; engines โกโโฃ also return
|
|
316
|
+
normalized warning arrays. Engine โก mirrors losses with the canonical marker
|
|
317
|
+
`// @zopia:warn CODE subject โ message (pointer)`. The CLI renders the same
|
|
318
|
+
warning as `Warning: CODE pointer: message` on **stderr**, leaving reverse JSON
|
|
319
|
+
on stdout parseable.
|
|
320
|
+
|
|
321
|
+
## ๐ก๏ธ Safety & boundaries
|
|
322
|
+
|
|
323
|
+
| # | Rule | Where enforced |
|
|
324
|
+
| --- | --- | --- |
|
|
325
|
+
| R-405 | **Pure core** โ schema/operation transforms work on in-memory values; documented file input, generated-tree writes/imports, and process output stay in public adapters and the CLI | conversion modules + `cli-command.ts` |
|
|
326
|
+
| R-406 | **outDir guard** โ every generated path is canonicalized and verified to stay inside `outDir`; regeneration refuses symlinked path ancestors, and manifest temporary writes use exclusive creation so stale symlinks cannot redirect output. Obsolete cleanup trusts only a fully validated manifest and never recursively deletes an output root | `api-docs-generate.ts` + manifest boundaries |
|
|
327
|
+
| R-407 | **Trusted-input contract** โ engine โฃ imports generated `.ts` files (executes them). This is by design (D-08) and only for trees that carry a valid zopia manifest | `manifest-to-openapi.ts` |
|
|
328
|
+
| R-408 | **No silent loss** โ every lossy/unsupported conversion produces a normalized `ZopiaWarning` (D-12): `{ code, at?, message }` (shape fixed by R-144). Public wrappers return or callback each warning; engine โก and generated api-doc files mirror schema warnings as canonical `// @zopia:warn โฆ` comments; CLI diagnostics go only to stderr | every engine + CLI |
|
|
329
|
+
| R-409 | **Idempotent regeneration** โ re-running engine โข with identical input + options produces byte-identical output; regenerating source-preserving engine โฃ output does too. Fixture properties cover Swagger 2.0 and OpenAPI 3.0/3.1 across layout/component modes, while stale source/config/incomplete-tree state is warned and repaired and only obsolete manifest-owned files are pruned; engine โฃ output is canonical (R-401) | round-trip + staleness tests |
|
|
330
|
+
|
|
331
|
+
## ๐ Performance
|
|
332
|
+
|
|
333
|
+
- ๐งฎ Operation and local-reference passes are deterministic traversals with
|
|
334
|
+
explicit seen sets for reference chains.
|
|
335
|
+
- ๐ณ Component source is rendered and validated before its files are written;
|
|
336
|
+
endpoint files are then rendered and written in planned order.
|
|
337
|
+
- ๐งต Cycle-aware definition/reachability state terminates recursive schemas and
|
|
338
|
+
emits lazy edges rather than expanding them forever.
|
|
339
|
+
- ๐ฆ Optional component extraction/reference imports avoid repeated component
|
|
340
|
+
definitions; default mode deliberately favors self-contained endpoint files.
|
|
341
|
+
|
|
342
|
+
## ๐ Next
|
|
343
|
+
|
|
344
|
+
- ๐งฉ Vocabulary used above โ [Concepts](05-concepts.md)
|
|
345
|
+
- ๐ Engine-by-engine rules โ [Conversions](06-conversions.md)
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# ๐งฉ Concepts & Glossary
|
|
2
|
+
|
|
3
|
+
The vocabulary of this project, pinned to exact versions. If a term is used in
|
|
4
|
+
any other zopia document, it means what it means here.
|
|
5
|
+
|
|
6
|
+
## ๐ The big picture
|
|
7
|
+
|
|
8
|
+
```mermaid
|
|
9
|
+
flowchart TB
|
|
10
|
+
S1["๐ Swagger 2.0<br/>(2016 spec)"] ---|dialect| JS1["๐ JSON Schema draft-04-ish"]
|
|
11
|
+
S2["๐ OpenAPI 3.0"] ---|dialect| JS2["๐ JSON Schema draft-07 subset<br/>+ nullable"]
|
|
12
|
+
S3["๐ OpenAPI 3.1"] ---|dialect| JS3["๐ JSON Schema 2020-12 (identical)"]
|
|
13
|
+
JS3 ---|same format| Z1["โ๏ธ Zod v4 schemas"]
|
|
14
|
+
S2 --> C["๐ api docs<br/>(.ts ยท km-api ยท zod v4)"]
|
|
15
|
+
S1 --> C
|
|
16
|
+
S3 --> C
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
> ๐ก **Key fact** โ OpenAPI 3.1's *Schema Object* **is** JSON Schema 2020-12
|
|
20
|
+
> ("the standards of openapi are the same as json schema"). That identity is
|
|
21
|
+
> what lets zopia treat 3.1 schemas and JSON Schema as one format.
|
|
22
|
+
|
|
23
|
+
## ๐ Terms
|
|
24
|
+
|
|
25
|
+
### ๐ Swagger 2.0
|
|
26
|
+
|
|
27
|
+
The 2016 API description format. Recognized by the top-level key
|
|
28
|
+
`"swagger": "2.0"`.
|
|
29
|
+
|
|
30
|
+
| ๐งฉ Piece | ๐ In Swagger 2.0 |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| ๐ Schemas | `definitions` (referenced as `#/definitions/X`) |
|
|
33
|
+
| ๐ Hosting | `host` + `basePath` + `schemes` |
|
|
34
|
+
| ๐ฆ Request bodies | a special parameter with `"in": "body"` (has `schema`) |
|
|
35
|
+
| ๐งพ Form data | parameters with `"in": "formData"` (media type from `consumes`) |
|
|
36
|
+
| ๐ช Cookie parameters | โ none (introduced in OpenAPI 3.0) |
|
|
37
|
+
| ๐ Security | `securityDefinitions` โ mapped to OpenAPI 3 `securitySchemes` |
|
|
38
|
+
| ๐ Schema dialect | draft-04-ish subset (no `nullable`, no `const`, โฆ) |
|
|
39
|
+
| ๐ฆ Responses | `responses` map โ each response may carry a single `schema` |
|
|
40
|
+
|
|
41
|
+
### ๐ OpenAPI 3.0
|
|
42
|
+
|
|
43
|
+
Recognized by `"openapi": "3.0.x"`.
|
|
44
|
+
|
|
45
|
+
| ๐งฉ Piece | ๐ In OpenAPI 3.0 |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| ๐ Schemas | `components.schemas` (referenced as `#/components/schemas/X`) |
|
|
48
|
+
| ๐ Hosting | `servers: [{ url, variables? }]` |
|
|
49
|
+
| ๐ฆ Request bodies | dedicated `requestBody` object with a `content` map (media type โ schema) |
|
|
50
|
+
| ๐ช Cookie parameters | โ
`"in": "cookie"` |
|
|
51
|
+
| ๐ Nullable | `nullable: true` keyword on schemas |
|
|
52
|
+
| ๐ Schema dialect | draft-07 **subset** + OpenAPI extensions (`nullable`, `discriminator`, `example`, `deprecated`, โฆ) |
|
|
53
|
+
|
|
54
|
+
### ๐ OpenAPI 3.1
|
|
55
|
+
|
|
56
|
+
Recognized by `"openapi": "3.1.x"`. Same shape as 3.0, but:
|
|
57
|
+
|
|
58
|
+
- ๐ Schema dialect = **JSON Schema 2020-12** (full equality)
|
|
59
|
+
- ๐ Nullability via `"type": ["string", "null"]` โ `nullable` is removed
|
|
60
|
+
- ๐ธ `examples` (array) replaces `example`; `const` is allowed
|
|
61
|
+
- ๐งฎ `exclusiveMinimum/Maximum` are **numbers** (were booleans in draft-04/07)
|
|
62
|
+
- ๐ช adds `webhooks` (not paths): v0.1.0 warns, preserves them in the manifest,
|
|
63
|
+
and restores them for 3.1 output but defers webhook endpoint files; valid local
|
|
64
|
+
path-item `$ref`s are supported and round-trip
|
|
65
|
+
- ๐ zopia treats 3.0 and 3.1 with the same normalizer + a small dialect shim
|
|
66
|
+
|
|
67
|
+
### ๐ JSON Schema
|
|
68
|
+
|
|
69
|
+
The vocabulary used to describe data shape (`type`, `properties`, `required`,
|
|
70
|
+
`enum`, `oneOf`, โฆ). zopia's engine โก accepts the union of keywords found in
|
|
71
|
+
the three dialects above and emits warnings for anything it cannot represent
|
|
72
|
+
in Zod (D-12). The canonical dialect of engine โ 's output is **2020-12**.
|
|
73
|
+
|
|
74
|
+
### ๐ $ref (reference)
|
|
75
|
+
|
|
76
|
+
A pointer like `"#/components/schemas/User"` that makes one schema *use*
|
|
77
|
+
another. Three shapes matter for zopia:
|
|
78
|
+
|
|
79
|
+
```jsonc
|
|
80
|
+
// 1๏ธโฃ operation โ component (responses, requestBody, parameters)
|
|
81
|
+
{ "schema": { "$ref": "#/components/schemas/User" } }
|
|
82
|
+
|
|
83
|
+
// 2๏ธโฃ component โ component (a part of another component)
|
|
84
|
+
{ "properties": { "address": { "$ref": "#/components/schemas/Address" } } }
|
|
85
|
+
|
|
86
|
+
// 3๏ธโฃ component โ itself (cycle โ always allowed, becomes z.lazy())
|
|
87
|
+
{ "properties": { "replies": { "type": "array", "items": { "$ref": "#/components/schemas/Comment" } } } }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
> ๐ **Rule R-501** โ zopia resolves *internal* refs natively (refs stay
|
|
91
|
+
> inside the bundled document). Spec file paths additionally bundle
|
|
92
|
+
> **same-folder** external refs before processing (D-17); everything else โ
|
|
93
|
+
> URLs, other folders, external refs in non-file inputs โ stays a
|
|
94
|
+
> `ZOPIA_REF_EXTERNAL` error.
|
|
95
|
+
|
|
96
|
+
### โ๏ธ Zod v4
|
|
97
|
+
|
|
98
|
+
The schema library (validation at runtime + types at compile time). zopia
|
|
99
|
+
depends on Zod **v4** idioms โ *not* v3 โ throughout:
|
|
100
|
+
|
|
101
|
+
| ๐งฉ v4 feature | ๐ Used by zopia |
|
|
102
|
+
| --- | --- |
|
|
103
|
+
| `z.toJSONSchema(schema, { target })` | engine โ โ built-in, no third-party converter (D-03); targets: `draft-2020-12` (default), `draft-07`, `draft-04`, `openapi-3.0` |
|
|
104
|
+
| `z.fromJSONSchema(schema)` | โ ๏ธ experimental in Zod โ used **only** as a cross-check in tests, never in the output path (D-04) |
|
|
105
|
+
| top-level format schemas | `z.email()`, `z.uuid()`, `z.url()`, `z.hostname()`, `z.ipv4()`, `z.ipv6()` โ โ ๏ธ each emits `format` **plus a strict `pattern`** (engine โ strips the redundant pair โ R-618); `z.url()` emits `format: "uri"` |
|
|
106
|
+
| ISO builders | `z.iso.datetime()`, `z.iso.date()`, `z.iso.time()`, `z.iso.duration()` โ โ ๏ธ `z.iso.time()` emits a pattern but **no `format` key** (round-trip needs the manifest overlay โ R-635) |
|
|
107
|
+
| *(no v4 API for arbitrary formats)* | custom `format` values โ `z.string()` + warning + manifest overlay preserving the format verbatim (R-627) |
|
|
108
|
+
| `z.enum([...])`, `z.literal(v)` | `enum` / `const` keywords |
|
|
109
|
+
| `z.union([...])`, `z.discriminatedUnion(key, [...])` | `oneOf` / `anyOf` (with the `discriminator` heuristic) |
|
|
110
|
+
| `z.intersection(a, b)` | `allOf` |
|
|
111
|
+
| `z.tuple([...])` | `prefixItems` / tuple `items` |
|
|
112
|
+
| `z.lazy(() => โฆ)` | circular `$ref`s (R-402) |
|
|
113
|
+
| `.meta({ title, description, examples, โฆ })` / `z.globalRegistry` | **all** metadata fields are copied verbatim into the JSON Schema output (verified); โ ๏ธ never set the `id` key โ it triggers `$def` extraction |
|
|
114
|
+
| `z.object({โฆ})` + `.strict()` / `.catchall(s)` / `.optional()` / `.default(v)` | objects, `additionalProperties`, `required`, `default` |
|
|
115
|
+
| `.min()` / `.max()` / `.int()` / `.regex()` / `.multipleOf()` | string/number/array constraints |
|
|
116
|
+
|
|
117
|
+
### ๐งฑ km-api
|
|
118
|
+
|
|
119
|
+
The user's endpoint-definition package โ **the make function of this project's
|
|
120
|
+
generated code**. zopia targets **km-api `^0.4.1` (0.4.x)** and generates the
|
|
121
|
+
following conceptual shape (formatting abridged):
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { makeApiConfig } from 'km-api';
|
|
125
|
+
|
|
126
|
+
const getUser = makeApiConfig({
|
|
127
|
+
method: 'GET', // ๐งญ IMethod โ HTTP method
|
|
128
|
+
pathShape: '/admin/users/{id}', // ๐ฃ๏ธ IPath โ OpenAPI {param} syntax (D-05)
|
|
129
|
+
operationId: 'getUser', // ๐ preserved in config + export name
|
|
130
|
+
auth: 'YES', // ๐ 'YES' | 'NO'
|
|
131
|
+
responseContentType: 'application/json',
|
|
132
|
+
summary: 'Get user by ID', // ๐
|
|
133
|
+
description: 'Retrieves โฆ', // ๐ (Markdown)
|
|
134
|
+
tags: ['#admin', '#users'], // ๐ท๏ธ km-api convention: '#' prefix
|
|
135
|
+
request: {
|
|
136
|
+
body: z.any(), // ๐ฆ required field โ z.any() โ no body
|
|
137
|
+
params: z.object({ id: z.uuid() }), // ๐ฃ๏ธ path params
|
|
138
|
+
query: z.object({}), // โ
|
|
139
|
+
headers: z.object({}), // ๐ฉ
|
|
140
|
+
cookies: z.object({}), // ๐ช
|
|
141
|
+
},
|
|
142
|
+
response: { // ๐ฆ status code โ Zod schema
|
|
143
|
+
200: z.object({ id: z.uuid(), name: z.string() }),
|
|
144
|
+
404: z.object({ message: z.string() }),
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Helper methods on the result (`makeFullPath`, `makeOpenApiPathShape`,
|
|
150
|
+
`convertResponseType`, โฆ) are available to consumers for free โ zopia never
|
|
151
|
+
needs them at generation time, and engine โฃ uses `makeOpenApiPathShape()` to
|
|
152
|
+
normalize paths back to `{param}` form.
|
|
153
|
+
|
|
154
|
+
> ๐ **Rule R-502** โ generated files import **only** `zod` and `km-api`.
|
|
155
|
+
> No zopia runtime is imported by generated code. Component files are
|
|
156
|
+
> generated as standalone files, and exact endpoint component references can
|
|
157
|
+
> import through the barrel. Nested component references are recursively rendered, including nested objects, arrays, compositions, nullable schemas, enums, constants, and additional-property schemas. Direct and mutual cyclic component graphs use lazy Zod references.
|
|
158
|
+
|
|
159
|
+
### ๐ km-api's type surface (verified against published `0.4.1`)
|
|
160
|
+
|
|
161
|
+
`makeApiConfig` is a **type-level factory** โ it does no runtime validation, so
|
|
162
|
+
the generated tree's contract is that it **typechecks** against the installed
|
|
163
|
+
published package (D-14; the golden-tree contract test enforces this, R-126):
|
|
164
|
+
|
|
165
|
+
| ๐งฉ Field | ๐ Published km-api 0.4.1 / zopia contract |
|
|
166
|
+
| --- | --- |
|
|
167
|
+
| `method` (`IMethod`) | all **8** standard methods โ `get/post/put/delete/head/options/patch/trace` (each case-insensitive: `GET`, `Get`, โฆ) |
|
|
168
|
+
| `response` keys | standard statuses, **any valid custom `100`โ`599` code** (`419`, `499`, `512`, โฆ), OpenAPI `1XX`โ`5XX` ranges, and the **`default`** key; all are pinned by generated compilation and validated against the source/output dialect |
|
|
169
|
+
| `responseContentType` / `requestContentType` | km-api's declarations enumerate known MIME values, while OpenAPI media-type maps are open; zopia emits every exact string and applies a narrow type-only assertion at this package boundary, preserving the runtime value and reverse conversion |
|
|
170
|
+
| `operationId` | any string โ a real km-api field in 0.4.1; zopia emits it and reuses it as the export identifier (R-732) |
|
|
171
|
+
|
|
172
|
+
Other fixed shapes (source-verified): `ITags` = strings **prefixed with `#`**;
|
|
173
|
+
`IPath` = string starting with `/`; `auth`, `disable`, and the semantically
|
|
174
|
+
separate `deprecated` field use `'YES' | 'NO'`; `request.body` is required (any
|
|
175
|
+
Zod schema), and `params/query/headers/cookies` are required Zod objects. The
|
|
176
|
+
remaining km-api gaps โ **per-parameter metadata**
|
|
177
|
+
(`style`, `explode`, `allowEmptyValue`, `deprecated`, `example`) and **response
|
|
178
|
+
`headers`** โ have no home in km-api; zopia preserves them in the manifest
|
|
179
|
+
(overlay / `responseOverlay`, R-635/R-754).
|
|
180
|
+
|
|
181
|
+
> ๐ **Where km-api lives.** zopia consumes the published `km-api@^0.4.1`
|
|
182
|
+
> npm package. No Git submodule or unpublished commit is required (D-15).
|
|
183
|
+
|
|
184
|
+
### ๐ api docs
|
|
185
|
+
|
|
186
|
+
The generated artifact: an `api_docs/` directory containing one `index.ts` per
|
|
187
|
+
endpoint (plus optional `components/**` and the manifest). Defined fully in
|
|
188
|
+
[API docs format](07-api-docs.md).
|
|
189
|
+
|
|
190
|
+
### ๐ฆ .zopia-manifest.json
|
|
191
|
+
|
|
192
|
+
The hidden metadata file written into `api_docs/` (D-06). It is what makes
|
|
193
|
+
engine โฃ lossless: exact paths & methods per file, spec identity
|
|
194
|
+
(kind/title/version/sha256), servers, tags, security schemes, the **full
|
|
195
|
+
component schemas**, **`$ref` placement** (per-API ref pointers), and
|
|
196
|
+
**non-representable facts** (overlay entries: titles, examples, custom
|
|
197
|
+
formats, unsupported keywords). See
|
|
198
|
+
[API docs format โ The manifest](07-api-docs.md).
|
|
199
|
+
|
|
200
|
+
### ๐งฌ Stage-specific representations
|
|
201
|
+
|
|
202
|
+
Engine โข narrows a validated document into operation-level IR and normalized
|
|
203
|
+
request/response contracts. Engine โฃ combines imported runtime values with the
|
|
204
|
+
versioned manifest instead of sharing one repository-wide model with engine โข.
|
|
205
|
+
See [Architecture โ The internal representations](04-architecture.md#-the-internal-representations).
|
|
206
|
+
|
|
207
|
+
### ๐ Round-trip
|
|
208
|
+
|
|
209
|
+
For a spec `S`: `openApiToApiDocs(S) โ apiDocsToOpenApi(...) โ S` โ equal after
|
|
210
|
+
**canonicalization** (R-401 order, whitespace-independent JSON equality, and
|
|
211
|
+
documented metadata moves to the manifest). Round-trips are *tested as
|
|
212
|
+
properties*, not by eye (see [Testing](11-testing.md#-round-trip-property-tests)).
|
|
213
|
+
|
|
214
|
+
## โ๏ธ Dialect comparison (the table that answers most "why?" questions)
|
|
215
|
+
|
|
216
|
+
| ๐งฉ Concern | Swagger 2.0 | OpenAPI 3.0 | OpenAPI 3.1 |
|
|
217
|
+
| --- | --- | --- | --- |
|
|
218
|
+
| ๐ท๏ธ Version key | `swagger: "2.0"` | `openapi: "3.0.x"` | `openapi: "3.1.x"` |
|
|
219
|
+
| ๐ Schema home | `definitions` | `components.schemas` | `components.schemas` |
|
|
220
|
+
| ๐ Schema dialect | draft-04-ish | draft-07 subset + extensions | **JSON Schema 2020-12** |
|
|
221
|
+
| ๐ Servers | `host`+`basePath`+`schemes` | `servers[]` | `servers[]` |
|
|
222
|
+
| ๐ฆ Body | param `in: body` | `requestBody.content` | `requestBody.content` |
|
|
223
|
+
| ๐งพ FormData | param `in: formData` | `requestBody.content['multipart/โฆ' \| 'x-www-form-urlencoded']` | same as 3.0 |
|
|
224
|
+
| ๐ช Cookie params | โ | โ
| โ
|
|
|
225
|
+
| ๐ Nullable | โ no standard way (draft-04 allows type arrays, but 2.0 tooling rarely supports them) | `nullable: true` | `type: ["โฆ", "null"]` |
|
|
226
|
+
| ๐ธ Examples | `examples` (media-type map, legacy) | `example` (single) | `examples` (array) |
|
|
227
|
+
| ๐ Security | `securityDefinitions` | `components.securitySchemes` | same as 3.0 |
|
|
228
|
+
| ๐ฆ Response schema | response.schema (single) | per media type | per media type |
|
|
229
|
+
| ๐งฎ exclusiveMinimum | boolean | boolean | **number** |
|
|
230
|
+
|
|
231
|
+
> ๐ **Rule R-503** โ the normalizers convert *everything* into the IR using
|
|
232
|
+
> the **OpenAPI-3.1-flavoured** representation (2020-12 schemas, numeric
|
|
233
|
+
> exclusive bounds, `type: [t, "null"]` for nullables). Dialect differences
|
|
234
|
+
> die at the IR boundary; they never leak into generated code.
|
|
235
|
+
|
|
236
|
+
## ๐ Next
|
|
237
|
+
|
|
238
|
+
- ๐ How the IR is produced and consumed โ [Conversions](06-conversions.md)
|
|
239
|
+
- ๐๏ธ Where the IR lives in code โ [Architecture](04-architecture.md)
|