diff --git a/.changeset/shared-protocol-types.md b/.changeset/shared-protocol-types.md new file mode 100644 index 00000000000..88a6f493599 --- /dev/null +++ b/.changeset/shared-protocol-types.md @@ -0,0 +1,11 @@ +--- +'@qwik.dev/devtools': patch +--- + +refactor(devtools): single source of truth for shared protocol types + +The VNode tree node, component detail entry, and render event shapes were declared +three times: in the browser extension, in the devtools UI, and in the kit client +bridge. They now live once in @qwik.dev/devtools/kit (protocol module) as +DevtoolsVNodeTreeNode, DevtoolsComponentDetailEntry, and DevtoolsRenderEvent, and +every consumer imports them from there. diff --git a/packages/browser-extension/src/shared/types.ts b/packages/browser-extension/src/shared/types.ts index a5a6a8a4e1a..4611effe080 100644 --- a/packages/browser-extension/src/shared/types.ts +++ b/packages/browser-extension/src/shared/types.ts @@ -1,10 +1,17 @@ import type { + DevtoolsVNodeTreeNode as VNodeTreeNode, + DevtoolsComponentDetailEntry as ComponentDetailEntry, + DevtoolsRenderEvent as RenderEvent, QwikDevtoolsComponentSnapshot, QwikDevtoolsSignalsSnapshot, QwikPerfStoreRemembered, QwikPreloadStoreRemembered, } from '@qwik.dev/devtools/kit'; +// The VNode/detail/render shapes are the shared data contract, owned by @qwik.dev/devtools/kit. +// Re-export under the local names so consumers keep one import source and cannot drift. +export type { VNodeTreeNode, ComponentDetailEntry, RenderEvent }; + export interface QwikContainerInfo { detected: boolean; version: string | null; @@ -16,27 +23,6 @@ export interface QwikContainerInfo { runtime: string | null; } -export interface VNodeTreeNode { - name?: string; - id: string; - label?: string; - props?: Record; - children?: VNodeTreeNode[]; -} - -export interface ComponentDetailEntry { - hookType: string; - variableName: string; - data: unknown; -} - -export interface RenderEvent { - component: string; - phase: string; - duration: number; - timestamp: number; -} - export type ExtensionMessage = | { type: 'DETECT_QWIK' } | { type: 'QWIK_DETECTION_RESULT'; payload: QwikContainerInfo } diff --git a/packages/devtools/kit/src/client-bridge.ts b/packages/devtools/kit/src/client-bridge.ts index f03295d32ac..486338479af 100644 --- a/packages/devtools/kit/src/client-bridge.ts +++ b/packages/devtools/kit/src/client-bridge.ts @@ -7,32 +7,14 @@ import type { QwikPerfStoreRemembered, QwikPreloadStoreRemembered } from './glob import { getQwikDevtoolsGlobal } from './global-store'; import { QWIK_DEVTOOLS_GLOBAL } from './protocol/globals'; import { DEVTOOLS_MESSAGES, type QwikDevtoolsPageMessage } from './protocol/messages'; +import type { DevtoolsVNodeTreeNode } from './protocol/vnode'; +import type { DevtoolsRenderEvent } from './protocol/perf'; +import type { DevtoolsComponentDetailEntry } from './protocol/hooks'; export interface InPageBridgeOptions { isBrowser: boolean; } -export interface DevtoolsRenderEvent { - component: string; - phase: string; - duration: number; - timestamp: number; -} - -export interface DevtoolsComponentDetailEntry { - hookType: string; - variableName: string; - data: unknown; -} - -export interface DevtoolsVNodeTreeNode { - name?: string; - id: string; - label?: string; - props?: Record; - children?: DevtoolsVNodeTreeNode[]; -} - export interface InPageBridge { readPerfData(): Promise; readPreloadStore(): Promise; diff --git a/packages/devtools/kit/src/protocol/hooks.ts b/packages/devtools/kit/src/protocol/hooks.ts index bb9d4738c91..5efeae9ceb2 100644 --- a/packages/devtools/kit/src/protocol/hooks.ts +++ b/packages/devtools/kit/src/protocol/hooks.ts @@ -45,6 +45,17 @@ export const SIGNAL_HOOK_TYPES = [ 'useContext', ] as const; +/** + * A single component hook entry with deeply serialized data, returned by the devtools hook's + * component detail lookup. Shared data contract: do not redeclare in consumers, import it from + * here. + */ +export interface DevtoolsComponentDetailEntry { + hookType: string; + variableName: string; + data: unknown; +} + export const INNER_USE_HOOK = 'useCollectHooks'; export const VIRTUAL_QWIK_DEVTOOLS_KEY = 'virtual-qwik-devtools.ts'; export const QWIK_DEVTOOLS_HOOK_VERSION = 1; diff --git a/packages/devtools/kit/src/protocol/perf.ts b/packages/devtools/kit/src/protocol/perf.ts index 8161539bfb2..9e6cb36e23e 100644 --- a/packages/devtools/kit/src/protocol/perf.ts +++ b/packages/devtools/kit/src/protocol/perf.ts @@ -6,3 +6,14 @@ export const PERF_PHASE_CSR = 'csr'; export const DEFAULT_PERF_STORE_EXPRESSION = '{ ssr: [], csr: [] }'; export type QwikDevtoolsPerfPhase = typeof PERF_PHASE_SSR | typeof PERF_PHASE_CSR; + +/** + * A single render event emitted by the performance runtime (CSR render with timing). Shared data + * contract: do not redeclare in consumers, import it from here. + */ +export interface DevtoolsRenderEvent { + component: string; + phase: string; + duration: number; + timestamp: number; +} diff --git a/packages/devtools/kit/src/protocol/vnode.ts b/packages/devtools/kit/src/protocol/vnode.ts index 08d709abe71..5943c24f957 100644 --- a/packages/devtools/kit/src/protocol/vnode.ts +++ b/packages/devtools/kit/src/protocol/vnode.ts @@ -18,3 +18,15 @@ export const QWIK_VNODE_PROTOCOL = { }, bridgeVirtualModuleId: 'virtual:qwik-devtools-bridge', } as const; + +/** + * Serializable VNode tree node exchanged between the page hook and the devtools UI/extension. + * Shared data contract: do not redeclare in consumers, import it from here. + */ +export interface DevtoolsVNodeTreeNode { + name?: string; + id: string; + label?: string; + props?: Record; + children?: DevtoolsVNodeTreeNode[]; +} diff --git a/packages/devtools/ui/src/devtools/page-data-source.ts b/packages/devtools/ui/src/devtools/page-data-source.ts index d599bb13d7b..aeb013cea88 100644 --- a/packages/devtools/ui/src/devtools/page-data-source.ts +++ b/packages/devtools/ui/src/devtools/page-data-source.ts @@ -6,9 +6,16 @@ import { type QwikPreloadStoreRemembered, type QwikDevtoolsComponentSnapshot, type QwikDevtoolsSignalsSnapshot, + type DevtoolsVNodeTreeNode as VNodeTreeNode, + type DevtoolsComponentDetailEntry as ComponentDetailEntry, + type DevtoolsRenderEvent as RenderEvent, } from '@qwik.dev/devtools/kit'; import { isBrowser } from '@qwik.dev/core'; +// The VNode/detail/render shapes are the shared data contract, owned by @qwik.dev/devtools/kit. +// Re-export under the local names so feature components keep one import source and cannot drift. +export type { VNodeTreeNode, ComponentDetailEntry, RenderEvent }; + /** * Abstraction for accessing page-level data from different contexts. * @@ -78,30 +85,6 @@ export interface PageDataSource { subscribeRenderEvents(cb: (event: RenderEvent) => void): (() => void) | null; } -/** A single render event emitted by the performance runtime. */ -export interface RenderEvent { - component: string; - phase: string; - duration: number; - timestamp: number; -} - -/** A single hook entry with deeply serialized data. */ -export interface ComponentDetailEntry { - hookType: string; - variableName: string; - data: unknown; -} - -/** Serializable VNode tree node (matches overlay's TreeNode shape). */ -export interface VNodeTreeNode { - name?: string; - id: string; - label?: string; - props?: Record; - children?: VNodeTreeNode[]; -} - /** * Default implementation that reads directly from `window` globals. Used when the devtools UI runs * as an in-app overlay (same document).