扎皮语name
一个Zig N-API包装器库和CLI,用于建造并出版跨平台的Node.js本地加通.
zapi提供两个主要组成部分:
- 齐格图书馆 (单位:千美元)
src/) - 将Node.js 本地添加器写入 Zig 中,并带有一个高水平的DSL 来映射 JavaScript 的类型系统 - 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 等同关系 | 关键方法 |
|---|---|---|
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) |
职能
导出功能的三种模式:
基本——直接测绘
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层.
核心类型
| 类型 | 说明 |
|---|---|
Env | N- API 环境, 提供创建值、 扔出错误、 管理范围的方法 |
Value | JavaScript 值处理, 包含类型检查、 属性访问、 转换的方法 |
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 | 链接 | 军火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
建设单一目标平台.
zapi build [options]
| 选项 | 说明 | 默认 |
|---|---|---|
--step | Zig 积分步 | zapi.step 从软件包.json |
--target | 目标三 | 当前平台 |
--optimize | Debug, (中文(简体) ), ReleaseSafe, (中文(简体) ), ReleaseFast, (中文(简体) ), ReleaseSmall | - 说吧 |
--zig-cwd | Zig 构建的工作目录 | . |
zapi build-artifacts
为所有配置的目标建造并收集文物.
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,保留无关条目
zapi prepublish [options]
| 选项 | 说明 | 默认 |
|---|---|---|
--artifacts-dir | 包含已建文物的目录 | artifacts |
--npm-dir | Npm 软件包目录 | npm |
--platforms-only | 准备目标包, 不更改主清单 | false |
--main-only | 只更新主清单 optionalDependencies | false |
--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 | 将主包留给另一个出版商,如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,登记查询,但绝不公布.
实例干跑 :
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 的释放以释放方式管理,请:
- 常规承诺合并为
main更新或创建发布 PR. - 合并 PR 标记一个新的 GitHub 发布并凸起
package.json。 。 。 . build.zig.zon与同一版本的版本同步.- 发布工作流程安装依赖性, 运行
pnpm build:js,并直接与npm publish通过 npm 信任的出版. - 没有
NPM_TOKENnpm 发布需要保密; GitHub Actions OIDC ()id-token: write与--provenance。 。 。 . - 出版的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);
分辨率命令 :
- 当地建筑:
zig-out/lib/<binaryName>.node - 已发布软件包 :
<pkg-name>-<target>
实例
见 实例/ 综合实例目录,包括:
- 所有 DSL 类型( 数量、 字符串、 布尔 、 BigInt 、 日期、 阵列、 对象、 键入箭头、 承诺)
- 处理出错并返回无效
- 带有静态工厂、实例工厂和可选参数的类别
- 计算获取器和设置器
- 嵌入式命名空间
- 模块生命周期钩(用工人线程重新计数进行倒置/清理)
- 回调和混合 DSL/N- API 使用
- 手动注册的低级 N- API
许可证
麻省理工学院