# @centient/resilience 中文文档

规范地址：https://npmcn.com/package/%40centient/resilience/

版本：0\.4\.0
npm 元数据更新时间：2026-09-13T13:35:42.807Z
中文译文更新时间：2026-11-09T07:43:09.808Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40centient/resilience
原文对照：https://npmcn.com/package/%40centient/resilience/?lang=original
项目主页：https://github.com/centient-labs/centient-sdk/tree/main/packages/resilience#readme
源码仓库：https://github.com/centient-labs/centient-sdk

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：108 kB，47 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=20\.0\.0
- 运行依赖：0 个
- 首次发布：2026-06-16T15:30:18.232Z
- 最新版本发布：2026-09-13T13:35:42.634Z
- 近 30 天下载量：4614（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：4（近 12 个月 4 个）

## 中文简介

零依赖性韧性原始:断路器,令牌-桶和受限键速率限制器,回放-活塞(加法+满法),用可注射故障分级器重试,LRU/TTL/SWR缓存,受限-货币池

## 安装

```sh
npm install @centient/resilience
```

也可以使用：`pnpm add @centient/resilience`、`yarn add @centient/resilience`、`bun add @centient/resilience`

## README

# @ 百分点/ 反应

百度包的零依赖性韧性原始:断路器,有受限密钥注册的符号-bucket速率限制器,回放-jitter,用可注射故障分级器重试,取消时有时钟超时,LRU/TTL/SWR缓存,和受限货币池.

每一次和 en依赖的行为都是由被注射的 `Clock` 或者说 `RandomSource` 没有 `Date.now()` 或者说 `Math.random()` 在任何逻辑路径中——因此原始人被检验为完全具有决定力. 货包遵循工厂功能公约(`createCircuitBreaker()`, (中文(简体) ), `createTokenBucket()`,(...)和 `Result` 歧视工会的错误模式 `@centient`。 。 。 .

## 安装

```bash
npm install @centient/resilience
```

或作为单体中的工作空间依赖:

```bash
pnpm add @centient/resilience --workspace
```

## 设计:注入时钟和随机性

塑造此软件包的单一规则 : **逻辑路径从不直接读取墙钟或全球RNG.** 相反,它需要一个 `Clock` (单位:千美元)`() => number`,划时代)和涉及焦急的,a `RandomSource` (单位:千美元)`() => number` 内 `[0, 1)`) (中文(简体) ).

```typescript
import { systemClock, createManualClock, systemRandom, fixedRandom } from "@centient/resilience";

// Production
const clock = systemClock; // the only place Date.now() is read

// Tests — advance time by hand, deterministically
const t = createManualClock(0);
t.advance(5_000); // 5 seconds pass
```

源码测试执行规则: `Date.now()` 可能只出现在 `clock.ts`, (中文(简体) ), `Math.random()` 仅在 `random.ts`。 。 。 .

## 结果类型

失败为普通结果的操作( 被拒绝的费率限制获取, 缓存误差升为出错) 返回 a `Result<T, E>` 而不是扔东西。 抛出被保留给程序员出错(无效配置).

```typescript
import { ok, err, isOk, type Result } from "@centient/resilience";

const r: Result<number, string> = ok(42);
if (isOk(r)) console.log(r.value); // 42
```

* * *

## 断路器

A 类 `CLOSED -> OPEN -> HALF_OPEN -> CLOSED` 国家机器。 接连失效会绊倒电路开通;在开通时,电话会被拒绝,直到开通时间快到来,然后一个单一的探测器(HALF OPEN)会用指数增加的回放(封顶)决定是关闭还是重新开通.

```typescript
import { createCircuitBreaker, CircuitOpenError } from "@centient/resilience";

const breaker = createCircuitBreaker({
  failureThreshold: 5,    // open after 5 consecutive failures
  openDurationMs: 30_000, // stay open 30s before probing
  backoffMultiplier: 2,   // double the open duration on each failed probe
  maxOpenDurationMs: 300_000,
});

try {
  const data = await breaker.execute(() => fetchFromUpstream());
} catch (e) {
  if (e instanceof CircuitOpenError) {
    // serve a fallback; e.retryAfterMs tells you when to try again
  }
}
```

带外录音(当您不把电话包进来时) `execute`:

```typescript
if (breaker.canExecute()) {
  try { doWork(); breaker.onSuccess(); }
  catch { breaker.onFailure(); }
}
```

### 纯国有机器

工厂包装了一台可以直接使用的纯净而不可改变的国产机器(取自"十字架" `circuit-breaker.ts`) (中文(简体) ). 每个函数都使用状态 + 时间- 毫升秒印并返回新状态 :

```typescript
import {
  createCircuitBreakerState,
  getDefaultCircuitBreakerConfig,
  checkCircuit,
  recordSuccess,
  recordFailure,
} from "@centient/resilience";

const config = getDefaultCircuitBreakerConfig();
let state = createCircuitBreakerState(config);
state = recordFailure(state, config, Date.now()); // you own the clock here
const { result, updatedState } = checkCircuit(state, Date.now());
```

**输出 :** `createCircuitBreaker`, (中文(简体) ), `CircuitOpenError`, (中文(简体) ), `getDefaultCircuitBreakerConfig`, (中文(简体) ), `createCircuitBreakerState`, (中文(简体) ), `checkCircuit`, (中文(简体) ), `recordSuccess`, (中文(简体) ), `recordFailure`类型 `CircuitBreaker`, (中文(简体) ), `CircuitBreakerConfig`, (中文(简体) ), `CircuitBreakerOptions`, (中文(简体) ), `CircuitBreakerState`, (中文(简体) ), `CircuitCheckResult`, (中文(简体) ), `CircuitState`。 。 。 .

* * *

## 托肯-巴克特率限制器

一桶能撑到 `capacity` 符号和再填充在 `refillPerSecond`。重新填充从时钟中粗略地计算——没有背景计时器.

```typescript
import { createTokenBucket } from "@centient/resilience";

const bucket = createTokenBucket({ capacity: 10, refillPerSecond: 5 });

// Non-blocking: returns a Result
const r = bucket.tryAcquire(3);
if (r.ok) {
  // 3 tokens consumed; r.value tokens remain
} else {
  // denied; r.error.retryAfterMs ms until enough tokens accrue
}

// Blocking: waits (via injected sleep) until tokens are available
await bucket.acquire(2);

bucket.available(); // current tokens after lazy refill
```

**输出 :** `createTokenBucket`类型 `TokenBucket`, (中文(简体) ), `TokenBucketConfig`, (中文(简体) ), `TokenBucketOptions`, (中文(简体) ), `RateLimitError`。 。 。 .

* * *

## 被绑定的按键限制器

`createKeyedLimiter` 每个密钥都有一个私人的标志桶,并绑入活的 (c) 登记簿,并附有出入许可令。 万键默认匹配 由平衡引导者推广原型;生产呼叫器应设置 `maxKeys` 他们的正常身份空间和威胁模式.

```typescript
import { createKeyedLimiter } from "@centient/resilience";

const limiter = createKeyedLimiter({
  capacity: 60,
  refillPerSecond: 1,
  maxKeys: 5_000,
});

const result = limiter.tryAcquire(accountId);
if (!result.ok) {
  // result.error.retryAfterMs tells the transport when this key may retry
}

limiter.available(accountId); // creates/promotes the key when it is absent
limiter.stats(); // { size, evictions, clockRegressions }
```

退出重置密钥的预算, 如果该密钥稍后返回 。 不给一个 无约束的攻击者控制值直接输入登记册: 身份、大小 `maxKeys` 因此,监测 `stats().evictions`。工作量 需要跨程序或持久执行的,需要一个呼叫者拥有的商店. 密钥按合同是条管弦;将复合维度编码为一 稳定的字符串,而不是为每个调用构建一个新的tuple或对象.

密钥外观是故意无阻. 不暴露隐私 桶子或(或) `acquire()`:飞行中服务员本来可以在被驱逐后幸存 同一键得到一个新的桶, 将执行分成两个 独立预算。 使用 `createTokenBucket()` 单屏蔽时直接 桶是必需的原始.

**输出 :** `createKeyedLimiter`类型 `KeyedLimiter`, (中文(简体) ), `KeyedLimiterConfig`, (中文(简体) ), `KeyedLimiterStats`。 。 。 .

* * *

## 与焦躁的后退

`linear` 复制 `@centient/sdk` 精确地重试时间表——基数 `attempt * baseDelayMs` 加快讯 `[0, jitterRatio * baseDelayMs)` (sdk 用户数) `jitterRatio = 0.5`)——使斯德克能以"入"来取取. `exponential` 实值 `baseDelayMs * factor^(attempt-1)`,封顶 `maxDelayMs`同样的添加剂焦躁.

```typescript
import { createBackoff } from "@centient/resilience";

// Linear (sdk-compatible): attempt 1 ∈ [1000, 1500), attempt 2 ∈ [2000, 2500)
const linear = createBackoff({ baseDelayMs: 1000 });
const delay = linear.delayFor(attempt); // sleep this many ms before retry

// Exponential with cap
const expo = createBackoff({
  baseDelayMs: 100,
  strategy: "exponential",
  factor: 2,
  maxDelayMs: 1000,
});
expo.baseFor(5); // 1000 (1600 capped)

// Deterministic jitter in tests
import { fixedRandom } from "@centient/resilience";
createBackoff({ baseDelayMs: 1000, random: fixedRandom(0) }).delayFor(1); // exactly 1000
```

`baseFor(attempt)` 财务报告和审定财务报表 `maxFor(attempt)` 揭开(封口)底部和全焦封口.

### 全焦 (无地板)

`jitter: "full"` 切换到 AWS “ 责任后退和慢跑 ” _满_ 变体 : `delay = random() * min(maxDelayMs, baseDelayMs * factor^(attempt-1))` - 穿着制服 `[0, cap)`, (中文(简体) ), **没有地板,可以是0**。默认值(`jitter: "additive"`)是不变的,所以现有的呼叫者没有区别.

当许多客户对同一上游退后时使用. 非零层重新组装出再试的舰队,并重新组装出引起出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出.

```typescript
const backoff = createBackoff({
  baseDelayMs: 500,
  strategy: "exponential",
  factor: 2,
  maxDelayMs: 5_000,
  jitter: "full",
});
backoff.delayFor(1); // ∈ [0, 500)   — can be exactly 0
backoff.delayFor(3); // ∈ [0, 2000)
backoff.baseFor(3);  // 2000 — in full mode the schedule value is the CAP, not the floor
```

`jitterRatio` 不适用于完全焦急(跨度来自调度值本身); 传递两个抛出,而不是默默地忽略一个.

### 累计延迟预算

重试链条最坏的情况是 _总和_ 拖延时间. `totalMaxFor(attempts)` 报告确切的信封 -- `sum(maxFor(1..attempts-1))`,从 `attempts` 尝试入睡 `attempts - 1` - 比粗糙更紧 `(attempts - 1) * maxDelayMs` 约束.

拨打预算时

```typescript
const backoff = createBackoff({
  baseDelayMs: 500, strategy: "exponential", factor: 2, maxDelayMs: 5_000,
  jitter: "full",
  attempts: 3,             // the chain the budget covers
  maxTotalDelayMs: 15_000, // must stay well inside a 30s tick
});
backoff.totalMaxFor(3);    // 1500 (500 + 1000) — throws at construction if it exceeded the budget
backoff.budgetedAttempts;  // 3 — withRetry refuses to run more than this
```

**输出 :** `createBackoff`类型 `Backoff`, (中文(简体) ), `BackoffConfig`, (中文(简体) ), `BackoffStrategy`, (中文(简体) ), `BackoffJitter`。 。 。 .

* * *

## 用可注入分类器重试

`withRetry` 运行 a 上的同步操作 `Backoff` 排程和重试 `shouldRetry` 上位接受 。 时间表、上游和 `sleep` 全部注射,所以在测试中循环是决定性的。 最后一次出错是在尝试被用尽后重新投放,没有改变——没有新的出错形状,因此现有的故障处理仍然具有权威性.

```typescript
import { createBackoff, withRetry } from "@centient/resilience";

const backoff = createBackoff({
  baseDelayMs: 500, strategy: "exponential", factor: 2,
  maxDelayMs: 5_000, jitter: "full", attempts: 3, maxTotalDelayMs: 15_000,
});

const crystal = await withRetry(() => client.crystals.get(id), {
  backoff, // attempts defaults to backoff.budgetedAttempts (3 here) — no need to repeat it
  onRetry: ({ attempt, delayMs, error }) => log.warn("retry", { attempt, delayMs, error }),
});
```

`attempts` 默认返回已声明 `budgetedAttempts`,或者在没有宣布预算时3个——一次在时间表上说明链长。 传递一个值 _上文_ 预算仍然投出;只有违约者才推迟执行时间表.

将每个调用一个逻辑操作包入- 将一个页码循环包入, 而不是每页取取取预算乘以页码.

### 分类是参数,不是政策

哪些错误值得重试 _电话_ 失败域 。 一个客户骑出上游棕褐色 想要请求超时重试( 他们是棕色出的主要瞬间)和未知错误 **没有** 被重审(不可分类的失败可能是非理想的部分成功,并重玩重复写作). `isTransientError` 是被包装的默认 :

| 类 | 复试 |
| --- | --- |
| 超时( E)`AbortError` 编号 : `TimeoutError` 编号 : `code: "TIMEOUT"` /"请求超时") | 对 |
| 5x (中文(简体) )`statusCode` 编号 : `status` 编号 : `response.status` (单位:500) | 对 |
| 网络`NETWORK_ERROR`, (中文(简体) ), `ECONNREFUSED`, (中文(简体) ), `ECONNRESET`, (中文(简体) ), `ETIMEDOUT`(中文(简体) ) | 对 |
| 4xx (400–499) (英语), **包括409起CAS冲突** | 没有 |
| Schema 验证 (`ZodError`(中文(简体) ) | 没有 |
| 其他一切(不详、非)`Error` 投掷) | 没有 |

一个状态代码,当存在时,胜过名称/代码/消息热度. 409是终端,因为盲目重播相同 `expectedVersion` 永远无法成功——呼叫者的比较和设置循环拥有冲突政策.

提供你自己的上游,或构建一个被包装的变体:

```typescript
import { createTransientErrorPredicate, withRetry } from "@centient/resilience";

const shouldRetry = createTransientErrorPredicate({
  isConflict: isCrystalVersionConflictError, // caller-owned conflicts, evaluated first
  retryableCodes: ["EAI_AGAIN"],             // extra transient codes
  retryUnknown: false,                       // default; true only if every op is idempotent
});

await withRetry(op, { backoff, shouldRetry });
```

### 取消和整个链条的最后期限

`signal` 在每次尝试(一个已经吸收的信号从未开始操作)之前,在每次失败之前,以及 _期间_ 每个后退睡眠——被注射的 `sleep` 缩短等待时间。 还交给了 `op`因此,合作行动在飞行中会松动,而不是跑去完成一个没人想要的结果.

`timeoutMs` 范围 **整个链条** ——每一个尝试加上他们之间的每次睡眠——这是束缚的 `attempts` 无法表达 : `attempts` 限制你问了多少次,但只有最后期限限制你等待多久,因为尝试的持续时间是上游的业务.

```typescript
const crystal = await withRetry((signal) => client.crystals.get(id, { signal }), {
  backoff,
  signal: request.signal, // caller cancellation — never retried
  timeoutMs: 20_000,      // covers every attempt AND every sleep
});
```

检查取消 **在此之前** `shouldRetry` 从来没有看到失败, 并且从来没有被重审。 这不是一种便利——被取消的操作通常会因自己的流产形状出错而失败(p`fetch` 提高a `DOMException` 名称 `AbortError`),被包装的分类学读作可重试的超时),所以留待分类器处理,取消请求会 _开始一个新的_。 。 。 .

是什么表面是信号中止的原因 _页:1_而不是流产的事实 由附件中止的信号 `withTimeout` 带着那个电话 `DeadlineExceededError` 或者说 `DeadlineTimerError`,链条重投它不变。 将每次中止都取消 `OperationAbortedError` 当附在后面的最后期限过后,或者更糟糕的是,当其定时器失效而从未执行过最后期限时,将报告撤回的请求.

**输出 :** `withRetry`, (中文(简体) ), `isTransientError`, (中文(简体) ), `createTransientErrorPredicate`类型 `ShouldRetry`, (中文(简体) ), `RetryConfig`, (中文(简体) ), `RetryAttemptInfo`, (中文(简体) ), `TransientErrorPredicateOptions`。 。 。 .

* * *

## 暂停和取消

`withTimeout` 对同步操作规定一个时钟截止时间,并将操作交给 `AbortSignal` 这样它就可以停止在最后期限——或呼叫者——撤回利息的时刻拉动资源.

```typescript
import { withTimeout, isDeadlineExceededError, isOperationAbortedError } from "@centient/resilience";

const body = await withTimeout((signal) => fetch(url, { signal }), { timeoutMs: 5_000 });

try {
  await withTimeout((signal) => work(signal), { timeoutMs: 5_000, signal: req.signal });
} catch (e) {
  if (isDeadlineExceededError(e)) metrics.timeouts++;
  else if (isOperationAbortedError(e)) return; // the caller left
  else throw e;
}
```

时间流过注射者 `Sleep` ——同一单等接合速率限制器和 `withRetry` 使用——所以这里没有读出墙上钟和用手进行测试来点燃最后期限,没有假定时器. `systemSleep` 当操作获胜时清除其待决的定时器,所以短操作的漫长期限不会使事件循环打开.

当最后期限或流产获胜时, `withTimeout` 拒绝 **马上**;它不等待操作通知。 只有慢事完成后发射的最后期限不是最后期限. 操作的信号被中止( 带有决定性错误作为它的 `reason`),其最终的拒绝被吸收,所以一个被放弃的操作从不露出无所处理的拒绝.

### 三个错误,因为它们意味着不同的东西

| 结果 | 错误 | 由 `isTransientError`? 吗? |
| --- | --- | --- |
| 截止日期已过 | `DeadlineExceededError` | **对** - 这是一个超时 |
| 呼叫者信号中断 | `OperationAbortedError` | **永远** |
| 注射时 `sleep` 被拒绝 | `DeadlineTimerError` | **永远** - 它会重生 |

初分分为分. 最后期限是 _尝试_: 火势恰到好处, 中止是 _打电话者的意图_:重放会复活被明确取消的作品. 一个赤裸的 `AbortController` 都崩溃成一个 `AbortError`,这使得在重试回回路必须决定的时候,区别是无法恢复的.

第三是接合失败,而不是行动. `Sleep`合同是 **页:1** - 流产初期,否则 `ms` ——因此拒绝(或同步抛出)意味着最后期限无法被执行. `withTimeout` 表面为 `DeadlineTimerError`,将拒绝值作为 `cause`,而不是吸收它:一个被吸收的人让呼叫等待一个可能永远不会解决的行动,这是一个默默地被删除的最后期限——比根本没有最后期限更糟糕,因为呼叫者相信他们有一个. 这是故意的 _没有_ 报告为 `DeadlineExceededError`因为最后期限没有过去 说它会让你看到一个缓慢的上游 而不是断裂的接缝.

所有这三者都得到 `name`,通过 `isDeadlineExceededError` 编号 : `isOperationAbortedError` 编号 : `isDeadlineTimerError`,所以分类存活了两本 本包在一个依赖树上 `instanceof` 不会的.

### 组成两个

串行期限内每一次尝试的最后期限,信号线通过:

```typescript
await withRetry(
  (signal) => withTimeout((s) => fetch(url, { signal: s }), { timeoutMs: 2_000, signal }),
  { backoff, timeoutMs: 20_000 },
);
```

内心 `DeadlineExceededError` 是默认分类器重试的瞬间;外部截止结束链。 线索 `signal` 内在的呼唤就是让外部最后期限缩短飞行尝试的时间——同样的错误等级,相反的判决,由范围而不是形状决定.

**输出 :** `withTimeout`, (中文(简体) ), `DeadlineExceededError`, (中文(简体) ), `OperationAbortedError`, (中文(简体) ), `DeadlineTimerError`, (中文(简体) ), `isDeadlineExceededError`, (中文(简体) ), `isOperationAbortedError`, (中文(简体) ), `isDeadlineTimerError`, (中文(简体) ), `neverAbortedSignal`类型 `TimeoutConfig`。 。 。 .

* * *

## 缓存( LRU / TTL / SWR)

容量限制的 LRU 缓存,可选 TTL 和 stale- while- revalidate. 阅读被推广到最近使用;溢出则驱逐最近使用最少的人.

```typescript
import { createCache } from "@centient/resilience";

// Plain LRU
const lru = createCache<string, number>({ maxSize: 100 });
lru.set("a", 1);
lru.get("a"); // { hit: true, status: "fresh", value: 1 }

// TTL + stale-while-revalidate
const cache = createCache<string, User>({
  maxSize: 1000,
  ttlMs: 60_000,                 // fresh for 60s
  staleWhileRevalidateMs: 30_000, // then served stale for 30s more
});

const r = cache.get("u1");
if (r.hit && r.status === "stale") {
  // serve r.value now, refresh in the background
  void refreshUser("u1").then((u) => cache.set("u1", u));
}
```

`get` 返回时 `{ hit, status, value? }` 地点 `status` 实值 `"fresh" | "stale" | "expired"`。误入或完全过期的条目是 `{ hit: false, status: "expired" }`。 。 。 . `stats()` 暴露出被击中/出错/减压/过期计数器.

**输出 :** `createCache`类型 `Cache`, (中文(简体) ), `CacheConfig`, (中文(简体) ), `CacheGetResult`, (中文(简体) ), `CacheEntryStatus`, (中文(简体) ), `CacheStats`。 。 。 .

* * *

## 固定货币池

最多运行同步任务 `concurrency` 飞行中; 其余队列 FIFO. 可选 `maxQueue` 雨棚负载( 以 `PoolRejectedError`而不是任由积压增加.

```typescript
import { createPool, PoolRejectedError } from "@centient/resilience";

const pool = createPool({ concurrency: 4 });
const results = await Promise.all(urls.map((u) => pool.run(() => fetch(u))));

await pool.onIdle(); // resolves when all work settles

// Bounded queue: reject when saturated
const bounded = createPool({ concurrency: 2, maxQueue: 10 });
try {
  await bounded.run(task);
} catch (e) {
  if (e instanceof PoolRejectedError) {
    // shed load
  }
}
```

`pool.active`, (中文(简体) ), `pool.queued`,以及 `pool.stats()` 报告占用情况.

**输出 :** `createPool`, (中文(简体) ), `PoolRejectedError`类型 `Pool`, (中文(简体) ), `PoolConfig`, (中文(简体) ), `PoolStats`。 。 。 .

* * *

## 共享原始出口

-   **时钟 :** `systemClock`, (中文(简体) ), `createManualClock`类型 `Clock`, (中文(简体) ), `ManualClock`
-   **睡觉:** `systemSleep`类型 `Sleep` ——单等接缝(`TokenBucket.acquire`, (中文(简体) ), `withRetry`, (中文(简体) ), `withTimeout`) (中文(简体) ). 签名为 `(ms, signal?) => Promise<void>`; 中止睡眠 **早决定而不是拒绝**,因为什么是流产 _表示_ 属于呼叫者,它知道是来自最后期限还是取消。 一个忽视的睡眠 `signal` 仍能满足这种需求, 拒绝不是合同的一部分,也不是取消的后路:拒绝的睡眠打破了接缝 `withTimeout` 说通过 `DeadlineTimerError` 而不是吸收它.
-   **随机 :** `systemRandom`, (中文(简体) ), `fixedRandom`, (中文(简体) ), `sequenceRandom`类型 `RandomSource`
-   **结果 :** `ok`, (中文(简体) ), `err`, (中文(简体) ), `isOk`, (中文(简体) ), `isErr`类型 `Result`, (中文(简体) ), `Ok`, (中文(简体) ), `Err`

## 许可证

麻省理工学院

## 常见问题

### @centient/resilience 是什么？

零依赖性韧性原始:断路器,令牌-桶和受限键速率限制器,回放-活塞\(加法\+满法\),用可注射故障分级器重试,LRU/TTL/SWR缓存,受限-货币池

### 如何安装 @centient/resilience？

在项目目录运行 npm install @centient/resilience，或使用 pnpm add / yarn add / bun add。

### @centient/resilience 支持 TypeScript 吗？

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

### @centient/resilience 支持 ESM 还是 CommonJS？

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

### @centient/resilience 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=20\.0\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @centient/resilience 可以商用吗？

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

### @centient/resilience 的下载量有多少？

根据 npm 官方下载统计，@centient/resilience 在 2026/10/10 – 2026/11/08 这 30 天内被下载 4,614 次，日均约 154 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @centient/resilience 还在维护吗？

最新版本 0\.4\.0 发布于 2026/09/13（1 个月前）；至今共发布 4 个版本，其中过去 12 个月发布了 4 个；首次发布于 2026/06/16。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

