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/LICENSE +21 -0
- package/README.md +331 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/eventful.d.ts +153 -0
- package/dist/eventful.d.ts.map +1 -0
- package/dist/factory.d.ts +89 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/index.cjs +1126 -0
- package/dist/index.cjs.map +15 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1093 -0
- package/dist/index.js.map +15 -0
- package/dist/interop.d.ts +163 -0
- package/dist/interop.d.ts.map +1 -0
- package/dist/observe.d.ts +182 -0
- package/dist/observe.d.ts.map +1 -0
- package/dist/symbols.d.ts +6 -0
- package/dist/symbols.d.ts.map +1 -0
- package/dist/types.d.ts +139 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +107 -0
- package/src/errors.ts +10 -0
- package/src/eventful.ts +323 -0
- package/src/factory.ts +948 -0
- package/src/index.ts +71 -0
- package/src/interop.ts +271 -0
- package/src/observe.ts +734 -0
- package/src/symbols.ts +12 -0
- package/src/types.ts +206 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Steve Kinney
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# Event Emission
|
|
2
|
+
|
|
3
|
+
A lightweight, zero-dependency, type-safe event system with DOM EventTarget ergonomics and TC39 Observable interoperability. Use one event source with callbacks, async iterators, and RxJS without losing TypeScript safety.
|
|
4
|
+
|
|
5
|
+
## Tasting notes
|
|
6
|
+
|
|
7
|
+
- **Typed events** - Event maps keep payloads and event names in sync
|
|
8
|
+
- **Familiar API** - `addEventListener`, `removeEventListener`, `dispatchEvent`
|
|
9
|
+
- **Observable-ready** - Works with RxJS and other TC39 Observable libraries
|
|
10
|
+
- **Async iteration** - `for await...of` over events with backpressure options
|
|
11
|
+
- **Wildcard listeners** - Listen to `*` or namespaced `user:*` patterns
|
|
12
|
+
- **Observable state** - Proxy any object and emit change events automatically
|
|
13
|
+
- **AbortSignal support** - Cleanup with AbortController
|
|
14
|
+
- **No dependencies** - Framework-agnostic, works in Node, Bun, and browsers
|
|
15
|
+
|
|
16
|
+
## When to use it
|
|
17
|
+
|
|
18
|
+
- Typed app-level event buses
|
|
19
|
+
- Bridging DOM events to RxJS pipelines
|
|
20
|
+
- State objects that emit change events for UI updates
|
|
21
|
+
- Component/service emitters without stringly-typed payloads
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install event-emission
|
|
27
|
+
# or
|
|
28
|
+
bun add event-emission
|
|
29
|
+
# or
|
|
30
|
+
pnpm add event-emission
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { createEventTarget } from 'event-emission';
|
|
37
|
+
|
|
38
|
+
type UserEvents = {
|
|
39
|
+
'user:login': { userId: string; timestamp: Date };
|
|
40
|
+
'user:logout': { userId: string };
|
|
41
|
+
error: Error;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const events = createEventTarget<UserEvents>();
|
|
45
|
+
|
|
46
|
+
events.addEventListener('user:login', (event) => {
|
|
47
|
+
console.log(`User ${event.detail.userId} logged in at ${event.detail.timestamp}`);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
events.dispatchEvent({
|
|
51
|
+
type: 'user:login',
|
|
52
|
+
detail: { userId: '123', timestamp: new Date() },
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Core concepts
|
|
57
|
+
|
|
58
|
+
- **Event map**: a TypeScript type that maps event names to payload types.
|
|
59
|
+
- **Event shape**: `{ type: string; detail: Payload }` for all listeners.
|
|
60
|
+
- **Unsubscribe**: `addEventListener` returns a function to remove the listener.
|
|
61
|
+
|
|
62
|
+
## API overview
|
|
63
|
+
|
|
64
|
+
- `createEventTarget<E>(options?)`
|
|
65
|
+
- `createEventTarget(target, { observe: true, ... })`
|
|
66
|
+
- `Eventful<E>` base class
|
|
67
|
+
- Interop: `fromEventTarget`, `forwardToEventTarget`, `pipe`
|
|
68
|
+
- Utilities: `isObserved`, `getOriginal`
|
|
69
|
+
|
|
70
|
+
## createEventTarget
|
|
71
|
+
|
|
72
|
+
### `createEventTarget<E>(options?)`
|
|
73
|
+
|
|
74
|
+
Creates a typed event target.
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
type Events = {
|
|
78
|
+
message: { text: string };
|
|
79
|
+
error: Error;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const target = createEventTarget<Events>();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### Options
|
|
86
|
+
|
|
87
|
+
| Option | Type | Description |
|
|
88
|
+
| ----------------- | ---------------------------------------- | -------------------------------------------- |
|
|
89
|
+
| `onListenerError` | `(type: string, error: unknown) => void` | Custom error handler for listener exceptions |
|
|
90
|
+
|
|
91
|
+
If a listener throws and no `onListenerError` is provided, an `error` event is emitted. If there are no `error` listeners, the error is re-thrown.
|
|
92
|
+
|
|
93
|
+
### Observable state
|
|
94
|
+
|
|
95
|
+
Create state objects that emit change events:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const state = createEventTarget({ count: 0, user: { name: 'Ada' } }, { observe: true });
|
|
99
|
+
|
|
100
|
+
state.addEventListener('update', (event) => {
|
|
101
|
+
console.log('State changed:', event.detail.current);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
state.addEventListener('update:count', (event) => {
|
|
105
|
+
console.log(
|
|
106
|
+
`Count changed from ${event.detail.previous.count} to ${event.detail.value}`,
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
state.count = 1; // Triggers 'update' and 'update:count'
|
|
111
|
+
state.user.name = 'Grace'; // Triggers 'update' and 'update:user.name'
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Observe options:**
|
|
115
|
+
|
|
116
|
+
| Option | Type | Default | Description |
|
|
117
|
+
| --------------- | ------------------------------- | -------- | ---------------------------------- |
|
|
118
|
+
| `observe` | `boolean` | `false` | Enable property change observation |
|
|
119
|
+
| `deep` | `boolean` | `true` | Observe nested objects |
|
|
120
|
+
| `cloneStrategy` | `'shallow' \| 'deep' \| 'path'` | `'path'` | How to clone previous state |
|
|
121
|
+
|
|
122
|
+
**Update event details:**
|
|
123
|
+
|
|
124
|
+
- `update` and `update:path` events include `{ value, current, previous }`.
|
|
125
|
+
- Array mutators emit method events like `update:items.push` with `{ method, args, added, removed, current, previous }`.
|
|
126
|
+
|
|
127
|
+
## Event listeners
|
|
128
|
+
|
|
129
|
+
### `addEventListener(type, listener, options?)`
|
|
130
|
+
|
|
131
|
+
Adds a listener and returns an unsubscribe function.
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
const unsubscribe = events.addEventListener('message', (event) => {
|
|
135
|
+
console.log(event.detail.text);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
unsubscribe();
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Options:**
|
|
142
|
+
|
|
143
|
+
| Option | Type | Description |
|
|
144
|
+
| -------- | ------------- | -------------------------------------------- |
|
|
145
|
+
| `once` | `boolean` | Remove listener after first invocation |
|
|
146
|
+
| `signal` | `AbortSignal` | Abort signal to remove listener when aborted |
|
|
147
|
+
|
|
148
|
+
### `once(type, listener, options?)`
|
|
149
|
+
|
|
150
|
+
Adds a one-time listener.
|
|
151
|
+
|
|
152
|
+
### `removeEventListener(type, listener)`
|
|
153
|
+
|
|
154
|
+
Removes a specific listener.
|
|
155
|
+
|
|
156
|
+
### `removeAllListeners(type?)`
|
|
157
|
+
|
|
158
|
+
Removes all listeners, or all listeners for a type.
|
|
159
|
+
|
|
160
|
+
### Wildcard listeners
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
events.addWildcardListener('*', (event) => {
|
|
164
|
+
console.log(`Got ${event.originalType}:`, event.detail);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
events.addWildcardListener('user:*', (event) => {
|
|
168
|
+
console.log(`User event: ${event.originalType}`);
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Wildcard events include `{ type: pattern, originalType, detail }`.
|
|
173
|
+
|
|
174
|
+
## Async iteration
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
for await (const event of events.events('message', { bufferSize: 16 })) {
|
|
178
|
+
console.log('Received:', event.detail.text);
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Iterator options:**
|
|
183
|
+
|
|
184
|
+
| Option | Type | Default | Description |
|
|
185
|
+
| ------------------ | ------------------------------------------- | --------------- | ------------------------------ |
|
|
186
|
+
| `signal` | `AbortSignal` | - | Abort signal to stop iteration |
|
|
187
|
+
| `bufferSize` | `number` | `Infinity` | Maximum buffered events |
|
|
188
|
+
| `overflowStrategy` | `'drop-oldest' \| 'drop-latest' \| 'throw'` | `'drop-oldest'` | Behavior when buffer is full |
|
|
189
|
+
|
|
190
|
+
When `overflowStrategy` is `throw`, the iterator throws `BufferOverflowError`.
|
|
191
|
+
|
|
192
|
+
## Observable interoperability
|
|
193
|
+
|
|
194
|
+
### `subscribe(type, observer)`
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
const subscription = events.subscribe('message', {
|
|
198
|
+
next: (event) => console.log(event.detail),
|
|
199
|
+
error: (err) => console.error(err),
|
|
200
|
+
complete: () => console.log('Done'),
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
subscription.unsubscribe();
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### `toObservable()`
|
|
207
|
+
|
|
208
|
+
Returns an Observable that emits all events.
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { from } from 'rxjs';
|
|
212
|
+
import { filter, map } from 'rxjs/operators';
|
|
213
|
+
|
|
214
|
+
const observable = from(events);
|
|
215
|
+
|
|
216
|
+
observable
|
|
217
|
+
.pipe(
|
|
218
|
+
filter((event) => event.type === 'message'),
|
|
219
|
+
map((event) => event.detail.text),
|
|
220
|
+
)
|
|
221
|
+
.subscribe(console.log);
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Lifecycle
|
|
225
|
+
|
|
226
|
+
### `complete()`
|
|
227
|
+
|
|
228
|
+
Marks the event target as complete, clears listeners, and ends iterators.
|
|
229
|
+
|
|
230
|
+
### `clear()`
|
|
231
|
+
|
|
232
|
+
Removes all listeners without marking as complete.
|
|
233
|
+
|
|
234
|
+
## Eventful base class
|
|
235
|
+
|
|
236
|
+
Extend `Eventful` to build typed emitters:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { Eventful } from 'event-emission';
|
|
240
|
+
|
|
241
|
+
class UserService extends Eventful<{
|
|
242
|
+
'user:created': { id: string; name: string };
|
|
243
|
+
'user:deleted': { id: string };
|
|
244
|
+
error: Error;
|
|
245
|
+
}> {
|
|
246
|
+
createUser(name: string) {
|
|
247
|
+
const id = crypto.randomUUID();
|
|
248
|
+
this.dispatchEvent({ type: 'user:created', detail: { id, name } });
|
|
249
|
+
return id;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## DOM interoperability
|
|
255
|
+
|
|
256
|
+
### `fromEventTarget(domTarget, eventTypes, options?)`
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { fromEventTarget } from 'event-emission';
|
|
260
|
+
|
|
261
|
+
type ButtonEvents = {
|
|
262
|
+
click: MouseEvent;
|
|
263
|
+
focus: FocusEvent;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const button = document.getElementById('my-button');
|
|
267
|
+
const events = fromEventTarget<ButtonEvents>(button, ['click', 'focus']);
|
|
268
|
+
|
|
269
|
+
events.addEventListener('click', (event) => {
|
|
270
|
+
console.log('Button clicked!', event.detail);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
events.destroy();
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### `forwardToEventTarget(source, domTarget, options?)`
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import { createEventTarget, forwardToEventTarget } from 'event-emission';
|
|
280
|
+
|
|
281
|
+
const events = createEventTarget<{ custom: { value: number } }>();
|
|
282
|
+
const element = document.getElementById('target');
|
|
283
|
+
|
|
284
|
+
const unsubscribe = forwardToEventTarget(events, element);
|
|
285
|
+
|
|
286
|
+
events.dispatchEvent({ type: 'custom', detail: { value: 42 } });
|
|
287
|
+
|
|
288
|
+
unsubscribe();
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### `pipe(source, target, options?)`
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
import { createEventTarget, pipe } from 'event-emission';
|
|
295
|
+
|
|
296
|
+
const componentEvents = createEventTarget<{ ready: void }>();
|
|
297
|
+
const appBus = createEventTarget<{ ready: void }>();
|
|
298
|
+
|
|
299
|
+
const unsubscribe = pipe(componentEvents, appBus);
|
|
300
|
+
|
|
301
|
+
unsubscribe();
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Note: `pipe(source, target)` forwards all events via a wildcard listener. The instance method `events.pipe(target)` only forwards event types that already have listeners when you call it.
|
|
305
|
+
|
|
306
|
+
## Utilities
|
|
307
|
+
|
|
308
|
+
### `isObserved(obj)`
|
|
309
|
+
|
|
310
|
+
Checks if an object is an observed proxy.
|
|
311
|
+
|
|
312
|
+
### `getOriginal(proxy)`
|
|
313
|
+
|
|
314
|
+
Returns the original unproxied object.
|
|
315
|
+
|
|
316
|
+
## TypeScript types
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
import type {
|
|
320
|
+
EventfulEvent,
|
|
321
|
+
EventTargetLike,
|
|
322
|
+
ObservableLike,
|
|
323
|
+
Observer,
|
|
324
|
+
Subscription,
|
|
325
|
+
WildcardEvent,
|
|
326
|
+
AddEventListenerOptionsLike,
|
|
327
|
+
ObservableEventMap,
|
|
328
|
+
PropertyChangeDetail,
|
|
329
|
+
ArrayMutationDetail,
|
|
330
|
+
} from 'event-emission';
|
|
331
|
+
```
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown when an async iterator's event buffer overflows
|
|
3
|
+
* and the overflow strategy is 'throw'.
|
|
4
|
+
*/
|
|
5
|
+
export declare class BufferOverflowError extends Error {
|
|
6
|
+
constructor(eventType: string, bufferSize: number);
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;CAIlD"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { SymbolObservable } from './symbols';
|
|
2
|
+
import type { AddEventListenerOptionsLike, EventfulEvent, EventsIteratorOptions, EventTargetLike, ObservableLike, Observer, Subscription, WildcardEvent } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Abstract base class for typed event emitters with DOM EventTarget
|
|
5
|
+
* and TC39 Observable compatibility.
|
|
6
|
+
*
|
|
7
|
+
* Extend this class to create custom event emitters with typed events.
|
|
8
|
+
* The class provides:
|
|
9
|
+
* - DOM EventTarget compatible API (addEventListener, removeEventListener, dispatchEvent)
|
|
10
|
+
* - TC39 Observable interop (subscribe, Symbol.observable)
|
|
11
|
+
* - Async iteration support (events() method)
|
|
12
|
+
* - Wildcard listeners for namespaced events
|
|
13
|
+
* - Lifecycle management (complete(), completed)
|
|
14
|
+
*
|
|
15
|
+
* Listener errors are handled via 'error' event: if a listener throws,
|
|
16
|
+
* an 'error' event is emitted. If no 'error' listener is registered,
|
|
17
|
+
* the error is re-thrown (Node.js behavior).
|
|
18
|
+
*
|
|
19
|
+
* @template E - Event map type where keys are event names and values are event detail types.
|
|
20
|
+
*
|
|
21
|
+
* @example Basic usage
|
|
22
|
+
* ```typescript
|
|
23
|
+
* // Define your emitter with typed events
|
|
24
|
+
* class UserService extends Eventful<{
|
|
25
|
+
* 'user:created': { id: string; name: string };
|
|
26
|
+
* 'user:deleted': { id: string };
|
|
27
|
+
* error: Error;
|
|
28
|
+
* }> {
|
|
29
|
+
* createUser(name: string) {
|
|
30
|
+
* const id = crypto.randomUUID();
|
|
31
|
+
* // ... create user logic
|
|
32
|
+
* this.dispatchEvent({ type: 'user:created', detail: { id, name } });
|
|
33
|
+
* }
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* const service = new UserService();
|
|
37
|
+
* service.addEventListener('user:created', (event) => {
|
|
38
|
+
* console.log(`Created user: ${event.detail.name}`);
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @example TC39 Observable interop
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const service = new UserService();
|
|
45
|
+
*
|
|
46
|
+
* // Subscribe to all events
|
|
47
|
+
* service.subscribe({
|
|
48
|
+
* next: (event) => console.log(event.type, event.detail),
|
|
49
|
+
* complete: () => console.log('Service completed'),
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* // Use with RxJS or other Observable libraries
|
|
53
|
+
* import { from } from 'rxjs';
|
|
54
|
+
* const observable = from(service);
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @example Async iteration
|
|
58
|
+
* ```typescript
|
|
59
|
+
* const service = new UserService();
|
|
60
|
+
*
|
|
61
|
+
* // Iterate over events as async iterator
|
|
62
|
+
* for await (const event of service.events('user:created')) {
|
|
63
|
+
* console.log(`User created: ${event.detail.name}`);
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare abstract class Eventful<E extends Record<string, any>> {
|
|
68
|
+
#private;
|
|
69
|
+
/**
|
|
70
|
+
* Returns this observable for Symbol.observable interop.
|
|
71
|
+
*/
|
|
72
|
+
[SymbolObservable]: () => ObservableLike<EventfulEvent<E[keyof E]>>;
|
|
73
|
+
constructor();
|
|
74
|
+
/**
|
|
75
|
+
* Adds an event listener for the specified event type.
|
|
76
|
+
* Returns an unsubscribe function for convenience.
|
|
77
|
+
*/
|
|
78
|
+
addEventListener<K extends keyof E & string>(type: K, listener: (event: EventfulEvent<E[K]>) => void | Promise<void>, options?: AddEventListenerOptionsLike): () => void;
|
|
79
|
+
/**
|
|
80
|
+
* Removes an event listener for the specified event type.
|
|
81
|
+
*/
|
|
82
|
+
removeEventListener<K extends keyof E & string>(type: K, listener: (event: EventfulEvent<E[K]>) => void | Promise<void>): void;
|
|
83
|
+
/**
|
|
84
|
+
* Dispatches an event to all registered listeners.
|
|
85
|
+
* Returns false if the emitter has been completed, true otherwise.
|
|
86
|
+
*/
|
|
87
|
+
dispatchEvent<K extends keyof E & string>(event: EventfulEvent<E[K]>): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Adds a one-time listener for the specified event type.
|
|
90
|
+
* Returns an unsubscribe function.
|
|
91
|
+
*/
|
|
92
|
+
once<K extends keyof E & string>(type: K, listener: (event: EventfulEvent<E[K]>) => void | Promise<void>, options?: Omit<AddEventListenerOptionsLike, 'once'>): () => void;
|
|
93
|
+
/**
|
|
94
|
+
* Removes all listeners, or those of the specified event type.
|
|
95
|
+
*/
|
|
96
|
+
removeAllListeners<K extends keyof E & string>(type?: K): void;
|
|
97
|
+
/**
|
|
98
|
+
* Removes all listeners. Does not trigger completion.
|
|
99
|
+
*/
|
|
100
|
+
clear(): void;
|
|
101
|
+
/**
|
|
102
|
+
* Pipe events from this emitter to another target.
|
|
103
|
+
* Note: Only forwards events for types that have listeners when pipe() is called.
|
|
104
|
+
* Events for types registered after piping won't be forwarded automatically.
|
|
105
|
+
*/
|
|
106
|
+
pipe<T extends Record<string, any>>(target: EventTargetLike<T>, mapFn?: <K extends keyof E & string>(event: EventfulEvent<E[K]>) => EventfulEvent<T[keyof T & string]> | null): () => void;
|
|
107
|
+
/**
|
|
108
|
+
* Adds a wildcard listener that receives events matching the pattern.
|
|
109
|
+
* Use '*' for all events, or 'namespace:*' for namespaced events.
|
|
110
|
+
*/
|
|
111
|
+
addWildcardListener(pattern: '*' | `${string}:*`, listener: (event: WildcardEvent<E>) => void | Promise<void>, options?: AddEventListenerOptionsLike): () => void;
|
|
112
|
+
/**
|
|
113
|
+
* Removes a wildcard listener.
|
|
114
|
+
*/
|
|
115
|
+
removeWildcardListener(pattern: '*' | `${string}:*`, listener: (event: WildcardEvent<E>) => void | Promise<void>): void;
|
|
116
|
+
/**
|
|
117
|
+
* Subscribes an observer to all events (untyped).
|
|
118
|
+
*/
|
|
119
|
+
subscribe(observerOrNext: Observer<EventfulEvent<E[keyof E]>> | ((value: EventfulEvent<E[keyof E]>) => void)): Subscription;
|
|
120
|
+
/**
|
|
121
|
+
* Subscribes an observer to events of a specific type (typed).
|
|
122
|
+
*/
|
|
123
|
+
subscribe<K extends keyof E & string>(type: K, observerOrNext?: Observer<EventfulEvent<E[K]>> | ((value: EventfulEvent<E[K]>) => void), error?: (err: unknown) => void, completeHandler?: () => void): Subscription;
|
|
124
|
+
/**
|
|
125
|
+
* Returns an observable that emits all events.
|
|
126
|
+
*/
|
|
127
|
+
toObservable(): ObservableLike<EventfulEvent<E[keyof E]>>;
|
|
128
|
+
/**
|
|
129
|
+
* Marks the emitter as complete. Invokes complete() on all observable subscribers,
|
|
130
|
+
* ends all async iterators, and suppresses further emits/dispatches.
|
|
131
|
+
* Idempotent.
|
|
132
|
+
*/
|
|
133
|
+
complete(): void;
|
|
134
|
+
/**
|
|
135
|
+
* Returns true if the emitter has been completed.
|
|
136
|
+
*/
|
|
137
|
+
get completed(): boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Returns an async iterator over events of the specified type.
|
|
140
|
+
*
|
|
141
|
+
* @param type - The event type to iterate over
|
|
142
|
+
* @param options - Iterator options (signal, bufferSize, overflowStrategy)
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* for await (const event of emitter.events('foo')) {
|
|
147
|
+
* console.log(event.detail);
|
|
148
|
+
* }
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
events<K extends keyof E & string>(type: K, options?: EventsIteratorOptions): AsyncIterableIterator<EventfulEvent<E[K]>>;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=eventful.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eventful.d.ts","sourceRoot":"","sources":["../src/eventful.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type ObservableEventMap, type ObserveOptions } from './observe';
|
|
2
|
+
import type { EventTargetLike } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Options for createEventTarget.
|
|
5
|
+
*
|
|
6
|
+
* @property onListenerError - Custom error handler called when a listener throws.
|
|
7
|
+
* If not provided, errors are emitted as 'error' events or re-thrown.
|
|
8
|
+
*/
|
|
9
|
+
export interface CreateEventTargetOptions {
|
|
10
|
+
/** Custom error handler for listener errors. Receives event type and error. */
|
|
11
|
+
onListenerError?: (type: string, error: unknown) => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Options for createEventTarget with observe mode.
|
|
15
|
+
* Extends CreateEventTargetOptions with proxy observation settings.
|
|
16
|
+
*
|
|
17
|
+
* @property observe - Must be true to enable observation mode.
|
|
18
|
+
* @property deep - If true, nested objects are also observed (default: false).
|
|
19
|
+
* @property cloneStrategy - Strategy for cloning previous state: 'shallow', 'deep', or 'path'.
|
|
20
|
+
*/
|
|
21
|
+
export interface CreateEventTargetObserveOptions extends CreateEventTargetOptions, ObserveOptions {
|
|
22
|
+
/** Must be true to enable observation mode. */
|
|
23
|
+
observe: true;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates a type-safe event target with DOM EventTarget and TC39 Observable compatibility.
|
|
27
|
+
*
|
|
28
|
+
* @template E - Event map type where keys are event names and values are event detail types.
|
|
29
|
+
* @param opts - Optional configuration options.
|
|
30
|
+
* @returns A type-safe event target implementing EventTargetLike.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // Define event types
|
|
35
|
+
* type Events = {
|
|
36
|
+
* 'user:login': { userId: string };
|
|
37
|
+
* 'user:logout': { reason: string };
|
|
38
|
+
* };
|
|
39
|
+
*
|
|
40
|
+
* // Create event target
|
|
41
|
+
* const events = createEventTarget<Events>();
|
|
42
|
+
*
|
|
43
|
+
* // Add typed listener
|
|
44
|
+
* events.addEventListener('user:login', (event) => {
|
|
45
|
+
* console.log(`User logged in: ${event.detail.userId}`);
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* // Dispatch typed event
|
|
49
|
+
* events.dispatchEvent({ type: 'user:login', detail: { userId: '123' } });
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @overload Creates a basic event target
|
|
53
|
+
*/
|
|
54
|
+
export declare function createEventTarget<E extends Record<string, any>>(opts?: CreateEventTargetOptions): EventTargetLike<E>;
|
|
55
|
+
/**
|
|
56
|
+
* Creates an observable proxy that dispatches events when properties change.
|
|
57
|
+
*
|
|
58
|
+
* @template T - The type of object to observe.
|
|
59
|
+
* @param target - The object to wrap with an observable proxy.
|
|
60
|
+
* @param opts - Configuration options with observe: true.
|
|
61
|
+
* @returns The proxied object with EventTargetLike methods mixed in.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // Create observable state
|
|
66
|
+
* const state = createEventTarget({ count: 0, user: { name: 'Alice' } }, {
|
|
67
|
+
* observe: true,
|
|
68
|
+
* deep: true,
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* // Listen for any update
|
|
72
|
+
* state.addEventListener('update', (event) => {
|
|
73
|
+
* console.log('State changed:', event.detail.current);
|
|
74
|
+
* });
|
|
75
|
+
*
|
|
76
|
+
* // Listen for specific property changes
|
|
77
|
+
* state.addEventListener('update:count', (event) => {
|
|
78
|
+
* console.log('Count changed to:', event.detail.value);
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* // Mutations trigger events automatically
|
|
82
|
+
* state.count = 1; // Triggers 'update' and 'update:count'
|
|
83
|
+
* state.user.name = 'Bob'; // Triggers 'update' and 'update:user.name'
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @overload Wraps an object with a Proxy that dispatches events on mutations
|
|
87
|
+
*/
|
|
88
|
+
export declare function createEventTarget<T extends object>(target: T, opts: CreateEventTargetObserveOptions): T & EventTargetLike<ObservableEventMap<T>>;
|
|
89
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACpB,MAAM,WAAW,CAAC;AAEnB,OAAO,KAAK,EAGV,eAAe,EAKhB,MAAM,SAAS,CAAC;AAcjB;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACvC,+EAA+E;IAC/E,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1D;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,+BACf,SAAQ,wBAAwB,EAAE,cAAc;IAChD,+CAA+C;IAC/C,OAAO,EAAE,IAAI,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC7D,IAAI,CAAC,EAAE,wBAAwB,GAC9B,eAAe,CAAC,CAAC,CAAC,CAAC;AAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAChD,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,+BAA+B,GACpC,CAAC,GAAG,eAAe,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC"}
|