Skip to content
Merged
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
88 changes: 56 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ Every generated SDK is a **complete, production-ready client** — not just type
| `specforge migrate` | Generate a migration guide between two spec versions |
| `specforge docs` | Generate a static HTML API documentation site |
| `specforge test` | Generate mock server tests from spec examples |
| `specforge version` | Apply API versioning (URL/header/query) to a spec's endpoints |
| `specforge versions` | List API versions in a spec directory |
| `specforge workspace` | Generate SDKs for all specs in a workspace config |
| `specforge workspace-init` | Generate a workspace config from a directory |
Expand Down Expand Up @@ -305,8 +306,12 @@ cargo build -p specforge-cli
<summary><b>TypeScript</b></summary>

```ts
import { createClient, bearerAuth, streamSse } from "./sdk-ts/src/index.ts";
import { createClient, bearerAuth } from "./sdk-ts/src/index.ts";
import { ApiClient } from "./sdk-ts/src/client";
import { streamSse } from "./sdk-ts/src/streaming";

// `bearerAuth` / `apiKeyAuth` are emitted only when the spec declares those
// security schemes. Specs with no security emit `anonymousAuth` instead.
const client = createClient({
baseUrl: "https://api.example.com",
auth: bearerAuth(() => process.env.API_TOKEN!),
Expand All @@ -318,13 +323,16 @@ const client = createClient({

const page = await client.pets.listPets({ limit: 20 });

// Streaming (SSE)
const res = await client.request("GET", "/events");
// Streaming (SSE) — request() lives on the underlying ApiClient.
const api = new ApiClient({ baseUrl: "https://api.example.com" });
const res = await api.request("GET", "/events");
Comment on lines +327 to +328

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Streaming client drops auth 🐞 Bug ≡ Correctness

The updated streaming example constructs a new ApiClient without passing auth, so streaming
requests will default to anonymousAuth() even though the example configures bearerAuth earlier.
This will fail (401/403) for authenticated streaming endpoints and is misleading as a copy/paste
snippet.
Agent Prompt
## Issue description
README’s streaming example creates `new ApiClient({ baseUrl })` which drops the configured auth. In generated SDKs, omitting `auth` defaults to `anonymousAuth()`, so secured streaming endpoints won’t receive credentials.

## Issue Context
Earlier in the snippet, `createClient({... auth: bearerAuth(...) ...})` is shown. The streaming example should reuse the same options (at least `auth`) to avoid a surprising unauthenticated request.

## Fix Focus Areas
- README.md[315-331]

## Proposed fix
Refactor to share options:
```ts
const options = {
  baseUrl: "https://api.example.com",
  auth: bearerAuth(() => process.env.API_TOKEN!),
  maxConcurrent: 8,
  dedupe: true,
  idempotency: true,
  retry: { maxRetries: 3 },
};

const client = createClient(options);

const api = new ApiClient(options);
const res = await api.request("GET", "/events");
```
(Or at minimum: `new ApiClient({ baseUrl, auth: bearerAuth(...) })`.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

for await (const ev of streamSse(res)) {
console.log(ev.event, ev.data);
}
```

> `createClient()` returns a tree-shakeable tag namespace (`client.pets.listPets`). For raw requests / streaming, use the `ApiClient` directly. The auth helpers (`bearerAuth`, `apiKeyAuth`) appear only when your spec declares those schemes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. Tree-shaking claim incorrect 🐞 Bug ≡ Correctness

README now states that createClient() returns a "tree-shakeable" tag namespace, but the generated
TS SDK explicitly warns that createClient() imports all tag modules and is not the tree-shakeable
path. This can mislead users into shipping larger bundles than expected.
Agent Prompt
## Issue description
README claims `createClient()` is tree-shakeable, but generated SDK docs state the opposite (it eagerly imports all tags). This mismatch misguides users about bundle size behavior.

## Issue Context
The generated TS barrel (`src/index.ts`) documents tree-shaking behavior and explicitly recommends importing individual tag modules instead of using `createClient()` when bundle size matters.

## Fix Focus Areas
- README.md[334-335]

## Proposed fix
Change the note to something like:
- “`createClient()` returns a convenient tag namespace (`client.pets.listPets`), but it imports all tags. For tree-shaking, import individual tag modules directly.”

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


</details>

<details>
Expand Down Expand Up @@ -394,24 +402,14 @@ while let Some(ev) = sse.next_event().await? {

## CLI reference

Run `specforge --help` for the full, current command list (the CLI is the source of truth — see the table above). The most-used commands:

```text
specforge <COMMAND>

Commands:
generate Generate an SDK from an OpenAPI spec
check Lint and validate an OpenAPI spec without generating
diff Compare two OpenAPI specs and report breaking changes
emit Emit the resolved IR as JSON (for external emitters / plugins)
init Scaffold a new minimal OpenAPI spec
convert Convert between OpenAPI 3.0 and 3.1
merge Merge multiple OpenAPI spec files into one
migrate Generate a migration guide between two spec versions
docs Generate static HTML API documentation
test Generate mock server tests for generated SDKs
versions List API versions in a spec directory
workspace Generate SDKs for all specs in a workspace config
workspace-init Generate a workspace config from a directory
help Print this message or the help of the given subcommand
specforge generate <SPEC> # Generate an SDK (the main command)
specforge check <SPEC> # Lint a spec (--strict = warnings as errors)
specforge diff <OLD> <NEW> # Breaking-change detection (exit 1 on breaking)
specforge emit <SPEC> # Dump resolved IR as JSON (for external emitters)
specforge <CMD> --help # Per-command flags
```

### `specforge generate`
Expand All @@ -423,16 +421,23 @@ Arguments:
<SPEC> Path to OpenAPI YAML or JSON

Options:
-o, --out <DIR> Output directory [default: ./generated]
-l, --lang <LANG> ts | go | rust [default: ts]
-n, --name <NAME> Package / module / crate name override
--version <VERSION> API version (when spec is a directory)
--profile Output timing breakdown for each pipeline stage
-v, --log-level <LEVEL> error|warn|info|debug|trace [default: info]
-o, --out <DIR> Output directory [default: ./generated]
-l, --lang <LANG> ts | go | rust [default: ts]
-n, --name <NAME> Package / module / crate name override
--version <VERSION> API version (when spec is a directory)
--profile Output timing breakdown for each pipeline stage
--include-webhooks Emit webhook handler types (OpenAPI 3.1)
--locale <LOCALE> Comma-separated locale codes for i18n errors (e.g. en,es)
--changelog Auto-generate CHANGELOG.md in the output dir
--changelog-previous <F> Previous spec to diff against (use with --changelog)
--version-prefix <PFX> Apply a URL path versioning prefix (e.g. v1)
--plugin <NAME> Use a WASM plugin emitter by name
-v, --log-level <LEVEL> error|warn|info|debug|trace [default: info]
-h, --help
-V, --version
```

> `-V, --version` is a top-level flag (`specforge -V`), not a `generate` option.

| Language | `-n` means | Default if omitted |
|----------|------------|--------------------|
| **ts** | npm package name in `package.json` | `@<title-slug>sdk` |
Expand All @@ -449,6 +454,11 @@ Arguments:

Options:
--strict Treat warnings as errors
--deprecations List deprecated operations and schemas
--disable <RULE> Disable a lint rule (repeatable)
--enable <RULE> Enable a lint rule (repeatable)
--severity <RULE:SEV> Set rule severity: error|warning|off (repeatable)
--config <FILE> Path to a lint config YAML file (.specforge.yaml)
-v, --log-level <LEVEL> error|warn|info|debug|trace [default: info]
```

Expand Down Expand Up @@ -869,8 +879,8 @@ See **[RELEASE.md](./RELEASE.md)** for the full checklist. Short version:
# 2. update CHANGELOG.md
./scripts/ci.sh full
./scripts/generate-examples.sh
git tag -a v0.1.0 -m "specforge v0.1.0"
git push origin v0.1.0
git tag -a v1.6.1 -m "specforge v1.6.1"
git push origin v1.6.1
```

Binary: `cargo build --release -p specforge-cli` → `target/release/specforge`.
Expand All @@ -889,6 +899,20 @@ Binary: `cargo build --release -p specforge-cli` → `target/release/specforge`.

## Status & roadmap

**v1.6.1** — codegen robustness + readability cleanup:

- [x] Doc-comment escaping (Rust + Go emitters) — real-world specs (GitHub, Stripe) with backticks, `/*`/`*/`, and multi-paragraph descriptions no longer break generated code
- [x] Reserved-name collision protection (Go + Rust) — spec models named like built-in SDK types (`ValidationError`, etc.) get a `Model` suffix
- [x] Per-emitter compile gates — generated SDKs are regenerated into a temp crate and compiled (`cargo check` / `go build` / `tsc --noEmit`) in CI
- [x] Regression tests now cover GitHub (~9MB) + Stripe (~7.6MB) specs in Rust + Go — 4 previously-failing tests now pass
- [x] **259 tests** passing; clippy warnings 120 → 2

**v1.6.0** — compile-gate tests + telemetry/validation middleware:

- [x] `ServiceContainer` DI, `RequestInterceptor`/`ResponseInterceptor`, response transformers
- [x] Validation middleware (`validation_middleware.rs`) wired into all emitters
- [x] Telemetry hooks wired into the TypeScript client

**v1.5.0** — web UI redesign, spec marketplace:

- [x] Professional light theme — complete rewrite (1713 lines, design system)
Expand Down Expand Up @@ -1030,7 +1054,7 @@ Binary: `cargo build --release -p specforge-cli` → `target/release/specforge`.
- [x] Multi-platform CI (Linux, macOS, Windows)
- [x] Richer generated READMEs (errors, pagination, concurrency, middleware, streaming)

**All roadmap items completed through v1.5.0. Production-ready with professional web UI and marketplace.**
**All roadmap items completed through v1.6.1. Production-ready: real-world specs (GitHub, Stripe) generate + compile in all languages, with per-emitter compile gates in CI.**

---

Expand All @@ -1041,15 +1065,15 @@ Binary: `cargo build --release -p specforge-cli` → `target/release/specforge`.
Use specforge in CI with the official GitHub Action:

```yaml
- uses: amafjarkasi/specforge-openapi-sdk-codegen@v0.2.2
- uses: amafjarkasi/specforge-openapi-sdk-codegen@v1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

3. Action pin docs mismatch 🐞 Bug ⚙ Maintainability

README updates the GitHub Action pin to @v1, but docs-site/docs/intro.md still recommends
@v0.2.2, leaving conflicting guidance within this repo. Users following different docs pages will
end up on different action versions.
Agent Prompt
## Issue description
The README now recommends using `amafjarkasi/specforge-openapi-sdk-codegen@v1`, but the docs-site intro page still shows `@v0.2.2`, creating inconsistent instructions.

## Issue Context
This PR changed README’s pin; to keep the documentation set coherent, update the docs-site GitHub Action snippet (and any similar snippets elsewhere) to match the chosen recommendation.

## Fix Focus Areas
- docs-site/docs/intro.md[909-923]

## Proposed fix
Update the docs-site snippet to use `@v1` (or explain why it intentionally differs), and optionally mirror the README’s pinning guidance (major pin vs exact tag).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

with:
command: generate
spec: openapi.yaml
lang: ts
output: ./sdk
```

Commands: `generate`, `check`, `diff`, `emit`
Commands: `generate`, `check`, `diff`, `emit`. Pin to a major version (`@v1`) for non-breaking updates, or a specific tag (`@v1.0.0`) for reproducibility.

### VS Code Extension

Expand Down
Loading