import { Context } from '@dxos/context'; import { type MaybePromise } from '@dxos/util'; import { type CleanupFn } from './cleanup.ts'; export type Effect = () => CleanupFn | undefined; interface EventEmitterLike { on(event: string, cb: (data?: any) => void): void; off(event: string, cb: (data?: any) => void): void; } export type ListenerOptions = { weak?: boolean; once?: boolean; }; type EventCallback = (data: T) => MaybePromise; /** * An EventEmitter variant that does not do event multiplexing and represents a single event. * * ## Typical usage: * ```javascript * class Model { * public readonly update = new Event(); * private readonly privateEvent = new Event(); * * onUpdate(data: UpdateData) { * ... * this.update.emit(data); * } * } * * model.update.on(data => { * ... * }); * ``` * * ## Advantages over traditional EventEmitter: * 1. User describes explicitly what events a class has as they are defined as class fields. * 2. User can explicitly define event visibility (public, protected, private). * 3. Having events as class fields allows the compiler to check for correct event usage. * 4. User can type the data that event will be emitting. * 5. Uses composition instead of inheritance. * 6. Removes the cases where event names intersect when used in cases with inheritance. * 7. Remove the need to namespace events when developing a class with events that will be used as a base-class. */ export declare class Event implements ReadOnlyEvent { /** * Wrap objects that have on/off style event emitters. */ static wrap(emitter: EventEmitterLike, eventName: string): Event; private readonly _listeners; private readonly _effects; /** * Emit an event. * In most cases should only be called by the class or entity containing the event. * All listeners are called in order of subscription with persistent ones first. * Listeners are called synchronously in the same stack. * A thrown exception in the listener will stop the event from being emitted to the rest of the listeners. * * @param data param that will be passed to all listeners. */ emit(data: T): void; /** * Emit an event and wait for async listeners to complete. * In most cases should only be called by the class or entity containing the event. * All listeners are called in order of subscription with persistent ones first. * Listeners are called sequentially. * * @param data param that will be passed to all listeners. */ emitAsync(data: T): Promise; /** * Register an event listener. * If provided callback was already registered as once-listener, it is made permanent. * * @param callback * @param options.weak If true, the callback will be weakly referenced and will be garbage collected if no other references to it exist. * @returns function that unsubscribes this event listener */ on(callback: EventCallback): CleanupFn; on(ctx: Context, callback: EventCallback, options?: ListenerOptions): CleanupFn; /** * Unsubscribe this callback from new events. Includes persistent and once-listeners. * NOTE: It is recommended to use `Event.on`'s return value instead. * If the callback is not subscribed this is no-op. * * @param callback */ off(callback: (data: T) => void): void; /** * Register a callback to be called only once when the next event is emitted. * If this callback is already registered as permanent listener, this is no-op. * * @param callback */ once(callback: (data: T) => void): CleanupFn; once(ctx: Context, callback: (data: T) => void): CleanupFn; /** * An async iterator that iterates over events. * This iterator runs indefinitely. */ [Symbol.asyncIterator](): AsyncIterator; /** * Returns a promise that resolves with the first event emitted that matches the provided predicate. * * @param predicate */ waitFor(predicate: (data: T) => boolean): Promise; /** * Returns a promise that resolves once a specific number of events was emitted since this method was called. * * @param expectedCount */ waitForCount(expectedCount: number): Promise; /** * Similar to waitFor, but the promise resolves immediately if the condition is already true. */ waitForCondition(predicate: () => boolean): Promise; /** * Returns the number of persistent listeners. */ listenerCount(): number; /** * Add a side effect that will be activated once the event has at least one subscriber. * The provided callback can return a function that will be used to clean up after the last subscriber unsubscribes from the event. * The API is similar to `useEffect` from React. * * ## Example: * ```typescript * event.addEffect(() => { * // do stuff * return () => { * // clean-up * }; * }); * ``` * * @returns Callback that will remove this effect once called. */ addEffect(effect: Effect): CleanupFn; /** * Triggers an event with at least `timeout` milliseconds between each event. * If the event is triggered more often, the event is delayed until the timeout is reached. * If event is emitted for the first time or event wasn't fired for `timeout` milliseconds, * the event is emitted after `timeout / 8` ms. */ debounce(timeout?: number): Event; /** * Turn any variant of `Event` into an `Event` discarding the callback parameter. */ discardParameter(): Event; /** * Pipe the events into another event. * @param event */ pipeInto(event: Event): CleanupFn; /** * Overridden to not return implementation details. */ toJSON(): { listenerCount: number; }; private _addListener; private _runEffects; private _cleanupEffects; } /** * A version of Event class which only has subscribe methods. * Useful in cases where you want to explicitly prohibit calling `emit` method. */ export interface ReadOnlyEvent { /** * Register an event listener. * If provided callback was already registered as once-listener, it is made permanent. * * @param callback * @param options.weak If true, the callback will be weakly referenced and will be garbage collected if no other references to it exist. * @returns function that unsubscribes this event listener */ on(callback: (data: T) => void): CleanupFn; on(ctx: Context, callback: (data: T) => void, options?: ListenerOptions): CleanupFn; /** * Unsubscribes this callback from new events. Includes persistent and once-listeners. * NOTE: It is recommended to us `Event.on`'s return value. * If the callback is not subscribed this is no-op. * * @param callback */ off(callback: (data: T) => void): void; /** * Register a callback to be called only once when the next event is emitted. * If this callback is already registered as permanent listener, this is no-op. * * @param callback */ once(callback: (data: T) => void): CleanupFn; /** * An async iterator that iterates over events. * This iterator runs indefinitely. */ [Symbol.asyncIterator](): AsyncIterator; /** * Returns a promise that resolves with the first event emitted that matches the provided predicate. * * @param predicate */ waitFor(predicate: (data: T) => boolean): Promise; /** * Returns a promise that resolves once a specific number of events was emitted since this method was called. * * @param expectedCount */ waitForCount(expectedCount: number): Promise; /** * Turn any variant of `Event` into an `Event` discarding the callback parameter. */ discardParameter(): Event; /** * Triggers an event with at least `timeout` milliseconds between each event. * If the event is triggered more often, the event is delayed until the timeout is reached. * If event is emitted for the first time or event wasn't fired for `timeout` milliseconds, * the event is emitted after `timeout / 8` ms. */ debounce(timeout?: number): Event; } export {}; //# sourceMappingURL=events.d.ts.map