refactor: use ES private fields to replace TypeScript private accessors

This commit is contained in:
Simon Chan 2023-05-25 23:00:08 +08:00
parent b87d76ca6e
commit d286a40c42
No known key found for this signature in database
GPG key ID: A8B69F750B9BCEDD
28 changed files with 434 additions and 442 deletions

View file

@ -48,24 +48,24 @@ export class AdbBanner {
return new AdbBanner(product, model, device, features); return new AdbBanner(product, model, device, features);
} }
private _product: string | undefined; #product: string | undefined;
public get product() { public get product() {
return this._product; return this.#product;
} }
private _model: string | undefined; #model: string | undefined;
public get model() { public get model() {
return this._model; return this.#model;
} }
private _device: string | undefined; #device: string | undefined;
public get device() { public get device() {
return this._device; return this.#device;
} }
private _features: AdbFeature[] = []; #features: AdbFeature[] = [];
public get features() { public get features() {
return this._features; return this.#features;
} }
public constructor( public constructor(
@ -74,9 +74,9 @@ export class AdbBanner {
device: string | undefined, device: string | undefined,
features: AdbFeature[] features: AdbFeature[]
) { ) {
this._product = product; this.#product = product;
this._model = model; this.#model = model;
this._device = device; this.#device = device;
this._features = features; this.#features = features;
} }
} }

View file

@ -5,7 +5,7 @@ import { BufferedReadableStream } from "@yume-chan/stream-extra";
import Struct, { ExactReadableEndedError } from "@yume-chan/struct"; import Struct, { ExactReadableEndedError } from "@yume-chan/struct";
import type { Adb, AdbIncomingSocketHandler } from "../adb.js"; import type { Adb, AdbIncomingSocketHandler } from "../adb.js";
import { decodeUtf8 } from "../utils/index.js"; import { decodeUtf8, hexToNumber } from "../utils/index.js";
export interface AdbForwardListener { export interface AdbForwardListener {
deviceSerial: string; deviceSerial: string;
@ -47,10 +47,15 @@ const AdbReverseErrorResponse = new Struct()
} }
}); });
async function readString(stream: BufferedReadableStream, length: number) {
const buffer = await stream.readExactly(length);
return decodeUtf8(buffer);
}
export class AdbReverseCommand extends AutoDisposable { export class AdbReverseCommand extends AutoDisposable {
protected adb: Adb; protected adb: Adb;
private readonly _deviceAddressToLocalAddress = new Map<string, string>(); readonly #deviceAddressToLocalAddress = new Map<string, string>();
public constructor(adb: Adb) { public constructor(adb: Adb) {
super(); super();
@ -58,19 +63,14 @@ export class AdbReverseCommand extends AutoDisposable {
this.adb = adb; this.adb = adb;
} }
private async createBufferedStream(service: string) { protected async createBufferedStream(service: string) {
const socket = await this.adb.createSocket(service); const socket = await this.adb.createSocket(service);
return new BufferedReadableStream(socket.readable); return new BufferedReadableStream(socket.readable);
} }
private async readString(stream: BufferedReadableStream, length: number) { protected async sendRequest(service: string) {
const buffer = await stream.readExactly(length);
return decodeUtf8(buffer);
}
private async sendRequest(service: string) {
const stream = await this.createBufferedStream(service); const stream = await this.createBufferedStream(service);
const success = (await this.readString(stream, 4)) === "OKAY"; const success = (await readString(stream, 4)) === "OKAY";
if (!success) { if (!success) {
await AdbReverseErrorResponse.deserialize(stream); await AdbReverseErrorResponse.deserialize(stream);
} }
@ -109,9 +109,8 @@ export class AdbReverseCommand extends AutoDisposable {
if (deviceAddress.startsWith("tcp:")) { if (deviceAddress.startsWith("tcp:")) {
const position = stream.position; const position = stream.position;
try { try {
const lengthString = await this.readString(stream, 4); const length = hexToNumber(await stream.readExactly(4));
const length = Number.parseInt(lengthString, 16); const port = await readString(stream, length);
const port = await this.readString(stream, length);
deviceAddress = `tcp:${Number.parseInt(port, 10)}`; deviceAddress = `tcp:${Number.parseInt(port, 10)}`;
} catch (e) { } catch (e) {
if ( if (
@ -150,7 +149,7 @@ export class AdbReverseCommand extends AutoDisposable {
try { try {
deviceAddress = await this.addExternal(deviceAddress, localAddress); deviceAddress = await this.addExternal(deviceAddress, localAddress);
this._deviceAddressToLocalAddress.set(deviceAddress, localAddress); this.#deviceAddressToLocalAddress.set(deviceAddress, localAddress);
return deviceAddress; return deviceAddress;
} catch (e) { } catch (e) {
await this.adb.transport.removeReverseTunnel(localAddress); await this.adb.transport.removeReverseTunnel(localAddress);
@ -160,7 +159,7 @@ export class AdbReverseCommand extends AutoDisposable {
public async remove(deviceAddress: string): Promise<void> { public async remove(deviceAddress: string): Promise<void> {
const localAddress = const localAddress =
this._deviceAddressToLocalAddress.get(deviceAddress); this.#deviceAddressToLocalAddress.get(deviceAddress);
if (localAddress) { if (localAddress) {
await this.adb.transport.removeReverseTunnel(localAddress); await this.adb.transport.removeReverseTunnel(localAddress);
} }
@ -172,7 +171,7 @@ export class AdbReverseCommand extends AutoDisposable {
public async removeAll(): Promise<void> { public async removeAll(): Promise<void> {
await this.adb.transport.clearReverseTunnels(); await this.adb.transport.clearReverseTunnels();
this._deviceAddressToLocalAddress.clear(); this.#deviceAddressToLocalAddress.clear();
await this.sendRequest(`reverse:killforward-all`); await this.sendRequest(`reverse:killforward-all`);

View file

@ -31,50 +31,50 @@ export class AdbSubprocessNoneProtocol implements AdbSubprocessProtocol {
); );
} }
private readonly _socket: AdbSocket; readonly #socket: AdbSocket;
private readonly _duplex: DuplexStreamFactory<Uint8Array, Uint8Array>; readonly #duplex: DuplexStreamFactory<Uint8Array, Uint8Array>;
// Legacy shell forwards all data to stdin. // Legacy shell forwards all data to stdin.
public get stdin() { public get stdin() {
return this._socket.writable; return this.#socket.writable;
} }
private _stdout: ReadableStream<Uint8Array>; #stdout: ReadableStream<Uint8Array>;
/** /**
* Legacy shell mixes stdout and stderr. * Legacy shell mixes stdout and stderr.
*/ */
public get stdout() { public get stdout() {
return this._stdout; return this.#stdout;
} }
private _stderr: ReadableStream<Uint8Array>; #stderr: ReadableStream<Uint8Array>;
/** /**
* `stderr` will always be empty. * `stderr` will always be empty.
*/ */
public get stderr() { public get stderr() {
return this._stderr; return this.#stderr;
} }
private _exit: Promise<number>; #exit: Promise<number>;
public get exit() { public get exit() {
return this._exit; return this.#exit;
} }
public constructor(socket: AdbSocket) { public constructor(socket: AdbSocket) {
this._socket = socket; this.#socket = socket;
// Link `stdout`, `stderr` and `stdin` together, // Link `stdout`, `stderr` and `stdin` together,
// so closing any of them will close the others. // so closing any of them will close the others.
this._duplex = new DuplexStreamFactory<Uint8Array, Uint8Array>({ this.#duplex = new DuplexStreamFactory<Uint8Array, Uint8Array>({
close: async () => { close: async () => {
await this._socket.close(); await this.#socket.close();
}, },
}); });
this._stdout = this._duplex.wrapReadable(this._socket.readable); this.#stdout = this.#duplex.wrapReadable(this.#socket.readable);
this._stderr = this._duplex.wrapReadable(new ReadableStream()); this.#stderr = this.#duplex.wrapReadable(new ReadableStream());
this._exit = this._duplex.closed.then(() => 0); this.#exit = this.#duplex.closed.then(() => 0);
} }
public resize() { public resize() {
@ -82,6 +82,6 @@ export class AdbSubprocessNoneProtocol implements AdbSubprocessProtocol {
} }
public kill() { public kill() {
return this._duplex.close(); return this.#duplex.close();
} }
} }

View file

@ -125,33 +125,33 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
); );
} }
private readonly _socket: AdbSocket; readonly #socket: AdbSocket;
private _socketWriter: WritableStreamDefaultWriter< #socketWriter: WritableStreamDefaultWriter<
Consumable<AdbShellProtocolPacketInit> Consumable<AdbShellProtocolPacketInit>
>; >;
private _stdin: WritableStream<Consumable<Uint8Array>>; #stdin: WritableStream<Consumable<Uint8Array>>;
public get stdin() { public get stdin() {
return this._stdin; return this.#stdin;
} }
private _stdout: ReadableStream<Uint8Array>; #stdout: ReadableStream<Uint8Array>;
public get stdout() { public get stdout() {
return this._stdout; return this.#stdout;
} }
private _stderr: ReadableStream<Uint8Array>; #stderr: ReadableStream<Uint8Array>;
public get stderr() { public get stderr() {
return this._stderr; return this.#stderr;
} }
private readonly _exit = new PromiseResolver<number>(); readonly #exit = new PromiseResolver<number>();
public get exit() { public get exit() {
return this._exit.promise; return this.#exit.promise;
} }
public constructor(socket: AdbSocket) { public constructor(socket: AdbSocket) {
this._socket = socket; this.#socket = socket;
// Check this image to help you understand the stream graph // Check this image to help you understand the stream graph
// cspell: disable-next-line // cspell: disable-next-line
@ -159,10 +159,10 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
let stdoutController!: PushReadableStreamController<Uint8Array>; let stdoutController!: PushReadableStreamController<Uint8Array>;
let stderrController!: PushReadableStreamController<Uint8Array>; let stderrController!: PushReadableStreamController<Uint8Array>;
this._stdout = new PushReadableStream<Uint8Array>((controller) => { this.#stdout = new PushReadableStream<Uint8Array>((controller) => {
stdoutController = controller; stdoutController = controller;
}); });
this._stderr = new PushReadableStream<Uint8Array>((controller) => { this.#stderr = new PushReadableStream<Uint8Array>((controller) => {
stderrController = controller; stderrController = controller;
}); });
@ -173,7 +173,7 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
write: async (chunk) => { write: async (chunk) => {
switch (chunk.id) { switch (chunk.id) {
case AdbShellProtocolId.Exit: case AdbShellProtocolId.Exit:
this._exit.resolve(chunk.data[0]!); this.#exit.resolve(chunk.data[0]!);
break; break;
case AdbShellProtocolId.Stdout: case AdbShellProtocolId.Stdout:
await stdoutController.enqueue(chunk.data); await stdoutController.enqueue(chunk.data);
@ -189,8 +189,8 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
() => { () => {
stdoutController.close(); stdoutController.close();
stderrController.close(); stderrController.close();
if (this._exit.state !== "resolved") { if (this.#exit.state !== "resolved") {
this._exit.reject( this.#exit.reject(
new Error("Socket ended without exit message") new Error("Socket ended without exit message")
); );
} }
@ -216,16 +216,16 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
) )
.pipeTo(socket.writable); .pipeTo(socket.writable);
this._stdin = pipeFrom( this.#stdin = pipeFrom(
multiplexer.createWriteable(), multiplexer.createWriteable(),
new StdinSerializeStream() new StdinSerializeStream()
); );
this._socketWriter = multiplexer.createWriteable().getWriter(); this.#socketWriter = multiplexer.createWriteable().getWriter();
} }
public async resize(rows: number, cols: number) { public async resize(rows: number, cols: number) {
await ConsumableWritableStream.write(this._socketWriter, { await ConsumableWritableStream.write(this.#socketWriter, {
id: AdbShellProtocolId.WindowSizeChange, id: AdbShellProtocolId.WindowSizeChange,
data: encodeUtf8( data: encodeUtf8(
// The "correct" format is `${rows}x${cols},${x_pixels}x${y_pixels}` // The "correct" format is `${rows}x${cols},${x_pixels}x${y_pixels}`
@ -237,6 +237,6 @@ export class AdbSubprocessShellProtocol implements AdbSubprocessProtocol {
} }
public kill() { public kill() {
return this._socket.close(); return this.#socket.close();
} }
} }

View file

@ -13,16 +13,14 @@ import type { AdbSocket } from "../../adb.js";
import { AutoResetEvent } from "../../utils/index.js"; import { AutoResetEvent } from "../../utils/index.js";
export class AdbSyncSocketLocked implements AsyncExactReadable { export class AdbSyncSocketLocked implements AsyncExactReadable {
private readonly _writer: WritableStreamDefaultWriter< readonly #writer: WritableStreamDefaultWriter<Consumable<Uint8Array>>;
Consumable<Uint8Array> readonly #readable: BufferedReadableStream;
>; readonly #socketLock: AutoResetEvent;
private readonly _readable: BufferedReadableStream; readonly #writeLock = new AutoResetEvent();
private readonly _socketLock: AutoResetEvent; readonly #combiner: BufferCombiner;
private readonly _writeLock = new AutoResetEvent();
private readonly _combiner: BufferCombiner;
public get position() { public get position() {
return this._readable.position; return this.#readable.position;
} }
public constructor( public constructor(
@ -31,71 +29,71 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
bufferSize: number, bufferSize: number,
lock: AutoResetEvent lock: AutoResetEvent
) { ) {
this._writer = writer; this.#writer = writer;
this._readable = readable; this.#readable = readable;
this._socketLock = lock; this.#socketLock = lock;
this._combiner = new BufferCombiner(bufferSize); this.#combiner = new BufferCombiner(bufferSize);
} }
private async writeInnerStream(buffer: Uint8Array) { private async writeInnerStream(buffer: Uint8Array) {
await ConsumableWritableStream.write(this._writer, buffer); await ConsumableWritableStream.write(this.#writer, buffer);
} }
public async flush() { public async flush() {
try { try {
await this._writeLock.wait(); await this.#writeLock.wait();
const buffer = this._combiner.flush(); const buffer = this.#combiner.flush();
if (buffer) { if (buffer) {
await this.writeInnerStream(buffer); await this.writeInnerStream(buffer);
} }
} finally { } finally {
this._writeLock.notifyOne(); this.#writeLock.notifyOne();
} }
} }
public async write(data: Uint8Array) { public async write(data: Uint8Array) {
try { try {
await this._writeLock.wait(); await this.#writeLock.wait();
for (const buffer of this._combiner.push(data)) { for (const buffer of this.#combiner.push(data)) {
await this.writeInnerStream(buffer); await this.writeInnerStream(buffer);
} }
} finally { } finally {
this._writeLock.notifyOne(); this.#writeLock.notifyOne();
} }
} }
public async readExactly(length: number) { public async readExactly(length: number) {
await this.flush(); await this.flush();
return await this._readable.readExactly(length); return await this.#readable.readExactly(length);
} }
public release(): void { public release(): void {
this._combiner.flush(); this.#combiner.flush();
this._socketLock.notifyOne(); this.#socketLock.notifyOne();
} }
} }
export class AdbSyncSocket { export class AdbSyncSocket {
private _lock = new AutoResetEvent(); readonly #lock = new AutoResetEvent();
private _socket: AdbSocket; readonly #socket: AdbSocket;
private _locked: AdbSyncSocketLocked; readonly #locked: AdbSyncSocketLocked;
public constructor(socket: AdbSocket, bufferSize: number) { public constructor(socket: AdbSocket, bufferSize: number) {
this._socket = socket; this.#socket = socket;
this._locked = new AdbSyncSocketLocked( this.#locked = new AdbSyncSocketLocked(
socket.writable.getWriter(), socket.writable.getWriter(),
new BufferedReadableStream(socket.readable), new BufferedReadableStream(socket.readable),
bufferSize, bufferSize,
this._lock this.#lock
); );
} }
public async lock() { public async lock() {
await this._lock.wait(); await this.#lock.wait();
return this._locked; return this.#locked;
} }
public async close() { public async close() {
await this._socket.close(); await this.#socket.close();
} }
} }

View file

@ -42,30 +42,30 @@ export class AdbSync extends AutoDisposable {
protected _adb: Adb; protected _adb: Adb;
protected _socket: AdbSyncSocket; protected _socket: AdbSyncSocket;
private _supportsStat: boolean; readonly #supportsStat: boolean;
private _supportsListV2: boolean; readonly #supportsListV2: boolean;
private _fixedPushMkdir: boolean; readonly #fixedPushMkdir: boolean;
private _supportsSendReceiveV2: boolean; readonly #supportsSendReceiveV2: boolean;
private _needPushMkdirWorkaround: boolean; readonly #needPushMkdirWorkaround: boolean;
public get supportsStat(): boolean { public get supportsStat(): boolean {
return this._supportsStat; return this.#supportsStat;
} }
public get supportsListV2(): boolean { public get supportsListV2(): boolean {
return this._supportsListV2; return this.#supportsListV2;
} }
public get fixedPushMkdir(): boolean { public get fixedPushMkdir(): boolean {
return this._fixedPushMkdir; return this.#fixedPushMkdir;
} }
public get supportsSendReceiveV2(): boolean { public get supportsSendReceiveV2(): boolean {
return this._supportsSendReceiveV2; return this.#supportsSendReceiveV2;
} }
public get needPushMkdirWorkaround(): boolean { public get needPushMkdirWorkaround(): boolean {
return this._needPushMkdirWorkaround; return this.#needPushMkdirWorkaround;
} }
public constructor(adb: Adb, socket: AdbSocket) { public constructor(adb: Adb, socket: AdbSocket) {
@ -74,14 +74,14 @@ export class AdbSync extends AutoDisposable {
this._adb = adb; this._adb = adb;
this._socket = new AdbSyncSocket(socket, adb.maxPayloadSize); this._socket = new AdbSyncSocket(socket, adb.maxPayloadSize);
this._supportsStat = adb.supportsFeature(AdbFeature.StatV2); this.#supportsStat = adb.supportsFeature(AdbFeature.StatV2);
this._supportsListV2 = adb.supportsFeature(AdbFeature.ListV2); this.#supportsListV2 = adb.supportsFeature(AdbFeature.ListV2);
this._fixedPushMkdir = adb.supportsFeature(AdbFeature.FixedPushMkdir); this.#fixedPushMkdir = adb.supportsFeature(AdbFeature.FixedPushMkdir);
this._supportsSendReceiveV2 = adb.supportsFeature( this.#supportsSendReceiveV2 = adb.supportsFeature(
AdbFeature.SendReceiveV2 AdbFeature.SendReceiveV2
); );
// https://android.googlesource.com/platform/packages/modules/adb/+/91768a57b7138166e0a3d11f79cd55909dda7014/client/file_sync_client.cpp#1361 // https://android.googlesource.com/platform/packages/modules/adb/+/91768a57b7138166e0a3d11f79cd55909dda7014/client/file_sync_client.cpp#1361
this._needPushMkdirWorkaround = this.#needPushMkdirWorkaround =
this._adb.supportsFeature(AdbFeature.ShellV2) && this._adb.supportsFeature(AdbFeature.ShellV2) &&
!this.fixedPushMkdir; !this.fixedPushMkdir;
} }

View file

@ -124,9 +124,8 @@ export class AdbAuthenticationProcessor implements Disposable {
private readonly credentialStore: AdbCredentialStore; private readonly credentialStore: AdbCredentialStore;
private pendingRequest = new PromiseResolver<AdbPacketData>(); #pendingRequest = new PromiseResolver<AdbPacketData>();
#iterator: AsyncIterator<AdbPacketData, void, void> | undefined;
private iterator: AsyncIterator<AdbPacketData, void, void> | undefined;
public constructor( public constructor(
authenticators: readonly AdbAuthenticator[], authenticators: readonly AdbAuthenticator[],
@ -137,7 +136,7 @@ export class AdbAuthenticationProcessor implements Disposable {
} }
private getNextRequest = (): Promise<AdbPacketData> => { private getNextRequest = (): Promise<AdbPacketData> => {
return this.pendingRequest.promise; return this.#pendingRequest.promise;
}; };
private async *invokeAuthenticator(): AsyncGenerator< private async *invokeAuthenticator(): AsyncGenerator<
@ -152,7 +151,7 @@ export class AdbAuthenticationProcessor implements Disposable {
)) { )) {
// If the authenticator yielded a response // If the authenticator yielded a response
// Prepare `nextRequest` for next authentication request // Prepare `nextRequest` for next authentication request
this.pendingRequest = new PromiseResolver(); this.#pendingRequest = new PromiseResolver();
// Yield the response to outer layer // Yield the response to outer layer
yield packet; yield packet;
@ -164,13 +163,13 @@ export class AdbAuthenticationProcessor implements Disposable {
} }
public async process(packet: AdbPacketData): Promise<AdbPacketData> { public async process(packet: AdbPacketData): Promise<AdbPacketData> {
if (!this.iterator) { if (!this.#iterator) {
this.iterator = this.invokeAuthenticator(); this.#iterator = this.invokeAuthenticator();
} }
this.pendingRequest.resolve(packet); this.#pendingRequest.resolve(packet);
const result = await this.iterator.next(); const result = await this.#iterator.next();
if (result.done) { if (result.done) {
throw new Error("No authenticator can handle the request"); throw new Error("No authenticator can handle the request");
} }
@ -179,6 +178,6 @@ export class AdbAuthenticationProcessor implements Disposable {
} }
public dispose() { public dispose() {
void this.iterator?.return?.(); void this.#iterator?.return?.();
} }
} }

View file

@ -43,28 +43,24 @@ export interface AdbPacketDispatcherOptions {
export class AdbPacketDispatcher implements Closeable { export class AdbPacketDispatcher implements Closeable {
// ADB socket id starts from 1 // ADB socket id starts from 1
// (0 means open failed) // (0 means open failed)
private readonly initializers = new AsyncOperationManager(1); readonly #initializers = new AsyncOperationManager(1);
/** /**
* Socket local ID to the socket controller. * Socket local ID to the socket controller.
*/ */
private readonly sockets = new Map<number, AdbDaemonSocketController>(); readonly #sockets = new Map<number, AdbDaemonSocketController>();
private _writer: WritableStreamDefaultWriter<Consumable<AdbPacketInit>>; #writer: WritableStreamDefaultWriter<Consumable<AdbPacketInit>>;
public readonly options: AdbPacketDispatcherOptions; public readonly options: AdbPacketDispatcherOptions;
private _closed = false; #closed = false;
private _disconnected = new PromiseResolver<void>(); #disconnected = new PromiseResolver<void>();
public get disconnected() { public get disconnected() {
return this._disconnected.promise; return this.#disconnected.promise;
} }
private _incomingSocketHandlers = new Map< #incomingSocketHandlers = new Map<string, AdbIncomingSocketHandler>();
string, #readAbortController = new AbortController();
AdbIncomingSocketHandler
>();
private _abortController = new AbortController();
public constructor( public constructor(
connection: ReadableWritablePair< connection: ReadableWritablePair<
@ -87,8 +83,8 @@ export class AdbPacketDispatcher implements Closeable {
await this.handleClose(packet); await this.handleClose(packet);
break; break;
case AdbCommand.Write: case AdbCommand.Write:
if (this.sockets.has(packet.arg1)) { if (this.#sockets.has(packet.arg1)) {
await this.sockets await this.#sockets
.get(packet.arg1)! .get(packet.arg1)!
.enqueue(packet.payload); .enqueue(packet.payload);
await this.sendPacket( await this.sendPacket(
@ -124,7 +120,7 @@ export class AdbPacketDispatcher implements Closeable {
// it's still possible to create another ADB connection. // it's still possible to create another ADB connection.
// So don't close `readable` here. // So don't close `readable` here.
preventCancel: true, preventCancel: true,
signal: this._abortController.signal, signal: this.#readAbortController.signal,
} }
) )
.then( .then(
@ -132,23 +128,23 @@ export class AdbPacketDispatcher implements Closeable {
this.dispose(); this.dispose();
}, },
(e) => { (e) => {
if (!this._closed) { if (!this.#closed) {
this._disconnected.reject(e); this.#disconnected.reject(e);
} }
this.dispose(); this.dispose();
} }
); );
this._writer = connection.writable.getWriter(); this.#writer = connection.writable.getWriter();
} }
private handleOk(packet: AdbPacketData) { private handleOk(packet: AdbPacketData) {
if (this.initializers.resolve(packet.arg1, packet.arg0)) { if (this.#initializers.resolve(packet.arg1, packet.arg0)) {
// Device successfully created the socket // Device successfully created the socket
return; return;
} }
const socket = this.sockets.get(packet.arg1); const socket = this.#sockets.get(packet.arg1);
if (socket) { if (socket) {
// Device has received last `WRTE` to the socket // Device has received last `WRTE` to the socket
socket.ack(); socket.ack();
@ -164,7 +160,7 @@ export class AdbPacketDispatcher implements Closeable {
// If the socket is still pending // If the socket is still pending
if ( if (
packet.arg0 === 0 && packet.arg0 === 0 &&
this.initializers.reject( this.#initializers.reject(
packet.arg1, packet.arg1,
new Error("Socket open failed") new Error("Socket open failed")
) )
@ -185,7 +181,7 @@ export class AdbPacketDispatcher implements Closeable {
*/ */
// Ignore `arg0` and search for the socket // Ignore `arg0` and search for the socket
const socket = this.sockets.get(packet.arg1); const socket = this.#sockets.get(packet.arg1);
if (socket) { if (socket) {
// The device want to close the socket // The device want to close the socket
if (!socket.closed) { if (!socket.closed) {
@ -196,7 +192,7 @@ export class AdbPacketDispatcher implements Closeable {
); );
} }
await socket.dispose(); await socket.dispose();
this.sockets.delete(packet.arg1); this.#sockets.delete(packet.arg1);
return; return;
} }
@ -209,22 +205,22 @@ export class AdbPacketDispatcher implements Closeable {
service: string, service: string,
handler: AdbIncomingSocketHandler handler: AdbIncomingSocketHandler
) { ) {
this._incomingSocketHandlers.set(service, handler); this.#incomingSocketHandlers.set(service, handler);
} }
public removeReverseTunnel(address: string) { public removeReverseTunnel(address: string) {
this._incomingSocketHandlers.delete(address); this.#incomingSocketHandlers.delete(address);
} }
public clearReverseTunnels() { public clearReverseTunnels() {
this._incomingSocketHandlers.clear(); this.#incomingSocketHandlers.clear();
} }
private async handleOpen(packet: AdbPacketData) { private async handleOpen(packet: AdbPacketData) {
// `AsyncOperationManager` doesn't support skipping IDs // `AsyncOperationManager` doesn't support skipping IDs
// Use `add` + `resolve` to simulate this behavior // Use `add` + `resolve` to simulate this behavior
const [localId] = this.initializers.add<number>(); const [localId] = this.#initializers.add<number>();
this.initializers.resolve(localId, undefined); this.#initializers.resolve(localId, undefined);
const remoteId = packet.arg0; const remoteId = packet.arg0;
let service = decodeUtf8(packet.payload); let service = decodeUtf8(packet.payload);
@ -232,7 +228,7 @@ export class AdbPacketDispatcher implements Closeable {
service = service.substring(0, service.length - 1); service = service.substring(0, service.length - 1);
} }
const handler = this._incomingSocketHandlers.get(service); const handler = this.#incomingSocketHandlers.get(service);
if (!handler) { if (!handler) {
await this.sendPacket(AdbCommand.Close, 0, remoteId); await this.sendPacket(AdbCommand.Close, 0, remoteId);
return; return;
@ -248,7 +244,7 @@ export class AdbPacketDispatcher implements Closeable {
try { try {
await handler(controller.socket); await handler(controller.socket);
this.sockets.set(localId, controller); this.#sockets.set(localId, controller);
await this.sendPacket(AdbCommand.OK, localId, remoteId); await this.sendPacket(AdbCommand.OK, localId, remoteId);
} catch (e) { } catch (e) {
await this.sendPacket(AdbCommand.Close, 0, remoteId); await this.sendPacket(AdbCommand.Close, 0, remoteId);
@ -260,7 +256,7 @@ export class AdbPacketDispatcher implements Closeable {
service += "\0"; service += "\0";
} }
const [localId, initializer] = this.initializers.add<number>(); const [localId, initializer] = this.#initializers.add<number>();
await this.sendPacket(AdbCommand.Open, localId, 0, service); await this.sendPacket(AdbCommand.Open, localId, 0, service);
// Fulfilled by `handleOk` // Fulfilled by `handleOk`
@ -272,7 +268,7 @@ export class AdbPacketDispatcher implements Closeable {
localCreated: true, localCreated: true,
service, service,
}); });
this.sockets.set(localId, controller); this.#sockets.set(localId, controller);
return controller.socket; return controller.socket;
} }
@ -291,7 +287,7 @@ export class AdbPacketDispatcher implements Closeable {
throw new Error("payload too large"); throw new Error("payload too large");
} }
await ConsumableWritableStream.write(this._writer, { await ConsumableWritableStream.write(this.#writer, {
command, command,
arg0, arg0,
arg1, arg1,
@ -306,24 +302,24 @@ export class AdbPacketDispatcher implements Closeable {
public async close() { public async close() {
// Send `CLSE` packets for all sockets // Send `CLSE` packets for all sockets
await Promise.all( await Promise.all(
Array.from(this.sockets.values(), (socket) => socket.close()) Array.from(this.#sockets.values(), (socket) => socket.close())
); );
// Stop receiving // Stop receiving
// It's possible that we haven't received all `CLSE` confirm packets, // It's possible that we haven't received all `CLSE` confirm packets,
// but it doesn't matter, the next connection can cope with them. // but it doesn't matter, the next connection can cope with them.
this._closed = true; this.#closed = true;
this._abortController.abort(); this.#readAbortController.abort();
this._writer.releaseLock(); this.#writer.releaseLock();
// `pipe().then()` will call `dispose` // `pipe().then()` will call `dispose`
} }
private dispose() { private dispose() {
for (const socket of this.sockets.values()) { for (const socket of this.#sockets.values()) {
socket.dispose().catch(unreachable); socket.dispose().catch(unreachable);
} }
this._disconnected.resolve(); this.#disconnected.resolve();
} }
} }

View file

@ -1,6 +1,6 @@
export * from "./auth.js"; export * from "./auth.js";
export * from "./connection.js";
export * from "./crypto.js"; export * from "./crypto.js";
export * from "./device.js";
export * from "./dispatcher.js"; export * from "./dispatcher.js";
export * from "./packet.js"; export * from "./packet.js";
export * from "./socket.js"; export * from "./socket.js";

View file

@ -34,7 +34,7 @@ export type AdbPacket = (typeof AdbPacket)["TDeserializeResult"];
* `AdvDaemonConnection#connect` will return a `ReadableStream<AdbPacketData>`, * `AdvDaemonConnection#connect` will return a `ReadableStream<AdbPacketData>`,
* allow each connection to encode `AdbPacket` in different methods. * allow each connection to encode `AdbPacket` in different methods.
* *
* `AdvDaemonConnection#connect` will return a `WritableStream<AdbPacketInit>`, * `AdbDaemonConnection#connect` will return a `WritableStream<AdbPacketInit>`,
* however, `AdbDaemonTransport` will transform `AdbPacketData` to `AdbPacketInit` for you, * however, `AdbDaemonTransport` will transform `AdbPacketData` to `AdbPacketInit` for you,
* so `AdbSocket#writable#write` only needs `AdbPacketData`. * so `AdbSocket#writable#write` only needs `AdbPacketData`.
*/ */

View file

@ -49,25 +49,25 @@ export class AdbDaemonSocketController
public readonly localCreated!: boolean; public readonly localCreated!: boolean;
public readonly service!: string; public readonly service!: string;
private _duplex: DuplexStreamFactory<Uint8Array, Consumable<Uint8Array>>; #duplex: DuplexStreamFactory<Uint8Array, Consumable<Uint8Array>>;
private _readable: ReadableStream<Uint8Array>; #readable: ReadableStream<Uint8Array>;
private _readableController!: PushReadableStreamController<Uint8Array>; #readableController!: PushReadableStreamController<Uint8Array>;
public get readable() { public get readable() {
return this._readable; return this.#readable;
} }
private _writePromise: PromiseResolver<void> | undefined; #writePromise: PromiseResolver<void> | undefined;
public readonly writable: WritableStream<Consumable<Uint8Array>>; public readonly writable: WritableStream<Consumable<Uint8Array>>;
private _closed = false; #closed = false;
/** /**
* Whether the socket is half-closed (i.e. the local side initiated the close). * Whether the socket is half-closed (i.e. the local side initiated the close).
* *
* It's only used by dispatcher to avoid sending another `CLSE` packet to remote. * It's only used by dispatcher to avoid sending another `CLSE` packet to remote.
*/ */
public get closed() { public get closed() {
return this._closed; return this.#closed;
} }
private _socket: AdbDaemonSocket; private _socket: AdbDaemonSocket;
@ -82,12 +82,12 @@ export class AdbDaemonSocketController
// cspell: disable-next-line // cspell: disable-next-line
// https://www.plantuml.com/plantuml/png/TL0zoeGm4ErpYc3l5JxyS0yWM6mX5j4C6p4cxcJ25ejttuGX88ZftizxUKmJI275pGhXl0PP_UkfK_CAz5Z2hcWsW9Ny2fdU4C1f5aSchFVxA8vJjlTPRhqZzDQMRB7AklwJ0xXtX0ZSKH1h24ghoKAdGY23FhxC4nS2pDvxzIvxb-8THU0XlEQJ-ZB7SnXTAvc_LhOckhMdLBnbtndpb-SB7a8q2SRD_W00 // https://www.plantuml.com/plantuml/png/TL0zoeGm4ErpYc3l5JxyS0yWM6mX5j4C6p4cxcJ25ejttuGX88ZftizxUKmJI275pGhXl0PP_UkfK_CAz5Z2hcWsW9Ny2fdU4C1f5aSchFVxA8vJjlTPRhqZzDQMRB7AklwJ0xXtX0ZSKH1h24ghoKAdGY23FhxC4nS2pDvxzIvxb-8THU0XlEQJ-ZB7SnXTAvc_LhOckhMdLBnbtndpb-SB7a8q2SRD_W00
this._duplex = new DuplexStreamFactory< this.#duplex = new DuplexStreamFactory<
Uint8Array, Uint8Array,
Consumable<Uint8Array> Consumable<Uint8Array>
>({ >({
close: async () => { close: async () => {
this._closed = true; this.#closed = true;
await this.dispatcher.sendPacket( await this.dispatcher.sendPacket(
AdbCommand.Close, AdbCommand.Close,
@ -100,14 +100,14 @@ export class AdbDaemonSocketController
}, },
dispose: () => { dispose: () => {
// Error out the pending writes // Error out the pending writes
this._writePromise?.reject(new Error("Socket closed")); this.#writePromise?.reject(new Error("Socket closed"));
}, },
}); });
this._readable = this._duplex.wrapReadable( this.#readable = this.#duplex.wrapReadable(
new PushReadableStream( new PushReadableStream(
(controller) => { (controller) => {
this._readableController = controller; this.#readableController = controller;
}, },
{ {
highWaterMark: options.highWaterMark ?? 16 * 1024, highWaterMark: options.highWaterMark ?? 16 * 1024,
@ -119,18 +119,18 @@ export class AdbDaemonSocketController
); );
this.writable = pipeFrom( this.writable = pipeFrom(
this._duplex.createWritable( this.#duplex.createWritable(
new ConsumableWritableStream<Uint8Array>({ new ConsumableWritableStream<Uint8Array>({
write: async (chunk) => { write: async (chunk) => {
// Wait for an ack packet // Wait for an ack packet
this._writePromise = new PromiseResolver(); this.#writePromise = new PromiseResolver();
await this.dispatcher.sendPacket( await this.dispatcher.sendPacket(
AdbCommand.Write, AdbCommand.Write,
this.localId, this.localId,
this.remoteId, this.remoteId,
chunk chunk
); );
await this._writePromise.promise; await this.#writePromise.promise;
}, },
}) })
), ),
@ -143,23 +143,23 @@ export class AdbDaemonSocketController
public async enqueue(data: Uint8Array) { public async enqueue(data: Uint8Array) {
// Consumer may abort the `ReadableStream` to close the socket, // Consumer may abort the `ReadableStream` to close the socket,
// it's OK to throw away further packets in this case. // it's OK to throw away further packets in this case.
if (this._readableController.abortSignal.aborted) { if (this.#readableController.abortSignal.aborted) {
return; return;
} }
await this._readableController.enqueue(data); await this.#readableController.enqueue(data);
} }
public ack() { public ack() {
this._writePromise?.resolve(); this.#writePromise?.resolve();
} }
public async close(): Promise<void> { public async close(): Promise<void> {
await this._duplex.close(); await this.#duplex.close();
} }
public dispose() { public dispose() {
return this._duplex.dispose(); return this.#duplex.dispose();
} }
} }
@ -176,37 +176,37 @@ export class AdbDaemonSocket
AdbDaemonSocketInfo, AdbDaemonSocketInfo,
ReadableWritablePair<Uint8Array, Consumable<Uint8Array>> ReadableWritablePair<Uint8Array, Consumable<Uint8Array>>
{ {
private _controller: AdbDaemonSocketController; #controller: AdbDaemonSocketController;
public get localId(): number { public get localId(): number {
return this._controller.localId; return this.#controller.localId;
} }
public get remoteId(): number { public get remoteId(): number {
return this._controller.remoteId; return this.#controller.remoteId;
} }
public get localCreated(): boolean { public get localCreated(): boolean {
return this._controller.localCreated; return this.#controller.localCreated;
} }
public get service(): string { public get service(): string {
return this._controller.service; return this.#controller.service;
} }
public get readable(): ReadableStream<Uint8Array> { public get readable(): ReadableStream<Uint8Array> {
return this._controller.readable; return this.#controller.readable;
} }
public get writable(): WritableStream<Consumable<Uint8Array>> { public get writable(): WritableStream<Consumable<Uint8Array>> {
return this._controller.writable; return this.#controller.writable;
} }
public get closed(): boolean { public get closed(): boolean {
return this._controller.closed; return this.#controller.closed;
} }
public constructor(controller: AdbDaemonSocketController) { public constructor(controller: AdbDaemonSocketController) {
this._controller = controller; this.#controller = controller;
} }
public close() { public close() {
return this._controller.close(); return this.#controller.close();
} }
} }

View file

@ -180,30 +180,30 @@ export class AdbDaemonTransport implements AdbTransport {
}); });
} }
private readonly _dispatcher: AdbPacketDispatcher; readonly #dispatcher: AdbPacketDispatcher;
private _serial: string; #serial: string;
public get serial() { public get serial() {
return this._serial; return this.#serial;
} }
private _protocolVersion: number; #protocolVersion: number;
public get protocolVersion() { public get protocolVersion() {
return this._protocolVersion; return this.#protocolVersion;
} }
private _maxPayloadSize: number; #maxPayloadSize: number;
public get maxPayloadSize() { public get maxPayloadSize() {
return this._maxPayloadSize; return this.#maxPayloadSize;
} }
private _banner: AdbBanner; #banner: AdbBanner;
public get banner() { public get banner() {
return this._banner; return this.#banner;
} }
public get disconnected() { public get disconnected() {
return this._dispatcher.disconnected; return this.#dispatcher.disconnected;
} }
public constructor({ public constructor({
@ -213,8 +213,8 @@ export class AdbDaemonTransport implements AdbTransport {
maxPayloadSize, maxPayloadSize,
banner, banner,
}: AdbDaemonSocketConnectorConstructionOptions) { }: AdbDaemonSocketConnectorConstructionOptions) {
this._serial = serial; this.#serial = serial;
this._banner = AdbBanner.parse(banner); this.#banner = AdbBanner.parse(banner);
let calculateChecksum: boolean; let calculateChecksum: boolean;
let appendNullToServiceString: boolean; let appendNullToServiceString: boolean;
@ -226,18 +226,18 @@ export class AdbDaemonTransport implements AdbTransport {
appendNullToServiceString = true; appendNullToServiceString = true;
} }
this._dispatcher = new AdbPacketDispatcher(connection, { this.#dispatcher = new AdbPacketDispatcher(connection, {
calculateChecksum, calculateChecksum,
appendNullToServiceString, appendNullToServiceString,
maxPayloadSize, maxPayloadSize,
}); });
this._protocolVersion = version; this.#protocolVersion = version;
this._maxPayloadSize = maxPayloadSize; this.#maxPayloadSize = maxPayloadSize;
} }
public connect(service: string): ValueOrPromise<AdbSocket> { public connect(service: string): ValueOrPromise<AdbSocket> {
return this._dispatcher.createSocket(service); return this.#dispatcher.createSocket(service);
} }
public addReverseTunnel( public addReverseTunnel(
@ -248,19 +248,19 @@ export class AdbDaemonTransport implements AdbTransport {
const id = Math.random().toString().substring(2); const id = Math.random().toString().substring(2);
address = `localabstract:reverse_${id}`; address = `localabstract:reverse_${id}`;
} }
this._dispatcher.addReverseTunnel(address, handler); this.#dispatcher.addReverseTunnel(address, handler);
return address; return address;
} }
public removeReverseTunnel(address: string): void { public removeReverseTunnel(address: string): void {
this._dispatcher.removeReverseTunnel(address); this.#dispatcher.removeReverseTunnel(address);
} }
public clearReverseTunnels(): void { public clearReverseTunnels(): void {
this._dispatcher.clearReverseTunnels(); this.#dispatcher.clearReverseTunnels();
} }
public close(): ValueOrPromise<void> { public close(): ValueOrPromise<void> {
return this._dispatcher.close(); return this.#dispatcher.close();
} }
} }

View file

@ -28,66 +28,10 @@ import {
import type { AdbIncomingSocketHandler, AdbSocket } from "../adb.js"; import type { AdbIncomingSocketHandler, AdbSocket } from "../adb.js";
import { AdbBanner } from "../banner.js"; import { AdbBanner } from "../banner.js";
import type { AdbFeature } from "../features.js"; import type { AdbFeature } from "../features.js";
import { NOOP } from "../utils/index.js"; import { NOOP, hexToNumber, numberToHex } from "../utils/index.js";
import { AdbServerTransport } from "./transport.js"; import { AdbServerTransport } from "./transport.js";
function hexCharToNumber(char: number) {
if (char < 48) {
throw new Error(`Invalid hex char ${char}`);
}
if (char < 58) {
return char - 48;
}
if (char < 65) {
throw new Error(`Invalid hex char ${char}`);
}
if (char < 71) {
return char - 55;
}
if (char < 97) {
throw new Error(`Invalid hex char ${char}`);
}
if (char < 103) {
return char - 87;
}
throw new Error(`Invalid hex char ${char}`);
}
// It's 22x faster than converting `data` to string then `Number.parseInt`
// https://jsbench.me/dglha94ozl/1
function hexToNumber(data: Uint8Array): number {
let result = 0;
for (let i = 0; i < data.length; i += 1) {
result = (result << 4) | hexCharToNumber(data[i]!);
}
return result;
}
function numberToHex(value: number) {
const result = new Uint8Array(4);
let index = 3;
while (index >= 0 && value > 0) {
const digit = value & 0xf;
value >>= 4;
if (digit < 10) {
result[index] = digit + 48;
} else {
result[index] = digit + 87;
}
index -= 1;
}
while (index >= 0) {
// '0'
result[index] = 48;
index -= 1;
}
return result;
}
export interface AdbServerConnectionOptions { export interface AdbServerConnectionOptions {
unref?: boolean | undefined; unref?: boolean | undefined;
signal?: AbortSignal | undefined; signal?: AbortSignal | undefined;

View file

@ -12,7 +12,7 @@ import type { AdbBanner } from "../banner.js";
import type { AdbServerClient } from "./client.js"; import type { AdbServerClient } from "./client.js";
export class AdbServerTransport implements AdbTransport { export class AdbServerTransport implements AdbTransport {
private _client: AdbServerClient; #client: AdbServerClient;
public readonly serial: string; public readonly serial: string;
@ -22,8 +22,8 @@ export class AdbServerTransport implements AdbTransport {
public readonly banner: AdbBanner; public readonly banner: AdbBanner;
private _closed = new PromiseResolver<void>(); #closed = new PromiseResolver<void>();
private _waitAbortController = new AbortController(); #waitAbortController = new AbortController();
public readonly disconnected: Promise<void>; public readonly disconnected: Promise<void>;
public constructor( public constructor(
@ -32,22 +32,22 @@ export class AdbServerTransport implements AdbTransport {
banner: AdbBanner, banner: AdbBanner,
transportId: bigint transportId: bigint
) { ) {
this._client = client; this.#client = client;
this.serial = serial; this.serial = serial;
this.banner = banner; this.banner = banner;
this.transportId = transportId; this.transportId = transportId;
this.disconnected = Promise.race([ this.disconnected = Promise.race([
this._closed.promise, this.#closed.promise,
client.waitFor({ transportId }, "disconnect", { client.waitFor({ transportId }, "disconnect", {
signal: this._waitAbortController.signal, signal: this.#waitAbortController.signal,
unref: true, unref: true,
}), }),
]); ]);
} }
public async connect(service: string): Promise<AdbSocket> { public async connect(service: string): Promise<AdbSocket> {
return await this._client.connectDevice( return await this.#client.connectDevice(
{ {
transportId: this.transportId, transportId: this.transportId,
}, },
@ -59,19 +59,19 @@ export class AdbServerTransport implements AdbTransport {
handler: AdbIncomingSocketHandler, handler: AdbIncomingSocketHandler,
address?: string address?: string
): Promise<string> { ): Promise<string> {
return await this._client.connection.addReverseTunnel(handler, address); return await this.#client.connection.addReverseTunnel(handler, address);
} }
public async removeReverseTunnel(address: string): Promise<void> { public async removeReverseTunnel(address: string): Promise<void> {
await this._client.connection.removeReverseTunnel(address); await this.#client.connection.removeReverseTunnel(address);
} }
public async clearReverseTunnels(): Promise<void> { public async clearReverseTunnels(): Promise<void> {
await this._client.connection.clearReverseTunnels(); await this.#client.connection.clearReverseTunnels();
} }
close(): ValueOrPromise<void> { close(): ValueOrPromise<void> {
this._closed.resolve(); this.#closed.resolve();
this._waitAbortController.abort(); this.#waitAbortController.abort();
} }
} }

View file

@ -2,39 +2,39 @@ import { PromiseResolver } from "@yume-chan/async";
import type { Disposable } from "@yume-chan/event"; import type { Disposable } from "@yume-chan/event";
export class AutoResetEvent implements Disposable { export class AutoResetEvent implements Disposable {
private _set: boolean; #set: boolean;
private readonly _queue: PromiseResolver<void>[] = []; readonly #queue: PromiseResolver<void>[] = [];
public constructor(initialSet = false) { public constructor(initialSet = false) {
this._set = initialSet; this.#set = initialSet;
} }
public wait(): Promise<void> { public wait(): Promise<void> {
if (!this._set) { if (!this.#set) {
this._set = true; this.#set = true;
if (this._queue.length === 0) { if (this.#queue.length === 0) {
return Promise.resolve(); return Promise.resolve();
} }
} }
const resolver = new PromiseResolver<void>(); const resolver = new PromiseResolver<void>();
this._queue.push(resolver); this.#queue.push(resolver);
return resolver.promise; return resolver.promise;
} }
public notifyOne() { public notifyOne() {
if (this._queue.length !== 0) { if (this.#queue.length !== 0) {
this._queue.pop()!.resolve(); this.#queue.pop()!.resolve();
} else { } else {
this._set = false; this.#set = false;
} }
} }
public dispose() { public dispose() {
for (const item of this._queue) { for (const item of this.#queue) {
item.reject(new Error("The AutoResetEvent has been disposed")); item.reject(new Error("The AutoResetEvent has been disposed"));
} }
this._queue.length = 0; this.#queue.length = 0;
} }
} }

View file

@ -7,39 +7,39 @@ interface WaitEntry {
} }
export class ConditionalVariable implements Disposable { export class ConditionalVariable implements Disposable {
private _locked = false; #locked = false;
private readonly _queue: WaitEntry[] = []; readonly #queue: WaitEntry[] = [];
public wait(condition: () => boolean): Promise<void> { public wait(condition: () => boolean): Promise<void> {
if (!this._locked) { if (!this.#locked) {
this._locked = true; this.#locked = true;
if (this._queue.length === 0 && condition()) { if (this.#queue.length === 0 && condition()) {
return Promise.resolve(); return Promise.resolve();
} }
} }
const resolver = new PromiseResolver<void>(); const resolver = new PromiseResolver<void>();
this._queue.push({ condition, resolver }); this.#queue.push({ condition, resolver });
return resolver.promise; return resolver.promise;
} }
public notifyOne() { public notifyOne() {
const entry = this._queue.shift(); const entry = this.#queue.shift();
if (entry) { if (entry) {
if (entry.condition()) { if (entry.condition()) {
entry.resolver.resolve(); entry.resolver.resolve();
} }
} else { } else {
this._locked = false; this.#locked = false;
} }
} }
public dispose(): void { public dispose(): void {
for (const item of this._queue) { for (const item of this.#queue) {
item.resolver.reject( item.resolver.reject(
new Error("The ConditionalVariable has been disposed") new Error("The ConditionalVariable has been disposed")
); );
} }
this._queue.length = 0; this.#queue.length = 0;
} }
} }

View file

@ -0,0 +1,58 @@
function hexCharToNumber(char: number) {
if (char < 48) {
throw new Error(`Invalid hex char ${char}`);
}
if (char < 58) {
// 0-9
return char - 48;
}
if (char < 65) {
throw new Error(`Invalid hex char ${char}`);
}
if (char < 71) {
// A-F
return char - 55;
}
if (char < 97) {
throw new Error(`Invalid hex char ${char}`);
}
if (char < 103) {
// a-f
return char - 87;
}
throw new Error(`Invalid hex char ${char}`);
}
// It's 22x faster than converting `data` to string then `Number.parseInt`
// https://jsbench.me/dglha94ozl/1
export function hexToNumber(data: Uint8Array): number {
let result = 0;
for (let i = 0; i < data.length; i += 1) {
result = (result << 4) | hexCharToNumber(data[i]!);
}
return result;
}
export function numberToHex(value: number) {
const result = new Uint8Array(4);
let index = 3;
while (index >= 0 && value > 0) {
const digit = value & 0xf;
value >>= 4;
if (digit < 10) {
result[index] = digit + 48;
} else {
result[index] = digit + 87;
}
index -= 1;
}
while (index >= 0) {
// '0'
result[index] = 48;
index -= 1;
}
return result;
}

View file

@ -2,4 +2,5 @@ export { decodeUtf8, encodeUtf8 } from "@yume-chan/struct";
export * from "./auto-reset-event.js"; export * from "./auto-reset-event.js";
export * from "./base64.js"; export * from "./base64.js";
export * from "./conditional-variable.js"; export * from "./conditional-variable.js";
export * from "./hex.js";
export * from "./no-op.js"; export * from "./no-op.js";

View file

@ -11,14 +11,14 @@ import { ReadableStream, WritableStream } from "./stream.js";
export class BufferedTransformStream<T> export class BufferedTransformStream<T>
implements ReadableWritablePair<T, Uint8Array> implements ReadableWritablePair<T, Uint8Array>
{ {
private _readable: ReadableStream<T>; #readable: ReadableStream<T>;
public get readable() { public get readable() {
return this._readable; return this.#readable;
} }
private _writable: WritableStream<Uint8Array>; #writable: WritableStream<Uint8Array>;
public get writable() { public get writable() {
return this._writable; return this.#writable;
} }
constructor( constructor(
@ -33,7 +33,7 @@ export class BufferedTransformStream<T>
}) })
); );
this._readable = new ReadableStream<T>({ this.#readable = new ReadableStream<T>({
async pull(controller) { async pull(controller) {
try { try {
const value = await transform(buffered); const value = await transform(buffered);
@ -56,7 +56,7 @@ export class BufferedTransformStream<T>
}, },
}); });
this._writable = new WritableStream({ this.#writable = new WritableStream({
async write(chunk) { async write(chunk) {
await sourceStreamController.enqueue(chunk); await sourceStreamController.enqueue(chunk);
}, },

View file

@ -9,13 +9,13 @@ const NOOP = () => {
}; };
export class BufferedReadableStream implements AsyncExactReadable { export class BufferedReadableStream implements AsyncExactReadable {
private buffered: Uint8Array | undefined; #buffered: Uint8Array | undefined;
private bufferedOffset = 0; #bufferedOffset = 0;
private bufferedLength = 0; #bufferedLength = 0;
private _position = 0; #position = 0;
public get position() { public get position() {
return this._position; return this.#position;
} }
protected readonly stream: ReadableStream<Uint8Array>; protected readonly stream: ReadableStream<Uint8Array>;
@ -31,7 +31,7 @@ export class BufferedReadableStream implements AsyncExactReadable {
if (done) { if (done) {
throw new ExactReadableEndedError(); throw new ExactReadableEndedError();
} }
this._position += value.byteLength; this.#position += value.byteLength;
return value; return value;
} }
@ -51,9 +51,9 @@ export class BufferedReadableStream implements AsyncExactReadable {
} }
if (array.byteLength > length) { if (array.byteLength > length) {
this.buffered = array; this.#buffered = array;
this.bufferedOffset = length; this.#bufferedOffset = length;
this.bufferedLength = array.byteLength - length; this.#bufferedLength = array.byteLength - length;
return array.subarray(0, length); return array.subarray(0, length);
} }
@ -71,9 +71,9 @@ export class BufferedReadableStream implements AsyncExactReadable {
} }
if (array.byteLength > length) { if (array.byteLength > length) {
this.buffered = array; this.#buffered = array;
this.bufferedOffset = length; this.#bufferedOffset = length;
this.bufferedLength = array.byteLength - length; this.#bufferedLength = array.byteLength - length;
result.set(array.subarray(0, length), index); result.set(array.subarray(0, length), index);
return result; return result;
} }
@ -93,20 +93,20 @@ export class BufferedReadableStream implements AsyncExactReadable {
*/ */
public readExactly(length: number): Uint8Array | Promise<Uint8Array> { public readExactly(length: number): Uint8Array | Promise<Uint8Array> {
// PERF: Add a synchronous path for reading from internal buffer // PERF: Add a synchronous path for reading from internal buffer
if (this.buffered) { if (this.#buffered) {
const array = this.buffered; const array = this.#buffered;
const offset = this.bufferedOffset; const offset = this.#bufferedOffset;
if (this.bufferedLength > length) { if (this.#bufferedLength > length) {
// PERF: `subarray` is slow // PERF: `subarray` is slow
// don't use it until absolutely necessary // don't use it until absolutely necessary
this.bufferedOffset += length; this.#bufferedOffset += length;
this.bufferedLength -= length; this.#bufferedLength -= length;
return array.subarray(offset, offset + length); return array.subarray(offset, offset + length);
} }
this.buffered = undefined; this.#buffered = undefined;
this.bufferedLength = 0; this.#bufferedLength = 0;
this.bufferedOffset = 0; this.#bufferedOffset = 0;
return this.readAsync(length, array.subarray(offset)); return this.readAsync(length, array.subarray(offset));
} }
@ -119,10 +119,10 @@ export class BufferedReadableStream implements AsyncExactReadable {
* @returns A `ReadableStream` * @returns A `ReadableStream`
*/ */
public release(): ReadableStream<Uint8Array> { public release(): ReadableStream<Uint8Array> {
if (this.bufferedLength > 0) { if (this.#bufferedLength > 0) {
return new PushReadableStream<Uint8Array>(async (controller) => { return new PushReadableStream<Uint8Array>(async (controller) => {
// Put the remaining data back to the stream // Put the remaining data back to the stream
const buffered = this.buffered!.subarray(this.bufferedOffset); const buffered = this.#buffered!.subarray(this.#bufferedOffset);
await controller.enqueue(buffered); await controller.enqueue(buffered);
controller.abortSignal.addEventListener("abort", () => { controller.abortSignal.addEventListener("abort", () => {

View file

@ -26,35 +26,35 @@ const createTask: Console["createTask"] =
})); }));
export class Consumable<T> { export class Consumable<T> {
private readonly task: Task; readonly #task: Task;
private readonly resolver: PromiseResolver<void>; readonly #resolver: PromiseResolver<void>;
public readonly value: T; public readonly value: T;
public readonly consumed: Promise<void>; public readonly consumed: Promise<void>;
public constructor(value: T) { public constructor(value: T) {
this.task = createTask("Consumable"); this.#task = createTask("Consumable");
this.value = value; this.value = value;
this.resolver = new PromiseResolver<void>(); this.#resolver = new PromiseResolver<void>();
this.consumed = this.resolver.promise; this.consumed = this.#resolver.promise;
} }
public consume() { public consume() {
this.resolver.resolve(); this.#resolver.resolve();
} }
public error(error: any) { public error(error: any) {
this.resolver.reject(error); this.#resolver.reject(error);
} }
public async tryConsume<U>(callback: (value: T) => U) { public async tryConsume<U>(callback: (value: T) => U) {
try { try {
// eslint-disable-next-line @typescript-eslint/await-thenable // eslint-disable-next-line @typescript-eslint/await-thenable
const result = await this.task.run(() => callback(this.value)); const result = await this.#task.run(() => callback(this.value));
this.consume(); this.consume();
return result; return result;
} catch (e) { } catch (e) {
this.resolver.reject(e); this.#resolver.reject(e);
throw e; throw e;
} }
} }

View file

@ -4,16 +4,16 @@ import { ConsumableTransformStream } from "./consumable.js";
* Splits or combines buffers to specified size. * Splits or combines buffers to specified size.
*/ */
export class BufferCombiner { export class BufferCombiner {
private _capacity: number; #capacity: number;
private readonly _buffer: Uint8Array; readonly #buffer: Uint8Array;
private _offset: number; #offset: number;
private _available: number; #available: number;
public constructor(size: number) { public constructor(size: number) {
this._capacity = size; this.#capacity = size;
this._buffer = new Uint8Array(size); this.#buffer = new Uint8Array(size);
this._offset = 0; this.#offset = 0;
this._available = size; this.#available = size;
} }
/** /**
@ -27,52 +27,52 @@ export class BufferCombiner {
let offset = 0; let offset = 0;
let available = data.byteLength; let available = data.byteLength;
if (this._offset !== 0) { if (this.#offset !== 0) {
if (available >= this._available) { if (available >= this.#available) {
this._buffer.set( this.#buffer.set(
data.subarray(0, this._available), data.subarray(0, this.#available),
this._offset this.#offset
); );
offset += this._available; offset += this.#available;
available -= this._available; available -= this.#available;
yield this._buffer; yield this.#buffer;
this._offset = 0; this.#offset = 0;
this._available = this._capacity; this.#available = this.#capacity;
if (available === 0) { if (available === 0) {
return; return;
} }
} else { } else {
this._buffer.set(data, this._offset); this.#buffer.set(data, this.#offset);
this._offset += available; this.#offset += available;
this._available -= available; this.#available -= available;
return; return;
} }
} }
while (available >= this._capacity) { while (available >= this.#capacity) {
const end = offset + this._capacity; const end = offset + this.#capacity;
yield data.subarray(offset, end); yield data.subarray(offset, end);
offset = end; offset = end;
available -= this._capacity; available -= this.#capacity;
} }
if (available > 0) { if (available > 0) {
this._buffer.set(data.subarray(offset), this._offset); this.#buffer.set(data.subarray(offset), this.#offset);
this._offset += available; this.#offset += available;
this._available -= available; this.#available -= available;
} }
} }
public flush(): Uint8Array | undefined { public flush(): Uint8Array | undefined {
if (this._offset === 0) { if (this.#offset === 0) {
return undefined; return undefined;
} }
const output = this._buffer.subarray(0, this._offset); const output = this.#buffer.subarray(0, this.#offset);
this._offset = 0; this.#offset = 0;
this._available = this._capacity; this.#available = this.#capacity;
return output; return output;
} }
} }

View file

@ -46,29 +46,29 @@ export interface DuplexStreamFactoryOptions {
* when any of them is closed, all other streams will be closed as well. * when any of them is closed, all other streams will be closed as well.
*/ */
export class DuplexStreamFactory<R, W> { export class DuplexStreamFactory<R, W> {
private readableControllers: ReadableStreamDefaultController<R>[] = []; #readableControllers: ReadableStreamDefaultController<R>[] = [];
private writers: WritableStreamDefaultWriter<W>[] = []; #writers: WritableStreamDefaultWriter<W>[] = [];
private _writableClosed = false; #writableClosed = false;
public get writableClosed() { public get writableClosed() {
return this._writableClosed; return this.#writableClosed;
} }
private _closed = new PromiseResolver<void>(); #closed = new PromiseResolver<void>();
public get closed() { public get closed() {
return this._closed.promise; return this.#closed.promise;
} }
private options: DuplexStreamFactoryOptions; readonly #options: DuplexStreamFactoryOptions;
public constructor(options?: DuplexStreamFactoryOptions) { public constructor(options?: DuplexStreamFactoryOptions) {
this.options = options ?? {}; this.#options = options ?? {};
} }
public wrapReadable(readable: ReadableStream<R>): WrapReadableStream<R> { public wrapReadable(readable: ReadableStream<R>): WrapReadableStream<R> {
return new WrapReadableStream<R>({ return new WrapReadableStream<R>({
start: (controller) => { start: (controller) => {
this.readableControllers.push(controller); this.#readableControllers.push(controller);
return readable; return readable;
}, },
cancel: async () => { cancel: async () => {
@ -84,7 +84,7 @@ export class DuplexStreamFactory<R, W> {
public createWritable(stream: WritableStream<W>): WritableStream<W> { public createWritable(stream: WritableStream<W>): WritableStream<W> {
const writer = stream.getWriter(); const writer = stream.getWriter();
this.writers.push(writer); this.#writers.push(writer);
// `WritableStream` has no way to tell if the remote peer has closed the connection. // `WritableStream` has no way to tell if the remote peer has closed the connection.
// So it only triggers `close`. // So it only triggers `close`.
@ -105,28 +105,28 @@ export class DuplexStreamFactory<R, W> {
} }
public async close() { public async close() {
if (this._writableClosed) { if (this.#writableClosed) {
return; return;
} }
this._writableClosed = true; this.#writableClosed = true;
// Call `close` first, so it can still write data to `WritableStream`s. // Call `close` first, so it can still write data to `WritableStream`s.
if ((await this.options.close?.()) !== false) { if ((await this.#options.close?.()) !== false) {
// `close` can return `false` to disable automatic `dispose`. // `close` can return `false` to disable automatic `dispose`.
await this.dispose(); await this.dispose();
} }
for (const writer of this.writers) { for (const writer of this.#writers) {
// NOOP: the writer is already closed // NOOP: the writer is already closed
writer.close().catch(NOOP); writer.close().catch(NOOP);
} }
} }
public async dispose() { public async dispose() {
this._writableClosed = true; this.#writableClosed = true;
this._closed.resolve(); this.#closed.resolve();
for (const controller of this.readableControllers) { for (const controller of this.#readableControllers) {
try { try {
controller.close(); controller.close();
} catch { } catch {
@ -134,6 +134,6 @@ export class DuplexStreamFactory<R, W> {
} }
} }
await this.options.dispose?.(); await this.#options.dispose?.();
} }
} }

View file

@ -2,15 +2,15 @@ import { WritableStream } from "./stream.js";
export class GatherStringStream extends WritableStream<string> { export class GatherStringStream extends WritableStream<string> {
// PERF: rope (concat strings) is faster than `[].join('')` // PERF: rope (concat strings) is faster than `[].join('')`
private _result = ""; #result = "";
public get result() { public get result() {
return this._result; return this.#result;
} }
public constructor() { public constructor() {
super({ super({
write: (chunk) => { write: (chunk) => {
this._result += chunk; this.#result += chunk;
}, },
}); });
} }

View file

@ -44,7 +44,7 @@ function getWrappedReadableStream<T>(
export class WrapReadableStream<T> extends ReadableStream<T> { export class WrapReadableStream<T> extends ReadableStream<T> {
public readable!: ReadableStream<T>; public readable!: ReadableStream<T>;
private reader!: ReadableStreamDefaultReader<T>; #reader!: ReadableStreamDefaultReader<T>;
public constructor( public constructor(
wrapper: wrapper:
@ -64,16 +64,16 @@ export class WrapReadableStream<T> extends ReadableStream<T> {
wrapper, wrapper,
controller controller
); );
this.reader = this.readable.getReader(); this.#reader = this.readable.getReader();
}, },
cancel: async (reason) => { cancel: async (reason) => {
await this.reader.cancel(reason); await this.#reader.cancel(reason);
if ("cancel" in wrapper) { if ("cancel" in wrapper) {
await wrapper.cancel?.(reason); await wrapper.cancel?.(reason);
} }
}, },
pull: async (controller) => { pull: async (controller) => {
const result = await this.reader.read(); const result = await this.#reader.read();
if (result.done) { if (result.done) {
controller.close(); controller.close();
if ("close" in wrapper) { if ("close" in wrapper) {

View file

@ -32,7 +32,7 @@ async function getWrappedWritableStream<T>(
export class WrapWritableStream<T> extends WritableStream<T> { export class WrapWritableStream<T> extends WritableStream<T> {
public writable!: WritableStream<T>; public writable!: WritableStream<T>;
private writer!: WritableStreamDefaultWriter<T>; #writer!: WritableStreamDefaultWriter<T>;
public constructor( public constructor(
wrapper: wrapper:
@ -49,13 +49,13 @@ export class WrapWritableStream<T> extends WritableStream<T> {
await Promise.resolve(); await Promise.resolve();
this.writable = await getWrappedWritableStream(wrapper); this.writable = await getWrappedWritableStream(wrapper);
this.writer = this.writable.getWriter(); this.#writer = this.writable.getWriter();
}, },
write: async (chunk) => { write: async (chunk) => {
await this.writer.write(chunk); await this.#writer.write(chunk);
}, },
abort: async (reason) => { abort: async (reason) => {
await this.writer.abort(reason); await this.#writer.abort(reason);
if ("close" in wrapper) { if ("close" in wrapper) {
await wrapper.close?.(); await wrapper.close?.();
} }
@ -65,7 +65,7 @@ export class WrapWritableStream<T> extends WritableStream<T> {
// Usually the inner stream is a logical sub-stream over the outer stream, // Usually the inner stream is a logical sub-stream over the outer stream,
// closing the outer stream first will make the inner stream incapable of // closing the outer stream first will make the inner stream incapable of
// sending data in its `close` handler. // sending data in its `close` handler.
await this.writer.close(); await this.#writer.close();
if ("close" in wrapper) { if ("close" in wrapper) {
await wrapper.close?.(); await wrapper.close?.();
} }

View file

@ -238,22 +238,22 @@ export class Struct<
public readonly options: Readonly<StructOptions>; public readonly options: Readonly<StructOptions>;
private _size = 0; #size = 0;
/** /**
* Gets the static size (exclude fields that can change size at runtime) * Gets the static size (exclude fields that can change size at runtime)
*/ */
public get size() { public get size() {
return this._size; return this.#size;
} }
private _fields: [ #fields: [
name: PropertyKey, name: PropertyKey,
definition: StructFieldDefinition<any, any, any> definition: StructFieldDefinition<any, any, any>
][] = []; ][] = [];
private _extra: Record<PropertyKey, unknown> = {}; #extra: Record<PropertyKey, unknown> = {};
private _postDeserialized?: StructPostDeserialized<any, any> | undefined; #postDeserialized?: StructPostDeserialized<any, any> | undefined;
public constructor(options?: Partial<Readonly<StructOptions>>) { public constructor(options?: Partial<Readonly<StructOptions>>) {
this.options = { ...StructDefaultOptions, ...options }; this.options = { ...StructDefaultOptions, ...options };
@ -276,20 +276,20 @@ export class Struct<
TName, TName,
TDefinition TDefinition
> { > {
for (const field of this._fields) { for (const field of this.#fields) {
if (field[0] === name) { if (field[0] === name) {
// Convert Symbol to string
const nameString = String(name);
throw new Error( throw new Error(
`This struct already have a field with name '${String( `This struct already have a field with name '${nameString}'`
name
)}'`
); );
} }
} }
this._fields.push([name, definition]); this.#fields.push([name, definition]);
const size = definition.getSize(); const size = definition.getSize();
this._size += size; this.#size += size;
// Force cast `this` to another type // Force cast `this` to another type
return this as any; return this as any;
@ -306,13 +306,13 @@ export class Struct<
TExtra & TOther["TExtra"], TExtra & TOther["TExtra"],
TPostDeserialized TPostDeserialized
> { > {
for (const field of other._fields) { for (const field of other.#fields) {
this._fields.push(field); this.#fields.push(field);
} }
this._size += other._size; this.#size += other.#size;
Object.defineProperties( Object.defineProperties(
this._extra, this.#extra,
Object.getOwnPropertyDescriptors(other._extra) Object.getOwnPropertyDescriptors(other.#extra)
); );
return this as any; return this as any;
} }
@ -498,7 +498,7 @@ export class Struct<
value: T & ThisType<Overwrite<Overwrite<TExtra, T>, TFields>> value: T & ThisType<Overwrite<Overwrite<TExtra, T>, TFields>>
): Struct<TFields, TOmitInitKey, Overwrite<TExtra, T>, TPostDeserialized> { ): Struct<TFields, TOmitInitKey, Overwrite<TExtra, T>, TPostDeserialized> {
Object.defineProperties( Object.defineProperties(
this._extra, this.#extra,
Object.getOwnPropertyDescriptors(value) Object.getOwnPropertyDescriptors(value)
); );
return this as any; return this as any;
@ -532,7 +532,7 @@ export class Struct<
callback?: StructPostDeserialized<TFields, TPostSerialize> callback?: StructPostDeserialized<TFields, TPostSerialize>
): Struct<TFields, TOmitInitKey, TExtra, TPostSerialize>; ): Struct<TFields, TOmitInitKey, TExtra, TPostSerialize>;
public postDeserialize(callback?: StructPostDeserialized<TFields, any>) { public postDeserialize(callback?: StructPostDeserialized<TFields, any>) {
this._postDeserialized = callback; this.#postDeserialized = callback;
return this as any; return this as any;
} }
@ -550,12 +550,12 @@ export class Struct<
): ValueOrPromise< ): ValueOrPromise<
StructDeserializedResult<TFields, TExtra, TPostDeserialized> StructDeserializedResult<TFields, TExtra, TPostDeserialized>
> { > {
const structValue = new StructValue(this._extra); const structValue = new StructValue(this.#extra);
let promise = SyncPromise.resolve(); let promise = SyncPromise.resolve();
const startPosition = stream.position; const startPosition = stream.position;
for (const [name, definition] of this._fields) { for (const [name, definition] of this.#fields) {
promise = promise promise = promise
.then(() => .then(() =>
definition.deserialize(this.options, stream, structValue) definition.deserialize(this.options, stream, structValue)
@ -580,14 +580,11 @@ export class Struct<
return promise return promise
.then(() => { .then(() => {
const object = structValue.value; const value = structValue.value;
// Run `postDeserialized` // Run `postDeserialized`
if (this._postDeserialized) { if (this.#postDeserialized) {
const override = this._postDeserialized.call( const override = this.#postDeserialized.call(value, value);
object,
object
);
// If it returns a new value, use that as result // If it returns a new value, use that as result
// Otherwise it only inspects/mutates the object in place. // Otherwise it only inspects/mutates the object in place.
if (override !== undefined) { if (override !== undefined) {
@ -595,7 +592,7 @@ export class Struct<
} }
} }
return object; return value;
}) })
.valueOrPromise(); .valueOrPromise();
} }
@ -620,7 +617,7 @@ export class Struct<
} }
} else { } else {
structValue = new StructValue({}); structValue = new StructValue({});
for (const [name, definition] of this._fields) { for (const [name, definition] of this.#fields) {
const fieldValue = definition.create( const fieldValue = definition.create(
this.options, this.options,
structValue, structValue,
@ -633,7 +630,7 @@ export class Struct<
let structSize = 0; let structSize = 0;
const fieldsInfo: { fieldValue: StructFieldValue; size: number }[] = []; const fieldsInfo: { fieldValue: StructFieldValue; size: number }[] = [];
for (const [name] of this._fields) { for (const [name] of this.#fields) {
const fieldValue = structValue.get(name); const fieldValue = structValue.get(name);
const size = fieldValue.getSize(); const size = fieldValue.getSize();
fieldsInfo.push({ fieldValue, size }); fieldsInfo.push({ fieldValue, size });

View file

@ -55,10 +55,10 @@ export const SyncPromise: SyncPromiseStatic = {
}; };
class PendingSyncPromise<T> implements SyncPromise<T> { class PendingSyncPromise<T> implements SyncPromise<T> {
private promise: PromiseLike<T>; #promise: PromiseLike<T>;
public constructor(promise: PromiseLike<T>) { public constructor(promise: PromiseLike<T>) {
this.promise = promise; this.#promise = promise;
} }
public then<TResult1 = T, TResult2 = never>( public then<TResult1 = T, TResult2 = never>(
@ -72,20 +72,20 @@ class PendingSyncPromise<T> implements SyncPromise<T> {
| undefined | undefined
) { ) {
return new PendingSyncPromise<TResult1 | TResult2>( return new PendingSyncPromise<TResult1 | TResult2>(
this.promise.then(onfulfilled, onrejected) this.#promise.then(onfulfilled, onrejected)
); );
} }
public valueOrPromise(): T | PromiseLike<T> { public valueOrPromise(): T | PromiseLike<T> {
return this.promise; return this.#promise;
} }
} }
class ResolvedSyncPromise<T> implements SyncPromise<T> { class ResolvedSyncPromise<T> implements SyncPromise<T> {
private value: T; #value: T;
public constructor(value: T) { public constructor(value: T) {
this.value = value; this.#value = value;
} }
public then<TResult1 = T>( public then<TResult1 = T>(
@ -97,19 +97,19 @@ class ResolvedSyncPromise<T> implements SyncPromise<T> {
if (!onfulfilled) { if (!onfulfilled) {
return this as any; return this as any;
} }
return SyncPromise.try(() => onfulfilled(this.value)); return SyncPromise.try(() => onfulfilled(this.#value));
} }
public valueOrPromise(): T | PromiseLike<T> { public valueOrPromise(): T | PromiseLike<T> {
return this.value; return this.#value;
} }
} }
class RejectedSyncPromise<T> implements SyncPromise<T> { class RejectedSyncPromise<T> implements SyncPromise<T> {
private reason: any; #reason: any;
public constructor(reason: any) { public constructor(reason: any) {
this.reason = reason; this.#reason = reason;
} }
public then<TResult1 = T, TResult2 = never>( public then<TResult1 = T, TResult2 = never>(
@ -125,10 +125,10 @@ class RejectedSyncPromise<T> implements SyncPromise<T> {
if (!onrejected) { if (!onrejected) {
return this as any; return this as any;
} }
return SyncPromise.try(() => onrejected(this.reason)); return SyncPromise.try(() => onrejected(this.#reason));
} }
public valueOrPromise(): T | PromiseLike<T> { public valueOrPromise(): T | PromiseLike<T> {
throw this.reason; throw this.#reason;
} }
} }