Skip to content

Add Biome no-type-assertion plugin and remove all as casts - #3

Merged
kamath merged 5 commits into
mainfrom
cursor/biome-no-type-assertion-7a32
Sep 25, 2026
Merged

kamath merged 5 commits into
mainfrom
cursor/biome-no-type-assertion-7a32

Conversation

@kamath

@kamath kamath commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds biome-plugin-no-type-assertion to the Biome setup from #2 and removes all 77 as type assertions it flagged. Where a value comes from outside the type system, it is now validated with zod (already a dependency of every package). Elsewhere, the cast is replaced by a type guard or TypeScript's own narrowing.

pnpm lint (main's biome check) now runs the formatter, the recommended rules and the plugin. as const is still allowed.

What changed

Untrusted data is parsed with zod instead of cast

  • HTTP bodies: /screen in app.ts, /api/chat (the body is parsed with zod, then messages are checked with the AI SDK's validateUIMessages), and web UI error and browser-handle responses.
  • CDP responses: /json/version, Page.captureScreenshot, and DOM.getContentQuads (its Quad type is now derived from a zod tuple).
  • Hosted provider API JSON, sandbox bridge payloads (JSON.parse of host-call arguments and log entries), and the browser handle saved in sessionStorage.
  • MCP tool results in the web app: CallToolResultSchema from the MCP SDK.

Browse command params

  • screenshot.type, mouse button, waitUntil, wait load <state>, and clip are validated by small zod-backed helpers (optEnum, oneOf, optClip). Invalid values now raise a BrowseCommandError instead of passing through. The allowed values are exported from page.ts (LOAD_STATES, MOUSE_BUTTONS), and the types derive from them.

QuickJS variant

  • @jitl/quickjs-wasmfile-release-sync ships CommonJS typings. Under NodeNext its default import is typed as { default: variant }, but ESM runtimes (Node, workerd) hand over the variant itself. The new sandbox/variant.ts unwraps it with a runtime type guard. It is used by runner.ts, worker.ts and runtime-bundled.ts.

browser_run input schema (tools)

  • The double cast that claimed both cdpUrl and sessionId exist is gone. inputSchema is now the honest union of the two modes.
  • The AI SDK and Mastra wrappers accept the union unchanged.
  • examples/02-mcp-server narrows it with an in check before handing the shape to registerTool.
  • The Mastra toModelOutput callbacks receive unknown because Mastra types output only through outputSchema, so they parse their input with zod.
  • LiveView.url now also accepts undefined.

Casts that were simply unnecessary

  • in-operator narrowing in the web UI's codeOf/textOf.
  • The AI SDK dynamic flag in examples/01-hello-world.
  • ErrorOptions in CdpEndpointError.
  • connectOverCdp in core.ts.
  • App.fetch is narrowed to the single argument every caller passes.

Tests

  • Result shapes are parsed with zod, so a wrong shape fails loudly.
  • Mastra tools get a real execute context (RequestContext + noopObserve) instead of {} as never.

Behavior changes

  • Browse commands reject enum values outside the documented LoadState, MouseButton and "png" | "jpeg" types; previously they were forwarded to CDP.
  • /api/chat returns 400 for a malformed body or malformed messages; previously a malformed body could throw and return 500.

Validation

  • pnpm lint and pnpm check (all 5 packages)
  • pnpm test (62 tests) and pnpm build
  • test:runtime and test:worker against real Chrome
  • test:e2e: 14/14 against real Chrome
Open in Web Open in Cursor 

cursoragent and others added 5 commits September 24, 2026 22:04
Lint-only config (formatter off, no preset rules) so the only check is the
no-type-assertion Grit plugin. Adds a root `pnpm lint` script.

Co-authored-by: Anirudh Kamath <kamath@users.noreply.github.com>
- Parse untrusted JSON (HTTP bodies, CDP responses, provider APIs, sandbox
  bridge payloads, sessionStorage) with zod instead of casting.
- Validate enum-like browse params (screenshot type, mouse button, load
  state, clip) with zod-backed helpers; export the allowed values from page.ts.
- Unwrap the QuickJS variant with a runtime guard: its CJS typings disagree
  with the ESM runtime shape.
- Let browser_run's input schema be the honest union of the two modes and
  narrow it where a single shape is needed (examples/02-mcp-server).
- Drop casts TypeScript can already narrow (in-operator, AI SDK dynamic flag).

Co-authored-by: Anirudh Kamath <kamath@users.noreply.github.com>
Test results are parsed with zod so a wrong shape fails loudly; Mastra tools
get a real execute context (RequestContext + noopObserve) instead of {} as never.

Co-authored-by: Anirudh Kamath <kamath@users.noreply.github.com>
Main (#2) added Biome with the recommended preset and formatter and
reformatted the codebase. Conflicts resolved by normalizing formatting on
both sides and three-way merging the content:

- biome.json/package.json: main's config and scripts, plus the
  no-type-assertion plugin.
- page.ts: keep main's Quad tuple and area(); derive Quad from a zod
  tuple schema so the quads cast stays removed.
- examples/02-mcp-server: keep main's sessions guard and this branch's
  sessionId shape narrowing.
- tools.test.ts: keep main's non-null-assertion removals, with zod parsing
  in place of the casts.
- api.chat.ts: type `let messages` for noImplicitAnyLet.
biome.json loads the no-type-assertion plugin from node_modules, which the
standalone setup-biome binary does not have.
@kamath
kamath marked this pull request as ready for review September 25, 2026 00:20
@kamath
kamath merged commit 3244f78 into main Sep 25, 2026
2 checks passed

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ac13c2e. Configure here.

errorText?: string;
};
const parsed = toolPartSchema.safeParse(part);
if (!parsed.success) return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In-progress tool calls hidden

Medium Severity

toolPartSchema treats input and output as required, but AI SDK dynamic-tool parts omit those keys while a call is streaming or waiting (input-streaming / input-available). asToolCall then fails safeParse and returns null, so in-progress browser_run rows never render and the existing running state is unused until the call finishes.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ac13c2e. Configure here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants