@yume-chan/adb-daemon-webusb 0.0.24 → 1.1.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.
@@ -0,0 +1,17 @@
1
+ import type { DeviceObserver } from "@yume-chan/adb";
2
+ import { AdbDaemonWebUsbDevice } from "./device.js";
3
+ import type { AdbDaemonWebUsbDeviceManager } from "./manager.js";
4
+ /**
5
+ * A watcher that listens for new WebUSB devices and notifies the callback when
6
+ * a new device is connected or disconnected.
7
+ */
8
+ export declare class AdbDaemonWebUsbDeviceObserver implements DeviceObserver<AdbDaemonWebUsbDevice> {
9
+ #private;
10
+ onDeviceAdd: import("@yume-chan/event").AddEventListener<AdbDaemonWebUsbDevice[], unknown>;
11
+ onDeviceRemove: import("@yume-chan/event").AddEventListener<AdbDaemonWebUsbDevice[], unknown>;
12
+ onListChange: import("@yume-chan/event").AddEventListener<AdbDaemonWebUsbDevice[], unknown>;
13
+ current: AdbDaemonWebUsbDevice[];
14
+ constructor(usb: USB, options?: AdbDaemonWebUsbDeviceManager.RequestDeviceOptions);
15
+ stop(): void;
16
+ }
17
+ //# sourceMappingURL=observer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.d.ts","sourceRoot":"","sources":["../src/observer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,OAAO,EACH,qBAAqB,EAExB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAC;AAIjE;;;GAGG;AACH,qBAAa,6BACT,YAAW,cAAc,CAAC,qBAAqB,CAAC;;IAOhD,WAAW,gFAA2B;IAGtC,cAAc,gFAA8B;IAG5C,YAAY,gFAA4B;IAExC,OAAO,EAAE,qBAAqB,EAAE,CAAM;gBAGlC,GAAG,EAAE,GAAG,EACR,OAAO,GAAE,4BAA4B,CAAC,oBAAyB;IA2CnE,IAAI,IAAI,IAAI;CAWf"}
@@ -0,0 +1,54 @@
1
+ import { EventEmitter } from "@yume-chan/event";
2
+ import { AdbDaemonWebUsbDevice, mergeDefaultAdbInterfaceFilter, } from "./device.js";
3
+ import { matchFilters } from "./utils.js";
4
+ /**
5
+ * A watcher that listens for new WebUSB devices and notifies the callback when
6
+ * a new device is connected or disconnected.
7
+ */
8
+ export class AdbDaemonWebUsbDeviceObserver {
9
+ #filters;
10
+ #exclusionFilters;
11
+ #usbManager;
12
+ #onDeviceAdd = new EventEmitter();
13
+ onDeviceAdd = this.#onDeviceAdd.event;
14
+ #onDeviceRemove = new EventEmitter();
15
+ onDeviceRemove = this.#onDeviceRemove.event;
16
+ #onListChange = new EventEmitter();
17
+ onListChange = this.#onListChange.event;
18
+ current = [];
19
+ constructor(usb, options = {}) {
20
+ this.#filters = mergeDefaultAdbInterfaceFilter(options.filters);
21
+ this.#exclusionFilters = options.exclusionFilters;
22
+ this.#usbManager = usb;
23
+ this.#usbManager.addEventListener("connect", this.#handleConnect);
24
+ this.#usbManager.addEventListener("disconnect", this.#handleDisconnect);
25
+ }
26
+ #handleConnect = (e) => {
27
+ const interface_ = matchFilters(e.device, this.#filters, this.#exclusionFilters);
28
+ if (!interface_) {
29
+ return;
30
+ }
31
+ const device = new AdbDaemonWebUsbDevice(e.device, interface_, this.#usbManager);
32
+ this.#onDeviceAdd.fire([device]);
33
+ this.current.push(device);
34
+ this.#onListChange.fire(this.current);
35
+ };
36
+ #handleDisconnect = (e) => {
37
+ const index = this.current.findIndex((device) => device.raw === e.device);
38
+ if (index !== -1) {
39
+ const device = this.current[index];
40
+ this.#onDeviceRemove.fire([device]);
41
+ this.current[index] = this.current[this.current.length - 1];
42
+ this.current.length -= 1;
43
+ this.#onListChange.fire(this.current);
44
+ }
45
+ };
46
+ stop() {
47
+ this.#usbManager.removeEventListener("connect", this.#handleConnect);
48
+ this.#usbManager.removeEventListener("disconnect", this.#handleDisconnect);
49
+ this.#onDeviceAdd.dispose();
50
+ this.#onDeviceRemove.dispose();
51
+ this.#onListChange.dispose();
52
+ }
53
+ }
54
+ //# sourceMappingURL=observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.js","sourceRoot":"","sources":["../src/observer.ts"],"names":[],"mappings":"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"}
package/esm/utils.d.ts CHANGED
@@ -1,14 +1,31 @@
1
- /// <reference types="w3c-web-usb" />
2
- export declare function isErrorName(e: unknown, name: string): boolean;
1
+ export declare function isErrorName(e: unknown, name: string): e is Error;
2
+ export type PickNonNullable<T, K extends keyof T> = {
3
+ [P in K]-?: NonNullable<T[P]>;
4
+ };
3
5
  /**
4
6
  * `classCode`, `subclassCode` and `protocolCode` are required
5
7
  * for selecting correct USB configuration and interface.
6
8
  */
7
- export type AdbDeviceFilter = USBDeviceFilter & Required<Pick<USBDeviceFilter, "classCode" | "subclassCode" | "protocolCode">>;
8
- export declare function findUsbAlternateInterface(device: USBDevice, filters: AdbDeviceFilter[]): {
9
+ export type UsbInterfaceFilter = PickNonNullable<USBDeviceFilter, "classCode" | "subclassCode" | "protocolCode">;
10
+ export declare function isUsbInterfaceFilter(filter: USBDeviceFilter): filter is UsbInterfaceFilter;
11
+ export interface UsbInterfaceIdentifier {
9
12
  configuration: USBConfiguration;
10
13
  interface_: USBInterface;
11
14
  alternate: USBAlternateInterface;
12
- };
15
+ }
16
+ export declare function findUsbInterface(device: USBDevice, filter: UsbInterfaceFilter): UsbInterfaceIdentifier | undefined;
13
17
  export declare function getSerialNumber(device: USBDevice): string;
18
+ /**
19
+ * Find the first pair of input and output endpoints from an alternate interface.
20
+ *
21
+ * ADB interface only has two endpoints, one for input and one for output.
22
+ */
23
+ export declare function findUsbEndpoints(endpoints: USBEndpoint[]): {
24
+ inEndpoint: USBEndpoint;
25
+ outEndpoint: USBEndpoint;
26
+ };
27
+ export declare function matchFilter(device: USBDevice, filter: USBDeviceFilter & UsbInterfaceFilter): UsbInterfaceIdentifier | false;
28
+ export declare function matchFilter(device: USBDevice, filter: USBDeviceFilter): boolean;
29
+ export declare function matchFilters(device: USBDevice, filters: (USBDeviceFilter & UsbInterfaceFilter)[], exclusionFilters?: USBDeviceFilter[]): UsbInterfaceIdentifier | false;
30
+ export declare function matchFilters(device: USBDevice, filters: USBDeviceFilter[], exclusionFilters?: USBDeviceFilter[]): boolean;
14
31
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";AAAA,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAO7D;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,eAAe,GACzC,QAAQ,CACJ,IAAI,CAAC,eAAe,EAAE,WAAW,GAAG,cAAc,GAAG,cAAc,CAAC,CACvE,CAAC;AAcN,wBAAgB,yBAAyB,CACrC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,eAAe,EAAE;;;;EAa7B;AAMD,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,UAMhD"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,IAAI,KAAK,CAOhE;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI;KAC/C,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAChC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,eAAe,CAC5C,eAAe,EACf,WAAW,GAAG,cAAc,GAAG,cAAc,CAChD,CAAC;AAEF,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,eAAe,GACxB,MAAM,IAAI,kBAAkB,CAM9B;AAaD,MAAM,WAAW,sBAAsB;IACnC,aAAa,EAAE,gBAAgB,CAAC;IAChC,UAAU,EAAE,YAAY,CAAC;IACzB,SAAS,EAAE,qBAAqB,CAAC;CACpC;AAED,wBAAgB,gBAAgB,CAC5B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,kBAAkB,GAC3B,sBAAsB,GAAG,SAAS,CAWpC;AAMD,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,UAMhD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,WAAW,EAAE;;;EAgCxD;AAED,wBAAgB,WAAW,CACvB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,GAAG,kBAAkB,GAC7C,sBAAsB,GAAG,KAAK,CAAC;AAClC,wBAAgB,WAAW,CACvB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,GACxB,OAAO,CAAC;AA8BX,wBAAgB,YAAY,CACxB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,CAAC,eAAe,GAAG,kBAAkB,CAAC,EAAE,EACjD,gBAAgB,CAAC,EAAE,eAAe,EAAE,GACrC,sBAAsB,GAAG,KAAK,CAAC;AAClC,wBAAgB,YAAY,CACxB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,eAAe,EAAE,EAC1B,gBAAgB,CAAC,EAAE,eAAe,EAAE,GACrC,OAAO,CAAC"}
package/esm/utils.js CHANGED
@@ -4,22 +4,27 @@ export function isErrorName(e, name) {
4
4
  // https://github.com/node-usb/node-usb/issues/573
5
5
  return (typeof e === "object" && e !== null && "name" in e && e.name === name);
6
6
  }
7
- function alternateMatchesFilter(alternate, filters) {
8
- return filters.some((filter) => alternate.interfaceClass === filter.classCode &&
7
+ export function isUsbInterfaceFilter(filter) {
8
+ return (filter.classCode !== undefined &&
9
+ filter.subclassCode !== undefined &&
10
+ filter.protocolCode !== undefined);
11
+ }
12
+ function matchUsbInterfaceFilter(alternate, filter) {
13
+ return (alternate.interfaceClass === filter.classCode &&
9
14
  alternate.interfaceSubclass === filter.subclassCode &&
10
15
  alternate.interfaceProtocol === filter.protocolCode);
11
16
  }
12
- export function findUsbAlternateInterface(device, filters) {
17
+ export function findUsbInterface(device, filter) {
13
18
  for (const configuration of device.configurations) {
14
19
  for (const interface_ of configuration.interfaces) {
15
20
  for (const alternate of interface_.alternates) {
16
- if (alternateMatchesFilter(alternate, filters)) {
21
+ if (matchUsbInterfaceFilter(alternate, filter)) {
17
22
  return { configuration, interface_, alternate };
18
23
  }
19
24
  }
20
25
  }
21
26
  }
22
- throw new TypeError("No matched alternate interface found");
27
+ return undefined;
23
28
  }
24
29
  function padNumber(value) {
25
30
  return value.toString(16).padStart(4, "0");
@@ -30,4 +35,70 @@ export function getSerialNumber(device) {
30
35
  }
31
36
  return padNumber(device.vendorId) + "x" + padNumber(device.productId);
32
37
  }
38
+ /**
39
+ * Find the first pair of input and output endpoints from an alternate interface.
40
+ *
41
+ * ADB interface only has two endpoints, one for input and one for output.
42
+ */
43
+ export function findUsbEndpoints(endpoints) {
44
+ if (endpoints.length === 0) {
45
+ throw new TypeError("No endpoints given");
46
+ }
47
+ let inEndpoint;
48
+ let outEndpoint;
49
+ for (const endpoint of endpoints) {
50
+ switch (endpoint.direction) {
51
+ case "in":
52
+ inEndpoint = endpoint;
53
+ if (outEndpoint) {
54
+ return { inEndpoint, outEndpoint };
55
+ }
56
+ break;
57
+ case "out":
58
+ outEndpoint = endpoint;
59
+ if (inEndpoint) {
60
+ return { inEndpoint, outEndpoint };
61
+ }
62
+ break;
63
+ }
64
+ }
65
+ if (!inEndpoint) {
66
+ throw new TypeError("No input endpoint found.");
67
+ }
68
+ if (!outEndpoint) {
69
+ throw new TypeError("No output endpoint found.");
70
+ }
71
+ throw new Error("unreachable");
72
+ }
73
+ export function matchFilter(device, filter) {
74
+ if (filter.vendorId !== undefined && device.vendorId !== filter.vendorId) {
75
+ return false;
76
+ }
77
+ if (filter.productId !== undefined &&
78
+ device.productId !== filter.productId) {
79
+ return false;
80
+ }
81
+ if (filter.serialNumber !== undefined &&
82
+ getSerialNumber(device) !== filter.serialNumber) {
83
+ return false;
84
+ }
85
+ if (isUsbInterfaceFilter(filter)) {
86
+ return findUsbInterface(device, filter) || false;
87
+ }
88
+ return true;
89
+ }
90
+ export function matchFilters(device, filters, exclusionFilters) {
91
+ if (exclusionFilters && exclusionFilters.length > 0) {
92
+ if (matchFilters(device, exclusionFilters)) {
93
+ return false;
94
+ }
95
+ }
96
+ for (const filter of filters) {
97
+ const result = matchFilter(device, filter);
98
+ if (result) {
99
+ return result;
100
+ }
101
+ }
102
+ return false;
103
+ }
33
104
  //# sourceMappingURL=utils.js.map
package/esm/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,CAAU,EAAE,IAAY;IAChD,+CAA+C;IAC/C,wBAAwB;IACxB,kDAAkD;IAClD,OAAO,CACH,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CACxE,CAAC;AACN,CAAC;AAWD,SAAS,sBAAsB,CAC3B,SAAgC,EAChC,OAA0B;IAE1B,OAAO,OAAO,CAAC,IAAI,CACf,CAAC,MAAM,EAAE,EAAE,CACP,SAAS,CAAC,cAAc,KAAK,MAAM,CAAC,SAAS;QAC7C,SAAS,CAAC,iBAAiB,KAAK,MAAM,CAAC,YAAY;QACnD,SAAS,CAAC,iBAAiB,KAAK,MAAM,CAAC,YAAY,CAC1D,CAAC;AACN,CAAC;AAED,MAAM,UAAU,yBAAyB,CACrC,MAAiB,EACjB,OAA0B;IAE1B,KAAK,MAAM,aAAa,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAChD,KAAK,MAAM,UAAU,IAAI,aAAa,CAAC,UAAU,EAAE,CAAC;YAChD,KAAK,MAAM,SAAS,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;gBAC5C,IAAI,sBAAsB,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC;oBAC7C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;gBACpD,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;IAED,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC5B,OAAO,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC7C,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACtB,OAAO,MAAM,CAAC,YAAY,CAAC;IAC/B,CAAC;IAED,OAAO,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAC1E,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yume-chan/adb-daemon-webusb",
3
- "version": "0.0.24",
3
+ "version": "1.1.0",
4
4
  "description": "Adb daemon transport connection for `@yume-chan/adb` using WebUSB API.",
5
5
  "keywords": [
6
6
  "webusb",
@@ -24,21 +24,25 @@
24
24
  "type": "module",
25
25
  "main": "esm/index.js",
26
26
  "types": "esm/index.d.ts",
27
+ "sideEffects": false,
27
28
  "dependencies": {
28
29
  "@types/w3c-web-usb": "^1.0.10",
29
- "@yume-chan/adb": "^0.0.24",
30
- "@yume-chan/stream-extra": "^0.0.24",
31
- "@yume-chan/struct": "^0.0.24"
30
+ "@yume-chan/adb": "^1.1.0",
31
+ "@yume-chan/event": "^1.0.0",
32
+ "@yume-chan/stream-extra": "^1.0.0",
33
+ "@yume-chan/struct": "^1.0.0"
32
34
  },
33
35
  "devDependencies": {
36
+ "@types/node": "^22.10.2",
37
+ "prettier": "^3.4.2",
38
+ "typescript": "^5.7.2",
34
39
  "@yume-chan/eslint-config": "^1.0.0",
35
- "@yume-chan/tsconfig": "^1.0.0",
36
- "prettier": "^3.3.2",
37
- "typescript": "^5.4.5"
40
+ "@yume-chan/test-runner": "^1.0.0",
41
+ "@yume-chan/tsconfig": "^1.0.0"
38
42
  },
39
43
  "scripts": {
40
44
  "build": "tsc -b tsconfig.build.json",
41
- "build:watch": "tsc -b tsconfig.build.json",
42
- "lint": "run-eslint && prettier src/**/*.ts --write --tab-width 4"
45
+ "lint": "run-eslint && prettier src/**/*.ts --write --tab-width 4",
46
+ "test": "run-test"
43
47
  }
44
48
  }
package/src/device.ts CHANGED
@@ -20,61 +20,35 @@ import {
20
20
  pipeFrom,
21
21
  } from "@yume-chan/stream-extra";
22
22
  import type { ExactReadable } from "@yume-chan/struct";
23
- import { EMPTY_UINT8_ARRAY } from "@yume-chan/struct";
23
+ import { EmptyUint8Array } from "@yume-chan/struct";
24
24
 
25
- import type { AdbDeviceFilter } from "./utils.js";
26
- import {
27
- findUsbAlternateInterface,
28
- getSerialNumber,
29
- isErrorName,
30
- } from "./utils.js";
25
+ import type { UsbInterfaceFilter, UsbInterfaceIdentifier } from "./utils.js";
26
+ import { findUsbEndpoints, getSerialNumber, isErrorName } from "./utils.js";
31
27
 
32
28
  /**
33
29
  * The default filter for ADB devices, as defined by Google.
34
30
  */
35
- export const ADB_DEFAULT_DEVICE_FILTER = {
31
+ export const AdbDefaultInterfaceFilter = {
36
32
  classCode: 0xff,
37
33
  subclassCode: 0x42,
38
34
  protocolCode: 1,
39
- } as const satisfies AdbDeviceFilter;
40
-
41
- /**
42
- * Find the first pair of input and output endpoints from an alternate interface.
43
- *
44
- * ADB interface only has two endpoints, one for input and one for output.
45
- */
46
- function findUsbEndpoints(endpoints: USBEndpoint[]) {
47
- if (endpoints.length === 0) {
48
- throw new TypeError("No endpoints given");
49
- }
50
-
51
- let inEndpoint: USBEndpoint | undefined;
52
- let outEndpoint: USBEndpoint | undefined;
53
-
54
- for (const endpoint of endpoints) {
55
- switch (endpoint.direction) {
56
- case "in":
57
- inEndpoint = endpoint;
58
- if (outEndpoint) {
59
- return { inEndpoint, outEndpoint };
60
- }
61
- break;
62
- case "out":
63
- outEndpoint = endpoint;
64
- if (inEndpoint) {
65
- return { inEndpoint, outEndpoint };
66
- }
67
- break;
68
- }
69
- }
70
-
71
- if (!inEndpoint) {
72
- throw new TypeError("No input endpoint found.");
73
- }
74
- if (!outEndpoint) {
75
- throw new TypeError("No output endpoint found.");
35
+ } as const satisfies UsbInterfaceFilter;
36
+
37
+ export function mergeDefaultAdbInterfaceFilter(
38
+ filters: USBDeviceFilter[] | undefined,
39
+ ): (USBDeviceFilter & UsbInterfaceFilter)[] {
40
+ if (!filters || filters.length === 0) {
41
+ return [AdbDefaultInterfaceFilter];
42
+ } else {
43
+ return filters.map((filter) => ({
44
+ ...filter,
45
+ classCode: filter.classCode ?? AdbDefaultInterfaceFilter.classCode,
46
+ subclassCode:
47
+ filter.subclassCode ?? AdbDefaultInterfaceFilter.subclassCode,
48
+ protocolCode:
49
+ filter.protocolCode ?? AdbDefaultInterfaceFilter.protocolCode,
50
+ }));
76
51
  }
77
- throw new Error("unreachable");
78
52
  }
79
53
 
80
54
  class Uint8ArrayExactReadable implements ExactReadable {
@@ -203,7 +177,7 @@ export class AdbDaemonWebUsbConnection
203
177
  if (zeroMask && (chunk.length & zeroMask) === 0) {
204
178
  await device.raw.transferOut(
205
179
  outEndpoint.endpointNumber,
206
- EMPTY_UINT8_ARRAY,
180
+ EmptyUint8Array,
207
181
  );
208
182
  }
209
183
  } catch (e) {
@@ -252,7 +226,7 @@ export class AdbDaemonWebUsbConnection
252
226
  );
253
227
  packet.payload = new Uint8Array(result.data!.buffer);
254
228
  } else {
255
- packet.payload = EMPTY_UINT8_ARRAY;
229
+ packet.payload = EmptyUint8Array;
256
230
  }
257
231
 
258
232
  return packet;
@@ -260,7 +234,7 @@ export class AdbDaemonWebUsbConnection
260
234
  } catch (e) {
261
235
  // On Windows, disconnecting the device will cause `NetworkError` to be thrown,
262
236
  // even before the `disconnect` event is fired.
263
- // We need to wait a little bit and check if the device is still connected.
237
+ // Wait a little while and check if the device is still connected.
264
238
  // https://github.com/WICG/webusb/issues/219
265
239
  if (isErrorName(e, "NetworkError")) {
266
240
  await new Promise<void>((resolve) => {
@@ -271,8 +245,6 @@ export class AdbDaemonWebUsbConnection
271
245
 
272
246
  if (closed) {
273
247
  return undefined;
274
- } else {
275
- throw e;
276
248
  }
277
249
  }
278
250
 
@@ -282,7 +254,7 @@ export class AdbDaemonWebUsbConnection
282
254
  }
283
255
 
284
256
  export class AdbDaemonWebUsbDevice implements AdbDaemonDevice {
285
- #filters: AdbDeviceFilter[];
257
+ #interface: UsbInterfaceIdentifier;
286
258
  #usbManager: USB;
287
259
 
288
260
  #raw: USBDevice;
@@ -307,22 +279,24 @@ export class AdbDaemonWebUsbDevice implements AdbDaemonDevice {
307
279
  */
308
280
  constructor(
309
281
  device: USBDevice,
310
- filters: AdbDeviceFilter[] = [ADB_DEFAULT_DEVICE_FILTER],
282
+ interface_: UsbInterfaceIdentifier,
311
283
  usbManager: USB,
312
284
  ) {
313
285
  this.#raw = device;
314
286
  this.#serial = getSerialNumber(device);
315
- this.#filters = filters;
287
+ this.#interface = interface_;
316
288
  this.#usbManager = usbManager;
317
289
  }
318
290
 
319
- async #claimInterface(): Promise<[USBEndpoint, USBEndpoint]> {
291
+ async #claimInterface(): Promise<{
292
+ inEndpoint: USBEndpoint;
293
+ outEndpoint: USBEndpoint;
294
+ }> {
320
295
  if (!this.#raw.opened) {
321
296
  await this.#raw.open();
322
297
  }
323
298
 
324
- const { configuration, interface_, alternate } =
325
- findUsbAlternateInterface(this.#raw, this.#filters);
299
+ const { configuration, interface_, alternate } = this.#interface;
326
300
 
327
301
  if (
328
302
  this.#raw.configuration?.configurationValue !==
@@ -336,7 +310,15 @@ export class AdbDaemonWebUsbDevice implements AdbDaemonDevice {
336
310
  }
337
311
 
338
312
  if (!interface_.claimed) {
339
- await this.#raw.claimInterface(interface_.interfaceNumber);
313
+ try {
314
+ await this.#raw.claimInterface(interface_.interfaceNumber);
315
+ } catch (e) {
316
+ if (isErrorName(e, "NetworkError")) {
317
+ throw new AdbDaemonWebUsbDevice.DeviceBusyError(e);
318
+ }
319
+
320
+ throw e;
321
+ }
340
322
  }
341
323
 
342
324
  if (
@@ -348,18 +330,14 @@ export class AdbDaemonWebUsbDevice implements AdbDaemonDevice {
348
330
  );
349
331
  }
350
332
 
351
- const { inEndpoint, outEndpoint } = findUsbEndpoints(
352
- alternate.endpoints,
353
- );
354
- return [inEndpoint, outEndpoint];
333
+ return findUsbEndpoints(alternate.endpoints);
355
334
  }
356
335
 
357
336
  /**
358
- * Claim the device and create a pair of `AdbPacket` streams to the ADB interface.
359
- * @returns The pair of `AdbPacket` streams.
337
+ * Open the device and create a new connection to the ADB Daemon.
360
338
  */
361
339
  async connect(): Promise<AdbDaemonWebUsbConnection> {
362
- const [inEndpoint, outEndpoint] = await this.#claimInterface();
340
+ const { inEndpoint, outEndpoint } = await this.#claimInterface();
363
341
  return new AdbDaemonWebUsbConnection(
364
342
  this,
365
343
  inEndpoint,
@@ -368,3 +346,13 @@ export class AdbDaemonWebUsbDevice implements AdbDaemonDevice {
368
346
  );
369
347
  }
370
348
  }
349
+
350
+ export namespace AdbDaemonWebUsbDevice {
351
+ export class DeviceBusyError extends Error {
352
+ constructor(cause?: Error) {
353
+ super("The device is already in used by another program", {
354
+ cause,
355
+ });
356
+ }
357
+ }
358
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from "./device.js";
2
2
  export * from "./manager.js";
3
3
  export * from "./utils.js";
4
- export * from "./watcher.js";
4
+ export * from "./observer.js";