fix: replace no-explicit-any with proper types
- Test mocks: use { children?: React.ReactNode } and Record<string, unknown>
- Editor.tsx: use unknown[] for BlockNote block arrays
- wikilinks.ts: add BlockLike/InlineItem interfaces for block processing
- useMcpBridge.ts: use unknown for resolve/reject and Record<string, unknown>
- ai-chat.ts: use unknown catch + instanceof Error check
- mock-tauri.ts: type messages array, use Record<string, unknown> for args
(keep one targeted eslint-disable for heterogeneous handler map)
- vite.config.ts: use http.IncomingMessage/ServerResponse, typed messages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -83,7 +83,7 @@ vi.mock('@blocknote/react', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@blocknote/mantine', () => ({
|
||||
BlockNoteView: ({ children }: any) => <div data-testid="blocknote-view">{children}</div>,
|
||||
BlockNoteView: ({ children }: { children?: React.ReactNode }) => <div data-testid="blocknote-view">{children}</div>,
|
||||
}))
|
||||
|
||||
vi.mock('@blocknote/mantine/style.css', () => ({}))
|
||||
|
||||
@@ -4,13 +4,13 @@ import { describe, it, expect, vi } from 'vitest'
|
||||
// Hoisted mock editor — available before vi.mock factory runs.
|
||||
// Tests can reconfigure spies (e.g. mockTryParse.mockResolvedValue) before rendering.
|
||||
const mockEditor = vi.hoisted(() => ({
|
||||
tryParseMarkdownToBlocks: vi.fn(async () => [] as any[]),
|
||||
tryParseMarkdownToBlocks: vi.fn(async () => [] as unknown[]),
|
||||
replaceBlocks: vi.fn(),
|
||||
insertBlocks: vi.fn(),
|
||||
document: [{ id: '1', type: 'paragraph', content: [], props: {}, children: [] }],
|
||||
insertInlineContent: vi.fn(),
|
||||
onMount: vi.fn((cb: () => void) => { cb(); return () => {} }),
|
||||
prosemirrorView: {} as any,
|
||||
prosemirrorView: {} as Record<string, unknown>,
|
||||
blocksToHTMLLossy: vi.fn(() => ''),
|
||||
_tiptapEditor: { commands: { setContent: vi.fn() } },
|
||||
}))
|
||||
@@ -33,7 +33,7 @@ vi.mock('@blocknote/react', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@blocknote/mantine', () => ({
|
||||
BlockNoteView: ({ children }: any) => <div data-testid="blocknote-view">{children}</div>,
|
||||
BlockNoteView: ({ children }: { children?: React.ReactNode }) => <div data-testid="blocknote-view">{children}</div>,
|
||||
}))
|
||||
|
||||
vi.mock('@blocknote/mantine/style.css', () => ({}))
|
||||
|
||||
@@ -238,7 +238,7 @@ export const Editor = memo(function Editor({
|
||||
},
|
||||
})
|
||||
// Cache parsed blocks per tab path for instant switching
|
||||
const tabCacheRef = useRef<Map<string, any[]>>(new Map())
|
||||
const tabCacheRef = useRef<Map<string, unknown[]>>(new Map())
|
||||
const prevActivePathRef = useRef<string | null>(null)
|
||||
const editorMountedRef = useRef(false)
|
||||
const pendingSwapRef = useRef<(() => void) | null>(null)
|
||||
@@ -281,7 +281,7 @@ export const Editor = memo(function Editor({
|
||||
const tab = tabs.find(t => t.entry.path === activeTabPath)
|
||||
if (!tab) return
|
||||
|
||||
const applyBlocks = (blocks: any[]) => {
|
||||
const applyBlocks = (blocks: unknown[]) => {
|
||||
try {
|
||||
const current = editor.document
|
||||
if (current.length > 0 && blocks.length > 0) {
|
||||
@@ -317,7 +317,7 @@ export const Editor = memo(function Editor({
|
||||
|
||||
try {
|
||||
const result = editor.tryParseMarkdownToBlocks(preprocessed)
|
||||
const handleBlocks = (blocks: any[]) => {
|
||||
const handleBlocks = (blocks: unknown[]) => {
|
||||
if (prevActivePathRef.current !== targetPath) return
|
||||
const withWikilinks = injectWikilinks(blocks)
|
||||
// Only cache non-empty results to avoid poisoning the cache
|
||||
@@ -326,12 +326,12 @@ export const Editor = memo(function Editor({
|
||||
}
|
||||
applyBlocks(withWikilinks)
|
||||
}
|
||||
if (result && typeof (result as any).then === 'function') {
|
||||
(result as unknown as Promise<any[]>).then(handleBlocks).catch((err) => {
|
||||
if (result && typeof (result as Promise<unknown[]>).then === 'function') {
|
||||
(result as Promise<unknown[]>).then(handleBlocks).catch((err) => {
|
||||
console.error('Async markdown parse failed:', err)
|
||||
})
|
||||
} else {
|
||||
handleBlocks(result as any[])
|
||||
handleBlocks(result as unknown[])
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to parse/swap editor content:', err)
|
||||
|
||||
@@ -11,8 +11,8 @@ import { useCallback, useRef, useState } from 'react'
|
||||
const DEFAULT_WS_URL = 'ws://localhost:9710'
|
||||
|
||||
interface PendingRequest {
|
||||
resolve: (value: any) => void
|
||||
reject: (reason: any) => void
|
||||
resolve: (value: unknown) => void
|
||||
reject: (reason: unknown) => void
|
||||
}
|
||||
|
||||
interface SearchResult {
|
||||
@@ -68,7 +68,7 @@ export function useMcpBridge(wsUrl = DEFAULT_WS_URL) {
|
||||
})
|
||||
}, [wsUrl])
|
||||
|
||||
const callTool = useCallback(async <T>(tool: string, args: Record<string, any>): Promise<T> => {
|
||||
const callTool = useCallback(async <T>(tool: string, args: Record<string, unknown>): Promise<T> => {
|
||||
const ws = await ensureConnection()
|
||||
const id = `mcp-${++idCounterRef.current}`
|
||||
|
||||
|
||||
@@ -1641,6 +1641,7 @@ index abc1234..${shortHash} 100644
|
||||
|
||||
let mockHasChanges = true
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- mock handler map accepts heterogeneous arg types
|
||||
const mockHandlers: Record<string, (args: any) => any> = {
|
||||
list_vault: () => MOCK_ENTRIES,
|
||||
get_note_content: (args: { path: string }) => MOCK_CONTENT[args.path] ?? '',
|
||||
@@ -1656,7 +1657,7 @@ const mockHandlers: Record<string, (args: any) => any> = {
|
||||
git_push: () => {
|
||||
return 'Everything up-to-date'
|
||||
},
|
||||
ai_chat: (args: { request: { messages: any[]; model?: string; system?: string } }) => {
|
||||
ai_chat: (args: { request: { messages: { role: string; content: string }[]; model?: string; system?: string } }) => {
|
||||
const lastMsg = args.request.messages[args.request.messages.length - 1]?.content ?? ''
|
||||
const lower = lastMsg.toLowerCase()
|
||||
let content = `I can help you with that. Could you provide more details about what you'd like to know?`
|
||||
@@ -1742,7 +1743,7 @@ export function updateMockContent(path: string, content: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function mockInvoke<T>(cmd: string, args?: any): Promise<T> {
|
||||
export async function mockInvoke<T>(cmd: string, args?: Record<string, unknown>): Promise<T> {
|
||||
// Try the vault API first for commands that read vault data
|
||||
const apiAvailable = await checkVaultApi()
|
||||
if (apiAvailable) {
|
||||
|
||||
@@ -177,8 +177,8 @@ export async function streamChat(
|
||||
|
||||
await readSseStream(reader, onChunk)
|
||||
onDone()
|
||||
} catch (err: any) {
|
||||
onError(err.message || 'Network error')
|
||||
} catch (err: unknown) {
|
||||
onError(err instanceof Error ? err.message : 'Network error')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,21 +8,36 @@ export function preProcessWikilinks(md: string): string {
|
||||
return md.replace(/\[\[([^\]]+)\]\]/g, (_m, target) => `${WL_START}${target}${WL_END}`)
|
||||
}
|
||||
|
||||
// Minimal shape of a BlockNote block for wikilink processing
|
||||
interface BlockLike {
|
||||
content?: InlineItem[]
|
||||
children?: BlockLike[]
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface InlineItem {
|
||||
type: string
|
||||
text?: string
|
||||
props?: Record<string, string>
|
||||
content?: unknown
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
/** Walk blocks and replace placeholder text with wikilink inline content */
|
||||
export function injectWikilinks(blocks: any[]): any[] {
|
||||
return blocks.map(block => {
|
||||
export function injectWikilinks(blocks: unknown[]): unknown[] {
|
||||
return (blocks as BlockLike[]).map(block => {
|
||||
if (block.content && Array.isArray(block.content)) {
|
||||
block.content = expandWikilinksInContent(block.content)
|
||||
}
|
||||
if (block.children && Array.isArray(block.children)) {
|
||||
block.children = injectWikilinks(block.children)
|
||||
block.children = injectWikilinks(block.children) as BlockLike[]
|
||||
}
|
||||
return block
|
||||
})
|
||||
}
|
||||
|
||||
function expandWikilinksInContent(content: any[]): any[] {
|
||||
const result: any[] = []
|
||||
function expandWikilinksInContent(content: InlineItem[]): InlineItem[] {
|
||||
const result: InlineItem[] = []
|
||||
for (const item of content) {
|
||||
if (item.type !== 'text' || typeof item.text !== 'string' || !item.text.includes(WL_START)) {
|
||||
result.push(item)
|
||||
|
||||
@@ -226,7 +226,7 @@ function vaultApiPlugin(): Plugin {
|
||||
|
||||
// --- AI Chat proxy helpers ---
|
||||
|
||||
function readRequestBody(req: any): Promise<string> {
|
||||
function readRequestBody(req: import('http').IncomingMessage): Promise<string> {
|
||||
return new Promise((resolve) => {
|
||||
let body = ''
|
||||
req.on('data', (chunk: Buffer) => { body += chunk.toString() })
|
||||
@@ -235,7 +235,7 @@ function readRequestBody(req: any): Promise<string> {
|
||||
}
|
||||
|
||||
async function forwardToAnthropic(params: {
|
||||
apiKey: string; model?: string; messages: any[]; system?: string; maxTokens?: number
|
||||
apiKey: string; model?: string; messages: { role: string; content: string }[]; system?: string; maxTokens?: number
|
||||
}): Promise<Response> {
|
||||
return fetch('https://api.anthropic.com/v1/messages', {
|
||||
method: 'POST',
|
||||
@@ -254,7 +254,7 @@ async function forwardToAnthropic(params: {
|
||||
})
|
||||
}
|
||||
|
||||
async function streamResponseBody(source: ReadableStream<Uint8Array>, res: any): Promise<void> {
|
||||
async function streamResponseBody(source: ReadableStream<Uint8Array>, res: import('http').ServerResponse): Promise<void> {
|
||||
const reader = source.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let done = false
|
||||
@@ -300,10 +300,10 @@ function aiChatProxyPlugin(): Plugin {
|
||||
} else {
|
||||
res.end()
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
res.statusCode = 500
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
res.end(JSON.stringify({ error: err.message || 'Internal server error' }))
|
||||
res.end(JSON.stringify({ error: err instanceof Error ? err.message : 'Internal server error' }))
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user