feat: create event bus server + client communication for devtools#11
feat: create event bus server + client communication for devtools#11AlemTuzlak merged 27 commits intomainfrom
Conversation
| import { DevtoolsClient } from '@tanstack/devtools/client' | ||
|
|
||
| const devtoolsClient = new DevtoolsClient() | ||
|
|
||
| devtoolsClient.connect() | ||
|
|
||
| export { devtoolsClient } |
There was a problem hiding this comment.
create a client connection to the server to listen to incoming events
More templates
@tanstack/devtools
@tanstack/devtools-event-bus
@tanstack/devtools-event-client
@tanstack/react-devtools
@tanstack/solid-devtools
commit: |
| devtoolsClient.onAll((event) => { | ||
| console.log('Received message:', event) | ||
| setEvents((prev) => [...prev, event]) | ||
| }) | ||
| devtoolsClient.emit({ | ||
| type: 'init', | ||
| payload: { | ||
| title: 'Client Plugin Initialized', | ||
| description: 'Listening for events', | ||
| }, | ||
| }) |
There was a problem hiding this comment.
listens to events from client/server
| <p>Devtools Client is connected.</p> | ||
| <button | ||
| onClick={() => | ||
| devtoolsClient.emit({ |
There was a problem hiding this comment.
emits the event to server/client listeners
| import { z } from 'zod' | ||
| const devtoolsServer = new DevtoolsServer() | ||
|
|
||
| devtoolsServer.start() |
There was a problem hiding this comment.
start the dev server to listen to events
| devtoolsServer.on('click-event', (payload) => { | ||
| console.log('Click event received:', payload) | ||
| }) | ||
|
|
||
| devtoolsServer.onAll((e) => { | ||
| console.log('All events:', e) | ||
| }) | ||
|
|
||
| setInterval(() => { | ||
| console.log('Emitting server event...') | ||
| devtoolsServer.emit({ | ||
| type: 'server-event', | ||
| payload: { | ||
| title: 'Server Event', | ||
| description: 'This is a custom event emitted by the server.', | ||
| }, | ||
| }) | ||
| }, 5000) |
There was a problem hiding this comment.
send/listen to client/server events
| const eventMap = { | ||
| 'query-devtools:test': z.object({ | ||
| title: z.string(), | ||
| description: z.string(), | ||
| }), | ||
| 'query-devtools:init': z.object({ | ||
| title: z.string(), | ||
| description: z.string(), | ||
| }), | ||
| 'query-devtools:b': z.object({ | ||
| title: z.string(), | ||
| description: z.string(), | ||
| }), | ||
| } satisfies EventMap<'query-devtools'> | ||
|
|
||
| class QueryDevtoolsPlugin extends DevtoolsPlugin<typeof eventMap> { | ||
| constructor() { | ||
| super({ | ||
| pluginId: 'query-devtools', | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| const plugin = new QueryDevtoolsPlugin() |
There was a problem hiding this comment.
potential plugin API right here, WDYT?
| }), | ||
| } satisfies EventMap<'query-devtools'> | ||
|
|
||
| class QueryDevtoolsPlugin extends DevtoolsPlugin<typeof eventMap> { |
There was a problem hiding this comment.
any typescript wizards who know how to infer the types automatically without providing type args by passing it into the constructor maybe and still constraining the keys to start with the pluginId?
| plugin.onAll((e) => { | ||
| if (e.type === 'query-devtools:test') { | ||
| console.log('Received query-devtools:test event:', e.payload) | ||
| } | ||
| }) | ||
| plugin.on('query-devtools:test', (e) => { | ||
| e.payload | ||
| }) |
| payload: TPayload | ||
| } | ||
|
|
||
| export class DevtoolsClient { |
There was a problem hiding this comment.
connects to the server, sends events there, also listens to the events from the server, uses websockets, falls back to SSE
| TEventMap extends Record<string, any>, | ||
| TPluginId extends string = TEventMap extends Record<infer P, any> | ||
| ? P extends `${infer Id}:${string}` | ||
| ? Id | ||
| : never | ||
| : never, |
There was a problem hiding this comment.
would appreciate some help on auto-inferring these without providing type-args
| var __EVENT_TARGET__: EventTarget | null | ||
| } | ||
|
|
||
| export class DevtoolsServer { |
There was a problem hiding this comment.
core of the event-bus, emits events to server listeners via EventTarget (might change it to WS instead) and to the client via WS and falls back to SSE.
|
Here's chatgpt's feedback https://chatgpt.com/share/6890e13d-d5ec-800c-a58a-a36afcb640e7 |
|
Thanks, it made some good points 🙏 |
|
Really excited by this! One thought I had was that it could be modelled on MessageChannel / MessagePort api (https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API), it then may be possible to make it plug-and-play with other tools that work with that interface. For most use cases the event emitter is perfect, just update the state in the devtools, but sometimes you need a RPC type mechanism (trigger some action within the page and read the results). If it was models on the message port api, it may be possible to use it with ComLink to implement RPC: https://github.com/GoogleChromeLabs/comlink For the DB devtools I intend to bake in an event bus from the start so that we can make a version that works with react native in future. |
|
Interesting, I need to check this API out, do you have some advanced examples to get a feeling for what's possible? |
…tools into feat/bus-connection
|
TODO:
|
…tools into feat/bus-connection
This PR does the following:
EventClientinterface from@tanstack/devtools-event-client.@tanstack/devtools-event-busand@tanstack/detools-event-client