Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions .beads/issues.jsonl

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
specs/storyden.yaml whitespace=-trailing-space
34 changes: 17 additions & 17 deletions .github/workflows/playground-wasm.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
name: Playground WASM

# Builds the website playground's WASM bundle whenever the generator changes,
# publishes it as a public release asset, and bumps website/playground-wasm.lock.
# Builds the website playground's WASM bundle for each release tag, publishes it
# as a public release asset, and bumps website/playground-wasm.lock on main.
# The lock-bump commit triggers Vercel's git auto-deploy, whose build fetches
# the asset via website/scripts/fetch-playground-wasm.mjs. No Vercel secrets.
#
# Self-retrigger is impossible by construction: the lock file lives under
# website/, outside this workflow's path filter.

on:
push:
branches: [main]
paths:
- "src/**"
- "wasm/**"
- "Cargo.toml"
- "Cargo.lock"
- "scripts/build-playground-wasm.sh"
tags: ["v*"]
workflow_dispatch:

permissions:
Expand Down Expand Up @@ -46,6 +37,7 @@ jobs:
run: ./scripts/build-playground-wasm.sh

- name: Package and publish release asset
id: publish
env:
GH_TOKEN: ${{ github.token }}
run: |
Expand All @@ -58,13 +50,22 @@ jobs:
--prerelease \
--title "Playground WASM $version (${GITHUB_SHA::7})" \
--notes "WASM bundle for openapi-to-rust.dev/playground, built from ${GITHUB_SHA}."
echo "$tag" > website/playground-wasm.lock
echo "tag=$tag" >> "$GITHUB_OUTPUT"

- name: Commit lock bump
# Pushes back to the ref the workflow ran on: main for the normal
# push-triggered flow, the dispatched branch for pre-merge test runs.
# Tag-triggered runs update main. Manual pre-merge runs update the
# branch they were dispatched from.
env:
WASM_RELEASE_TAG: ${{ steps.publish.outputs.tag }}
run: |
set -euo pipefail
target_branch="$GITHUB_REF_NAME"
if [[ "$GITHUB_REF_TYPE" == "tag" ]]; then
target_branch="main"
fi
git fetch origin "$target_branch"
git checkout -B "$target_branch" "origin/$target_branch"
echo "$WASM_RELEASE_TAG" > website/playground-wasm.lock
if git diff --quiet -- website/playground-wasm.lock; then
echo "lock unchanged; nothing to deploy"
exit 0
Expand All @@ -73,5 +74,4 @@ jobs:
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add website/playground-wasm.lock
git commit -m "chore: bump playground wasm to $(cat website/playground-wasm.lock) [skip ci]"
git pull --rebase origin "$GITHUB_REF_NAME"
git push origin "HEAD:$GITHUB_REF_NAME"
git push origin "HEAD:$target_branch"
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ scripts/spec-compile.sh anthropic openai # generator/client output
scripts/spec-compile.sh # broad generator/type changes
```

The full corpus generates and compile-checks 54 OpenAPI documents and can take
The full corpus generates and compile-checks 55 OpenAPI documents and can take
several minutes. CI runs a fast generation tier on pull requests and the full
compile tier weekly or on manual dispatch.

Expand Down
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ committed output.

We originally built this internally at [GPU CLI](https://gpu-cli.sh) to generate typed Rust clients for OpenAI, Anthropic, Cloudflare, and other large APIs. After battle-testing it against real-world specs with complex union types, discriminated enums, streaming endpoints, and the occasional spec/API drift, we decided to open source it.

The repository contains **55 real-world specs**. The supported OpenAPI corpus is
54 specs (one Gitea document is Swagger 2.0 and intentionally skipped). Pull
The repository contains **56 real-world specs**. The supported OpenAPI corpus is
55 specs (one Gitea document is Swagger 2.0 and intentionally skipped). Pull
requests compile-check the OpenAI and Anthropic production specs; a scheduled
and manually runnable CI tier checks all 54 OpenAPI specs.
and manually runnable CI tier checks all 55 OpenAPI specs.

Release history and breaking changes live in the [changelog](CHANGELOG.md).

Expand Down Expand Up @@ -110,6 +110,7 @@ enable_async_client = true
[http_client]
base_url = "https://api.example.com"
timeout_seconds = 30
max_response_body_bytes = 8388608

[http_client.retry]
max_retries = 3
Expand Down Expand Up @@ -261,6 +262,7 @@ use crate::generated::types::*;
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = HttpClient::new()
.with_base_url("https://api.example.com")
.with_max_response_body_bytes(8 * 1024 * 1024)
.with_api_key(std::env::var("API_KEY")?);

let req = CreateResourceRequest { /* … */ };
Expand All @@ -269,6 +271,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
```

Generated clients cap every response they buffer in memory at 8 MiB by
default. Set `http_client.max_response_body_bytes` when generating, or use the
runtime builder above for a particular client. If a cumulative chunked or
fixed-length body crosses the limit, the call returns
`HttpError::ResponseTooLarge { limit }` before appending the chunk that would
exceed it. Successful SSE responses remain streaming; only SSE error responses
are buffered under the same cap.

## What the generated types look like

A tour of patterns the generator emits, from real outputs.
Expand Down Expand Up @@ -572,6 +582,7 @@ registry_only = false # only generate the registry (skip types
[http_client]
base_url = "https://api.example.com"
timeout_seconds = 30 # 1-3600
max_response_body_bytes = 8388608 # buffered responses; 8 MiB default

[http_client.retry]
max_retries = 3 # 0-10
Expand Down Expand Up @@ -665,10 +676,10 @@ scripts/spec-compile.sh # generate + cargo-check every spec in specs/ (full co

The compile tiers are intentionally different:

- Every pull request and push to `main` generates all 54 supported OpenAPI
- Every pull request and push to `main` generates all 55 supported OpenAPI
specs, then compile-checks the Anthropic and OpenAI production specs against
each generated `REQUIRED_DEPS.toml`.
- Weekly scheduled CI and manual workflow runs compile-check all 54 supported
- Weekly scheduled CI and manual workflow runs compile-check all 55 supported
OpenAPI specs. The bundled Gitea Swagger 2.0 document is reported as skipped.
- Local `scripts/spec-compile.sh` runs the same full tier; pass spec names as
arguments for a smaller targeted run.
Expand Down
1 change: 1 addition & 0 deletions examples/complete_workflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ fn demonstrate_rust_api(
http_client_config: Some(HttpClientConfig {
base_url: Some("https://api.example.com".to_string()),
timeout_seconds: Some(30),
max_response_body_bytes: Some(8 * 1024 * 1024),
default_headers: {
let mut headers = std::collections::HashMap::new();
headers.insert("content-type".to_string(), "application/json".to_string());
Expand Down
5 changes: 5 additions & 0 deletions specs/download.sh
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ download "cloudflare-dns" \
"https://raw.githubusercontent.com/cloudflare/api-schemas/main/openapi.json" \
"cloudflare-dns.json"

# --- Community Platforms ---
download "storyden" \
"https://raw.githubusercontent.com/Southclaws/storyden/main/api/openapi.yaml" \
"storyden.yaml"

# --- Misc DevTools ---
download "railway" \
"https://docs.railway.com/reference/public-api-spec.json" \
Expand Down
Loading
Loading