{"_id":"@absolutejs/queue-redis","_rev":"4-74b92cf8c0cbc929ae5d4cbf67f7284f","name":"@absolutejs/queue-redis","dist-tags":{"latest":"0.0.4"},"versions":{"0.0.1":{"name":"@absolutejs/queue-redis","version":"0.0.1","keywords":["queue","jobs","redis","absolutejs","durable","worker"],"author":"Alex Kahn","license":"Apache-2.0","_id":"@absolutejs/queue-redis@0.0.1","maintainers":[{"name":"alexkahndev","email":"alexkahn2019@gmail.com"}],"dist":{"shasum":"f4035e586ee853efbda21ef6d0b1d89cbfd9f725","tarball":"https://registry.npmjs.org/@absolutejs/queue-redis/-/queue-redis-0.0.1.tgz","fileCount":6,"integrity":"sha512-wxPKjSEbrhaGI7MUZzQx9qMAgZIhPTCZIN+usRnqz/SkWtQvIJT7owrXd+UNrvBg/HDm4vY/0JMwyuPALPtgbA==","signatures":[{"sig":"MEYCIQCsi+GJ8LiMru0X5goEK/0zf6GA9RmwDZkqfCmCO1foJQIhAJ5GdlgQTFiZsZyQcWuRBeDmCA3EOBAssblXtYcInW/l","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":41026},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","shasum":"f4035e586ee853efbda21ef6d0b1d89cbfd9f725","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"scripts":{"test":"bun test","build":"rm -rf dist && bun build src/index.ts --outdir dist --sourcemap --target=bun --external @absolutejs/queue && tsc --project tsconfig.build.json","format":"prettier --write \"./**/*.{ts,json,md}\"","release":"bun run format && bun run build && bun publish","typecheck":"tsc --noEmit"},"_npmUser":{"name":"alexkahndev","email":"alexkahn2019@gmail.com"},"_integrity":"sha512-wxPKjSEbrhaGI7MUZzQx9qMAgZIhPTCZIN+usRnqz/SkWtQvIJT7owrXd+UNrvBg/HDm4vY/0JMwyuPALPtgbA==","repository":{"url":"https://github.com/absolutejs/queue-adapters.git","type":"git","directory":"redis"},"_npmVersion":"10.8.3","description":"Redis-backed JobStore for @absolutejs/queue. Atomic Lua claim, sorted-set scheduling by runAt, per-job hash records. For shops running Redis instead of (or alongside) Postgres.","directories":{},"_nodeVersion":"24.3.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"prettier":"3.5.3","@types/bun":"1.3.14","typescript":"5.8.3","@absolutejs/queue":"^0.2.0","@sinclair/typebox":"^0.34.49"},"peerDependencies":{"@absolutejs/queue":">= 0.1.0"},"_npmOperationalInternal":{"tmp":"tmp/queue-redis_0.0.1_1780172928691_0.8172923859834029","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@absolutejs/queue-redis","version":"0.0.2","keywords":["queue","jobs","redis","absolutejs","durable","worker"],"author":"Alex Kahn","license":"Apache-2.0","_id":"@absolutejs/queue-redis@0.0.2","maintainers":[{"name":"alexkahndev","email":"alexkahn2019@gmail.com"}],"homepage":"https://github.com/absolutejs/queue-adapters/tree/main/redis","bugs":{"url":"https://github.com/absolutejs/queue-adapters/issues"},"dist":{"shasum":"02ab05a3089c3222a91132ba8bcd03c148e58487","tarball":"https://registry.npmjs.org/@absolutejs/queue-redis/-/queue-redis-0.0.2.tgz","fileCount":11,"integrity":"sha512-RqIt1ZbS5oPxAi5TnjDguYnNfk2wF0ZkqVYtfLNRzHcSLM5NHCh8knjmh3BSfZhkc5IMfzfQKvW6PPPEfZSaHQ==","signatures":[{"sig":"MEQCIBx4BwCnAuNkHE1mZHjLZhgzTbZq6qgjKiqgSkj7kEx6AiATeW2Ne3H4gMwEgSty3V9FqZ00Uel9SOxKb+DuOtJNaw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1135394},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","shasum":"02ab05a3089c3222a91132ba8bcd03c148e58487","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./manifest":{"types":"./dist/manifest.d.ts","import":"./dist/manifest.js","default":"./dist/manifest.js"},"./manifest.json":"./dist/manifest.json"},"scripts":{"test":"bun test","build":"rm -rf dist && bun build src/index.ts src/manifest.ts --outdir dist --sourcemap --target=bun --external @absolutejs/queue && tsc --project tsconfig.build.json && absolute-manifest emit","format":"prettier --write \"./**/*.{ts,json,md}\"","release":"bun run format && bun run build && bun publish","typecheck":"tsc --noEmit"},"_npmUser":{"name":"alexkahndev","email":"alexkahn2019@gmail.com"},"_integrity":"sha512-RqIt1ZbS5oPxAi5TnjDguYnNfk2wF0ZkqVYtfLNRzHcSLM5NHCh8knjmh3BSfZhkc5IMfzfQKvW6PPPEfZSaHQ==","absolutejs":{"manifestContract":1},"repository":{"url":"https://github.com/absolutejs/queue-adapters.git","type":"git","directory":"redis"},"_npmVersion":"10.8.3","description":"Redis-backed JobStore for @absolutejs/queue. Atomic Lua claim, sorted-set scheduling by runAt, per-job hash records. For shops running Redis instead of (or alongside) Postgres.","directories":{},"_nodeVersion":"24.3.0","dependencies":{"@sinclair/typebox":"^0.34.0","@absolutejs/manifest":"^0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"prettier":"3.5.3","@types/bun":"1.3.14","typescript":"5.8.3","@absolutejs/queue":"^0.2.0","@sinclair/typebox":"^0.34.49"},"peerDependencies":{"@absolutejs/queue":">= 0.1.0"},"_npmOperationalInternal":{"tmp":"tmp/queue-redis_0.0.2_1783965316997_0.9452049573644468","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@absolutejs/queue-redis","version":"0.0.3","keywords":["queue","jobs","redis","absolutejs","durable","worker"],"author":"Alex Kahn","license":"Apache-2.0","_id":"@absolutejs/queue-redis@0.0.3","maintainers":[{"name":"alexkahndev","email":"alexkahn2019@gmail.com"}],"homepage":"https://github.com/absolutejs/queue-adapters/tree/main/redis","bugs":{"url":"https://github.com/absolutejs/queue-adapters/issues"},"dist":{"shasum":"632526f049e05c7d834728f5ca3507274eb77811","tarball":"https://registry.npmjs.org/@absolutejs/queue-redis/-/queue-redis-0.0.3.tgz","fileCount":11,"integrity":"sha512-pBJk000ORehPvWev/nMjRK9lwDFfU32GCdx4qzV6yVLCbXY+Iar1049az1ghOqLNyTMZvlUymL5z2BGYFo/+mg==","signatures":[{"sig":"MEUCIQCn2jQCHhyElwJlX2FA4QGgsMtxfTlUsUrXYIq58NjTlAIgPeGF9ZJtpKDvAw14d8CbhValdwMrz4DsEXLgKQ7R7nE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":498390},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","shasum":"632526f049e05c7d834728f5ca3507274eb77811","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./manifest":{"types":"./dist/manifest.d.ts","import":"./dist/manifest.js","default":"./dist/manifest.js"},"./manifest.json":"./dist/manifest.json"},"scripts":{"test":"bun test","build":"rm -rf dist && bun build src/index.ts src/manifest.ts --outdir dist --sourcemap --target=bun --external @absolutejs/queue && tsc --project tsconfig.build.json && absolute-manifest emit","format":"prettier --write \"./**/*.{ts,json,md}\"","release":"bun run format && bun run build && bun publish","typecheck":"tsc --noEmit"},"_npmUser":{"name":"alexkahndev","email":"alexkahn2019@gmail.com"},"_integrity":"sha512-pBJk000ORehPvWev/nMjRK9lwDFfU32GCdx4qzV6yVLCbXY+Iar1049az1ghOqLNyTMZvlUymL5z2BGYFo/+mg==","absolutejs":{"runtimePeers":{"@absolutejs/queue":{"range":">=0.7.0 <0.8.0","tested":"0.7.0","buildExternals":["@absolutejs/queue"],"artifactImports":["@absolutejs/queue"]}},"manifestContract":2},"repository":{"url":"https://github.com/absolutejs/queue-adapters.git","type":"git","directory":"redis"},"_npmVersion":"10.8.3","description":"Redis-backed JobStore for @absolutejs/queue. Atomic Lua claim, sorted-set scheduling by runAt, per-job hash records. For shops running Redis instead of (or alongside) Postgres.","directories":{},"_nodeVersion":"24.3.0","dependencies":{"@sinclair/typebox":"^0.34.0","@absolutejs/manifest":"^0.7.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"prettier":"3.5.3","@types/bun":"1.3.14","typescript":"5.8.3","@absolutejs/queue":"0.7.0","@sinclair/typebox":"^0.34.49"},"peerDependencies":{"@absolutejs/queue":">=0.7.0 <0.8.0"},"_npmOperationalInternal":{"tmp":"tmp/queue-redis_0.0.3_1784913538604_0.9270499022565182","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"@absolutejs/queue-redis","version":"0.0.4","description":"Redis-backed JobStore for @absolutejs/queue. Atomic Lua claim, sorted-set scheduling by runAt, per-job hash records. For shops running Redis instead of (or alongside) Postgres.","repository":{"type":"git","url":"git+https://github.com/absolutejs/queue-adapters.git","directory":"redis"},"homepage":"https://github.com/absolutejs/queue-adapters/tree/main/redis","bugs":{"url":"https://github.com/absolutejs/queue-adapters/issues"},"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","type":"module","license":"Apache-2.0","author":{"name":"Alex Kahn"},"publishConfig":{"access":"public"},"keywords":["queue","jobs","redis","absolutejs","durable","worker"],"scripts":{"build":"rm -rf dist && bun build src/index.ts src/manifest.ts --outdir dist --sourcemap --target=bun --external @absolutejs/queue && tsc --project tsconfig.build.json && absolute-manifest emit","test":"bun test","typecheck":"tsc --noEmit","format":"prettier --write \"./**/*.{ts,json,md}\"","release":"bun run format && bun run build && bun publish"},"peerDependencies":{"@absolutejs/queue":">=0.8.0 <0.9.0"},"devDependencies":{"@absolutejs/queue":"0.8.0","@sinclair/typebox":"^0.34.49","@types/bun":"1.3.14","prettier":"3.5.3","typescript":"5.8.3"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"},"./manifest":{"types":"./dist/manifest.d.ts","import":"./dist/manifest.js","default":"./dist/manifest.js"},"./manifest.json":"./dist/manifest.json"},"absolutejs":{"manifestContract":2},"dependencies":{"@absolutejs/manifest":"^0.9.0","@sinclair/typebox":"^0.34.0"},"_id":"@absolutejs/queue-redis@0.0.4","gitHead":"68e68c703850b3a2ee6886e67005a190c3849ba8","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-6kzWTZWci7w/NoCXqYnfT8S+/GkUjsksxMF96syphVpuuPxmXP8wH0KMO+h4IKxmDLEzcVEu2aJIlakGHWetAw==","shasum":"e6d2889ccf054c83cce09eb826bf0a4aa29c906b","tarball":"https://registry.npmjs.org/@absolutejs/queue-redis/-/queue-redis-0.0.4.tgz","fileCount":11,"unpackedSize":506256,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICE06vDXwO42Fy7kfpGs8BOJWaXC0HZeg954N1osk4ZsAiAwxGxlGqquNEAi29723pBqieWURTyPV5JajpyikUlvvQ=="}]},"_npmUser":{"name":"alexkahndev","email":"alexkahn2019@gmail.com"},"directories":{},"maintainers":[{"name":"alexkahndev","email":"alexkahn2019@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/queue-redis_0.0.4_1787346109661_0.6011523969132593"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-30T20:28:48.534Z","modified":"2026-08-21T21:01:50.013Z","0.0.1":"2026-05-30T20:28:48.828Z","0.0.2":"2026-07-13T17:55:17.242Z","0.0.3":"2026-07-24T17:18:58.757Z","0.0.4":"2026-08-21T21:01:49.818Z"},"bugs":{"url":"https://github.com/absolutejs/queue-adapters/issues"},"author":{"name":"Alex Kahn"},"license":"Apache-2.0","homepage":"https://github.com/absolutejs/queue-adapters/tree/main/redis","keywords":["queue","jobs","redis","absolutejs","durable","worker"],"repository":{"type":"git","url":"git+https://github.com/absolutejs/queue-adapters.git","directory":"redis"},"description":"Redis-backed JobStore for @absolutejs/queue. Atomic Lua claim, sorted-set scheduling by runAt, per-job hash records. For shops running Redis instead of (or alongside) Postgres.","maintainers":[{"name":"alexkahndev","email":"alexkahn2019@gmail.com"}],"readme":"# @absolutejs/queue-redis\n\nRedis-backed `JobStore` for\n[@absolutejs/queue](https://github.com/absolutejs/queue). Sibling to\n[`@absolutejs/queue-postgres`](../postgres) — same `JobStore`\ncontract, different transport.\n\n**Docs:** [absolutejs.com/documentation/queue-overview#redis-adapter](https://absolutejs.com/documentation/queue-overview#redis-adapter)\n\n## When to use Redis vs Postgres\n\n| Concern | `queue-redis` | `queue-postgres` |\n|---|---|---|\n| Already have Redis | Win | New dep to operate |\n| Already have Postgres | Skip — fewer deps | Win |\n| Durability after Redis crash | RDB snapshots + AOF (config-dependent) | WAL — point-in-time recovery |\n| Throughput | Higher per-key (in-memory) | Lower (transactional) |\n| Multi-region | Native cluster geo-replication | PG logical replication (heavier) |\n| At-most-once semantics | Lease-based reap-on-expire | Same |\n\nBoth adapters implement identical `JobStore` shape — swap them by\nconfig.\n\n## Install\n\n```sh\nbun add @absolutejs/queue @absolutejs/queue-redis\nbun add ioredis      # OR\nbun add redis        # node-redis v4+\n```\n\n## Usage with `ioredis`\n\n```ts\nimport { Redis } from 'ioredis';\nimport {\n  createJobRegistry,\n  createQueueWorker,\n  defineJobs,\n  t,\n  type JobMapFromDefinition\n} from '@absolutejs/queue';\nimport { createRedisJobStore } from '@absolutejs/queue-redis';\n\nconst redis = new Redis(process.env.REDIS_URL!);\n\nconst jobs = defineJobs({\n  'email.send': t.Object({ to: t.String(), subject: t.String() }),\n});\n\nconst store = createRedisJobStore<JobMapFromDefinition<typeof jobs>>({\n  client: redis,                    // ioredis structurally satisfies RedisCommandClient\n  keyPrefix: 'myapp:queue:',         // optional\n});\n\nconst registry = createJobRegistry(jobs).on('email.send', async (payload) => {\n  await sendEmail(payload);\n});\n\nconst worker = createQueueWorker({ store, registry });\nworker.start();\n```\n\n## Usage with `node-redis` v4+\n\n```ts\nimport { createClient } from 'redis';\n\nconst client = createClient({ url: process.env.REDIS_URL });\nawait client.connect();\n\n// node-redis's typed wrappers have slightly different signatures\n// (e.g. `client.hSet(key, fields)` is camelCase). Adapt:\nconst adapted: RedisCommandClient = {\n  hset: (key, fields) => client.hSet(key, fields),\n  hgetall: (key) => client.hGetAll(key),\n  hdel: (key, ...fields) => client.hDel(key, fields),\n  del: (...keys) => client.del(keys),\n  zadd: (key, score, member) => client.zAdd(key, { score, value: member }),\n  zrem: (key, ...members) => client.zRem(key, members),\n  zrangebyscore: (key, min, max, offset, count) =>\n    client.zRangeByScore(key, min, max,\n      offset !== undefined && count !== undefined\n        ? { LIMIT: { offset, count } }\n        : undefined),\n  zcard: (key) => client.zCard(key),\n  sadd: (key, ...m) => client.sAdd(key, m),\n  srem: (key, ...m) => client.sRem(key, m),\n  set: (key, value, mode) => client.set(key, value, mode === 'NX' ? { NX: true } : undefined),\n  get: (key) => client.get(key),\n  scard: (key) => client.sCard(key),\n  smembers: (key) => client.sMembers(key),\n  eval: (script, keys, args) => client.eval(script, { keys, arguments: args }),\n};\n\nconst store = createRedisJobStore({ client: adapted });\n```\n\n## Atomic claim via Lua\n\n`claimDue` runs a Lua script that atomically:\n\n1. `ZRANGEBYSCORE due 0 now LIMIT 0 N` — find due jobs\n2. `ZREM due id` for each — remove from the due set\n3. `ZADD claimed (now+leaseMs) id` for each — add to claimed with expiry\n4. `HSET <job> status=claimed lockedAt=now lockedBy=worker`\n5. `HGETALL <job>` for each — return the payloads\n\nWithout Lua, two concurrent workers could race the ZRANGEBYSCORE → ZREM\ngap and both think they own the same job. With Lua, the entire 5-step\nsequence is one atomic operation — Redis runs Lua single-threaded.\n\n`reapStuck` uses a sibling Lua script to find expired-lease claimed\njobs and move them back to due.\n\n## Storage layout\n\nAll keys prefixed by `keyPrefix` (default `'absolutejs:queue:'`):\n\n- `<prefix>job:<id>` — HASH per job (the record)\n- `<prefix>due` — ZSET keyed by `runAt`\n- `<prefix>claimed` — ZSET keyed by `lockedAt + leaseMs` (lease expiry)\n- `<prefix>idempotency:<key>` — STRING mapping idempotency key → job id\n\n## v0.0.1 surface\n\n| Method | Status |\n|---|---|\n| `enqueue` (with idempotency) | ✓ |\n| `claimDue` (atomic Lua) | ✓ |\n| `complete` | ✓ |\n| `fail` (with retry / dead-letter) | ✓ |\n| `reapStuck` (atomic Lua) | ✓ |\n| `get` | ✓ |\n| `countByStatus` | partial — counts pending + claimed; v0.0.1 returns 0 for done/dead/canceled |\n| `cancel`, `list`, `listByKind`, `retry` | deferred to 0.1.0 |\n\nThe deferred methods need a separate maintained index (`<prefix>byKind:<kind>`\nSET, `<prefix>byStatus:<status>` SET) which would cost a write on every\nstatus transition. v0.0.1 ships without to keep the hot path lean;\n0.1.0 adds opt-in lazy indexing.\n\n## Crash safety\n\nRedis pub/sub-style queues are subject to your Redis durability\nconfig:\n\n- **AOF on, fsync every second**: at most 1s of work lost on Redis crash\n- **AOF on, fsync always**: no work lost, slower\n- **RDB only**: minutes of work lost — NOT recommended for queues\n\nFor at-most-once semantics, Redis durability is sufficient. For\nat-least-once, the worker should mark a job complete/failed only AFTER\nthe side effect succeeds — `@absolutejs/queue`'s worker already does\nthis.\n\n## License\n\nApache 2.0. Tier B substrate-adjacent — rides\n`@absolutejs/queue` (BSL Tier A).\n","readmeFilename":"README.md"}