chore(lint): no-explicit-any in app (batch 4)

Covers composer, message-input, tooltip, workspace-desktop-tabs-row,
and the e2e helpers (app, node-ws-factory, terminal-probes).
This commit is contained in:
Mohamed Boudra
2026-04-24 06:59:07 +07:00
parent 13d3de2b1d
commit a4755cda0e
7 changed files with 125 additions and 77 deletions
+16 -10
View File
@@ -33,20 +33,23 @@ async function ensureE2EStorageSeeded(page: Page): Promise<void> {
try {
const parsed = JSON.parse(raw);
if (!Array.isArray(parsed) || parsed.length !== 1) return true;
const entry = parsed[0] as any;
const entry = parsed[0] as { serverId?: string; connections?: unknown };
if (entry?.serverId !== serverId) return true;
const connections = entry?.connections;
if (!Array.isArray(connections)) return true;
if (
connections.some(
(c: any) =>
(c: { type?: string; endpoint?: string }) =>
c?.type === "directTcp" &&
typeof c?.endpoint === "string" &&
/:6767\b/.test(c.endpoint),
)
)
return true;
return !connections.some((c: any) => c?.type === "directTcp" && c?.endpoint === endpoint);
return !connections.some(
(c: { type?: string; endpoint?: string }) =>
c?.type === "directTcp" && c?.endpoint === endpoint,
);
} catch {
return true;
}
@@ -96,7 +99,7 @@ async function assertE2EUsesSeededTestDaemon(page: Page): Promise<void> {
throw new Error("E2E expected @paseo:daemon-registry to be set before app load.");
}
let registry: any;
let registry: unknown;
try {
registry = JSON.parse(snapshot.registryRaw);
} catch {
@@ -109,7 +112,7 @@ async function assertE2EUsesSeededTestDaemon(page: Page): Promise<void> {
);
}
const daemon = registry[0];
const daemon = registry[0] as { serverId?: string; connections?: unknown };
if (typeof daemon?.serverId !== "string" || daemon.serverId.length === 0) {
throw new Error(
`E2E expected seeded daemon to have a string serverId (got ${String(daemon?.serverId)}).`,
@@ -124,7 +127,10 @@ async function assertE2EUsesSeededTestDaemon(page: Page): Promise<void> {
const connections: unknown = daemon?.connections;
if (
!Array.isArray(connections) ||
!connections.some((c: any) => c?.type === "directTcp" && c?.endpoint === expectedEndpoint)
!connections.some(
(c: { type?: string; endpoint?: string }) =>
c?.type === "directTcp" && c?.endpoint === expectedEndpoint,
)
) {
throw new Error(
`E2E expected seeded daemon connections to include directTcp ${expectedEndpoint} (got ${JSON.stringify(connections)}).`,
@@ -133,7 +139,7 @@ async function assertE2EUsesSeededTestDaemon(page: Page): Promise<void> {
if (
Array.isArray(connections) &&
connections.some(
(c: any) =>
(c: { type?: string; endpoint?: string }) =>
c?.type === "directTcp" && typeof c?.endpoint === "string" && /:6767\b/.test(c.endpoint),
)
) {
@@ -146,7 +152,7 @@ async function assertE2EUsesSeededTestDaemon(page: Page): Promise<void> {
throw new Error("E2E expected @paseo:create-agent-preferences to be set before app load.");
}
try {
const prefs = JSON.parse(snapshot.prefsRaw) as any;
const prefs = JSON.parse(snapshot.prefsRaw) as { serverId?: string };
if (prefs?.serverId !== daemon.serverId) {
throw new Error(
`E2E expected create-agent-preferences.serverId to match seeded daemon serverId (${daemon.serverId}) (got ${String(prefs?.serverId)}).`,
@@ -343,8 +349,8 @@ export const ensureHostSelected = async (page: Page) => {
const registryRaw = localStorage.getItem("@paseo:daemon-registry");
const prefsRaw = localStorage.getItem("@paseo:create-agent-preferences");
if (!registryRaw || !prefsRaw) return { ok: false, reason: "missing storage" } as const;
const registry = JSON.parse(registryRaw) as any[];
const prefs = JSON.parse(prefsRaw) as any;
const registry = JSON.parse(registryRaw) as Array<{ serverId?: string }>;
const prefs = JSON.parse(prefsRaw) as { serverId?: string };
if (!Array.isArray(registry) || registry.length !== 1)
return { ok: false, reason: "registry shape" } as const;
const serverId = registry[0]?.serverId;
+9 -9
View File
@@ -5,15 +5,15 @@ interface WebSocketLike {
send: (data: string | Uint8Array | ArrayBuffer) => void;
close: (code?: number, reason?: string) => void;
binaryType?: string;
on?: (event: string, listener: (...args: any[]) => void) => void;
off?: (event: string, listener: (...args: any[]) => void) => void;
removeListener?: (event: string, listener: (...args: any[]) => void) => void;
addEventListener?: (event: string, listener: (event: any) => void) => void;
removeEventListener?: (event: string, listener: (event: any) => void) => void;
onopen?: ((event: any) => void) | null;
onclose?: ((event: any) => void) | null;
onerror?: ((event: any) => void) | null;
onmessage?: ((event: any) => void) | null;
on?: (event: string, listener: (...args: unknown[]) => void) => void;
off?: (event: string, listener: (...args: unknown[]) => void) => void;
removeListener?: (event: string, listener: (...args: unknown[]) => void) => void;
addEventListener?: (event: string, listener: (event: unknown) => void) => void;
removeEventListener?: (event: string, listener: (event: unknown) => void) => void;
onopen?: ((event: unknown) => void) | null;
onclose?: ((event: unknown) => void) | null;
onerror?: ((event: unknown) => void) | null;
onmessage?: ((event: unknown) => void) | null;
}
export type NodeWebSocketFactory = (
+41 -9
View File
@@ -83,8 +83,12 @@ export interface LatencyStats {
export async function installTerminalRenderProbe(page: Page): Promise<void> {
await page.addInitScript(() => {
interface ProbeTerm {
write?: (data: string | Uint8Array, callback?: () => void) => void;
__paseoRenderProbeWriteWrapped?: boolean;
}
interface ProbeState {
term: any;
term: ProbeTerm | undefined;
setCount: number;
unsetCount: number;
writeCount: number;
@@ -101,7 +105,10 @@ export async function installTerminalRenderProbe(page: Page): Promise<void> {
startSampling: (durationMs: number) => void;
}
const win = window as any;
const win = window as unknown as Record<string, unknown> & {
__terminalRenderProbe?: ProbeState;
__paseoTerminal?: ProbeTerm;
};
const existingDescriptor = Object.getOwnPropertyDescriptor(win, "__paseoTerminal");
const getExisting = () =>
existingDescriptor?.get ? existingDescriptor.get.call(win) : existingDescriptor?.value;
@@ -182,7 +189,7 @@ export async function installTerminalRenderProbe(page: Page): Promise<void> {
get() {
return probe.term;
},
set(next: any) {
set(next: ProbeTerm | undefined) {
if (next === undefined) {
probe.unsetCount += 1;
probe.events.push({ at: performance.now(), type: "unset" });
@@ -230,20 +237,30 @@ export async function installTerminalRenderProbe(page: Page): Promise<void> {
});
}
interface TerminalRenderProbeWindow {
__terminalRenderProbe: {
reset: () => void;
startSampling: (durationMs: number) => void;
snapshot: () => TerminalRenderProbeSnapshot;
};
}
export async function resetTerminalRenderProbe(page: Page): Promise<void> {
await page.evaluate(() => {
(window as any).__terminalRenderProbe.reset();
(window as unknown as TerminalRenderProbeWindow).__terminalRenderProbe.reset();
});
}
export async function startTerminalFrameSampling(page: Page, durationMs = 2500): Promise<void> {
await page.evaluate((ms) => {
(window as any).__terminalRenderProbe.startSampling(ms);
(window as unknown as TerminalRenderProbeWindow).__terminalRenderProbe.startSampling(ms);
}, durationMs);
}
export async function readTerminalRenderProbe(page: Page): Promise<TerminalRenderProbeSnapshot> {
return page.evaluate(() => (window as any).__terminalRenderProbe.snapshot());
return page.evaluate(() =>
(window as unknown as TerminalRenderProbeWindow).__terminalRenderProbe.snapshot(),
);
}
export async function terminalVisibleText(page: Page): Promise<string> {
@@ -639,7 +656,10 @@ export async function installTerminalKeystrokeStressProbe(page: Page): Promise<v
get() {
return terminal;
},
set(next: any) {
set(next: {
write?: (data: string | Uint8Array, callback?: () => void) => void;
__paseoKeystrokeProbeWriteWrapped?: boolean;
}) {
terminal = next;
if (next?.write && !next.__paseoKeystrokeProbeWriteWrapped) {
const originalWrite = next.write.bind(next);
@@ -665,9 +685,18 @@ export async function installTerminalKeystrokeStressProbe(page: Page): Promise<v
});
}
interface TerminalKeystrokeStressProbeWindow {
__terminalKeystrokeStressProbe: {
reset: () => void;
report: (text: string) => TerminalKeystrokeStressReport;
};
}
export async function resetTerminalKeystrokeStressProbe(page: Page): Promise<void> {
await page.evaluate(() => {
(window as any).__terminalKeystrokeStressProbe.reset();
(
window as unknown as TerminalKeystrokeStressProbeWindow
).__terminalKeystrokeStressProbe.reset();
});
}
@@ -676,7 +705,10 @@ export async function readTerminalKeystrokeStressReport(
inputText: string,
): Promise<TerminalKeystrokeStressReport> {
return page.evaluate(
(text) => (window as any).__terminalKeystrokeStressProbe.report(text),
(text) =>
(
window as unknown as TerminalKeystrokeStressProbeWindow
).__terminalKeystrokeStressProbe.report(text),
inputText,
);
}
+7 -7
View File
@@ -842,8 +842,8 @@ export function Composer({
const cancelButtonStyle = useMemo(
() => [
styles.cancelButton as any,
(!isConnected || isCancellingAgent ? styles.buttonDisabled : undefined) as any,
styles.cancelButton as object,
(!isConnected || isCancellingAgent ? styles.buttonDisabled : undefined) as object | undefined,
],
[isConnected, isCancellingAgent],
);
@@ -1105,9 +1105,9 @@ export function Composer({
const voiceButtonDisabled = !isConnected || isVoiceSwitching;
const realtimeVoiceButtonStyle = useCallback(
({ hovered }: PressableStateCallbackType & { hovered?: boolean }) => [
styles.realtimeVoiceButton as any,
(Boolean(hovered) ? styles.iconButtonHovered : undefined) as any,
(voiceButtonDisabled ? styles.buttonDisabled : undefined) as any,
styles.realtimeVoiceButton as object,
(Boolean(hovered) ? styles.iconButtonHovered : undefined) as object | undefined,
(voiceButtonDisabled ? styles.buttonDisabled : undefined) as object | undefined,
],
[voiceButtonDisabled],
);
@@ -1250,7 +1250,7 @@ export function Composer({
);
}
const styles = StyleSheet.create(((theme: Theme) => ({
const styles = StyleSheet.create((theme: Theme) => ({
container: {
flexDirection: "column",
position: "relative",
@@ -1415,6 +1415,6 @@ const styles = StyleSheet.create(((theme: Theme) => ({
color: theme.colors.palette.red[500],
fontSize: theme.fontSize.sm,
},
})) as any) as Record<string, any>;
})) as unknown as Record<string, object>;
const QUEUE_SEND_BUTTON_STYLE = [styles.queueActionButton, styles.queueSendButton];
+19 -12
View File
@@ -19,6 +19,7 @@ import {
forwardRef,
} from "react";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import type { Theme } from "@/styles/theme";
import { ArrowUp, Mic, MicOff, CornerDownLeft, Plus, Square } from "lucide-react-native";
import Animated, { useSharedValue, useAnimatedStyle, withTiming } from "react-native-reanimated";
import { useDictation } from "@/hooks/use-dictation";
@@ -657,8 +658,9 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
const getWebTextArea = useCallback((): TextAreaHandle | null => {
const current = textInputRef.current;
if (!current) return null;
if (typeof (current as any).getNativeRef === "function") {
const native = (current as any).getNativeRef();
const candidate = current as { getNativeRef?: () => unknown };
if (typeof candidate.getNativeRef === "function") {
const native = candidate.getNativeRef();
if (isTextAreaLike(native)) return native;
}
if (isTextAreaLike(current)) return current;
@@ -692,11 +694,16 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
return;
}
const textarea = getWebTextArea();
const textarea = getWebTextArea() as
| (TextAreaHandle & {
addEventListener?: (type: string, listener: (e: ClipboardEvent) => void) => void;
removeEventListener?: (type: string, listener: (e: ClipboardEvent) => void) => void;
})
| null;
if (
!textarea ||
typeof (textarea as any).addEventListener !== "function" ||
typeof (textarea as any).removeEventListener !== "function"
typeof textarea.addEventListener !== "function" ||
typeof textarea.removeEventListener !== "function"
) {
return;
}
@@ -727,10 +734,10 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
});
};
(textarea as any).addEventListener("paste", handlePaste);
textarea.addEventListener("paste", handlePaste);
return () => {
disposed = true;
(textarea as any).removeEventListener("paste", handlePaste);
textarea.removeEventListener?.("paste", handlePaste);
};
}, [
disabled,
@@ -1227,7 +1234,7 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
);
});
const styles = StyleSheet.create(((theme: any) => ({
const styles = StyleSheet.create((theme: Theme) => ({
container: {
position: "relative",
},
@@ -1272,11 +1279,11 @@ const styles = StyleSheet.create(((theme: any) => ({
fontWeight: theme.fontWeight.normal,
lineHeight: theme.fontSize.base * 1.4,
...(isWeb
? {
outlineStyle: "none" as const,
? ({
outlineStyle: "none",
outlineWidth: 0,
outlineColor: "transparent",
}
} as object)
: {}),
},
buttonRow: {
@@ -1362,4 +1369,4 @@ const styles = StyleSheet.create(((theme: any) => ({
right: 0,
bottom: 0,
},
})) as any) as Record<string, any>;
})) as unknown as Record<string, object>;
+22 -22
View File
@@ -307,24 +307,24 @@ export function TooltipTrigger({
}, [clearOpenTimer]);
const handleHoverIn = useCallback(
(e?: any) => {
onHoverIn?.(e);
(e?: unknown) => {
onHoverIn?.(e as never);
scheduleOpen();
},
[onHoverIn, scheduleOpen],
);
const handleHoverOut = useCallback(
(e?: any) => {
onHoverOut?.(e);
(e?: unknown) => {
onHoverOut?.(e as never);
close();
},
[onHoverOut, close],
);
const handleFocus = useCallback(
(e: any) => {
onFocus?.(e);
(e: unknown) => {
onFocus?.(e as never);
if (!ctx.enabled || disabled) return;
if (!shouldOpenOnFocus()) return;
clearOpenTimer();
@@ -334,16 +334,16 @@ export function TooltipTrigger({
);
const handleBlur = useCallback(
(e: any) => {
onBlur?.(e);
(e: unknown) => {
onBlur?.(e as never);
close();
},
[close, onBlur],
);
const handlePress = useCallback(
(e: any) => {
onPress?.(e);
(e: unknown) => {
onPress?.(e as never);
if (!ctx.enabled || disabled) {
return;
}
@@ -372,7 +372,7 @@ export function TooltipTrigger({
onPointerLeave: handleHoverOut,
onMouseEnter: handleHoverIn,
onMouseLeave: handleHoverOut,
} as any)
} as object)
: null),
};
@@ -382,21 +382,21 @@ export function TooltipTrigger({
throw new Error("TooltipTrigger with asChild expects a single React element child");
}
const childProps = child.props as Record<string, any>;
const childProps = child.props as Record<string, unknown>;
const mergedProps = {
...childProps,
...triggerProps,
disabled: childProps.disabled || disabled,
onHoverIn: composeEventHandlers(childProps.onHoverIn, handleHoverIn),
onHoverOut: composeEventHandlers(childProps.onHoverOut, handleHoverOut),
onFocus: composeEventHandlers(childProps.onFocus, handleFocus),
onBlur: composeEventHandlers(childProps.onBlur, handleBlur),
onPress: composeEventHandlers(childProps.onPress, handlePress),
onPointerEnter: composeEventHandlers(childProps.onPointerEnter, handleHoverIn),
onPointerLeave: composeEventHandlers(childProps.onPointerLeave, handleHoverOut),
onMouseEnter: composeEventHandlers(childProps.onMouseEnter, handleHoverIn),
onMouseLeave: composeEventHandlers(childProps.onMouseLeave, handleHoverOut),
} as Record<string, any>;
onHoverIn: composeEventHandlers(childProps.onHoverIn as never, handleHoverIn),
onHoverOut: composeEventHandlers(childProps.onHoverOut as never, handleHoverOut),
onFocus: composeEventHandlers(childProps.onFocus as never, handleFocus),
onBlur: composeEventHandlers(childProps.onBlur as never, handleBlur),
onPress: composeEventHandlers(childProps.onPress as never, handlePress),
onPointerEnter: composeEventHandlers(childProps.onPointerEnter as never, handleHoverIn),
onPointerLeave: composeEventHandlers(childProps.onPointerLeave as never, handleHoverOut),
onMouseEnter: composeEventHandlers(childProps.onMouseEnter as never, handleHoverIn),
onMouseLeave: composeEventHandlers(childProps.onMouseLeave as never, handleHoverOut),
} as Record<string, unknown>;
const existingRefProp = childProps[triggerRefProp] as Ref<View | null> | undefined;
mergedProps[triggerRefProp] = (node: View | null) => {
@@ -30,7 +30,10 @@ import {
} from "lucide-react-native";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { SortableInlineList } from "@/components/sortable-inline-list";
import type { DraggableRenderItemInfo } from "@/components/draggable-list.types";
import type {
DraggableListDragHandleProps,
DraggableRenderItemInfo,
} from "@/components/draggable-list.types";
import { isNative, isWeb } from "@/constants/platform";
import {
ContextMenu,
@@ -220,7 +223,7 @@ function TabChip({
setHoveredCloseTabKey: Dispatch<SetStateAction<string | null>>;
onNavigateTab: (tabId: string) => void;
onCloseTab: (tabId: string) => Promise<void> | void;
dragHandleProps: any;
dragHandleProps: DraggableListDragHandleProps | undefined;
}) {
const { theme } = useUnistyles();
const { closeButtonTestId, contextMenuTestId, menuEntries } = resolvedTab;
@@ -243,7 +246,7 @@ function TabChip({
const tabChipStyle = useCallback(
() => [
styles.tab,
isWeb && isDragging && ({ cursor: "grabbing" } as const),
isWeb && isDragging && ({ cursor: "grabbing" } as object),
{
minWidth: resolvedTabWidth,
width: resolvedTabWidth,
@@ -322,10 +325,10 @@ function TabChip({
<Tooltip delayDuration={400} enabledOnDesktop enabledOnMobile={false}>
<TooltipTrigger asChild triggerRefProp="triggerRef">
<ContextMenuTrigger
{...(dragHandleProps?.attributes as any)}
{...(dragHandleProps?.listeners as any)}
{...(dragHandleProps?.attributes as object | undefined)}
{...(dragHandleProps?.listeners as object | undefined)}
testID={`workspace-tab-${tab.key}`}
triggerRef={dragHandleProps?.setActivatorNodeRef as any}
triggerRef={dragHandleProps?.setActivatorNodeRef as unknown as undefined}
enabledOnMobile={false}
style={tabChipStyle}
onHoverIn={handleTabHoverIn}
@@ -359,7 +362,7 @@ function TabChip({
{showCloseButton ? (
<Pressable
{...(closeButtonDragBlockers as any)}
{...(closeButtonDragBlockers as object | undefined)}
testID={closeButtonTestId}
disabled={isClosingTab}
onPressIn={handleCloseButtonPressIn}
@@ -771,7 +774,7 @@ function ResolvedDesktopTabChip({
setHoveredCloseTabKey: Dispatch<SetStateAction<string | null>>;
onNavigateTab: (tabId: string) => void;
onCloseTab: (tabId: string) => Promise<void> | void;
dragHandleProps: any;
dragHandleProps: DraggableListDragHandleProps | undefined;
showDropIndicatorBefore: boolean;
showDropIndicatorAfter: boolean;
}) {