openapi-chain

Development

Use Node.js 24.16.0 (see .node-version) and pnpm 10.34.5. CI also checks Node.js 22.22.2 and 26, plus Windows and macOS on Node.js 24.

corepack enable
corepack prepare pnpm@10.34.5 --activate
pnpm install --frozen-lockfile
pnpm check

If your Node.js installation does not include Corepack, install pnpm 10.34.5 using the pnpm installation guide.

CommandPurpose
pnpm devRebuild the library on changes
pnpm buildUse Turbo to build the published packages in dependency order, with local output caching
pnpm lint / pnpm lint:fixOxlint checks, including type-aware rules; optional fixes
pnpm format / pnpm format:checkFormat or check using Oxfmt
pnpm typecheck / pnpm typecheck:ts7Strict source, test and example checks with pinned TS 6 / TS 7
pnpm check:ts7TS 7 source, installed-consumer and type-budget gates (build first)
pnpm benchmark:types:ts7 / pnpm benchmark:editor:ts7Native compiler complexity scenarios / actual native LSP completion samples
pnpm test / pnpm test:watchRun Vitest once or in watch mode
pnpm test:coverageRun tests with V8 coverage and 90% thresholds
pnpm test:corpus:fixedCompare the fixed corpus with its approved baseline; TS6 and TS7 run in pnpm check
pnpm test:cliRun CLI filesystem, generation and command-contract regressions after building
pnpm test:releaseRun offline registry visibility, provenance and timeout regressions
pnpm verify:publishedCheck exact registry versions and provenance metadata, then install and exercise all three public packages
pnpm test:cli:packageInstall runtime/CLI tarballs and verify the installed command, generated types and browser bundle
pnpm test:package / pnpm verify:packageVerify all three entries in an isolated tarball consumer
pnpm size:checkEnforce the 3584-byte transitive core gzip limit after building
pnpm checkRun the complete local quality gate, including a fresh build
pnpm commitCreate a Conventional Commit using Commitizen
pnpm changesetDescribe a user-facing change and its version impact
pnpm version:packagesApply changesets, update the lockfile and regenerate the scoped CLI fixture
pnpm test:generatedRegenerate and compare the pinned OpenAPI fixtures
pnpm test:browserRun Chromium integration after building and installing its browser
pnpm benchmarkRebuild and measure type scale, runtime overhead and comparable bundle sizes
pnpm benchmark:competitors / pnpm benchmark:competitors:typesCompare request overhead (Fetch mock and loopback HTTP) / TS 6 and TS 7 type-checking cost with pinned competing clients
pnpm website:dev / pnpm website:buildRun the documentation website locally / export the static site to website/out
pnpm website:sync / pnpm test:websiteRegenerate site pages from repository Markdown / test the sync and check every doc link and anchor
pnpm benchmark:real-schemas --compiler=ts6 / --compiler=ts7Download pinned GitHub and Stripe documents (network) and compare openapi-chain and openapi-fetch type-checking cost
pnpm cleanRemove build and coverage output

test:package needs pnpm build first. It packs and installs the package in a temporary directory, verifies the file allowlist, and checks ESM/CJS imports plus NodeNext declaration resolution for core, strict and metadata, typed operations, mocked requests and real local HTTP. It does not publish anything.

Choose the right check

pnpm check runs a fresh build/package lint first, followed by formatting, typed lint, generated-fixture freshness, TypeScript, runtime coverage, CLI and release-verifier regressions, fixed corpus baselines, migration/scoped checks, installed runtime/CLI/Query tarball consumers, core gzip size, and TS 6/TS 7 type-scale/scoping gates. TS 7 also checks an isolated generator installation and installed declarations. It excludes the separate Chromium suite, public-registry consumer check and runtime/size/metadata microbenchmarks.

For a focused behavior change, run its Vitest file during iteration, then the full gate before submitting. For documentation, check links/anchors and typecheck examples against their actual generated schema; keep measured claims tied to a dated verification report. pnpm format formats the whole repository, so inspect the diff and avoid including unrelated formatting changes.

For transport or browser behavior:

pnpm build
pnpm exec playwright install chromium
pnpm test:browser

On Linux CI, the workflow uses pnpm exec playwright install --with-deps chromium. The suite starts local loopback servers and checks real Chromium Fetch behavior, including multipart, CORS/cookies, cancellation, binary and streaming responses. It does not qualify Firefox or WebKit.

For compiler, serialization or type changes that real documents could expose, run the opt-in corpus check after pnpm build:

pnpm test:corpus --types=120

It runs the preferred OpenAPI 3.x document of every API listed by APIs.guru through metadata compilation and every operation through core and strict clients with a capturing transport; --types=N also generates N documents with the CLI and type-checks typed calls. The first run downloads about 600 MB into .cache/corpus; later runs reuse it, and --offline skips downloads (set NODE_USE_ENV_PROXY=1 if direct Fetch cannot reach the network). It fails on crashes, hangs, missing or invalid JSON documents and type errors in generated consumers. Contract rejections are reported in groups, with full results in .cache/corpus/results.json. Add --baseline=<reviewed-file> to reject new compilation failures, request rejections and suspicious output. The small fixed regression corpus runs offline under TS6 and TS7 in pnpm check; see corpus regression checks for baseline review and reproduction. See the corpus report for the method and a dated result.

For schema changes, run pnpm generate:example, format the generated declarations, then pnpm test:generated and pnpm typecheck. The Petstore, Items and conformance fixtures use that workflow. The scoped catalog uses pnpm generate:scoped and pnpm test:scoped through the official CLI; its generated directory must not be reformatted. For performance changes, use pnpm benchmark; see measurement methods. Runtime/metadata timings are observations, not CI timing gates.

Project conventions

  • Add exports to the appropriate public entry: packages/core/src/index.ts, packages/core/src/strict.ts or packages/core/src/metadata.ts. Keep strict/compiler imports out of core. Put behavior tests in test/*.test.ts and compile-time regressions in test/*.typecheck.ts.
  • Use explicit .js extensions for relative TypeScript imports under NodeNext.
  • The runtime package has no dependencies. The separate packages/cli/ workspace owns Node-only generation dependencies and pins a private TypeScript 5.9.3 compatible with its generator. Keep those dependencies out of runtime entries.
  • TypeScript 6.0.3 remains the compiler API/build dependency; typescript7 is a pinned npm alias to TypeScript 7.0.2 for the recommended performance baseline. Both are checked. Use named scripts, not bare tsc, because their executable names collide. pnpm-workspace.yaml permits this exact version for openapi-typescript 7.13.0, whose declared peer range is ^5.x, while retaining strict peer checks. Independent applications need their own scoped configuration. Compiler upgrades must pass declaration, generated fixture, installed-consumer and type-scale checks.
  • Only built output and package metadata, README, license and an optional changelog ship from the core and query packages. The CLI ships its Node source.
  • pnpm install installs Husky hooks. Pre-commit runs lint-staged; commit-msg runs commitlint. The full type-aware check runs in pnpm check and CI.
  • sideEffects: false assumes library modules do not perform import-time side effects. Update the declaration if future modules require them.
  • Dependency lifecycle scripts are denied by default. Review and explicitly allow any future dependency that needs a build script in pnpm-workspace.yaml.

See CONTRIBUTING.md for the contributor and release workflows.

Release setup

Changesets v3 and its v2 GitHub Actions manage version PRs, changelogs, package artifacts, npm publication and GitHub releases. The workflow separates verification, packing and publication; only the publishing job has an OIDC permission.

The release workflow runs on main and supports an existing v<core version> tag for recovery. All three packages are already published, with Trusted Publishing configured. SCOPED_PUBLISH_ENABLED=true records that setup; RELEASE_ENABLED=false is an emergency stop.

Before releasing:

  1. Review each package version, pnpm changeset status, the changesets and the compatibility policy. Run pnpm check and Chromium, and confirm the private vulnerability reporting channel is enabled.
  2. Keep the Trusted Publisher configured for each npm package: GitHub owner jskits, repository openapi-chain, workflow filename release.yml, and the matching environment setting. A new package needs its own bootstrap publication and publisher configuration before adding it to the release flow.
  3. Keep Allow GitHub Actions to create and approve pull requests enabled. The version job uses contents: write and pull-requests: write for the release PR, actions: write to dispatch validation, and statuses: write to mark it pending; publication uses a separate OIDC permission.

version:packages applies Changesets' independent package releases and refreshes the generated scoped fixture. The v<version> recovery path validates the core package version; use the normal Changesets flow for independent CLI and Query releases.

Pushing changesets to main creates or updates a version PR. The version job marks its head SHA with a pending Release PR CI commit status, then dispatches the release PR validation workflow. Its guard requires the dispatch SHA to equal the expected SHA and the PR to remain open, target main, and use this repository's changeset-release/main branch at that SHA. It then runs the full Node/OS matrix and Chromium through the read-only reusable CI workflow at the immutable SHA. A separate job reports Release PR CI success only when that validation succeeds, and failure when the reusable CI does not succeed. If the guard fails, the whole run is canceled, or the status API fails, the pending status can remain until the version job or validation is rerun.

This is a commit status, which GitHub reflects on PRs involving that commit. GitHub does not evaluate workflow_dispatch job checks as required PR checks, even when they ran on the PR head SHA; see required-check troubleshooting. The Release PR CI status provides a separate result for this release PR; it does not replace the existing required matrix and Chromium checks. Do not make this context a global requirement for main unless every other PR also emits it. Review the PR status and approve its eligible pull_request workflow runs before merging when GitHub requests approval for runs created by GITHUB_TOKEN.

Merging the version PR runs the publish verification again before packaging and publication. Only the publish job receives id-token: write. It publishes the packed artifact with lifecycle scripts disabled, then creates Git tags and GitHub releases. The workflow requires the publish-plan and package artifact IDs so a missing artifact cannot fall back to publishing from the checkout.

The pinned Node.js version provides npm; the publish job checks npm is at least 11.5.1, as required for Trusted Publishing. pnpm 10 delegates tarball publication to that npm CLI. Authentication uses OIDC, with no NPM_TOKEN, NODE_AUTH_TOKEN, or setup-node registry-url configuration. Provenance is enabled explicitly. If the npm Trusted Publisher uses an environment name, add the exact same environment to the publish job before running it.

After publication, the workflow runs pnpm verify:published's script against the versions in each package manifest. It waits up to 30 minutes for all exact versions, integrity and provenance metadata to be visible at registry.npmjs.org, then installs the three packages with empty npm configuration and cache. The installed consumer checks CLI generation and drift, ESM/CommonJS entry points, core/strict HTTP requests and Query snapshots/fetchers. It uses a local HTTP server and requires no API credentials. The publish job has a 45-minute limit, including registry propagation and installation. pnpm test:release exercises pending metadata and timeout behavior offline inside the normal quality gate.

Run pnpm verify:published from the released commit to reproduce this check; a checkout with unpublished versions should time out. It verifies metadata presence and actual npm installation, not an independent cryptographic audit of the provenance attestation. Historical recovery tags that predate this script skip the new consumer step and report it as skipped in the summary; current releases require it.

After a release, check the Actions publication and consumer-verification summary, Git tag and GitHub release. If publication succeeds but verification, tag or GitHub release creation fails, inspect those remote states before retrying; do not bump the version, republish merely during propagation, or replace an existing tag to retry a failed run.

The explicit workflow_dispatch provides a separate validation result while default-token PR events wait for a maintainer's approval. GitHub documents both behaviors in its workflow-trigger rules. If dispatch fails, rerun the version job or manually run release-pr-ci.yml on changeset-release/main with the current PR head as expected_sha and its number as pr_number; do not merge based only on successful version generation or the dispatch request itself.

Recover a release PR permission failure

If the Changesets version step reports GitHub Actions is not permitted to create or approve pull requests, version generation may already have succeeded and the release branch may already have been pushed. The failure is the repository policy for Actions-created PRs, not npm authentication or the package version.

In Settings → Actions → General → Workflow permissions, enable Allow GitHub Actions to create and approve pull requests. Keep the default token permissions read-only: the version job already declares contents: write and pull-requests: write, but those declarations do not override the separate repository policy. An organization administrator must resolve an inherited restriction if the checkbox cannot be enabled.

After correcting the setting, rerun the failed jobs and confirm that Changesets creates or updates the release PR. Do not bump the package version, delete the release branch, merge the release PR, or change npm credentials merely to retry this failure. A successful version job prepares a PR; publication follows only after the release PR is merged and the publish path passes verification.

The workflow adds recovery guidance to the job summary when the version action fails, while preserving the original failure status. It does not query or change repository administration settings using GITHUB_TOKEN; that endpoint requires administration access beyond this job's permissions. See GitHub's workflow-permissions API.

Reference: Changesets automation, npm Trusted Publishing, tsdown package validation.

Query adapter package

pnpm build builds both runtime and the separate @openapi-chain/query package. pnpm test:query:package installs packed artifacts into a fresh consumer, checks ESM/CommonJS and TanStack/SWR types, performs real HTTP requests and checks browser bundle isolation. pnpm check repeats consumer verification with TS 6 and TS 7. Changesets includes this workspace package; the release packing job builds it through the root build command. The adapter has no runtime dependencies and does not alter the core gzip budget.

Documentation website

website/ is a private workspace package: a Fumadocs site on Next.js, exported as static files. It publishes the repository's Markdown rather than a copy of it. website/scripts/sync-docs.mjs turns docs/**/*.md, CONTRIBUTING.md, SECURITY.md and the package changelogs into website/content/docs/**/*.md before dev, build and typecheck. It uses each level-1 heading as the page title and its first sentence as the page description, drops the navigation line under the heading, and rewrites links: documents become site pages, and other repository files become GitHub links. Generated .md pages are ignored by Git; hand-written site pages are .mdx, and navigation lives in website/content/docs/**/meta.json.

The sync fails on a broken relative link, a missing heading anchor, or a published document absent from every meta.json. pnpm test:website runs that check across the repository and is part of pnpm check. pnpm website:dev watches the source documents and regenerates pages on save.

llms.txt, llms-full.txt and each page's Markdown copy are read by tools that do not apply the site's base path, so their site links are absolute. After exporting, pnpm website:build fails if one of those files contains a root-relative link or a site link without an exported page.

CI builds the site below /openapi-chain, the path GitHub Pages serves it from. The Website workflow deploys main to GitHub Pages when documentation or the site changes. It needs Settings → Pages → Build and deployment → Source: GitHub Actions. The site's lint, formatting and TypeScript follow the repository configuration; its Next.js and Fumadocs dependencies stay in the website package and never reach published packages.

On this page