diff --git a/.changeset/tidy-tools-reconnect.md b/.changeset/tidy-tools-reconnect.md new file mode 100644 index 000000000..2dc233f3f --- /dev/null +++ b/.changeset/tidy-tools-reconnect.md @@ -0,0 +1,8 @@ +--- +'@tanstack/pacer-devtools': patch +'@tanstack/pacer': patch +--- + +Fix Pacer Devtools discovering utilities and receiving their updates when the +devtools panel mounts after the event client has exhausted its connection +retries. diff --git a/docs/reference/functions/emitChange.md b/docs/reference/functions/emitChange.md index 21acc089d..4dcc6213a 100644 --- a/docs/reference/functions/emitChange.md +++ b/docs/reference/functions/emitChange.md @@ -9,7 +9,7 @@ title: emitChange function emitChange(event, instance): void; ``` -Defined in: [event-client.ts:117](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L117) +Defined in: [event-client.ts:173](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L173) ## Type Parameters diff --git a/docs/reference/functions/getPacerDevtoolsInstance.md b/docs/reference/functions/getPacerDevtoolsInstance.md index 638f2875b..48e143533 100644 --- a/docs/reference/functions/getPacerDevtoolsInstance.md +++ b/docs/reference/functions/getPacerDevtoolsInstance.md @@ -9,7 +9,7 @@ title: getPacerDevtoolsInstance function getPacerDevtoolsInstance(key): unknown; ``` -Defined in: [event-client.ts:25](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L25) +Defined in: [event-client.ts:53](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L53) ## Parameters diff --git a/docs/reference/interfaces/PacerEventMap.md b/docs/reference/interfaces/PacerEventMap.md index e4787d7d5..ec7f7b38a 100644 --- a/docs/reference/interfaces/PacerEventMap.md +++ b/docs/reference/interfaces/PacerEventMap.md @@ -5,7 +5,7 @@ title: PacerEventMap # Interface: PacerEventMap -Defined in: [event-client.ts:66](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L66) +Defined in: [event-client.ts:122](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L122) Suffix-only keys: EventClient prepends `pluginId:` (`pacer:`) at runtime for `emit` / `on`. Wire `type` values are `pacer:${key}`. @@ -18,7 +18,7 @@ for `emit` / `on`. Wire `type` values are `pacer:${key}`. AsyncBatcher: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:78](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L78) +Defined in: [event-client.ts:134](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L134) *** @@ -28,7 +28,7 @@ Defined in: [event-client.ts:78](https://github.com/TanStack/pacer/blob/main/pac AsyncDebouncer: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:79](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L79) +Defined in: [event-client.ts:135](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L135) *** @@ -38,7 +38,7 @@ Defined in: [event-client.ts:79](https://github.com/TanStack/pacer/blob/main/pac AsyncQueuer: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:80](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L80) +Defined in: [event-client.ts:136](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L136) *** @@ -48,7 +48,7 @@ Defined in: [event-client.ts:80](https://github.com/TanStack/pacer/blob/main/pac AsyncRateLimiter: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:81](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L81) +Defined in: [event-client.ts:137](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L137) *** @@ -58,7 +58,7 @@ Defined in: [event-client.ts:81](https://github.com/TanStack/pacer/blob/main/pac AsyncRetryer: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:82](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L82) +Defined in: [event-client.ts:138](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L138) *** @@ -68,7 +68,7 @@ Defined in: [event-client.ts:82](https://github.com/TanStack/pacer/blob/main/pac AsyncThrottler: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:83](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L83) +Defined in: [event-client.ts:139](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L139) *** @@ -78,7 +78,7 @@ Defined in: [event-client.ts:83](https://github.com/TanStack/pacer/blob/main/pac Batcher: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:84](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L84) +Defined in: [event-client.ts:140](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L140) *** @@ -88,7 +88,7 @@ Defined in: [event-client.ts:84](https://github.com/TanStack/pacer/blob/main/pac d-AsyncBatcher: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:67](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L67) +Defined in: [event-client.ts:123](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L123) *** @@ -98,7 +98,7 @@ Defined in: [event-client.ts:67](https://github.com/TanStack/pacer/blob/main/pac d-AsyncDebouncer: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:68](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L68) +Defined in: [event-client.ts:124](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L124) *** @@ -108,7 +108,7 @@ Defined in: [event-client.ts:68](https://github.com/TanStack/pacer/blob/main/pac d-AsyncQueuer: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:69](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L69) +Defined in: [event-client.ts:125](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L125) *** @@ -118,7 +118,7 @@ Defined in: [event-client.ts:69](https://github.com/TanStack/pacer/blob/main/pac d-AsyncRateLimiter: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:70](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L70) +Defined in: [event-client.ts:126](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L126) *** @@ -128,7 +128,7 @@ Defined in: [event-client.ts:70](https://github.com/TanStack/pacer/blob/main/pac d-AsyncRetryer: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:71](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L71) +Defined in: [event-client.ts:127](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L127) *** @@ -138,7 +138,7 @@ Defined in: [event-client.ts:71](https://github.com/TanStack/pacer/blob/main/pac d-AsyncThrottler: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:72](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L72) +Defined in: [event-client.ts:128](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L128) *** @@ -148,7 +148,7 @@ Defined in: [event-client.ts:72](https://github.com/TanStack/pacer/blob/main/pac d-Batcher: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:73](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L73) +Defined in: [event-client.ts:129](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L129) *** @@ -158,7 +158,7 @@ Defined in: [event-client.ts:73](https://github.com/TanStack/pacer/blob/main/pac d-Debouncer: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:74](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L74) +Defined in: [event-client.ts:130](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L130) *** @@ -168,7 +168,7 @@ Defined in: [event-client.ts:74](https://github.com/TanStack/pacer/blob/main/pac d-Queuer: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:75](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L75) +Defined in: [event-client.ts:131](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L131) *** @@ -178,7 +178,7 @@ Defined in: [event-client.ts:75](https://github.com/TanStack/pacer/blob/main/pac d-RateLimiter: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:76](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L76) +Defined in: [event-client.ts:132](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L132) *** @@ -188,7 +188,7 @@ Defined in: [event-client.ts:76](https://github.com/TanStack/pacer/blob/main/pac d-Throttler: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:77](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L77) +Defined in: [event-client.ts:133](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L133) *** @@ -198,7 +198,7 @@ Defined in: [event-client.ts:77](https://github.com/TanStack/pacer/blob/main/pac Debouncer: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:85](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L85) +Defined in: [event-client.ts:141](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L141) *** @@ -208,7 +208,7 @@ Defined in: [event-client.ts:85](https://github.com/TanStack/pacer/blob/main/pac Queuer: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:86](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L86) +Defined in: [event-client.ts:142](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L142) *** @@ -218,7 +218,7 @@ Defined in: [event-client.ts:86](https://github.com/TanStack/pacer/blob/main/pac RateLimiter: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:87](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L87) +Defined in: [event-client.ts:143](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L143) *** @@ -228,4 +228,4 @@ Defined in: [event-client.ts:87](https://github.com/TanStack/pacer/blob/main/pac Throttler: PacerDevtoolsWirePayload; ``` -Defined in: [event-client.ts:88](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L88) +Defined in: [event-client.ts:144](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L144) diff --git a/docs/reference/type-aliases/PacerEventName.md b/docs/reference/type-aliases/PacerEventName.md index b6d79240d..4e83fe1a2 100644 --- a/docs/reference/type-aliases/PacerEventName.md +++ b/docs/reference/type-aliases/PacerEventName.md @@ -9,4 +9,4 @@ title: PacerEventName type PacerEventName = keyof PacerEventMap; ``` -Defined in: [event-client.ts:91](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L91) +Defined in: [event-client.ts:147](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L147) diff --git a/docs/reference/variables/pacerEventClient.md b/docs/reference/variables/pacerEventClient.md index b2b5323dc..9374939ae 100644 --- a/docs/reference/variables/pacerEventClient.md +++ b/docs/reference/variables/pacerEventClient.md @@ -9,4 +9,4 @@ title: pacerEventClient const pacerEventClient: PacerEventClient; ``` -Defined in: [event-client.ts:129](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L129) +Defined in: [event-client.ts:189](https://github.com/TanStack/pacer/blob/main/packages/pacer/src/event-client.ts#L189) diff --git a/packages/pacer-devtools/package.json b/packages/pacer-devtools/package.json index c657658b3..debf88eea 100644 --- a/packages/pacer-devtools/package.json +++ b/packages/pacer-devtools/package.json @@ -77,7 +77,7 @@ "src" ], "peerDependencies": { - "@tanstack/pacer": ">=0.16.4" + "@tanstack/pacer": ">=0.21.2" }, "dependencies": { "@tanstack/devtools-ui": "0.5.2", diff --git a/packages/pacer-devtools/src/PacerContextProvider.tsx b/packages/pacer-devtools/src/PacerContextProvider.tsx index eaae892ff..8f68034de 100644 --- a/packages/pacer-devtools/src/PacerContextProvider.tsx +++ b/packages/pacer-devtools/src/PacerContextProvider.tsx @@ -3,6 +3,7 @@ import { createContext, createEffect, onCleanup, useContext } from 'solid-js' import { getPacerDevtoolsInstance, pacerEventClient, + subscribeToPacerDevtoolsInstances, } from '@tanstack/pacer/event-client' import type { AsyncBatcher, @@ -56,13 +57,8 @@ const PacerDevtoolsContext = createContext< type UtilListKey = Exclude /** - * Match TanStack Form devtools: subscribe with {@link pacerEventClient.on} per - * event suffix so the same `pacer:${suffix}` channel the library emits on is - * observed. `onAllPluginEvents` only sees `tanstack-devtools-global`, which is - * not always relayed the same way by the shell. - * - * `d-*` suffixes are used when the devtools panel pushes state back (see - * ActionButtons); those must update the panel store too. + * Bus subscriptions preserve relayed devtools events. The instance subscription + * below additionally replays same-runtime utilities when this provider mounts. */ const PACER_DEVTOOLS_UTIL_EVENTS: Array<{ listKey: UtilListKey @@ -116,31 +112,47 @@ export function PacerContextProvider(props: { children: any }) { ) createEffect(() => { - const cleanups: Array<() => void> = [] + const updateInstance = ( + event: PacerEventName, + key: string, + instance: unknown, + ) => { + if (!instance || typeof instance !== 'object') return - for (const { listKey, suffixes } of PACER_DEVTOOLS_UTIL_EVENTS) { - for (const suffix of suffixes) { - cleanups.push( - pacerEventClient.on(suffix, (e) => { - const payload = e.payload - const key = payload.key - if (!key) return + const eventConfig = PACER_DEVTOOLS_UTIL_EVENTS.find(({ suffixes }) => + suffixes.includes(event), + ) + if (!eventConfig) return + + setStore( + produce((draft) => { + const list = draft[eventConfig.listKey] as Array<{ key: string }> + const index = list.findIndex((item) => item.key === key) + const inst = instance as { key: string } + if (index !== -1) { + list[index] = inst as (typeof list)[number] + } else { + list.push(inst as (typeof list)[number]) + } + draft.lastUpdatedByKey[key] = Date.now() + }), + ) + } - const instance = getPacerDevtoolsInstance(key) - if (!instance || typeof instance !== 'object') return + const cleanups = [ + subscribeToPacerDevtoolsInstances(({ event, key, instance }) => { + updateInstance(event, key, instance) + }), + ] - setStore( - produce((draft) => { - const list = draft[listKey] as Array<{ key: string }> - const index = list.findIndex((item) => item.key === key) - const inst = instance as { key: string } - if (index !== -1) { - list[index] = inst as (typeof list)[number] - } else { - list.push(inst as (typeof list)[number]) - } - draft.lastUpdatedByKey[key] = Date.now() - }), + for (const { suffixes } of PACER_DEVTOOLS_UTIL_EVENTS) { + for (const suffix of suffixes) { + cleanups.push( + pacerEventClient.on(suffix, ({ payload }) => { + updateInstance( + suffix, + payload.key, + getPacerDevtoolsInstance(payload.key), ) }), ) @@ -148,9 +160,7 @@ export function PacerContextProvider(props: { children: any }) { } onCleanup(() => { - for (const cleanup of cleanups) { - cleanup() - } + cleanups.forEach((cleanup) => cleanup()) }) }) return ( diff --git a/packages/pacer-devtools/tests/index.test.ts b/packages/pacer-devtools/tests/index.test.ts deleted file mode 100644 index 350ef3ade..000000000 --- a/packages/pacer-devtools/tests/index.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { describe, expect, it } from 'vitest' - -describe('test suite', () => { - it('should work', () => { - expect(true).toBe(true) - }) -}) diff --git a/packages/pacer-devtools/tests/index.test.tsx b/packages/pacer-devtools/tests/index.test.tsx new file mode 100644 index 000000000..141863cf2 --- /dev/null +++ b/packages/pacer-devtools/tests/index.test.tsx @@ -0,0 +1,102 @@ +import { render } from 'solid-js/web' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { Debouncer } from '@tanstack/pacer' +import { subscribeToPacerDevtoolsInstances } from '@tanstack/pacer/event-client' +import { + PacerContextProvider, + usePacerDevtoolsState, +} from '../src/PacerContextProvider' + +afterEach(() => { + vi.useRealTimers() +}) + +describe('PacerContextProvider', () => { + it('discovers existing utilities and receives updates after the event client gives up connecting', () => { + vi.useFakeTimers() + + const connect = vi.fn() + window.addEventListener('tanstack-connect', connect) + + const existingKey = 'created-before-devtools' + const existingDebouncer = new Debouncer((_value: string) => {}, { + key: existingKey, + wait: 100, + }) + + vi.advanceTimersByTime(5_000) + expect(connect).toHaveBeenCalledTimes(5) + + let state: ReturnType | undefined + const StateObserver = () => { + state = usePacerDevtoolsState() + return null + } + const dispose = render( + () => ( + + + + ), + document.createElement('div'), + ) + + expect(state?.debouncers).toContain(existingDebouncer) + + const initialUpdatedAt = state?.lastUpdatedByKey[existingKey] + vi.advanceTimersByTime(1) + existingDebouncer.maybeExecute('updated after mount') + + expect(state?.lastUpdatedByKey[existingKey]).toBeGreaterThan( + initialUpdatedAt!, + ) + expect(connect).toHaveBeenCalledTimes(5) + + const updatedAtBeforeBusEvent = state?.lastUpdatedByKey[existingKey] + vi.advanceTimersByTime(1) + window.dispatchEvent( + new CustomEvent('pacer:Debouncer', { + detail: { + type: 'pacer:Debouncer', + pluginId: 'pacer', + payload: { + key: existingKey, + options: existingDebouncer.options, + store: { state: existingDebouncer.store.state }, + }, + }, + }), + ) + + expect(state?.lastUpdatedByKey[existingKey]).toBeGreaterThan( + updatedAtBeforeBusEvent!, + ) + + const cleanupThrowingObserver = subscribeToPacerDevtoolsInstances(() => { + throw new Error('observer failed') + }) + expect(() => + existingDebouncer.maybeExecute('observer failure is isolated'), + ).not.toThrow() + cleanupThrowingObserver() + + const laterKey = 'created-after-devtools' + const laterDebouncer = new Debouncer((_value: string) => {}, { + key: laterKey, + wait: 100, + }) + + expect(state?.debouncers).toContain(laterDebouncer) + expect(state?.lastUpdatedByKey[laterKey]).toBeDefined() + expect(connect).toHaveBeenCalledTimes(5) + + const updatedAtBeforeDispose = state?.lastUpdatedByKey[existingKey] + dispose() + vi.advanceTimersByTime(1) + existingDebouncer.maybeExecute('updated after dispose') + + expect(state?.lastUpdatedByKey[existingKey]).toBe(updatedAtBeforeDispose) + + window.removeEventListener('tanstack-connect', connect) + }) +}) diff --git a/packages/pacer-devtools/vitest.config.ts b/packages/pacer-devtools/vitest.config.ts index 597cf0ecf..c60b628b9 100644 --- a/packages/pacer-devtools/vitest.config.ts +++ b/packages/pacer-devtools/vitest.config.ts @@ -1,9 +1,26 @@ +import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' import solid from 'vite-plugin-solid' import packageJson from './package.json' with { type: 'json' } export default defineConfig({ plugins: [solid()], + resolve: { + alias: [ + { + find: '@tanstack/pacer/event-client', + replacement: fileURLToPath( + new URL('../pacer/src/event-client.ts', import.meta.url), + ), + }, + { + find: '@tanstack/pacer', + replacement: fileURLToPath( + new URL('../pacer/src/index.ts', import.meta.url), + ), + }, + ], + }, test: { name: packageJson.name, dir: './', diff --git a/packages/pacer/src/event-client.ts b/packages/pacer/src/event-client.ts index aad99a591..7dad7884e 100644 --- a/packages/pacer/src/event-client.ts +++ b/packages/pacer/src/event-client.ts @@ -13,17 +13,73 @@ export interface PacerDevtoolsWirePayload { options: unknown } -const pacerDevtoolsInstancesByKey = new Map() +/** + * The panel is a same-runtime consumer of live instances. Keep a replayable + * registry and direct subscription alongside the serialized event-bus + * transport so a lazy-mounted panel does not depend on its connection window. + */ +const pacerDevtoolsInstancesByKey = new Map< + string, + PacerDevtoolsStoredInstance +>() +const pacerDevtoolsInstanceListeners = new Set< + (registration: PacerDevtoolsInstanceRegistration) => void +>() + +export interface PacerDevtoolsInstanceRegistration { + event: PacerEventName + key: string + instance: unknown +} + +type PacerDevtoolsStoredInstance = { + event?: PacerEventName + key: string + instance: unknown +} export function registerPacerDevtoolsInstance( key: string, instance: unknown, ): void { - pacerDevtoolsInstancesByKey.set(key, instance) + const previous = pacerDevtoolsInstancesByKey.get(key) + pacerDevtoolsInstancesByKey.set(key, { + event: previous?.event, + key, + instance, + }) } export function getPacerDevtoolsInstance(key: string): unknown { - return pacerDevtoolsInstancesByKey.get(key) + return pacerDevtoolsInstancesByKey.get(key)?.instance +} + +function notifyPacerDevtoolsInstanceListener( + listener: (registration: PacerDevtoolsInstanceRegistration) => void, + registration: PacerDevtoolsInstanceRegistration, +): void { + try { + listener(registration) + } catch { + // Devtools observers must never affect application behavior. + } +} + +export function subscribeToPacerDevtoolsInstances( + listener: (registration: PacerDevtoolsInstanceRegistration) => void, +): () => void { + pacerDevtoolsInstanceListeners.add(listener) + for (const registration of pacerDevtoolsInstancesByKey.values()) { + if (registration.event) { + notifyPacerDevtoolsInstanceListener(listener, { + ...registration, + event: registration.event, + }) + } + } + return () => { + pacerDevtoolsInstanceListeners.delete(listener) + } } function cloneJsonSafe(value: unknown): unknown { @@ -122,8 +178,12 @@ export const emitChange = ( if (!key) { return } - registerPacerDevtoolsInstance(key, instance) + const registration = { event, key, instance } + pacerDevtoolsInstancesByKey.set(key, registration) pacerEventClient.emit(event, toPacerDevtoolsWirePayload({ ...instance, key })) + for (const listener of pacerDevtoolsInstanceListeners) { + notifyPacerDevtoolsInstanceListener(listener, registration) + } } export const pacerEventClient = new PacerEventClient()