chore: fix review comments

This commit is contained in:
Simon Chan 2025-09-10 19:04:38 +08:00
parent e6ad77f672
commit b423c79755
No known key found for this signature in database
GPG key ID: A8B69F750B9BCEDD
29 changed files with 447 additions and 242 deletions

View file

@ -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.

View file

@ -37,7 +37,7 @@ export class ScrcpyOptions1_15 implements ScrcpyOptions<Init> {
readonly value: Required<Init>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}

View file

@ -44,7 +44,7 @@ export class ScrcpyOptions1_17
readonly value: Required<Init>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}

View file

@ -44,7 +44,7 @@ export class ScrcpyOptions1_18
readonly value: Required<Init>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}

View file

@ -43,7 +43,7 @@ export class ScrcpyOptions1_21
readonly value: Required<Init>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -121,7 +121,21 @@ export class ScrcpyOptions1_21
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -43,7 +43,7 @@ export class ScrcpyOptions1_22
readonly value: Required<Init>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -121,7 +121,21 @@ export class ScrcpyOptions1_22
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -43,7 +43,7 @@ export class ScrcpyOptions1_23
readonly value: Required<Init>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -121,7 +121,21 @@ export class ScrcpyOptions1_23
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -43,7 +43,7 @@ export class ScrcpyOptions1_24
readonly value: Required<Init>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -121,7 +121,21 @@ export class ScrcpyOptions1_24
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -43,7 +43,7 @@ export class ScrcpyOptions1_25
readonly value: Required<Init>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -121,7 +121,21 @@ export class ScrcpyOptions1_25
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -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,

View file

@ -44,7 +44,7 @@ export class ScrcpyOptions2_0
readonly value: Required<Init>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -128,7 +128,21 @@ export class ScrcpyOptions2_0
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -44,7 +44,7 @@ export class ScrcpyOptions2_1<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -128,7 +128,21 @@ export class ScrcpyOptions2_1<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -44,7 +44,7 @@ export class ScrcpyOptions2_2<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -132,7 +132,21 @@ export class ScrcpyOptions2_2<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -44,7 +44,7 @@ export class ScrcpyOptions2_3<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -132,7 +132,21 @@ export class ScrcpyOptions2_3<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -48,7 +48,7 @@ export class ScrcpyOptions2_4<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -149,7 +149,21 @@ export class ScrcpyOptions2_4<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -48,7 +48,7 @@ export class ScrcpyOptions2_6<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -153,7 +153,21 @@ export class ScrcpyOptions2_6<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -48,7 +48,7 @@ export class ScrcpyOptions2_7<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -153,7 +153,21 @@ export class ScrcpyOptions2_7<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -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,
},

View file

@ -48,7 +48,7 @@ export class ScrcpyOptions3_0<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -153,7 +153,21 @@ export class ScrcpyOptions3_0<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -48,7 +48,7 @@ export class ScrcpyOptions3_1<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -153,7 +153,21 @@ export class ScrcpyOptions3_1<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -48,7 +48,7 @@ export class ScrcpyOptions3_2<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -153,7 +153,21 @@ export class ScrcpyOptions3_2<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -48,7 +48,7 @@ export class ScrcpyOptions3_3_1<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes() {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -153,7 +153,21 @@ export class ScrcpyOptions3_3_1<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
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,
);
}

View file

@ -24,7 +24,7 @@ export type BipedalGenerator<This, T, A extends unknown[]> = (
this: This,
then: <U>(value: MaybePromiseLike<U>) => Iterable<unknown, U, unknown>,
...args: A
) => Generator<unknown, T, unknown>;
) => Generator<unknown, MaybePromiseLike<T>, unknown>;
/* #__NO_SIDE_EFFECTS__ */
export function bipedal<This, T, A extends unknown[]>(

View file

@ -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<unknown, string, Record<string, unknown>, 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<Uint8Array>;
let deserialize: BipedalFieldDeserializer<
unknown,
Record<string, unknown>
>;
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<string, unknown>
>;
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<string, unknown>
>;
let init: (
value: unknown,
dependencies: Record<string, unknown>,
) => 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<unknown, Record<string, unknown>>;
let init: (
value: unknown,
dependencies: Record<string, unknown>,
) => 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 });
}

View file

@ -25,7 +25,10 @@ export interface Field<T, OmitInit extends string, D, Raw = T>
FieldDeserializer<T, D> {
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<D> {
@ -44,5 +47,8 @@ export interface FieldDeserializer<T, D> {
export interface FieldOptions<T, OmitInit extends string, D, Raw = T> {
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;
}

View file

@ -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;
}
}

View file

@ -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 () => {

View file

@ -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 () => {

View file

@ -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 () => {