diff --git a/api-documents/kit.watcher.addlistener.md b/api-documents/kit.watcher.addlistener.md
index e226b51..60b5752 100644
--- a/api-documents/kit.watcher.addlistener.md
+++ b/api-documents/kit.watcher.addlistener.md
@@ -7,7 +7,7 @@
Signature:
```typescript
-addListener>(type: K, callback: EventListener, K>): this;
+addListener>(type: K, callback: EventListener, K>, options?: AddEventListenerOptions): this;
```
## Parameters
@@ -16,6 +16,7 @@ addListener>(type: K, callback: EventListener<T>, K> | |
+| options | AddEventListenerOptions | (Optional) |
Returns:
diff --git a/api-documents/kit.watcher.md b/api-documents/kit.watcher.md
index 30965b4..59c4cd2 100644
--- a/api-documents/kit.watcher.md
+++ b/api-documents/kit.watcher.md
@@ -49,7 +49,7 @@ export declare abstract class Watchernode to key
If the key is changed, the same node will call through forEachRemove
then forEach
|
| [defaultStarterForThen()](./kit.watcher.defaultstarterforthen.md) | | |
| [dismissSingleModeWarning()](./kit.watcher.dismisssinglemodewarning.md) | | Dismiss the warning that let you enable single mode but the warning is false positive. |
diff --git a/api-documents/kit.webextensionmessage.eventregistry.md b/api-documents/kit.webextensionmessage.eventregistry.md
index d749d5e..daab9fd 100644
--- a/api-documents/kit.webextensionmessage.eventregistry.md
+++ b/api-documents/kit.webextensionmessage.eventregistry.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-protected get eventRegistry(): EventRegistry;
+protected get eventRegistry(): Emitter;
```
diff --git a/api-documents/kit.webextensionmessage.md b/api-documents/kit.webextensionmessage.md
index 97304d4..9892f7a 100644
--- a/api-documents/kit.webextensionmessage.md
+++ b/api-documents/kit.webextensionmessage.md
@@ -22,7 +22,7 @@ export declare class WebExtensionMessage
| --- | --- | --- | --- |
| [domain](./kit.webextensionmessage.domain.md) | | string | Same message name within different domain won't collide with each other. |
| [enableLog](./kit.webextensionmessage.enablelog.md) | | boolean | |
-| [eventRegistry](./kit.webextensionmessage.eventregistry.md) | | EventRegistry | |
+| [eventRegistry](./kit.webextensionmessage.eventregistry.md) | | Emitter<any> | |
| [events](./kit.webextensionmessage.events.md) | | { readonly \[K in keyof Message\]: [UnboundedRegistry](./kit.unboundedregistry.md)<Message\[K\]>; } | Event listeners |
| [log](./kit.webextensionmessage.log.md) | | (...args: unknown\[\]) => void | |
| [logFormatter](./kit.webextensionmessage.logformatter.md) | | (instance: this, key: string, data: unknown) => unknown\[\] | |
diff --git a/doc/holoflows-kit.api.report.md b/doc/holoflows-kit.api.report.md
index ddf1d53..ccd41a9 100644
--- a/doc/holoflows-kit.api.report.md
+++ b/doc/holoflows-kit.api.report.md
@@ -7,7 +7,7 @@
///
import { Emitter } from '@servie/events';
-import { EventListener as EventListener_2 } from '@servie/events';
+import type { EventListener as EventListener_2 } from '@servie/events';
// @public
export function assertEnvironment(env: Environment): void;
@@ -258,7 +258,7 @@ export class ValueRef {
export abstract class Watcher implements PromiseLike> {
constructor(liveSelector: LiveSelector);
// (undocumented)
- addListener>(type: K, callback: EventListener_2, K>): this;
+ addListener>(type: K, callback: EventListener_2, K>, options?: AddEventListenerOptions): this;
assignKeys(keyAssigner: (node: T, index: number, arr: readonly T[]) => Q): this;
// (undocumented)
protected defaultStarterForThen(): void;
@@ -348,10 +348,8 @@ export class WebExtensionMessage {
get domain(): string;
// (undocumented)
enableLog: boolean;
- // Warning: (ae-forgotten-export) The symbol "EventRegistry" needs to be exported by the entry point index.d.ts
- //
// (undocumented)
- protected get eventRegistry(): EventRegistry;
+ protected get eventRegistry(): Emitter;
get events(): {
readonly [K in keyof Message]: UnboundedRegistry;
};
diff --git a/src/DOM/Watcher.ts b/src/DOM/Watcher.ts
index 3f16059..10f4d61 100644
--- a/src/DOM/Watcher.ts
+++ b/src/DOM/Watcher.ts
@@ -10,12 +10,13 @@
* - Event watcher (based on addEventListener)
*/
import { DOMProxy, DOMProxyOptions } from './Proxy.js'
-import { Emitter, EventListener } from '@servie/events'
+import type { EventListener } from '@servie/events'
import type { LiveSelector } from './LiveSelector.js'
import { Deadline, requestIdleCallback } from '../util/requestIdleCallback.js'
import { isNil, uniqWith, intersectionWith, differenceWith } from 'lodash-es'
import { timeout } from '../util/timeout.js'
+import { createEventTarget } from '../util/EventTarget.js'
/**
* Use LiveSelector to watch dom change
@@ -23,8 +24,7 @@ import { timeout } from '../util/timeout.js'
export abstract class Watcher
implements PromiseLike>
{
- private eventEmitter: Emitter> = new Emitter()
- private removeListenerWeakMap = new Map>()
+ private events = createEventTarget>()
/**
* The liveSelector that this object holds.
*/
@@ -320,28 +320,28 @@ export abstract class Watcher 0) {
+ if (this.events.has('onIteration') && changedNodes.length + goneKeys.length + newKeys.length > 0) {
// Make a copy to prevent modifications
const newMap = new Map(newKeys.map((key) => [key, findFromNew(key)!]))
const removedMap = new Map(goneKeys.map((key) => [key, findFromLast(key)!]))
const currentMap = new Map(thisKeyList.map((key) => [key, findFromNew(key)!]))
- this.eventEmitter.emit('onIteration', {
+ this.events.emit('onIteration', {
new: newMap,
removed: removedMap,
current: currentMap,
})
}
- if (this.eventEmitter.$.onChange.size !== 0)
+ if (this.events.has('onChange'))
for (const [oldNode, newNode, oldKey, newKey] of changedNodes) {
- this.eventEmitter.emit('onChange', { oldValue: oldNode, newValue: newNode, oldKey, newKey })
+ this.events.emit('onChange', { oldValue: oldNode, newValue: newNode, oldKey, newKey })
}
- if (this.eventEmitter.$.onRemove.size !== 0)
+ if (this.events.has('onRemove'))
for (const key of goneKeys) {
- this.eventEmitter.emit('onRemove', { key, value: findFromLast(key)! })
+ this.events.emit('onRemove', { key, value: findFromLast(key)! })
}
- if (this.eventEmitter.$.onAdd.size !== 0)
+ if (this.events.has('onAdd'))
for (const key of newKeys) {
- this.eventEmitter.emit('onAdd', { key, value: findFromNew(key)! })
+ this.events.emit('onAdd', { key, value: findFromNew(key)! })
}
// For firstDOMProxy
const first = currentIteration[0]
@@ -392,7 +392,7 @@ export abstract class Watcher>(type: K, callback: EventListener, K>): this {
- if (!this.removeListenerWeakMap.has(type)) this.removeListenerWeakMap.set(type, new WeakMap())
- this.removeListenerWeakMap.get(type)!.set(callback, this.eventEmitter.on(type, callback))
+ addListener>(
+ type: K,
+ callback: EventListener, K>,
+ options?: AddEventListenerOptions,
+ ): this {
+ this.events.add(type, callback, options)
return this
}
removeListener>(type: K, callback: EventListener, K>): this {
- this.removeListenerWeakMap.get(type)?.get(callback)?.()
+ this.events.remove(type, callback)
return this
}
//#endregion
diff --git a/src/DOM/Watchers/EventWatcher.ts b/src/DOM/Watchers/EventWatcher.ts
index c4ccfc9..8a3810c 100644
--- a/src/DOM/Watchers/EventWatcher.ts
+++ b/src/DOM/Watchers/EventWatcher.ts
@@ -27,6 +27,7 @@ export class EventWatcher<
this.requestIdleCallback(this.scheduleWatcherCheck, { timeout: 500 })
}
override startWatch(signal?: AbortSignal) {
+ super.startWatch()
signal?.addEventListener(
'abort',
() => {
diff --git a/src/Extension/MessageChannel.ts b/src/Extension/MessageChannel.ts
index d39ebc3..e9de14d 100644
--- a/src/Extension/MessageChannel.ts
+++ b/src/Extension/MessageChannel.ts
@@ -2,6 +2,7 @@
/* eslint-disable no-bitwise */
import { Emitter } from '@servie/events'
import { EventIterator } from 'event-iterator'
+import { createEventTarget } from '../util/EventTarget.js'
import { Environment, getEnvironment, isEnvironment } from './Context.js'
/**
@@ -231,9 +232,9 @@ export class WebExtensionMessage {
}
public enableLog = false
public log: (...args: unknown[]) => void = console.log
- #eventRegistry: EventRegistry = new Emitter()
+ #eventRegistry = createEventTarget>()
protected get eventRegistry() {
- return this.#eventRegistry
+ return this.#eventRegistry.emitter
}
}
//#region Internal message handling
@@ -272,7 +273,7 @@ function shouldAcceptThisMessage(target: BoundTarget) {
function UnboundedRegistry(
instance: WebExtensionMessage,
eventName: string,
- eventListener: Emitter,
+ eventListener: ReturnType>,
): UnboundedRegistry {
//#region Batch message
let pausing = false
@@ -294,17 +295,11 @@ function UnboundedRegistry(
})
}
let binder: TargetBoundEventRegistry
- const removeListenerWeakMap = new WeakMap()
function on(cb: (data: T) => void, options?: TargetBoundEventListenerOptions) {
- const off = eventListener.on(eventName, cb)
- removeListenerWeakMap.set(cb, off)
-
- if (options?.once) eventListener.on(eventName, off)
- if (options?.signal) options.signal.addEventListener('abort', off, { once: true })
- return off
+ return eventListener.add(eventName, cb, options)
}
function off(cb: (data: T) => void) {
- removeListenerWeakMap.get(cb)?.()
+ eventListener.remove(eventName, cb)
}
function pause() {
pausing = true
@@ -343,7 +338,6 @@ function UnboundedRegistry(
}
return self
}
-type EventRegistry = Emitter>
type BoundTarget =
| { kind: 'tab'; id: number }
| { kind: 'target'; target: MessageTarget | Environment }
diff --git a/src/util/EventTarget.ts b/src/util/EventTarget.ts
new file mode 100644
index 0000000..e8ae891
--- /dev/null
+++ b/src/util/EventTarget.ts
@@ -0,0 +1,33 @@
+import { Emitter, EventListener, once, ValidEventArgs } from '@servie/events'
+
+/** @internal */
+export function createEventTarget() {
+ const emitter = new Emitter()
+ const offWeakMap = new Map>()
+ function getOff(key: PropertyKey) {
+ if (offWeakMap.has(key)) return offWeakMap.get(key)!
+ const off = new WeakMap()
+ offWeakMap.set(key, off)
+ return off
+ }
+
+ return {
+ has(key: keyof T) {
+ return (emitter.$[key]?.size || 0) > 0
+ },
+ add(event: K, callback: EventListener, options?: AddEventListenerOptions) {
+ const off = options?.once ? once(emitter, event, callback) : emitter.on(event, callback)
+
+ getOff(event).set(callback, off)
+ options?.signal?.addEventListener('abort', off, { once: true })
+ return off
+ },
+ remove(event: K, callback: EventListener) {
+ getOff(event).get(callback)?.()
+ },
+ emitter,
+ emit(type: K, ...args: ValidEventArgs) {
+ emitter.emit(type, ...args)
+ }
+ }
+}