usb 2.5.2-alpha.1 → 2.6.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/CHANGELOG.md +21 -0
- package/dist/index.d.ts +10 -6
- package/dist/index.js +27 -11
- package/dist/index.js.map +1 -1
- package/dist/usb/endpoint.js +3 -1
- package/dist/usb/endpoint.js.map +1 -1
- package/dist/webusb/index.js +40 -24
- package/dist/webusb/index.js.map +1 -1
- package/dist/webusb/webusb-device.d.ts +1 -1
- package/dist/webusb/webusb-device.js +24 -30
- package/dist/webusb/webusb-device.js.map +1 -1
- package/package.json +12 -12
- package/prebuilds/android-arm/node.napi.armv7.node +0 -0
- package/prebuilds/android-arm64/node.napi.armv8.node +0 -0
- package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
- package/prebuilds/linux-arm/node.napi.armv6.node +0 -0
- package/prebuilds/linux-arm/node.napi.armv7.node +0 -0
- package/prebuilds/linux-arm64/node.napi.armv8.node +0 -0
- package/prebuilds/linux-ia32/node.napi.node +0 -0
- package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/node.napi.musl.node +0 -0
- package/prebuilds/win32-ia32/node.napi.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/.eslintignore +0 -4
- package/.eslintrc.json +0 -87
- package/.gitattributes +0 -1
- package/.gitmodules +0 -3
- package/.vscode/launch.json +0 -15
- package/.vscode/tasks.json +0 -23
- package/libusb/.gitattributes +0 -7
- package/libusb/msvc/.gitattributes +0 -3
- package/tsc/index.ts +0 -75
- package/tsc/usb/bindings.ts +0 -310
- package/tsc/usb/capability.ts +0 -22
- package/tsc/usb/descriptors.ts +0 -180
- package/tsc/usb/device.ts +0 -331
- package/tsc/usb/endpoint.ts +0 -229
- package/tsc/usb/index.ts +0 -149
- package/tsc/usb/interface.ts +0 -167
- package/tsc/webusb/index.ts +0 -374
- package/tsc/webusb/webusb-device.ts +0 -490
- package/tsconfig.json +0 -17
- package/typedoc.json +0 -9
package/tsc/webusb/index.ts
DELETED
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
import * as usb from '../usb';
|
|
2
|
-
import { EventEmitter } from 'events';
|
|
3
|
-
import { WebUSBDevice } from './webusb-device';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* USB Options
|
|
7
|
-
*/
|
|
8
|
-
export interface USBOptions {
|
|
9
|
-
/**
|
|
10
|
-
* Optional `device found` callback function to allow the user to select a device
|
|
11
|
-
*/
|
|
12
|
-
devicesFound?: (devices: USBDevice[]) => Promise<USBDevice | void>;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Optional array of preconfigured allowed devices
|
|
16
|
-
*/
|
|
17
|
-
allowedDevices?: USBDeviceFilter[];
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Optional flag to automatically allow all devices
|
|
21
|
-
*/
|
|
22
|
-
allowAllDevices?: boolean;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Optional timeout (in milliseconds) to use for the device control transfers
|
|
26
|
-
*/
|
|
27
|
-
deviceTimeout?: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Convenience method to get the WebUSB interface available
|
|
32
|
-
*/
|
|
33
|
-
export const getWebUsb = (): USB => {
|
|
34
|
-
if (navigator && navigator.usb) {
|
|
35
|
-
return navigator.usb;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return new WebUSB();
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export class WebUSB implements USB {
|
|
42
|
-
|
|
43
|
-
protected emitter = new EventEmitter();
|
|
44
|
-
protected knownDevices: Map<string, USBDevice> = new Map();
|
|
45
|
-
protected allowedDevices: USBDeviceFilter[];
|
|
46
|
-
|
|
47
|
-
constructor(private options: USBOptions = {}) {
|
|
48
|
-
this.allowedDevices = options.allowedDevices || [];
|
|
49
|
-
|
|
50
|
-
const deviceConnectCallback = async (device: usb.Device) => {
|
|
51
|
-
const webDevice = await WebUSBDevice.createInstance(device);
|
|
52
|
-
|
|
53
|
-
// When connected, emit an event if it is an allowed device
|
|
54
|
-
if (webDevice && this.isAllowedDevice(webDevice)) {
|
|
55
|
-
const deviceId = this.getDeviceId(device);
|
|
56
|
-
if (deviceId) {
|
|
57
|
-
this.knownDevices.set(deviceId, webDevice);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const event = {
|
|
61
|
-
type: 'connect',
|
|
62
|
-
device: webDevice
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
this.emitter.emit('connect', event);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const deviceDisconnectCallback = async (device: usb.Device) => {
|
|
70
|
-
const deviceId = this.getDeviceId(device);
|
|
71
|
-
|
|
72
|
-
// When disconnected, emit an event if the device was a known allowed device
|
|
73
|
-
if (deviceId !== undefined && this.knownDevices.has(deviceId)) {
|
|
74
|
-
const webDevice = this.knownDevices.get(deviceId);
|
|
75
|
-
|
|
76
|
-
if (webDevice && this.isAllowedDevice(webDevice)) {
|
|
77
|
-
const event = {
|
|
78
|
-
type: 'disconnect',
|
|
79
|
-
device: webDevice
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
this.emitter.emit('disconnect', event);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
this.emitter.on('newListener', event => {
|
|
88
|
-
const listenerCount = this.emitter.listenerCount(event);
|
|
89
|
-
|
|
90
|
-
if (listenerCount !== 0) {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (event === 'connect') {
|
|
95
|
-
usb.addListener('attach', deviceConnectCallback);
|
|
96
|
-
} else if (event === 'disconnect') {
|
|
97
|
-
usb.addListener('detach', deviceDisconnectCallback);
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
this.emitter.on('removeListener', event => {
|
|
102
|
-
const listenerCount = this.emitter.listenerCount(event);
|
|
103
|
-
|
|
104
|
-
if (listenerCount !== 0) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (event === 'connect') {
|
|
109
|
-
usb.removeListener('attach', deviceConnectCallback);
|
|
110
|
-
} else if (event === 'disconnect') {
|
|
111
|
-
usb.removeListener('detach', deviceDisconnectCallback);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
private _onconnect: ((ev: USBConnectionEvent) => void) | undefined;
|
|
117
|
-
public set onconnect(fn: (ev: USBConnectionEvent) => void) {
|
|
118
|
-
if (this._onconnect) {
|
|
119
|
-
this.removeEventListener('connect', this._onconnect);
|
|
120
|
-
this._onconnect = undefined;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (fn) {
|
|
124
|
-
this._onconnect = fn;
|
|
125
|
-
this.addEventListener('connect', this._onconnect);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
private _ondisconnect: ((ev: USBConnectionEvent) => void) | undefined;
|
|
130
|
-
public set ondisconnect(fn: (ev: USBConnectionEvent) => void) {
|
|
131
|
-
if (this._ondisconnect) {
|
|
132
|
-
this.removeEventListener('disconnect', this._ondisconnect);
|
|
133
|
-
this._ondisconnect = undefined;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (fn) {
|
|
137
|
-
this._ondisconnect = fn;
|
|
138
|
-
this.addEventListener('disconnect', this._ondisconnect);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
public addEventListener(type: 'connect' | 'disconnect', listener: (this: this, ev: USBConnectionEvent) => void): void;
|
|
143
|
-
public addEventListener(type: 'connect' | 'disconnect', listener: EventListener): void;
|
|
144
|
-
public addEventListener(type: string, listener: (ev: USBConnectionEvent) => void): void {
|
|
145
|
-
this.emitter.addListener(type, listener);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
public removeEventListener(type: 'connect' | 'disconnect', callback: (this: this, ev: USBConnectionEvent) => void): void;
|
|
149
|
-
public removeEventListener(type: 'connect' | 'disconnect', callback: EventListener): void;
|
|
150
|
-
public removeEventListener(type: string, callback: (this: this, ev: USBConnectionEvent) => void): void {
|
|
151
|
-
this.emitter.removeListener(type, callback);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
public dispatchEvent(_event: Event): boolean {
|
|
155
|
-
// Don't dispatch from here
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Requests a single Web USB device
|
|
161
|
-
* @param options The options to use when scanning
|
|
162
|
-
* @returns Promise containing the selected device
|
|
163
|
-
*/
|
|
164
|
-
public async requestDevice(options?: USBDeviceRequestOptions): Promise<USBDevice> {
|
|
165
|
-
// Must have options
|
|
166
|
-
if (!options) {
|
|
167
|
-
throw new TypeError('requestDevice error: 1 argument required, but only 0 present');
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Options must be an object
|
|
171
|
-
if (options.constructor !== {}.constructor) {
|
|
172
|
-
throw new TypeError('requestDevice error: parameter 1 (options) is not an object');
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Must have a filter
|
|
176
|
-
if (!options.filters) {
|
|
177
|
-
throw new TypeError('requestDevice error: required member filters is undefined');
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Filter must be an array
|
|
181
|
-
if (options.filters.constructor !== [].constructor) {
|
|
182
|
-
throw new TypeError('requestDevice error: the provided value cannot be converted to a sequence');
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Check filters
|
|
186
|
-
options.filters.forEach(filter => {
|
|
187
|
-
// Protocol & Subclass
|
|
188
|
-
if (filter.protocolCode && !filter.subclassCode) {
|
|
189
|
-
throw new TypeError('requestDevice error: subclass code is required');
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Subclass & Class
|
|
193
|
-
if (filter.subclassCode && !filter.classCode) {
|
|
194
|
-
throw new TypeError('requestDevice error: class code is required');
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
let devices = await this.loadDevices(options.filters);
|
|
199
|
-
devices = devices.filter(device => this.filterDevice(options, device));
|
|
200
|
-
|
|
201
|
-
if (devices.length === 0) {
|
|
202
|
-
throw new Error('requestDevice error: no devices found');
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
// If no devicesFound function, select the first device found
|
|
207
|
-
const device = this.options.devicesFound ? await this.options.devicesFound(devices) : devices[0];
|
|
208
|
-
|
|
209
|
-
if (!device) {
|
|
210
|
-
throw new Error('selected device not found');
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (!this.isAllowedDevice(device)) {
|
|
214
|
-
this.allowedDevices.push({
|
|
215
|
-
vendorId: device.vendorId,
|
|
216
|
-
productId: device.productId,
|
|
217
|
-
serialNumber: device.serialNumber
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return device;
|
|
222
|
-
} catch (error) {
|
|
223
|
-
throw new Error(`requestDevice error: ${error}`);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Gets all allowed Web USB devices which are connected
|
|
229
|
-
* @returns Promise containing an array of devices
|
|
230
|
-
*/
|
|
231
|
-
public async getDevices(): Promise<USBDevice[]> {
|
|
232
|
-
let preFilters: USBDeviceFilter[] | undefined;
|
|
233
|
-
|
|
234
|
-
if (!this.options.allowAllDevices) {
|
|
235
|
-
// Create pre-filters
|
|
236
|
-
preFilters = this.allowedDevices.map(device => ({
|
|
237
|
-
vendorId: device.vendorId || undefined,
|
|
238
|
-
productId: device.productId || undefined,
|
|
239
|
-
serialNumber: device.serialNumber || undefined
|
|
240
|
-
}));
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Refresh devices and filter for allowed ones
|
|
244
|
-
const devices = await this.loadDevices(preFilters);
|
|
245
|
-
return devices.filter(device => this.isAllowedDevice(device));
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
private async loadDevices(preFilters?: USBDeviceFilter[]): Promise<USBDevice[]> {
|
|
249
|
-
let devices = usb.getDeviceList();
|
|
250
|
-
|
|
251
|
-
// Pre-filter devices
|
|
252
|
-
devices = this.preFilterDevices(devices, preFilters);
|
|
253
|
-
|
|
254
|
-
const webDevices: USBDevice[] = [];
|
|
255
|
-
|
|
256
|
-
for (const device of devices) {
|
|
257
|
-
if (this.options.deviceTimeout) {
|
|
258
|
-
device.timeout = this.options.deviceTimeout;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const webDevice = await WebUSBDevice.createInstance(device);
|
|
262
|
-
if (webDevice) {
|
|
263
|
-
webDevices.push(webDevice);
|
|
264
|
-
|
|
265
|
-
const deviceId = this.getDeviceId(device);
|
|
266
|
-
if (deviceId) {
|
|
267
|
-
this.knownDevices.set(deviceId, webDevice);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
return webDevices;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
private preFilterDevices(devices: usb.Device[], preFilters?: USBDeviceFilter[]): usb.Device[] {
|
|
276
|
-
if (!preFilters || !preFilters.length) {
|
|
277
|
-
return devices;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Just pre-filter on vid/pid
|
|
281
|
-
return devices.filter(device => preFilters.some(filter => {
|
|
282
|
-
// Vendor
|
|
283
|
-
if (filter.vendorId && filter.vendorId !== device.deviceDescriptor.idVendor) return false;
|
|
284
|
-
|
|
285
|
-
// Product
|
|
286
|
-
if (filter.productId && filter.productId !== device.deviceDescriptor.idProduct) return false;
|
|
287
|
-
|
|
288
|
-
// Ignore serial number for node-usb as it requires device connection
|
|
289
|
-
return true;
|
|
290
|
-
}));
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
private filterDevice(options: USBDeviceRequestOptions, device: USBDevice): boolean {
|
|
294
|
-
if (!options.filters || !options.filters.length) {
|
|
295
|
-
return true;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
return options.filters.some(filter => {
|
|
299
|
-
// Vendor
|
|
300
|
-
if (filter.vendorId && filter.vendorId !== device.vendorId) return false;
|
|
301
|
-
|
|
302
|
-
// Product
|
|
303
|
-
if (filter.productId && filter.productId !== device.productId) return false;
|
|
304
|
-
|
|
305
|
-
// Class
|
|
306
|
-
if (filter.classCode) {
|
|
307
|
-
|
|
308
|
-
if (!device.configuration) {
|
|
309
|
-
return false;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Interface Descriptors
|
|
313
|
-
const match = device.configuration.interfaces.some(iface => {
|
|
314
|
-
// Class
|
|
315
|
-
if (filter.classCode && filter.classCode !== iface.alternate.interfaceClass) return false;
|
|
316
|
-
|
|
317
|
-
// Subclass
|
|
318
|
-
if (filter.subclassCode && filter.subclassCode !== iface.alternate.interfaceSubclass) return false;
|
|
319
|
-
|
|
320
|
-
// Protocol
|
|
321
|
-
if (filter.protocolCode && filter.protocolCode !== iface.alternate.interfaceProtocol) return false;
|
|
322
|
-
|
|
323
|
-
return true;
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
if (match) {
|
|
327
|
-
return true;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Class
|
|
332
|
-
if (filter.classCode && filter.classCode !== device.deviceClass) return false;
|
|
333
|
-
|
|
334
|
-
// Subclass
|
|
335
|
-
if (filter.subclassCode && filter.subclassCode !== device.deviceSubclass) return false;
|
|
336
|
-
|
|
337
|
-
// Protocol
|
|
338
|
-
if (filter.protocolCode && filter.protocolCode !== device.deviceProtocol) return false;
|
|
339
|
-
|
|
340
|
-
// Serial
|
|
341
|
-
if (filter.serialNumber && filter.serialNumber !== device.serialNumber) return false;
|
|
342
|
-
|
|
343
|
-
return true;
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
private getDeviceId(device: usb.Device): string | undefined {
|
|
348
|
-
if (device.busNumber === undefined || device.deviceAddress === undefined) {
|
|
349
|
-
return undefined;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
return `${device.busNumber}.${device.deviceAddress}`;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
private isAllowedDevice(device: USBDeviceFilter): boolean {
|
|
356
|
-
if (this.options.allowAllDevices) {
|
|
357
|
-
return true;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
const isSameDevice = (device1: USBDeviceFilter, device2: USBDeviceFilter): boolean => {
|
|
361
|
-
return (device1.productId === device2.productId
|
|
362
|
-
&& device1.vendorId === device2.vendorId
|
|
363
|
-
&& device1.serialNumber === device2.serialNumber);
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
for (const i in this.allowedDevices) {
|
|
367
|
-
if (isSameDevice(device, this.allowedDevices[i])) {
|
|
368
|
-
return true;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
return false;
|
|
373
|
-
}
|
|
374
|
-
}
|