zopia 0.3.0 โ 0.4.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 +40 -0
- package/README.md +48 -16
- package/docs/07-api-docs.md +77 -2
- package/docs/09-configuration.md +2 -2
- package/docs/10-usage.md +26 -3
- package/package.json +10 -2
- package/src/conversions/api-docs-generate.ts +6 -12
- package/src/conversions/api-docs-names.ts +54 -0
- package/src/conversions/manifest-writer.ts +1 -1
- package/src/conversions/openapi-to-api-docs.ts +5 -10
- package/src/runtime/create-api-docs.ts +321 -0
- package/src/runtime.ts +10 -0
- package/docs/01-overview.md +0 -94
- package/docs/02-targets.md +0 -55
- package/docs/03-roadmap.md +0 -205
- package/docs/04-architecture.md +0 -345
- package/docs/05-concepts.md +0 -239
- package/docs/06-conversions.md +0 -493
- package/docs/08-components.md +0 -223
- package/docs/11-testing.md +0 -267
- package/docs/12-standards.md +0 -242
- package/docs/README.md +0 -42
- package/docs/publish-workflow.yml.example +0 -48
package/docs/03-roadmap.md
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
# ๐บ๏ธ Roadmap
|
|
2
|
-
|
|
3
|
-
zopia ships in phases. **Phase 0 is this documentation set** โ the contracts
|
|
4
|
-
are fixed before a line of code is written, so implementation never re-decides
|
|
5
|
-
anything.
|
|
6
|
-
|
|
7
|
-
## ๐ฆ Phase 0 โ Documentation & standards โ
|
|
8
|
-
|
|
9
|
-
The standard documentation of the project:
|
|
10
|
-
|
|
11
|
-
- ๐ `docs/` โ overview, targets, roadmap, architecture, concepts, conversions,
|
|
12
|
-
api-docs format, components, configuration, usage, testing, standards
|
|
13
|
-
- ๐ `README.md` โ project home with the documentation map
|
|
14
|
-
- ๐ `CHANGELOG.md` โ Keep-a-Changelog format + the "changelog after commits" rule
|
|
15
|
-
- ๐ `LICENSE` (MIT) ยท ๐งน `.gitignore`
|
|
16
|
-
- ๐ Key decisions **D-01 โฆ D-15** recorded in [Standards](12-standards.md#-key-decisions)
|
|
17
|
-
|
|
18
|
-
**Completed:** the documentation fixed the Phase 1 contracts before
|
|
19
|
-
implementation; it now describes the implemented v0.1.0 behavior and deferred scope.
|
|
20
|
-
|
|
21
|
-
## ๐ Phase 1 โ The four engines (v0.1.0) โ
|
|
22
|
-
|
|
23
|
-
**Released:** 2026-09-28. Everything in [Targets](02-targets.md) is marked โ
:
|
|
24
|
-
|
|
25
|
-
- [x] โ๏ธ Package scaffold โ ESM `package.json` (Bun-first, zero direct runtime
|
|
26
|
-
dependencies; `zod` + `km-api ^0.4.1` peers from npm โ D-15), strict `tsconfig`,
|
|
27
|
-
Vitest/V8, pinned Bun, and checked-in `bun.lock`
|
|
28
|
-
- [x] โ **Engine 1** โ `zodToJsonSchema()` on top of `z.toJSONSchema()`
|
|
29
|
-
([rules](06-conversions.md))
|
|
30
|
-
- [x] โก **Engine 2** โ `jsonSchemaToZod()` recursive emitter
|
|
31
|
-
([rules](06-conversions.md))
|
|
32
|
-
- [x] โข **Engine 3** โ `openApiToApiDocs()` โ normalize v2/v3 โ operation IR
|
|
33
|
-
โ render `directory` / `flat` trees of `index.ts` files
|
|
34
|
-
([rules](06-conversions.md),
|
|
35
|
-
[format](07-api-docs.md))
|
|
36
|
-
- [x] โฃ **Engine 4** โ `apiDocsToOpenApi()` โ manifest-driven reverse
|
|
37
|
-
conversion to OpenAPI 3.0/3.1
|
|
38
|
-
([rules](06-conversions.md))
|
|
39
|
-
- [x] ๐งฑ Component options โ `insertComponents`, `useComponentAsReference`,
|
|
40
|
-
nested references, and direct/mutual cycles ([rules](08-components.md))
|
|
41
|
-
- [x] ๐ **YAML input** โ owned deterministic YAML 1.2 core-schema parser,
|
|
42
|
-
`.yaml`/`.yml` paths and inline YAML text, anchors/aliases/merge keys,
|
|
43
|
-
stable `ZOPIA_SPEC_INVALID_YAML` ([rules](06-conversions.md#engine-โข--openapi--api-docs), D-16)
|
|
44
|
-
- [x] ๐ **External `$ref`s** โ same-folder references bundled inline for
|
|
45
|
-
spec file paths, byte-identical to inline twins ([rules](06-conversions.md#engine-โข--openapi--api-docs), D-17)
|
|
46
|
-
- [x] ๐ฆ Manifest writer/reader โ `.zopia-manifest.json` (D-06)
|
|
47
|
-
- [x] โ ๏ธ **Warnings pipeline** โ stable typed codes, exact JSON Pointer locations,
|
|
48
|
-
deterministic collection/callbacks, generated-code markers, and CLI stderr
|
|
49
|
-
reporting across engines โ โโฃ (R-144/R-408/D-12)
|
|
50
|
-
- [x] โป๏ธ **Manifest staleness** โ canonical source/config comparison, invalid and
|
|
51
|
-
incomplete-tree detection, source-located warnings, safe obsolete-artifact
|
|
52
|
-
pruning, and symlink-safe regeneration (R-741โฆR-743)
|
|
53
|
-
- [x] ๐ **Reverse security fallback** โ manifest-authoritative requirements,
|
|
54
|
-
validated metadata, collision-safe deterministic bearer synthesis,
|
|
55
|
-
OpenAPI/Swagger representations, and operation-located warnings (R-656)
|
|
56
|
-
- [x] ๐ **Typed errors** โ one immutable stable-code catalogue and `ZopiaError`
|
|
57
|
-
boundary across engines, manifests, generated-module imports, filesystem
|
|
58
|
-
operations, warnings, and CLI validation, with locations, hints, and causes
|
|
59
|
-
(R-141โฆR-143/R-404)
|
|
60
|
-
- [x] ๐ **Round-trip contract** โ fixture-backed source-dialect identity across
|
|
61
|
-
Swagger 2.0 and OpenAPI 3.0/3.1, every layout/component mode, refs/cycles,
|
|
62
|
-
lossy-keyword overlays, path-item metadata, and byte-identical regeneration;
|
|
63
|
-
supported Zod โ JSON Schema pipelines converge after canonicalization
|
|
64
|
-
(T-10/T-11/R-409)
|
|
65
|
-
- [x] โจ๏ธ **CLI contract** โ strict `generate` / `reverse` grammar, complete option
|
|
66
|
-
mapping and help, isolated stdout/stderr channels, stable exit statuses,
|
|
67
|
-
trusted-tree disclosure, and direct/package execution ([usage](10-usage.md#-cli))
|
|
68
|
-
- [x] ๐ธ **Golden generated-tree contract** โ deliberate reproducible updates,
|
|
69
|
-
complete byte-for-byte tree comparisons in all canonical layouts, and
|
|
70
|
-
strict compilation against installed published `km-api@0.4.1`, including
|
|
71
|
-
`trace`, custom/default statuses, and extension media types (R-112/R-126)
|
|
72
|
-
- [x] ๐ **Coverage gates** โ V8 measures every source file; overall line/function
|
|
73
|
-
coverage is gated at 90%, branches at 85%, conversion engines at 95% lines,
|
|
74
|
-
and every source file at 80% lines, with omitted-file detection
|
|
75
|
-
- [x] ๐ฃ **Bun gate** โ pinned Bun 1.2.21, frozen `bun.lock` installation,
|
|
76
|
-
typecheck, Vitest, coverage, direct/package CLI smoke tests, and Bun-native
|
|
77
|
-
generated-TypeScript reverse imports run through one command (T-14/D-01)
|
|
78
|
-
- [x] ๐งช **Vitest suite** โ unit, integration, round-trip, golden-tree, JSDoc,
|
|
79
|
-
CLI, and package-release contracts run in the pinned Bun gate
|
|
80
|
-
([testing](11-testing.md))
|
|
81
|
-
- [x] ๐ **JSDoc audit** โ every exported declaration and exposed interface/class
|
|
82
|
-
member has a useful summary; public callables document parameters and return
|
|
83
|
-
values; optional configuration fields state defaults; examples and links are
|
|
84
|
-
checked; and named-only exports are enforced by an AST contract suite (T-12,
|
|
85
|
-
R-131โฆR-135/R-1003)
|
|
86
|
-
- [x] ๐ฆ **Package/release readiness** โ public npm metadata, an allowlisted
|
|
87
|
-
source archive, executable CLI, isolated packed-consumer import and CLI
|
|
88
|
-
smoke tests, and a mandatory prepublish gate (R-191โฆR-193)
|
|
89
|
-
- [x] ๐ **Documentation status sync** โ current module/test layouts, generated
|
|
90
|
-
examples, Phase 1 completion labels, and deferred boundaries agree with
|
|
91
|
-
the implementation and are guarded by a documentation contract test
|
|
92
|
-
- [x] ๐ First versioned entry in `CHANGELOG.md` โ **v0.1.0**
|
|
93
|
-
|
|
94
|
-
### โ
Definition of done โ Phase 1 (met)
|
|
95
|
-
|
|
96
|
-
The v0.1.0 release satisfies all of the following:
|
|
97
|
-
|
|
98
|
-
1. ๐ `bun run test` is green โ unit + integration + round-trip suites
|
|
99
|
-
2. ๐ Coverage gates are met (see [Testing โ Coverage gates](11-testing.md#-coverage-gates))
|
|
100
|
-
3. ๐ Round-trip: for every fixture spec, `openapi(docs(spec))` equals `spec` after canonicalization
|
|
101
|
-
4. ๐งฉ All four engine docs sections are implemented exactly as specified (mapping tables are the contract)
|
|
102
|
-
5. ๐ Every public symbol has JSDoc; `tsc --noEmit` (strict) passes
|
|
103
|
-
6. ๐ `CHANGELOG.md` v0.1.0 entry exists and `README` status banner is updated
|
|
104
|
-
7. โ
**km-api dependency gate** (D-15) โ `km-api@0.4.1` is published and zopia
|
|
105
|
-
uses `km-api: ^0.4.1` from npm; no Git submodule or unpublished commit remains.
|
|
106
|
-
The dependency remains installed and type-checkable in every release gate.
|
|
107
|
-
|
|
108
|
-
## ๐งฐ Phase 2 โ Breadth (v0.2.x) โ
|
|
109
|
-
|
|
110
|
-
- ๐ **YAML input** โ โ
accept `swagger.yaml` / `openapi.yaml` (D-13 lifts โ D-16)
|
|
111
|
-
- ๐ **External `$ref`s** โ โ
resolve references to other files in the same folder (D-17)
|
|
112
|
-
- ๐งฉ **Reusable parameters & responses** โ โ
emitted as their own component
|
|
113
|
-
files `components/parameters/<Name>/index.ts` and
|
|
114
|
-
`components/responses/<Name>/index.ts` with `<Name>Parameter` / `<Name>Response`
|
|
115
|
-
exports, kind barrels, manifest `kind` entries, and a reverse conversion that
|
|
116
|
-
refreshes declarations from the modules while restoring every use-site `$ref`
|
|
117
|
-
verbatim (D-18; km-api has no standalone-parameter concept, so modules hold the
|
|
118
|
-
derived schema only โ name/location/`required` remain operation data)
|
|
119
|
-
- ๐งพ **`zopia.config.ts`** โ โ
project-level config file with working-directory
|
|
120
|
-
discovery, explicit `--config` paths, validated `{ generate, reverse }` defaults,
|
|
121
|
-
and stable precedence CLI flag > config value > built-in default (D-19);
|
|
122
|
-
CLI flags stay available
|
|
123
|
-
- ๐ค **OpenAPI 2.0 output** โ โ
engine โฃ accepts `version: '2.0'` (CLI
|
|
124
|
-
`--version 2.0`, config `reverse.version`) and downgrades 3.x-sourced
|
|
125
|
-
manifests into Swagger 2.0 documents: `nullable` becomes `x-nullable`,
|
|
126
|
-
`requestBody` becomes `body`/`formData` parameters, `components` becomes
|
|
127
|
-
top-level `definitions`/`parameters`/`responses`, `servers` decomposes into
|
|
128
|
-
`host`/`basePath`/`schemes`, and unrepresentable 3.x features (webhooks,
|
|
129
|
-
`jsonSchemaDialect`, cookie params, `links`, multi-flow OAuth2, โฆ) drop with
|
|
130
|
-
deterministic `ZOPIA_WARN_DIALECT_DOWNGRADE` warnings (D-20)
|
|
131
|
-
- ๐งช **More JSON Schema keywords** โ โ
`propertyNames` graduates from the D-12
|
|
132
|
-
approximation to native conversion: the exact
|
|
133
|
-
`{ type: 'object', propertyNames: { type: 'string', <pattern/length constraints> }, additionalProperties: <schema> }`
|
|
134
|
-
form now converts to `z.record(key, value)` (and back) with no warning or
|
|
135
|
-
frozen overlay (D-22); every other listed keyword keeps its runtime-refinement
|
|
136
|
-
+ frozen-overlay round-trip (`patternProperties`, `if/then/else`,
|
|
137
|
-
`minProperties/maxProperties`, non-native `propertyNames` forms, `contains`) (Phase 1: documented approximation + warning, D-12)
|
|
138
|
-
- ๐ช **3.1 webhook endpoint generation** โ โ
`document.webhooks` operations now
|
|
139
|
-
generate real endpoint files under `webhooks/<name>/<method>/index.ts`
|
|
140
|
-
alongside path operations (D-23). The manifest records `webhooks[]` entries
|
|
141
|
-
with the same reference/overlay/security metadata as path endpoints plus
|
|
142
|
-
`webhookOrder` and a `webhooksOverlay` (item-level metadata, `x-` names, and
|
|
143
|
-
operation-less items stay verbatim), so engine โฃ reassembles webhooks in exact
|
|
144
|
-
source order for 3.1 output and omits them with source-located
|
|
145
|
-
`ZOPIA_WARN_WEBHOOKS` warnings for 3.0/2.0 output. Operation-less webhook maps
|
|
146
|
-
keep the Phase 1 manifest-only behavior (no endpoint files, forward warning)
|
|
147
|
-
- ๐ **Watch mode** โ โ
`zopia generate --watch` regenerates whenever the spec
|
|
148
|
-
file changes (S-88): an immediate initial run, 50 ms-coalesced re-runs that
|
|
149
|
-
serialize against in-flight generation, run errors printed to stderr while
|
|
150
|
-
watching continues, and abort/cleanup on exit. Uses `fs.watch` with the
|
|
151
|
-
existing stale-tree/prune pipeline, so spec edits refresh owned files in place
|
|
152
|
-
|
|
153
|
-
## ๐ Phase 3 โ Ecosystem (v0.3+) โ
|
|
154
|
-
|
|
155
|
-
- ๐งน **`zopia validate`** โ โ
CLI command + `validateZopia()` API lint specs
|
|
156
|
-
and generated trees (S-89): specs are checked for dialect validity, broken
|
|
157
|
-
local `$ref`s, endpoint-planning failures (name collisions, cross-namespace
|
|
158
|
-
duplicate operationIds), and components unreachable from any operation
|
|
159
|
-
(transitive; Swagger `definitions` included). Generated trees are checked for
|
|
160
|
-
manifest presence/validity, a successful reverse dry-run, and km-api peer
|
|
161
|
-
drift. Findings are deterministic sorted diagnostics with stable
|
|
162
|
-
`ZOPIA_VALIDATE_*` lint codes; the CLI prints them on stdout and exits `1`
|
|
163
|
-
when any error-severity finding exists
|
|
164
|
-
- โป๏ธ **Incremental regeneration** โ โ
unchanged generated files keep their
|
|
165
|
-
mtimes (byte-identical regeneration writes nothing); the opt-in merge-safe
|
|
166
|
-
custom layer (D-24, S-90) exports a `custom` companion namespace per
|
|
167
|
-
endpoint/webhook scaffolded once and never overwritten (`--custom`,
|
|
168
|
-
`generate.custom`, or the `custom` generate option)
|
|
169
|
-
- ๐ **Diff tool** โ โ
`zopia diff old.json new.json` + `diffOpenApiSpecs()`
|
|
170
|
-
API (S-91): semantic comparison of dialect, info, endpoints (with
|
|
171
|
-
parameter/request-body/response details), webhooks, schema components,
|
|
172
|
-
document fields, and `x-` extensions; key order is ignored; deterministic
|
|
173
|
-
`+`/`-`/`~` human-readable lines with a summary; differences are data on
|
|
174
|
-
stdout, so the CLI exits `0` for changed pairs
|
|
175
|
-
- ๐งฉ **Presets** โ โ
split-generation layouts (S-92): `--preset multi-tag`
|
|
176
|
-
routes each operation by its primary tag, `--preset multi-server` by the
|
|
177
|
-
effective first server (repeatedly), producing one independently
|
|
178
|
-
reverse-convertible api-docs sub-tree per bucket; untagged/default-server
|
|
179
|
-
operations land in `untagged`/`https-default-server`-style buckets; nothing
|
|
180
|
-
to split โ the normal single tree. Programmatic `preset` option plus
|
|
181
|
-
`generate.preset` configuration; `planPresetBuckets()` exposes the pure
|
|
182
|
-
deterministic planner (collision-safe slugs, `ZOPIA_WARN_PRESET_PRIMARY_TAG`
|
|
183
|
-
on multi-tagged operations) and results report `trees[]`
|
|
184
|
-
- ๐งโ๐ป **VS Code extension** โ โ
navigate spec โ generated code both ways
|
|
185
|
-
(S-93): manifest-driven navigation core (`loadNavigationIndex`,
|
|
186
|
-
`specToLocations`/`treeToSpecLocation`, one-pass JSON
|
|
187
|
-
`specPointersToLines`/`specPointerAtLine` cursor resolution, YAML
|
|
188
|
-
operationId fallback) plus the `zopia navigate` CLI (`--to-code` /
|
|
189
|
-
`--to-spec`) and a plain-JS extension package under `editors/vscode/`
|
|
190
|
-
resolving the workspace's own zopia install
|
|
191
|
-
|
|
192
|
-
## ๐งฎ Versioning
|
|
193
|
-
|
|
194
|
-
SemVer, strictly:
|
|
195
|
-
|
|
196
|
-
| ๐ Bump | When |
|
|
197
|
-
| --- | --- |
|
|
198
|
-
| `MAJOR` | A generated-file format or manifest format breaks (`zopia:manifest@1` โ `@2`) |
|
|
199
|
-
| `MINOR` | New option, new engine feature, new CLI flag โ backward compatible |
|
|
200
|
-
| `PATCH` | Bug fixes, doc corrections, mapping-table clarifications |
|
|
201
|
-
|
|
202
|
-
## ๐ Next
|
|
203
|
-
|
|
204
|
-
- ๐ฏ The exact list of what Phase 1 builds โ [Targets](02-targets.md)
|
|
205
|
-
- ๐๏ธ How the pieces fit โ [Architecture](04-architecture.md)
|
package/docs/04-architecture.md
DELETED
|
@@ -1,345 +0,0 @@
|
|
|
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)
|