From fb6175a799890d4df6b0dea76e02f903403df07a Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 10:57:00 +0100 Subject: [PATCH 1/3] feat: simplify EventClient types to accept unprefixed event maps EventClient now accepts event maps with plain event names instead of requiring the pluginId prefix in every key. The prefix is handled purely at runtime. This removes the complex conditional types that were stripping prefixes from emit/on/createEventPayload signatures. --- examples/preact/basic/src/plugin.ts | 6 +- .../preact/custom-devtools/src/eventClient.ts | 5 +- examples/react/basic/src/plugin.ts | 6 +- .../react/custom-devtools/src/eventClient.ts | 5 +- examples/react/https/src/plugin.ts | 6 +- .../devtools/route-navigation-event-client.ts | 9 +-- .../react/time-travel/src/zustand-client.ts | 4 +- packages/devtools-client/src/index.ts | 22 +++---- packages/event-bus-client/src/plugin.ts | 62 +++++-------------- packages/event-bus-client/tests/index.test.ts | 8 +-- 10 files changed, 48 insertions(+), 85 deletions(-) diff --git a/examples/preact/basic/src/plugin.ts b/examples/preact/basic/src/plugin.ts index 8bac08e3..ba149842 100644 --- a/examples/preact/basic/src/plugin.ts +++ b/examples/preact/basic/src/plugin.ts @@ -1,15 +1,15 @@ import { EventClient } from '@tanstack/devtools-event-client' interface EventMap { - 'query-devtools:test': { + 'test': { title: string description: string } - 'query-devtools:init': { + 'init': { title: string description: string } - 'query-devtools:query': { + 'query': { title: string description: string } diff --git a/examples/preact/custom-devtools/src/eventClient.ts b/examples/preact/custom-devtools/src/eventClient.ts index d884641d..58c49e82 100644 --- a/examples/preact/custom-devtools/src/eventClient.ts +++ b/examples/preact/custom-devtools/src/eventClient.ts @@ -1,15 +1,12 @@ import { EventClient } from '@tanstack/devtools-event-client' type EventMap = { - // The key of the event map is a combination of {pluginId}:{eventSuffix} - // The value is the expected type of the event payload - 'custom-devtools:counter-state': { count: number; history: Array } + 'counter-state': { count: number; history: Array } } class CustomEventClient extends EventClient { constructor() { super({ - // The pluginId must match that of the event map key pluginId: 'custom-devtools', debug: true, }) diff --git a/examples/react/basic/src/plugin.ts b/examples/react/basic/src/plugin.ts index 8bac08e3..ba149842 100644 --- a/examples/react/basic/src/plugin.ts +++ b/examples/react/basic/src/plugin.ts @@ -1,15 +1,15 @@ import { EventClient } from '@tanstack/devtools-event-client' interface EventMap { - 'query-devtools:test': { + 'test': { title: string description: string } - 'query-devtools:init': { + 'init': { title: string description: string } - 'query-devtools:query': { + 'query': { title: string description: string } diff --git a/examples/react/custom-devtools/src/eventClient.ts b/examples/react/custom-devtools/src/eventClient.ts index d884641d..58c49e82 100644 --- a/examples/react/custom-devtools/src/eventClient.ts +++ b/examples/react/custom-devtools/src/eventClient.ts @@ -1,15 +1,12 @@ import { EventClient } from '@tanstack/devtools-event-client' type EventMap = { - // The key of the event map is a combination of {pluginId}:{eventSuffix} - // The value is the expected type of the event payload - 'custom-devtools:counter-state': { count: number; history: Array } + 'counter-state': { count: number; history: Array } } class CustomEventClient extends EventClient { constructor() { super({ - // The pluginId must match that of the event map key pluginId: 'custom-devtools', debug: true, }) diff --git a/examples/react/https/src/plugin.ts b/examples/react/https/src/plugin.ts index 8bac08e3..ba149842 100644 --- a/examples/react/https/src/plugin.ts +++ b/examples/react/https/src/plugin.ts @@ -1,15 +1,15 @@ import { EventClient } from '@tanstack/devtools-event-client' interface EventMap { - 'query-devtools:test': { + 'test': { title: string description: string } - 'query-devtools:init': { + 'init': { title: string description: string } - 'query-devtools:query': { + 'query': { title: string description: string } diff --git a/examples/react/start/src/devtools/route-navigation-event-client.ts b/examples/react/start/src/devtools/route-navigation-event-client.ts index 7f131220..91afa2c0 100644 --- a/examples/react/start/src/devtools/route-navigation-event-client.ts +++ b/examples/react/start/src/devtools/route-navigation-event-client.ts @@ -9,14 +9,11 @@ export interface RouteNavigationEvent { } type RouteNavigationEventMap = { - 'route-navigation:navigate': RouteNavigationEvent - 'route-navigation:clear': undefined + 'navigate': RouteNavigationEvent + 'clear': undefined } -class RouteNavigationEventClient extends EventClient< - RouteNavigationEventMap, - 'route-navigation' -> { +class RouteNavigationEventClient extends EventClient { constructor() { super({ pluginId: 'route-navigation', diff --git a/examples/react/time-travel/src/zustand-client.ts b/examples/react/time-travel/src/zustand-client.ts index 404dfd75..3efddf81 100644 --- a/examples/react/time-travel/src/zustand-client.ts +++ b/examples/react/time-travel/src/zustand-client.ts @@ -2,8 +2,8 @@ import { EventClient } from '@tanstack/devtools-event-client' import { createStore } from 'zustand' interface ZustandEventMap { - 'zustand:stateChange': any - 'zustand:revertSnapshot': any + 'stateChange': any + 'revertSnapshot': any } export const eventClient = new EventClient({ pluginId: 'zustand', diff --git a/packages/devtools-client/src/index.ts b/packages/devtools-client/src/index.ts index c0d33534..2c0303a1 100644 --- a/packages/devtools-client/src/index.ts +++ b/packages/devtools-client/src/index.ts @@ -53,37 +53,37 @@ export interface PluginInjection { } interface EventMap { - 'tanstack-devtools-core:ready': { + 'ready': { packageJson: PackageJson | null outdatedDeps: OutdatedDeps | null } - 'tanstack-devtools-core:outdated-deps-read': { + 'outdated-deps-read': { outdatedDeps: OutdatedDeps | null } - 'tanstack-devtools-core:package-json-read': { + 'package-json-read': { packageJson: PackageJson | null } - 'tanstack-devtools-core:mounted': void - 'tanstack-devtools-core:install-devtools': PluginInjection - 'tanstack-devtools-core:devtools-installed': { + 'mounted': void + 'install-devtools': PluginInjection + 'devtools-installed': { packageName: string success: boolean error?: string } - 'tanstack-devtools-core:add-plugin-to-devtools': PluginInjection - 'tanstack-devtools-core:plugin-added': { + 'add-plugin-to-devtools': PluginInjection + 'plugin-added': { packageName: string success: boolean error?: string } - 'tanstack-devtools-core:bump-package-version': PluginInjection & { + 'bump-package-version': PluginInjection & { devtoolsPackage: string minVersion?: string } - 'tanstack-devtools-core:package-json-updated': { + 'package-json-updated': { packageJson: PackageJson | null } - 'tanstack-devtools-core:trigger-toggled': { + 'trigger-toggled': { isOpen: boolean } } diff --git a/packages/event-bus-client/src/plugin.ts b/packages/event-bus-client/src/plugin.ts index 99b36151..8145c3c6 100644 --- a/packages/event-bus-client/src/plugin.ts +++ b/packages/event-bus-client/src/plugin.ts @@ -8,19 +8,15 @@ declare global { } type AllDevtoolsEvents> = { - [Key in keyof TEventMap]: TanStackDevtoolsEvent -}[keyof TEventMap] + [Key in keyof TEventMap & string]: TanStackDevtoolsEvent< + Key, + TEventMap[Key] + > +}[keyof TEventMap & string] -export class EventClient< - TEventMap extends Record, - TPluginId extends string = TEventMap extends Record - ? P extends `${infer Id}:${string}` - ? Id - : never - : never, -> { +export class EventClient> { #enabled = true - #pluginId: TPluginId + #pluginId: string #eventTarget: () => EventTarget #debug: boolean #queuedEvents: Array> @@ -80,7 +76,7 @@ export class EventClient< enabled = true, reconnectEveryMs = 300, }: { - pluginId: TPluginId + pluginId: string debug?: boolean reconnectEveryMs?: number enabled?: boolean @@ -194,16 +190,9 @@ export class EventClient< this.dispatchCustomEvent('tanstack-dispatch-event', event) } - createEventPayload< - TSuffix extends Extract< - keyof TEventMap, - `${TPluginId & string}:${string}` - > extends `${TPluginId & string}:${infer S}` - ? S - : never, - >( - eventSuffix: TSuffix, - payload: TEventMap[`${TPluginId & string}:${TSuffix}`], + createEventPayload( + eventSuffix: TEvent, + payload: TEventMap[TEvent], ) { return { type: `${this.#pluginId}:${eventSuffix}`, @@ -211,16 +200,9 @@ export class EventClient< pluginId: this.#pluginId, } } - emit< - TSuffix extends Extract< - keyof TEventMap, - `${TPluginId & string}:${string}` - > extends `${TPluginId & string}:${infer S}` - ? S - : never, - >( - eventSuffix: TSuffix, - payload: TEventMap[`${TPluginId & string}:${TSuffix}`], + emit( + eventSuffix: TEvent, + payload: TEventMap[TEvent], ) { if (!this.#enabled) { this.debugLog( @@ -262,20 +244,10 @@ export class EventClient< return this.emitEventToBus(this.createEventPayload(eventSuffix, payload)) } - on< - TSuffix extends Extract< - keyof TEventMap, - `${TPluginId & string}:${string}` - > extends `${TPluginId & string}:${infer S}` - ? S - : never, - >( - eventSuffix: TSuffix, + on( + eventSuffix: TEvent, cb: ( - event: TanStackDevtoolsEvent< - `${TPluginId & string}:${TSuffix}`, - TEventMap[`${TPluginId & string}:${TSuffix}`] - >, + event: TanStackDevtoolsEvent, ) => void, options?: { withEventTarget?: boolean diff --git a/packages/event-bus-client/tests/index.test.ts b/packages/event-bus-client/tests/index.test.ts index 3d9bc2ba..1a043d28 100644 --- a/packages/event-bus-client/tests/index.test.ts +++ b/packages/event-bus-client/tests/index.test.ts @@ -66,9 +66,9 @@ describe('EventClient', () => { const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') const targetListenSpy = vi.spyOn(target, 'addEventListener') const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') - const cleanup = client.on('test:event', () => {}) + const cleanup = client.on('event', () => {}) cleanup() - client.emit('test:event', { foo: 'bar' }) + client.emit('event', { foo: 'bar' }) expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) expect(targetListenSpy).toHaveBeenCalledWith( expect.any(String), @@ -90,9 +90,9 @@ describe('EventClient', () => { const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') const targetListenSpy = vi.spyOn(target, 'addEventListener') const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') - const cleanup = client.on('test:event', () => {}) + const cleanup = client.on('event', () => {}) cleanup() - client.emit('test:event', { foo: 'bar' }) + client.emit('event', { foo: 'bar' }) expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) expect(targetListenSpy).toHaveBeenCalledWith( expect.any(String), From 2c685c0324996cb4760156fad6b278f0ed4f9cde Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 09:58:04 +0000 Subject: [PATCH 2/3] ci: apply automated fixes --- examples/preact/basic/src/plugin.ts | 6 +++--- examples/react/basic/src/plugin.ts | 6 +++--- examples/react/https/src/plugin.ts | 6 +++--- .../start/src/devtools/route-navigation-event-client.ts | 4 ++-- examples/react/time-travel/src/zustand-client.ts | 4 ++-- packages/devtools-client/src/index.ts | 4 ++-- packages/event-bus-client/src/plugin.ts | 9 ++------- 7 files changed, 17 insertions(+), 22 deletions(-) diff --git a/examples/preact/basic/src/plugin.ts b/examples/preact/basic/src/plugin.ts index ba149842..c682050d 100644 --- a/examples/preact/basic/src/plugin.ts +++ b/examples/preact/basic/src/plugin.ts @@ -1,15 +1,15 @@ import { EventClient } from '@tanstack/devtools-event-client' interface EventMap { - 'test': { + test: { title: string description: string } - 'init': { + init: { title: string description: string } - 'query': { + query: { title: string description: string } diff --git a/examples/react/basic/src/plugin.ts b/examples/react/basic/src/plugin.ts index ba149842..c682050d 100644 --- a/examples/react/basic/src/plugin.ts +++ b/examples/react/basic/src/plugin.ts @@ -1,15 +1,15 @@ import { EventClient } from '@tanstack/devtools-event-client' interface EventMap { - 'test': { + test: { title: string description: string } - 'init': { + init: { title: string description: string } - 'query': { + query: { title: string description: string } diff --git a/examples/react/https/src/plugin.ts b/examples/react/https/src/plugin.ts index ba149842..c682050d 100644 --- a/examples/react/https/src/plugin.ts +++ b/examples/react/https/src/plugin.ts @@ -1,15 +1,15 @@ import { EventClient } from '@tanstack/devtools-event-client' interface EventMap { - 'test': { + test: { title: string description: string } - 'init': { + init: { title: string description: string } - 'query': { + query: { title: string description: string } diff --git a/examples/react/start/src/devtools/route-navigation-event-client.ts b/examples/react/start/src/devtools/route-navigation-event-client.ts index 91afa2c0..d3922603 100644 --- a/examples/react/start/src/devtools/route-navigation-event-client.ts +++ b/examples/react/start/src/devtools/route-navigation-event-client.ts @@ -9,8 +9,8 @@ export interface RouteNavigationEvent { } type RouteNavigationEventMap = { - 'navigate': RouteNavigationEvent - 'clear': undefined + navigate: RouteNavigationEvent + clear: undefined } class RouteNavigationEventClient extends EventClient { diff --git a/examples/react/time-travel/src/zustand-client.ts b/examples/react/time-travel/src/zustand-client.ts index 3efddf81..615b00f4 100644 --- a/examples/react/time-travel/src/zustand-client.ts +++ b/examples/react/time-travel/src/zustand-client.ts @@ -2,8 +2,8 @@ import { EventClient } from '@tanstack/devtools-event-client' import { createStore } from 'zustand' interface ZustandEventMap { - 'stateChange': any - 'revertSnapshot': any + stateChange: any + revertSnapshot: any } export const eventClient = new EventClient({ pluginId: 'zustand', diff --git a/packages/devtools-client/src/index.ts b/packages/devtools-client/src/index.ts index 2c0303a1..c99c399a 100644 --- a/packages/devtools-client/src/index.ts +++ b/packages/devtools-client/src/index.ts @@ -53,7 +53,7 @@ export interface PluginInjection { } interface EventMap { - 'ready': { + ready: { packageJson: PackageJson | null outdatedDeps: OutdatedDeps | null } @@ -63,7 +63,7 @@ interface EventMap { 'package-json-read': { packageJson: PackageJson | null } - 'mounted': void + mounted: void 'install-devtools': PluginInjection 'devtools-installed': { packageName: string diff --git a/packages/event-bus-client/src/plugin.ts b/packages/event-bus-client/src/plugin.ts index 8145c3c6..9fd2d07b 100644 --- a/packages/event-bus-client/src/plugin.ts +++ b/packages/event-bus-client/src/plugin.ts @@ -8,10 +8,7 @@ declare global { } type AllDevtoolsEvents> = { - [Key in keyof TEventMap & string]: TanStackDevtoolsEvent< - Key, - TEventMap[Key] - > + [Key in keyof TEventMap & string]: TanStackDevtoolsEvent }[keyof TEventMap & string] export class EventClient> { @@ -246,9 +243,7 @@ export class EventClient> { on( eventSuffix: TEvent, - cb: ( - event: TanStackDevtoolsEvent, - ) => void, + cb: (event: TanStackDevtoolsEvent) => void, options?: { withEventTarget?: boolean }, From eb3594fedbeda96cca9a2ae6276ece7853cc5a40 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 4 Mar 2026 10:59:57 +0100 Subject: [PATCH 3/3] chore: add patch changeset for event client type simplification --- .changeset/simplify-event-client-types.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/simplify-event-client-types.md diff --git a/.changeset/simplify-event-client-types.md b/.changeset/simplify-event-client-types.md new file mode 100644 index 00000000..c12133fc --- /dev/null +++ b/.changeset/simplify-event-client-types.md @@ -0,0 +1,6 @@ +--- +'@tanstack/devtools-event-client': patch +'@tanstack/devtools-client': patch +--- + +Simplify EventClient types to accept unprefixed event maps