|
| 1 | +import type { Action, EventHandler } from './types' |
| 2 | + |
| 3 | +/** |
| 4 | + * Defines a new typed event with a payload and tag. |
| 5 | + * |
| 6 | + * @template Tag - A unique identifier for the event. |
| 7 | + * @template P - The payload type. |
| 8 | + * |
| 9 | + * @param tag - The tag for the event. |
| 10 | + * @param _infer - Optionally used to infer payload type (can use `withPayload<T>()`). |
| 11 | + * |
| 12 | + * @returns The event that you can subscribe to and listen. |
| 13 | + * |
| 14 | + * @example |
| 15 | + * ```ts |
| 16 | + * const event = defineEvent('user.created') |
| 17 | + * |
| 18 | + * // or with a payload |
| 19 | + * const eventWithPayload = defineEvent('user.created', withPayload<{ id: number }>()) |
| 20 | + * ``` |
| 21 | + */ |
| 22 | +export function defineEvent<const Tag, P = void>( |
| 23 | + tag: Tag, |
| 24 | + // eslint-disable-next-line @typescript-eslint/no-unused-vars |
| 25 | + _infer?: P, |
| 26 | +): Action<P, Tag> { |
| 27 | + const handlers = new Set<EventHandler<P, Action<P, Tag>>>() |
| 28 | + |
| 29 | + const ev = ((payload: P): void => handlers.forEach(fn => fn(payload, tag))) as Action<P, Tag> |
| 30 | + |
| 31 | + // @ts-expect-error we only assign this property once here |
| 32 | + ev.tag = tag |
| 33 | + |
| 34 | + ev.on = function on(fn: EventHandler<P, Action<P, Tag>>): () => void { |
| 35 | + handlers.add(fn) |
| 36 | + |
| 37 | + return (): void => { |
| 38 | + handlers.delete(fn) |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + return ev |
| 43 | +} |
| 44 | + |
| 45 | +/** |
| 46 | + * Utility function to help infer payload types when defining events. |
| 47 | + * Does nothing at runtime. |
| 48 | + * |
| 49 | + * @example |
| 50 | + * ```ts |
| 51 | + * const event = defineEvent('user.updated', withPayload<{ id: string }>()) |
| 52 | + * ``` |
| 53 | + */ |
| 54 | +export const withPayload = (() => {}) as <T>() => T |
| 55 | + |
| 56 | +/** |
| 57 | + * A utility function to unsubscribe from multiple subscriptions at once. |
| 58 | + * |
| 59 | + * @param unsubscribers - An array of unsubscribe functions. |
| 60 | + * |
| 61 | + * @returns A single function that unsubscribes from all. |
| 62 | + * |
| 63 | + * @example |
| 64 | + * ```ts |
| 65 | + * const newUserEvent = defineEvent('user.created') |
| 66 | + * const deleteUserEvent = defineEvent('user.deleted') |
| 67 | + * |
| 68 | + * const unsubscribe = mergeSubscriptions( |
| 69 | + * newUserEvent.on(() => {}), |
| 70 | + * deleteUserEvent.on(() => {}), |
| 71 | + * ) |
| 72 | + * ``` |
| 73 | + */ |
| 74 | +export function mergeSubscriptions(...unsubscribers: Array<() => void>): () => void { |
| 75 | + return function unsubscribe(): void { |
| 76 | + unsubscribers.forEach(fn => fn()) |
| 77 | + } |
| 78 | +} |
0 commit comments