Skip to content

Release 2.0.0-beta.5 - #432

Merged
saeedjamshaid merged 5 commits into
betafrom
dev
Oct 9, 2026
Merged

saeedjamshaid merged 5 commits into
betafrom
dev

Conversation

@saeedjamshaid

Copy link
Copy Markdown
Collaborator

Promotes dev to beta, expected to publish 2.0.0-beta.5 to the npm beta dist-tag as a GitHub prerelease. The run commits nothing back to beta; the tag goes on this merge commit.

Merge with Create a merge commit.

What releases

🤖 Generated with Claude Code

mehnoorsiddiqui and others added 5 commits October 7, 2026 15:02
The API playground's OAuth sign-in (authorization code and implicit
flows) now redirects to `<portal url>/oauth/callback` from every
endpoint page, so portal owners register one redirect URI with their
provider (Slack, for example) instead of every endpoint page's URL. Refs
apimatic/apimatic-io#2255.

fumadocs-openapi 12.1 added `oauthRedirectUrl` for this
([fuma-nama/fumadocs#3611](fuma-nama/fumadocs#3611)),
with a `createOAuthHandler()` route that needs a server. Published
portals are static files, so a prerendered page does the same in the
browser: it sends the provider's answer back to the page that started
the sign-in, or stays to show the provider's error. It refuses a
starting page on another site, and forgets the starting page once read.
When it can't tell where the sign-in started, it links to the portal's
home page.

The CLI now refuses a content page at `/oauth/callback`, which the
template's route would hide, and its reserved-address error now says
such a page would be served "where the portal puts pages of its own".

Not done here:
- Sign-in has been tried against a mock provider, not a real one yet.
- The callback URL and its home-page link ignore a base path for now;
both need one once portals can be hosted under a path
(apimatic-io#2275).
- Portal-owner docs for the single redirect URI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Muhammad Rafay Nadeem <muhammad.nadeem@apimatic.io>
Follow-up to #418, which was approved on the condition that its review
comments are addressed in a separate PR. Every comment is addressed
here.

## Changes

- **Real paths for the dependencies and the Vite binary** (finding 3).
The package lookup answers canonical paths, so a Windows 8.3 short name
or a symlinked install can no longer leave the entry's `/@fs/` import
refused in every browser. The new unit test fails without the change on
a pnpm checkout.
- **The narrowest directory holding the dependencies** (finding 1). The
allow list took the outermost `node_modules` above each linked package:
every global package under an npm global install, and only each
package's own directory under a pnpm global install, where 69 of the
page's 120 `/@fs/` files fell outside it. It now takes the directory all
the linked installations share: the CLI's own `node_modules` under npm,
the `.pnpm` or `links` store under pnpm. Where the installations share
nothing short of the disk's root, as with an `npm link`ed dependency on
another drive, each is listed on its own rather than widening to the
root. Unit tests cover the four layouts in the review's table and the
divergent case.
- **Vite's default workspace root is kept** (finding 5), and the project
directory is `portalProjectDirectory` after the glossary (inline
comment).
- **Nothing is skipped** (finding 9, tightened in review). A missing
`node_modules` or a link leading nowhere stops the config load with its
own error, naming the path, rather than surfacing later as a blank page.
- **Serve tests check what came back** (finding 7): a JavaScript content
type for the dependency file and `image/png` for the static image, since
a missing file answers 200 with the fallback page. Cleanup only removes
what set-up made (finding 10).
- **One-line comments**, as the review's table asks.

## Recommendation 5: `Cache-Control: no-store`, tested in Safari

Tested on macOS, Safari, from the `dev` commit before #418 so the bug
could show. Control: the first preview loaded and the next preview on
the same port went blank. With `no-store` on the page: every run blank.
With `no-store` on every module too: first run loaded, every later run
blank, Safari restarted between. In Web Inspector the entry module,
`virtual:tanstack-start-dev-client-entry`, showed `(memory)` as its
transfer size in every case, on reload too. Safari serves the module
from its memory cache whatever the header says, which confirms finding
6's premise and rules out the header as a fix. The allow list stays.

## Verification

- Full suite, both end-to-end suites, lint and type check pass.
- Replaying the review's measurement: the page's 120 `/@fs/` files,
asked of a fresh server before anything else, are all served from a real
`npm install -g` and a real `pnpm add -g` (pnpm 11, `links` store). The
old boundary refused 69 under pnpm.
- A build of this branch at 7754fb5 on macOS, Safari, with the stale
cache from earlier previews in place: quickstart, two repeat previews, a
page with a static image over two previews, and a second quickstart all
loaded. The commits since restructure the fold, add the root guard and
let the lookup throw; the unit and serve tests cover them, and on a
project linked like the CLI links it the old and new lookup answer the
same allow list.

## Note on Windows CI

As finding 8 says, on `windows-latest` the test project lands inside the
repository, which Vite's default already trusts, so the serve tests pass
there with or without the allow list. They guard it on macOS and Linux.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…dopted projects (#405)

## Summary

`apimatic api validate` works inside a project without `--file` or
`--url`: it zips `<input>/src/spec/` and validates the whole folder.
`quickstart` validates an adopted project the same way, and keeps the
project's own `apimatic.json`.

## `api validate`

- **No flags:** zips everything in `<input>/src/spec/` and validates it.
`-i`/`--input` is new and defaults to `./`. A spec split across files
stays together with what it `$ref`s, and findings name the file inside
the archive (`schemas/pet.yaml:1:1`). A single document is labelled with
its own name instead of `UntitledAPI`.
- **Flags:** `--file`, `--url` and `-i` are mutually exclusive (`[--file
<value> | --url <value> | -i <value>]`). Each is refused, by name, when
given empty or blank, so `--file "$SPEC" -i "$DIR"` with `SPEC` unset
points at `--file`.
- **Refused before anything is uploaded:**
  - a missing or empty `spec/`
  - a `spec/` that cannot be read, with the reason
- a symlink or junction anywhere in `spec/`, each one named (the zip
library used to crash on a linked folder)
- a `--file` that cannot be read, in `api transform` too (it used to
throw `ENOENT` with no outro)
- **`--url` downloads** are named by `Content-Disposition`, or by the
URL path without its query string. `sanitizeFilename` now strips path
separators as it was meant to, so names like `../../escaped.json` or
`specs/openapi.json` stay inside the temp directory.

## `quickstart`

**Adopted project** (`src/spec/` holds anything):

- Validates all of `spec/`, as `api validate` does. It used to upload
only the portal's document, so a split spec failed quickstart and passed
`api validate`.
- Requires `src/apimatic.json`, as valid JSON in the schema version it
reads. A missing file, one that is not valid JSON, or one such as
`"schemaVersion": 2` stops it before validating, and the refusal says
why: "it is not valid JSON", or the version it does not read. It no
longer creates the file.
- Checks `spec/` as the portal build does (`resolveSpecs()`), so Swagger
2.0 is refused with the build's `api transform` command before anything
is written.
- Uses `apimatic.json` as it is, adding only these:
- `portal.site.name` when missing, named after the first spec
`resolveSpecs()` returns (the whole `site` when the block has none)
- the placeholder `plugin` block when there is none. An existing block
is left alone, malformed or not, for the backend to report.
- `languages`, asked for only when the file names none (no block, or
`{}`, by the rule the build applies), and only after the additions
above. Choosing none cancels and leaves those additions in place. A
block of the wrong shape is not asked about or written to, and is left
for the build to report.
- Writes `content/index.md` and `content/nav.json` only when `content/`
is missing.

**New project:** a spec the user names is copied or downloaded into a
`user-spec/` temp folder, apart from the sample's `sample/`, so the two
never share a path.

**Validation failure note:** names the command that validates the same
spec: `apimatic api validate` for an adopted project, or `--file=<path>`
/ `--url=<url>` on its own line for a named one. The OpenAPI 3.x refusal
names a named spec by its file name, not the wizard's temporary copy.

When `apimatic.json` or its `portal` block is missing, `portal generate`
and `portal serve` now say "Run `apimatic quickstart` in an empty
directory with a sample spec to see an example of a valid portal."

<details>
<summary>Messages</summary>

```text
$ apimatic api validate --file ""
    Error: Parsing --file
    	The value is empty. Give a value, or leave the flag out.

$ apimatic api validate --file does-not-exist.json
■  'C:\…\does-not-exist.json' does not exist or could not be read.

$ apimatic api validate    # spec/ missing or empty
■  No API specification found in 'C:\…\src\spec'. Add yours there, point --input at the directory that holds 'src', or give a spec with --file or --url.

$ apimatic api validate    # a symlink in spec/
■  'schemas' in 'C:\…\src\spec' is a symlink, which cannot be uploaded for validation. Replace it with a copy of the files it points to.

$ apimatic quickstart      # spec/ filled, no apimatic.json
■  'C:\…\src\spec' already holds files, but 'C:\…\src' has no 'apimatic.json'.
│  Add the project's 'apimatic.json' there, or run apimatic quickstart from a directory with no 'src' to start a new portal.

$ apimatic quickstart      # spec/ filled, "schemaVersion": 2
■  The 'apimatic.json' in 'C:\…\src' cannot be used: its 'schemaVersion' is 2, which this version of the CLI does not read; it reads 1.
```

</details>

## Code changes

- `ResourceInput` includes `ProjectContext`, built by
`createResourceInput` when no flag is given.
`ResourceContext.resolveTo(input, tempDirectory)` turns any input into
the one file to upload: `spec/` zipped, a URL downloaded, or a file
copied.
- `ValidateAction.execute` takes an input or an already-resolved
`ResourceContext`, so quickstart validates the copy it scaffolds from.
It returns `ActionResult<void, ValidateActionFailure>`, whose error is
`'invalid'` or `'unchecked'`, in place of `SpecCheck` and `onChecked`.
Quickstart treats a failure with no error as `'unchecked'`.
- `api transform` still requires `--file` or `--url`
(`createFileOrUrlInput`), since it names its output after the input
file.
- `CONTEXT.md` adds **Spec directory**. `.ai/skills/action.md` resolves
through `ResourceContext.resolveTo` and reports with `specUnavailable`.
- `commands/api/validate.ts`, `resource-input.ts`,
`resource-context.ts`, `actions/api/transform.ts`, `flags-provider.ts`
and `file-download-service.ts` switch to single quotes; they had never
been through Prettier.
- The README's `api validate` section is updated by hand from the
120-column `--help`, and its "Upgrading from 1.x" section is removed: it
sent upgraders to run quickstart in a project that has no
`apimatic.json`, which quickstart now refuses.

## Verification

**`api validate` against the live service**

- A two-file spec with a cross-file `$ref` passes with no flags;
`--file` on its main document fails with "Reference could not be
resolved".
- `-i test/resources/portal-inputs/default` gives the same findings as
`--file` on its one document.
- A junction in `spec/`, a missing `--file`, and an empty `--file`,
`--url` or `-i` each fail with their message and exit 1.
- `--url` names carrying `../`, `../../../../`, `..\..\`, `nested/dir/`
or `..%2F` are all written inside the temp directory. Before the fix,
the deeper ones escaped it and a folder name failed with `ENOENT`.

**`quickstart` against the live service** (adopted unless noted)

- Template `v2` (`"site": {}`, three languages, no plugin): gains
`site.name` and the placeholder plugin, `content/` and every other value
stay as they were, no languages question, and the preview serves.
- Two specs with `"site": {}`: named after the first spec, and the
preview serves.
- A malformed `plugin` block is left as written, and the backend reports
`'pluginId' must be lower-case kebab-case`.
- `{}`: asked for languages once `portal.site`, the placeholder plugin
and `content/` are written. The choice is recorded and the preview
serves.
- `"languages": { "typescript": true }`: no languages question, and the
preview reports `'languages.typescript' is not a JSON object.`
`"schemaVersion": 2` is refused before step 1, by version, and a
truncated `apimatic.json` as "not valid JSON", each with the file
untouched and nothing written.
- A read-only `src/` stops with "'apimatic.json' could not be written",
and nothing is written.
- A two-file spec passes validation and the `spec/` check. An invalid
spec, a `spec/` holding only a `README.md`, Swagger 2.0, and a missing
`apimatic.json` each stop with their message and leave `src/` untouched.
- New project from a URL: still asks for languages, records them, and
the preview serves.

**Tests:** `pnpm test` gives 1666 passing, 46 pending, and `eslint src
test` is clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Oct 9, 2026

Copy link
Copy Markdown

@saeedjamshaid
saeedjamshaid merged commit 1415033 into beta Oct 9, 2026
10 of 11 checks passed
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.0.0-beta.5 🎉

The release is available on:

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants