@candid-core/schema

中文文档
0.5.0发布于 2026/10/27Apache-2.0TypeScriptES Module零依赖构建来源已验证近 30 天下载 2131

Candid的Zod-style schema runtime:具有静态推论,结构验证功能的建设者,一个TypeScript-native 二进制编码器,以及一个从坦率的Canonical Contract文档中构建出相同计划的加载器.

此软件包已被标记为弃用

Renamed to @meantjs/schema

中文使用文档
文档目录9 节
  1. 校长和 @icp-sdk/core
  2. 主要价值
  3. 从a .did 文件
  4. 服务及菌类参考文献
  5. 打开 OK/err 结果
  6. 支持矩阵
  7. 域形( 有意决定 )
  8. 核查
  9. 证明

@ candid- core/ schema

一个 Zod 风格的运行时间 不错,驱动器 实心条子 合同模型:具有静态推论、结构验证、, a TypeScript-native Candid 二进制解码器, 以及一个装入器, 构造相同的 运行时从合同文件中发出的计划。 只有坎迪地层—— 呼叫弹匣( 运输、 身份、 演员) 属于您的层 在上面继续发展(下级) (中文(简体) ).

npm install --save-exact @candid-core/schema

这就是整个安装:没有运行时间依赖关系,也没有同行,主 处理包括(主要价值 页:1.

0.5.0,无 latest 释放. 这个README描述为0.5.0. 升级 从0.4.0起,在运行时间没有任何变化,只有一条省略规则: schemaFromContract,并仅解码于 类型级别 : decode 财务报告和审定财务报表 decodeArgs 从 ./codec 现在输入其 因此,从解码值变为 键入其计划不描述停止编译。 移动两个软件包 组合: @candid-core/cli 0.4.0对 与准确的这个释放. 从0.3.0升级到仅添加类型 WithMode 财务报告和审定财务报表 ModeOf,该模块 cLI自0.3.0使用后生成. 从0.2.0升级是一次突破 变化:0.2.0也出口 ./actor, (中文(简体) ), ./transport-icp, (中文(简体) ), ./forms 财务报告和审定财务报表 ./labels,宣布可选 @icp-sdk/core 端点,键入主机为 { toText(): string },以及 拒绝 opt opt T, (中文(简体) ), opt null 财务报告和审定财务报表 opt reserved保留 --save-exact数字 : 包是1.0前的,所以任何微小放出都可能打破下一个,以及 @candid-core/cli 和它完全一个释放.

@candid-core/schema 0.3 打破 0.2 API(主值为条形文字,倒塌的选项为框式,每 vec nat8 是一个 Uint8Array,生成的模块使用新的绑定布局,并可能省略声明,以及 ./actor, (中文(简体) ), ./transport-icp, (中文(简体) ), ./forms 财务报告和审定财务报表 ./labels 子路径已去; 没有兼容层 .

每一变化都记录在 更改日志,哪些船舶 在焦油丸中:自0.4.0下起 0.5.0,自0.3.0下 0.5.0 财务报告和审定财务报表 0.4.0,并且从0.2.0 这些, 0.3.0 财务报告和审定财务报表 0.3.0-beta.1编辑 移徙说明 从0.4.0起, (中文(简体) ), 从0.3.0 财务报告和审定财务报表 从0.2.0 显示每个断开对您的代码意味着什么 编译器显示它.

import { c, principal, type Infer } from "@candid-core/schema";
import { validate } from "@candid-core/schema/validate";
import { encode, decode } from "@candid-core/schema/codec";

const Account = c.record({ owner: c.principal, balance: c.nat });
type Account = Infer<typeof Account>; // { owner: Principal; balance: bigint }

const value: Account = { owner: principal("aaaaa-aa"), balance: 5n };

validate(Account, value); // { ok: true } | { ok: false, issues }
const encoded = encode(Account, value); // { ok: true, bytes } | { ok: false, issues }
if (encoded.ok) {
  decode(Account, encoded.bytes); // { ok: true, value: Account } | { ok: false, issues }
}

模块, 每个子路径导出 :

  • . - 计划核心: c 建筑师, Schema<in out T> (故意 以无常者发誓, Infer,而且节点接口将行走者缩小到-+ resolveSchema 财务报告和审定财务报表 serviceMethods 读回一个,因为一个计划 名称为 rec 间接与服务的方法是一个表, 编辑 Principal 类型及其 principal 财务报告和审定财务报表 isPrincipal 函数.
  • ./validate ——有界限,已失效的结构验证;从未投出 任何值上(密钥未知的选项对象或不是 非负式安全整数抛出 TypeError,在每个子路径中:选项是 使用稳定代码和 $- 根深蒂固的道路——加 isResultSchema 财务报告和审定财务报表 unwrapResult,计划指导读取 variant { ok; err } 公约 下级。 。 。 .
  • ./contract - 运行时从峡谷建造同样的计划 JSON合同文件——或单一文件 ContractEnvelope 携带 其散列强制字段名表.
  • ./codec —— Candid 二进制线条格式, 有图案导向, 有 斯克的强制关系 在解码和明确的资源预算。 已验证 双向对照参考执行的向量。 编码 字节仅依赖于数值和 Candid 类型,绝不取决于是否 计划是生成、装入或手工建造的.

validate, (中文(简体) ), encode 财务报告和审定财务报表 decode 把他们的工作放在明显的堆栈上,而不是 JavaScript 调用堆栈, 因此配置的限制是他们唯一的界限 : 默认 maxDepth * 256人在工作中拒绝敌对的深度价值或信息 和这个极限成正比, 一个值得信赖的宿主 升起它可以走一个 10万级链接列表或ICRC-3无论引擎的堆栈大小值—— 一个工人的小堆积包括——每通电话都有同样的答案.

这四个是整个导出地图: 没有别的了,一条深入 dist/ 包含,可导入.

校长和 @icp-sdk/core

此软件包不依赖于 @icp-sdk/core ——非运行时间,不在其 声明,而不是作为对等者——因此,所有编译和运行时没有安装SDK,严格地说 类型脚本 skipLibCheck 不包含。 校长就是他的神话 文本,所以 SDK 值在您自己的边界上转换一次,与 principal()。 。 。 .

主要价值

c.principal 类型为 Principal从根导出 : 主文本作为品牌字符串——CRC-32校验和的小写基础32和 id 字节,破折叠组合为5个,例如 "ryjl3-tyaaa-aaaaa-aaaba-cai"。 。 。 . 这个 principal 服务参考文献 Principal 我也一样。 这是双向合同:

  • 你得到的东西. 解码返回条形文字本身——平面 运行时的字符串 。 因此,解码答复仍然有效 JSON.stringify 联合国 主要序列化作为其文本), structuredClone 财务报告和审定财务报表 postMessage, (中文(简体) ), 比较与 ===,并起到 Map 键或缓存键内。 没错 不是一个SDK Principal 实例; 需要类转换的代码 sDK的车 Principal.fromText(value)。 。 。 .
  • 尽能所施. 没错 Principal验证和编码 接受一个其文字是空洞而拒绝其他一切的字符串—— 非词性拼写,对象为 toText()一个SDK Principal 实例——与 invalid_type,两个同意的案件。 做一个 与 principal(),该文本或任何东西 toText()数字 :
import { c, isPrincipal, principal, type Principal } from "@candid-core/schema";
import { encode } from "@candid-core/schema/codec";

declare const sdkPrincipal: { toText(): string }; // an @icp-sdk/core Principal, say

const owner: Principal = principal(sdkPrincipal); // once, at your boundary
const ledger = principal("ryjl3-tyaaa-aaaaa-aaaba-cai");

isPrincipal("aaaaa-aa"); // true: the management canister
isPrincipal("AAAAA-AA"); // false: not canonical
encode(c.record({ owner: c.principal }), { owner }); // { ok: true, bytes }
void ledger;

principal() 拒绝而不是修复。 文字必须已经是空洞的 – 小写,每五个字符后一个破折号,别的地方,匹配 校验和, 最后一个字符中的零补丁比特, 最多29字节的代号 - 或者它会扔出 TypeError;既不是一个字符串也不是一个 对象为 toText()。 。 。 . "aaaaa-aa" (管理罐)和 "2vxsx-fae" (匿名校长)是犬科; "AAAAA-AA" 财务报告和审定财务报表 "aaaaaaa" 没有. isPrincipal 是匹配的守护者, 相同的检查 validate 财务报告和审定财务报表 encode 应用。 该品牌只存在于类型系统中,所以 字符串不能代表主机, 没有该检查, 没有运行时间 费用.

这个名字和SDK的班级一致 输入两个别名的代码: import type { Principal as CandidPrincipal } from "@candid-core/schema"。 。 。 .

从a .did 文件

两条路由编译 Candid 源码到文档 schemaFromContract 消耗,两者都产生生成的模块所携带的精确的计数.

没有Rust工具链, @candid-core/cli 运行与 Web Assembly 相同的编译器和生成器。 它写出生成的 模块和a ContractEnvelope 文档, ./generated/service.envelope.json, (中文(简体) ), 的字节与 compile --envelope 输出如下:

# The CLI release that pairs with this one: it peers exactly 0.5.0.
npx @candid-core/[email protected] gen ./service.did -o ./generated

有"一箱一箱"是两个命令 安装编译器, meant,哪个 发表于 candid-core 在它重新命名之前。 这是1.0前,所以针 版本此发行对 :

cargo install meant --version 0.1.0 --locked
meant compile ./service.did --envelope > ./service.json

compile --envelope 打印一个自定义文档: a ContractEnvelope 守住峡谷 contract 加一个 extensions 地图 org.candid-core.field-names/v1 条目带有字段名称表。 A 类 合同仓库权威字段标签 编号,而不是文本,所以名称 旅行副带 -- -- 和信封扩展部分在运河外直播 设计身份,所以携带他们 永远不会移动一个 contract_id。 。 。 . schemaFromContract 消耗整个文档 :

import { readFileSync } from "node:fs";
import { schemaFromContract } from "@candid-core/schema/contract";

const built = schemaFromContract(JSON.parse(readFileSync("./service.json", "utf8")));
if (!built.ok) {
  throw new Error(JSON.stringify(built.issues));
}

built.schemas.Account; // one Schema per declaration, in declaration order
built.actor; // the service schema, when the document has one
built.omitted; // what was left out, and why — the list the generator reports

二文件流也起作用:平原 compile (无) --envelope)指纹 { ok, contract, source_info }名称 [container, id, name] 三进 source_info.field_labels - 其 label.kind 实值 "named" - 通过为 页:1 names 选项与赤色 contract数字 :

import { schemaFromContract, type FieldNameEntry } from "@candid-core/schema/contract";

declare const compiled: { contract: unknown };
declare const names: FieldNameEntry[];

schemaFromContract(compiled.contract, { names });

这两条路线都产生同样的计划 和明确的 names 选项总是胜过带信封的名字——信封的表格 然后就完全不咨询.

schemas 准确持有申报 生成的模块输出 同一文件。 无法代表生成模块的声明被忽略, 利用每一个宣言和行动者方法——通过巢穴 func 财务报告和审定财务报表 service 类型,直到包含的宣布,并列出 内 omitted 作为 { kind, name, reason, via? },按生成器顺序和 及其理由代码: reserved_field_name (一个真正命名为 _N_ id渲染,作为键会读回错误的线id, ambiguous_variant_arm, (中文(简体) ), reserved_export_name (以名称命名的声明) actor 或者说 Actor, (中文(简体) ), invalid_declaration_name (名称不是) 标识符形状),以及 references_omitted,其 via 名称省略 引用声明。 演员从不省略;只输方法 列出。 无效的文档仍然失败, 与 issues。 。 。 .

位置和数字标签无名称并被跳过;这些字段 由生态系统的 _id_ 公约,恰好是生成者制造的 他们。 每个名字——信封携带或呼叫者提供相同——都是 hash-enforce:它必须是其id的 Candid 假象,所以一个表是 关闭失败, 而不是悄悄重命名一个字段 。 完全没有通过桌子 使每个领域成为 _id_,这也是什么 compile --no-source-info 离开你.

生成的模块导入此软件包为命名空间 $,各声明 名称下的宣布类型,并绑定其计划 $- 前缀的本地出口名称,所以没有Candid 名称 - c, (中文(简体) ), Array, (中文(简体) ), delete - 与模块本身的绑定相撞,您导入 你写的名字( e)import { Tokens, actor, type Actor } from "./ledger"),以及 一个编译器也给它们取了名。 无法成为模块类型的名称 (单位:千美元)Array, (中文(简体) ), delete保留 $ 本地类型。 这个 .did医生 注释和参数名称在导出类型上成为 JSDoc, 有记录 属性和可变武器,以及 Actor和每个 vec nat8 是一个 Uint8Array,但是其元素类型被命名.

服务及菌类参考文献

A 类 service schema 描述一个罐子界面;它不叫一个. serviceMethods 读取其方法表——名称、模式、参数和结果 schemas——和代码将每个调用时的论据和回音转换为 Candidd 字节,这是调用层从此软件包中需要的:

import { c, principal, serviceMethods } from "@candid-core/schema";
import { encodeArgs, decodeArgs } from "@candid-core/schema/codec";

const Account = c.record({ owner: c.principal, subaccount: c.opt(c.vec(c.nat8)) });
const Ledger = c.service({
  balance_of: c.func([Account], [c.nat], "query"),
});

const method = serviceMethods(Ledger).get("balance_of");
if (method !== undefined) {
  const request = encodeArgs(method.args, [
    { owner: principal("aaaaa-aa"), subaccount: null },
  ]);
  // `request.bytes` is the argument a transport sends on the `method.mode`
  // path; the reply's bytes decode with `decodeArgs(method.results, reply)`.
  void request;
  void decodeArgs;
}

请求字节是一个声音缓存或分解键:类型表是 从 Candid 类型结构所建, 所以一个调用编码到相同的字节 其计划是否来自生成的模块,来自 schemaFromContract, (中文(简体) ), 或从 c.* 和上面的一样,无论按键排列值 拼写其地.

运输、身份、证书核查、重复和援引 已解码的 func 引用是调用层的工作, 而不是这个软件包的: 一个 func 价值 保留惰性 { principal, method } 双人 仍在生成模块 导出输入的调用接口为类型 Actor 旁边 actor 服务计划,因为 c.rec 从 a 中删除方法结构 计划 类型 ——计程带值,而不是调用——所以调用层不能 把它从 typeof。 。 。 .

每种方法 Actor 也带有其模式: 它是调用签名 与 WithMode<mode>,以及 ModeOf 将模式读回,所以一个呼叫 层可以拒绝在编译时构建读取的更新.

import type { ModeOf, WithMode } from "@candid-core/schema";

type Actor = {
  fee: (() => Promise<bigint>) & WithMode<"query">;
  transfer: ((amount: bigint) => Promise<void>) & WithMode<"update">;
};
type FeeMode = ModeOf<Actor["fee"]>; // "query"

标记不会改变任何东西: 呼叫签名, keyof Actor 编辑 模块的导出是相同的,一个普通的async函数仍然执行 这个方法。 一个 Actor 用手书写,没有读作整体 MethodMode 结合,模式未知.

打开 OK/err 结果

variant { ok : T; err : E } 通用罐子结果公约,以及 寻常的解码方法就是 价值 页:1 ok编号 :err 密钥——在任何记录上误射的猜测,合法携带 那些字段名称, 一个不能输入错误的有效载荷。 是否得到答复 实值 结果,每一只手臂都携带着一个图谋:

import { c, type Infer } from "@candid-core/schema";
import { isResultSchema, unwrapResult } from "@candid-core/schema/validate";

const TransferError = c.variant({
  bad_fee: c.record({ expected_fee: c.nat }),
  too_old: c.null,
});
const TransferResult = c.variant({ ok: c.nat, err: TransferError });

isResultSchema(TransferResult); // true
isResultSchema(c.record({ ok: c.bool, err: c.opt(c.text) })); // false: a record is not a variant

declare const reply: unknown; // whatever the call decoded to

const outcome = unwrapResult(TransferResult, reply);
if (outcome.issues) {
  // Not a value of this schema at all: the issues `validate` would report.
  throw new Error(outcome.issues[0].message);
} else if (outcome.ok) {
  const blockIndex: bigint = outcome.value;
  void blockIndex;
} else {
  const failure: Infer<typeof TransferError> = outcome.error;
  void failure;
}

这两种拼法都得到承认,因为 双对数字 : ok编号 :err,这是莫托克的 Result.Result 生产,以及 Ok编号 :ErrRust的直截了当的衍生物 双臂和双臂 添加一个变体 第三个臂不是结果,因为将它映射到两个州会降低一个. 赤齿臂( C)variant { ok; err : text }打开 null单曲 candid 的值 null 它声明.

一个 err 臂是一个值,而不是一个例外:一个没有投出,一个 格式错误的值以 { ok: false, issues } 而不是作为一个 也一样 一个不是结果变体的计划 实值 程序员 错误和丢弃 TypeError。 。 。 .

支持矩阵

以被包装的柏油丸来测量——文物 npm pack 生产, 采取和消费作为一种依赖性 @arethetypeswrong/cli 财务报告和审定财务报表 直接编译,不从清单中推断.

类型脚本≥ 5.0
moduleResolutionnode16, (中文(简体) ), nodenext, (中文(简体) ), bundler
模块格式仅限无害环境管理 - 没有共同JS建造船只
来自无害环境管理的节点≥ 16 (行使16.20、18.20、20.19、22.12、25.9)
节点,来自普通JS require()≥ 20.19 ≥ 22.12,则 await import()
TypeScript, 来自普通JS 工程≥ 5.8 与 "module": "nodenext"

类型脚本 5.0 是一个硬地板,它是一个 剖析 下方错误,不是 类型错误 : c.tuple 以 const 类型参数 - 5.0 特性使tuple的元素类型不会扩大——所以4.9停止在 error TS1139: Type parameter declaration expected. 内 dist/schema.d.ts 在它输入前检查任何东西.

node10 分辨率完全无法看到此软件包. 没有高层 main 或者说 types 字段,仅一个 exports 映射, 所以每次导入失败 TS2307 — TypeScript 的自带信件指示您移动到 node16, (中文(简体) ), nodenext, (中文(简体) ), 或者说 bundler。 。 。 .

支持无害环境管理本身不足以作为节点. 建设目标ES2020 并且没有下层, 所以可选的连锁 和无效的连接到达 dist/ 逐字记录——它们出现在八个单元中的四个单元中 dist/ 船舶——以及 V8 8.0 语法,14前的节点可以解析. 上面楼层是 最古老的版本 这个软件包实际上运行 而不是最古老的 可能发挥作用:16.20.2 行使和通过每一个子路径,没有比这更古老的东西 被索赔.

从普通JS,两层独立. 跑步时, require() 联 合 国 一个ES模块是节点在20.19和22.12中添加的;低于所投的 ERR_REQUIRE_ESM时 await import("@candid-core/schema") 成功时间 每个版本测试。 在类型一级,a "type": "commonjs" 项目需求 类型脚本 5.8 财务报告和审定财务报表 "module": "nodenext" —— 说 "module": "node16" 被钉住 至节点16语义,仍拒绝使用 TS1479 每测试一个编译器, 最高包括7.0.

这是故意的 没有 engines 字段两个楼层都狭窄,即: ES2020 无害环境管理能力节点,a require(esm)- 适合通用JS的 - 和 在安装元数据时执行会发出警告,或失败 engine-strict,对于消费者则不适用. 这里有记载 换句话说.

域形( 有意决定 )

类型描述现代域,而非代理-js运行时形状: opt T 实值 T | null - 除了内在类型的选择 null (单位:千美元)opt opt T, (中文(简体) ), opt null, (中文(简体) ), opt reserved)为 { some: T } | null这样 None, (中文(简体) ), Some(None), (中文(简体) ), 财务报告和审定财务报表 Some(Some(x)) 保留三个不同的值——变体是: { tag, value } 受歧视的工会 vec nat8 (单位:千美元)blob)为 Uint8Array, (中文(简体) ), nat编号 :int64位整数为 bigint校长是他们的教条 文字, 标记 Principal 绳子. 与代理人-js值形状的相容性是一个明显的非目标,记录在 计划的问题追踪器.

核查

每一份发行都开着一个包装的消费门 上面写着:, 在上传任何信息之前: 焦油球 npm pack 在犯罪时生产 被提取到一个干净的工程,在严格的类型Script下编译 skipLibCheck 关闭并执行——根和每一个子路径导出,一个真实的 编码/验证回程。 发布步骤然后从中构建并上传 同样的承诺。 此文件中的类型脚本 由同一扇门编成, 与被包装的文物相对, 所以这里有一个例子 无法从文件包中漂移。 解码器是额外的 对照参考执行方案本身的线线进行双向核查 向量计.

证明

生成的装订、合同模式和符合要求的大门都住在 实心 寄存器; 此软件包版本独立( 前 1. 0) . 变动情况 船在焦油和记录中 释放, 编译器板条箱版本它与(meant,作为 candid-core 在它重新命名之前).

版本与发布记录

@candid-core/schema 共发布 8 个版本,首次发布于 2026/07/31;过去 12 个月发布了 8 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.5.0latest2026/10/2713 天前
0.4.02026/10/1129 天前
0.3.02026/10/061 个月前
0.3.0-beta.1beta2026/10/021 个月前
0.2.02026/08/252 个月前
0.1.12026/08/033 个月前

其他发布通道:bootstrap → 0.0.0-bootstrap,beta → 0.3.0-beta.1。

关于 @candid-core/schema 的常见问题

@candid-core/schema 是什么?

Candid的Zod-style schema runtime:具有静态推论,结构验证功能的建设者,一个TypeScript-native 二进制编码器,以及一个从坦率的Canonical Contract文档中构建出相同计划的加载器.注意:该包已被作者标记为弃用——Renamed to @meantjs/schema

如何安装 @candid-core/schema?

在项目目录运行 npm install @candid-core/schema,或使用 pnpm add / yarn add / bun add。

@candid-core/schema 支持 TypeScript 吗?

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

@candid-core/schema 支持 ESM 还是 CommonJS?

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

@candid-core/schema 可以商用吗?

@candid-core/schema 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@candid-core/schema 的下载量有多少?

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

@candid-core/schema 还在维护吗?

最新版本 0.5.0 发布于 2026/10/27(13 天前);至今共发布 8 个版本,其中过去 12 个月发布了 8 个;首次发布于 2026/07/31。作者已将其标记为弃用,新项目建议寻找替代方案。