Socket
Socket
Sign inDemoInstall

usb

Package Overview
Dependencies
Maintainers
3
Versions
96
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

usb - npm Package Compare versions

Comparing version 2.0.0-alpha.2 to 2.0.1

CHANGELOG.md

13

dist/usb/bindings.d.ts

@@ -18,2 +18,11 @@ /// <reference types="node" />

export declare function _disableHotplugEvents(): void;
export declare function _getLibusbCapability(capability: number): number;
/**
* Restore (re-reference) the hotplug events unreferenced by `unrefHotplugEvents()`
*/
export declare function refHotplugEvents(): void;
/**
* Unreference the hotplug events from the event loop, allowing the process to exit even when listening for the `attach` and `detach` events
*/
export declare function unrefHotplugEvents(): void;
/** Represents a USB transfer */

@@ -216,2 +225,6 @@ export declare class Transfer {

export declare const LIBUSB_DT_BOS_SIZE: number;
export declare const LIBUSB_CAP_HAS_CAPABILITY: number;
export declare const LIBUSB_CAP_HAS_HOTPLUG: number;
export declare const LIBUSB_CAP_HAS_HID_ACCESS: number;
export declare const LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER: number;
/** Input/output error */

@@ -218,0 +231,0 @@ export declare const LIBUSB_ERROR_IO: number;

3

dist/usb/bindings.js

@@ -6,5 +6,6 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
var path_1 = require("path");
/* eslint-disable @typescript-eslint/no-var-requires */
var usb = require('bindings')('usb_bindings');
var usb = require('node-gyp-build')(path_1.join(__dirname, '..', '..'));
module.exports = usb;
//# sourceMappingURL=bindings.js.map
"use strict";
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var events_1 = require("events");

@@ -13,2 +24,65 @@ var device_1 = require("./device");

});
// Polling mechanism for discovering device changes until this is fixed:
// https://github.com/libusb/libusb/issues/86
var pollTimeout = 500;
var hotplugSupported = usb._getLibusbCapability(usb.LIBUSB_CAP_HAS_HOTPLUG) > 0;
var pollingHotplug = false;
var pollDevices = [];
var pollHotplug = function (start) {
var e_1, _a, e_2, _b;
if (start === void 0) { start = false; }
if (start) {
pollingHotplug = true;
}
else if (!pollingHotplug) {
return;
}
var devices = usb.getDeviceList();
if (!start) {
var _loop_1 = function (device) {
var found = pollDevices.find(function (item) { return item.deviceAddress === device.deviceAddress; });
if (!found) {
usb.emit('attach', device);
}
};
try {
// Find attached devices
for (var devices_1 = __values(devices), devices_1_1 = devices_1.next(); !devices_1_1.done; devices_1_1 = devices_1.next()) {
var device = devices_1_1.value;
_loop_1(device);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (devices_1_1 && !devices_1_1.done && (_a = devices_1.return)) _a.call(devices_1);
}
finally { if (e_1) throw e_1.error; }
}
var _loop_2 = function (device) {
var found = devices.find(function (item) { return item.deviceAddress === device.deviceAddress; });
if (!found) {
usb.emit('detach', device);
}
};
try {
// Find detached devices
for (var pollDevices_1 = __values(pollDevices), pollDevices_1_1 = pollDevices_1.next(); !pollDevices_1_1.done; pollDevices_1_1 = pollDevices_1.next()) {
var device = pollDevices_1_1.value;
_loop_2(device);
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (pollDevices_1_1 && !pollDevices_1_1.done && (_b = pollDevices_1.return)) _b.call(pollDevices_1);
}
finally { if (e_2) throw e_2.error; }
}
}
pollDevices = devices;
setTimeout(function () {
pollHotplug();
}, pollTimeout);
};
usb.on('newListener', function (event) {

@@ -20,3 +94,8 @@ if (event !== 'attach' && event !== 'detach') {

if (listenerCount === 0) {
usb._enableHotplugEvents();
if (hotplugSupported) {
usb._enableHotplugEvents();
}
else {
pollHotplug(true);
}
}

@@ -30,3 +109,8 @@ });

if (listenerCount === 0) {
usb._disableHotplugEvents();
if (hotplugSupported) {
usb._disableHotplugEvents();
}
else {
pollingHotplug = false;
}
}

@@ -33,0 +117,0 @@ });

/// <reference types="w3c-web-usb" />
import { TypedEventTarget } from './typed-event-target';
/// <reference types="node" />
import { EventEmitter } from 'events';
/**

@@ -8,25 +9,33 @@ * USB Options

/**
* A `device found` callback function to allow the user to select a device
* Optional `device found` callback function to allow the user to select a device
*/
devicesFound?: (devices: Array<USBDevice>) => Promise<USBDevice | void>;
}
/**
* @hidden
*/
export declare type USBEvents = {
devicesFound?: (devices: USBDevice[]) => Promise<USBDevice | void>;
/**
* USBDevice connected event
* Optional array of preconfigured allowed devices
*/
connect: USBConnectionEvent;
allowedDevices?: USBDeviceFilter[];
/**
* USBDevice disconnected event
* Optional flag to automatically allow all devices
*/
disconnect: USBConnectionEvent;
};
export declare class WebUSB extends TypedEventTarget<USBEvents> implements USB {
allowAllDevices?: boolean;
/**
* Optional timeout (in milliseconds) to use for the device control transfers
*/
deviceTimeout?: number;
}
export declare class WebUSB implements USB {
private options;
private allowedDevices;
protected emitter: EventEmitter;
protected knownDevices: Map<string, USBDevice>;
protected allowedDevices: USBDeviceFilter[];
constructor(options?: USBOptions);
set onconnect(listener: (event: USBConnectionEvent) => void);
set ondisconnect(listener: (event: USBConnectionEvent) => void);
private _onconnect;
set onconnect(fn: (ev: USBConnectionEvent) => void);
private _ondisconnect;
set ondisconnect(fn: (ev: USBConnectionEvent) => void);
addEventListener(type: 'connect' | 'disconnect', listener: (this: this, ev: USBConnectionEvent) => void): void;
addEventListener(type: 'connect' | 'disconnect', listener: EventListener): void;
removeEventListener(type: 'connect' | 'disconnect', callback: (this: this, ev: USBConnectionEvent) => void): void;
removeEventListener(type: 'connect' | 'disconnect', callback: EventListener): void;
dispatchEvent(_event: Event): boolean;
/**

@@ -45,5 +54,5 @@ * Requests a single Web USB device

private preFilterDevices;
private isSameDevice;
private replaceAllowedDevice;
private filterDevice;
private getDeviceId;
private isAllowedDevice;
}
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

@@ -67,16 +52,88 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }

var usb = require("../usb");
var events_1 = require("events");
var webusb_device_1 = require("./webusb-device");
var typed_event_target_1 = require("./typed-event-target");
var WebUSB = /** @class */ (function (_super) {
__extends(WebUSB, _super);
var WebUSB = /** @class */ (function () {
function WebUSB(options) {
var _this = this;
if (options === void 0) { options = {}; }
var _this = _super.call(this) || this;
_this.options = options;
_this.allowedDevices = [];
return _this;
this.options = options;
this.emitter = new events_1.EventEmitter();
this.knownDevices = new Map();
this.allowedDevices = options.allowedDevices || [];
var deviceConnectCallback = function (device) { return __awaiter(_this, void 0, void 0, function () {
var webDevice, deviceId, event_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, webusb_device_1.WebUSBDevice.createInstance(device)];
case 1:
webDevice = _a.sent();
// When connected, emit an event if it is an allowed device
if (webDevice && this.isAllowedDevice(webDevice)) {
deviceId = this.getDeviceId(device);
if (deviceId) {
this.knownDevices.set(deviceId, webDevice);
}
event_1 = {
type: 'connect',
device: webDevice
};
this.emitter.emit('connect', event_1);
}
return [2 /*return*/];
}
});
}); };
var deviceDisconnectCallback = function (device) { return __awaiter(_this, void 0, void 0, function () {
var deviceId, webDevice, event_2;
return __generator(this, function (_a) {
deviceId = this.getDeviceId(device);
// When disconnected, emit an event if the device was a known allowed device
if (deviceId !== undefined && this.knownDevices.has(deviceId)) {
webDevice = this.knownDevices.get(deviceId);
if (webDevice && this.isAllowedDevice(webDevice)) {
event_2 = {
type: 'disconnect',
device: webDevice
};
this.emitter.emit('disconnect', event_2);
}
}
return [2 /*return*/];
});
}); };
this.emitter.on('newListener', function (event) {
var listenerCount = _this.emitter.listenerCount(event);
if (listenerCount !== 0) {
return;
}
if (event === 'connect') {
usb.addListener('attach', deviceConnectCallback);
}
else if (event === 'disconnect') {
usb.addListener('detach', deviceDisconnectCallback);
}
});
this.emitter.on('removeListener', function (event) {
var listenerCount = _this.emitter.listenerCount(event);
if (listenerCount !== 0) {
return;
}
if (event === 'connect') {
usb.removeListener('attach', deviceConnectCallback);
}
else if (event === 'disconnect') {
usb.removeListener('detach', deviceDisconnectCallback);
}
});
}
Object.defineProperty(WebUSB.prototype, "onconnect", {
set: function (listener) {
this.addEventListener('connect', listener);
set: function (fn) {
if (this._onconnect) {
this.removeEventListener('connect', this._onconnect);
this._onconnect = undefined;
}
if (fn) {
this._onconnect = fn;
this.addEventListener('connect', this._onconnect);
}
},

@@ -87,4 +144,11 @@ enumerable: false,

Object.defineProperty(WebUSB.prototype, "ondisconnect", {
set: function (listener) {
this.addEventListener('disconnect', listener);
set: function (fn) {
if (this._ondisconnect) {
this.removeEventListener('disconnect', this._ondisconnect);
this._ondisconnect = undefined;
}
if (fn) {
this._ondisconnect = fn;
this.addEventListener('disconnect', this._ondisconnect);
}
},

@@ -94,2 +158,12 @@ enumerable: false,

});
WebUSB.prototype.addEventListener = function (type, listener) {
this.emitter.addListener(type, listener);
};
WebUSB.prototype.removeEventListener = function (type, callback) {
this.emitter.removeListener(type, callback);
};
WebUSB.prototype.dispatchEvent = function (_event) {
// Don't dispatch from here
return false;
};
/**

@@ -157,3 +231,3 @@ * Requests a single Web USB device

}
if (!this.replaceAllowedDevice(device)) {
if (!this.isAllowedDevice(device)) {
this.allowedDevices.push({

@@ -185,19 +259,14 @@ vendorId: device.vendorId,

case 0:
preFilters = this.allowedDevices.map(function (device) { return ({
vendorId: device.vendorId || undefined,
productId: device.productId || undefined,
serialNumber: device.serialNumber || undefined
}); });
if (!this.options.allowAllDevices) {
// Create pre-filters
preFilters = this.allowedDevices.map(function (device) { return ({
vendorId: device.vendorId || undefined,
productId: device.productId || undefined,
serialNumber: device.serialNumber || undefined
}); });
}
return [4 /*yield*/, this.loadDevices(preFilters)];
case 1:
devices = _a.sent();
devices = devices.filter(function (device) {
for (var i in _this.allowedDevices) {
if (_this.isSameDevice(device, _this.allowedDevices[i])) {
return true;
}
}
return false;
});
return [2 /*return*/, devices];
return [2 /*return*/, devices.filter(function (device) { return _this.isAllowedDevice(device); })];
}

@@ -209,3 +278,3 @@ });

return __awaiter(this, void 0, void 0, function () {
var devices, webDevices, devices_1, devices_1_1, device, webDevice, e_1_1;
var devices, webDevices, devices_1, devices_1_1, device, webDevice, deviceId, e_1_1;
var e_1, _a;

@@ -216,6 +285,4 @@ return __generator(this, function (_b) {

devices = usb.getDeviceList();
if (preFilters) {
// Pre-filter devices
devices = this.preFilterDevices(devices, preFilters);
}
// Pre-filter devices
devices = this.preFilterDevices(devices, preFilters);
webDevices = [];

@@ -230,6 +297,15 @@ _b.label = 1;

device = devices_1_1.value;
if (this.options.deviceTimeout) {
device.timeout = this.options.deviceTimeout;
}
return [4 /*yield*/, webusb_device_1.WebUSBDevice.createInstance(device)];
case 3:
webDevice = _b.sent();
webDevices.push(webDevice);
if (webDevice) {
webDevices.push(webDevice);
deviceId = this.getDeviceId(device);
if (deviceId) {
this.knownDevices.set(deviceId, webDevice);
}
}
_b.label = 4;

@@ -256,2 +332,5 @@ case 4:

WebUSB.prototype.preFilterDevices = function (devices, preFilters) {
if (!preFilters || !preFilters.length) {
return devices;
}
// Just pre-filter on vid/pid

@@ -269,21 +348,6 @@ return devices.filter(function (device) { return preFilters.some(function (filter) {

};
WebUSB.prototype.isSameDevice = function (device1, device2) {
return (device1.productId === device2.productId
&& device1.vendorId === device2.vendorId
&& device1.serialNumber === device2.serialNumber);
};
WebUSB.prototype.replaceAllowedDevice = function (device) {
for (var i in this.allowedDevices) {
if (this.isSameDevice(device, this.allowedDevices[i])) {
this.allowedDevices[i] = {
vendorId: device.vendorId,
productId: device.productId,
serialNumber: device.serialNumber
};
return true;
}
WebUSB.prototype.filterDevice = function (options, device) {
if (!options.filters || !options.filters.length) {
return true;
}
return false;
};
WebUSB.prototype.filterDevice = function (options, device) {
return options.filters.some(function (filter) {

@@ -333,5 +397,27 @@ // Vendor

};
WebUSB.prototype.getDeviceId = function (device) {
if (device.busNumber === undefined || device.deviceAddress === undefined) {
return undefined;
}
return device.busNumber + "." + device.deviceAddress;
};
WebUSB.prototype.isAllowedDevice = function (device) {
if (this.options.allowAllDevices) {
return true;
}
var isSameDevice = function (device1, device2) {
return (device1.productId === device2.productId
&& device1.vendorId === device2.vendorId
&& device1.serialNumber === device2.serialNumber);
};
for (var i in this.allowedDevices) {
if (isSameDevice(device, this.allowedDevices[i])) {
return true;
}
}
return false;
};
return WebUSB;
}(typed_event_target_1.TypedEventTarget));
}());
exports.WebUSB = WebUSB;
//# sourceMappingURL=index.js.map

@@ -8,3 +8,3 @@ /// <reference types="w3c-web-usb" />

private device;
static createInstance(device: usb.Device): Promise<WebUSBDevice>;
static createInstance(device: usb.Device): Promise<WebUSBDevice | undefined>;
readonly usbVersionMajor: number;

@@ -11,0 +11,0 @@ readonly usbVersionMinor: number;

@@ -82,11 +82,16 @@ "use strict";

return __awaiter(this, void 0, void 0, function () {
var instance;
return __generator(this, function (_a) {
switch (_a.label) {
var instance, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
instance = new WebUSBDevice(device);
return [4 /*yield*/, instance.initialize()];
case 1:
_a.sent();
_b.sent();
return [2 /*return*/, instance];
case 2:
_a = _b.sent();
return [2 /*return*/, undefined];
case 3: return [2 /*return*/];
}

@@ -93,0 +98,0 @@ });

@@ -5,10 +5,10 @@ {

"license": "MIT",
"version": "2.0.0-alpha.2",
"version": "2.0.1",
"main": "dist/index.js",
"engines": {
"node": ">=10"
"node": ">=10.16.0"
},
"repository": {
"type": "git",
"url": "https://github.com/tessel/node-usb.git"
"url": "https://github.com/node-usb/node-usb.git"
},

@@ -41,4 +41,3 @@ "contributors": [

"prepare": "yarn compile",
"install": "prebuild-install --runtime napi --verbose || node-gyp rebuild",
"prebuild": "prebuild --runtime napi -t 4 --force --strip --verbose",
"install": "node-gyp-build",
"clean": "git clean -dfx",

@@ -50,10 +49,18 @@ "compile": "tsc && yarn lint && yarn docs",

"full-test": "mocha --require coffeescript/register test/*.coffee",
"valgrind": "coffee -c test/usb.coffee; valgrind --leak-check=full --show-possibly-lost=no node --expose-gc --trace-gc node_modules/mocha/bin/_mocha -R spec",
"docs": "typedoc"
"valgrind": "coffee -c test/*.coffee; valgrind --leak-check=full --show-possibly-lost=no node --expose-gc --trace-gc node_modules/mocha/bin/_mocha -R spec",
"docs": "typedoc",
"prebuild": "prebuildify --napi --strip",
"prebuild-darwin": "prebuildify --napi --strip --arch x64+arm64",
"prebuild-win32-x86": "prebuildify --napi --strip",
"prebuild-win32-x64": "prebuildify --napi --strip",
"prebuild-linux-x86": "prebuildify-cross -i ghcr.io/node-usb/linux-x86 --napi --strip",
"prebuild-linux-x64": "prebuildify-cross -i ghcr.io/node-usb/centos7-devtoolset7 -i ghcr.io/node-usb/alpine --napi --strip --tag-libc",
"prebuild-linux-arm": "prebuildify-cross -i ghcr.io/node-usb/linux-arm64 -i ghcr.io/node-usb/linux-armv7 -i ghcr.io/node-usb/linux-armv6 --napi --strip --tag-armv",
"prebuild-android-arm": "prebuildify-cross -i ghcr.io/node-usb/android-arm64 -i ghcr.io/node-usb/android-armv7 --napi --strip --tag-armv",
"prebuild-download": "prebuildify-ci download"
},
"dependencies": {
"@types/w3c-web-usb": "^1.0.4",
"bindings": "^1.4.0",
"node-addon-api": "^3.1.0",
"prebuild-install": "^6.1.1"
"node-addon-api": "^4.2.0",
"node-gyp-build": "^4.3.0"
},

@@ -66,5 +73,8 @@ "devDependencies": {

"eslint": "^7.29.0",
"mocha": "^8.3.2",
"prebuild": "^10.0.1",
"typedoc": "^0.20.36",
"mocha": "^9.1.3",
"node-gyp": "^7.1.2",
"prebuildify": "^4.2.1",
"prebuildify-ci": "^1.0.5",
"prebuildify-cross": "^4.0.1",
"typedoc": "^0.22.10",
"typescript": "~4.2.4"

@@ -71,0 +81,0 @@ },

@@ -65,4 +65,4 @@ import { promisify } from 'util';

// WebUSB Device class for turning a core usb.Device into a webusb device
// WebUSB Device class for turning a legacy usb.Device into a webusb device
WebUSBDevice
};

@@ -5,6 +5,7 @@ // Definitions from DefinitelyTyped, thanks to:

import { join } from 'path';
import type { DeviceDescriptor, ConfigDescriptor, BosDescriptor } from './descriptors';
/* eslint-disable @typescript-eslint/no-var-requires */
const usb = require('bindings')('usb_bindings');
const usb = require('node-gyp-build')(join(__dirname, '..', '..'));
module.exports = usb;

@@ -31,3 +32,14 @@

export declare function _disableHotplugEvents(): void;
export declare function _getLibusbCapability(capability: number): number;
/**
* Restore (re-reference) the hotplug events unreferenced by `unrefHotplugEvents()`
*/
export declare function refHotplugEvents(): void;
/**
* Unreference the hotplug events from the event loop, allowing the process to exit even when listening for the `attach` and `detach` events
*/
export declare function unrefHotplugEvents(): void;
/** Represents a USB transfer */

@@ -262,2 +274,8 @@ export declare class Transfer {

// libusb_capability
export declare const LIBUSB_CAP_HAS_CAPABILITY: number;
export declare const LIBUSB_CAP_HAS_HOTPLUG: number;
export declare const LIBUSB_CAP_HAS_HID_ACCESS: number;
export declare const LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER: number;
// libusb_error

@@ -264,0 +282,0 @@ /** Input/output error */

@@ -24,21 +24,61 @@ import { EventEmitter } from 'events';

/* eslint-disable @typescript-eslint/no-empty-interface */
interface Device extends ExtendedDevice {}
interface Device extends ExtendedDevice { }
interface DeviceEvents extends EventListeners<DeviceEvents> {
attach: Device;
detach: Device;
}
interface DeviceEvents extends EventListeners<DeviceEvents> {
attach: Device;
detach: Device;
}
function addListener<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void;
function removeListener<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void;
function on<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void;
function off<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void;
function once<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void;
function listeners<K extends keyof DeviceEvents>(event: K): ((arg: DeviceEvents[K]) => void)[];
function rawListeners<K extends keyof DeviceEvents>(event: K): ((arg: DeviceEvents[K]) => void)[];
function removeAllListeners<K extends keyof DeviceEvents>(event?: K): void;
function emit<K extends keyof DeviceEvents>(event: K, arg: DeviceEvents[K]): boolean;
function listenerCount<K extends keyof DeviceEvents>(event: K): number;
function addListener<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void;
function removeListener<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void;
function on<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void;
function off<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void;
function once<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void;
function listeners<K extends keyof DeviceEvents>(event: K): ((arg: DeviceEvents[K]) => void)[];
function rawListeners<K extends keyof DeviceEvents>(event: K): ((arg: DeviceEvents[K]) => void)[];
function removeAllListeners<K extends keyof DeviceEvents>(event?: K): void;
function emit<K extends keyof DeviceEvents>(event: K, arg: DeviceEvents[K]): boolean;
function listenerCount<K extends keyof DeviceEvents>(event: K): number;
}
// Polling mechanism for discovering device changes until this is fixed:
// https://github.com/libusb/libusb/issues/86
const pollTimeout = 500;
const hotplugSupported = usb._getLibusbCapability(usb.LIBUSB_CAP_HAS_HOTPLUG) > 0;
let pollingHotplug = false;
let pollDevices: usb.Device[] = [];
const pollHotplug = (start = false) => {
if (start) {
pollingHotplug = true;
} else if (!pollingHotplug) {
return;
}
const devices = usb.getDeviceList();
if (!start) {
// Find attached devices
for (const device of devices) {
const found = pollDevices.find(item => item.deviceAddress === device.deviceAddress);
if (!found) {
usb.emit('attach', device);
}
}
// Find detached devices
for (const device of pollDevices) {
const found = devices.find(item => item.deviceAddress === device.deviceAddress);
if (!found) {
usb.emit('detach', device);
}
}
}
pollDevices = devices;
setTimeout(() => {
pollHotplug();
}, pollTimeout);
};
usb.on('newListener', event => {

@@ -50,3 +90,7 @@ if (event !== 'attach' && event !== 'detach') {

if (listenerCount === 0) {
usb._enableHotplugEvents();
if (hotplugSupported) {
usb._enableHotplugEvents();
} else {
pollHotplug(true);
}
}

@@ -61,3 +105,7 @@ });

if (listenerCount === 0) {
usb._disableHotplugEvents();
if (hotplugSupported) {
usb._disableHotplugEvents();
} else {
pollingHotplug = false;
}
}

@@ -64,0 +112,0 @@ });

@@ -31,3 +31,3 @@ import { LibUSBException, LIBUSB_ENDPOINT_IN, Device } from './bindings';

const len = this.descriptor.endpoints.length;
for (let i=0; i<len; i++){
for (let i = 0; i < len; i++) {
const desc = this.descriptor.endpoints[i];

@@ -149,3 +149,3 @@ const c = (desc.bEndpointAddress & LIBUSB_ENDPOINT_IN) ? InEndpoint : OutEndpoint;

this.device.__setInterface(this.id, altSetting, error => {
if (!error){
if (!error) {
this.altSetting = altSetting;

@@ -152,0 +152,0 @@ this.refresh();

import * as usb from '../usb';
import { EventEmitter } from 'events';
import { WebUSBDevice } from './webusb-device';
import { TypedEventTarget } from './typed-event-target';

@@ -10,46 +10,140 @@ /**

/**
* A `device found` callback function to allow the user to select a device
* Optional `device found` callback function to allow the user to select a device
*/
devicesFound?: (devices: Array<USBDevice>) => Promise<USBDevice | void>;
}
devicesFound?: (devices: USBDevice[]) => Promise<USBDevice | void>;
/**
* @hidden
*/
interface Device {
vendorId?: number;
productId?: number;
serialNumber?: string;
}
/**
* Optional array of preconfigured allowed devices
*/
allowedDevices?: USBDeviceFilter[];
/**
* @hidden
*/
export type USBEvents = {
/**
* USBDevice connected event
* Optional flag to automatically allow all devices
*/
connect: USBConnectionEvent;
allowAllDevices?: boolean;
/**
* USBDevice disconnected event
* Optional timeout (in milliseconds) to use for the device control transfers
*/
disconnect: USBConnectionEvent;
};
deviceTimeout?: number;
}
export class WebUSB extends TypedEventTarget<USBEvents> implements USB {
export class WebUSB implements USB {
private allowedDevices: Array<Device> = [];
protected emitter = new EventEmitter();
protected knownDevices: Map<string, USBDevice> = new Map();
protected allowedDevices: USBDeviceFilter[];
constructor(private options: USBOptions = {}) {
super();
this.allowedDevices = options.allowedDevices || [];
const deviceConnectCallback = async (device: usb.Device) => {
const webDevice = await WebUSBDevice.createInstance(device);
// When connected, emit an event if it is an allowed device
if (webDevice && this.isAllowedDevice(webDevice)) {
const deviceId = this.getDeviceId(device);
if (deviceId) {
this.knownDevices.set(deviceId, webDevice);
}
const event = {
type: 'connect',
device: webDevice
};
this.emitter.emit('connect', event);
}
};
const deviceDisconnectCallback = async (device: usb.Device) => {
const deviceId = this.getDeviceId(device);
// When disconnected, emit an event if the device was a known allowed device
if (deviceId !== undefined && this.knownDevices.has(deviceId)) {
const webDevice = this.knownDevices.get(deviceId);
if (webDevice && this.isAllowedDevice(webDevice)) {
const event = {
type: 'disconnect',
device: webDevice
};
this.emitter.emit('disconnect', event);
}
}
};
this.emitter.on('newListener', event => {
const listenerCount = this.emitter.listenerCount(event);
if (listenerCount !== 0) {
return;
}
if (event === 'connect') {
usb.addListener('attach', deviceConnectCallback);
} else if (event === 'disconnect') {
usb.addListener('detach', deviceDisconnectCallback);
}
});
this.emitter.on('removeListener', event => {
const listenerCount = this.emitter.listenerCount(event);
if (listenerCount !== 0) {
return;
}
if (event === 'connect') {
usb.removeListener('attach', deviceConnectCallback);
} else if (event === 'disconnect') {
usb.removeListener('detach', deviceDisconnectCallback);
}
});
}
public set onconnect(listener: (event: USBConnectionEvent) => void) {
this.addEventListener('connect', listener);
private _onconnect: ((ev: USBConnectionEvent) => void) | undefined;
public set onconnect(fn: (ev: USBConnectionEvent) => void) {
if (this._onconnect) {
this.removeEventListener('connect', this._onconnect);
this._onconnect = undefined;
}
if (fn) {
this._onconnect = fn;
this.addEventListener('connect', this._onconnect);
}
}
public set ondisconnect(listener: (event: USBConnectionEvent) => void) {
this.addEventListener('disconnect', listener);
private _ondisconnect: ((ev: USBConnectionEvent) => void) | undefined;
public set ondisconnect(fn: (ev: USBConnectionEvent) => void) {
if (this._ondisconnect) {
this.removeEventListener('disconnect', this._ondisconnect);
this._ondisconnect = undefined;
}
if (fn) {
this._ondisconnect = fn;
this.addEventListener('disconnect', this._ondisconnect);
}
}
public addEventListener(type: 'connect' | 'disconnect', listener: (this: this, ev: USBConnectionEvent) => void): void;
public addEventListener(type: 'connect' | 'disconnect', listener: EventListener): void;
public addEventListener(type: string, listener: (ev: USBConnectionEvent) => void): void {
this.emitter.addListener(type, listener);
}
public removeEventListener(type: 'connect' | 'disconnect', callback: (this: this, ev: USBConnectionEvent) => void): void;
public removeEventListener(type: 'connect' | 'disconnect', callback: EventListener): void;
public removeEventListener(type: string, callback: (this: this, ev: USBConnectionEvent) => void): void {
this.emitter.removeListener(type, callback);
}
public dispatchEvent(_event: Event): boolean {
// Don't dispatch from here
return false;
}
/**

@@ -109,3 +203,3 @@ * Requests a single Web USB device

if (!this.replaceAllowedDevice(device)) {
if (!this.isAllowedDevice(device)) {
this.allowedDevices.push({

@@ -119,3 +213,3 @@ vendorId: device.vendorId,

return device;
} catch(error) {
} catch (error) {
throw new Error(`requestDevice error: ${error}`);

@@ -130,31 +224,23 @@ }

public async getDevices(): Promise<USBDevice[]> {
// Create pre-filters
const preFilters = this.allowedDevices.map(device => ({
vendorId: device.vendorId || undefined,
productId: device.productId || undefined,
serialNumber: device.serialNumber || undefined
}));
let preFilters: USBDeviceFilter[] | undefined;
if (!this.options.allowAllDevices) {
// Create pre-filters
preFilters = this.allowedDevices.map(device => ({
vendorId: device.vendorId || undefined,
productId: device.productId || undefined,
serialNumber: device.serialNumber || undefined
}));
}
// Refresh devices and filter for allowed ones
let devices = await this.loadDevices(preFilters);
devices = devices.filter(device => {
for (const i in this.allowedDevices) {
if (this.isSameDevice(device, this.allowedDevices[i])) {
return true;
}
}
return false;
});
return devices;
const devices = await this.loadDevices(preFilters);
return devices.filter(device => this.isAllowedDevice(device));
}
private async loadDevices(preFilters?: Array<Device>): Promise<USBDevice[]> {
private async loadDevices(preFilters?: USBDeviceFilter[]): Promise<USBDevice[]> {
let devices = usb.getDeviceList();
if (preFilters) {
// Pre-filter devices
devices = this.preFilterDevices(devices, preFilters);
}
// Pre-filter devices
devices = this.preFilterDevices(devices, preFilters);

@@ -164,4 +250,15 @@ const webDevices: USBDevice[] = [];

for (const device of devices) {
if (this.options.deviceTimeout) {
device.timeout = this.options.deviceTimeout;
}
const webDevice = await WebUSBDevice.createInstance(device);
webDevices.push(webDevice);
if (webDevice) {
webDevices.push(webDevice);
const deviceId = this.getDeviceId(device);
if (deviceId) {
this.knownDevices.set(deviceId, webDevice);
}
}
}

@@ -172,3 +269,7 @@

private preFilterDevices(devices: Array<usb.Device>, preFilters: Array<Device>): Array<usb.Device> {
private preFilterDevices(devices: usb.Device[], preFilters?: USBDeviceFilter[]): usb.Device[] {
if (!preFilters || !preFilters.length) {
return devices;
}
// Just pre-filter on vid/pid

@@ -187,24 +288,7 @@ return devices.filter(device => preFilters.some(filter => {

private isSameDevice(device1: Device, device2: Device): boolean {
return (device1.productId === device2.productId
&& device1.vendorId === device2.vendorId
&& device1.serialNumber === device2.serialNumber);
}
private replaceAllowedDevice(device: USBDevice): boolean {
for (const i in this.allowedDevices) {
if (this.isSameDevice(device, this.allowedDevices[i])) {
this.allowedDevices[i] = {
vendorId: device.vendorId,
productId: device.productId,
serialNumber: device.serialNumber
};
return true;
}
private filterDevice(options: USBDeviceRequestOptions, device: USBDevice): boolean {
if (!options.filters || !options.filters.length) {
return true;
}
return false;
}
private filterDevice(options: USBDeviceRequestOptions, device: USBDevice): boolean {
return options.filters.some(filter => {

@@ -258,2 +342,30 @@ // Vendor

}
private getDeviceId(device: usb.Device): string | undefined {
if (device.busNumber === undefined || device.deviceAddress === undefined) {
return undefined;
}
return `${device.busNumber}.${device.deviceAddress}`;
}
private isAllowedDevice(device: USBDeviceFilter): boolean {
if (this.options.allowAllDevices) {
return true;
}
const isSameDevice = (device1: USBDeviceFilter, device2: USBDeviceFilter): boolean => {
return (device1.productId === device2.productId
&& device1.vendorId === device2.vendorId
&& device1.serialNumber === device2.serialNumber);
};
for (const i in this.allowedDevices) {
if (isSameDevice(device, this.allowedDevices[i])) {
return true;
}
}
return false;
}
}

@@ -15,6 +15,10 @@ import * as usb from '../usb';

export class WebUSBDevice implements USBDevice {
public static async createInstance(device: usb.Device): Promise<WebUSBDevice> {
const instance = new WebUSBDevice(device);
await instance.initialize();
return instance;
public static async createInstance(device: usb.Device): Promise<WebUSBDevice | undefined> {
try {
const instance = new WebUSBDevice(device);
await instance.initialize();
return instance;
} catch {
return undefined;
}
}

@@ -102,5 +106,5 @@

interfaceNumber: iface.interfaceNumber,
alternate : iface.alternate,
alternates : iface.alternates,
claimed : false
alternate: iface.alternate,
alternates: iface.alternates,
claimed: false
};

@@ -133,3 +137,3 @@ }

const config = this.configurations.find(configuration => configuration.configurationValue === configurationValue);
const config = this.configurations.find(configuration => configuration.configurationValue === configurationValue);
if (!config) {

@@ -177,5 +181,5 @@ throw new Error('selectConfiguration error: configuration not found');

interfaceNumber,
alternate : iface.alternate,
alternates : iface.alternates,
claimed : true
alternate: iface.alternate,
alternates: iface.alternates,
claimed: true
};

@@ -201,5 +205,5 @@ } catch (error) {

interfaceNumber,
alternate : iface.alternate,
alternates : iface.alternates,
claimed : false
alternate: iface.alternate,
alternates: iface.alternates,
claimed: false
};

@@ -283,3 +287,3 @@ }

const buffer = data ? Buffer.from(data) : Buffer.alloc(0);
const bytesWritten = <number> await controlTransfer(type, setup.request, setup.value, setup.index, buffer);
const bytesWritten = <number>await controlTransfer(type, setup.request, setup.value, setup.index, buffer);

@@ -286,0 +290,0 @@ return {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc