npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aurelienbbn/agentlint-plugin-tanstack-query

v0.1.3

Published

Custom agentlint rules for TanStack Query projects.

Readme

@aurelienbbn/agentlint-plugin-tanstack-query

npm downloads CI license node

4 agentlint reviews for TanStack Query: every query and mutation shows its loading, error and stale states; data is fetched by key, not by command.

[!WARNING] Requires the engine @aurelienbbn/agentlint >=0.3.0 <0.5.0 as a peer. Evidence.

Quick start

agentlint init --preset "@aurelienbbn/agentlint-plugin-tanstack-query#starterPreset"
agentlint rules test
agentlint rules scan --review
agentlint next --format json

init keeps an existing config and prints the install command; never installs. Calibrate bindings before requiring agentlint check --all.

import { defineConfig } from "@aurelienbbn/agentlint";
import { queryFreshnessIntent, strictPreset } from "@aurelienbbn/agentlint-plugin-tanstack-query";

export default defineConfig({ extends: [strictPreset], rules: [queryFreshnessIntent] });

Rules

| Rule | strictPreset | starterPreset | Authority | Standard rev | Detector | Skips | | --------------------------- | :------------: | :-------------: | --------- | :----------: | :------: | ----------------- | | query-state-coverage | ✅ | ✅ | agent | 2 ⚠️ | 2 | | | mutation-state-coverage | ✅ | | agent | 1 | 1 | tests, test utils | | imperative-query-fetching | ✅ | | agent | 1 | 1 | tests | | query-freshness-intent | 🧪 | | agent | 1 | 1 | tests, test utils |

✅ in preset · 🧪 opinionated, add queryFreshnessIntent to rules. Presets ignore **/*.d.ts.

⚠️ query-state-coverage rev 2: UI hooks own the review. Shared queryOptions / infiniteQueryOptions no longer get duplicate prompts; Suspense hooks are included, with pending and error handling allowed in surrounding Suspense/error boundaries.

trpc.todo.list.useQuery(undefined); // ❌ query-state-coverage
queryOptions({ queryKey, queryFn }); // ✅ shared options, no duplicate prompt
trpc.todo.save.useMutation(); // ❌ mutation-state-coverage
React.useEffect(() => {
  todos.refetch();
}, [filter]); // ❌ imperative-query-fetching
useQuery({ queryKey: ["user", id], queryFn, enabled: Boolean(id) }); // ✅ input in the key
queryOptions({ queryKey, queryFn, staleTime: 0, refetchOnMount: false }); // ❌ query-freshness-intent

Recognized: documented hook names, explicit type arguments, member calls (tRPC, openapi-react-query). Not yet: renamed imports and custom wrapper hooks; they need consumer fixtures first.

useQuery<Array<Todo>, Error>({ queryKey: ["items"] }); // ❌ fires
api?.useQuery("get", "/todos"); // ❌ fires: optional chain
const { data } = useSuspenseQuery(todosOptions); // ❌ fires: reviewed against Suspense + error boundary
prefetchQuery({ queryKey: ["items"] }); // ✅ silent
myUseQuery({});
trpc.todo.useQueryState(); // ✅ silent: not a hook name

Hooks: useQuery, useQueries, useInfiniteQuery, useSuspenseQuery, useSuspenseQueries, useSuspenseInfiniteQuery, with or without type arguments, bare or as the last segment of a member chain.

| Case | Review checks | | -------------------------------------- | ------------------------------------------------------------------------------------- | | Suspense hooks | the Suspense fallback and a resettable error boundary, not local isPending branches | | call mentions enabled or skipToken | lazy query: spinners use isLoading or fetchStatus, never isPending | | every query | stale data stays visible after a failed background refetch | | placeholderData | results are marked with isPlaceholderData |

Fires on bare, generic and member-chain useMutation calls (useMutation<Todo, Error, Input>(…)). Silent: useMutationState, custom wrappers like useSaveMutation().

pending feedback · duplicate-submission safety · actionable errors
deliberate retries · success reconciliation · paused/offline behavior

Generic interaction contract; does not duplicate Shopify's form and save-bar reviews.

useQuery({ queryKey: ["search"], queryFn: () => search(term), enabled: false }); // ❌ fires
trpc.user.byId.useQuery(id, { enabled: false }); // ❌ fires
const onClick = () => {
  void refetch();
}; // ✅ silent: event handler
export const flag = { name: "beta", enabled: false }; // ✅ silent: not query options

| Fires on | Where | | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | literal enabled: false | a query options object: has queryKey/queryFn, or is an argument of a query hook or queryOptions/infiniteQueryOptions | | refetch(), x.refetch(), refetchTodos() | inside a useEffect / useLayoutEffect callback, in a file that uses a query hook |

Apollo-only files stay silent.

One finding per options object, inside query options or defaultOptions.queries:

| Fires on | Stays silent | | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------- | | refetchOnWindowFocus / refetchOnMount / refetchOnReconnect: false | a non-zero staleTime beside it | | gcTime: 0 | | | retry: false or retry: 0 | retry outside query options (upload(file, { retry: false })) | | new QueryClient( in a file with "use client" or hydration calls, no staleTime | a plain new QueryClient() elsewhere |

queryOptions({ queryKey, queryFn, refetchOnMount: "always", retry: 2 }); // ✅ silent

| Fact | Detail | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | rule shape | lifecycle, standard (revision), detector (version), binding (id, authority, scope, material options) | | composing | defineConfig({ extends: [preset] }) or defineConfig({ rules: [configuredRule] }); repeated uses need distinct binding ids | | authority | defaults permit agent acceptance; repository owners choose scope and can raise authority to human | | accepting a finding | requires matching current evidence and authority | | fixtures | @aurelienbbn/agentlint/testing runs the embedded activation/silence fixtures with the real parser |

Registered contract inventory

Generated from package exports by pnpm catalog. Rule-specific options and limitations are described above and in the source tests.

| Rule/check | Trigger or review scope | | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | imperative-query-fetching | Flags queries fetched by command: a literal enabled: false in query options, or refetch() called from an effect. | | mutation-state-coverage | Flags useMutation calls, including member-call spellings, that need user-visible pending, error, retry, success, and duplicate-submission review. | | query-freshness-intent | Flags switched-off refetch triggers, retries, or cache time (refetchOn*: false, retry: false\|0, gcTime: 0) and server-rendered QueryClients without a staleTime, which need a stated freshness intent. | | query-state-coverage | Flags TanStack Query hook calls (useQuery, useQueries, useInfiniteQuery and their suspense variants, including generic and member-call spellings) that need visible-state coverage. |

Credited concepts

  • TanStack Query mutations guide (documentation inspiration; independently implemented)
  • TkDodo's blog, "React Query FAQs" by Dominik Dorfmeister (concept: inputs belong in the query key, not in refetch; independently worded)
  • TkDodo's blog, "Status Checks in React Query" by Dominik Dorfmeister (concept: data-first status ordering, independently worded)

Credits

Guidance and detectors are independently written; nothing copied.

| Rule | Concept source | | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | query-state-coverage | data-first status ordering: TkDodo's blog by Dominik Dorfmeister, "Status Checks in React Query", and the TanStack Query guides | | imperative-query-fetching | "inputs belong in the query key": TkDodo's blog, "React Query FAQs", and the TanStack Query guides | | mutation-state-coverage | the official TanStack Query mutations guide | | starterPreset onboarding | desloppify by Peter O'Malley: conceptual inspiration, no code or guidance copied |