From f7e13c0fc7e8c5cc207791535af5c286e73cad1c Mon Sep 17 00:00:00 2001 From: Kirill Mokevnin Date: Sun, 28 Jun 2026 00:14:15 +0300 Subject: [PATCH] =?UTF-8?q?refactor:=20=D1=83=D0=BF=D1=80=D0=BE=D1=89?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20MCP-=D1=81=D0=B5=D1=80=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=20=D0=BD=D0=B0=20docusaurus-plugin-mcp-server=201.?= =?UTF-8?q?0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Обновление плагина до 1.0 закрыло три апстрим-PR, ради которых держались хаки. Все три убраны: - PR #77: плагин теперь сканирует path.html, а не только index.html — убран shimHtmlForMcp/wrappedMcpPlugin (build нашёл все 106 роутов). - PR #78: появилась опция flexsearch в плагине и встроенный провайдер больше не грузится через dynamic import() — убран кастомный индексер и монки-патч McpDocsServer.prototype._doInitialize в воркере. Каталог mcp-providers/ удалён целиком, его заменяет общий flexsearch.config.ts (один конфиг для build и runtime — библиотека требует их совпадения). Индекс остался компактным (2.0 МБ). Co-Authored-By: Claude Opus 4.8 --- docusaurus.config.ts | 74 +------ flexsearch.config.ts | 27 +++ mcp-providers/lean-flexsearch.ts | 161 -------------- mcp-providers/lean-indexer.ts | 39 ---- mcp-providers/lean-provider.ts | 60 ----- package.json | 12 +- pnpm-lock.yaml | 369 ++++++++++++++++--------------- pnpm-workspace.yaml | 4 + worker/index.ts | 29 +-- 9 files changed, 236 insertions(+), 539 deletions(-) create mode 100644 flexsearch.config.ts delete mode 100644 mcp-providers/lean-flexsearch.ts delete mode 100644 mcp-providers/lean-indexer.ts delete mode 100644 mcp-providers/lean-provider.ts diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 6db9b09..f326490 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -1,76 +1,10 @@ -import path from 'path'; -import { promises as fs } from 'fs'; -import { pathToFileURL } from 'url'; import { themes as prismThemes } from 'prism-react-renderer'; -import mcpServerPlugin from 'docusaurus-plugin-mcp-server'; -import type { Config, Plugin, LoadContext } from '@docusaurus/types'; +import type { Config } from '@docusaurus/types'; import type * as Preset from '@docusaurus/preset-classic'; - -// Absolute file:// URL works with the plugin's dynamic import() loader, -// which resolves relative paths against its own dist directory. -// -// Once https://github.com/scalvert/docusaurus-plugin-mcp-server/pull/78 lands, -// drop `mcp-providers/` and replace with `flexsearch: { tokenize: 'strict', -// context: false, ... }` directly in plugin options below. -const leanIndexerSpecifier = pathToFileURL( - path.join(__dirname, 'mcp-providers/lean-indexer.ts'), -).href; +import flexsearchConfig from './flexsearch.config'; // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) -// trailingSlash: false → Docusaurus emits pages as `path.html`, but -// docusaurus-plugin-mcp-server only scans `index.html`. Docusaurus runs all -// plugin postBuild hooks via Promise.all, so a separate shim plugin would -// race the MCP plugin. We wrap the MCP plugin instead and run the shim -// sequentially inside the same postBuild. -// -// Remove `shimHtmlForMcp` and `wrappedMcpPlugin` once this lands upstream: -// https://github.com/scalvert/docusaurus-plugin-mcp-server/pull/77 -async function shimHtmlForMcp(outDir: string) { - const skip = new Set(['assets', 'img', 'static', 'mcp']); - async function walk(dir: string) { - const entries = await fs.readdir(dir, { withFileTypes: true }); - for (const entry of entries) { - if (entry.isDirectory()) { - if (skip.has(entry.name)) continue; - await walk(path.join(dir, entry.name)); - } else if ( - entry.isFile() && - entry.name.endsWith('.html') && - entry.name !== 'index.html' && - entry.name !== '404.html' - ) { - const base = entry.name.slice(0, -'.html'.length); - const targetDir = path.join(dir, base); - const targetFile = path.join(targetDir, 'index.html'); - try { - await fs.access(targetFile); - continue; - } catch {} - await fs.mkdir(targetDir, { recursive: true }); - await fs.copyFile(path.join(dir, entry.name), targetFile); - } - } - } - await walk(outDir); -} - -async function wrappedMcpPlugin(ctx: LoadContext, opts: unknown): Promise { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const inner = await (mcpServerPlugin as any)(ctx, opts); - const originalPostBuild = inner.postBuild; - return { - ...inner, - name: inner.name ?? 'docusaurus-plugin-mcp-server', - async postBuild(args: { outDir: string; [k: string]: unknown }) { - await shimHtmlForMcp(args.outDir); - if (originalPostBuild) { - await originalPostBuild.call(inner, args); - } - }, - }; -} - const config: Config = { title: 'Справочный центр Хекслета', tagline: 'Ответы на частые вопросы и инструкции для студентов', @@ -78,13 +12,13 @@ const config: Config = { plugins: [ [ - wrappedMcpPlugin, + require.resolve('docusaurus-plugin-mcp-server'), { server: { name: 'hexlet-help', version: '1.0.0', }, - indexers: [leanIndexerSpecifier], + flexsearch: flexsearchConfig, }, ], [ diff --git a/flexsearch.config.ts b/flexsearch.config.ts new file mode 100644 index 0000000..e0d44be --- /dev/null +++ b/flexsearch.config.ts @@ -0,0 +1,27 @@ +import type { FlexSearchConfig } from 'docusaurus-plugin-mcp-server'; + +// FlexSearch tuned for Russian content. The plugin's defaults are tuned for +// English (forward tokenize + bidirectional context + English stemmer) and +// produce an 80+ MB index on our docs. +// +// - tokenize: 'strict' indexes whole words only (no prefix explosion). +// - context: false drops the bidirectional context that bloats the index. +// - resolution: 3 is enough for relevance ranking on ~100 docs. +// - encode: lowercase split on Russian punctuation, no English stemmer. +// +// This config MUST be identical at build time (docusaurus.config.ts) and at +// runtime (worker/index.ts) — otherwise the runtime provider deserializes the +// index with the wrong shape and returns no results. +const flexsearchConfig: FlexSearchConfig = { + tokenize: 'strict', + resolution: 3, + context: false, + cache: 100, + encode: (str: string) => + String(str) + .toLowerCase() + .split(/[\s\-_.,;:!?'"()[\]{}«»—–]+/) + .filter(Boolean), +}; + +export default flexsearchConfig; diff --git a/mcp-providers/lean-flexsearch.ts b/mcp-providers/lean-flexsearch.ts deleted file mode 100644 index a5e2738..0000000 --- a/mcp-providers/lean-flexsearch.ts +++ /dev/null @@ -1,161 +0,0 @@ -// The whole `mcp-providers/` directory exists because -// docusaurus-plugin-mcp-server hardcodes a FlexSearch config that's tuned -// for English (forward tokenize + bidirectional context) and produces an -// 80+ MB index on Russian content. -// -// Drop this directory once the upstream PR lands and the released version -// exposes a `flexsearch` option: -// https://github.com/scalvert/docusaurus-plugin-mcp-server/pull/78 -import FlexSearch from 'flexsearch'; -import type { ProcessedDoc, SearchResult } from 'docusaurus-plugin-mcp-server'; - -const FIELD_WEIGHTS: Record = { - title: 3, - headings: 2, - description: 1.5, - content: 1, -}; - -// Lean FlexSearch config for Russian text: -// - tokenize: "strict" indexes whole words only (no prefix explosion). -// - no `context` (the default plugin uses bidirectional context depth 2, -// which is the main source of the 80+ MB index bloat). -// - low `resolution` is enough for relevance ranking on ~100 docs. -// - lowercase-only encode; no English stemmer. -export function createLeanIndex() { - return new (FlexSearch as unknown as { Document: new (opts: unknown) => LeanIndex }).Document({ - tokenize: 'strict', - cache: 100, - resolution: 3, - encode: (str: string) => - String(str) - .toLowerCase() - .split(/[\s\-_.,;:!?'"()[\]{}«»—–]+/) - .filter(Boolean), - document: { - id: 'id', - index: ['title', 'content', 'headings', 'description'], - store: ['title', 'description'], - }, - }); -} - -interface LeanIndex { - add(doc: Record): void; - search(query: string, opts: { limit: number; enrich: boolean }): FlexFieldResult[]; - export(fn: (key: string, value: unknown) => void): Promise; - import(key: string, value: unknown): Promise; -} - -interface FlexFieldResult { - field: string; - result: Array; -} - -export function addDocumentToIndex(index: LeanIndex, doc: ProcessedDoc, baseUrl: string): void { - const id = baseUrl ? `${baseUrl.replace(/\/$/, '')}${doc.route}` : doc.route; - index.add({ - id, - title: doc.title, - content: doc.markdown, - headings: doc.headings.map((h) => h.text).join(' '), - description: doc.description, - }); -} - -export function buildLeanIndex(docs: ProcessedDoc[], baseUrl: string): LeanIndex { - const index = createLeanIndex(); - for (const doc of docs) { - addDocumentToIndex(index, doc, baseUrl); - } - return index; -} - -export async function exportIndex(index: LeanIndex): Promise> { - const data: Record = {}; - await index.export((key, value) => { - data[key] = value; - }); - return data; -} - -export async function importIndex(data: Record): Promise { - const index = createLeanIndex(); - for (const [key, value] of Object.entries(data)) { - await index.import(key, value); - } - return index; -} - -function escapeRegExp(str: string): string { - return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); -} - -function generateSnippet(markdown: string, query: string): string { - const maxLength = 200; - if (!markdown) return ''; - const normalized = markdown.replace(/\s+/g, ' ').trim(); - const lower = normalized.toLowerCase(); - const term = query.split(/\s+/).find(Boolean)?.toLowerCase() ?? ''; - const idx = term ? lower.indexOf(term) : -1; - if (idx === -1) { - return normalized.slice(0, maxLength) + (normalized.length > maxLength ? '...' : ''); - } - const start = Math.max(0, idx - 60); - const end = Math.min(normalized.length, idx + 140); - return ( - (start > 0 ? '...' : '') + - normalized.slice(start, end) + - (end < normalized.length ? '...' : '') - ); -} - -function findMatchingHeadings(doc: ProcessedDoc, query: string): string[] { - // The plugin's tool template renders matchingHeadings via `.join(", ")`, - // so we return plain strings to avoid "[object Object]" output. - if (!doc.headings || !query) return []; - const re = new RegExp(escapeRegExp(query), 'i'); - return doc.headings - .filter((h) => re.test(h.text)) - .slice(0, 3) - .map((h) => h.text); -} - -export function queryIndex( - index: LeanIndex, - docs: Record, - query: string, - options: { limit?: number } = {}, -): SearchResult[] { - const { limit = 16 } = options; - const rawResults = index.search(query, { limit: limit * 3, enrich: true }); - const docScores = new Map(); - for (const fieldResult of rawResults) { - const weight = FIELD_WEIGHTS[fieldResult.field] ?? 1; - const results = fieldResult.result; - for (let i = 0; i < results.length; i += 1) { - const item = results[i]; - if (!item) continue; - const docId = typeof item === 'string' ? item : item.id; - const positionScore = (results.length - i) / results.length; - docScores.set(docId, (docScores.get(docId) ?? 0) + positionScore * weight); - } - } - const results: SearchResult[] = []; - for (const [docId, score] of docScores) { - const doc = docs[docId]; - if (!doc) continue; - results.push({ - url: docId, - route: doc.route, - title: doc.title, - score, - snippet: generateSnippet(doc.markdown, query), - // SearchResult.matchingHeadings is typed as DocHeading[]; we return - // strings on purpose (see findMatchingHeadings above). - matchingHeadings: findMatchingHeadings(doc, query) as unknown as SearchResult['matchingHeadings'], - }); - } - results.sort((a, b) => b.score - a.score); - return results.slice(0, limit); -} diff --git a/mcp-providers/lean-indexer.ts b/mcp-providers/lean-indexer.ts deleted file mode 100644 index c11b01d..0000000 --- a/mcp-providers/lean-indexer.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { buildLeanIndex, exportIndex } from './lean-flexsearch.ts'; -import type { ContentIndexer, ProcessedDoc, ProviderContext } from 'docusaurus-plugin-mcp-server'; - -export default class LeanFlexSearchIndexer implements ContentIndexer { - readonly name = 'lean-flexsearch'; - private baseUrl = ''; - private docsIndex: Record = {}; - private exportedIndex: Record | null = null; - private docCount = 0; - - async initialize(context: ProviderContext): Promise { - this.baseUrl = context.baseUrl ?? ''; - this.docsIndex = {}; - this.exportedIndex = null; - this.docCount = 0; - } - - async indexDocuments(docs: ProcessedDoc[]): Promise { - this.docCount = docs.length; - for (const doc of docs) { - this.docsIndex[`${this.baseUrl}${doc.route}`] = doc; - } - console.log('[LeanFlexSearch] Building lean search index...'); - const index = buildLeanIndex(docs, this.baseUrl); - this.exportedIndex = await exportIndex(index); - console.log(`[LeanFlexSearch] Indexed ${this.docCount} documents`); - } - - async finalize(): Promise> { - const artifacts = new Map(); - artifacts.set('docs.json', this.docsIndex); - artifacts.set('search-index.json', this.exportedIndex); - return artifacts; - } - - async getManifestData(): Promise> { - return { searchEngine: 'lean-flexsearch' }; - } -} diff --git a/mcp-providers/lean-provider.ts b/mcp-providers/lean-provider.ts deleted file mode 100644 index 740a547..0000000 --- a/mcp-providers/lean-provider.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { importIndex, queryIndex } from './lean-flexsearch.ts'; -import type { - ProcessedDoc, - ProviderContext, - SearchOptions, - SearchProvider, - SearchProviderInitData, - SearchResult, -} from 'docusaurus-plugin-mcp-server'; - -type LeanIndex = Awaited>; - -export default class LeanFlexSearchProvider implements SearchProvider { - readonly name = 'lean-flexsearch'; - private docs: Record | null = null; - private searchIndex: LeanIndex | null = null; - private ready = false; - - async initialize(_context: ProviderContext, initData?: SearchProviderInitData): Promise { - if (!initData?.docs || !initData?.indexData) { - throw new Error('[LeanFlexSearch] Provider requires pre-loaded docs and indexData'); - } - this.docs = initData.docs; - this.searchIndex = await importIndex(initData.indexData); - this.ready = true; - } - - isReady(): boolean { - return this.ready && this.docs !== null && this.searchIndex !== null; - } - - async search(query: string, options?: SearchOptions): Promise { - if (!this.isReady() || !this.docs || !this.searchIndex) { - throw new Error('[LeanFlexSearch] Not initialized'); - } - return queryIndex(this.searchIndex, this.docs, query, { limit: options?.limit ?? 16 }); - } - - async getDocument(url: string): Promise { - if (!this.docs) throw new Error('[LeanFlexSearch] Not initialized'); - return this.docs[url] ?? null; - } - - async healthCheck(): Promise<{ healthy: boolean; message: string }> { - if (!this.isReady()) return { healthy: false, message: 'not initialized' }; - return { healthy: true, message: `ready with ${this.getDocCount()} documents` }; - } - - getDocCount(): number { - return this.docs ? Object.keys(this.docs).length : 0; - } - - getDocs(): Record | null { - return this.docs; - } - - getSearchIndex(): LeanIndex | null { - return this.searchIndex; - } -} diff --git a/package.json b/package.json index 8b31457..0048dc2 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,9 @@ "@mdx-js/react": "^3.1.1", "@orama/plugin-docusaurus-v3": "^3.1.18", "clsx": "^2.1.1", - "docusaurus-plugin-mcp-server": "^0.12.0", - "flexsearch": "0.7.43", - "hono": "^4.12.25", + "docusaurus-plugin-mcp-server": "^1.0.0", + "flexsearch": "0.8.212", + "hono": "^4.12.27", "prism-react-renderer": "^2.4.1", "react": "^19.2.7", "react-dom": "^19.2.7" @@ -34,9 +34,9 @@ "@docusaurus/module-type-aliases": "3.10.1", "@docusaurus/tsconfig": "3.10.1", "@docusaurus/types": "3.10.1", - "npm-check-updates": "^22.2.3", + "npm-check-updates": "^22.2.8", "typescript": "~6.0.3", - "wrangler": "^4.100.0" + "wrangler": "^4.105.0" }, "browserslist": { "production": [ @@ -53,5 +53,5 @@ "engines": { "node": ">=25.0" }, - "packageManager": "pnpm@11.7.0" + "packageManager": "pnpm@11.9.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38e7daf..f5fcc0d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,14 +33,14 @@ importers: specifier: ^2.1.1 version: 2.1.1 docusaurus-plugin-mcp-server: - specifier: ^0.12.0 - version: 0.12.0(@docusaurus/core@3.10.1(@docusaurus/faster@3.10.1(@docusaurus/types@3.10.1(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.7))(@rspack/core@1.7.11)(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(@docusaurus/types@3.10.1(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@3.25.76) + specifier: ^1.0.0 + version: 1.0.0(@docusaurus/core@3.10.1(@docusaurus/faster@3.10.1(@docusaurus/types@3.10.1(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.7))(@rspack/core@1.7.11)(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(@docusaurus/types@3.10.1(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@3.25.76) flexsearch: - specifier: 0.7.43 - version: 0.7.43 + specifier: 0.8.212 + version: 0.8.212 hono: - specifier: ^4.12.25 - version: 4.12.25 + specifier: ^4.12.27 + version: 4.12.27 prism-react-renderer: specifier: ^2.4.1 version: 2.4.1(react@19.2.7) @@ -61,14 +61,14 @@ importers: specifier: 3.10.1 version: 3.10.1(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) npm-check-updates: - specifier: ^22.2.3 - version: 22.2.3 + specifier: ^22.2.8 + version: 22.2.8 typescript: specifier: ~6.0.3 version: 6.0.3 wrangler: - specifier: ^4.100.0 - version: 4.100.0 + specifier: ^4.105.0 + version: 4.105.0 packages: @@ -810,32 +810,32 @@ packages: workerd: optional: true - '@cloudflare/workerd-darwin-64@1.20260611.1': - resolution: {integrity: sha512-iJICldmi4sBGgi7IrQles8cStOGXM/Tmv95C4OODVs6VIbMsJPqThUM5h3uYVQNULuJ8I/aVvnJ3Eh/wZCKwuA==} + '@cloudflare/workerd-darwin-64@1.20260625.1': + resolution: {integrity: sha512-naCfBv0WnnTQIQPTniqMoUlklOIFjrAcSn1X+IAOhY8aFLF/xGYtFjs1eEE8sFib3ZuChGGpU23FFORVczqr0A==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260611.1': - resolution: {integrity: sha512-yBbVXvbZyltR3I7NJdC4C4ItkItjZSiabcA/3HzEWOUQjLVKFqRh4so6ToHr70VCYh8VGeR8EDZL23igLhXqFQ==} + '@cloudflare/workerd-darwin-arm64@1.20260625.1': + resolution: {integrity: sha512-jmH6zjp6Wrux46+qtFwDwrj+vd7s5bdwEqeGvdnwE0a4IEeAhKs0L42HQOyID+g5lkrHq9m55+AbhtmRAm63Pw==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260611.1': - resolution: {integrity: sha512-PfNjpxOlaIgZFYuhD7+neEEewCN2Ud993wEEN0fmbtSOax1AK53LGqmXUDvFhnbkHxJLFAxYCSNISW8QbzaAIg==} + '@cloudflare/workerd-linux-64@1.20260625.1': + resolution: {integrity: sha512-MiQkpA/dX8d83Zp64pzHUKfd6ca4cvwxnNobSP6CnXvfESvnNI9pfa+nfwnParla36sPmnYntNkjR7NjRuDeKQ==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260611.1': - resolution: {integrity: sha512-GEp4XbuIKjlF8pakqXcUDJfKiJosD/Q7S83J0d+r+z9XIlYGfF3ntm08e2aiF5TFTwp3fnG4yMoPUAKNhNJpvQ==} + '@cloudflare/workerd-linux-arm64@1.20260625.1': + resolution: {integrity: sha512-LxxW7Qv60Xvv37+w6gUSDpYZziyqMy+cZWd9IvSA5ehVgKAxmzEaYPMiSZlxk32nbIWL9u/tfjXYCOKJ4Lo+XQ==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260611.1': - resolution: {integrity: sha512-S6JkS0kEbcCKs19RGqEPhjCRbP8GBkQwqYLp2fhBJtD/KTlwqLzOJ9E6PQ7gQKgWHtxy1NBG3oXarlNFRNU/dw==} + '@cloudflare/workerd-windows-64@1.20260625.1': + resolution: {integrity: sha512-LH6iIX1HHaTwVKV5VokDxxUErXJzQoNZFRwVm7Vx/3fB/ApcTcRCUaMqcxI4as94jEUqg+pmX5czOndiveohow==} engines: {node: '>=16'} cpu: [x64] os: [win32] @@ -1417,158 +1417,158 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1582,8 +1582,8 @@ packages: '@noble/hashes': optional: true - '@gleanwork/mcp-config-schema@4.3.0': - resolution: {integrity: sha512-qajPyFZoNYVhFJ0IBgSQDRaLFPL5ysgTjdzCJYx7SG1lygHHz220Fj5lC7wP2/ntJiCmiQv8jl7zAS5kOnDNgg==} + '@gleanwork/mcp-config-schema@5.2.0': + resolution: {integrity: sha512-2ldjVVC+ZcgSPXcwahqqZralI0JgMNeHas234psL/KytK74CLXX0OKuLEdI4SUdcHLnnQQSgShvQqy2UYb+lPA==} engines: {node: '>=18.0.0'} peerDependencies: zod: ^3.25.76 @@ -3551,8 +3551,8 @@ packages: resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} engines: {node: '>=6'} - docusaurus-plugin-mcp-server@0.12.0: - resolution: {integrity: sha512-8qAomIqUNQtFK9HjFZTzeX1QBvbTesSWGILlgAclxMaur2QFtpf83NY/568a8DxH03k2ZTW4dXb97YYGMxhkZg==} + docusaurus-plugin-mcp-server@1.0.0: + resolution: {integrity: sha512-YXx/X2JwIntMjZBYALhn+hVHysUwMSev6gAHYzQKGUF9FEwV7UttViKZJV3Bi9QclR/AJ9ICBUUz1mcAKhCV2w==} engines: {node: '>=20.0.0'} hasBin: true peerDependencies: @@ -3693,8 +3693,8 @@ packages: esast-util-from-js@2.0.1: resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} - esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} engines: {node: '>=18'} hasBin: true @@ -3878,6 +3878,9 @@ packages: flexsearch@0.7.43: resolution: {integrity: sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg==} + flexsearch@0.8.212: + resolution: {integrity: sha512-wSyJr1GUWoOOIISRu+X2IXiOcVfg9qqBRyCPRUdLMIGJqPzMo+jMRlvE83t14v1j0dRMEaBbER/adQjp6Du2pw==} + follow-redirects@1.15.11: resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} engines: {node: '>=4.0'} @@ -4163,8 +4166,12 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - hono@4.12.25: - resolution: {integrity: sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ==} + hono@4.12.26: + resolution: {integrity: sha512-uyZtpnYxM9CmQ7QsQknM4zN8EftNqhON1qYeIKM0Se67CCEe2c44xyGURwB0axX2fBDu1dqHrHAc1hmNT8ITkw==} + engines: {node: '>=16.9.0'} + + hono@4.12.27: + resolution: {integrity: sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==} engines: {node: '>=16.9.0'} hpack.js@2.1.6: @@ -5111,8 +5118,8 @@ packages: peerDependencies: webpack: ^5.0.0 - miniflare@4.20260611.0: - resolution: {integrity: sha512-i+JwEo8vN96naz1WL3ntFgFyRluBDYL408zwhHKvR2jefJ464KsZ/gCmJAQ5k+oaWeb5Ug+s7yne5AyiAEswjg==} + miniflare@4.20260625.0: + resolution: {integrity: sha512-3kKXwRUObJsnBYPBgR0NiNZYKF/yv8GFyha1cx2EeAEraxNODgRVcyeRo+F1ok1tg5Mg7iUpOWSkknQTHuFhwA==} engines: {node: '>=22.0.0'} hasBin: true @@ -5205,8 +5212,8 @@ packages: not@0.1.0: resolution: {integrity: sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA==} - npm-check-updates@22.2.3: - resolution: {integrity: sha512-n8HZ0ywZugYRgEqIm67hbZC5L/9S/gLX+MkGchJB43pP2xib1+u76OpVEJRZ4QP2BksPRi5M1CCStJVfc16+/A==} + npm-check-updates@22.2.8: + resolution: {integrity: sha512-jL2cs1TSDSj3ieDlMVFf0bZFVqLNtXt+ExvCMF1OYxR7PiVjFcmAP80rJFNOFrrt0KLyPqeRBZtD7xZu9emd8w==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: '>=10.0.0'} hasBin: true @@ -6660,8 +6667,8 @@ packages: resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} engines: {node: '>=20.18.1'} - undici@7.24.8: - resolution: {integrity: sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ==} + undici@7.28.0: + resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} engines: {node: '>=20.18.1'} unenv@2.0.0-rc.24: @@ -6935,17 +6942,17 @@ packages: wildcard@2.0.1: resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} - workerd@1.20260611.1: - resolution: {integrity: sha512-CS/640T7pIJ2HYX6x2DwKFGbcSckAWN3tgcdq+ptB6SaqjWUhlzIgA/YhPuwIU+/NnMnGpqOFX/hC18Oyge63w==} + workerd@1.20260625.1: + resolution: {integrity: sha512-GApQvFX52SDM6L4u0+RRnUDB1wJOnEwoXjinkmOPtIyofWBxrlZckdegJSYc1leg++lLZ3+DQ4zMVmBqYVtzfA==} engines: {node: '>=16'} hasBin: true - wrangler@4.100.0: - resolution: {integrity: sha512-dSQO7DO+mD6XDzkVWIWBoGLO3yw+lacWSc/KhFvd7pgfpth+kX98qb5SGRHZN8ACCDhhfwzDLXwB6qHsIHhfBg==} + wrangler@4.105.0: + resolution: {integrity: sha512-7dXFH6OLj1Fv0y6ZeRPUxFTkp+duWD7/xxVi/1c0vfOeEYwIFKWB7cdqnY05DvY1Ta3BnqAwRkXfLs8PDj538g==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^4.20260611.1 + '@cloudflare/workers-types': ^4.20260625.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -6984,8 +6991,8 @@ packages: utf-8-validate: optional: true - ws@8.20.1: - resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -8065,25 +8072,25 @@ snapshots: '@cloudflare/kv-asset-handler@0.5.0': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260611.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260625.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260611.1 + workerd: 1.20260625.1 - '@cloudflare/workerd-darwin-64@1.20260611.1': + '@cloudflare/workerd-darwin-64@1.20260625.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260611.1': + '@cloudflare/workerd-darwin-arm64@1.20260625.1': optional: true - '@cloudflare/workerd-linux-64@1.20260611.1': + '@cloudflare/workerd-linux-64@1.20260625.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260611.1': + '@cloudflare/workerd-linux-arm64@1.20260625.1': optional: true - '@cloudflare/workerd-windows-64@1.20260611.1': + '@cloudflare/workerd-windows-64@1.20260625.1': optional: true '@cmfcmf/docusaurus-search-local@2.0.1(@docusaurus/core@3.10.1(@docusaurus/faster@3.10.1(@docusaurus/types@3.10.1(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.7))(@rspack/core@1.7.11)(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(search-insights@2.17.3)': @@ -9255,89 +9262,89 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.27.3': + '@esbuild/aix-ppc64@0.28.1': optional: true - '@esbuild/android-arm64@0.27.3': + '@esbuild/android-arm64@0.28.1': optional: true - '@esbuild/android-arm@0.27.3': + '@esbuild/android-arm@0.28.1': optional: true - '@esbuild/android-x64@0.27.3': + '@esbuild/android-x64@0.28.1': optional: true - '@esbuild/darwin-arm64@0.27.3': + '@esbuild/darwin-arm64@0.28.1': optional: true - '@esbuild/darwin-x64@0.27.3': + '@esbuild/darwin-x64@0.28.1': optional: true - '@esbuild/freebsd-arm64@0.27.3': + '@esbuild/freebsd-arm64@0.28.1': optional: true - '@esbuild/freebsd-x64@0.27.3': + '@esbuild/freebsd-x64@0.28.1': optional: true - '@esbuild/linux-arm64@0.27.3': + '@esbuild/linux-arm64@0.28.1': optional: true - '@esbuild/linux-arm@0.27.3': + '@esbuild/linux-arm@0.28.1': optional: true - '@esbuild/linux-ia32@0.27.3': + '@esbuild/linux-ia32@0.28.1': optional: true - '@esbuild/linux-loong64@0.27.3': + '@esbuild/linux-loong64@0.28.1': optional: true - '@esbuild/linux-mips64el@0.27.3': + '@esbuild/linux-mips64el@0.28.1': optional: true - '@esbuild/linux-ppc64@0.27.3': + '@esbuild/linux-ppc64@0.28.1': optional: true - '@esbuild/linux-riscv64@0.27.3': + '@esbuild/linux-riscv64@0.28.1': optional: true - '@esbuild/linux-s390x@0.27.3': + '@esbuild/linux-s390x@0.28.1': optional: true - '@esbuild/linux-x64@0.27.3': + '@esbuild/linux-x64@0.28.1': optional: true - '@esbuild/netbsd-arm64@0.27.3': + '@esbuild/netbsd-arm64@0.28.1': optional: true - '@esbuild/netbsd-x64@0.27.3': + '@esbuild/netbsd-x64@0.28.1': optional: true - '@esbuild/openbsd-arm64@0.27.3': + '@esbuild/openbsd-arm64@0.28.1': optional: true - '@esbuild/openbsd-x64@0.27.3': + '@esbuild/openbsd-x64@0.28.1': optional: true - '@esbuild/openharmony-arm64@0.27.3': + '@esbuild/openharmony-arm64@0.28.1': optional: true - '@esbuild/sunos-x64@0.27.3': + '@esbuild/sunos-x64@0.28.1': optional: true - '@esbuild/win32-arm64@0.27.3': + '@esbuild/win32-arm64@0.28.1': optional: true - '@esbuild/win32-ia32@0.27.3': + '@esbuild/win32-ia32@0.28.1': optional: true - '@esbuild/win32-x64@0.27.3': + '@esbuild/win32-x64@0.28.1': optional: true '@exodus/bytes@1.14.1(@noble/hashes@1.8.0)': optionalDependencies: '@noble/hashes': 1.8.0 - '@gleanwork/mcp-config-schema@4.3.0(zod@3.25.76)': + '@gleanwork/mcp-config-schema@5.2.0(zod@3.25.76)': dependencies: glob: 13.0.6 js-yaml: 4.1.1 @@ -9351,9 +9358,9 @@ snapshots: dependencies: '@hapi/hoek': 9.3.0 - '@hono/node-server@1.19.14(hono@4.12.25)': + '@hono/node-server@1.19.14(hono@4.12.26)': dependencies: - hono: 4.12.25 + hono: 4.12.26 '@img/colour@1.1.0': {} @@ -9670,7 +9677,7 @@ snapshots: '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': dependencies: - '@hono/node-server': 1.19.14(hono@4.12.25) + '@hono/node-server': 1.19.14(hono@4.12.26) ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 @@ -9680,7 +9687,7 @@ snapshots: eventsource-parser: 3.1.0 express: 5.2.1 express-rate-limit: 8.5.2(express@5.2.1) - hono: 4.12.25 + hono: 4.12.26 jose: 6.2.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -11482,10 +11489,10 @@ snapshots: dependencies: '@leichtgewicht/ip-codec': 2.0.5 - docusaurus-plugin-mcp-server@0.12.0(@docusaurus/core@3.10.1(@docusaurus/faster@3.10.1(@docusaurus/types@3.10.1(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.7))(@rspack/core@1.7.11)(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(@docusaurus/types@3.10.1(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@3.25.76): + docusaurus-plugin-mcp-server@1.0.0(@docusaurus/core@3.10.1(@docusaurus/faster@3.10.1(@docusaurus/types@3.10.1(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.7))(@rspack/core@1.7.11)(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(@docusaurus/types@3.10.1(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(zod@3.25.76): dependencies: '@docusaurus/types': 3.10.1(@swc/core@1.15.32)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@gleanwork/mcp-config-schema': 4.3.0(zod@3.25.76) + '@gleanwork/mcp-config-schema': 5.2.0(zod@3.25.76) '@modelcontextprotocol/sdk': 1.29.0(zod@3.25.76) flexsearch: 0.7.43 fs-extra: 11.3.3 @@ -11634,34 +11641,34 @@ snapshots: esast-util-from-estree: 2.0.0 vfile-message: 4.0.3 - esbuild@0.27.3: + esbuild@0.28.1: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.3 - '@esbuild/android-arm': 0.27.3 - '@esbuild/android-arm64': 0.27.3 - '@esbuild/android-x64': 0.27.3 - '@esbuild/darwin-arm64': 0.27.3 - '@esbuild/darwin-x64': 0.27.3 - '@esbuild/freebsd-arm64': 0.27.3 - '@esbuild/freebsd-x64': 0.27.3 - '@esbuild/linux-arm': 0.27.3 - '@esbuild/linux-arm64': 0.27.3 - '@esbuild/linux-ia32': 0.27.3 - '@esbuild/linux-loong64': 0.27.3 - '@esbuild/linux-mips64el': 0.27.3 - '@esbuild/linux-ppc64': 0.27.3 - '@esbuild/linux-riscv64': 0.27.3 - '@esbuild/linux-s390x': 0.27.3 - '@esbuild/linux-x64': 0.27.3 - '@esbuild/netbsd-arm64': 0.27.3 - '@esbuild/netbsd-x64': 0.27.3 - '@esbuild/openbsd-arm64': 0.27.3 - '@esbuild/openbsd-x64': 0.27.3 - '@esbuild/openharmony-arm64': 0.27.3 - '@esbuild/sunos-x64': 0.27.3 - '@esbuild/win32-arm64': 0.27.3 - '@esbuild/win32-ia32': 0.27.3 - '@esbuild/win32-x64': 0.27.3 + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 escalade@3.2.0: {} @@ -11915,6 +11922,8 @@ snapshots: flexsearch@0.7.43: {} + flexsearch@0.8.212: {} + follow-redirects@1.15.11: {} form-data-encoder@2.1.4: {} @@ -12405,7 +12414,9 @@ snapshots: dependencies: react-is: 16.13.1 - hono@4.12.25: {} + hono@4.12.26: {} + + hono@4.12.27: {} hpack.js@2.1.6: dependencies: @@ -13686,13 +13697,13 @@ snapshots: tapable: 2.3.0 webpack: 5.105.2(@swc/core@1.15.32) - miniflare@4.20260611.0: + miniflare@4.20260625.0: dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.34.5 - undici: 7.24.8 - workerd: 1.20260611.1 - ws: 8.20.1 + undici: 7.28.0 + workerd: 1.20260625.1 + ws: 8.21.0 youch: 4.1.0-beta.10 transitivePeerDependencies: - bufferutil @@ -13765,7 +13776,7 @@ snapshots: not@0.1.0: {} - npm-check-updates@22.2.3: {} + npm-check-updates@22.2.8: {} npm-run-path@4.0.1: dependencies: @@ -15592,7 +15603,7 @@ snapshots: undici@7.22.0: {} - undici@7.24.8: {} + undici@7.28.0: {} unenv@2.0.0-rc.24: dependencies: @@ -15965,24 +15976,24 @@ snapshots: wildcard@2.0.1: {} - workerd@1.20260611.1: + workerd@1.20260625.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260611.1 - '@cloudflare/workerd-darwin-arm64': 1.20260611.1 - '@cloudflare/workerd-linux-64': 1.20260611.1 - '@cloudflare/workerd-linux-arm64': 1.20260611.1 - '@cloudflare/workerd-windows-64': 1.20260611.1 + '@cloudflare/workerd-darwin-64': 1.20260625.1 + '@cloudflare/workerd-darwin-arm64': 1.20260625.1 + '@cloudflare/workerd-linux-64': 1.20260625.1 + '@cloudflare/workerd-linux-arm64': 1.20260625.1 + '@cloudflare/workerd-windows-64': 1.20260625.1 - wrangler@4.100.0: + wrangler@4.105.0: dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260611.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260625.1) blake3-wasm: 2.1.5 - esbuild: 0.27.3 - miniflare: 4.20260611.0 + esbuild: 0.28.1 + miniflare: 4.20260625.0 path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260611.1 + workerd: 1.20260625.1 optionalDependencies: fsevents: 2.3.3 transitivePeerDependencies: @@ -16008,7 +16019,7 @@ snapshots: ws@8.19.0: {} - ws@8.20.1: {} + ws@8.21.0: {} wsl-utils@0.1.0: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e3df65b..e7098a8 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,6 +4,10 @@ allowBuilds: esbuild: true sharp: false workerd: true +minimumReleaseAgeExclude: + - hono@4.12.26 + - docusaurus-plugin-mcp-server@1.0.0 + - npm-check-updates@22.2.8 onlyBuiltDependencies: - core-js - core-js-pure diff --git a/worker/index.ts b/worker/index.ts index 7918867..8cbb222 100644 --- a/worker/index.ts +++ b/worker/index.ts @@ -3,35 +3,12 @@ import { cors } from 'hono/cors'; import { McpDocsServer } from 'docusaurus-plugin-mcp-server'; import docs from '../build/mcp/docs.json'; import searchIndex from '../build/mcp/search-index.json'; -import LeanFlexSearchProvider from '../mcp-providers/lean-provider.ts'; +import flexsearchConfig from '../flexsearch.config.ts'; const NAME = 'hexlet-help'; const VERSION = '1.0.0'; const BASE_URL = 'https://help.hexlet.io'; -// McpDocsServer.searchProvider is normally resolved via loadSearchProvider(), -// which uses dynamic import() — won't work in a Worker bundle. We monkey-patch -// the prototype to inject our pre-imported LeanFlexSearchProvider instead. -// -// Replace this whole block with `search: new LeanFlexSearchProvider()` in the -// McpDocsServer config once this PR is released: -// https://github.com/scalvert/docusaurus-plugin-mcp-server/pull/78 -// eslint-disable-next-line @typescript-eslint/no-explicit-any -(McpDocsServer.prototype as any)._doInitialize = async function () { - const provider = new LeanFlexSearchProvider(); - await provider.initialize( - { - baseUrl: this.config.baseUrl ?? '', - serverName: this.config.name, - serverVersion: this.config.version ?? '1.0.0', - outputDir: '', - }, - { docs: this.config.docs, indexData: this.config.searchIndexData }, - ); - this.searchProvider = provider; - this.initialized = true; -}; - let server: McpDocsServer | null = null; const getServer = (): McpDocsServer => (server ??= new McpDocsServer({ @@ -40,6 +17,10 @@ const getServer = (): McpDocsServer => name: NAME, version: VERSION, baseUrl: BASE_URL, + // The built-in 'flexsearch' provider is bundled statically (no dynamic + // import), so it works in the Worker. This config must match the one used + // at build time in docusaurus.config.ts, or the index deserializes wrong. + flexsearch: flexsearchConfig, // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any));