套餐限制、配额阈值和功能开关规则通常最终会进入客户端 bundle,竞争对手或客户可以直接从文件中读出它们。
在纯 JavaScript 中,你的定价表读起来就是一份规格说明
当定价表、配额映射和功能开关列表以纯 JavaScript 的形式存在时,它们读起来就是你商业模式的文档。任何人都可以打开 bundle,看到你的各个层级在哪里分界、什么会触发升级推销,以及哪些开关控制着哪些功能。
编码智能体在这个 bundle 上过一遍,就能把零散的值整理成一份有条理的规格说明。这份说明可以被照抄、在支持工单里被拿来据理力争,或者在你察觉之前就被用来绕过某个限制。
编码后的值,重写后的结构
在任何正数的 complexity 目标下,字符串字面量都会被编码,因此表格和阈值在你提供的文件中不再是可读的文本。它们周围的结构也会被重写。仔细研读输出的阅读者能恢复出一个等价的计算过程,并且必须从中重建出你的规则原本的含义。
轮换是让恢复出来的副本无法一直有用的关键。每次发布都使用新的种子,意味着别人上一次重建出来的东西,描述的是一个你已经不再提供的构建。
在存放规则的模块上提高目标
在预设阶梯上把整个 bundle 提高一级,或者当预设的粒度不合适时直接设置目标:
{
"preset": "medium",
"complexity": 40
}若只想标记存放规则的那个模块,就使用指令。指令在 Pro 构建上生效。
/* @afterpack preset=extreme */
export function resolveQuota(plan: Plan, usage: Usage): number {
return TIER_LIMITS[plan.tier] - usage.consumed;
}
/* @afterpack end */strings.leaks.max 把“没有可读字面量残留”变成一道构建关口,而不是靠肉眼检查的东西。失败时会给出 DIAG_SURVIVING_READABLE_LITERAL,并指向漏网的那个字面量。
有字面量残留时让构建失败
在 CI 中用 --strings.leaks.max=0 构建,并把非零退出视为构建失败。然后打开 Protection Map,检查规则模块的着色是否达到了你为它设定的目标。
无论你发布什么,都仍然可以被观察到的内容
轮换构建会让别人抓取的规则副本在下一次发布时过时,而这正是值得拥有的部分。这里讨论的范围是那些只为在 bundle 内部被读取而存在的值;你的 UI 渲染出来的价格或你的 API 返回的限制,无论 bundle 长什么样都可以被观察到,所以请把页面显示的任何内容都视为公开的,并在服务端强制执行每一项限制。
下一步
- 威胁模型:静态阅读者能恢复什么、不能恢复什么。
- 预设:完整的阶梯,以及每一级的复杂度目标。
- Protection Map:确认规则模块得到了你所配置的处理。