# @12-apps/entitlements 中文文档

规范地址：https://npmcn.com/package/%4012-apps/entitlements/

版本：3\.11\.0
npm 元数据更新时间：2026-10-01T16:16:38.553Z
中文译文更新时间：2026-11-05T20:01:46.758Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%4012-apps/entitlements
原文对照：https://npmcn.com/package/%4012-apps/entitlements/?lang=original
项目主页：https://github.com/12-apps/shared-packages#readme
源码仓库：https://github.com/12-apps/shared-packages

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

- 包体积（解压后）：376 kB，62 个文件
- 模块格式：ES Module
- TypeScript 类型：未声明
- Node.js 要求：\>=22\.0\.0
- 运行依赖：1 个
- peer 依赖：hono \>=4\.0\.0，react \>=19\.0\.0，react-dom \>=19\.0\.0，@12-apps/wiring \>=1\.3\.0
- 首次发布：2026-08-05T02:17:05.828Z
- 最新版本发布：2026-10-01T16:16:37.502Z
- 近 30 天下载量：32487（2026-10-04 至 2026-11-02，npm 官方下载统计）
- 已发布版本数：51（近 12 个月 51 个）

## 中文简介

通用的、可移动的计划/地点:分层解析(代码/计划/状况/租户)、数字配额、每个地点的取消政策和402-vs-403拒绝语义。 无框架核心加创建 ApiEnitions (./server,./hono适配器)并创建WebEn

## 安装

```sh
npm install @12-apps/entitlements
```

也可以使用：`pnpm add @12-apps/entitlements`、`yarn add @12-apps/entitlements`、`bun add @12-apps/entitlements`

## README

# `@12-apps/entitlements`

通用的、便携式的 **计划/福利** 层。 无框架, 无存储, **无帐单**零运行时间依赖.

分步线导览 [`ADOPTING.md`](https://npmcn.com/package/%4012-apps/entitlements/ADOPTING.md); 此文件 是概念 + API 引用.

在无框架核心之外,包裹将SURFACE作为两所工厂运送 (插件和游戏合同): `createApiEntitlements(config)` 下级 `./server` (框架中立路线描述符——快照,计划视图+定价卡, 更改计划的请求——加上警卫、使用记录、原子 定额警卫、保留水印、租户设定作家和 通知频道政策),a `./hono` 搭载他们的适配器,以及 `createWebEntitlements(config)` 下级 `./react` (计划屏幕 `withEntitlement` 页面门和升级的快捷主机)。 棱镜部分 页:1 `RetentionWatermark` 编辑 `entitlements-coverage` 门脚本船 包裹。 见Adopting.md 第10节.

**商业政策是HOST的,是必需的.** 等级,什么 他们被称为,他们的成本, 他们读的货币, 他们的间隔 重复和可能要求更改的许可都作为配置到达, 在大会上检查 `assertApiEntitlementsConfig`唯一一个 软件包回馈是 `entitlementsPermissions(labels)` - 单位代号 (单位:千美元)`plan:request`(a) 保护自己的文字,由东道主写成——东道主 编成其RBAC目录. 见 [`ADOPTING.md`](https://npmcn.com/package/%4012-apps/entitlements/ADOPTING.md) 第11节.

* * *

## 问题解决

大部分的应用软件最后都有两条进入控制轴,并试图让其中之一做 第三个工作:

| 层 | 问题 | 拥有者 | 范围 | 拒绝 |
| --- | --- | --- | --- | --- |
| 代码 | 建筑支持它吗? | 页:1 | 全球 | 404 (韩语) |
| **应享权利** | **房客的计划能弥补吗?** | **平台/计费** | **租户** | **402 (韩语)** |
| 租户的设定 | 房客把它关了吗? | 租户管理员 | 租户 | 隐藏 |
| 非洲区域局许可 | 可能 _此用户_ 做到这一点? | 租户管理员 | 用户 | 403 (韩语) |

将计划编码为 **非洲区域局** 关键值失败 : 权限为每个用户, 计划是按租户计的 所以计划改变必须重写每个角色 - 包括租户自己写作的任何角色,然后必须 升级时不重写或被销毁。 作为单独的一层,降级就是一层 写作和角色不受影响,所以再升级立即恢复了一切.

给房客自己编码 **特性旗** 无法信任 : 租户可注销的操作开关。 应享权利为平台写作和 对抗。 形状相同,方向相反.

所以:第三层. 这个包裹.

## 有两条规则能奏效

**1\. 它对金钱一无所知.** 没有价格,没有货币,没有间隔,没有 提供单位 ID 。 它接受一个已经解决的权利地图 加上粗糙的 寿命周期状况`active` 编号 : `restricted` 编号 : `suspended`) (中文(简体) ). 计费系统地图 进入这三个国家 _一个可能的作者_ 计划 层——从不依赖. 使一个有手分级的宿主, 内部的β程序,或者没有任何帐单 使用这个不变.

**2\. 依次检查应享权利和许可,不得交叉.**

```ts
// ✗ intersecting — loses the reason
const effective = permissions ∩ entitlements;   // everything is just a 403

// ✓ sequential — keeps it
await requireEntitlement('exports.bulk', { tenantId }); // 402 → upsell
await requirePermission('exports:read', { scope });     // 403 → "Ask your admin"
```

交叉,你无法再分辨 _"你的计划不包括这个"_ 从 _"你的角色不包括这个"_. 上市面—— 完全享有权利——因这种区别而死亡.

## 决议令

一个特性只有在每层都同意时才可用:

```
1. code    registry declares it                      → 'not-supported'
2. plan    plan ∪ overrides grants it                → 'not-entitled'      (upsell)
3. status  account not restricted/suspended          → 'restricted' | 'suspended'
4. tenant  tenant hasn't switched it off             → 'disabled-by-tenant'
```

这个 **原因** 一直到呼叫者为止 这就是整个设计: 仅限 `not-entitled` 是一个上市。 提供“ 升级以解锁” 特性 房客已经付了钱 自己也关了 都是假的 `requiredPlan` 实值 `null` 对于其他的否认.

有资格的租户永远不会被迫——第4层总是胜过第2层.

* * *

## API 密码

### `defineFeatures(catalog)`

已键入特性注册 。 镜像 `@12-apps/rbac`是因为 `definePermissions`: 通过一个 `const` 映射如此未知的密钥失败类型检查而不是静默解析到 "没有资格".

```ts
const FEATURES = defineFeatures({
  'exports.bulk':      { onRevoke: 'hide' },
  'webhooks.outbound': { onRevoke: 'disable' },
  'seats.included':    { kind: 'quota', onRevoke: 'readonly' },
  approvals:           { onRevoke: 'disable', defaultWhenEntitled: false },
  'records.read':      { retainWhenRestricted: true },
} as const);

type AppFeature = (typeof FEATURES.list)[number];
```

| 字段 | 默认 | 含义 |
| --- | --- | --- |
| `kind` | `'boolean'` | `'quota'` 数字限制 |
| `onRevoke` | `'hide'` | 主机对现有数据所做的—— `hide` 编号 : `readonly` 编号 : `disable` |
| `defaultWhenEntitled` | `true` | 直到房客选择何时 `false` (用于写吉他) |
| `retainWhenRestricted` | `false` | 活下来了 : 保持读取路径的开放, 限制会降解而不是砖块 |

特征密钥是线到 **密码门**。计划只能引用 已经存在;它们永远不能铸造出新的.

### `definePlans(registry, plans)`

等级, 用 `extends` 财务报告和审定财务报表 **编译时被平整** 所以运行时间 解析从不走一链 坏目录在启动时失败.

```ts
const PLANS = definePlans(FEATURES, {
  solo:  { entitlements: { 'records.read': true, 'seats.included': 1 } },
  team:  { extends: 'solo', entitlements: { 'webhooks.outbound': true, 'seats.included': 5 } },
  scale: { extends: 'team', entitlements: { 'exports.bulk': true, 'seats.included': 'unlimited' } },
} as const);
```

申报令被认为是最便宜的 – 最富有和驱动器 `cheapestWith()`, (中文(简体) ), (即: `requiredPlan` 每402个 更丰富的层次可能 _下调_ 继承 值( 设置配额到 `0`以及提高它。 周期和未申报特征 键投出.

### `createEntitlements(config)`

```ts
export const entitlements = createEntitlements({
  features: FEATURES,
  plans: PLANS,          // optional — omit for hand-assigned entitlements
  source,                // port: load(tenantId)
  usage,                 // port: count(tenantId, feature) — quotas only
  cache,                 // port: optional read-through cache
});
```

| 方法 | 返回时 |
| --- | --- |
| `check(tenantId, feature)` | `EntitlementDecision` 永远不要拒绝 |
| `checkQuota(tenantId, feature)` | 决定+ `used` 编号 : `remaining` 编号 : `exceeded` |
| `checkAll(tenantId)` | 一个传中每个特性 |
| `require(tenantId, feature)` | 掷出 `EntitlementRequiredError` → **402 (韩语)** |
| `requireQuota(tenantId, feature, need?)` | 掷出 `QuotaExceededError` → **402 (韩语)** |
| `toSnapshot(tenantId)` | 浏览器的 JSON 可串行投影 |
| `invalidate(tenantId)` | 丢弃缓存状态 |

### 港口——整个东道主接头

```ts
interface EntitlementSource<F> {
  load(tenantId: string): Promise<{
    plan: EntitlementMap<F>;
    planKey?: string | null;
    overrides?: EntitlementMap<F>;   // comped / beta / enterprise, layered OVER the plan
    settings?: SettingsMap<F>;       // the tenant's own off-switches
    status?: LifecycleStatus;
  }>;
}

interface UsageCounter<F> {
  count(tenantId: string, feature: F): Promise<number>;
}

interface EntitlementCache {
  get(key): Promise<string | null>;
  set(key, value, ttlSeconds?): Promise<void>;
  del(key): Promise<void>;
}
```

一个 **覆盖** 双向的图层: 它可以授予计算 特性 _财务报告和审定财务报表_ 取消一个,不碰计划目录。 因为一个超能力 _替换_ 计划的价值,而不是与它合并, 一个否认来自 一载 `requiredPlan: null` - 租户买不起的计划 都抬不起 平台撤销,他们目前的计划 很可能是给予它.

### 配额

`EntitlementValue = boolean | number | 'unlimited'`。 。 。 .

`0` 表示“ 无标题” 的真正值, 因此一个等级可以明确为零 放弃了继承的配额. `'unlimited'` 保持一个哨兵,而不是 `Infinity` 这样 地图幸存 `JSON.stringify`。 。 。 .

```ts
await entitlements.requireQuota(tenantId, 'seats.included');    // +1
await entitlements.requireQuota(tenantId, 'seats.included', 5); // bulk create
```

> ⚠️ **检查时的行为不是原子.** 两个同时生成可以同时读取 `used = 9` 以10为限 和两个通行证。 引擎返回 a _决定_; 通过在插入的交易中计数来进行解剖执行或 如果超龄确实重要,则有DB限制(始终是座位), 通常不用于电话).

### 降级

**降级绝不能删除数据 .** 每个决定都带有 `policy` 即使拒绝,所以主机知道 如何处理已经行 已存在 :

| 政策 | 主机行为 |
| --- | --- |
| `hide` | 表面消失,行未动 |
| `readonly` | 列保持可见和可用, `create`编号 :`update` 拒绝 |
| `disable` | 已停用但保留的行; 重新获得资格时重新启动 |

超等同形: 现存行能活下来, `create` 返回 402.

### 邓宁自由了

`restricted` 只是另一个解析层,所以一个违法的房客需要 **没有 单独锁定机制** 一个 `status` 书写关闭每道门 守护您已经写好了, 而特性标记 `retainWhenRestricted` 保持开放 故此账户会退化而不是砖砌. `suspended` 不允许有任何例外.

也不提供高价: 房客已经付了钱,所以 正在安顿下来, 消息属于主机.

* * *

## 反应

```tsx
import { EntitlementsProvider, Entitled, Locked, useQuota } from '@12-apps/entitlements/react';

<EntitlementsProvider snapshot={snapshot} onUpsell={openPricingModal}>
  <Entitled feature="exports.bulk" fallback={<ExportsTeaser />}>
    <ExportsPage />
  </Entitled>

  <Locked feature="webhooks.outbound">
    {({ requiredPlan, upsell }) => (
      <button onClick={upsell}>🔒 Disponível no plano {requiredPlan}</button>
    )}
  </Locked>
</EntitlementsProvider>
```

客户 **永远不再重解** 它收到一个由服务器制作的快照 完全一样 `@12-apps/rbac`是因为 `RbacProvider` 取一个已经解决的 权限设置 .

`<Locked>` 无头而无所为 `disabled-by-tenant` 或者说 `not-supported`所以只要在销售时出现涨价 也许吧. `onUpsell` 是逃生舱门: **软件包不拥有升级 模式** ——定价副本和CTA正在应用品牌,拥有这些品牌将是什么 停止重用此选项 .

`useQuota(feature, used)` 从呼叫者那里取用,因为活计数是 不在快照中; 列表中的组件已经知道 .

> **边界 :** `/react` 只能导入类型和纯洁的助手——绝不是一个端口, 适配器或发动机. 由 `src/react/__tests__/boundary.test.ts`。 。 。 .

* * *

## 测试

```bash
pnpm --filter @12-apps/entitlements test
```

三个套房是可移植门 他们回答不同 问:

| 套间 | 证明什么 |
| --- | --- |
| [`portability.test.ts`](https://npmcn.com/package/%4012-apps/entitlements/src/__tests__/portability.test.ts) | eNGINE是可移植的——一个玩具取款的SaaS,有自己的目录、自己的计划以及两个港口 |
| [`portability-surface.test.tsx`](https://npmcn.com/package/%4012-apps/entitlements/src/__tests__/portability-surface.test.tsx) | sURFACES是 ——它AMONTS Hono路由器 和反应工厂 一个音乐厅 并拒绝一个字 从任何其他东道主的词汇 |
| [`packed-artifact.test.ts`](https://npmcn.com/package/%4012-apps/entitlements/src/__tests__/packed-artifact.test.ts) | "TARBALL是,它问 `npm pack` 哪些文件可以上传和扫描所有已公布的文件 |

中间一个之所以存在,是因为第一个还不够:每一个默认 实际泄漏(BRL价格 `/mês` 间隔, 硬编码顶端 级, 复制一个主机的名词) 坐在一个没有引擎呼叫的表面上. 最后一个存在是因为 `files` 发表 `src`, (中文(简体) ), `prisma`, (中文(简体) ), `scripts` 财务报告和审定财务报表 每个 `*.md` ——超过任何渲染的屏幕所能显示.

`memory.ts` 为测试和当地提供内源/使用/缓存适配器 发展.

## 常见问题

### @12-apps/entitlements 是什么？

通用的、可移动的计划/地点:分层解析\(代码/计划/状况/租户\)、数字配额、每个地点的取消政策和402-vs-403拒绝语义。 无框架核心加创建 ApiEnitions \(\./server,\./hono适配器\)并创建WebEn

### 如何安装 @12-apps/entitlements？

在项目目录运行 npm install @12-apps/entitlements，或使用 pnpm add / yarn add / bun add。

### @12-apps/entitlements 支持 TypeScript 吗？

该包的 package\.json 没有声明类型入口。需要类型提示时，可以在 npm 上查找社区维护的 @types/12-apps\_\_entitlements 是否存在。

### @12-apps/entitlements 支持 ESM 还是 CommonJS？

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

### @12-apps/entitlements 需要什么版本的 Node\.js？

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

### @12-apps/entitlements 可以商用吗？

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

### @12-apps/entitlements 的下载量有多少？

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

### @12-apps/entitlements 还在维护吗？

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

