# @absolutejs/metering 中文文档

规范地址：https://npmcn.com/package/%40absolutejs/metering/

版本：0\.8\.0
npm 元数据更新时间：2026-10-22T06:40:25.795Z
中文译文更新时间：2026-11-08T08:47:07.437Z
软件包许可：BSL-1\.1

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

原始 npm 页面：https://www.npmjs.com/package/%40absolutejs/metering
原文对照：https://npmcn.com/package/%40absolutejs/metering/?lang=original
项目主页：https://github.com/absolutejs/metering
源码仓库：https://github.com/absolutejs/metering

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

- 包体积（解压后）：583 kB，20 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：2 个
- peer 依赖：elysia ^2\.0\.0-rc\.0，bun-types ^1\.3\.14
- 首次发布：2026-05-29T19:09:03.078Z
- 最新版本发布：2026-10-22T06:40:20.334Z
- 近 30 天下载量：1844（2026-10-05 至 2026-11-03，npm 官方下载统计）
- 已发布版本数：12（近 12 个月 12 个）

## 中文简介

租户成本分配+多租户运行时间预算执行. 从@absolutejs/sync和产卵/idle/出产事件到@absolutejs/runtime; 滚动 CPU-ms、字节-egress、休眠-GB-seconds; 每名租户的行程

## 安装

```sh
npm install @absolutejs/metering
```

也可以使用：`pnpm add @absolutejs/metering`、`yarn add @absolutejs/metering`、`bun add @absolutejs/metering`

## README

# @absolutejs/metering (帮助)

租户付费+多租户Bun运行时间的预算执行.

为在一个主机下运行许多小型 Bun 应用程序的 PaaS 供应商所建. 消费 `handlerMetrics` 从 [`@absolutejs/sync`](https://github.com/absolutejs/sync) 和生命周期事件 [`@absolutejs/runtime`](https://github.com/absolutejs/runtime), (中文(简体) ), 每个租户翻来翻去 任何租户一到就开断路器 超出预算范围。 运行时间和运行时间之间的库 SB-6 层 下游的收费/可观察管道.

```ts
import { createMeter, consoleSink } from "@absolutejs/metering";

const meter = createMeter({
  sinks: [consoleSink, influxSink],
  budgets: {
    "*": { cpuMs: 60_000, requests: 10_000 }, // free-tier default
    "acme-prod": { cpuMs: 600_000, requests: 1_000_000 }, // paid override
  },
  onBreach: ({ tenant, dimension, observed, limit }) => {
    suspendAtRouter(tenant, { dimension, observed, limit });
  },
});

// Wire it into a sync engine: sync handlerMetrics records → meter.record(...)
syncEngine.handlerMetrics = (record) => {
  meter.record({
    type: "handler",
    tenant: currentTenantId(),
    mutationName: record.mutationName,
    durationMs: record.durationMs,
    cpuMs: record.cpuMs,
    heapBytes: record.heapBytes,
    ok: record.ok,
    errorName: record.errorName,
  });
};

// And a runtime: spawn/idle-kill/exit transitions → meter.record(...)
runtime.options.onTransition = (event) => {
  meter.record({
    type: "process",
    tenant: event.key,
    transition: event.type,
    durationMs: event.durationMs,
  });
};

// And @absolutejs/runtime@0.1.0's Linux observation events:
runtime.options.onMetrics = (event) => {
  if (event.type === "observation") {
    meter.record({
      type: "observation",
      tenant: event.key,
      cpuMs: event.cpuMs,
      rssBytes: event.rssBytes,
      at: event.at,
    });
  }
};

// Record one terminal AI agent run. `costMicros` is caller-calculated so model
// pricing stays an operator policy instead of becoming stale library data.
meter.record({
  type: "ai",
  tenant: tenantId,
  requestId,
  provider: "anthropic",
  model: "claude-sonnet-4-6",
  inputTokens: finish.usage.inputTokens,
  outputTokens: finish.usage.outputTokens,
  cacheReadInputTokens: finish.usage.cacheReadInputTokens,
  cacheWriteInputTokens: finish.usage.cacheWriteInputTokens,
  durationMs: finish.durationMs,
  turns: finish.turns,
  costMicros,
  ok: finish.reason === "complete",
  stopReason: finish.reason,
});

// Inside a hosted workload, hand handler/request events to the authenticated
// host-local collector. The PaaS injects this endpoint, token, and per-process
// source id; no tenant id is accepted from application code.
import { createWorkloadMeterReporter } from "@absolutejs/metering/reporter";
import { workloadMeterElysia } from "@absolutejs/metering/elysia";

const reporter = createWorkloadMeterReporter({
  endpoint: process.env.ABSOLUTE_METER_ENDPOINT!,
  sourceId: process.env.ABSOLUTE_METER_SOURCE_ID!,
  token: process.env.ABSOLUTE_METER_TOKEN!,
});
const app = new Elysia().use(workloadMeterElysia({ reporter }));
syncEngine.handlerMetrics = reporter.handlerMetrics;

// In your request handler, gate on the meter:
if (!meter.allow(tenantId))
  return new Response("Quota exceeded", { status: 429 });
```

## 水面 (0.2.0) (中文(简体) )

| API 密码 | 目的 |
| --- | --- |
| `createMeter(options)` | 工厂。 回返a `Meter`。 。 。 . |
| `meter.record(event)` | 接受一个 `MeterEvent` —— 说 `handler`, (中文(简体) ), `process`, (中文(简体) ), `observation`,或 `ai`。更新滚动, 扇出到下沉, 可能绊倒断路器. |
| `meter.allow(tenant)` | 机前出门. 回返 `false` 如果任何累积预算跌倒,任何滚动窗口规则目前已经结束,或 `reset()` 粘稠的累计旅行之后还没有被叫来. |
| `meter.usage(tenant)` | 卷起的快照: `cpuMs`, (中文(简体) ), `processCpuMs`, (中文(简体) ), `bytesEgress`, (中文(简体) ), `hibernationGbSeconds`, (中文(简体) ), `processRssBytesPeak`等 (中文(简体) ). |
| `meter.rollingSum(tenant, dimension, windowMs)` | 目前滚动窗口总计 `(tenant, dimension, window)`对于客户端“您在此窗口中保留了 N 请求” 显示. |
| `meter.rollingFor(tenant)` | 积极滚动规则. |
| `meter.reset(tenant)` | 清除一个累积断路器,而不将累积的用法零化。 滚窗出行 自动清空 当事件排出. |
| `meter.clear(tenant)` | 零积用并清取出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出. |
| `meter.tenants()` | 至今为止,每个房客都见过. |
| `meter.budget(tenant)` | 积极累计预算. |
| `meter.tripped(tenant)` | 重新评价滚动规则;称它可以解开窗户已耗尽的租户的电话. |
| `meter.snapshot()` 编号 : `restore(snap)` | 相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相接相相接相相接相接相接相相相相相相相相接相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相 活生生硬地重启;账单不重置为零. |
| `meter.dispose()` | 等待每一个水槽 `flush?`,则 `close?`。 。 。 . |

### 银色

A 类 `MeterSink` 要么是一个函数 `(event) => void | Promise<void>` 或对象 `{ ingest, flush?, close? }`辛克被按顺序扇出. **掷出或 从一个汇中拒绝不会停止以后的汇** - 计分器在 收费的关键路径。 错误被登录到 stderr; 记录器继续运行 .

打开 `dispose()`,每个物体形状的槽 `flush()` 正在等待 (横跨 然后是每个 `close()` 正在等待中。 倒冲被记录为+被吞; 之后的汇仍然被冲走. 这就是批量适配器(Stripe, Influx, ClickHouse) 在关闭时不要放弃最后几起事件.

捆绑: `consoleSink`. 后作为 " 通流 " / " 普罗米修斯 " / " 花纹 " 船的适配器 兄弟姐妹的包裹.

### 累计预算

`budgets['*']` 是默认的;按租户计算的条目将覆盖它。 任意维度 击出其极限出行的断裂器; `onBreach` 火灾 **每次旅行一次** (电话) `reset()` 重新武装。 之后的事件仍在积累——法案保留 即使门关了 也还是会不断增长 这和真正的收费方法相符.

尺寸 : `cpuMs`, (中文(简体) ), `processCpuMs`, (中文(简体) ), `bytesEgress`, (中文(简体) ), `requests`, (中文(简体) ), `errors`, (中文(简体) ), `hibernationGbSeconds`, (中文(简体) ), `aiRequests`, (中文(简体) ), `aiInputTokens`, (中文(简体) ), `aiOutputTokens`, (中文(简体) ), `aiCostMicros`。 。 。 .

### 滚动窗口预算

```ts
createMeter({
  rollingBudgets: {
    "*": [
      { dimension: "errors", windowMs: 5 * 60_000, limit: 50 }, // 50 errors / 5 min trips the breaker
      { dimension: "requests", windowMs: 1 * 60_000, limit: 1_000 }, // 1k req / min rate cap
    ],
    "acme-prod": [
      { dimension: "cpuMs", windowMs: 60_000, limit: 50_000 }, // 50s sandbox CPU / minute
    ],
  },
});
```

滚动金额到达时的滚动窗口规则出行 `limit`。 。 。 . **它会关闭 自动** 由于事件排出窗口——没有 `reset()` 需要帮助。 这是 与累计预算的差额,该差额一直持续到 `reset()`两者 (a) 可在同一租户上设置各种类型; `allow()` 实值 `false` 如果有任何规则旅行.

### 观察会计

`@absolutejs/runtime@0.1.0` 发射 `{ type: 'observation', cpuMs, rssBytes }` 打开 一个可配置的间隔。 计数器处理 `cpuMs` 自产卵以来的共性 从上次观察开始就把三角洲充电 A 类 `process` 事件 `transition === 'spawn'`, (中文(简体) ), `'adopt'`, (中文(简体) ), `'restored'`,或 `'exit'` 重新设置 因此,新程序或新监管程序不会重复收费。 仅a 真实 `spawn` 增加产卵计数器.

### 休眠会计

`@absolutejs/runtime` 发射 `idle-kill` 编号 : `lru-evict` 转换; 计量 电话员负责计算 GB 秒的租户的架子,同时 冬眠并传入 `hibernationGbSeconds` 编辑 `process` 活动. 计数器总结了它看到的值——它没有推断出这些值.

### 抓图+还原

```ts
const json = JSON.stringify(meter.snapshot());
await persistToDisk('/var/lib/meter/state.json', json);

// On shard restart:
const restored = createMeter({ ... same config ... });
restored.restore(JSON.parse(await readFromDisk('/var/lib/meter/state.json')));
```

该快照捕捉每个租户的用法,被绊倒的状态,滚动的窗口状态, 和最后观测基准,因此下一次观测 a 明智的三角洲,而不是跳到 累积的- 自进程启动值.

## 建筑作用

-   **`@absolutejs/sync`** - 发射 `handlerMetrics` 记录每个沙盒变异.
-   **`@absolutejs/runtime`** - 在每个产卵上发出生命周期事件/闲置杀死/退出.
-   **`@absolutejs/metering`** —— 说 _这个图书馆_将每个租户的门翻出.
-   **`@absolutejs/router`** (计划)——消费 `meter.allow()` 拒绝交通 超量租户在边缘.

## 许可证

BSL 1.1为主机多租户计量/成本分配/每租户计费类别(Stripe Metered Billing, Orb, Metronome, Lago, Amberflo, Cloudflare Workers计费, Convex使用仪表板,Vercel使用仪表板)设置了命名的例外. 见 [自由主义](https://npmcn.com/package/%40absolutejs/metering/LICENSE). 更改日期: 4 从首次发布起; 更改许可证: Apache 2.0.

## 常见问题

### @absolutejs/metering 是什么？

租户成本分配\+多租户运行时间预算执行\. 从@absolutejs/sync和产卵/idle/出产事件到@absolutejs/runtime; 滚动 CPU-ms、字节-egress、休眠-GB-seconds; 每名租户的行程

### 如何安装 @absolutejs/metering？

在项目目录运行 npm install @absolutejs/metering，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：bun-types（^1\.3\.14）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @absolutejs/metering 支持 TypeScript 吗？

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

### @absolutejs/metering 支持 ESM 还是 CommonJS？

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

### @absolutejs/metering 可以商用吗？

@absolutejs/metering 使用 BSL-1\.1 许可。声明的许可为 BSL-1\.1，请阅读许可原文确认使用条件。以上为概括，不构成法律意见，请以许可原文为准。

### @absolutejs/metering 的下载量有多少？

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

### @absolutejs/metering 还在维护吗？

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

