Skip to content

feat: root-config filtering of channels/operations (AsyncAPI) and paths (OpenAPI)#431

Open
jonaslagoni wants to merge 16 commits into
mainfrom
enable_filtering
Open

feat: root-config filtering of channels/operations (AsyncAPI) and paths (OpenAPI)#431
jonaslagoni wants to merge 16 commits into
mainfrom
enable_filtering

Conversation

@jonaslagoni

Copy link
Copy Markdown
Contributor

What

Adds an opt-in, default-off root-config filter option that restricts code generation to a subset of an input document — specific AsyncAPI channels/operations or OpenAPI paths/operations — instead of everything. Filtering happens once, while the document is loaded, so every downstream generator (payloads, parameters, headers, types, channels, client, protocols) sees the already-subsetted document with no per-generator changes.

// OpenAPI: match against path template or operationId
{
  "inputType": "openapi",
  "inputPath": "./openapi.yaml",
  "filter": { "include": ["/users", "/users/**", "/orders"], "exclude": ["/users/{id}/audit"] },
  "generators": [ /* ... */ ]
}
// AsyncAPI: match against channel address, channel id, or operation id
{
  "inputType": "asyncapi",
  "inputPath": "./asyncapi.yaml",
  "filter": { "include": ["user/**", "orders/created"], "exclude": ["**/internal"] },
  "generators": [ /* ... */ ]
}

Component schemas (and, for AsyncAPI, messages) left orphaned by the filtering are pruned automatically. JSON Schema input has no filter (no channels/paths to filter).

Why

Users frequently want to generate code for only part of a large specification — a single API surface, one channel family, or excluding internal endpoints — without hand-editing the source document. The existing per-generator includeChannels/includeHeaders flags are on/off toggles, not per-item selectors. A single root-level filter, applied at load time, covers every generator and every protocol uniformly.

Changes

Config surface

  • New zodInputFilter (include/exclude glob string arrays, both defaulted) attached to the AsyncAPI and OpenAPI config branches only (src/codegen/types.ts).
  • minimatch@^9.0.5 added as a direct dependency (already hoisted transitively, browser-safe).
  • Regenerated schemas/*.json from the Zod source.

Matching primitive (src/codegen/filter.ts)

  • matchesFilter({candidates, include, exclude}) — minimatch globs; empty include = match-all, exclude applied after include, multi-candidate OR-match.
  • normalizeFilter / isFilterActive helpers and a shared recursive collectExtensionValues reachability walker.

AsyncAPI filter (src/codegen/inputs/asyncapi/filter.ts)

  • Computes keep-sets from the parsed model, applies version-aware JSON surgery (v2 publish/subscribe nesting; v3 top-level operations + dropping the redundant components.channels/operations/messages mirrors to avoid re-parse double-counting), prunes orphaned components.schemas, and re-parses with the loader's own parser instance. Works for v2 and v3.

OpenAPI filter (src/codegen/inputs/openapi/filter.ts)

  • In-place path/method deletion using an explicit HTTP-method whitelist (non-method path-item keys like parameters/summary preserved), derived-operationId matching, and reachability-based orphan pruning of components.schemas (3.x) / definitions (2.0) via x-modelgen-inferred-name.

Loader wiring

  • Filter threaded through all three context-building flows — CLI file (realizeGeneratorContext), Node in-memory (realizeInMemoryGeneratorContext), and browser (src/browser/generate.ts / src/browser/parser.ts). Loader signatures moved to object parameters; all call sites (incl. tests) updated. Loaders early-return the unfiltered document when the filter is inactive.

Example + docs

  • New examples/openapi-filtering/ (runnable, with README and committed output).
  • docs/configurations.md filter section + docs/inputs/asyncapi.md / openapi.md notes.

Testing

  • Unit (npm test): 36 new tests — glob semantics (test/codegen/filter.test.ts), AsyncAPI v2+v3 retention/dual-map/orphan pruning (test/codegen/inputs/asyncapi/filter.test.ts), OpenAPI v2+v3 path/method/derived-id/orphan pruning (test/codegen/inputs/openapi/filter.test.ts), and config parsing/defaults/branch-presence (test/codegen/configurations.spec.ts).
  • Runtime (test/runtime/typescript/test/filter.spec.ts): asserts kept surfaces are generated + behave, dropped surfaces are absent, and orphaned models are pruned, over real filtered generation.
  • Regression guard: all 78 existing generator snapshots unchanged, proving the no-filter path is byte-identical.

Verification run on this branch:

  • npm run build
  • npm run lint (eslint max-warnings 0 + typecheck:test)
  • npm test — 56 suites, 717 passed / 1 skipped, 78 snapshots
  • Runtime filter generation + filter.spec.ts (8 passed; payloads preset, no brokers required)

Notes

  • Not a breaking change. Additive and default-off: with no filter (every existing config) the loaders early-return before any filtering and output is unchanged — confirmed by the unchanged snapshots. Not a major version bump.
  • Runtime src/ drift (out of scope): regenerating the runtime project surfaced three pre-existing http_client.ts files that were stale relative to the merged HttpError feature (feat: typed HttpError + input-driven error handling #429). They are unrelated to filtering and were intentionally left out of this PR — they should be regenerated + committed separately.
  • prepare:pr was validated via its constituent steps (build → generate:assets → lint:fix → test:update → runtime generate); the npm ci step is skipped per the known runtime lockfile-drift caveat.
  • Follow-ups (explicitly out of scope): no CLI --filter flag, no regex/tag/predicate filters, no --dry-run report of what was filtered.

jonaslagoni and others added 15 commits July 23, 2026 19:33
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AsyncAPI (asyncapi-regular) and OpenAPI (new openapi-filter.json) filter
configs plus filter.spec.ts asserting kept surfaces exist and dropped
surfaces + orphaned models are absent. Specs currently RED against the
unfiltered generation, as intended.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…D GREEN)

filterAsyncapiJson does model-read keep-set computation, version-aware
JSON surgery (v2 publish/subscribe, v3 dual-map + components mirrors),
and orphan schema pruning. Wired into Node file/in-memory and browser
loaders with early-return on inactive filter. Loader signatures moved to
object params; all call sites updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… RED)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…aders (TDD GREEN)

filterOpenapiDocument does path/method deletion (explicit HTTP-method
whitelist, derived operationId matching) and reachability-based orphan
pruning of components.schemas/definitions via x-modelgen-inferred-name.
Wired into Node file/in-memory + browser OpenAPI loaders. Added
normalizeFilter so partial filters (root Zod defaults not re-materialized
by realizeConfiguration) don't crash. Loader signatures moved to object
params; all call sites updated. Runtime filter.spec now GREEN.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…all flows

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Regenerated all runtime configs cleanly (incl. the two filter configs) and
committed the filtered payload output. filter.spec.ts passes: kept surfaces
generated, dropped surfaces + orphaned models absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rated docs

markdown-toc appends a trailing newline on each run and oclif readme embeds the
local platform/node version; neither is a real change. Restored README.md,
contributing.md, migrations/v0.md, usage.md to base so the PR carries only the
intended filter docs + regenerated schemas.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
De-duplicated the recursive reachability collector shared by the AsyncAPI and
OpenAPI orphan-pruning helpers into a single collectExtensionValues primitive
in src/codegen/filter.ts, keyed on the extension name each parser uses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jonaslagoni
jonaslagoni requested a review from ALagoni97 as a code owner July 23, 2026 18:43
@vercel

vercel Bot commented Jul 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
the-codegen-project Ready Ready Preview, Comment Jul 23, 2026 7:45pm
the-codegen-project-mcp Ready Ready Preview, Comment Jul 23, 2026 7:45pm

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.

2 participants