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 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
+ ```
@@ -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"}