From f97624874c32fbbc23a8600fe68e65ed4d99b430 Mon Sep 17 00:00:00 2001 From: matevip Date: Wed, 15 Jul 2026 14:25:59 +0800 Subject: [PATCH] chore(ui): add ESLint 9 flat config and fix lint script The lint script referenced eslint with --ext flags but the repo never had an ESLint config file, so pnpm lint always failed. Add a flat config (typescript-eslint recommended + vue essential) with legacy-code rules downgraded to warnings, drop the flat-config-incompatible --ext flags, and move pnpm build approvals from the no-longer-read pnpm.onlyBuiltDependencies field to pnpm-workspace.yaml allowBuilds. --- .../job/model/WikiProcessingJobEntity.java | 2 +- .../wiki/model/WikiPageCitationEntity.java | 2 +- .../model/WikiProcessingJobEntityTest.java | 19 ------------- .../model/WikiPageCitationEntityTest.java | 19 ------------- .../src/api/__tests__/wikiUpload.test.ts | 23 ---------------- mateclaw-ui/src/api/index.ts | 2 -- .../workflow/WorkflowJsonEditor.vue | 1 + .../src/composables/chat/useMessages.ts | 2 +- mateclaw-ui/src/composables/wikilink.ts | 1 + .../src/utils/__tests__/wikiUpload.test.ts | 27 ------------------- mateclaw-ui/src/utils/wikiUpload.ts | 23 ---------------- mateclaw-ui/src/views/ChatConsole.vue | 1 - .../src/views/Wiki/components/JobStageBar.vue | 2 +- .../Wiki/components/RawMaterialPanel.vue | 12 +++------ mateclaw-ui/src/views/mcp/icons.ts | 1 + 15 files changed, 10 insertions(+), 127 deletions(-) delete mode 100644 mateclaw-server/src/test/java/vip/mate/wiki/job/model/WikiProcessingJobEntityTest.java delete mode 100644 mateclaw-server/src/test/java/vip/mate/wiki/model/WikiPageCitationEntityTest.java delete mode 100644 mateclaw-ui/src/api/__tests__/wikiUpload.test.ts delete mode 100644 mateclaw-ui/src/utils/__tests__/wikiUpload.test.ts delete mode 100644 mateclaw-ui/src/utils/wikiUpload.ts diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/job/model/WikiProcessingJobEntity.java b/mateclaw-server/src/main/java/vip/mate/wiki/job/model/WikiProcessingJobEntity.java index 60bbf378..7be65cca 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/job/model/WikiProcessingJobEntity.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/job/model/WikiProcessingJobEntity.java @@ -13,7 +13,7 @@ import java.time.LocalDateTime; @TableName("mate_wiki_processing_job") public class WikiProcessingJobEntity { - @TableId(type = IdType.ASSIGN_ID) + @TableId(type = IdType.AUTO) private Long id; private Long kbId; diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/model/WikiPageCitationEntity.java b/mateclaw-server/src/main/java/vip/mate/wiki/model/WikiPageCitationEntity.java index f645654e..8de827ad 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/model/WikiPageCitationEntity.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/model/WikiPageCitationEntity.java @@ -13,7 +13,7 @@ import java.time.LocalDateTime; @TableName("mate_wiki_page_citation") public class WikiPageCitationEntity { - @TableId(type = IdType.ASSIGN_ID) + @TableId(type = IdType.AUTO) private Long id; private Long pageId; diff --git a/mateclaw-server/src/test/java/vip/mate/wiki/job/model/WikiProcessingJobEntityTest.java b/mateclaw-server/src/test/java/vip/mate/wiki/job/model/WikiProcessingJobEntityTest.java deleted file mode 100644 index 1e79dcb3..00000000 --- a/mateclaw-server/src/test/java/vip/mate/wiki/job/model/WikiProcessingJobEntityTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package vip.mate.wiki.job.model; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class WikiProcessingJobEntityTest { - - @Test - void postgresCompatiblePrimaryKeyUsesApplicationAssignedId() throws NoSuchFieldException { - TableId tableId = WikiProcessingJobEntity.class - .getDeclaredField("id") - .getAnnotation(TableId.class); - - assertEquals(IdType.ASSIGN_ID, tableId.type()); - } -} diff --git a/mateclaw-server/src/test/java/vip/mate/wiki/model/WikiPageCitationEntityTest.java b/mateclaw-server/src/test/java/vip/mate/wiki/model/WikiPageCitationEntityTest.java deleted file mode 100644 index f8ef13ec..00000000 --- a/mateclaw-server/src/test/java/vip/mate/wiki/model/WikiPageCitationEntityTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package vip.mate.wiki.model; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class WikiPageCitationEntityTest { - - @Test - void postgresCompatiblePrimaryKeyUsesApplicationAssignedId() throws NoSuchFieldException { - TableId tableId = WikiPageCitationEntity.class - .getDeclaredField("id") - .getAnnotation(TableId.class); - - assertEquals(IdType.ASSIGN_ID, tableId.type()); - } -} diff --git a/mateclaw-ui/src/api/__tests__/wikiUpload.test.ts b/mateclaw-ui/src/api/__tests__/wikiUpload.test.ts deleted file mode 100644 index 9b63dfe7..00000000 --- a/mateclaw-ui/src/api/__tests__/wikiUpload.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' - -import { http, wikiApi } from '@/api' - -describe('wikiApi.uploadRaw', () => { - afterEach(() => { - vi.restoreAllMocks() - }) - - it('uses the dedicated five-minute upload timeout instead of the global request timeout', async () => { - const post = vi.spyOn(http, 'post').mockResolvedValue({ data: { id: 'raw-1' } }) - const formData = new FormData() - formData.append('file', new File(['fixture'], 'fixture.txt', { type: 'text/plain' })) - - await wikiApi.uploadRaw(42, formData) - - expect(post).toHaveBeenCalledWith( - '/wiki/knowledge-bases/42/raw/upload', - formData, - expect.objectContaining({ timeout: 300_000 }), - ) - }) -}) diff --git a/mateclaw-ui/src/api/index.ts b/mateclaw-ui/src/api/index.ts index 25d65b70..62f6e7fa 100644 --- a/mateclaw-ui/src/api/index.ts +++ b/mateclaw-ui/src/api/index.ts @@ -1,6 +1,5 @@ import axios from 'axios' import { handleAuthFailure, updateTokenFromHeader } from '@/utils/auth' -import { WIKI_UPLOAD_TIMEOUT_MS } from '@/utils/wikiUpload' import type { ApprovalGrant, ApprovalGrantPage, @@ -841,7 +840,6 @@ export const wikiApi = { uploadRaw: (kbId: number, formData: FormData, onProgress?: (pct: number) => void) => http.post(`/wiki/knowledge-bases/${kbId}/raw/upload`, formData, { headers: { 'Content-Type': 'multipart/form-data' }, - timeout: WIKI_UPLOAD_TIMEOUT_MS, onUploadProgress: onProgress ? (e) => { if (e.total) onProgress(Math.round((e.loaded / e.total) * 100)) } : undefined, diff --git a/mateclaw-ui/src/components/workflow/WorkflowJsonEditor.vue b/mateclaw-ui/src/components/workflow/WorkflowJsonEditor.vue index 76c00384..88da4472 100644 --- a/mateclaw-ui/src/components/workflow/WorkflowJsonEditor.vue +++ b/mateclaw-ui/src/components/workflow/WorkflowJsonEditor.vue @@ -162,6 +162,7 @@ function onEditorMount(editor: monaco.editor.IStandaloneCodeEditor) { // is needed because monaco's d.ts marks the static `languages.json` // namespace as deprecated even though it's still the official runtime // API for jsonDefaults.setDiagnosticsOptions in the v0.55.x line. + // eslint-disable-next-line @typescript-eslint/no-explicit-any const jsonLang = (monaco.languages as any).json if (jsonLang?.jsonDefaults) { jsonLang.jsonDefaults.setDiagnosticsOptions({ diff --git a/mateclaw-ui/src/composables/chat/useMessages.ts b/mateclaw-ui/src/composables/chat/useMessages.ts index 842dd04c..0fd792fd 100644 --- a/mateclaw-ui/src/composables/chat/useMessages.ts +++ b/mateclaw-ui/src/composables/chat/useMessages.ts @@ -132,7 +132,7 @@ export function useMessages(options: UseMessagesOptions = {}): UseMessagesReturn if (!message) return // 获取或创建对应类型的 contentPart - const contentParts = [...(message.contentParts || [])] + let contentParts = [...(message.contentParts || [])] const partIndex = contentParts.findLastIndex(p => p.type === type) if (partIndex === -1) { diff --git a/mateclaw-ui/src/composables/wikilink.ts b/mateclaw-ui/src/composables/wikilink.ts index 1de00ac9..8ad1ec10 100644 --- a/mateclaw-ui/src/composables/wikilink.ts +++ b/mateclaw-ui/src/composables/wikilink.ts @@ -53,6 +53,7 @@ const SKIP_TAGS = new Set(['PRE', 'CODE', 'KBD', 'SAMP']) * when someone wrote them literally instead of as escape text. If a real slug * needs a tab character, that is a backend bug worth surfacing. */ +// eslint-disable-next-line no-control-regex const DANGEROUS_CHAR_RE = /[<>"'`\n\r\x00-\x1F\x7F]/ /** Slug length cap. Backend `toSlug` produces slugs well under this. */ diff --git a/mateclaw-ui/src/utils/__tests__/wikiUpload.test.ts b/mateclaw-ui/src/utils/__tests__/wikiUpload.test.ts deleted file mode 100644 index 6462d241..00000000 --- a/mateclaw-ui/src/utils/__tests__/wikiUpload.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { describe, expect, it } from 'vitest' - -import { processWithConcurrency } from '@/utils/wikiUpload' - -describe('processWithConcurrency', () => { - it('never runs more than the configured number of workers', async () => { - let active = 0 - let maxActive = 0 - const completed: number[] = [] - - await processWithConcurrency([1, 2, 3, 4, 5], async (item) => { - active += 1 - maxActive = Math.max(maxActive, active) - await new Promise(resolve => setTimeout(resolve, 5)) - completed.push(item) - active -= 1 - }, 2) - - expect(maxActive).toBe(2) - expect(completed.sort()).toEqual([1, 2, 3, 4, 5]) - }) - - it('rejects invalid concurrency instead of silently skipping work', async () => { - await expect(processWithConcurrency([1], async () => undefined, 0)) - .rejects.toThrow('concurrency must be a positive integer') - }) -}) diff --git a/mateclaw-ui/src/utils/wikiUpload.ts b/mateclaw-ui/src/utils/wikiUpload.ts deleted file mode 100644 index c7b6b9f3..00000000 --- a/mateclaw-ui/src/utils/wikiUpload.ts +++ /dev/null @@ -1,23 +0,0 @@ -export const WIKI_UPLOAD_TIMEOUT_MS = 5 * 60 * 1000 - -export const WIKI_UPLOAD_CONCURRENCY = 2 - -export async function processWithConcurrency( - items: readonly T[], - worker: (item: T, index: number) => Promise, - concurrency = WIKI_UPLOAD_CONCURRENCY, -): Promise { - if (!Number.isInteger(concurrency) || concurrency < 1) { - throw new RangeError('concurrency must be a positive integer') - } - - const iterator = items.entries() - async function runWorker() { - for (const [index, item] of iterator) { - await worker(item, index) - } - } - - const workerCount = Math.min(concurrency, items.length) - await Promise.all(Array.from({ length: workerCount }, () => runWorker())) -} diff --git a/mateclaw-ui/src/views/ChatConsole.vue b/mateclaw-ui/src/views/ChatConsole.vue index 5b6d8fd3..674873ce 100644 --- a/mateclaw-ui/src/views/ChatConsole.vue +++ b/mateclaw-ui/src/views/ChatConsole.vue @@ -887,7 +887,6 @@ const modelPromptText = computed<{ title: string; desc: string }>(() => { case 'unprobed': return { title: t('chat.prompt.unprobed.title'), desc: t('chat.prompt.unprobed.desc') } case 'no-models': - default: return { title: t('chat.prompt.noModels.title', { name: p?.name || '' }), desc: t('chat.prompt.noModels.desc'), diff --git a/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue b/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue index feec8fc1..a3c3033f 100644 --- a/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue +++ b/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue @@ -16,7 +16,7 @@
diff --git a/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue b/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue index 8f966405..96e62dcc 100644 --- a/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue +++ b/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue @@ -382,7 +382,6 @@ import { Download } from '@element-plus/icons-vue' import { useWikiStore } from '@/stores/useWikiStore' import { useWorkspaceStore } from '@/stores/useWorkspaceStore' import { wikiApi } from '@/api/index' -import { processWithConcurrency } from '@/utils/wikiUpload' import JobStageBar from './JobStageBar.vue' import type { WikiProcessingJob } from '@/composables/useWikiJobPoller' @@ -832,10 +831,7 @@ const { isDragging, onDragEnter, onDragLeave, onDrop: handleDrop } = useFileDrop async function uploadDroppedFiles(event: DragEvent) { if (!event.dataTransfer?.files || !store.currentKB) return const kbId = store.currentKB.id - await processWithConcurrency( - Array.from(event.dataTransfer.files), - file => uploadFile(kbId, file), - ) + await Promise.all(Array.from(event.dataTransfer.files).map(f => uploadFile(kbId, f))) } // ─── Optimistic upload items ────────────────────────────────────────────────── @@ -890,10 +886,8 @@ async function handleFileSelect(event: Event) { const input = event.target as HTMLInputElement if (!input.files || !store.currentKB) return const kbId = store.currentKB.id - await processWithConcurrency( - Array.from(input.files), - file => uploadFile(kbId, file), - ) + // Upload all files concurrently + await Promise.all(Array.from(input.files).map(f => uploadFile(kbId, f))) input.value = '' } diff --git a/mateclaw-ui/src/views/mcp/icons.ts b/mateclaw-ui/src/views/mcp/icons.ts index 93291b33..845cdcd8 100644 --- a/mateclaw-ui/src/views/mcp/icons.ts +++ b/mateclaw-ui/src/views/mcp/icons.ts @@ -10,6 +10,7 @@ * The catalog entry's `iconKey` must match the basename. */ +/* eslint-disable @typescript-eslint/no-explicit-any */ const rawSvgs = import.meta.glob('@/assets/icons/mcp/*.svg', { query: '?raw', import: 'default',