Merge branch 'v2/main' into v2/fix/2165-oauth-recovery-floating-promises
This commit is contained in:
commit
26fdc48bf1
1 changed files with 1088 additions and 772 deletions
|
|
@ -25,6 +25,55 @@ export class TypedEventGeneric<
|
|||
* implementation so the subclass remains assignable to EventTarget.
|
||||
*/
|
||||
export class TypedEventTarget<EventMap extends object> extends EventTarget {
|
||||
/**
|
||||
* Per-event-type dispatch counter. Read through `getEventRevision` by
|
||||
* `useStoreSnapshot` (core/react) to tell "nothing has happened on this
|
||||
* event since I last looked" from "something has", without comparing the
|
||||
* snapshot's contents (#1955).
|
||||
*/
|
||||
private readonly eventRevisions = new Map<string, number>();
|
||||
|
||||
/**
|
||||
* How many times an event of this type has been dispatched from this
|
||||
* target. Monotonic per type, and per type only: an `errorChange` does not
|
||||
* advance `toolsChange`, so a subscriber to one is not re-read because of
|
||||
* the other.
|
||||
*
|
||||
* The counter — not the list's contents — is what a `useSyncExternalStore`
|
||||
* snapshot is cached against. That distinction is load-bearing rather than
|
||||
* an optimization: these stores hand out a defensive copy, so comparing
|
||||
* contents is the only alternative, and a contents comparison cannot see an
|
||||
* in-place mutation of an entry the list already holds. `MessageLogState`
|
||||
* does exactly that when a response is folded into its request entry
|
||||
* (filling in `response`/`duration`), and it dispatches — so the counter
|
||||
* moves and the fold reaches the UI, where a shallow compare would report
|
||||
* "unchanged" and never re-render it.
|
||||
*/
|
||||
getEventRevision(type: keyof EventMap & string): number {
|
||||
return this.eventRevisions.get(type) ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden solely to advance the counter. It sits on `dispatchEvent`
|
||||
* rather than on `dispatchTypedEvent` because that is the one funnel every
|
||||
* dispatch passes through — the typed helper below delegates to it, and so
|
||||
* does a hand-rolled `dispatchEvent(new CustomEvent(...))`, which the tests
|
||||
* use extensively. A counter bumped only in the typed helper would be
|
||||
* silently stale for those, i.e. exactly where nobody would look for it.
|
||||
*
|
||||
* Bumped BEFORE dispatching: the state mutation always precedes the
|
||||
* dispatch, so by the time a listener runs the counter must already reflect
|
||||
* it, or a snapshot read from inside that listener would return the cached
|
||||
* pre-change value.
|
||||
*/
|
||||
override dispatchEvent(event: Event): boolean {
|
||||
this.eventRevisions.set(
|
||||
event.type,
|
||||
(this.eventRevisions.get(event.type) ?? 0) + 1,
|
||||
);
|
||||
return super.dispatchEvent(event);
|
||||
}
|
||||
|
||||
dispatchTypedEvent<K extends keyof EventMap>(
|
||||
type: K,
|
||||
...args: EventMap[K] extends void ? [] : [detail: EventMap[K]]
|
||||
|
|
|
|||
Loading…
Reference in a new issue