diff --git a/AGENTS.md b/AGENTS.md index 0d5443b535..5400e37000 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,248 +1,153 @@ # AGENTS.md -This file provides guidance to AI coding agents working in this repository. - -## Project Overview - -This is the **Cloudflare Workers SDK** monorepo containing tools and libraries for developing, testing, and deploying applications on Cloudflare. The main components are Wrangler (CLI), Miniflare (local dev simulator), and Create Cloudflare (project scaffolding). - -## Development Commands - -**Package Management:** - -- Use `pnpm` - never use npm or yarn -- `pnpm install` - Install dependencies for all packages -- `pnpm build` - Build all packages (uses Turbo for caching) - -**Testing:** - -- `pnpm test:ci` - Run tests in CI mode -- `pnpm test:e2e` - Run end-to-end tests (requires Cloudflare credentials) -- `pnpm test -F "pattern"` - Run a single test by name pattern - -**Code Quality:** - -- `pnpm check` - Run all checks (lint, type, format) -- `pnpm fix` - Auto-fix linting issues and format code - -**Working with Specific Packages:** - -- `pnpm run build --filter ` - Build specific package -- `pnpm run test:ci --filter ` - Test specific package -- `pnpm --filter test:watch` - Watch mode for a specific package - -## Architecture Overview - -**Core Tools:** - -- `packages/wrangler/` - Main CLI tool for Workers development and deployment -- `packages/miniflare/` - Local development simulator powered by workerd runtime -- `packages/create-cloudflare/` - Project scaffolding CLI (C3) -- `packages/vite-plugin-cloudflare/` - Vite plugin for Cloudflare Workers - -**Development & Testing:** - -- `packages/vitest-plugin/` - Vitest integration for testing Workers in actual runtime -- `packages/chrome-devtools-patches/` - Modified Chrome DevTools for Workers debugging - -**Shared Libraries:** - -- `packages/pages-shared/` - Code shared between Wrangler and Cloudflare Pages -- `packages/workers-shared/` - Code shared between Wrangler and Workers Assets -- `packages/workers-utils/` - Utility package for common Worker operations -- `packages/workflows-shared/` - Internal Cloudflare Workflows functionality -- `packages/containers-shared/` - Shared container functionality -- `packages/unenv-preset/` - Cloudflare preset for unenv (Node.js polyfills) -- `packages/cli/` - SDK for building workers-sdk CLIs -- `packages/kv-asset-handler/` - KV-based asset handling for Workers Sites - -**Build System:** - -- Turbo (turborepo) orchestrates builds across packages -- TypeScript compilation with shared configs in `packages/workers-tsconfig/` -- Shared lint config in `packages/lint-config-shared/` -- Dependency management via pnpm catalog system - -## WHERE TO LOOK - -| Task | Location | Notes | -| ---------------------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Add/modify a CLI command | `packages/wrangler/src/` | Commands registered in `src/index.ts` (2k+ line yargs tree) | -| Change local dev behavior | `packages/miniflare/src/` | `src/index.ts` is the main `Miniflare` class | -| Modify Workers runtime simulation | `packages/miniflare/src/workers/` | ~30 embedded worker scripts, built via `worker:` virtual imports | -| Add a test fixture | `fixtures/` | Each fixture is a full workspace member with own `package.json` | -| Shared config types/validation | `packages/workers-utils/src/config/` | `validation.ts` is the config normalizer (large file) | -| Test helpers (runInTempDir, seed, mockConsole) | `packages/workers-utils/src/test-helpers/` | Shared across wrangler, miniflare, others | -| Cloudflare API mocks for tests | `packages/wrangler/src/__tests__/helpers/msw/` | MSW handlers per API domain | -| CI workflows | `.github/workflows/` | `test-and-check.yml` is the primary gate | -| Build/deploy scripts | `tools/deployments/` | Validation + deployment helpers, run via `esbuild-register` | -| Deploy/versions-upload validation | `packages/deploy-helpers/src/deploy/helpers/validate-worker-props.ts` | `validateWorkerProps()` for sync checks, `preUploadApiChecks()` for API checks (service metadata, config diff, secrets, workflows). All new pre-upload validation goes here. | -| Changeset config and rules | `.changeset/README.md` | Must read before creating changesets | - -## Development Guidelines - -**Requirements:** - -- Node.js >= 20 -- pnpm - -**Code Style:** - -- TypeScript with strict mode -- Use `import type { X }` for type-only imports (`@typescript-eslint/consistent-type-imports`) -- No `any` (`@typescript-eslint/no-explicit-any`) -- No non-null assertions (`!`) -- No floating promises - must be awaited or explicitly voided (`@typescript-eslint/no-floating-promises`) -- Always use curly braces for control flow (`curly: error`) -- Use `node:` prefix for Node.js imports (`import/enforce-node-protocol-usage`) -- Prefix unused variables with `_` -- No `.only()` in tests (`no-only-tests/no-only-tests`) -- Prefer `function` declarations over `const` arrow function assignments for named/exported functions -- Add a well formatted JSDoc comment to every function, ideally including tags such as `@param` and `@returns` -- ESLint disable comments must use double-dash separator: `// eslint-disable-next-line rule-name -- reason here` -- Never modify generated files directly — modify the generator or config, then regenerate -- Format with oxfmt - run `pnpm prettify` in the workspace root before committing -- All changes to published packages require a changeset (see below) - -**Formatting (oxfmt):** - -- Tabs (not spaces), double quotes, semicolons, trailing commas (es5) -- Import order enforced: builtins → third-party → parent → sibling → index → types -- `sortPackageJson` option sorts package.json keys - -**Security:** - -- Custom ESLint rule `workers-sdk/no-unsafe-command-execution`: no template literals or string concatenation in `exec`/`spawn`/`execFile` calls (command injection prevention, CWE-78). Disabled in test files only. - -**Dependencies:** - -- Packages must bundle deps into distributables; runtime `dependencies` are forbidden except for an explicit allowlist -- External (non-bundled) deps must be declared in `scripts/deps.ts` with `EXTERNAL_DEPENDENCIES` and a comment explaining why -- After updating dependencies, always run `pnpm i` to also update the package lock file - -**Testing Standards:** - -- Unit tests with Vitest for all packages -- Fixture tests in `/fixtures` directory for filesystem/Worker scenarios -- E2E tests require real Cloudflare account credentials -- Use `vitest-plugin` for testing actual Workers runtime behavior -- Shared vitest config (`vitest.shared.ts`): 50s timeouts, `retry: 1`, `restoreMocks: true` -- Vitest 4 pool config: use `maxWorkers: 1` instead of the removed `poolOptions.forks.singleFork: true` when tests must run sequentially -- **`expect` must come from test context** — never `import { expect } from "vitest"`: - - Use destructured test context: `it("name", ({ expect }) => { ... })` - - For helper functions that need `expect`, pass it as a parameter with type `ExpectStatic` - - Always use `import type` for `ExpectStatic`: `import { beforeAll, type ExpectStatic, test } from "vitest"` - - When test context is unavailable (e.g. setup files), use `node:assert` instead - - E2E vitest configs do NOT set `globals: true` — this rule is critical there; forgetting `{ expect }` in the callback causes `ReferenceError` at runtime -- When changing user-facing strings or output messages, update corresponding test snapshots -- New test fixtures in `vitest-plugin-examples/` must include a `tsconfig.json` -- Test fixtures serve as user-facing recipes — use clean patterns, avoid type casting where possible -- Use the `runInTmpDir()` utility instead of mocking filesystem operations. Real filesystem operations are preferred over mocking. The utility creates isolated temporary directories, handles cleanup automatically in `afterEach` hooks, and allows tests to write actual files and assert against them -- Use the `mockConsoleMethods()` helper to capture stdout/stderr. Use the pattern `const std = mockConsoleMethods()` in test setup, then access captured output via `std.out`, `std.err`, `std.warn` properties. Assert against captured output using `expect(std.out).toMatchInlineSnapshot()` -- Run specific wrangler test files locally using `pnpm -w test:ci -F wrangler -- [test-file-name]` (e.g. `pnpm -w test:ci -F wrangler -- r2.test.ts`) - -**Git Workflow:** - -- Check you are not on main before committing. Create a new branch for your work from main if needed. -- Clean commit history required before first review -- Don't squash commits after review -- Never commit without changesets for user-facing changes -- PR template requirements: Remove "Fixes #..." line when no relevant issue exists, keep all checkboxes (don't delete unchecked ones) - -**Creating Pull Requests:** - -- Always use the PR template from `.github/PULL_REQUEST_TEMPLATE.md` - do not replace it with your own format -- Fill in the template: replace the issue link placeholder, add description, check appropriate boxes -- Keep all checkboxes in the template (don't delete unchecked ones) -- PR title format: `[package name] description` (e.g. `[wrangler] Fix bug in dev command`) -- If the change doesn't require a changeset, add the `no-changeset-required` label -- CI validates the PR description (see `tools/deployments/validate-pr-description.ts`). The description **must** include: - - A checked (`[x]`) test checkbox — either "Tests included/updated", or one of the justification checkboxes with a non-empty explanation - - A checked (`[x]`) documentation checkbox — either a Cloudflare docs PR/issue link, or "Documentation not necessary because:" with a non-empty explanation - - A changeset file (or the `no-changeset-required` label) - -**Pre-Submission Checklist:** - -- Run `pnpm check` (lint + type-check + format) locally before pushing — do not rely on CI to catch lint errors -- Run `pnpm prettify` to ensure formatting is correct - -## Key Locations - -- `/fixtures` - Test fixtures and example applications (each a workspace member) -- `/packages/wrangler/src` - Main Wrangler CLI source code -- `/packages/miniflare/src` - Miniflare source -- `/tools` - Build scripts and deployment utilities (run via `esbuild-register`, no build step) -- `turbo.json` - Turbo build configuration -- `pnpm-workspace.yaml` - Workspace configuration (~156 workspace members) - -## Testing Strategy - -**Package-specific tests:** Most packages have their own test suites -**Integration tests:** Use fixtures to test real-world scenarios -**E2E tests:** Test against actual Cloudflare services (requires auth) -**Workers runtime tests:** Use vitest-plugin for workerd-specific behavior - -Run `pnpm check` before submitting changes to ensure all quality gates pass. - -## Changesets - -Every change to package code requires a changeset or it will not trigger a release. Read `.changeset/README.md` before creating changesets. - -**Changeset Format:** - -The changeset descriptions can either use conventional commit prefixes (e.g., "fix: remove unused option") or -start with a capital letter and describe the change directly (e.g., "Remove unused option" not"). - -**Changeset Rules:** - -- Major versions for `wrangler` are currently **forbidden** -- `patch`: bug fixes; `minor`: new features, deprecations, experimental breaking changes; `major`: stable breaking changes only -- No h1/h2/h3 headers in changeset descriptions (changelog uses h3) -- Config examples must use `wrangler.json` (JSONC), not `wrangler.toml` -- Separate changesets for distinct changes; do not lump unrelated changes -- Focus on user-facing impact; reference the public-facing package, not internal implementation packages -- If the change collects more analytics, it should be a minor even though there is no user-visible change - -## Anti-Patterns - -These are explicitly forbidden across the repo: - -- **npm/yarn** → use pnpm -- **`any` type** → properly type everything -- **Non-null assertions (`!`)** → use type narrowing -- **Floating promises** → await or void explicitly -- **Missing curly braces** → always brace control flow -- **`console.*` in wrangler** → use the `logger` singleton -- **Direct Cloudflare REST API calls** → use the Cloudflare TypeScript SDK -- **Named imports from `ci-info`** → use default import (`import ci from "ci-info"`) -- **Runtime dependencies** → bundle deps; external deps need explicit allowlist entry -- **Committing to main** → always work on a branch -- **Trivial/obvious code comments** → don't add comments that restate what the code does; comments should explain "why", not "what" -- **Duplicating types/constants across packages** → export from the owning package and import where needed - -## Subdirectory Knowledge - -Packages with their own AGENTS.md for deeper context: - -- `packages/wrangler/AGENTS.md` - CLI architecture, command structure, test patterns -- `packages/miniflare/AGENTS.md` - Worker simulation, embedded workers, build system -- `packages/vite-plugin-cloudflare/AGENTS.md` - Plugin architecture, playground setup -- `packages/create-cloudflare/AGENTS.md` - Scaffolding, template system -- `packages/vitest-plugin/AGENTS.md` - 3-context architecture, cloudflare:test module -- `packages/workers-utils/AGENTS.md` - Shared config validation, test helpers - -When making architectural changes to a package (renaming files, adding entry points, changing build output), update the relevant AGENTS.md to reflect the new structure. - -## Cloudflare Workers Specifics - -- When removing or modifying scheduled functions in Cloudflare Workers, remember to update both the code in the Worker file and the corresponding cron trigger in the `wrangler.jsonc` configuration file. - -## Adding Native Node.js Module Support (unenv-preset) - -- The authoritative source for Node.js module compatibility flags and dates is the workerd repository's `compatibility-date.capnp` file at https://github.com/cloudflare/workerd/blob/main/src/workerd/io/compatibility-date.capnp. -- If the module is marked as `$experimental` in workerd (no `$impliedByAfterDate`), follow the pattern used by other experimental modules in `preset.ts`. -- The pattern for adding a new module override involves: - - Creating a `getOverrides()` function similar to existing ones (e.g., `getVmOverrides()`) - - Adding the override to `getCloudflarePreset()` and spreading into `dynamicNativeModules` and `dynamicHybridModules` - - Adding tests to `packages/wrangler/e2e/unenv-preset/preset.test.ts` - - Adding test functions to `packages/wrangler/e2e/unenv-preset/worker/index.ts` +This file helps AI coding agents work effectively in the Cloudflare Workers SDK +monorepo. Prefer authoritative configuration and documentation over copying +details into this file: copied versions, rule lists, and counts become stale. + +## Start Here + +- Use `pnpm`, not npm or yarn. +- Use the Node.js and pnpm versions declared in `package.json`. +- Install dependencies with `pnpm install`. +- Run commands from the workspace root unless package documentation says + otherwise. +- Before changing a package, read its `AGENTS.md` if it has one. +- Do not edit generated files directly. Change their source or generator and + regenerate them. + +## Common Commands + +The root `package.json` is authoritative for available scripts. + +- `pnpm build` — build the workspace with Turbo. +- `pnpm test:ci` — run tests in CI mode. +- `pnpm test:e2e` — run end-to-end tests; many require Cloudflare credentials. +- `pnpm check` — run the repository's validation, lint, type, and format checks. +- `pnpm fix` — apply supported lint and formatting fixes. +- `pnpm prettify` — format files with oxfmt. +- `pnpm run