diff --git a/libraries/adb-daemon-webusb/src/observer.ts b/libraries/adb-daemon-webusb/src/observer.ts index e662033c..5fa8ae88 100644 --- a/libraries/adb-daemon-webusb/src/observer.ts +++ b/libraries/adb-daemon-webusb/src/observer.ts @@ -1,5 +1,5 @@ import type { DeviceObserver } from "@yume-chan/adb"; -import { EventEmitter } from "@yume-chan/event"; +import { EventEmitter, StickyEventEmitter } from "@yume-chan/event"; import { AdbDaemonWebUsbDevice, @@ -34,7 +34,7 @@ export class AdbDaemonWebUsbDeviceObserver #onDeviceRemove = new EventEmitter(); onDeviceRemove = this.#onDeviceRemove.event; - #onListChange = new EventEmitter(); + #onListChange = new StickyEventEmitter(); onListChange = this.#onListChange.event; current: AdbDaemonWebUsbDevice[] = []; diff --git a/libraries/adb/src/server/observer.ts b/libraries/adb/src/server/observer.ts index 85dbc33b..b8ec2f60 100644 --- a/libraries/adb/src/server/observer.ts +++ b/libraries/adb/src/server/observer.ts @@ -1,4 +1,4 @@ -import { EventEmitter } from "@yume-chan/event"; +import { EventEmitter, StickyEventEmitter } from "@yume-chan/event"; import { Ref } from "../utils/index.js"; @@ -95,6 +95,17 @@ export class AdbServerDeviceObserverOwner { throw options.signal.reason; } + const onDeviceAdd = new EventEmitter(); + const onDeviceRemove = new EventEmitter(); + const onListChange = new StickyEventEmitter(); + const onError = new StickyEventEmitter(); + + const observer = { onDeviceAdd, onDeviceRemove, onListChange, onError }; + // Register `observer` before `#connect`. + // Because `#connect` might immediately receive some data + // and want to trigger observers + this.#observers.push(observer); + this.#stream ??= this.#connect(); const stream = await this.#stream; @@ -103,14 +114,6 @@ export class AdbServerDeviceObserverOwner { throw options.signal.reason; } - const onDeviceAdd = new EventEmitter(); - const onDeviceRemove = new EventEmitter(); - const onListChange = new EventEmitter(); - const onError = new EventEmitter(); - - const observer = { onDeviceAdd, onDeviceRemove, onListChange, onError }; - this.#observers.push(observer); - const ref = new Ref(options); const stop = async () => { diff --git a/libraries/event/src/event-emitter.ts b/libraries/event/src/event-emitter.ts index d7f417c0..adc25c73 100644 --- a/libraries/event/src/event-emitter.ts +++ b/libraries/event/src/event-emitter.ts @@ -62,7 +62,7 @@ export class EventEmitter implements Disposable { fire(e: TEvent) { for (const info of this.listeners.slice()) { - info.listener.apply(info.thisArg, [e, ...info.args]); + info.listener.call(info.thisArg, e, ...info.args); } } diff --git a/libraries/event/src/index.ts b/libraries/event/src/index.ts index 1d74c000..7e74f200 100644 --- a/libraries/event/src/index.ts +++ b/libraries/event/src/index.ts @@ -1,4 +1,5 @@ export * from "./disposable.js"; -export * from "./event.js"; export * from "./event-emitter.js"; +export * from "./event.js"; +export * from "./sticky-event-emitter.js"; export * from "./utils.js"; diff --git a/libraries/event/src/sticky-event-emitter.ts b/libraries/event/src/sticky-event-emitter.ts new file mode 100644 index 00000000..b1290dc4 --- /dev/null +++ b/libraries/event/src/sticky-event-emitter.ts @@ -0,0 +1,23 @@ +import { EventEmitter, type EventListenerInfo } from "./event-emitter.js"; +import type { RemoveEventListener } from "./event.js"; + +export class StickyEventEmitter extends EventEmitter< + TEvent, + TResult +> { + #value: TEvent | undefined; + + protected override addEventListener( + info: EventListenerInfo, + ): RemoveEventListener { + if (this.#value) { + info.listener.call(info.thisArg, this.#value, ...info.args); + } + return super.addEventListener(info); + } + + override fire(e: TEvent): void { + this.#value = e; + super.fire(e); + } +}