@nxgt/janus-webhooks-redis

中文文档
0.6.7发布于 2026/11/02MITTypeScriptES Module零依赖

The Redis queue for @nxgt/janus-webhooks on @nxgt/redis: deliveries that outlive the process, claimed atomically under a lease

原始使用文档
文档目录8 节
  1. Install
  2. Usage
  3. API
  4. What Redis holds
  5. Traps
  6. Documentation
  7. Type safety, counted
  8. Licence

@nxgt/janus-webhooks-redis

The Redis queue for @nxgt/janus-webhooks: deliveries wait in Redis, on @nxgt/redis's connection, shared by every process of your application. A retry waiting when a process crashes, restarts or is redeployed is sent by the next one, and a request cut short is sent again once its lease lapses.

It implements the WebhookQueue port, and passes the @nxgt/janus-webhooks/conformance suite against a real Redis, outages included, on every CI run.

0.x. A minor version may still change the surface; the changelog says how.

Install

bun add @nxgt/janus-webhooks-redis @nxgt/janus-webhooks @nxgt/janus @nxgt/redis
bun add zod                  # @nxgt/redis's peer, zod 4, if your application has none
bun add -d typescript        # 6 or 7

Every peer is required:

  • @nxgt/janus-webhooks, whose port it implements;
  • @nxgt/janus, whose UserEvent it holds and whose StoreFailure it throws — a peer, never a dependency, so instanceof StoreFailure holds in your code;
  • @nxgt/redis, >=0.3.1 <1, whose connection wraps Bun's own RedisClient, so this runs on Bun;
  • typescript 6 or 7.

The @nxgt/redis floor is tested, not claimed: the package's specs and its typecheck run on @nxgt/redis 0.3.1 as well, in the Floors job, on every CI run.

It needs Redis 7.0 or later, or Valkey — see what Redis must be configured with. Like @nxgt/janus, it expects "moduleResolution": "bundler".

Usage

import { webhooks } from '@nxgt/janus-webhooks';
import { createRedisWebhookQueue } from '@nxgt/janus-webhooks-redis';
import { connectRedis } from '@nxgt/redis';

const secret = process.env.CRM_WEBHOOK_SECRET; // whsec_…, from mintWebhookSecret()
if (!secret) throw new Error('CRM_WEBHOOK_SECRET is not set');

const redis = await connectRedis(process.env.REDIS_URL ?? 'redis://localhost:6379', {
    enableOfflineQueue: false, // an outage fails the insert at once, not after 31 s
});

export const listener = webhooks({
    endpoints: [{ id: 'crm', url: 'https://crm.example.com/hooks/janus', secrets: [secret] }],
    queue: createRedisWebhookQueue(redis), // every process passes the same Redis and prefix
});

// janus({ …, events: listener })

process.on('SIGTERM', async () => {
    await listener.close(); // gives nothing up: what waits is sent by the next process
    process.exit(0);
});

Give each endpoint an id when you first pass a queue: it is what the queue knows the endpoint by. Wiring covers the connection, the prefix, and what Redis must be configured with.

API

ExportWhat it is
createRedisWebhookQueue(redis, options?)Returns a WebhookQueue, for webhooks({ queue }). redis is what connectRedis returns. It connects to nothing and creates nothing: the keys are made by the first insert.
RedisWebhookQueueOptions{ prefix? }: what every key starts with, janus:webhooks: by default. Every process sharing deliveries passes the same one.

What Redis holds

Each delivery's event and endpoint id — never a URL or a secret — with one Lua script per method, so no two claims answer one delivery. Wiring has each key and how long it stays.

Traps

  • Hold the new event types back until every process sharing a queue is upgraded. A delivery of user.secondFactorEnabled or user.secondFactorDisabled, written by 0.2.0, is STORE_FAILED (a reply that is not … a user event type) in a 0.1.x process that claims it — and so is one of user.recoveryCodesRegenerated or user.recoveryCodeUsed, written by 0.3.0, in a 0.2.x process, and one of user.passwordChanged or user.emailChanged, written by 0.4.0, in a 0.3.x process, and one of user.newDeviceSignedIn, written by 0.5.0, in a 0.4.x process, and one of user.avatarChanged or user.avatarRemoved, written by 0.6.0, in a 0.5.x process — and that endpoint's claims fail there until every process is upgraded. Upgrade @nxgt/janus, @nxgt/janus-webhooks and @nxgt/janus-webhooks-redis together — their peer ranges move as one — with each endpoint limited to the types the older processes know, and drop the limit once every process runs the new versions:

    webhooks({
      queue,
      endpoints: [{ id: 'crm', url, secrets: [secret], types: ['user.created', 'user.emailVerified', 'user.passwordReset', 'user.deleted'] }],
    });
    

    That list is for 0.1.x processes. When the oldest run 0.2.x, which read the second factor's events but not the recovery codes', add those two:

    types: ['user.created', 'user.emailVerified', 'user.passwordReset', 'user.secondFactorEnabled', 'user.secondFactorDisabled', 'user.deleted'],
    

    When the oldest run 0.3.x, which read the recovery codes' events but not the change events', add those two:

    types: ['user.created', 'user.emailVerified', 'user.passwordReset', 'user.secondFactorEnabled', 'user.secondFactorDisabled', 'user.recoveryCodesRegenerated', 'user.recoveryCodeUsed', 'user.deleted'],
    

    When the oldest run 0.4.x, which read the change events but not the new device's, add those two:

    types: ['user.created', 'user.emailVerified', 'user.passwordReset', 'user.passwordChanged', 'user.emailChanged', 'user.secondFactorEnabled', 'user.secondFactorDisabled', 'user.recoveryCodesRegenerated', 'user.recoveryCodeUsed', 'user.deleted'],
    
  • Eviction is data loss. A Redis whose maxmemory-policy evicts keys drops deliveries without a word — no retry, no onGivingUp. Run this on a Redis with noeviction; a full one then refuses the insert with OOM, which janus reports as JANUS_EVENT_FAILED.

  • Persistence is what makes it durable. Without RDB or AOF, restarting Redis loses every delivery waiting in it. Enable AOF (appendfsync everysec loses at most a second of inserts on a crash) or RDB snapshots.

  • Redis Cluster is not supported. A script touches the keys of a delivery and of its endpoint, which may live in different slots. Use a single Redis, or a primary with replicas.

  • By default, an outage makes every flow wait 31 seconds. The listener awaits the insert, and Bun's client queues commands while it reconnects. With enableOfflineQueue: false the insert fails at once, the flow goes on, and the event is reported as JANUS_EVENT_FAILED.

  • One prefix per application. Two applications sharing a prefix share deliveries, and each gives up the other's endpoints as endpointRemoved after orphanGrace.

Documentation

Type safety, counted

Four plausible mistakes are refused by the compiler, each with a @ts-expect-error case in test/types/queue.ts:

  • a URL instead of a connection;
  • Bun's client instead of the connection that holds it;
  • the promise connectRedis answers, not awaited;
  • a prefix that is not a string.

Licence

MIT

版本与发布记录

@nxgt/janus-webhooks-redis 共发布 33 个版本,首次发布于 2026/09/27;过去 12 个月发布了 33 个版本。

版本发布日期距今
0.6.7latest2026/11/021 天前
0.6.62026/11/021 天前
0.6.52026/10/304 天前
0.6.42026/10/276 天前
0.6.32026/10/277 天前
0.6.22026/10/277 天前

关于 @nxgt/janus-webhooks-redis 的常见问题

@nxgt/janus-webhooks-redis 是什么?

@nxgt/janus-webhooks在@nxgt/ Redis上的Redis队列:超过进程的交付,在租约下自动声明

如何安装 @nxgt/janus-webhooks-redis?

在项目目录运行 npm install @nxgt/janus-webhooks-redis,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:typescript(^6.0.3 || ^7.0.0)、@nxgt/janus(^0.36.0)、@nxgt/redis(>=0.3.1 <1)、@nxgt/janus-webhooks(^0.8.7)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@nxgt/janus-webhooks-redis 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@nxgt/janus-webhooks-redis 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@nxgt/janus-webhooks-redis 可以商用吗?

@nxgt/janus-webhooks-redis 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@nxgt/janus-webhooks-redis 还在维护吗?

最新版本 0.6.7 发布于 2026/11/02(1 天前);至今共发布 33 个版本,其中过去 12 个月发布了 33 个;首次发布于 2026/09/27。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。