# AGENTS.md — Chanfana

Chanfana is an OpenAPI 3/3.1 schema generator and request validator for Hono and itty-router on Cloudflare Workers. It uses Zod v4 for schemas and provides auto CRUD endpoints with D1 support.

## Commands

```bash
npm test                              # Run all tests (tsc + vitest)
npm test -- coverage-gaps.test.ts     # Run a single test file by name
npm test -- --coverage                # Run with Istanbul coverage report
npm run build                         # Build CJS + ESM to dist/
npm run lint                          # Lint via Biome (auto-fixes on failure)
```

Tests run inside `@cloudflare/vitest-pool-workers` with a real D1 binding. Config: `tests/vitest.config.mts`.

## Project Layout

```
src/
  adapters/        # Router adapters: hono.ts, ittyRouter.ts
  endpoints/       # Auto CRUD: create, read, update, delete, list
    d1/            # D1-specific implementations + SQL utilities (base.ts)
  zod/             # OpenAPI registry merger
  openapi.ts       # Core handler: route registration, schema generation, doc UI
  route.ts         # Base OpenAPIRoute class (validation, lifecycle, error handling)
  parameters.ts    # Query/path param coercion (string → number/boolean/BigInt/Date)
  exceptions.ts    # ApiException hierarchy (12 exception classes, codes 7000–7012)
  types.ts         # Shared TypeScript types
  index.ts         # Barrel re-exports (export * from every module)
tests/integration/ # All tests (no unit test directory)
docs/              # VitePress documentation
skills/            # AI coding skills (write-endpoints)
```

## Code Style

**Formatter/Linter**: Biome (`biome.json`). Key settings:
- 2-space indent, 120 char line width, double quotes, always semicolons, trailing commas on multiline
- `noExplicitAny` is OFF — `any` is used deliberately throughout

**TypeScript** (`tsconfig.json`): `strict: true`, `verbatimModuleSyntax: true` (requires `import type` for type-only imports), target ES2022, bundler module resolution.

**Imports**: Biome auto-organizes. External packages first (alphabetical), then relative imports. No blank lines between groups. Use `import type` for type-only:
```typescript
import { z } from "zod";
import type { AnyZodObject, RouteParameter } from "./types";
// or mixed:
import { MetaGenerator, type MetaInput, type O } from "./types";
```

**Naming**:
- Classes: `PascalCase` — suffixed `Endpoint` or `Exception` (`CreateEndpoint`, `NotFoundException`)
- Functions/methods/variables: `camelCase` (`getValidatedData`, `coerceInputs`)
- Module-level constants: `SCREAMING_SNAKE_CASE` (`HIJACKED_METHODS`)
- User-facing config keys: `snake_case` (`docs_url`, `openapi_url`, `default_message`)
- Booleans: `is`/`has` prefix (`isVisible`, `isRoute`, `includesPath`)
- Unused params: `_` prefix (`_args`, `_e`, `_oldObj`)

**Types**: Return types are generally inferred. Parameters are always explicitly typed. Class properties always have explicit types or initializers. `@ts-expect-error` is used when needed (e.g., `_meta` in endpoint subclasses).

**Error handling**: Exception class hierarchy rooted at `ApiException extends Error`. Each has `buildResponse()` and `static schema()`. Pattern in `execute()`:
```typescript
try {
  resp = await this.handle(...args);
} catch (e) {
  if (this.params?.raiseOnError) throw e;
  const errorResponse = formatChanfanaError(e);
  if (errorResponse) return errorResponse;
  throw e; // unknown error: re-throw
}
```

**Async**: Always `async/await`, never `.then()` chains. Use `for...of` for iteration, never `for...in`.

**Exports**: All named, no default exports. Barrel file `src/index.ts` uses `export *`.

## Zod v4 Rules (Critical)

All code must use Zod v4 syntax. Common mistakes:
```typescript
// WRONG (v3)          → CORRECT (v4)
z.string().email()     → z.email()
z.string().uuid()      → z.uuid()
z.string().datetime()  → z.iso.datetime()
z.string().date()      → z.iso.date()
z.string().url()       → z.url()
z.nativeEnum(X)        → z.enum([...])
obj.strict()           → z.strictObject({...})
{ message: "..." }     → { error: "..." }
z.ZodTypeAny           → z.ZodType
```

## Testing Patterns

Tests live in `tests/integration/`. Framework: Vitest with `describe`/`it` (not `test`).

**Two request-building approaches**:
1. `buildRequest({ method, path })` — plain object for itty-router (add `json: () => ({...})` for body)
2. `new Request(url, { method, body, headers })` — for Hono or when body/headers needed

**Test endpoint classes** are defined inline at the top of test files, before `describe` blocks. Named descriptively with `Endpoint` suffix (`FalsyDefaultsEndpoint`, `ThrowNotFoundEndpoint`).

**D1 tests** use `import { env } from "cloudflare:test"` and pass `env` as second arg to `router.fetch()`. Setup with raw SQL in `beforeEach`.

**Standard assertions**:
```typescript
expect(request.status).toBe(200);
expect(resp.success).toBe(true);
expect(resp.result).toEqual({ ... });
expect(resp.errors[0].code).toBe(7001);
```

## Architecture Quick Reference

**OpenAPIRoute lifecycle**: `execute()` → reset caches → `handle()` → catch errors → auto-JSON-wrap response

**Auto endpoints** (`CreateEndpoint`, `ReadEndpoint`, etc.) require a `_meta` property with `model.schema` (Zod), `model.primaryKeys`, and `model.tableName` (for D1). Optional `tags` for OpenAPI grouping. Support `before()`/`after()` hooks.

**Router adapters** (`fromHono`, `fromIttyRouter`) return a Proxy that intercepts route registration to capture OpenAPI metadata, then delegates to the underlying router.

**D1 endpoints** extend the base CRUD classes with SQL generation. Use parameterized queries exclusively. `d1/base.ts` provides `validateSqlIdentifier()`, `buildSafeFilters()`, `buildPrimaryKeyFilters()`, `handleDbError()`.

## Changesets

This project uses `@changesets/cli`. Add a changeset for user-facing changes:
```bash
npx changeset        # Interactive prompt
```
For internal-only changes (tests, CI), use an empty changeset (frontmatter only, no package entry).

## Key Resources

- Docs: https://chanfana.pages.dev
- Source: https://github.com/cloudflare/chanfana
- Detailed coding skill: `skills/write-endpoints/SKILL.md`
- Zod v4: https://zod.dev/v4
