import type { LifetimeNode } from '../types.js'; import type { EachDiff } from '../tracking/each-diff.js'; import type { DisposerEvent } from '../tracking/disposer-log.js'; import type { CoverageSnapshot } from '../tracking/coverage.js'; import type { PendingEffect, EffectTimelineEntry, EffectMatch } from '../tracking/effect-timeline.js'; export interface SignalMessageRecord { index: number; timestamp: number; msg: unknown; stateBefore: unknown; stateAfter: unknown; effects: unknown[]; } export interface StateDiff { added: Record; removed: Record; changed: Record; } export interface MessageRecord { index: number; timestamp: number; msg: unknown; stateBefore: unknown; stateAfter: unknown; effects: unknown[]; /** Present only on the legacy runtime, which computes a dirty mask per update. */ dirtyMask?: number; } export interface BindingDebugInfo { index: number; mask: number; lastValue: unknown; kind: string; key: string | undefined; dead: boolean; perItem: boolean; } export interface UpdateExplanation { bindingIndex: number; bindingMask: number; lastDirtyMask: number; matched: boolean; accessorResult: unknown; lastValue: unknown; changed: boolean; } export interface ComponentInfo { name: string; file: string | null; line: number | null; /** Identifies which runtime mounted the component. */ runtime?: 'signal' | 'legacy'; } export interface MessageSchemaInfo { discriminant: string; variants: Record>; } export interface BindingLocation { bindingIndex: number; kind: string; key: string | undefined; mask: number; lastValue: unknown; /** How the binding's node relates to the matched element. */ relation: 'self' | 'text-child' | 'comment-child'; } export interface ElementReport { selector: string; tagName: string; attributes: Record; classes: string[]; dataset: Record; text: string; computed: { display: string; visibility: string; position: string; width: number; height: number; }; boundingBox: { x: number; y: number; width: number; height: number; }; bindings: Array<{ bindingIndex: number; kind: string; mask: number; lastValue: unknown; relation: 'self' | 'text-child' | 'comment-child'; }>; } export interface HydrationDivergence { path: string; kind: 'attribute' | 'text' | 'structural'; server: unknown; client: unknown; } /** * The relay-callable debug surface of a mounted LLui component. * * Required methods are implemented by every runtime (and by * `installSignalDebug`). Optional methods are binding/scope/effect * introspection that only the legacy runtime provides — callers must * feature-detect and degrade when they are absent. */ export interface LluiDebugAPI { getState(): unknown; send(msg: unknown): void; flush(): void; getMessageHistory(opts?: { since?: number; limit?: number; }): MessageRecord[]; evalUpdate(msg: unknown): { state: unknown; effects: unknown[]; }; exportTrace(): { lluiTrace: 1; component: string; generatedBy: string; timestamp: string; entries: Array<{ msg: unknown; expectedState: unknown; expectedEffects: unknown[]; }>; }; clearLog(): void; validateMessage(msg: unknown): ValidationError[] | null; searchState(query: string): unknown; getMessageSchema(): MessageSchemaInfo | object | null; getStateSchema(): object | null; getEffectSchema(): object | null; getComponentInfo(): ComponentInfo; snapshotState(): unknown; restoreState(snap: unknown): void; getBindings?(): BindingDebugInfo[]; whyDidUpdate?(bindingIndex: number): UpdateExplanation; getMaskLegend?(): Record | null; decodeMask?(mask: number): string[]; getBindingsFor?(selector: string): BindingLocation[]; getBindingGraph?(): Array<{ statePath: string; bindingIndices: number[]; }>; getBindingSource?(bindingIndex: number): { file: string; line: number; column: number; } | null; forceRerender?(): { changedBindings: number[]; }; getEachDiff?(sinceIndex?: number): EachDiff[]; getScopeTree?(opts?: { depth?: number; scopeId?: string; }): LifetimeNode; getDisposerLog?(limit?: number): DisposerEvent[]; inspectElement?(selector: string): ElementReport | null; getRenderedHtml?(selector?: string, maxlength?: number): string; dispatchDomEvent?(selector: string, type: string, init?: EventInit): { dispatched: boolean; messagesProducedIndices: number[]; resultingState: unknown | null; }; getFocus?(): { selector: string | null; tagName: string | null; selectionStart: number | null; selectionEnd: number | null; }; getHydrationReport?(): HydrationDivergence[]; getPendingEffects?(): PendingEffect[]; getEffectTimeline?(limit?: number): EffectTimelineEntry[]; mockEffect?(match: EffectMatch, response: unknown, opts?: { persist?: boolean; }): { mockId: string; }; resolveEffect?(effectId: string, response: unknown): { resolved: boolean; }; stepBack?(n: number, mode: 'pure' | 'live'): { state: unknown; rewindDepth: number; }; getCoverage?(): CoverageSnapshot; getCompiledSource?(viewFn?: string): { pre: string; post: string; } | null; getMsgMaskMap?(): Record | null; evalInPage?(code: string): { result: unknown | { error: string; }; sideEffects: { stateChanged: StateDiff | null; newHistoryEntries: number; newPendingEffects: PendingEffect[]; dirtyBindingIndices: number[]; }; }; } /** Everything the signal debug API needs from a mounted component. Supplied by * mountSignalComponent; keeps this module decoupled from the mount internals. */ export interface SignalDebugHooks { name: string; getState: () => unknown; /** replace state and re-render (restore / time-travel) */ setState: (s: unknown) => void; send: (msg: unknown) => void; /** pure reducer, normalized to [state, effects] (for evalUpdate / dry-run) */ pureUpdate: (s: unknown, msg: unknown) => [unknown, unknown[]]; /** captured message log (newest last); installSignalDebug reads it live */ history: readonly SignalMessageRecord[]; clearHistory: () => void; msgSchema?: object; stateSchema?: object; effectSchema?: object; componentMeta?: { file: string; line: number; }; } export interface ValidationError { path: string; message: string; /** Set by the legacy validator; the signal validator omits these. */ expected?: string; received?: string; /** * A complete, valid example message — attached to the FIRST error so an LLM * can construct the corrected message in one shot instead of inferring the * shape from the schema. Built for the targeted variant (or a representative * variant when the variant is unknown/missing), reusing any valid fields the * caller already supplied. */ example?: Record; } /** Build the signal debug API and register it. Returns an unregister function. */ export declare function installSignalDebug(hooks: SignalDebugHooks): () => void; /** * Register the MCP relay for this page. Discovery: fetch the Vite * plugin's `/__llui_mcp_status` marker for the live port and connect; if * unreachable, fall back to the compile-time `port`. No retry loop — * `window.__lluiConnect(port?)` is exposed for manual/late connection, * and the vite-plugin's `llui:mcp-ready` HMR event also forwards here. */ export declare function startRelay(port?: number): void; //# sourceMappingURL=devtools.d.ts.map