@c7-digital/ledger

中文文档
0.0.48发布于 2026/11/07TypeScriptES Module构建来源已验证近 30 天下载 2209

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

中文使用文档
文档目录20 节
  1. 概览
  2. 特征
  3. 版本
  4. 安装
  5. 使用量
  6. 服务账户代号
  7. 错误
  8. 迁移到0.0.48
  9. 移动到 0.045
  10. 迁移到 0.043
  11. 迁移到 0.042
  12. 迁移到 0.041
  13. 移动到 0.0.40
  14. 迁移到 0.0.39
  15. 迁移到 0.038
  16. 迁移到 0.037
  17. 迁移到 0.034
  18. 从@daml/ledger 迁移
  19. 发展
  20. OpenAPI 规格

@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.

这个 已支持的版本表格 列出Canton、Splice和Daml SDK版本的每个版本。 发行对象为Canton一行;不支持旧行.

安装

pnpm install @c7-digital/ledger

或者从源头构建:

pnpm install
pnpm build

使用量

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) (中文(简体) ).

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 拥有状态和状态的 反应体,标记为如何读取:

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 达到坎顿有效载荷 解决一个裸载荷和一个钱包门道包装器, 所以一个呼叫覆盖每个 拒绝的路径可以到达.

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 需要时, 它们在树上活下来了 复制的包裹副本.

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添加;没有删除.

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 (见 服务账户代号: 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 躯体。 派对过滤器在 eventFormat3.6州参与者拒绝贬值的最高级别 filter 默认(DEPRECATED_API_DISABLED) (中文(简体) ).
  • templateFilter(templateId, includeCreatedEventBlob?) 财务报告和审定财务报表 interfaceFilter(interfaceId, includeCreatedEventBlob?) 构建 IdentifierFilters. 界面过滤器总是询问界面视图.
  • 类型 IdentifierFilter 财务报告和审定财务报表 GetActiveContractsRequest 导出.
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 检查呼叫站点, 该站点在复制的软件包副本中失败 也是自由函数所避免的准确反模式.

// 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的分类:

// 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 都像以前一样 只有密码 构造 在实际操作中,它意味着测试和模拟,需要更新:

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 规格生成的新类型定义

发展

# 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. 添加一行到 已支持的版本表格。 。 。 .
  5. 运行 pnpm build 用于再生类型.

版本与发布记录

@c7-digital/ledger 共发布 48 个版本,首次发布于 2025/12/16;过去 12 个月发布了 48 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.0.48latest2026/11/071 天前
0.0.472026/11/045 天前
0.0.462026/11/026 天前
0.0.452026/11/018 天前
0.0.442026/10/2416 天前
0.0.432026/10/1624 天前

关于 @c7-digital/ledger 的常见问题

@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 和提交记录判断。