refactor: remove TypeScript enum and namespace to improve tree-shaking

This commit is contained in:
Simon Chan 2024-08-30 17:27:01 +08:00
parent b1787e8ef4
commit 2c8912e9ac
No known key found for this signature in database
GPG key ID: A8B69F750B9BCEDD
34 changed files with 744 additions and 619 deletions

View file

@ -1,11 +1,13 @@
import type { AdbFeature } from "./features.js";
export enum AdbBannerKey {
Product = "ro.product.name",
Model = "ro.product.model",
Device = "ro.product.device",
Features = "features",
}
export const AdbBannerKey = {
Product: "ro.product.name",
Model: "ro.product.model",
Device: "ro.product.device",
Features: "features",
} as const;
export type AdbBannerKey = (typeof AdbBannerKey)[keyof typeof AdbBannerKey];
export class AdbBanner {
static parse(banner: string) {

View file

@ -3,9 +3,13 @@ import Struct, { StructEmptyError } from "@yume-chan/struct";
import type { Adb } from "../adb.js";
const Version = new Struct({ littleEndian: true }).uint32("version");
const Version =
/* #__PURE__ */
new Struct({ littleEndian: true }).uint32("version");
export const AdbFrameBufferV1 = new Struct({ littleEndian: true })
export const AdbFrameBufferV1 =
/* #__PURE__ */
new Struct({ littleEndian: true })
.uint32("bpp")
.uint32("size")
.uint32("width")
@ -22,7 +26,9 @@ export const AdbFrameBufferV1 = new Struct({ littleEndian: true })
export type AdbFrameBufferV1 = (typeof AdbFrameBufferV1)["TDeserializeResult"];
export const AdbFrameBufferV2 = new Struct({ littleEndian: true })
export const AdbFrameBufferV2 =
/* #__PURE__ */
new Struct({ littleEndian: true })
.uint32("bpp")
.uint32("colorSpace")
.uint32("size")

View file

@ -14,7 +14,9 @@ export interface AdbForwardListener {
remoteName: string;
}
const AdbReverseStringResponse = new Struct()
const AdbReverseStringResponse =
/* #__PURE__ */
new Struct()
.string("length", { length: 4 })
.string("content", { lengthField: "length", lengthFieldRadix: 16 });
@ -33,9 +35,9 @@ export class AdbReverseNotSupportedError extends AdbReverseError {
}
}
const AdbReverseErrorResponse = new Struct()
.concat(AdbReverseStringResponse)
.postDeserialize((value) => {
const AdbReverseErrorResponse =
/* #__PURE__ */
new Struct().concat(AdbReverseStringResponse).postDeserialize((value) => {
// https://issuetracker.google.com/issues/37066218
// ADB on Android <9 can't create reverse tunnels when connected wirelessly (ADB over Wi-Fi),
// and returns this confusing "more than one device/emulator" error.

View file

@ -19,17 +19,22 @@ import { encodeUtf8 } from "../../../utils/index.js";
import type { AdbSubprocessProtocol } from "./types.js";
export enum AdbShellProtocolId {
Stdin,
Stdout,
Stderr,
Exit,
CloseStdin,
WindowSizeChange,
}
export const AdbShellProtocolId = {
Stdin: 0,
Stdout: 1,
Stderr: 2,
Exit: 3,
CloseStdin: 4,
WindowSizeChange: 5,
} as const;
export type AdbShellProtocolId =
(typeof AdbShellProtocolId)[keyof typeof AdbShellProtocolId];
// This packet format is used in both directions.
export const AdbShellProtocolPacket = new Struct({ littleEndian: true })
export const AdbShellProtocolPacket =
/* #__PURE__ */
new Struct({ littleEndian: true })
.uint8("id", placeholder<AdbShellProtocolId>())
.uint32("length")
.uint8Array("data", { lengthField: "length" });

View file

@ -14,7 +14,9 @@ export interface AdbSyncEntry extends AdbSyncStat {
name: string;
}
export const AdbSyncEntryResponse = new Struct({ littleEndian: true })
export const AdbSyncEntryResponse =
/* #__PURE__ */
new Struct({ littleEndian: true })
.concat(AdbSyncLstatResponse)
.uint32("nameLength")
.string("name", { lengthField: "nameLength" });
@ -22,7 +24,9 @@ export const AdbSyncEntryResponse = new Struct({ littleEndian: true })
export type AdbSyncEntryResponse =
(typeof AdbSyncEntryResponse)["TDeserializeResult"];
export const AdbSyncEntry2Response = new Struct({ littleEndian: true })
export const AdbSyncEntry2Response =
/* #__PURE__ */
new Struct({ littleEndian: true })
.concat(AdbSyncStatResponse)
.uint32("nameLength")
.string("name", { lengthField: "nameLength" });

View file

@ -6,7 +6,9 @@ import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js";
import { AdbSyncResponseId, adbSyncReadResponses } from "./response.js";
import type { AdbSyncSocket } from "./socket.js";
export const AdbSyncDataResponse = new Struct({ littleEndian: true })
export const AdbSyncDataResponse =
/* #__PURE__ */
new Struct({ littleEndian: true })
.uint32("dataLength")
.uint8Array("data", { lengthField: "dataLength" });

View file

@ -25,9 +25,9 @@ export interface AdbSyncPushV1Options {
packetSize?: number;
}
export const AdbSyncOkResponse = new Struct({ littleEndian: true }).uint32(
"unused",
);
export const AdbSyncOkResponse =
/* #__PURE__ */
new Struct({ littleEndian: true }).uint32("unused");
async function pipeFileData(
locked: AdbSyncSocketLocked,
@ -86,19 +86,22 @@ export async function adbSyncPushV1({
}
}
export enum AdbSyncSendV2Flags {
None = 0,
Brotli = 1,
export const AdbSyncSendV2Flags = {
None: 0,
Brotli: 1,
/**
* 2
*/
Lz4 = 1 << 1,
Lz4: 1 << 1,
/**
* 4
*/
Zstd = 1 << 2,
DryRun = 0x80000000,
}
Zstd: 1 << 2,
DryRun: 0x80000000,
} as const;
export type AdbSyncSendV2Flags =
(typeof AdbSyncSendV2Flags)[keyof typeof AdbSyncSendV2Flags];
export interface AdbSyncPushV2Options extends AdbSyncPushV1Options {
/**
@ -110,7 +113,9 @@ export interface AdbSyncPushV2Options extends AdbSyncPushV1Options {
dryRun?: boolean;
}
export const AdbSyncSendV2Request = new Struct({ littleEndian: true })
export const AdbSyncSendV2Request =
/* #__PURE__ */
new Struct({ littleEndian: true })
.uint32("id")
.uint32("mode")
.uint32("flags", placeholder<AdbSyncSendV2Flags>());

View file

@ -4,22 +4,22 @@ import { encodeUtf8 } from "../../utils/index.js";
import { adbSyncEncodeId } from "./response.js";
export namespace AdbSyncRequestId {
export const List = adbSyncEncodeId("LIST");
export const ListV2 = adbSyncEncodeId("LIS2");
export const Send = adbSyncEncodeId("SEND");
export const SendV2 = adbSyncEncodeId("SND2");
export const Lstat = adbSyncEncodeId("STAT");
export const Stat = adbSyncEncodeId("STA2");
export const LstatV2 = adbSyncEncodeId("LST2");
export const Data = adbSyncEncodeId("DATA");
export const Done = adbSyncEncodeId("DONE");
export const Receive = adbSyncEncodeId("RECV");
}
export const AdbSyncRequestId = {
List: adbSyncEncodeId("LIST"),
ListV2: adbSyncEncodeId("LIS2"),
Send: adbSyncEncodeId("SEND"),
SendV2: adbSyncEncodeId("SND2"),
Lstat: adbSyncEncodeId("STAT"),
Stat: adbSyncEncodeId("STA2"),
LstatV2: adbSyncEncodeId("LST2"),
Data: adbSyncEncodeId("DATA"),
Done: adbSyncEncodeId("DONE"),
Receive: adbSyncEncodeId("RECV"),
} as const;
export const AdbSyncNumberRequest = new Struct({ littleEndian: true })
.uint32("id")
.uint32("arg");
export const AdbSyncNumberRequest =
/* #__PURE__ */
new Struct({ littleEndian: true }).uint32("id").uint32("arg");
export interface AdbSyncWritable {
write(buffer: Uint8Array): Promise<void>;

View file

@ -18,27 +18,31 @@ function encodeAsciiUnchecked(value: string): Uint8Array {
* Encode ID to numbers for faster comparison
* @param value A 4-character string
* @returns A 32-bit integer by encoding the string as little-endian
*
* #__NO_SIDE_EFFECTS__
*/
export function adbSyncEncodeId(value: string): number {
const buffer = encodeAsciiUnchecked(value);
return getUint32LittleEndian(buffer, 0);
}
export namespace AdbSyncResponseId {
export const Entry = adbSyncEncodeId("DENT");
export const Entry2 = adbSyncEncodeId("DNT2");
export const Lstat = adbSyncEncodeId("STAT");
export const Stat = adbSyncEncodeId("STA2");
export const Lstat2 = adbSyncEncodeId("LST2");
export const Done = adbSyncEncodeId("DONE");
export const Data = adbSyncEncodeId("DATA");
export const Ok = adbSyncEncodeId("OKAY");
export const Fail = adbSyncEncodeId("FAIL");
}
export const AdbSyncResponseId = {
Entry: adbSyncEncodeId("DENT"),
Entry2: adbSyncEncodeId("DNT2"),
Lstat: adbSyncEncodeId("STAT"),
Stat: adbSyncEncodeId("STA2"),
Lstat2: adbSyncEncodeId("LST2"),
Done: adbSyncEncodeId("DONE"),
Data: adbSyncEncodeId("DATA"),
Ok: adbSyncEncodeId("OKAY"),
Fail: adbSyncEncodeId("FAIL"),
};
export class AdbSyncError extends Error {}
export const AdbSyncFailResponse = new Struct({ littleEndian: true })
export const AdbSyncFailResponse =
/* #__PURE__ */
new Struct({ littleEndian: true })
.uint32("messageLength")
.string("message", { lengthField: "messageLength" })
.postDeserialize((object) => {

View file

@ -5,11 +5,13 @@ import { AdbSyncResponseId, adbSyncReadResponse } from "./response.js";
import type { AdbSyncSocket } from "./socket.js";
// https://github.com/python/cpython/blob/4e581d64b8aff3e2eda99b12f080c877bb78dfca/Lib/stat.py#L36
export enum LinuxFileType {
Directory = 0o04,
File = 0o10,
Link = 0o12,
}
export const LinuxFileType = {
Directory: 0o04,
File: 0o10,
Link: 0o12,
} as const;
export type LinuxFileType = (typeof LinuxFileType)[keyof typeof LinuxFileType];
export interface AdbSyncStat {
mode: number;
@ -24,7 +26,9 @@ export interface AdbSyncStat {
ctime?: bigint;
}
export const AdbSyncLstatResponse = new Struct({ littleEndian: true })
export const AdbSyncLstatResponse =
/* #__PURE__ */
new Struct({ littleEndian: true })
.int32("mode")
.int32("size")
.int32("mtime")
@ -45,31 +49,45 @@ export const AdbSyncLstatResponse = new Struct({ littleEndian: true })
export type AdbSyncLstatResponse =
(typeof AdbSyncLstatResponse)["TDeserializeResult"];
export enum AdbSyncStatErrorCode {
SUCCESS = 0,
EACCES = 13,
EEXIST = 17,
EFAULT = 14,
EFBIG = 27,
EINTR = 4,
EINVAL = 22,
EIO = 5,
EISDIR = 21,
ELOOP = 40,
EMFILE = 24,
ENAMETOOLONG = 36,
ENFILE = 23,
ENOENT = 2,
ENOMEM = 12,
ENOSPC = 28,
ENOTDIR = 20,
EOVERFLOW = 75,
EPERM = 1,
EROFS = 30,
ETXTBSY = 26,
}
export const AdbSyncStatErrorCode = {
SUCCESS: 0,
EACCES: 13,
EEXIST: 17,
EFAULT: 14,
EFBIG: 27,
EINTR: 4,
EINVAL: 22,
EIO: 5,
EISDIR: 21,
ELOOP: 40,
EMFILE: 24,
ENAMETOOLONG: 36,
ENFILE: 23,
ENOENT: 2,
ENOMEM: 12,
ENOSPC: 28,
ENOTDIR: 20,
EOVERFLOW: 75,
EPERM: 1,
EROFS: 30,
ETXTBSY: 26,
} as const;
export const AdbSyncStatResponse = new Struct({ littleEndian: true })
export type AdbSyncStatErrorCode =
(typeof AdbSyncStatErrorCode)[keyof typeof AdbSyncStatErrorCode];
const AdbSyncStatErrorName =
/* #__PURE__ */
Object.fromEntries(
Object.entries(AdbSyncStatErrorCode).map(([key, value]) => [
value,
key,
]),
);
export const AdbSyncStatResponse =
/* #__PURE__ */
new Struct({ littleEndian: true })
.uint32("error", placeholder<AdbSyncStatErrorCode>())
.uint64("dev")
.uint64("ino")
@ -91,7 +109,7 @@ export const AdbSyncStatResponse = new Struct({ littleEndian: true })
})
.postDeserialize((object) => {
if (object.error) {
throw new Error(AdbSyncStatErrorCode[object.error]);
throw new Error(AdbSyncStatErrorName[object.error]);
}
});

View file

@ -1,16 +1,20 @@
import { Consumable, TransformStream } from "@yume-chan/stream-extra";
import Struct from "@yume-chan/struct";
export enum AdbCommand {
Auth = 0x48545541, // 'AUTH'
Close = 0x45534c43, // 'CLSE'
Connect = 0x4e584e43, // 'CNXN'
Okay = 0x59414b4f, // 'OKAY'
Open = 0x4e45504f, // 'OPEN'
Write = 0x45545257, // 'WRTE'
}
export const AdbCommand = {
Auth: 0x48545541, // 'AUTH'
Close: 0x45534c43, // 'CLSE'
Connect: 0x4e584e43, // 'CNXN'
Okay: 0x59414b4f, // 'OKAY'
Open: 0x4e45504f, // 'OPEN'
Write: 0x45545257, // 'WRTE'
} as const;
export const AdbPacketHeader = new Struct({ littleEndian: true })
export type AdbCommand = (typeof AdbCommand)[keyof typeof AdbCommand];
export const AdbPacketHeader =
/* #__PURE__ */
new Struct({ littleEndian: true })
.uint32("command")
.uint32("arg0")
.uint32("arg1")
@ -22,7 +26,9 @@ export type AdbPacketHeader = (typeof AdbPacketHeader)["TDeserializeResult"];
type AdbPacketHeaderInit = (typeof AdbPacketHeader)["TInit"];
export const AdbPacket = new Struct({ littleEndian: true })
export const AdbPacket =
/* #__PURE__ */
new Struct({ littleEndian: true })
.concat(AdbPacketHeader)
.uint8Array("payload", { lengthField: "payloadLength" });

View file

@ -1,13 +1,15 @@
// The order follows
// https://cs.android.com/android/platform/superproject/+/master:packages/modules/adb/transport.cpp;l=77;drc=6d14d35d0241f6fee145f8e54ffd77252e8d29fd
export enum AdbFeature {
ShellV2 = "shell_v2",
Cmd = "cmd",
StatV2 = "stat_v2",
ListV2 = "ls_v2",
FixedPushMkdir = "fixed_push_mkdir",
Abb = "abb",
AbbExec = "abb_exec",
SendReceiveV2 = "sendrecv_v2",
DelayedAck = "delayed_ack",
}
export const AdbFeature = {
ShellV2: "shell_v2",
Cmd: "cmd",
StatV2: "stat_v2",
ListV2: "ls_v2",
FixedPushMkdir: "fixed_push_mkdir",
Abb: "abb",
AbbExec: "abb_exec",
SendReceiveV2: "sendrecv_v2",
DelayedAck: "delayed_ack",
} as const;
export type AdbFeature = (typeof AdbFeature)[keyof typeof AdbFeature];

View file

@ -1,3 +1,4 @@
const [charToIndex, indexToChar, paddingChar] = /* #__PURE__ */ (() => {
// Array is faster than object literal or `Map`
const charToIndex: number[] = [];
const indexToChar: number[] = [];
@ -7,7 +8,11 @@ function addRange(start: string, end: string) {
const charCodeStart = start.charCodeAt(0);
const charCodeEnd = end.charCodeAt(0);
for (let charCode = charCodeStart; charCode <= charCodeEnd; charCode += 1) {
for (
let charCode = charCodeStart;
charCode <= charCodeEnd;
charCode += 1
) {
charToIndex[charCode] = indexToChar.length;
indexToChar.push(charCode);
}
@ -19,6 +24,9 @@ addRange("0", "9");
addRange("+", "+");
addRange("/", "/");
return [charToIndex, indexToChar, paddingChar];
})();
/**
* Calculate the required length of the output buffer for the given input length.
*

View file

@ -1,3 +1,4 @@
/* #__NO_SIDE_EFFECTS__ */
export const NOOP = () => {
// no-op
};

View file

@ -31,7 +31,32 @@ const BatteryDumpFields: Record<
current: { type: "number", field: "current" },
};
const Status = {
Unknown: 1,
Charging: 2,
Discharging: 3,
NotCharging: 4,
Full: 5,
} as const;
const Health = {
Unknown: 1,
Good: 2,
Overheat: 3,
Dead: 4,
OverVoltage: 5,
UnspecifiedFailure: 6,
Cold: 7,
} as const;
const Battery = {
Status,
Health,
};
export class DumpSys extends AdbCommandBase {
static readonly Battery = Battery;
async diskStats() {
const output = await this.adb.subprocess.spawnAndWaitLegacy([
"dumpsys",
@ -113,6 +138,9 @@ export class DumpSys extends AdbCommandBase {
export namespace DumpSys {
export namespace Battery {
export type Status = (typeof Status)[keyof typeof Status];
export type Health = (typeof Health)[keyof typeof Health];
export interface Info {
acPowered: boolean;
usbPowered: boolean;
@ -131,23 +159,5 @@ export namespace DumpSys {
technology?: string;
current?: number;
}
export enum Status {
Unknown = 1,
Charging,
Discharging,
NotCharging,
Full,
}
export enum Health {
Unknown = 1,
Good,
Overheat,
Dead,
OverVoltage,
UnspecifiedFailure,
Cold,
}
}
}

View file

@ -17,30 +17,39 @@ import Struct, { decodeUtf8 } from "@yume-chan/struct";
// so instead of adding to core library, it's implemented here
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/android/log.h;l=141;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d
export enum LogId {
All = -1,
Main,
Radio,
Events,
System,
Crash,
Stats,
Security,
Kernel,
}
export const LogId = {
All: -1,
Main: 0,
Radio: 1,
Events: 2,
System: 3,
Crash: 4,
Stats: 5,
Security: 6,
Kernel: 7,
} as const;
export type LogId = (typeof LogId)[keyof typeof LogId];
const LogIdName =
/* #__PURE__ */
Object.fromEntries(Object.entries(LogId).map(([k, v]) => [v, k]));
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/android/log.h;l=73;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d
export enum AndroidLogPriority {
Unknown,
Default,
Verbose,
Debug,
Info,
Warn,
Error,
Fatal,
Silent,
}
export const AndroidLogPriority = {
Unknown: 0,
Default: 1,
Verbose: 2,
Debug: 3,
Info: 4,
Warn: 5,
Error: 6,
Fatal: 7,
Silent: 8,
} as const;
export type AndroidLogPriority =
(typeof AndroidLogPriority)[keyof typeof AndroidLogPriority];
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;l=140;drc=8dbf3b2bb6b6d1652d9797e477b9abd03278bb79
export const AndroidLogPriorityToCharacter: Record<AndroidLogPriority, string> =
@ -56,16 +65,18 @@ export const AndroidLogPriorityToCharacter: Record<AndroidLogPriority, string> =
[AndroidLogPriority.Silent]: "S",
};
export enum LogcatFormat {
Brief,
Process,
Tag,
Thread,
Raw,
Time,
ThreadTime,
Long,
}
export const LogcatFormat = {
Brief: 0,
Process: 1,
Tag: 2,
Thread: 3,
Raw: 4,
Time: 5,
ThreadTime: 6,
Long: 7,
} as const;
export type LogcatFormat = (typeof LogcatFormat)[keyof typeof LogcatFormat];
export interface LogcatFormatModifiers {
microseconds?: boolean;
@ -88,7 +99,9 @@ export interface LogcatOptions {
const NANOSECONDS_PER_SECOND = BigInt(1e9);
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/log/log_read.h;l=39;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d
export const LoggerEntry = new Struct({ littleEndian: true })
export const LoggerEntry =
/* #__PURE__ */
new Struct({ littleEndian: true })
.uint16("payloadSize")
.uint16("headerSize")
.int32("pid")
@ -385,7 +398,7 @@ export interface LogSize {
export class Logcat extends AdbCommandBase {
static logIdToName(id: LogId): string {
return LogId[id];
return LogIdName[id]!;
}
static logNameToId(name: string): LogId {

View file

@ -92,10 +92,9 @@ class VideoFrameCapturer {
}
}
const VideoFrameCapturerPool = /*@__PURE__*/ new Pool(
() => new VideoFrameCapturer(),
4,
);
const VideoFrameCapturerPool =
/* #__PURE__ */
new Pool(() => new VideoFrameCapturer(), 4);
export interface WebCodecsVideoDecoderInit {
/**

View file

@ -1,3 +1,3 @@
import { Struct } from "@yume-chan/struct";
export const EmptyControlMessage = new Struct().uint8("type");
export const EmptyControlMessage = /* #__PURE__ */ new Struct().uint8("type");

View file

@ -205,7 +205,9 @@ export enum AndroidKeyCode {
AndroidPaste,
}
export const ScrcpyInjectKeyCodeControlMessage = new Struct()
export const ScrcpyInjectKeyCodeControlMessage =
/* #__PURE__ */
new Struct()
.uint8("type")
.uint8("action", placeholder<AndroidKeyEventAction>())
.uint32("keyCode", placeholder<AndroidKeyCode>())

View file

@ -1,6 +1,8 @@
import Struct from "@yume-chan/struct";
export const ScrcpyInjectTextControlMessage = new Struct()
export const ScrcpyInjectTextControlMessage =
/* #__PURE__ */
new Struct()
.uint8("type")
.uint32("length")
.string("text", { lengthField: "length" });

View file

@ -6,7 +6,9 @@ export enum AndroidScreenPowerMode {
Normal = 2,
}
export const ScrcpySetScreenPowerModeControlMessage = new Struct()
export const ScrcpySetScreenPowerModeControlMessage =
/* #__PURE__ */
new Struct()
.uint8("type")
.uint8("mode", placeholder<AndroidScreenPowerMode>());

View file

@ -23,14 +23,18 @@ export const SCRCPY_CONTROL_MESSAGE_TYPES_1_16: readonly ScrcpyControlMessageTyp
/* 10 */ ScrcpyControlMessageType.RotateDevice,
];
export const ScrcpyMediaStreamRawPacket = new Struct()
export const ScrcpyMediaStreamRawPacket =
/* #__PURE__ */
new Struct()
.uint64("pts")
.uint32("size")
.uint8Array("data", { lengthField: "size" });
export const SCRCPY_MEDIA_PACKET_FLAG_CONFIG = 1n << 63n;
export const ScrcpyInjectTouchControlMessage1_16 = new Struct()
export const ScrcpyInjectTouchControlMessage1_16 =
/* #__PURE__ */
new Struct()
.uint8("type")
.uint8("action", placeholder<AndroidMotionEventAction>())
.uint64("pointerId")
@ -46,7 +50,9 @@ export type ScrcpyInjectTouchControlMessage1_16 =
export const ScrcpyBackOrScreenOnControlMessage1_16 = EmptyControlMessage;
export const ScrcpySetClipboardControlMessage1_15 = new Struct()
export const ScrcpySetClipboardControlMessage1_15 =
/* #__PURE__ */
new Struct()
.uint8("type")
.uint32("length")
.string("content", { lengthField: "length" });
@ -54,6 +60,6 @@ export const ScrcpySetClipboardControlMessage1_15 = new Struct()
export type ScrcpySetClipboardControlMessage1_15 =
(typeof ScrcpySetClipboardControlMessage1_15)["TInit"];
export const ScrcpyClipboardDeviceMessage = new Struct()
.uint32("length")
.string("content", { lengthField: "length" });
export const ScrcpyClipboardDeviceMessage =
/* #__PURE__ */
new Struct().uint32("length").string("content", { lengthField: "length" });

View file

@ -8,7 +8,9 @@ export interface ScrcpyScrollController {
): Uint8Array | undefined;
}
export const ScrcpyInjectScrollControlMessage1_16 = new Struct()
export const ScrcpyInjectScrollControlMessage1_16 =
/* #__PURE__ */
new Struct()
.uint8("type")
.uint32("pointerX")
.uint32("pointerY")

View file

@ -42,7 +42,9 @@ export interface ScrcpyOptionsInit1_18
powerOffOnClose?: boolean;
}
export const ScrcpyBackOrScreenOnControlMessage1_18 = new Struct()
export const ScrcpyBackOrScreenOnControlMessage1_18 =
/* #__PURE__ */
new Struct()
.concat(ScrcpyBackOrScreenOnControlMessage1_16)
.uint8("action", placeholder<AndroidKeyEventAction>());

View file

@ -10,7 +10,9 @@ import type { ScrcpyOptionsInit1_18 } from "./1_18.js";
import { ScrcpyOptions1_18 } from "./1_18.js";
import { ScrcpyOptions, toScrcpyOptionValue } from "./types.js";
export const ScrcpyAckClipboardDeviceMessage = new Struct().uint64("sequence");
export const ScrcpyAckClipboardDeviceMessage =
/* #__PURE__ */
new Struct().uint64("sequence");
export interface ScrcpyOptionsInit1_21 extends ScrcpyOptionsInit1_18 {
clipboardAutosync?: boolean;
@ -20,7 +22,9 @@ function toSnakeCase(input: string): string {
return input.replace(/([A-Z])/g, "_$1").toLowerCase();
}
export const ScrcpySetClipboardControlMessage1_21 = new Struct()
export const ScrcpySetClipboardControlMessage1_21 =
/* #__PURE__ */
new Struct()
.uint8("type")
.uint64("sequence")
.int8("paste", placeholder<boolean>())

View file

@ -5,9 +5,9 @@ import {
ScrcpyScrollController1_16,
} from "../1_16/index.js";
export const ScrcpyInjectScrollControlMessage1_22 = new Struct()
.concat(ScrcpyInjectScrollControlMessage1_16)
.int32("buttons");
export const ScrcpyInjectScrollControlMessage1_22 =
/* #__PURE__ */
new Struct().concat(ScrcpyInjectScrollControlMessage1_16).int32("buttons");
export type ScrcpyInjectScrollControlMessage1_22 =
(typeof ScrcpyInjectScrollControlMessage1_22)["TInit"];

View file

@ -27,7 +27,9 @@ const ScrcpySignedFloatFieldDefinition = new NumberFieldDefinition(
ScrcpySignedFloatNumberVariant,
);
export const ScrcpyInjectScrollControlMessage1_25 = new Struct()
export const ScrcpyInjectScrollControlMessage1_25 =
/* #__PURE__ */
new Struct()
.uint8("type", ScrcpyControlMessageType.InjectScroll as const)
.uint32("pointerX")
.uint32("pointerY")

View file

@ -30,7 +30,9 @@ import type {
} from "./types.js";
import { ScrcpyOptions } from "./types.js";
export const ScrcpyInjectTouchControlMessage2_0 = new Struct()
export const ScrcpyInjectTouchControlMessage2_0 =
/* #__PURE__ */
new Struct()
.uint8("type")
.uint8("action", placeholder<AndroidMotionEventAction>())
.uint64("pointerId")

View file

@ -17,6 +17,121 @@ function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
}
export class Consumable<T> {
static readonly WritableStream = class WritableStream<
in T,
> extends NativeWritableStream<Consumable<T>> {
static async write<T>(
writer: WritableStreamDefaultWriter<Consumable<T>>,
value: T,
) {
const consumable = new Consumable(value);
await writer.write(consumable);
await consumable.consumed;
}
constructor(
sink: Consumable.WritableStreamSink<T>,
strategy?: QueuingStrategy<T>,
) {
let wrappedStrategy: QueuingStrategy<Consumable<T>> | undefined;
if (strategy) {
wrappedStrategy = {};
if ("highWaterMark" in strategy) {
wrappedStrategy.highWaterMark = strategy.highWaterMark;
}
if ("size" in strategy) {
wrappedStrategy.size = (chunk) => {
return strategy.size!(
chunk instanceof Consumable ? chunk.value : chunk,
);
};
}
}
super(
{
start(controller) {
return sink.start?.(controller);
},
async write(chunk, controller) {
await chunk.tryConsume((chunk) =>
sink.write?.(chunk, controller),
);
},
abort(reason) {
return sink.abort?.(reason);
},
close() {
return sink.close?.();
},
},
wrappedStrategy,
);
}
};
static readonly ReadableStream = class ReadableStream<
T,
> extends NativeReadableStream<Consumable<T>> {
static async enqueue<T>(
controller: { enqueue: (chunk: Consumable<T>) => void },
chunk: T,
) {
const output = new Consumable(chunk);
controller.enqueue(output);
await output.consumed;
}
constructor(
source: Consumable.ReadableStreamSource<T>,
strategy?: QueuingStrategy<T>,
) {
let wrappedController:
| Consumable.ReadableStreamController<T>
| undefined;
let wrappedStrategy: QueuingStrategy<Consumable<T>> | undefined;
if (strategy) {
wrappedStrategy = {};
if ("highWaterMark" in strategy) {
wrappedStrategy.highWaterMark = strategy.highWaterMark;
}
if ("size" in strategy) {
wrappedStrategy.size = (chunk) => {
return strategy.size!(chunk.value);
};
}
}
super(
{
async start(controller) {
wrappedController = {
async enqueue(chunk) {
await ReadableStream.enqueue(controller, chunk);
},
close() {
controller.close();
},
error(reason) {
controller.error(reason);
},
};
await source.start?.(wrappedController);
},
async pull() {
await source.pull?.(wrappedController!);
},
async cancel(reason) {
await source.cancel?.(reason);
},
},
wrappedStrategy,
);
}
};
readonly #task: Task;
readonly #resolver: PromiseResolver<void>;
@ -76,58 +191,7 @@ export namespace Consumable {
close?(): void | PromiseLike<void>;
}
export class WritableStream<in T> extends NativeWritableStream<
Consumable<T>
> {
static async write<T>(
writer: WritableStreamDefaultWriter<Consumable<T>>,
value: T,
) {
const consumable = new Consumable(value);
await writer.write(consumable);
await consumable.consumed;
}
constructor(
sink: WritableStreamSink<T>,
strategy?: QueuingStrategy<T>,
) {
let wrappedStrategy: QueuingStrategy<Consumable<T>> | undefined;
if (strategy) {
wrappedStrategy = {};
if ("highWaterMark" in strategy) {
wrappedStrategy.highWaterMark = strategy.highWaterMark;
}
if ("size" in strategy) {
wrappedStrategy.size = (chunk) => {
return strategy.size!(
chunk instanceof Consumable ? chunk.value : chunk,
);
};
}
}
super(
{
start(controller) {
return sink.start?.(controller);
},
async write(chunk, controller) {
await chunk.tryConsume((chunk) =>
sink.write?.(chunk, controller),
);
},
abort(reason) {
return sink.abort?.(reason);
},
close() {
return sink.close?.();
},
},
wrappedStrategy,
);
}
}
export type WritableStream<in T> = typeof Consumable.WritableStream<T>;
export interface ReadableStreamController<T> {
enqueue(chunk: T): Promise<void>;
@ -145,61 +209,5 @@ export namespace Consumable {
cancel?(reason: unknown): void | PromiseLike<void>;
}
export class ReadableStream<T> extends NativeReadableStream<Consumable<T>> {
static async enqueue<T>(
controller: { enqueue: (chunk: Consumable<T>) => void },
chunk: T,
) {
const output = new Consumable(chunk);
controller.enqueue(output);
await output.consumed;
}
constructor(
source: ReadableStreamSource<T>,
strategy?: QueuingStrategy<T>,
) {
let wrappedController: ReadableStreamController<T> | undefined;
let wrappedStrategy: QueuingStrategy<Consumable<T>> | undefined;
if (strategy) {
wrappedStrategy = {};
if ("highWaterMark" in strategy) {
wrappedStrategy.highWaterMark = strategy.highWaterMark;
}
if ("size" in strategy) {
wrappedStrategy.size = (chunk) => {
return strategy.size!(chunk.value);
};
}
}
super(
{
async start(controller) {
wrappedController = {
async enqueue(chunk) {
await ReadableStream.enqueue(controller, chunk);
},
close() {
controller.close();
},
error(reason) {
controller.error(reason);
},
};
await source.start?.(wrappedController);
},
async pull() {
await source.pull?.(wrappedController!);
},
async cancel(reason) {
await source.cancel?.(reason);
},
},
wrappedStrategy,
);
}
}
export type ReadableStream<T> = typeof Consumable.ReadableStream<T>;
}

View file

@ -0,0 +1,90 @@
import { Consumable } from "./consumable.js";
import type { MaybeConsumable } from "./maybe-consumable.js";
import type {
QueuingStrategy,
WritableStreamDefaultController,
} from "./stream.js";
import {
WritableStream as NativeWritableStream,
TransformStream,
} from "./stream.js";
export function getValue<T>(value: MaybeConsumable<T>): T {
return value instanceof Consumable ? value.value : value;
}
export function tryConsume<T, R>(
value: T,
callback: (value: T extends Consumable<infer U> ? U : T) => R,
): R {
if (value instanceof Consumable) {
return value.tryConsume(callback);
} else {
return callback(value as never);
}
}
export class UnwrapStream<T> extends TransformStream<MaybeConsumable<T>, T> {
constructor() {
super({
transform(chunk, controller) {
tryConsume(chunk, (chunk) => {
controller.enqueue(chunk as T);
});
},
});
}
}
export interface WritableStreamSink<in T> {
start?(
controller: WritableStreamDefaultController,
): void | PromiseLike<void>;
write?(
chunk: T,
controller: WritableStreamDefaultController,
): void | PromiseLike<void>;
abort?(reason: unknown): void | PromiseLike<void>;
close?(): void | PromiseLike<void>;
}
export class WritableStream<in T> extends NativeWritableStream<
MaybeConsumable<T>
> {
constructor(sink: WritableStreamSink<T>, strategy?: QueuingStrategy<T>) {
let wrappedStrategy: QueuingStrategy<MaybeConsumable<T>> | undefined;
if (strategy) {
wrappedStrategy = {};
if ("highWaterMark" in strategy) {
wrappedStrategy.highWaterMark = strategy.highWaterMark;
}
if ("size" in strategy) {
wrappedStrategy.size = (chunk) => {
return strategy.size!(
chunk instanceof Consumable ? chunk.value : chunk,
);
};
}
}
super(
{
start(controller) {
return sink.start?.(controller);
},
async write(chunk, controller) {
await tryConsume(chunk, (chunk) =>
sink.write?.(chunk as T, controller),
);
},
abort(reason) {
return sink.abort?.(reason);
},
close() {
return sink.close?.();
},
},
wrappedStrategy,
);
}
}

View file

@ -1,101 +1,5 @@
import { Consumable } from "./consumable.js";
import type {
QueuingStrategy,
WritableStreamDefaultController,
} from "./stream.js";
import {
WritableStream as NativeWritableStream,
TransformStream,
} from "./stream.js";
import type { Consumable } from "./consumable.js";
export type MaybeConsumable<T> = T | Consumable<T>;
export namespace MaybeConsumable {
export function getValue<T>(value: MaybeConsumable<T>): T {
return value instanceof Consumable ? value.value : value;
}
export function tryConsume<T, R>(
value: T,
callback: (value: T extends Consumable<infer U> ? U : T) => R,
): R {
if (value instanceof Consumable) {
return value.tryConsume(callback);
} else {
return callback(value as never);
}
}
export class UnwrapStream<T> extends TransformStream<
MaybeConsumable<T>,
T
> {
constructor() {
super({
transform(chunk, controller) {
MaybeConsumable.tryConsume(chunk, (chunk) => {
controller.enqueue(chunk as T);
});
},
});
}
}
export interface WritableStreamSink<in T> {
start?(
controller: WritableStreamDefaultController,
): void | PromiseLike<void>;
write?(
chunk: T,
controller: WritableStreamDefaultController,
): void | PromiseLike<void>;
abort?(reason: unknown): void | PromiseLike<void>;
close?(): void | PromiseLike<void>;
}
export class WritableStream<in T> extends NativeWritableStream<
MaybeConsumable<T>
> {
constructor(
sink: WritableStreamSink<T>,
strategy?: QueuingStrategy<T>,
) {
let wrappedStrategy:
| QueuingStrategy<MaybeConsumable<T>>
| undefined;
if (strategy) {
wrappedStrategy = {};
if ("highWaterMark" in strategy) {
wrappedStrategy.highWaterMark = strategy.highWaterMark;
}
if ("size" in strategy) {
wrappedStrategy.size = (chunk) => {
return strategy.size!(
chunk instanceof Consumable ? chunk.value : chunk,
);
};
}
}
super(
{
start(controller) {
return sink.start?.(controller);
},
async write(chunk, controller) {
await MaybeConsumable.tryConsume(chunk, (chunk) =>
sink.write?.(chunk as T, controller),
);
},
abort(reason) {
return sink.abort?.(reason);
},
close() {
return sink.close?.();
},
},
wrappedStrategy,
);
}
}
}
export * as MaybeConsumable from "./maybe-consumable-ns.js";

View file

@ -32,15 +32,13 @@ interface GlobalExtension {
TransformStream: typeof TransformStreamType;
}
const Global = globalThis as unknown as GlobalExtension;
export const AbortController = Global.AbortController;
export type ReadableStream<out T> = ReadableStreamType<T>;
export const ReadableStream = Global.ReadableStream;
export type WritableStream<in T> = WritableStreamType<T>;
export const WritableStream = Global.WritableStream;
export type TransformStream<I, O> = TransformStreamType<I, O>;
export const TransformStream = Global.TransformStream;
export const {
AbortController,
ReadableStream,
WritableStream,
TransformStream,
} = globalThis as unknown as GlobalExtension;

View file

@ -33,7 +33,7 @@ class MockDeserializationStream implements ExactReadable {
describe("Struct", () => {
describe(".constructor", () => {
it("should initialize fields", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
assert.deepStrictEqual(struct.options, StructDefaultOptions);
assert.strictEqual(struct.size, 0);
});
@ -82,7 +82,7 @@ describe("Struct", () => {
}
it("should push a field and update size", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
const field1 = "foo";
const fieldDefinition1 = new MockFieldDefinition(4);
@ -104,7 +104,7 @@ describe("Struct", () => {
});
it("should throw an error if field name already exists", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
const fieldName = "foo";
struct.field(fieldName, new MockFieldDefinition(4));
assert.throws(() => {
@ -115,7 +115,7 @@ describe("Struct", () => {
describe("#number", () => {
it("`int8` should append an `int8` field", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
struct.int8("foo");
assert.strictEqual(struct.size, 1);
@ -125,7 +125,7 @@ describe("Struct", () => {
});
it("`uint8` should append an `uint8` field", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
struct.uint8("foo");
assert.strictEqual(struct.size, 1);
@ -135,7 +135,7 @@ describe("Struct", () => {
});
it("`int16` should append an `int16` field", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
struct.int16("foo");
assert.strictEqual(struct.size, 2);
@ -145,7 +145,7 @@ describe("Struct", () => {
});
it("`uint16` should append an `uint16` field", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
struct.uint16("foo");
assert.strictEqual(struct.size, 2);
@ -155,7 +155,7 @@ describe("Struct", () => {
});
it("`int32` should append an `int32` field", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
struct.int32("foo");
assert.strictEqual(struct.size, 4);
@ -165,7 +165,7 @@ describe("Struct", () => {
});
it("`uint32` should append an `uint32` field", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
struct.uint32("foo");
assert.strictEqual(struct.size, 4);
@ -175,7 +175,7 @@ describe("Struct", () => {
});
it("`int64` should append an `int64` field", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
struct.int64("foo");
assert.strictEqual(struct.size, 8);
@ -185,7 +185,7 @@ describe("Struct", () => {
});
it("`uint64` should append an `uint64` field", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
struct.uint64("foo");
assert.strictEqual(struct.size, 8);
@ -197,7 +197,7 @@ describe("Struct", () => {
describe("#uint8ArrayLike", () => {
describe("FixedLengthBufferLikeFieldDefinition", () => {
it("`#uint8Array` with fixed length", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
struct.uint8Array("foo", { length: 10 });
assert.strictEqual(struct.size, 10);
@ -214,7 +214,7 @@ describe("Struct", () => {
});
it("`#string` with fixed length", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
struct.string("foo", { length: 10 });
assert.strictEqual(struct.size, 10);
@ -233,7 +233,9 @@ describe("Struct", () => {
describe("VariableLengthBufferLikeFieldDefinition", () => {
it("`#uint8Array` with variable length", () => {
const struct = new Struct().int8("barLength");
const struct = /* #__PURE__ */ new Struct().int8(
"barLength",
);
assert.strictEqual(struct.size, 1);
struct.uint8Array("bar", { lengthField: "barLength" });
@ -255,7 +257,9 @@ describe("Struct", () => {
});
it("`#string` with variable length", () => {
const struct = new Struct().int8("barLength");
const struct = /* #__PURE__ */ new Struct().int8(
"barLength",
);
assert.strictEqual(struct.size, 1);
struct.string("bar", { lengthField: "barLength" });
@ -280,9 +284,11 @@ describe("Struct", () => {
describe("#concat", () => {
it("should append all fields from other struct", () => {
const sub = new Struct().int16("int16").int32("int32");
const sub = /* #__PURE__ */ new Struct()
.int16("int16")
.int32("int32");
const struct = new Struct()
const struct = /* #__PURE__ */ new Struct()
.int8("int8")
.concat(sub)
.int64("int64");
@ -312,7 +318,9 @@ describe("Struct", () => {
describe("#deserialize", () => {
it("should deserialize without dynamic size fields", () => {
const struct = new Struct().int8("foo").int16("bar");
const struct = /* #__PURE__ */ new Struct()
.int8("foo")
.int16("bar");
const stream = new MockDeserializationStream();
stream.readExactly.mock.mockImplementationOnce(
@ -339,7 +347,7 @@ describe("Struct", () => {
});
it("should deserialize with dynamic size fields", () => {
const struct = new Struct()
const struct = /* #__PURE__ */ new Struct()
.int8("fooLength")
.uint8Array("foo", { lengthField: "fooLength" });
@ -376,7 +384,10 @@ describe("Struct", () => {
describe("#extra", () => {
it("should accept plain field", () => {
const struct = new Struct().extra({ foo: 42, bar: true });
const struct = /* #__PURE__ */ new Struct().extra({
foo: 42,
bar: true,
});
const stream = new MockDeserializationStream();
const result = struct.deserialize(stream);
@ -411,7 +422,7 @@ describe("Struct", () => {
});
it("should accept accessors", () => {
const struct = new Struct().extra({
const struct = /* #__PURE__ */ new Struct().extra({
get foo() {
return 42;
},
@ -438,7 +449,7 @@ describe("Struct", () => {
describe("#postDeserialize", () => {
it("can throw errors", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
const callback = mock.fn(() => {
throw new Error("mock");
});
@ -450,7 +461,7 @@ describe("Struct", () => {
});
it("can replace return value", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
const callback = mock.fn(() => "mock");
struct.postDeserialize(callback);
@ -461,7 +472,7 @@ describe("Struct", () => {
});
it("can return nothing", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
const callback = mock.fn();
struct.postDeserialize(callback);
@ -473,7 +484,7 @@ describe("Struct", () => {
});
it("should overwrite callback", () => {
const struct = new Struct();
const struct = /* #__PURE__ */ new Struct();
const callback1 = mock.fn();
struct.postDeserialize(callback1);
@ -492,7 +503,9 @@ describe("Struct", () => {
describe("#serialize", () => {
it("should serialize without dynamic size fields", () => {
const struct = new Struct().int8("foo").int16("bar");
const struct = /* #__PURE__ */ new Struct()
.int8("foo")
.int16("bar");
const result = new Uint8Array(
struct.serialize({ foo: 0x42, bar: 0x1024 }),
@ -505,7 +518,7 @@ describe("Struct", () => {
});
it("should serialize with dynamic size fields", () => {
const struct = new Struct()
const struct = /* #__PURE__ */ new Struct()
.int8("fooLength")
.uint8Array("foo", { lengthField: "fooLength" });

View file

@ -82,9 +82,10 @@ interface GlobalExtension {
const { TextEncoder, TextDecoder } = globalThis as unknown as GlobalExtension;
const SharedEncoder = new TextEncoder();
const SharedDecoder = new TextDecoder();
const SharedEncoder = /* #__PURE__ */ new TextEncoder();
const SharedDecoder = /* #__PURE__ */ new TextDecoder();
/* #__NO_SIDE_EFFECTS__ */
export function encodeUtf8(input: string): Uint8Array {
return SharedEncoder.encode(input);
}