From b423c79755864c8664f761385f8b83f646a1d73a Mon Sep 17 00:00:00 2001 From: Simon Chan <1330321+yume-chan@users.noreply.github.com> Date: Wed, 10 Sep 2025 19:04:38 +0800 Subject: [PATCH] chore: fix review comments --- .changeset/clean-moons-obey.md | 7 + libraries/scrcpy/src/1_15/options.ts | 2 +- libraries/scrcpy/src/1_17/options.ts | 2 +- libraries/scrcpy/src/1_18/options.ts | 2 +- libraries/scrcpy/src/1_21/options.ts | 18 +- libraries/scrcpy/src/1_22/options.ts | 18 +- libraries/scrcpy/src/1_23/options.ts | 18 +- libraries/scrcpy/src/1_24/options.ts | 18 +- libraries/scrcpy/src/1_25/options.ts | 18 +- libraries/scrcpy/src/2_0/impl/defaults.ts | 2 +- libraries/scrcpy/src/2_0/options.ts | 18 +- libraries/scrcpy/src/2_1/options.ts | 18 +- libraries/scrcpy/src/2_2/options.ts | 18 +- libraries/scrcpy/src/2_3/options.ts | 18 +- libraries/scrcpy/src/2_4/options.ts | 18 +- libraries/scrcpy/src/2_6/options.ts | 18 +- libraries/scrcpy/src/2_7/options.ts | 18 +- libraries/scrcpy/src/3_0/impl/defaults.ts | 2 +- libraries/scrcpy/src/3_0/options.ts | 18 +- libraries/scrcpy/src/3_1/options.ts | 18 +- libraries/scrcpy/src/3_2/options.ts | 18 +- libraries/scrcpy/src/3_3_1/options.ts | 18 +- libraries/struct/src/bipedal.ts | 2 +- libraries/struct/src/buffer.ts | 345 ++++++++++------------ libraries/struct/src/field/types.ts | 10 +- libraries/struct/src/struct.ts | 4 +- toolchain/side-effect-test/generate.mjs | 15 +- toolchain/side-effect-test/src/adb.js | 4 + toolchain/side-effect-test/src/scrcpy.js | 4 + 29 files changed, 447 insertions(+), 242 deletions(-) create mode 100644 .changeset/clean-moons-obey.md diff --git a/.changeset/clean-moons-obey.md b/.changeset/clean-moons-obey.md new file mode 100644 index 00000000..20977a89 --- /dev/null +++ b/.changeset/clean-moons-obey.md @@ -0,0 +1,7 @@ +--- +"@yume-chan/scrcpy": major +--- + +Convert `ScrcpyOptionsX\_YY.prototype.controlMessageTypes` to a map between internal control message types to version-specific values. + +Generally, you would use `ScrcpyControlMessageSerializer`, `ScrcpyControlMessageWriter`, or `AdbScrcpyClient.prototype.controller` to send control messages to device, instead of using `controlMessageTypes` directly. diff --git a/libraries/scrcpy/src/1_15/options.ts b/libraries/scrcpy/src/1_15/options.ts index 6883b90b..c48e5139 100644 --- a/libraries/scrcpy/src/1_15/options.ts +++ b/libraries/scrcpy/src/1_15/options.ts @@ -37,7 +37,7 @@ export class ScrcpyOptions1_15 implements ScrcpyOptions { readonly value: Required; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } diff --git a/libraries/scrcpy/src/1_17/options.ts b/libraries/scrcpy/src/1_17/options.ts index 31499c38..ec8af85f 100644 --- a/libraries/scrcpy/src/1_17/options.ts +++ b/libraries/scrcpy/src/1_17/options.ts @@ -44,7 +44,7 @@ export class ScrcpyOptions1_17 readonly value: Required; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } diff --git a/libraries/scrcpy/src/1_18/options.ts b/libraries/scrcpy/src/1_18/options.ts index 29b1799d..c2e1ec3d 100644 --- a/libraries/scrcpy/src/1_18/options.ts +++ b/libraries/scrcpy/src/1_18/options.ts @@ -44,7 +44,7 @@ export class ScrcpyOptions1_18 readonly value: Required; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } diff --git a/libraries/scrcpy/src/1_21/options.ts b/libraries/scrcpy/src/1_21/options.ts index 459faccd..6d62d567 100644 --- a/libraries/scrcpy/src/1_21/options.ts +++ b/libraries/scrcpy/src/1_21/options.ts @@ -43,7 +43,7 @@ export class ScrcpyOptions1_21 readonly value: Required; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -121,7 +121,21 @@ export class ScrcpyOptions1_21 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/1_22/options.ts b/libraries/scrcpy/src/1_22/options.ts index 499058dd..4692c346 100644 --- a/libraries/scrcpy/src/1_22/options.ts +++ b/libraries/scrcpy/src/1_22/options.ts @@ -43,7 +43,7 @@ export class ScrcpyOptions1_22 readonly value: Required; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -121,7 +121,21 @@ export class ScrcpyOptions1_22 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/1_23/options.ts b/libraries/scrcpy/src/1_23/options.ts index 082e79c9..581986e0 100644 --- a/libraries/scrcpy/src/1_23/options.ts +++ b/libraries/scrcpy/src/1_23/options.ts @@ -43,7 +43,7 @@ export class ScrcpyOptions1_23 readonly value: Required; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -121,7 +121,21 @@ export class ScrcpyOptions1_23 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/1_24/options.ts b/libraries/scrcpy/src/1_24/options.ts index 46a55d0e..24fbb143 100644 --- a/libraries/scrcpy/src/1_24/options.ts +++ b/libraries/scrcpy/src/1_24/options.ts @@ -43,7 +43,7 @@ export class ScrcpyOptions1_24 readonly value: Required; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -121,7 +121,21 @@ export class ScrcpyOptions1_24 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/1_25/options.ts b/libraries/scrcpy/src/1_25/options.ts index 70c74980..7bae7dd5 100644 --- a/libraries/scrcpy/src/1_25/options.ts +++ b/libraries/scrcpy/src/1_25/options.ts @@ -43,7 +43,7 @@ export class ScrcpyOptions1_25 readonly value: Required; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -121,7 +121,21 @@ export class ScrcpyOptions1_25 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/2_0/impl/defaults.ts b/libraries/scrcpy/src/2_0/impl/defaults.ts index dadc8505..770f8ea5 100644 --- a/libraries/scrcpy/src/2_0/impl/defaults.ts +++ b/libraries/scrcpy/src/2_0/impl/defaults.ts @@ -5,7 +5,7 @@ export const Defaults = { ...{ ...PrevImpl.Defaults, // Remove obsolete values - // replies on minifier to flatten the nested spread + // relies on the minifier to flatten the nested spread bitRate: undefined, codecOptions: undefined, encoderName: undefined, diff --git a/libraries/scrcpy/src/2_0/options.ts b/libraries/scrcpy/src/2_0/options.ts index ccb66951..4a81b792 100644 --- a/libraries/scrcpy/src/2_0/options.ts +++ b/libraries/scrcpy/src/2_0/options.ts @@ -44,7 +44,7 @@ export class ScrcpyOptions2_0 readonly value: Required; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -128,7 +128,21 @@ export class ScrcpyOptions2_0 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/2_1/options.ts b/libraries/scrcpy/src/2_1/options.ts index 31c5dadc..76533c6b 100644 --- a/libraries/scrcpy/src/2_1/options.ts +++ b/libraries/scrcpy/src/2_1/options.ts @@ -44,7 +44,7 @@ export class ScrcpyOptions2_1 readonly value: Required>; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -128,7 +128,21 @@ export class ScrcpyOptions2_1 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/2_2/options.ts b/libraries/scrcpy/src/2_2/options.ts index 33870dc2..b3ea2a48 100644 --- a/libraries/scrcpy/src/2_2/options.ts +++ b/libraries/scrcpy/src/2_2/options.ts @@ -44,7 +44,7 @@ export class ScrcpyOptions2_2 readonly value: Required>; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -132,7 +132,21 @@ export class ScrcpyOptions2_2 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/2_3/options.ts b/libraries/scrcpy/src/2_3/options.ts index f4bd1001..3db8925f 100644 --- a/libraries/scrcpy/src/2_3/options.ts +++ b/libraries/scrcpy/src/2_3/options.ts @@ -44,7 +44,7 @@ export class ScrcpyOptions2_3 readonly value: Required>; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -132,7 +132,21 @@ export class ScrcpyOptions2_3 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/2_4/options.ts b/libraries/scrcpy/src/2_4/options.ts index 1bdf2736..5f0ecff9 100644 --- a/libraries/scrcpy/src/2_4/options.ts +++ b/libraries/scrcpy/src/2_4/options.ts @@ -48,7 +48,7 @@ export class ScrcpyOptions2_4 readonly value: Required>; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -149,7 +149,21 @@ export class ScrcpyOptions2_4 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/2_6/options.ts b/libraries/scrcpy/src/2_6/options.ts index 3a3a7318..e8b1600b 100644 --- a/libraries/scrcpy/src/2_6/options.ts +++ b/libraries/scrcpy/src/2_6/options.ts @@ -48,7 +48,7 @@ export class ScrcpyOptions2_6 readonly value: Required>; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -153,7 +153,21 @@ export class ScrcpyOptions2_6 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/2_7/options.ts b/libraries/scrcpy/src/2_7/options.ts index 3de4aed9..e2f2b896 100644 --- a/libraries/scrcpy/src/2_7/options.ts +++ b/libraries/scrcpy/src/2_7/options.ts @@ -48,7 +48,7 @@ export class ScrcpyOptions2_7 readonly value: Required>; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -153,7 +153,21 @@ export class ScrcpyOptions2_7 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/3_0/impl/defaults.ts b/libraries/scrcpy/src/3_0/impl/defaults.ts index 5cd69c07..5008b2ed 100644 --- a/libraries/scrcpy/src/3_0/impl/defaults.ts +++ b/libraries/scrcpy/src/3_0/impl/defaults.ts @@ -5,7 +5,7 @@ export const Defaults = { ...{ ...PrevImpl.Defaults, // Remove obsolete values - // replies on minifier to flatten the nested spread + // relies on the minifier to flatten the nested spread lockVideoOrientation: undefined, }, diff --git a/libraries/scrcpy/src/3_0/options.ts b/libraries/scrcpy/src/3_0/options.ts index f3779e47..1458342c 100644 --- a/libraries/scrcpy/src/3_0/options.ts +++ b/libraries/scrcpy/src/3_0/options.ts @@ -48,7 +48,7 @@ export class ScrcpyOptions3_0 readonly value: Required>; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -153,7 +153,21 @@ export class ScrcpyOptions3_0 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/3_1/options.ts b/libraries/scrcpy/src/3_1/options.ts index 9da7a6cd..b235e856 100644 --- a/libraries/scrcpy/src/3_1/options.ts +++ b/libraries/scrcpy/src/3_1/options.ts @@ -48,7 +48,7 @@ export class ScrcpyOptions3_1 readonly value: Required>; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -153,7 +153,21 @@ export class ScrcpyOptions3_1 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/3_2/options.ts b/libraries/scrcpy/src/3_2/options.ts index c082cbba..dfcc1727 100644 --- a/libraries/scrcpy/src/3_2/options.ts +++ b/libraries/scrcpy/src/3_2/options.ts @@ -48,7 +48,7 @@ export class ScrcpyOptions3_2 readonly value: Required>; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -153,7 +153,21 @@ export class ScrcpyOptions3_2 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/scrcpy/src/3_3_1/options.ts b/libraries/scrcpy/src/3_3_1/options.ts index 0fd25bb9..360f5eeb 100644 --- a/libraries/scrcpy/src/3_3_1/options.ts +++ b/libraries/scrcpy/src/3_3_1/options.ts @@ -48,7 +48,7 @@ export class ScrcpyOptions3_3_1 readonly value: Required>; - get controlMessageTypes() { + get controlMessageTypes(): typeof ControlMessageTypes { return ControlMessageTypes; } @@ -153,7 +153,21 @@ export class ScrcpyOptions3_3_1 serializeSetClipboardControlMessage( message: ScrcpySetClipboardControlMessage, ): Uint8Array | [Uint8Array, Promise] { - return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + if (!this.#ackClipboardHandler) { + if (!this.value.control) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `control: true`", + ); + } else if (!this.value.clipboardAutosync) { + throw new Error( + "`serializeSetClipboardControlMessage` requires `clipboardAutosync: true`", + ); + } else { + throw new Error("unreachable"); + } + } + + return this.#ackClipboardHandler.serializeSetClipboardControlMessage( message, ); } diff --git a/libraries/struct/src/bipedal.ts b/libraries/struct/src/bipedal.ts index 56588659..b37ac06b 100644 --- a/libraries/struct/src/bipedal.ts +++ b/libraries/struct/src/bipedal.ts @@ -24,7 +24,7 @@ export type BipedalGenerator = ( this: This, then: (value: MaybePromiseLike) => Iterable, ...args: A -) => Generator; +) => Generator, unknown>; /* #__NO_SIDE_EFFECTS__ */ export function bipedal( diff --git a/libraries/struct/src/buffer.ts b/libraries/struct/src/buffer.ts index c7f73c9b..92a57a4a 100644 --- a/libraries/struct/src/buffer.ts +++ b/libraries/struct/src/buffer.ts @@ -1,22 +1,26 @@ -import type { Field } from "./field/index.js"; +import type { + BipedalFieldDeserializer, + ByobFieldSerializer, + Field, +} from "./field/index.js"; import { field } from "./field/index.js"; export const EmptyUint8Array = new Uint8Array(0); function copyMaybeDifferentLength( - dist: Uint8Array, + dest: Uint8Array, source: Uint8Array, index: number, length: number, ) { if (source.length < length) { - dist.set(source, index); + dest.set(source, index); // Clear trailing bytes - dist.fill(0, index + source.length, index + length); + dest.fill(0, index + source.length, index + length); } else if (source.length === length) { - dist.set(source, index); + dest.set(source, index); } else { - dist.set(source.subarray(0, length), index); + dest.set(source.subarray(0, length), index); } } @@ -129,67 +133,47 @@ export function buffer( ): Field, Uint8Array> { // Fixed length if (typeof lengthOrField === "number") { - if (converter) { - if (lengthOrField === 0) { - return field( - 0, - "byob", - () => {}, - // eslint-disable-next-line require-yield - function* () { - return converter.convert(EmptyUint8Array); - }, - ); - } - - return field( - lengthOrField, - "byob", - (value, { buffer, index }) => { - copyMaybeDifferentLength( - buffer, - value, - index, - lengthOrField, - ); - }, - function* (then, reader) { - const array = yield* then( - reader.readExactly(lengthOrField), - ); - return converter.convert(array); - }, - { - init(value) { - return converter.back(value); - }, - }, - ); - } + let serialize: ByobFieldSerializer; + let deserialize: BipedalFieldDeserializer< + unknown, + Record + >; + let init: ((value: unknown) => Uint8Array) | undefined; if (lengthOrField === 0) { - return field( - 0, - "byob", - () => {}, + serialize = () => {}; + + if (converter) { // eslint-disable-next-line require-yield - function* () { + deserialize = function* () { + return converter.convert(EmptyUint8Array); + }; + } else { + // eslint-disable-next-line require-yield + deserialize = function* () { return EmptyUint8Array; - }, - ); + }; + } + } else { + serialize = (value, { buffer, index }) => + copyMaybeDifferentLength(buffer, value, index, lengthOrField); + + if (converter) { + deserialize = function* (then, reader) { + const array = reader.readExactly(lengthOrField); + return converter.convert(yield* then(array)); + }; + init = (value) => converter.back(value); + } else { + // eslint-disable-next-line require-yield + deserialize = function* (_then, reader) { + const array = reader.readExactly(lengthOrField); + return array; + }; + } } - return field( - lengthOrField, - "byob", - (value, { buffer, index }) => { - copyMaybeDifferentLength(buffer, value, index, lengthOrField); - }, - // eslint-disable-next-line require-yield - function* (_then, reader) { - return reader.readExactly(lengthOrField); - }, - ); + return field(lengthOrField, "byob", serialize, deserialize, { init }); } // Declare length field @@ -199,48 +183,31 @@ export function buffer( typeof lengthOrField === "function") && "serialize" in lengthOrField ) { + let deserialize: BipedalFieldDeserializer< + unknown, + Record + >; + let init: ((value: unknown) => Uint8Array) | undefined; + if (converter) { - return field( - lengthOrField.size, - "default", - (value, { littleEndian }) => { - if (lengthOrField.type === "default") { - const lengthBuffer = lengthOrField.serialize( - value.length, - { littleEndian }, - ); - const result = new Uint8Array( - lengthBuffer.length + value.length, - ); - result.set(lengthBuffer, 0); - result.set(value, lengthBuffer.length); - return result; - } else { - const result = new Uint8Array( - lengthOrField.size + value.length, - ); - lengthOrField.serialize(value.length, { - buffer: result, - index: 0, - littleEndian, - }); - result.set(value, lengthOrField.size); - return result; - } - }, - function* (then, reader, context) { - const length = yield* then( - lengthOrField.deserialize(reader, context), - ); - const array = yield* then(reader.readExactly(length)); - return converter.convert(array); - }, - { - init(value) { - return converter.back(value); - }, - }, - ); + deserialize = function* (then, reader, context) { + const length = yield* then( + lengthOrField.deserialize(reader, context), + ); + const array = + length !== 0 ? reader.readExactly(length) : EmptyUint8Array; + return converter.convert(yield* then(array)); + }; + init = (value) => converter.back(value); + } else { + deserialize = function* (then, reader, context) { + const length = yield* then( + lengthOrField.deserialize(reader, context), + ); + const array = + length !== 0 ? reader.readExactly(length) : EmptyUint8Array; + return array; + }; } return field( @@ -251,6 +218,11 @@ export function buffer( const lengthBuffer = lengthOrField.serialize(value.length, { littleEndian, }); + + if (value.length === 0) { + return lengthBuffer; + } + const result = new Uint8Array( lengthBuffer.length + value.length, ); @@ -270,112 +242,91 @@ export function buffer( return result; } }, - function* (then, reader, context) { - const length = yield* then( - lengthOrField.deserialize(reader, context), - ); - return yield* then(reader.readExactly(length)); - }, + deserialize, + { init }, ); } - // Reference exiting length field + // Reference existing length field if (typeof lengthOrField === "string") { - if (converter) { - return field( - 0, - "default", - (source) => source, - // eslint-disable-next-line require-yield - function* (_then, reader, { dependencies }) { - const length = dependencies[lengthOrField] as number; - if (length === 0) { - return EmptyUint8Array; - } + let deserialize: BipedalFieldDeserializer< + unknown, + Record + >; + let init: ( + value: unknown, + dependencies: Record, + ) => Uint8Array; - return reader.readExactly(length); - }, - { - init(value, dependencies) { - const array = converter.back(value); - dependencies[lengthOrField] = array.length; - return array; - }, - }, - ); + if (converter) { + deserialize = function* (then, reader, { dependencies }) { + const length = dependencies[lengthOrField] as number; + const array = + length !== 0 ? reader.readExactly(length) : EmptyUint8Array; + return converter.convert(yield* then(array)); + }; + init = (value, dependencies) => { + const array = converter.back(value); + dependencies[lengthOrField] = array.length; + return array; + }; + } else { + // eslint-disable-next-line require-yield + deserialize = function* (_then, reader, { dependencies }) { + const length = dependencies[lengthOrField] as number; + const array = + length !== 0 ? reader.readExactly(length) : EmptyUint8Array; + return array; + }; + init = (value, dependencies) => { + const array = value as Uint8Array; + dependencies[lengthOrField] = array.length; + return array; + }; } - return field( - 0, - "default", - (source) => source, - // eslint-disable-next-line require-yield - function* (_then, reader, { dependencies }) { - const length = dependencies[lengthOrField] as number; - if (length === 0) { - return EmptyUint8Array; - } - - return reader.readExactly(length); - }, - { - init(value, dependencies) { - dependencies[lengthOrField] = (value as Uint8Array).length; - return undefined; - }, - }, - ); + return field(0, "default", (source) => source, deserialize, { init }); } - // Reference existing length field + converter + let deserialize: BipedalFieldDeserializer>; + let init: ( + value: unknown, + dependencies: Record, + ) => Uint8Array; + + // Reference existing length field + length converter if (converter) { - return field( - 0, - "default", - (source) => source, - // eslint-disable-next-line require-yield - function* (_then, reader, { dependencies }) { - const rawLength = dependencies[lengthOrField.field]; - const length = lengthOrField.convert(rawLength); - if (length === 0) { - return EmptyUint8Array; - } - - return reader.readExactly(length); - }, - { - init(value, dependencies) { - const array = converter.back(value); - dependencies[lengthOrField.field] = lengthOrField.back( - array.length, - ); - return array; - }, - }, - ); - } - - return field( - 0, - "default", - (source) => source, - // eslint-disable-next-line require-yield - function* (_then, reader, { dependencies }) { + deserialize = function* (then, reader, { dependencies }) { const rawLength = dependencies[lengthOrField.field]; const length = lengthOrField.convert(rawLength); - if (length === 0) { - return EmptyUint8Array; - } + const array = + length !== 0 ? reader.readExactly(length) : EmptyUint8Array; + return converter.convert(yield* then(array)); + }; + init = (value, dependencies) => { + const array = converter.back(value); + dependencies[lengthOrField.field] = lengthOrField.back( + array.length, + ); + return array; + }; + } else { + // eslint-disable-next-line require-yield + deserialize = function* (_then, reader, { dependencies }) { + const rawLength = dependencies[lengthOrField.field]; + const length = lengthOrField.convert(rawLength); + const array = + length !== 0 ? reader.readExactly(length) : EmptyUint8Array; + return array; + }; + init = (value, dependencies) => { + const array = value as Uint8Array; + dependencies[lengthOrField.field] = lengthOrField.back( + array.length, + ); + return array; + }; + } - return reader.readExactly(length); - }, - { - init(value, dependencies) { - dependencies[lengthOrField.field] = lengthOrField.back( - (value as Uint8Array).length, - ); - return undefined; - }, - }, - ); + return field(0, "default", (source) => source, deserialize, { init }); } diff --git a/libraries/struct/src/field/types.ts b/libraries/struct/src/field/types.ts index 5b520942..17694c5d 100644 --- a/libraries/struct/src/field/types.ts +++ b/libraries/struct/src/field/types.ts @@ -25,7 +25,10 @@ export interface Field FieldDeserializer { omitInit: OmitInit | undefined; - init?(value: T, dependencies: D): Raw | undefined; + /** + * A function to convert deserialized value back to raw value for serialization. + */ + init?(value: T, dependencies: D): Raw; } export interface FieldDeserializeContext { @@ -44,5 +47,8 @@ export interface FieldDeserializer { export interface FieldOptions { omitInit?: OmitInit; dependencies?: D; - init?: (value: T, dependencies: D) => Raw | undefined; + /** + * A function to convert deserialized value back to raw value for serialization. + */ + init?: ((value: T, dependencies: D) => Raw) | undefined; } diff --git a/libraries/struct/src/struct.ts b/libraries/struct/src/struct.ts index 6c169032..2c01d32f 100644 --- a/libraries/struct/src/struct.ts +++ b/libraries/struct/src/struct.ts @@ -132,9 +132,7 @@ export function struct< for (const [key, field] of fieldList) { if (key in temp && "init" in field) { const result = field.init?.(temp[key], temp as never); - if (result !== undefined) { - temp[key] = result; - } + temp[key] = result; } } diff --git a/toolchain/side-effect-test/generate.mjs b/toolchain/side-effect-test/generate.mjs index 5eb33344..caa862b6 100644 --- a/toolchain/side-effect-test/generate.mjs +++ b/toolchain/side-effect-test/generate.mjs @@ -1,12 +1,23 @@ import { writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; async function generateTest(packageName, filename) { const exports = await import(packageName); - const names = Object.keys(exports); + const names = Object.keys(exports) + .filter( + (name) => + name !== "default" && name !== "__esModule" && name !== "then", + ) + .sort(); writeFileSync( - filename, + resolve(dirname(fileURLToPath(import.meta.url)), filename), ` +// Generated by toolchain/side-effect-test/generate.mjs +// DO NOT MODIFY THIS FILE MANUALLY +/* eslint-disable */ + import { ${names.join(", ")} } from "${packageName}"; export default () => { diff --git a/toolchain/side-effect-test/src/adb.js b/toolchain/side-effect-test/src/adb.js index e5fc5991..46da3b4e 100644 --- a/toolchain/side-effect-test/src/adb.js +++ b/toolchain/side-effect-test/src/adb.js @@ -1,4 +1,8 @@ +// Generated by toolchain/side-effect-test/generate.mjs +// DO NOT MODIFY THIS FILE MANUALLY +/* eslint-disable */ + import { ADB_DAEMON_DEFAULT_INITIAL_PAYLOAD_SIZE, ADB_DAEMON_VERSION_OMIT_CHECKSUM, ADB_SYNC_MAX_PACKET_SIZE, ASN1_NULL, ASN1_OCTET_STRING, ASN1_OID, ASN1_SEQUENCE, Adb, AdbAuthType, AdbBanner, AdbBannerKey, AdbCommand, AdbDaemonSocket, AdbDaemonSocketController, AdbDaemonTransport, AdbDefaultAuthenticator, AdbDeviceFeatures, AdbFeature, AdbFrameBufferError, AdbFrameBufferForbiddenError, AdbFrameBufferUnsupportedVersionError, AdbFrameBufferV1, AdbFrameBufferV2, AdbNoneProtocolProcessImpl, AdbNoneProtocolPtyProcess, AdbNoneProtocolSubprocessService, AdbPacket, AdbPacketDispatcher, AdbPacketHeader, AdbPacketSerializeStream, AdbPower, AdbReverseError, AdbReverseNotSupportedError, AdbReverseService, AdbServerClient, AdbServerDeviceObserverOwner, AdbServerStream, AdbServerTransport, AdbServiceBase, AdbShellProtocolId, AdbShellProtocolPacket, AdbShellProtocolProcessImpl, AdbShellProtocolPtyProcess, AdbShellProtocolSubprocessService, AdbSubprocessService, AdbSync, AdbSyncDataResponse, AdbSyncEntry2Response, AdbSyncEntryResponse, AdbSyncError, AdbSyncFailResponse, AdbSyncLstatResponse, AdbSyncNumberRequest, AdbSyncOkResponse, AdbSyncRequestId, AdbSyncResponseId, AdbSyncSendV2Flags, AdbSyncSendV2Request, AdbSyncSocket, AdbSyncSocketLocked, AdbSyncStatErrorCode, AdbSyncStatResponse, AdbTcpIpService, AutoResetEvent, FAIL, LinuxFileType, NOOP, Ref, SHA1_DIGEST_INFO, SHA1_DIGEST_LENGTH, ToArrayStream, adbGeneratePublicKey, adbGetPublicKeySize, adbNoneProtocolSpawner, adbShellProtocolSpawner, adbSyncLstat, adbSyncOpenDir, adbSyncOpenDirV1, adbSyncOpenDirV2, adbSyncPull, adbSyncPullGenerator, adbSyncPush, adbSyncPushV1, adbSyncPushV2, adbSyncReadResponse, adbSyncReadResponses, adbSyncStat, adbSyncWriteRequest, calculateBase64EncodedLength, calculateChecksum, createLazyPromise, decodeBase64, decodeUtf8, decodeUtf8Chunked, dirname, encodeBase64, encodeUtf8, escapeArg, framebuffer, getBigUint, hexToNumber, modInverse, powMod, raceSignal, rsaParsePrivateKey, rsaSign, sequenceEqual, setBigUint, splitCommand, toLocalUint8Array, unorderedRemove, unreachable, write4HexDigits } from "@yume-chan/adb"; export default () => { diff --git a/toolchain/side-effect-test/src/scrcpy.js b/toolchain/side-effect-test/src/scrcpy.js index c0abc731..59469c56 100644 --- a/toolchain/side-effect-test/src/scrcpy.js +++ b/toolchain/side-effect-test/src/scrcpy.js @@ -1,4 +1,8 @@ +// Generated by toolchain/side-effect-test/generate.mjs +// DO NOT MODIFY THIS FILE MANUALLY +/* eslint-disable */ + import { AndroidAvcLevel, AndroidAvcProfile, AndroidHevcLevel, AndroidHevcProfile, AndroidKeyCode, AndroidKeyEventAction, AndroidKeyEventMeta, AndroidKeyNames, AndroidMotionEventAction, AndroidMotionEventButton, AndroidScreenPowerMode, DefaultServerPath, EmptyControlMessage, ScrcpyAudioCodec, ScrcpyBackOrScreenOnControlMessage, ScrcpyCaptureOrientation, ScrcpyCodecOptions, ScrcpyControlMessageSerializer, ScrcpyControlMessageType, ScrcpyControlMessageWriter, ScrcpyCrop, ScrcpyDeviceMessageParsers, ScrcpyInjectKeyCodeControlMessage, ScrcpyInjectScrollControlMessage, ScrcpyInjectTextControlMessage, ScrcpyInjectTouchControlMessage, ScrcpyInstanceId, ScrcpyLockOrientation, ScrcpyNewDisplay, ScrcpyOptions1_15, ScrcpyOptions1_15_1, ScrcpyOptions1_16, ScrcpyOptions1_17, ScrcpyOptions1_18, ScrcpyOptions1_19, ScrcpyOptions1_20, ScrcpyOptions1_21, ScrcpyOptions1_22, ScrcpyOptions1_23, ScrcpyOptions1_24, ScrcpyOptions1_25, ScrcpyOptions2_0, ScrcpyOptions2_1, ScrcpyOptions2_1_1, ScrcpyOptions2_2, ScrcpyOptions2_3, ScrcpyOptions2_3_1, ScrcpyOptions2_4, ScrcpyOptions2_5, ScrcpyOptions2_6, ScrcpyOptions2_6_1, ScrcpyOptions2_7, ScrcpyOptions3_0, ScrcpyOptions3_0_1, ScrcpyOptions3_0_2, ScrcpyOptions3_1, ScrcpyOptions3_2, ScrcpyOptions3_3, ScrcpyOptions3_3_1, ScrcpyOptions3_3_2, ScrcpyOptionsLatest, ScrcpyOrientation, ScrcpyPointerId, ScrcpySetClipboardControlMessage, ScrcpySetDisplayPowerControlMessage, ScrcpyUHidCreateControlMessage, ScrcpyUHidOutputDeviceMessage, ScrcpyVideoCodecId, ScrcpyVideoCodecNameMap, ScrcpyVideoSizeImpl, clamp, isScrcpyOptionValue, omit, toScrcpyOptionValue } from "@yume-chan/scrcpy"; export default () => {