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

@absolutejs/rate-limit

v0.3.0

Published

First-class 2026 rate limit for Bun + Elysia. GCRA by default (exact, O(1) memory per key, no boundary effect — the algorithm Stripe uses), IETF draft-09 RateLimit-* headers, IPv6 /64 grouping, X-Forwarded-For trust modes, BigInt nanosecond TAT, pluggable

Readme

@absolutejs/rate-limit

First-class, from-scratch, 2026 rate limit for Bun + Elysia. GCRA by default (the algorithm Stripe uses — exact, O(1) memory per key, no boundary effects), IETF draft-09 RateLimit-* headers, IPv6 /64 grouping, X-Forwarded-For trust modes, BigInt nanosecond TAT (no float drift), pluggable store (LRU in-memory bundled).

import { Elysia } from 'elysia';
import { rateLimit, gcra } from '@absolutejs/rate-limit';

new Elysia()
  .use(rateLimit({
    algorithm: gcra({ requestsPerPeriod: 100, periodMs: 60_000, burst: 20 }),
    key: 'ip',
    trustedProxies: 1, // single CDN/LB hop trusted
  }))
  .get('/', () => 'ok')
  .listen(3000);

Why not just use the existing libraries

The leader in the Elysia ecosystem is rayriffy/elysia-rate-limit. It uses a token bucket over a Map with a setInterval cleanup. That's fine, but every choice in it is from 2020. The 2026 from-scratch answers:

| Concern | What others do | What we do | |---|---|---| | Algorithm | Token bucket (allows brief bursts at refill boundaries) | GCRA by default. Exact, no boundary effect, one BigInt of state per key. Token bucket and sliding window also bundled when you want them. | | Time precision | Date.now() ms (float drift over long-lived keys) | BigInt nanoseconds for GCRA TAT. Drift-free over any horizon. | | IPv6 | Per-/128 (one cap per device — a /64 user with N devices gets N× their cap) | Per-/64 group by default (RIR allocation = one user). Configurable. | | Proxy trust | Trust X-Forwarded-For blindly OR ignore it | trustedProxies: N — honor only the last N hops; spoof-resistant by default (N=0). | | Headers | X-RateLimit-* (legacy GitHub-style) | IETF draft-09 RateLimit-* + RateLimit-Policy by default. Legacy + both modes also available. | | Store cleanup | setInterval sweeper (timer per process; lock contention on iteration) | Lazy TTL at lookup time. No background timer. | | Hot path | Async even on memory store | Sync when the store is sync, async only when it has to be. | | CDN headers | XFF only | XFF + cf-connecting-ip + fly-client-ip + true-client-ip + x-real-ip. |

Surface

Elysia plugin (@absolutejs/rate-limit)

rateLimit({
  algorithm: gcra({ requestsPerPeriod: 100, periodMs: 60_000, burst: 20 }),
  store: memoryStore({ maxKeys: 100_000 }),

  // key — defaults to 'ip' (uses extractIp).
  key: 'ip',                                                   // built-in
  // key: 'authorization',                                       // built-in
  // key: (ctx) => ctx.request.headers.get('x-user') ?? null,    // custom

  trustedProxies: 1,         // honor only the last hop of XFF (typical: 1)
  ipv6Prefix: 64,            // group IPv6 by /64

  skip: (ctx) => ctx.request.headers.get('authorization') === ADMIN_TOKEN,

  headers: 'standard',       // 'standard' | 'legacy' | 'both' | false

  onLimit: (ctx, info) => new Response(JSON.stringify({
    ok: false,
    retryAfterSec: info.decision.retryAfterSec,
  }), { status: 429, headers: { 'Content-Type': 'application/json' } }),
})

Algorithms (@absolutejs/rate-limit/core)

import { gcra, tokenBucket, slidingWindow, memoryStore } from '@absolutejs/rate-limit/core';

const limiter = gcra({ requestsPerPeriod: 100, periodMs: 60_000, burst: 20 });
const store = memoryStore();
const decision = limiter.check(store, 'user-42', Date.now());
// → { allowed, limit, remaining, retryAfterSec, resetSec, policy }

Use the core directly for non-HTTP rate-limiting — WebSocket-message rate limiting, queue-consumer throttling, AI-call quotas.

Store interface

type Store = {
  update<T>(key: string, ttlMs: number, fn: (prev: T | null) => T): T | Promise<T>;
  delete?(key: string): void | Promise<void>;
  clear?(): void | Promise<void>;
};

Implement update atomically (read-modify-write); the rest is optional. For Bun in-process: memoryStore(). For multi-replica Bun deployments: Redis with a Lua script for GCRA atomicity (sibling package, @absolutejs/rate-limit-redis, ships later).

Algorithm picker

  • GCRA — pick this unless you have a reason not to. Exact, O(1) memory, no boundary effects, BigInt math = no drift. Burst is configurable; set burst: 0 for strict pacing.
  • Token bucket — pick this when you specifically WANT the "fill the bucket and let the user fire it all at once" behavior. Cleaner mental model for end-users.
  • Sliding window — pick this when the customer-facing story is "you have N requests in the last M seconds." It's an approximation that can under-count by ~1 slot at heavy clustering, but it's the most intuitive to explain on a status page.

License

BSL 1.1 with a named carveout for the hosted rate-limit / WAF / API gateway throttling category (Cloudflare WAF + Rate Limiting, AWS WAF + API Gateway throttling, Kong, Tyk, Imperva, Akamai, GCP Cloud Armor, Azure Front Door rate-limiting, Upstash Ratelimit, Stripe's API gateway throttling). See LICENSE. Change Date: 4 years from first release; Change License: Apache 2.0.