@yume-chan/adb 0.0.23 → 0.0.24

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 (135) hide show
  1. package/CHANGELOG.json +33 -0
  2. package/CHANGELOG.md +15 -1
  3. package/LICENSE +1 -1
  4. package/esm/adb.d.ts +2 -2
  5. package/esm/adb.d.ts.map +1 -1
  6. package/esm/adb.js +2 -2
  7. package/esm/adb.js.map +1 -1
  8. package/esm/commands/power.d.ts.map +1 -1
  9. package/esm/commands/power.js +6 -5
  10. package/esm/commands/power.js.map +1 -1
  11. package/esm/commands/reverse.d.ts.map +1 -1
  12. package/esm/commands/reverse.js +22 -10
  13. package/esm/commands/reverse.js.map +1 -1
  14. package/esm/commands/subprocess/command.js +3 -3
  15. package/esm/commands/subprocess/command.js.map +1 -1
  16. package/esm/commands/subprocess/protocols/none.d.ts +2 -2
  17. package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
  18. package/esm/commands/subprocess/protocols/shell.d.ts +3 -3
  19. package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
  20. package/esm/commands/subprocess/protocols/shell.js +5 -6
  21. package/esm/commands/subprocess/protocols/shell.js.map +1 -1
  22. package/esm/commands/subprocess/protocols/types.d.ts +2 -2
  23. package/esm/commands/subprocess/protocols/types.d.ts.map +1 -1
  24. package/esm/commands/sync/list.d.ts +2 -5
  25. package/esm/commands/sync/list.d.ts.map +1 -1
  26. package/esm/commands/sync/list.js +2 -4
  27. package/esm/commands/sync/list.js.map +1 -1
  28. package/esm/commands/sync/pull.d.ts +1 -4
  29. package/esm/commands/sync/pull.d.ts.map +1 -1
  30. package/esm/commands/sync/pull.js +2 -2
  31. package/esm/commands/sync/pull.js.map +1 -1
  32. package/esm/commands/sync/push.d.ts +4 -7
  33. package/esm/commands/sync/push.d.ts.map +1 -1
  34. package/esm/commands/sync/push.js +3 -6
  35. package/esm/commands/sync/push.js.map +1 -1
  36. package/esm/commands/sync/request.d.ts +13 -18
  37. package/esm/commands/sync/request.d.ts.map +1 -1
  38. package/esm/commands/sync/request.js +23 -28
  39. package/esm/commands/sync/request.js.map +1 -1
  40. package/esm/commands/sync/response.d.ts +18 -12
  41. package/esm/commands/sync/response.d.ts.map +1 -1
  42. package/esm/commands/sync/response.js +39 -17
  43. package/esm/commands/sync/response.js.map +1 -1
  44. package/esm/commands/sync/socket.d.ts +2 -2
  45. package/esm/commands/sync/socket.d.ts.map +1 -1
  46. package/esm/commands/sync/socket.js +10 -5
  47. package/esm/commands/sync/socket.js.map +1 -1
  48. package/esm/commands/sync/stat.d.ts +0 -3
  49. package/esm/commands/sync/stat.d.ts.map +1 -1
  50. package/esm/commands/sync/stat.js +0 -2
  51. package/esm/commands/sync/stat.js.map +1 -1
  52. package/esm/commands/sync/sync.d.ts +4 -3
  53. package/esm/commands/sync/sync.d.ts.map +1 -1
  54. package/esm/commands/sync/sync.js +4 -1
  55. package/esm/commands/sync/sync.js.map +1 -1
  56. package/esm/commands/tcpip.js +1 -1
  57. package/esm/commands/tcpip.js.map +1 -1
  58. package/esm/daemon/auth.js +1 -1
  59. package/esm/daemon/auth.js.map +1 -1
  60. package/esm/daemon/crypto.d.ts +3 -2
  61. package/esm/daemon/crypto.d.ts.map +1 -1
  62. package/esm/daemon/crypto.js +56 -49
  63. package/esm/daemon/crypto.js.map +1 -1
  64. package/esm/daemon/dispatcher.d.ts +35 -10
  65. package/esm/daemon/dispatcher.d.ts.map +1 -1
  66. package/esm/daemon/dispatcher.js +44 -31
  67. package/esm/daemon/dispatcher.js.map +1 -1
  68. package/esm/daemon/packet.d.ts +2 -2
  69. package/esm/daemon/packet.d.ts.map +1 -1
  70. package/esm/daemon/packet.js +13 -12
  71. package/esm/daemon/packet.js.map +1 -1
  72. package/esm/daemon/socket.d.ts +10 -5
  73. package/esm/daemon/socket.d.ts.map +1 -1
  74. package/esm/daemon/socket.js +17 -15
  75. package/esm/daemon/socket.js.map +1 -1
  76. package/esm/daemon/transport.d.ts +55 -15
  77. package/esm/daemon/transport.d.ts.map +1 -1
  78. package/esm/daemon/transport.js +9 -10
  79. package/esm/daemon/transport.js.map +1 -1
  80. package/esm/server/client.d.ts +128 -50
  81. package/esm/server/client.d.ts.map +1 -1
  82. package/esm/server/client.js +325 -124
  83. package/esm/server/client.js.map +1 -1
  84. package/esm/server/transport.js +4 -4
  85. package/esm/server/transport.js.map +1 -1
  86. package/esm/utils/base64.d.ts.map +1 -1
  87. package/esm/utils/base64.js +33 -22
  88. package/esm/utils/base64.js.map +1 -1
  89. package/esm/utils/hex.d.ts +1 -1
  90. package/esm/utils/hex.d.ts.map +1 -1
  91. package/esm/utils/hex.js +12 -14
  92. package/esm/utils/hex.js.map +1 -1
  93. package/esm/utils/index.d.ts +1 -1
  94. package/esm/utils/index.d.ts.map +1 -1
  95. package/esm/utils/index.js +1 -1
  96. package/esm/utils/index.js.map +1 -1
  97. package/esm/utils/sequence-equal.d.ts +2 -0
  98. package/esm/utils/sequence-equal.d.ts.map +1 -0
  99. package/esm/utils/sequence-equal.js +12 -0
  100. package/esm/utils/sequence-equal.js.map +1 -0
  101. package/package.json +12 -14
  102. package/src/adb.ts +7 -4
  103. package/src/commands/power.ts +7 -5
  104. package/src/commands/reverse.ts +30 -17
  105. package/src/commands/subprocess/command.ts +3 -3
  106. package/src/commands/subprocess/protocols/none.ts +2 -2
  107. package/src/commands/subprocess/protocols/shell.ts +7 -11
  108. package/src/commands/subprocess/protocols/types.ts +2 -2
  109. package/src/commands/sync/list.ts +2 -4
  110. package/src/commands/sync/pull.ts +2 -2
  111. package/src/commands/sync/push.ts +7 -10
  112. package/src/commands/sync/request.ts +32 -32
  113. package/src/commands/sync/response.ts +47 -20
  114. package/src/commands/sync/socket.ts +13 -8
  115. package/src/commands/sync/stat.ts +0 -2
  116. package/src/commands/sync/sync.ts +8 -3
  117. package/src/commands/tcpip.ts +1 -1
  118. package/src/daemon/auth.ts +1 -1
  119. package/src/daemon/crypto.ts +64 -55
  120. package/src/daemon/dispatcher.ts +104 -42
  121. package/src/daemon/packet.ts +21 -14
  122. package/src/daemon/socket.ts +28 -25
  123. package/src/daemon/transport.ts +71 -24
  124. package/src/server/client.ts +420 -208
  125. package/src/server/transport.ts +4 -4
  126. package/src/utils/base64.ts +33 -22
  127. package/src/utils/hex.ts +16 -14
  128. package/src/utils/index.ts +1 -1
  129. package/src/utils/sequence-equal.ts +13 -0
  130. package/tsconfig.build.tsbuildinfo +1 -1
  131. package/esm/utils/conditional-variable.d.ts +0 -8
  132. package/esm/utils/conditional-variable.d.ts.map +0 -1
  133. package/esm/utils/conditional-variable.js +0 -34
  134. package/esm/utils/conditional-variable.js.map +0 -1
  135. package/src/utils/conditional-variable.ts +0 -45
@@ -1,12 +1,11 @@
1
1
  import { PromiseResolver } from "@yume-chan/async";
2
2
  import type {
3
- Consumable,
4
3
  PushReadableStreamController,
5
4
  ReadableStream,
6
5
  WritableStreamDefaultWriter,
7
6
  } from "@yume-chan/stream-extra";
8
7
  import {
9
- ConsumableWritableStream,
8
+ MaybeConsumable,
10
9
  PushReadableStream,
11
10
  StructDeserializeStream,
12
11
  WritableStream,
@@ -64,9 +63,9 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
64
63
  }
65
64
 
66
65
  readonly #socket: AdbSocket;
67
- #writer: WritableStreamDefaultWriter<Consumable<Uint8Array>>;
66
+ #writer: WritableStreamDefaultWriter<MaybeConsumable<Uint8Array>>;
68
67
 
69
- #stdin: WritableStream<Consumable<Uint8Array>>;
68
+ #stdin: WritableStream<MaybeConsumable<Uint8Array>>;
70
69
  get stdin() {
71
70
  return this.#stdin;
72
71
  }
@@ -140,23 +139,20 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
140
139
 
141
140
  this.#writer = this.#socket.writable.getWriter();
142
141
 
143
- this.#stdin = new WritableStream<Consumable<Uint8Array>>({
142
+ this.#stdin = new MaybeConsumable.WritableStream<Uint8Array>({
144
143
  write: async (chunk) => {
145
- await ConsumableWritableStream.write(
146
- this.#writer,
144
+ await this.#writer.write(
147
145
  AdbShellProtocolPacket.serialize({
148
146
  id: AdbShellProtocolId.Stdin,
149
- data: chunk.value,
147
+ data: chunk,
150
148
  }),
151
149
  );
152
- chunk.consume();
153
150
  },
154
151
  });
155
152
  }
156
153
 
157
154
  async resize(rows: number, cols: number) {
158
- await ConsumableWritableStream.write(
159
- this.#writer,
155
+ await this.#writer.write(
160
156
  AdbShellProtocolPacket.serialize({
161
157
  id: AdbShellProtocolId.WindowSizeChange,
162
158
  // The "correct" format is `${rows}x${cols},${x_pixels}x${y_pixels}`
@@ -1,5 +1,5 @@
1
1
  import type {
2
- Consumable,
2
+ MaybeConsumable,
3
3
  ReadableStream,
4
4
  WritableStream,
5
5
  } from "@yume-chan/stream-extra";
@@ -11,7 +11,7 @@ export interface AdbSubprocessProtocol {
11
11
  /**
12
12
  * A WritableStream that writes to the `stdin` stream.
13
13
  */
14
- readonly stdin: WritableStream<Consumable<Uint8Array>>;
14
+ readonly stdin: WritableStream<MaybeConsumable<Uint8Array>>;
15
15
 
16
16
  /**
17
17
  * The `stdout` stream of the process.
@@ -17,8 +17,7 @@ export interface AdbSyncEntry extends AdbSyncStat {
17
17
  export const AdbSyncEntryResponse = new Struct({ littleEndian: true })
18
18
  .concat(AdbSyncLstatResponse)
19
19
  .uint32("nameLength")
20
- .string("name", { lengthField: "nameLength" })
21
- .extra({ id: AdbSyncResponseId.Entry as const });
20
+ .string("name", { lengthField: "nameLength" });
22
21
 
23
22
  export type AdbSyncEntryResponse =
24
23
  (typeof AdbSyncEntryResponse)["TDeserializeResult"];
@@ -26,8 +25,7 @@ export type AdbSyncEntryResponse =
26
25
  export const AdbSyncEntry2Response = new Struct({ littleEndian: true })
27
26
  .concat(AdbSyncStatResponse)
28
27
  .uint32("nameLength")
29
- .string("name", { lengthField: "nameLength" })
30
- .extra({ id: AdbSyncResponseId.Entry2 as const });
28
+ .string("name", { lengthField: "nameLength" });
31
29
 
32
30
  export type AdbSyncEntry2Response =
33
31
  (typeof AdbSyncEntry2Response)["TDeserializeResult"];
@@ -8,8 +8,7 @@ import type { AdbSyncSocket } from "./socket.js";
8
8
 
9
9
  export const AdbSyncDataResponse = new Struct({ littleEndian: true })
10
10
  .uint32("dataLength")
11
- .uint8Array("data", { lengthField: "dataLength" })
12
- .extra({ id: AdbSyncResponseId.Data as const });
11
+ .uint8Array("data", { lengthField: "dataLength" });
13
12
 
14
13
  export type AdbSyncDataResponse =
15
14
  (typeof AdbSyncDataResponse)["TDeserializeResult"];
@@ -52,6 +51,7 @@ export function adbSyncPull(
52
51
  socket: AdbSyncSocket,
53
52
  path: string,
54
53
  ): ReadableStream<Uint8Array> {
54
+ // TODO: use `ReadableStream.from` when it's supported
55
55
  return new PushReadableStream(async (controller) => {
56
56
  for await (const data of adbSyncPullGenerator(socket, path)) {
57
57
  await controller.enqueue(data);
@@ -1,8 +1,8 @@
1
- import type { Consumable, ReadableStream } from "@yume-chan/stream-extra";
1
+ import type { ReadableStream } from "@yume-chan/stream-extra";
2
2
  import {
3
3
  AbortController,
4
- ConsumableWritableStream,
5
4
  DistributionStream,
5
+ MaybeConsumable,
6
6
  } from "@yume-chan/stream-extra";
7
7
  import Struct, { placeholder } from "@yume-chan/struct";
8
8
 
@@ -18,7 +18,7 @@ export const ADB_SYNC_MAX_PACKET_SIZE = 64 * 1024;
18
18
  export interface AdbSyncPushV1Options {
19
19
  socket: AdbSyncSocket;
20
20
  filename: string;
21
- file: ReadableStream<Consumable<Uint8Array>>;
21
+ file: ReadableStream<MaybeConsumable<Uint8Array>>;
22
22
  type?: LinuxFileType;
23
23
  permission?: number;
24
24
  mtime?: number;
@@ -31,7 +31,7 @@ export const AdbSyncOkResponse = new Struct({ littleEndian: true }).uint32(
31
31
 
32
32
  async function pipeFileData(
33
33
  locked: AdbSyncSocketLocked,
34
- file: ReadableStream<Consumable<Uint8Array>>,
34
+ file: ReadableStream<MaybeConsumable<Uint8Array>>,
35
35
  packetSize: number,
36
36
  mtime: number,
37
37
  ) {
@@ -40,7 +40,7 @@ async function pipeFileData(
40
40
  const abortController = new AbortController();
41
41
  file.pipeThrough(new DistributionStream(packetSize, true))
42
42
  .pipeTo(
43
- new ConsumableWritableStream({
43
+ new MaybeConsumable.WritableStream({
44
44
  write: async (chunk) => {
45
45
  await adbSyncWriteRequest(
46
46
  locked,
@@ -97,10 +97,7 @@ export enum AdbSyncSendV2Flags {
97
97
  * 4
98
98
  */
99
99
  Zstd = 1 << 2,
100
- /**
101
- * 0x80000000
102
- */
103
- DryRun = (1 << 31) >>> 0,
100
+ DryRun = 0x80000000,
104
101
  }
105
102
 
106
103
  export interface AdbSyncPushV2Options extends AdbSyncPushV1Options {
@@ -114,7 +111,7 @@ export interface AdbSyncPushV2Options extends AdbSyncPushV1Options {
114
111
  }
115
112
 
116
113
  export const AdbSyncSendV2Request = new Struct({ littleEndian: true })
117
- .uint32("id", placeholder<AdbSyncRequestId>())
114
+ .uint32("id")
118
115
  .uint32("mode")
119
116
  .uint32("flags", placeholder<AdbSyncSendV2Flags>());
120
117
 
@@ -2,53 +2,53 @@ import Struct from "@yume-chan/struct";
2
2
 
3
3
  import { encodeUtf8 } from "../../utils/index.js";
4
4
 
5
- export enum AdbSyncRequestId {
6
- List = "LIST",
7
- ListV2 = "LIS2",
8
- Send = "SEND",
9
- SendV2 = "SND2",
10
- Lstat = "STAT",
11
- Stat = "STA2",
12
- LstatV2 = "LST2",
13
- Data = "DATA",
14
- Done = "DONE",
15
- Receive = "RECV",
5
+ import { adbSyncEncodeId } from "./response.js";
6
+
7
+ export namespace AdbSyncRequestId {
8
+ export const List = adbSyncEncodeId("LIST");
9
+ export const ListV2 = adbSyncEncodeId("LIS2");
10
+ export const Send = adbSyncEncodeId("SEND");
11
+ export const SendV2 = adbSyncEncodeId("SND2");
12
+ export const Lstat = adbSyncEncodeId("STAT");
13
+ export const Stat = adbSyncEncodeId("STA2");
14
+ export const LstatV2 = adbSyncEncodeId("LST2");
15
+ export const Data = adbSyncEncodeId("DATA");
16
+ export const Done = adbSyncEncodeId("DONE");
17
+ export const Receive = adbSyncEncodeId("RECV");
16
18
  }
17
19
 
18
20
  export const AdbSyncNumberRequest = new Struct({ littleEndian: true })
19
- .string("id", { length: 4 })
21
+ .uint32("id")
20
22
  .uint32("arg");
21
23
 
22
- export const AdbSyncDataRequest = new Struct({ littleEndian: true })
23
- .concat(AdbSyncNumberRequest)
24
- .uint8Array("data", { lengthField: "arg" });
25
-
26
24
  export interface AdbSyncWritable {
27
25
  write(buffer: Uint8Array): Promise<void>;
28
26
  }
29
27
 
30
28
  export async function adbSyncWriteRequest(
31
29
  writable: AdbSyncWritable,
32
- id: AdbSyncRequestId | string,
30
+ id: number | string,
33
31
  value: number | string | Uint8Array,
34
32
  ): Promise<void> {
33
+ if (typeof id === "string") {
34
+ id = adbSyncEncodeId(id);
35
+ }
36
+
35
37
  if (typeof value === "number") {
36
- const buffer = AdbSyncNumberRequest.serialize({
37
- id,
38
- arg: value,
39
- });
40
- await writable.write(buffer);
41
- } else if (typeof value === "string") {
42
- // Let `writable` buffer writes
43
- const buffer = encodeUtf8(value);
44
38
  await writable.write(
45
- AdbSyncNumberRequest.serialize({ id, arg: buffer.byteLength }),
39
+ AdbSyncNumberRequest.serialize({ id, arg: value }),
46
40
  );
47
- await writable.write(buffer);
48
- } else {
49
- await writable.write(
50
- AdbSyncNumberRequest.serialize({ id, arg: value.byteLength }),
51
- );
52
- await writable.write(value);
41
+ return;
42
+ }
43
+
44
+ if (typeof value === "string") {
45
+ value = encodeUtf8(value);
53
46
  }
47
+
48
+ // `writable` is buffered, it copies inputs to an internal buffer,
49
+ // so don't concatenate headers and data here, that will be an unnecessary copy.
50
+ await writable.write(
51
+ AdbSyncNumberRequest.serialize({ id, arg: value.length }),
52
+ );
53
+ await writable.write(value);
54
54
  }
@@ -1,22 +1,39 @@
1
+ import { getUint32LittleEndian } from "@yume-chan/no-data-view";
1
2
  import type {
2
3
  AsyncExactReadable,
3
4
  StructLike,
4
5
  StructValueType,
5
6
  } from "@yume-chan/struct";
6
- import Struct from "@yume-chan/struct";
7
+ import Struct, { decodeUtf8 } from "@yume-chan/struct";
7
8
 
8
- import { decodeUtf8 } from "../../utils/index.js";
9
+ function encodeAsciiUnchecked(value: string): Uint8Array {
10
+ const result = new Uint8Array(value.length);
11
+ for (let i = 0; i < value.length; i += 1) {
12
+ result[i] = value.charCodeAt(i);
13
+ }
14
+ return result;
15
+ }
16
+
17
+ /**
18
+ * Encode ID to numbers for faster comparison
19
+ * @param value A 4-character string
20
+ * @returns A 32-bit integer by encoding the string as little-endian
21
+ */
22
+ export function adbSyncEncodeId(value: string): number {
23
+ const buffer = encodeAsciiUnchecked(value);
24
+ return getUint32LittleEndian(buffer, 0);
25
+ }
9
26
 
10
- export enum AdbSyncResponseId {
11
- Entry = "DENT",
12
- Entry2 = "DNT2",
13
- Lstat = "STAT",
14
- Stat = "STA2",
15
- Lstat2 = "LST2",
16
- Done = "DONE",
17
- Data = "DATA",
18
- Ok = "OKAY",
19
- Fail = "FAIL",
27
+ export namespace AdbSyncResponseId {
28
+ export const Entry = adbSyncEncodeId("DENT");
29
+ export const Entry2 = adbSyncEncodeId("DNT2");
30
+ export const Lstat = adbSyncEncodeId("STAT");
31
+ export const Stat = adbSyncEncodeId("STA2");
32
+ export const Lstat2 = adbSyncEncodeId("LST2");
33
+ export const Done = adbSyncEncodeId("DONE");
34
+ export const Data = adbSyncEncodeId("DATA");
35
+ export const Ok = adbSyncEncodeId("OKAY");
36
+ export const Fail = adbSyncEncodeId("FAIL");
20
37
  }
21
38
 
22
39
  export class AdbSyncError extends Error {}
@@ -30,18 +47,24 @@ export const AdbSyncFailResponse = new Struct({ littleEndian: true })
30
47
 
31
48
  export async function adbSyncReadResponse<T>(
32
49
  stream: AsyncExactReadable,
33
- id: AdbSyncResponseId,
50
+ id: number | string,
34
51
  type: StructLike<T>,
35
52
  ): Promise<T> {
36
- const actualId = decodeUtf8(await stream.readExactly(4));
37
- switch (actualId) {
53
+ if (typeof id === "string") {
54
+ id = adbSyncEncodeId(id);
55
+ }
56
+
57
+ const buffer = await stream.readExactly(4);
58
+ switch (getUint32LittleEndian(buffer, 0)) {
38
59
  case AdbSyncResponseId.Fail:
39
60
  await AdbSyncFailResponse.deserialize(stream);
40
61
  throw new Error("Unreachable");
41
62
  case id:
42
63
  return await type.deserialize(stream);
43
64
  default:
44
- throw new Error(`Expected '${id}', but got '${actualId}'`);
65
+ throw new Error(
66
+ `Expected '${id}', but got '${decodeUtf8(buffer)}'`,
67
+ );
45
68
  }
46
69
  }
47
70
 
@@ -49,12 +72,16 @@ export async function* adbSyncReadResponses<
49
72
  T extends Struct<object, PropertyKey, object, unknown>,
50
73
  >(
51
74
  stream: AsyncExactReadable,
52
- id: AdbSyncResponseId,
75
+ id: number | string,
53
76
  type: T,
54
77
  ): AsyncGenerator<StructValueType<T>, void, void> {
78
+ if (typeof id === "string") {
79
+ id = adbSyncEncodeId(id);
80
+ }
81
+
55
82
  while (true) {
56
- const actualId = decodeUtf8(await stream.readExactly(4));
57
- switch (actualId) {
83
+ const buffer = await stream.readExactly(4);
84
+ switch (getUint32LittleEndian(buffer, 0)) {
58
85
  case AdbSyncResponseId.Fail:
59
86
  await AdbSyncFailResponse.deserialize(stream);
60
87
  throw new Error("Unreachable");
@@ -70,7 +97,7 @@ export async function* adbSyncReadResponses<
70
97
  break;
71
98
  default:
72
99
  throw new Error(
73
- `Expected '${id}' or '${AdbSyncResponseId.Done}', but got '${actualId}'`,
100
+ `Expected '${id}' or '${AdbSyncResponseId.Done}', but got '${decodeUtf8(buffer)}'`,
74
101
  );
75
102
  }
76
103
  }
@@ -1,11 +1,11 @@
1
1
  import type {
2
- Consumable,
2
+ MaybeConsumable,
3
3
  WritableStreamDefaultWriter,
4
4
  } from "@yume-chan/stream-extra";
5
5
  import {
6
6
  BufferCombiner,
7
7
  BufferedReadableStream,
8
- ConsumableWritableStream,
8
+ Consumable,
9
9
  } from "@yume-chan/stream-extra";
10
10
  import type { AsyncExactReadable } from "@yume-chan/struct";
11
11
 
@@ -13,7 +13,7 @@ import type { AdbSocket } from "../../adb.js";
13
13
  import { AutoResetEvent } from "../../utils/index.js";
14
14
 
15
15
  export class AdbSyncSocketLocked implements AsyncExactReadable {
16
- readonly #writer: WritableStreamDefaultWriter<Consumable<Uint8Array>>;
16
+ readonly #writer: WritableStreamDefaultWriter<MaybeConsumable<Uint8Array>>;
17
17
  readonly #readable: BufferedReadableStream;
18
18
  readonly #socketLock: AutoResetEvent;
19
19
  readonly #writeLock = new AutoResetEvent();
@@ -24,7 +24,7 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
24
24
  }
25
25
 
26
26
  constructor(
27
- writer: WritableStreamDefaultWriter<Consumable<Uint8Array>>,
27
+ writer: WritableStreamDefaultWriter<MaybeConsumable<Uint8Array>>,
28
28
  readable: BufferedReadableStream,
29
29
  bufferSize: number,
30
30
  lock: AutoResetEvent,
@@ -35,8 +35,9 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
35
35
  this.#combiner = new BufferCombiner(bufferSize);
36
36
  }
37
37
 
38
- async #writeInnerStream(buffer: Uint8Array) {
39
- await ConsumableWritableStream.write(this.#writer, buffer);
38
+ async #write(buffer: Uint8Array) {
39
+ // `#combiner` will reuse the buffer, so we need to use the Consumable pattern
40
+ await Consumable.WritableStream.write(this.#writer, buffer);
40
41
  }
41
42
 
42
43
  async flush() {
@@ -44,7 +45,7 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
44
45
  await this.#writeLock.wait();
45
46
  const buffer = this.#combiner.flush();
46
47
  if (buffer) {
47
- await this.#writeInnerStream(buffer);
48
+ await this.#write(buffer);
48
49
  }
49
50
  } finally {
50
51
  this.#writeLock.notifyOne();
@@ -55,7 +56,7 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
55
56
  try {
56
57
  await this.#writeLock.wait();
57
58
  for (const buffer of this.#combiner.push(data)) {
58
- await this.#writeInnerStream(buffer);
59
+ await this.#write(buffer);
59
60
  }
60
61
  } finally {
61
62
  this.#writeLock.notifyOne();
@@ -63,11 +64,15 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
63
64
  }
64
65
 
65
66
  async readExactly(length: number) {
67
+ // The request may still be in the internal buffer.
68
+ // Call `flush` to send it before starting reading
66
69
  await this.flush();
67
70
  return await this.#readable.readExactly(length);
68
71
  }
69
72
 
70
73
  release(): void {
74
+ // In theory, the writer shouldn't leave anything in the buffer,
75
+ // but to be safe, call `flush` to throw away any remaining data.
71
76
  this.#combiner.flush();
72
77
  this.#socketLock.notifyOne();
73
78
  }
@@ -29,7 +29,6 @@ export const AdbSyncLstatResponse = new Struct({ littleEndian: true })
29
29
  .int32("size")
30
30
  .int32("mtime")
31
31
  .extra({
32
- id: AdbSyncResponseId.Lstat as const,
33
32
  get type() {
34
33
  return (this.mode >> 12) as LinuxFileType;
35
34
  },
@@ -83,7 +82,6 @@ export const AdbSyncStatResponse = new Struct({ littleEndian: true })
83
82
  .uint64("mtime")
84
83
  .uint64("ctime")
85
84
  .extra({
86
- id: AdbSyncResponseId.Stat as const,
87
85
  get type() {
88
86
  return (this.mode >> 12) as LinuxFileType;
89
87
  },
@@ -1,5 +1,5 @@
1
1
  import { AutoDisposable } from "@yume-chan/event";
2
- import type { Consumable, ReadableStream } from "@yume-chan/stream-extra";
2
+ import type { MaybeConsumable, ReadableStream } from "@yume-chan/stream-extra";
3
3
 
4
4
  import type { Adb, AdbSocket } from "../../adb.js";
5
5
  import { AdbFeature } from "../../features.js";
@@ -9,6 +9,7 @@ import type { AdbSyncEntry } from "./list.js";
9
9
  import { adbSyncOpenDir } from "./list.js";
10
10
  import { adbSyncPull } from "./pull.js";
11
11
  import { adbSyncPush } from "./push.js";
12
+ import type { AdbSyncSocketLocked } from "./socket.js";
12
13
  import { AdbSyncSocket } from "./socket.js";
13
14
  import type { AdbSyncStat, LinuxFileType } from "./stat.js";
14
15
  import { adbSyncLstat, adbSyncStat } from "./stat.js";
@@ -31,7 +32,7 @@ export function dirname(path: string): string {
31
32
 
32
33
  export interface AdbSyncWriteOptions {
33
34
  filename: string;
34
- file: ReadableStream<Consumable<Uint8Array>>;
35
+ file: ReadableStream<MaybeConsumable<Uint8Array>>;
35
36
  type?: LinuxFileType;
36
37
  permission?: number;
37
38
  mtime?: number;
@@ -150,7 +151,7 @@ export class AdbSync extends AutoDisposable {
150
151
  */
151
152
  async write(options: AdbSyncWriteOptions): Promise<void> {
152
153
  if (this.needPushMkdirWorkaround) {
153
- // It may fail if the path is already existed.
154
+ // It may fail if `filename` already exists.
154
155
  // Ignore the result.
155
156
  // TODO: sync: test push mkdir workaround (need an Android 8 device)
156
157
  await this._adb.subprocess.spawnAndWait([
@@ -167,6 +168,10 @@ export class AdbSync extends AutoDisposable {
167
168
  });
168
169
  }
169
170
 
171
+ lockSocket(): Promise<AdbSyncSocketLocked> {
172
+ return this._socket.lock();
173
+ }
174
+
170
175
  override async dispose() {
171
176
  super.dispose();
172
177
  await this._socket.close();
@@ -47,7 +47,7 @@ export class AdbTcpIpCommand extends AdbCommandBase {
47
47
 
48
48
  async setPort(port: number): Promise<string> {
49
49
  if (port <= 0) {
50
- throw new Error(`Invalid port ${port}`);
50
+ throw new TypeError(`Invalid port ${port}`);
51
51
  }
52
52
 
53
53
  const output = await this.adb.createSocketAndWait(`tcpip:${port}`);
@@ -83,7 +83,7 @@ export const AdbSignatureAuthenticator: AdbAuthenticator = async function* (
83
83
  command: AdbCommand.Auth,
84
84
  arg0: AdbAuthType.Signature,
85
85
  arg1: 0,
86
- payload: new Uint8Array(signature),
86
+ payload: signature,
87
87
  };
88
88
  }
89
89
  };