feat(client-generator): agent-friendly generators — python, go, php, cli + neutral authoring toolkit and eject workflow - #3016
feat(client-generator): agent-friendly generators — python, go, php, cli + neutral authoring toolkit and eject workflow#3016Marshevskyy wants to merge 193 commits into
Conversation
🦋 Changeset detectedLatest commit: 2a46d21 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Performance Benchmark (Lower is Faster)
|
|
📦 A new experimental 🧪 version v0.0.0-snapshot.1785856098 of Redocly CLI has been published for testing. Install with NPM: npm install @redocly/cli@0.0.0-snapshot.1785856098 |
Feedback from Rebilly (Replay Admin + Rebilly Core)Tried
Overall: generation succeeds for both; TypeScript still works when co-selected with 1. Core list one-shot vs
|
Follow-up: Python generator (same snapshot)Tried
Overall: generation succeeds; sync + async clients work; offline smokes pass for Admin cursor pagination and Core offset pagination + Python is in good shape relative to PHP on several points we flagged earlier — calling those out as already-good, then listing Python-specific asks. Already better than PHP (positive)
Python-specific asks1. Output filename is not a normal import pathEmitted names follow the TS stem:
Neither is a valid/idiomatic Python module name ( Ask: emit an importable name by default, e.g. 2. Cross-language auth config key mismatchPython resolves API keys from Ask: document the per-language auth dict shape next to each other (or normalize to one key with a documented alias). Easy footgun when someone copies a TS/PHP snippet into Python. 3. Reserved-word fields (
|
Clarification on single-file outputOne more product note on the earlier “large single-file” asks (PHP ~4.7MB / Python ~4.4MB for Rebilly Core): We like the single-file default and would keep it. It’s a great “Download client” artifact from Redoc/API docs — one Please treat the size/split comments as optional escape hatches for huge descriptions, not a request to change the default:
The other asks still stand, especially for Python: importable filename ( |
Follow-up: Go generator (same snapshot)Tried
Overall: generation succeeds; Same single-file preference as before: keep the one-file default for Redoc/API docs downloads. Core Go is notably smaller than Core PHP (~4.7MB) / Python (~4.4MB), which helps that story. Already in good shape (positive)
Go-specific asks1. Emit
|
Follow-up:
|
|
|
||
| Added agent-friendly client generation: built-in `python`, `go`, `php`, and `cli` generators, a language-neutral authoring toolkit with a per-generator `AGENTS.md` skill, an `eject-generator` command, `x-codeSamples` output, and verification against large real-world descriptions — with every generator now emitting through source-text templates. | ||
|
|
||
| **Note:** the AST exports (`ts`, `printStatements`, `schemaToTypeNode`, …) were removed from `@redocly/client-generator/generate` in favor of the text toolkit (`tsType`, `tsJsdoc`, `codeLiteral`). |
| - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 | ||
| with: | ||
| python-version: '3.12' | ||
| - name: Install httpx (the large-descriptions Python import bar needs it) | ||
| run: pip install httpx | ||
| - name: Cache the pinned GitHub REST description | ||
| uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 | ||
| with: | ||
| path: tests/e2e/generate-client/.cache | ||
| key: large-descriptions-${{ hashFiles('tests/e2e/generate-client/large-descriptions.test.ts') }} |
There was a problem hiding this comment.
I think we need a separate test suite for generator.
I think it will be very big and slow eventually when we start testing compiled languages.
There was a problem hiding this comment.
Agreed, and it's already the biggest suite. Splitting generator tests (client-generator unit +
tests/e2e/generate-client, including the large-description compile bars) into their own vitest
suite and CI job so compiled-language testing can grow without slowing the main e2e job.
| Your agent (or you) edits the generator, `redocly generate-client` rebuilds the client, and next week's spec change regenerates with the customization intact. | ||
|
|
||
| Ejectable generators: `python`, `go`, `php` — the language generators built on the language-neutral authoring toolkit. | ||
| The TypeScript `sdk` and its satellite generators are customized through `client.setup`, middleware, and configuration instead; running `eject-generator sdk` prints that guidance. |
There was a problem hiding this comment.
I think typescript and satellites should be ejectable too as separate generators
There was a problem hiding this comment.
Agreed. Docs now say every built-in is ejectable. Mechanically the language generators are one
self-contained file, while the TypeScript ones are thin entries over shared emitters, so eject
will bundle each generator with the emitters it uses into a single .mjs, keeping the
@redocly/client-generator imports external. sdk.mjs will be large, but it's one file you own,
which is the point.
| | ---------- | ------- | ---------------------------------------------------------------------------------------------------- | | ||
| | generator | string | Built-in generator to eject: `python`, `go`, or `php`. | | ||
| | `--dir` | string | Directory to eject into. Default `./generators`. | | ||
| | `--update` | boolean | Three-way merge a newer generator version into your customized copy; conflicts get standard markers. | |
| Ejecting writes four things: | ||
|
|
||
| - `<dir>/<name>.mjs` — the generator, the exact code the built-in runs, readable plain ESM. | ||
| - `<dir>/.pristine/<name>.mjs` — a pristine snapshot (commit it); `--update` uses it as the merge base. |
There was a problem hiding this comment.
I think we don't need it. can update compare with the latest upstream version?
There was a problem hiding this comment.
Dropping .pristine/ entirely. --update now uses the version recorded in the ejected file's
header as the merge base, so nothing extra is committed and there's nothing to keep in sync.
| ### Code samples for docs | ||
|
|
||
| A generator that knows how to call an operation can also document it: implement the optional `sample(operation, ctx)` hook to return one idiomatic snippet (`{ lang, label, source }`) per operation. | ||
| With `codeSamples: true` in the `client` block, generation collects every selected generator's samples into `<output stem>.code-samples.yaml` — an [OpenAPI Overlay](https://spec.openapis.org/overlay/latest.html) adding `x-codeSamples` per operation, ready for docs tooling to apply. |
There was a problem hiding this comment.
I think we don't have support for Overlays. We need to either add support for overlays in bundle or figure out some other format
There was a problem hiding this comment.
we will think about it in separate PR
|
|
||
| The `cli` generator emits `<stem>.cli.ts` — a zero-dependency, bin-ready command-line interface over the generated client. | ||
| Path params are positional, query params become typed `--kebab-name` flags (enums list their choices in `--help`, array params repeat the flag), and JSON request bodies arrive via `--json '<json>'`, `--json @file.json`, or `--json @-` (stdin). | ||
| When `zod` is co-selected, requests are validated before they are sent. |
There was a problem hiding this comment.
I think it shoud just do it by default without the need to coselect zod
There was a problem hiding this comment.
Done. cli declares requires: ['sdk', 'zod'] and the resolver now pulls prerequisites in, so
--generator cli alone emits all three files and validates (exit 3) with nothing extra to
select. Same for the wrappers: --generator tanstack-query brings the sdk it wraps. The
trade-off is a zod runtime dependency for the CLI
| npx tsx src/client.cli.ts orders listOrders --status open --limit 10 | ||
| npx tsx src/client.cli.ts orders createOrder --json @order.json | ||
| npx tsx src/client.cli.ts orders listOrders --page-all # one JSON page per line | ||
| npx tsx src/client.cli.ts schema createOrder # request/response schemas |
| `tanstack-query`, `swr`, and `cli` wrap the throw-mode `sdk` client, so they require `--error-mode throw`; `transformers` requires `--date-type Date`. | ||
| See the [`zod`](https://github.com/Redocly/redocly-cli/tree/main/tests/e2e/generate-client/examples/zod), [`tanstack-query`](https://github.com/Redocly/redocly-cli/tree/main/tests/e2e/generate-client/examples/tanstack-query), and [`mock`](https://github.com/Redocly/redocly-cli/tree/main/tests/e2e/generate-client/examples/mock) examples. | ||
|
|
||
| ### Generated CLI |
There was a problem hiding this comment.
I think it should also be able to generate documentation for the CLI (markdown file)
But the same applies for other clients so it can be the next stage.
There was a problem hiding this comment.
It may need some documentation templates that can be ejected then too.
There was a problem hiding this comment.
Built it — there's now a cli-docs generator that writes .cli.md beside the generated CLI: usage, global flags, credential environment variables, the exit-code table, and one section per command with its positional arguments and flags (type, required, enum choices, description). --generator cli-docs is the whole selection, since it pulls in the CLI it documents.
On ejectable templates: the renderer is the template. Every built-in generator is ejectable now, so redocly eject-generator cli-docs hands you the page layout as code you own — same language and toolkit as everything else, no template syntax and no extra dependency to learn. Light customization stays declarative through the generator's own options (title, frontmatter) under client.options.cli-docs; anything structural is an eject. We deliberately didn't add a template engine, because that would be a second customization mechanism sitting next to eject.
One property worth calling out: the page renders from the same command table the CLI dispatches on, and the same functions the runtime uses to address groups and name credential variables — so the docs can't drift from the tool. An e2e walks the generated CLI's own --help at both levels and fails if any command it lists is missing a section.
| The `python` generator emits a self-contained `<stem>.py` next to the configured output — a full Python SDK over [httpx](https://www.python-httpx.org/) (`pip install httpx`, Python ≥ 3.9): | ||
| typed dataclass models (allOf flattened, enums, discriminated unions decoded by their discriminator), a `Client` and an `AsyncClient` with one method per operation, auth, retries with `Retry-After` and jittered backoff, timeouts, idempotency keys, middleware hooks, pagination iterators (`<op>_pages()` / `<op>_items()`, `async for` variants), SSE streaming, multipart bodies, `<op>_with_headers()` envelope variants for operations that declare response headers, and a `Servers` class for templated server URLs. |
There was a problem hiding this comment.
Why we need all of this details. It should be same as typescript one
There was a problem hiding this comment.
it doesn't support any of options we support for typescript? why?
There was a problem hiding this comment.
You were right on both counts, and one of them was a bug: serverUrl was silently ignored by the
language generators (fixed, 795f58ca9). dateType: Date is now implemented for all three
(ea3795e47). Options a language genuinely can't apply no longer vanish — they warn with the
reason, or fail fast where the output would be wrong (d6cf2ba4e). The docs no longer enumerate
per-language caveats: the language sections state that these are the TypeScript client in another
language, with one table for the differences the language forces (error idiom, date type, header
envelope, auth shape, reserved-word suffix, file layout).
| A generator adds artifacts _next to_ the client — it doesn't change the generated client's behavior; for that, use [publisher defaults](#publisher-defaults) or let the consumer compose [middleware](./use-generated-client.md#middleware). | ||
|
|
||
| A generator is `{ name, run }` (plus optional compatibility metadata); author it with `defineGenerator` from the package root, and build real TypeScript with the emit toolkit from `@redocly/client-generator/generate` — the same `ts.factory` + printer the built-in generators use, so the schema→type mapping matches the sdk's exactly: | ||
| A generator is `{ name, run }` (plus optional compatibility metadata); author it with `defineGenerator` from the package root. |
There was a problem hiding this comment.
maybe it should also export additional options this generator may support? (e.g. as json schema)
We can support them via config only for example.
There was a problem hiding this comment.
Yes, a generator declares options as a JSON Schema, validated before run, and publishers set them under client.options.<generator> in config.
…, discriminatorCases, nullability, enums)
… from the root and /generate
…oaded built-in generators
… hooks, sdk as reference implementation
…d test, and guide rework
…lkit-import names
… with build-time embed
… table with reflective hydration
…mples and a dogfooding guard
… helpers — slice-2 lessons landed, python items typed
…tdlib-only, vetted, embedded
Co-authored-by: Jacek Łękawa <164185257+JLekawa@users.noreply.github.com>
Co-authored-by: Jacek Łękawa <164185257+JLekawa@users.noreply.github.com>
| '@redocly/cli': minor | ||
| --- | ||
|
|
||
| Added agent-friendly client generation: `python`, `go`, `php`, `cli`, and `cli-docs` generators in addition to TypeScript generators. |
There was a problem hiding this comment.
I think changelog should be simpler
There was a problem hiding this comment.
I'll correct it right before merge.
| process.exit(await runCli([{ commands: [login] }, ...SOURCES], process.argv.slice(2))); | ||
| ``` | ||
|
|
||
| The CLI resolves credentials from `wiring.env`. |
There was a problem hiding this comment.
wiring.env is the environment map the CLI reads credentials from. The generated entry sets it to process.env, so in normal use it is the environment. It is injected instead of read globally so the engine stays testable and a host can run the CLI in-process without writing secrets into the global environment.
There was a problem hiding this comment.
corrected a bit docs
|
|
||
| #### CLI reference docs | ||
|
|
||
| The `cli-docs` generator writes `<output>.cli.md`, a Markdown reference. |
There was a problem hiding this comment.
I think cli generator can generate docs as part of the generator itself, (same about other generators)
There was a problem hiding this comment.
Agreed on the other generators, but docs are a separate generator on purpose, mostly because of eject
| } | ||
| ``` | ||
|
|
||
| #### Where the languages genuinely differ |
|
|
||
| Auth accepts a static credential, or a provider function that the client resolves for each request: | ||
|
|
||
| ```python |
There was a problem hiding this comment.
we have code tabs markdoc tag for those
There was a problem hiding this comment.
Before I was adding markdoc tags, but @tatomyr preferred native markdown format.
There was a problem hiding this comment.
I think it should be in the runtime/go
same runtime/php and runtime/python
and runtime/typescript
There was a problem hiding this comment.
looks like duplicate, this file already exists in assets (same for each generator)
There was a problem hiding this comment.
The generator AGENTS.md — not a duplicate: it is the source the shipped asset is compiled from. npm run prepare runs scripts/ejected-skill.mjs over src/generators//AGENTS.md and writes eject-assets/skills/-generator/SKILL.md, keeping the design sections verbatim and rewriting the intro and the modify loop for a user's repo (their file is generators/.mjs, not our index.ts, and they have no prepare or vitest). A test pins the asset to the source, so they cannot drift. Nothing said this in the file, which is a fair complaint — every source skill now states it, and the note sits in the paragraph the transform replaces, so the shipped skills are byte-identical.
There was a problem hiding this comment.
Why it is not in the generators/cli
Some generators are implemented in their generator folders and some are somehow in the core
I think it's confusing
There was a problem hiding this comment.
AI:
emitters/cli.ts — the rule was real but unwritten, so this looked arbitrary. generators//index.ts is the entry: options, output paths, and a call to a renderer. The renderer lives in emitters/ because that is where the shared pieces are — operation-signature.ts, ts-type.ts, pagination.ts, sse.ts. cli.ts has three consumers: the cli generator, cli-docs (which renders its page from the same command table, which is what stops the docs from drifting), and the package entry, which exports its composed-entry renderer. Moving it into generators/cli/ would make one generator import another generator's internals. The exception you spotted is real: python, go, and php compose with nothing in emitters/, so each is one self-contained file, which is also what lets eject-generator hand a user its source instead of a bundle. I wrote all of this into ARCHITECTURE.md.
| * const-objects, type guards; skipped when the document declares no schemas) and | ||
| * `<stem>.ts` (everything else, which `export *`s the schemas module). | ||
| */ | ||
| export const typescriptGenerator: Generator = ({ model, outputPath, outputMode, emit }) => { |
There was a problem hiding this comment.
This can't be the reference implementation. I imports some code from core.
The runtime cli tests used a product-specific credential prefix. The example API in this repository is the cafe, so the fixtures now use CAFE_SHOP and CAFE_SYNCER.
sdk-docs writes one Markdown reference for each SDK selected in the same run (<stem>.python.md, <stem>.go.md, and so on). A page carries the security schemes and one section per operation: the method and path, the parameters, the request body, the response type, and a call sample in that language. The call samples come from each SDK generator's own `sample` hook, which the pipeline passes to `run` as the new `samples` input. Because of this, a page cannot drift from the SDK beside it, and the ejected copy does not carry the SDK generators. Selecting sdk-docs without an SDK fails with the fix in the message: the generator never pulls a language in on its own. Also clarifies `wiring.env` in the generated-CLI guide. The field is the injected environment map, and a wrapper that stores a token writes the token to `process.env` before it runs a command.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
There are 7 total unresolved issues (including 5 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 381a54b. Configure here.
| // One section per tag, in the order the description declares them, then the untagged | ||
| // operations — the same grouping the CLI and the split output modes use. | ||
| const operations = model.services.flatMap((service) => service.operations); | ||
| const paginated = resolveModelPagination(model, options.pagination); |
There was a problem hiding this comment.
Docs use wrong pagination resolver
Medium Severity
sdk-docs marks pagination via resolveModelPagination, the TypeScript verifier that throws on explicit rules that fail schema fit. Language SDKs use paginationRuleFor, which does not. Co-selecting sdk-docs with python/go/php can fail the whole run on a pagination config those SDKs would accept, and can also disagree with which operations those SDKs actually paginate.
Reviewed by Cursor Bugbot for commit 381a54b. Configure here.
| ); | ||
| if (paginated) { | ||
| printer.line('This operation is paginated, so the SDK gives it page and item iterators.'); | ||
| } |
There was a problem hiding this comment.
Missing SSE and binary notes
Low Severity
The sdk-docs design requires per-operation behavior notes for paginated, SSE, and binary responses, but the renderer only emits the paginated line. SSE and binary operations are documented like ordinary JSON calls, so the reference omits how those SDKs actually behave.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 381a54b. Configure here.
The table restated the errorMode, dateType, outputMode, and runtime rows of the client reference, and the response-header, auth, and reserved-name text of the sections around it. The namespacing facts and the warning for an option a language cannot apply were the only parts stated nowhere else, so they move into the Language SDKs prose.
The hand-written SDK runtimes sat in three sibling folders at the package root: go-runtime, php-runtime, and python-runtime. They are now runtime/go, runtime/php, and runtime/python, so one folder holds every language runtime. The TypeScript runtime stays in src/runtime, because it is compiled source that ships in lib and that a `runtime: package` client imports. The other three are never compiled: the prepare script embeds them as strings.
…he shipped asset `src/generators/<name>/AGENTS.md` and `eject-assets/skills/<name>-generator/SKILL.md` read as two copies of one document. The second is generated from the first by `npm run prepare`, which rewrites the intro and the modify loop for a user's repository. Each source skill now says so, so nobody edits the generated asset. The note sits in the intro paragraph, which the transform replaces, so the shipped skills are byte-identical.
The split between generators/ and emitters/ was implicit, so `emitters/cli.ts` looked misplaced. The module map now gives the rule: a generator folder holds the entry, emitters/ holds the renderer that composes with the shared pieces, and the three self-contained language SDKs are the stated exception.
…ementation In the custom-generator guide that reads as "copy this generator", but it is a short entry over renderers that are internal to the package, so an author cannot import what it uses. The guide now says the generator implements the sample hook, and points authors at the runnable examples, which use only the public toolkit.
Two findings from the review bot on the new page renderer. The pagination note came from `resolveModelPagination`, the TypeScript verifier, which throws on a rule it cannot check against the response schema. The language SDKs resolve pagination with `paginationRuleFor`, which does not, so a python-only run could fail over a page, and the page could mark a different set of operations than the SDK beside it. The note now comes from the same helper the SDKs use. The skill promised a note for paginated, streaming, and binary operations, and only the paginated note existed. A streaming or binary operation now says so, decided by the success content type, which needs no import of the TypeScript emitters.
The benchmark job's pnpm pin moved on both sides; main's 11.22.0 wins.
The pagination bullet added to the source skill belongs to the design sections, which ship, so the generated asset needed regenerating with it.
…ra auth setters Review found the composed-CLI section reads as a contradiction: an earlier section says the word after the bin name is a tag slug, while the composed example puts an api alias there. Both are true and they never met. The guide now compares the two setups, says the tag groups of an api nest inside its alias, and says why the example's shorter form works: a bare operationId resolves when it is unambiguous. An e2e now covers that shorter form, so the documented spelling is verified rather than asserted. `binName` led with what it is not, so it now leads with what it is. The auth section read as three interfaces in TypeScript against one elsewhere. Every language gives credentials to an instance through its constructor; TypeScript adds two setters only because it also exports a module-level client.


What this adds
Client generation for languages beyond TypeScript, built so that both we and our users extend it with AI agents driven by per-generator design skills.
New built-in generators
python— self-contained client over httpx: typed dataclasses, sync + async clients, pagination iterators, SSE, multipart, retries, discriminated-union decode via aDISCRIMINATORSregistry.go— stdlib-only single file: structs with json tags, typed-const enums,(T, error)methods,context.Context, range-over-func pagination, SSE.goPackagesets the package clause.php— PHP ≥ 8.1 over the curl extension, zero Composer dependencies: promoted-constructor models, native enums, named-argument methods,Generator-based pagination, SSE over a curl_multi pump.cli— a bin-ready command-line interface over the sdk. It validates requests by default: selectingclipulls in the generators it needs (sdkandzod), so nothing extra has to be listed.binNamenames the command.cli-docs— the Markdown reference for that CLI (<stem>.cli.md): usage, global flags, credential variables, exit codes, and every command with its arguments and flags. It renders from the same command table the CLI dispatches on, so the page cannot drift from the tool.All three SDK languages have parity on auth (bearer/basic/apiKey with token providers), retries with
Retry-After+ jittered backoff, timeouts, idempotency keys, middleware, pagination, SSE, multipart, binary downloads, templated-server helpers (Serversclass /<Name>URLfunctions), and response-header envelopes (<op>WithHeadersvariants mirroring the TS{ envelope: true }option from #3002).The authoring model
Printer(indentation-aware source-text builder), naming (casing,identifierFor,RESERVED_WORDS), schema semantics (flattenAllOf,discriminatorCases, nullability,enumValues,headerCoerceType,schemaAtPointer),paginationRuleFor, andNotSupportedErrorfor rejecting an option the generator can't honor. A dogfooding guard test pins that the built-in language generators use nothing else.typescriptdependency in the authoring path. The TypeScript emitters render source text like everything else; thets.factoryexports are gone.typescriptremains an optional peer needed only to bake a--setupmodule, the one place we parse TypeScript.run(unknown key, wrong type, value outside anenum, missing required key) with defaults applied. Publishers set them underclient.options.<generator>.Eject workflow
redocly eject-generator <name>vendors any built-in generator into the repo as an editable.mjs. A language generator ships as its own source; a TypeScript generator ships bundled with the emitters it uses (unminified, one comment per source module), sosdk,zod,mock,swr,tanstack-query,transformers,cli, andcli-docsare ejectable too. An ejected-unmodified generator produces byte-identical output, proven in e2e.Eject also:
.claude/skills/<name>-generator/SKILL.md) plus the shared authoring skill, where agents auto-load them, and leaves a short pointer beside the code;@redocly/client-generatorindevDependenciesand adds the entry toclient.generators, editing the config text so comments survive;--updatethree-way merges with no committed snapshot: the merge base is the version recorded in the ejected file's own header, fetched from the registry when it differs from the installed one.Compatibility
Generator compatibility is the package version under semver, not an invented number: a generator declares
requiresGenerator(^1.2.0,~1.2.0,>=1.2.0, or an exact version), and a CLI outside that range says which version it ships, which the generator needs, and how to reconcile them. Ejected generators record it automatically. Custom generators also run behind a validated contract: load-time shape validation, output-path containment (no writes outside--output), andrun()result validation. An IR-shape snapshot test forces the "additive or breaking?" question on any model change. Failures are attributable (Generator "<name>" failed: …) and categorized in telemetry along with eject/update outcomes (coarse categories only — never file contents, paths, or user-chosen names).Verification
tsc/py_compile+import /go build+go vet+gofmt/php -l+require(tests/e2e/generate-client/large-descriptions.test.ts).npm run generatorsruns the client-generator unit tests plustests/e2e/generate-client, sharded two ways, so a growing set of compiled-language bars cannot slow the job everything else shares.npm run e2ecovers everything else.tests/e2e/generate-client/examples/.Docs
New command page (
eject-generator), a customize-client-generation guide, per-language usage notes with one table for the differences each language forces, and configuration reference updates (goPackage,binName,options,codeSamples).Note for reviewers
The per-operation pagination extension is now
x-redoclyPagination(camelCase, like every other Redocly extension). A description that still declaresx-redocly-paginationsilently loses its pagination rule — called out in the changeset.Check yourself
Security
Note
High Risk
Large experimental surface with breaking config/extension renames and new generated runtime behavior; misconfigured pagination or generator names can fail silently or at generation time across languages.
Overview
Expands client generation beyond TypeScript with built-in
python,go,php,cli,cli-docs, andsdk-docsgenerators, a language-neutral authoring toolkit (per-generator JSON Schema options,requiresGeneratorcompatibility), andredocly eject-generatorto vendor any built-in generator plus agent skills into the repo with config wiring and--updatethree-way merges.Breaking: the default TypeScript generator is now
typescript(notsdk); pagination operations must usex-redoclyPagination— the oldx-redocly-paginationname is no longer read.Adds
client.cliOutput/binName/goPackageconfig,codeSamplesoverlays, and extended docs for CLI composition and SDK usage. CI splitsnpm run client-generators(unit + generate-client e2e with Python/Go/PHP toolchains, sharded) from the main e2e job; CLI build copieseject-assets. Telemetry records coarsegenerate-client/eject-generatorusage without paths or user-chosen names.Reviewed by Cursor Bugbot for commit 2a46d21. Bugbot is set up for automated code reviews on this repo. Configure here.