@yume-chan/adb 0.0.20 → 0.0.22

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.
Files changed (132) hide show
  1. package/CHANGELOG.json +45 -0
  2. package/CHANGELOG.md +22 -1
  3. package/README.md +46 -364
  4. package/esm/adb.d.ts +9 -5
  5. package/esm/adb.d.ts.map +1 -1
  6. package/esm/adb.js +21 -11
  7. package/esm/adb.js.map +1 -1
  8. package/esm/banner.d.ts.map +1 -1
  9. package/esm/banner.js +1 -1
  10. package/esm/banner.js.map +1 -1
  11. package/esm/commands/base.d.ts.map +1 -1
  12. package/esm/commands/base.js.map +1 -1
  13. package/esm/commands/framebuffer.d.ts +9 -0
  14. package/esm/commands/framebuffer.d.ts.map +1 -1
  15. package/esm/commands/framebuffer.js +27 -3
  16. package/esm/commands/framebuffer.js.map +1 -1
  17. package/esm/commands/power.d.ts.map +1 -1
  18. package/esm/commands/power.js.map +1 -1
  19. package/esm/commands/reverse.d.ts.map +1 -1
  20. package/esm/commands/reverse.js.map +1 -1
  21. package/esm/commands/subprocess/command.d.ts +1 -1
  22. package/esm/commands/subprocess/command.d.ts.map +1 -1
  23. package/esm/commands/subprocess/command.js +15 -13
  24. package/esm/commands/subprocess/command.js.map +1 -1
  25. package/esm/commands/subprocess/protocols/none.d.ts +2 -1
  26. package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
  27. package/esm/commands/subprocess/protocols/none.js +11 -14
  28. package/esm/commands/subprocess/protocols/none.js.map +1 -1
  29. package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
  30. package/esm/commands/subprocess/protocols/shell.js +21 -17
  31. package/esm/commands/subprocess/protocols/shell.js.map +1 -1
  32. package/esm/commands/subprocess/utils.js.map +1 -1
  33. package/esm/commands/sync/list.d.ts +2 -1
  34. package/esm/commands/sync/list.d.ts.map +1 -1
  35. package/esm/commands/sync/list.js +2 -2
  36. package/esm/commands/sync/list.js.map +1 -1
  37. package/esm/commands/sync/pull.d.ts.map +1 -1
  38. package/esm/commands/sync/pull.js +4 -0
  39. package/esm/commands/sync/pull.js.map +1 -1
  40. package/esm/commands/sync/push.js +1 -1
  41. package/esm/commands/sync/push.js.map +1 -1
  42. package/esm/commands/sync/request.js +1 -1
  43. package/esm/commands/sync/request.js.map +1 -1
  44. package/esm/commands/sync/response.d.ts +3 -1
  45. package/esm/commands/sync/response.d.ts.map +1 -1
  46. package/esm/commands/sync/response.js +5 -3
  47. package/esm/commands/sync/response.js.map +1 -1
  48. package/esm/commands/sync/socket.d.ts +1 -1
  49. package/esm/commands/sync/socket.d.ts.map +1 -1
  50. package/esm/commands/sync/socket.js +7 -3
  51. package/esm/commands/sync/socket.js.map +1 -1
  52. package/esm/commands/sync/stat.js +2 -2
  53. package/esm/commands/sync/stat.js.map +1 -1
  54. package/esm/commands/sync/sync.d.ts +15 -0
  55. package/esm/commands/sync/sync.d.ts.map +1 -1
  56. package/esm/commands/sync/sync.js +17 -2
  57. package/esm/commands/sync/sync.js.map +1 -1
  58. package/esm/commands/tcpip.d.ts +21 -0
  59. package/esm/commands/tcpip.d.ts.map +1 -1
  60. package/esm/commands/tcpip.js +18 -0
  61. package/esm/commands/tcpip.js.map +1 -1
  62. package/esm/daemon/auth.d.ts +11 -9
  63. package/esm/daemon/auth.d.ts.map +1 -1
  64. package/esm/daemon/auth.js +21 -12
  65. package/esm/daemon/auth.js.map +1 -1
  66. package/esm/daemon/crypto.js +2 -2
  67. package/esm/daemon/crypto.js.map +1 -1
  68. package/esm/daemon/dispatcher.d.ts +6 -5
  69. package/esm/daemon/dispatcher.d.ts.map +1 -1
  70. package/esm/daemon/dispatcher.js +69 -58
  71. package/esm/daemon/dispatcher.js.map +1 -1
  72. package/esm/daemon/packet.d.ts +5 -5
  73. package/esm/daemon/packet.d.ts.map +1 -1
  74. package/esm/daemon/packet.js +2 -2
  75. package/esm/daemon/packet.js.map +1 -1
  76. package/esm/daemon/socket.d.ts +7 -14
  77. package/esm/daemon/socket.d.ts.map +1 -1
  78. package/esm/daemon/socket.js +58 -46
  79. package/esm/daemon/socket.js.map +1 -1
  80. package/esm/daemon/transport.d.ts +16 -4
  81. package/esm/daemon/transport.d.ts.map +1 -1
  82. package/esm/daemon/transport.js +11 -7
  83. package/esm/daemon/transport.js.map +1 -1
  84. package/esm/features.js +1 -1
  85. package/esm/features.js.map +1 -1
  86. package/esm/server/client.d.ts +12 -8
  87. package/esm/server/client.d.ts.map +1 -1
  88. package/esm/server/client.js +41 -22
  89. package/esm/server/client.js.map +1 -1
  90. package/esm/server/transport.d.ts.map +1 -1
  91. package/esm/server/transport.js.map +1 -1
  92. package/esm/utils/auto-reset-event.d.ts.map +1 -1
  93. package/esm/utils/auto-reset-event.js.map +1 -1
  94. package/esm/utils/base64.js.map +1 -1
  95. package/esm/utils/conditional-variable.d.ts.map +1 -1
  96. package/esm/utils/conditional-variable.js.map +1 -1
  97. package/esm/utils/hex.js.map +1 -1
  98. package/package.json +13 -14
  99. package/src/adb.ts +49 -35
  100. package/src/banner.ts +7 -7
  101. package/src/commands/base.ts +1 -1
  102. package/src/commands/framebuffer.ts +31 -3
  103. package/src/commands/power.ts +9 -9
  104. package/src/commands/reverse.ts +13 -13
  105. package/src/commands/subprocess/command.ts +24 -25
  106. package/src/commands/subprocess/protocols/none.ts +23 -27
  107. package/src/commands/subprocess/protocols/shell.ts +42 -37
  108. package/src/commands/sync/list.ts +11 -7
  109. package/src/commands/sync/pull.ts +7 -4
  110. package/src/commands/sync/push.ts +6 -6
  111. package/src/commands/sync/request.ts +3 -3
  112. package/src/commands/sync/response.ts +8 -6
  113. package/src/commands/sync/socket.ts +19 -14
  114. package/src/commands/sync/stat.ts +5 -5
  115. package/src/commands/sync/sync.ts +32 -17
  116. package/src/commands/tcpip.ts +46 -2
  117. package/src/daemon/auth.ts +49 -30
  118. package/src/daemon/crypto.ts +9 -9
  119. package/src/daemon/dispatcher.ts +104 -88
  120. package/src/daemon/packet.ts +2 -2
  121. package/src/daemon/socket.ts +106 -115
  122. package/src/daemon/transport.ts +49 -29
  123. package/src/server/client.ts +88 -77
  124. package/src/server/transport.ts +13 -13
  125. package/src/utils/auto-reset-event.ts +4 -4
  126. package/src/utils/base64.ts +5 -5
  127. package/src/utils/conditional-variable.ts +4 -4
  128. package/tsconfig.build.tsbuildinfo +1 -1
  129. package/esm/daemon/connection.d.ts +0 -11
  130. package/esm/daemon/connection.d.ts.map +0 -1
  131. package/esm/daemon/connection.js +0 -2
  132. package/esm/daemon/connection.js.map +0 -1
@@ -27,19 +27,34 @@ import { AdbCommand, calculateChecksum } from "./packet.js";
27
27
 
28
28
  export const ADB_DAEMON_VERSION_OMIT_CHECKSUM = 0x01000001;
29
29
 
30
+ export type AdbDaemonConnection = ReadableWritablePair<
31
+ AdbPacketData,
32
+ Consumable<AdbPacketInit>
33
+ >;
34
+
30
35
  interface AdbDaemonAuthenticationOptions {
31
36
  serial: string;
32
- connection: ReadableWritablePair<AdbPacketData, Consumable<AdbPacketInit>>;
37
+ connection: AdbDaemonConnection;
33
38
  credentialStore: AdbCredentialStore;
34
39
  authenticators?: AdbAuthenticator[];
40
+ /**
41
+ * Whether to preserve the connection open after the `AdbDaemonTransport` is closed.
42
+ */
43
+ preserveConnection?: boolean | undefined;
44
+ debugSlowRead?: boolean | undefined;
35
45
  }
36
46
 
37
47
  interface AdbDaemonSocketConnectorConstructionOptions {
38
48
  serial: string;
39
- connection: ReadableWritablePair<AdbPacketData, Consumable<AdbPacketInit>>;
49
+ connection: AdbDaemonConnection;
40
50
  version: number;
41
51
  maxPayloadSize: number;
42
52
  banner: string;
53
+ /**
54
+ * Whether to preserve the connection open after the `AdbDaemonTransport` is closed.
55
+ */
56
+ preserveConnection?: boolean | undefined;
57
+ debugSlowRead?: boolean | undefined;
43
58
  }
44
59
 
45
60
  export class AdbDaemonTransport implements AdbTransport {
@@ -51,11 +66,12 @@ export class AdbDaemonTransport implements AdbTransport {
51
66
  * on the same connection. Because every time the device receives a `CNXN` packet,
52
67
  * it resets all internal state, and starts a new authentication process.
53
68
  */
54
- public static async authenticate({
69
+ static async authenticate({
55
70
  serial,
56
71
  connection,
57
72
  credentialStore,
58
73
  authenticators = ADB_DEFAULT_AUTHENTICATORS,
74
+ ...options
59
75
  }: AdbDaemonAuthenticationOptions): Promise<AdbDaemonTransport> {
60
76
  // Initially, set to highest-supported version and payload size.
61
77
  let version = 0x01000001;
@@ -65,7 +81,7 @@ export class AdbDaemonTransport implements AdbTransport {
65
81
  const resolver = new PromiseResolver<string>();
66
82
  const authProcessor = new AdbAuthenticationProcessor(
67
83
  authenticators,
68
- credentialStore
84
+ credentialStore,
69
85
  );
70
86
 
71
87
  // Here is similar to `AdbPacketDispatcher`,
@@ -80,14 +96,13 @@ export class AdbDaemonTransport implements AdbTransport {
80
96
  version = Math.min(version, packet.arg0);
81
97
  maxPayloadSize = Math.min(
82
98
  maxPayloadSize,
83
- packet.arg1
99
+ packet.arg1,
84
100
  );
85
101
  resolver.resolve(decodeUtf8(packet.payload));
86
102
  break;
87
103
  case AdbCommand.Auth: {
88
- const response = await authProcessor.process(
89
- packet
90
- );
104
+ const response =
105
+ await authProcessor.process(packet);
91
106
  await sendPacket(response);
92
107
  break;
93
108
  }
@@ -105,19 +120,19 @@ export class AdbDaemonTransport implements AdbTransport {
105
120
  // Don't cancel the source ReadableStream on AbortSignal abort.
106
121
  preventCancel: true,
107
122
  signal: abortController.signal,
108
- }
123
+ },
109
124
  )
110
125
  .then(
111
126
  () => {
112
127
  if (resolver.state === "running") {
113
128
  resolver.reject(
114
- new Error("Connection closed unexpectedly")
129
+ new Error("Connection closed unexpectedly"),
115
130
  );
116
131
  }
117
132
  },
118
133
  (e) => {
119
134
  resolver.reject(e);
120
- }
135
+ },
121
136
  );
122
137
 
123
138
  const writer = connection.writable.getWriter();
@@ -147,7 +162,7 @@ export class AdbDaemonTransport implements AdbTransport {
147
162
  AdbFeature.AbbExec,
148
163
  "remount_shell",
149
164
  "track_app",
150
- "sendrecv_v2",
165
+ AdbFeature.SendReceiveV2,
151
166
  "sendrecv_v2_brotli",
152
167
  "sendrecv_v2_lz4",
153
168
  "sendrecv_v2_zstd",
@@ -180,43 +195,49 @@ export class AdbDaemonTransport implements AdbTransport {
180
195
  version,
181
196
  maxPayloadSize,
182
197
  banner,
198
+ ...options,
183
199
  });
184
200
  }
185
201
 
202
+ #connection: AdbDaemonConnection;
203
+ get connection() {
204
+ return this.#connection;
205
+ }
206
+
186
207
  readonly #dispatcher: AdbPacketDispatcher;
187
208
 
188
209
  #serial: string;
189
- public get serial() {
210
+ get serial() {
190
211
  return this.#serial;
191
212
  }
192
213
 
193
214
  #protocolVersion: number;
194
- public get protocolVersion() {
215
+ get protocolVersion() {
195
216
  return this.#protocolVersion;
196
217
  }
197
218
 
198
- #maxPayloadSize: number;
199
- public get maxPayloadSize() {
200
- return this.#maxPayloadSize;
219
+ get maxPayloadSize() {
220
+ return this.#dispatcher.options.maxPayloadSize;
201
221
  }
202
222
 
203
223
  #banner: AdbBanner;
204
- public get banner() {
224
+ get banner() {
205
225
  return this.#banner;
206
226
  }
207
227
 
208
- public get disconnected() {
228
+ get disconnected() {
209
229
  return this.#dispatcher.disconnected;
210
230
  }
211
231
 
212
- public constructor({
232
+ constructor({
213
233
  serial,
214
234
  connection,
215
235
  version,
216
- maxPayloadSize,
217
236
  banner,
237
+ ...options
218
238
  }: AdbDaemonSocketConnectorConstructionOptions) {
219
239
  this.#serial = serial;
240
+ this.#connection = connection;
220
241
  this.#banner = AdbBanner.parse(banner);
221
242
 
222
243
  let calculateChecksum: boolean;
@@ -232,20 +253,19 @@ export class AdbDaemonTransport implements AdbTransport {
232
253
  this.#dispatcher = new AdbPacketDispatcher(connection, {
233
254
  calculateChecksum,
234
255
  appendNullToServiceString,
235
- maxPayloadSize,
256
+ ...options,
236
257
  });
237
258
 
238
259
  this.#protocolVersion = version;
239
- this.#maxPayloadSize = maxPayloadSize;
240
260
  }
241
261
 
242
- public connect(service: string): ValueOrPromise<AdbSocket> {
262
+ connect(service: string): ValueOrPromise<AdbSocket> {
243
263
  return this.#dispatcher.createSocket(service);
244
264
  }
245
265
 
246
- public addReverseTunnel(
266
+ addReverseTunnel(
247
267
  handler: AdbIncomingSocketHandler,
248
- address?: string
268
+ address?: string,
249
269
  ): string {
250
270
  if (!address) {
251
271
  const id = Math.random().toString().substring(2);
@@ -255,15 +275,15 @@ export class AdbDaemonTransport implements AdbTransport {
255
275
  return address;
256
276
  }
257
277
 
258
- public removeReverseTunnel(address: string): void {
278
+ removeReverseTunnel(address: string): void {
259
279
  this.#dispatcher.removeReverseTunnel(address);
260
280
  }
261
281
 
262
- public clearReverseTunnels(): void {
282
+ clearReverseTunnels(): void {
263
283
  this.#dispatcher.clearReverseTunnels();
264
284
  }
265
285
 
266
- public close(): ValueOrPromise<void> {
286
+ close(): ValueOrPromise<void> {
267
287
  return this.#dispatcher.close();
268
288
  }
269
289
  }
@@ -3,13 +3,11 @@
3
3
  import { PromiseResolver } from "@yume-chan/async";
4
4
  import type {
5
5
  AbortSignal,
6
- Consumable,
7
6
  ReadableWritablePair,
8
7
  WritableStreamDefaultWriter,
9
8
  } from "@yume-chan/stream-extra";
10
9
  import {
11
10
  BufferedReadableStream,
12
- DuplexStreamFactory,
13
11
  UnwrapConsumableStream,
14
12
  WrapWritableStream,
15
13
  } from "@yume-chan/stream-extra";
@@ -25,7 +23,7 @@ import {
25
23
  encodeUtf8,
26
24
  } from "@yume-chan/struct";
27
25
 
28
- import type { AdbIncomingSocketHandler, AdbSocket } from "../adb.js";
26
+ import type { AdbIncomingSocketHandler, AdbSocket, Closeable } from "../adb.js";
29
27
  import { AdbBanner } from "../banner.js";
30
28
  import type { AdbFeature } from "../features.js";
31
29
  import { NOOP, hexToNumber, numberToHex } from "../utils/index.js";
@@ -37,14 +35,20 @@ export interface AdbServerConnectionOptions {
37
35
  signal?: AbortSignal | undefined;
38
36
  }
39
37
 
40
- export interface AdbServerConnection {
38
+ export interface AdbServerConnection
39
+ extends ReadableWritablePair<Uint8Array, Uint8Array>,
40
+ Closeable {
41
+ get closed(): Promise<void>;
42
+ }
43
+
44
+ export interface AdbServerConnector {
41
45
  connect(
42
- options?: AdbServerConnectionOptions
43
- ): ValueOrPromise<ReadableWritablePair<Uint8Array, Uint8Array>>;
46
+ options?: AdbServerConnectionOptions,
47
+ ): ValueOrPromise<AdbServerConnection>;
44
48
 
45
49
  addReverseTunnel(
46
50
  handler: AdbIncomingSocketHandler,
47
- address?: string
51
+ address?: string,
48
52
  ): ValueOrPromise<string>;
49
53
 
50
54
  removeReverseTunnel(address: string): ValueOrPromise<void>;
@@ -57,10 +61,8 @@ export interface AdbServerSocket extends AdbSocket {
57
61
  }
58
62
 
59
63
  export type AdbServerDeviceSelector =
60
- | {
61
- serial: string;
62
- }
63
64
  | { transportId: bigint }
65
+ | { serial: string }
64
66
  | { usb: true }
65
67
  | { tcp: true }
66
68
  | undefined;
@@ -74,18 +76,18 @@ export interface AdbServerDevice {
74
76
  }
75
77
 
76
78
  export class AdbServerClient {
77
- public static readonly VERSION = 41;
79
+ static readonly VERSION = 41;
78
80
 
79
- public readonly connection: AdbServerConnection;
81
+ readonly connection: AdbServerConnector;
80
82
 
81
- public constructor(connection: AdbServerConnection) {
83
+ constructor(connection: AdbServerConnector) {
82
84
  this.connection = connection;
83
85
  }
84
86
 
85
- public static readString(stream: ExactReadable): string;
86
- public static readString(stream: AsyncExactReadable): PromiseLike<string>;
87
- public static readString(
88
- stream: ExactReadable | AsyncExactReadable
87
+ static readString(stream: ExactReadable): string;
88
+ static readString(stream: AsyncExactReadable): PromiseLike<string>;
89
+ static readString(
90
+ stream: ExactReadable | AsyncExactReadable,
89
91
  ): string | PromiseLike<string> {
90
92
  return SyncPromise.try(() => stream.readExactly(4))
91
93
  .then((buffer) => {
@@ -98,9 +100,9 @@ export class AdbServerClient {
98
100
  .valueOrPromise();
99
101
  }
100
102
 
101
- public static async writeString(
103
+ static async writeString(
102
104
  writer: WritableStreamDefaultWriter<Uint8Array>,
103
- value: string
105
+ value: string,
104
106
  ): Promise<void> {
105
107
  const valueBuffer = encodeUtf8(value);
106
108
  const buffer = new Uint8Array(4 + valueBuffer.length);
@@ -109,8 +111,8 @@ export class AdbServerClient {
109
111
  await writer.write(buffer);
110
112
  }
111
113
 
112
- public static async readOkay(
113
- stream: ExactReadable | AsyncExactReadable
114
+ static async readOkay(
115
+ stream: ExactReadable | AsyncExactReadable,
114
116
  ): Promise<void> {
115
117
  const response = decodeUtf8(await stream.readExactly(4));
116
118
  if (response === "OKAY") {
@@ -125,38 +127,49 @@ export class AdbServerClient {
125
127
  throw new Error(`Unexpected response: ${response}`);
126
128
  }
127
129
 
128
- public async connect(
130
+ async connect(
129
131
  request: string,
130
- options?: AdbServerConnectionOptions
131
- ): Promise<ReadableWritablePair<Uint8Array, Uint8Array>> {
132
+ options?: AdbServerConnectionOptions,
133
+ ): Promise<AdbServerConnection> {
132
134
  const connection = await this.connection.connect(options);
133
135
 
134
- const writer = connection.writable.getWriter();
135
- await AdbServerClient.writeString(writer, request);
136
+ try {
137
+ const writer = connection.writable.getWriter();
138
+ await AdbServerClient.writeString(writer, request);
139
+ writer.releaseLock();
140
+ } catch (e) {
141
+ await connection.readable.cancel();
142
+ await connection.close();
143
+ throw e;
144
+ }
136
145
 
137
146
  const readable = new BufferedReadableStream(connection.readable);
138
-
139
147
  try {
140
- // `raceSignal` throws if the signal is aborted,
148
+ // `raceSignal` throws when the signal is aborted,
141
149
  // so the `catch` block can close the connection.
142
150
  await raceSignal(
143
151
  () => AdbServerClient.readOkay(readable),
144
- options?.signal
152
+ options?.signal,
145
153
  );
146
154
 
147
- writer.releaseLock();
148
155
  return {
149
156
  readable: readable.release(),
150
157
  writable: connection.writable,
158
+ get closed() {
159
+ return connection.closed;
160
+ },
161
+ async close() {
162
+ await connection.close();
163
+ },
151
164
  };
152
165
  } catch (e) {
153
- writer.close().catch(NOOP);
154
- readable.cancel().catch(NOOP);
166
+ await readable.cancel().catch(NOOP);
167
+ await connection.close();
155
168
  throw e;
156
169
  }
157
170
  }
158
171
 
159
- public async getVersion(): Promise<number> {
172
+ async getVersion(): Promise<number> {
160
173
  const connection = await this.connect("host:version");
161
174
  const readable = new BufferedReadableStream(connection.readable);
162
175
  try {
@@ -169,22 +182,22 @@ export class AdbServerClient {
169
182
  }
170
183
  }
171
184
 
172
- public async validateVersion() {
185
+ async validateVersion() {
173
186
  const version = await this.getVersion();
174
187
  if (version !== AdbServerClient.VERSION) {
175
188
  throw new Error(
176
- `adb server version (${version}) doesn't match this client (${AdbServerClient.VERSION})`
189
+ `adb server version (${version}) doesn't match this client (${AdbServerClient.VERSION})`,
177
190
  );
178
191
  }
179
192
  }
180
193
 
181
- public async killServer(): Promise<void> {
194
+ async killServer(): Promise<void> {
182
195
  const connection = await this.connect("host:kill");
183
196
  connection.writable.close().catch(NOOP);
184
197
  connection.readable.cancel().catch(NOOP);
185
198
  }
186
199
 
187
- public async getServerFeatures(): Promise<AdbFeature[]> {
200
+ async getServerFeatures(): Promise<AdbFeature[]> {
188
201
  const connection = await this.connect("host:host-features");
189
202
  const readable = new BufferedReadableStream(connection.readable);
190
203
  try {
@@ -196,7 +209,7 @@ export class AdbServerClient {
196
209
  }
197
210
  }
198
211
 
199
- public async getDevices(): Promise<AdbServerDevice[]> {
212
+ async getDevices(): Promise<AdbServerDevice[]> {
200
213
  const connection = await this.connect("host:devices-l");
201
214
  const readable = new BufferedReadableStream(connection.readable);
202
215
  try {
@@ -253,10 +266,7 @@ export class AdbServerClient {
253
266
  }
254
267
  }
255
268
 
256
- public formatDeviceService(
257
- device: AdbServerDeviceSelector,
258
- command: string
259
- ) {
269
+ formatDeviceService(device: AdbServerDeviceSelector, command: string) {
260
270
  if (!device) {
261
271
  return `host:${command}`;
262
272
  }
@@ -282,8 +292,8 @@ export class AdbServerClient {
282
292
  * @param device The device selector
283
293
  * @returns The transport ID of the selected device, and the features supported by the device.
284
294
  */
285
- public async getDeviceFeatures(
286
- device: AdbServerDeviceSelector
295
+ async getDeviceFeatures(
296
+ device: AdbServerDeviceSelector,
287
297
  ): Promise<{ transportId: bigint; features: AdbFeature[] }> {
288
298
  // Usually the client sends a device command using `connectDevice`,
289
299
  // so the command got forwarded and handled by ADB daemon.
@@ -309,9 +319,9 @@ export class AdbServerClient {
309
319
  * @param service The service to forward
310
320
  * @returns An `AdbServerSocket` that can be used to communicate with the service
311
321
  */
312
- public async connectDevice(
322
+ async connectDevice(
313
323
  device: AdbServerDeviceSelector,
314
- service: string
324
+ service: string,
315
325
  ): Promise<AdbServerSocket> {
316
326
  await this.validateVersion();
317
327
 
@@ -333,8 +343,18 @@ export class AdbServerClient {
333
343
  }
334
344
 
335
345
  const connection = await this.connect(switchService);
346
+
347
+ try {
348
+ const writer = connection.writable.getWriter();
349
+ await AdbServerClient.writeString(writer, service);
350
+ writer.releaseLock();
351
+ } catch (e) {
352
+ await connection.readable.cancel();
353
+ await connection.close();
354
+ throw e;
355
+ }
356
+
336
357
  const readable = new BufferedReadableStream(connection.readable);
337
- const writer = connection.writable.getWriter();
338
358
  try {
339
359
  if (transportId === undefined) {
340
360
  const array = await readable.readExactly(8);
@@ -342,39 +362,30 @@ export class AdbServerClient {
342
362
  const dataView = new DataView(
343
363
  array.buffer,
344
364
  array.byteOffset,
345
- array.byteLength
365
+ array.byteLength,
346
366
  );
347
367
  transportId = BigIntFieldType.Uint64.getter(dataView, 0, true);
348
368
  }
349
369
 
350
- await AdbServerClient.writeString(writer, service);
351
370
  await AdbServerClient.readOkay(readable);
352
371
 
353
- writer.releaseLock();
354
-
355
- const duplex = new DuplexStreamFactory<
356
- Uint8Array,
357
- Consumable<Uint8Array>
358
- >();
359
- const wrapReadable = duplex.wrapReadable(readable.release());
360
- const wrapWritable = duplex.createWritable(
361
- new WrapWritableStream(connection.writable).bePipedThroughFrom(
362
- new UnwrapConsumableStream()
363
- )
364
- );
365
-
366
372
  return {
367
373
  transportId,
368
374
  service,
369
- readable: wrapReadable,
370
- writable: wrapWritable,
371
- close() {
372
- return duplex.close();
375
+ readable: readable.release(),
376
+ writable: new WrapWritableStream(
377
+ connection.writable,
378
+ ).bePipedThroughFrom(new UnwrapConsumableStream()),
379
+ get closed() {
380
+ return connection.closed;
381
+ },
382
+ async close() {
383
+ await connection.close();
373
384
  },
374
385
  };
375
386
  } catch (e) {
376
- writer.close().catch(NOOP);
377
- readable.cancel().catch(NOOP);
387
+ await readable.cancel().catch(NOOP);
388
+ await connection.close();
378
389
  throw e;
379
390
  }
380
391
  }
@@ -386,10 +397,10 @@ export class AdbServerClient {
386
397
  * @param options The options
387
398
  * @returns A promise that resolves when the condition is met.
388
399
  */
389
- public async waitFor(
400
+ async waitFor(
390
401
  device: AdbServerDeviceSelector,
391
402
  state: "device" | "disconnect",
392
- options?: AdbServerConnectionOptions
403
+ options?: AdbServerConnectionOptions,
393
404
  ): Promise<void> {
394
405
  let type: string;
395
406
  if (!device) {
@@ -410,7 +421,7 @@ export class AdbServerClient {
410
421
  // might not be available yet.
411
422
  const service = this.formatDeviceService(
412
423
  device,
413
- `wait-for-${type}-${state}`
424
+ `wait-for-${type}-${state}`,
414
425
  );
415
426
 
416
427
  // `connect` resolves when server writes `OKAY`,
@@ -418,33 +429,33 @@ export class AdbServerClient {
418
429
  await this.connect(service, options);
419
430
  }
420
431
 
421
- public async createTransport(
422
- device: AdbServerDeviceSelector
432
+ async createTransport(
433
+ device: AdbServerDeviceSelector,
423
434
  ): Promise<AdbServerTransport> {
424
435
  const { transportId, features } = await this.getDeviceFeatures(device);
425
436
 
426
437
  const devices = await this.getDevices();
427
438
  const info = devices.find(
428
- (device) => device.transportId === transportId
439
+ (device) => device.transportId === transportId,
429
440
  );
430
441
 
431
442
  const banner = new AdbBanner(
432
443
  info?.product,
433
444
  info?.model,
434
445
  info?.device,
435
- features
446
+ features,
436
447
  );
437
448
 
438
449
  return new AdbServerTransport(
439
450
  this,
440
451
  info?.serial ?? "",
441
452
  banner,
442
- transportId
453
+ transportId,
443
454
  );
444
455
  }
445
456
  }
446
457
 
447
- async function raceSignal<T>(
458
+ export async function raceSignal<T>(
448
459
  callback: () => Promise<T>,
449
460
  ...signals: (AbortSignal | undefined)[]
450
461
  ): Promise<T> {
@@ -14,23 +14,23 @@ import type { AdbServerClient } from "./client.js";
14
14
  export class AdbServerTransport implements AdbTransport {
15
15
  #client: AdbServerClient;
16
16
 
17
- public readonly serial: string;
17
+ readonly serial: string;
18
18
 
19
- public readonly transportId: bigint;
19
+ readonly transportId: bigint;
20
20
 
21
- public readonly maxPayloadSize: number = 1 * 1024 * 1024;
21
+ readonly maxPayloadSize: number = 1 * 1024 * 1024;
22
22
 
23
- public readonly banner: AdbBanner;
23
+ readonly banner: AdbBanner;
24
24
 
25
25
  #closed = new PromiseResolver<void>();
26
26
  #waitAbortController = new AbortController();
27
- public readonly disconnected: Promise<void>;
27
+ readonly disconnected: Promise<void>;
28
28
 
29
- public constructor(
29
+ constructor(
30
30
  client: AdbServerClient,
31
31
  serial: string,
32
32
  banner: AdbBanner,
33
- transportId: bigint
33
+ transportId: bigint,
34
34
  ) {
35
35
  this.#client = client;
36
36
  this.serial = serial;
@@ -46,27 +46,27 @@ export class AdbServerTransport implements AdbTransport {
46
46
  ]);
47
47
  }
48
48
 
49
- public async connect(service: string): Promise<AdbSocket> {
49
+ async connect(service: string): Promise<AdbSocket> {
50
50
  return await this.#client.connectDevice(
51
51
  {
52
52
  transportId: this.transportId,
53
53
  },
54
- service
54
+ service,
55
55
  );
56
56
  }
57
57
 
58
- public async addReverseTunnel(
58
+ async addReverseTunnel(
59
59
  handler: AdbIncomingSocketHandler,
60
- address?: string
60
+ address?: string,
61
61
  ): Promise<string> {
62
62
  return await this.#client.connection.addReverseTunnel(handler, address);
63
63
  }
64
64
 
65
- public async removeReverseTunnel(address: string): Promise<void> {
65
+ async removeReverseTunnel(address: string): Promise<void> {
66
66
  await this.#client.connection.removeReverseTunnel(address);
67
67
  }
68
68
 
69
- public async clearReverseTunnels(): Promise<void> {
69
+ async clearReverseTunnels(): Promise<void> {
70
70
  await this.#client.connection.clearReverseTunnels();
71
71
  }
72
72
 
@@ -5,11 +5,11 @@ export class AutoResetEvent implements Disposable {
5
5
  #set: boolean;
6
6
  readonly #queue: PromiseResolver<void>[] = [];
7
7
 
8
- public constructor(initialSet = false) {
8
+ constructor(initialSet = false) {
9
9
  this.#set = initialSet;
10
10
  }
11
11
 
12
- public wait(): Promise<void> {
12
+ wait(): Promise<void> {
13
13
  if (!this.#set) {
14
14
  this.#set = true;
15
15
 
@@ -23,7 +23,7 @@ export class AutoResetEvent implements Disposable {
23
23
  return resolver.promise;
24
24
  }
25
25
 
26
- public notifyOne() {
26
+ notifyOne() {
27
27
  if (this.#queue.length !== 0) {
28
28
  this.#queue.pop()!.resolve();
29
29
  } else {
@@ -31,7 +31,7 @@ export class AutoResetEvent implements Disposable {
31
31
  }
32
32
  }
33
33
 
34
- public dispose() {
34
+ dispose() {
35
35
  for (const item of this.#queue) {
36
36
  item.reject(new Error("The AutoResetEvent has been disposed"));
37
37
  }