# @c7-digital/ledger 中文文档

规范地址：https://npmcn.com/package/%40c7-digital/ledger/

版本：0\.0\.48
npm 元数据更新时间：2026-11-07T04:54:42.678Z
中文译文更新时间：2026-11-08T22:48:07.346Z
软件包许可：未声明

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

原始 npm 页面：https://www.npmjs.com/package/%40c7-digital/ledger
原文对照：https://npmcn.com/package/%40c7-digital/ledger/?lang=original
项目主页：https://github.com/C7-Digital/c7_ledger#readme
源码仓库：https://github.com/C7-Digital/c7_ledger

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

- 包体积（解压后）：3.4 MB，224 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=24\.0\.0
- 运行依赖：4 个
- peer 依赖：yaml ^2\.8\.1，@daml/types \>=3\.4\.0 \<4\.0\.0，openid-client ^6\.5\.0
- 首次发布：2025-12-16T15:08:41.906Z
- 最新版本发布：2026-11-07T04:54:42.386Z
- 近 30 天下载量：2209（2026-10-09 至 2026-11-07，npm 官方下载统计）
- 已发布版本数：48（近 12 个月 48 个）

## 中文简介

OpenAPI v2兼容的数据库分类账客户端

## 安装

```sh
npm install @c7-digital/ledger
```

也可以使用：`pnpm add @c7-digital/ledger`、`yarn add @c7-digital/ledger`、`bun add @c7-digital/ledger`

## README

# @c7-数字/引导器

OpenAPI v2 兼容的 Daml 分类账客户端( 可以替换) `@daml/ledger`为新Canton JSON API v2.

## 概览

此软件包提供了TypeScript客户端,用于与 Canton 的 JSON Ledger API v2 交互,该客户端取代了已贬值的 v1 API `@daml/ledger` 目标.

## 特征

-   **类型安全 API**: 从 OpenAPI 规格生成类型脚本
-   **OpenAPI v2 支持**:瞄准新的Canton JSON API v2终点
-   **自动生成类型**: 用途 `openapi-typescript` 生成准确的类型定义

## 版本

Npm 软件包有自己的 `0.0.x` 版本线,独立于Canton Spec 它的目标。 1.0前,每次发布都是补丁起落;打破的更改也得到一个 `Migrating to 0.0.N` 页:1 Canton 运行时间和 JSON API spec 此客户端的构建对象被单独版本并浮出水面 `SDK_VERSION` (见下文),不作为软件包版本.

这个包船对抗两个独立版本的东西:

-   **JSON 编导 API spec** (表面为 `SDK_VERSION`: `3.6.5-snapshot.20261027`从坎顿 `release-line-3.6`. Splice 0.9.x运行 Canton 3.6.x,这是DevNet,TestNet和MainNet服务的内容. 仅检查此规格( N)`specs/openapi_<version>.yaml` 财务报告和审定财务报表 `specs/asyncapi_<version>.yaml`) (中文(简体) ).
-   **达姆SDK** (单位:千美元)`@daml/types`: 同伴依赖(`>=3.4.0 <4.0.0`),所以应用程序选择其副本. 测试和CI使用3.5.2 Splice 0.9.x 搭配的 SDK.

这个 [已支持的版本表格](https://npmcn.com/package/%40c7-digital/README.md#supported-versions) 列出Canton、Splice和Daml SDK版本的每个版本。 发行对象为Canton一行;不支持旧行.

## 安装

```bash
pnpm install @c7-digital/ledger
```

或者从源头构建:

```bash
pnpm install
pnpm build
```

## 使用量

```typescript
import { Ledger } from "@c7-digital/ledger";

const ledger = new Ledger({
  token: "your-jwt-token",
  httpBaseUrl: "http://localhost:7575",
});

// Query contracts
const contracts = await ledger.query(MyTemplate);

// Create contracts
const result = await ledger.create(MyTemplate, payload, [actAsParty]);

// Exercise choices
const choiceResult = await ledger.exercise(MyChoice, contractId, argument, [actAsParty]);

// Read whole transactions (update id, record time, exercised choices) a page
// at a time; the participant can cut a page short, so loop on reachedOffset.
const participantId = await ledger.getParticipantId();
const end = await ledger.getLedgerEnd();
let offset = 0;
while (offset < end) {
  const page = await ledger.getTransactionPage(
    { beginExclusive: offset, endInclusive: end },
    { parties: [party] },
  );
  for (const tx of page.transactions) console.log(tx.updateId, tx.recordTime);
  offset = page.reachedOffset;
}
```

## 服务账户代号

在分类账上作为服务账户导入的服务器 `@c7-digital/ledger/service-account`。它通过 OAuth 2.0 获得符号 任何OpenID Connecting 发行器提供的客户端证书,它保留了符号 无效。 此切入点与主要切入点分开,原因有两个: 服务器可以保密客户端, 它需要可选的对等依赖 `openid-client` (单位:千美元)`pnpm add openid-client`) (中文(简体) ).

```typescript
import { Ledger } from "@c7-digital/ledger";
import {
  ClientCredentialsTokenSource,
  TokenRefresher,
} from "@c7-digital/ledger/service-account";

const source = new ClientCredentialsTokenSource({
  issuer: new URL("https://auth.example.com/realms/my-realm"),
  clientId: "my-service",
  clientSecret: process.env.CLIENT_SECRET!,
  audience: "https://ledger.example.com", // only if the issuer needs it
});

let ledger: Ledger | null = null;
const refresher = new TokenRefresher({
  source,
  onToken: ({ token }) => ledger?.setToken(token),
  onFailure: ({ error, failures, retryInMs }) =>
    console.warn("token refresh failed", { error, failures, retryInMs }),
});

const { token } = await refresher.start(); // throws if the first token fails
ledger = new Ledger({ token, httpBaseUrl: "https://participant.example.com" });
```

-   `TokenRefresher` 以80%的象征寿命刷新。 失败之后 反转( 5 s, 每次翻倍, 最多60 s) 直至刷新 成功 。 拥有自己标志的流也必须在 `onToken`。该标志过期得太快,无法在过期前刷新 算作失败.
-   `onToken` 财务报告和审定财务报表 `onFailure` 能作同声. 抛出或拒绝承诺 记录下来,时间表不变.
-   `refreshNow()` 立即获得新的代号, 例如分类账拒绝后 当前的符号。 电话在刷新 加入这个刷新.
-   `status()` 给最后的刷新, 下一个刷新, 符号过期 最后一个错误和失败次数,用于健康页面.
-   `stop()` 停止新鲜.
-   `ClientCredentialsTokenSource` 通过发行商找到符号端点 `.well-known/openid-configuration`它把客户的秘密传送到 当发行方支持时请求正文 `client_secret_post`,则在 授权页眉( E)`client_secret_basic`) (中文(简体) ). 除非 `allowInsecureHttp` 设置; 仅为本地发行人设置。 与 `allowInsecureHttp`,一个 http 符号端点必须具有相同的来源 (scheme, host and port)作为发行商,因为客户端的秘密会去它.
-   任何其他 `TokenSource` (对象有: `mint()`与 `TokenRefresher` 我也一样.

## 错误

一个非OK HTTP 响应丢出一个 `LedgerApiError` 拥有状态和状态的 反应体,标记为如何读取:

```typescript
export type LedgerErrorBody =
  | { kind: "canton"; error: JsCantonError }  // a rejection Canton can explain
  | { kind: "json"; value: unknown }          // some other structured error
  | { kind: "text"; text: string }            // a proxy's HTML page, a plain message
  | { kind: "empty" };                        // no body, or unreadable
```

标记记录了一次的决定, 当身体被读取, 所以一个呼叫者做 不从中重新找到它 `typeof` 无法从 JSON 字符串中分辨 一个 HTML 错误页.

`cantonError` 财务报告和审定财务报表 `responseBody` 来源于 `body`,所以他们不能 不同意这一点。 使用 `cantonErrorOf` 达到坎顿有效载荷 解决一个裸载荷和一个钱包门道包装器, 所以一个呼叫覆盖每个 拒绝的路径可以到达.

```typescript
try {
  await ledger.exercise(MyChoice, cid, arg, [party]);
} catch (e) {
  const canton = cantonErrorOf(e);
  if (canton && isRetryable(canton)) return retry();
  if (e instanceof LedgerApiError && e.body.kind === "text") {
    // Markup here means a proxy answered, not the ledger.
  }
}
```

将提交重试/一意孤行,改为Canton's _结构化_ 分类 `errorCategory` 整数和准确 `code`),从不传递消息. 三个 帮助过往 `unknown` 组成运输(`LedgerApiError`()和广州 (单位:千美元)`cantonError`)信号,所以呼叫者不会重新定义:

-   **我应该再试一次吗?** `isRetriableSubmit(e)` - 运输瞬间 (408 / 429 / 5xx) 或州级复议拒绝(`errorCategory` ∈ 参数 / 短暂 / 截止日期 / 搜索- 后端 .
-   **我必须保持不变 `commandId`? 吗?** `isIndeterminateSubmit(e)` - HTTP 503或a 广 `deadline` (第3类) 呈文可能已经犯过错,所以重试 与 **不变** `commandId` 等一会 平地瞬间(429)是安全的 重新尝试新鲜。 检查这个 **在此之前** `isRetriableSubmit`。 。 。 .
-   **我重新交代了没有?** `isAlreadyApplied(e)` —— 说 `DUPLICATE_COMMAND`(单位:千美元) 重试 _不变_ `commandId` 发现参与者已经应用了 之前的尝试。 呼叫者重试不确定的提交文件 稳定 `commandId` 把这当成成功.
-   **我的档案已经没有了?** `isAlreadyArchived(e)` —— 说 `isAlreadyApplied` **或者说** 合同已经不复存在(`CONTRACT_NOT_FOUND`, (中文(简体) ), `LOCAL_VERDICT_INACTIVE_CONTRACTS`) (中文(简体) ). 用于任何归档的应用程序 _政策_ 将它视为成功 留在呼叫者.
-   **确认永久失败?** `isConfirmedPermanentReject(e)` 结构化 坎顿拒绝 坎顿标记是不可重审的 并且还没有存档. 赤出 HTTP 4xx / 纯错误为 **虚假** (因被忽略而关闭).

每一个都要 `unknown` 并阅读结构状态(`httpStatusOf`,还有 因此,它们直接工作在一个 `catch` 约束 - 不 `instanceof` 需要时, 它们在树上活下来了 复制的包裹副本.

```typescript
try {
  await ledger.exercise(Archive, cid, arg, [party]);
} catch (e) {
  if (isAlreadyArchived(e)) return; // idempotent: already gone
  if (isIndeterminateSubmit(e)) return retrySameCommandId();
  if (isRetriableSubmit(e)) return retry();
  if (isConfirmedPermanentReject(e)) return markIgnored(); // domain policy
  throw e;
}
```

`message` 上面有120个字符的体型,所以记录 它不能发出整个错误页。 全部值保持不变 `body`。 。 。 .

### 迁移到0.0.48

添加数 `Ledger.getLatestPrunedOffsets()` 财务报告和审定财务报表 `TypedHttpClient.getLatestPrunedOffsets()`,改为 `GET /v2/state/latest-pruned-offsets`类型 `PrunedOffsets`。读取 刷新参与者的历史必须始于 `participantPrunedUpToInclusive`添加;没有删除.

```typescript
const { participantPrunedUpToInclusive } = await ledger.getLatestPrunedOffsets();
const end = await ledger.getLedgerEnd();
const page = await ledger.getTransactionPage(
  { beginExclusive: participantPrunedUpToInclusive, endInclusive: end },
  { parties: [party] },
);
```

### 移动到 0.045

添加切入点 `@c7-digital/ledger/service-account` (见 [服务账户代号](https://npmcn.com/package/%40c7-digital/ledger/#readme-service-account-tokens): `TokenRefresher`, (中文(简体) ), `ClientCredentialsTokenSource`, (中文(简体) ), `retryDelayMs`类型 `TokenSource`, (中文(简体) ), `MintedToken`, (中文(简体) ), `RefreshFailure`, (中文(简体) ), `TokenRefresherStatus`, (中文(简体) ), `TokenRefresherOptions` 财务报告和审定财务报表 `ClientCredentialsOptions`。 。 。 . `openid-client` 是一个 新的可选同伴依赖,仅此切入点需要。 添加剂; 没有删除.

### 迁移到 0.043

导出请求体建构器 `Ledger.query` 财务报告和审定财务报表 `Ledger.queryInterface` 使用时。 当请求被重复时使用 运输,例如CIP-0103钱包 `ledgerApi` 代理,所以身体已经 和那个一样 `Ledger` 发送。 添加; 没有删除.

-   `activeContractsRequest(parties, filters, activeAtOffset, verbose?)` 正在建设 页:1 `POST /v2/state/active-contracts` 躯体。 派对过滤器在 `eventFormat`3.6州参与者拒绝贬值的最高级别 `filter` 默认(`DEPRECATED_API_DISABLED`) (中文(简体) ).
-   `templateFilter(templateId, includeCreatedEventBlob?)` 财务报告和审定财务报表 `interfaceFilter(interfaceId, includeCreatedEventBlob?)` 构建 `IdentifierFilter`s. 界面过滤器总是询问界面视图.
-   类型 `IdentifierFilter` 财务报告和审定财务报表 `GetActiveContractsRequest` 导出.

```typescript
import { activeContractsRequest, templateFilter } from "@c7-digital/ledger/lite";

const body = activeContractsRequest([party], [templateFilter(Token.templateId)], offset);
```

### 迁移到 0.042

增加更新历史和参与者阅读。 添加; 没有删除.

-   `Ledger.getTransactionPage(range, { parties, shape, limit })` 返回整个 交易 `(beginExclusive, endInclusive]` (默认形状:分类账) 影响,因此,存在已行使的选择) `reachedOffset` 将恢复 从.
-   `Ledger.getParticipantId()` 财务报告和审定财务报表 `TypedHttpClient.getParticipantId()` 读取 `GET /v2/parties/participant-id`。 。 。 .
-   `TypedHttpClient.getUpdates(request, { limit })` 发送 `POST /v2/updates`。 。 。 .
-   `Ledger.getLedgerEnd()` 现为公用(它是私用) 一秒缓存.

### 迁移到 0.041

查询时间 `"end"` 现在看一样 `Ledger` 例本身的写作。 A 类 已经成功 `create`, (中文(简体) ), `exercise`, (中文(简体) ), `exerciseResult`, (中文(简体) ), `submit` 或者说 `submitWithDisclosures` 将缓存分类账端移动到 页:1 之前,一秒钟的分类账端缓存可以提供 因此,查询仍然可以返回合同中 书信存档了 无 API 更改 .

### 移动到 0.0.40

添加数 `isConfirmedPermanentReject(e)` 结构化的州不可复辩拒绝, 不包括已经存档的无操作程序。 短程运输 4xx / 平面误差 `false` 所以,忽略和处理 呼叫者失败关闭。 添加; 没有删除.

### 迁移到 0.0.39

添加数 `isAlreadyApplied(e)` —— 说 `DUPLICATE_COMMAND` 单独( 已经命令) (已判决),对于尝试无常者 `commandId` 并且必须把破解的打击视为成功. `isAlreadyArchived` 现在 编曲:`isAlreadyApplied` ——行为相同,没有改变 它的召唤者。 添加; 没有删除.

### 迁移到 0.038

添加结构化,可重复使用的提交分类器,使消费者停止手卷: `isRetriableSubmit`, (中文(简体) ), `isIndeterminateSubmit`,以及 `isAlreadyArchived` (见上文), 加号 `httpStatusOf` 读取一个结构状态。 全部读取 `cantonError`是因为 `code` 编号 : `errorCategory` 和 HTTP 状态 — 没有信件字符串匹配 .

短暂的人生 `LedgerApiError.isTransient()` 编号 : `.isIndeterminate()` 方法 (0.0.37)为: **删除** 一种方法迫使 `instanceof` 检查呼叫站点, 该站点在复制的软件包副本中失败 也是自由函数所避免的准确反模式.

```typescript
// 0.0.37
if (e instanceof LedgerApiError && e.isIndeterminate()) retrySameCommandId();
// 0.0.38
if (isIndeterminateSubmit(e)) retrySameCommandId();
```

### 迁移到 0.037

这个 `submitError` 帮助者( e)`isArchiveAlreadyDoneError`, (中文(简体) ), `isIndeterminateArchiveTimeout`, (中文(简体) ), `isIndeterminateSubmitError`, (中文(简体) ), `isLockedContractsError`, (中文(简体) ), `isTransientArchiveError`页:1 他们重复了 `cantonError` ——取而代之的是Canton的分类:

```typescript
// before
if (isTransientArchiveError(e)) retry();
if (isIndeterminateSubmitError(e)) retrySameCommandId();

// after (0.0.38 free functions)
if (isIndeterminateSubmit(e)) retrySameCommandId();
else if (isRetriableSubmit(e)) retry();
```

合同缺失或闲置是否成功,是征召人政策—— `isAlreadyArchived(e)` 报告事实; 呼叫者决定停止作为成功.

### 迁移到 0.034

这个 `LedgerApiError` 构造器需要标记 `LedgerErrorBody` 而不是一个 未标记 `unknown`。读取错误时没有变化 `status`, (中文(简体) ), `statusText`, (中文(简体) ), `cantonError`,以及 `responseBody` 都像以前一样 只有密码 _构造_ 在实际操作中,它意味着测试和模拟,需要更新:

```typescript
new LedgerApiError(404, "Not Found", cantonPayload)      // before
new LedgerApiError(404, "Not Found", { kind: "canton", error: cantonPayload })

new LedgerApiError(403, "Forbidden", "<html>…</html>")   // before
new LedgerApiError(403, "Forbidden", { kind: "text", text: "<html>…</html>" })
```

二辩相形式仍指"无体"并需要改变.

`ScanApiError` 内 `@c7-digital/scan` 同样的 `status`, (中文(简体) ), `statusText`, (中文(简体) ), `body`,以及 `responseBody`,所以一个 `catch` 读取一个集 田间。 没有 `canton` 案例.

## 从@daml/ledger 迁移

关键差异 :

-   `create()` 财务报告和审定财务报表 `exercise()` 现在需要一种 `actAs` 参数
-   一些方法签名已经更新以匹配 OpenAPI v2 spec
-   根据 OpenAPI 规格生成的新类型定义

## 发展

```bash
# Build the package
pnpm build

# Clean build artifacts
pnpm clean

# Watch for changes
pnpm watch

# Run tests
pnpm test

# Lint code
pnpm lint
```

## OpenAPI 规格

该套件采用了由Canton公司提供的JSON Ledger API规格,作为销售商 `specs/openapi_<canton-version>.yaml` 财务报告和审定财务报表 `specs/asyncapi_<canton-version>.yaml`。该版本编码了Canton运行时的发布.

要搬入新的Canton线:

1.  获取网络运行的Canton线的规格. 两道道:
    -   **从广东来的** (两个文件):在 `release-line-<major.minor>` (或最新数据) `<major.minor>-open-source-<date>` 树枝 树枝 树枝 `community/ledger/ledger-json-api/src/test/resources/json-api-docs/{openapi,asyncapi}.yaml`。 Splice 只销售 OpenAPI 文件, 下 `canton/` 它的释放线。 用它来检查 路径和计划一致.
    -   **从现场参与者**: 卷轴 `http://<participant>:<json-api-port>/docs/openapi` 财务报告和审定财务报表 `/docs/asyncapi`。 。 。 .
2.  替换文件为 `ledger/specs/` 与 `{openapi,asyncapi}_<canton-version>.yaml`,并删除上一对.
3.  更改构建脚本 `--sdk-version=<version>` 标记为 `ledger/package.json` (旗帜名称为历史;它选择了 spec 文件)和预期 `SDK_VERSION` 内 `src/api-surface.test.ts`。 。 。 .
4.  添加一行到 [已支持的版本表格](https://npmcn.com/package/%40c7-digital/README.md#supported-versions)。 。 。 .
5.  运行 `pnpm build` 用于再生类型.

## 常见问题

### @c7-digital/ledger 是什么？

OpenAPI v2兼容的数据库分类账客户端

### 如何安装 @c7-digital/ledger？

在项目目录运行 npm install @c7-digital/ledger，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：@daml/types（\>=3\.4\.0 \<4\.0\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。它还提供命令行工具 ledger-build，可以用 npx ledger-build 直接运行。

### @c7-digital/ledger 支持 TypeScript 吗？

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

### @c7-digital/ledger 支持 ESM 还是 CommonJS？

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

### @c7-digital/ledger 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=24\.0\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @c7-digital/ledger 可以商用吗？

@c7-digital/ledger 使用 未声明 许可。package\.json 没有声明许可证。没有许可证的代码默认保留全部权利，商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括，不构成法律意见，请以许可原文为准。

### @c7-digital/ledger 的下载量有多少？

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

### @c7-digital/ledger 还在维护吗？

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

