@jixo/opendweb-client-sdk 0.0.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
Binary file
package/index.d.ts ADDED
@@ -0,0 +1,158 @@
1
+ /** 事件判别联合:relay-* 事件必携带完整快照 payload(非可选) */
2
+ /** 代理策略:none=直连;from-env=读环境代理;url 形态仅 SDK 使用 */
3
+ export type HttpProxyOptions = 'none' | 'from-env' | { url: string }
4
+
5
+ /** 从 SDK 错误消息的 [<kebab-code>] 前缀派生稳定错误码(SCREAMING_SNAKE);无前缀返回 null */
6
+ export declare function deriveErrorCode(message: string): string | null
7
+
8
+ export type FabricEventJs =
9
+ | {
10
+ type: 'peer-connected' | 'peer-disconnected'
11
+ endpointId: string
12
+ }
13
+ | { type: 'roster-updated' }
14
+ | { type: 'message'; from: string; data: Buffer }
15
+ | { type: 'path-changed'; endpointId: string; status: 'direct' | 'relay' | 'unknown' }
16
+ | { type: 'relay-online' | 'relay-offline'; relay: RelayStatusJs }
17
+
18
+ /** relay 状态快照(relayStatus() 返回值与 relay-* 事件 payload 同构) */
19
+ export interface RelayStatusJs {
20
+ /** 字面量联合(非 string) */
21
+ mode: 'disabled' | 'custom' | 'n0'
22
+ /** 配置的 relay URL 列表(disabled 为空数组;n0 恒为 ["https://relay.iroh.network"]) */
23
+ urls: string[]
24
+ /** null <=> mode === "disabled";否则为任一 relay 已连接 */
25
+ online: boolean | null
26
+ /** 最近一次连接错误类别(脱敏:无凭证段;无错误为 null) */
27
+ lastError: string | null
28
+ }
29
+
30
+ /* auto-generated by NAPI-RS */
31
+ /* eslint-disable */
32
+ /**
33
+ * dweb fabric:应用级组网的 Node SDK 入口。
34
+ * 工厂:Fabric.createRoot / Fabric.open / Fabric.attach;完成后调用 shutdown 释放资源。
35
+ */
36
+ export declare class Fabric {
37
+ /** 创建新 fabric(本节点成为 root,可签发邀请与撤销)。 */
38
+ static createRoot(opts: FabricOptions, secret?: SecretSeedHandle | undefined | null): Promise<Fabric>
39
+ /** 打开已有 fabric(数据目录已含名册)。 */
40
+ static open(opts: FabricOptions, secret?: SecretSeedHandle | undefined | null): Promise<Fabric>
41
+ /** 以加入者身份起步(空名册;随后调用 join 兑换邀请)。 */
42
+ static attach(opts: FabricOptions, fabricIdHex: string, secret?: SecretSeedHandle | undefined | null): Promise<Fabric>
43
+ /**
44
+ * 一步加入:从令牌解析 fabric_id,attach + 兑换 + 持久化名册。
45
+ * 令牌前置检查(解码/过期/地址规范化)先于本地数据面加载与身份句柄消费
46
+ * (D11 冻结顺序:令牌自身错误优先于目录检查)。
47
+ */
48
+ static joinWithToken(opts: FabricOptions, token: string, secret?: SecretSeedHandle | undefined | null): Promise<Fabric>
49
+ /** 本节点 EndpointId(z-base-32,52 字符) */
50
+ get endpointId(): string
51
+ /** fabric id(hex) */
52
+ fabricIdHex(): Promise<string>
53
+ /** 当前有效成员投影 */
54
+ members(): Promise<Array<Member>>
55
+ /** 查询 EndpointId 是否为有效成员 */
56
+ isMember(endpointId: string): Promise<boolean>
57
+ /**
58
+ * 签发邀请令牌(root-only)。返回 dweb1. 前缀的自包含字符串。
59
+ * 第三参 opts 透传签发安全门逃生阀:relay 为空且 advertiseAddrs 为空时,
60
+ * 无 opts 或 allowRelayless !== true => reject([invite-without-relay])。
61
+ */
62
+ invite(ttlMs: number, recipient?: string | undefined | null, opts?: InviteOptions | undefined | null): Promise<string>
63
+ /** 兑换邀请令牌加入 fabric(issuer-online 单次兑换)。 */
64
+ join(token: string): Promise<void>
65
+ /** 连接成员(常规通道;双向门控 + 名册同步)。幂等(活跃连接直接成功)。 */
66
+ connect(endpointId: string): Promise<void>
67
+ /** 断开与某成员的会话 */
68
+ disconnect(endpointId: string): Promise<void>
69
+ /** 发送不透明二进制 envelope */
70
+ send(endpointId: string, data: Buffer): Promise<void>
71
+ /** 撤销成员(root-only);投影收紧并断开既有会话。 */
72
+ revoke(endpointId: string): Promise<void>
73
+ /** 设置本节点显示名 */
74
+ setDisplayName(name: string): Promise<void>
75
+ /** 查询与某成员的当前路径类型:"direct" | "relay" | "unknown" */
76
+ linkStatus(endpointId: string): Promise<string>
77
+ /**
78
+ * relay 状态快照(零等待,读内核缓存)。快照优先于事件:初始事实一律先查
79
+ * 本方法,事件只承载后续跳变。禁用模式 online 为 null(而非 false)。
80
+ */
81
+ relayStatus(): Promise<RelayStatusJs>
82
+ /**
83
+ * 显式导出身份(identity export,不含 roster)为 `dwebkey1.` 加密串。
84
+ * 交由产品自行安全保存(如账号系统加密托管——密文上云、口令在用户)。
85
+ */
86
+ exportSecretPassphrase(passphrase: string): Promise<string>
87
+ /**
88
+ * 注册事件回调(可多次调用,多个回调都会收到全部事件)。
89
+ * 返回句柄 id;off(id) 注销(index.js 包装为取消订阅函数)。
90
+ */
91
+ on(callback: (event: FabricEventJs) => void): () => void
92
+ /** 注销事件回调(on 返回的 id)。 */
93
+ off(id: number): void
94
+ /** 优雅关闭:断开全部会话并释放网络资源。幂等。 */
95
+ shutdown(): Promise<void>
96
+ }
97
+
98
+ /**
99
+ * 受保护的身份种子句柄:内部持有 [`SecretSeed`](zeroize-on-drop),
100
+ * 被 Fabric 构造消费(take)后即失效。用于"导入 token / 产品托管解密后"
101
+ * 向 Fabric 注入身份而不经手明文 JS 字符串。内部 Arc:句柄可 clone 进
102
+ * 配置对象,take 语义全局共享(一次消费)。
103
+ */
104
+ export declare class SecretSeedHandle {
105
+ /** 派生 EndpointId(z32 展示串;不暴露种子本体)。 */
106
+ get endpointId(): string
107
+ /** 是否仍持有种子(未被消费)。 */
108
+ get available(): boolean
109
+ }
110
+
111
+ /** Fabric 构造配置 */
112
+ export interface FabricOptions {
113
+ /** 数据目录(名册持久化位置;secret 默认实现也指向此目录) */
114
+ dataDir: string
115
+ relay?: RelayOptions
116
+ /** 写入邀请令牌的 issuer 直连地址(host:port;逐项校验,非法构造 reject) */
117
+ advertiseAddrs?: Array<string>
118
+ /**
119
+ * HTTP 控制面(relay 连接)代理所有权;缺省 "none"。
120
+ * iroh endpoint 不读进程环境变量;QUIC 数据面永不经代理。
121
+ */
122
+ httpProxy?: 'none' | 'from-env' | { url: string }
123
+ /** join 总时限(毫秒);缺省 30000;值域 [1000, 600000],越界构造 reject */
124
+ joinTimeoutMs?: number
125
+ }
126
+
127
+ /** httpProxy 的 `{ url }` 形态("none" | "from-env" 为字符串形态)。 */
128
+ export interface HttpProxyUrl {
129
+ url: string
130
+ }
131
+
132
+ /**
133
+ * 导入 `dwebkey1.` 身份导出串:口令派生解密,返回受保护的种子句柄。
134
+ * (不落盘;注入 Fabric 用工厂的 secret 参数。Argon2 在阻塞线程池执行。)
135
+ */
136
+ export declare function importSecret(token: string, passphrase: string): Promise<SecretSeedHandle>
137
+
138
+ /** invite 逃生阀选项(D3)。 */
139
+ export interface InviteOptions {
140
+ /** 允许签发无 relay 令牌;调用方自担可达性责任(带外路径)。 */
141
+ allowRelayless?: boolean
142
+ }
143
+
144
+ /** 成员信息 */
145
+ export interface Member {
146
+ endpointId: string
147
+ displayName?: string
148
+ sinceMs: number
149
+ }
150
+
151
+ /** SDK 原生层版本 */
152
+ export declare function nativeVersion(): string
153
+
154
+ /** relay 配置(判别联合:非法组合构造期拒绝) */
155
+ export type RelayOptions =
156
+ | { mode?: 'n0' }
157
+ | { mode: 'disabled' }
158
+ | { mode: 'custom'; urls: [string, ...string[]] }
package/index.js CHANGED
@@ -1 +1,94 @@
1
- module.exports = {};
1
+ // 平台守卫 + 原生模块加载 + 事件解包:v0.1 darwin-arm64。
2
+ // 加载策略:把 .node 拷贝到 os.tmpdir() 下的内容寻址新路径再 require——
3
+ // 规避网络磁盘(SMB)页缓存不一致导致的 CODESIGNING "Invalid Page",
4
+ // 以及 dyld 对既有路径的坏闭包缓存(同名覆写后同字节仍加载失败)。
5
+ // 事件解包:原生层以 JSON 字符串投递事件(TSFN 对象转换在 napi 3.12 不稳定),
6
+ // 此处还原为带 Buffer 的类型化对象。
7
+ const fs = require("node:fs");
8
+ const os = require("node:os");
9
+ const path = require("node:path");
10
+ const crypto = require("node:crypto");
11
+
12
+ const PLATFORM_BINARIES = {
13
+ "darwin-arm64": "dweb.darwin-arm64.node",
14
+ "win32-x64": "dweb.win32-x64.node",
15
+ };
16
+ const SUPPORTED = `${process.platform}-${process.arch}`;
17
+ const BINARY = PLATFORM_BINARIES[SUPPORTED];
18
+
19
+ if (!BINARY) {
20
+ throw new Error(
21
+ `@jixo/opendweb-client-sdk: 当前平台 ${SUPPORTED} 暂不支持。v0.1 提供 ${Object.keys(PLATFORM_BINARIES).join(" / ")} 原生二进制。`,
22
+ );
23
+ }
24
+
25
+ const SRC = path.join(__dirname, BINARY);
26
+
27
+ function loadViaTmp() {
28
+ const buf = fs.readFileSync(SRC);
29
+ const hash = crypto.createHash("sha256").update(buf).digest("hex").slice(0, 24);
30
+ try {
31
+ // 0700 私有目录规避可预测路径的 symlink/TOCTOU 窗口
32
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "opendweb-sdk-"));
33
+ const dest = path.join(dir, `${hash}.node`);
34
+ const fd = fs.openSync(dest, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY, 0o755);
35
+ try {
36
+ fs.writeFileSync(fd, buf);
37
+ fs.fsyncSync(fd);
38
+ } finally {
39
+ fs.closeSync(fd);
40
+ }
41
+ return require(dest);
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+
47
+ const Native = loadViaTmp() ?? require(SRC);
48
+
49
+ // on() → 取消订阅函数:原生层返回回调 id,off(id) 注销(event_callbacks 移除句柄)。
50
+ const nativeOn = Native.Fabric.prototype.on;
51
+ const nativeOff = Native.Fabric.prototype.off;
52
+ Native.Fabric.prototype.on = function onWrapped(callback) {
53
+ // 原生 TSFN 为 error-first 回调:(err, jsonString)
54
+ const id = nativeOn.call(this, (err, json) => {
55
+ if (err) return;
56
+ const ev = JSON.parse(json);
57
+ if (typeof ev.dataBase64 === "string") {
58
+ ev.data = Buffer.from(ev.dataBase64, "base64");
59
+ delete ev.dataBase64;
60
+ }
61
+ callback(ev);
62
+ });
63
+ return () => nativeOff.call(this, id);
64
+ };
65
+
66
+ /**
67
+ * 从 SDK 错误消息的 [<kebab-code>] 前缀派生稳定错误码(SCREAMING_SNAKE)。
68
+ * napi Error 无自定义 code 通道——前缀约定见 contracts/error-matrix.md。
69
+ * 无前缀时返回 null(未分类的底层错误)。
70
+ */
71
+ function deriveErrorCode(message) {
72
+ if (typeof message !== "string") return null;
73
+ const m = message.match(/^\[([a-z0-9-]+)\]\s/);
74
+ if (!m) return null;
75
+ return m[1].replace(/-/g, "_").toUpperCase();
76
+ }
77
+ Native.deriveErrorCode = deriveErrorCode;
78
+
79
+ // relayStatus():napi Option::None 序列化为 undefined,契约(C0 d.ts)要求 null——
80
+ // 包装归一(事件 payload 的 relay 快照经 JSON.parse 天然是 null,无需处理)。
81
+ const nativeRelayStatus = Native.Fabric.prototype.relayStatus;
82
+ if (typeof nativeRelayStatus === "function") {
83
+ Native.Fabric.prototype.relayStatus = async function relayStatusWrapped() {
84
+ const r = await nativeRelayStatus.call(this);
85
+ return {
86
+ mode: r.mode,
87
+ urls: r.urls ?? [],
88
+ online: r.online ?? null,
89
+ lastError: r.lastError ?? null,
90
+ };
91
+ };
92
+ }
93
+
94
+ module.exports = Native;
package/package.json CHANGED
@@ -1,9 +1,36 @@
1
1
  {
2
2
  "name": "@jixo/opendweb-client-sdk",
3
- "version": "0.0.0",
4
- "description": "Generated placeholder package reserved by pnpm-pub.",
3
+ "version": "0.2.0",
4
+ "description": "@jixo/opendweb-client-sdk dweb application-level fabric SDK for Node (napi-rs binding)",
5
+ "license": "MIT OR Apache-2.0",
5
6
  "main": "index.js",
7
+ "types": "index.d.ts",
8
+ "napi": {
9
+ "binaryName": "dweb",
10
+ "targets": [
11
+ "aarch64-apple-darwin"
12
+ ]
13
+ },
14
+ "scripts": {
15
+ "build": "napi build --platform --release --target aarch64-apple-darwin --no-js && node scripts/fix-dts.mjs",
16
+ "build:debug": "napi build --platform --target aarch64-apple-darwin --no-js",
17
+ "test": "node --test --test-force-exit",
18
+ "typecheck": "tsc --noEmit",
19
+ "build:win": "PATH=\"$HOME/.cargo/bin:$PATH\" LIBNODE_PATH=\"$HOME/libnode-win\" cargo build --release --target x86_64-pc-windows-gnu -p dweb-client-sdk && cp \"$HOME/.cargo-target/dweb/x86_64-pc-windows-gnu/release/dweb_client_sdk.dll\" dweb.win32-x64.node"
20
+ },
21
+ "devDependencies": {
22
+ "@napi-rs/cli": "3.7.0",
23
+ "typescript": "^5.6.0",
24
+ "vitest": "^3.0.0"
25
+ },
6
26
  "files": [
7
- "index.js"
8
- ]
27
+ "index.js",
28
+ "index.d.ts",
29
+ "dweb.darwin-arm64.node",
30
+ "dweb.win32-x64.node"
31
+ ],
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/Gaubee/dweb.git"
35
+ }
9
36
  }