event-emission 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/symbols.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Symbol.observable polyfill for TC39 Observable interop.
3
+ * This ensures the symbol exists even in environments that don't support it natively.
4
+ */
5
+ export const SymbolObservable: symbol =
6
+ (typeof Symbol === 'function' && (Symbol as { observable?: symbol }).observable) ||
7
+ Symbol.for('@@observable');
8
+
9
+ // Assign to make Symbol.observable available
10
+ if (typeof Symbol === 'function') {
11
+ (Symbol as { observable?: symbol }).observable = SymbolObservable;
12
+ }
package/src/types.ts ADDED
@@ -0,0 +1,206 @@
1
+ import { SymbolObservable } from './symbols';
2
+
3
+ // Declare queueMicrotask globally so we don't require DOM lib
4
+ // This is available in Node.js and all modern browsers
5
+ declare global {
6
+ function queueMicrotask(callback: () => void): void;
7
+ }
8
+
9
+ /**
10
+ * Event structure passed to listeners.
11
+ */
12
+ export interface EventfulEvent<Detail> {
13
+ /** The event type identifier. */
14
+ type: string;
15
+ /** The event payload data. */
16
+ detail: Detail;
17
+ }
18
+
19
+ /**
20
+ * Event passed to wildcard listeners, includes the original event type.
21
+ */
22
+ export interface WildcardEvent<E extends Record<string, unknown>> {
23
+ type: '*' | `${string}:*`;
24
+ originalType: keyof E & string;
25
+ detail: E[keyof E];
26
+ }
27
+
28
+ /**
29
+ * Minimal AbortSignal lookalike so consumers do not need DOM libs.
30
+ */
31
+ export interface MinimalAbortSignal {
32
+ readonly aborted: boolean;
33
+ readonly reason?: unknown;
34
+ addEventListener: (type: 'abort', listener: () => void, options?: unknown) => void;
35
+ removeEventListener: (type: 'abort', listener: () => void, options?: unknown) => void;
36
+ }
37
+
38
+ /**
39
+ * Options for addEventListener.
40
+ */
41
+ export type AddEventListenerOptionsLike = {
42
+ once?: boolean;
43
+ signal?: MinimalAbortSignal;
44
+ };
45
+
46
+ /**
47
+ * TC39 Observable observer interface.
48
+ */
49
+ export interface Observer<T> {
50
+ next?: (value: T) => void;
51
+ error?: (err: unknown) => void;
52
+ complete?: () => void;
53
+ }
54
+
55
+ /**
56
+ * TC39 Observable subscription interface.
57
+ */
58
+ export interface Subscription {
59
+ unsubscribe(): void;
60
+ readonly closed: boolean;
61
+ }
62
+
63
+ /**
64
+ * TC39 Observable-like interface.
65
+ */
66
+ export interface ObservableLike<T> {
67
+ subscribe(
68
+ observerOrNext?: Observer<T> | ((value: T) => void),
69
+ error?: (err: unknown) => void,
70
+ complete?: () => void,
71
+ ): Subscription;
72
+ [SymbolObservable](): ObservableLike<T>;
73
+ }
74
+
75
+ /**
76
+ * Overflow strategy for async iterator buffer.
77
+ */
78
+ export type OverflowStrategy = 'drop-oldest' | 'drop-latest' | 'throw';
79
+
80
+ /**
81
+ * Options for the events() async iterator.
82
+ */
83
+ export interface AsyncIteratorOptions {
84
+ signal?: MinimalAbortSignal;
85
+ bufferSize?: number;
86
+ overflowStrategy?: OverflowStrategy;
87
+ }
88
+
89
+ /**
90
+ * Options for the events() async iterator (alias for AsyncIteratorOptions).
91
+ */
92
+ export type EventsIteratorOptions = AsyncIteratorOptions;
93
+
94
+ /**
95
+ * Options for interop helpers.
96
+ */
97
+ export interface InteropOptions {
98
+ signal?: MinimalAbortSignal;
99
+ }
100
+
101
+ /**
102
+ * Minimal DOM Event interface for interop.
103
+ */
104
+ export interface DOMEventLike {
105
+ type: string;
106
+ detail?: unknown;
107
+ }
108
+
109
+ /**
110
+ * Minimal DOM EventTarget interface for interop.
111
+ */
112
+ export interface DOMEventTargetLike {
113
+ addEventListener(type: string, listener: (event: DOMEventLike) => void): void;
114
+ removeEventListener(type: string, listener: (event: DOMEventLike) => void): void;
115
+ dispatchEvent(event: DOMEventLike): boolean;
116
+ }
117
+
118
+ /**
119
+ * Internal listener record type.
120
+ */
121
+ export type Listener<E> = {
122
+ fn: (event: EventfulEvent<E>) => void | Promise<void>;
123
+ once?: boolean;
124
+ signal?: MinimalAbortSignal;
125
+ abortHandler?: () => void;
126
+ };
127
+
128
+ /**
129
+ * Internal wildcard listener record type.
130
+ */
131
+ export type WildcardListener<E extends Record<string, unknown>> = {
132
+ fn: (event: WildcardEvent<E>) => void | Promise<void>;
133
+ pattern: '*' | `${string}:*`;
134
+ once?: boolean;
135
+ signal?: MinimalAbortSignal;
136
+ abortHandler?: () => void;
137
+ };
138
+
139
+ /**
140
+ * Type-safe event target interface compatible with DOM EventTarget
141
+ * and TC39 Observable patterns.
142
+ */
143
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Generic constraint requires any for flexibility
144
+ export interface EventTargetLike<E extends Record<string, any>> {
145
+ addEventListener: <K extends keyof E & string>(
146
+ type: K,
147
+ listener: (event: EventfulEvent<E[K]>) => void | Promise<void>,
148
+ options?: AddEventListenerOptionsLike,
149
+ ) => () => void;
150
+ removeEventListener: <K extends keyof E & string>(
151
+ type: K,
152
+ listener: (event: EventfulEvent<E[K]>) => void | Promise<void>,
153
+ ) => void;
154
+ dispatchEvent: <K extends keyof E & string>(event: EventfulEvent<E[K]>) => boolean;
155
+ clear: () => void;
156
+
157
+ // Ergonomics
158
+ once: <K extends keyof E & string>(
159
+ type: K,
160
+ listener: (event: EventfulEvent<E[K]>) => void | Promise<void>,
161
+ options?: Omit<AddEventListenerOptionsLike, 'once'>,
162
+ ) => () => void;
163
+ removeAllListeners: <K extends keyof E & string>(type?: K) => void;
164
+ /**
165
+ * Pipe events from this emitter to another target.
166
+ * Note: Only forwards events for types that have listeners when pipe() is called.
167
+ * Events for types registered after piping won't be forwarded automatically.
168
+ */
169
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Generic constraint requires any
170
+ pipe: <T extends Record<string, any>>(
171
+ target: EventTargetLike<T>,
172
+ mapFn?: <K extends keyof E & string>(
173
+ event: EventfulEvent<E[K]>,
174
+ ) => EventfulEvent<T[keyof T & string]> | null,
175
+ ) => () => void;
176
+ complete: () => void;
177
+ readonly completed: boolean;
178
+
179
+ // Wildcard support
180
+ addWildcardListener: (
181
+ pattern: '*' | `${string}:*`,
182
+ listener: (event: WildcardEvent<E>) => void | Promise<void>,
183
+ options?: AddEventListenerOptionsLike,
184
+ ) => () => void;
185
+ removeWildcardListener: (
186
+ pattern: '*' | `${string}:*`,
187
+ listener: (event: WildcardEvent<E>) => void | Promise<void>,
188
+ ) => void;
189
+
190
+ // Observable interop
191
+ subscribe: <K extends keyof E & string>(
192
+ type: K,
193
+ observerOrNext?:
194
+ | Observer<EventfulEvent<E[K]>>
195
+ | ((value: EventfulEvent<E[K]>) => void),
196
+ error?: (err: unknown) => void,
197
+ complete?: () => void,
198
+ ) => Subscription;
199
+ toObservable: () => ObservableLike<EventfulEvent<E[keyof E]>>;
200
+
201
+ // Async iterator
202
+ events: <K extends keyof E & string>(
203
+ type: K,
204
+ options?: AsyncIteratorOptions,
205
+ ) => AsyncIterableIterator<EventfulEvent<E[K]>>;
206
+ }