npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

darwin-corebluetooth-connected-peripherals-recovery

v0.1.1

Published

Enumerate the CoreBluetooth peripherals already connected to macOS by service UUID — the non-scan path for devices that stopped advertising. Node-API v8, prebuilt for darwin-arm64 and darwin-x64.

Readme

darwin-corebluetooth-connected-peripherals-recovery

查询 macOS 上系统已连接的 CoreBluetooth 外设(按 Service UUID 过滤)。Node-API v8,随包提供 darwin-arm64 / darwin-x64 prebuild。

const { findConnectedPeripherals } = require("darwin-corebluetooth-connected-peripherals-recovery");

const peripherals = await findConnectedPeripherals(["AB5E0001-5A21-4F05-BC7D-AF01F617B664"]);
// [ { id: 'FCC65679-E122-7B92-1EDD-0214EB717DFE', name: '小米蓝牙语音遥控器' } ]

为什么需要它

BLE 外设一旦被连接通常就停止广播,此时主动扫描永远扫不到它(已连接的 HID 遥控器、手环都常见)。CoreBluetooth 为此提供了非扫描入口:

[self.central retrieveConnectedPeripheralsWithServices:@[serviceUUID]];

问题是不是每个 BLE 抽象层都会用到它。例如 unified-ble-manager 4.0.27 的 CoreBluetooth backend 刻意不注册 address targeting(Apple 平台无法从蓝牙地址发起连接),也不暴露 connected-peripheral 查询,于是"设备已连接 → 不再广播 → 扫描扫不到"就成了一条死路。这个包把那一个调用单独暴露出来,供这类抽象层补一条合成 advertisement 或做诊断。

对照:Linux 的 BlueZ 不需要这层(Device1 对象在 ObjectManager 里天然可见,UBM 已注册 peer:address-targeting);Windows 也有同类缺口(WinRT 有 FromBluetoothAddressAsync,但同样取决于抽象层有没有用)。

平台支持

| 平台 | 状态 | | --- | --- | | macOS (darwin-arm64 / darwin-x64) | ✅ 已实机验证 | | 其它平台 | ❌ findConnectedPeripherals() 抛错(fail-closed,不做降级) |

包本身可以安装在任何平台(npm install 在非 macOS 上是干净的 no-op),但只有 macOS 上有实现。

安装

npm install darwin-corebluetooth-connected-peripherals-recovery

发布版带 prebuild,安装时不会编译任何东西,也不会落到源码构建:发布包不含 binding.gyp / src/(见下方"为什么发布包不带 binding.gyp")。因此第三方工具也不会替它编译——@electron/rebuild(即 electron-builder install-app-deps、electron-rebuild)只按"包的根目录里有没有 binding.gyp"判定原生模块,看不到它就直接跳过。源码构建只存在于源码检出(需要 Xcode Command Line Tools)。

首次调用会弹出系统蓝牙授权;未授权时调用以明确错误失败,不会静默返回空数组。

API

findConnectedPeripherals(serviceUuids, options?)

返回系统当前已连接、且在广播里声明过任一给定 Service 的外设。

findConnectedPeripherals(
  serviceUuids: readonly string[],     // 至少一个 CBUUID 字符串(128 位或 16/32 位短形式)
  options?: { timeoutMs?: number },    // 等待 CoreBluetooth 就绪的上限,默认 5000;0 表示不设上限
): Promise<readonly { id: string, name: string | null }[]>
  • id 是 CBPeripheral.identifier.UUIDString:由 host 与设备共同派生、只在当前这台 Mac 上有意义的不透明标识。它不是蓝牙硬件地址,两者之间没有可换算关系(Apple 不提供该 API)。
  • name 在外设没有可用名称时为 null。
  • timeoutMs 是必需的健壮性上限:适配器可能长时间停在非终态(例如未授权后回调不再到来),没有它 Promise 会永远 pending。超时以 CoreBluetooth did not report a usable adapter state within N ms 拒绝。
  • 参数非法(空数组、非字符串元素、负数 timeout)抛 TypeError,且先于原生模块加载——非 macOS 上也能拿到参数错误。
  • 返回的数组和每个元素都是冻结的。

capabilities

const { capabilities } = require("darwin-corebluetooth-connected-peripherals-recovery");
// darwin → { platform: 'darwin', status: 'supported', backend: 'corebluetooth', reason: null }

不加载原生模块,可在任何平台上安全地做特性判断。

resolveArtifactPath()

返回当前会被加载的 .node 路径(没有则为 null)。只做路径判断、不加载模块,用于诊断"到底加载了哪个产物"。设 DARWIN_CB_CONNECTED_PERIPHERALS_SOURCE=1 可让本地 build/ 产物优先于 prebuild。

接到一个 BLE 抽象层上

典型用法是补一条合成 advertisement —— 外设本来就不在广播,所以把非扫描查询的结果伪装成一次广播事件,交给上层原有的 connect/GATT 流程。以 unified-ble-manager 的 CoreBluetooth boundary 为例:

const { createConnectedPeripheralRecoveryBoundary } = require("./your-boundary-adapter.cjs");
// CoreBluetooth 的 advertisement 形状是 { nativePeerId, localName, serviceUuids, rssi, ... }
// nativePeerId 必须填入上面拿到的 id,UBM 之后会用它 connect

注意各家抽象层的 advertisement 形状并不通用:WinRT 的必需字段里有 scanToken,且没有 manufacturerData/serviceData。适配层要按目标平台单独写。

构建与发布

npm install                # 安装 devDependencies(node-addon-api、node-gyp)
npm run build:prebuilds    # 依次构建 darwin-arm64 / darwin-x64 并用 lipo 复核架构
npm test                   # 需要真实已连接的设备;无设备时相关用例只断言结构

产物布局遵循 Node 原生模块的 prebuild 约定:

prebuilds/darwin-arm64/corebluetooth_connected_peripherals.node
prebuilds/darwin-x64/corebluetooth_connected_peripherals.node

prebuilds/ 会提交进仓库并随包发布;build/ 是本地中间产物,已忽略。

为什么发布包不带 binding.gyp

binding.gyp 与 src/ 只存在于源码检出,不随 npm 包发布(package.json 的 files 是白名单,没列进去就不会打包)。

原因是 @electron/rebuild 判定"这个模块需要重建"的唯一依据,就是模块根目录里有没有 binding.gyp:

// electron/rebuild → Rebuilder.rebuild()
const nativeModulePaths = candidatePaths.filter((p) =>
  fs.existsSync(path.resolve(p, "binding.gyp")),
);

它不看平台、不看 os 字段,也不看本包的 install 脚本。所以只要发布包里带着 binding.gyp,消费者在 Linux / Windows 上做 Electron 打包时就会被强行 node-gyp rebuild,撞上 src/addon_stub.cc 的 #error,把对方的 CI 打挂——而本包在非 macOS 上本来就没有可编译的东西。darwin-arm64 / darwin-x64 是 Node 在 macOS 上的全部目标,两者都随包发布,因此"安装时源码构建"对消费者不可达;去掉它换来的是"任何平台上都没有工具会去编译这个包",顺带 macOS 上也不再有重复编译。

同理,Node-API v8 的产物与运行时 ABI 无关,Electron 直接复用随包的 .node,不需要按运行时/ABI 铺矩阵。⚠️ 打包 Electron 时 .node 必须保持在 asar 之外,并纳入签名/公证流程。

边界

这个包只做查询:不配对、不连接、不做 GATT 服务发现与订阅、不读取任何特征值。拿到 id 之后的一切都由调用方的 BLE 栈负责。

License

MIT