.agents/skills/tanstack-pacer/SKILL.md
Framework-agnostic debouncing, throttling, rate limiting, queuing, and batching utilities for SolidJS.
npx skillsauth add em-jones/staccato-toolkit tanstack-pacerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
TanStack Pacer provides a unified, type-safe toolkit for controlling function execution timing. It offers class-based APIs, factory functions, and SolidJS signals/hooks for debouncing, throttling, rate limiting, queuing, and batching.
Core: @tanstack/pacer
Solid: @tanstack/solid-pacer
Status: Beta
npm install @tanstack/pacer
npm install @tanstack/solid-pacer # SolidJS hooks
Delays execution until after a period of inactivity.
import { Debouncer } from '@tanstack/pacer'
const debouncer = new Debouncer(
(query: string) => fetchSearchResults(query),
{
wait: 300, // ms of inactivity before execution
leading: false, // Execute on leading edge (default: false)
trailing: true, // Execute on trailing edge (default: true)
maxWait: 1000, // Force execution after 1s of continuous calls
enabled: true,
onExecute: (result) => console.log(result),
}
)
debouncer.maybeExecute('search term')
debouncer.cancel()
debouncer.getExecutionCount()
debouncer.setOptions({ wait: 500 }) // Dynamic reconfiguration
import { debounce } from '@tanstack/pacer'
const debouncedSearch = debounce(
(query: string) => fetchResults(query),
{ wait: 300 }
)
debouncedSearch('term')
debouncedSearch.cancel()
import {
useDebouncer,
useDebouncedCallback,
useDebouncedState,
useDebouncedValue,
} from '@tanstack/solid-pacer'
// Full debouncer instance with signal
const debouncer = useDebouncer(fn, { wait: 300 })
// Simple debounced function
const debouncedFn = useDebouncedCallback(fn, { wait: 300 })
// Debounced state management
const [debouncedValue, setValue] = useDebouncedState(initialValue, { wait: 300 })
// Debounced reactive value (returns accessor)
const debouncedValue = useDebouncedValue(reactiveValue, { wait: 300 })
import { createSignal } from 'solid-js'
import { useDebouncedValue } from '@tanstack/solid-pacer'
const [query, setQuery] = createSignal('')
// Returns a signal that updates after debounce delay
const debouncedQuery = useDebouncedValue(query, { wait: 300 })
// Access in JSX
debouncedQuery() // Current debounced value
Limits execution to at most once per interval.
import { Throttler } from '@tanstack/pacer'
const throttler = new Throttler(
(position: { x: number; y: number }) => updatePosition(position),
{
wait: 100, // Minimum interval between executions
leading: true, // Execute immediately on first call (default: true)
trailing: true, // Execute after interval with last args (default: true)
enabled: true,
onExecute: (result) => console.log(result),
}
)
throttler.maybeExecute({ x: 100, y: 200 })
throttler.cancel()
import {
useThrottler,
useThrottledCallback,
useThrottledState,
useThrottledValue,
} from '@tanstack/solid-pacer'
const throttledFn = useThrottledCallback(handleScroll, { wait: 100 })
// Throttled state - returns signal
const [throttledPos, setPos] = useThrottledState({ x: 0, y: 0 }, { wait: 100 })
// Access throttled value
throttledPos() // Current throttled value
Controls execution with a maximum count within a time window.
import { RateLimiter } from '@tanstack/pacer'
const limiter = new RateLimiter(
async (endpoint: string) => fetch(endpoint).then(r => r.json()),
{
limit: 10, // Max executions per window
window: 60000, // Time window in ms (60s)
enabled: true,
onExecute: (result) => console.log(result),
onReject: (...args) => console.warn('Rate limited:', args),
}
)
limiter.maybeExecute('/api/data') // Rejected if limit exceeded
limiter.getExecutionCount()
limiter.getRejectionCount()
import {
useRateLimiter,
useRateLimitedCallback,
useRateLimitedState,
useRateLimitedValue,
} from '@tanstack/solid-pacer'
const rateLimitedFn = useRateLimitedCallback(apiCall, { limit: 5, window: 1000 })
// Returns signal with rate limit state
const [rateLimitedState, setState] = useRateLimitedState(apiCall, { limit: 5, window: 1000 })
Sequential execution with configurable concurrency.
import { Queue } from '@tanstack/pacer'
const queue = new Queue({
concurrency: 1, // Max concurrent tasks
started: true, // Start processing immediately
})
queue.add(() => uploadFile(file1))
queue.add(() => uploadFile(file2))
queue.start()
queue.pause()
queue.clear()
queue.getSize() // Pending count
queue.getPending() // Currently executing count
import { createQueuer } from '@tanstack/solid-pacer'
const { add, start, pause, clear, size, pending } = createQueuer({
concurrency: 2,
})
// Returns signals
size() // Current queue size
pending() // Currently executing count
Groups calls for combined processing.
import { Batcher } from '@tanstack/pacer'
const batcher = new Batcher(
(items: LogEntry[]) => sendBatchToServer(items),
{
maxSize: 50, // Auto-flush at 50 items
wait: 1000, // Auto-flush after 1s
}
)
batcher.add(logEntry) // Accumulates
batcher.flush() // Manual flush
batcher.getSize() // Current batch size
batcher.clear() // Discard batch
import { createBatcher } from '@tanstack/solid-pacer'
const { add, flush, clear, size } = createBatcher(
(items: LogEntry[]) => sendBatchToServer(items),
{ maxSize: 50, wait: 1000 }
)
// Returns signals
size() // Current batch size
import { AsyncDebouncer, asyncDebounce, AsyncThrottler, asyncThrottle } from '@tanstack/pacer'
const asyncDebouncer = new AsyncDebouncer(
async (query: string) => {
const response = await fetch(`/api/search?q=${query}`)
return response.json()
},
{ wait: 300 }
)
// Solid async hooks
import { useAsyncDebouncer, useAsyncThrottler } from '@tanstack/solid-pacer'
const asyncDebouncer = useAsyncDebouncer(fn, { wait: 300 })
const asyncThrottler = useAsyncThrottler(fn, { wait: 100 })
| Scenario | Utility | Why | |----------|---------|-----| | Search input | Debouncer | Wait for user to stop typing | | Scroll events | Throttler | Periodic updates during activity | | API protection | RateLimiter | Hard limit on call frequency | | File uploads | Queue | Sequential processing | | Analytics events | Batcher | Group for efficiency | | Network requests | AsyncDebouncer | Handle abort/retry |
leading: true): Execute immediately, suppress until wait expires. Good for button clicks.trailing: true): Execute after activity stops. Good for search inputs.import { createSignal } from 'solid-js'
import { useDebouncedValue, useThrottledValue } from '@tanstack/solid-pacer'
const [input, setInput] = createSignal('')
// Debounced version - updates after 300ms of no changes
const debouncedInput = useDebouncedValue(input, { wait: 300 })
// Throttled version - updates at most every 100ms
const throttledInput = useThrottledValue(input, { wait: 100 })
import { useThrottledCallback } from '@tanstack/solid-pacer'
function ScrollComponent() {
const handleScroll = useThrottledCallback((e) => {
console.log(e.scrollTop)
}, { wait: 100 })
return <div onScroll={handleScroll}>...</div>
}
import { useDebouncedState } from '@tanstack/solid-pacer'
function SearchForm() {
const [query, setQuery] = useDebouncedState('', { wait: 300 })
// query() returns current debounced value
// setQuery updates immediately
return (
<input
value={query()}
onInput={(e) => setQuery(e.target.value)}
/>
)
}
maxWait with debouncing to guarantee execution during continuous activitysetOptions for dynamic reconfiguration (e.g., reducing wait for power users)onReject on RateLimiter to inform users when they're rate limitedmaxWait with debounce for long-running continuous eventsuseDebouncedValue() not useDebouncedValue)maxWait for continuous events that must execute(End of file - total 281 lines)
tools
<!--VITE PLUS START--> # Using Vite+, the Unified Toolchain for the Web This project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, but it invokes Vite through `vp dev` and `vp build`. ## Vite+ Workflow `vp` is a global binary that handles the full development lifecycle. Run `vp help` to pr
development
Guide for building performant data tables. Uses tanstack-table for table logic (sorting, filtering, pagination) and tanstack-virtual for rendering large datasets efficiently.
development
Expert guidance for building observable, expressive, and fault-tolerant TypeScript applications using the effect-ts/effect ecosystem. Covers Effect<A, E, R> type, error management, dependency injection via Layers, observability (logging, metrics, tracing), concurrency with Fibers, retry/scheduling, Schema validation, Streams, and Sinks.
tools
Complete E2E (end-to-end) and integration testing skill for TypeScript/NestJS projects using Jest, real infrastructure via Docker, and GWT pattern. ALWAYS use this skill when user needs to: **SETUP** - Initialize or configure E2E testing infrastructure: - Set up E2E testing for a new project - Configure docker-compose for testing (Kafka, PostgreSQL, MongoDB, Redis) - Create jest-e2e.config.ts or E2E Jest configuration - Set up test helpers for database, Kafka, or Redis - Configure .env.e2e environment variables - Create test/e2e directory structure **WRITE** - Create or add E2E/integration tests: - Write, create, add, or generate e2e tests or integration tests - Test API endpoints, workflows, or complete features end-to-end - Test with real databases, message brokers, or external services - Test Kafka consumers/producers, event-driven workflows - Working on any file ending in .e2e-spec.ts or in test/e2e/ directory - Use GWT (Given-When-Then) pattern for tests **REVIEW** - Audit or evaluate E2E tests: - Review existing E2E tests for quality - Check test isolation and cleanup patterns - Audit GWT pattern compliance - Evaluate assertion quality and specificity - Check for anti-patterns (multiple WHEN actions, conditional assertions) **RUN** - Execute or analyze E2E test results: - Run E2E tests - Start/stop Docker infrastructure for testing - Analyze E2E test results - Verify Docker services are healthy - Interpret test output and failures **DEBUG** - Fix failing or flaky E2E tests: - Fix failing E2E tests - Debug flaky tests or test isolation issues - Troubleshoot connection errors (database, Kafka, Redis) - Fix timeout issues or async operation failures - Diagnose race conditions or state leakage - Debug Kafka message consumption issues **OPTIMIZE** - Improve E2E test performance: - Speed up slow E2E tests - Optimize Docker infrastructure startup - Replace fixed waits with smart polling - Reduce beforeEach cleanup time - Improve test parallelization where safe Keywords: e2e, end-to-end, integration test, e2e-spec.ts, test/e2e, Jest, supertest, NestJS, Kafka, Redpanda, PostgreSQL, MongoDB, Redis, docker-compose, GWT pattern, Given-When-Then, real infrastructure, test isolation, flaky test, MSW, nock, waitForMessages, fix e2e, debug e2e, run e2e, review e2e, optimize e2e, setup e2e