@drupflare/cartridge

中文文档
0.3.1发布于 2026/10/01MITTypeScriptES Module构建来源已验证近 30 天下载 2201

在 Cloudflare 持久对象中运行一个被屏蔽的被解释器

中文使用文档
文档目录17 节
  1. 目录
  2. 为什么是卡特里奇
  3. QQ 安装
  4. QQ 快速启动
  5. 这里是什么
  6. 子路径导出
  7. Ergonomics (Ergonomics) (英语)
  8. QQ 错误处理
  9. * 留校口
  10. · 断开口罩
  11. QQ 文件系统
  12. 主管
  13. 尾矿工人
  14. 🏗 二层
  15. 测试
  16. 有关仓库
  17. 许可证

弹匣

在 Cloudflare 持久对象中运行一个被屏蔽的被解释器

Build Interpreters Prettier codecov License

墨盒在一个 Cloudflare 持久对象内运行一个被屏蔽的被子解释器 。 过 createCartridge() 一个主题模块和呼叫 run();再入门;中断口罩 文件系统为您设置了线程 。 每件物品都会被出口给需要的呼叫者.

工人运行时间是同步的,而原声翻译不是同步的。 两项重叠的援引 共享一个可变的机器—— globals, 打开文件描述符, 会话状态 —— 和 interleave into 似乎错误的输出而不是失败.

这里没有关于 PHP 的具体信息 。 实际建筑贯穿 tests/interpreters/ 每推一推: 第8.3页 (日语), 卢亚 5.4 (是月亮), 氯乙烯 3 (P), 快速JS (速记), 克鲁比3.4 (Ruby.wasm, over WASI 而不是标注)和 爪哇语 (字节框,提前编译而未解读). 见 推进 用户.md 每个语言状态和每种语言的食谱.


目录


为什么是卡特里奇

一个被翻译者是 单可变机。环球,自动装入器缓存,容器 指针,打开文件描述符和会话状态都是全过程的. 两起重叠的援引 不要大声地失败。 它们相互交织,产生可信的错误输出.

Cloudflare的运行时间是相反的形状. 它是事件驱动的, 它将幸福地提供第二 请求时,第一个已停放。 持久物体缩小范围,但不要关闭.

所以,主机必须提供 四个东西 口译员不能为自己提供: 串行化,一种方式 在主机呼叫中挡住一个中断, 这个文件系统在启动时不会花一大笔钱, 和一条不会在它修理的东西里面运行的修理道.

每一个都默不作声。 无假名的回叫表象 SuspendError 从哪里 opcode计数器在数月后着陆 负载不足.


QQ 安装

bun add @drupflare/cartridge

两个运行时间依赖关系, fflate 财务报告和审定财务报表 fzstd两个同步减压器 一个懒惰的山 在同步内充气 open() 预压缩的翻译器充气 范围; 两点都不能等待 DecompressionStream,它也仅限于gzip和放出.


QQ 快速启动

import { createCartridge } from '@drupflare/cartridge';

const cartridge = createCartridge({
  // whatever your build's factory is; forward the io it gives you
  instantiate: (io) => initInterpreter({ print: io.print, printErr: io.printErr }),
  // files every run should see, written once at boot
  files: { 'lib/helper.txt': 'shared by every run' }
});

const result = await cartridge.run('print("hello")');
console.log(result.stdoutText); // "hello\n"
console.log(result.status); // 0

这是默认路径。 每个条目都是连续的,每个 print 装入口罩,脚本是 在文件系统之前 callMain 输入,并用名称报告被泄露的口罩深度 毒死下一个跑道.

你的建筑必须满足 是两个成员:

interface Interpreter {
  FS: MountFS; // emscripten's FS: mkdir, writeFile, and optionally utime
  // may answer a promise, for an adapter with work to finish after main returns
  callMain(argv: string[]): number | void | Promise<number | void>;
}

这是标语为 main()- 程序与 -sINVOKE_RUN=0 财务报告和审定财务报表 -sEXPORTED_RUNTIME_METHODS=callMain,FS。 。 。 . 大多数出版的建筑不这样做曾经是月亮 活地, 快速标记、红宝石、花字节和字节都检查过了,6个出口中没有一个 callMain,因此将其写作"双行适配器"是正常的例而非倒置. createMemoryFS() 当一个建筑也没有文件系统时,则提供另一半,以及 FS 是三种方法签名 而不是标注对象, 所以一个WASI预开也满足它。 见食谱在 推进 用户.md,每个标签 实际运行.


这里是什么

模块它会做什么
lazy-fs.tsMEMFS 从每个文件包的分层上挂上第一个打开的文件
supervisor.ts断路器 修理梯子
cartridge.tscreateCartridge():高级默认值,以及 RunResult
mount.ts急切地奔流而行,驾车而行, mountRecord(), (中文(简体) ), createMemoryFS()
tail-worker.ts{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}把一个尾巴工人的事件 归纳成一个人类读到的东西 {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}我从小到大
mask.ts重算中断口罩,加上 VM 切片计数器
inflate.ts同步 zstd: 内容大小, inflateZstd,一个被支持的解码器
util.tsfromUtf8编号 :toUtf8, (中文(简体) ), encodeJson编号 :decodeJson, (中文(简体) ), toBytes, (中文(简体) ), splitLines
serialize.tsGate: 1名口译员, 一次引用
errors.ts五个名称出错词汇表,每个都有一个稳定的点 code
worker-shim.ts使标注 ENVIRONMENT=worker 在工人身上建造即时电源d

子路径导出

一个包裹, sideEffects 诚实地宣布, 和一个子路径 每一个顾虑 你没有使用.

导入里面有什么东西
@drupflare/cartridge下方除了烟雾
@drupflare/cartridge/cartridgecreateCartridge(), (中文(简体) ), RunResult, (中文(简体) ), Interpreter
@drupflare/cartridge/gateGate, (中文(简体) ), doGate, (中文(简体) ), GateStats
@drupflare/cartridge/maskcreateMask单子, vmFromBinary切片计数器
@drupflare/cartridge/fs两座山, mountRecord, (中文(简体) ), createMemoryFS,两个包索引形状
@drupflare/cartridge/inflate同步zstd,用于一个在后靴上充气的预压缩模块
@drupflare/cartridge/supervisor铁丝网, CircuitBreaker,分类账 DDL
@drupflare/cartridge/tail佣人和纯洁的仆人
@drupflare/cartridge/util字节、字符串和 JSON 便利
@drupflare/cartridge/errors错误词汇表
@drupflare/cartridge/shim副作用; 在粘结值评估前导入以生效

[尖叫声] sideEffects 是数组而不是 false。 。 。 . worker-shim.ts 补丁 globalThis 在导入时间; 声明包无副作用将允许捆绑树摇 删掉 Asyncify 断接并恢复 无法捕获 请求杀死。 声明 true 将禁用摇动整个软件包。 该阵列是唯一诚实的答案,和 tests/exports.spec.ts 钉住它.


Ergonomics (Ergonomics) (英语)

永远不要建立 TextEncoder 用于使用此软件包. 每个有效载荷 string | Uint8Array· ; 将字节交回的任何东西 也都交回解码文本.

import { createCartridge, fromUtf8, toUtf8 } from '@drupflare/cartridge';

const cartridge = createCartridge({ instantiate });

// a string or bytes, both fine
await cartridge.run('print(1)');
await cartridge.run(fromUtf8('print(1)'));

// structured input, no encoder at the call site
await cartridge.writeJson('input.json', { items: [1, 2, 3] });

const result = await cartridge.run('read input.json and print a total');
result.stdout; // Uint8Array, the primitive
result.stdoutText; // string, what you wanted
result.json<{ total: number }>(); // parsed
result.lines(); // ['{"total":6}']
result.lastLine(); // the last line, no trailing empty element
result.assertOk(); // throws InterpreterError naming the status and stderr

生道犹在

// the module itself, instantiated if needed, handed over inside the gate
const raw = await cartridge.interpreter();

// or run arbitrary work against it, still serialised against every other entry
await cartridge.withInterpreter((module) => module.FS.writeFile('/tmp/x', 'by hand'));

命名是固定的,所以可以学习一次: to*编号 :from* 用于编解码器(toUtf8, (中文(简体) ), fromUtf8, (中文(简体) ), *Json 页:1 序列/区间(writeJson, (中文(简体) ), encodeJson, (中文(简体) ), decodeJson, (中文(简体) ), *Text 用于解码字段(stdoutText, (中文(简体) ), .json() 编号 : .lines() 最终结果.


QQ 错误处理

五个名字,一个基地,每个都带一个稳定的点 code匹配代码,而不是 信件文本.

类默认代码升起时
CartridgeErrorcartridge.error底; 以及 JSON 编码拒绝
GateErrorgate.error闸门被滥用,或者看到两跑一次
MaskViolationErrormask.violation面罩不均匀,或蒙面时试图悬浮
MountErrormount.error一个包是无法到达的, 或者FS拒绝写
BudgetErrorbudget.exceeded切片、居民字节上限或中断津贴
InterpreterErrorinterpreter.error即时,无入站,无出站,有口罩
import { InterpreterError, isCartridgeError } from '@drupflare/cartridge';

try {
  (await cartridge.run(script)).assertOk();
} catch (error) {
  if (error instanceof InterpreterError && error.code === 'interpreter.nonzero_exit') {
    // the script failed, which is different from the interpreter failing
  } else if (isCartridgeError(error)) {
    // something in the cartridge layer; error.code says which seam
  }
}

代码使用与 supervisor.ts是因为 Finding.code,所以一个抛出的错误和一个 有记录的关于同一缺陷的发现与没有翻译表相关.


* 留校口

Gate 串行输入解释器:一次引用一次,排入FIFO顺序.

今天每个主机的呼叫都是同步的,所以还没有重叠. 一个暂停电话——JSPI, a 正在等待的查询, 一个出局的取回—— 足够第二个请求输入, 而第一个请求是 停车要求中.

有两个不同的机制:

机制工作地点为何不只这个
Gate一个纯洁的劳动者,一个坚忍的、, wrangler dev便携式,什么测试驱动
ctx.blockConcurrencyWhile()仅限持久对象更强的,运行时间停止了 事件,但它的上限为 30 s ,不能被巢

doGate(gate, ctx) 是组合,和顺序事项: 门已输入 第一个 编辑 挡板被取入其中 绝不相反; blockConcurrencyWhile 无法筑巢. 传出 a ctx 改为 createCartridge() 你自动得到它.

[叹息] 在电话里找到门 已经挂起它就永远挂起,没有错误. alarm() 整个封口时 fetch() 因此,在添加有门的入站点之前,请检查是否 你已经在其中之一.


· 断开口罩

mask.ts 是最没有明显存在理由的作品. _zend_wasm_slice_arm(period) 火灾 越战中断 密码计数器,不是接缝和JSPI不能中止 堆与JS 里面的画框.. SuspendError: trying to suspend JS frames. 多个主机的电话正好设置了这个框架 翻译堆栈下方: SQL桥, 里面的解码器, 记录器, 每种能力 呼叫,每个懒惰的FS inflateSync和每个 print 调用.

因此中断时间必须在主机通话期间被扣住并随后被放出. 面具 实值 重新计算由于这些呼叫巢穴, 它暴露了柜台 所以泄漏是可见的而不是 推断:

import { createMask } from '@drupflare/cartridge/mask';

const mask = createMask();

const rows = mask.withMask(() => {
  // any host call that puts a JS frame under the interpreter goes here
  return [];
});

const { enters, nested, maxDepth, deferred, violations } = mask.stats();

4个属性,每个属性都是一种无声错误的方式:

  1. 被贬. 案例之巢——主机呼叫可以触发懒惰的FS读取——和不计数 内出入口的口罩解密.
  2. 旗子上挂着一面旗子. zend_wasm_tick_fired() 跳过 EG(vm_interrupt) 除非宿主记得,否则边界就会消失.
  3. 预算在解职后重新检查不仅在民意测验现场 一个充气成员为~1毫秒 本地比例,所以一个被遮住的窗口可以自行超过一片.
  4. 肯定的断言 暂停从不发生在深度0以上. MaskViolationError 被丢弃 而不是吞没.

createCartridge() 给每个弹匣一个 新鲜 口罩而不是模块单子: 二 一个隔离的口译是两个C边的计数器,共用一个主计数器将使两者都成为 深度错误。 单通为工人使用的单通解释案保留出口.

[注] 主机轨道的深度是 东道主 深处。 C处理器在使用期间自我伪装 自己的回报,所以 zend_wasm_slice_stat(4) 在收益范围内读取1,这是正确的,而不是 违反。 永远不要断言这一点.


QQ 文件系统

3个挂载,用于3个不同大小的问题.

函数对于费用
mountRecord()手持的几个文件无;平地 Record<string, string | bytes>
mountDrupalStreaming()一整棵树,在靴子上铸造一分一分,一分一分
mountDrupalLazy()一整棵树, 每个文件在第一个上充气 open()1个居民压缩blob+1个LRU

mountRecord() 是一个第一需要的 createCartridge({ files }) 给你打电话 A 类 包是 11 421 文件 CMS 树的正确答案 和 3 文件的错误答案 an 翻译需要运行一个脚本.

当建筑有了 没有自己的文件系统 - 速成图不出口,并出口一台TeaVM或 WASI目标没有标记 FS 或者是.. createMemoryFS() 是一个 MountFS 一个以上 Map,与 read() 财务报告和审定财务报表 readText() 所以适配器从不构建 TextDecoder。 。 。 . MountFS.utime 可选 出于同样的原因:是月亮的Lua建造了一艘真正的船标 FS 没有 utime 完全没有,而且只有 mountDrupalStreaming() 从不叫它.

懒惰的山体开始冷却 温度为3,754毫秒 cpuTime 财务报告和审定财务报表 3,066毫秒是那座山。图层按顺序合并,所以驱动程序或补丁可以阴影一个 未重建的基底包,以及 InflateStats 报告实际夸大的内容——哪些 是衡量懒惰是否付出的尺度.

[大 StreamPackEntry 财务报告和审定财务报表 PerFilePackEntry 不属于一类,不得合并. o 表示 不同的东西:流线索引被相抵入了 INFLated 连接 core.bin.gz 生产、每个文件的指数相抵为压缩blob和需要 c 旁边 l一个 覆盖两者的界面将提供 o 两个意义和制造 c 可选 materialise(), (中文(简体) ), 计算 blob.subarray(e.o, e.o + e.c) 并且会得到 NaN。 。 。 .

还有两项行为:

  • 图层在节点创建前合并,所以后面的一层会覆盖同一路径上较早的一层.
  • 平面还原取决于打开的文件描述器表,在相同的fd数字上. 窗口对齐 已经 没有 物质:测量和伪造的(将每个inode转换为1个和500个两种) 恢复同字节。 丢弃 /dev/urandom单 fd 扔出; 丢出 sqlite 的 fd 给定 锁定- 校正错误 80-120 二档,在边缘运行时间是一个吊 请求,而不是错误。 如果你触摸了挂载或快照路径,那就是无常.

R2支持层成本 零子请求,使R2成为可变层的正确商店.


主管

supervisor.ts 持有必须是 JavaScript 的健康层的一半 : 修补路径不能 取决于它修复的子系统.

译者不能看到JS 被扔出一个垃圾堆, @ (单位:千美元) catch 既无用,又整个援引无效——不能观察它自己的孤立被杀死 无法相信自己一旦被下毒后会修复自己。 所以这些班级的检测和修理 都活下来了 在这里.

每条绊倒线都相当于一个缺陷 这个项目已经运出,然后找到. 这是 选择标准, 并每个人命名它的事件。 没人见过火的支票就是装饰.

断开器会衰减而不是循环: 窗口内相同的代码 N 次会提升一个运行 梯子,一个干净的间隔会衰变一跑, 并用一个命名的理由限制尝试.

import {
  CircuitBreaker,
  quarantineDecision,
  runHostTripwires
} from '@drupflare/cartridge/supervisor';

const findings = runHostTripwires({ status: 200, bytes: 0, path: '/' });
const { quarantine, reason } = quarantineDecision(findings);
// quarantine === true, reason === 'render.empty: 200 with 0 bytes'

检疫击败了错误的输出. 一个503 Retry-After 答案比0字节200还好 这个工程运出了0字节200——然后缓存了它,然后从边缘服务.


尾矿工人

wrangler tail 仅在操作员被附加时报告 CPU。 尾巴工人也一样 追踪平台内部的事件,持续且没有操作员在场.

也带有 金丝雀。CPU分配结果 整个切片设计依赖于 无证行为:工作停放在一个持久对象引用中,工作恢复在另一个对象引用中 收取的费用 恢复 引证. 如果Cloudflare改变这一点,产品中没有任何失败 大声地——让自己不再适应。 这么说吧 evaluateCanary() 重新检查变量,阈值为 比率 而不是毫秒。 绝对 cpuTime 以colo为单位的不同(一种 一次部署的46毫秒是另一次部署的75毫秒).


🏗 二层

姊妹图书馆 edgeport 使用双层规则: src/core/ 唯一的代码 导入平台原始,每个公共模块都以此为基础. 这个规则不会转移 在这里.

弹匣没有从平台上进口任何东西. grep -rn "from 'cloudflare" src/ 返回无. 其原始内容是: Module 带有 FS - 被立即攻击 消费者 交给我 instantiate,而持久对象状态作为可选对象到达 BlockingContext 上面有一个方法。 所以两层的分裂是用来保护的 已满足于没有要包含的平台导入 .

转让和应用什么:一个包的子路径输出,诚实的 sideEffects 声明,一个统一的出错词汇表,带有逃生舱门的ergonomics,以及配方,每个配方由 一个绿色的光谱.


测试

bun run typecheck
bun run test # in workerd
bun run test:coverage
bun run test:interpreters # 40 assertions against real wasm builds, in node

382通过,99%的发言. 门在下方 @cloudflare/vitest-pool-workers,所以这个 内部执行 工人d 而不是节点,和覆盖使用 provider: 'istanbul' 而不是说 v8。这是一个运行时间事实,而不是偏好:v8提供者从节点读取覆盖 检查员,这些测试 运行在工人的隔离, 所以它属性0 而每次测试 通过.

四个括号,每个括号都诚实地说明可以证明什么:

括号何处证明
行为过假多数规格合同,没有 40 MB 二进制
真实的平台原始mount.spec.ts (真正的gzip,真实) Response, (中文(简体) ), Fetcher(中文(简体) )向上并写入路径到结束
来文方的断言lazy-fs.spec.ts,该部分 mask.spec.ts没有假的可以进入
一个真正的原子弹翻译器tests/interpreters/ (PHP,Lua,CPython,快活JS,Ruby,Java)一个真正的建筑满足 { FS, callMain }

最后一道是唯一能让 ADVANCED_USAGE.md 说 已验证它的建筑是 被钉住 devDependencies 故能修饰之以相接 e2e.yml 每推一推,都有警卫 如果进行零测试或跳过任何测试, 则失败 : 一个默默跳过的道读取为通道 .


仓库这是什么
drupflare/worker消费者:Cloudflare工人上的Drupal 11
drupflare/durabledb进口 /gate 财务报告和审定财务报表 /mask 用于其SQL桥
drupflare/phasm构建解释器,包括 VM 中断补丁口罩驱动器
drupflare/stream-httpưμ㼯A https:// 流包, 在一个被注入的抓取上
drupflare/untarl取出无节点API的焦油和焦油.gz

许可证

MIT (c) 格雷戈里·米切尔 2026. 见 自由主义。 。 。 .

版本与发布记录

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

版本发布日期距今
0.3.1latest2026/10/011 个月前
0.3.02026/09/301 个月前
0.2.02026/08/292 个月前
0.1.32026/08/222 个月前
0.1.22026/08/152 个月前
0.1.12026/08/152 个月前

关于 @drupflare/cartridge 的常见问题

@drupflare/cartridge 是什么?

在 Cloudflare 持久对象中运行一个被屏蔽的被解释器

如何安装 @drupflare/cartridge?

在项目目录运行 npm install @drupflare/cartridge,或使用 pnpm add / yarn add / bun add。

@drupflare/cartridge 支持 TypeScript 吗?

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

@drupflare/cartridge 支持 ESM 还是 CommonJS?

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

@drupflare/cartridge 可以商用吗?

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

@drupflare/cartridge 的下载量有多少?

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

@drupflare/cartridge 还在维护吗?

最新版本 0.3.1 发布于 2026/10/01(1 个月前);至今共发布 7 个版本,其中过去 12 个月发布了 7 个;首次发布于 2026/08/14。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。