From 84087b3a5081c403ff14a7c3e625f71441c3bd7b Mon Sep 17 00:00:00 2001 From: ssdeanx Date: Thu, 15 Jan 2026 08:45:34 -0500 Subject: [PATCH 1/2] chore: refactor codebase for improved type safety and consistency - Changed several type definitions from `type` to `interface` for better extensibility in components such as `PieSlice`, `ConfirmationContextValue`, and `QueueMessagePart`. - Updated function parameters and return types to use more consistent array syntax (e.g., `Array` instead of `Type[]`) in components like `DataTableProps` and `AttachmentsContext`. - Refactored various components to use `void` for fire-and-forget function calls, ensuring no promises are returned where void is expected, particularly in `ChatProvider` and `WorkflowActions`. - Removed unnecessary ESLint disable comments to clean up the code and improve readability. - Adjusted default parameter values to use shorthand syntax in hooks like `useDebounce` and `useToggle`. - Improved conditional return statements for clarity and consistency across multiple components. - Removed a test template file that was no longer needed. - Updated ESLint configuration to enhance type checking and ensure better adherence to coding standards. --- app/admin/_components/admin-sidebar.tsx | 2 +- app/api/contact/route.ts | 3 +- app/chat/components/agent-artifact.tsx | 21 +- app/chat/components/agent-confirmation.tsx | 8 +- app/chat/components/agent-web-preview.tsx | 8 +- app/chat/components/chat-messages.tsx | 30 ++- app/chat/components/chat.types.ts | 129 +++++----- app/chat/providers/chat-context-types.ts | 30 +-- app/chat/providers/chat-context.tsx | 7 +- app/components/api-components.tsx | 3 +- app/components/charts/PieWidget.tsx | 2 +- app/dashboard/_components/data-table.tsx | 16 +- app/dashboard/_components/empty-state.tsx | 2 +- app/dashboard/_components/stat-card.tsx | 2 +- app/networks/providers/network-context.tsx | 6 +- app/workflows/components/workflow-actions.tsx | 4 +- app/workflows/providers/workflow-context.tsx | 2 +- convex/_generated/api.d.ts | 2 +- convex/_generated/api.js | 2 +- convex/_generated/server.js | 2 +- eslint.config.js | 236 +++++++++--------- hooks/use-debounce.ts | 2 +- hooks/use-utils.ts | 6 +- .../ai-elements/chain-of-thought.tsx | 2 +- src/components/ai-elements/code-block.tsx | 2 +- src/components/ai-elements/confirmation.tsx | 14 +- src/components/ai-elements/context.tsx | 2 +- src/components/ai-elements/loader.tsx | 2 +- src/components/ai-elements/message.tsx | 2 +- src/components/ai-elements/plan.tsx | 2 +- src/components/ai-elements/prompt-input.tsx | 32 +-- src/components/ai-elements/queue.tsx | 6 +- src/components/ai-elements/reasoning.tsx | 2 +- src/components/ai-elements/shimmer.tsx | 2 +- src/components/ai-elements/tool.tsx | 2 +- src/components/ai-elements/web-preview.tsx | 2 +- src/mastra/agents/calendarAgent.ts | 2 +- src/mastra/config/lance.ts | 1 - src/mastra/config/mongodb.ts | 1 - src/mastra/config/pg-storage.ts | 1 - src/mastra/lib/http-client.ts | 4 +- src/mastra/tools/chartjs.tool.ts | 3 +- .../tools/tests/alpha-vantage.tool.test.ts | 2 +- .../tools/tests/csv-to-json.tool.test.ts | 2 +- src/mastra/tools/tests/cytoscape.tool.test.ts | 3 +- .../tools/tests/json-to-csv.tool.test.ts | 2 +- src/mastra/tools/tests/url-tool.test.ts | 2 +- src/mastra/tools/weather-tool.ts | 11 +- test-templates/simple-utility.template.ts | 44 ---- ui/accordion.tsx | 4 +- ui/radio-group.tsx | 4 +- ui/switch.tsx | 2 +- ui/tabs.tsx | 2 +- 53 files changed, 329 insertions(+), 358 deletions(-) delete mode 100644 test-templates/simple-utility.template.ts diff --git a/app/admin/_components/admin-sidebar.tsx b/app/admin/_components/admin-sidebar.tsx index 3b485b0..6a0b6d2 100644 --- a/app/admin/_components/admin-sidebar.tsx +++ b/app/admin/_components/admin-sidebar.tsx @@ -13,7 +13,7 @@ import { Settings, } from 'lucide-react' -type NavItem = { +interface NavItem { title: string href: Route icon: React.ComponentType<{ className?: string }> diff --git a/app/api/contact/route.ts b/app/api/contact/route.ts index 9620da7..9d7cf71 100644 --- a/app/api/contact/route.ts +++ b/app/api/contact/route.ts @@ -1,4 +1,5 @@ -import { NextRequest, NextResponse } from 'next/server' +import type { NextRequest} from 'next/server'; +import { NextResponse } from 'next/server' interface ContactFormData { firstName: string diff --git a/app/chat/components/agent-artifact.tsx b/app/chat/components/agent-artifact.tsx index 4c6d165..ba766ae 100644 --- a/app/chat/components/agent-artifact.tsx +++ b/app/chat/components/agent-artifact.tsx @@ -30,7 +30,7 @@ import type { ArtifactData } from './chat.types' interface AgentArtifactProps { artifact: ArtifactData onClose?: () => void - // eslint-disable-next-line no-unused-vars + onCodeUpdate?: (artifactId: string, newCode: string) => void } @@ -59,14 +59,17 @@ export function AgentArtifact({ artifact.type === 'code' && PREVIEWABLE_LANGUAGES.includes(normalizeLanguage(artifact.language)) - const handleCopy = useCallback(async () => { - try { - await navigator.clipboard.writeText(editedCode) - setCopied(true) - setTimeout(() => setCopied(false), 2000) - } catch (err) { - void err + const handleCopy = useCallback(() => { + const doCopy = async () => { + try { + await navigator.clipboard.writeText(editedCode) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } catch (err) { + void err + } } + void doCopy() }, [editedCode]) const handleDownload = useCallback(() => { @@ -233,7 +236,7 @@ export function AgentArtifactCompact({ // Floating action button for quick access to editor interface ArtifactEditorFABProps { artifact: ArtifactData - // eslint-disable-next-line no-unused-vars + onCodeChange?: (newCode: string) => void } diff --git a/app/chat/components/agent-confirmation.tsx b/app/chat/components/agent-confirmation.tsx index a6463dc..25a7d64 100644 --- a/app/chat/components/agent-confirmation.tsx +++ b/app/chat/components/agent-confirmation.tsx @@ -28,10 +28,10 @@ interface AgentConfirmationProps { state: ToolUIPart['state'] severity?: ConfirmationSeverity className?: string - // eslint-disable-next-line no-unused-vars - onApprove(approvalId: string): void - // eslint-disable-next-line no-unused-vars - onReject(approvalId: string): void + + onApprove: (approvalId: string) => void + + onReject: (approvalId: string) => void } const severityConfig: Record< diff --git a/app/chat/components/agent-web-preview.tsx b/app/chat/components/agent-web-preview.tsx index 18440cf..cb720d7 100644 --- a/app/chat/components/agent-web-preview.tsx +++ b/app/chat/components/agent-web-preview.tsx @@ -36,7 +36,7 @@ import { CheckIcon, PlayIcon, RotateCcwIcon, - // eslint-disable-next-line no-unused-vars + Edit3Icon, EyeIcon, SplitIcon, @@ -54,7 +54,7 @@ type PreviewStatus = 'idle' | 'running' | 'success' | 'error' interface AgentWebPreviewProps { preview: WebPreviewData onClose?: () => void - // eslint-disable-next-line no-unused-vars + onCodeChange?: (code: string) => void defaultTab?: 'preview' | 'code' height?: string | number @@ -444,7 +444,7 @@ export function AgentWebPreview({ variant="ghost" size="sm" className="h-6 gap-1 px-2 text-xs" - onClick={handleCopy} + onClick={() => void handleCopy()} > {copied ? ( <> @@ -786,7 +786,7 @@ interface AgentCodeSandboxProps { title?: string dependencies?: Record onClose?: () => void - // eslint-disable-next-line no-unused-vars + onCodeChange?: (code: string) => void editable?: boolean } diff --git a/app/chat/components/chat-messages.tsx b/app/chat/components/chat-messages.tsx index e41b914..3ddb5d2 100644 --- a/app/chat/components/chat-messages.tsx +++ b/app/chat/components/chat-messages.tsx @@ -1,4 +1,4 @@ -/* eslint-disable no-unused-vars */ + /* eslint-disable no-console */ 'use client' @@ -75,6 +75,7 @@ import type { } from '@mastra/ai-sdk' import { AgentTool } from '@/ui/agent-tool' import { cn } from '@/lib/utils' +import type { LLMStepResult } from '@mastra/core/agent' type MastraDataPart = | AgentDataPart @@ -189,14 +190,16 @@ function extractTasksFromText(content: string): AgentTaskData[] { function CopyButton({ text }: { text: string }) { const [copied, setCopied] = useState(false) - const handleCopy = useCallback(async () => { - try { - await navigator.clipboard.writeText(text) - setCopied(true) - setTimeout(() => setCopied(false), 2000) - } catch (err) { - console.error('Failed to copy:', err) - } + const handleCopy = useCallback(() => { + navigator.clipboard + .writeText(text) + .then(() => { + setCopied(true) + setTimeout(() => setCopied(false), 2000) + }) + .catch((err) => { + console.error('Failed to copy:', err) + }) }, [text]) return ( @@ -618,13 +621,16 @@ function MessageItem({ partType === 'data-tool-workflow' || partType === 'data-tool-network' ) { - const nestedPart = part as any + const nestedPart = part as MastraDataPart + // Use the actual nested data as the prop value. Cast to the expected type for TypeScript + // (LLMStepResult is a type-only import so it cannot be used as a runtime value). + const dataValue = (nestedPart as { data?: unknown }).data ?? {} return ( ) } diff --git a/app/chat/components/chat.types.ts b/app/chat/components/chat.types.ts index 2bf441f..e76c8d8 100644 --- a/app/chat/components/chat.types.ts +++ b/app/chat/components/chat.types.ts @@ -1,112 +1,111 @@ -import type { ReactNode } from 'react' import type { DynamicToolUIPart } from 'ai' +import type { ReactNode } from 'react' import type { ToolInvocationState as ChatToolInvocationState } from '../providers/chat-context-types' export interface Citation { - id: string - number: string - title: string - url: string - description?: string - quote?: string + id: string + number: string + title: string + url: string + description?: string + quote?: string } export interface AgentToolsProps { - tools: Array - className?: string + tools: Array + className?: string } export type TaskStepStatus = 'pending' | 'running' | 'completed' | 'error' export interface TaskStep { - id: string - text: string - status: TaskStepStatus - file?: { - name: string - icon?: string - } + id: string + text: string + status: TaskStepStatus + file?: { + name: string + icon?: string + } } export interface AgentTaskData { - title: string - steps: TaskStep[] + title: string + steps: TaskStep[] } export interface ArtifactData { - id: string - title: string - description?: string - type: 'code' | 'markdown' | 'json' | 'text' | 'html' | 'react' - language?: string - content: string + id: string + title: string + description?: string + type: 'code' | 'markdown' | 'json' | 'text' | 'html' | 'react' + language?: string + content: string } export interface PlanStep { - text: string - completed?: boolean + text: string + completed?: boolean } export interface AgentPlanData { - title: string - description: string - steps: PlanStep[] | string[] - isStreaming?: boolean - currentStep?: number + title: string + description: string + steps: PlanStep[] | string[] + isStreaming?: boolean + currentStep?: number } export interface ReasoningStep { - id: string - label: string - description?: string - status: 'complete' | 'active' | 'pending' - searchResults?: string[] - duration?: number + id: string + label: string + description?: string + status: 'complete' | 'active' | 'pending' + searchResults?: string[] + duration?: number } export interface AgentSuggestionsProps { - suggestions: string[] - // eslint-disable-next-line no-unused-vars - onSelect: (suggestion: string) => void - disabled?: boolean - className?: string + suggestions: string[] + onSelect: (suggestion: string) => void + disabled?: boolean + className?: string } export interface AgentSourcesProps { - sources: Array<{ url: string; title: string }> - className?: string - maxVisible?: number + sources: Array<{ url: string; title: string }> + className?: string + maxVisible?: number } export interface AgentReasoningProps { - reasoning: string - isStreaming: boolean - duration?: number - className?: string + reasoning: string + isStreaming: boolean + duration?: number + className?: string } export type ConfirmationSeverity = 'info' | 'warning' | 'danger' export interface QueuedTask { - id: string - title: string - description?: string - status: 'pending' | 'running' | 'completed' | 'failed' - createdAt?: Date - completedAt?: Date - error?: string + id: string + title: string + description?: string + status: 'pending' | 'running' | 'completed' | 'failed' + createdAt?: Date + completedAt?: Date + error?: string } export interface WebPreviewData { - id: string - url: string - title?: string - code?: string - language?: string - html?: string - editable?: boolean - showConsole?: boolean - height?: number + id: string + url: string + title?: string + code?: string + language?: string + html?: string + editable?: boolean + showConsole?: boolean + height?: number } export type InlineCitationRender = ReactNode[] diff --git a/app/chat/providers/chat-context-types.ts b/app/chat/providers/chat-context-types.ts index 4221812..78acead 100644 --- a/app/chat/providers/chat-context-types.ts +++ b/app/chat/providers/chat-context-types.ts @@ -103,44 +103,44 @@ export interface ChatContextValue { // Temporarily indicates an agent switch is in progress and sends are blocked // Actions - // eslint-disable-next-line no-unused-vars + sendMessage: (text: string, files?: File[]) => void stopGeneration: () => void clearMessages: () => void - // eslint-disable-next-line no-unused-vars + selectAgent: (agentId: string) => void - // eslint-disable-next-line no-unused-vars + selectModel: (modelId: string) => void dismissError: () => void - // eslint-disable-next-line no-unused-vars + setFocusMode: (enabled: boolean) => void // Task management - // eslint-disable-next-line no-unused-vars + addTask: (task: Omit) => string - // eslint-disable-next-line no-unused-vars + updateTask: (taskId: string, updates: Partial) => void - // eslint-disable-next-line no-unused-vars + removeTask: (taskId: string) => void // Confirmation management - // eslint-disable-next-line no-unused-vars + approveConfirmation: (confirmationId: string) => void - // eslint-disable-next-line no-unused-vars + rejectConfirmation: (confirmationId: string, reason?: string) => void // Checkpoint management - // eslint-disable-next-line no-unused-vars + createCheckpoint: (messageIndex: number, label?: string) => string - // eslint-disable-next-line no-unused-vars + restoreCheckpoint: (checkpointId: string) => void - // eslint-disable-next-line no-unused-vars + removeCheckpoint: (checkpointId: string) => void // Web Preview management - // eslint-disable-next-line no-unused-vars + setWebPreview: (preview: WebPreviewData | null) => void // Memory management - // eslint-disable-next-line no-unused-vars + setThreadId: (threadId: string) => void - // eslint-disable-next-line no-unused-vars + setResourceId: (resourceId: string) => void } diff --git a/app/chat/providers/chat-context.tsx b/app/chat/providers/chat-context.tsx index 06bc308..dcbae61 100644 --- a/app/chat/providers/chat-context.tsx +++ b/app/chat/providers/chat-context.tsx @@ -345,9 +345,10 @@ export function ChatProvider({ return } setChatError(null) - aiSendMessage({ + // Fire-and-forget to avoid returning a Promise where a void is expected. + void aiSendMessage({ text: text.trim(), - // @ts-ignore - attachments support in AI SDK v5 + // @ts-expect-error - attachments support in AI SDK v5 attachments: files, }) }, @@ -355,7 +356,7 @@ export function ChatProvider({ ) const stopGeneration = useCallback(() => { - stop() + void stop() }, [stop]) const clearMessages = useCallback(() => { diff --git a/app/components/api-components.tsx b/app/components/api-components.tsx index 09d6210..d660c56 100644 --- a/app/components/api-components.tsx +++ b/app/components/api-components.tsx @@ -1,6 +1,7 @@ 'use client' -import { ReactNode, useState } from 'react' +import type { ReactNode} from 'react'; +import { useState } from 'react' import { Button } from '@/ui/button' import { Badge } from '@/ui/badge' import { CopyIcon, CheckIcon } from 'lucide-react' diff --git a/app/components/charts/PieWidget.tsx b/app/components/charts/PieWidget.tsx index 8661b68..9d0f1ad 100644 --- a/app/components/charts/PieWidget.tsx +++ b/app/components/charts/PieWidget.tsx @@ -2,7 +2,7 @@ import { Legend, Pie, PieChart, ResponsiveContainer, Tooltip } from 'recharts' -export type PieSlice = { name: string; value: number; fill?: string } +export interface PieSlice { name: string; value: number; fill?: string } interface PieWidgetProps { data: PieSlice[] diff --git a/app/dashboard/_components/data-table.tsx b/app/dashboard/_components/data-table.tsx index f8b9c54..fa354a9 100644 --- a/app/dashboard/_components/data-table.tsx +++ b/app/dashboard/_components/data-table.tsx @@ -22,7 +22,7 @@ export interface Column { } interface DataTableProps { - columns: Column[] + columns: Array> data: T[] searchPlaceholder?: string searchKey?: keyof T @@ -52,7 +52,7 @@ export function DataTable({ const [page, setPage] = useState(0) const filteredData = useMemo(() => { - if (!search || !searchKey) return data + if (!search || !searchKey) {return data} const searchLower = search.toLowerCase() return data.filter((row) => { const value = row[searchKey] @@ -64,9 +64,9 @@ export function DataTable({ }, [data, search, searchKey]) const sortedData = useMemo(() => { - if (!sortColumn) return filteredData + if (!sortColumn) {return filteredData} const column = columns.find((c) => c.id === sortColumn) - if (!column) return filteredData + if (!column) {return filteredData} return [...filteredData].sort((a, b) => { let aVal: unknown @@ -82,9 +82,9 @@ export function DataTable({ return 0 } - if (aVal === bVal) return 0 - if (aVal == null) return 1 - if (bVal == null) return -1 + if (aVal === bVal) {return 0} + if (aVal == null) {return 1} + if (bVal == null) {return -1} const comparison = aVal < bVal ? -1 : 1 return sortDirection === 'asc' ? comparison : -comparison @@ -113,7 +113,7 @@ export function DataTable({ } if (column.accessorKey) { const value = row[column.accessorKey] - if (value === null || value === undefined) return '-' + if (value === null || value === undefined) {return '-'} return String(value) } return '-' diff --git a/app/dashboard/_components/empty-state.tsx b/app/dashboard/_components/empty-state.tsx index 0ab1ac8..5fcf92d 100644 --- a/app/dashboard/_components/empty-state.tsx +++ b/app/dashboard/_components/empty-state.tsx @@ -1,6 +1,6 @@ import { cn } from '@/lib/utils' import { Button } from '@/ui/button' -import { LucideIcon } from 'lucide-react' +import type { LucideIcon } from 'lucide-react' interface EmptyStateProps { icon?: LucideIcon diff --git a/app/dashboard/_components/stat-card.tsx b/app/dashboard/_components/stat-card.tsx index 3b7d20a..8f56b3d 100644 --- a/app/dashboard/_components/stat-card.tsx +++ b/app/dashboard/_components/stat-card.tsx @@ -5,7 +5,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/ui/card' import { Skeleton } from '@/ui/skeleton' import Link from 'next/link' import type { Route } from 'next' -import { LucideIcon } from 'lucide-react' +import type { LucideIcon } from 'lucide-react' interface StatCardProps { title: string diff --git a/app/networks/providers/network-context.tsx b/app/networks/providers/network-context.tsx index c97c213..fe37b2d 100644 --- a/app/networks/providers/network-context.tsx +++ b/app/networks/providers/network-context.tsx @@ -78,16 +78,16 @@ export interface NetworkContextValue { error: string | null stopExecution: () => void clearHistory: () => void - // eslint-disable-next-line no-unused-vars + selectNetwork: (networkId: NetworkId) => void - // eslint-disable-next-line no-unused-vars + sendMessage: (text: string) => void } const NetworkContext = createContext(null) export function useNetworkContext(): NetworkContextValue { - // eslint-disable-line react-refresh/only-export-components + const context = useContext(NetworkContext) if (!context) { throw new Error( diff --git a/app/workflows/components/workflow-actions.tsx b/app/workflows/components/workflow-actions.tsx index f5e2d49..a70dab8 100644 --- a/app/workflows/components/workflow-actions.tsx +++ b/app/workflows/components/workflow-actions.tsx @@ -10,7 +10,7 @@ import { LayoutPanelLeftIcon, LayoutPanelTopIcon, LayoutGridIcon, -} from 'lucide-react' +} from 'lucide-react' import { useCallback } from 'react' import { useReactFlow } from '@xyflow/react' @@ -61,7 +61,7 @@ export function WorkflowActions() { title="Horizontal Layout" > - +