AfterPack 可以通过两种方式从 Cloudflare Worker 运行,用的是同一个包、同一个调用。不传密钥时,Free 引擎在你的 Worker 中运行,是一个从 npm 安装的 WebAssembly 模块。传入你的 Pro 密钥后,构建会在 AfterPack 的云端以 Pro 引擎运行。
本页介绍这两种方式,然后说明如何缓存结果,让每个资源在每个轮换窗口内只构建一次。
AfterPack,在你自己的 Worker 中
@afterpack/wasm 是编译成 WebAssembly 的 AfterPack。它与 CLI 和框架插件运行的是同一个 Rust 引擎,只是面向 wasm32 构建,而不是面向你机器的架构。
npm install @afterpack/wasmimport { obfuscate } from "@afterpack/wasm";
export default {
async fetch(request) {
const source = await request.text();
const result = await obfuscate({ path: "app.js", source }, { preset: "hard" });
return new Response(result.bytes, {
headers: { "content-type": "application/javascript" },
});
},
};集成就是这么多。不需要 init 调用,不需要 nodejs_compat 标志,也不需要为 .wasm 文件编写 wrangler 规则。该模块没有任何 WebAssembly 导入,因此会在你第一次调用 obfuscate 时同步实例化,而包的 exports 映射会自动为你的打包工具提供适用于 Worker 的入口。同样的导入在 Node 中也能用,那里会由另一个入口从磁盘读取二进制文件。
其接口与 @afterpack/core 完全相同——obfuscate、obfuscateAll、version,全部是异步的,遇到无法发布的构建都会抛出异常。配置是配置页面中记录的同一个对象,包括 preset 和 seed。
@afterpack/core 附带原生二进制文件,速度更快。当宿主无法加载原生二进制时再选用 @afterpack/wasm:Worker、沙箱化的 CI 镜像、边缘函数。
它的代价
引擎二进制文件已经编译好,随包附带。
| 字节 | 体积 | |
|---|---|---|
原始 .wasm | 4,573,321 | 4.36 MiB |
| gzip 压缩后 | 1,307,772 | 1.25 MiB |
Cloudflare 对 Worker 的体积限制是免费套餐压缩后 3 MB、付费套餐 10 MB,因此引擎在两者中都放得下。免费套餐放不下的是工作量:它把每次调用的 CPU 限制在 10 ms,而在笔记本电脑的一个核心上混淆一个 185 KB 的 bundle,在默认的 light 预设下需要不到 0.3 秒,在 medium 下约需 1 秒。超出演示范围的用途都需要 Workers Paid。
在每个请求上都混淆同一份资源的 Worker,每次都要付出这份 CPU 开销。如果你并不需要每个请求都换一种新形态,就按轮换窗口缓存结果,或者在构建中完成这项工作并提供其输出,以便控制费用。
你托管的包能做什么、不能做什么
你安装的 .wasm 执行的是 Free 保护。Pro 变换只在 AfterPack 的基础设施上运行,因此这个包里没有任何可以解锁的东西:没有可以绕过的密钥检查,也没有能把托管副本变成 Pro 构建的开关。key 的作用是把工作路由到云端;它不会在本地启用任何东西。
发布的包带有 npm provenance 记录——包版本、构建它的工作流,以及产物摘要——因此你可以把自己安装的字节与产生它们的那次构建对应起来。
Pro,从你自己的 Worker 中
在同一个 obfuscate() 调用的配置里传入你的 key(例如来自 Worker secret),构建就会在 AfterPack 的云端以 Pro 引擎运行:
import { obfuscate } from "@afterpack/wasm";
export default {
async fetch(request, env) {
const source = await request.text();
const result = await obfuscate(
{ path: "app.js", source },
{ preset: "hard", key: env.AFTERPACK_KEY },
);
return new Response(result.bytes, {
headers: { "content-type": "application/javascript" },
});
},
};包会替你向 POST /v1/obfuscate/batch 发起请求,因此 Worker 永远不需要手写 API 调用。对 Worker 来说过大的文件会自动转到容器中处理。Pro 引擎永远不会进入你的 Worker:它只在 AfterPack 的基础设施上运行,这就是上文所说的层级边界。
每次调用都是一次计量的 Pro 构建(Pro 的费用),外加一次在 Cloudflare 网络内部完成的快速请求。你可以在每个请求上都构建,但每次都会计费,所以更好的做法是按轮换窗口缓存结果。
你的源代码会离开你的 Worker,在 AfterPack 的云端处理:在内存中处理,不会持久化,详见隐私与数据处理。如果无法连接云端,调用会失败即中止:它会抛出异常,不会发布任何未受保护的内容。云端 API 参考是完整的线路契约,部署模式解释了引擎换了运行位置之后会有什么变化。
声称能在你自己的基础设施上运行 Pro 变换的包不是我们的。在安装任何使用这个名字的东西之前,请检查 npm provenance 记录。
按轮换窗口缓存结果
从当前时间窗口派生 seed,并用包含该窗口的键缓存结果。这样每个位置在每个窗口内只构建一次资源,并一直提供到窗口结束,Free 和 Pro 都是如此:
import { obfuscate } from "@afterpack/wasm";
import source from "./pricing.client.js";
const WINDOW_SECONDS = 600;
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
if (url.pathname !== "/pricing.js") {
return new Response("Not found", { status: 404 });
}
const now = Math.floor(Date.now() / 1000);
const slot = Math.floor(now / WINDOW_SECONDS);
const secondsLeft = (slot + 1) * WINDOW_SECONDS - now;
const cacheKey = new Request(`${url.origin}/pricing.js?slot=${slot}`);
let response = await caches.default.match(cacheKey);
if (!response) {
const result = await obfuscate(
{ path: "pricing.js", source },
{ preset: "hard", seed: `pricing.js:${slot}`, key: env.AFTERPACK_KEY },
);
response = new Response(result.bytes, {
headers: {
"content-type": "application/javascript",
"cache-control": `public, max-age=${secondsLeft}`,
},
});
ctx.waitUntil(caches.default.put(cacheKey, response.clone()));
}
response = new Response(response.body, response);
response.headers.set("cache-control", `public, max-age=${secondsLeft}`);
return response;
},
};如果没有设置 AFTERPACK_KEY secret,key 就是 undefined,同一个 Worker 会在你的 Worker 内以 Free 构建。源文件通过 wrangler 的 Text 规则以字符串形式导入,做法与完整演练相同。
窗口同时出现在 seed 和缓存键里,因此运行同一部署的每个位置在一个窗口内都提供相同的字节,新窗口也永远不会读到旧条目。返回的 cache-control 会倒数到窗口结束,因此浏览器不会把副本保留到窗口之后。Cache API 按数据中心存储条目,在 dashboard 编辑器中不起作用,所以请用 wrangler dev 或已部署的路由来测试。
如何选择
| 你想要 | 使用 |
|---|---|
| 在 Worker 中混淆,任何东西都不离开你的账户 | @afterpack/wasm,即本页 |
| 在普通的 Node 构建中混淆 | @afterpack/core 或 CLI |
| Pro 加固和按区域的指令,从 Worker 中使用 | 带上 key 的同一个调用,它会在 AfterPack 的云端构建 |
| 每个窗口换一种新形态,而不必为每位访客构建一次 | 轮换窗口缓存 |