@12-apps/entitlements

中文文档
3.11.0发布于 2026/10/02MITES Module构建来源已验证近 30 天下载 3.2万

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

中文使用文档
文档目录13 节
  1. 问题解决
  2. 有两条规则能奏效
  3. 决议令
  4. API 密码
  5. defineFeatures(catalog)
  6. definePlans(registry, plans)
  7. createEntitlements(config)
  8. 港口——整个东道主接头
  9. 配额
  10. 降级
  11. 邓宁自由了
  12. 反应
  13. 测试

@12-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 第11节.


问题解决

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

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

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

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

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

有两条规则能奏效

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

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

// ✗ 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 映射如此未知的密钥失败类型检查而不是静默解析到 "没有资格".

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
defaultWhenEntitledtrue直到房客选择何时 false (用于写吉他)
retainWhenRestrictedfalse活下来了 : 保持读取路径的开放, 限制会降解而不是砖块

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

definePlans(registry, plans)

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

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)

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)丢弃缓存状态

港口——整个东道主接头

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。 。 。 .

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 不允许有任何例外.

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


反应

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。 。 。 .


测试

pnpm --filter @12-apps/entitlements test

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

套间证明什么
portability.test.tseNGINE是可移植的——一个玩具取款的SaaS,有自己的目录、自己的计划以及两个港口
portability-surface.test.tsxsURFACES是 ——它AMONTS Hono路由器 和反应工厂 一个音乐厅 并拒绝一个字 从任何其他东道主的词汇
packed-artifact.test.ts"TARBALL是,它问 npm pack 哪些文件可以上传和扫描所有已公布的文件

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

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

版本与发布记录

@12-apps/entitlements 共发布 51 个版本,首次发布于 2026/08/05;过去 12 个月发布了 51 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
3.11.0latest2026/10/021 个月前
3.10.12026/09/111 个月前
3.10.02026/08/262 个月前
3.9.02026/08/252 个月前
3.8.02026/08/252 个月前
3.7.02026/08/252 个月前

关于 @12-apps/entitlements 的常见问题

@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 和提交记录判断。