Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a13d64c
Phase 1: RED tests for expanded config discovery search places
jonaslagoni Jul 23, 2026
36b289f
Phase 2: shared CONFIG_SEARCH_PLACES constant + doc mirrors
jonaslagoni Jul 23, 2026
ac6ec3a
Phase 3: RED tests for init guardrails and flag/answer composition
jonaslagoni Jul 23, 2026
a4853f9
Phase 4: init guardrails, flag/answer composition, protocols, gitignore
jonaslagoni Jul 23, 2026
cd7317e
Phase 5: RED test for empty-protocols channels warning
jonaslagoni Jul 23, 2026
29a842d
Phase 6: warn when channels generator produces no protocol functions
jonaslagoni Jul 23, 2026
70af44a
Phase 7: hide abstract codegen base command from manifest and docs
jonaslagoni Jul 23, 2026
b7fef41
Phase 8: fix examples/jsonschema-models to install, generate and build
jonaslagoni Jul 23, 2026
1ba7878
Phase 9: correct docs support matrix for OpenAPI channels/client
jonaslagoni Jul 23, 2026
06f2798
Phase 11: RED tests for AsyncAPI payload multi-message truncation
jonaslagoni Jul 24, 2026
ad6a74c
Phase 12: fix AsyncAPI payload multi-message truncation (skip-and-warn)
jonaslagoni Jul 24, 2026
abac367
Phase 13: RED tests for AsyncAPI headers oneOf union
jonaslagoni Jul 24, 2026
264707f
Phase 14: AsyncAPI headers oneOf union for multi-message channels
jonaslagoni Jul 24, 2026
6331107
Phase 15: RED tests for HTTP client baseURL from document servers
jonaslagoni Jul 24, 2026
95f2318
Phase 16: default HTTP client baseURL from document servers
jonaslagoni Jul 24, 2026
5b602a6
Phase 17: RED tests for OpenAPI extraction fidelity and warnings
jonaslagoni Jul 24, 2026
91bf903
Phase 18: OpenAPI extraction fidelity and warnings
jonaslagoni Jul 24, 2026
5cfb254
Phase 19: wire orphaned runtime suites into scripts and CI
jonaslagoni Jul 24, 2026
33ba42b
Phase 20: unfilter blackbox matrix, match input types, shard CI
jonaslagoni Jul 24, 2026
36f26bc
Phase 21: rewrite website workflow to actually trigger and build
jonaslagoni Jul 24, 2026
e2fd6cc
Phase 22: add examples smoke-test workflow
jonaslagoni Jul 24, 2026
f26dcf5
Phase 23: document input fidelity, baseURL precedence, init flags
jonaslagoni Jul 24, 2026
e69f775
Phase 25: regenerate assets (README tocs, prettier formatting)
jonaslagoni Jul 24, 2026
2cf93c2
Phase 26: regenerate runtime surfaces + verify full quality gate
jonaslagoni Jul 24, 2026
ae53c60
fix: exclude reply messages from the AsyncAPI channel header union
jonaslagoni Jul 24, 2026
4bdcf03
fix(ci): make examples smoke test robust
jonaslagoni Jul 24, 2026
23100e3
docs: note reply messages are excluded from the AsyncAPI header union
jonaslagoni Jul 24, 2026
a064b10
refactor: reduce AsyncAPI headers processor cognitive complexity
jonaslagoni Jul 24, 2026
f1493df
refactor(ci): run examples smoke test via 'npm run test:examples'
jonaslagoni Jul 24, 2026
a891b58
feat(ci): add generate:examples and refresh examples on release
jonaslagoni Jul 24, 2026
fa1b113
refactor(ci): fold generate:examples into generate:assets instead of …
jonaslagoni Jul 24, 2026
4cabd05
fix(ci): resolve packed CLI tarball path robustly in generateExamples
jonaslagoni Jul 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion .github/workflows/blackbox-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,24 @@ on:
types: [opened, reopened, synchronize, ready_for_review]
jobs:
test:
name: BlackBox testing
name: BlackBox testing - ${{ matrix.bucket }}
if: "github.event.pull_request.draft == false &&!((github.actor == 'asyncapi-bot' && startsWith(github.event.pull_request.title, 'ci: update global workflows')) || (github.actor == 'asyncapi-bot' && startsWith(github.event.pull_request.title, 'chore(release):')) || (github.actor == 'allcontributors' && startsWith(github.event.pull_request.title, 'docs: add')))"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# The full 12-config x 23-file matrix is sharded into config buckets so
# no single job runs the whole (npm-install-heavy) cross-product. Each
# bucket lists config-name substrings (see test/blackbox/test_files.ts).
include:
- bucket: channels
configs: 'channels'
- bucket: client
configs: 'client'
- bucket: models
configs: 'payload,modelina'
- bucket: headers-params-jsonschema
configs: 'headers,parameters,jsonschema'
steps:
- name: Checkout repository
uses: actions/checkout@v3
Expand All @@ -22,4 +37,6 @@ jobs:
- name: Build library
run: npm install && npm run build
- name: Test output
env:
BLACKBOX_CONFIG: ${{ matrix.configs }}
run: npm run test:blackbox
35 changes: 35 additions & 0 deletions .github/workflows/examples-testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Examples smoke testing

on:
pull_request:
paths:
- 'src/**'
- 'examples/**'
- 'package.json'
branches:
- main
types: [opened, reopened, synchronize, ready_for_review]

jobs:
examples:
name: Generate every example against the PR build
runs-on: ubuntu-latest
if: "github.event.pull_request.draft == false"
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check package-lock version
uses: asyncapi/.github/.github/actions/get-node-version-from-package-lock@master
id: lockversion
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "${{ steps.lockversion.outputs.version }}"
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
- name: Install root dependencies
run: npm ci
- name: Smoke-test every example
# Builds + packs the CLI and generates every example against it. The
# exact same command runs locally: `npm run test:examples`.
run: npm run test:examples
12 changes: 12 additions & 0 deletions .github/workflows/runtime-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ jobs:
service: ''
test-command: 'test:regular'
needs-service: false
- test-type: 'WebSocket'
service: ''
test-command: 'test:websocket'
needs-service: false
- test-type: 'Organization'
service: ''
test-command: 'test:organization'
needs-service: false
- test-type: 'Payload Types'
service: ''
test-command: 'test:payload-types'
needs-service: false
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand Down
50 changes: 19 additions & 31 deletions .github/workflows/website-pr-testing.yml
Original file line number Diff line number Diff line change
@@ -1,53 +1,41 @@
name: Test website

on:
pull_request_target:
pull_request:
paths:
- 'website'
- 'website/vercel.json'
- 'website/**'
- 'src/browser/**'
- 'esbuild.browser.mjs'
branches:
- main
types: [opened, reopened, synchronize, ready_for_review]

jobs:
lighthouse-ci:
name: Lighthouse CI
website-build:
name: Build website (incl. browser bundle)
runs-on: ubuntu-latest

if: "github.event.pull_request.draft == false"
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Check package-lock version
uses: asyncapi/.github/.github/actions/get-node-version-from-package-lock@master
id: lockversion
- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: "${{ steps.lockversion.outputs.version }}"
cache: 'npm'
cache-dependency-path: '**/package-lock.json'

- name: Website Install dependencies
id: first-installation-website
run: cd website && npm install --loglevel verbose
continue-on-error: true

- if: steps.first-installation-website.outputs.status == 'failure'
name: Website Clear NPM cache and install deps again
run: |
cd website && npm cache clean --force
cd website && npm install --loglevel verbose

- name: Lint website
run: cd website && npm run lint

- name: Install root dependencies
run: npm ci
- name: Build browser bundle
run: npm run build:browser
- name: Install website dependencies
run: cd website && npm ci
- name: Copy browser bundle into the website
run: cd website && npm run copy:bundle
- name: Verify the browser bundle was produced
run: test -f website/static/codegen.browser.mjs
- name: Build website
run: cd website && npm run build

- name: Await Vercel Preview
uses: patrickedqvist/wait-for-vercel-preview@v1.3.2
id: vercel
with:
token: ${{ secrets.GITHUB_TOKEN }}
max_timeout: 600
check_interval: 10
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ coverage
test/config/src/__gen__/
/playground
test/codegen/generators/*/output
.claude
.claude
# Packed CLI tarball from npm pack (e.g. test:examples)
the-codegen-project-cli-*.tgz
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ Connect AI assistants like Claude Code, Cursor, and Windsurf to The Codegen Proj








19 changes: 10 additions & 9 deletions docs/configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,22 @@ For detailed usage instructions and all available options, see the [CLI usage do

## Configuration File Lookup

If no explicit configuration file is sat, it will be looked for in the following order:
- package.json
If no explicit configuration file is sat, it will be looked for in the following order (earlier names take priority):
- codegen.json
- codegen.yaml
- codegen.yml
- codegen.js
- codegen.ts
- codegen.mjs
- codegen.cjs
- package.json (the `codegen` property)
- .codegenrc
- .codegenrc.json
- .codegenrc.yaml
- .codegenrc.yml
- .codegenrc.js
- .codegenrc.ts
- .codegenrc.mjs
- .codegenrc.cjs
- .config/codegenrc
- .config/codegenrc.json
Expand All @@ -50,13 +58,6 @@ If no explicit configuration file is sat, it will be looked for in the following
- codegen.config.ts
- codegen.config.mjs
- codegen.config.cjs
- codegen.json
- codegen.yaml
- codegen.yml
- codegen.js
- codegen.ts
- codegen.mjs
- codegen.cjs

## TypeScript Configuration

Expand Down
2 changes: 2 additions & 0 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ Prefix that follows specification is not enough though. Remember that the title








4 changes: 3 additions & 1 deletion docs/generators/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ All available generators, across languages and inputs:
| **Inputs** | [`payloads`](./payloads.md) | [`parameters`](./parameters.md) | [`headers`](./headers.md) | [`types`](./types.md) | [`channels`](./channels.md) | [`client`](./client.md) | [`models`](./models.md) | [`custom`](./custom.md) |
|---|---|---|---|---|---|---|---|---|
| AsyncAPI | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| OpenAPI | ✅ | ✅ | ✅ | ✅ | | | ✅ | ✅ |
| OpenAPI | ✅ | ✅ | ✅ | ✅ | | | ✅ | ✅ |
| JSON Schema | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |

> OpenAPI `channels` and `client` generate an HTTP client — see the [`openapi-http-client` example](../../examples/openapi-http-client/).

| **Languages** | [`payloads`](./payloads.md) | [`parameters`](./parameters.md) | [`headers`](./headers.md) | [`types`](./types.md) | [`channels`](./channels.md) | [`client`](./client.md) | [`models`](./models.md) | [`custom`](./custom.md) |
|---|---|---|---|---|---|---|---|---|
| TypeScript | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
7 changes: 7 additions & 0 deletions docs/getting-started/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ Add a configuration file, either manually or through the CLI;
codegen init
```

For non-interactive/CI use, pass the generators explicitly (the `languages` flag defaults to `typescript`), for example:
```sh
codegen init --no-tty --input-type asyncapi --input-file ./asyncapi.yml \
--include-payloads --include-channels --channels-protocols nats
```
At least one generator must be selected — `codegen init` exits with an error rather than writing a configuration that would generate nothing. For AsyncAPI `channels`/`client`, choose the messaging protocols with `--channels-protocols` (repeatable). See [the usage documentation](../usage.md#codegen-init) for the full flag list.

<div align="center">


Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started/generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ Different generators work with different input types:
| `parameters` | ✅ | ✅ | ❌ |
| `headers` | ✅ | ✅ | ❌ |
| `types` | ✅ | ✅ | ❌ |
| `channels` | ✅ | | ❌ |
| `client` | ✅ | | ❌ |
| `channels` | ✅ | | ❌ |
| `client` | ✅ | | ❌ |
| `models` | ✅ | ✅ | ✅ |
| `custom` | ✅ | ✅ | ✅ |

Expand Down
13 changes: 13 additions & 0 deletions docs/inputs/asyncapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,19 @@ Here's a complete basic AsyncAPI document example to get you started:
}
```

## Multi-message channels

A channel may carry more than one message. Both the payload and header models reflect **all** of them:

- **Payloads** — when two or more messages on a channel carry a payload, the generated payload is a `oneOf` union of every payload. A message with no payload is skipped from the union (with a warning) instead of truncating the rest; when exactly one message carries a payload, a plain (non-union) model is produced.
- **Headers** — likewise, when two or more messages declare headers, the channel's header model is a `oneOf` union of each message's headers. A single header-bearing message produces the same standalone header model as before; header-less messages alongside header-bearing ones are skipped with a warning. Reply-only messages are excluded from this union — the channel headers model the request side, so a request never carries the reply's headers.

This means adding a payload-less or header-less message to a multi-message channel no longer silently drops the other messages' models.

## Servers and base URL

When a channel is generated for the `http_client` protocol, the document's first `http`/`https` server URL becomes the generated client's default `baseUrl`. Non-HTTP servers (e.g. `nats`, `kafka`) are ignored for the HTTP client. See [HTTP client base URL precedence](../protocols/http_client.md#base-url).

## Extensions

To customize the code generation through the AsyncAPI document, use the `x-the-codegen-project` [extension object](https://www.asyncapi.com/docs/reference/specification/v3.0.0#specificationExtensions) with the following properties:
Expand Down
22 changes: 20 additions & 2 deletions docs/inputs/openapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ Input support; `openapi`
| [`parameters`](../generators/parameters.md) | ✅ |
| [`headers`](../generators/headers.md) | ✅ |
| [`types`](../generators/types.md) | ✅ |
| [`channels`](../generators/channels.md) | |
| [`client`](../generators/client.md) | |
| [`channels`](../generators/channels.md) | |
| [`client`](../generators/client.md) | |
| [`custom`](../generators/custom.md) | ✅ |
| [`models`](../generators/custom.md) | ✅ |

Expand All @@ -36,6 +36,24 @@ Create a configuration file that specifies OpenAPI as the input type:
}
```

## Content types

Request and response bodies are extracted from JSON content types: `application/json`, `text/json`, and any type whose subtype ends in `+json` (for example `application/hal+json` or `application/vnd.api+json`). When several JSON content types are present, `application/json` is preferred.

When an operation declares a body but **none** of its content types are JSON (for example an XML-only response), no payload model is generated and a warning naming the operation and its content types is logged — the omission is never silent.

## Servers and base URL

For the `channels`/`client` HTTP client, the document's first `http`/`https` server URL becomes the generated default `baseUrl`. Server URL variables are substituted with their declared defaults; a server whose variable has no default, or whose URL is relative, is skipped with a log. See [HTTP client base URL precedence](../protocols/http_client.md#base-url).

## Current limitations

These constructs are not generated and are reported with a warning rather than dropped silently:

- **Cookie parameters** — `in: cookie` parameters have no generated handling and are dropped (path, query, and header parameters are unaffected).
- **Webhooks** — the OpenAPI 3.1 `webhooks` section is not traversed.
- **Non-JSON bodies** — only JSON-family content types produce payload models (see [Content types](#content-types)).

## Remote URL inputs

`inputPath` accepts an `http://` or `https://` URL. Optional authentication (bearer token, API key, or custom headers) is configured via the `auth` field. Cross-spec `$ref` URLs are also resolved through the same auth-aware HTTP client. See the [configurations guide](../configurations.md#remote-url-inputs) for examples and the [auth scope and security considerations](../configurations.md#auth-scope-and-security-considerations) section — the configured headers are sent to every `$ref` target as well as the root URL.
Expand Down
2 changes: 2 additions & 0 deletions docs/migrations/v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ import * as NodeFetch from 'node-fetch';








10 changes: 10 additions & 0 deletions docs/protocols/http_client.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,16 @@ console.log(connect.data.safepayAccountId);

See the runnable [`openapi-http-client` example](https://github.com/the-codegen-project/cli/tree/main/examples/openapi-http-client) for a complete, self-contained setup.

## Base URL

Every generated call accepts an optional `baseUrl`. The value used at runtime follows this precedence, highest first:

1. **`context.baseUrl`** passed to the call (e.g. `getUser({ baseUrl: 'https://api.example.com' })`) — always wins.
2. **The document's first HTTP(S) server** — when the AsyncAPI `servers` (or OpenAPI `servers`) section declares an `http`/`https` server, its URL becomes the generated default. Non-HTTP servers (nats, kafka, …), relative URLs, and OpenAPI server URLs whose variables have no default are skipped.
3. **`http://localhost:3000`** — the fallback when the document declares no usable HTTP(S) server.

So a document with `servers: [{ url: 'https://api.example.com' }]` generates clients that target `https://api.example.com` by default, and you only pass `baseUrl` to override it (for example, to point at a staging environment).

## Authentication

The HTTP client uses a discriminated union for authentication, providing excellent TypeScript autocomplete support.
Expand Down
Loading
Loading