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

@askalf/redisflex

v0.2.1

Published

One Redis API. Two modes. ioredis for production, in-process Map+EventEmitter for standalone / dev. Also ships a BullMQ-shaped in-memory queue so you can drop the Redis dep entirely when you don't need it.

Readme

@askalf/redisflex

One Redis API. Two modes. Same call sites.

Switch between real Redis (ioredis) and in-process Redis (Map + EventEmitter) with one line of config. Production runs on real Redis; dev / standalone / "no-Docker mode" runs in-process. Same get/set, counters, hashes, lists, sets, sorted sets, pub/sub — drop the Redis server when you don't need it.

Also ships a BullMQ-shaped in-memory queue so you can drop the Redis dep entirely for queueing too.

npm install @askalf/redisflex

CI npm License

Why

Most apps use Redis for three things: cache, pub/sub, and queueing. Most dev environments don't want to spin up a Redis server for any of them. Most CI environments REALLY don't want it.

redisflex swaps a real Redis connection for an in-process implementation that speaks the same surface — get/set, counters, hashes, lists, sets, sorted sets, pub/sub, expiry, and a sliding-window-rate-limit-shaped Lua eval. Plus a tiny BullMQ-API-compatible queue if you use BullMQ for jobs.

Your call sites stay identical. Flip the mode in config and your app no longer needs Redis to run.

Use it

Direct

import { createRedisAdapter } from '@askalf/redisflex';

// Production — real Redis
const redis = createRedisAdapter({
  mode: 'ioredis',
  url: process.env.REDIS_URL!,
});

// Dev / standalone — in-process
const redis = createRedisAdapter({ mode: 'memory' });

await redis.set('user:1', 'alice');
await redis.publish('events', 'user.created');

From environment

import { createRedisAdapterFromEnv } from '@askalf/redisflex';

// REDISFLEX_MODE=memory     → in-process
// otherwise                 → ioredis at $REDIS_URL (or redis://localhost:6379)
const redis = createRedisAdapterFromEnv();

Custom env-var names:

const redis = createRedisAdapterFromEnv({
  modeEnvVar: 'MYAPP_MODE',
  urlEnvVar: 'MYAPP_REDIS',
  defaultUrl: null, // null = throw if URL missing, instead of defaulting
});

In-memory queue

import { InMemoryQueue, InMemoryWorker } from '@askalf/redisflex';

const queue = new InMemoryQueue('emails');
const worker = new InMemoryWorker(
  'emails',
  async (job) => {
    await sendEmail(job.data);
  },
  { queue, concurrency: 4 },
);

worker.on('completed', (job) => console.log('sent', job.id));
worker.on('failed', (job, err) => console.error('failed', job.id, err));
worker.on('drained', () => console.log('all caught up')); // once per drain

await queue.add('welcome', { to: '[email protected]' });
await queue.add('reminder', { to: '[email protected]' }, {
  delay: 60_000,
  attempts: 3,
  backoff: { delay: 5000 }, // exponential: 5s, 10s, 20s
});

await queue.getJobCounts();
// { delayed: 1, waiting: 0, active: 1, completed: 0, failed: 0 }

await worker.close(); // waits for in-flight jobs; close(true) skips the wait

The shape matches BullMQ's Queue / Worker so you can swap to real BullMQ later by changing imports. Jobs move through the BullMQ states (delayed → waiting → active → completed/failed; a retrying job counts as delayed while its backoff timer is pending) — getJobs(states) and getJobCounts(...states) report them, and the queue re-emits completed/failed so you don't need a separate QueueEvents object. Completed/failed history is retained for introspection, capped at 1000 each (oldest dropped); set removeOnComplete/removeOnFail per job to skip retention.

What's covered

| Family | Operations | |---|---| | Key/Value | get, set (incl. trailing EX <seconds> / PX <ms>), setex, mget, del, keys, exists | | Counters | incr, decr, incrby, hincrby | | Hashes | hset, hget, hgetall, hdel | | Lists | lpush, rpush, lpop, rpop, llen, lrange | | Sets | sadd, srem, smembers, sismember, scard | | TTL | expire, pexpire, ttl, pttl | | Sorted Sets | zadd, zcard, zscore, zrange, zrangebyscore, zrem, zremrangebyscore (incl. -inf/+inf and exclusive (n bounds) | | Pub/Sub | publish, subscribe, unsubscribe, psubscribe, on('message'/'pmessage') | | Scripting | eval (sliding-window-rate-limit shape recognized in memory mode) | | Lifecycle | duplicate, quit |

That's enough surface for cache, pub/sub, BullMQ-style queues, sliding-window rate limits, and most idiomatic Redis usage. Streams, geo, cluster, MULTI/EXEC, bitmap ops aren't covered. Open an issue if you need one — most are mechanical to add.

Note on set options: memory mode handles EX/PX and throws on anything else (NX, XX, KEEPTTL, ...) instead of silently ignoring it, so the two modes can't quietly diverge. ioredis mode passes all options through to the server.

Lua eval in memory mode

Memory mode recognizes the canonical sliding-window-rate-limit Lua script (ZADD + ZREMRANGEBYSCORE + ZCARD + EXPIRE, args layout [key, now, window-ms, member?]) and returns the post-add count. That's enough for typical rate-limiter use.

Anything else: returns 0 and prints a stderr warning once per process. Pass { silentEvalFallback: true } to suppress the warning. If you need a different Lua script handled natively, open an issue — they're ~5 lines each in memory-adapter.ts.

Pub/Sub: duplicate() semantics

Real Redis requires a separate connection for pub/sub vs commands. IoRedisAdapter.duplicate() calls Redis.prototype.duplicate() — a fresh TCP connection. MemoryRedisAdapter.duplicate() returns a new adapter that shares the underlying EventEmitter + state, so pub/sub fans out the same way real Redis does.

What it isn't

  • Not a cluster client. ioredis mode supports cluster URLs; memory mode is single-node by definition.
  • Not durable. Memory mode loses everything on process restart. Persistence is your application's problem (database run table, snapshot to disk, etc.).
  • Not a full Lua interpreter. See above.
  • Not a substitute for real Redis under load. The data structures are correct but not optimized for high throughput. Use real Redis in production.

License

MIT — see LICENSE.

Also by askalf

| Project | What it does | |---------|-------------| | browser-bridge | Stealth headless Chromium in a container. CDP on 9222 — Playwright/Puppeteer/MCP-compatible. | | dario | Local LLM router. Use your Claude Max/Pro subscription as an API. | | pgflex | One Postgres API. Two modes (real PG ↔ PGlite WASM). |


Built by Sprayberry Labs

This is one of the open-source building blocks from Sprayberry Labs — an independent studio (Atlanta, GA) that ships bespoke software and fixed-price code & security audits, delivered with the AI workforce these tools are part of.

Part of the askalf ecosystem — a self-hosted AI workforce platform, now in early access.

Got a codebase that needs an expert read? → Scan a repo — free mini-audit, or see the $1,500 fixed-price Audit and build Sprints. · sprayberrylabs.com · [email protected]


Part of Own Your Stack — own your AI infrastructure instead of renting it. Built by Thomas Sprayberry.