diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..7e54016 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,3 @@ +# GitHub Copilot Instructions + +See [AGENTS.md](../AGENTS.md) for repository instructions. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f5af546 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,216 @@ +# AGENTS.md + +This file is for AI coding agents working in this repository. + +## Repository Purpose + +Better Fetch is a TypeScript monorepo for an advanced `fetch` wrapper. The core package, `@better-fetch/fetch`, provides typed fetch responses, Standard Schema validation, route schemas, lifecycle hooks, plugin support, authorization helpers, timeout and retry behavior, and error-as-value handling. The repository also includes `@better-fetch/logger`, a documentation site, and a Bun playground. + +Primary user-facing docs are published at . Local docs source lives in `doc/content/docs/`. + +## Architecture Summary + +The main package entry point is `packages/better-fetch/src/index.ts`. + +Core flow in `packages/better-fetch/src/fetch.ts`: + +1. `initializePlugins(url, options)` +2. Resolve fetch implementation with `getFetch` +3. Build `AbortController`, URL, headers, body, method, and request context +4. Run `onRequest` hooks +5. Execute `fetch(context.url, context)` +6. Run `onResponse` hooks +7. Parse success response, validate `output`, run `onSuccess` +8. Parse error response, run `onError`, retry when configured +9. Return `{ data, error }` unless `throw: true` is set + +`createFetch` in `packages/better-fetch/src/create-fetch/index.ts` wraps `betterFetch` with default options, header merging, schema application, plugin options, and optional `catchAllError`. + +## Important Directories + +- `packages/better-fetch/src/`: core library source. +- `packages/better-fetch/src/create-fetch/`: typed client factory, schema helpers, and type inference. +- `packages/better-fetch/src/test/`: Vitest tests for core behavior. +- `packages/logger/src/`: logger plugin source. +- `packages/logger/test/`: logger plugin tests. +- `doc/app/`: Next.js app routes and layout for the docs site. +- `doc/content/docs/`: MDX documentation source. +- `dev/`: Bun playground. +- `.github/workflows/`: CI and release workflows. + +## Coding Conventions + +- Use TypeScript. +- Keep exports flowing through package entry points. +- Prefer existing helpers over duplicating behavior: + - URL handling: `packages/better-fetch/src/url.ts` + - Request/response utilities: `packages/better-fetch/src/utils.ts` + - Auth headers: `packages/better-fetch/src/auth.ts` + - Plugin contracts: `packages/better-fetch/src/plugins.ts` + - Retry strategies: `packages/better-fetch/src/retry.ts` +- Preserve strict typing and type-level tests in Vitest. +- Biome is the configured formatter/import organizer. +- The Biome linter is disabled in `biome.json`; do not describe `pnpm lint` as a semantic lint pass. +- Avoid adding unrelated refactors while changing request behavior; the public API is type-heavy and easy to regress. + +## Design Patterns + +- Error-as-value is the default: successful calls return `{ data, error: null }`; failed HTTP responses return `{ data: null, error }`. +- `throw: true` changes successful return shape to parsed data and throws `BetterFetchError` on failed HTTP responses. +- Route schemas use Standard Schema V1 and are applied by an internal plugin created in `createFetch`. +- Plugins can mutate URL/options in `init`, add hooks, and optionally provide schemas. +- Hooks run in registration order. +- Request context identity is intentionally stable across replacing `onRequest` hooks. Tests cover this behavior. +- Headers passed as `Headers` are merged into a spreadable plain object before plugin initialization so plugins do not lose user headers. + +## Dependency Management + +- Package manager: pnpm `11.1.1`. +- Node version: `.nvmrc` specifies `24`. +- Workspaces are defined in `pnpm-workspace.yaml`: `packages/*`, `doc`, and `dev`. +- Package builds use `tsup`. +- Tests use Vitest. +- Docs use Next.js 14, React 18, Tailwind CSS, and Fumadocs. +- The dev playground uses Bun. + +## Commands + +Install: + +```sh +pnpm install +``` + +Build packages: + +```sh +pnpm build +``` + +Build docs: + +```sh +pnpm --filter doc build +``` + +Typecheck: + +```sh +pnpm typecheck +``` + +Test: + +```sh +pnpm test +pnpm test:watch +``` + +Format/check: + +```sh +pnpm lint +pnpm format +``` + +Run package watch mode: + +```sh +pnpm dev +``` + +Run docs locally: + +```sh +pnpm --filter doc dev +``` + +Run Bun playground: + +```sh +pnpm --filter dev serve +pnpm --filter dev client +``` + +## Common Workflows + +- Core fetch behavior change: update `packages/better-fetch/src/fetch.ts` or helper modules, then add tests in `packages/better-fetch/src/test/`. +- URL behavior change: update `packages/better-fetch/src/url.ts` and add coverage in `packages/better-fetch/src/test/url.test.ts`. +- Body/header parsing change: update `packages/better-fetch/src/utils.ts` and add coverage in `utils.test.ts` or `fetch.test.ts`. +- Type inference change: update files in `packages/better-fetch/src/create-fetch/` and add `expectTypeOf` coverage in `create.test.ts`. +- Plugin API change: update `packages/better-fetch/src/plugins.ts`, core hook wiring, and plugin tests. +- Logger change: update `packages/logger/src/` and `packages/logger/test/logger.test.ts`. +- Docs change: update MDX under `doc/content/docs/` and check the Next/Fumadocs app. + +## Self-Improvement Protocol + +Agents should keep this file accurate as the repository evolves. + +- When you discover a durable repository fact that would help future agents, add it to `AGENTS.md` in the most relevant section. +- When existing guidance in `AGENTS.md` is wrong, stale, ambiguous, or contradicted by source files, correct it in the same change. +- Base updates on repository evidence such as source code, package manifests, tests, docs, or CI configuration. +- Do not add guesses, temporary observations, local machine details, or task-specific notes that will not help future work. +- Keep updates concise and actionable; prefer file paths and commands over broad advice. + + +## Debugging Tips + +- Use `customFetchImpl` in tests to isolate behavior without network calls. +- Use H3/Listhen test servers when behavior depends on real request/response handling. +- Check `onRequest` context to confirm URL, method, headers, body, and signal before fetch executes. +- Check `hookOptions.cloneResponse` before reading a response body in hooks. +- Retry count tests expect the original failed request plus retry attempts. +- For validation failures, inspect `ValidationError.issues`. + +## Files Requiring Extra Caution + +- `packages/better-fetch/src/types.ts`: public option and response types. +- `packages/better-fetch/src/create-fetch/types.ts`: type inference for route schemas, plugin schemas, params, defaults, and `throw`. +- `packages/better-fetch/src/fetch.ts`: request lifecycle order. +- `packages/better-fetch/src/url.ts`: URL construction and encoding security. +- `packages/better-fetch/src/utils.ts`: serialization and parser behavior. +- `packages/better-fetch/src/plugins.ts`: public plugin and hook contracts. +- `.github/workflows/release.yml`: npm publishing behavior. +- `pnpm-lock.yaml`: dependency graph. + +## Things Not To Modify Casually + +- Do not change public type names, exported functions, or package exports without checking downstream impact. +- Do not remove Standard Schema compatibility or make Zod a hard runtime dependency of the core package. +- Do not change URL encoding rules without updating tests for path params, query params, and reserved path segments. +- Do not change hook order without updating docs and tests. +- Do not change package publishing fields (`main`, `module`, `types`, `exports`, `files`) without validating package output. +- Do not edit generated docs artifacts such as `doc/.map` if generated by Fumadocs tooling; update source MDX/config instead. + +## PR Expectations + +No PR template exists in the repository. Match CI expectations: + +- Build succeeds with `pnpm build`. +- Typecheck succeeds with `pnpm typecheck`. +- Tests pass with `pnpm test`. +- Formatting/import organization passes with `pnpm lint`. +- Behavior changes include focused tests. +- Public API changes include docs updates under `doc/content/docs/` when applicable. + +## Common Pitfalls + +- Root `pnpm build` builds only `packages/*`; it does not build `doc`. +- `pnpm lint` is Biome check with the Biome linter disabled, so it mostly covers formatting/import organization. +- `getURL` exists in both `packages/better-fetch/src/url.ts` and `packages/better-fetch/src/utils.ts`; core `betterFetch` imports from `url.ts`. +- `errorSchema` exists in types/options but is not currently applied in `fetch.ts`. +- Published docs may describe features differently from current source; verify against local code before changing implementation. +- Docs production metadata uses `VERCEL_URL`, but no deployment config file is present. + +## Glossary + +- `betterFetch`: Direct async fetch wrapper exported from `@better-fetch/fetch`. +- `createFetch`: Factory for configured fetch clients. +- `createSchema`: Helper for route schemas. +- Standard Schema: Validator interface used for schema-agnostic validation and type inference. +- Fetch schema: Map of route keys to schemas for `input`, `output`, `query`, `params`, `headers`, and method. +- Method modifier: Route prefix such as `@get/`, `@post/`, `@put/`, `@patch/`, or `@delete/`. +- Hook: Lifecycle callback such as `onRequest`, `onResponse`, `onSuccess`, `onError`, or `onRetry`. +- Plugin: Object with `id`, `name`, optional `init`, hooks, schema, and plugin-specific options. +- `BetterFetchError`: Error thrown for failed HTTP responses when `throw: true`. +- `ValidationError`: Error thrown when Standard Schema validation fails. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d2e013d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +# Claude Instructions + +See [AGENTS.md](./AGENTS.md) for repository instructions. diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..52c8261 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,3 @@ +# Gemini Instructions + +See [AGENTS.md](./AGENTS.md) for repository instructions. diff --git a/README.md b/README.md index 5f8b80f..8b3916e 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,236 @@ # Better Fetch -Advanced fetch wrapper for typescript with standard schema validations (using zod, valibot, arktype or any other compliant validator), pre-defined routes, callbacks, plugins and more. Works on the browser, node (version 18+), workers, deno and bun. +Better Fetch is an advanced `fetch` wrapper for TypeScript. It supports Standard Schema-compatible runtime validation and type inference, pre-defined route schemas, hooks, plugins, authorization helpers, retry behavior, and error-as-value responses. -# Documentation +The primary package is `@better-fetch/fetch` (`packages/better-fetch`). The repository also contains `@better-fetch/logger` (`packages/logger`), a Next.js/Fumadocs documentation site (`doc`), and a Bun playground (`dev`). -https://better-fetch.vercel.app/ +Documentation: + +## Features + +- `betterFetch` for direct requests with typed `data` and `error` responses. +- `createFetch` for configured clients with defaults such as `baseURL`, `headers`, `auth`, `retry`, plugins, and schemas. +- Standard Schema V1 validation for request body, query, params, headers, and output data. +- Route schemas through `createSchema`, including strict route inference and method modifiers such as `@post/path`. +- Hooks for request lifecycle stages: `onRequest`, `onResponse`, `onSuccess`, `onError`, and `onRetry`. +- Plugin API for request/response behavior and plugin-provided route schemas. +- Built-in Bearer, Basic, and Custom authorization header helpers. +- Timeout support through `AbortController`. +- Numeric, linear, and exponential retry strategies. +- Automatic response parsing for JSON, text, and binary responses. +- Cross-runtime fetch support when a global `fetch` implementation is available, with `customFetchImpl` for custom runtimes and tests. + +## Packages + +| Package | Location | Purpose | +| --- | --- | --- | +| `@better-fetch/fetch` | `packages/better-fetch` | Core fetch wrapper, typed client factory, schema helpers, hooks, plugins, retry, and utility types. | +| `@better-fetch/logger` | `packages/logger` | Better Fetch plugin that logs request, success, error, and retry events using `consola` by default. | +| `doc` | `doc` | Next.js 14 and Fumadocs documentation site. | +| `dev` | `dev` | Bun playground used for local examples. | + +## Architecture Overview + +The public exports for `@better-fetch/fetch` are collected in `packages/better-fetch/src/index.ts`. + +Core request flow lives in `packages/better-fetch/src/fetch.ts`: + +1. Initialize plugins with `initializePlugins`. +2. Resolve the fetch implementation with `getFetch`. +3. Normalize URL, headers, body, method, timeout, and abort signal. +4. Run `onRequest` hooks. +5. Execute `fetch`. +6. Run `onResponse` hooks. +7. Parse successful responses and optionally validate `output`. +8. Run `onSuccess`, or parse error responses and run `onError`. +9. Retry when configured. +10. Return `{ data, error }` or throw `BetterFetchError` when `throw: true`. + +Important modules: + +- `packages/better-fetch/src/create-fetch/` implements `createFetch`, route schema typing, schema application, strict schema inference, and plugin option inference. +- `packages/better-fetch/src/url.ts` handles `baseURL`, absolute URLs, query serialization, dynamic path params, path segment encoding, and method modifier cleanup. +- `packages/better-fetch/src/utils.ts` handles headers, body serialization, response type detection, custom fetch lookup, JSON parsing, timeout wiring, and Standard Schema validation. +- `packages/better-fetch/src/auth.ts` builds authorization headers. +- `packages/better-fetch/src/plugins.ts` defines hook and plugin contracts. +- `packages/better-fetch/src/retry.ts` defines retry options and strategies. +- `packages/logger/src/index.ts` implements the logger plugin. + +## Technology Stack + +- TypeScript +- pnpm workspaces +- `tsup` for package builds +- Vitest for tests +- Biome for formatting and import organization +- Next.js 14, React 18, Tailwind CSS, and Fumadocs for the docs site +- Bun for the `dev` playground +- GitHub Actions for CI and npm release workflows + +## Prerequisites + +- Node.js `24`, from `.nvmrc`. +- pnpm `11.1.1`, from `package.json`. +- Bun is needed only for the `dev` workspace scripts. +- A Standard Schema-compatible validator, such as Zod, Valibot, or ArkType, is needed only if you use runtime validation. + +## Installation + +For consumers: + +```sh +pnpm add @better-fetch/fetch +``` + +With runtime validation: + +```sh +pnpm add @better-fetch/fetch zod +``` + +Logger plugin: + +```sh +pnpm add @better-fetch/logger +``` + +## Local Development + +Install dependencies from the repository root: + +```sh +pnpm install +``` + +Start the core package in watch mode: + +```sh +pnpm dev +``` + +Start the documentation site: + +```sh +pnpm --filter doc dev +``` + +Run the Bun playground: + +```sh +pnpm --filter dev serve +pnpm --filter dev client +``` + +## Build + +Build publishable packages: + +```sh +pnpm build +``` + +Build the docs site: + +```sh +pnpm --filter doc build +``` + +The root `build` script only builds `packages/*`; it does not build `doc`. + +## Tests + +Run all package tests: + +```sh +pnpm test +``` + +Run tests in watch mode: + +```sh +pnpm test:watch +``` + +Tests are configured by `vitest.workspace.ts` and run against `packages/*`. + +## Typechecking + +```sh +pnpm typecheck +``` + +This runs `typecheck` across workspaces with `pnpm -r typecheck`. + +## Linting and Formatting + +Check formatting/import organization: + +```sh +pnpm lint +``` + +Apply Biome fixes: + +```sh +pnpm format +``` + +`biome.json` enables formatting and import organization. The Biome linter is disabled. + +## Environment Variables + +No repository-wide required environment variables are documented in source. + +The docs app reads `VERCEL_URL` outside development in `doc/lib/metadata.ts` and `doc/lib/utils.ts`. `.env` files are ignored by `.gitignore`. + +## Project Structure + +```text +. +├── .github/workflows/ # CI and release workflows +├── dev/ # Bun playground +├── doc/ # Next.js/Fumadocs documentation site +├── packages/ +│ ├── better-fetch/ # Core @better-fetch/fetch package +│ └── logger/ # @better-fetch/logger plugin package +├── biome.json # Biome formatting/import organization config +├── bump.config.ts # bumpp package version targets +├── package.json # Root workspace scripts +├── pnpm-workspace.yaml # Workspace definitions +└── vitest.workspace.ts # Vitest workspace config +``` + +## CI and Release + +CI is defined in `.github/workflows/ci.yml`. It runs on pull requests, pushes to `main`, and merge queue events. The workflow installs dependencies with pnpm, builds packages, typechecks, and runs tests. + +Releases are defined in `.github/workflows/release.yml`. Tags matching `v*` trigger a build and `pnpm -r publish --provenance --access public --no-git-checks`. Tags containing `alpha` or `beta` publish with the matching npm dist tag; other release tags require the commit to be on `main` and publish as `latest`. + +## Contributing + +No dedicated contributing guide or pull request template is present in the repository. Based on CI and scripts, contributors should: + +1. Install with `pnpm install`. +2. Make focused changes in the relevant workspace. +3. Run `pnpm build`, `pnpm typecheck`, and `pnpm test`. +4. Run `pnpm lint` or `pnpm format` for Biome formatting/import organization. +5. Add or update Vitest coverage when behavior changes. + +## Troubleshooting + +- `No fetch implementation found`: provide `customFetchImpl` or run in a runtime with global `fetch`. +- Relative URL errors: pass `baseURL` when calling relative paths. +- Validation errors: Standard Schema validation throws `ValidationError`; use `disableValidation` only when intentionally bypassing validation. +- Type inference with `throw: true` and generics: prefer `createFetch({ throw: true })` or follow the documented generic workaround. +- Docs production metadata: ensure `VERCEL_URL` exists when deploying the docs app in a Vercel-style environment. + +## Unknowns + +- No deployment configuration file was found for the docs site. +- No security policy was found. +- No repository-specific contributing guide was found. +- No `.env.example` was found. ## License -MIT +MIT. See `LICENSE.md` and `packages/better-fetch/LICENSE`.