hamlib 0.2.6 → 0.3.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.
- package/README.md +77 -6
- package/binding.gyp +8 -2
- package/docs/build-hamlib-from-source.md +640 -0
- package/docs/prebuilt-bundling.md +347 -0
- package/index.d.ts +127 -7
- package/lib/index.js +162 -1
- package/package.json +5 -2
- package/prebuilds/darwin-arm64/libhamlib.4.dylib +0 -0
- package/prebuilds/darwin-arm64/node.napi.node +0 -0
- package/prebuilds/darwin-x64/libhamlib.4.dylib +0 -0
- package/prebuilds/darwin-x64/node.napi.node +0 -0
- package/prebuilds/linux-arm64/libhamlib.so +0 -0
- package/prebuilds/linux-arm64/libhamlib.so.4 +0 -0
- package/prebuilds/linux-arm64/libhamlib.so.4.0.7 +0 -0
- package/prebuilds/linux-arm64/node.napi.node +0 -0
- package/prebuilds/linux-x64/libhamlib.so +0 -0
- package/prebuilds/linux-x64/libhamlib.so.4 +0 -0
- package/prebuilds/linux-x64/libhamlib.so.4.0.7 +0 -0
- package/prebuilds/linux-x64/node.napi.node +0 -0
- package/prebuilds/win32-x64/hamlib_shim.dll +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/src/hamlib.cpp +216 -7
- package/src/hamlib.h +14 -0
- package/src/shim/hamlib_shim.c +125 -0
- package/src/shim/hamlib_shim.h +53 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# 预构建二进制依赖打包与路径修正(全平台)
|
|
2
|
+
|
|
3
|
+
目标
|
|
4
|
+
|
|
5
|
+
- 为 Node 原生扩展生成可直接分发的预构建(prebuilds),并随包携带必要的运行库,做到“开箱即用”。
|
|
6
|
+
- 采用成熟的现成工具(dylibbundler、patchelf、objdump/ldd),在 CI 中自动完成依赖复制与加载路径修正,尽量减少自定义脚本。
|
|
7
|
+
- 避免把系统自带的基础库(如 macOS 的系统框架、Linux 的 glibc)打包进来,保持包体和合规性。
|
|
8
|
+
|
|
9
|
+
目录结构
|
|
10
|
+
|
|
11
|
+
- 预构建产物放在 `prebuilds/<platform>-<arch>/` 下:
|
|
12
|
+
- **所有平台统一**:将三方运行库与 `.node` 文件放在同级目录,通过 `@loader_path/`(macOS)、`$ORIGIN`(Linux)或默认加载器(Windows)加载。
|
|
13
|
+
- 这样可以避免嵌套路径问题,简化配置,三平台保持一致。
|
|
14
|
+
|
|
15
|
+
通用原则
|
|
16
|
+
|
|
17
|
+
- 只随包携带非系统库:
|
|
18
|
+
- 常见需要携带:`libfftw3f(.threads)`、`libgfortran`、`libquadmath`、(Linux 上可选)`libstdc++`、`libgcc_s`。
|
|
19
|
+
- 不携带:macOS 系统框架(如 Accelerate)、Linux glibc、Windows 系统 DLL。
|
|
20
|
+
- 加载路径改写:
|
|
21
|
+
- macOS:改为 `@loader_path/<name>.dylib`(同级目录)。
|
|
22
|
+
- Linux:设置(或覆盖)RPATH/RUNPATH 为 `$ORIGIN`(同级目录)。
|
|
23
|
+
- Windows:无需 rpath,DLL 与 `.node` 同级即可被加载器找到。
|
|
24
|
+
- 验证:在 CI 中使用 `otool -L`(macOS)、`ldd`(Linux)、`objdump -p`(Windows)输出检查结果,确保无绝对路径残留且依赖可解析。
|
|
25
|
+
|
|
26
|
+
平台做法与所用工具
|
|
27
|
+
|
|
28
|
+
1) macOS(使用 `dylibbundler`)
|
|
29
|
+
|
|
30
|
+
- 依赖:`brew install dylibbundler`
|
|
31
|
+
- 执行要点:
|
|
32
|
+
- 仅声明 Homebrew 的搜索目录,避免复制系统库:
|
|
33
|
+
- `/opt/homebrew/opt/fftw/lib`、`/opt/homebrew/opt/gcc@14/lib/gcc/14`(Intel 则 `/usr/local/opt/...`)。
|
|
34
|
+
- 目标目录:`prebuilds/darwin-*`(与 .node 同级),前缀:`@loader_path/`。
|
|
35
|
+
- 示例命令:
|
|
36
|
+
```
|
|
37
|
+
dylibbundler \
|
|
38
|
+
-x prebuilds/darwin-arm64/wsjtx_lib_nodejs.node \
|
|
39
|
+
-d prebuilds/darwin-arm64 \
|
|
40
|
+
-p "@loader_path/" \
|
|
41
|
+
-s /opt/homebrew/opt/fftw/lib \
|
|
42
|
+
-s /opt/homebrew/opt/gcc/lib/gcc/current \
|
|
43
|
+
-s /opt/homebrew/opt/gcc@14/lib/gcc/14 \
|
|
44
|
+
-s /usr/local/opt/fftw/lib \
|
|
45
|
+
-s /usr/local/opt/gcc/lib/gcc/current \
|
|
46
|
+
-s /usr/local/opt/gcc@14/lib/gcc/14 \
|
|
47
|
+
-b
|
|
48
|
+
```
|
|
49
|
+
- 验证:
|
|
50
|
+
```
|
|
51
|
+
otool -L prebuilds/darwin-arm64/wsjtx_lib_nodejs.node
|
|
52
|
+
```
|
|
53
|
+
应看到 `@loader_path/libfftw3f.3.dylib` 等(同级目录),无 Homebrew 绝对路径。
|
|
54
|
+
|
|
55
|
+
2) Linux(使用 `patchelf` + `ldd`)
|
|
56
|
+
|
|
57
|
+
- 依赖:`apt-get install -y patchelf`(以及 `ldd`)
|
|
58
|
+
- 复制依赖:用 `ldd` 列出 `.node` 的依赖,筛选并复制目标库到与 `.node` 同级目录:
|
|
59
|
+
- 包括 `libfftw3f*`、`libgfortran*`、`libquadmath*`、`libgcc_s*`(可酌情包含 `libstdc++*`)。
|
|
60
|
+
- 排除 glibc(`libc`, `libm`, `libpthread`, `ld-linux` 等)。
|
|
61
|
+
- 设置 RPATH:
|
|
62
|
+
```
|
|
63
|
+
patchelf --set-rpath '$ORIGIN' prebuilds/linux-*/wsjtx_lib_nodejs.node
|
|
64
|
+
```
|
|
65
|
+
- 验证:
|
|
66
|
+
```
|
|
67
|
+
ldd prebuilds/linux-*/wsjtx_lib_nodejs.node | grep -v 'linux-vdso\|ld-linux\|libc\|libm\|libpthread\|libdl'
|
|
68
|
+
```
|
|
69
|
+
应无 "not found",关键库解析到同目录。
|
|
70
|
+
|
|
71
|
+
3) Windows(MinGW/MSYS2 工具链)
|
|
72
|
+
|
|
73
|
+
- 列出依赖:`objdump -p build/Release/wsjtx_lib_nodejs.node | grep "DLL Name"`
|
|
74
|
+
- 复制到与 `.node` 同级目录(Windows 加载器不搜索子目录):
|
|
75
|
+
- 常见 DLL:`libfftw3f-3.dll`, `libfftw3f_threads-3.dll`, `libgfortran-5.dll`, `libgcc_s_seh-1.dll`, `libstdc++-6.dll`, `libwinpthread-1.dll`。
|
|
76
|
+
- 验证:运行 `npm test` 或在 CI 中尝试直接 `require()` 加载。
|
|
77
|
+
|
|
78
|
+
CI 集成(简化、可重复)
|
|
79
|
+
|
|
80
|
+
- **所有平台统一方案**:将依赖库与 `.node` 文件放在同级目录
|
|
81
|
+
- macOS:用 `dylibbundler` 自动复制并改写到 `prebuilds/darwin-*/`;输出 `otool -L` 结果到日志。
|
|
82
|
+
- Linux:安装 `patchelf`;复制 `ldd` 识别出的目标库到 `prebuilds/linux-*/` 并 `patchelf --set-rpath '$ORIGIN'`;输出 `ldd` 结果。
|
|
83
|
+
- Windows:用 `objdump` 枚举 DLL 并从 MinGW 目录复制到与 `.node` 同级;输出依赖列表。
|
|
84
|
+
|
|
85
|
+
发布前校验
|
|
86
|
+
|
|
87
|
+
- 生成 `prebuilds/*/` 后:
|
|
88
|
+
- macOS:`otool -L` 检查路径应为 `@loader_path/<lib>.dylib`(同级目录)。
|
|
89
|
+
- Linux:`ldd` 无 "not found",并且关键库解析到同目录。
|
|
90
|
+
- Windows:`objdump -p` 的 DLL 在同级目录存在。
|
|
91
|
+
|
|
92
|
+
注意事项
|
|
93
|
+
|
|
94
|
+
- macOS 改写依赖会使原签名失效,dylibbundler 会进行 ad-hoc 签名;若集成到 Electron/App,需对最终产物统一签名/公证。
|
|
95
|
+
- Linux 不要尝试捆绑或静态链接 glibc;可考虑 `-static-libstdc++ -static-libgcc` 减少 .so 数量。
|
|
96
|
+
- **所有平台统一采用同级目录方案**,避免嵌套路径导致的 `@loader_path/native/native/` 等问题(详见下方"常见陷阱")。
|
|
97
|
+
|
|
98
|
+
## 常见陷阱与解决方案
|
|
99
|
+
|
|
100
|
+
### ⚠️ 陷阱1: dylibbundler 修改了错误的文件
|
|
101
|
+
|
|
102
|
+
**问题描述**:
|
|
103
|
+
在 GitHub Actions 或 CI 环境中,可能出现以下情况:
|
|
104
|
+
1. 日志显示 `otool -L build/Release/wsjtx_lib_nodejs.node` 的依赖路径已正确修改为 `@loader_path/xxx.dylib`
|
|
105
|
+
2. 但下载到本地的 `prebuilds/darwin-arm64/wsjtx_lib_nodejs.node` 仍然是绝对路径(如 `/opt/homebrew/...`)
|
|
106
|
+
|
|
107
|
+
**根本原因**:
|
|
108
|
+
构建脚本的执行顺序错误:
|
|
109
|
+
```bash
|
|
110
|
+
# ❌ 错误的顺序
|
|
111
|
+
cp build/Release/wsjtx_lib_nodejs.node "$TARGET_DIR/" # 先复制
|
|
112
|
+
NODE_FILE="build/Release/wsjtx_lib_nodejs.node" # 指向原始文件
|
|
113
|
+
dylibbundler -x "$NODE_FILE" ... # 修改的是原始文件
|
|
114
|
+
# 结果: prebuilds/ 中的文件是修改前复制的,从未被 dylibbundler 处理过!
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**解决方案**:
|
|
118
|
+
确保 `dylibbundler` 处理的是**最终要打包的文件**:
|
|
119
|
+
```bash
|
|
120
|
+
# ✅ 正确的顺序
|
|
121
|
+
cp build/Release/wsjtx_lib_nodejs.node "$TARGET_DIR/" # 先复制
|
|
122
|
+
NODE_FILE="$TARGET_DIR/wsjtx_lib_nodejs.node" # 指向目标文件
|
|
123
|
+
dylibbundler -x "$NODE_FILE" ... # 修改目标文件
|
|
124
|
+
# 结果: prebuilds/ 中的文件就是被 dylibbundler 处理过的!
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**验证方法**:
|
|
128
|
+
```bash
|
|
129
|
+
# 在 CI 日志中,应该看到处理的是 prebuilds/ 中的文件
|
|
130
|
+
echo "Processing: $NODE_FILE" # 应该输出 prebuilds/darwin-arm64/wsjtx_lib_nodejs.node
|
|
131
|
+
|
|
132
|
+
# 验证时检查的也应该是 prebuilds/ 中的文件
|
|
133
|
+
otool -L "$NODE_FILE" # 而不是 build/Release/wsjtx_lib_nodejs.node
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### ⚠️ 陷阱2: 嵌套子目录导致的路径解析错误
|
|
137
|
+
|
|
138
|
+
**问题描述**:
|
|
139
|
+
在 Electron 应用中加载模块时报错:
|
|
140
|
+
```
|
|
141
|
+
Error: dlopen(...): Library not loaded: @loader_path/native/libfftw3f.3.dylib
|
|
142
|
+
Referenced from: .../native/libfftw3f_threads.3.dylib
|
|
143
|
+
Reason: tried: '.../native/native/libfftw3f.3.dylib' (no such file)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**根本原因**:
|
|
147
|
+
使用 `native/` 子目录时,dylib 之间的依赖路径解析出现嵌套:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
目录结构:
|
|
151
|
+
prebuilds/darwin-arm64/
|
|
152
|
+
├── wsjtx_lib_nodejs.node
|
|
153
|
+
└── native/
|
|
154
|
+
├── libfftw3f.3.dylib
|
|
155
|
+
└── libfftw3f_threads.3.dylib
|
|
156
|
+
|
|
157
|
+
libfftw3f_threads.3.dylib 的依赖:
|
|
158
|
+
@loader_path/native/libfftw3f.3.dylib
|
|
159
|
+
|
|
160
|
+
解析过程:
|
|
161
|
+
@loader_path = native/ (libfftw3f_threads.3.dylib 所在目录)
|
|
162
|
+
@loader_path/native/libfftw3f.3.dylib = native/native/libfftw3f.3.dylib ❌
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
这是因为 `dylibbundler` 在处理时:
|
|
166
|
+
- 修改 `.node` 文件的依赖 → `@loader_path/native/xxx.dylib` ✅ 正确
|
|
167
|
+
- 修改 `native/` 目录下的 dylib 依赖 → **也是** `@loader_path/native/xxx.dylib` ❌ 错误!
|
|
168
|
+
|
|
169
|
+
但当 dylib **自己在 native/ 目录**时,`@loader_path` 就是 `native/` 本身,再加上 `/native/` 就变成了 `native/native/`。
|
|
170
|
+
|
|
171
|
+
**解决方案**:
|
|
172
|
+
**不使用子目录**,将所有 dylib 与 `.node` 文件放在同级目录:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# macOS
|
|
176
|
+
dylibbundler -x "$TARGET_DIR/wsjtx_lib_nodejs.node" \
|
|
177
|
+
-d "$TARGET_DIR" \ # 同级目录
|
|
178
|
+
-p "@loader_path/" \ # 不加 native/
|
|
179
|
+
-s /opt/homebrew/opt/fftw/lib \
|
|
180
|
+
-b
|
|
181
|
+
|
|
182
|
+
# Linux
|
|
183
|
+
patchelf --set-rpath '$ORIGIN' "$TARGET_DIR/wsjtx_lib_nodejs.node" # 不加 /native
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
这样所有依赖都是 `@loader_path/xxx.dylib`,解析到同目录,简单清晰。
|
|
187
|
+
|
|
188
|
+
### ⚠️ 陷阱3: Windows prebuildify spawn EINVAL 错误
|
|
189
|
+
|
|
190
|
+
**问题描述**:
|
|
191
|
+
在 Windows CI 环境中运行 `prebuildify --napi --strip` 时失败:
|
|
192
|
+
```
|
|
193
|
+
Error: spawn EINVAL
|
|
194
|
+
at ChildProcess.spawn (node:internal/child_process:414:11)
|
|
195
|
+
at Object.spawn (node:child_process:761:9)
|
|
196
|
+
at prebuildify/index.js:212:22
|
|
197
|
+
errno: -4071,
|
|
198
|
+
code: 'EINVAL',
|
|
199
|
+
syscall: 'spawn'
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**根本原因**:
|
|
203
|
+
1. Windows 上的 `node-gyp` 是 `.cmd` 批处理文件,不是可执行程序
|
|
204
|
+
2. Node.js 的 `child_process.spawn()` 默认**不能**直接执行 `.cmd` 文件
|
|
205
|
+
3. 需要通过 `cmd.exe /c` 包装执行,或设置 `{shell: true}` 选项
|
|
206
|
+
4. `prebuildify` 内部使用 `proc.spawn(opts.nodeGyp, args, { stdio: 'inherit' })` 调用 node-gyp
|
|
207
|
+
5. 没有设置 `shell: true`,导致在 Windows 上抛出 `EINVAL` 错误
|
|
208
|
+
|
|
209
|
+
**技术细节**:
|
|
210
|
+
```javascript
|
|
211
|
+
// prebuildify 内部代码(简化)
|
|
212
|
+
var child = proc.spawn(opts.nodeGyp, args, {
|
|
213
|
+
cwd: opts.cwd,
|
|
214
|
+
env: opts.env,
|
|
215
|
+
stdio: opts.quiet ? 'ignore' : 'inherit'
|
|
216
|
+
// ❌ 缺少 shell: true
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// 在 Windows 上,opts.nodeGyp 通常是 "node-gyp.cmd"
|
|
220
|
+
// spawn 无法直接执行 .cmd 文件,导致 EINVAL 错误
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**为什么常规方案无效**:
|
|
224
|
+
|
|
225
|
+
尝试 1 - 在 workflow 中设置 shell:
|
|
226
|
+
```yaml
|
|
227
|
+
# ❌ 无效:只影响 workflow 步骤,不影响 prebuildify 内部的 spawn
|
|
228
|
+
- name: Generate prebuilds
|
|
229
|
+
shell: cmd
|
|
230
|
+
run: npm run prebuild
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
尝试 2 - 设置环境变量:
|
|
234
|
+
```yaml
|
|
235
|
+
# ❌ 无效:只改变路径,不解决 spawn 无法执行 .cmd 的问题
|
|
236
|
+
- run: |
|
|
237
|
+
set PREBUILD_NODE_GYP=node-gyp.cmd
|
|
238
|
+
npx prebuildify --napi --strip
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
尝试 3 - 直接调用 node-gyp:
|
|
242
|
+
```yaml
|
|
243
|
+
# ⚠️ 部分有效:绕过 prebuildify,但失去其便利性
|
|
244
|
+
- run: |
|
|
245
|
+
node-gyp rebuild
|
|
246
|
+
# 需要手动创建 prebuilds 目录结构
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**正确解决方案**:
|
|
250
|
+
通过 **monkey-patching** `child_process.spawn` 来拦截和修复 `.cmd` 调用。
|
|
251
|
+
|
|
252
|
+
创建 `scripts/run-prebuildify.js`:
|
|
253
|
+
```javascript
|
|
254
|
+
#!/usr/bin/env node
|
|
255
|
+
// Windows-safe prebuildify runner that wraps child_process.spawn for node-gyp
|
|
256
|
+
const os = require('os');
|
|
257
|
+
const path = require('path');
|
|
258
|
+
const cp = require('child_process');
|
|
259
|
+
|
|
260
|
+
// 保存原始的 spawn 函数
|
|
261
|
+
const origSpawn = cp.spawn;
|
|
262
|
+
|
|
263
|
+
// 替换 spawn 函数
|
|
264
|
+
cp.spawn = function patchedSpawn(command, args, options) {
|
|
265
|
+
if (process.platform === 'win32') {
|
|
266
|
+
// 检测是否是 .cmd 文件或 node-gyp
|
|
267
|
+
const isCmd = /\.cmd$/i.test(command) || /node-gyp(\.js)?$/i.test(command);
|
|
268
|
+
if (isCmd) {
|
|
269
|
+
// 通过 cmd.exe 包装执行
|
|
270
|
+
const cmdExe = process.env.ComSpec || 'cmd.exe';
|
|
271
|
+
const cmdline = [command].concat(args || []).join(' ');
|
|
272
|
+
return origSpawn(cmdExe, ['/d', '/s', '/c', cmdline],
|
|
273
|
+
Object.assign({ stdio: 'inherit' }, options, { shell: false }));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// 非 Windows 或非 .cmd 文件,使用原始 spawn
|
|
277
|
+
return origSpawn(command, args, options);
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// 在 patch 之后调用 prebuildify
|
|
281
|
+
const prebuildify = require('prebuildify');
|
|
282
|
+
|
|
283
|
+
const opts = {
|
|
284
|
+
napi: true,
|
|
285
|
+
strip: true,
|
|
286
|
+
cwd: process.cwd(),
|
|
287
|
+
arch: process.env.PREBUILD_ARCH || os.arch(),
|
|
288
|
+
platform: process.env.PREBUILD_PLATFORM || os.platform(),
|
|
289
|
+
nodeGyp: process.env.PREBUILD_NODE_GYP || path.join(
|
|
290
|
+
process.cwd(),
|
|
291
|
+
'node_modules',
|
|
292
|
+
'.bin',
|
|
293
|
+
process.platform === 'win32' ? 'node-gyp.cmd' : 'node-gyp'
|
|
294
|
+
)
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
prebuildify(opts, (err) => {
|
|
298
|
+
if (err) {
|
|
299
|
+
console.error(err.stack || err.message || String(err));
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
console.log('prebuildify completed successfully');
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
在 GitHub Actions workflow 中使用:
|
|
307
|
+
```yaml
|
|
308
|
+
- name: Generate prebuilds (Windows)
|
|
309
|
+
if: matrix.os == 'windows-latest'
|
|
310
|
+
run: node scripts/run-prebuildify.js
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**工作流程**:
|
|
314
|
+
1. `run-prebuildify.js` 首先 patch `child_process.spawn` 函数
|
|
315
|
+
2. 然后调用 `prebuildify(opts, callback)`
|
|
316
|
+
3. `prebuildify` 内部尝试 `spawn('node-gyp.cmd', args, ...)`
|
|
317
|
+
4. 被 patch 的 `spawn` 自动检测到 `.cmd` 文件
|
|
318
|
+
5. 自动转换为 `spawn('cmd.exe', ['/c', 'node-gyp.cmd', ...args], ...)`
|
|
319
|
+
6. 成功执行 ✅
|
|
320
|
+
|
|
321
|
+
**为什么这个方案有效**:
|
|
322
|
+
- ✅ 在 prebuildify 执行之前注入补丁
|
|
323
|
+
- ✅ 所有通过 prebuildify 的 spawn 调用都会被自动修复
|
|
324
|
+
- ✅ 对 Linux/macOS 透明(不影响其他平台)
|
|
325
|
+
- ✅ 无需修改 prebuildify 源码
|
|
326
|
+
- ✅ 经过生产环境验证(node-hamlib v0.1.22+)
|
|
327
|
+
|
|
328
|
+
**注意事项**:
|
|
329
|
+
1. 这个脚本**必须**在调用 prebuildify 之前执行 patch
|
|
330
|
+
2. 不能使用 `npm run prebuild` 直接调用 prebuildify,因为那样 patch 不会生效
|
|
331
|
+
3. 必须使用 `node scripts/run-prebuildify.js` 来执行
|
|
332
|
+
4. 这是 Windows 平台特有的解决方案,Linux/macOS 不需要
|
|
333
|
+
|
|
334
|
+
**验证方法**:
|
|
335
|
+
```bash
|
|
336
|
+
# 本地 Windows 测试
|
|
337
|
+
node scripts/run-prebuildify.js
|
|
338
|
+
|
|
339
|
+
# 应该看到输出:
|
|
340
|
+
# prebuildify completed successfully
|
|
341
|
+
# prebuilds/win32-x64/node.napi.node 已创建
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**历史教训**:
|
|
345
|
+
在重构打包流程时,如果删除了这个脚本,Windows 构建会立即失败。这是一个**不可替代**的关键脚本,直到 prebuildify 官方修复这个问题为止。
|
|
346
|
+
|
|
347
|
+
参考命令均已集成到 GitHub Actions,避免自定义脚本堆叠;特殊场景请在本机按上述工具命令单步调试验证。
|
package/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Connection information interface
|
|
3
5
|
*/
|
|
@@ -152,6 +154,63 @@ interface MemoryChannelInfo {
|
|
|
152
154
|
ctcssTone?: number;
|
|
153
155
|
}
|
|
154
156
|
|
|
157
|
+
interface SpectrumScopeInfo {
|
|
158
|
+
id: number;
|
|
159
|
+
name: string;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
interface SpectrumModeInfo {
|
|
163
|
+
id: number;
|
|
164
|
+
name: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
interface SpectrumAverageModeInfo {
|
|
168
|
+
id: number;
|
|
169
|
+
name: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
interface SpectrumLine {
|
|
173
|
+
scopeId: number;
|
|
174
|
+
dataLevelMin: number;
|
|
175
|
+
dataLevelMax: number;
|
|
176
|
+
signalStrengthMin: number;
|
|
177
|
+
signalStrengthMax: number;
|
|
178
|
+
mode: number;
|
|
179
|
+
centerFreq: number;
|
|
180
|
+
spanHz: number;
|
|
181
|
+
lowEdgeFreq: number;
|
|
182
|
+
highEdgeFreq: number;
|
|
183
|
+
dataLength: number;
|
|
184
|
+
data: Buffer;
|
|
185
|
+
timestamp: number;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
interface SpectrumCapabilities {
|
|
189
|
+
asyncDataSupported?: boolean;
|
|
190
|
+
scopes: SpectrumScopeInfo[];
|
|
191
|
+
modes: SpectrumModeInfo[];
|
|
192
|
+
spans: number[];
|
|
193
|
+
avgModes: SpectrumAverageModeInfo[];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
interface SpectrumSupportSummary extends SpectrumCapabilities {
|
|
197
|
+
supported: boolean;
|
|
198
|
+
asyncDataSupported: boolean;
|
|
199
|
+
hasSpectrumFunction: boolean;
|
|
200
|
+
hasSpectrumHoldFunction: boolean;
|
|
201
|
+
hasTransceiveFunction: boolean;
|
|
202
|
+
configurableLevels: string[];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
interface SpectrumConfig {
|
|
206
|
+
hold?: boolean;
|
|
207
|
+
mode?: number;
|
|
208
|
+
spanHz?: number;
|
|
209
|
+
speed?: number;
|
|
210
|
+
referenceLevel?: number;
|
|
211
|
+
averageMode?: number;
|
|
212
|
+
}
|
|
213
|
+
|
|
155
214
|
/**
|
|
156
215
|
* Split mode info interface
|
|
157
216
|
*/
|
|
@@ -178,8 +237,10 @@ interface SplitStatusInfo {
|
|
|
178
237
|
type LevelType = 'AF' | 'RF' | 'SQL' | 'RFPOWER' | 'MICGAIN' | 'IF' | 'APF' | 'NR' |
|
|
179
238
|
'PBT_IN' | 'PBT_OUT' | 'CWPITCH' | 'KEYSPD' | 'NOTCHF' | 'COMP' |
|
|
180
239
|
'AGC' | 'BKINDL' | 'BALANCE' | 'VOXGAIN' | 'VOXDELAY' | 'ANTIVOX' |
|
|
181
|
-
'STRENGTH' | 'RAWSTR' | 'SWR' | 'ALC' | 'RFPOWER_METER' |
|
|
182
|
-
'COMP_METER' | 'VD_METER' | 'ID_METER' | 'TEMP_METER' |
|
|
240
|
+
'STRENGTH' | 'RAWSTR' | 'SWR' | 'ALC' | 'RFPOWER_METER' | 'RFPOWER_METER_WATTS' |
|
|
241
|
+
'COMP_METER' | 'VD_METER' | 'ID_METER' | 'TEMP_METER' |
|
|
242
|
+
'SPECTRUM_MODE' | 'SPECTRUM_SPAN' | 'SPECTRUM_EDGE_LOW' | 'SPECTRUM_EDGE_HIGH' |
|
|
243
|
+
'SPECTRUM_SPEED' | 'SPECTRUM_REF' | 'SPECTRUM_AVG' | 'SPECTRUM_ATT' | string;
|
|
183
244
|
|
|
184
245
|
/**
|
|
185
246
|
* Function type
|
|
@@ -187,7 +248,8 @@ type LevelType = 'AF' | 'RF' | 'SQL' | 'RFPOWER' | 'MICGAIN' | 'IF' | 'APF' | 'N
|
|
|
187
248
|
type FunctionType = 'FAGC' | 'NB' | 'COMP' | 'VOX' | 'TONE' | 'TSQL' | 'SBKIN' |
|
|
188
249
|
'FBKIN' | 'ANF' | 'NR' | 'AIP' | 'APF' | 'TUNER' | 'XIT' |
|
|
189
250
|
'RIT' | 'LOCK' | 'MUTE' | 'VSC' | 'REV' | 'SQL' | 'ABM' |
|
|
190
|
-
'BC' | 'MBC' | 'AFC' | 'SATMODE' | 'SCOPE' | 'RESUME' |
|
|
251
|
+
'BC' | 'MBC' | 'AFC' | 'SATMODE' | 'SCOPE' | 'RESUME' | 'TRANSCEIVE' |
|
|
252
|
+
'SPECTRUM' | 'SPECTRUM_HOLD' |
|
|
191
253
|
'TBURST' | string;
|
|
192
254
|
|
|
193
255
|
/**
|
|
@@ -278,7 +340,7 @@ interface SerialConfigOptions {
|
|
|
278
340
|
/**
|
|
279
341
|
* HamLib class - for controlling amateur radio devices
|
|
280
342
|
*/
|
|
281
|
-
declare class HamLib {
|
|
343
|
+
declare class HamLib extends EventEmitter {
|
|
282
344
|
/**
|
|
283
345
|
* Constructor
|
|
284
346
|
* @param model Radio model number (execute rigctl -l to find your device model number)
|
|
@@ -1193,7 +1255,7 @@ declare class HamLib {
|
|
|
1193
1255
|
*/
|
|
1194
1256
|
reset(resetType?: 'NONE' | 'SOFT' | 'VFO' | 'MCALL' | 'MASTER'): Promise<number>;
|
|
1195
1257
|
|
|
1196
|
-
// ===== Rig Info /
|
|
1258
|
+
// ===== Rig Info / Spectrum / Conf (async) =====
|
|
1197
1259
|
|
|
1198
1260
|
/**
|
|
1199
1261
|
* Get rig identification info (model, firmware version, etc.)
|
|
@@ -1210,6 +1272,62 @@ declare class HamLib {
|
|
|
1210
1272
|
*/
|
|
1211
1273
|
sendRaw(data: Buffer, replyMaxLen: number, terminator?: Buffer): Promise<Buffer>;
|
|
1212
1274
|
|
|
1275
|
+
/**
|
|
1276
|
+
* Get official Hamlib spectrum metadata exposed by the backend.
|
|
1277
|
+
*/
|
|
1278
|
+
getSpectrumCapabilities(): Promise<SpectrumCapabilities>;
|
|
1279
|
+
|
|
1280
|
+
/**
|
|
1281
|
+
* Summarize whether official Hamlib spectrum streaming is usable.
|
|
1282
|
+
*/
|
|
1283
|
+
getSpectrumSupportSummary(): Promise<SpectrumSupportSummary>;
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* Apply spectrum-related settings using official Hamlib level/function APIs.
|
|
1287
|
+
*/
|
|
1288
|
+
configureSpectrum(config?: SpectrumConfig): Promise<SpectrumSupportSummary>;
|
|
1289
|
+
|
|
1290
|
+
/**
|
|
1291
|
+
* Start receiving official Hamlib spectrum line events.
|
|
1292
|
+
*/
|
|
1293
|
+
startSpectrumStream(callback?: (line: SpectrumLine) => void): Promise<boolean>;
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* Stop receiving official Hamlib spectrum line events.
|
|
1297
|
+
*/
|
|
1298
|
+
stopSpectrumStream(): Promise<boolean>;
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* High-level managed spectrum startup.
|
|
1302
|
+
*/
|
|
1303
|
+
startManagedSpectrum(config?: SpectrumConfig): Promise<boolean>;
|
|
1304
|
+
|
|
1305
|
+
/**
|
|
1306
|
+
* High-level managed spectrum shutdown.
|
|
1307
|
+
*/
|
|
1308
|
+
stopManagedSpectrum(): Promise<boolean>;
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* Listen for official spectrum line events.
|
|
1312
|
+
*/
|
|
1313
|
+
on(event: 'spectrumLine', listener: (line: SpectrumLine) => void): this;
|
|
1314
|
+
once(event: 'spectrumLine', listener: (line: SpectrumLine) => void): this;
|
|
1315
|
+
off(event: 'spectrumLine', listener: (line: SpectrumLine) => void): this;
|
|
1316
|
+
|
|
1317
|
+
/**
|
|
1318
|
+
* Listen for managed spectrum state changes.
|
|
1319
|
+
*/
|
|
1320
|
+
on(event: 'spectrumStateChanged', listener: (state: { active: boolean }) => void): this;
|
|
1321
|
+
once(event: 'spectrumStateChanged', listener: (state: { active: boolean }) => void): this;
|
|
1322
|
+
off(event: 'spectrumStateChanged', listener: (state: { active: boolean }) => void): this;
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* Listen for asynchronous spectrum errors.
|
|
1326
|
+
*/
|
|
1327
|
+
on(event: 'spectrumError', listener: (error: Error) => void): this;
|
|
1328
|
+
once(event: 'spectrumError', listener: (error: Error) => void): this;
|
|
1329
|
+
off(event: 'spectrumError', listener: (error: Error) => void): this;
|
|
1330
|
+
|
|
1213
1331
|
/**
|
|
1214
1332
|
* Set a backend configuration parameter
|
|
1215
1333
|
* @param name Configuration token name
|
|
@@ -1409,7 +1527,7 @@ interface VfoInfo {
|
|
|
1409
1527
|
}
|
|
1410
1528
|
|
|
1411
1529
|
/**
|
|
1412
|
-
*
|
|
1530
|
+
* hamlib module export object
|
|
1413
1531
|
*/
|
|
1414
1532
|
declare const nodeHamlib: {
|
|
1415
1533
|
HamLib: typeof HamLib;
|
|
@@ -1420,9 +1538,11 @@ export { ConnectionInfo, ModeInfo, SupportedRigInfo, AntennaInfo, VFO, RadioMode
|
|
|
1420
1538
|
MemoryChannelInfo, SplitModeInfo, SplitStatusInfo, LevelType, FunctionType,
|
|
1421
1539
|
ScanType, VfoOperationType, SerialConfigParam, SerialBaudRate, SerialParity,
|
|
1422
1540
|
SerialHandshake, SerialControlState, PttType, DcdType, SerialConfigOptions,
|
|
1541
|
+
SpectrumScopeInfo, SpectrumModeInfo, SpectrumAverageModeInfo, SpectrumLine,
|
|
1542
|
+
SpectrumCapabilities, SpectrumSupportSummary, SpectrumConfig,
|
|
1423
1543
|
ClockInfo, VfoInfo, HamLib };
|
|
1424
1544
|
|
|
1425
1545
|
// Support both CommonJS and ES module exports
|
|
1426
1546
|
// @ts-ignore
|
|
1427
1547
|
export = nodeHamlib;
|
|
1428
|
-
export default nodeHamlib;
|
|
1548
|
+
export default nodeHamlib;
|