Skip to content

Upgrade to Vitest 4.1, Vite 8, and @cloudflare/vitest-pool-workers 0.13#1138

Merged
threepointone merged 3 commits into
mainfrom
update-deps
Mar 20, 2026
Merged

Upgrade to Vitest 4.1, Vite 8, and @cloudflare/vitest-pool-workers 0.13#1138
threepointone merged 3 commits into
mainfrom
update-deps

Conversation

@threepointone
Copy link
Copy Markdown
Contributor

@threepointone threepointone commented Mar 20, 2026

Summary

Upgrades the test and build infrastructure to Vitest 4.1, Vite 8, and @cloudflare/vitest-pool-workers 0.13. This is a large migration that touches ~215 files across all packages, examples, and experimental projects.

Key dependency changes

  • vitest 3.2.4 → 4.1.0
  • vite 7.x → 8.0.1
  • @cloudflare/vitest-pool-workers 0.12.21 → 0.13.2
  • @cloudflare/workers-types → 4.20260317.1
  • @vitest/browser-playwright added (new requirement for vitest 4)
  • @rolldown/plugin-babel + @babel/plugin-proposal-decorators added (oxc workaround)
  • vite-plugin-devtools-json removed (inlined into scripts/)
  • Zod peer dependency narrowed: ^3.25.0 || ^4.0.0^4.0.0 (required for z.fromJSONSchema())

Migration guide

1. Vitest config: defineWorkersConfigcloudflareTest plugin

The old defineWorkersConfig from @cloudflare/vitest-pool-workers/config is removed. The new API uses a Vite plugin:

// Before
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
export default defineWorkersConfig({
  test: {
    poolOptions: {
      workers: {
        isolatedStorage: false,
        singleWorker: true,
        wrangler: { configPath: "./wrangler.jsonc" }
      }
    }
  }
});

// After
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";
export default defineConfig({
  plugins: [
    cloudflareTest({
      wrangler: { configPath: path.join(import.meta.dirname, "wrangler.jsonc") }
    })
  ]
});

Important: All path-based options (configPath, setupFiles, include, exclude) must use absolute paths via path.join(import.meta.dirname, ...) — the new plugin resolves relative to the project root, not the config file.

The isolatedStorage and singleWorker pool options no longer exist.

2. Test imports: cloudflare:testcloudflare:workers

env and SELF moved out of cloudflare:test:

// Before
import { env, SELF } from "cloudflare:test";

// After
import { env, exports } from "cloudflare:workers";
// SELF.fetch(...) → exports.default.fetch(...)

Test utilities (createExecutionContext, runInDurableObject, introspectWorkflowInstance, etc.) remain in cloudflare:test.

3. Type declarations: ProvidedEnvCloudflare.Env

The old ProvidedEnv interface on cloudflare:test is replaced by augmenting the Cloudflare namespace from @cloudflare/workers-types:

// Before (in each test file)
declare module "cloudflare:test" {
  interface ProvidedEnv extends Env {}
}

// After (centralized env.d.ts per test suite)
/// <reference types="@cloudflare/vitest-pool-workers/types" />

type _WorkerEnv = import("./worker").Env;

declare namespace Cloudflare {
  interface Env extends _WorkerEnv {}
  interface GlobalProps {
    mainModule: typeof import("./worker");
  }
}

Key details:

  • declare namespace Cloudflare in a module file (any file with import) must be wrapped in declare global { ... }. We centralized all declarations into env.d.ts files to avoid this pitfall.
  • Use a type alias (type _WorkerEnv = import("./worker").Env) then extends _WorkerEnv — using extends import("./worker").Env directly doesn't propagate properties correctly.
  • GlobalProps.mainModule types exports.default so exports.default.fetch() is properly typed.
  • The tsconfig types array changed: @cloudflare/vitest-pool-workers@cloudflare/vitest-pool-workers/types (the cloudflare:test types moved to a subpath export).

4. Agent class generics

With Cloudflare.Env augmented, Record<string, unknown> no longer satisfies extends Cloudflare.Env. Test agent classes need updating:

// Before
class MyAgent extends Agent<Record<string, unknown>> {}

// After — omit the generic (defaults to Cloudflare.Env)
class MyAgent extends Agent {}

// Or with a State type:
class MyAgent extends Agent<Cloudflare.Env, MyState> {}

5. React browser test config

Vitest 4 changed the browser provider API:

// Before
browser: { provider: "playwright" }

// After
import { playwright } from "@vitest/browser-playwright";
browser: { provider: playwright() }

render() from vitest-browser-react is now async — all render() calls need await.

6. vi.fn() mock constructors

Vitest 4 no longer allows arrow functions in vi.fn() to be used as constructors:

// Before (breaks with "is not a constructor")
vi.fn(() => mockInstance)

// After
vi.fn(function () { return mockInstance; })

7. DO stub RPC calls must be awaited

The new pool workers runs DO stubs via async RPC (no more singleWorker mode). Calls like agentStub.sql and other DO stub methods that were previously synchronous now need await:

// Before (sync in singleWorker mode)
agentStub.sql`INSERT INTO ...`;
const response = await agentStub.fetch(request);

// After (async RPC)
await agentStub.sql`INSERT INTO ...`;
const response = await agentStub.fetch(request);

8. Cold start warmup in test setup

The first exports.default.fetch() or DO stub call triggers full Vite module graph resolution. In CI this can take >10s, causing the first test in each file to timeout. Add a warmup request in the setup file:

import { beforeAll } from "vitest";
import { exports } from "cloudflare:workers";

beforeAll(async () => {
  await exports.default.fetch("http://warmup/");
}, 30_000);

Issues encountered and fixes

Oxc doesn't support TC39 decorators

Vite 8 replaced esbuild with oxc for TypeScript transforms. Oxc doesn't support TC39 decorators yet (oxc#9170), causing SyntaxError: Invalid or unexpected token when workerd evaluates code with @callable().

Fix: Added @rolldown/plugin-babel with @babel/plugin-proposal-decorators as a Vite plugin. Created a shared scripts/vite-plugin-decorator-transform.ts used by all vitest configs and vite configs that process decorator syntax. Added to 18 example/experimental/site vite configs.

Dynamic import("ai") hangs in DO context

The await import("ai") in mcp/client.ts caused exports.default.fetch() to hang indefinitely when routing to a Durable Object in a monorepo workspace. The Vite module runner tried to lazy-load @ai-sdk/gateway (a transitive dep of ai) via RPC from inside the DO context, crossing workerd's DO I/O isolation boundary.

Root cause: In the old vitest, SELF dispatched through workerd's service binding mechanism (pre-bundled modules). The new exports.default.fetch() runs through Vite's module runner, which lazy-loads uncached modules on demand. In a workspace, the module graph is large enough that some transitive deps stay uncached until a DO is instantiated.

Fix: Replaced await import("ai") with z.fromJSONSchema() from zod (already a direct dependency). This eliminates the ai runtime dependency entirely from the agents core — ai is now a type-only import. The ensureJsonSchema() method was removed as it's no longer needed. Zod peer dependency narrowed to ^4.0.0 since z.fromJSONSchema() is a Zod 4-only API.

Kumo theme override

The updated @cloudflare/kumo package uses Tailwind v4's @theme blocks for CSS custom properties. @theme declarations are unlayered CSS that beats @layer base regardless of source order. The workers.css theme from @cloudflare/agents-ui was in @layer base, so Kumo's blue brand color overrode the Workers orange.

Fix: Moved workers.css declarations out of @layer base to unlayered CSS. The [data-theme="workers"] selector has higher specificity than Kumo's :root, so it wins.

vitest-browser-react act() environment

Updated the vitest-browser-react patch for v2.1.0 — prevents the library from resetting IS_REACT_ACT_ENVIRONMENT to false after act() calls complete (needed for ai-chat react tests). For agents react tests (integration tests with real WebSocket connections), wrapped render to reset the flag after mounting since async WebSocket updates legitimately happen outside act().

New shared utilities

File Purpose
scripts/vite-plugin-decorator-transform.ts Shared Vite plugin for TC39 decorator support via babel
scripts/vite-plugin-devtools-json.ts Inlined Chrome DevTools JSON plugin (removes npm dep)
scripts/typecheck.ts Added filter arg for scoped type checking (tsx scripts/typecheck.ts agents)
packages/*/src/tests/env.d.ts Centralized Cloudflare.Env + GlobalProps type augmentation
packages/*/src/tests/setup.ts Worker warmup + DO cleanup (all packages)
patches/vitest-browser-react+2.1.0.patch Prevents act() environment reset

Test plan

  • packages/agents workers — 45/45 files, 745 tests pass, 8 skipped
  • packages/agents react — 14 tests pass (1 skipped)
  • packages/agents x402 — 33 tests pass
  • packages/agents cli — 12 tests pass
  • packages/agents e2e — 1 test pass
  • packages/ai-chat workers — 31/31 files, 297 tests pass
  • packages/ai-chat react — 28 tests pass
  • packages/think — 7/7 files, 126 tests pass
  • packages/voice workers — 5/5 files, 113 tests pass
  • packages/voice react — 152 tests pass
  • packages/shell — 3/3 files, 189 tests pass
  • packages/codemode — 8/8 files, 192 tests pass
  • packages/worker-bundler — 3/3 files, 121 tests pass
  • Type checking passes for all test tsconfigs

Add a vite decorator-transform plugin and wire it into many example/experimental vite.configs, migrate tests for Vitest (new env.d.ts files and numerous test updates), and add a changeset describing MCP schema conversion (replace dynamic import("ai") with z.fromJSONSchema and remove ensureJsonSchema). Also bump multiple example/experimental dependencies (kumo, tailwindcss, nanoid, viem, jose, postal-mime, cronstrue, etc.), update package.json/package-lock, add new scripts and patch files, and remove an old vitest-browser-react patch.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 20, 2026

🦋 Changeset detected

Latest commit: e3803ff

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
agents Minor
@cloudflare/ai-chat Minor
@cloudflare/think Minor
@cloudflare/codemode Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Mar 20, 2026

Open in StackBlitz

agents

npm i https://pkg.pr.new/agents@1138

@cloudflare/ai-chat

npm i https://pkg.pr.new/@cloudflare/ai-chat@1138

@cloudflare/codemode

npm i https://pkg.pr.new/@cloudflare/codemode@1138

hono-agents

npm i https://pkg.pr.new/hono-agents@1138

@cloudflare/shell

npm i https://pkg.pr.new/@cloudflare/shell@1138

@cloudflare/think

npm i https://pkg.pr.new/@cloudflare/think@1138

@cloudflare/voice

npm i https://pkg.pr.new/@cloudflare/voice@1138

@cloudflare/worker-bundler

npm i https://pkg.pr.new/@cloudflare/worker-bundler@1138

commit: e3803ff

Bump peer dependency range to require Zod ^4.0.0 across packages and update the changeset to reflect Zod v4 and MCP tool schema conversion (replace dynamic import with z.fromJSONSchema(), remove ensureJsonSchema()). Add Vitest test setup that warms up the Cloudflare worker module graph (beforeAll exports.default.fetch) and retains a short afterAll delay to avoid noisy Durable Object close-handler logs. Add a new setup file and enable setupFiles for the think package, and increase a flaky resumable-streaming test delay from 200ms to 1000ms to reduce CI timeouts.
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 9 additional findings in Devin Review.

Open in Devin Review

Comment thread packages/agents/src/index.ts
@threepointone threepointone merged commit 36e2020 into main Mar 20, 2026
4 checks passed
@threepointone threepointone deleted the update-deps branch March 20, 2026 11:51
@github-actions github-actions Bot mentioned this pull request Mar 20, 2026
@threepointone threepointone mentioned this pull request Apr 20, 2026
5 tasks
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.

1 participant