event-emission 0.2.0 → 0.2.1
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/README.md +80 -24
- package/dist/event-emission.d.ts +13 -14
- package/dist/event-emission.d.ts.map +1 -1
- package/dist/factory.d.ts +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/index.cjs +474 -285
- package/dist/index.cjs.map +8 -9
- package/dist/index.d.ts +1 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +449 -278
- package/dist/index.js.map +8 -9
- package/dist/interoperability.cjs +1605 -0
- package/dist/interoperability.cjs.map +15 -0
- package/dist/{interop.d.ts → interoperability.d.ts} +2 -1
- package/dist/interoperability.d.ts.map +1 -0
- package/dist/interoperability.js +1555 -0
- package/dist/interoperability.js.map +15 -0
- package/dist/observable.cjs +286 -0
- package/dist/observable.cjs.map +11 -0
- package/dist/observable.js +253 -0
- package/dist/observable.js.map +11 -0
- package/dist/observe.cjs +344 -0
- package/dist/observe.cjs.map +10 -0
- package/dist/observe.d.ts +6 -1
- package/dist/observe.d.ts.map +1 -1
- package/dist/observe.js +313 -0
- package/dist/observe.js.map +10 -0
- package/dist/symbols.d.ts +1 -1
- package/dist/types.cjs +35 -0
- package/dist/types.cjs.map +9 -0
- package/dist/types.d.ts +60 -25
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -0
- package/dist/types.js.map +9 -0
- package/package.json +25 -1
- package/src/event-emission.ts +26 -20
- package/src/factory.ts +538 -218
- package/src/index.ts +4 -33
- package/src/{interop.ts → interoperability.ts} +24 -6
- package/src/observe.ts +54 -17
- package/src/symbols.ts +1 -1
- package/src/types.ts +75 -30
- package/dist/interop.d.ts.map +0 -1
package/src/factory.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from './observe';
|
|
8
8
|
import { SymbolObservable } from './symbols';
|
|
9
9
|
import type {
|
|
10
|
+
AddEventListenerOptionsLike,
|
|
10
11
|
AsyncIteratorOptions,
|
|
11
12
|
EmissionEvent,
|
|
12
13
|
EventTargetLike,
|
|
@@ -23,13 +24,40 @@ import type {
|
|
|
23
24
|
*/
|
|
24
25
|
function matchesWildcard(eventType: string, pattern: string): boolean {
|
|
25
26
|
if (pattern === '*') return true;
|
|
26
|
-
|
|
27
|
-
const namespace = pattern.slice(0, -2);
|
|
28
|
-
return eventType.startsWith(namespace + ':');
|
|
29
|
-
}
|
|
30
|
-
return false;
|
|
27
|
+
return pattern.endsWith(':*') && eventType.startsWith(pattern.slice(0, -2) + ':');
|
|
31
28
|
}
|
|
32
29
|
|
|
30
|
+
type EventPathStruct = {
|
|
31
|
+
invocationTarget: unknown;
|
|
32
|
+
invocationTargetInShadowTree: boolean;
|
|
33
|
+
shadowAdjustedTarget: unknown;
|
|
34
|
+
relatedTarget: unknown;
|
|
35
|
+
touchTargets: unknown[];
|
|
36
|
+
rootOfClosedTree: boolean;
|
|
37
|
+
slotInClosedTree: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type EventDispatchState = {
|
|
41
|
+
dispatchFlag: boolean;
|
|
42
|
+
initializedFlag: boolean;
|
|
43
|
+
stopPropagationFlag: boolean;
|
|
44
|
+
stopImmediatePropagationFlag: boolean;
|
|
45
|
+
canceledFlag: boolean;
|
|
46
|
+
inPassiveListenerFlag: boolean;
|
|
47
|
+
composedFlag: boolean;
|
|
48
|
+
eventPhase: number;
|
|
49
|
+
currentTarget: unknown;
|
|
50
|
+
target: unknown;
|
|
51
|
+
timeStamp: number;
|
|
52
|
+
path: EventPathStruct[];
|
|
53
|
+
type: string;
|
|
54
|
+
bubbles: boolean;
|
|
55
|
+
cancelable: boolean;
|
|
56
|
+
isTrusted: boolean;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const EVENT_STATE = Symbol('event-emission:event-state');
|
|
60
|
+
|
|
33
61
|
/**
|
|
34
62
|
* Options for createEventTarget.
|
|
35
63
|
*
|
|
@@ -46,7 +74,7 @@ export interface CreateEventTargetOptions {
|
|
|
46
74
|
* Extends CreateEventTargetOptions with proxy observation settings.
|
|
47
75
|
*
|
|
48
76
|
* @property observe - Must be true to enable observation mode.
|
|
49
|
-
* @property deep - If true, nested objects are also observed (default:
|
|
77
|
+
* @property deep - If true, nested objects are also observed (default: true).
|
|
50
78
|
* @property cloneStrategy - Strategy for cloning previous state: 'shallow', 'deep', or 'path'.
|
|
51
79
|
*/
|
|
52
80
|
export interface CreateEventTargetObserveOptions
|
|
@@ -159,6 +187,7 @@ export function createEventTarget<T extends object, E extends Record<string, any
|
|
|
159
187
|
const proxy = createObservableProxy(target, eventTarget, {
|
|
160
188
|
deep: opts.deep,
|
|
161
189
|
cloneStrategy: opts.cloneStrategy,
|
|
190
|
+
deepClone: opts.deepClone,
|
|
162
191
|
});
|
|
163
192
|
|
|
164
193
|
// Copy eventTarget methods onto the proxy
|
|
@@ -209,50 +238,238 @@ export function createEventTarget<T extends object, E extends Record<string, any
|
|
|
209
238
|
function createEventTargetInternal<E extends Record<string, any>>(
|
|
210
239
|
opts?: CreateEventTargetOptions,
|
|
211
240
|
): EventTargetLike<E> {
|
|
212
|
-
const listeners = new Map<string,
|
|
241
|
+
const listeners = new Map<string, Array<Listener<E[keyof E]>>>();
|
|
213
242
|
const wildcardListeners = new Set<WildcardListener<E>>();
|
|
214
243
|
let isCompleted = false;
|
|
215
244
|
const completionCallbacks = new Set<() => void>();
|
|
216
245
|
|
|
246
|
+
const now = () =>
|
|
247
|
+
typeof globalThis.performance?.now === 'function'
|
|
248
|
+
? globalThis.performance.now()
|
|
249
|
+
: Date.now();
|
|
250
|
+
|
|
251
|
+
const initializeEventState = (
|
|
252
|
+
state: EventDispatchState,
|
|
253
|
+
type: string,
|
|
254
|
+
bubbles: boolean,
|
|
255
|
+
cancelable: boolean,
|
|
256
|
+
) => {
|
|
257
|
+
state.initializedFlag = true;
|
|
258
|
+
state.stopPropagationFlag = false;
|
|
259
|
+
state.stopImmediatePropagationFlag = false;
|
|
260
|
+
state.canceledFlag = false;
|
|
261
|
+
state.isTrusted = false;
|
|
262
|
+
state.target = null;
|
|
263
|
+
state.currentTarget = null;
|
|
264
|
+
state.eventPhase = 0;
|
|
265
|
+
state.type = type;
|
|
266
|
+
state.bubbles = bubbles;
|
|
267
|
+
state.cancelable = cancelable;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const setCanceledFlag = (state: EventDispatchState) => {
|
|
271
|
+
if (state.cancelable && !state.inPassiveListenerFlag) {
|
|
272
|
+
state.canceledFlag = true;
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
217
276
|
/**
|
|
218
|
-
* Helper to create a DOM-compatible
|
|
277
|
+
* Helper to create a DOM-compatible event.
|
|
219
278
|
*/
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
279
|
+
const createEvent = <T>(
|
|
280
|
+
type: string,
|
|
281
|
+
detail: T,
|
|
282
|
+
init?: {
|
|
283
|
+
bubbles?: boolean;
|
|
284
|
+
cancelable?: boolean;
|
|
285
|
+
composed?: boolean;
|
|
286
|
+
timeStamp?: number;
|
|
287
|
+
target?: unknown;
|
|
288
|
+
currentTarget?: unknown;
|
|
289
|
+
eventPhase?: number;
|
|
290
|
+
},
|
|
291
|
+
): EmissionEvent<T> => {
|
|
292
|
+
const state: EventDispatchState = {
|
|
293
|
+
dispatchFlag: false,
|
|
294
|
+
initializedFlag: true,
|
|
295
|
+
stopPropagationFlag: false,
|
|
296
|
+
stopImmediatePropagationFlag: false,
|
|
297
|
+
canceledFlag: false,
|
|
298
|
+
inPassiveListenerFlag: false,
|
|
299
|
+
composedFlag: Boolean(init?.composed),
|
|
300
|
+
eventPhase: init?.eventPhase ?? 0,
|
|
301
|
+
currentTarget: init?.currentTarget ?? init?.target ?? null,
|
|
302
|
+
target: init?.target ?? null,
|
|
303
|
+
timeStamp: init?.timeStamp ?? now(),
|
|
304
|
+
path: [],
|
|
305
|
+
type,
|
|
306
|
+
bubbles: Boolean(init?.bubbles),
|
|
307
|
+
cancelable: Boolean(init?.cancelable),
|
|
227
308
|
isTrusted: false,
|
|
228
|
-
timeStamp: Date.now(),
|
|
229
|
-
NONE: 0,
|
|
230
|
-
CAPTURING_PHASE: 1,
|
|
231
|
-
AT_TARGET: 2,
|
|
232
|
-
BUBBLING_PHASE: 3,
|
|
233
|
-
composedPath: () => [target],
|
|
234
|
-
stopImmediatePropagation: () => {},
|
|
235
|
-
stopPropagation: () => {},
|
|
236
309
|
};
|
|
237
310
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
{
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
311
|
+
const event = { detail } as EmissionEvent<T>;
|
|
312
|
+
Object.defineProperties(event, {
|
|
313
|
+
type: {
|
|
314
|
+
get: () => state.type,
|
|
315
|
+
enumerable: true,
|
|
316
|
+
configurable: true,
|
|
317
|
+
},
|
|
318
|
+
bubbles: {
|
|
319
|
+
get: () => state.bubbles,
|
|
320
|
+
enumerable: true,
|
|
321
|
+
configurable: true,
|
|
322
|
+
},
|
|
323
|
+
cancelable: {
|
|
324
|
+
get: () => state.cancelable,
|
|
325
|
+
enumerable: true,
|
|
326
|
+
configurable: true,
|
|
327
|
+
},
|
|
328
|
+
cancelBubble: {
|
|
329
|
+
get: () => state.stopPropagationFlag,
|
|
330
|
+
set: (value: boolean) => {
|
|
331
|
+
if (value) state.stopPropagationFlag = true;
|
|
332
|
+
},
|
|
333
|
+
enumerable: true,
|
|
334
|
+
configurable: true,
|
|
335
|
+
},
|
|
336
|
+
composed: {
|
|
337
|
+
get: () => state.composedFlag,
|
|
338
|
+
enumerable: true,
|
|
339
|
+
configurable: true,
|
|
340
|
+
},
|
|
341
|
+
currentTarget: {
|
|
342
|
+
get: () => state.currentTarget,
|
|
343
|
+
enumerable: true,
|
|
344
|
+
configurable: true,
|
|
345
|
+
},
|
|
346
|
+
defaultPrevented: {
|
|
347
|
+
get: () => state.canceledFlag,
|
|
348
|
+
enumerable: true,
|
|
349
|
+
configurable: true,
|
|
350
|
+
},
|
|
351
|
+
eventPhase: {
|
|
352
|
+
get: () => state.eventPhase,
|
|
353
|
+
enumerable: true,
|
|
354
|
+
configurable: true,
|
|
355
|
+
},
|
|
356
|
+
isTrusted: {
|
|
357
|
+
get: () => state.isTrusted,
|
|
358
|
+
enumerable: true,
|
|
359
|
+
configurable: true,
|
|
360
|
+
},
|
|
361
|
+
returnValue: {
|
|
362
|
+
get: () => !state.canceledFlag,
|
|
363
|
+
set: (value: boolean) => {
|
|
364
|
+
if (value === false) setCanceledFlag(state);
|
|
365
|
+
},
|
|
366
|
+
enumerable: true,
|
|
367
|
+
configurable: true,
|
|
368
|
+
},
|
|
369
|
+
srcElement: {
|
|
370
|
+
get: () => state.target,
|
|
371
|
+
enumerable: true,
|
|
372
|
+
configurable: true,
|
|
373
|
+
},
|
|
374
|
+
target: {
|
|
375
|
+
get: () => state.target,
|
|
376
|
+
enumerable: true,
|
|
377
|
+
configurable: true,
|
|
378
|
+
},
|
|
379
|
+
timeStamp: {
|
|
380
|
+
get: () => state.timeStamp,
|
|
381
|
+
enumerable: true,
|
|
382
|
+
configurable: true,
|
|
383
|
+
},
|
|
384
|
+
composedPath: {
|
|
385
|
+
value: () => state.path.map((entry) => entry.invocationTarget),
|
|
386
|
+
enumerable: true,
|
|
387
|
+
configurable: true,
|
|
388
|
+
},
|
|
389
|
+
initEvent: {
|
|
390
|
+
value: (newType: string, bubbles = false, cancelable = false) => {
|
|
391
|
+
if (state.dispatchFlag) return;
|
|
392
|
+
initializeEventState(state, newType, Boolean(bubbles), Boolean(cancelable));
|
|
393
|
+
},
|
|
394
|
+
enumerable: true,
|
|
395
|
+
configurable: true,
|
|
396
|
+
},
|
|
397
|
+
preventDefault: {
|
|
398
|
+
value: () => setCanceledFlag(state),
|
|
399
|
+
enumerable: true,
|
|
400
|
+
configurable: true,
|
|
401
|
+
},
|
|
402
|
+
stopImmediatePropagation: {
|
|
403
|
+
value: () => {
|
|
404
|
+
state.stopPropagationFlag = true;
|
|
405
|
+
state.stopImmediatePropagationFlag = true;
|
|
406
|
+
},
|
|
407
|
+
enumerable: true,
|
|
408
|
+
configurable: true,
|
|
409
|
+
},
|
|
410
|
+
stopPropagation: {
|
|
411
|
+
value: () => {
|
|
412
|
+
state.stopPropagationFlag = true;
|
|
253
413
|
},
|
|
414
|
+
enumerable: true,
|
|
415
|
+
configurable: true,
|
|
416
|
+
},
|
|
417
|
+
NONE: { value: 0, enumerable: true, configurable: true },
|
|
418
|
+
CAPTURING_PHASE: { value: 1, enumerable: true, configurable: true },
|
|
419
|
+
AT_TARGET: { value: 2, enumerable: true, configurable: true },
|
|
420
|
+
BUBBLING_PHASE: { value: 3, enumerable: true, configurable: true },
|
|
421
|
+
[EVENT_STATE]: {
|
|
422
|
+
value: state,
|
|
423
|
+
enumerable: false,
|
|
424
|
+
configurable: false,
|
|
254
425
|
},
|
|
255
|
-
)
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
return event;
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const getEventState = (event: EmissionEvent<unknown>): EventDispatchState | undefined =>
|
|
432
|
+
(event as { [EVENT_STATE]?: EventDispatchState })[EVENT_STATE];
|
|
433
|
+
|
|
434
|
+
const normalizeAddListenerOptions = (
|
|
435
|
+
options?: AddEventListenerOptionsLike | boolean,
|
|
436
|
+
) => {
|
|
437
|
+
if (typeof options === 'boolean') {
|
|
438
|
+
return {
|
|
439
|
+
capture: options,
|
|
440
|
+
passive: false,
|
|
441
|
+
once: false,
|
|
442
|
+
signal: null as AddEventListenerOptionsLike['signal'] | null,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return {
|
|
447
|
+
capture: Boolean(options?.capture),
|
|
448
|
+
passive: Boolean(options?.passive),
|
|
449
|
+
once: Boolean(options?.once),
|
|
450
|
+
signal: options?.signal ?? null,
|
|
451
|
+
};
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const normalizeCaptureOption = (options?: { capture?: boolean } | boolean) => {
|
|
455
|
+
if (typeof options === 'boolean') return options;
|
|
456
|
+
return Boolean(options?.capture);
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const removeListenerRecord = (type: string, record: Listener<E[keyof E]>) => {
|
|
460
|
+
if (record.removed) return;
|
|
461
|
+
record.removed = true;
|
|
462
|
+
|
|
463
|
+
const list = listeners.get(type);
|
|
464
|
+
if (list) {
|
|
465
|
+
const idx = list.indexOf(record);
|
|
466
|
+
if (idx >= 0) list.splice(idx, 1);
|
|
467
|
+
if (list.length === 0) listeners.delete(type);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (record.signal && record.abortHandler) {
|
|
471
|
+
record.signal.removeEventListener('abort', record.abortHandler);
|
|
472
|
+
}
|
|
256
473
|
};
|
|
257
474
|
|
|
258
475
|
// Helper to handle listener errors: emit 'error' event or re-throw if no listener
|
|
@@ -267,33 +484,35 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
267
484
|
}
|
|
268
485
|
|
|
269
486
|
const errorListeners = listeners.get('error');
|
|
270
|
-
if (errorListeners && errorListeners.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
try {
|
|
275
|
-
const fn = rec.fn as (event: EmissionEvent<unknown>) => void | Promise<void>;
|
|
276
|
-
void fn(errorEvent as EmissionEvent<E[keyof E]>);
|
|
277
|
-
} catch {
|
|
278
|
-
// Swallow errors from error handlers to prevent infinite loops
|
|
279
|
-
}
|
|
280
|
-
if (rec.once) errorListeners.delete(rec);
|
|
281
|
-
}
|
|
487
|
+
if (errorListeners && errorListeners.length > 0) {
|
|
488
|
+
dispatchEvent({ type: 'error', detail: error } as Parameters<
|
|
489
|
+
EventTargetLike<E>['dispatchEvent']
|
|
490
|
+
>[0]);
|
|
282
491
|
} else {
|
|
283
492
|
// No 'error' listener - re-throw (Node.js behavior)
|
|
284
493
|
throw error;
|
|
285
494
|
}
|
|
286
495
|
};
|
|
287
496
|
|
|
288
|
-
const notifyWildcardListeners = (
|
|
497
|
+
const notifyWildcardListeners = (
|
|
498
|
+
eventType: string,
|
|
499
|
+
event: EmissionEvent<E[keyof E]>,
|
|
500
|
+
) => {
|
|
289
501
|
if (wildcardListeners.size === 0) return;
|
|
290
502
|
|
|
291
503
|
for (const rec of Array.from(wildcardListeners)) {
|
|
292
504
|
if (!matchesWildcard(eventType, rec.pattern)) continue;
|
|
293
505
|
|
|
294
|
-
// Create wildcard event based on
|
|
295
|
-
const
|
|
296
|
-
|
|
506
|
+
// Create wildcard event based on a DOM-compatible event
|
|
507
|
+
const baseEvent = createEvent(rec.pattern, event.detail, {
|
|
508
|
+
target,
|
|
509
|
+
currentTarget: target,
|
|
510
|
+
eventPhase: 2,
|
|
511
|
+
bubbles: event.bubbles,
|
|
512
|
+
cancelable: event.cancelable,
|
|
513
|
+
composed: event.composed,
|
|
514
|
+
});
|
|
515
|
+
const wildcardEvent: WildcardEvent<E> = Object.defineProperties(baseEvent, {
|
|
297
516
|
originalType: { value: eventType, enumerable: true, configurable: true },
|
|
298
517
|
}) as WildcardEvent<E>;
|
|
299
518
|
|
|
@@ -317,6 +536,11 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
317
536
|
} finally {
|
|
318
537
|
if (rec.once) wildcardListeners.delete(rec);
|
|
319
538
|
}
|
|
539
|
+
|
|
540
|
+
const state = getEventState(wildcardEvent);
|
|
541
|
+
if (state?.stopImmediatePropagationFlag || state?.stopPropagationFlag) {
|
|
542
|
+
break;
|
|
543
|
+
}
|
|
320
544
|
}
|
|
321
545
|
};
|
|
322
546
|
|
|
@@ -325,36 +549,57 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
325
549
|
listener,
|
|
326
550
|
options,
|
|
327
551
|
) => {
|
|
328
|
-
if (isCompleted) {
|
|
552
|
+
if (isCompleted || !listener) {
|
|
329
553
|
// Return no-op unsubscribe if already completed
|
|
330
554
|
return () => {};
|
|
331
555
|
}
|
|
332
556
|
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
};
|
|
339
|
-
let set = listeners.get(type);
|
|
340
|
-
if (!set) {
|
|
341
|
-
set = new Set();
|
|
342
|
-
listeners.set(type, set);
|
|
557
|
+
const { capture, passive, once, signal } = normalizeAddListenerOptions(options);
|
|
558
|
+
let list = listeners.get(type);
|
|
559
|
+
if (!list) {
|
|
560
|
+
list = [];
|
|
561
|
+
listeners.set(type, list);
|
|
343
562
|
}
|
|
344
|
-
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
record.signal.removeEventListener('abort', record.abortHandler);
|
|
563
|
+
|
|
564
|
+
for (const existing of list) {
|
|
565
|
+
if (existing.original === listener && existing.capture === capture) {
|
|
566
|
+
return () =>
|
|
567
|
+
removeEventListener(type, listener, options as boolean | { capture?: boolean });
|
|
350
568
|
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const original = listener as Listener<E[keyof E]>['original'];
|
|
572
|
+
const callback =
|
|
573
|
+
typeof listener === 'function'
|
|
574
|
+
? (listener as Listener<E[keyof E]>['callback'])
|
|
575
|
+
: (event: EmissionEvent<E[keyof E]>) =>
|
|
576
|
+
(
|
|
577
|
+
listener as {
|
|
578
|
+
handleEvent: Listener<E[keyof E]>['callback'];
|
|
579
|
+
}
|
|
580
|
+
).handleEvent(event);
|
|
581
|
+
|
|
582
|
+
const record: Listener<E[keyof E]> = {
|
|
583
|
+
type,
|
|
584
|
+
original,
|
|
585
|
+
callback,
|
|
586
|
+
capture,
|
|
587
|
+
passive,
|
|
588
|
+
once,
|
|
589
|
+
signal,
|
|
590
|
+
removed: false,
|
|
351
591
|
};
|
|
352
|
-
|
|
353
|
-
|
|
592
|
+
list.push(record);
|
|
593
|
+
|
|
594
|
+
const unsubscribe = () => removeListenerRecord(type, record);
|
|
595
|
+
|
|
596
|
+
if (signal) {
|
|
597
|
+
const onAbort = () => removeListenerRecord(type, record);
|
|
354
598
|
record.abortHandler = onAbort;
|
|
355
|
-
|
|
356
|
-
if (
|
|
599
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
600
|
+
if (signal.aborted) onAbort();
|
|
357
601
|
}
|
|
602
|
+
|
|
358
603
|
return unsubscribe;
|
|
359
604
|
};
|
|
360
605
|
|
|
@@ -366,6 +611,11 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
366
611
|
if (isCompleted) return () => {};
|
|
367
612
|
|
|
368
613
|
const opts2 = options ?? {};
|
|
614
|
+
for (const existing of wildcardListeners) {
|
|
615
|
+
if (existing.pattern === pattern && existing.fn === listener) {
|
|
616
|
+
return () => removeWildcardListener(pattern, listener);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
369
619
|
const record: WildcardListener<E> = {
|
|
370
620
|
fn: listener,
|
|
371
621
|
pattern,
|
|
@@ -395,37 +645,44 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
395
645
|
pattern,
|
|
396
646
|
listener,
|
|
397
647
|
) => {
|
|
398
|
-
for (const record of wildcardListeners) {
|
|
648
|
+
for (const record of Array.from(wildcardListeners)) {
|
|
399
649
|
if (record.pattern === pattern && record.fn === listener) {
|
|
400
650
|
wildcardListeners.delete(record);
|
|
401
651
|
if (record.signal && record.abortHandler) {
|
|
402
652
|
record.signal.removeEventListener('abort', record.abortHandler);
|
|
403
653
|
}
|
|
404
|
-
break;
|
|
405
654
|
}
|
|
406
655
|
}
|
|
407
656
|
};
|
|
408
657
|
|
|
409
|
-
const
|
|
410
|
-
|
|
658
|
+
const invokeListeners = (
|
|
659
|
+
eventType: string,
|
|
660
|
+
event: EmissionEvent<E[keyof E]>,
|
|
661
|
+
phase: 'capturing' | 'bubbling',
|
|
662
|
+
listenersSnapshot: Array<Listener<E[keyof E]>>,
|
|
663
|
+
) => {
|
|
664
|
+
const state = getEventState(event);
|
|
665
|
+
if (!state || state.stopPropagationFlag) return;
|
|
411
666
|
|
|
412
|
-
|
|
413
|
-
|
|
667
|
+
state.currentTarget = target;
|
|
668
|
+
state.target = target;
|
|
669
|
+
state.eventPhase = event.AT_TARGET;
|
|
414
670
|
|
|
415
|
-
|
|
416
|
-
|
|
671
|
+
for (const rec of listenersSnapshot) {
|
|
672
|
+
if (rec.removed) continue;
|
|
673
|
+
if (phase === 'capturing' && !rec.capture) continue;
|
|
674
|
+
if (phase === 'bubbling' && rec.capture) continue;
|
|
675
|
+
|
|
676
|
+
if (rec.once) removeListenerRecord(rec.type, rec);
|
|
417
677
|
|
|
418
|
-
|
|
419
|
-
if (!set || set.size === 0) return true;
|
|
420
|
-
for (const rec of Array.from(set)) {
|
|
678
|
+
if (rec.passive) state.inPassiveListenerFlag = true;
|
|
421
679
|
try {
|
|
422
|
-
const res = rec.
|
|
680
|
+
const res = rec.callback.call(state.currentTarget, event);
|
|
423
681
|
if (res && typeof res.then === 'function') {
|
|
424
682
|
res.catch((error) => {
|
|
425
683
|
try {
|
|
426
|
-
handleListenerError(
|
|
684
|
+
handleListenerError(eventType, error);
|
|
427
685
|
} catch (rethrown) {
|
|
428
|
-
// Re-throw async errors via queueMicrotask to preserve stack trace
|
|
429
686
|
queueMicrotask(() => {
|
|
430
687
|
throw rethrown;
|
|
431
688
|
});
|
|
@@ -433,41 +690,116 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
433
690
|
});
|
|
434
691
|
}
|
|
435
692
|
} catch (error) {
|
|
436
|
-
handleListenerError(
|
|
693
|
+
handleListenerError(eventType, error);
|
|
437
694
|
} finally {
|
|
438
|
-
if (rec.
|
|
695
|
+
if (rec.passive) state.inPassiveListenerFlag = false;
|
|
439
696
|
}
|
|
697
|
+
|
|
698
|
+
if (state.stopImmediatePropagationFlag) break;
|
|
440
699
|
}
|
|
441
|
-
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
const dispatchEvent: EventTargetLike<E>['dispatchEvent'] = (eventInput) => {
|
|
703
|
+
if (isCompleted) return false;
|
|
704
|
+
|
|
705
|
+
let event: EmissionEvent<E[keyof E]>;
|
|
706
|
+
let state: EventDispatchState | undefined;
|
|
707
|
+
|
|
708
|
+
if (eventInput && typeof eventInput === 'object') {
|
|
709
|
+
state = getEventState(eventInput as EmissionEvent<unknown>);
|
|
710
|
+
if (state) {
|
|
711
|
+
event = eventInput as EmissionEvent<E[keyof E]>;
|
|
712
|
+
} else {
|
|
713
|
+
const input = eventInput as {
|
|
714
|
+
type: string;
|
|
715
|
+
detail?: E[keyof E];
|
|
716
|
+
bubbles?: boolean;
|
|
717
|
+
cancelable?: boolean;
|
|
718
|
+
composed?: boolean;
|
|
719
|
+
timeStamp?: number;
|
|
720
|
+
};
|
|
721
|
+
if (typeof input.type !== 'string') {
|
|
722
|
+
throw new TypeError('Event type must be a string');
|
|
723
|
+
}
|
|
724
|
+
event = createEvent(input.type, input.detail as E[keyof E], {
|
|
725
|
+
bubbles: input.bubbles,
|
|
726
|
+
cancelable: input.cancelable,
|
|
727
|
+
composed: input.composed,
|
|
728
|
+
timeStamp: input.timeStamp,
|
|
729
|
+
});
|
|
730
|
+
state = getEventState(event)!;
|
|
731
|
+
}
|
|
732
|
+
} else {
|
|
733
|
+
throw new TypeError('dispatchEvent expects an event object');
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
const dispatchState = state ?? getEventState(event)!;
|
|
737
|
+
|
|
738
|
+
if (dispatchState.dispatchFlag || !dispatchState.initializedFlag) {
|
|
739
|
+
const message =
|
|
740
|
+
'Failed to execute dispatchEvent: event is already being dispatched';
|
|
741
|
+
if (typeof globalThis.DOMException === 'function') {
|
|
742
|
+
throw new globalThis.DOMException(message, 'InvalidStateError');
|
|
743
|
+
}
|
|
744
|
+
const err = new Error(message);
|
|
745
|
+
err.name = 'InvalidStateError';
|
|
746
|
+
throw err;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
dispatchState.isTrusted = false;
|
|
750
|
+
dispatchState.dispatchFlag = true;
|
|
751
|
+
dispatchState.path = [
|
|
752
|
+
{
|
|
753
|
+
invocationTarget: target,
|
|
754
|
+
invocationTargetInShadowTree: false,
|
|
755
|
+
shadowAdjustedTarget: target,
|
|
756
|
+
relatedTarget: null,
|
|
757
|
+
touchTargets: [],
|
|
758
|
+
rootOfClosedTree: false,
|
|
759
|
+
slotInClosedTree: false,
|
|
760
|
+
},
|
|
761
|
+
];
|
|
762
|
+
|
|
763
|
+
// Notify wildcard listeners first (no overhead if none registered)
|
|
764
|
+
notifyWildcardListeners(dispatchState.type, event);
|
|
765
|
+
|
|
766
|
+
const list = listeners.get(dispatchState.type);
|
|
767
|
+
const snapshot = list ? list.slice() : [];
|
|
768
|
+
invokeListeners(dispatchState.type, event, 'capturing', snapshot);
|
|
769
|
+
invokeListeners(dispatchState.type, event, 'bubbling', snapshot);
|
|
770
|
+
|
|
771
|
+
dispatchState.eventPhase = event.NONE;
|
|
772
|
+
dispatchState.currentTarget = null;
|
|
773
|
+
dispatchState.path = [];
|
|
774
|
+
dispatchState.dispatchFlag = false;
|
|
775
|
+
dispatchState.stopPropagationFlag = false;
|
|
776
|
+
dispatchState.stopImmediatePropagationFlag = false;
|
|
777
|
+
|
|
778
|
+
return !dispatchState.canceledFlag;
|
|
442
779
|
};
|
|
443
780
|
|
|
444
781
|
const removeEventListener: EventTargetLike<E>['removeEventListener'] = (
|
|
445
782
|
type,
|
|
446
783
|
listener,
|
|
784
|
+
options,
|
|
447
785
|
) => {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
break;
|
|
786
|
+
if (!listener) return;
|
|
787
|
+
const capture = normalizeCaptureOption(options);
|
|
788
|
+
const list = listeners.get(type);
|
|
789
|
+
if (!list) return;
|
|
790
|
+
|
|
791
|
+
for (const record of [...list]) {
|
|
792
|
+
if (record.original === listener && record.capture === capture) {
|
|
793
|
+
removeListenerRecord(type, record);
|
|
458
794
|
}
|
|
459
795
|
}
|
|
460
796
|
};
|
|
461
797
|
|
|
462
798
|
const clear = () => {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (record.signal && record.abortHandler) {
|
|
467
|
-
record.signal.removeEventListener('abort', record.abortHandler);
|
|
468
|
-
}
|
|
799
|
+
for (const [type, list] of Array.from(listeners.entries())) {
|
|
800
|
+
for (const record of [...list]) {
|
|
801
|
+
removeListenerRecord(type, record);
|
|
469
802
|
}
|
|
470
|
-
set.clear();
|
|
471
803
|
}
|
|
472
804
|
listeners.clear();
|
|
473
805
|
|
|
@@ -491,7 +823,7 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
491
823
|
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
492
824
|
let opts: OnOptions;
|
|
493
825
|
if (typeof options === 'boolean') {
|
|
494
|
-
opts = {
|
|
826
|
+
opts = { capture: options };
|
|
495
827
|
} else {
|
|
496
828
|
opts = options ?? {};
|
|
497
829
|
}
|
|
@@ -546,31 +878,27 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
546
878
|
};
|
|
547
879
|
|
|
548
880
|
const onceMethod: EventTargetLike<E>['once'] = (type, listener, options) => {
|
|
549
|
-
|
|
881
|
+
if (typeof options === 'boolean') {
|
|
882
|
+
return addEventListener(type, listener, { capture: options, once: true });
|
|
883
|
+
}
|
|
884
|
+
return addEventListener(type, listener, { ...(options ?? {}), once: true });
|
|
550
885
|
};
|
|
551
886
|
|
|
552
887
|
const removeAllListeners: EventTargetLike<E>['removeAllListeners'] = (type) => {
|
|
553
888
|
if (type !== undefined) {
|
|
554
|
-
const
|
|
555
|
-
if (
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
if (record.signal && record.abortHandler) {
|
|
559
|
-
record.signal.removeEventListener('abort', record.abortHandler);
|
|
560
|
-
}
|
|
889
|
+
const list = listeners.get(type);
|
|
890
|
+
if (list) {
|
|
891
|
+
for (const record of [...list]) {
|
|
892
|
+
removeListenerRecord(type, record);
|
|
561
893
|
}
|
|
562
|
-
set.clear();
|
|
563
894
|
listeners.delete(type);
|
|
564
895
|
}
|
|
565
896
|
} else {
|
|
566
897
|
// Clear all listeners for all types
|
|
567
|
-
for (const
|
|
568
|
-
for (const record of
|
|
569
|
-
|
|
570
|
-
record.signal.removeEventListener('abort', record.abortHandler);
|
|
571
|
-
}
|
|
898
|
+
for (const [eventType, list] of Array.from(listeners.entries())) {
|
|
899
|
+
for (const record of [...list]) {
|
|
900
|
+
removeListenerRecord(eventType, record);
|
|
572
901
|
}
|
|
573
|
-
set.clear();
|
|
574
902
|
}
|
|
575
903
|
listeners.clear();
|
|
576
904
|
|
|
@@ -587,64 +915,49 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
587
915
|
/**
|
|
588
916
|
* Pipe events from this emitter to another target.
|
|
589
917
|
*
|
|
590
|
-
*
|
|
591
|
-
* when pipe() is called. Events for types registered afterward won't be piped.
|
|
592
|
-
*
|
|
593
|
-
* To ensure all events are piped, add at least one listener for each event type
|
|
594
|
-
* before calling pipe().
|
|
918
|
+
* Forwards all events. If mapFn returns null, the event is skipped.
|
|
595
919
|
*/
|
|
596
920
|
const pipe: EventTargetLike<E>['pipe'] = (target, mapFn) => {
|
|
597
921
|
if (isCompleted) {
|
|
598
922
|
return () => {};
|
|
599
923
|
}
|
|
600
924
|
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
// Type assertion via unknown is needed because mapFn output type matches target's event map
|
|
616
|
-
target.dispatchEvent(
|
|
617
|
-
mapped as unknown as Parameters<typeof target.dispatchEvent>[0],
|
|
618
|
-
);
|
|
619
|
-
}
|
|
620
|
-
} else {
|
|
621
|
-
// Type assertion via unknown is needed because caller ensures E and T are compatible
|
|
925
|
+
const unsubscribe = addWildcardListener('*', (event) => {
|
|
926
|
+
if (mapFn) {
|
|
927
|
+
const mapped = mapFn(
|
|
928
|
+
createEvent(event.originalType, event.detail, {
|
|
929
|
+
target,
|
|
930
|
+
currentTarget: target,
|
|
931
|
+
eventPhase: 2,
|
|
932
|
+
bubbles: event.bubbles,
|
|
933
|
+
cancelable: event.cancelable,
|
|
934
|
+
composed: event.composed,
|
|
935
|
+
}) as EmissionEvent<E[keyof E & string], keyof E & string>,
|
|
936
|
+
);
|
|
937
|
+
if (mapped !== null) {
|
|
938
|
+
// Type assertion via unknown is needed because mapFn output type matches target's event map
|
|
622
939
|
target.dispatchEvent(
|
|
623
|
-
|
|
940
|
+
mapped as unknown as Parameters<typeof target.dispatchEvent>[0],
|
|
624
941
|
);
|
|
625
942
|
}
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
}
|
|
943
|
+
} else {
|
|
944
|
+
// Type assertion via unknown is needed because caller ensures E and T are compatible
|
|
945
|
+
target.dispatchEvent({
|
|
946
|
+
type: event.originalType,
|
|
947
|
+
detail: event.detail,
|
|
948
|
+
} as unknown as Parameters<typeof target.dispatchEvent>[0]);
|
|
949
|
+
}
|
|
950
|
+
});
|
|
634
951
|
|
|
635
952
|
// Clean up on completion
|
|
636
953
|
const completionUnsub = () => {
|
|
637
|
-
|
|
638
|
-
unsub();
|
|
639
|
-
}
|
|
954
|
+
unsubscribe();
|
|
640
955
|
};
|
|
641
956
|
completionCallbacks.add(completionUnsub);
|
|
642
957
|
|
|
643
958
|
return () => {
|
|
644
959
|
completionCallbacks.delete(completionUnsub);
|
|
645
|
-
|
|
646
|
-
unsub();
|
|
647
|
-
}
|
|
960
|
+
unsubscribe();
|
|
648
961
|
};
|
|
649
962
|
};
|
|
650
963
|
|
|
@@ -669,13 +982,10 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
669
982
|
completionCallbacks.clear();
|
|
670
983
|
|
|
671
984
|
// Clear all listeners
|
|
672
|
-
for (const
|
|
673
|
-
for (const record of
|
|
674
|
-
|
|
675
|
-
record.signal.removeEventListener('abort', record.abortHandler);
|
|
676
|
-
}
|
|
985
|
+
for (const [eventType, list] of Array.from(listeners.entries())) {
|
|
986
|
+
for (const record of [...list]) {
|
|
987
|
+
removeListenerRecord(eventType, record);
|
|
677
988
|
}
|
|
678
|
-
set.clear();
|
|
679
989
|
}
|
|
680
990
|
listeners.clear();
|
|
681
991
|
|
|
@@ -688,7 +998,7 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
688
998
|
wildcardListeners.clear();
|
|
689
999
|
};
|
|
690
1000
|
|
|
691
|
-
// Observable
|
|
1001
|
+
// Observable interoperability
|
|
692
1002
|
const subscribe: EventTargetLike<E>['subscribe'] = (
|
|
693
1003
|
type,
|
|
694
1004
|
observerOrNext,
|
|
@@ -782,7 +1092,16 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
782
1092
|
|
|
783
1093
|
const wildcardListener = (event: WildcardEvent<E>) => {
|
|
784
1094
|
// Emit an augmented event with the actual type
|
|
785
|
-
observer.next(
|
|
1095
|
+
observer.next(
|
|
1096
|
+
createEvent(event.originalType, event.detail, {
|
|
1097
|
+
target,
|
|
1098
|
+
currentTarget: target,
|
|
1099
|
+
eventPhase: 2,
|
|
1100
|
+
bubbles: event.bubbles,
|
|
1101
|
+
cancelable: event.cancelable,
|
|
1102
|
+
composed: event.composed,
|
|
1103
|
+
}),
|
|
1104
|
+
);
|
|
786
1105
|
};
|
|
787
1106
|
|
|
788
1107
|
const unsubscribe = addWildcardListener('*', wildcardListener);
|
|
@@ -804,22 +1123,22 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
804
1123
|
function events<K extends keyof E & string>(
|
|
805
1124
|
type: K,
|
|
806
1125
|
options?: AsyncIteratorOptions,
|
|
807
|
-
): AsyncIterableIterator<EmissionEvent<E[K]>> {
|
|
1126
|
+
): AsyncIterableIterator<EmissionEvent<E[K], K>> {
|
|
808
1127
|
// If already completed, return an iterator that immediately yields done
|
|
809
1128
|
if (isCompleted) {
|
|
810
1129
|
return {
|
|
811
1130
|
[Symbol.asyncIterator]() {
|
|
812
1131
|
return this;
|
|
813
1132
|
},
|
|
814
|
-
next(): Promise<IteratorResult<EmissionEvent<E[K]>>> {
|
|
1133
|
+
next(): Promise<IteratorResult<EmissionEvent<E[K], K>>> {
|
|
815
1134
|
return Promise.resolve({
|
|
816
|
-
value: undefined as unknown as EmissionEvent<E[K]>,
|
|
1135
|
+
value: undefined as unknown as EmissionEvent<E[K], K>,
|
|
817
1136
|
done: true,
|
|
818
1137
|
});
|
|
819
1138
|
},
|
|
820
|
-
return(): Promise<IteratorResult<EmissionEvent<E[K]>>> {
|
|
1139
|
+
return(): Promise<IteratorResult<EmissionEvent<E[K], K>>> {
|
|
821
1140
|
return Promise.resolve({
|
|
822
|
-
value: undefined as unknown as EmissionEvent<E[K]>,
|
|
1141
|
+
value: undefined as unknown as EmissionEvent<E[K], K>,
|
|
823
1142
|
done: true,
|
|
824
1143
|
});
|
|
825
1144
|
},
|
|
@@ -830,26 +1149,33 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
830
1149
|
const bufferSize = options?.bufferSize ?? Infinity;
|
|
831
1150
|
const overflowStrategy = options?.overflowStrategy ?? 'drop-oldest';
|
|
832
1151
|
|
|
833
|
-
const buffer: Array<EmissionEvent<E[K]>> = [];
|
|
834
|
-
let resolve: ((result: IteratorResult<EmissionEvent<E[K]>>) => void) | null = null;
|
|
1152
|
+
const buffer: Array<EmissionEvent<E[K], K>> = [];
|
|
1153
|
+
let resolve: ((result: IteratorResult<EmissionEvent<E[K], K>>) => void) | null = null;
|
|
835
1154
|
let done = false;
|
|
836
1155
|
let hasOverflow = false;
|
|
1156
|
+
let onAbort: (() => void) | null = null;
|
|
1157
|
+
const cleanupAbortListener = () => {
|
|
1158
|
+
if (signal && onAbort) {
|
|
1159
|
+
signal.removeEventListener('abort', onAbort);
|
|
1160
|
+
}
|
|
1161
|
+
};
|
|
837
1162
|
|
|
838
1163
|
const unsub = addEventListener(type, (event) => {
|
|
839
1164
|
if (done) return;
|
|
1165
|
+
const typedEvent = event;
|
|
840
1166
|
|
|
841
1167
|
if (resolve) {
|
|
842
1168
|
// Someone is waiting, resolve immediately
|
|
843
1169
|
const r = resolve;
|
|
844
1170
|
resolve = null;
|
|
845
|
-
r({ value:
|
|
1171
|
+
r({ value: typedEvent, done: false });
|
|
846
1172
|
} else {
|
|
847
1173
|
// Buffer the event
|
|
848
1174
|
if (buffer.length >= bufferSize && bufferSize !== Infinity) {
|
|
849
1175
|
switch (overflowStrategy) {
|
|
850
1176
|
case 'drop-oldest':
|
|
851
1177
|
buffer.shift();
|
|
852
|
-
buffer.push(
|
|
1178
|
+
buffer.push(typedEvent);
|
|
853
1179
|
break;
|
|
854
1180
|
case 'drop-latest':
|
|
855
1181
|
// Don't add the new event
|
|
@@ -859,10 +1185,11 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
859
1185
|
completionCallbacks.delete(onComplete);
|
|
860
1186
|
done = true;
|
|
861
1187
|
hasOverflow = true;
|
|
1188
|
+
cleanupAbortListener();
|
|
862
1189
|
return;
|
|
863
1190
|
}
|
|
864
1191
|
} else {
|
|
865
|
-
buffer.push(
|
|
1192
|
+
buffer.push(typedEvent);
|
|
866
1193
|
}
|
|
867
1194
|
}
|
|
868
1195
|
});
|
|
@@ -870,16 +1197,16 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
870
1197
|
// Handle completion
|
|
871
1198
|
const onComplete = () => {
|
|
872
1199
|
done = true;
|
|
1200
|
+
cleanupAbortListener();
|
|
873
1201
|
if (resolve) {
|
|
874
1202
|
const r = resolve;
|
|
875
1203
|
resolve = null;
|
|
876
|
-
r({ value: undefined as unknown as EmissionEvent<E[K]>, done: true });
|
|
1204
|
+
r({ value: undefined as unknown as EmissionEvent<E[K], K>, done: true });
|
|
877
1205
|
}
|
|
878
1206
|
};
|
|
879
1207
|
completionCallbacks.add(onComplete);
|
|
880
1208
|
|
|
881
1209
|
// Handle abort signal
|
|
882
|
-
let onAbort: (() => void) | null = null;
|
|
883
1210
|
if (signal) {
|
|
884
1211
|
onAbort = () => {
|
|
885
1212
|
done = true;
|
|
@@ -888,33 +1215,23 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
888
1215
|
if (resolve) {
|
|
889
1216
|
const r = resolve;
|
|
890
1217
|
resolve = null;
|
|
891
|
-
r({ value: undefined as unknown as EmissionEvent<E[K]>, done: true });
|
|
1218
|
+
r({ value: undefined as unknown as EmissionEvent<E[K], K>, done: true });
|
|
892
1219
|
}
|
|
893
1220
|
};
|
|
894
1221
|
signal.addEventListener('abort', onAbort, { once: true });
|
|
895
1222
|
if (signal.aborted) onAbort();
|
|
896
1223
|
}
|
|
897
1224
|
|
|
898
|
-
const iterator: AsyncIterableIterator<EmissionEvent<E[K]>> = {
|
|
1225
|
+
const iterator: AsyncIterableIterator<EmissionEvent<E[K], K>> = {
|
|
899
1226
|
[Symbol.asyncIterator]() {
|
|
900
1227
|
return this;
|
|
901
1228
|
},
|
|
902
|
-
async next(): Promise<IteratorResult<EmissionEvent<E[K]>>> {
|
|
1229
|
+
async next(): Promise<IteratorResult<EmissionEvent<E[K], K>>> {
|
|
903
1230
|
// Drain buffered events first, even if done
|
|
904
1231
|
if (buffer.length > 0) {
|
|
905
1232
|
return { value: buffer.shift()!, done: false };
|
|
906
1233
|
}
|
|
907
1234
|
|
|
908
|
-
// After buffer is drained, check for overflow error
|
|
909
|
-
if (hasOverflow) {
|
|
910
|
-
hasOverflow = false;
|
|
911
|
-
throw new BufferOverflowError(type, bufferSize);
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
if (done) {
|
|
915
|
-
return { value: undefined as unknown as EmissionEvent<E[K]>, done: true };
|
|
916
|
-
}
|
|
917
|
-
|
|
918
1235
|
// Prevent concurrent next() calls - if there's already a pending promise, reject
|
|
919
1236
|
if (resolve !== null) {
|
|
920
1237
|
return Promise.reject(
|
|
@@ -925,25 +1242,30 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
925
1242
|
}
|
|
926
1243
|
|
|
927
1244
|
// Wait for next event
|
|
928
|
-
return new Promise<IteratorResult<EmissionEvent<E[K]>>>(
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1245
|
+
return new Promise<IteratorResult<EmissionEvent<E[K], K>>>(
|
|
1246
|
+
(_resolve, _reject) => {
|
|
1247
|
+
if (hasOverflow) {
|
|
1248
|
+
hasOverflow = false;
|
|
1249
|
+
_reject(new BufferOverflowError(type, bufferSize));
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
if (done) {
|
|
1253
|
+
_resolve({
|
|
1254
|
+
value: undefined as unknown as EmissionEvent<E[K], K>,
|
|
1255
|
+
done: true,
|
|
1256
|
+
});
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
resolve = _resolve;
|
|
1260
|
+
},
|
|
1261
|
+
);
|
|
940
1262
|
},
|
|
941
|
-
return(): Promise<IteratorResult<EmissionEvent<E[K]>>> {
|
|
1263
|
+
return(): Promise<IteratorResult<EmissionEvent<E[K], K>>> {
|
|
942
1264
|
// Resolve any pending promise before cleanup
|
|
943
1265
|
if (resolve) {
|
|
944
1266
|
const r = resolve;
|
|
945
1267
|
resolve = null;
|
|
946
|
-
r({ value: undefined as unknown as EmissionEvent<E[K]>, done: true });
|
|
1268
|
+
r({ value: undefined as unknown as EmissionEvent<E[K], K>, done: true });
|
|
947
1269
|
}
|
|
948
1270
|
|
|
949
1271
|
done = true;
|
|
@@ -951,12 +1273,10 @@ function createEventTargetInternal<E extends Record<string, any>>(
|
|
|
951
1273
|
unsub();
|
|
952
1274
|
|
|
953
1275
|
// Clean up abort signal listener
|
|
954
|
-
|
|
955
|
-
signal.removeEventListener('abort', onAbort);
|
|
956
|
-
}
|
|
1276
|
+
cleanupAbortListener();
|
|
957
1277
|
|
|
958
1278
|
return Promise.resolve({
|
|
959
|
-
value: undefined as unknown as EmissionEvent<E[K]>,
|
|
1279
|
+
value: undefined as unknown as EmissionEvent<E[K], K>,
|
|
960
1280
|
done: true,
|
|
961
1281
|
});
|
|
962
1282
|
},
|