Skip to content

Web playground (WASM) and merge correctness fixes - #12

Merged
includeamin merged 15 commits into
mainfrom
improve-http-request-handling
Oct 1, 2026
Merged

includeamin merged 15 commits into
mainfrom
improve-http-request-handling

Conversation

@includeamin

Copy link
Copy Markdown
Owner

Adds a browser playground for openapi-aggregator, hosted on GitHub Pages. It runs the same merge engine as the CLI, compiled to WebAssembly. The PR also fixes several merge-correctness issues in the CLI and library.

Merge and CLI fixes

  • CLI output format: the CLI now honours output.format from the config. -f still overrides it.
  • Stable output: component types are emitted in a fixed order, so the output no longer changes between runs.
  • Per-operation path merge: GET /users from one source and POST /users from another now merge into one path instead of conflicting.
  • Shared identical definitions: identical components and operations are shared instead of counting as conflicts. A component that only looks identical is still renamed if it references a renamed component.
  • Safe renames: renaming never overwrites an existing component (b_2_Pet is used when b_Pet is taken). Security-requirement keys and discriminator.mapping values are now updated along with $refs.
  • Security semantics: top-level security stays top-level only when every source declares the same requirements. Otherwise each source's requirements are copied onto its own operations, so no operation gains or loses authentication.
  • OpenAPI 3.1 and naming:
    • webhooks merge like paths, and components.pathItems is kept.
    • Sources that mix OpenAPI minor versions produce a warning.
    • Source names are cleaned up wherever they become part of a path or component name.
    • A URL source with no name is named after its host.
  • Strict config: unknown keys are rejected, a source must have exactly one of path or url, and headers is only allowed on URL sources.
  • Environment variables: ${VAR} in a URL source's url and header values is read from the environment. Expanded values never appear in error messages.
  • Loading: sources load concurrently with one shared HTTP client. A Swagger 2.0 file gets a clear "only OpenAPI 3.x is supported" error.
  • Release workflow: the version bump now commits Cargo.lock, and release notes are passed through env: instead of being pasted into the shell.

Workspace split

Crate Contents
crates/openapi-aggregator-core Config types, parsing, validation and merge. No I/O; CI checks it builds for wasm32-unknown-unknown.
crates/openapi-aggregator The existing library and CLI: file and HTTP loaders. Re-exports core, so openapi_aggregator::* paths keep working.
crates/openapi-aggregator-wasm A wasm-bindgen wrapper exposing parseConfig and aggregate.

Web playground (web/)

The app is plain TypeScript built with Vite.

  • Editors: a workspace of spec files (paste, upload, or import from a URL) and a YAML config editor that shows config errors inline. The config format is the same one the CLI reads.
  • Output: the merged YAML or JSON, an API reference rendered with Scalar, and a Problems tab listing errors and warnings.
  • Examples and sharing: six examples, one per feature. Share links store the compressed config and files in the URL hash.
  • Privacy:
    • Variables and the CORS proxy prefix are stored only in that browser and never included in share links.
    • A config opened from a share link fetches nothing until the user clicks Allow fetching, so a shared config can't send the viewer's variables to another server.

Breaking changes

These need a changelog note for 0.7.0.

  • openapi_aggregator::Error: the Parse, InvalidSpec, MergeConflict, Config and NoSources variants now sit behind Error::Core(..). Display messages are unchanged.
  • Configs with unknown keys, or a literal ${...} in a URL or header with no matching environment variable, now fail instead of being silently accepted.
  • Overlapping specs that used to fail under conflict_strategy: error may now merge cleanly.

Testing

  • Rust: 72 tests (cargo test --all-features). Clippy and fmt are clean.
  • Web: 51 vitest unit tests (1 skipped because it needs the network); the engine and examples tests load the real .wasm build. 4 Playwright end-to-end tests, including one that checks a shared config makes no request before consent.
  • Manual: checked in a browser for light and dark mode, a 375 px wide screen, the remote Petstore example, and share-link round trips.

Before merging

  • Set Settings → Pages → Build and deployment → Source to GitHub Actions. This is a one-time repo setting; without it, pages.yml can't deploy.

Known follow-ups (not in this PR)

  • Merge logic:
    • Renaming is slow for very deep $ref chains (about 1 s at depth 1,000).
    • Webhook operation tags aren't prefixed when tag_prefix: source_name is set.
    • Under rename, a moved path can duplicate an operationId. This already happened on main.
  • Pages workflow: the build job gets the pages and id-token permissions too; move them to the deploy job. Pages also deploys even if CI fails.
  • Proxy encoding: a proxy prefix ending in ?url= drops the target URL's own query string; the target URL should be encoded.
  • Publishing: the CLI crate can't be published to crates.io yet, because its dependency on core has no version.

- respect output.format from config in the CLI (-f still overrides)
- emit components in a stable order
- never overwrite an existing component when renaming (b_2_Pet)
- merge paths/webhooks per operation; identical definitions are shared
- sanitise source names used in paths/component names; URL sources default to host
- merge webhooks and top-level security; keep components.pathItems and extensions
- warn when sources mix OpenAPI minor versions (aggregate_with_report)
- reject unknown config keys and ambiguous sources with clear errors
- expand ${VAR} in HTTP source url and headers
- load sources concurrently with a shared HTTP client
… discriminator renames

- push a source's top-level security down to its operations when sources disagree
- rename security requirement keys and discriminator.mapping values with their components
- keep ${VAR} values out of HTTP error messages
…e edits

- configs opened from a share link don't fetch URL sources until the user allows it
- error messages show URL templates, never substituted variable values
- New file and Rename no longer overwrite or shadow an existing file
- the API reference shows the latest spec after its first load and retries a failed load
@includeamin
includeamin merged commit 518380c into main Oct 1, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant