Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,25 +163,24 @@ impl Drop for Emitter {
}
}

async fn list_devices(error_prefix: &'static str) -> Result<Vec<nusb::DeviceInfo>> {
async fn list_devices() -> Result<Vec<nusb::DeviceInfo>> {
run_blocking(move || {
nusb::list_devices()
Ok(nusb::list_devices()
.wait()
.map(|devices| devices.collect::<Vec<_>>())
.map_err(|e| format!("{error_prefix} error: {e}"))
.map_or(Vec::new(), |devices| devices.collect::<Vec<_>>()))
})
.await
}

#[napi(js_name = "nativeGetDevices")]
pub async fn getDevices() -> Result<Vec<UsbDevice>> {
let devices = list_devices("getDevices").await?;
let devices = list_devices().await?;
Ok(devices.into_iter().map(UsbDevice::new).collect())
}

#[napi(js_name = "nativeFindDeviceByIds")]
pub async fn findDeviceByIds(vendorId: u16, productId: u16) -> Result<Option<UsbDevice>> {
let device = list_devices("findDeviceByIds")
let device = list_devices()
.await?
.into_iter()
.find(|dev| dev.vendor_id() == vendorId && dev.product_id() == productId);
Expand All @@ -190,7 +189,7 @@ pub async fn findDeviceByIds(vendorId: u16, productId: u16) -> Result<Option<Usb

#[napi(js_name = "nativeFindDeviceBySerial")]
pub async fn findDeviceBySerial(serialNumber: String) -> Result<Option<UsbDevice>> {
let device = list_devices("findDeviceBySerial")
let device = list_devices()
.await?
.into_iter()
.find(|dev| dev.serial_number() == Some(serialNumber.as_str()));
Expand Down
27 changes: 27 additions & 0 deletions test/webusb.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,33 @@ describe('WebUSB Module', () => {
it('should describe basic constants', () => {
assert.notEqual(webusb, undefined, 'webusb must be undefined');
});

it('should not throw when the hotplug watcher cannot start', async () => {
const customWebusb = new WebUSB();
const listener = () => {};
const expectedError = new Error('watch devices error');
const warnings = [];
const originalWarn = console.warn;

customWebusb.nativeEmitter = {
addAttach: async () => { throw expectedError; },
addDetach: async () => {},
removeAttach: async () => {},
removeDetach: async () => {},
};

console.warn = (...args) => warnings.push(args);

try {
customWebusb.addEventListener('connect', listener);
await new Promise(resolve => setImmediate(resolve));

assert.ok(warnings.some(args => args[0] === 'usb: hotplug watcher error:' && args[1] === expectedError));
} finally {
customWebusb.removeEventListener('connect', listener);
console.warn = originalWarn;
}
});
});

describe('allowedDevices', () => {
Expand Down
8 changes: 6 additions & 2 deletions tsc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ class WebUSB extends EventTarget implements USB {
private queueNativeEmitterOperation(operation: () => Promise<void>): void {
const pending = this.nativeEmitterQueue.then(operation);
this.nativeEmitterQueue = pending.catch(() => {});
pending.catch(error => setTimeout(() => { throw error; }, 0));
// A hotplug watcher that can't start (e.g. no netlink in a container) must
// not crash the host process, so report it rather than rethrowing.
pending.catch(error => console.warn('usb: hotplug watcher error:', error));
}

private deviceConnectCallback = async (device: UsbDevice) => {
Expand Down Expand Up @@ -194,7 +196,9 @@ class WebUSB extends EventTarget implements USB {
await this.nativeEmitter.addAttach(this.deviceConnectCallback);
await this.nativeEmitter.addDetach(this.deviceDisconnectCallback);
});
nativeGetDevices().then(devices => devices.forEach(device => this.knownDevices.set(device.handle, device)));
nativeGetDevices()
.then(devices => devices.forEach(device => this.knownDevices.set(device.handle, device)))
.catch(error => console.warn('usb: failed to list devices for hotplug:', error));
}
}

Expand Down
Loading