@yume-chan/adb 0.0.16 → 0.0.18

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 (177) hide show
  1. package/CHANGELOG.json +18 -0
  2. package/CHANGELOG.md +13 -1
  3. package/LICENSE +1 -1
  4. package/esm/adb.d.ts +13 -8
  5. package/esm/adb.d.ts.map +1 -1
  6. package/esm/adb.js +63 -38
  7. package/esm/adb.js.map +1 -1
  8. package/esm/auth.d.ts +4 -4
  9. package/esm/auth.d.ts.map +1 -1
  10. package/esm/auth.js +6 -6
  11. package/esm/auth.js.map +1 -1
  12. package/esm/backend.d.ts +3 -3
  13. package/esm/backend.d.ts.map +1 -1
  14. package/esm/commands/base.d.ts +2 -2
  15. package/esm/commands/base.d.ts.map +1 -1
  16. package/esm/commands/base.js +1 -1
  17. package/esm/commands/base.js.map +1 -1
  18. package/esm/commands/framebuffer.d.ts +22 -6
  19. package/esm/commands/framebuffer.d.ts.map +1 -1
  20. package/esm/commands/framebuffer.js +33 -45
  21. package/esm/commands/framebuffer.js.map +1 -1
  22. package/esm/commands/install.d.ts +2 -2
  23. package/esm/commands/install.d.ts.map +1 -1
  24. package/esm/commands/install.js +12 -5
  25. package/esm/commands/install.js.map +1 -1
  26. package/esm/commands/power.d.ts.map +1 -1
  27. package/esm/commands/power.js +13 -9
  28. package/esm/commands/power.js.map +1 -1
  29. package/esm/commands/reverse.d.ts +5 -5
  30. package/esm/commands/reverse.d.ts.map +1 -1
  31. package/esm/commands/reverse.js +20 -18
  32. package/esm/commands/reverse.js.map +1 -1
  33. package/esm/commands/subprocess/command.d.ts +52 -0
  34. package/esm/commands/subprocess/command.d.ts.map +1 -0
  35. package/esm/commands/subprocess/command.js +81 -0
  36. package/esm/commands/subprocess/command.js.map +1 -0
  37. package/esm/commands/subprocess/index.d.ts +1 -53
  38. package/esm/commands/subprocess/index.d.ts.map +1 -1
  39. package/esm/commands/subprocess/index.js +1 -85
  40. package/esm/commands/subprocess/index.js.map +1 -1
  41. package/esm/commands/subprocess/protocols/none.d.ts +5 -5
  42. package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
  43. package/esm/commands/subprocess/protocols/none.js +5 -3
  44. package/esm/commands/subprocess/protocols/none.js.map +1 -1
  45. package/esm/commands/subprocess/protocols/shell.d.ts +4 -4
  46. package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
  47. package/esm/commands/subprocess/protocols/shell.js +28 -20
  48. package/esm/commands/subprocess/protocols/shell.js.map +1 -1
  49. package/esm/commands/subprocess/protocols/types.d.ts +7 -7
  50. package/esm/commands/subprocess/protocols/types.d.ts.map +1 -1
  51. package/esm/commands/sync/index.d.ts +1 -1
  52. package/esm/commands/sync/index.d.ts.map +1 -1
  53. package/esm/commands/sync/index.js +1 -1
  54. package/esm/commands/sync/index.js.map +1 -1
  55. package/esm/commands/sync/list.d.ts +7 -7
  56. package/esm/commands/sync/list.d.ts.map +1 -1
  57. package/esm/commands/sync/list.js +34 -50
  58. package/esm/commands/sync/list.js.map +1 -1
  59. package/esm/commands/sync/pull.d.ts +5 -4
  60. package/esm/commands/sync/pull.d.ts.map +1 -1
  61. package/esm/commands/sync/pull.js +21 -22
  62. package/esm/commands/sync/pull.js.map +1 -1
  63. package/esm/commands/sync/push.d.ts +4 -4
  64. package/esm/commands/sync/push.d.ts.map +1 -1
  65. package/esm/commands/sync/push.js +7 -11
  66. package/esm/commands/sync/push.js.map +1 -1
  67. package/esm/commands/sync/request.d.ts +4 -4
  68. package/esm/commands/sync/request.d.ts.map +1 -1
  69. package/esm/commands/sync/request.js +6 -6
  70. package/esm/commands/sync/request.js.map +1 -1
  71. package/esm/commands/sync/response.d.ts +5 -10
  72. package/esm/commands/sync/response.d.ts.map +1 -1
  73. package/esm/commands/sync/response.js +35 -26
  74. package/esm/commands/sync/response.js.map +1 -1
  75. package/esm/commands/sync/stat.d.ts +7 -7
  76. package/esm/commands/sync/stat.d.ts.map +1 -1
  77. package/esm/commands/sync/stat.js +48 -55
  78. package/esm/commands/sync/stat.js.map +1 -1
  79. package/esm/commands/sync/sync.d.ts +7 -7
  80. package/esm/commands/sync/sync.d.ts.map +1 -1
  81. package/esm/commands/sync/sync.js +25 -22
  82. package/esm/commands/sync/sync.js.map +1 -1
  83. package/esm/crypto.d.ts.map +1 -1
  84. package/esm/crypto.js +26 -16
  85. package/esm/crypto.js.map +1 -1
  86. package/esm/index.d.ts +9 -10
  87. package/esm/index.d.ts.map +1 -1
  88. package/esm/index.js +9 -10
  89. package/esm/index.js.map +1 -1
  90. package/esm/packet.d.ts +8 -8
  91. package/esm/packet.d.ts.map +1 -1
  92. package/esm/packet.js +14 -12
  93. package/esm/packet.js.map +1 -1
  94. package/esm/socket/dispatcher.d.ts +11 -6
  95. package/esm/socket/dispatcher.d.ts.map +1 -1
  96. package/esm/socket/dispatcher.js +28 -20
  97. package/esm/socket/dispatcher.js.map +1 -1
  98. package/esm/socket/index.d.ts +1 -1
  99. package/esm/socket/index.d.ts.map +1 -1
  100. package/esm/socket/index.js +1 -1
  101. package/esm/socket/index.js.map +1 -1
  102. package/esm/socket/socket.d.ts +5 -5
  103. package/esm/socket/socket.d.ts.map +1 -1
  104. package/esm/socket/socket.js +37 -17
  105. package/esm/socket/socket.js.map +1 -1
  106. package/esm/utils/auto-reset-event.d.ts +1 -1
  107. package/esm/utils/auto-reset-event.d.ts.map +1 -1
  108. package/esm/utils/auto-reset-event.js +2 -2
  109. package/esm/utils/base64.d.ts +23 -0
  110. package/esm/utils/base64.d.ts.map +1 -1
  111. package/esm/utils/base64.js +36 -29
  112. package/esm/utils/base64.js.map +1 -1
  113. package/package.json +19 -17
  114. package/src/adb.ts +140 -86
  115. package/src/auth.ts +27 -18
  116. package/src/backend.ts +7 -4
  117. package/src/commands/base.ts +3 -2
  118. package/src/commands/framebuffer.ts +48 -47
  119. package/src/commands/install.ts +20 -10
  120. package/src/commands/power.ts +14 -10
  121. package/src/commands/reverse.ts +53 -33
  122. package/src/commands/subprocess/command.ts +134 -0
  123. package/src/commands/subprocess/index.ts +1 -137
  124. package/src/commands/subprocess/protocols/none.ts +10 -6
  125. package/src/commands/subprocess/protocols/shell.ts +72 -39
  126. package/src/commands/subprocess/protocols/types.ts +12 -8
  127. package/src/commands/sync/index.ts +1 -1
  128. package/src/commands/sync/list.ts +63 -68
  129. package/src/commands/sync/pull.ts +53 -37
  130. package/src/commands/sync/push.ts +26 -15
  131. package/src/commands/sync/request.ts +20 -21
  132. package/src/commands/sync/response.ts +63 -53
  133. package/src/commands/sync/stat.ts +91 -90
  134. package/src/commands/sync/sync.ts +56 -34
  135. package/src/crypto.ts +61 -33
  136. package/src/features.ts +1 -1
  137. package/src/index.ts +9 -11
  138. package/src/packet.ts +40 -30
  139. package/src/socket/dispatcher.ts +119 -77
  140. package/src/socket/index.ts +1 -1
  141. package/src/socket/socket.ts +68 -26
  142. package/src/utils/auto-reset-event.ts +3 -3
  143. package/src/utils/base64.ts +75 -42
  144. package/tsconfig.build.json +1 -1
  145. package/tsconfig.build.tsbuildinfo +1 -1
  146. package/esm/stream/buffered.d.ts +0 -39
  147. package/esm/stream/buffered.d.ts.map +0 -1
  148. package/esm/stream/buffered.js +0 -143
  149. package/esm/stream/buffered.js.map +0 -1
  150. package/esm/stream/detect.d.ts +0 -2
  151. package/esm/stream/detect.d.ts.map +0 -1
  152. package/esm/stream/detect.js +0 -6
  153. package/esm/stream/detect.js.map +0 -1
  154. package/esm/stream/detect.native.d.ts +0 -240
  155. package/esm/stream/detect.native.d.ts.map +0 -1
  156. package/esm/stream/detect.native.js +0 -60
  157. package/esm/stream/detect.native.js.map +0 -1
  158. package/esm/stream/detect.polyfill.d.ts +0 -18
  159. package/esm/stream/detect.polyfill.d.ts.map +0 -1
  160. package/esm/stream/detect.polyfill.js +0 -4
  161. package/esm/stream/detect.polyfill.js.map +0 -1
  162. package/esm/stream/index.d.ts +0 -4
  163. package/esm/stream/index.d.ts.map +0 -1
  164. package/esm/stream/index.js +0 -4
  165. package/esm/stream/index.js.map +0 -1
  166. package/esm/stream/transform.d.ts +0 -124
  167. package/esm/stream/transform.d.ts.map +0 -1
  168. package/esm/stream/transform.js +0 -379
  169. package/esm/stream/transform.js.map +0 -1
  170. package/src/stream/buffered.ts +0 -168
  171. package/src/stream/detect.native.ts +0 -362
  172. package/src/stream/detect.polyfill.ts +0 -22
  173. package/src/stream/detect.ts +0 -7
  174. package/src/stream/index.ts +0 -3
  175. package/src/stream/transform.ts +0 -474
  176. package/tsconfig.test.json +0 -9
  177. package/web-streams-polyfill-4.0.0-beta.2.tgz +0 -0
@@ -6,24 +6,24 @@
6
6
  import { AdbCommandBase } from "./base.js";
7
7
 
8
8
  export class AdbPower extends AdbCommandBase {
9
- public reboot(name: string = '') {
9
+ public reboot(name = "") {
10
10
  return this.adb.createSocketAndWait(`reboot:${name}`);
11
11
  }
12
12
 
13
13
  public bootloader() {
14
- return this.reboot('bootloader');
14
+ return this.reboot("bootloader");
15
15
  }
16
16
 
17
17
  public fastboot() {
18
- return this.reboot('fastboot');
18
+ return this.reboot("fastboot");
19
19
  }
20
20
 
21
21
  public recovery() {
22
- return this.reboot('recovery');
22
+ return this.reboot("recovery");
23
23
  }
24
24
 
25
25
  public sideload() {
26
- return this.reboot('sideload');
26
+ return this.reboot("sideload");
27
27
  }
28
28
 
29
29
  /**
@@ -32,15 +32,19 @@ export class AdbPower extends AdbCommandBase {
32
32
  * Only works on some Qualcomm devices.
33
33
  */
34
34
  public qualcommEdlMode() {
35
- return this.reboot('edl');
35
+ return this.reboot("edl");
36
36
  }
37
37
 
38
38
  public powerOff() {
39
- return this.adb.subprocess.spawnAndWaitLegacy(['reboot', '-p']);
39
+ return this.adb.subprocess.spawnAndWaitLegacy(["reboot", "-p"]);
40
40
  }
41
41
 
42
- public powerButton(longPress: boolean = false) {
43
- return this.adb.subprocess.spawnAndWaitLegacy(['input', 'keyevent', longPress ? '--longpress POWER' : 'POWER']);
42
+ public powerButton(longPress = false) {
43
+ return this.adb.subprocess.spawnAndWaitLegacy([
44
+ "input",
45
+ "keyevent",
46
+ longPress ? "--longpress POWER" : "POWER",
47
+ ]);
44
48
  }
45
49
 
46
50
  /**
@@ -49,6 +53,6 @@ export class AdbPower extends AdbCommandBase {
49
53
  * Only works on Samsung devices.
50
54
  */
51
55
  public samsungOdin() {
52
- return this.reboot('download');
56
+ return this.reboot("download");
53
57
  }
54
58
  }
@@ -1,10 +1,17 @@
1
1
  // cspell: ignore killforward
2
2
 
3
- import { AutoDisposable } from '@yume-chan/event';
4
- import Struct from '@yume-chan/struct';
5
- import type { Adb } from "../adb.js";
6
- import type { AdbIncomingSocketHandler, AdbSocket } from '../socket/index.js';
7
- import { AdbBufferedStream, BufferedStreamEndedError } from '../stream/index.js';
3
+ import { AutoDisposable } from "@yume-chan/event";
4
+ import {
5
+ BufferedReadableStream,
6
+ BufferedReadableStreamEndedError,
7
+ } from "@yume-chan/stream-extra";
8
+ import Struct from "@yume-chan/struct";
9
+
10
+ import { type Adb } from "../adb.js";
11
+ import {
12
+ type AdbIncomingSocketHandler,
13
+ type AdbSocket,
14
+ } from "../socket/index.js";
8
15
  import { decodeUtf8 } from "../utils/index.js";
9
16
 
10
17
  export interface AdbForwardListener {
@@ -15,20 +22,21 @@ export interface AdbForwardListener {
15
22
  remoteName: string;
16
23
  }
17
24
 
18
- const AdbReverseStringResponse =
19
- new Struct()
20
- .string('length', { length: 4 })
21
- .string('content', { lengthField: 'length', lengthFieldRadix: 16 });
25
+ const AdbReverseStringResponse = new Struct()
26
+ .string("length", { length: 4 })
27
+ .string("content", { lengthField: "length", lengthFieldRadix: 16 });
22
28
 
23
- const AdbReverseErrorResponse =
24
- new Struct()
25
- .fields(AdbReverseStringResponse)
26
- .postDeserialize((value) => {
27
- throw new Error(value.content);
28
- });
29
+ const AdbReverseErrorResponse = new Struct()
30
+ .fields(AdbReverseStringResponse)
31
+ .postDeserialize((value) => {
32
+ throw new Error(value.content);
33
+ });
29
34
 
30
35
  export class AdbReverseCommand extends AutoDisposable {
31
- protected localAddressToHandler = new Map<string, AdbIncomingSocketHandler>();
36
+ protected localAddressToHandler = new Map<
37
+ string,
38
+ AdbIncomingSocketHandler
39
+ >();
32
40
 
33
41
  protected deviceAddressToLocalAddress = new Map<string, string>();
34
42
 
@@ -40,24 +48,26 @@ export class AdbReverseCommand extends AutoDisposable {
40
48
  super();
41
49
 
42
50
  this.adb = adb;
43
- this.addDisposable(this.adb.addIncomingSocketHandler(this.handleIncomingSocket));
51
+ this.addDisposable(
52
+ this.adb.onIncomingSocket(this.handleIncomingSocket)
53
+ );
44
54
  }
45
55
 
46
56
  protected handleIncomingSocket = async (socket: AdbSocket) => {
47
57
  let address = socket.serviceString;
48
58
  // ADB daemon appends `\0` to the service string
49
- address = address.replace(/\0/g, '');
59
+ address = address.replace(/\0/g, "");
50
60
  return !!(await this.localAddressToHandler.get(address)?.(socket));
51
61
  };
52
62
 
53
63
  private async createBufferedStream(service: string) {
54
64
  const socket = await this.adb.createSocket(service);
55
- return new AdbBufferedStream(socket);
65
+ return new BufferedReadableStream(socket.readable);
56
66
  }
57
67
 
58
68
  private async sendRequest(service: string) {
59
69
  const stream = await this.createBufferedStream(service);
60
- const success = decodeUtf8(await stream.read(4)) === 'OKAY';
70
+ const success = decodeUtf8(await stream.read(4)) === "OKAY";
61
71
  if (!success) {
62
72
  await AdbReverseErrorResponse.deserialize(stream);
63
73
  }
@@ -65,11 +75,15 @@ export class AdbReverseCommand extends AutoDisposable {
65
75
  }
66
76
 
67
77
  public async list(): Promise<AdbForwardListener[]> {
68
- const stream = await this.createBufferedStream('reverse:list-forward');
78
+ const stream = await this.createBufferedStream("reverse:list-forward");
69
79
 
70
80
  const response = await AdbReverseStringResponse.deserialize(stream);
71
- return response.content!.split('\n').map(line => {
72
- const [deviceSerial, localName, remoteName] = line.split(' ') as [string, string, string];
81
+ return response.content!.split("\n").map((line) => {
82
+ const [deviceSerial, localName, remoteName] = line.split(" ") as [
83
+ string,
84
+ string,
85
+ string
86
+ ];
73
87
  return { deviceSerial, localName, remoteName };
74
88
  });
75
89
 
@@ -79,34 +93,38 @@ export class AdbReverseCommand extends AutoDisposable {
79
93
  /**
80
94
  * @param deviceAddress The address adbd on device is listening on. Can be `tcp:0` to let adbd choose an available TCP port by itself.
81
95
  * @param localAddress Native ADB client will open a connection to this address when reverse connection received. In WebADB, it's only used to uniquely identify a reverse tunnel registry, `handler` will be called to handle the connection.
82
- * @param handler A callback to handle incoming connections
83
- * @returns If `deviceAddress` is `tcp:0`, return `tcp:{ACTUAL_LISTENING_PORT}`; otherwise, return `deviceAddress`.
96
+ * @param handler A callback to handle incoming connections. It must return `true` if it accepts the connection.
97
+ * @returns `tcp:{ACTUAL_LISTENING_PORT}`, If `deviceAddress` is `tcp:0`; otherwise, `deviceAddress`.
84
98
  */
85
99
  public async add(
86
100
  deviceAddress: string,
87
101
  localAddress: string,
88
- handler: AdbIncomingSocketHandler,
102
+ handler: AdbIncomingSocketHandler
89
103
  ): Promise<string> {
90
- const stream = await this.sendRequest(`reverse:forward:${deviceAddress};${localAddress}`);
104
+ // TODO(reverse): handle ADB bug
105
+ // https://issuetracker.google.com/issues/37066218
106
+ const stream = await this.sendRequest(
107
+ `reverse:forward:${deviceAddress};${localAddress}`
108
+ );
91
109
 
92
110
  // `tcp:0` tells the device to pick an available port.
93
- // Begin with Android 8, device will respond with the selected port for all `tcp:` requests.
94
- if (deviceAddress.startsWith('tcp:')) {
111
+ // On Android >=8, device will respond with the selected port for all `tcp:` requests.
112
+ if (deviceAddress.startsWith("tcp:")) {
95
113
  let length: number | undefined;
96
114
  try {
97
115
  length = Number.parseInt(decodeUtf8(await stream.read(4)), 16);
98
116
  } catch (e) {
99
- if (!(e instanceof BufferedStreamEndedError)) {
117
+ if (!(e instanceof BufferedReadableStreamEndedError)) {
100
118
  throw e;
101
119
  }
102
120
 
103
- // Device before Android 8 doesn't have this response.
121
+ // Android <8 doesn't have this response.
104
122
  // (the stream is closed now)
105
123
  // Can be safely ignored.
106
124
  }
107
125
 
108
126
  if (length !== undefined) {
109
- const port = decodeUtf8(await stream.read(length!));
127
+ const port = decodeUtf8(await stream.read(length));
110
128
  deviceAddress = `tcp:${Number.parseInt(port, 10)}`;
111
129
  }
112
130
  }
@@ -122,7 +140,9 @@ export class AdbReverseCommand extends AutoDisposable {
122
140
  await this.sendRequest(`reverse:killforward:${deviceAddress}`);
123
141
 
124
142
  if (this.deviceAddressToLocalAddress.has(deviceAddress)) {
125
- this.localAddressToHandler.delete(this.deviceAddressToLocalAddress.get(deviceAddress)!);
143
+ this.localAddressToHandler.delete(
144
+ this.deviceAddressToLocalAddress.get(deviceAddress)!
145
+ );
126
146
  this.deviceAddressToLocalAddress.delete(deviceAddress);
127
147
  }
128
148
 
@@ -0,0 +1,134 @@
1
+ import { DecodeUtf8Stream, GatherStringStream } from "@yume-chan/stream-extra";
2
+
3
+ import { AdbCommandBase } from "../base.js";
4
+
5
+ import {
6
+ AdbSubprocessNoneProtocol,
7
+ AdbSubprocessShellProtocol,
8
+ type AdbSubprocessProtocol,
9
+ type AdbSubprocessProtocolConstructor,
10
+ } from "./protocols/index.js";
11
+
12
+ export interface AdbSubprocessOptions {
13
+ /**
14
+ * A list of `AdbSubprocessProtocolConstructor`s to be used.
15
+ *
16
+ * Different `AdbSubprocessProtocol` has different capabilities, thus requires specific adaptations.
17
+ * Check their documentations for details.
18
+ *
19
+ * The first protocol whose `isSupported` returns `true` will be used.
20
+ * If no `AdbSubprocessProtocol` is supported, an error will be thrown.
21
+ *
22
+ * @default [AdbSubprocessShellProtocol, AdbSubprocessNoneProtocol]
23
+ */
24
+ protocols: AdbSubprocessProtocolConstructor[];
25
+ }
26
+
27
+ const DEFAULT_OPTIONS: AdbSubprocessOptions = {
28
+ protocols: [AdbSubprocessShellProtocol, AdbSubprocessNoneProtocol],
29
+ };
30
+
31
+ export interface AdbSubprocessWaitResult {
32
+ stdout: string;
33
+ stderr: string;
34
+ exitCode: number;
35
+ }
36
+
37
+ export class AdbSubprocess extends AdbCommandBase {
38
+ private async createProtocol(
39
+ mode: "pty" | "raw",
40
+ command?: string | string[],
41
+ options?: Partial<AdbSubprocessOptions>
42
+ ): Promise<AdbSubprocessProtocol> {
43
+ const { protocols } = { ...DEFAULT_OPTIONS, ...options };
44
+
45
+ let Constructor: AdbSubprocessProtocolConstructor | undefined;
46
+ for (const item of protocols) {
47
+ // It's async so can't use `Array#find`
48
+ if (await item.isSupported(this.adb)) {
49
+ Constructor = item;
50
+ break;
51
+ }
52
+ }
53
+
54
+ if (!Constructor) {
55
+ throw new Error("No specified protocol is supported by the device");
56
+ }
57
+
58
+ if (Array.isArray(command)) {
59
+ command = command.join(" ");
60
+ } else if (command === undefined) {
61
+ // spawn the default shell
62
+ command = "";
63
+ }
64
+ return await Constructor[mode](this.adb, command);
65
+ }
66
+
67
+ /**
68
+ * Spawns an executable in PTY (interactive) mode.
69
+ * @param command The command to run. If omitted, the default shell will be spawned.
70
+ * @param options The options for creating the `AdbSubprocessProtocol`
71
+ * @returns A new `AdbSubprocessProtocol` instance connecting to the spawned process.
72
+ */
73
+ public shell(
74
+ command?: string | string[],
75
+ options?: Partial<AdbSubprocessOptions>
76
+ ): Promise<AdbSubprocessProtocol> {
77
+ return this.createProtocol("pty", command, options);
78
+ }
79
+
80
+ /**
81
+ * Spawns an executable and pipe the output.
82
+ * @param command The command to run, or an array of strings containing both command and args.
83
+ * @param options The options for creating the `AdbSubprocessProtocol`
84
+ * @returns A new `AdbSubprocessProtocol` instance connecting to the spawned process.
85
+ */
86
+ public spawn(
87
+ command: string | string[],
88
+ options?: Partial<AdbSubprocessOptions>
89
+ ): Promise<AdbSubprocessProtocol> {
90
+ return this.createProtocol("raw", command, options);
91
+ }
92
+
93
+ /**
94
+ * Spawns a new process, waits until it exits, and returns the entire output.
95
+ * @param command The command to run
96
+ * @param options The options for creating the `AdbSubprocessProtocol`
97
+ * @returns The entire output of the command
98
+ */
99
+ public async spawnAndWait(
100
+ command: string | string[],
101
+ options?: Partial<AdbSubprocessOptions>
102
+ ): Promise<AdbSubprocessWaitResult> {
103
+ const shell = await this.spawn(command, options);
104
+
105
+ const stdout = new GatherStringStream();
106
+ const stderr = new GatherStringStream();
107
+
108
+ const [, , exitCode] = await Promise.all([
109
+ shell.stdout.pipeThrough(new DecodeUtf8Stream()).pipeTo(stdout),
110
+ shell.stderr.pipeThrough(new DecodeUtf8Stream()).pipeTo(stderr),
111
+ shell.exit,
112
+ ]);
113
+
114
+ return {
115
+ stdout: stdout.result,
116
+ stderr: stderr.result,
117
+ exitCode,
118
+ };
119
+ }
120
+
121
+ /**
122
+ * Spawns a new process, waits until it exits, and returns the entire output.
123
+ * @param command The command to run
124
+ * @returns The entire output of the command
125
+ */
126
+ public async spawnAndWaitLegacy(
127
+ command: string | string[]
128
+ ): Promise<string> {
129
+ const { stdout } = await this.spawnAndWait(command, {
130
+ protocols: [AdbSubprocessNoneProtocol],
131
+ });
132
+ return stdout;
133
+ }
134
+ }
@@ -1,139 +1,3 @@
1
- import type { Adb } from '../../adb.js';
2
- import { DecodeUtf8Stream, GatherStringStream } from "../../stream/index.js";
3
- import { AdbSubprocessNoneProtocol, AdbSubprocessShellProtocol, type AdbSubprocessProtocol, type AdbSubprocessProtocolConstructor } from './protocols/index.js';
4
-
1
+ export * from './command.js';
5
2
  export * from './protocols/index.js';
6
3
  export * from './utils.js';
7
-
8
- export interface AdbSubprocessOptions {
9
- /**
10
- * A list of `AdbSubprocessProtocolConstructor`s to be used.
11
- *
12
- * Different `AdbSubprocessProtocol` has different capabilities, thus requires specific adaptations.
13
- * Check their documentations for details.
14
- *
15
- * The first protocol whose `isSupported` returns `true` will be used.
16
- * If no `AdbSubprocessProtocol` is supported, an error will be thrown.
17
- *
18
- * @default [AdbSubprocessShellProtocol, AdbSubprocessNoneProtocol]
19
- */
20
- protocols: AdbSubprocessProtocolConstructor[];
21
- }
22
-
23
- const DEFAULT_OPTIONS: AdbSubprocessOptions = {
24
- protocols: [AdbSubprocessShellProtocol, AdbSubprocessNoneProtocol],
25
- };
26
-
27
- export interface AdbSubprocessWaitResult {
28
- stdout: string;
29
- stderr: string;
30
- exitCode: number;
31
- }
32
-
33
- export class AdbSubprocess {
34
- public readonly adb: Adb;
35
-
36
- public constructor(adb: Adb) {
37
- this.adb = adb;
38
- }
39
-
40
- private async createProtocol(
41
- mode: 'pty' | 'raw',
42
- command?: string | string[],
43
- options?: Partial<AdbSubprocessOptions>
44
- ): Promise<AdbSubprocessProtocol> {
45
- const { protocols } = { ...DEFAULT_OPTIONS, ...options };
46
-
47
- let Constructor: AdbSubprocessProtocolConstructor | undefined;
48
- for (const item of protocols) {
49
- // It's async so can't use `Array#find`
50
- if (await item.isSupported(this.adb)) {
51
- Constructor = item;
52
- break;
53
- }
54
- }
55
-
56
- if (!Constructor) {
57
- throw new Error('No specified protocol is supported by the device');
58
- }
59
-
60
- if (Array.isArray(command)) {
61
- command = command.join(' ');
62
- } else if (command === undefined) {
63
- // spawn the default shell
64
- command = '';
65
- }
66
- return await Constructor[mode](this.adb, command);
67
- }
68
-
69
- /**
70
- * Spawns an executable in PTY (interactive) mode.
71
- * @param command The command to run. If omitted, the default shell will be spawned.
72
- * @param options The options for creating the `AdbSubprocessProtocol`
73
- * @returns A new `AdbSubprocessProtocol` instance connecting to the spawned process.
74
- */
75
- public shell(
76
- command?: string | string[],
77
- options?: Partial<AdbSubprocessOptions>
78
- ): Promise<AdbSubprocessProtocol> {
79
- return this.createProtocol('pty', command, options);
80
- }
81
-
82
- /**
83
- * Spawns an executable and pipe the output.
84
- * @param command The command to run, or an array of strings containing both command and args.
85
- * @param options The options for creating the `AdbSubprocessProtocol`
86
- * @returns A new `AdbSubprocessProtocol` instance connecting to the spawned process.
87
- */
88
- public spawn(
89
- command: string | string[],
90
- options?: Partial<AdbSubprocessOptions>
91
- ): Promise<AdbSubprocessProtocol> {
92
- return this.createProtocol('raw', command, options);
93
- }
94
-
95
- /**
96
- * Spawns a new process, waits until it exits, and returns the entire output.
97
- * @param command The command to run
98
- * @param options The options for creating the `AdbSubprocessProtocol`
99
- * @returns The entire output of the command
100
- */
101
- public async spawnAndWait(
102
- command: string | string[],
103
- options?: Partial<AdbSubprocessOptions>
104
- ): Promise<AdbSubprocessWaitResult> {
105
- const shell = await this.spawn(command, options);
106
-
107
- const stdout = new GatherStringStream();
108
- const stderr = new GatherStringStream();
109
-
110
- const [, , exitCode] = await Promise.all([
111
- shell.stdout
112
- .pipeThrough(new DecodeUtf8Stream())
113
- .pipeTo(stdout),
114
- shell.stderr
115
- .pipeThrough(new DecodeUtf8Stream())
116
- .pipeTo(stderr),
117
- shell.exit
118
- ]);
119
-
120
- return {
121
- stdout: stdout.result,
122
- stderr: stderr.result,
123
- exitCode,
124
- };
125
- }
126
-
127
- /**
128
- * Spawns a new process, waits until it exits, and returns the entire output.
129
- * @param command The command to run
130
- * @returns The entire output of the command
131
- */
132
- public async spawnAndWaitLegacy(command: string | string[]): Promise<string> {
133
- const { stdout } = await this.spawnAndWait(
134
- command,
135
- { protocols: [AdbSubprocessNoneProtocol] }
136
- );
137
- return stdout;
138
- }
139
- }
@@ -1,7 +1,9 @@
1
- import type { Adb } from "../../../adb.js";
2
- import type { AdbSocket } from "../../../socket/index.js";
3
- import { DuplexStreamFactory, ReadableStream } from "../../../stream/index.js";
4
- import type { AdbSubprocessProtocol } from "./types.js";
1
+ import { DuplexStreamFactory, ReadableStream } from '@yume-chan/stream-extra';
2
+
3
+ import { type Adb } from "../../../adb.js";
4
+ import { type AdbSocket } from "../../../socket/index.js";
5
+
6
+ import { type AdbSubprocessProtocol } from "./types.js";
5
7
 
6
8
  /**
7
9
  * The legacy shell
@@ -20,7 +22,7 @@ export class AdbSubprocessNoneProtocol implements AdbSubprocessProtocol {
20
22
 
21
23
  public static async raw(adb: Adb, command: string) {
22
24
  // `shell,raw:${command}` also triggers raw mode,
23
- // But is not supported before Android 7.
25
+ // But is not supported on Android version <7.
24
26
  return new AdbSubprocessNoneProtocol(await adb.createSocket(`exec:${command}`));
25
27
  }
26
28
 
@@ -49,6 +51,8 @@ export class AdbSubprocessNoneProtocol implements AdbSubprocessProtocol {
49
51
  public constructor(socket: AdbSocket) {
50
52
  this.socket = socket;
51
53
 
54
+ // Link `stdout`, `stderr` and `stdin` together,
55
+ // so closing any of them will close the others.
52
56
  this.duplex = new DuplexStreamFactory<Uint8Array, Uint8Array>({
53
57
  close: async () => {
54
58
  await this.socket.close();
@@ -61,7 +65,7 @@ export class AdbSubprocessNoneProtocol implements AdbSubprocessProtocol {
61
65
  }
62
66
 
63
67
  public resize() {
64
- // Not supported
68
+ // Not supported, but don't throw.
65
69
  }
66
70
 
67
71
  public kill() {