@chainsafe/zapi

中文文档
4.3.0发布于 2026/10/31MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 1.1万

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

中文使用文档
文档目录39 节
  1. 安装
  2. Zig 添加项目设置
  3. Zig 库 - 快速启动
  4. JS 类型参考
  5. 职能
  6. 基本——直接测绘
  7. 处理错误 - !T 成为JS的例外
  8. 无效返回—— ?T 变成 undefined
  9. 类
  10. 可选参数
  11. 获取器和设置器
  12. 从事类型工作
  13. 输入对象
  14. 已打入阵列
  15. 承诺
  16. Async 任务
  17. 回电
  18. 命名空间
  19. 常数和常数
  20. 模块周期
  21. 混合 DSL 和 N- API
  22. 高级: 低等 N- API
  23. 核心类型
  24. 创建函数
  25. 创建类
  26. Async 工作( 线程池)
  27. 线索安全函数
  28. 处理错误
  29. CLI 工具
  30. 配置
  31. 支持的目标
  32. 全球选项
  33. 图标
  34. 释放工作流程
  35. 处理错误
  36. 运行时间加载
  37. requireNapiLibrary(packageDir)
  38. 实例
  39. 许可证

扎皮语name

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

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

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

安装

npm install -D @chainsafe/zapi

Zig 添加项目设置

examples/ 是一个完整的 Zig 消费项目 有自己的 build.zig.zon 财务报告和审定财务报表 build.zig。zapi 软件包构建不配置 实例.

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

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

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数字 :

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

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

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

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

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 集合 .

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 用途 :

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(), .{})。 。 。 .

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

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


JS 类型参考

类型JS 等同关系关键方法
NumbernumbertoI32(), (中文(简体) ), toF64(), (中文(简体) ), assertI32(), (中文(简体) ), from(anytype)
StringstringtoSlice(buf), (中文(简体) ), toOwnedSlice(alloc), (中文(简体) ), len(), (中文(简体) ), from([]const u8)
BooleanbooleantoBool(), (中文(简体) ), assertBool(), (中文(简体) ), from(bool)
BigIntbiginttoI64(), (中文(简体) ), toU64(), (中文(简体) ), toI128(), (中文(简体) ), from(anytype)
DateDatetoTimestamp(), (中文(简体) ), from(f64)
ArrayArrayget(i), (中文(简体) ), getNumber(i), (中文(简体) ), length(), (中文(简体) ), set(i, val)
Object(T)objectget(), (中文(简体) ), set(value) —— 说 T 字段必须是 DSL 类型
FunctionFunctioncall(args)
ValueanyisNumber(), (中文(简体) ), asNumber(),类型检查/缩小
Uint8Array 包括:.TypedArraytoSlice(), (中文(简体) ), toArray(len), (中文(简体) ), from(slice)
OwnedUint8Array 包括:.TypedArrayfromOwnedSlice(allocator, data), (中文(简体) ), fromSlice(allocator, data)
Promise(T)Promiseresolve(value), (中文(简体) ), reject(err)

职能

导出功能的三种模式:

基本——直接测绘

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

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

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

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 类导出 :

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实例。 没有单独的作者化“ 构件” 标记 :

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);

同级实例方法也有效:

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) 成为可选的联署材料论据:

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 登记财产获取者:

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方法输出

从事类型工作

输入对象

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
}

已打入阵列

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 传输自有分配器的切片 不复制其元素:

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 最终确定器运行。 如果外部矩阵缓冲器是 不支持, 返回原错误; 没有执行副本倒置 .

承诺

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数字 :

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 对普通案件:

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 任务失败不消耗,所以打电话者 errdefers必须释放(如上)。 一次 spawn 成功 帮助者拥有任务和调用 deinit 在约定之后.

回电

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

命名空间

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

// 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)导出为可假定值属性——在模块根上,在命名空间内,在类上(作为构造器上的静态):

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

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

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

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


模块周期

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

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

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() 删除短寿命缓冲的免费簿记 :

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

核心类型

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

创建函数

手动样式

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

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 处理参数/ 返回转换 :

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);

参数提示

控制参数的转换方式 :

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 脚本

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

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 复制字节,并仅在该复制成功后才消耗源.

创建类

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 密集型工作 :

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 :

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 失败时 :

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数字 :

{
  "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链接军火64glibc 图像
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

建设单一目标平台.

zapi build [options]
选项说明默认
--stepZig 积分步zapi.step 从软件包.json
--target目标三当前平台
--optimizeDebug, (中文(简体) ), ReleaseSafe, (中文(简体) ), ReleaseFast, (中文(简体) ), ReleaseSmall- 说吧
--zig-cwdZig 构建的工作目录.

zapi build-artifacts

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

zapi build-artifacts [options]
选项说明默认
--stepZig 积分步zapi.step 从软件包.json
--optimize优化水平- 说吧
--zig-cwdZig 构建的工作目录.
--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,保留无关条目
zapi prepublish [options]
选项说明默认
--artifacts-dir包含已建文物的目录artifacts
--npm-dirNpm 软件包目录npm
--platforms-only准备目标包, 不更改主清单false
--main-only只更新主清单 optionalDependenciesfalse

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

示例输出 :

▶ Preparing [email protected] 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.

zapi publish [options] [-- <npm-args>]
选项说明默认
--npm-dir包含 npm 包的目录npm
--dry-run预览不出版而公布的内容false
--platforms-only将主包留给另一个出版商,如Lernafalse
--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,登记查询,但绝不公布.

实例干跑 :

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)

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

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>

实例

见 实例/ 综合实例目录,包括:

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

许可证

麻省理工学院

版本与发布记录

@chainsafe/zapi 共发布 11 个版本,首次发布于 2026/02/04;过去 12 个月发布了 11 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
4.3.0latest2026/10/319 天前
4.2.02026/10/3010 天前
4.1.02026/09/251 个月前
4.0.02026/08/192 个月前
3.1.02026/07/103 个月前
3.0.02026/07/024 个月前

关于 @chainsafe/zapi 的常见问题

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