Guidelines for AI coding agents working in this repository.
@studiolambda/query is a lightweight, isomorphic, framework-agnostic async data management library (SWR-style). It has bindings for React 19+ and Solid.js.
# Install dependencies
npm install
# Run all tests
npm test
# Run tests in watch mode
npm run dev
# Run a single test file
npx vitest run src/query/query.test.ts
# Run tests matching a pattern
npx vitest run -t "can query resources"
# Run with coverage
npm run test:cover
# Lint
npm run lint
# Format code
npm run format
# Check formatting
npm run format:check
# Build (runs format:check and lint first)
npm run build
# Build without checks
npm run build:onlysrc/
query/ # Core library (framework-agnostic)
react/ # React bindings
hooks/ # useQuery, useQueryBasic, etc.
components/# QueryProvider, QueryPrefetch, etc.
solid/ # Solid.js bindings (partial)
- Single quotes, no semicolons
- 2-space indentation (no tabs)
- 100 character line width
- Trailing commas:
es5 - Always use parentheses in arrow functions:
(x) => x - Configuration:
.oxfmtrc.json
- External/framework imports first, then internal imports
- Use path aliases:
query:index,query/react:context,query/react:hooks/useQuery - Use inline
typekeyword for type imports:import { type Options } from 'query:index'
- Multi-line imports with trailing comma:
import { type Caches, type CacheType, type ItemsCacheItem } from 'query:cache'
- Strict mode enabled with
noUnusedLocals,noUnusedParameters,noImplicitReturns - Use
interfacefor object shapes,typefor unions and function signatures - Use
readonlyon interface properties - Generic type parameters with defaults:
<T = unknown> - Explicit type assertions when needed:
as T
| Element | Convention | Example |
|---|---|---|
| Files (modules) | camelCase | useQuery.ts, cache.ts |
| Files (components) | PascalCase | QueryProvider.tsx |
| Test files | .test.ts/.test.tsx suffix |
query.test.ts |
| Functions | camelCase | createQuery, defaultFetcher |
| React hooks | use prefix |
useQuery, useQueryActions |
| Types/Interfaces | PascalCase | Cache, Configuration |
| Function types | *Function suffix |
FetcherFunction, MutationFunction |
| Props interfaces | *Props suffix |
QueryProviderProps |
- Use
functiondeclarations for named/exported functions (not arrow functions) - Use arrow functions only for callbacks and inline functions
- Use
async/awaitfor async code
// Correct - regular function for exports
export function createQuery(options?: Configuration): Query {
// ...
}
// Correct - arrow for callbacks
events.addEventListener(`${event}:${key}`, listener)- Named exports only - never use default exports
- Use barrel files (
index.ts) for re-exports - Factory pattern for main API:
createQuery()returns object with methods
- Simple
throw new Error('message')for errors - Try-catch with event emission pattern for async operations
- Explicit empty catch
catch(() => {})when intentionally silencing errors
- JSDoc-style block comments for function documentation
- Inline comments for explaining specific logic
- Document interface properties with JSDoc
/**
* Subscribes to a given keyed event.
*/
function subscribe<T = unknown>(...) {
// For the refetching event, we want to immediately return...
}- Test framework: Vitest with happy-dom environment
- Use
describe.concurrent()for parallel test execution - Destructure
expectfrom test context:it('...', async ({ expect }) => { ... }) - React tests use
act()andcreateRoot
import { describe, it, vi } from 'vitest'
describe.concurrent('feature', function () {
it('does something', async ({ expect }) => {
// test code
expect(result).toBe(expected)
})
})- Configuration:
.oxlintrc.json - React plugin enabled with hooks rules
- Vitest plugin enabled for test files
- TypeScript plugin enabled
react-in-jsx-scoperule disabled (using new JSX transform)- Use
// oxlint-disable-next-lineto disable rules inline
This project uses React Compiler (babel-plugin-react-compiler v1.0.0+) to automatically optimize React components at build time.
- Runs via
@rolldown/plugin-babelwithreactCompilerPreset()exported from@vitejs/plugin-react - Applied only to
src/react/**/*.tsxfiles - Plugin order in
vite.config.ts:react()(JSX transform) thenbabel()(compiler) — this is the officially recommended order - Peer dependencies:
@babel/core,@rolldown/plugin-babel,babel-plugin-react-compiler
- Do NOT use
useMemo,useCallback, orReact.memo— the compiler handles memoization automatically - Do NOT mutate props, state, or values returned from hooks — the compiler assumes immutability
- All React code must strictly follow the Rules of React
useEffectEventis used for stable event handler references that should not be listed as effect dependencies- Prefer
functiondeclarations for components (not arrow functions assigned to variables)
This project uses Conventional Commits and git-cliff for automated changelog generation and version bumping.
feat: new functionality (triggers minor bump)fix: bug fix in library code (triggers patch bump)docs: documentation changesrefactor,perf,style,test: non-breaking improvements
Important: Use chore (not fix or feat) for CI, build, or tooling changes that don't affect the published library. For example, use chore(ci): fix workflow instead of fix(ci): fix workflow. Using fix or feat for CI-only changes will trigger an unnecessary version bump and release.
The project uses a single GitHub Actions workflow (main.yml) with three jobs:
- check — lint, format check, tests
- build — verifies the build succeeds (depends on check)
- release — version bump, changelog, npm publish, GitHub Release (depends on build, only on push to main)
The build job does not verify the build on PRs — only the release job builds before publishing. The separate build job exists as an early CI gate so build failures are caught before the release job runs. The release job builds again from scratch because GitHub Actions jobs don't share artifacts between runners.
npm publish uses --ignore-scripts to avoid double-building (the release job already ran build:only before publishing, so prepack is unnecessary).
- Node.js 25+ (see
.nvmrc) - npm 11+ (package manager)
- TypeScript ~5.9.3
- React 19.2+ (peer dependency)