diff --git a/.changeset/metal-aliens-juggle.md b/.changeset/metal-aliens-juggle.md new file mode 100644 index 00000000..5ea3dc72 --- /dev/null +++ b/.changeset/metal-aliens-juggle.md @@ -0,0 +1,15 @@ +--- +'@tanstack/devtools-event-client': minor +'@tanstack/react-devtools': minor +'@tanstack/solid-devtools': minor +'@tanstack/devtools-event-bus': minor +'@tanstack/devtools': minor +--- + +Added event bus functionality into @tanstack/devtools + +- @tanstack/devtools now comes with an integrated Event Bus on the Client. +- The Event Bus allows for seamless communication between different parts of your running application + without tight coupling. +- Exposed APIs for publishing and subscribing to events. +- Added config for the client event bus diff --git a/examples/react/basic/package.json b/examples/react/basic/package.json index e511d082..509d7ff1 100644 --- a/examples/react/basic/package.json +++ b/examples/react/basic/package.json @@ -9,13 +9,15 @@ "test:types": "tsc" }, "dependencies": { + "@tanstack/devtools-event-client": "workspace:^", "@tanstack/react-devtools": "^0.1.1", "@tanstack/react-query": "^5.83.0", "@tanstack/react-query-devtools": "^5.83.0", "@tanstack/react-router": "^1.130.2", "@tanstack/react-router-devtools": "^1.130.2", "react": "^19.1.0", - "react-dom": "^19.1.0" + "react-dom": "^19.1.0", + "zod": "^4.0.14" }, "devDependencies": { "@types/react": "^19.1.2", diff --git a/examples/react/basic/src/index.tsx b/examples/react/basic/src/index.tsx index 14987e3f..a08f1873 100644 --- a/examples/react/basic/src/index.tsx +++ b/examples/react/basic/src/index.tsx @@ -1,6 +1,17 @@ import ReactDOM from 'react-dom/client' import Devtools from './setup' +import { queryPlugin } from './plugin' +setTimeout(() => { + queryPlugin.emit('test', { + title: 'Test Event', + description: + 'This is a test event from the TanStack Query Devtools plugin.', + }) +}, 1000) +queryPlugin.on('test', (event) => { + console.log('Received test event:', event) +}) function App() { return (
diff --git a/examples/react/basic/src/plugin.ts b/examples/react/basic/src/plugin.ts new file mode 100644 index 00000000..8bac08e3 --- /dev/null +++ b/examples/react/basic/src/plugin.ts @@ -0,0 +1,25 @@ +import { EventClient } from '@tanstack/devtools-event-client' + +interface EventMap { + 'query-devtools:test': { + title: string + description: string + } + 'query-devtools:init': { + title: string + description: string + } + 'query-devtools:query': { + title: string + description: string + } +} +class QueryDevtoolsPlugin extends EventClient { + constructor() { + super({ + pluginId: 'query-devtools', + }) + } +} + +export const queryPlugin = new QueryDevtoolsPlugin() diff --git a/examples/react/start/package.json b/examples/react/start/package.json index 65931747..59a6ff5f 100644 --- a/examples/react/start/package.json +++ b/examples/react/start/package.json @@ -17,21 +17,25 @@ "@prisma/extension-accelerate": "^2.0.2", "@prisma/studio-core": "^0.5.1", "@tailwindcss/vite": "^4.0.6", + "@tanstack/devtools-event-bus": "workspace:^", + "@tanstack/devtools-event-client": "workspace:^", "@tanstack/react-devtools": "^0.1.1", "@tanstack/react-query": "^5.83.0", "@tanstack/react-query-devtools": "^5.83.0", "@tanstack/react-router": "^1.130.2", "@tanstack/react-router-devtools": "^1.130.2", "@tanstack/react-router-with-query": "^1.130.2", - "@tanstack/react-start": "^1.130.2", + "@tanstack/react-start": "^1.130.15", "@tanstack/router-plugin": "^1.121.2", "prisma": "^6.13.0", "react": "^19.1.0", "react-dom": "^19.1.0", "tailwindcss": "^4.0.6", - "vite-tsconfig-paths": "^5.1.4" + "vite-tsconfig-paths": "^5.1.4", + "zod": "^4.0.14" }, "devDependencies": { + "@tanstack/devtools": "workspace:^", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.2.0", "@types/react": "^19.1.2", diff --git a/examples/react/start/src/components/client-plugin.tsx b/examples/react/start/src/components/client-plugin.tsx new file mode 100644 index 00000000..34532482 --- /dev/null +++ b/examples/react/start/src/components/client-plugin.tsx @@ -0,0 +1,41 @@ +import { queryPlugin } from '@/plugin' +import { useEffect, useState } from 'react' + +export default function ClientPlugin() { + const [events, setEvents] = useState< + Array<{ type: string; payload: { title: string; description: string } }> + >([]) + useEffect(() => { + const cleanup = queryPlugin.on('test', (event) => { + console.log('Received message in here:', event) + setEvents((prev) => [...prev, event as any]) + }) + + return cleanup + }, []) + return ( +
+

Client Plugin Initialized

+

Devtools Client is connected.

+ + {events.map((event, i) => ( +
+

{event.payload.title}

+

{event.payload.description}

+
+ ))} +
+ ) +} diff --git a/examples/react/start/src/components/devtools.tsx b/examples/react/start/src/components/devtools.tsx index 5833a28b..0c3e11b3 100644 --- a/examples/react/start/src/components/devtools.tsx +++ b/examples/react/start/src/components/devtools.tsx @@ -3,6 +3,7 @@ import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools' import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools' import { TanstackDevtools } from '@tanstack/react-devtools' import { StudioPlugin } from './prisma-plugin' +import ClientPlugin from './client-plugin' const queryClient = new QueryClient() @@ -11,6 +12,9 @@ export default function DevtoolsExample() { <> , }, + { + name: 'Client Plugin', + render: , + }, ]} /> diff --git a/examples/react/start/src/plugin.ts b/examples/react/start/src/plugin.ts new file mode 100644 index 00000000..18b3f2fc --- /dev/null +++ b/examples/react/start/src/plugin.ts @@ -0,0 +1,26 @@ +import { EventClient } from '@tanstack/devtools-event-client' + +interface EventMap { + 'query-devtools:test': { + title: string + description: string + } + 'query-devtools:init': { + title: string + description: string + } + 'query-devtools:query': { + title: string + description: string + } +} + +class QueryDevtoolsClient extends EventClient { + constructor() { + super({ + pluginId: 'query-devtools', + }) + } +} + +export const queryPlugin = new QueryDevtoolsClient() diff --git a/examples/react/start/src/server-setup.ts b/examples/react/start/src/server-setup.ts new file mode 100644 index 00000000..d6937f91 --- /dev/null +++ b/examples/react/start/src/server-setup.ts @@ -0,0 +1,7 @@ +import { ServerEventBus } from '@tanstack/devtools-event-bus/server' + +const devtoolsServer = new ServerEventBus() + +devtoolsServer.start() + +export { devtoolsServer } diff --git a/examples/react/start/vite.config.ts b/examples/react/start/vite.config.ts index f2b854b4..8a70c308 100644 --- a/examples/react/start/vite.config.ts +++ b/examples/react/start/vite.config.ts @@ -3,6 +3,7 @@ import { tanstackStart } from '@tanstack/react-start/plugin/vite' import viteReact from '@vitejs/plugin-react' import viteTsConfigPaths from 'vite-tsconfig-paths' import tailwindcss from '@tailwindcss/vite' +//import { devtoolsServer } from './src/server-setup' const config = defineConfig({ plugins: [ @@ -10,6 +11,7 @@ const config = defineConfig({ viteTsConfigPaths({ projects: ['./tsconfig.json'], }), + tailwindcss(), tanstackStart({ customViteReactPlugin: true, diff --git a/package.json b/package.json index 448084f2..77bd0de2 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "changeset:version": "changeset version && pnpm install --no-frozen-lockfile && pnpm prettier:write", "clean": "find . -name 'dist' -type d -prune -exec rm -rf {} +", "clean:node_modules": "find . -name 'node_modules' -type d -prune -exec rm -rf {} +", + "clean:all": "pnpm run clean && pnpm run clean:node_modules", "dev": "pnpm run watch", "docs:generate": "node scripts/generateDocs.js", "format": "pnpm run prettier:write", diff --git a/packages/devtools/README.md b/packages/devtools/README.md index 42286506..4de2d3cd 100644 --- a/packages/devtools/README.md +++ b/packages/devtools/README.md @@ -27,8 +27,8 @@ In order to create a plugin for TanStack Devtools, you can use the `plugins` arg import { TanStackDevtoolsCore } from '@tanstack/devtools' const devtools = new TanStackDevtoolsCore({ - options: { - // your options here + config: { + // your config here }, plugins: [ { diff --git a/packages/devtools/package.json b/packages/devtools/package.json index 25d2ee0c..20a24b42 100644 --- a/packages/devtools/package.json +++ b/packages/devtools/package.json @@ -54,6 +54,7 @@ }, "dependencies": { "@solid-primitives/keyboard": "^1.2.8", + "@tanstack/devtools-event-bus": "workspace:*", "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.7" diff --git a/packages/devtools/src/core.tsx b/packages/devtools/src/core.tsx index 4f40d921..b372db34 100644 --- a/packages/devtools/src/core.tsx +++ b/packages/devtools/src/core.tsx @@ -1,7 +1,9 @@ import { lazy } from 'solid-js' import { Portal, render } from 'solid-js/web' +import { ClientEventBus } from '@tanstack/devtools-event-bus/client' import { DevtoolsProvider } from './context/devtools-context' import { initialState } from './context/devtools-store' +import type { ClientEventBusConfig } from '@tanstack/devtools-event-bus/client' import type { TanStackDevtoolsConfig, TanStackDevtoolsPlugin, @@ -34,6 +36,7 @@ export interface TanStackDevtoolsInit { * ``` */ plugins?: Array + eventBusConfig?: ClientEventBusConfig } export class TanStackDevtoolsCore { @@ -44,9 +47,12 @@ export class TanStackDevtoolsCore { #isMounted = false #dispose?: () => void #Component: any + #eventBus: ClientEventBus | undefined + #eventBusConfig: ClientEventBusConfig | undefined constructor(init: TanStackDevtoolsInit) { this.#plugins = init.plugins || [] + this.#eventBusConfig = init.eventBusConfig this.#config = { ...this.#config, ...init.config, @@ -60,9 +66,9 @@ export class TanStackDevtoolsCore { const mountTo = el const dispose = render(() => { this.#Component = lazy(() => import('./devtools')) - const Devtools = this.#Component - + this.#eventBus = new ClientEventBus(this.#eventBusConfig) + this.#eventBus.start() return ( @@ -80,6 +86,7 @@ export class TanStackDevtoolsCore { if (!this.#isMounted) { throw new Error('Devtools is not mounted') } + this.#eventBus?.stop() this.#dispose?.() this.#isMounted = false } @@ -91,3 +98,5 @@ export class TanStackDevtoolsCore { } } } + +export type { ClientEventBusConfig } diff --git a/packages/devtools/src/index.ts b/packages/devtools/src/index.ts index 8b8497ef..058273ba 100644 --- a/packages/devtools/src/index.ts +++ b/packages/devtools/src/index.ts @@ -1,6 +1,6 @@ export { PLUGIN_CONTAINER_ID, PLUGIN_TITLE_CONTAINER_ID } from './constants' export { TanStackDevtoolsCore } from './core' -export type { TanStackDevtoolsInit } from './core' +export type { TanStackDevtoolsInit, ClientEventBusConfig } from './core' export type { TanStackDevtoolsPlugin, TanStackDevtoolsConfig, diff --git a/packages/devtools/tsconfig.json b/packages/devtools/tsconfig.json index 1c53831b..50837fb4 100644 --- a/packages/devtools/tsconfig.json +++ b/packages/devtools/tsconfig.json @@ -1,6 +1,12 @@ { "extends": "../../tsconfig.json", - "include": ["src", "eslint.config.js", "vite.config.ts", "tests"], + "include": [ + "src", + "eslint.config.js", + "vite.config.ts", + "tests", + "src/server" + ], "compilerOptions": { "jsxImportSource": "solid-js", "jsx": "preserve" diff --git a/packages/event-bus-client/README.md b/packages/event-bus-client/README.md new file mode 100644 index 00000000..614a17d7 --- /dev/null +++ b/packages/event-bus-client/README.md @@ -0,0 +1,39 @@ +# @tanstack/devtools + +This package is still under active development and might have breaking changes in the future. Please use it with caution. + +## General Usage + +```tsx +import { EventClient } from '@tanstack/devtools-event-client' + +interface EventMap { + 'query-devtools:a': { foo: string } + 'query-devtools:b': { foo: number } +} + +class QueryDevtoolsPlugin extends EventClient { + constructor() { + super({ + pluginId: 'query-devtools', + }) + } +} + +export const queryPlugin = new QueryDevtoolsPlugin() + +// I'm fully typed here +plugin.emit('a', { + foo: 'bar', +}) +plugin.on('b', (e) => { + // I'm fully typed here + e.payload.foo +}) +``` + +## Understanding the implementation + +The `EventClient` class is a base class for creating plugins that can subscribe to events in the Tanstack Devtools event bus. It allows you to define a set of events and their corresponding data schemas using a standard-schema based schemas. + +It will work on both the client/server side and all you have to worry about is emitting/listening to events. diff --git a/packages/event-bus-client/eslint.config.js b/packages/event-bus-client/eslint.config.js new file mode 100644 index 00000000..e472c69e --- /dev/null +++ b/packages/event-bus-client/eslint.config.js @@ -0,0 +1,10 @@ +// @ts-check + +import rootConfig from '../../eslint.config.js' + +export default [ + ...rootConfig, + { + rules: {}, + }, +] diff --git a/packages/event-bus-client/package.json b/packages/event-bus-client/package.json new file mode 100644 index 00000000..49021398 --- /dev/null +++ b/packages/event-bus-client/package.json @@ -0,0 +1,58 @@ +{ + "name": "@tanstack/devtools-event-client", + "version": "0.1.1", + "description": "TanStack Event Client is a lightweight event client for TanStack Devtools event bus.", + "author": "Tanner Linsley", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/TanStack/devtools.git", + "directory": "packages/devtools" + }, + "homepage": "https://tanstack.com/devtools", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "keywords": [ + "devtools" + ], + "type": "module", + "types": "dist/esm/index.d.ts", + "main": "dist/cjs/index.cjs", + "module": "dist/esm/index.js", + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/cjs/index.d.cts", + "default": "./dist/cjs/index.cjs" + } + }, + "./package.json": "./package.json" + }, + "sideEffects": false, + "engines": { + "node": ">=18" + }, + "files": [ + "dist/", + "src" + ], + "scripts": { + "clean": "premove ./build ./dist", + "lint:fix": "eslint ./src --fix", + "test:eslint": "eslint ./src", + "test:lib": "vitest", + "test:lib:dev": "pnpm test:lib --watch", + "test:types": "tsc", + "test:build": "publint --strict", + "build": "vite build" + }, + "devDependencies": { + "@tanstack/devtools-event-bus": "workspace:*" + } +} diff --git a/packages/event-bus-client/src/index.ts b/packages/event-bus-client/src/index.ts new file mode 100644 index 00000000..6b3402a0 --- /dev/null +++ b/packages/event-bus-client/src/index.ts @@ -0,0 +1 @@ +export { EventClient } from './plugin' diff --git a/packages/event-bus-client/src/plugin.ts b/packages/event-bus-client/src/plugin.ts new file mode 100644 index 00000000..192cb0dd --- /dev/null +++ b/packages/event-bus-client/src/plugin.ts @@ -0,0 +1,150 @@ +interface TanStackDevtoolsEvent { + type: TEventName + payload: TPayload + pluginId?: string // Optional pluginId to filter events by plugin +} +declare global { + // eslint-disable-next-line no-var + var __TANSTACK_EVENT_TARGET__: EventTarget | null +} + +type AllDevtoolsEvents> = { + [Key in keyof TEventMap]: TanStackDevtoolsEvent +}[keyof TEventMap] + +export class EventClient< + TEventMap extends Record, + TPluginId extends string = TEventMap extends Record + ? P extends `${infer Id}:${string}` + ? Id + : never + : never, +> { + #pluginId: TPluginId + #eventTarget: () => EventTarget + #debug: boolean + + constructor({ + pluginId, + debug = false, + }: { + pluginId: TPluginId + debug?: boolean + }) { + this.#pluginId = pluginId + this.#eventTarget = this.getGlobalTarget + this.#debug = debug + this.debugLog(' Initializing event subscription for plugin', this.#pluginId) + } + + private debugLog(...args: Array) { + if (this.#debug) { + console.log(`🌴 [tanstack-devtools:${this.#pluginId}-plugin]`, ...args) + } + } + private getGlobalTarget() { + // server one is the global event target + if ( + typeof globalThis !== 'undefined' && + globalThis.__TANSTACK_EVENT_TARGET__ + ) { + this.debugLog('Using global event target') + return globalThis.__TANSTACK_EVENT_TARGET__ + } + // CLient event target is the window object + if (typeof window !== 'undefined') { + this.debugLog('Using window as event target') + + return window + } + + this.debugLog('Using new EventTarget as fallback') + return new EventTarget() + } + + getPluginId() { + return this.#pluginId + } + + private emitEventToBus(event: TanStackDevtoolsEvent) { + this.debugLog('Emitting event to client bus', event) + this.#eventTarget().dispatchEvent( + new CustomEvent('tanstack-dispatch-event', { detail: event }), + ) + } + + emit< + TSuffix extends Extract< + keyof TEventMap, + `${TPluginId & string}:${string}` + > extends `${TPluginId & string}:${infer S}` + ? S + : never, + >( + eventSuffix: TSuffix, + payload: TEventMap[`${TPluginId & string}:${TSuffix}`], + ) { + this.emitEventToBus({ + type: `${this.#pluginId}:${eventSuffix}`, + payload, + pluginId: this.#pluginId, + }) + } + + on< + TSuffix extends Extract< + keyof TEventMap, + `${TPluginId & string}:${string}` + > extends `${TPluginId & string}:${infer S}` + ? S + : never, + >( + eventSuffix: TSuffix, + cb: ( + event: TanStackDevtoolsEvent< + `${TPluginId & string}:${TSuffix}`, + TEventMap[`${TPluginId & string}:${TSuffix}`] + >, + ) => void, + ) { + const eventName = `${this.#pluginId}:${eventSuffix}` as const + const handler = (e: Event) => { + this.debugLog('Received event from bus', (e as CustomEvent).detail) + cb((e as CustomEvent).detail) + } + this.#eventTarget().addEventListener(eventName, handler) + this.debugLog('Registered event to bus', eventName) + return () => { + this.#eventTarget().removeEventListener(eventName, handler) + } + } + + onAll(cb: (event: TanStackDevtoolsEvent) => void) { + const handler = (e: Event) => { + const event = (e as CustomEvent).detail + + cb(event) + } + this.#eventTarget().addEventListener('tanstack-devtools-global', handler) + return () => + this.#eventTarget().removeEventListener( + 'tanstack-devtools-global', + handler, + ) + } + onAllPluginEvents(cb: (event: AllDevtoolsEvents) => void) { + const handler = (e: Event) => { + const event = (e as CustomEvent).detail + if (this.#pluginId && event.pluginId !== this.#pluginId) { + return + } + cb(event) + } + this.#eventTarget().addEventListener('tanstack-devtools-global', handler) + return () => + this.#eventTarget().removeEventListener( + 'tanstack-devtools-global', + handler, + ) + } +} diff --git a/packages/event-bus-client/tests/index.test.ts b/packages/event-bus-client/tests/index.test.ts new file mode 100644 index 00000000..37eb501c --- /dev/null +++ b/packages/event-bus-client/tests/index.test.ts @@ -0,0 +1,226 @@ +import { describe, expect, it, vi } from 'vitest' +import { EventClient } from '../src' +import { ClientEventBus } from '@tanstack/devtools-event-bus/client' + +// start the client bus for testing +new ClientEventBus().start() +// client bus uses window to dispatch events +const clientBusEmitTarget = window + +describe('EventClient', () => { + describe('debug config', () => { + it('should emit logs when debug set to true and have the correct plugin name', () => { + const consoleSpy = vi.spyOn(console, 'log') + new EventClient({ + debug: true, + pluginId: 'test', + }) + expect(consoleSpy).toHaveBeenCalledWith( + '🌴 [tanstack-devtools:test-plugin]', + ' Initializing event subscription for plugin', + 'test', + ) + }) + + it("shouldn't emit logs when debug set to false", () => { + const consoleSpy = vi.spyOn(console, 'log') + new EventClient({ + debug: false, + pluginId: 'test', + }) + expect(consoleSpy).not.toHaveBeenCalled() + }) + }) + + describe('getGlobalTarget', () => { + it('if the global target is set it should re-use it for emitting/listening/removing of events', () => { + const target = new EventTarget() + globalThis.__TANSTACK_EVENT_TARGET__ = target + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') + const targetListenSpy = vi.spyOn(target, 'addEventListener') + const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') + const cleanup = client.on('test:event', () => {}) + cleanup() + client.emit('test:event', { foo: 'bar' }) + expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) + expect(targetListenSpy).toHaveBeenCalledWith( + expect.any(String), + expect.any(Function), + ) + expect(targetRemoveSpy).toHaveBeenCalledWith( + expect.any(String), + expect.any(Function), + ) + globalThis.__TANSTACK_EVENT_TARGET__ = null + }) + + it('should use the window object if the globalTarget is not set for emitting/listening/removing of events', () => { + const target = window + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const targetEmitSpy = vi.spyOn(target, 'dispatchEvent') + const targetListenSpy = vi.spyOn(target, 'addEventListener') + const targetRemoveSpy = vi.spyOn(target, 'removeEventListener') + const cleanup = client.on('test:event', () => {}) + cleanup() + client.emit('test:event', { foo: 'bar' }) + expect(targetEmitSpy).toHaveBeenCalledWith(expect.any(Event)) + expect(targetListenSpy).toHaveBeenCalledWith( + expect.any(String), + expect.any(Function), + ) + expect(targetRemoveSpy).toHaveBeenCalledWith( + expect.any(String), + expect.any(Function), + ) + }) + }) + + describe('on', () => { + it('should register an event with the pluginId (event => test:event)', () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + + const eventBusSpy = vi.spyOn(clientBusEmitTarget, 'addEventListener') + client.on('event', () => {}) + expect(eventBusSpy).toHaveBeenCalledWith( + 'test:event', + expect.any(Function), + ) + }) + it('should register an event listener for the specified event and get events when they are emitted', () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + const cleanup = client.on('event', eventHandler) + client.emit('event', { foo: 'bar' }) + expect(eventHandler).toHaveBeenCalledWith({ + type: 'test:event', + payload: { foo: 'bar' }, + pluginId: 'test', + }) + cleanup() + }) + + it("shouldn't get an event if unregistered before it comes", () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + const cleanup = client.on('event', eventHandler) + cleanup() + client.emit('event', { foo: 'bar' }) + expect(eventHandler).not.toHaveBeenCalled() + }) + + it("shouldn't get an event if it's not registered to it", () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + client.on('event', eventHandler) + client.emit('other_event', { foo: 'bar' }) + expect(eventHandler).not.toHaveBeenCalled() + }) + }) + + describe('emit', () => { + it('should emit an event with the correct type and payload to the event bus', () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + client.on('event', eventHandler) + client.emit('event', { foo: 'bar' }) + expect(eventHandler).toHaveBeenCalledWith({ + type: 'test:event', + payload: { foo: 'bar' }, + pluginId: 'test', + }) + }) + }) + + describe('onAll', () => { + it('should listen to all events even if they do not come from the registered client', () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + client.onAll(eventHandler) + client.emit('event', { foo: 'bar' }) + expect(eventHandler).toHaveBeenCalledWith({ + type: 'test:event', + payload: { foo: 'bar' }, + pluginId: 'test', + }) + clientBusEmitTarget.dispatchEvent( + new CustomEvent('tanstack-dispatch-event', { + detail: { + type: 'other-plugin', + payload: { foo: 'bar' }, + }, + }), + ) + + expect(eventHandler).lastCalledWith({ + type: 'other-plugin', + payload: { foo: 'bar' }, + }) + }) + }) + + describe('onAllPluginEvents', () => { + it('should listen to all events that come from the plugin', () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + client.onAllPluginEvents(eventHandler) + client.emit('event', { foo: 'bar' }) + client.emit('event2', { foo: 'bar' }) + expect(eventHandler).nthCalledWith(1, { + type: 'test:event', + payload: { foo: 'bar' }, + pluginId: 'test', + }) + expect(eventHandler).nthCalledWith(2, { + type: 'test:event2', + payload: { foo: 'bar' }, + pluginId: 'test', + }) + }) + + it('should ignore events that do not come from the plugin', () => { + const client = new EventClient({ + debug: false, + pluginId: 'test', + }) + const eventHandler = vi.fn() + client.onAllPluginEvents(eventHandler) + clientBusEmitTarget.dispatchEvent( + new CustomEvent('tanstack-dispatch-event', { + detail: { + type: 'other-plugin', + payload: { foo: 'bar' }, + }, + }), + ) + expect(eventHandler).not.toHaveBeenCalled() + }) + }) +}) diff --git a/packages/event-bus-client/tests/test-setup.ts b/packages/event-bus-client/tests/test-setup.ts new file mode 100644 index 00000000..a9d0dd31 --- /dev/null +++ b/packages/event-bus-client/tests/test-setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom/vitest' diff --git a/packages/event-bus-client/tsconfig.docs.json b/packages/event-bus-client/tsconfig.docs.json new file mode 100644 index 00000000..2880b4df --- /dev/null +++ b/packages/event-bus-client/tsconfig.docs.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["tests", "src"] +} diff --git a/packages/event-bus-client/tsconfig.json b/packages/event-bus-client/tsconfig.json new file mode 100644 index 00000000..1838eb23 --- /dev/null +++ b/packages/event-bus-client/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src", + "eslint.config.js", + "vite.config.ts", + "tests", + "src/server" + ] +} diff --git a/packages/event-bus-client/vite.config.ts b/packages/event-bus-client/vite.config.ts new file mode 100644 index 00000000..79dae7cd --- /dev/null +++ b/packages/event-bus-client/vite.config.ts @@ -0,0 +1,23 @@ +import { defineConfig, mergeConfig } from 'vitest/config' +import { tanstackViteConfig } from '@tanstack/config/vite' +import packageJson from './package.json' + +const config = defineConfig({ + plugins: [], + test: { + name: packageJson.name, + dir: './', + watch: false, + environment: 'jsdom', + setupFiles: ['./tests/test-setup.ts'], + globals: true, + }, +}) + +export default mergeConfig( + config, + tanstackViteConfig({ + entry: ['./src/index.ts'], + srcDir: './src', + }), +) diff --git a/packages/event-bus/README.md b/packages/event-bus/README.md new file mode 100644 index 00000000..918d61b2 --- /dev/null +++ b/packages/event-bus/README.md @@ -0,0 +1,33 @@ +# @tanstack/devtools-event-bus + +This package is still under active development and might have breaking changes in the future. Please use it with caution. + +## General Usage + +### Server Event Bus + +```tsx +import { ServerEventBus } from '@tanstack/devtools-event-bus/server' +// Start the server event bus +const devtoolsServer = new ServerEventBus() + +devtoolsServer.start() + +export { devtoolsServer } +``` + +### Client Event Bus + +```ts +import { ClientEventBus } from '@tanstack/devtools-event-bus/client' +// Start the client event bus +const devtoolsClient = new ClientEventBus() + +devtoolsClient.start() + +export { devtoolsClient } +``` + +## Plugins + +Check out @tanstack/devtools-event-client for more information on how to create plugins for the event bus. diff --git a/packages/event-bus/eslint.config.js b/packages/event-bus/eslint.config.js new file mode 100644 index 00000000..e472c69e --- /dev/null +++ b/packages/event-bus/eslint.config.js @@ -0,0 +1,10 @@ +// @ts-check + +import rootConfig from '../../eslint.config.js' + +export default [ + ...rootConfig, + { + rules: {}, + }, +] diff --git a/packages/event-bus/package.json b/packages/event-bus/package.json new file mode 100644 index 00000000..dcb16231 --- /dev/null +++ b/packages/event-bus/package.json @@ -0,0 +1,82 @@ +{ + "name": "@tanstack/devtools-event-bus", + "version": "0.1.1", + "description": "TanStack Event Bus is a lightweight event bus for TanStack Devtools.", + "author": "Tanner Linsley", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/TanStack/devtools.git", + "directory": "packages/devtools" + }, + "homepage": "https://tanstack.com/devtools", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "keywords": [ + "devtools" + ], + "type": "module", + "types": "dist/esm/index.d.ts", + "main": "dist/cjs/index.cjs", + "module": "dist/esm/index.js", + "exports": { + "./server": { + "import": { + "types": "./dist/esm/server/index.d.ts", + "default": "./dist/esm/server/index.js" + }, + "require": { + "types": "./dist/cjs/server/index.d.cts", + "default": "./dist/cjs/server/index.cjs" + } + }, + "./client": { + "import": { + "types": "./dist/esm/client/index.d.ts", + "default": "./dist/esm/client/index.js" + }, + "require": { + "types": "./dist/cjs/client/index.d.cts", + "default": "./dist/cjs/client/index.cjs" + } + }, + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/cjs/index.d.cts", + "default": "./dist/cjs/index.cjs" + } + }, + "./package.json": "./package.json" + }, + "sideEffects": false, + "engines": { + "node": ">=18" + }, + "files": [ + "dist/", + "src" + ], + "scripts": { + "clean": "premove ./build ./dist", + "lint:fix": "eslint ./src --fix", + "test:eslint": "eslint ./src", + "test:lib": "vitest", + "test:lib:dev": "pnpm test:lib --watch", + "test:types": "tsc", + "test:build": "publint --strict", + "build": "vite build" + }, + "dependencies": { + "ws": "^8.18.3" + }, + "devDependencies": { + "@types/node": "^22.15.2", + "@types/ws": "^8.18.1" + } +} diff --git a/packages/event-bus/src/client/client.ts b/packages/event-bus/src/client/client.ts new file mode 100644 index 00000000..0695b781 --- /dev/null +++ b/packages/event-bus/src/client/client.ts @@ -0,0 +1,165 @@ +interface TanStackDevtoolsEvent { + type: TEventName + payload: TPayload + pluginId?: string // Optional pluginId to filter events by plugin +} + +export interface ClientEventBusConfig { + /** + * Optional flag to indicate if the devtools server event bus is available to connect to. + * This is used to determine if the devtools can connect to the server for real-time event streams. + */ + connectToServerBus?: boolean + + /** + * Optional flag to enable debug mode for the event bus. + */ + debug?: boolean + + /** + * Optional port to connect to the devtools server event bus. + * Defaults to 42069. + */ + port?: number +} + +export class ClientEventBus { + #port: number + #socket: WebSocket | null + #eventSource: EventSource | null + #eventTarget: EventTarget + #debug: boolean + #connectToServerBus: boolean + #dispatcher = (e: Event) => { + const event = (e as CustomEvent).detail + this.emitToServer(event) + this.emitToClients(event) + } + constructor({ + port = 42069, + debug = false, + connectToServerBus = false, + }: ClientEventBusConfig = {}) { + this.#debug = debug + this.#eventSource = null + this.#port = port + this.#socket = null + this.#connectToServerBus = connectToServerBus + this.#eventTarget = this.getGlobalTarget() + this.debugLog('Initializing client event bus') + } + + private emitToClients(event: TanStackDevtoolsEvent) { + this.debugLog('Emitting event from client bus', event) + const specificEvent = new CustomEvent(event.type, { detail: event }) + this.debugLog('Emitting event to specific client listeners', event) + this.#eventTarget.dispatchEvent(specificEvent) + const globalEvent = new CustomEvent('tanstack-devtools-global', { + detail: event, + }) + this.debugLog('Emitting event to global client listeners', event) + this.#eventTarget.dispatchEvent(globalEvent) + } + + private emitToServer(event: TanStackDevtoolsEvent) { + const json = JSON.stringify(event) + // try to emit it to the event bus first + if (this.#socket && this.#socket.readyState === WebSocket.OPEN) { + this.debugLog('Emitting event to server via WS', event) + this.#socket.send(json) + // try to emit to SSE if WebSocket is not available (this will only happen on the client side) + } else if (this.#eventSource) { + this.debugLog('Emitting event to server via SSE', event) + + fetch(`http://localhost:${this.#port}/__devtools/send`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: json, + }).catch(() => {}) + } + } + start() { + this.debugLog('Starting client event bus') + if (typeof window === 'undefined') { + return + } + if (this.#connectToServerBus) { + this.connect() + } + this.#eventTarget = window + this.#eventTarget.addEventListener( + 'tanstack-dispatch-event', + this.#dispatcher, + ) + } + stop() { + this.debugLog('Stopping client event bus') + if (typeof window === 'undefined') { + return + } + this.#eventTarget.removeEventListener( + 'tanstack-dispatch-event', + this.#dispatcher, + ) + this.#eventSource?.close() + this.#socket?.close() + this.#socket = null + this.#eventSource = null + } + private getGlobalTarget() { + if (typeof window !== 'undefined') { + return window + } + + return new EventTarget() + } + private debugLog(...messages: Array) { + if (this.#debug) { + console.log('🌴 [tanstack-devtools:client-bus]', ...messages) + } + } + private connectSSE() { + this.debugLog('Connecting to SSE server') + this.#eventSource = new EventSource( + `http://localhost:${this.#port}/__devtools/sse`, + ) + this.#eventSource.onmessage = (e) => { + this.debugLog('Received message from SSE server', e.data) + this.handleEventReceived(e.data) + } + } + + private connectWebSocket() { + this.debugLog('Connecting to WebSocket server') + + this.#socket = new WebSocket(`ws://localhost:${this.#port}/__devtools/ws`) + this.#socket.onmessage = (e) => { + this.debugLog('Received message from server', e.data) + this.handleEventReceived(e.data) + } + this.#socket.onclose = () => { + this.debugLog('WebSocket connection closed') + this.#socket = null + } + this.#socket.onerror = () => { + this.debugLog('WebSocket connection error') + } + } + + private connect() { + try { + this.connectWebSocket() + } catch { + // Do not try to connect if we're on the server side + if (typeof window === 'undefined') return + this.connectSSE() + } + } + + private handleEventReceived(data: string) { + try { + const event = JSON.parse(data) as TanStackDevtoolsEvent + this.emitToClients(event) + } catch {} + } +} diff --git a/packages/event-bus/src/client/index.ts b/packages/event-bus/src/client/index.ts new file mode 100644 index 00000000..c083f323 --- /dev/null +++ b/packages/event-bus/src/client/index.ts @@ -0,0 +1,2 @@ +export { ClientEventBus } from './client' +export type { ClientEventBusConfig } from './client' diff --git a/packages/event-bus/src/index.ts b/packages/event-bus/src/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/event-bus/src/server/index.ts b/packages/event-bus/src/server/index.ts new file mode 100644 index 00000000..03485a72 --- /dev/null +++ b/packages/event-bus/src/server/index.ts @@ -0,0 +1,2 @@ +export { ServerEventBus } from './server' +export type { TanstackDevtoolsEvent } from './server' diff --git a/packages/event-bus/src/server/server.ts b/packages/event-bus/src/server/server.ts new file mode 100644 index 00000000..932381f5 --- /dev/null +++ b/packages/event-bus/src/server/server.ts @@ -0,0 +1,205 @@ +import http from 'node:http' +import { WebSocket, WebSocketServer } from 'ws' + +// Shared types +export interface TanstackDevtoolsEvent< + TEventName extends string, + TPayload = any, +> { + type: TEventName + payload: TPayload + pluginId?: string // Optional pluginId to filter events by plugin +} +// Used so no new server starts up when HMR happens +declare global { + // eslint-disable-next-line no-var + var __TANSTACK_DEVTOOLS_SERVER__: http.Server | null + // eslint-disable-next-line no-var + var __TANSTACK_DEVTOOLS_WSS_SERVER__: WebSocketServer | null + // eslint-disable-next-line no-var + var __EVENT_TARGET__: EventTarget | null +} + +export class ServerEventBus { + #eventTarget: EventTarget + #clients = new Set() + #sseClients = new Set() + #server: http.Server | null = null + #wssServer: WebSocketServer | null = null + #port: number + #debug: boolean + #dispatcher = (e: Event) => { + const event = (e as CustomEvent).detail + this.debugLog('Dispatching event from dispatcher, forwarding', event) + this.emit(event) + } + + constructor({ port = 42069, debug = false } = {}) { + this.#port = port + this.#eventTarget = globalThis.__EVENT_TARGET__ ?? new EventTarget() + // we want to set the global event target only once so that we can emit/listen to events on the server + if (!globalThis.__EVENT_TARGET__) { + globalThis.__EVENT_TARGET__ = this.#eventTarget + } + this.#server = globalThis.__TANSTACK_DEVTOOLS_SERVER__ ?? null + this.#wssServer = globalThis.__TANSTACK_DEVTOOLS_WSS_SERVER__ ?? null + this.#debug = debug + this.debugLog('Initializing server event bus') + } + + private debugLog(...args: Array) { + if (this.#debug) { + console.log('🌴 [tanstack-devtools:server-bus] ', ...args) + } + } + + private emitToServer(event: TanstackDevtoolsEvent) { + this.debugLog('Emitting event to specific server listeners', event) + this.#eventTarget.dispatchEvent( + new CustomEvent(event.type, { detail: event }), + ) + this.debugLog('Emitting event to global server listeners', event) + this.#eventTarget.dispatchEvent( + new CustomEvent('tanstack-devtools-global', { detail: event }), + ) + } + + private emitEventToClients(event: TanstackDevtoolsEvent) { + this.debugLog('Emitting event to clients', event) + const json = JSON.stringify(event) + + for (const client of this.#clients) { + if (client.readyState === WebSocket.OPEN) { + client.send(json) + } + } + for (const res of this.#sseClients) { + res.write(`data: ${json}\n\n`) + } + } + + private emit(event: TanstackDevtoolsEvent) { + this.emitEventToClients(event) + this.emitToServer(event) + } + + private createSSEServer() { + if (this.#server) { + return this.#server + } + const server = http.createServer((req, res) => { + if (req.url === '/__devtools/sse') { + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + 'Access-Control-Allow-Origin': '*', + }) + res.write('\n') + this.debugLog('New SSE client connected') + this.#sseClients.add(res) + req.on('close', () => this.#sseClients.delete(res)) + return + } + + if (req.url === '/__devtools/send' && req.method === 'POST') { + let body = '' + req.on('data', (chunk) => (body += chunk)) + req.on('end', () => { + try { + const msg = JSON.parse(body) + this.debugLog('Received event from client', msg) + this.emitToServer(msg) + } catch {} + }) + res.writeHead(200).end() + return + } + + res.statusCode = 404 + res.end() + }) + globalThis.__TANSTACK_DEVTOOLS_SERVER__ = server + this.#server = server + return server + } + + private createWebSocketServer() { + if (this.#wssServer) { + return this.#wssServer + } + + const wss = new WebSocketServer({ noServer: true }) + this.#wssServer = wss + globalThis.__TANSTACK_DEVTOOLS_WSS_SERVER__ = wss + return wss + } + + private handleNewConnection(wss: WebSocketServer) { + wss.on('connection', (ws: WebSocket) => { + this.debugLog('New WebSocket client connected') + this.#clients.add(ws) + ws.on('close', () => { + this.debugLog('WebSocket client disconnected') + this.#clients.delete(ws) + }) + ws.on('message', (msg) => { + this.debugLog('Received message from WebSocket client', msg.toString()) + const data = JSON.parse(msg.toString()) + this.emitToServer(data) + }) + }) + } + + start() { + if (process.env.NODE_ENV !== 'development') return + if (this.#server || this.#wssServer) { + // console.warn('Server is already running') + return + } + this.debugLog('Starting server event bus') + const server = this.createSSEServer() + const wss = this.createWebSocketServer() + + this.#eventTarget.addEventListener( + 'tanstack-dispatch-event', + this.#dispatcher, + ) + this.handleNewConnection(wss) + + // Handle connection upgrade for WebSocket + server.on('upgrade', (req, socket, head) => { + if (req.url === '/__devtools/ws') { + wss.handleUpgrade(req, socket, head, (ws) => { + this.debugLog('WebSocket connection established') + wss.emit('connection', ws, req) + }) + } + }) + + server.listen(this.#port, () => { + this.debugLog(`Listening on http://localhost:${this.#port}`) + }) + } + + stop() { + this.#server?.close(() => { + this.debugLog('Server stopped') + }) + this.#wssServer?.close(() => { + this.debugLog('WebSocket server stopped') + }) + this.debugLog('Clearing all connections') + this.#clients.clear() + this.#sseClients.forEach((res) => res.end()) + this.#sseClients.clear() + this.debugLog('Cleared all WS/SSE connections') + this.#server = null + this.#wssServer = null + this.#eventTarget.removeEventListener( + 'tanstack-dispatch-event', + this.#dispatcher, + ) + this.debugLog('[tanstack-devtools] All connections cleared') + } +} diff --git a/packages/event-bus/tests/index.test.ts b/packages/event-bus/tests/index.test.ts new file mode 100644 index 00000000..305c4ead --- /dev/null +++ b/packages/event-bus/tests/index.test.ts @@ -0,0 +1,88 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { ClientEventBus } from '../src/client' + +describe('ClientEventBus', () => { + describe('debug', () => { + afterEach(() => { + vi.restoreAllMocks() + }) + it('should log events to the console when debug set to true', () => { + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) + const clientBus = new ClientEventBus({ debug: true }) + clientBus.start() + + expect(logSpy).toHaveBeenCalledWith( + '🌴 [tanstack-devtools:client-bus]', + 'Initializing client event bus', + ) + logSpy.mockRestore() + clientBus.stop() + }) + + it('should not log events to the console when debug set to false', () => { + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) + const clientBus = new ClientEventBus({ debug: false }) + clientBus.start() + + expect(logSpy).not.toHaveBeenCalled() + logSpy.mockRestore() + clientBus.stop() + }) + }) + it('should emit events to a subscribed listener', () => { + const clientBus = new ClientEventBus() + clientBus.start() + const handler = vi.fn() + window.addEventListener('test:event', handler) + + window.dispatchEvent( + new CustomEvent('tanstack-dispatch-event', { + detail: { + type: 'test:event', + payload: { foo: 'bar' }, + }, + }), + ) + expect(handler).toHaveBeenCalled() + clientBus.stop() + }) + + it('should emit events to listeners that are subscribed', () => { + const clientBus = new ClientEventBus() + clientBus.start() + const handler = vi.fn() + window.addEventListener('test:event', handler) + const secondHandler = vi.fn() + window.addEventListener('test:event', secondHandler) + + window.dispatchEvent( + new CustomEvent('tanstack-dispatch-event', { + detail: { + type: 'test:event', + payload: { foo: 'bar' }, + }, + }), + ) + expect(handler).toHaveBeenCalled() + expect(secondHandler).toHaveBeenCalled() + clientBus.stop() + }) + + it('should emit events to global listeners when they are subscribed', () => { + const clientBus = new ClientEventBus() + clientBus.start() + const handler = vi.fn() + window.addEventListener('tanstack-devtools-global', handler) + + window.dispatchEvent( + new CustomEvent('tanstack-dispatch-event', { + detail: { + type: 'test:event', + payload: { foo: 'bar' }, + }, + }), + ) + expect(handler).toHaveBeenCalled() + clientBus.stop() + }) +}) diff --git a/packages/event-bus/tests/test-setup.ts b/packages/event-bus/tests/test-setup.ts new file mode 100644 index 00000000..a9d0dd31 --- /dev/null +++ b/packages/event-bus/tests/test-setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom/vitest' diff --git a/packages/event-bus/tsconfig.docs.json b/packages/event-bus/tsconfig.docs.json new file mode 100644 index 00000000..2880b4df --- /dev/null +++ b/packages/event-bus/tsconfig.docs.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["tests", "src"] +} diff --git a/packages/event-bus/tsconfig.json b/packages/event-bus/tsconfig.json new file mode 100644 index 00000000..1838eb23 --- /dev/null +++ b/packages/event-bus/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src", + "eslint.config.js", + "vite.config.ts", + "tests", + "src/server" + ] +} diff --git a/packages/event-bus/vite.config.ts b/packages/event-bus/vite.config.ts new file mode 100644 index 00000000..0060a798 --- /dev/null +++ b/packages/event-bus/vite.config.ts @@ -0,0 +1,23 @@ +import { defineConfig, mergeConfig } from 'vitest/config' +import { tanstackViteConfig } from '@tanstack/config/vite' +import packageJson from './package.json' + +const config = defineConfig({ + plugins: [], + test: { + name: packageJson.name, + dir: './', + watch: false, + environment: 'jsdom', + setupFiles: ['./tests/test-setup.ts'], + globals: true, + }, +}) + +export default mergeConfig( + config, + tanstackViteConfig({ + entry: ['./src/client/index.ts', './src/server/index.ts', './src/index.ts'], + srcDir: './src', + }), +) diff --git a/packages/react-devtools/src/devtools.tsx b/packages/react-devtools/src/devtools.tsx index 1f5753cd..c988c289 100644 --- a/packages/react-devtools/src/devtools.tsx +++ b/packages/react-devtools/src/devtools.tsx @@ -7,6 +7,7 @@ import { import { createPortal } from 'react-dom' import type { JSX } from 'react' import type { + ClientEventBusConfig, TanStackDevtoolsConfig, TanStackDevtoolsPlugin, } from '@tanstack/devtools' @@ -83,6 +84,10 @@ export interface TanStackDevtoolsReactInit { * the settings are persisted in local storage and changed through the settings panel. */ config?: Partial + /** + * Configuration for the TanStack Devtools client event bus. + */ + eventBusConfig?: ClientEventBusConfig } const convertRender = ( @@ -95,6 +100,7 @@ const convertRender = ( export const TanstackDevtools = ({ plugins, config, + eventBusConfig, }: TanStackDevtoolsReactInit) => { const devToolRef = useRef(null) const [pluginContainer, setPluginContainer] = useState( @@ -109,6 +115,7 @@ export const TanstackDevtools = ({ () => new TanStackDevtoolsCore({ config, + eventBusConfig, plugins: plugins?.map((plugin) => { return { ...plugin, @@ -141,9 +148,7 @@ export const TanstackDevtools = ({ devtools.mount(devToolRef.current) } - return () => { - devtools.unmount() - } + return () => devtools.unmount() }, [devtools]) return ( diff --git a/packages/solid-devtools/src/devtools.tsx b/packages/solid-devtools/src/devtools.tsx index f4d4157e..795a6943 100644 --- a/packages/solid-devtools/src/devtools.tsx +++ b/packages/solid-devtools/src/devtools.tsx @@ -3,6 +3,7 @@ import { createEffect, createSignal, onCleanup, onMount } from 'solid-js' import { Portal } from 'solid-js/web' import type { JSX } from 'solid-js' import type { + ClientEventBusConfig, TanStackDevtoolsConfig, TanStackDevtoolsPlugin, } from '@tanstack/devtools' @@ -85,13 +86,22 @@ interface TanstackDevtoolsInit { * initial state of the devtools when it is started for the first time. Afterwards, * the settings are persisted in local storage and changed through the settings panel. */ - config?: TanStackDevtoolsConfig + config?: Partial + /** + * Configuration for the TanStack Devtools client event bus. + */ + eventBusConfig?: ClientEventBusConfig } -export const TanstackDevtools = ({ config, plugins }: TanstackDevtoolsInit) => { +export const TanstackDevtools = ({ + config, + plugins, + eventBusConfig, +}: TanstackDevtoolsInit) => { const [devtools] = createSignal( new TanStackDevtoolsCore({ config, + eventBusConfig, plugins: plugins?.map((plugin) => ({ ...plugin, name: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 82b0050d..62289d57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ importers: examples/react/basic: dependencies: + '@tanstack/devtools-event-client': + specifier: workspace:^ + version: link:../../../packages/event-bus-client '@tanstack/react-devtools': specifier: ^0.1.1 version: link:../../../packages/react-devtools @@ -88,13 +91,16 @@ importers: version: 1.130.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tanstack/react-router-devtools': specifier: ^1.130.2 - version: 1.130.2(@tanstack/react-router@1.130.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.130.9)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.7)(tiny-invariant@1.3.3) + version: 1.130.2(@tanstack/react-router@1.130.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.130.12)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.7)(tiny-invariant@1.3.3) react: specifier: ^19.1.0 version: 19.1.0 react-dom: specifier: ^19.1.0 version: 19.1.0(react@19.1.0) + zod: + specifier: ^4.0.14 + version: 4.0.14 devDependencies: '@types/react': specifier: ^19.1.2 @@ -151,6 +157,12 @@ importers: '@tailwindcss/vite': specifier: ^4.0.6 version: 4.1.11(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@tanstack/devtools-event-bus': + specifier: workspace:^ + version: link:../../../packages/event-bus + '@tanstack/devtools-event-client': + specifier: workspace:^ + version: link:../../../packages/event-bus-client '@tanstack/react-devtools': specifier: ^0.1.1 version: link:../../../packages/react-devtools @@ -165,13 +177,13 @@ importers: version: 1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tanstack/react-router-devtools': specifier: ^1.130.2 - version: 1.130.2(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.130.9)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.7)(tiny-invariant@1.3.3) + version: 1.130.2(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.130.12)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.7)(tiny-invariant@1.3.3) '@tanstack/react-router-with-query': specifier: ^1.130.2 - version: 1.130.9(@tanstack/react-query@5.83.0(react@19.1.0))(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.130.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.130.9(@tanstack/react-query@5.83.0(react@19.1.0))(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.130.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tanstack/react-start': - specifier: ^1.130.2 - version: 1.130.9(@netlify/blobs@9.1.2)(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@vitejs/plugin-react@4.7.0(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + specifier: ^1.130.15 + version: 1.130.15(@netlify/blobs@9.1.2)(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@vitejs/plugin-react@4.7.0(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) '@tanstack/router-plugin': specifier: ^1.121.2 version: 1.130.9(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) @@ -190,7 +202,13 @@ importers: vite-tsconfig-paths: specifier: ^5.1.4 version: 5.1.4(typescript@5.8.3)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + zod: + specifier: ^4.0.14 + version: 4.0.14 devDependencies: + '@tanstack/devtools': + specifier: workspace:^ + version: link:../../../packages/devtools '@testing-library/dom': specifier: ^10.4.0 version: 10.4.1 @@ -222,6 +240,8 @@ importers: specifier: ^4.2.4 version: 4.2.4 + examples/react/start/generated/prisma: {} + examples/solid/basic: dependencies: '@tanstack/solid-devtools': @@ -238,7 +258,7 @@ importers: version: 1.130.2(solid-js@1.9.7) '@tanstack/solid-router-devtools': specifier: ^1.129.8 - version: 1.130.2(@tanstack/router-core@1.130.9)(@tanstack/solid-router@1.130.2(solid-js@1.9.7))(csstype@3.1.3)(solid-js@1.9.7)(tiny-invariant@1.3.3) + version: 1.130.2(@tanstack/router-core@1.130.12)(@tanstack/solid-router@1.130.2(solid-js@1.9.7))(csstype@3.1.3)(solid-js@1.9.7)(tiny-invariant@1.3.3) solid-js: specifier: ^1.9.7 version: 1.9.7 @@ -255,6 +275,9 @@ importers: '@solid-primitives/keyboard': specifier: ^1.2.8 version: 1.2.8(solid-js@1.9.7) + '@tanstack/devtools-event-bus': + specifier: workspace:* + version: link:../event-bus clsx: specifier: ^2.1.1 version: 2.1.1 @@ -269,6 +292,25 @@ importers: specifier: ^2.11.6 version: 2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + packages/event-bus: + dependencies: + ws: + specifier: ^8.18.3 + version: 8.18.3 + devDependencies: + '@types/node': + specifier: ^22.15.2 + version: 22.15.2 + '@types/ws': + specifier: ^8.18.1 + version: 8.18.1 + + packages/event-bus-client: + devDependencies: + '@tanstack/devtools-event-bus': + specifier: workspace:* + version: link:../event-bus + packages/react-devtools: dependencies: '@tanstack/devtools': @@ -2172,8 +2214,8 @@ packages: peerDependencies: solid-js: '>=1.9.7' - '@tanstack/directive-functions-plugin@1.129.7': - resolution: {integrity: sha512-2VvlVmDvwHOnDAXQQa+gnhDnWPW59JcqePFf1ujOG0QGv+pw1G+JzHpiLZs4Dwr4myMxMGzFp5AWtvF96rpE7Q==} + '@tanstack/directive-functions-plugin@1.130.12': + resolution: {integrity: sha512-Q5O6D0iPFZGfpj7WYoiXoYRnpfePO61SveAJQL3EWMATGGBueD90JkbCDRXP+Ib4fcWjlmj10tXRUXDMeEapxA==} engines: {node: '>=12'} peerDependencies: vite: '>=6.0.0' @@ -2186,6 +2228,10 @@ packages: resolution: {integrity: sha512-I3YTkbe4RZQN54Qw4+IUhOjqG2DdbG2+EBWuQfew4MEk0eddLYAQVa50BZVww4/D2eh5I9vEk2Fd1Y0Wty7pug==} engines: {node: '>=12'} + '@tanstack/history@1.130.12': + resolution: {integrity: sha512-2VO1nNFDWojgZ7Uqv/OJfH6LphZQ1kE6l8sI3YBgSPtj3qN6I/rsoTHW9rGjwiDO8sQoDRXod2hpH6HMs5NDsw==} + engines: {node: '>=12'} + '@tanstack/publish-config@0.2.0': resolution: {integrity: sha512-RC0yRBFJvGuR58tKQUIkMXVEiATXgESIc+3/NTqoCC7D2YOF4fZGmHGYIanFEPQH7EGfQ5+Bwi+H6BOtKnymtw==} engines: {node: '>=18'} @@ -2235,6 +2281,13 @@ packages: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' + '@tanstack/react-router@1.130.12': + resolution: {integrity: sha512-7BYgOpGc1vK8MH1LIFLLBudGpH46GQy+hewnP7dNQJ4KHmkwPHv958L1IMA9jU/rs5g1ZH5n1f33BAMOBXUMYQ==} + engines: {node: '>=12'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + '@tanstack/react-router@1.130.2': resolution: {integrity: sha512-gDWChae5jszlBs/IYSZ46QS85iyfDrfukalV5hU2tU52Q7a3IAtr7SPSIVkClZsU4JT4GwZ35NcGHzDQ/8NQzA==} engines: {node: '>=12'} @@ -2249,29 +2302,29 @@ packages: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start-client@1.130.9': - resolution: {integrity: sha512-iGz4vvIaRHM0CGuxC7TvdPmB4HVYyOJg06LdYaJr7QiXjANiMjkQhm2SUEe5mPn6QVLv7CX8zX3ErRoAhMBwzA==} + '@tanstack/react-start-client@1.130.12': + resolution: {integrity: sha512-ATV8zccbwkSC7OP7t59yfxeOVcNjIViUThDKVQ2r8YQGUSIqbi05EYNeG1D89Jb4sLdSeXWoPnxGQS9/2veBDA==} engines: {node: '>=12'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start-plugin@1.130.9': - resolution: {integrity: sha512-ideuWfOazh4xLw9Ld6Qt1DS+W+Q+t0pKWRu/QKuAZgpH+z5QG8PwpwbzTKKIWk79fJT0yyxA8bpKydBuJdN0Ag==} + '@tanstack/react-start-plugin@1.130.15': + resolution: {integrity: sha512-kZ1WBWNLtgl0al9Ahbq/zfEuKc+CezCGNqWb7B8byIjthnRcdrXBaah35zG65FMEzAk0e+F1bpS+5Q2RNxvthQ==} engines: {node: '>=12'} peerDependencies: '@vitejs/plugin-react': '>=4.3.4' vite: '>=6.0.0' - '@tanstack/react-start-server@1.130.9': - resolution: {integrity: sha512-6Ka/5pP/sqdki81YgalgCq8h5Xz5NLJZ2gvAPrGZI8/Uc4vtD6SY6SuZbu/Q5ZgYlp4xWhEiKlkZe6KkuPwCPA==} + '@tanstack/react-start-server@1.130.12': + resolution: {integrity: sha512-5gW1V6fqPwFiG+J7W7k7DmB75wjP6zoy6hQcoxlKurMaFm3VTtck9d6mrfOz/47TUB+xb95aSf56Yqq/tuAO7g==} engines: {node: '>=12'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start@1.130.9': - resolution: {integrity: sha512-4mHdI7zBtBRU9+31E4fVSH7hT3Cz08/yW1NdEMp8PllSKJZLG/630StPqeceCTePiFqpvB6NEcS5iFoixA8XJg==} + '@tanstack/react-start@1.130.15': + resolution: {integrity: sha512-mb2mcA0CTYzLs8rKf+wiUT2vSFh85nohQJQCS5pRG5JgIPEWxuIODcx+e2Fn2jiORb4khQNm5vm/qMqmAAkarw==} engines: {node: '>=12'} peerDependencies: '@vitejs/plugin-react': '>=4.3.4' @@ -2285,6 +2338,10 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@tanstack/router-core@1.130.12': + resolution: {integrity: sha512-emq3cRU9Na1hnIToojzkfJcOZm/MG2bv9M+Kr/elUxEf83enGEwQXC1EKezTuwNgeJrOv8vPJdEhWM7IQodnHQ==} + engines: {node: '>=12'} + '@tanstack/router-core@1.130.2': resolution: {integrity: sha512-d5hYEEAvNUImpoomTlP2tRelX4JiNx3g2uk6xAO/aPKuMYdfntBSV7xbKuWZEhSwqeN2Z4qD3YyQEXBa4Fu7Mg==} engines: {node: '>=12'} @@ -2305,10 +2362,35 @@ packages: csstype: optional: true + '@tanstack/router-generator@1.130.15': + resolution: {integrity: sha512-2TICfuSN8oYydTHd+nATkKV4B37XRrAWrwK9+g5dPVUP9lhLy7FQy3IVcb1HRXFXvW0zr5zPNxXErTrOmrceyA==} + engines: {node: '>=12'} + '@tanstack/router-generator@1.130.9': resolution: {integrity: sha512-DsQ7kLQmMk0L3K3/QtuMveILQWAarzfv6DpLMN9+fHlIFuw7OQinVrzD9i+9oAKcYzsBgM1iez5IMXYpCta1gA==} engines: {node: '>=12'} + '@tanstack/router-plugin@1.130.15': + resolution: {integrity: sha512-ovYGN0a5CxIPkVdbJPLAqwlE0eUYhHm0PkPCH0TxR24XpEGaCxAOw92DriLRZj9R4xTg5oeJqM+3wiZJfujx/A==} + engines: {node: '>=12'} + peerDependencies: + '@rsbuild/core': '>=1.0.2' + '@tanstack/react-router': ^1.130.12 + vite: '>=5.0.0 || >=6.0.0' + vite-plugin-solid: ^2.11.2 + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: + optional: true + '@tanstack/router-plugin@1.130.9': resolution: {integrity: sha512-/QmDbj+z/UysHeMi9F4sJqh76clSX4QFjIdOgO7u8nXkgjSBfPcyWos+1rw9ovn+DeDmyKlvzt3BEIU1+bvptA==} engines: {node: '>=12'} @@ -2334,8 +2416,12 @@ packages: resolution: {integrity: sha512-I2OyQF5U6sxHJApXKCUmCncTHKcpj4681FwyxpYg5QYOatHcn/zVMl7Rj4h36fu8/Lo2ZRLxUMd5kmXgp5Pb/A==} engines: {node: '>=12'} - '@tanstack/server-functions-plugin@1.129.7': - resolution: {integrity: sha512-s0foXwzTAn7FOVeZCNM7BuJr5Y/hUKt2yizsTjlG2i9kWRI8K7g0GbJPNgP86iIVGAEsKmOoJH00HUwLTVNajg==} + '@tanstack/router-utils@1.130.12': + resolution: {integrity: sha512-vyk7qduNrVrJWgUXHqRyZrFLOL9YJ/4ycN5PbJ2cLRBln01NkG/abKTHi32V31yMehxkxAO0EoicicvalnV0FA==} + engines: {node: '>=12'} + + '@tanstack/server-functions-plugin@1.130.12': + resolution: {integrity: sha512-aiYxrC6A8jPojAhHfH10YiwfI626yTBlyzIvdPzVvv3DWkRA7yShSOc6FDuwX+oSPmScJSIX/OAHTzJnStY5GA==} engines: {node: '>=12'} '@tanstack/solid-query-devtools@5.83.0': @@ -2367,34 +2453,34 @@ packages: peerDependencies: solid-js: ^1.6.0 - '@tanstack/start-client-core@1.130.9': - resolution: {integrity: sha512-/Rot3+3eOaTOM5lyosNByAk0j+zVmZ5SE2F37dV6dk1OiurObHslkFIT2UquQ+sI+ZzzKw8h6leSoOXPhRM2gg==} + '@tanstack/start-client-core@1.130.12': + resolution: {integrity: sha512-UedcsrVeObBlmTJ180oC2llOQY9WhkTjylEV9w7hgQ5QHmbu1i6Jw3WPjIrT1KjSxGRG+t7jmafs2AyICpIpQg==} engines: {node: '>=12'} - '@tanstack/start-plugin-core@1.130.9': - resolution: {integrity: sha512-h2vFCTneLVWos7xqAgVTXfPPrbsTR3MyZ6431zcIn4AbbfE7G8mIJKYBWvV2iIW+cY4CIwW/+7oz0Q6YxP1k8Q==} + '@tanstack/start-plugin-core@1.130.15': + resolution: {integrity: sha512-zhHLoSIRuM8NmsZP12RIRSUFlF3yZraWBfcg/6XDfwJVYmYGyWZSWhCjzXQe2XerXhTvpTscDJcBIWzZZRC85g==} engines: {node: '>=12'} peerDependencies: vite: '>=6.0.0' - '@tanstack/start-server-core@1.130.9': - resolution: {integrity: sha512-Bgau6dGRrmKCjSlNwuyrxsZGBu+R/sMQ303tgJqngXXG9p6mzCVa9yb0GoZqGvMfE5MvzS70AauXVV7hgeRePQ==} + '@tanstack/start-server-core@1.130.12': + resolution: {integrity: sha512-xDuuv4nRECfEnXkA3W3yRw+ci4XUjway2CWopGFDDGyTJn/Ff1OSyLvZxyGGrBLPeVk8+vzLoO3y39UUO8HWaQ==} engines: {node: '>=12'} - '@tanstack/start-server-functions-client@1.130.9': - resolution: {integrity: sha512-0tZmahdSLJcLlARJS8BVjzVrA7jYjkZLgwzeQ+TuuupPi84TmD+ZWQF9xUgHdWV17ZBm7nmKMzrXXeL981M1jQ==} + '@tanstack/start-server-functions-client@1.130.12': + resolution: {integrity: sha512-sXPVGilBGZlx8Kxc6NHpE3kRSk7mArvjgETQbzHmxJAv0ut7FeXNSWVd5LyXGHWdQ83cP1AKhAz129Una6U/gw==} engines: {node: '>=12'} - '@tanstack/start-server-functions-fetcher@1.130.9': - resolution: {integrity: sha512-MH95sd65uTbVVe3JfwoCo/UX5n989Mry6UQxNqsiXwtmTmpxya/M3JRdHKYcMidZbLW6VB472W3y2aqdbr0dJw==} + '@tanstack/start-server-functions-fetcher@1.130.12': + resolution: {integrity: sha512-tzYrcsKmFSLGuzrWwlguJSl8fzuLVxAl+OJbfBcJ/pVpkvi/aJ9toLV8h/M10wHhlZQWyu69lb0MAak3eCE+dA==} engines: {node: '>=12'} - '@tanstack/start-server-functions-server@1.129.7': - resolution: {integrity: sha512-iHECAPMycUv1S8gc47ldm4oFYqAzhfgwt7hnwOAx3W+y+qwTn9WP/+S2TWXyo+2QvsJX4+b1NaZSRyQQsd9Ypg==} + '@tanstack/start-server-functions-server@1.130.12': + resolution: {integrity: sha512-VXiXpfkRcQmrVa45eTztJvLRx0N9E5ggaZxyRQcYU3FTnTADeC+MC7q0eiB0t46if4N/39XwSYCiyu6c1UL1Lw==} engines: {node: '>=12'} - '@tanstack/start-storage-context@1.130.9': - resolution: {integrity: sha512-aTenkZfNe9CJ/kJfXIK9+3JqnG43SRu8Y8hcGHGghr60vCUiKX/RIfoxKg4OEIvq3fr7s1amlJoEcIDmawsE9w==} + '@tanstack/start-storage-context@1.130.12': + resolution: {integrity: sha512-qs2vos0jO1SFjgi9yHQOqjwuYPEu4VX22r/8JDE1fIfSDuRIZkYKHtxKEooXrZtZhqsOrzf0tulpGCGcIImCcQ==} engines: {node: '>=12'} '@tanstack/store@0.7.0': @@ -2506,6 +2592,9 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} @@ -6498,6 +6587,9 @@ packages: zod@3.24.3: resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==} + zod@4.0.14: + resolution: {integrity: sha512-nGFJTnJN6cM2v9kXL+SOBq3AtjQby3Mv5ySGFof5UGRHrRioSJ5iG680cYNjE/yWk671nROcpPj4hAS8nyLhSw==} + snapshots: '@adobe/css-tools@4.4.2': {} @@ -7521,8 +7613,8 @@ snapshots: '@jridgewell/source-map@0.3.10': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 '@jridgewell/sourcemap-codec@1.5.0': {} @@ -7671,7 +7763,7 @@ snapshots: '@netlify/zip-it-and-ship-it@12.2.1(rollup@4.46.2)': dependencies: - '@babel/parser': 7.27.0 + '@babel/parser': 7.28.0 '@babel/types': 7.28.0 '@netlify/binary-info': 1.0.0 '@netlify/serverless-functions-api': 2.1.3 @@ -7909,7 +8001,7 @@ snapshots: '@rollup/pluginutils': 5.1.4(rollup@4.46.2) commondir: 1.0.1 estree-walker: 2.0.2 - fdir: 6.4.4(picomatch@4.0.3) + fdir: 6.4.6(picomatch@4.0.3) is-reference: 1.2.1 magic-string: 0.30.17 picomatch: 4.0.3 @@ -8394,13 +8486,13 @@ snapshots: - csstype - utf-8-validate - '@tanstack/directive-functions-plugin@1.129.7(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + '@tanstack/directive-functions-plugin@1.130.12(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.28.0 '@babel/traverse': 7.28.0 '@babel/types': 7.28.2 - '@tanstack/router-utils': 1.129.7 + '@tanstack/router-utils': 1.130.12 babel-dead-code-elimination: 1.0.10 tiny-invariant: 1.3.3 vite: 7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) @@ -8428,6 +8520,8 @@ snapshots: '@tanstack/history@1.129.7': {} + '@tanstack/history@1.130.12': {} + '@tanstack/publish-config@0.2.0': dependencies: '@commitlint/parse': 19.8.1 @@ -8465,10 +8559,10 @@ snapshots: '@tanstack/query-core': 5.83.0 react: 19.1.0 - '@tanstack/react-router-devtools@1.130.2(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.130.9)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.7)(tiny-invariant@1.3.3)': + '@tanstack/react-router-devtools@1.130.2(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.130.12)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.7)(tiny-invariant@1.3.3)': dependencies: '@tanstack/react-router': 1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@tanstack/router-devtools-core': 1.130.2(@tanstack/router-core@1.130.9)(csstype@3.1.3)(solid-js@1.9.7)(tiny-invariant@1.3.3) + '@tanstack/router-devtools-core': 1.130.2(@tanstack/router-core@1.130.12)(csstype@3.1.3)(solid-js@1.9.7)(tiny-invariant@1.3.3) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -8477,10 +8571,10 @@ snapshots: - solid-js - tiny-invariant - '@tanstack/react-router-devtools@1.130.2(@tanstack/react-router@1.130.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.130.9)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.7)(tiny-invariant@1.3.3)': + '@tanstack/react-router-devtools@1.130.2(@tanstack/react-router@1.130.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.130.12)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.7)(tiny-invariant@1.3.3)': dependencies: '@tanstack/react-router': 1.130.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@tanstack/router-devtools-core': 1.130.2(@tanstack/router-core@1.130.9)(csstype@3.1.3)(solid-js@1.9.7)(tiny-invariant@1.3.3) + '@tanstack/router-devtools-core': 1.130.2(@tanstack/router-core@1.130.12)(csstype@3.1.3)(solid-js@1.9.7)(tiny-invariant@1.3.3) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -8489,13 +8583,24 @@ snapshots: - solid-js - tiny-invariant - '@tanstack/react-router-with-query@1.130.9(@tanstack/react-query@5.83.0(react@19.1.0))(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.130.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@tanstack/react-router-with-query@1.130.9(@tanstack/react-query@5.83.0(react@19.1.0))(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.130.12)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@tanstack/react-query': 5.83.0(react@19.1.0) '@tanstack/react-router': 1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@tanstack/router-core': 1.130.9 + '@tanstack/router-core': 1.130.12 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + '@tanstack/react-router@1.130.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@tanstack/history': 1.130.12 + '@tanstack/react-store': 0.7.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tanstack/router-core': 1.130.12 + isbot: 5.1.28 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 '@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: @@ -8519,20 +8624,20 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/react-start-client@1.130.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@tanstack/react-start-client@1.130.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@tanstack/react-router': 1.130.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@tanstack/router-core': 1.130.9 - '@tanstack/start-client-core': 1.130.9 + '@tanstack/react-router': 1.130.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tanstack/router-core': 1.130.12 + '@tanstack/start-client-core': 1.130.12 cookie-es: 1.2.2 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/react-start-plugin@1.130.9(@netlify/blobs@9.1.2)(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@vitejs/plugin-react@4.7.0(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + '@tanstack/react-start-plugin@1.130.15(@netlify/blobs@9.1.2)(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@vitejs/plugin-react@4.7.0(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': dependencies: - '@tanstack/start-plugin-core': 1.130.9(@netlify/blobs@9.1.2)(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@tanstack/start-plugin-core': 1.130.15(@netlify/blobs@9.1.2)(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) '@vitejs/plugin-react': 4.7.0(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) pathe: 2.0.3 vite: 7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) @@ -8569,25 +8674,25 @@ snapshots: - webpack - xml2js - '@tanstack/react-start-server@1.130.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@tanstack/react-start-server@1.130.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@tanstack/history': 1.129.7 - '@tanstack/react-router': 1.130.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@tanstack/router-core': 1.130.9 - '@tanstack/start-client-core': 1.130.9 - '@tanstack/start-server-core': 1.130.9 + '@tanstack/history': 1.130.12 + '@tanstack/react-router': 1.130.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tanstack/router-core': 1.130.12 + '@tanstack/start-client-core': 1.130.12 + '@tanstack/start-server-core': 1.130.12 h3: 1.13.0 isbot: 5.1.28 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - '@tanstack/react-start@1.130.9(@netlify/blobs@9.1.2)(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@vitejs/plugin-react@4.7.0(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + '@tanstack/react-start@1.130.15(@netlify/blobs@9.1.2)(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@vitejs/plugin-react@4.7.0(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': dependencies: - '@tanstack/react-start-client': 1.130.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@tanstack/react-start-plugin': 1.130.9(@netlify/blobs@9.1.2)(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@vitejs/plugin-react@4.7.0(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) - '@tanstack/react-start-server': 1.130.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@tanstack/start-server-functions-client': 1.130.9(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) - '@tanstack/start-server-functions-server': 1.129.7(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@tanstack/react-start-client': 1.130.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tanstack/react-start-plugin': 1.130.15(@netlify/blobs@9.1.2)(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@vitejs/plugin-react@4.7.0(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@tanstack/react-start-server': 1.130.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tanstack/start-server-functions-client': 1.130.12(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@tanstack/start-server-functions-server': 1.130.12(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) '@vitejs/plugin-react': 4.7.0(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) @@ -8631,6 +8736,16 @@ snapshots: react-dom: 19.1.0(react@19.1.0) use-sync-external-store: 1.5.0(react@19.1.0) + '@tanstack/router-core@1.130.12': + dependencies: + '@tanstack/history': 1.130.12 + '@tanstack/store': 0.7.2 + cookie-es: 1.2.2 + seroval: 1.3.2 + seroval-plugins: 1.3.2(seroval@1.3.2) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + '@tanstack/router-core@1.130.2': dependencies: '@tanstack/history': 1.129.7 @@ -8651,9 +8766,9 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/router-devtools-core@1.130.2(@tanstack/router-core@1.130.9)(csstype@3.1.3)(solid-js@1.9.7)(tiny-invariant@1.3.3)': + '@tanstack/router-devtools-core@1.130.2(@tanstack/router-core@1.130.12)(csstype@3.1.3)(solid-js@1.9.7)(tiny-invariant@1.3.3)': dependencies: - '@tanstack/router-core': 1.130.9 + '@tanstack/router-core': 1.130.12 clsx: 2.1.1 goober: 2.1.16(csstype@3.1.3) solid-js: 1.9.7 @@ -8661,6 +8776,19 @@ snapshots: optionalDependencies: csstype: 3.1.3 + '@tanstack/router-generator@1.130.15': + dependencies: + '@tanstack/router-core': 1.130.12 + '@tanstack/router-utils': 1.130.12 + '@tanstack/virtual-file-routes': 1.129.7 + prettier: 3.5.3 + recast: 0.23.11 + source-map: 0.7.6 + tsx: 4.20.3 + zod: 3.24.3 + transitivePeerDependencies: + - supports-color + '@tanstack/router-generator@1.130.9': dependencies: '@tanstack/router-core': 1.130.9 @@ -8674,6 +8802,29 @@ snapshots: transitivePeerDependencies: - supports-color + '@tanstack/router-plugin@1.130.15(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + dependencies: + '@babel/core': 7.28.0 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.0) + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.2 + '@tanstack/router-core': 1.130.12 + '@tanstack/router-generator': 1.130.15 + '@tanstack/router-utils': 1.130.12 + '@tanstack/virtual-file-routes': 1.129.7 + babel-dead-code-elimination: 1.0.10 + chokidar: 3.6.0 + unplugin: 2.3.5 + zod: 3.24.3 + optionalDependencies: + '@tanstack/react-router': 1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + vite: 7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + vite-plugin-solid: 2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + transitivePeerDependencies: + - supports-color + '@tanstack/router-plugin@1.130.9(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': dependencies: '@babel/core': 7.28.0 @@ -8708,7 +8859,18 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/server-functions-plugin@1.129.7(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + '@tanstack/router-utils@1.130.12': + dependencies: + '@babel/core': 7.28.0 + '@babel/generator': 7.28.0 + '@babel/parser': 7.28.0 + '@babel/preset-typescript': 7.27.1(@babel/core@7.28.0) + ansis: 4.1.0 + diff: 8.0.2 + transitivePeerDependencies: + - supports-color + + '@tanstack/server-functions-plugin@1.130.12(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.28.0 @@ -8717,7 +8879,7 @@ snapshots: '@babel/template': 7.27.2 '@babel/traverse': 7.28.0 '@babel/types': 7.28.2 - '@tanstack/directive-functions-plugin': 1.129.7(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@tanstack/directive-functions-plugin': 1.130.12(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) babel-dead-code-elimination: 1.0.10 tiny-invariant: 1.3.3 transitivePeerDependencies: @@ -8735,9 +8897,9 @@ snapshots: '@tanstack/query-core': 5.83.0 solid-js: 1.9.7 - '@tanstack/solid-router-devtools@1.130.2(@tanstack/router-core@1.130.9)(@tanstack/solid-router@1.130.2(solid-js@1.9.7))(csstype@3.1.3)(solid-js@1.9.7)(tiny-invariant@1.3.3)': + '@tanstack/solid-router-devtools@1.130.2(@tanstack/router-core@1.130.12)(@tanstack/solid-router@1.130.2(solid-js@1.9.7))(csstype@3.1.3)(solid-js@1.9.7)(tiny-invariant@1.3.3)': dependencies: - '@tanstack/router-devtools-core': 1.130.2(@tanstack/router-core@1.130.9)(csstype@3.1.3)(solid-js@1.9.7)(tiny-invariant@1.3.3) + '@tanstack/router-devtools-core': 1.130.2(@tanstack/router-core@1.130.12)(csstype@3.1.3)(solid-js@1.9.7)(tiny-invariant@1.3.3) '@tanstack/solid-router': 1.130.2(solid-js@1.9.7) solid-js: 1.9.7 transitivePeerDependencies: @@ -8763,25 +8925,25 @@ snapshots: '@tanstack/store': 0.7.0 solid-js: 1.9.7 - '@tanstack/start-client-core@1.130.9': + '@tanstack/start-client-core@1.130.12': dependencies: - '@tanstack/router-core': 1.130.9 - '@tanstack/start-storage-context': 1.130.9 + '@tanstack/router-core': 1.130.12 + '@tanstack/start-storage-context': 1.130.12 cookie-es: 1.2.2 tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/start-plugin-core@1.130.9(@netlify/blobs@9.1.2)(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + '@tanstack/start-plugin-core@1.130.15(@netlify/blobs@9.1.2)(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': dependencies: '@babel/code-frame': 7.26.2 '@babel/core': 7.28.0 - '@babel/types': 7.27.0 - '@tanstack/router-core': 1.130.9 - '@tanstack/router-generator': 1.130.9 - '@tanstack/router-plugin': 1.130.9(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) - '@tanstack/router-utils': 1.129.7 - '@tanstack/server-functions-plugin': 1.129.7(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) - '@tanstack/start-server-core': 1.130.9 + '@babel/types': 7.28.2 + '@tanstack/router-core': 1.130.12 + '@tanstack/router-generator': 1.130.15 + '@tanstack/router-plugin': 1.130.15(@tanstack/react-router@1.130.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.7)(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)))(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@tanstack/router-utils': 1.130.12 + '@tanstack/server-functions-plugin': 1.130.12(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@tanstack/start-server-core': 1.130.12 '@types/babel__code-frame': 7.0.6 '@types/babel__core': 7.20.5 babel-dead-code-elimination: 1.0.10 @@ -8826,42 +8988,42 @@ snapshots: - webpack - xml2js - '@tanstack/start-server-core@1.130.9': + '@tanstack/start-server-core@1.130.12': dependencies: - '@tanstack/history': 1.129.7 - '@tanstack/router-core': 1.130.9 - '@tanstack/start-client-core': 1.130.9 - '@tanstack/start-storage-context': 1.130.9 + '@tanstack/history': 1.130.12 + '@tanstack/router-core': 1.130.12 + '@tanstack/start-client-core': 1.130.12 + '@tanstack/start-storage-context': 1.130.12 h3: 1.13.0 isbot: 5.1.28 tiny-invariant: 1.3.3 tiny-warning: 1.0.3 unctx: 2.4.1 - '@tanstack/start-server-functions-client@1.130.9(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + '@tanstack/start-server-functions-client@1.130.12(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': dependencies: - '@tanstack/server-functions-plugin': 1.129.7(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) - '@tanstack/start-server-functions-fetcher': 1.130.9 + '@tanstack/server-functions-plugin': 1.130.12(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@tanstack/start-server-functions-fetcher': 1.130.12 transitivePeerDependencies: - supports-color - vite - '@tanstack/start-server-functions-fetcher@1.130.9': + '@tanstack/start-server-functions-fetcher@1.130.12': dependencies: - '@tanstack/router-core': 1.130.9 - '@tanstack/start-client-core': 1.130.9 + '@tanstack/router-core': 1.130.12 + '@tanstack/start-client-core': 1.130.12 - '@tanstack/start-server-functions-server@1.129.7(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + '@tanstack/start-server-functions-server@1.130.12(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': dependencies: - '@tanstack/server-functions-plugin': 1.129.7(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@tanstack/server-functions-plugin': 1.130.12(vite@7.0.6(@types/node@22.15.2)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) tiny-invariant: 1.3.3 transitivePeerDependencies: - supports-color - vite - '@tanstack/start-storage-context@1.130.9': + '@tanstack/start-storage-context@1.130.12': dependencies: - '@tanstack/router-core': 1.130.9 + '@tanstack/router-core': 1.130.12 '@tanstack/store@0.7.0': {} @@ -8993,6 +9155,10 @@ snapshots: '@types/unist@3.0.3': {} + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.15.2 + '@types/yauzl@2.10.3': dependencies: '@types/node': 22.15.2 @@ -9993,7 +10159,7 @@ snapshots: detective-typescript@14.0.0(typescript@5.8.3): dependencies: - '@typescript-eslint/typescript-estree': 8.31.0(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) ast-module-types: 6.0.1 node-source-walk: 7.0.1 typescript: 5.8.3 @@ -10591,7 +10757,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.0 + debug: 4.4.1 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -10631,10 +10797,6 @@ snapshots: optionalDependencies: picomatch: 4.0.2 - fdir@6.4.4(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - fdir@6.4.6(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -10976,7 +11138,7 @@ snapshots: dependencies: '@ioredis/commands': 1.3.0 cluster-key-slot: 1.1.2 - debug: 4.4.0 + debug: 4.4.1 denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -11226,7 +11388,7 @@ snapshots: lambda-local@2.2.0: dependencies: commander: 10.0.1 - dotenv: 16.5.0 + dotenv: 16.6.1 winston: 3.17.0 lazystream@1.0.1: @@ -11303,7 +11465,7 @@ snapshots: get-port-please: 3.2.0 h3: 1.15.4 http-shutdown: 1.2.2 - jiti: 2.4.2 + jiti: 2.5.1 mlly: 1.7.4 node-forge: 1.3.1 pathe: 1.1.2 @@ -11389,8 +11551,8 @@ snapshots: magicast@0.3.5: dependencies: - '@babel/parser': 7.27.0 - '@babel/types': 7.27.0 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 source-map-js: 1.2.1 markdown-it@14.1.0: @@ -11558,7 +11720,7 @@ snapshots: hookable: 5.5.3 httpxy: 0.1.7 ioredis: 5.6.1 - jiti: 2.4.2 + jiti: 2.5.1 klona: 2.0.6 knitwork: 1.2.0 listhen: 1.9.0 @@ -11650,7 +11812,7 @@ snapshots: node-source-walk@7.0.1: dependencies: - '@babel/parser': 7.27.0 + '@babel/parser': 7.28.0 nopt@8.1.0: dependencies: @@ -11862,7 +12024,7 @@ snapshots: parse-json@8.3.0: dependencies: - '@babel/code-frame': 7.26.2 + '@babel/code-frame': 7.27.1 index-to-position: 1.1.0 type-fest: 4.41.0 @@ -12295,7 +12457,7 @@ snapshots: send@1.2.0: dependencies: - debug: 4.4.0 + debug: 4.4.1 encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -12769,7 +12931,7 @@ snapshots: unctx@2.4.1: dependencies: - acorn: 8.14.1 + acorn: 8.15.0 estree-walker: 3.0.3 magic-string: 0.30.17 unplugin: 2.3.5 @@ -12888,7 +13050,7 @@ snapshots: dependencies: citty: 0.1.6 defu: 6.1.4 - jiti: 2.4.2 + jiti: 2.5.1 knitwork: 1.2.0 scule: 1.3.0 @@ -13261,3 +13423,5 @@ snapshots: zod: 3.24.3 zod@3.24.3: {} + + zod@4.0.14: {}