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.
Files changed (47) hide show
  1. package/CHANGELOG.md +354 -0
  2. package/LICENSE +21 -0
  3. package/README.md +167 -0
  4. package/bin/zopia.js +20 -0
  5. package/docs/01-overview.md +94 -0
  6. package/docs/02-targets.md +55 -0
  7. package/docs/03-roadmap.md +205 -0
  8. package/docs/04-architecture.md +345 -0
  9. package/docs/05-concepts.md +239 -0
  10. package/docs/06-conversions.md +493 -0
  11. package/docs/07-api-docs.md +337 -0
  12. package/docs/08-components.md +223 -0
  13. package/docs/09-configuration.md +167 -0
  14. package/docs/10-usage.md +208 -0
  15. package/docs/11-testing.md +267 -0
  16. package/docs/12-standards.md +242 -0
  17. package/docs/README.md +42 -0
  18. package/docs/publish-workflow.yml.example +48 -0
  19. package/package.json +77 -0
  20. package/src/api-docs-navigation.ts +353 -0
  21. package/src/cli-command.ts +537 -0
  22. package/src/cli.ts +4 -0
  23. package/src/config.ts +190 -0
  24. package/src/conversions/api-docs-facade.ts +42 -0
  25. package/src/conversions/api-docs-generate.ts +567 -0
  26. package/src/conversions/api-docs-layout.ts +39 -0
  27. package/src/conversions/api-docs-plan.ts +130 -0
  28. package/src/conversions/api-docs-presets.ts +246 -0
  29. package/src/conversions/json-schema-to-zod.ts +931 -0
  30. package/src/conversions/manifest-staleness.ts +211 -0
  31. package/src/conversions/manifest-to-openapi.ts +1861 -0
  32. package/src/conversions/manifest-writer.ts +778 -0
  33. package/src/conversions/openapi-contracts.ts +333 -0
  34. package/src/conversions/openapi-external-ref.ts +233 -0
  35. package/src/conversions/openapi-ir.ts +74 -0
  36. package/src/conversions/openapi-ref.ts +38 -0
  37. package/src/conversions/openapi-to-api-docs-public.ts +466 -0
  38. package/src/conversions/openapi-to-api-docs.ts +203 -0
  39. package/src/conversions/openapi.ts +80 -0
  40. package/src/conversions/reverse-security.ts +68 -0
  41. package/src/conversions/yaml.ts +876 -0
  42. package/src/conversions/zod-to-json-schema.ts +536 -0
  43. package/src/diff.ts +353 -0
  44. package/src/errors.ts +114 -0
  45. package/src/index.ts +80 -0
  46. package/src/validation.ts +299 -0
  47. package/src/warnings.ts +164 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,354 @@
1
+ # πŸ“œ Changelog
2
+
3
+ All notable changes to **zopia** are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### ✨ Added
11
+ - πŸ“¦ **npm publish pipeline** β€” `.github/workflows/publish.yml` publishes on GitHub Release creation (or manually) with the pinned Bun toolchain, verifies the release tag matches `package.json`'s version, and runs `npm publish --provenance --access public`. Its only credential is the repository-secret `NODE_AUTH_TOKEN` (npm `NPM_TOKEN`) β€” never handled in chat or commits; npm trusted-publishing (OIDC) is supported by the declared `id-token` permission.
12
+
13
+ ### πŸ› Fixed
14
+ - πŸ§ͺ Release-gate coverage regression (round 9): the full Bun release gate (`bun run release:check`, invoked by `prepublishOnly`) exposed **branch coverage 84.41% < the 85% threshold** β€” a genuine release blocker invisible to the plain test suite. Recovered to **85.08%** with 12 targeted behavior tests: exported warning helpers (`normalizeZopiaWarnings`/`rebaseZopiaWarning`/`formatZopiaWarning`/`formatZopiaWarningComment` β€” previously uncovered public API), scanner array rigs (nested arrays/malformed bodies), invalid generate-option shapes (object/array/unknown key/`outDir`/mode/booleans), input shapes (JSON text/YAML text/file paths/invalid JSON), duplicate `operationId` rejection with deterministic derived-rename (verifying reverse round-trips the original `undefined`), diff `$ref` guard shapes, invalid reusable-parameter declarations for both dialects, and navigation manifest-shape guards (malformed entries, operationId first-wins, absent-id labels, every unsupported-pointer hint).
15
+
16
+ ## [0.3.0] - 2026-09-29
17
+
18
+ ### πŸ› Fixed
19
+ - 🧭 Item-level navigation pointers (round 8): the navigation core's documented `#/paths/<path>` (every method of the item) and `#/webhooks/<name>` shapes silently failed with an "unsupported spec pointer" error β€” item-level pointers now enumerate every routed operation of the item plus its custom companions in deterministic file order, and unknown items keep their typed `ZOPIA_CONFIG_INVALID` failure (`spec pointer has no generated module`).
20
+ - 🧰 Preset routing holes (round 7): op-less path/webhook items β€” shared `parameters` blocks, `summary`/item `x-` metadata, `x-` webhook-map entries' namesakes β€” silently disappeared from **every** preset bucket (documented as traveling verbatim like components and `x-` map entries; they now ride with every bucket), and an explicit empty `servers: []` override on an operation or path item was treated as *inherit the parent servers* instead of the specification-mandated default server `/` (an unlucky set could silently collapse the whole multi-server split into a fallthrough); the nearest explicit `servers` array is now decisive, empty or not. The presets added entry from the previous commit was also seated in a floating `### ✨ Added` block **outside** `## [Unreleased]` and has been moved into it.
21
+ - πŸ” Diff coverage holes (round 6): changes to the named component registries β€” `components.parameters`/`responses`/`securitySchemes`/`requestBodies`/`headers`/`links`/`callbacks`/`examples`/`pathItems` (3.x) and `parameters`/`responses`/`securityDefinitions` (Swagger 2.0, cross-dialect aligned with side-appropriate pointers β€” an auth-scheme change reported nothing) β€” plus path-item and webhook-item metadata (`summary`/`description`/`servers`, item `x-` keys, resolved through `@ref` chains with sibling-wins semantics and the generation-era typed failures) and `x-` extension entries inside `paths`/`webhooks` are now reported instead of silently invisible.
22
+ - πŸ›‘οΈ Custom companion hardening (round 5): a **directory** at a companion's `custom.ts` path (which would shadow the sibling module's `./custom` import) now fails with a typed `ZOPIA_FS_OUTSIDE_OUTDIR` instead of silently emitting a broken tree; companion paths are derived through a dedicated helper that rejects endpoint modules not living in their own directory; path segments literally named `custom.ts` keep working both layouts through planner renaming and now have scaffold coverage asserting every generated module's companion is a real file.
23
+
24
+ ### ✨ Added
25
+ - 🧭 **Spec ↔ code navigation + VS Code extension (Phase 3, S-93).** New
26
+ manifest-driven navigation core in zopia: `loadNavigationIndex()` /
27
+ `navigationIndexFromManifest()` build a deterministic index of any
28
+ generated tree (or preset bucket root) answering both directions β€”
29
+ `specToLocations()` (pointer β†’ endpoint/webhook/component files plus
30
+ `custom.ts` companions when enabled) and `treeToSpecLocation()` (file β†’
31
+ pointer, including component barrels and the manifest itself). Editor
32
+ integrations get a dependency-free single-pass JSON scanner:
33
+ `specPointersToLines()` / `specPointerToLine()` (exact declaration
34
+ lines, RFC-6901 escaping, minified documents), `specPointerAtLine()`
35
+ (cursor rule), and `pointerForOperationId()` (YAML fallback). New CLI
36
+ `zopia navigate <docs-dir> --to-code <pointer> | --to-spec <file>` prints
37
+ one stable line per location; unmatched queries fail with typed
38
+ `ZOPIA_CONFIG_INVALID`/`ZOPIA_DOCS_MISSING_MANIFEST`. The VS Code
39
+ extension ships under `editors/vscode/` as a zero-build CommonJS package
40
+ resolving the workspace's own zopia install: `Zopia: Open generated code`
41
+ (spec cursor β†’ module) and `Zopia: Open spec location` (tree file β†’
42
+ spec declaration line, exact for JSON, `operationId`-marker fallback for
43
+ YAML).
44
+ - 🧰 **Split-generation presets (S-92).** `openApiToApiDocs` gains a `preset`
45
+ option β€” `multi-tag` routes each operation by its primary tag and
46
+ `multi-server` by the effective first server (operation β†’ path item β†’
47
+ document) β€” generating one independently reverse-convertible api-docs
48
+ sub-tree per bucket under collision-safe lowercase slug directories
49
+ (`untagged`, `https-api.example.com`, `pet-store`, `pet-store-2`, …). The
50
+ pure planner is exported as `planPresetBuckets()`; results report
51
+ `trees[]` (`{ name, directory, manifestPath? }`) and multi-tagged
52
+ operations emit `ZOPIA_WARN_PRESET_PRIMARY_TAG`. When a spec has nothing to
53
+ split (no tags / one effective server) zopia falls through to the normal
54
+ single tree. CLI `--preset multi-tag|multi-server` prints
55
+ `zopia generate <input>: N preset trees in <outDir> (…)`, and
56
+ `generate.preset` configures project defaults.
57
+
58
+ > πŸ“Œ **Convention** β€” every commit that changes behaviour, the public API, or the
59
+ > documentation adds an entry under `Unreleased`. When a release is cut, the
60
+ > `Unreleased` section is renamed to the new version with its date.
61
+ > See [docs/12-standards.md β†’ Changelog convention](docs/12-standards.md#-changelog-convention).
62
+
63
+ ---
64
+ - πŸ” **Spec diff tool (Phase 3, S-91)** β€” new `zopia diff old.json new.json` CLI command plus `diffOpenApiSpecs()` / `diffOpenApiDocuments()` APIs: semantic comparison of two Swagger 2.0 / OpenAPI 3.0/3.1 inputs (JSON paths, YAML paths, inline text, or objects β€” loaded with the same rules as generation). Changes are grouped and deterministically ordered: dialect, `info` fields, endpoints (added/removed labeled `METHOD path (operationId)` in path-primary order; shared operations emit a header plus scalar `->` transitions, parameter add/remove/change, request-body presence/content, response status adds/removals/changes, security, tags, and `x-` extensions), webhooks, schema components (dialect-aligned `#/definitions/…` vs `#/components/schemas/…` pointers), document fields, and root extensions. Object-key order never counts as a change; detected changes print as `+`/`-`/`~` lines with a `zopia diff …: N changes (A added, R removed, C changed)` summary to stdout, stderr stays silent, and changed pairs exit `0` (differences are data). Unreadable/invalid inputs fail with the existing typed `ZOPIA_SPEC_*` codes.
65
+ - ♻️ **Incremental regeneration + merge-safe custom companions (Phase 3, D-24, S-90)** β€” generation now writes only files whose rendered bytes differ: byte-identical endpoints, components, and the manifest keep their mtimes, so watch mode and bundler caches stop churning on no-op regenerations. The opt-in `custom` layer (generate option `custom: true`, CLI `--custom`, config `generate.custom`) appends `export * as custom from './custom';` to every endpoint and webhook module and scaffolds a sibling `custom.ts` exactly once β€” an existing file or symlink at that path is never touched, custom files are never manifest-owned (staleness pruning can't delete them), and toggling the option off removes only the export line via the normal skip-aware rewrite. The manifest records `options.custom` only when enabled; toggling it reports the new `ZOPIA_WARN_STALE_TREE` reason `custom-companions-changed`. Non-boolean `custom` values fail with `ZOPIA_CONFIG_INVALID` at every layer.
66
+ - 🧹 **`zopia validate` (Phase 3, S-89)** β€” new CLI command and `validateZopia()` API: specs are checked for dialect validity, broken local `$ref`s (error at the offending pointer), endpoint-planning failures (name collisions, cross-namespace duplicate `operationId`s), and components no operation can reach β€” transitively, covering OpenAPI `components.schemas` and Swagger `definitions` with stable `ZOPIA_VALIDATE_UNREACHABLE_COMPONENT` warnings. Generated trees are checked for manifest presence/validity, a complete reverse dry-run, and km-api peer drift (`ZOPIA_VALIDATE_KM_API_DRIFT` β€” outside the declared `^0.4.1` range or unresolvable near the tree; warning-severity, never blocking). Findings return as deterministic sorted diagnostics `{ severity, code, at, message }`; the CLI prints them to stdout with a summary line and exits `1` only when an error-severity finding exists. Also exported: `readOpenApiSourceInput`, `validateOpenApiReferences`, and `assertUniqueOperationIdsAcrossScopes` (shared between generation and validation).
67
+ - 🧭 Dialect reverse hardening (round 3): reversing to 3.0 or 2.0 no longer crashes with `ZOPIA_REF_NOT_FOUND` when a path item `$ref`s into a container the target dialect omits (`components.pathItems` or the webhooks section) β€” the operation expands in place with the existing `ZOPIA_WARN_DIALECT_DOWNGRADE` diagnostic, while references into surviving containers stay verbatim. Generation now also rejects duplicate `operationId`s across the path and webhook namespaces up front (`ZOPIA_SPEC_INVALID`, document-wide uniqueness per the OpenAPI spec); the reverse guard remains for hand-edited trees.
68
+ - 🧷 Webhook round-trip hardening: pure `$ref` webhook items reverse to their verbatim source shape (`webhookItemRef` parity with path-item refs β€” edited generated modules expand the operation as a spec-legal sibling), empty `x-` webhook entries (for example `x-empty: {}`) survive β‘’β†’β‘£ through the webhook order list, and malformed webhook items (`null` / scalar / array) raise `ZOPIA_SPEC_INVALID` instead of an untyped crash. Usage and configuration docs now document `zopia generate --watch`.
69
+ - πŸ‘€ Watch mode: `zopia generate --watch` keeps engine β‘’ running against the spec file β€” the initial generation runs identically to non-watch mode (same options, same warnings, same errors), every file change is coalesced (50 ms) and serialized against in-flight runs, a failure prints the typed error on stderr and keeps watching with the previous tree untouched, and in-place regeneration reuses the stale-tree ownership pipeline (one deterministic `ZOPIA_WARN_STALE_TREE` before refresh). The watcher subscribes to the spec's parent directory and filters on the basename, so atomic saves (write-temp + rename) keep regenerating instead of silently ending the watch. Embedding entry points get the same loop through `runGenerateWatch(input, options, output?, signal?)` in `cli-command` with an `AbortSignal` stop hook (S-88).
70
+ - πŸͺ OpenAPI 3.1 webhook endpoint generation (D-23): `document.webhooks` operations now emit real endpoint files at `webhooks/<name>/<method>/index.ts` through the same planning, collision-avoidance, and component-reference pipeline as path operations. The manifest gains `webhooks[]` records (file/name/method/operationId/sourceOperation/refs/overlay/responseOverlay/security), a `webhookOrder` list preserving the exact source key order, and a `webhooksOverlay` for item-level metadata (`parameters`, `x-` keys, and `x-` or operation-less entries); non-empty `webhooks` maps move out of `documentOverlay` into these records. Reverse conversion imports the generated webhook modules for runtime refresh, restores local path-item `$ref` items inline, enforces global `operationId` uniqueness across path and webhook operations, and reassembles `document.webhooks` in exact source order for 3.1 output β€” while 3.0/2.0 output omits webhooks with the source-located `ZOPIA_WARN_WEBHOOKS` warning. Webhook maps with no operations keep the earlier manifest-only behavior with a forward `ZOPIA_WARN_WEBHOOKS`. Non-3.1 manifests carrying `webhooks` fail validation deterministically. New public helpers: `planWebhookDocsFiles`, `collectOpenApiWebhookOperations`, and `webhookRuntimePath`.
71
+ - πŸ“ YAML input for engine β‘’ (D-13 lifts, D-16): `openApiToApiDocs` and the CLI accept `.yaml`/`.yml` spec paths, inline YAML text, and extension-less YAML files through a new owned, deterministic YAML 1.2 core-schema parser (`src/conversions/yaml.ts`). It supports block/flow collections, plain/single/double-quoted scalars, literal/folded block scalars with chomping and indent indicators, comments, anchors/aliases/`<<` merge keys, `%YAML 1.x` directives and single-document `---`/`...` markers, and parses to exactly the values an equivalent JSON document yields.
72
+ - 🚨 `ZOPIA_SPEC_INVALID_YAML` (MINOR error-code addition, R-142): every YAML syntax/structure rejection β€” tab indentation, duplicate keys, undefined aliases, custom tags, multiple documents, complex `?` keys, non-JSON numbers (`.inf`/`.nan`), and more β€” fails with a line-located typed error; unreadable or malformed `.yaml`/`.yml` files report the YAML code with the file path in `at`.
73
+ - πŸ”— External `$ref` resolution for spec files (D-17): `openApiToApiDocs` and the CLI bundle same-folder references β€” `other.yaml`/`other.json`/`other.yml` with optional `#` JSON Pointers, `./` spellings, and whole-file targets β€” inline before normalization. Nested cross-file references resolve against their owning file, sibling keys next to a `$ref` win over bundled content, every sibling file is read exactly once, and bundled content is deep-cloned, so generated trees, warnings, reverse conversion, and manifest hashes are byte-identical to an equivalent inline spec; reverse conversion emits the bundled single-file document and never re-splits files.
74
+ - 🧭 Deterministic external-ref failure matrix (D-17): targets outside the spec folder (URLs, `../`, absolute paths, subdirectories, drive names, non-spec extensions) and any external ref in object/text inputs keep `ZOPIA_REF_EXTERNAL`; unparsable targets fail with `ZOPIA_SPEC_INVALID_JSON`/`ZOPIA_SPEC_INVALID_YAML` at the file path; missing pointers, bad fragments, circular chains, sibling keys on non-object targets, and expansion past the 512-level guard fail with `ZOPIA_REF_NOT_FOUND` at the referencing pointer.
75
+ - ♻️ Reusable parameters and responses as component modules (D-18): in components mode, declared reusable parameters (`#/components/parameters/…`, Swagger 2.0 `#/parameters/…`) and responses (`#/components/responses/…`, Swagger 2.0 `#/responses/…`) now generate their own files `components/parameters/<Name>/index.ts` / `components/responses/<Name>/index.ts` exporting `<Name>Parameter` / `<Name>Response` β€” the module holds the derived schema only (name/location/`required` stay operation data; schema-less responses render `z.void()` at use sites and emit no module). Bare `$ref` use sites import through the new per-kind barrels (`components/parameters/index.ts`, `components/responses/index.ts`, emitted only when that kind has declarations) β€” including Swagger 2.0 bare body/`formData` parameters and whole-response references β€” while sibling-merged `$ref`s keep their inline composition. The manifest records `{ name, kind: "parameter"|"response", file, schema, overlay }` entries; reverse conversion refreshes each declaration from the current module (developer edits win) and restores every use-site `$ref` verbatim from manifest placements, and stale-tree detection owns the new barrels. Chained reusable-parameter declarations (`$ref` β†’ `$ref` β†’ concrete) resolve fully with a cycle guard on both conversions.
76
+ - 🧾 Project config file (D-19): the CLI discovers `zopia.config.ts` (then `zopia.config.mts`) next to the working directory, or takes an explicit `--config <path>`; the file is validated `{ generate: { mode, insertComponents, useComponentAsReference, manifest, outDir }, reverse: { version, out } }` defaults with per-key `ZOPIA_CONFIG_INVALID` errors, and merges with immutable precedence **CLI flag > config value > built-in default** (`--no-manifest` always wins). `zopia generate` accepts omitting `<output-dir>` when the config supplies `generate.outDir`; the library exposes `loadZopiaConfig` and `defineConfig`.
77
+ - πŸ§ͺ Native record-object conversion (D-22): engine β‘‘ now maps the exact object form `{ "type": "object", "propertyNames": { "type": "string", "pattern"/"minLength"/"maxLength" }, "additionalProperties": <schema> }` (with no other object-structure keywords) onto `z.record(key, value)` β€” the same form Zod emits, so these schemas convert with zero warnings and zero frozen overlays while key constraints are enforced natively at runtime. Non-native `propertyNames` spellings (implicit/absent `type`, enums, unions, annotations, companions like `properties`/`required`/count bounds, boolean or absent `additionalProperties`, invalid key patterns) keep the D-12 runtime-refinement + frozen-overlay behavior with byte-exact reverse restoration.
78
+ - πŸ“€ OpenAPI 2.0 reverse output (D-20): `apiDocsToOpenApi`/`manifestToOpenApi`/`manifestFileToOpenApi` (and the CLI `--version 2.0` / config `reverse.version`) now convert 3.0/3.1-sourced manifests into Swagger 2.0 documents. The downgrade rewrites manifests into the source-dialect Swagger reconstruction path: `nullable` spellings (3.0 `nullable`, 3.1 `type` unions/`anyOf` with `null`) become `x-nullable`, `requestBody` becomes a `body` parameter β€” or `formData` parameters for form media types β€” `components.schemas` becomes `definitions`, reusable parameters/responses move to the top-level `parameters`/`responses` maps with constraint-folded scalar declarations, `servers[0]` decomposes into `host`/`basePath`/`schemes`, and operation/document `consumes`/`produces` derive from request/response media types. Features Swagger 2.0 cannot represent β€” webhooks, `jsonSchemaDialect`, cookie parameters, response `links`, `callbacks`, multi-flow OAuth2, OIDC/mTLS and non-basic `http` schemes, complex parameter constraints, extra media types, server variables β€” drop with a deterministic `ZOPIA_WARN_DIALECT_DOWNGRADE` (or the existing `ZOPIA_WARN_WEBHOOKS`) warning at the exact pointer; runtime-native gates (`Swagger 2.0 does not support cookie parameters`, primitive-only parameter shapes) degrade the same way instead of throwing only when the dialect was downgraded, so native Swagger sources keep their strict behavior.
79
+
80
+ ### πŸ› Fixed
81
+ - πŸ“ YAML parser hardening after adversarial review: bare `-` sequence items followed by sibling dashes no longer nest the siblings (each item is `null`); `-` stays a plain scalar in mapping-value position (`k: -`); `#` lines indented as block-scalar content are preserved instead of silently dropped; comments inside multi-line flow collections no longer corrupt quote/depth tracking, and dedented flow closers are accepted; flow collections accept trailing commas; a bare `': '` inside a flow plain scalar terminates it so missing commas fail; block-scalar headers reject junk that is not a comment; multi-line plain continuations that look like a mapping/sequence entry fail instead of folding; content after the `...` marker fails; alias resolutions clone anchored values so downstream walkers never see shared identity.
82
+
83
+ ## [0.1.0] - 2026-09-28
84
+
85
+ ### ✨ Added
86
+ - πŸ“¦ Prepared the public `zopia@0.1.0` package with complete npm metadata, an allowlisted source distribution, synchronized release identity, executable CLI permissions, packed-consumer library/CLI smoke tests, and a mandatory prepublish release gate.
87
+ - πŸ“– Completed the JSDoc audit: every exported declaration and exposed public shape member now has useful API documentation, public callables specify parameters and return values, optional configuration fields state defaults, the public example is runnable, and an AST contract suite prevents regressions in summaries, tags, links, examples, and named-only exports.
88
+ - ⌨️ Completed the CLI contract: `generate` and `reverse` now use strict command-specific parsing, reject missing/extra/unknown/incompatible/duplicate/valueless arguments before engine work, accept options around positionals, expose complete trusted-tree-aware help, preserve stdout/stderr isolation, and return stable success, user-error, and internal-error exit statuses.
89
+ - 🟣 Completed the Bun gate: added the authoritative `bun.lock`, pinned-runtime and frozen-install validation, ESM package metadata, one CI-ready gate command, full typecheck/test/coverage checks, direct and packaged CLI smoke tests, and a real Bun generation/reverse cycle that imports generated TypeScript.
90
+ - πŸ“ˆ Completed the coverage gates: `bun run coverage` now measures every `src/**/*.ts` file with Vitest V8, enforces 90% overall line/function and 85% branch coverage, requires 95% aggregate conversion-engine lines and 80% lines in every source file, rejects omitted files, and regression-tests CLI success, output-file, typed-error, and unexpected-error paths.
91
+ - πŸ“Έ Completed the golden generated-tree contract: checked in deterministic directory, flat, component-reference, and km-api type-surface trees; added deliberate `golden:update` regeneration, complete byte/path comparisons with stale-tree detection, import-boundary checks, and strict no-emit compilation against published `km-api@0.4.1`; arbitrary OpenAPI media types now preserve their exact runtime strings across km-api's enumerated declaration boundary.
92
+ - πŸ” Completed the round-trip contract with source-dialect fixture properties for Swagger 2.0 and OpenAPI 3.0/3.1, all layout/component strategies, nested/cyclic/path-item refs, lossy-keyword overlays, exact version/frame/path metadata and example forms, Swagger form constraints, Zod ↔ JSON Schema convergence, and byte-identical regeneration; fixed every mismatch exposed by the new suite.
93
+ - ⚠️ Completed the warnings pipeline across all four engines: warning codes are now a stable typed catalogue; diagnostics are validated, sanitized, deduplicated, deterministically sorted, and JSON-Pointer-located; nested/runtime warnings are rebased to their exact source or output nodes; reverse conversion reports runtime Zod losses, legacy info/default-security fallbacks, and OpenAPI 3.1β†’3.0 omissions through results and `onWarning`; canonical generated-code markers and CLI stderr output keep reverse stdout valid JSON.
94
+ - ♻️ Completed manifest staleness handling: regeneration now compares canonical source identity, layout, component options, manifest retention, and owned-file presence; reports invalid/incomplete/drifted trees with `ZOPIA_WARN_STALE_TREE`; safely prunes only obsolete files claimed by a validated prior manifest while preserving custom files; removes old manifests when disabled; and refuses symlinked output ancestors or manifest temporary-file symlinks.
95
+ - πŸ” Completed reverse security fallback handling: manifest operation/global requirements remain authoritative and are structurally validated; runtime `auth: 'YES'` with no recorded requirement now reuses one deterministic collision-safe bearer scheme across operations, preserves existing definitions, emits the correct OpenAPI or source-dialect Swagger representation, and reports one exact-pointer warning per fallback operation.
96
+ - πŸ›‘ Completed typed error handling across the package: `ZOPIA_ERROR_CODES` now defines one immutable stable catalogue; engines, low-level helpers, manifests, generated-module imports, filesystem writers, warnings, and CLI validation expose `ZopiaError` rather than raw exceptions, with actionable hints, discoverable locations, and preserved causes.
97
+ - πŸ“¦ Completed the versioned manifest writer/reader contract: engine β‘’ now delegates to a dedicated canonical, validated, atomic writer that records complete OpenAPI/Swagger frame, component, endpoint, reference, overlay, response, and security metadata; generated manifests are byte-stable, portable-path-safe, literal-aware when collecting `$ref`s, and share their typed contract and source hash with reverse conversion and stale-tree detection.
98
+ - πŸ“„ Completed the Engine β‘’ public API: added `openApiToApiDocs()` with file/object/JSON-text input, validated defaults and typed `ZopiaError`s, sorted result metadata, structured conversion/staleness warnings, reference preflight, and application/json-first media selection; the CLI now delegates to the same API.
99
+ - πŸ“ Completed Engine β‘‘: JSON Schema conversion now accepts `.json` paths, returns structured warnings and manifest-compatible overlays, emits visible `@zopia:warn` markers, preserves annotations, emits lazy local definitions and discriminated unions, and hands lossy schema restorations through endpoint and component manifests for reverse conversion.
100
+ - βš›οΈ Completed Engine β‘ : Zod conversion now defaults to OpenAPI 3.1 with dialect markers enabled, preserves metadata without `id`-driven extraction, converts every Zod-reported unrepresentable site to `{}` with structured `ZOPIA_WARN_UNREPRESENTABLE` callbacks, recursively canonicalizes keyword order, and strips built-in format patterns plus safe-integer sentinel bounds.
101
+ - πŸ“€ Reverse conversion now selects OpenAPI 3.0 or 3.1 through `ZopiaReverseOptions.version`; the documented `apiDocsToOpenApi()` API and CLI default to 3.1, selected dialects drive runtime schema serialization, and Swagger manifests can be emitted as OpenAPI 3.x.
102
+ - ✨ JSON Schema conversion now supports draft-04/06/07 `dependencies` with both property arrays and schema dependencies, including boolean schemas and malformed-entry warnings.
103
+ - ✨ Keyword-only object, array, string, and numeric schemas now enforce constraints only for matching JSON instance types while leaving other types valid.
104
+ - ✨ JSON Schema conversion now enforces `if`/`then`/`else` conditionals, including boolean branches, typed-parent context, exact keyword-only branch applicability, and malformed-definition warnings.
105
+ - πŸ–₯️ The npm CLI now invokes a Node-compatible wrapper that launches the TypeScript CLI through Bun instead of asking Node to execute TypeScript directly; help now lists the component-reference option.
106
+ - πŸ§ͺ Endpoint generation now preserves OpenAPI request and response examples in km-api's examples structure, including Swagger 2.0 response examples and local `$ref` targets.
107
+ - 🧩 Added initial component-file and sorted component-barrel generation with `insertComponents`.
108
+ - 🧩 Component generation validates names and renders all component contents before writing files, preventing partial output from validation/conversion failures.
109
+ - 🧩 `useComponentAsReference` now imports exact endpoint component schema references through the component barrel; component alias files, self-references, direct object-property references, and direct array-item references now emit safely quoted imports while preserving original component directory names.
110
+ - πŸ§ͺ Added regression coverage for nested array component imports and safe quoting of their generated paths.
111
+ - 🧩 Object and array components now import direct nested references while preserving recursive rendering, tuple rest, `unevaluatedItems`, and tuple/array length and order-independent nested-object uniqueness constraints, nullable recursion, and correct strict/passthrough object behavior; lazy root schemas for direct self-cycles, lazy references for mutual component cycles, nested `oneOf`/`anyOf`/`allOf` compositions, nullable schemas, enums, and constants remain supported; corresponding component documentation was updated; documentation now records direct and mutual cycle support.
112
+ - 🧩 JSON Schema conversion now emits valid Zod `.min()`/`.max()` calls for numeric `minimum`/`maximum` constraints, legacy boolean exclusive bounds, valid `.regex(new RegExp(...))` code for `pattern`, hostname (including label validation), IPv4, IPv6, base64, byte, base64url, emoji, time, duration, OpenAPI signed integer formats with int32 bounds, and unsigned integer formats with uint32 bounds, plus base64 and hexadecimal content encoding, with invalid non-string content-encoding usage warnings, and approximates `patternProperties`, `propertyNames` (including enum/const/string rules), `minProperties`, and `maxProperties` with visible Zod catchall/refinement code; array `uniqueItems` and `contains` constraints, positive `multipleOf` (including decimal floating-point values), `unevaluatedProperties`, `dependentRequired`, dependent schemas (with malformed-definition warnings), and `not` exclusions now emit without false unsupported-keyword warnings; invalid `multipleOf` values for numbers and integers produce warnings are emitted as refinements, malformed dependent rules and invalid property-name regexes now produce warnings instead of throwing, and `allOf` combines multiple scalar rules such as password requirements.
113
+ - πŸ“¦ Component generation results now include the generated `components/index.ts` barrel.
114
+ - πŸ—οΈ Added filesystem generation for endpoint `index.ts` files from the validated API-doc plan.
115
+ - 🧾 Generated endpoint exports and schema helper names now sanitize non-identifier and reserved names into valid TypeScript names.
116
+ - 🏷️ Generated endpoint metadata now preserves normalized tags and explicit security overrides.
117
+ - βš™οΈ Started Phase 1 with Zod/JSON Schema conversion, OpenAPI normalization, operation collection, API-doc layout planning, and the optional ergonomic facade.
118
+ - 🧬 Added validated OpenAPI operation contract extraction for request/response media types.
119
+ - πŸ”— Added strict local OpenAPI JSON Pointer reference handling and documentation.
120
+ - βœ… Added validation for response status keys, descriptions, request-body content, and chained component references.
121
+ - πŸ“€ Added Swagger 2.0 `formData` request-contract extraction and strict field validation.
122
+ - πŸ”— Local path-item references now support chaining and circular-reference detection.
123
+ - πŸ–₯️ Added initial `zopia generate` and `zopia reverse` CLI commands, with generation mode, component-reference, component, manifest, reverse `--out`, and `--help` options; the CLI now uses the project-standard Bun runtime.
124
+ - πŸ”„ Added manifest-driven OpenAPI reconstruction while preserving original operation objects, plus a file-based `manifestFileToOpenApi()` API.
125
+ - πŸ“– Aligned reverse-conversion documentation with the implemented manifest-based API.
126
+ - πŸ”„ Added generated-manifest round-trip regression tests covering operation IDs, explicit security arrays, Swagger `basePath`, Swagger security definitions, and flat generation without a manifest.
127
+ - πŸ› οΈ Reverse conversion now validates manifest source kinds and restores Swagger `basePath`, `host`, `schemes`, `consumes`, `produces`, and `securityDefinitions` metadata.
128
+ - πŸ“ Manifests now preserve and restore OpenAPI `info.description`, additional info fields, document extensions, `externalDocs`, `webhooks`, and `jsonSchemaDialect`.
129
+ - πŸ“¦ Added default `.zopia-manifest.json` generation with source hash, security metadata, component schemas, API file mappings, per-operation `$ref` metadata, operation overlays, and response-header overlays.
130
+ - πŸ”’ Synchronized the lockfile peer dependency range with `km-api: ^0.4.1`.
131
+ - πŸ—‚οΈ Manifest source kinds now normalize versioned OpenAPI values to `openapi-3.0` or `openapi-3.1`.
132
+ - πŸ” Manifest source hashes now use canonical key ordering, avoiding false changes when JSON property order differs, and reject circular/unsupported input values explicitly.
133
+
134
+ ### πŸ”„ Changed
135
+ - πŸš€ Released Phase 1 as `zopia@0.1.0` and synchronized the release date and repository status for the Phase 2 handoff.
136
+ - πŸ“– Public API documentation is now audited across named-only exports, nested type shapes, callable properties/signatures, parameters, returns, direct throws, defaults, links, and examples that semantically typecheck against the real source API.
137
+ - πŸ§ͺ Every documented T-13 scenario now carries an executable `S-…` test identifier, with contracts that prevent matrix, documentation-structure, test-hygiene, deterministic-output, and forbidden-runtime behavior from drifting.
138
+ - 🚒 The release toolchain now pins Vitest and its V8 coverage provider to 4.1.11, uses Bun for TypeScript utility scripts, validates the complete Bun gate contract, and audits post-release user-visible commits for same-commit changelog updates.
139
+ - 🧹 Completed the R-111 test-isolation contract: every test-created temporary tree now uses one shared after-each cleanup helper, including failure and symlink fixtures, and a contract test prevents direct unmanaged temporary-directory creation from returning.
140
+ - πŸ§ͺ Completed the remaining T-13 scenario-matrix regressions for method-named/deep parameterized paths, all documented format and upper-bound mappings, Zod `fromJSONSchema` behavior parity, and deterministic output from both schema engines.
141
+ - πŸ“š Synchronized current documentation with the completed Phase 1 implementation: status labels, actual source/test layout and pipeline boundaries, generated golden examples, facade/component-reuse behavior, dependency wording, and safety/performance claims now match the repository; added a contract test for documentation links, completion state, concrete paths, and canonical output drift.
142
+ - πŸ”„ File-backed reverse conversion now safely applies manifest `$ref` placement, schema `set`/`remove`/frozen-node overlays, operation overlays, and response overlays after Zod re-serialization while preserving a developer-selected different component reference.
143
+ - 🧬 Runtime endpoint and component schemas now take precedence over manifest schema snapshots, including developer edits that switch a `$ref` to a different component.
144
+ - πŸ”„ File-based reverse conversion now re-serializes edited endpoint body, parameter, and response Zod schemas through Engine β‘  with request-input and response-output semantics across OpenAPI 3.x and Swagger 2.0.
145
+ - 🧱 File-based reverse conversion now securely imports emitted component modules, converts edited Zod schemas to the source API dialect, and preserves references between imported components.
146
+ - πŸ”„ File-based reverse conversion now securely imports generated endpoint TypeScript modules and lets edited km-api method, path, operation ID, summary, description, tags, and deprecation metadata override manifest snapshots.
147
+ - ⬆️ Updated the km-api dependency and peer dependency to `^0.4.1`; generated deprecated OpenAPI operations now emit `deprecated: 'YES'`.
148
+ - πŸ“– Corrected reverse-conversion documentation to keep `deprecated` separate from `disable`.
149
+ - πŸ” Generated endpoint auth metadata now uses km-api's required `'YES' | 'NO'` values.
150
+ - πŸ“– Corrected conversion documentation to describe km-api auth as `'YES' | 'NO'` rather than a boolean.
151
+ - πŸ“š Updated component documentation to cover implemented exact, nested, and cyclic imports.
152
+ - ℹ️ Deprecated operations remain preserved in the validated IR; they are not conflated with km-api's distinct `disable` status.
153
+ - πŸ“š Updated the roadmap and dependency standards to reflect the published `km-api@0.4.1` npm dependency.
154
+ - πŸ“š Corrected the architecture documentation to reference the implemented local-reference resolvers.
155
+ - πŸ”€ Operation-level parameters now correctly override path-level parameters.
156
+
157
+ ### πŸ› Fixed
158
+ - πŸ› Release validation now audits the release commit itself and accepts the required fresh, empty `Unreleased` section after a version is cut instead of incorrectly demanding an unreleased change.
159
+ - πŸ› Canonical generation and round-trip comparison now use explicit code-unit ordering instead of host-locale collation, keeping output identical across machines and locales.
160
+ - πŸ› Coverage configuration now follows Vitest 4's include-all-source contract instead of using the removed `coverage.all` option, so the pinned release typecheck and coverage gate remain executable.
161
+ - πŸ› T-10 manifests now retain source path order, empty Path Items, and explicit empty schema-component containers; reverse conversion also preserves boolean/tuple/local-definition schema syntax, schema-less media types, absent optional flags, and unconstrained request bodies instead of silently normalizing or deleting them.
162
+ - πŸ› T-11 reverse regeneration now keeps source-order-sensitive collision plans and every fixture/layout/component tree byte-identical, recognizes exact `z.never()` and plain `z.enum()` schemas, normalizes generated scalar intersections without round-trip drift, emits whitespace-clean canonical endpoint files, and canonicalizes emitted Zod map/literal key order while reverse conversion conditionally retains source-only structure (boolean spelling, `required` order, empty maps/definitions, object openness, draft tuples, and reference-free frozen schemas) without overriding developer schema, membership, tuple-member, validation, or strictness edits.
163
+ - πŸ› T-5/T-6 endpoint planning now keeps method directories leaf-only when literal path segments equal HTTP methods and disambiguates endpoint directories that would collide structurally with `.zopia-manifest.json`; component name `index.ts` is rejected before writes because it conflicts with the component barrel.
164
+ - πŸ› T-7/T-8/T-9 generation now rejects null or malformed schema-component shapes, keeps names already ending in `Schema` from gaining a second suffix, applies the complete Engine β‘‘ constraint/annotation behavior to emitted components, enforces `$ref` sibling constraints, and ignores `$ref`-looking literal/default/extension data while selecting real component importsβ€”even beneath literal-looking property names such as `default`.
165
+ - πŸ› Engine β‘‘ now reports malformed definition containers/entries and nested `null` schema nodes with exact warnings instead of silently ignoring them or throwing, and `uniqueItems` now rejects cyclic, coercible, BigInt, and other non-JSON candidates without making `safeParse()` throw.
166
+ - πŸ› OpenAPI normalization now rejects ambiguous version fields and dialect-incompatible or typoed root fields instead of selecting one dialect or silently dropping unsupported document data; Swagger 2.0 array parameters also validate every nested Items Object type, and malformed `consumes`/`produces` lists fail instead of accepting illegal item schemas or silently selecting fallback media types.
167
+ - πŸ› Engine β‘‘ now emits native `z.ipv4()` and `z.ipv6()` schemas instead of silently falling back to unrestricted strings after calling the removed Zod string `.ip()` method.
168
+ - πŸ› Engine β‘  now localizes non-JSON Zod defaults and metadata as unrepresentable schema sites with exact warning pointers instead of silently normalizing non-finite values, dropping functions, or throwing for symbols and other unsupported values.
169
+ - πŸ› Engine β‘‘ now applies `nullable` and `default` to the complete local-reference schema, rejects non-JSON defaults, constants, enum members, and annotations with structured warnings, preserves structured enums by JSON equality, and retains OpenAPI access/deprecation/XML plus vendor-extension annotations in generated Zod metadata.
170
+ - πŸ› Reverse conversion now rejects malformed edited endpoint path templates, path-parameter/template mismatches, dialect-invalid response statuses/descriptions, duplicate reconstructed operation IDs, edited path/method collisions, and invalid in-memory manifest operations instead of returning invalid OpenAPI documents; runtime collisions are classified as generated-module failures.
171
+ - πŸ› Source contract validation now rejects path keys containing query strings or fragments, unsupported/typoed path-item fields, missing or unrelated path parameters, dialect-mixed parameter/request/response shapes, multiple Swagger body parameters, illegal Swagger parameter types/locations, and out-of-range response codes instead of silently dropping invalid operation data.
172
+ - πŸ› Corrected Engine β‘‘ tuple semantics so prefix positions are optional unless required by `minItems`, tuple `minItems`/`maxItems` are enforced with valid refinements, every matching `patternProperties` schema is applied, unmatched and required-but-undeclared keys follow `additionalProperties`, and arbitrary local `$ref` siblings intersect instead of overwriting referenced constraints.
173
+ - πŸ› Made endpoint planning collision-safe for repeated slashes, root-like paths, case-only path differences, component artifact conflicts, and colliding derived operation IDs; explicit operation IDs remain authoritative, root endpoint component imports now use their actual planned depth, and generated paths/components are rejected when they are not portable across supported filesystems.
174
+ - πŸ› Local references now decode URI-fragment percent escapes and distinguish direct components from nested component pointers; nested pointers are inlined without inventing nonexistent component imports.
175
+ - πŸ› File-backed reverse conversion now keys module refreshes by generated-file content, so same-size edits with restored timestamps are not hidden by the runtime module cache.
176
+ - πŸ› Engine β‘’ now validates all endpoint renders before writing component artifacts, classifies endpoint files beneath an OpenAPI `/components` path as endpoints rather than mistaking every `components/` prefix for a generated schema artifact, and reports syntactically valid non-object JSON as `ZOPIA_SPEC_INVALID` instead of invalid JSON.
177
+ - πŸ› Reverse dialect translation now preserves literal example/default/enum data, nullable `$ref` semantics, combined exclusive bounds, and every Swagger `consumes`/`produces` media type while omitting 3.1-only document fields from 3.0 output.
178
+ - πŸ› File-backed reverse conversion now replaces stale media types and examples after generated-code edits, honors Swagger form-to-body transitions and legacy response examples, and rejects Swagger cookie/object parameters instead of emitting invalid documents.
179
+ - πŸ› Escaped JSON Pointer component and reusable-object names now resolve correctly; direct component aliases preserve `$ref` siblings, and generated `~` directories can be imported at runtime.
180
+ - πŸ› Multi-config endpoint modules now select the named config matching the manifest operation before an unrelated default export.
181
+ - πŸ› Direct component aliases now use distinct lazy Zod schemas so reverse conversion can distinguish an unchanged alias from a developer-selected target.
182
+ - πŸ› Runtime endpoint imports now support generated directories containing `{path}` segments, and Swagger body schemas are emitted from normalized operation contracts instead of falling back to `z.any()`.
183
+ - πŸ› Generated component schemas now import `additionalProperties` references, keep deeply nested cycles lazy, preserve open-object behavior, annotations, property-count bounds, and flexible tuple cardinality, and round-trip unique arrays plus structured `const`/`enum` values.
184
+ - πŸ› OpenAPI 3.1 Zod conversion now emits JSON Schema 2020-12 shapes instead of falling through to legacy tuple forms.
185
+ - πŸ› Generated component modules and their barrel now contain real newlines, keeping the emitted TypeScript executable at runtime.
186
+ - πŸ› JSON Schema conversion now warns for malformed `not` schemas, ignores malformed mixed `dependentRequired` entries instead of partially enforcing them, and uses own-property dependency checks.
187
+ - πŸ› Manifest reverse conversion now preserves non-schema OpenAPI component sections and reusable Swagger parameter and response definitions.
188
+ - πŸ› Generated endpoint auth is now `NO` when an OpenAPI security requirement contains an empty alternative that permits anonymous access.
189
+ - πŸ› Endpoint generation now honors `useComponentAsReference` for operation- and path-level parameter schemas, preserves nested component references in endpoint schemas, imports their component definitions, and inlines direct or nested local schema references when component imports are disabled.
190
+ - πŸ› OpenAPI operation collection now resolves parameter references before duplicate detection and override merging; referenced Swagger body and form-data parameters are extracted correctly.
191
+ - πŸ› JSON Schema conversion now honors OpenAPI 3.0 `nullable: true` around the complete schema and warns for malformed nullable flags.
192
+ - πŸ› JSON Schema conversion now preserves sibling constraints alongside `enum`, `const`, `oneOf`, `anyOf`, and `allOf`, and validates structured enum/constant values using order-independent JSON object equality.
193
+ - πŸ› JSON Schema conversion now preserves empty `prefixItems` and legacy tuple definitions, including typed or forbidden `items`, `additionalItems`, and `unevaluatedItems` rest values.
194
+ - πŸ› JSON Schema conversion now honors `false` boolean schemas in `contains` and `propertyNames`, warns for malformed values, and no longer reports empty schemas as unsupported types.
195
+
196
+ ### πŸ›‘οΈ Security
197
+ - πŸ›‘οΈ Updated and locked the test dependency tree to a zero-vulnerability npm audit resolution while retaining the declared Node 20 compatibility range.
198
+ - πŸ›‘οΈ Warning deduplication now uses collision-free structured identities, and formatted diagnostics/comments escape control characters in locations while sanitizing control characters in messages.
199
+ - πŸ›‘οΈ Manifest file ownership now detects case-insensitive path collisions before trees are written on case-sensitive hosts.
200
+ - πŸ›‘οΈ File-backed reverse conversion now preflights every endpoint and emitted-component path before importing code, reports missing manifests as `ZOPIA_DOCS_MISSING_MANIFEST`, and reports missing or renamed generated files as `ZOPIA_DOCS_MANIFEST_MISMATCH`.
201
+ - πŸ”’ Generated component and parameter Zod shapes now use computed property keys, while example metadata is reconstructed with `JSON.parse`, preserving `__proto__` as ordinary data at runtime.
202
+ - πŸ”’ Endpoint generation now preserves prototype-like request example names without mutating the examples object's prototype or dropping metadata.
203
+ - πŸ”’ Endpoint generation now escapes line terminators in source metadata comments, preventing malformed or injected generated TypeScript.
204
+ - πŸ”’ Reverse conversion now rejects unsafe or duplicate manifest operations and prevents overlays from replacing canonical OpenAPI fields.
205
+ - πŸ›‘οΈ JSON Schema conversion now validates numeric, size, pattern, object, and array keyword values before generating Zod code, warns instead of emitting malformed expressions, and ignores constraints on inapplicable instance types.
206
+ - πŸ”’ Reverse conversion restores manifest overlays with prototype-safe property definition.
207
+ - πŸ›‘οΈ Component generation now preserves OpenAPI 3.1 boolean schemas instead of converting them to unconstrained schemas.
208
+ - πŸ›‘οΈ Added validation for malformed schemas, unsafe paths, references, identifiers, operation IDs, and facade properties.
209
+ - πŸ›‘οΈ JSON Pointer resolution now rejects inherited object properties.
210
+
211
+ ## [0.0.1] - 2026-09-24
212
+
213
+ ### βœ… Released
214
+ - πŸ“š Published the Phase 0 documentation and standards baseline.
215
+ - πŸ“¦ Switched to the published `km-api@^0.4.0` npm dependency.
216
+ - 🧭 Established the Phase 1 public API and implementation contract.
217
+
218
+ ### ✨ Added
219
+
220
+ - πŸ“š Complete project documentation standard under [`docs/`](docs/) covering:
221
+ overview, targets, roadmap, architecture, concepts, the four conversion
222
+ engines, the api-docs output format, components, configuration, usage,
223
+ testing strategy, and engineering standards.
224
+ - 🏠 Project [`README.md`](README.md) with feature overview, quick look, and
225
+ the documentation map.
226
+ - πŸ“„ [`CHANGELOG.md`](CHANGELOG.md) using the Keep-a-Changelog format.
227
+ - πŸ” [`LICENSE`](LICENSE) (MIT) and [`.gitignore`](.gitignore) for the
228
+ Bun / TypeScript workspace.
229
+
230
+ ### πŸ“ Decisions
231
+
232
+ - πŸ”‘ Recorded the first architecture decisions (**D-01 … D-15**) in
233
+ [docs/12-standards.md β†’ Key decisions](docs/12-standards.md#-key-decisions).
234
+
235
+ ### πŸ”„ Changed
236
+ - πŸ“– Documented the optional ergonomic facade and explicit bracket notation for path parameters.
237
+
238
+ - πŸ”— **km-api is now vendored as a git submodule** (`km-api/`, branch
239
+ `feat/open-unions-v0-4-0`) β€” the complete 0.4.0 change set (10 src/test
240
+ files + `CHANGES.md` / `README.md` / `rules.md`; **170/170 tests, `tsc`
241
+ clean**) is committed in the clone's own `.git`, kept local and **never
242
+ pushed or published** until the final release step. The earlier handoff
243
+ patch file (`0001-feat-v0.4.0-...patch`) is removed β€” superseded by the
244
+ submodule. Final step (D-15): push the branch β†’ publish `km-api@0.4.0` β†’
245
+ remove the submodule β†’ `km-api: ^0.4.0` from npm.
246
+ - 🀝 **Aligned with km-api 0.4.0** (`komeilm76/km-api` β€” the additive
247
+ release now lives in this repository as the `km-api/` git submodule on
248
+ branch `feat/open-unions-v0-4-0`, committed locally and pending the final
249
+ push + publish β€” see above and D-15):
250
+ `TRACE` method, any custom numeric status code + `default` response key,
251
+ open (any-MIME) content types, and a real `operationId` field.
252
+ Consequences for zopia: all of that is now **emitted as code** β€”
253
+ `R-642` redefined as the km-api 0.4.0 typecheck contract (D-14); manifest
254
+ keys `skipped` / `requestMediaType` / `responseMediaType` removed;
255
+ `responseOverlay` narrowed to response facts with no km-api home (today:
256
+ response `headers`, R-754); peer dependency β†’ `km-api ^0.4`; test
257
+ scenarios S-26/S-68/S-69 updated to the emission path.
258
+
259
+ ### πŸ› Fixed
260
+
261
+ - 🎯 Fourth pass β€” km-api 0.4.0 precision audit (every claim re-checked
262
+ against the submodule source line by line):
263
+ - **security requirements are now part of the contract** β€” km-api's config
264
+ stores only the `auth` boolean, so the actual requirement lists live in
265
+ the manifest: new `defaultSecurity` (spec-level) and `apis[].security`
266
+ (per-operation, incl. explicit `[]`) fields (R-653/R-656); the missing
267
+ OpenAPI 3.x `security` normalization row added (v2 row corrected);
268
+ scenario S-71 pins a multi-scheme + scopes + `security: []` round-trip
269
+ - `204 β†’ z.void()` reworded β€” that is **zopia's** marker; km-api's own
270
+ README examples use `z.object({})` for 204 (both typecheck, but the
271
+ docs no longer cite a non-existent km-api convention)
272
+ - method enumeration now uses **km-api's real `IMethod` order**
273
+ (`get, post, put, delete, head, options, patch, trace`) everywhere β€”
274
+ R-401's canonical file order unified with it (was three different
275
+ orderings across four places)
276
+ - T-7's practical-content list gained `operationId`; stray "latest"
277
+ version references normalized to `0.4.x`
278
+ - πŸ” Third documentation review pass (cross-audited against the km-api 0.4.0
279
+ submodule source and live Zod 4.6.5 probes):
280
+ - `z.map()` corrected to **unrepresentable** (R-614) β€” only `z.record()`
281
+ maps to `additionalProperties` (R-616); `IExamplesMap` is the real
282
+ km-api examples type (not `IEndpointExamples`)
283
+ - stale `km-api ^0.3` references corrected to `0.4.x` (Overview stack
284
+ table, Usage install table); `responseContentType` described with the
285
+ open 0.4.0 union (not the old closed-union guard)
286
+ - fixture examples: `role` gains `.optional()` (it is not in the Admin
287
+ API's `required` list, R-623); the Usage end-to-end tree now shows all
288
+ four operations
289
+ - duplicated blocks removed (Architecture module-tree `ir/`, Roadmap
290
+ Phase 2 bullet); changelog section markers normalized to R-174
291
+ - round-trip property sketch aligned with the public API (`outDir` +
292
+ R-111 temp dirs, no invented `tree.dir`); D-06 / 07 "manifest always
293
+ written" softened to "by default" (the `manifest` option exists β€”
294
+ Configuration); R-714's disambiguation invariant scoped to the endpoint
295
+ area (component dirs also hold `index.ts`)
296
+ - README project structure and 09's engine-β‘  option pointer corrected
297
+ - πŸ›οΈ Deeper review against primary sources (zod.dev, km-api `0.3.3` source,
298
+ OpenAPI specs) β€” second round:
299
+ - **km-api closed unions are now the documented emission boundary (R-642,
300
+ D-14)** β€” read from `km-api`'s source: `IMethod` has **no `trace`**
301
+ (TRACE operations are skipped + manifest `skipped[]`);
302
+ `IHttpStatusCode` excludes `419`/`427`/`444`/`499`/`509`/`512+` **and
303
+ `default`** (such responses β†’ manifest `responseOverlay`); content types
304
+ are closed unions (exotic media types β†’ field omitted, actual type kept
305
+ in the manifest)
306
+ - new manifest keys: `skipped`, `apis[].requestMediaType` /
307
+ `responseMediaType`, `apis[].responseOverlay` (R-754)
308
+ - engine β‘  adopts Zod's native **`io` parameter** (R-615): request schemas
309
+ convert with `io: 'input'` (defaulted fields naturally stay out of
310
+ `required`), responses with `io: 'output'` β€” replaces the ad-hoc
311
+ `required` normalization
312
+ - R-614 now lists Zod's **official unrepresentable set** (incl.
313
+ `z.void()`, `z.date()`, `z.int64()`, …); 204 β†’ `z.void()` explicitly
314
+ detected *before* engine β‘  (R-654)
315
+ - R-612/R-633: metadata flows through **`.meta({ title, description,
316
+ examples })`** (verified verbatim in output), not comments
317
+ - `format: 'byte'` β†’ `z.base64()`; unmapped formats generalized to any base
318
+ type; Swagger 2.0 `int32/int64` primitive params pinned (with
319
+ `ZOPIA_WARN_INT64`)
320
+ - non-schema refs (reusable parameters/responses) inlined by the normalizer
321
+ (R-402 scope); malformed or unsupported path-item references are rejected; 3.1
322
+ `webhooks` β†’ warning
323
+ - km-api cheatsheet: source-verified closed-union table, type-level-only
324
+ `makeApiConfig`, `operationId` is not a km-api field
325
+ - new test scenarios S-26/S-52/S-68…S-70 and rule R-126 (golden trees must
326
+ **typecheck** against installed km-api)
327
+ - πŸ“ Document review against the real libraries (Zod **4.6.5**, run locally):
328
+ - engine β‘  now specifies **R-618** β€” stripping of Zod's redundant
329
+ built-in `format`+`pattern` pairs and safe-integer sentinel bounds
330
+ (`Β±(2β΅Β³βˆ’1)`), so output stays spec-clean and round-trips exact
331
+ - engine β‘‘: removed the non-existent `z.string().openFormat()` mapping β€”
332
+ custom formats become `z.string()` + warning + manifest overlay; `time`
333
+ and `url`/`uri` alias drift handled by overlay (R-627/R-635)
334
+ - `z.set` documented as **unrepresentable** (`{}` + warning), not as
335
+ `uniqueItems`; `z.map`/`z.record` mapped to their native Zod shape
336
+ - engine β‘£ serializer value normalizations pinned (R-654): sentinel
337
+ bounds, const-literal unions β†’ `enum`, defaulted keys out of `required`
338
+ - manifest redesigned (R-751…R-753): full component schemas **always**
339
+ carried, per-API `refs` pointers restore `$ref` placement, `overlay`
340
+ entries restore non-representable keywords β€” the reverse trip is now
341
+ lossless in **all** modes, not only with components emitted
342
+ - Swagger 2.0 `examples` (legacy media-type β†’ value shape) normalization
343
+ corrected; v2 nullability claim corrected
344
+ - status banner, module tree, rule numbering, and cross-links audited
345
+ and made consistent
346
+
347
+ ### 🚧 Planned (Phase 1 implementation)
348
+
349
+ - πŸ”„ Conversion engines: `zod β†’ JSON Schema`, `JSON Schema β†’ zod`,
350
+ `OpenAPI β†’ api docs`, `api docs β†’ OpenAPI`.
351
+ - πŸ“‚ `directory` and `flat` api-docs layouts, `.ts` index files built with
352
+ `makeApiConfig()` from `km-api`.
353
+ - 🧱 `insertComponents` and `useComponentAsReference` options.
354
+ - πŸ§ͺ Vitest suite covering the full scenario matrix.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 komeilm76
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ <div align="center">
2
+
3
+ # 🧬 zopia
4
+
5
+ **Type-safe OpenAPI ↔ Zod toolkit** for generating, validating, and transforming API schemas.
6
+
7
+ `Swagger 2.0` Β· `OpenAPI 3.x` Β· `JSON Schema` Β· `Zod v4` Β· `km-api`
8
+
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9%2B-blue.svg)](https://www.typescriptlang.org/)
11
+ [![Zod](https://img.shields.io/badge/Zod-4.x-purple.svg)](https://zod.dev/)
12
+ [![km-api](https://img.shields.io/badge/km--api-0.4.x-0ea5e9.svg)](https://www.npmjs.com/package/km-api)
13
+ [![Runtime](https://img.shields.io/badge/Runtime-Bun%201.x-black.svg)](https://bun.sh/)
14
+ [![Tests](https://img.shields.io/badge/Tests-vitest-10b981.svg)](https://vitest.dev/)
15
+
16
+ βœ… **Status β€” Phase 3 complete Β· v0.3.0 released**
17
+
18
+ </div>
19
+
20
+ ---
21
+
22
+ ## ✨ What is zopia?
23
+
24
+ **zopia** turns the documents your API already has β€” `swagger.json` / OpenAPI
25
+ files β€” into **type-safe, ready-to-use endpoint code**, and turns that code back
26
+ into a spec. It is the bridge between four formats an API team lives in:
27
+
28
+ ```text
29
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β‘’ openapi β†’ api docs β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
30
+ β”‚ swagger.json│───────────────────────▢│ api_docs/** β”‚
31
+ β”‚ (v2 / v3) │◀───────────────────────│ .ts Β· km-api Β· zodβ”‚
32
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β‘£ api docs β†’ openapi β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
33
+
34
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β‘  zod β†’ JSON Schema β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
35
+ β”‚ Zod v4 │───────────────────────▢│ JSON Schemaβ”‚
36
+ β”‚ schemas│◀───────────────────────│ β”‚
37
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β‘‘ JSON Schema β†’ zod β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
38
+ ```
39
+
40
+ Developers stop hand-writing validation, schemas, and documentation. zopia
41
+ generates **valid, documented, type-safe** artifacts in seconds β€” and every
42
+ artifact can be converted back, so nothing is ever lost.
43
+
44
+ ## 🎯 Why zopia?
45
+
46
+ | πŸ’Έ Pain today | πŸ› οΈ What zopia does |
47
+ | --- | --- |
48
+ | The spec and the code drift apart | Code is **generated from** the spec β€” or the spec is **regenerated from** the code |
49
+ | Hand-written validation is slow and error-prone | **Zod v4** schemas produced from JSON Schema keywords, losslessly |
50
+ | Reused components are copy-pasted everywhere | `$ref` graphs (component β†’ component) resolved into clean imports |
51
+ | Swagger 2.0 ↔ OpenAPI 3.x differences are confusing | One internal model normalizes **both** dialects |
52
+
53
+ ## πŸš€ Features
54
+
55
+ - πŸ”„ **Four conversion engines**
56
+ 1. `zod β†’ JSON Schema` β€” powered by Zod v4's built-in `z.toJSONSchema()`
57
+ 2. `JSON Schema β†’ zod` β€” custom emitter producing idiomatic, readable Zod v4 code
58
+ 3. `OpenAPI β†’ api docs` β€” Swagger 2.0 & OpenAPI 3.0/3.1 β†’ tree of `.ts` endpoint files
59
+ 4. `api docs β†’ OpenAPI` β€” regenerate a full spec from the generated tree (lossless)
60
+ - πŸ“– **Dual spec support** β€” `swagger: "2.0"` and `openapi: "3.0.x" / "3.1.x"`
61
+ - 🧱 **`$ref` resolution** β€” nested component references and circular schemas (via `z.lazy()`)
62
+ - πŸ“‚ **Two output layouts** β€” `directory` (path β†’ nested folders) and `flat` (one folder per endpoint)
63
+ - 🧩 **Component options** β€” `insertComponents` and recursive endpoint component-reference imports are implemented; direct and mutual cyclic imports use lazy schemas
64
+ - ⚑ **km-api native** β€” every `index.ts` builds its endpoint with `makeApiConfig()` from `km-api` (0.4.x)
65
+ - πŸ”’ **Lossless round-trips** β€” a hidden manifest (`.zopia-manifest.json`) keeps every conversion reversible
66
+ - πŸ§ͺ **Tested by design** β€” pinned Vitest 4.1.11 suite covering the full identified scenario matrix, run with Bun
67
+ - πŸ“– **100% JSDoc** β€” every public symbol and callable shape is contract-audited; every decision is recorded
68
+
69
+ ## πŸ“– Quick look
70
+
71
+ > The public API below is the **Phase 1 contract** and is covered by tests.
72
+ > See [docs/10-usage.md](docs/10-usage.md).
73
+
74
+ ```ts
75
+ import {
76
+ zodToJsonSchema, // β‘  Zod v4 β†’ JSON Schema
77
+ jsonSchemaToZod, // β‘‘ JSON Schema β†’ Zod v4 (code + runtime schema)
78
+ openApiToApiDocs, // β‘’ swagger.json / openapi.json β†’ api_docs/**
79
+ apiDocsToOpenApi, // β‘£ api_docs/** β†’ openapi.json
80
+ } from 'zopia';
81
+
82
+ // β‘’ Generate a tree of type-safe endpoint files from any spec
83
+ await openApiToApiDocs('swagger.json', {
84
+ mode: 'directory', // 'directory' (default) | 'flat'
85
+ insertComponents: false, // emit components/** (default false)
86
+ useComponentAsReference: false, // import emitted components (default false)
87
+ });
88
+ // └─ one <path>/<method>/index.ts per operation + .zopia-manifest.json
89
+
90
+ // β‘£ Convert the tree back into a spec
91
+ const { openapi } = await apiDocsToOpenApi('api_docs', { version: '3.1' });
92
+ ```
93
+
94
+ ## πŸ“š Documentation
95
+
96
+ Everything about the project β€” targets, architecture, conversion rules,
97
+ output format, configuration, testing, standards β€” lives in [`docs/`](docs/).
98
+
99
+ | πŸ“„ Document | Contents |
100
+ | --- | --- |
101
+ | 🧭 [Overview](docs/01-overview.md) | What zopia is, the problem it solves, principles, non-goals |
102
+ | 🎯 [Targets](docs/02-targets.md) | The explicit, testable targets of this project |
103
+ | πŸ—ΊοΈ [Roadmap](docs/03-roadmap.md) | Phases, milestones, definition of done |
104
+ | πŸ—οΈ [Architecture](docs/04-architecture.md) | Modules, pipeline, internal model, error model, safety |
105
+ | 🧩 [Concepts](docs/05-concepts.md) | Glossary β€” Swagger 2.0, OpenAPI 3.x, JSON Schema, `$ref`, Zod v4, km-api |
106
+ | πŸ”„ [Conversions](docs/06-conversions.md) | The four engines: algorithms, mapping tables, edge cases |
107
+ | πŸ“„ [API docs format](docs/07-api-docs.md) | `directory` & `flat` layouts, `index.ts` contract, manifest |
108
+ | 🧱 [Components](docs/08-components.md) | `insertComponents` / `useComponentAsReference`, `$ref` graphs |
109
+ | βš™οΈ [Configuration](docs/09-configuration.md) | Full option reference, defaults, validation rules |
110
+ | πŸš€ [Usage](docs/10-usage.md) | Installation, programmatic API, CLI, end-to-end example |
111
+ | πŸ§ͺ [Testing](docs/11-testing.md) | Vitest strategy, scenario matrix, fixtures, coverage gates |
112
+ | πŸ“ [Standards](docs/12-standards.md) | Code, JSDoc, commits, changelog, releases, key decisions |
113
+
114
+ ## πŸ—οΈ Project structure
115
+
116
+ ```text
117
+ zopia/
118
+ β”œβ”€β”€ README.md # 🏠 This file
119
+ β”œβ”€β”€ CHANGELOG.md # πŸ“œ Keep-a-Changelog history
120
+ β”œβ”€β”€ LICENSE # πŸ” MIT
121
+ β”œβ”€β”€ package.json # πŸ“¦ npm dependency on km-api ^0.4.1
122
+ β”œβ”€β”€ docs/ # πŸ“š Project documentation (the standard)
123
+ β”‚ β”œβ”€β”€ README.md # πŸ“– Documentation map
124
+ β”‚ β”œβ”€β”€ 01-overview.md # 🧭 Overview
125
+ β”‚ β”œβ”€β”€ 02-targets.md # 🎯 Targets
126
+ β”‚ β”œβ”€β”€ 03-roadmap.md # πŸ—ΊοΈ Roadmap
127
+ β”‚ β”œβ”€β”€ 04-architecture.md # πŸ—οΈ Architecture
128
+ β”‚ β”œβ”€β”€ 05-concepts.md # 🧩 Concepts & glossary
129
+ β”‚ β”œβ”€β”€ 06-conversions.md # πŸ”„ Conversion engines
130
+ β”‚ β”œβ”€β”€ 07-api-docs.md # πŸ“„ API docs format
131
+ β”‚ β”œβ”€β”€ 08-components.md # 🧱 Components
132
+ β”‚ β”œβ”€β”€ 09-configuration.md # βš™οΈ Configuration
133
+ β”‚ β”œβ”€β”€ 10-usage.md # πŸš€ Usage
134
+ β”‚ β”œβ”€β”€ 11-testing.md # πŸ§ͺ Testing
135
+ β”‚ └── 12-standards.md # πŸ“ Engineering standards
136
+ β”œβ”€β”€ src/ # βš™οΈ Published package source
137
+ └── tests/ # πŸ§ͺ Unit, integration, contract & round-trip suites
138
+ ```
139
+
140
+ ## πŸ§ͺ Development
141
+
142
+ zopia is a **Bun-first** project (see [docs/12-standards.md](docs/12-standards.md)):
143
+
144
+ ```bash
145
+ bun install --frozen-lockfile # πŸ“¦ reproducible install from bun.lock
146
+ bun run typecheck # βœ… strict TypeScript
147
+ bun run test # πŸ§ͺ vitest (all scenarios)
148
+ bun run coverage # πŸ“ˆ enforced coverage report
149
+ bun run package:check # πŸ“¦ pack, install, import, and run the npm artifact
150
+ bun run release:check # 🚒 complete pinned-Bun prepublish gate
151
+ ```
152
+
153
+ > βœ… `bun run release:check` is the CI-equivalent prepublish check: it verifies
154
+ > the pinned Bun version and frozen lockfile, then runs TypeScript, Vitest,
155
+ > coverage, direct CLI generation/reverse smoke tests, and an isolated install
156
+ > of the exact npm archive with packed-library and packed-CLI smoke tests.
157
+
158
+ ## 🀝 Contributing
159
+
160
+ Read [docs/12-standards.md](docs/12-standards.md) first β€” it defines code style,
161
+ JSDoc rules, the commit convention, and the changelog rule. Every feature ships
162
+ **with** its documentation in the same commit.
163
+
164
+ ## πŸ“„ License
165
+
166
+ Released under the [MIT License](LICENSE) β€” the same standard as the rest of
167
+ the `km-*` package family.
package/bin/zopia.js ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const directory = dirname(fileURLToPath(import.meta.url));
7
+ const cli = resolve(directory, '..', 'src', 'cli.ts');
8
+ const child = spawn('bun', [cli, ...process.argv.slice(2)], { stdio: 'inherit' });
9
+ child.on('error', (error) => {
10
+ if (error.code === 'ENOENT') {
11
+ console.error('zopia requires Bun to run its CLI. Install Bun from https://bun.sh');
12
+ } else {
13
+ console.error(error.message);
14
+ }
15
+ process.exitCode = 1;
16
+ });
17
+ child.on('exit', (code, signal) => {
18
+ if (signal) process.kill(process.pid, signal);
19
+ else process.exitCode = code ?? 1;
20
+ });