# @chainsafe/zapi 中文文档

规范地址：https://npmcn.com/package/%40chainsafe/zapi/

版本：4\.3\.0
npm 元数据更新时间：2026-10-31T05:12:19.276Z
中文译文更新时间：2026-11-09T09:40:39.033Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40chainsafe/zapi
原文对照：https://npmcn.com/package/%40chainsafe/zapi/?lang=original
项目主页：https://github.com/ChainSafe/zapi/tree/main/#readme
源码仓库：https://github.com/ChainSafe/zapi

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

- 包体积（解压后）：136 kB，43 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：0 个
- 首次发布：2026-02-03T22:27:52.077Z
- 最新版本发布：2026-10-31T05:12:18.958Z
- 近 30 天下载量：11490（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：11（近 12 个月 11 个）

## 中文简介

用于管理和发布 Zig NAPI 软件包的工具 .

## 安装

```sh
npm install @chainsafe/zapi
```

也可以使用：`pnpm add @chainsafe/zapi`、`yarn add @chainsafe/zapi`、`bun add @chainsafe/zapi`

## README

# 扎皮语name

一个Zig N-API包装器库和CLI,用于建造并出版跨平台的Node.js本地加通.

zapi提供两个主要组成部分:

1.  **齐格图书馆** (单位:千美元)`src/`) - 将Node.js 本地添加器写入 Zig 中,并带有一个高水平的DSL 来映射 JavaScript 的类型系统
2.  **CLI 工具** (单位:千美元)`ts/`) - 为交叉编译和发布多平台 npm 包构建工具

## 安装

```bash
npm install -D @chainsafe/zapi
```

## Zig 添加项目设置

[`examples/`](https://npmcn.com/package/%40chainsafe/zapi/examples/) 是一个完整的 Zig 消费项目 有自己的 [`build.zig.zon`](https://npmcn.com/package/%40chainsafe/zapi/examples/build.zig.zon) 财务报告和审定财务报表 [`build.zig`](https://npmcn.com/package/%40chainsafe/zapi/examples/build.zig)。zapi 软件包构建不配置 实例.

大多数项目都有一个附加项目。 第二个逻辑加词仅在 软件包产生另一个最终 `.node` 库中有一个单独的根模块。 这个 实例项目产生6个附加项目; `example_js_dsl.node` 财务报告和审定财务报表 `example_addon_isolation.node` 都使用 DSL 类,因此每个类都收到 他们自己的身份。 某一项目不需要为 身份到物质:一个节点进程可以从不相干包中加载附加物, 每个加通必须拒绝其他人创建的类对象.

消费者的 `build.zig` 配置软件包,然后附加生成的 模块导出或交换 `js.class`数字 :

```zig
const std = @import("std");
const zapi_build = @import("zapi");
const zbuild = @import("zbuild");

pub fn build(b: *std.Build) !void {
    @setEvalBranchQuota(200_000);
    const manifest = @import("build.zig.zon");
    const result = try zbuild.configureBuild(b, manifest, .{});

    zapi_build.addAddonIdentity(
        b,
        result.library("example_js_dsl").?,
        manifest,
    );
    zapi_build.addAddonIdentity(
        b,
        result.library("example_addon_isolation").?,
        manifest,
    );
}
```

相应的根模块将生成的导入传递到 `js.exportModule`数字 :

```zig
comptime {
    js.exportModule(@This(), .{
        .identity = @import("zapi_addon_identity"),
    });
}
```

见 [`examples/js_dsl/mod.zig`](https://npmcn.com/package/%40chainsafe/zapi/examples/js_dsl/mod.zig) 普通类 页:1 [`examples/addon_isolation/mod.zig`](https://npmcn.com/package/%40chainsafe/zapi/examples/addon_isolation/mod.zig) 用于测试交叉addon隔离的同名类在第二个加通中. 仅功能模块不需要身份;完整工程保存 [`examples/function_only_dsl/mod.zig`](https://npmcn.com/package/%40chainsafe/zapi/examples/function_only_dsl/mod.zig) 打开 `js.exportModule(@This(), .{})`。 。 。 .

`addAddonIdentity` 创建 `zapi_addon_identity` 从消费者进口 软件包名称、版本、指纹和最后编译步骤名称。 不创建 导入为源文件。 一个软件包中不同的加法需要独一无二 编译步骤名称和单独的根模块. 身份没有使用 `dest_sub_path` 或节点加载文件的路径,所以复制和 同一编译的文件系统化名 `.node` 保持兼容性.

实例声明 `zapi` 财务报告和审定财务报表 `zbuild` 作为 URL/hash 依赖。 建起来 以下列方式对帐:

```bash
cd examples
zig build --fork=..
```

对于另一个消费项目,使用 `zig build` 通常情况下。 当地扎皮期间 开发,保持 URL/hash 的依赖性,并用 `zig build --fork=/path/to/zapi`页:1 `.path` 依赖性不暴露 依赖关系 `build.zig` 作为进口.

* * *

## Zig 库 - 快速启动

DSL是写出本地加通的默认方法. 导入 `js` 从 zapi 并写出正常的 Zig 函数—— zapi 自动处理所有 N- API 集合 .

```zig
const js = @import("zapi").js;

pub fn add(a: js.Number, b: js.Number) !js.Number {
    return js.Number.from(try a.toI32() + try b.toI32());
}

pub const Counter = struct {
    pub const js_meta = js.class(.{
        .properties = .{
            .count = js.prop(.{ .get = true, .set = false }),
        },
    });

    _count: i32,

    pub fn init(start: js.Number) !Counter {
        return .{ ._count = try start.toI32() };
    }

    pub fn increment(self: *Counter) void {
        self._count += 1;
    }

    // Getter: obj.count (not obj.count())
    pub fn count(self: Counter) js.Number {
        return js.Number.from(self._count);
    }
};

comptime {
    js.exportModule(@This(), .{
        .identity = @import("zapi_addon_identity"),
    });
}
```

**JavaScript 用途 :**

```js
const mod = require('./my_module.node');
mod.add(1, 2); // 3
const c = new mod.Counter(0);
c.increment();
c.count; // 1 (getter, not a method call)
```

`pub` 函数自动导出,并带有 `js_meta = js.class(...)` 成为联署材料课程。 类类型标记在编译时由 Zig 生成 软件包名称、版本、指纹、附加文物名称和类类型名称. 这保留了两个加载相容的相容副本,同时隔离不同的 外加和包版本. 因此,从 `v1.0.0` 财务报告和审定财务报表 `v1.0.1` 无法在同一过程中交换 DSL 类对象,即使它们 本土类定义在其他方面是相容的. 只导出模块 函数,从不接受或返回 DSL 类可继续使用 `js.exportModule(@This(), .{})`。 。 。 .

```text
FNV-1a-128(package@version#fingerprint::addon::ZigType)
```

低等包装和转换API明确取同一种身份类型. 无法接受或返回 DSL 类通过的代码路径 `js.NoAddonIdentity`。 。 。 .

* * *

## JS 类型参考

| 类型 | JS 等同关系 | 关键方法 |
| --- | --- | --- |
| `Number` | `number` | `toI32()`, (中文(简体) ), `toF64()`, (中文(简体) ), `assertI32()`, (中文(简体) ), `from(anytype)` |
| `String` | `string` | `toSlice(buf)`, (中文(简体) ), `toOwnedSlice(alloc)`, (中文(简体) ), `len()`, (中文(简体) ), `from([]const u8)` |
| `Boolean` | `boolean` | `toBool()`, (中文(简体) ), `assertBool()`, (中文(简体) ), `from(bool)` |
| `BigInt` | `bigint` | `toI64()`, (中文(简体) ), `toU64()`, (中文(简体) ), `toI128()`, (中文(简体) ), `from(anytype)` |
| `Date` | `Date` | `toTimestamp()`, (中文(简体) ), `from(f64)` |
| `Array` | `Array` | `get(i)`, (中文(简体) ), `getNumber(i)`, (中文(简体) ), `length()`, (中文(简体) ), `set(i, val)` |
| `Object(T)` | `object` | `get()`, (中文(简体) ), `set(value)` —— 说 `T` 字段必须是 DSL 类型 |
| `Function` | `Function` | `call(args)` |
| `Value` | `any` | `isNumber()`, (中文(简体) ), `asNumber()`,类型检查/缩小 |
| `Uint8Array` 包括:. | `TypedArray` | `toSlice()`, (中文(简体) ), `toArray(len)`, (中文(简体) ), `from(slice)` |
| `OwnedUint8Array` 包括:. | `TypedArray` | `fromOwnedSlice(allocator, data)`, (中文(简体) ), `fromSlice(allocator, data)` |
| `Promise(T)` | `Promise` | `resolve(value)`, (中文(简体) ), `reject(err)` |

* * *

## 职能

导出功能的三种模式:

### 基本——直接测绘

```zig
pub fn add(a: Number, b: Number) !Number {
    return Number.from(try a.toI32() + try b.toI32());
}
```

### 处理错误 - `!T` 成为JS的例外

```zig
pub fn safeDivide(a: Number, b: Number) !Number {
    const divisor = try b.toI32();
    if (divisor == 0) return error.DivisionByZero;
    return Number.from(@divTrunc(try a.toI32(), divisor));
}
```

联合来文: `try { safeDivide(10, 0) } catch (e) { /* "DivisionByZero" */ }`

### 无效返回—— `?T` 变成 `undefined`

```zig
pub fn findValue(arr: Array, target: Number) ?Number {
    const len = arr.length() catch return null;
    // ... search, return null if not found
}
```

* * *

## 类

带有 `js_meta = js.class(...)` 以 JavaScript 类导出 :

```zig
pub const Timer = struct {
    pub const js_meta = js.class(.{});
    start: i64,

    pub fn init() Timer {
        return .{ .start = std.time.milliTimestamp() };
    }

    pub fn elapsed(self: Timer) js.Number {
        return js.Number.from(std.time.milliTimestamp() - self.start);
    }

    pub fn reset(self: *Timer) void {
        self.start = std.time.milliTimestamp();
    }

    pub fn deinit(self: *Timer) void {
        _ = self;
    }
};
```

**方法分类:**

| 签署 | JS 行为 |
| --- | --- |
| `pub fn init(...)` | 构造器( R)`new Class(...)`)——必须返回 `T` 或者说 `!T` |
| `pub fn method(self: T, ...)` | 不可移动实例方法 |
| `pub fn method(self: *T, ...)` | 可变实例方法 |
| `pub fn method(self: T, ...) !T` | 将新的联合来文案件退回案件的方法 |
| `pub fn method(...) !T` | 静态方法返回新的JS案例 |
| `pub fn method(...)` | 静态方法( 无自定义, 返回非 T) |
| `pub fn deinit(self: *T)` | 可选GC销毁器 |

返回类的方法或功能自动实现新的JS实例。 没有单独的作者化“ 构件” 标记 :

```zig
pub const PublicKey = struct {
    pub const js_meta = js.class(.{});
    pk: bls.PublicKey,

    pub fn init() PublicKey {
        return .{ .pk = undefined };
    }

    // Static factory: PublicKey.fromBytes(bytes)
    pub fn fromBytes(bytes: js.Uint8Array) !PublicKey {
        const slice = try bytes.toSlice();
        return .{ .pk = try bls.PublicKey.deserialize(slice) };
    }
};
```

联合来文: `const pk = PublicKey.fromBytes(bytes);`

同级实例方法也有效:

```zig
pub fn clone(self: MyState) !MyState {
    const cloned = try self.data.clone();
    return .{ .data = cloned };
}
```

联合来文: `const newState = state.clone();` ——返回一个新的实例,原样不变.

### 可选参数

可选 DSL 类型的参数 (`?js.Number`, (中文(简体) ), `?js.Boolean`(b) 成为可选的联署材料论据:

```zig
pub fn fromBytes(bytes: js.Uint8Array, validate: ?js.Boolean) !PublicKey {
    const do_validate = if (validate) |v| try v.toBool() else false;
    // ...
}
```

联合来文: `PublicKey.fromBytes(bytes)` 或者说 `PublicKey.fromBytes(bytes, true)`

### 获取器和设置器

内部声明属性 `js_meta` 与 `js.prop` 登记财产获取者:

```zig
pub const Config = struct {
    pub const js_meta = js.class(.{
        .properties = .{
            .volume = js.prop(.{ .get = true, .set = true }),
            .muted = js.prop(.{ .get = true, .set = true }),
            .label = js.prop(.{ .get = true, .set = false }),
        },
    });

    _volume: i32,
    _muted: bool,
    _label: []const u8,

    pub fn init() Config {
        return .{ ._volume = 50, ._muted = false, ._label = "default" };
    }

    // Read-write: obj.volume / obj.volume = 80
    pub fn volume(self: Config) js.Number {
        return js.Number.from(self._volume);
    }
    pub fn setVolume(self: *Config, value: js.Number) !void {
        const v = try value.toI32();
        if (v < 0 or v > 100) return error.VolumeOutOfRange;
        self._volume = v;
    }

    // Read-only: obj.label
    pub fn label(self: Config) js.String {
        return js.String.from(self._label);
    }
};
```

联合来文: `cfg.volume = 80; cfg.label; // "default"`

**规则:**

-   `pub const js_meta = js.class(.{})` 作为联署部队的标志
-   `.properties = .{ .name = js.prop(.{ .get = true, .set = false }) }` 以 `pub fn name(...)`
-   `.properties = .{ .name = js.prop(.{ .get = true, .set = true }) }` 登记册获取/改进方法 `name` 财务报告和审定财务报表 `setName`
-   `.properties = .{ .name = js.prop(.{ .get = "customGetter", .set = false }) }` 注册一个由特定命名方法支持的获取器
-   接入器支持方法不作为可调用JS方法输出

* * *

## 从事类型工作

### 输入对象

```zig
const Config = struct { host: String, port: Number, verbose: Boolean };

pub fn connect(config: Object(Config)) !String {
    const c = try config.get();
    // access c.host, c.port, c.verbose
}
```

### 已打入阵列

```zig
pub fn sum(data: Uint8Array) !Number {
    const slice = try data.toSlice();
    var total: i32 = 0;
    for (slice) |byte| total += @intCast(byte);
    return Number.from(total);
}
```

使用自有返回类型向 JavaScript 传输自有分配器的切片 不复制其元素:

```zig
pub fn serialize() !js.OwnedUint8Array {
    const allocator = js.allocator();
    const data = try allocator.alloc(u8, 32);
    // Fill data...
    return js.OwnedUint8Array.fromOwnedSlice(allocator, data);
}
```

在不复制的情况下返回该值会将其分配到 JavaScript 令兹克主入空. N- API 接受外部内存前的失败 将所有权留在Zig,以便能正常释放。 分配者必须留下 有效, 直至 ArrayBuffer 最终确定器运行。 如果外部矩阵缓冲器是 不支持, 返回原错误; 没有执行副本倒置 .

### 承诺

```zig
pub fn asyncOp(val: Number) !Promise(Number) {
    var promise = try js.createPromise(Number);
    try promise.resolve(val);  // must resolve or reject before returning
    return promise;
}
```

`Promise(T)` 在此 DSL 路径中,只有同步:在导出函数返回前解决或拒绝它。 对于必须摆脱JS线的工作,请使用 `js.spawn` 见下文.

### Async 任务

`js.spawn` 在Libuv工人池上执行一项任务,并返回一份JS承诺,在完成后予以解决。 一个任务就是任何结构 `compute`, (中文(简体) ), `resolve`,以及 `deinit`数字 :

```zig
const ScaleTask = struct {
    data: []u32,
    factor: u32,

    // Worker thread — must not touch napi or DSL values.
    pub fn compute(self: *ScaleTask) !void {
        for (self.data) |*value| value.* *= self.factor;
    }

    // JS thread — the DSL env context is established, so DSL types work here.
    pub fn resolve(self: *ScaleTask, _: napi.Env) !js.OwnedUint32Array {
        const owned: js.OwnedUint32Array = .fromOwnedSlice(js.allocator(), self.data);
        self.data = &.{};  // ownership handed to JS, no copy
        return owned;
    }

    // Safe to call after resolve transferred ownership.
    pub fn deinit(self: *ScaleTask) void {
        js.allocator().free(self.data);
    }
};

pub fn asyncScale(data: js.Uint32Array, factor: Number) !Value {
    const copy = try js.allocator().dupe(u32, try data.toSlice());
    errdefer js.allocator().free(copy);
    return js.spawn(ScaleTask, .{ .data = copy, .factor = @intCast(factor.assertI32()) }, "asyncScale");
}
```

`resolve` 可能返回 DSL 类型 (`js.Number`),一个自有的打出阵列(不复制而转让), `napi.Value`,或 `void`。 。 。 .

若为 `compute` 返回承诺所拒绝的错误 `Error(@errorName(err))`添加可选 `reject(self: *Task, env: napi.Env, err: anyerror) !napi.Value` 建立拒绝的价值你自己—— `js.errorWithMessage(env, msg)` 构建平原 `Error` 对普通案件:

```zig
pub fn reject(_: *Task, env: napi.Env, err: anyerror) !napi.Value {
    return js.errorWithMessage(env, switch (err) {
        error.ComputeFailed => "worker could not finish the job",
        else => @errorName(err),
    });
}
```

所有权:如果 `spawn` 任务失败不消耗,所以打电话者 `errdefer`s必须释放(如上)。 一次 `spawn` 成功 帮助者拥有任务和调用 `deinit` 在约定之后.

### 回电

```zig
pub fn applyCallback(val: Number, cb: Function) !Value {
    return try cb.call(.{val});
}
```

* * *

## 命名空间

导入 Zig 模块为 `pub const` 创建 JS 命名空间。 DSL 递归登记所有与DSL相容的宣告 :

```zig
// root.zig
pub const math = @import("math.zig");     // → exports.math.multiply(...)
pub const crypto = @import("crypto.zig"); // → exports.crypto.PublicKey, etc.

comptime {
    js.exportModule(@This(), .{
        .identity = @import("zapi_addon_identity"),
    });
}
```

命名空间任意筑巢——一个具有更多 `pub const` 进口会造成更深的巢穴.

* * *

## 常数和常数

斯卡尔和字符串 `pub const` 解码器(入、出、出), `[]const u8`)导出为可假定值属性——在模块根上,在命名空间内,在类上(作为构造器上的静态):

```zig
pub const SHUFFLE_ROUNDS_MAINNET: u32 = 90; // → exports.SHUFFLE_ROUNDS_MAINNET
pub const LIBRARY = "zapi";                 // → exports.LIBRARY
```

A 类 `pub const` enum导出为将每个标签名称(verbatim,无大小写转换)映射到整数的被冻结的平面对象——同样在模块根上,在命名空间内,以及在分类上:

```zig
pub const ByteCount = enum(u8) { one = 1, two = 2 }; // → exports.ByteCount = {one: 1, two: 2}
```

`pub var` 解密状态是可变状态,不是常数,从不导出. 跳过其他曲线形状(结构值、阵列等);通过 `.register` 钩. 整形凸起和圆柱标记必须装入 `i64` (反之为编译错误).

* * *

## 模块周期

`exportModule` 接受带有原子嵌入重计的可选生命周期钩:

```zig
comptime {
    js.exportModule(@This(), .{
        .identity = @import("zapi_addon_identity"),
        .init = fn (refcount: u32) !void,    // called before registration (0 = first env)
        .cleanup = fn (refcount: u32) void,  // called on env exit (0 = last env)
    });
}
```

这使得工人线程情景能够安全共享状态初始化.

* * *

## 混合 DSL 和 N- API

```zig
pub fn advanced() !Value {
    const e = js.env();      // access low-level napi.Env
    const obj = try e.createObject();
    // use any napi.Env method...
    return .{ .val = obj };
}
```

**上下文访问器 :**

| 函数 | 说明 |
| --- | --- |
| `js.env()` | 当前 N- API 环境( 线程本地化, 由 DSL 调用设置) |
| `js.io()` | 共享 `std.Io` 在至少一个 N- API 环境活动时为附加保留控件 |
| `js.allocator()` | 土著拨款分配人 |
| `js.scratch()` | 临时分配的竞技场,最外层回调时释放 |
| `js.thisArg()` | 联合来文 `this` 数值(可用实例方法/方法/方法) |

`js.scratch()` 删除短寿命缓冲的免费簿记 :

```zig
pub fn shout(name: String) !String {
    const s = try name.toOwnedSlice(js.scratch()); // no free needed
    return String.from(try std.ascii.allocUpperString(js.scratch(), s));
}
```

内斯特德召回(Zig calls JS,它召回了Zig) 分享竞技场,所以外在记忆留下 有效,直到最外部的回调返回。 使用 `js.allocator()` 对于任何超过 调用,例如类字段、最终确定器拥有的数据,或者在 `js.spawn`是因为 `compute`。 。 。 .

* * *

## 高级: 低等 N- API

DSL 层处理大多数使用大小写. 当需要完全控制处理范围,自动工作,线程安全功能或其他高级特性时,会降入N-API层.

### 核心类型

| 类型 | 说明 |
| --- | --- |
| `Env` | N- API 环境, 提供创建值、 扔出错误、 管理范围的方法 |
| `Value` | JavaScript 值处理, 包含类型检查、 属性访问、 转换的方法 |
| `CallbackInfo` | 提供访问函数参数和 `this` 约束 |
| `HandleScope` | 防止在一个范围内收集垃圾值 |
| `EscapableHandleScope` | 类似 HandleScope 但允许一个值逃脱 |
| `Ref` | 持续提及垃圾收集后留下的价值 |
| `Deferred` | 承诺书/拒绝书 |
| `AsyncWork` | 在主线程上完成回调的线程池上运行工作 |
| `ThreadSafeFunction` | 从任何线索安全呼叫 JavaScript |
| `AsyncContext` | 同步资源跟踪的背景 |

### 创建函数

#### 手动样式

使用原始完全控制 `Env` 财务报告和审定财务报表 `Value`数字 :

```zig
fn add_manual(env: napi.Env, info: napi.CallbackInfo(2)) !napi.Value {
    const a = try info.arg(0).getValueInt32();
    const b = try info.arg(1).getValueInt32();
    return try env.createInt32(a + b);
}
```

#### 自动转换方式 `createCallback`

让 zapi 处理参数/ 返回转换 :

```zig
const napi = @import("zapi").napi;

// Arguments and return value are automatically converted
fn add(a: i32, b: i32) i32 {
    return a + b;
}

// Register with automatic wrapping
try env.createFunction("add", 2, napi.createCallback(2, add, .{}), null);
```

#### 参数提示

控制参数的转换方式 :

```zig
napi.createCallback(2, myFunc, .{
    .args = .{ .env, .auto, .value, .data, .string, .buffer },
    .returns = .value,  // or .string, .buffer, .external_buffer, .auto
});
```

| 提示 | 说明 |
| --- | --- |
| `.auto` | 自动类型转换;字节切换返回被复制到 JS 缓冲器 |
| `.env` | 弹出 `napi.Env` |
| `.value` | 传出原始 `napi.Value` |
| `.data` | 用户数据指针通过以创建功能 |
| `.string` | 转换为/从 `[]const u8` |
| `.buffer` | 借入缓冲参数作为字节切片; 复制字节切片返回 |
| `.external_buffer` | 转移 a `OwnedBuffer` 不复制到 Java 脚本 |

外部缓冲需要明确的所有权转让:

```zig
const allocator = std.heap.page_allocator;

fn makeExternalBuffer() !napi.OwnedBuffer {
    const data = try allocator.dupe(u8, "external data");
    return napi.OwnedBuffer.fromOwnedSlice(allocator, data);
}

const callback = napi.createCallback(0, makeExternalBuffer, .{
    .returns = .external_buffer,
});
```

`OwnedBuffer.intoValue` 在所有权转让后空出来源,所以推迟 `deinit` 这是安全的. N- API 接受外部内存之前的失败 它为空,因为定稿者可能已经拥有分配。 一旦N-API接受外部缓冲器, 分配器必须保持有效,直到其最终确定器运行。 如果环境不允许外部 缓冲器, `intoValue` 复制字节,并仅在该复制成功后才消耗源.

### 创建类

```zig
const napi = @import("zapi").napi;

const Timer = struct {
    start: i64,

    pub fn read(self: *Timer) i64 {
        return std.time.milliTimestamp() - self.start;
    }
};

try env.defineClass(
    "Timer",
    0,
    timerConstructor,
    null,
    &[_]napi.c.napi_property_descriptor{
        .{ .utf8name = "read", .method = napi.wrapCallback(0, Timer.read) },
    },
);
```

### Async 工作( 线程池)

从主线运行 CPU 密集型工作 :

```zig
const napi = @import("zapi").napi;

const Work = struct {
    a: i32,
    b: i32,
    result: i32,
    deferred: napi.Deferred,
};

fn execute(env: napi.Env, data: *Work) void {
    // Runs on thread pool - don't call JS here!
    data.result = data.a + data.b;
}

fn complete(env: napi.Env, status: napi.status.Status, data: *Work) void {
    // Back on main thread - resolve the promise
    const result = env.createInt32(data.result) catch return;
    data.deferred.resolve(result) catch return;
}

// Create async work
const work = try napi.AsyncWork(Work).create(env, null, name, execute, complete, &data);
try work.queue();
```

### 线索安全函数

从任意线索调用 JavaScript :

```zig
const napi = @import("zapi").napi;

const tsfn = try env.createThreadsafeFunction(
    jsCallback,        // JS function to call
    context,           // User context
    "name",
    0,                 // Max queue size (0 = unlimited)
    1,                 // Initial thread count
    null,              // Finalize data
    null,              // Finalize callback
    myCallJsCallback,  // Called on main thread
);

// From any thread:
try tsfn.call(&data, .blocking);
```

### 处理错误

所有N -API呼叫返回 `NapiError` 失败时 :

```zig
const napi = @import("zapi").napi;

fn myFunction(env: napi.Env) !void {
    // Errors propagate naturally
    const value = try env.createStringUtf8("hello");

    // Throw JavaScript errors
    try env.throwError("ERR_CODE", "Something went wrong");
    try env.throwTypeError("ERR_TYPE", "Expected a number");
}
```

* * *

## CLI 工具

### 配置

添加一个 `zapi` 字段为 `package.json`数字 :

```json
{
  "name": "my-addon",
  "zapi": {
    "binaryName": "my-addon",
    "step": "my-lib",
    "targets": [
      "x86_64-unknown-linux-gnu",
      "x86_64-unknown-linux-musl",
      "aarch64-unknown-linux-gnu",
      "aarch64-unknown-linux-musl",
      "x86_64-apple-darwin",
      "aarch64-apple-darwin",
      "x86_64-pc-windows-msvc"
    ]
  }
}
```

### 支持的目标

| 目标 | 平台 | 拱门 | 上诉 |
| --- | --- | --- | --- |
| `aarch64-apple-darwin` | 马科斯 | 军火64 | \- 说吧 |
| `x86_64-apple-darwin` | 马科斯 | x64 (韩语) | \- 说吧 |
| `aarch64-unknown-linux-gnu` | 链接 | 军火64 | glibc 图像 |
| `aarch64-unknown-linux-musl` | 链接 | 军火64 | 马斯尔 |
| `x86_64-unknown-linux-gnu` | 链接 | x64 (韩语) | glibc 图像 |
| `x86_64-unknown-linux-musl` | 链接 | x64 (韩语) | 马斯尔 |
| `x86_64-pc-windows-msvc` | 窗口 | x64 (韩语) | 微调c |

### 全球选项

| 选项 | 说明 |
| --- | --- |
| `--help`, (中文(简体) ), `-h` | 显示帮助消息 |
| `--version`, (中文(简体) ), `-v` | 显示版本编号 |

### 图标

#### `zapi build`

建设单一目标平台.

```bash
zapi build [options]
```

| 选项 | 说明 | 默认 |
| --- | --- | --- |
| `--step` | Zig 积分步 | `zapi.step` 从软件包.json |
| `--target` | 目标三 | 当前平台 |
| `--optimize` | `Debug`, (中文(简体) ), `ReleaseSafe`, (中文(简体) ), `ReleaseFast`, (中文(简体) ), `ReleaseSmall` | \- 说吧 |
| `--zig-cwd` | Zig 构建的工作目录 | `.` |

#### `zapi build-artifacts`

为所有配置的目标建造并收集文物.

```bash
zapi build-artifacts [options]
```

| 选项 | 说明 | 默认 |
| --- | --- | --- |
| `--step` | Zig 积分步 | `zapi.step` 从软件包.json |
| `--optimize` | 优化水平 | \- 说吧 |
| `--zig-cwd` | Zig 构建的工作目录 | `.` |
| `--artifacts-dir` | 文物输出目录 | `artifacts` |

**示例输出 :**

```
▶ Building my-addon for 6 target(s)...
[1/6] Building for x86_64-unknown-linux-gnu...
  → Moving artifact to artifacts/x86_64-unknown-linux-gnu
[2/6] Building for aarch64-apple-darwin...
  → Moving artifact to artifacts/aarch64-apple-darwin
...
✓ Built 6 artifact(s) to artifacts/
```

#### `zapi prepublish`

编写 npm 软件包供发布 :

-   创建 `npm/<target>/` 每个目标的目录
-   编译副本 `.node` 从文物到目标包的二进制
-   生成 `package.json` 每个目标包(有正确的) `os`, (中文(简体) ), `cpu`, (中文(简体) ), `libc`(中文(简体) )
-   更新主机的本地条目 `package.json`是因为 `optionalDependencies`,保留无关条目

```bash
zapi prepublish [options]
```

| 选项 | 说明 | 默认 |
| --- | --- | --- |
| `--artifacts-dir` | 包含已建文物的目录 | `artifacts` |
| `--npm-dir` | Npm 软件包目录 | `npm` |
| `--platforms-only` | 准备目标包, 不更改主清单 | `false` |
| `--main-only` | 只更新主清单 `optionalDependencies` | `false` |

`--platforms-only` 财务报告和审定财务报表 `--main-only` 两者相互排斥。 没有两种选择,两种软件包都准备就绪。 编制工作是当地进行的,不提出登记请求。 工艺品仍可供再试;缺失的文物未做好准备。 生成的平台表继承了包装器 `publishConfig`。 。 。 .

**示例输出 :**

```
▶ Preparing my-addon@1.0.0 for publishing...
▶ Copying artifacts to npm packages...
  → x86_64-unknown-linux-gnu → npm/x86_64-unknown-linux-gnu/my-addon.node
▶ Generating target package.json files...
  → Created npm/x86_64-unknown-linux-gnu/package.json
✓ Prepared 6 target package(s) in npm/
▶ Updating package.json with optionalDependencies...
✓ Prepared main package
```

#### `zapi publish`

将所有特定目标包和主包发布到 npm.

```bash
zapi publish [options] [-- <npm-args>]
```

| 选项 | 说明 | 默认 |
| --- | --- | --- |
| `--npm-dir` | 包含 npm 包的目录 | `npm` |
| `--dry-run` | 预览不出版而公布的内容 | `false` |
| `--platforms-only` | 将主包留给另一个出版商,如Lerna | `false` |
| `--skip-existing` | 跳过已发布的确切版本; 注册错误中止发布 | `false` |

之后的任何参数 `--` 直接传递到 `npm publish` (例如, `--access public`, (中文(简体) ), `--tag beta`) (中文(简体) ).

对于Lerna管理的工作空间释放, 建造文物,然后运行 `zapi prepublish --platforms-only` 财务报告和审定财务报表 `zapi publish --platforms-only --skip-existing -- --access public`。这些命令使主清单保持不变。 设置主软件包 `prepack` 脚本到 `zapi prepublish --main-only` 因此,它的精确平台依赖性是在包装过程中产生的,在勒纳检查工作树后. 该步骤必须启用生命周期脚本。 勒纳随后在工作空间旁边发布主包.

出版需要平台显示包装器的当前名称和版本相匹配,并显示其二进制. zapi在提出登记请求之前验证每个平台。 默认先发布平台,最后发布包装; `--platforms-only` 将包装分配给外部出版商。 要出版的只是包装,使用 `npm publish`。 。 。 .

与 `--skip-existing`,版本查询使用目的注册,包括 `publishConfig` 和明确的 npm CLI 覆盖。 现有版本没有受到任何影响,包括它们的对立面。 这支持重试一个部分发布; 更改一个版本的 dict- tags 是单独的 `npm dist-tag` 操作。 干跑进行本地验证,并 `--skip-existing`,登记查询,但绝不公布.

**实例干跑 :**

```bash
zapi publish --dry-run
```

```
▶ [DRY RUN] Publishing 6 target package(s) + main package with concurrency 1...
[1/7] Would publish x86_64-unknown-linux-gnu
  → Directory: /path/to/npm/x86_64-unknown-linux-gnu
...
✓ [DRY RUN] Would publish 7 package(s); skipped 0
```

### 释放工作流程

GitHub 的释放以释放方式管理,请:

1.  常规承诺合并为 `main` 更新或创建发布 PR.
2.  合并 PR 标记一个新的 GitHub 发布并凸起 `package.json`。 。 。 .
3.  `build.zig.zon` 与同一版本的版本同步.
4.  发布工作流程安装依赖性, 运行 `pnpm build:js`,并直接与 `npm publish` 通过 npm 信任的出版.
5.  没有 `NPM_TOKEN` npm 发布需要保密; GitHub Actions OIDC ()`id-token: write`与 `--provenance`。 。 。 .
6.  出版的npm软件包只是JS的发售(`lib/` 财务报告和审定财务报表 `ts/`) (中文(简体) ).

### 处理错误

设定 `DEBUG=1` 用于错误的完整堆栈痕迹.

* * *

## 运行时间加载

### `requireNapiLibrary(packageDir)`

装入本地加通, 自动选择当前平台的正确二进制 :

```typescript
import { requireNapiLibrary } from "@chainsafe/zapi";
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";

const __dirname = dirname(fileURLToPath(import.meta.url));
const addon = requireNapiLibrary(__dirname);
```

分辨率命令 :

1.  当地建筑: `zig-out/lib/<binaryName>.node`
2.  已发布软件包 : `<pkg-name>-<target>`

* * *

## 实例

见 [实例/](https://npmcn.com/package/%40chainsafe/zapi/examples/) 综合实例目录,包括:

-   所有 DSL 类型( 数量、 字符串、 布尔 、 BigInt 、 日期、 阵列、 对象、 键入箭头、 承诺)
-   处理出错并返回无效
-   带有静态工厂、实例工厂和可选参数的类别
-   计算获取器和设置器
-   嵌入式命名空间
-   模块生命周期钩(用工人线程重新计数进行倒置/清理)
-   回调和混合 DSL/N- API 使用
-   手动注册的低级 N- API

## 许可证

麻省理工学院

## 常见问题

### @chainsafe/zapi 是什么？

用于管理和发布 Zig NAPI 软件包的工具 \.

### 如何安装 @chainsafe/zapi？

在项目目录运行 npm install @chainsafe/zapi，或使用 pnpm add / yarn add / bun add。它还提供命令行工具 zapi，可以用 npx zapi 直接运行。

### @chainsafe/zapi 支持 TypeScript 吗？

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

### @chainsafe/zapi 支持 ESM 还是 CommonJS？

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

### @chainsafe/zapi 可以商用吗？

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

### @chainsafe/zapi 的下载量有多少？

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

### @chainsafe/zapi 还在维护吗？

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

