@arabold/docs-mcp-server 1.20.0 → 1.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/{DocumentManagementService-BH02TJEe.js → DocumentManagementService-C1xAzouZ.js} +127 -18
- package/dist/DocumentManagementService-C1xAzouZ.js.map +1 -0
- package/dist/assets/main.css +1 -1
- package/dist/assets/main.js +47 -47
- package/dist/assets/main.js.map +1 -1
- package/dist/index.js +2017 -644
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/public/assets/main.css +1 -1
- package/public/assets/main.js +47 -47
- package/public/assets/main.js.map +1 -1
- package/dist/DocumentManagementService-BH02TJEe.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentManagementService-BH02TJEe.js","sources":["../src/splitter/errors.ts","../src/splitter/GreedySplitter.ts","../src/utils/string.ts","../src/splitter/splitters/CodeContentSplitter.ts","../src/splitter/splitters/TableContentSplitter.ts","../src/splitter/splitters/TextContentSplitter.ts","../src/splitter/SemanticMarkdownSplitter.ts","../src/store/DocumentRetrieverService.ts","../src/store/DocumentStore.ts","../src/store/DocumentManagementService.ts"],"sourcesContent":["/**\n * Base error class for all splitter-related errors\n */\nexport class SplitterError extends Error {}\n\n/**\n * Thrown when content cannot be split further while maintaining its validity\n * (e.g., markdown tables require headers, code blocks require language and backticks)\n */\nexport class MinimumChunkSizeError extends SplitterError {\n constructor(size: number, maxSize: number) {\n super(\n `Cannot split content any further. Content requires minimum chunk size of ${size} bytes, but maximum allowed is ${maxSize} bytes.`,\n );\n }\n}\n\n/**\n * Generic error for content splitting failures\n */\nexport class ContentSplitterError extends SplitterError {}\n","import type { ContentChunk, DocumentSplitter, SectionContentType } from \"./types\";\n\n/**\n * Takes small document chunks and greedily concatenates them into larger, more meaningful units\n * while preserving document structure and semantic boundaries.\n *\n * This approach improves embedding quality by:\n * - Maintaining context by keeping related content together\n * - Respecting natural document breaks at major section boundaries (H1/H2)\n * - Ensuring chunks are large enough to capture meaningful relationships\n * - Preventing chunks from becoming too large for effective embedding\n */\nexport class GreedySplitter implements DocumentSplitter {\n private baseSplitter: DocumentSplitter;\n private minChunkSize: number;\n private preferredChunkSize: number;\n\n /**\n * Combines a base document splitter with size constraints to produce optimally-sized chunks.\n * The base splitter handles the initial semantic splitting, while this class handles\n * the concatenation strategy.\n */\n constructor(\n baseSplitter: DocumentSplitter,\n minChunkSize: number,\n preferredChunkSize: number,\n ) {\n this.baseSplitter = baseSplitter;\n this.minChunkSize = minChunkSize;\n this.preferredChunkSize = preferredChunkSize;\n }\n\n /**\n * Uses a greedy concatenation strategy to build optimally-sized chunks. Small chunks\n * are combined until they reach the minimum size, but splits are preserved at major\n * section boundaries to maintain document structure. This balances the need for\n * context with semantic coherence.\n */\n async splitText(markdown: string): Promise<ContentChunk[]> {\n const initialChunks = await this.baseSplitter.splitText(markdown);\n const concatenatedChunks: ContentChunk[] = [];\n let currentChunk: ContentChunk | null = null;\n\n for (const nextChunk of initialChunks) {\n if (currentChunk) {\n if (this.wouldExceedMaxSize(currentChunk, nextChunk)) {\n concatenatedChunks.push(currentChunk);\n currentChunk = this.cloneChunk(nextChunk);\n continue;\n }\n if (\n currentChunk.content.length >= this.minChunkSize &&\n this.startsNewMajorSection(nextChunk)\n ) {\n concatenatedChunks.push(currentChunk);\n currentChunk = this.cloneChunk(nextChunk);\n continue;\n }\n currentChunk.content += `\\n${nextChunk.content}`;\n currentChunk.section = this.mergeSectionInfo(currentChunk, nextChunk);\n currentChunk.types = this.mergeTypes(currentChunk.types, nextChunk.types);\n } else {\n currentChunk = this.cloneChunk(nextChunk);\n }\n }\n\n if (currentChunk) {\n concatenatedChunks.push(currentChunk);\n }\n\n return concatenatedChunks;\n }\n\n private cloneChunk(chunk: ContentChunk): ContentChunk {\n return {\n types: [...chunk.types],\n content: chunk.content,\n section: {\n level: chunk.section.level,\n path: [...chunk.section.path],\n },\n };\n }\n\n /**\n * H1 and H2 headings represent major conceptual breaks in the document.\n * Preserving these splits helps maintain the document's logical structure.\n */\n private startsNewMajorSection(chunk: ContentChunk): boolean {\n return chunk.section.level === 1 || chunk.section.level === 2;\n }\n\n /**\n * Size limit check to ensure chunks remain within embedding model constraints.\n * Essential for maintaining consistent embedding quality and avoiding truncation.\n */\n private wouldExceedMaxSize(\n currentChunk: ContentChunk | null,\n nextChunk: ContentChunk,\n ): boolean {\n if (!currentChunk) {\n return false;\n }\n return (\n currentChunk.content.length + nextChunk.content.length > this.preferredChunkSize\n );\n }\n\n /**\n * Checks if one path is a prefix of another path, indicating a parent-child relationship\n */\n private isPathIncluded(parentPath: string[], childPath: string[]): boolean {\n if (parentPath.length >= childPath.length) return false;\n return parentPath.every((part, i) => part === childPath[i]);\n }\n\n /**\n * Merges section metadata when concatenating chunks, following these rules:\n * 1. Level: Always uses the lowest (most general) level between chunks\n * 2. Path selection:\n * - For parent-child relationships (one path includes the other), uses the child's path\n * - For siblings/unrelated sections, uses the common parent path\n * - If no common path exists, uses the root path ([])\n */\n private mergeSectionInfo(\n currentChunk: ContentChunk,\n nextChunk: ContentChunk,\n ): ContentChunk[\"section\"] {\n // Always use the lowest level\n const level = Math.min(currentChunk.section.level, nextChunk.section.level);\n\n // If sections are exactly equal, preserve all metadata\n if (\n currentChunk.section.level === nextChunk.section.level &&\n currentChunk.section.path.length === nextChunk.section.path.length &&\n currentChunk.section.path.every((p, i) => p === nextChunk.section.path[i])\n ) {\n return currentChunk.section;\n }\n\n // Check if one path includes the other\n if (this.isPathIncluded(currentChunk.section.path, nextChunk.section.path)) {\n return {\n path: nextChunk.section.path,\n level,\n };\n }\n\n if (this.isPathIncluded(nextChunk.section.path, currentChunk.section.path)) {\n return {\n path: currentChunk.section.path,\n level,\n };\n }\n\n // Find common parent path\n const commonPath = this.findCommonPrefix(\n currentChunk.section.path,\n nextChunk.section.path,\n );\n\n return {\n path: commonPath,\n level,\n };\n }\n\n private mergeTypes(\n currentTypes: SectionContentType[],\n nextTypes: SectionContentType[],\n ): SectionContentType[] {\n return [...new Set([...currentTypes, ...nextTypes])];\n }\n\n /**\n * Returns longest common prefix between two paths\n */\n private findCommonPrefix(path1: string[], path2: string[]): string[] {\n const common: string[] = [];\n for (let i = 0; i < Math.min(path1.length, path2.length); i++) {\n if (path1[i] === path2[i]) {\n common.push(path1[i]);\n } else {\n break;\n }\n }\n return common;\n }\n}\n","/**\n * Thoroughly removes all types of whitespace characters from both ends of a string.\n * Handles spaces, tabs, line breaks, and carriage returns.\n */\nexport const fullTrim = (str: string): string => {\n return str.replace(/^[\\s\\r\\n\\t]+|[\\s\\r\\n\\t]+$/g, \"\");\n};\n","import { MinimumChunkSizeError } from \"../errors\";\nimport type { ContentSplitter, ContentSplitterOptions } from \"./types\";\n\n/**\n * Splits code content while preserving language information and formatting.\n * Uses line boundaries for splitting and ensures each chunk is properly\n * wrapped with language-specific code block markers.\n */\nexport class CodeContentSplitter implements ContentSplitter {\n constructor(private options: ContentSplitterOptions) {}\n\n async split(content: string): Promise<string[]> {\n // Determine language and strip triple backticks from content\n const language = content.match(/^```(\\w+)\\n/)?.[1];\n const strippedContent = content.replace(/^```(\\w*)\\n/, \"\").replace(/```\\s*$/, \"\");\n\n const lines = strippedContent.split(\"\\n\");\n const chunks: string[] = [];\n let currentChunkLines: string[] = [];\n\n for (const line of lines) {\n // Check if a single line with code block markers exceeds chunkSize\n const singleLineSize = this.wrap(line, language).length;\n if (singleLineSize > this.options.chunkSize) {\n throw new MinimumChunkSizeError(singleLineSize, this.options.chunkSize);\n }\n\n currentChunkLines.push(line);\n const newChunkContent = this.wrap(currentChunkLines.join(\"\\n\"), language);\n const newChunkSize = newChunkContent.length;\n\n if (newChunkSize > this.options.chunkSize && currentChunkLines.length > 1) {\n // remove last item\n const lastLine = currentChunkLines.pop();\n // wrap content and create chunk\n chunks.push(this.wrap(currentChunkLines.join(\"\\n\"), language));\n currentChunkLines = [lastLine as string];\n }\n }\n\n if (currentChunkLines.length > 0) {\n chunks.push(this.wrap(currentChunkLines.join(\"\\n\"), language));\n }\n\n return chunks;\n }\n\n protected wrap(content: string, language?: string | null): string {\n return `\\`\\`\\`${language || \"\"}\\n${content.replace(/\\n+$/, \"\")}\\n\\`\\`\\``;\n }\n}\n","import { MinimumChunkSizeError } from \"../errors\";\nimport type { ContentSplitter, ContentSplitterOptions } from \"./types\";\n\n/**\n * Interface representing the structure of a parsed markdown table\n */\ninterface ParsedTable {\n headers: string[];\n separator: string;\n rows: string[];\n}\n\n/**\n * Splits table content while preserving headers and table formatting.\n * Each chunk maintains the table structure with headers and separator row.\n */\nexport class TableContentSplitter implements ContentSplitter {\n constructor(private options: ContentSplitterOptions) {}\n\n /**\n * Splits table content into chunks while preserving table structure\n */\n async split(content: string): Promise<string[]> {\n const parsedTable = this.parseTable(content);\n if (!parsedTable) {\n return [content];\n }\n\n const { headers, rows } = parsedTable;\n\n const chunks: string[] = [];\n let currentRows: string[] = [];\n\n for (const row of rows) {\n // Check if a single row with headers exceeds chunkSize\n const singleRowSize = this.wrap(row, headers).length;\n if (singleRowSize > this.options.chunkSize) {\n throw new MinimumChunkSizeError(singleRowSize, this.options.chunkSize);\n }\n\n const newChunkContent = this.wrap([...currentRows, row].join(\"\\n\"), headers);\n const newChunkSize = newChunkContent.length;\n if (newChunkSize > this.options.chunkSize && currentRows.length > 0) {\n // Add current chunk, start new\n chunks.push(this.wrap(currentRows.join(\"\\n\"), headers));\n currentRows = [row];\n } else {\n currentRows.push(row);\n }\n }\n\n if (currentRows.length > 0) {\n chunks.push(this.wrap(currentRows.join(\"\\n\"), headers));\n }\n\n // No merging of table chunks\n return chunks;\n }\n\n protected wrap(content: string, headers: string[]): string {\n const headerRow = `| ${headers.join(\" | \")} |`;\n const separatorRow = `|${headers.map(() => \"---\").join(\"|\")}|`;\n return [headerRow, separatorRow, content].join(\"\\n\");\n }\n\n private parseTable(content: string): ParsedTable | null {\n const lines = content.trim().split(\"\\n\");\n if (lines.length < 3) return null; // Need at least headers, separator, and one data row\n\n const headers = this.parseRow(lines[0]);\n if (!headers) return null;\n\n const separator = lines[1];\n if (!this.isValidSeparator(separator)) return null;\n\n const rows = lines.slice(2).filter((row) => row.trim() !== \"\");\n\n return { headers, separator, rows };\n }\n\n /**\n * Parses a table row into cells\n */\n private parseRow(row: string): string[] | null {\n if (!row.includes(\"|\")) return null;\n return row\n .split(\"|\")\n .map((cell) => cell.trim())\n .filter((cell) => cell !== \"\");\n }\n\n /**\n * Validates the separator row of the table\n */\n private isValidSeparator(separator: string): boolean {\n return separator.includes(\"|\") && /^\\|?[\\s-|]+\\|?$/.test(separator);\n }\n}\n","import { RecursiveCharacterTextSplitter } from \"langchain/text_splitter\";\nimport { fullTrim } from \"../../utils/string\";\nimport { MinimumChunkSizeError } from \"../errors\";\nimport type { ContentSplitter, ContentSplitterOptions } from \"./types\";\n\n/**\n * Splits text content using a hierarchical approach:\n * 1. Try splitting by paragraphs (double newlines)\n * 2. If chunks still too large, split by single newlines\n * 3. Finally, use word boundaries via LangChain's splitter\n */\nexport class TextContentSplitter implements ContentSplitter {\n constructor(private options: ContentSplitterOptions) {}\n\n /**\n * Splits text content into chunks while trying to preserve semantic boundaries.\n * Prefers paragraph breaks, then line breaks, finally falling back to word boundaries.\n */\n async split(content: string): Promise<string[]> {\n const trimmedContent = fullTrim(content);\n\n if (trimmedContent.length <= this.options.chunkSize) {\n return [trimmedContent];\n }\n\n // Check for unsplittable content (e.g., a single word longer than chunkSize)\n const words = trimmedContent.split(/\\s+/);\n const longestWord = words.reduce((max, word) =>\n word.length > max.length ? word : max,\n );\n if (longestWord.length > this.options.chunkSize) {\n throw new MinimumChunkSizeError(longestWord.length, this.options.chunkSize);\n }\n\n // First try splitting by paragraphs (double newlines)\n const paragraphChunks = this.splitByParagraphs(trimmedContent);\n if (this.areChunksValid(paragraphChunks)) {\n // No merging for paragraph chunks; they are already semantically separated\n return paragraphChunks;\n }\n\n // If that doesn't work, try splitting by single newlines\n const lineChunks = this.splitByLines(trimmedContent);\n if (this.areChunksValid(lineChunks)) {\n return this.mergeChunks(lineChunks, \"\\n\");\n }\n\n // Finally, fall back to word-based splitting using LangChain\n const wordChunks = await this.splitByWords(trimmedContent);\n return this.mergeChunks(wordChunks, \" \");\n }\n\n /**\n * Checks if all chunks are within the maximum size limit\n */\n private areChunksValid(chunks: string[]): boolean {\n return chunks.every((chunk) => chunk.length <= this.options.chunkSize);\n }\n\n /**\n * Splits text into chunks by paragraph boundaries (double newlines)\n */\n private splitByParagraphs(text: string): string[] {\n const paragraphs = text\n .split(/\\n\\s*\\n/)\n .map((p) => fullTrim(p))\n .filter(Boolean);\n\n return paragraphs.filter((chunk) => chunk.length > 2);\n }\n\n /**\n * Splits text into chunks by line boundaries\n */\n private splitByLines(text: string): string[] {\n const lines = text\n .split(/\\n/)\n .map((line) => fullTrim(line))\n .filter(Boolean);\n\n return lines.filter((chunk) => chunk.length > 1);\n }\n\n /**\n * Uses LangChain's recursive splitter for word-based splitting as a last resort\n */\n private async splitByWords(text: string): Promise<string[]> {\n const splitter = new RecursiveCharacterTextSplitter({\n chunkSize: this.options.chunkSize,\n chunkOverlap: 0,\n });\n\n const chunks = await splitter.splitText(text);\n return chunks;\n }\n\n /**\n * Attempts to merge small chunks with previous chunks to minimize fragmentation.\n * Only merges if combined size is within maxChunkSize.\n */\n protected mergeChunks(chunks: string[], separator: string): string[] {\n const mergedChunks: string[] = [];\n let currentChunk: string | null = null;\n\n for (const chunk of chunks) {\n if (currentChunk === null) {\n currentChunk = chunk;\n continue;\n }\n\n const currentChunkSize = this.getChunkSize(currentChunk);\n const nextChunkSize = this.getChunkSize(chunk);\n\n if (currentChunkSize + nextChunkSize + separator.length <= this.options.chunkSize) {\n // Merge chunks\n currentChunk = `${currentChunk}${separator}${chunk}`;\n } else {\n // Add the current chunk to the result and start a new one\n mergedChunks.push(currentChunk);\n currentChunk = chunk;\n }\n }\n\n if (currentChunk) {\n mergedChunks.push(currentChunk);\n }\n\n return mergedChunks;\n }\n\n protected getChunkSize(chunk: string): number {\n return chunk.length;\n }\n\n protected wrap(content: string): string {\n return content;\n }\n}\n","import { RecursiveCharacterTextSplitter } from \"langchain/text_splitter\";\nimport remarkGfm from \"remark-gfm\";\nimport remarkHtml from \"remark-html\";\nimport remarkParse from \"remark-parse\";\nimport TurndownService from \"turndown\";\nimport { unified } from \"unified\";\nimport { createJSDOM } from \"../utils/dom\";\nimport { logger } from \"../utils/logger\";\nimport { fullTrim } from \"../utils/string\";\nimport { ContentSplitterError, MinimumChunkSizeError } from \"./errors\";\nimport { CodeContentSplitter } from \"./splitters/CodeContentSplitter\";\nimport { TableContentSplitter } from \"./splitters/TableContentSplitter\";\nimport { TextContentSplitter } from \"./splitters/TextContentSplitter\";\nimport type { ContentChunk, DocumentSplitter, SectionContentType } from \"./types\";\n\n/**\n * Represents a section of content within a document,\n * typically defined by a heading\n */\ninterface DocumentSection {\n level: number;\n path: string[]; // Full path including parent headings\n content: {\n type: SectionContentType;\n text: string;\n }[];\n}\n\n/**\n * Splits markdown documents into semantic chunks while preserving\n * structure and distinguishing between different content types.\n *\n * The splitting process happens in two steps:\n * 1. Split document into sections based on headings (H1-H3 only)\n * 2. Split section content into smaller chunks based on preferredChunkSize\n */\nexport class SemanticMarkdownSplitter implements DocumentSplitter {\n private turndownService: TurndownService;\n public textSplitter: TextContentSplitter;\n public codeSplitter: CodeContentSplitter;\n public tableSplitter: TableContentSplitter;\n\n constructor(\n private preferredChunkSize: number,\n private maxChunkSize: number,\n ) {\n this.turndownService = new TurndownService({\n headingStyle: \"atx\",\n hr: \"---\",\n bulletListMarker: \"-\",\n codeBlockStyle: \"fenced\",\n emDelimiter: \"_\",\n strongDelimiter: \"**\",\n linkStyle: \"inlined\",\n });\n\n // Add table rule to preserve markdown table format\n this.turndownService.addRule(\"table\", {\n filter: [\"table\"],\n replacement: (_content, node) => {\n const table = node as HTMLTableElement;\n const headers = Array.from(table.querySelectorAll(\"th\")).map(\n (th) => th.textContent?.trim() || \"\",\n );\n const rows = Array.from(table.querySelectorAll(\"tr\")).filter(\n (tr) => !tr.querySelector(\"th\"),\n );\n\n if (headers.length === 0 && rows.length === 0) return \"\";\n\n let markdown = \"\\n\";\n if (headers.length > 0) {\n markdown += `| ${headers.join(\" | \")} |\\n`;\n markdown += `|${headers.map(() => \"---\").join(\"|\")}|\\n`;\n }\n\n for (const row of rows) {\n const cells = Array.from(row.querySelectorAll(\"td\")).map(\n (td) => td.textContent?.trim() || \"\",\n );\n markdown += `| ${cells.join(\" | \")} |\\n`;\n }\n\n return markdown;\n },\n });\n\n // Text splitter uses preferred chunk size (keeps paragraphs together if possible)\n this.textSplitter = new TextContentSplitter({\n chunkSize: this.preferredChunkSize,\n });\n // Code/table splitters use the hard chunk size (avoid splitting unless necessary)\n this.codeSplitter = new CodeContentSplitter({\n chunkSize: this.maxChunkSize,\n });\n this.tableSplitter = new TableContentSplitter({\n chunkSize: this.maxChunkSize,\n });\n }\n\n /**\n * Main entry point for splitting markdown content\n */\n async splitText(markdown: string): Promise<ContentChunk[]> {\n const html = await this.markdownToHtml(markdown);\n const dom = await this.parseHtml(html);\n const sections = await this.splitIntoSections(dom);\n return this.splitSectionContent(sections);\n }\n\n /**\n * Step 1: Split document into sections based on H1-H6 headings,\n * as well as code blocks and tables.\n */\n private async splitIntoSections(dom: Document): Promise<DocumentSection[]> {\n const body = dom.querySelector(\"body\");\n if (!body) {\n throw new Error(\"Invalid HTML structure: no body element found\");\n }\n\n let currentSection = this.createRootSection();\n const sections: DocumentSection[] = [];\n const stack: DocumentSection[] = [currentSection];\n\n // Process each child of the body\n for (const element of Array.from(body.children)) {\n const headingMatch = element.tagName.match(/H([1-6])/);\n\n if (headingMatch) {\n // Create new section for H1-H6 heading\n const level = Number.parseInt(headingMatch[1], 10);\n const title = fullTrim(element.textContent || \"\");\n\n // Pop sections from stack until we find the parent level\n while (stack.length > 1 && stack[stack.length - 1].level >= level) {\n stack.pop();\n }\n\n // Start new section with the header\n currentSection = {\n level,\n path: [\n ...stack.slice(1).reduce((acc: string[], s) => {\n const lastPath = s.path[s.path.length - 1];\n if (lastPath) acc.push(lastPath);\n return acc;\n }, []),\n title,\n ],\n content: [\n {\n type: \"heading\",\n text: `${\"#\".repeat(level)} ${title}`,\n },\n ],\n };\n\n sections.push(currentSection);\n stack.push(currentSection);\n } else if (element.tagName === \"PRE\") {\n // Code blocks are kept as separate chunks\n const code = element.querySelector(\"code\");\n const language = code?.className.replace(\"language-\", \"\") || \"\";\n const content = code?.textContent || element.textContent || \"\";\n const markdown = `${\"```\"}${language}\\n${content}\\n${\"```\"}`;\n\n currentSection = {\n level: currentSection.level,\n path: currentSection.path,\n content: [\n {\n type: \"code\",\n text: markdown,\n },\n ],\n } satisfies DocumentSection;\n sections.push(currentSection);\n } else if (element.tagName === \"TABLE\") {\n // Tables are kept as separate chunks\n const markdown = fullTrim(this.turndownService.turndown(element.outerHTML));\n\n currentSection = {\n level: currentSection.level,\n path: currentSection.path,\n content: [\n {\n type: \"table\",\n text: markdown,\n },\n ],\n } satisfies DocumentSection;\n sections.push(currentSection);\n } else {\n const markdown = fullTrim(this.turndownService.turndown(element.innerHTML));\n if (markdown) {\n // Create a new section for the text content\n currentSection = {\n level: currentSection.level,\n path: currentSection.path,\n content: [\n {\n type: \"text\",\n text: markdown,\n },\n ],\n } satisfies DocumentSection;\n sections.push(currentSection);\n }\n }\n }\n\n return sections;\n }\n\n /**\n * Step 2: Split section content into smaller chunks\n */\n private async splitSectionContent(\n sections: DocumentSection[],\n ): Promise<ContentChunk[]> {\n const chunks: ContentChunk[] = [];\n\n for (const section of sections) {\n for (const content of section.content) {\n let splitContent: string[] = [];\n\n try {\n switch (content.type) {\n case \"heading\":\n case \"text\": {\n splitContent = await this.textSplitter.split(content.text);\n break;\n }\n case \"code\": {\n splitContent = await this.codeSplitter.split(content.text);\n break;\n }\n case \"table\": {\n splitContent = await this.tableSplitter.split(content.text);\n break;\n }\n }\n } catch (err) {\n // If it's a MinimumChunkSizeError, use RecursiveCharacterTextSplitter directly\n if (err instanceof MinimumChunkSizeError) {\n logger.warn(\n `⚠ Cannot split ${content.type} chunk normally, using RecursiveCharacterTextSplitter: ${err.message}`,\n );\n\n // Create a RecursiveCharacterTextSplitter with aggressive settings to ensure splitting\n const splitter = new RecursiveCharacterTextSplitter({\n chunkSize: this.maxChunkSize,\n chunkOverlap: Math.min(20, Math.floor(this.maxChunkSize * 0.1)),\n // Use more aggressive separators including empty string as last resort\n separators: [\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\\t\",\n \".\",\n \",\",\n \";\",\n \":\",\n \"-\",\n \"(\",\n \")\",\n \"[\",\n \"]\",\n \"{\",\n \"}\",\n \"\",\n ],\n });\n\n const chunks = await splitter.splitText(content.text);\n if (chunks.length === 0) {\n // If still no chunks, use the most extreme approach: just truncate\n splitContent = [content.text.substring(0, this.maxChunkSize)];\n } else {\n splitContent = chunks;\n }\n } else {\n // Convert other error message to string, handling non-Error objects\n const errMessage = err instanceof Error ? err.message : String(err);\n throw new ContentSplitterError(\n `Failed to split ${content.type} content: ${errMessage}`,\n );\n }\n }\n\n // Create chunks from split content\n chunks.push(\n ...splitContent.map(\n (text): ContentChunk => ({\n types: [content.type],\n content: text,\n section: {\n level: section.level,\n path: section.path,\n },\n }),\n ),\n );\n }\n }\n\n return chunks;\n }\n\n /**\n * Helper to create the root section\n */\n private createRootSection(): DocumentSection {\n return {\n level: 0,\n path: [],\n content: [],\n };\n }\n\n /**\n * Convert markdown to HTML using remark\n */\n private async markdownToHtml(markdown: string): Promise<string> {\n const html = await unified()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkHtml)\n .process(markdown);\n\n return `<!DOCTYPE html>\n <html>\n <body>\n ${String(html)}\n </body>\n </html>`;\n }\n\n /**\n * Parse HTML\n */\n private async parseHtml(html: string): Promise<Document> {\n // Use createJSDOM which includes default options like virtualConsole\n const { window } = createJSDOM(html);\n return window.document;\n }\n}\n","import type { Document } from \"@langchain/core/documents\";\nimport type { DocumentStore } from \"./DocumentStore\";\nimport type { StoreSearchResult } from \"./types\";\n\nconst CHILD_LIMIT = 5;\nconst SIBLING_LIMIT = 2;\n\nexport class DocumentRetrieverService {\n private documentStore: DocumentStore;\n\n constructor(documentStore: DocumentStore) {\n this.documentStore = documentStore;\n }\n\n /**\n * Collects all related chunk IDs for a given initial hit.\n * Returns an object with url, hitId, relatedIds (Set), and score.\n */\n private async getRelatedChunkIds(\n library: string,\n version: string,\n doc: Document,\n siblingLimit = SIBLING_LIMIT,\n childLimit = CHILD_LIMIT,\n ): Promise<{\n url: string;\n hitId: string;\n relatedIds: Set<string>;\n score: number;\n }> {\n const id = doc.id as string;\n const url = doc.metadata.url as string;\n const score = doc.metadata.score as number;\n const relatedIds = new Set<string>();\n relatedIds.add(id);\n\n // Parent\n const parent = await this.documentStore.findParentChunk(library, version, id);\n if (parent) {\n relatedIds.add(parent.id as string);\n }\n\n // Preceding Siblings\n const precedingSiblings = await this.documentStore.findPrecedingSiblingChunks(\n library,\n version,\n id,\n siblingLimit,\n );\n for (const sib of precedingSiblings) {\n relatedIds.add(sib.id as string);\n }\n\n // Child Chunks\n const childChunks = await this.documentStore.findChildChunks(\n library,\n version,\n id,\n childLimit,\n );\n for (const child of childChunks) {\n relatedIds.add(child.id as string);\n }\n\n // Subsequent Siblings\n const subsequentSiblings = await this.documentStore.findSubsequentSiblingChunks(\n library,\n version,\n id,\n siblingLimit,\n );\n for (const sib of subsequentSiblings) {\n relatedIds.add(sib.id as string);\n }\n\n return { url, hitId: id, relatedIds, score };\n }\n\n /**\n * Groups related chunk info by URL, deduplicates IDs, and finds max score per URL.\n */\n private groupAndPrepareFetch(\n relatedInfos: Array<{\n url: string;\n hitId: string;\n relatedIds: Set<string>;\n score: number;\n }>,\n ): Map<string, { uniqueChunkIds: Set<string>; maxScore: number }> {\n const urlMap = new Map<string, { uniqueChunkIds: Set<string>; maxScore: number }>();\n for (const info of relatedInfos) {\n let entry = urlMap.get(info.url);\n if (!entry) {\n entry = { uniqueChunkIds: new Set(), maxScore: info.score };\n urlMap.set(info.url, entry);\n }\n for (const id of info.relatedIds) {\n entry.uniqueChunkIds.add(id);\n }\n if (info.score > entry.maxScore) {\n entry.maxScore = info.score;\n }\n }\n return urlMap;\n }\n\n /**\n * Finalizes the merged result for a URL group by fetching, sorting, and joining content.\n */\n private async finalizeResult(\n library: string,\n version: string,\n url: string,\n uniqueChunkIds: Set<string>,\n maxScore: number,\n ): Promise<StoreSearchResult> {\n const ids = Array.from(uniqueChunkIds);\n const docs = await this.documentStore.findChunksByIds(library, version, ids);\n // Already sorted by sort_order in findChunksByIds\n const content = docs.map((d) => d.pageContent).join(\"\\n\\n\");\n // TODO: Apply code block merging here if/when implemented\n return {\n url,\n content,\n score: maxScore,\n };\n }\n\n /**\n * Searches for documents and expands the context around the matches.\n * @param library The library name.\n * @param version The library version.\n * @param query The search query.\n * @param version The library version (optional, defaults to searching documents without a version).\n * @param query The search query.\n * @param limit The optional limit for the initial search results.\n * @returns An array of strings representing the aggregated content of the retrieved chunks.\n */\n async search(\n library: string,\n version: string | null | undefined,\n query: string,\n limit?: number,\n ): Promise<StoreSearchResult[]> {\n // Normalize version: null/undefined becomes empty string, then lowercase\n const normalizedVersion = (version ?? \"\").toLowerCase();\n\n const initialResults = await this.documentStore.findByContent(\n library,\n normalizedVersion,\n query,\n limit ?? 10,\n );\n\n // Step 1: Expand context for each initial hit (collect related chunk IDs)\n const relatedInfos = await Promise.all(\n initialResults.map((doc) =>\n this.getRelatedChunkIds(library, normalizedVersion, doc),\n ),\n );\n\n // Step 2: Group by URL, deduplicate, and find max score\n const urlMap = this.groupAndPrepareFetch(relatedInfos);\n\n // Step 3: For each URL group, fetch, sort, and format the merged result\n const results: StoreSearchResult[] = [];\n for (const [url, { uniqueChunkIds, maxScore }] of urlMap.entries()) {\n const result = await this.finalizeResult(\n library,\n normalizedVersion,\n url,\n uniqueChunkIds,\n maxScore,\n );\n results.push(result);\n }\n\n return results;\n }\n}\n","import type { Document } from \"@langchain/core/documents\";\nimport type { Embeddings } from \"@langchain/core/embeddings\";\nimport Database, { type Database as DatabaseType } from \"better-sqlite3\";\nimport semver from \"semver\";\nimport * as sqliteVec from \"sqlite-vec\";\nimport type { ScraperOptions } from \"../scraper/types\";\nimport type { DocumentMetadata } from \"../types\";\nimport { EMBEDDING_BATCH_CHARS, EMBEDDING_BATCH_SIZE } from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\nimport { applyMigrations } from \"./applyMigrations\";\nimport { ConnectionError, DimensionError, StoreError } from \"./errors\";\nimport type { StoredScraperOptions } from \"./types\";\nimport {\n type DbDocument,\n type DbQueryResult,\n type DbVersion,\n type DbVersionWithLibrary,\n denormalizeVersionName,\n mapDbDocumentToDocument,\n normalizeVersionName,\n VECTOR_DIMENSION,\n type VersionScraperOptions,\n type VersionStatus,\n} from \"./types\";\n\ninterface RawSearchResult extends DbDocument {\n vec_score?: number;\n fts_score?: number;\n}\n\ninterface RankedResult extends RawSearchResult {\n vec_rank?: number;\n fts_rank?: number;\n rrf_score: number;\n}\n\n/**\n * Manages document storage and retrieval using SQLite with vector and full-text search capabilities.\n * Provides direct access to SQLite with prepared statements to store and query document\n * embeddings along with their metadata. Supports versioned storage of documents for different\n * libraries, enabling version-specific document retrieval and searches.\n */\nexport class DocumentStore {\n private readonly db: DatabaseType;\n private embeddings!: Embeddings;\n private readonly dbDimension: number = VECTOR_DIMENSION;\n private modelDimension!: number;\n private statements!: {\n getById: Database.Statement<[bigint]>;\n insertDocument: Database.Statement<\n [bigint, bigint, string, string, string, number, string]\n >;\n insertEmbedding: Database.Statement<[bigint, bigint, bigint, string]>;\n deleteDocuments: Database.Statement<[string, string, string]>;\n deleteDocumentsByUrl: Database.Statement<[string, string, string, string]>;\n queryVersions: Database.Statement<[string]>;\n checkExists: Database.Statement<[string, string]>;\n queryLibraryVersions: Database.Statement<[]>;\n getChildChunks: Database.Statement<\n [string, string, string, number, string, bigint, number]\n >;\n getPrecedingSiblings: Database.Statement<\n [string, string, string, bigint, string, number]\n >;\n getSubsequentSiblings: Database.Statement<\n [string, string, string, bigint, string, number]\n >;\n getParentChunk: Database.Statement<[string, string, string, string, bigint]>;\n insertLibrary: Database.Statement<[string]>;\n getLibraryIdByName: Database.Statement<[string]>;\n // New version-related statements\n insertVersion: Database.Statement<[number, string | null]>;\n resolveVersionId: Database.Statement<[number, string | null]>;\n getVersionById: Database.Statement<[number]>;\n queryVersionsByLibraryId: Database.Statement<[number]>;\n // Status tracking statements\n updateVersionStatus: Database.Statement<[string, string | null, number]>;\n updateVersionProgress: Database.Statement<[number, number, number]>;\n getVersionsByStatus: Database.Statement<string[]>;\n // Scraper options statements\n updateVersionScraperOptions: Database.Statement<[string, string, number]>;\n getVersionWithOptions: Database.Statement<[number]>;\n getVersionsBySourceUrl: Database.Statement<[string]>;\n };\n\n /**\n * Calculates Reciprocal Rank Fusion score for a result\n */\n private calculateRRF(vecRank?: number, ftsRank?: number, k = 60): number {\n let rrf = 0;\n if (vecRank !== undefined) {\n rrf += 1 / (k + vecRank);\n }\n if (ftsRank !== undefined) {\n rrf += 1 / (k + ftsRank);\n }\n return rrf;\n }\n\n /**\n * Assigns ranks to search results based on their scores\n */\n private assignRanks(results: RawSearchResult[]): RankedResult[] {\n // Create maps to store ranks\n const vecRanks = new Map<number, number>();\n const ftsRanks = new Map<number, number>();\n\n // Sort by vector scores and assign ranks\n results\n .filter((r) => r.vec_score !== undefined)\n .sort((a, b) => (b.vec_score ?? 0) - (a.vec_score ?? 0))\n .forEach((result, index) => {\n vecRanks.set(Number(result.id), index + 1);\n });\n\n // Sort by BM25 scores and assign ranks\n results\n .filter((r) => r.fts_score !== undefined)\n .sort((a, b) => (b.fts_score ?? 0) - (a.fts_score ?? 0))\n .forEach((result, index) => {\n ftsRanks.set(Number(result.id), index + 1);\n });\n\n // Combine results with ranks and calculate RRF\n return results.map((result) => ({\n ...result,\n vec_rank: vecRanks.get(Number(result.id)),\n fts_rank: ftsRanks.get(Number(result.id)),\n rrf_score: this.calculateRRF(\n vecRanks.get(Number(result.id)),\n ftsRanks.get(Number(result.id)),\n ),\n }));\n }\n\n constructor(dbPath: string) {\n if (!dbPath) {\n throw new StoreError(\"Missing required database path\");\n }\n\n // Only establish database connection in constructor\n this.db = new Database(dbPath);\n }\n\n /**\n * Sets up prepared statements for database queries\n */\n private prepareStatements(): void {\n const statements = {\n getById: this.db.prepare<[bigint]>(\"SELECT * FROM documents WHERE id = ?\"),\n insertDocument: this.db.prepare<\n [bigint, bigint, string, string, string, number, string]\n >(\n \"INSERT INTO documents (library_id, version_id, url, content, metadata, sort_order, indexed_at) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n ),\n insertEmbedding: this.db.prepare<[bigint, bigint, bigint, string]>(\n \"INSERT INTO documents_vec (rowid, library_id, version_id, embedding) VALUES (?, ?, ?, ?)\",\n ),\n insertLibrary: this.db.prepare<[string]>(\n \"INSERT INTO libraries (name) VALUES (?) ON CONFLICT(name) DO NOTHING\",\n ),\n getLibraryIdByName: this.db.prepare<[string]>(\n \"SELECT id FROM libraries WHERE name = ?\",\n ),\n // New version-related statements\n insertVersion: this.db.prepare<[number, string | null]>(\n \"INSERT INTO versions (library_id, name, status) VALUES (?, ?, 'not_indexed') ON CONFLICT(library_id, name) DO NOTHING\",\n ),\n resolveVersionId: this.db.prepare<[number, string | null]>(\n \"SELECT id FROM versions WHERE library_id = ? AND name IS ?\",\n ),\n getVersionById: this.db.prepare<[number]>(\"SELECT * FROM versions WHERE id = ?\"),\n queryVersionsByLibraryId: this.db.prepare<[number]>(\n \"SELECT * FROM versions WHERE library_id = ? ORDER BY name\",\n ),\n deleteLibraryDocuments: this.db.prepare<[string, string, string]>(\n `DELETE FROM documents\n WHERE library_id = (SELECT id FROM libraries WHERE name = ?)\n AND version_id = (\n SELECT v.id FROM versions v \n WHERE v.library_id = (SELECT id FROM libraries WHERE name = ?)\n AND COALESCE(v.name, '') = COALESCE(?, '')\n )`,\n ),\n deleteDocuments: this.db.prepare<[string, string, string]>(\n `DELETE FROM documents\n WHERE library_id = (SELECT id FROM libraries WHERE name = ?)\n AND version_id = (\n SELECT v.id FROM versions v \n WHERE v.library_id = (SELECT id FROM libraries WHERE name = ?)\n AND COALESCE(v.name, '') = COALESCE(?, '')\n )`,\n ),\n deleteDocumentsByUrl: this.db.prepare<[string, string, string, string]>(\n `DELETE FROM documents\n WHERE url = ?\n AND library_id = (SELECT id FROM libraries WHERE name = ?)\n AND version_id = (\n SELECT v.id FROM versions v \n WHERE v.library_id = (SELECT id FROM libraries WHERE name = ?)\n AND COALESCE(v.name, '') = COALESCE(?, '')\n )`,\n ),\n getDocumentBySort: this.db.prepare<[string, string]>(\n `SELECT d.id\n FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n LIMIT 1`,\n ),\n queryVersions: this.db.prepare<[string]>(\n `SELECT DISTINCT v.name\n FROM versions v\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n ORDER BY v.name`,\n ),\n checkExists: this.db.prepare<[string, string]>(\n `SELECT d.id FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n LIMIT 1`,\n ),\n // Library/version aggregation including versions without documents and status/progress fields\n queryLibraryVersions: this.db.prepare<[]>(\n `SELECT\n l.name as library,\n COALESCE(v.name, '') as version,\n v.id as versionId,\n v.status as status,\n v.progress_pages as progressPages,\n v.progress_max_pages as progressMaxPages,\n v.source_url as sourceUrl,\n MIN(d.indexed_at) as indexedAt,\n COUNT(d.id) as documentCount,\n COUNT(DISTINCT d.url) as uniqueUrlCount\n FROM versions v\n JOIN libraries l ON v.library_id = l.id\n LEFT JOIN documents d ON d.version_id = v.id\n GROUP BY v.id\n ORDER BY l.name, version`,\n ),\n getChildChunks: this.db.prepare<\n [string, string, string, number, string, bigint, number]\n >(`\n SELECT d.* FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n AND json_array_length(json_extract(d.metadata, '$.path')) = ?\n AND json_extract(d.metadata, '$.path') LIKE ? || '%'\n AND d.sort_order > (SELECT sort_order FROM documents WHERE id = ?)\n ORDER BY d.sort_order\n LIMIT ?\n `),\n getPrecedingSiblings: this.db.prepare<\n [string, string, string, bigint, string, number]\n >(`\n SELECT d.* FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n AND d.sort_order < (SELECT sort_order FROM documents WHERE id = ?)\n AND json_extract(d.metadata, '$.path') = ?\n ORDER BY d.sort_order DESC\n LIMIT ?\n `),\n getSubsequentSiblings: this.db.prepare<\n [string, string, string, bigint, string, number]\n >(`\n SELECT d.* FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n AND d.sort_order > (SELECT sort_order FROM documents WHERE id = ?)\n AND json_extract(d.metadata, '$.path') = ?\n ORDER BY d.sort_order\n LIMIT ?\n `),\n getParentChunk: this.db.prepare<[string, string, string, string, bigint]>(`\n SELECT d.* FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n AND json_extract(d.metadata, '$.path') = ?\n AND d.sort_order < (SELECT sort_order FROM documents WHERE id = ?)\n ORDER BY d.sort_order DESC\n LIMIT 1\n `),\n // Status tracking statements\n updateVersionStatus: this.db.prepare<[string, string | null, number]>(\n \"UPDATE versions SET status = ?, error_message = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?\",\n ),\n updateVersionProgress: this.db.prepare<[number, number, number]>(\n \"UPDATE versions SET progress_pages = ?, progress_max_pages = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?\",\n ),\n getVersionsByStatus: this.db.prepare<[string]>(\n \"SELECT v.*, l.name as library_name FROM versions v JOIN libraries l ON v.library_id = l.id WHERE v.status IN (SELECT value FROM json_each(?))\",\n ),\n // Scraper options statements\n updateVersionScraperOptions: this.db.prepare<[string, string, number]>(\n \"UPDATE versions SET source_url = ?, scraper_options = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?\",\n ),\n getVersionWithOptions: this.db.prepare<[number]>(\n \"SELECT * FROM versions WHERE id = ?\",\n ),\n getVersionsBySourceUrl: this.db.prepare<[string]>(\n \"SELECT v.*, l.name as library_name FROM versions v JOIN libraries l ON v.library_id = l.id WHERE v.source_url = ? ORDER BY v.created_at DESC\",\n ),\n };\n this.statements = statements;\n }\n\n /**\n * Pads a vector to the fixed database dimension by appending zeros.\n * Throws an error if the input vector is longer than the database dimension.\n */\n private padVector(vector: number[]): number[] {\n if (vector.length > this.dbDimension) {\n throw new Error(\n `Vector dimension ${vector.length} exceeds database dimension ${this.dbDimension}`,\n );\n }\n if (vector.length === this.dbDimension) {\n return vector;\n }\n return [...vector, ...new Array(this.dbDimension - vector.length).fill(0)];\n }\n\n /**\n * Initializes embeddings client using environment variables for configuration.\n *\n * The embedding model is configured using DOCS_MCP_EMBEDDING_MODEL environment variable.\n * Format: \"provider:model_name\" (e.g., \"google:text-embedding-004\") or just \"model_name\"\n * for OpenAI (default).\n *\n * Supported providers and their required environment variables:\n * - openai: OPENAI_API_KEY (and optionally OPENAI_API_BASE, OPENAI_ORG_ID)\n * - google: GOOGLE_APPLICATION_CREDENTIALS (path to service account JSON)\n * - aws: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION (or BEDROCK_AWS_REGION)\n * - microsoft: Azure OpenAI credentials (AZURE_OPENAI_API_*)\n */\n private async initializeEmbeddings(): Promise<void> {\n const modelSpec = process.env.DOCS_MCP_EMBEDDING_MODEL || \"text-embedding-3-small\";\n\n // Import dynamically to avoid circular dependencies\n const { createEmbeddingModel } = await import(\"./embeddings/EmbeddingFactory\");\n this.embeddings = createEmbeddingModel(modelSpec);\n\n // Determine the model's actual dimension by embedding a test string\n const testVector = await this.embeddings.embedQuery(\"test\");\n this.modelDimension = testVector.length;\n\n if (this.modelDimension > this.dbDimension) {\n throw new DimensionError(modelSpec, this.modelDimension, this.dbDimension);\n }\n }\n\n /**\n * Escapes a query string for use with SQLite FTS5 MATCH operator.\n * Wraps the query in double quotes and escapes internal double quotes.\n */\n private escapeFtsQuery(query: string): string {\n // Escape internal double quotes by doubling them\n const escapedQuotes = query.replace(/\"/g, '\"\"');\n // Wrap the entire string in double quotes\n return `\"${escapedQuotes}\"`;\n }\n\n /**\n * Initializes database connection and ensures readiness\n */\n async initialize(): Promise<void> {\n try {\n // 1. Load extensions first (moved before migrations)\n sqliteVec.load(this.db);\n\n // 2. Apply migrations (after extensions are loaded)\n applyMigrations(this.db);\n\n // 3. Initialize prepared statements\n this.prepareStatements();\n\n // 4. Initialize embeddings client (await to catch errors)\n await this.initializeEmbeddings();\n } catch (error) {\n // Re-throw StoreError directly, wrap others in ConnectionError\n if (error instanceof StoreError) {\n throw error;\n }\n throw new ConnectionError(\"Failed to initialize database connection\", error);\n }\n }\n\n /**\n * Gracefully closes database connections\n */\n async shutdown(): Promise<void> {\n this.db.close();\n }\n\n /**\n * Resolves a library name and version string to library_id and version_id.\n * Creates library and version records if they don't exist.\n */\n async resolveLibraryAndVersionIds(\n library: string,\n version: string,\n ): Promise<{ libraryId: number; versionId: number }> {\n const normalizedLibrary = library.toLowerCase();\n const normalizedVersion = denormalizeVersionName(version.toLowerCase());\n\n // Insert or get library_id\n this.statements.insertLibrary.run(normalizedLibrary);\n const libraryIdRow = this.statements.getLibraryIdByName.get(normalizedLibrary) as\n | { id: number }\n | undefined;\n if (!libraryIdRow || typeof libraryIdRow.id !== \"number\") {\n throw new StoreError(`Failed to resolve library_id for library: ${library}`);\n }\n const libraryId = libraryIdRow.id;\n\n // Insert or get version_id\n // Reuse existing unversioned entry if present; storing '' ensures UNIQUE constraint applies\n this.statements.insertVersion.run(libraryId, normalizedVersion);\n const versionIdRow = this.statements.resolveVersionId.get(\n libraryId,\n normalizedVersion === null ? \"\" : normalizedVersion,\n ) as { id: number } | undefined;\n if (!versionIdRow || typeof versionIdRow.id !== \"number\") {\n throw new StoreError(\n `Failed to resolve version_id for library: ${library}, version: ${version}`,\n );\n }\n\n return { libraryId, versionId: versionIdRow.id };\n }\n\n /**\n * Retrieves all unique versions for a specific library\n */\n async queryUniqueVersions(library: string): Promise<string[]> {\n try {\n const rows = this.statements.queryVersions.all(library.toLowerCase()) as Array<{\n name: string | null;\n }>;\n return rows.map((row) => normalizeVersionName(row.name));\n } catch (error) {\n throw new ConnectionError(\"Failed to query versions\", error);\n }\n }\n\n /**\n * Updates the status of a version record in the database.\n * @param versionId The version ID to update\n * @param status The new status to set\n * @param errorMessage Optional error message for failed statuses\n */\n async updateVersionStatus(\n versionId: number,\n status: VersionStatus,\n errorMessage?: string,\n ): Promise<void> {\n try {\n this.statements.updateVersionStatus.run(status, errorMessage ?? null, versionId);\n } catch (error) {\n throw new StoreError(`Failed to update version status: ${error}`);\n }\n }\n\n /**\n * Updates the progress counters for a version being indexed.\n * @param versionId The version ID to update\n * @param pages Current number of pages processed\n * @param maxPages Total number of pages to process\n */\n async updateVersionProgress(\n versionId: number,\n pages: number,\n maxPages: number,\n ): Promise<void> {\n try {\n this.statements.updateVersionProgress.run(pages, maxPages, versionId);\n } catch (error) {\n throw new StoreError(`Failed to update version progress: ${error}`);\n }\n }\n\n /**\n * Retrieves versions by their status.\n * @param statuses Array of statuses to filter by\n * @returns Array of version records matching the statuses\n */\n async getVersionsByStatus(statuses: VersionStatus[]): Promise<DbVersionWithLibrary[]> {\n try {\n const statusJson = JSON.stringify(statuses);\n const rows = this.statements.getVersionsByStatus.all(\n statusJson,\n ) as DbVersionWithLibrary[];\n return rows;\n } catch (error) {\n throw new StoreError(`Failed to get versions by status: ${error}`);\n }\n }\n\n /**\n * Stores scraper options for a version to enable reproducible indexing.\n * @param versionId The version ID to update\n * @param options Complete scraper options used for indexing\n */\n async storeScraperOptions(versionId: number, options: ScraperOptions): Promise<void> {\n try {\n // biome-ignore lint/correctness/noUnusedVariables: Extract source URL and exclude runtime-only fields using destructuring\n const { url: source_url, library, version, signal, ...scraper_options } = options;\n\n const optionsJson = JSON.stringify(scraper_options);\n this.statements.updateVersionScraperOptions.run(source_url, optionsJson, versionId);\n } catch (error) {\n throw new StoreError(`Failed to store scraper options: ${error}`);\n }\n }\n\n /**\n * Retrieves stored scraping configuration (source URL and options) for a version.\n * Returns null when no source URL is recorded (not re-indexable).\n */\n async getScraperOptions(versionId: number): Promise<StoredScraperOptions | null> {\n try {\n const row = this.statements.getVersionWithOptions.get(versionId) as\n | DbVersion\n | undefined;\n\n if (!row?.source_url) {\n return null;\n }\n\n let parsed: VersionScraperOptions = {} as VersionScraperOptions;\n if (row.scraper_options) {\n try {\n parsed = JSON.parse(row.scraper_options) as VersionScraperOptions;\n } catch (e) {\n logger.warn(`⚠️ Invalid scraper_options JSON for version ${versionId}: ${e}`);\n parsed = {} as VersionScraperOptions;\n }\n }\n\n return { sourceUrl: row.source_url, options: parsed };\n } catch (error) {\n throw new StoreError(`Failed to get scraper options: ${error}`);\n }\n }\n\n /**\n * Finds versions that were indexed from the same source URL.\n * Useful for finding similar configurations or detecting duplicates.\n * @param url Source URL to search for\n * @returns Array of versions with the same source URL\n */\n async findVersionsBySourceUrl(url: string): Promise<DbVersionWithLibrary[]> {\n try {\n const rows = this.statements.getVersionsBySourceUrl.all(\n url,\n ) as DbVersionWithLibrary[];\n return rows;\n } catch (error) {\n throw new StoreError(`Failed to find versions by source URL: ${error}`);\n }\n }\n\n /**\n * Verifies existence of documents for a specific library version\n */\n async checkDocumentExists(library: string, version: string): Promise<boolean> {\n try {\n const normalizedVersion = version.toLowerCase();\n const result = this.statements.checkExists.get(\n library.toLowerCase(),\n normalizedVersion,\n );\n return result !== undefined;\n } catch (error) {\n throw new ConnectionError(\"Failed to check document existence\", error);\n }\n }\n\n /**\n * Retrieves a mapping of all libraries to their available versions with details.\n */\n async queryLibraryVersions(): Promise<\n Map<\n string,\n Array<{\n version: string;\n versionId: number;\n status: VersionStatus; // Persisted enum value\n progressPages: number;\n progressMaxPages: number;\n sourceUrl: string | null;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null;\n }>\n >\n > {\n try {\n // Define the expected row structure from the GROUP BY query (including versions without documents)\n interface LibraryVersionRow {\n library: string;\n version: string;\n versionId: number;\n status: VersionStatus;\n progressPages: number;\n progressMaxPages: number;\n sourceUrl: string | null;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null; // MIN() may return null\n }\n\n const rows = this.statements.queryLibraryVersions.all() as LibraryVersionRow[];\n const libraryMap = new Map<\n string,\n Array<{\n version: string;\n versionId: number;\n status: VersionStatus;\n progressPages: number;\n progressMaxPages: number;\n sourceUrl: string | null;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null;\n }>\n >();\n\n for (const row of rows) {\n // Process all rows, including those where version is \"\" (unversioned)\n const library = row.library;\n if (!libraryMap.has(library)) {\n libraryMap.set(library, []);\n }\n\n // Format indexedAt to ISO string if available\n const indexedAtISO = row.indexedAt ? new Date(row.indexedAt).toISOString() : null;\n\n libraryMap.get(library)?.push({\n version: row.version,\n versionId: row.versionId,\n // Preserve raw string status here; DocumentManagementService will cast to VersionStatus\n status: row.status,\n progressPages: row.progressPages,\n progressMaxPages: row.progressMaxPages,\n sourceUrl: row.sourceUrl,\n documentCount: row.documentCount,\n uniqueUrlCount: row.uniqueUrlCount,\n indexedAt: indexedAtISO,\n });\n }\n\n // Sort versions within each library: unversioned first, then semantically\n for (const versions of libraryMap.values()) {\n versions.sort((a, b) => {\n if (a.version === \"\" && b.version !== \"\") {\n return -1; // a (unversioned) comes first\n }\n if (a.version !== \"\" && b.version === \"\") {\n return 1; // b (unversioned) comes first\n }\n if (a.version === \"\" && b.version === \"\") {\n return 0; // Should not happen with GROUP BY, but handle anyway\n }\n // Both are non-empty, use semver compare with fallback to string compare\n try {\n return semver.compare(a.version, b.version);\n } catch (_error) {\n // Fallback to lexicographic comparison if semver parsing fails\n return a.version.localeCompare(b.version);\n }\n });\n }\n\n return libraryMap;\n } catch (error) {\n throw new ConnectionError(\"Failed to query library versions\", error);\n }\n }\n\n /**\n * Stores documents with library and version metadata, generating embeddings\n * for vector similarity search. Automatically removes any existing documents\n * for the same URLs before adding new ones to prevent UNIQUE constraint violations.\n */\n async addDocuments(\n library: string,\n version: string,\n documents: Document[],\n ): Promise<void> {\n try {\n if (documents.length === 0) {\n return;\n }\n\n // Extract unique URLs from the documents being added\n const urls = new Set<string>();\n for (const doc of documents) {\n const url = doc.metadata.url as string;\n if (!url || typeof url !== \"string\" || !url.trim()) {\n throw new StoreError(\"Document metadata must include a valid URL\");\n }\n urls.add(url);\n }\n\n // Generate embeddings in batch\n const texts = documents.map((doc) => {\n const header = `<title>${doc.metadata.title}</title>\\n<url>${doc.metadata.url}</url>\\n<path>${doc.metadata.path.join(\" / \")}</path>\\n`;\n return `${header}${doc.pageContent}`;\n });\n\n // Batch embedding creation to avoid token limit errors\n // Use size-based batching to prevent 413 errors\n const maxBatchChars =\n Number(process.env.DOCS_MCP_EMBEDDING_BATCH_CHARS) || EMBEDDING_BATCH_CHARS;\n const rawEmbeddings: number[][] = [];\n\n let currentBatch: string[] = [];\n let currentBatchSize = 0;\n let batchCount = 0;\n\n for (const text of texts) {\n const textSize = text.length;\n\n // If adding this text would exceed the limit, process the current batch first\n if (currentBatchSize + textSize > maxBatchChars && currentBatch.length > 0) {\n batchCount++;\n logger.debug(\n `🔄 Processing embedding batch ${batchCount}: ${currentBatch.length} texts, ${currentBatchSize} chars`,\n );\n const batchEmbeddings = await this.embeddings.embedDocuments(currentBatch);\n rawEmbeddings.push(...batchEmbeddings);\n currentBatch = [];\n currentBatchSize = 0;\n }\n\n // Add text to current batch\n currentBatch.push(text);\n currentBatchSize += textSize;\n\n // Also respect the count-based limit for APIs that have per-request item limits\n if (currentBatch.length >= EMBEDDING_BATCH_SIZE) {\n batchCount++;\n logger.debug(\n `🔄 Processing embedding batch ${batchCount}: ${currentBatch.length} texts, ${currentBatchSize} chars`,\n );\n const batchEmbeddings = await this.embeddings.embedDocuments(currentBatch);\n rawEmbeddings.push(...batchEmbeddings);\n currentBatch = [];\n currentBatchSize = 0;\n }\n }\n\n // Process any remaining texts in the final batch\n if (currentBatch.length > 0) {\n batchCount++;\n logger.debug(\n `🔄 Processing final embedding batch ${batchCount}: ${currentBatch.length} texts, ${currentBatchSize} chars`,\n );\n const batchEmbeddings = await this.embeddings.embedDocuments(currentBatch);\n rawEmbeddings.push(...batchEmbeddings);\n }\n const paddedEmbeddings = rawEmbeddings.map((vector) => this.padVector(vector));\n\n // Resolve library and version IDs (creates them if they don't exist)\n const { libraryId, versionId } = await this.resolveLibraryAndVersionIds(\n library,\n version,\n );\n\n // Delete existing documents for these URLs to prevent UNIQUE constraint violations\n // This must happen AFTER resolveLibraryAndVersionIds to ensure the library/version exist\n for (const url of urls) {\n const deletedCount = await this.deleteDocumentsByUrl(library, version, url);\n if (deletedCount > 0) {\n logger.debug(`🗑️ Deleted ${deletedCount} existing documents for URL: ${url}`);\n }\n }\n\n // Insert documents in a transaction\n const transaction = this.db.transaction((docs: typeof documents) => {\n for (let i = 0; i < docs.length; i++) {\n const doc = docs[i];\n const url = doc.metadata.url as string;\n\n // Insert into main documents table using foreign key IDs\n const result = this.statements.insertDocument.run(\n BigInt(libraryId),\n BigInt(versionId),\n url,\n doc.pageContent,\n JSON.stringify(doc.metadata),\n i,\n new Date().toISOString(), // Pass current timestamp for indexed_at\n );\n const rowId = result.lastInsertRowid;\n\n // Insert into vector table using foreign key IDs\n this.statements.insertEmbedding.run(\n BigInt(rowId),\n BigInt(libraryId),\n BigInt(versionId),\n JSON.stringify(paddedEmbeddings[i]),\n );\n }\n });\n\n transaction(documents);\n } catch (error) {\n throw new ConnectionError(\"Failed to add documents to store\", error);\n }\n }\n\n /**\n * Removes documents matching specified library and version\n * @returns Number of documents deleted\n */\n async deleteDocuments(library: string, version: string): Promise<number> {\n try {\n const normalizedVersion = version.toLowerCase();\n const result = this.statements.deleteDocuments.run(\n library.toLowerCase(),\n library.toLowerCase(), // library name appears twice in the query\n normalizedVersion,\n );\n return result.changes;\n } catch (error) {\n throw new ConnectionError(\"Failed to delete documents\", error);\n }\n }\n\n /**\n * Removes documents for a specific URL within a library and version\n * @returns Number of documents deleted\n */\n async deleteDocumentsByUrl(\n library: string,\n version: string,\n url: string,\n ): Promise<number> {\n try {\n const normalizedVersion = version.toLowerCase();\n const result = this.statements.deleteDocumentsByUrl.run(\n url,\n library.toLowerCase(),\n library.toLowerCase(), // library name appears twice in the query\n normalizedVersion,\n );\n return result.changes;\n } catch (error) {\n throw new ConnectionError(\"Failed to delete documents by URL\", error);\n }\n }\n\n /**\n * Retrieves a document by its ID.\n * @param id The ID of the document.\n * @returns The document, or null if not found.\n */\n async getById(id: string): Promise<Document | null> {\n try {\n const row = this.statements.getById.get(BigInt(id)) as DbQueryResult<DbDocument>;\n if (!row) {\n return null;\n }\n\n return mapDbDocumentToDocument(row);\n } catch (error) {\n throw new ConnectionError(`Failed to get document by ID ${id}`, error);\n }\n }\n\n /**\n * Finds documents matching a text query using hybrid search.\n * Combines vector similarity search with full-text search using Reciprocal Rank Fusion.\n */\n async findByContent(\n library: string,\n version: string,\n query: string,\n limit: number,\n ): Promise<Document[]> {\n try {\n const rawEmbedding = await this.embeddings.embedQuery(query);\n const embedding = this.padVector(rawEmbedding);\n const ftsQuery = this.escapeFtsQuery(query); // Escape the query for FTS\n const normalizedVersion = version.toLowerCase();\n\n const stmt = this.db.prepare(`\n WITH vec_distances AS (\n SELECT\n dv.rowid as id,\n dv.distance as vec_distance\n FROM documents_vec dv\n JOIN versions v ON dv.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND dv.embedding MATCH ?\n AND dv.k = ?\n ORDER BY dv.distance\n ),\n fts_scores AS (\n SELECT\n f.rowid as id,\n bm25(documents_fts, 10.0, 1.0, 5.0, 1.0) as fts_score\n FROM documents_fts f\n JOIN documents d ON f.rowid = d.id\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND documents_fts MATCH ?\n ORDER BY fts_score\n LIMIT ?\n )\n SELECT\n d.id,\n d.content,\n d.metadata,\n COALESCE(1 / (1 + v.vec_distance), 0) as vec_score,\n COALESCE(-MIN(f.fts_score, 0), 0) as fts_score\n FROM documents d\n LEFT JOIN vec_distances v ON d.id = v.id\n LEFT JOIN fts_scores f ON d.id = f.id\n WHERE v.id IS NOT NULL OR f.id IS NOT NULL\n `);\n\n const rawResults = stmt.all(\n library.toLowerCase(),\n normalizedVersion,\n JSON.stringify(embedding),\n limit,\n library.toLowerCase(),\n normalizedVersion,\n ftsQuery, // Use the escaped query\n limit,\n ) as RawSearchResult[];\n\n // Apply RRF ranking\n const rankedResults = this.assignRanks(rawResults);\n\n // Sort by RRF score and take top results\n const topResults = rankedResults\n .sort((a, b) => b.rrf_score - a.rrf_score)\n .slice(0, limit);\n\n return topResults.map((row) => ({\n ...mapDbDocumentToDocument(row),\n metadata: {\n ...JSON.parse(row.metadata),\n id: row.id,\n score: row.rrf_score,\n vec_rank: row.vec_rank,\n fts_rank: row.fts_rank,\n },\n }));\n } catch (error) {\n throw new ConnectionError(\n `Failed to find documents by content with query \"${query}\"`,\n error,\n );\n }\n }\n\n /**\n * Finds child chunks of a given document based on path hierarchy.\n */\n async findChildChunks(\n library: string,\n version: string,\n id: string,\n limit: number,\n ): Promise<Document[]> {\n try {\n const parent = await this.getById(id);\n if (!parent) {\n return [];\n }\n\n const parentPath = (parent.metadata as DocumentMetadata).path ?? [];\n const parentUrl = (parent.metadata as DocumentMetadata).url;\n const normalizedVersion = version.toLowerCase();\n\n const result = this.statements.getChildChunks.all(\n library.toLowerCase(),\n normalizedVersion,\n parentUrl,\n parentPath.length + 1,\n JSON.stringify(parentPath),\n BigInt(id),\n limit,\n ) as Array<DbDocument>;\n\n return result.map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(`Failed to find child chunks for ID ${id}`, error);\n }\n }\n\n /**\n * Finds preceding sibling chunks of a given document.\n */\n async findPrecedingSiblingChunks(\n library: string,\n version: string,\n id: string,\n limit: number,\n ): Promise<Document[]> {\n try {\n const reference = await this.getById(id);\n if (!reference) {\n return [];\n }\n\n const refMetadata = reference.metadata as DocumentMetadata;\n const normalizedVersion = version.toLowerCase();\n\n const result = this.statements.getPrecedingSiblings.all(\n library.toLowerCase(),\n normalizedVersion,\n refMetadata.url,\n BigInt(id),\n JSON.stringify(refMetadata.path),\n limit,\n ) as Array<DbDocument>;\n\n return result.reverse().map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(\n `Failed to find preceding sibling chunks for ID ${id}`,\n error,\n );\n }\n }\n\n /**\n * Finds subsequent sibling chunks of a given document.\n */\n async findSubsequentSiblingChunks(\n library: string,\n version: string,\n id: string,\n limit: number,\n ): Promise<Document[]> {\n try {\n const reference = await this.getById(id);\n if (!reference) {\n return [];\n }\n\n const refMetadata = reference.metadata;\n const normalizedVersion = version.toLowerCase();\n\n const result = this.statements.getSubsequentSiblings.all(\n library.toLowerCase(),\n normalizedVersion,\n refMetadata.url,\n BigInt(id),\n JSON.stringify(refMetadata.path),\n limit,\n ) as Array<DbDocument>;\n\n return result.map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(\n `Failed to find subsequent sibling chunks for ID ${id}`,\n error,\n );\n }\n }\n\n /**\n * Finds the parent chunk of a given document.\n */\n async findParentChunk(\n library: string,\n version: string,\n id: string,\n ): Promise<Document | null> {\n try {\n const child = await this.getById(id);\n if (!child) {\n return null;\n }\n\n const childMetadata = child.metadata as DocumentMetadata;\n const path = childMetadata.path ?? [];\n const parentPath = path.slice(0, -1);\n\n if (parentPath.length === 0) {\n return null;\n }\n\n const normalizedVersion = version.toLowerCase();\n const result = this.statements.getParentChunk.get(\n library.toLowerCase(),\n normalizedVersion,\n childMetadata.url,\n JSON.stringify(parentPath),\n BigInt(id),\n ) as DbQueryResult<DbDocument>;\n\n if (!result) {\n return null;\n }\n\n return mapDbDocumentToDocument(result);\n } catch (error) {\n throw new ConnectionError(`Failed to find parent chunk for ID ${id}`, error);\n }\n }\n\n /**\n * Fetches multiple documents by their IDs in a single call.\n * Returns an array of Document objects, sorted by their sort_order.\n */\n async findChunksByIds(\n library: string,\n version: string,\n ids: string[],\n ): Promise<Document[]> {\n if (!ids.length) return [];\n try {\n const normalizedVersion = version.toLowerCase();\n // Use parameterized query for variable number of IDs\n const placeholders = ids.map(() => \"?\").join(\",\");\n const stmt = this.db.prepare(\n `SELECT d.* FROM documents d\n JOIN libraries l ON d.library_id = l.id\n JOIN versions v ON d.version_id = v.id\n WHERE l.name = ? \n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.id IN (${placeholders}) \n ORDER BY d.sort_order`,\n );\n const rows = stmt.all(\n library.toLowerCase(),\n normalizedVersion,\n ...ids,\n ) as DbDocument[];\n return rows.map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(\"Failed to fetch documents by IDs\", error);\n }\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { Document } from \"@langchain/core/documents\";\nimport envPaths from \"env-paths\";\nimport Fuse from \"fuse.js\";\nimport semver from \"semver\";\nimport type { ScraperOptions } from \"../scraper/types\";\nimport { GreedySplitter, SemanticMarkdownSplitter } from \"../splitter\";\nimport type { ContentChunk, DocumentSplitter } from \"../splitter/types\";\nimport { LibraryNotFoundError, VersionNotFoundError } from \"../tools\";\nimport {\n SPLITTER_MAX_CHUNK_SIZE,\n SPLITTER_MIN_CHUNK_SIZE,\n SPLITTER_PREFERRED_CHUNK_SIZE,\n} from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\nimport { getProjectRoot } from \"../utils/paths\";\nimport { DocumentRetrieverService } from \"./DocumentRetrieverService\";\nimport { DocumentStore } from \"./DocumentStore\";\nimport { StoreError } from \"./errors\";\nimport type {\n DbVersionWithLibrary,\n FindVersionResult,\n LibrarySummary,\n ScraperConfig,\n StoreSearchResult,\n VersionRef,\n VersionStatus,\n VersionSummary,\n} from \"./types\";\n\n/**\n * Provides semantic search capabilities across different versions of library documentation.\n */\nexport class DocumentManagementService {\n private readonly store: DocumentStore;\n private readonly documentRetriever: DocumentRetrieverService;\n private readonly splitter: DocumentSplitter;\n\n /**\n * Normalizes a version string, converting null or undefined to an empty string\n * and converting to lowercase.\n */\n private normalizeVersion(version?: string | null): string {\n return (version ?? \"\").toLowerCase();\n }\n\n constructor() {\n let dbPath: string;\n let dbDir: string;\n\n // 1. Check Environment Variable\n const envStorePath = process.env.DOCS_MCP_STORE_PATH;\n if (envStorePath) {\n dbDir = envStorePath;\n dbPath = path.join(dbDir, \"documents.db\");\n logger.debug(`💾 Using database directory from DOCS_MCP_STORE_PATH: ${dbDir}`);\n } else {\n // 2. Check Old Local Path\n const projectRoot = getProjectRoot();\n const oldDbDir = path.join(projectRoot, \".store\");\n const oldDbPath = path.join(oldDbDir, \"documents.db\");\n const oldDbExists = fs.existsSync(oldDbPath); // Check file existence specifically\n\n if (oldDbExists) {\n dbPath = oldDbPath;\n dbDir = oldDbDir;\n logger.debug(`💾 Using legacy database path: ${dbPath}`);\n } else {\n // 3. Use Standard Path\n const standardPaths = envPaths(\"docs-mcp-server\", { suffix: \"\" });\n dbDir = standardPaths.data;\n dbPath = path.join(dbDir, \"documents.db\");\n logger.debug(`💾 Using standard database directory: ${dbDir}`);\n }\n }\n\n // Ensure the chosen directory exists\n try {\n fs.mkdirSync(dbDir, { recursive: true });\n } catch (error) {\n // Log potential error during directory creation but proceed\n // The DocumentStore constructor might handle DB file creation errors\n logger.error(`⚠️ Failed to create database directory ${dbDir}: ${error}`);\n }\n\n this.store = new DocumentStore(dbPath);\n this.documentRetriever = new DocumentRetrieverService(this.store);\n\n const semanticSplitter = new SemanticMarkdownSplitter(\n SPLITTER_PREFERRED_CHUNK_SIZE,\n SPLITTER_MAX_CHUNK_SIZE,\n );\n const greedySplitter = new GreedySplitter(\n semanticSplitter,\n SPLITTER_MIN_CHUNK_SIZE,\n SPLITTER_PREFERRED_CHUNK_SIZE,\n );\n\n this.splitter = greedySplitter;\n }\n\n /**\n * Initializes the underlying document store.\n */\n async initialize(): Promise<void> {\n await this.store.initialize();\n }\n\n /**\n * Shuts down the underlying document store.\n */\n\n async shutdown(): Promise<void> {\n logger.debug(\"Shutting down store manager\");\n await this.store.shutdown();\n }\n\n // Status tracking methods for pipeline integration\n\n /**\n * Gets versions by their current status.\n */\n async getVersionsByStatus(statuses: VersionStatus[]): Promise<DbVersionWithLibrary[]> {\n return this.store.getVersionsByStatus(statuses);\n }\n\n /**\n * Updates the status of a version.\n */\n async updateVersionStatus(\n versionId: number,\n status: VersionStatus,\n errorMessage?: string,\n ): Promise<void> {\n return this.store.updateVersionStatus(versionId, status, errorMessage);\n }\n\n /**\n * Updates the progress of a version being indexed.\n */\n async updateVersionProgress(\n versionId: number,\n pages: number,\n maxPages: number,\n ): Promise<void> {\n return this.store.updateVersionProgress(versionId, pages, maxPages);\n }\n\n /**\n * Stores scraper options for a version to enable reproducible indexing.\n */\n async storeScraperOptions(versionId: number, options: ScraperOptions): Promise<void> {\n return this.store.storeScraperOptions(versionId, options);\n }\n\n /**\n * Retrieves stored scraper options for a version.\n */\n /**\n * Retrieves stored scraping configuration for a version.\n */\n async getScraperOptions(versionId: number): Promise<ScraperConfig | null> {\n return this.store.getScraperOptions(versionId);\n }\n\n /**\n * Ensures a library/version exists using a VersionRef and returns version ID.\n * Delegates to existing ensureLibraryAndVersion for storage.\n */\n async ensureVersion(ref: VersionRef): Promise<number> {\n const normalized = {\n library: ref.library.trim().toLowerCase(),\n version: (ref.version ?? \"\").trim().toLowerCase(),\n };\n return this.ensureLibraryAndVersion(normalized.library, normalized.version);\n }\n\n /**\n * Returns enriched library summaries including version status/progress and counts.\n * Uses existing store APIs; keeps DB details encapsulated.\n */\n async listLibraries(): Promise<LibrarySummary[]> {\n const libMap = await this.store.queryLibraryVersions();\n const summaries: LibrarySummary[] = [];\n for (const [library, versions] of libMap) {\n const vs = versions.map(\n (v) =>\n ({\n id: v.versionId,\n ref: { library, version: v.version },\n status: v.status as VersionStatus,\n // Include progress only while indexing is active; set undefined for COMPLETED\n progress:\n v.status === \"completed\"\n ? undefined\n : { pages: v.progressPages, maxPages: v.progressMaxPages },\n counts: { documents: v.documentCount, uniqueUrls: v.uniqueUrlCount },\n indexedAt: v.indexedAt,\n sourceUrl: v.sourceUrl ?? undefined,\n }) satisfies VersionSummary,\n );\n summaries.push({ library, versions: vs });\n }\n return summaries;\n }\n\n /**\n * Finds versions that were indexed from the same source URL.\n */\n async findVersionsBySourceUrl(url: string): Promise<DbVersionWithLibrary[]> {\n return this.store.findVersionsBySourceUrl(url);\n }\n\n /**\n * Validates if a library exists in the store (either versioned or unversioned).\n * Throws LibraryNotFoundError with suggestions if the library is not found.\n * @param library The name of the library to validate.\n * @throws {LibraryNotFoundError} If the library does not exist.\n */\n async validateLibraryExists(library: string): Promise<void> {\n logger.info(`🔎 Validating existence of library: ${library}`);\n const normalizedLibrary = library.toLowerCase(); // Ensure consistent casing\n\n // Check for both versioned and unversioned documents\n const versions = await this.listVersions(normalizedLibrary);\n const hasUnversioned = await this.exists(normalizedLibrary, \"\"); // Check explicitly for unversioned\n\n if (versions.length === 0 && !hasUnversioned) {\n logger.warn(`⚠️ Library '${library}' not found.`);\n\n // Library doesn't exist, fetch all libraries to provide suggestions\n const allLibraries = await this.listLibraries();\n const libraryNames = allLibraries.map((lib) => lib.library);\n\n let suggestions: string[] = [];\n if (libraryNames.length > 0) {\n const fuse = new Fuse(libraryNames, {\n // Configure fuse.js options if needed (e.g., threshold)\n // isCaseSensitive: false, // Handled by normalizing library names\n // includeScore: true,\n threshold: 0.4, // Adjust threshold for desired fuzziness (0=exact, 1=match anything)\n });\n const results = fuse.search(normalizedLibrary);\n // Take top 3 suggestions\n suggestions = results.slice(0, 3).map((result) => result.item);\n logger.info(`🔍 Found suggestions: ${suggestions.join(\", \")}`);\n }\n\n throw new LibraryNotFoundError(library, suggestions);\n }\n\n logger.info(`✅ Library '${library}' confirmed to exist.`);\n }\n\n /**\n * Returns a list of all available semantic versions for a library.\n */\n async listVersions(library: string): Promise<string[]> {\n const versions = await this.store.queryUniqueVersions(library);\n return versions.filter((v) => semver.valid(v));\n }\n\n /**\n * Checks if documents exist for a given library and optional version.\n * If version is omitted, checks for documents without a specific version.\n */\n async exists(library: string, version?: string | null): Promise<boolean> {\n const normalizedVersion = this.normalizeVersion(version);\n return this.store.checkDocumentExists(library, normalizedVersion);\n }\n\n /**\n * Finds the most appropriate version of documentation based on the requested version.\n * When no target version is specified, returns the latest version.\n *\n * Version matching behavior:\n * - Exact versions (e.g., \"18.0.0\"): Matches that version or any earlier version\n * - X-Range patterns (e.g., \"5.x\", \"5.2.x\"): Matches within the specified range\n * - \"latest\" or no version: Returns the latest available version\n *\n * For documentation, we prefer matching older versions over no match at all,\n * since older docs are often still relevant and useful.\n * Also checks if unversioned documents exist for the library.\n */\n async findBestVersion(\n library: string,\n targetVersion?: string,\n ): Promise<FindVersionResult> {\n const libraryAndVersion = `${library}${targetVersion ? `@${targetVersion}` : \"\"}`;\n logger.info(`🔍 Finding best version for ${libraryAndVersion}`);\n\n // Check if unversioned documents exist *before* filtering for valid semver\n const hasUnversioned = await this.store.checkDocumentExists(library, \"\");\n const versionStrings = await this.listVersions(library);\n\n if (versionStrings.length === 0) {\n if (hasUnversioned) {\n logger.info(`ℹ️ Unversioned documents exist for ${library}`);\n return { bestMatch: null, hasUnversioned: true };\n }\n // Throw error only if NO versions (semver or unversioned) exist\n logger.warn(`⚠️ No valid versions found for ${library}`);\n // Fetch detailed versions to pass to the error constructor\n const allLibraryDetails = await this.store.queryLibraryVersions();\n const libraryDetails = allLibraryDetails.get(library) ?? [];\n throw new VersionNotFoundError(library, targetVersion ?? \"\", libraryDetails);\n }\n\n let bestMatch: string | null = null;\n\n if (!targetVersion || targetVersion === \"latest\") {\n bestMatch = semver.maxSatisfying(versionStrings, \"*\");\n } else {\n const versionRegex = /^(\\d+)(?:\\.(?:x(?:\\.x)?|\\d+(?:\\.(?:x|\\d+))?))?$|^$/;\n if (!versionRegex.test(targetVersion)) {\n logger.warn(`⚠️ Invalid target version format: ${targetVersion}`);\n // Don't throw yet, maybe unversioned exists\n } else {\n // Restore the previous logic with fallback\n let range = targetVersion;\n if (!semver.validRange(targetVersion)) {\n // If it's not a valid range (like '1.2' or '1'), treat it like a tilde range\n range = `~${targetVersion}`;\n } else if (semver.valid(targetVersion)) {\n // If it's an exact version, allow matching it OR any older version\n range = `${range} || <=${targetVersion}`;\n }\n // If it was already a valid range (like '1.x'), use it directly\n bestMatch = semver.maxSatisfying(versionStrings, range);\n }\n }\n\n if (bestMatch) {\n logger.info(`✅ Found best match version ${bestMatch} for ${libraryAndVersion}`);\n } else {\n logger.warn(`⚠️ No matching semver version found for ${libraryAndVersion}`);\n }\n\n // If no semver match found, but unversioned exists, return that info.\n // If a semver match was found, return it along with unversioned status.\n // If no semver match AND no unversioned, throw error.\n if (!bestMatch && !hasUnversioned) {\n // Fetch detailed versions to pass to the error constructor\n const allLibraryDetails = await this.store.queryLibraryVersions();\n const libraryDetails = allLibraryDetails.get(library) ?? [];\n throw new VersionNotFoundError(library, targetVersion ?? \"\", libraryDetails);\n }\n\n return { bestMatch, hasUnversioned };\n }\n\n /**\n * Removes all documents for a specific library and optional version.\n * If version is omitted, removes documents without a specific version.\n */\n async removeAllDocuments(library: string, version?: string | null): Promise<void> {\n const normalizedVersion = this.normalizeVersion(version);\n logger.info(\n `🗑️ Removing all documents from ${library}@${normalizedVersion || \"[no version]\"} store`,\n );\n const count = await this.store.deleteDocuments(library, normalizedVersion);\n logger.info(`📊 Deleted ${count} documents`);\n }\n\n /**\n * Adds a document to the store, splitting it into smaller chunks for better search results.\n * Uses SemanticMarkdownSplitter to maintain markdown structure and content types during splitting.\n * Preserves hierarchical structure of documents and distinguishes between text and code segments.\n * If version is omitted, the document is added without a specific version.\n */\n async addDocument(\n library: string,\n version: string | null | undefined,\n document: Document,\n ): Promise<void> {\n const normalizedVersion = this.normalizeVersion(version);\n const url = document.metadata.url as string;\n if (!url || typeof url !== \"string\" || !url.trim()) {\n throw new StoreError(\"Document metadata must include a valid URL\");\n }\n\n logger.info(`📚 Adding document: ${document.metadata.title}`);\n\n if (!document.pageContent.trim()) {\n throw new Error(\"Document content cannot be empty\");\n }\n\n // Split document into semantic chunks\n const chunks = await this.splitter.splitText(document.pageContent);\n\n // Convert semantic chunks to documents\n const splitDocs = chunks.map((chunk: ContentChunk) => ({\n pageContent: chunk.content,\n metadata: {\n ...document.metadata,\n level: chunk.section.level,\n path: chunk.section.path,\n },\n }));\n logger.info(`✂️ Split document into ${splitDocs.length} chunks`);\n\n // Add split documents to store\n await this.store.addDocuments(library, normalizedVersion, splitDocs);\n }\n\n /**\n * Searches for documentation content across versions.\n * Uses hybrid search (vector + FTS).\n * If version is omitted, searches documents without a specific version.\n */\n async searchStore(\n library: string,\n version: string | null | undefined,\n query: string,\n limit = 5,\n ): Promise<StoreSearchResult[]> {\n const normalizedVersion = this.normalizeVersion(version);\n return this.documentRetriever.search(library, normalizedVersion, query, limit);\n }\n\n // Deprecated simple listing removed: enriched listLibraries() is canonical\n\n /**\n * Ensures a library and version exist in the database and returns the version ID.\n * Creates the library and version records if they don't exist.\n */\n async ensureLibraryAndVersion(library: string, version: string): Promise<number> {\n // Use the same resolution logic as addDocuments but return the version ID\n const normalizedLibrary = library.toLowerCase();\n const normalizedVersion = this.normalizeVersion(version);\n\n // This will create the library and version if they don't exist\n const { versionId } = await this.store.resolveLibraryAndVersionIds(\n normalizedLibrary,\n normalizedVersion,\n );\n\n return versionId;\n }\n}\n"],"names":["chunks","semver","path"],"mappings":";;;;;;;;;;;;;;;;;;;;AAGO,MAAM,sBAAsB,MAAM;AAAC;AAMnC,MAAM,8BAA8B,cAAc;AAAA,EACvD,YAAY,MAAc,SAAiB;AACzC;AAAA,MACE,4EAA4E,IAAI,kCAAkC,OAAO;AAAA,IAAA;AAAA,EAE7H;AACF;AAKO,MAAM,6BAA6B,cAAc;AAAC;ACRlD,MAAM,eAA2C;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOR,YACE,cACA,cACA,oBACA;AACA,SAAK,eAAe;AACpB,SAAK,eAAe;AACpB,SAAK,qBAAqB;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,UAAU,UAA2C;AACzD,UAAM,gBAAgB,MAAM,KAAK,aAAa,UAAU,QAAQ;AAChE,UAAM,qBAAqC,CAAA;AAC3C,QAAI,eAAoC;AAExC,eAAW,aAAa,eAAe;AACrC,UAAI,cAAc;AAChB,YAAI,KAAK,mBAAmB,cAAc,SAAS,GAAG;AACpD,6BAAmB,KAAK,YAAY;AACpC,yBAAe,KAAK,WAAW,SAAS;AACxC;AAAA,QACF;AACA,YACE,aAAa,QAAQ,UAAU,KAAK,gBACpC,KAAK,sBAAsB,SAAS,GACpC;AACA,6BAAmB,KAAK,YAAY;AACpC,yBAAe,KAAK,WAAW,SAAS;AACxC;AAAA,QACF;AACA,qBAAa,WAAW;AAAA,EAAK,UAAU,OAAO;AAC9C,qBAAa,UAAU,KAAK,iBAAiB,cAAc,SAAS;AACpE,qBAAa,QAAQ,KAAK,WAAW,aAAa,OAAO,UAAU,KAAK;AAAA,MAC1E,OAAO;AACL,uBAAe,KAAK,WAAW,SAAS;AAAA,MAC1C;AAAA,IACF;AAEA,QAAI,cAAc;AAChB,yBAAmB,KAAK,YAAY;AAAA,IACtC;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,WAAW,OAAmC;AACpD,WAAO;AAAA,MACL,OAAO,CAAC,GAAG,MAAM,KAAK;AAAA,MACtB,SAAS,MAAM;AAAA,MACf,SAAS;AAAA,QACP,OAAO,MAAM,QAAQ;AAAA,QACrB,MAAM,CAAC,GAAG,MAAM,QAAQ,IAAI;AAAA,MAAA;AAAA,IAC9B;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBAAsB,OAA8B;AAC1D,WAAO,MAAM,QAAQ,UAAU,KAAK,MAAM,QAAQ,UAAU;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,mBACN,cACA,WACS;AACT,QAAI,CAAC,cAAc;AACjB,aAAO;AAAA,IACT;AACA,WACE,aAAa,QAAQ,SAAS,UAAU,QAAQ,SAAS,KAAK;AAAA,EAElE;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,YAAsB,WAA8B;AACzE,QAAI,WAAW,UAAU,UAAU,OAAQ,QAAO;AAClD,WAAO,WAAW,MAAM,CAAC,MAAM,MAAM,SAAS,UAAU,CAAC,CAAC;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,iBACN,cACA,WACyB;AAEzB,UAAM,QAAQ,KAAK,IAAI,aAAa,QAAQ,OAAO,UAAU,QAAQ,KAAK;AAG1E,QACE,aAAa,QAAQ,UAAU,UAAU,QAAQ,SACjD,aAAa,QAAQ,KAAK,WAAW,UAAU,QAAQ,KAAK,UAC5D,aAAa,QAAQ,KAAK,MAAM,CAAC,GAAG,MAAM,MAAM,UAAU,QAAQ,KAAK,CAAC,CAAC,GACzE;AACA,aAAO,aAAa;AAAA,IACtB;AAGA,QAAI,KAAK,eAAe,aAAa,QAAQ,MAAM,UAAU,QAAQ,IAAI,GAAG;AAC1E,aAAO;AAAA,QACL,MAAM,UAAU,QAAQ;AAAA,QACxB;AAAA,MAAA;AAAA,IAEJ;AAEA,QAAI,KAAK,eAAe,UAAU,QAAQ,MAAM,aAAa,QAAQ,IAAI,GAAG;AAC1E,aAAO;AAAA,QACL,MAAM,aAAa,QAAQ;AAAA,QAC3B;AAAA,MAAA;AAAA,IAEJ;AAGA,UAAM,aAAa,KAAK;AAAA,MACtB,aAAa,QAAQ;AAAA,MACrB,UAAU,QAAQ;AAAA,IAAA;AAGpB,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,IAAA;AAAA,EAEJ;AAAA,EAEQ,WACN,cACA,WACsB;AACtB,WAAO,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,cAAc,GAAG,SAAS,CAAC,CAAC;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,OAAiB,OAA2B;AACnE,UAAM,SAAmB,CAAA;AACzB,aAAS,IAAI,GAAG,IAAI,KAAK,IAAI,MAAM,QAAQ,MAAM,MAAM,GAAG,KAAK;AAC7D,UAAI,MAAM,CAAC,MAAM,MAAM,CAAC,GAAG;AACzB,eAAO,KAAK,MAAM,CAAC,CAAC;AAAA,MACtB,OAAO;AACL;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;ACxLO,MAAM,WAAW,CAAC,QAAwB;AAC/C,SAAO,IAAI,QAAQ,8BAA8B,EAAE;AACrD;ACEO,MAAM,oBAA+C;AAAA,EAC1D,YAAoB,SAAiC;AAAjC,SAAA,UAAA;AAAA,EAAkC;AAAA,EAEtD,MAAM,MAAM,SAAoC;AAE9C,UAAM,WAAW,QAAQ,MAAM,aAAa,IAAI,CAAC;AACjD,UAAM,kBAAkB,QAAQ,QAAQ,eAAe,EAAE,EAAE,QAAQ,WAAW,EAAE;AAEhF,UAAM,QAAQ,gBAAgB,MAAM,IAAI;AACxC,UAAM,SAAmB,CAAA;AACzB,QAAI,oBAA8B,CAAA;AAElC,eAAW,QAAQ,OAAO;AAExB,YAAM,iBAAiB,KAAK,KAAK,MAAM,QAAQ,EAAE;AACjD,UAAI,iBAAiB,KAAK,QAAQ,WAAW;AAC3C,cAAM,IAAI,sBAAsB,gBAAgB,KAAK,QAAQ,SAAS;AAAA,MACxE;AAEA,wBAAkB,KAAK,IAAI;AAC3B,YAAM,kBAAkB,KAAK,KAAK,kBAAkB,KAAK,IAAI,GAAG,QAAQ;AACxE,YAAM,eAAe,gBAAgB;AAErC,UAAI,eAAe,KAAK,QAAQ,aAAa,kBAAkB,SAAS,GAAG;AAEzE,cAAM,WAAW,kBAAkB,IAAA;AAEnC,eAAO,KAAK,KAAK,KAAK,kBAAkB,KAAK,IAAI,GAAG,QAAQ,CAAC;AAC7D,4BAAoB,CAAC,QAAkB;AAAA,MACzC;AAAA,IACF;AAEA,QAAI,kBAAkB,SAAS,GAAG;AAChC,aAAO,KAAK,KAAK,KAAK,kBAAkB,KAAK,IAAI,GAAG,QAAQ,CAAC;AAAA,IAC/D;AAEA,WAAO;AAAA,EACT;AAAA,EAEU,KAAK,SAAiB,UAAkC;AAChE,WAAO,SAAS,YAAY,EAAE;AAAA,EAAK,QAAQ,QAAQ,QAAQ,EAAE,CAAC;AAAA;AAAA,EAChE;AACF;AClCO,MAAM,qBAAgD;AAAA,EAC3D,YAAoB,SAAiC;AAAjC,SAAA,UAAA;AAAA,EAAkC;AAAA;AAAA;AAAA;AAAA,EAKtD,MAAM,MAAM,SAAoC;AAC9C,UAAM,cAAc,KAAK,WAAW,OAAO;AAC3C,QAAI,CAAC,aAAa;AAChB,aAAO,CAAC,OAAO;AAAA,IACjB;AAEA,UAAM,EAAE,SAAS,KAAA,IAAS;AAE1B,UAAM,SAAmB,CAAA;AACzB,QAAI,cAAwB,CAAA;AAE5B,eAAW,OAAO,MAAM;AAEtB,YAAM,gBAAgB,KAAK,KAAK,KAAK,OAAO,EAAE;AAC9C,UAAI,gBAAgB,KAAK,QAAQ,WAAW;AAC1C,cAAM,IAAI,sBAAsB,eAAe,KAAK,QAAQ,SAAS;AAAA,MACvE;AAEA,YAAM,kBAAkB,KAAK,KAAK,CAAC,GAAG,aAAa,GAAG,EAAE,KAAK,IAAI,GAAG,OAAO;AAC3E,YAAM,eAAe,gBAAgB;AACrC,UAAI,eAAe,KAAK,QAAQ,aAAa,YAAY,SAAS,GAAG;AAEnE,eAAO,KAAK,KAAK,KAAK,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC;AACtD,sBAAc,CAAC,GAAG;AAAA,MACpB,OAAO;AACL,oBAAY,KAAK,GAAG;AAAA,MACtB;AAAA,IACF;AAEA,QAAI,YAAY,SAAS,GAAG;AAC1B,aAAO,KAAK,KAAK,KAAK,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC;AAAA,IACxD;AAGA,WAAO;AAAA,EACT;AAAA,EAEU,KAAK,SAAiB,SAA2B;AACzD,UAAM,YAAY,KAAK,QAAQ,KAAK,KAAK,CAAC;AAC1C,UAAM,eAAe,IAAI,QAAQ,IAAI,MAAM,KAAK,EAAE,KAAK,GAAG,CAAC;AAC3D,WAAO,CAAC,WAAW,cAAc,OAAO,EAAE,KAAK,IAAI;AAAA,EACrD;AAAA,EAEQ,WAAW,SAAqC;AACtD,UAAM,QAAQ,QAAQ,KAAA,EAAO,MAAM,IAAI;AACvC,QAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,UAAM,UAAU,KAAK,SAAS,MAAM,CAAC,CAAC;AACtC,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,YAAY,MAAM,CAAC;AACzB,QAAI,CAAC,KAAK,iBAAiB,SAAS,EAAG,QAAO;AAE9C,UAAM,OAAO,MAAM,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,IAAI,KAAA,MAAW,EAAE;AAE7D,WAAO,EAAE,SAAS,WAAW,KAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKQ,SAAS,KAA8B;AAC7C,QAAI,CAAC,IAAI,SAAS,GAAG,EAAG,QAAO;AAC/B,WAAO,IACJ,MAAM,GAAG,EACT,IAAI,CAAC,SAAS,KAAK,KAAA,CAAM,EACzB,OAAO,CAAC,SAAS,SAAS,EAAE;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,WAA4B;AACnD,WAAO,UAAU,SAAS,GAAG,KAAK,kBAAkB,KAAK,SAAS;AAAA,EACpE;AACF;ACtFO,MAAM,oBAA+C;AAAA,EAC1D,YAAoB,SAAiC;AAAjC,SAAA,UAAA;AAAA,EAAkC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtD,MAAM,MAAM,SAAoC;AAC9C,UAAM,iBAAiB,SAAS,OAAO;AAEvC,QAAI,eAAe,UAAU,KAAK,QAAQ,WAAW;AACnD,aAAO,CAAC,cAAc;AAAA,IACxB;AAGA,UAAM,QAAQ,eAAe,MAAM,KAAK;AACxC,UAAM,cAAc,MAAM;AAAA,MAAO,CAAC,KAAK,SACrC,KAAK,SAAS,IAAI,SAAS,OAAO;AAAA,IAAA;AAEpC,QAAI,YAAY,SAAS,KAAK,QAAQ,WAAW;AAC/C,YAAM,IAAI,sBAAsB,YAAY,QAAQ,KAAK,QAAQ,SAAS;AAAA,IAC5E;AAGA,UAAM,kBAAkB,KAAK,kBAAkB,cAAc;AAC7D,QAAI,KAAK,eAAe,eAAe,GAAG;AAExC,aAAO;AAAA,IACT;AAGA,UAAM,aAAa,KAAK,aAAa,cAAc;AACnD,QAAI,KAAK,eAAe,UAAU,GAAG;AACnC,aAAO,KAAK,YAAY,YAAY,IAAI;AAAA,IAC1C;AAGA,UAAM,aAAa,MAAM,KAAK,aAAa,cAAc;AACzD,WAAO,KAAK,YAAY,YAAY,GAAG;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,QAA2B;AAChD,WAAO,OAAO,MAAM,CAAC,UAAU,MAAM,UAAU,KAAK,QAAQ,SAAS;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,MAAwB;AAChD,UAAM,aAAa,KAChB,MAAM,SAAS,EACf,IAAI,CAAC,MAAM,SAAS,CAAC,CAAC,EACtB,OAAO,OAAO;AAEjB,WAAO,WAAW,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,MAAwB;AAC3C,UAAM,QAAQ,KACX,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,SAAS,IAAI,CAAC,EAC5B,OAAO,OAAO;AAEjB,WAAO,MAAM,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,aAAa,MAAiC;AAC1D,UAAM,WAAW,IAAI,+BAA+B;AAAA,MAClD,WAAW,KAAK,QAAQ;AAAA,MACxB,cAAc;AAAA,IAAA,CACf;AAED,UAAM,SAAS,MAAM,SAAS,UAAU,IAAI;AAC5C,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMU,YAAY,QAAkB,WAA6B;AACnE,UAAM,eAAyB,CAAA;AAC/B,QAAI,eAA8B;AAElC,eAAW,SAAS,QAAQ;AAC1B,UAAI,iBAAiB,MAAM;AACzB,uBAAe;AACf;AAAA,MACF;AAEA,YAAM,mBAAmB,KAAK,aAAa,YAAY;AACvD,YAAM,gBAAgB,KAAK,aAAa,KAAK;AAE7C,UAAI,mBAAmB,gBAAgB,UAAU,UAAU,KAAK,QAAQ,WAAW;AAEjF,uBAAe,GAAG,YAAY,GAAG,SAAS,GAAG,KAAK;AAAA,MACpD,OAAO;AAEL,qBAAa,KAAK,YAAY;AAC9B,uBAAe;AAAA,MACjB;AAAA,IACF;AAEA,QAAI,cAAc;AAChB,mBAAa,KAAK,YAAY;AAAA,IAChC;AAEA,WAAO;AAAA,EACT;AAAA,EAEU,aAAa,OAAuB;AAC5C,WAAO,MAAM;AAAA,EACf;AAAA,EAEU,KAAK,SAAyB;AACtC,WAAO;AAAA,EACT;AACF;ACrGO,MAAM,yBAAqD;AAAA,EAMhE,YACU,oBACA,cACR;AAFQ,SAAA,qBAAA;AACA,SAAA,eAAA;AAER,SAAK,kBAAkB,IAAI,gBAAgB;AAAA,MACzC,cAAc;AAAA,MACd,IAAI;AAAA,MACJ,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,iBAAiB;AAAA,MACjB,WAAW;AAAA,IAAA,CACZ;AAGD,SAAK,gBAAgB,QAAQ,SAAS;AAAA,MACpC,QAAQ,CAAC,OAAO;AAAA,MAChB,aAAa,CAAC,UAAU,SAAS;AAC/B,cAAM,QAAQ;AACd,cAAM,UAAU,MAAM,KAAK,MAAM,iBAAiB,IAAI,CAAC,EAAE;AAAA,UACvD,CAAC,OAAO,GAAG,aAAa,UAAU;AAAA,QAAA;AAEpC,cAAM,OAAO,MAAM,KAAK,MAAM,iBAAiB,IAAI,CAAC,EAAE;AAAA,UACpD,CAAC,OAAO,CAAC,GAAG,cAAc,IAAI;AAAA,QAAA;AAGhC,YAAI,QAAQ,WAAW,KAAK,KAAK,WAAW,EAAG,QAAO;AAEtD,YAAI,WAAW;AACf,YAAI,QAAQ,SAAS,GAAG;AACtB,sBAAY,KAAK,QAAQ,KAAK,KAAK,CAAC;AAAA;AACpC,sBAAY,IAAI,QAAQ,IAAI,MAAM,KAAK,EAAE,KAAK,GAAG,CAAC;AAAA;AAAA,QACpD;AAEA,mBAAW,OAAO,MAAM;AACtB,gBAAM,QAAQ,MAAM,KAAK,IAAI,iBAAiB,IAAI,CAAC,EAAE;AAAA,YACnD,CAAC,OAAO,GAAG,aAAa,UAAU;AAAA,UAAA;AAEpC,sBAAY,KAAK,MAAM,KAAK,KAAK,CAAC;AAAA;AAAA,QACpC;AAEA,eAAO;AAAA,MACT;AAAA,IAAA,CACD;AAGD,SAAK,eAAe,IAAI,oBAAoB;AAAA,MAC1C,WAAW,KAAK;AAAA,IAAA,CACjB;AAED,SAAK,eAAe,IAAI,oBAAoB;AAAA,MAC1C,WAAW,KAAK;AAAA,IAAA,CACjB;AACD,SAAK,gBAAgB,IAAI,qBAAqB;AAAA,MAC5C,WAAW,KAAK;AAAA,IAAA,CACjB;AAAA,EACH;AAAA,EA7DQ;AAAA,EACD;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,EA+DP,MAAM,UAAU,UAA2C;AACzD,UAAM,OAAO,MAAM,KAAK,eAAe,QAAQ;AAC/C,UAAM,MAAM,MAAM,KAAK,UAAU,IAAI;AACrC,UAAM,WAAW,MAAM,KAAK,kBAAkB,GAAG;AACjD,WAAO,KAAK,oBAAoB,QAAQ;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,kBAAkB,KAA2C;AACzE,UAAM,OAAO,IAAI,cAAc,MAAM;AACrC,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,MAAM,+CAA+C;AAAA,IACjE;AAEA,QAAI,iBAAiB,KAAK,kBAAA;AAC1B,UAAM,WAA8B,CAAA;AACpC,UAAM,QAA2B,CAAC,cAAc;AAGhD,eAAW,WAAW,MAAM,KAAK,KAAK,QAAQ,GAAG;AAC/C,YAAM,eAAe,QAAQ,QAAQ,MAAM,UAAU;AAErD,UAAI,cAAc;AAEhB,cAAM,QAAQ,OAAO,SAAS,aAAa,CAAC,GAAG,EAAE;AACjD,cAAM,QAAQ,SAAS,QAAQ,eAAe,EAAE;AAGhD,eAAO,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,EAAE,SAAS,OAAO;AACjE,gBAAM,IAAA;AAAA,QACR;AAGA,yBAAiB;AAAA,UACf;AAAA,UACA,MAAM;AAAA,YACJ,GAAG,MAAM,MAAM,CAAC,EAAE,OAAO,CAAC,KAAe,MAAM;AAC7C,oBAAM,WAAW,EAAE,KAAK,EAAE,KAAK,SAAS,CAAC;AACzC,kBAAI,SAAU,KAAI,KAAK,QAAQ;AAC/B,qBAAO;AAAA,YACT,GAAG,CAAA,CAAE;AAAA,YACL;AAAA,UAAA;AAAA,UAEF,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM,GAAG,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK;AAAA,YAAA;AAAA,UACrC;AAAA,QACF;AAGF,iBAAS,KAAK,cAAc;AAC5B,cAAM,KAAK,cAAc;AAAA,MAC3B,WAAW,QAAQ,YAAY,OAAO;AAEpC,cAAM,OAAO,QAAQ,cAAc,MAAM;AACzC,cAAM,WAAW,MAAM,UAAU,QAAQ,aAAa,EAAE,KAAK;AAC7D,cAAM,UAAU,MAAM,eAAe,QAAQ,eAAe;AAC5D,cAAM,WAAW,GAAG,KAAK,GAAG,QAAQ;AAAA,EAAK,OAAO;AAAA,EAAK,KAAK;AAE1D,yBAAiB;AAAA,UACf,OAAO,eAAe;AAAA,UACtB,MAAM,eAAe;AAAA,UACrB,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM;AAAA,YAAA;AAAA,UACR;AAAA,QACF;AAEF,iBAAS,KAAK,cAAc;AAAA,MAC9B,WAAW,QAAQ,YAAY,SAAS;AAEtC,cAAM,WAAW,SAAS,KAAK,gBAAgB,SAAS,QAAQ,SAAS,CAAC;AAE1E,yBAAiB;AAAA,UACf,OAAO,eAAe;AAAA,UACtB,MAAM,eAAe;AAAA,UACrB,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM;AAAA,YAAA;AAAA,UACR;AAAA,QACF;AAEF,iBAAS,KAAK,cAAc;AAAA,MAC9B,OAAO;AACL,cAAM,WAAW,SAAS,KAAK,gBAAgB,SAAS,QAAQ,SAAS,CAAC;AAC1E,YAAI,UAAU;AAEZ,2BAAiB;AAAA,YACf,OAAO,eAAe;AAAA,YACtB,MAAM,eAAe;AAAA,YACrB,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cAAA;AAAA,YACR;AAAA,UACF;AAEF,mBAAS,KAAK,cAAc;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,oBACZ,UACyB;AACzB,UAAM,SAAyB,CAAA;AAE/B,eAAW,WAAW,UAAU;AAC9B,iBAAW,WAAW,QAAQ,SAAS;AACrC,YAAI,eAAyB,CAAA;AAE7B,YAAI;AACF,kBAAQ,QAAQ,MAAA;AAAA,YACd,KAAK;AAAA,YACL,KAAK,QAAQ;AACX,6BAAe,MAAM,KAAK,aAAa,MAAM,QAAQ,IAAI;AACzD;AAAA,YACF;AAAA,YACA,KAAK,QAAQ;AACX,6BAAe,MAAM,KAAK,aAAa,MAAM,QAAQ,IAAI;AACzD;AAAA,YACF;AAAA,YACA,KAAK,SAAS;AACZ,6BAAe,MAAM,KAAK,cAAc,MAAM,QAAQ,IAAI;AAC1D;AAAA,YACF;AAAA,UAAA;AAAA,QAEJ,SAAS,KAAK;AAEZ,cAAI,eAAe,uBAAuB;AACxC,mBAAO;AAAA,cACL,kBAAkB,QAAQ,IAAI,0DAA0D,IAAI,OAAO;AAAA,YAAA;AAIrG,kBAAM,WAAW,IAAI,+BAA+B;AAAA,cAClD,WAAW,KAAK;AAAA,cAChB,cAAc,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,eAAe,GAAG,CAAC;AAAA;AAAA,cAE9D,YAAY;AAAA,gBACV;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,cAAA;AAAA,YACF,CACD;AAED,kBAAMA,UAAS,MAAM,SAAS,UAAU,QAAQ,IAAI;AACpD,gBAAIA,QAAO,WAAW,GAAG;AAEvB,6BAAe,CAAC,QAAQ,KAAK,UAAU,GAAG,KAAK,YAAY,CAAC;AAAA,YAC9D,OAAO;AACL,6BAAeA;AAAAA,YACjB;AAAA,UACF,OAAO;AAEL,kBAAM,aAAa,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAClE,kBAAM,IAAI;AAAA,cACR,mBAAmB,QAAQ,IAAI,aAAa,UAAU;AAAA,YAAA;AAAA,UAE1D;AAAA,QACF;AAGA,eAAO;AAAA,UACL,GAAG,aAAa;AAAA,YACd,CAAC,UAAwB;AAAA,cACvB,OAAO,CAAC,QAAQ,IAAI;AAAA,cACpB,SAAS;AAAA,cACT,SAAS;AAAA,gBACP,OAAO,QAAQ;AAAA,gBACf,MAAM,QAAQ;AAAA,cAAA;AAAA,YAChB;AAAA,UACF;AAAA,QACF;AAAA,MAEJ;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAqC;AAC3C,WAAO;AAAA,MACL,OAAO;AAAA,MACP,MAAM,CAAA;AAAA,MACN,SAAS,CAAA;AAAA,IAAC;AAAA,EAEd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eAAe,UAAmC;AAC9D,UAAM,OAAO,MAAM,UAChB,IAAI,WAAW,EACf,IAAI,SAAS,EACb,IAAI,UAAU,EACd,QAAQ,QAAQ;AAEnB,WAAO;AAAA;AAAA;AAAA,YAGC,OAAO,IAAI,CAAC;AAAA;AAAA;AAAA,EAGtB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,UAAU,MAAiC;AAEvD,UAAM,EAAE,OAAA,IAAW,YAAY,IAAI;AACnC,WAAO,OAAO;AAAA,EAChB;AACF;ACtVA,MAAM,cAAc;AACpB,MAAM,gBAAgB;AAEf,MAAM,yBAAyB;AAAA,EAC5B;AAAA,EAER,YAAY,eAA8B;AACxC,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,mBACZ,SACA,SACA,KACA,eAAe,eACf,aAAa,aAMZ;AACD,UAAM,KAAK,IAAI;AACf,UAAM,MAAM,IAAI,SAAS;AACzB,UAAM,QAAQ,IAAI,SAAS;AAC3B,UAAM,iCAAiB,IAAA;AACvB,eAAW,IAAI,EAAE;AAGjB,UAAM,SAAS,MAAM,KAAK,cAAc,gBAAgB,SAAS,SAAS,EAAE;AAC5E,QAAI,QAAQ;AACV,iBAAW,IAAI,OAAO,EAAY;AAAA,IACpC;AAGA,UAAM,oBAAoB,MAAM,KAAK,cAAc;AAAA,MACjD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAEF,eAAW,OAAO,mBAAmB;AACnC,iBAAW,IAAI,IAAI,EAAY;AAAA,IACjC;AAGA,UAAM,cAAc,MAAM,KAAK,cAAc;AAAA,MAC3C;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAEF,eAAW,SAAS,aAAa;AAC/B,iBAAW,IAAI,MAAM,EAAY;AAAA,IACnC;AAGA,UAAM,qBAAqB,MAAM,KAAK,cAAc;AAAA,MAClD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAEF,eAAW,OAAO,oBAAoB;AACpC,iBAAW,IAAI,IAAI,EAAY;AAAA,IACjC;AAEA,WAAO,EAAE,KAAK,OAAO,IAAI,YAAY,MAAA;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKQ,qBACN,cAMgE;AAChE,UAAM,6BAAa,IAAA;AACnB,eAAW,QAAQ,cAAc;AAC/B,UAAI,QAAQ,OAAO,IAAI,KAAK,GAAG;AAC/B,UAAI,CAAC,OAAO;AACV,gBAAQ,EAAE,gBAAgB,oBAAI,OAAO,UAAU,KAAK,MAAA;AACpD,eAAO,IAAI,KAAK,KAAK,KAAK;AAAA,MAC5B;AACA,iBAAW,MAAM,KAAK,YAAY;AAChC,cAAM,eAAe,IAAI,EAAE;AAAA,MAC7B;AACA,UAAI,KAAK,QAAQ,MAAM,UAAU;AAC/B,cAAM,WAAW,KAAK;AAAA,MACxB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eACZ,SACA,SACA,KACA,gBACA,UAC4B;AAC5B,UAAM,MAAM,MAAM,KAAK,cAAc;AACrC,UAAM,OAAO,MAAM,KAAK,cAAc,gBAAgB,SAAS,SAAS,GAAG;AAE3E,UAAM,UAAU,KAAK,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,MAAM;AAE1D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IAAA;AAAA,EAEX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,OACJ,SACA,SACA,OACA,OAC8B;AAE9B,UAAM,qBAAqB,WAAW,IAAI,YAAA;AAE1C,UAAM,iBAAiB,MAAM,KAAK,cAAc;AAAA,MAC9C;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,IAAA;AAIX,UAAM,eAAe,MAAM,QAAQ;AAAA,MACjC,eAAe;AAAA,QAAI,CAAC,QAClB,KAAK,mBAAmB,SAAS,mBAAmB,GAAG;AAAA,MAAA;AAAA,IACzD;AAIF,UAAM,SAAS,KAAK,qBAAqB,YAAY;AAGrD,UAAM,UAA+B,CAAA;AACrC,eAAW,CAAC,KAAK,EAAE,gBAAgB,UAAU,KAAK,OAAO,WAAW;AAClE,YAAM,SAAS,MAAM,KAAK;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAEF,cAAQ,KAAK,MAAM;AAAA,IACrB;AAEA,WAAO;AAAA,EACT;AACF;ACzIO,MAAM,cAAc;AAAA,EACR;AAAA,EACT;AAAA,EACS,cAAsB;AAAA,EAC/B;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,EAyCA,aAAa,SAAkB,SAAkB,IAAI,IAAY;AACvE,QAAI,MAAM;AACV,QAAI,YAAY,QAAW;AACzB,aAAO,KAAK,IAAI;AAAA,IAClB;AACA,QAAI,YAAY,QAAW;AACzB,aAAO,KAAK,IAAI;AAAA,IAClB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,SAA4C;AAE9D,UAAM,+BAAe,IAAA;AACrB,UAAM,+BAAe,IAAA;AAGrB,YACG,OAAO,CAAC,MAAM,EAAE,cAAc,MAAS,EACvC,KAAK,CAAC,GAAG,OAAO,EAAE,aAAa,MAAM,EAAE,aAAa,EAAE,EACtD,QAAQ,CAAC,QAAQ,UAAU;AAC1B,eAAS,IAAI,OAAO,OAAO,EAAE,GAAG,QAAQ,CAAC;AAAA,IAC3C,CAAC;AAGH,YACG,OAAO,CAAC,MAAM,EAAE,cAAc,MAAS,EACvC,KAAK,CAAC,GAAG,OAAO,EAAE,aAAa,MAAM,EAAE,aAAa,EAAE,EACtD,QAAQ,CAAC,QAAQ,UAAU;AAC1B,eAAS,IAAI,OAAO,OAAO,EAAE,GAAG,QAAQ,CAAC;AAAA,IAC3C,CAAC;AAGH,WAAO,QAAQ,IAAI,CAAC,YAAY;AAAA,MAC9B,GAAG;AAAA,MACH,UAAU,SAAS,IAAI,OAAO,OAAO,EAAE,CAAC;AAAA,MACxC,UAAU,SAAS,IAAI,OAAO,OAAO,EAAE,CAAC;AAAA,MACxC,WAAW,KAAK;AAAA,QACd,SAAS,IAAI,OAAO,OAAO,EAAE,CAAC;AAAA,QAC9B,SAAS,IAAI,OAAO,OAAO,EAAE,CAAC;AAAA,MAAA;AAAA,IAChC,EACA;AAAA,EACJ;AAAA,EAEA,YAAY,QAAgB;AAC1B,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,WAAW,gCAAgC;AAAA,IACvD;AAGA,SAAK,KAAK,IAAI,SAAS,MAAM;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAA0B;AAChC,UAAM,aAAa;AAAA,MACjB,SAAS,KAAK,GAAG,QAAkB,sCAAsC;AAAA,MACzE,gBAAgB,KAAK,GAAG;AAAA,QAGtB;AAAA,MAAA;AAAA,MAEF,iBAAiB,KAAK,GAAG;AAAA,QACvB;AAAA,MAAA;AAAA,MAEF,eAAe,KAAK,GAAG;AAAA,QACrB;AAAA,MAAA;AAAA,MAEF,oBAAoB,KAAK,GAAG;AAAA,QAC1B;AAAA,MAAA;AAAA;AAAA,MAGF,eAAe,KAAK,GAAG;AAAA,QACrB;AAAA,MAAA;AAAA,MAEF,kBAAkB,KAAK,GAAG;AAAA,QACxB;AAAA,MAAA;AAAA,MAEF,gBAAgB,KAAK,GAAG,QAAkB,qCAAqC;AAAA,MAC/E,0BAA0B,KAAK,GAAG;AAAA,QAChC;AAAA,MAAA;AAAA,MAEF,wBAAwB,KAAK,GAAG;AAAA,QAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA;AAAA,MAQF,iBAAiB,KAAK,GAAG;AAAA,QACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA;AAAA,MAQF,sBAAsB,KAAK,GAAG;AAAA,QAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA;AAAA,MASF,mBAAmB,KAAK,GAAG;AAAA,QACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA;AAAA,MAQF,eAAe,KAAK,GAAG;AAAA,QACrB;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA;AAAA,MAMF,aAAa,KAAK,GAAG;AAAA,QACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA;AAAA;AAAA,MAQF,sBAAsB,KAAK,GAAG;AAAA,QAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA;AAAA,MAiBF,gBAAgB,KAAK,GAAG,QAEtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAYD;AAAA,MACD,sBAAsB,KAAK,GAAG,QAE5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAWD;AAAA,MACD,uBAAuB,KAAK,GAAG,QAE7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAWD;AAAA,MACD,gBAAgB,KAAK,GAAG,QAAkD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAWzE;AAAA;AAAA,MAED,qBAAqB,KAAK,GAAG;AAAA,QAC3B;AAAA,MAAA;AAAA,MAEF,uBAAuB,KAAK,GAAG;AAAA,QAC7B;AAAA,MAAA;AAAA,MAEF,qBAAqB,KAAK,GAAG;AAAA,QAC3B;AAAA,MAAA;AAAA;AAAA,MAGF,6BAA6B,KAAK,GAAG;AAAA,QACnC;AAAA,MAAA;AAAA,MAEF,uBAAuB,KAAK,GAAG;AAAA,QAC7B;AAAA,MAAA;AAAA,MAEF,wBAAwB,KAAK,GAAG;AAAA,QAC9B;AAAA,MAAA;AAAA,IACF;AAEF,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,UAAU,QAA4B;AAC5C,QAAI,OAAO,SAAS,KAAK,aAAa;AACpC,YAAM,IAAI;AAAA,QACR,oBAAoB,OAAO,MAAM,+BAA+B,KAAK,WAAW;AAAA,MAAA;AAAA,IAEpF;AACA,QAAI,OAAO,WAAW,KAAK,aAAa;AACtC,aAAO;AAAA,IACT;AACA,WAAO,CAAC,GAAG,QAAQ,GAAG,IAAI,MAAM,KAAK,cAAc,OAAO,MAAM,EAAE,KAAK,CAAC,CAAC;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAc,uBAAsC;AAClD,UAAM,YAAY,QAAQ,IAAI,4BAA4B;AAG1D,UAAM,EAAE,qBAAA,IAAyB,MAAM,OAAO,gCAA+B;AAC7E,SAAK,aAAa,qBAAqB,SAAS;AAGhD,UAAM,aAAa,MAAM,KAAK,WAAW,WAAW,MAAM;AAC1D,SAAK,iBAAiB,WAAW;AAEjC,QAAI,KAAK,iBAAiB,KAAK,aAAa;AAC1C,YAAM,IAAI,eAAe,WAAW,KAAK,gBAAgB,KAAK,WAAW;AAAA,IAC3E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAe,OAAuB;AAE5C,UAAM,gBAAgB,MAAM,QAAQ,MAAM,IAAI;AAE9C,WAAO,IAAI,aAAa;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAA4B;AAChC,QAAI;AAEF,gBAAU,KAAK,KAAK,EAAE;AAGtB,sBAAgB,KAAK,EAAE;AAGvB,WAAK,kBAAA;AAGL,YAAM,KAAK,qBAAA;AAAA,IACb,SAAS,OAAO;AAEd,UAAI,iBAAiB,YAAY;AAC/B,cAAM;AAAA,MACR;AACA,YAAM,IAAI,gBAAgB,4CAA4C,KAAK;AAAA,IAC7E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAA0B;AAC9B,SAAK,GAAG,MAAA;AAAA,EACV;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,4BACJ,SACA,SACmD;AACnD,UAAM,oBAAoB,QAAQ,YAAA;AAClC,UAAM,oBAAoB,uBAAuB,QAAQ,YAAA,CAAa;AAGtE,SAAK,WAAW,cAAc,IAAI,iBAAiB;AACnD,UAAM,eAAe,KAAK,WAAW,mBAAmB,IAAI,iBAAiB;AAG7E,QAAI,CAAC,gBAAgB,OAAO,aAAa,OAAO,UAAU;AACxD,YAAM,IAAI,WAAW,6CAA6C,OAAO,EAAE;AAAA,IAC7E;AACA,UAAM,YAAY,aAAa;AAI/B,SAAK,WAAW,cAAc,IAAI,WAAW,iBAAiB;AAC9D,UAAM,eAAe,KAAK,WAAW,iBAAiB;AAAA,MACpD;AAAA,MACA,sBAAsB,OAAO,KAAK;AAAA,IAAA;AAEpC,QAAI,CAAC,gBAAgB,OAAO,aAAa,OAAO,UAAU;AACxD,YAAM,IAAI;AAAA,QACR,6CAA6C,OAAO,cAAc,OAAO;AAAA,MAAA;AAAA,IAE7E;AAEA,WAAO,EAAE,WAAW,WAAW,aAAa,GAAA;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBAAoB,SAAoC;AAC5D,QAAI;AACF,YAAM,OAAO,KAAK,WAAW,cAAc,IAAI,QAAQ,aAAa;AAGpE,aAAO,KAAK,IAAI,CAAC,QAAQ,qBAAqB,IAAI,IAAI,CAAC;AAAA,IACzD,SAAS,OAAO;AACd,YAAM,IAAI,gBAAgB,4BAA4B,KAAK;AAAA,IAC7D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,oBACJ,WACA,QACA,cACe;AACf,QAAI;AACF,WAAK,WAAW,oBAAoB,IAAI,QAAQ,gBAAgB,MAAM,SAAS;AAAA,IACjF,SAAS,OAAO;AACd,YAAM,IAAI,WAAW,oCAAoC,KAAK,EAAE;AAAA,IAClE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,sBACJ,WACA,OACA,UACe;AACf,QAAI;AACF,WAAK,WAAW,sBAAsB,IAAI,OAAO,UAAU,SAAS;AAAA,IACtE,SAAS,OAAO;AACd,YAAM,IAAI,WAAW,sCAAsC,KAAK,EAAE;AAAA,IACpE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,oBAAoB,UAA4D;AACpF,QAAI;AACF,YAAM,aAAa,KAAK,UAAU,QAAQ;AAC1C,YAAM,OAAO,KAAK,WAAW,oBAAoB;AAAA,QAC/C;AAAA,MAAA;AAEF,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,IAAI,WAAW,qCAAqC,KAAK,EAAE;AAAA,IACnE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,oBAAoB,WAAmB,SAAwC;AACnF,QAAI;AAEF,YAAM,EAAE,KAAK,YAAY,SAAS,SAAS,QAAQ,GAAG,oBAAoB;AAE1E,YAAM,cAAc,KAAK,UAAU,eAAe;AAClD,WAAK,WAAW,4BAA4B,IAAI,YAAY,aAAa,SAAS;AAAA,IACpF,SAAS,OAAO;AACd,YAAM,IAAI,WAAW,oCAAoC,KAAK,EAAE;AAAA,IAClE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,kBAAkB,WAAyD;AAC/E,QAAI;AACF,YAAM,MAAM,KAAK,WAAW,sBAAsB,IAAI,SAAS;AAI/D,UAAI,CAAC,KAAK,YAAY;AACpB,eAAO;AAAA,MACT;AAEA,UAAI,SAAgC,CAAA;AACpC,UAAI,IAAI,iBAAiB;AACvB,YAAI;AACF,mBAAS,KAAK,MAAM,IAAI,eAAe;AAAA,QACzC,SAAS,GAAG;AACV,iBAAO,KAAK,+CAA+C,SAAS,KAAK,CAAC,EAAE;AAC5E,mBAAS,CAAA;AAAA,QACX;AAAA,MACF;AAEA,aAAO,EAAE,WAAW,IAAI,YAAY,SAAS,OAAA;AAAA,IAC/C,SAAS,OAAO;AACd,YAAM,IAAI,WAAW,kCAAkC,KAAK,EAAE;AAAA,IAChE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,wBAAwB,KAA8C;AAC1E,QAAI;AACF,YAAM,OAAO,KAAK,WAAW,uBAAuB;AAAA,QAClD;AAAA,MAAA;AAEF,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,IAAI,WAAW,0CAA0C,KAAK,EAAE;AAAA,IACxE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBAAoB,SAAiB,SAAmC;AAC5E,QAAI;AACF,YAAM,oBAAoB,QAAQ,YAAA;AAClC,YAAM,SAAS,KAAK,WAAW,YAAY;AAAA,QACzC,QAAQ,YAAA;AAAA,QACR;AAAA,MAAA;AAEF,aAAO,WAAW;AAAA,IACpB,SAAS,OAAO;AACd,YAAM,IAAI,gBAAgB,sCAAsC,KAAK;AAAA,IACvE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,uBAeJ;AACA,QAAI;AAeF,YAAM,OAAO,KAAK,WAAW,qBAAqB,IAAA;AAClD,YAAM,iCAAiB,IAAA;AAevB,iBAAW,OAAO,MAAM;AAEtB,cAAM,UAAU,IAAI;AACpB,YAAI,CAAC,WAAW,IAAI,OAAO,GAAG;AAC5B,qBAAW,IAAI,SAAS,EAAE;AAAA,QAC5B;AAGA,cAAM,eAAe,IAAI,YAAY,IAAI,KAAK,IAAI,SAAS,EAAE,YAAA,IAAgB;AAE7E,mBAAW,IAAI,OAAO,GAAG,KAAK;AAAA,UAC5B,SAAS,IAAI;AAAA,UACb,WAAW,IAAI;AAAA;AAAA,UAEf,QAAQ,IAAI;AAAA,UACZ,eAAe,IAAI;AAAA,UACnB,kBAAkB,IAAI;AAAA,UACtB,WAAW,IAAI;AAAA,UACf,eAAe,IAAI;AAAA,UACnB,gBAAgB,IAAI;AAAA,UACpB,WAAW;AAAA,QAAA,CACZ;AAAA,MACH;AAGA,iBAAW,YAAY,WAAW,UAAU;AAC1C,iBAAS,KAAK,CAAC,GAAG,MAAM;AACtB,cAAI,EAAE,YAAY,MAAM,EAAE,YAAY,IAAI;AACxC,mBAAO;AAAA,UACT;AACA,cAAI,EAAE,YAAY,MAAM,EAAE,YAAY,IAAI;AACxC,mBAAO;AAAA,UACT;AACA,cAAI,EAAE,YAAY,MAAM,EAAE,YAAY,IAAI;AACxC,mBAAO;AAAA,UACT;AAEA,cAAI;AACF,mBAAOC,gBAAO,QAAQ,EAAE,SAAS,EAAE,OAAO;AAAA,UAC5C,SAAS,QAAQ;AAEf,mBAAO,EAAE,QAAQ,cAAc,EAAE,OAAO;AAAA,UAC1C;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,IAAI,gBAAgB,oCAAoC,KAAK;AAAA,IACrE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aACJ,SACA,SACA,WACe;AACf,QAAI;AACF,UAAI,UAAU,WAAW,GAAG;AAC1B;AAAA,MACF;AAGA,YAAM,2BAAW,IAAA;AACjB,iBAAW,OAAO,WAAW;AAC3B,cAAM,MAAM,IAAI,SAAS;AACzB,YAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,CAAC,IAAI,QAAQ;AAClD,gBAAM,IAAI,WAAW,4CAA4C;AAAA,QACnE;AACA,aAAK,IAAI,GAAG;AAAA,MACd;AAGA,YAAM,QAAQ,UAAU,IAAI,CAAC,QAAQ;AACnC,cAAM,SAAS,UAAU,IAAI,SAAS,KAAK;AAAA,OAAkB,IAAI,SAAS,GAAG;AAAA,QAAiB,IAAI,SAAS,KAAK,KAAK,KAAK,CAAC;AAAA;AAC3H,eAAO,GAAG,MAAM,GAAG,IAAI,WAAW;AAAA,MACpC,CAAC;AAID,YAAM,gBACJ,OAAO,QAAQ,IAAI,8BAA8B,KAAK;AACxD,YAAM,gBAA4B,CAAA;AAElC,UAAI,eAAyB,CAAA;AAC7B,UAAI,mBAAmB;AACvB,UAAI,aAAa;AAEjB,iBAAW,QAAQ,OAAO;AACxB,cAAM,WAAW,KAAK;AAGtB,YAAI,mBAAmB,WAAW,iBAAiB,aAAa,SAAS,GAAG;AAC1E;AACA,iBAAO;AAAA,YACL,iCAAiC,UAAU,KAAK,aAAa,MAAM,WAAW,gBAAgB;AAAA,UAAA;AAEhG,gBAAM,kBAAkB,MAAM,KAAK,WAAW,eAAe,YAAY;AACzE,wBAAc,KAAK,GAAG,eAAe;AACrC,yBAAe,CAAA;AACf,6BAAmB;AAAA,QACrB;AAGA,qBAAa,KAAK,IAAI;AACtB,4BAAoB;AAGpB,YAAI,aAAa,UAAU,sBAAsB;AAC/C;AACA,iBAAO;AAAA,YACL,iCAAiC,UAAU,KAAK,aAAa,MAAM,WAAW,gBAAgB;AAAA,UAAA;AAEhG,gBAAM,kBAAkB,MAAM,KAAK,WAAW,eAAe,YAAY;AACzE,wBAAc,KAAK,GAAG,eAAe;AACrC,yBAAe,CAAA;AACf,6BAAmB;AAAA,QACrB;AAAA,MACF;AAGA,UAAI,aAAa,SAAS,GAAG;AAC3B;AACA,eAAO;AAAA,UACL,uCAAuC,UAAU,KAAK,aAAa,MAAM,WAAW,gBAAgB;AAAA,QAAA;AAEtG,cAAM,kBAAkB,MAAM,KAAK,WAAW,eAAe,YAAY;AACzE,sBAAc,KAAK,GAAG,eAAe;AAAA,MACvC;AACA,YAAM,mBAAmB,cAAc,IAAI,CAAC,WAAW,KAAK,UAAU,MAAM,CAAC;AAG7E,YAAM,EAAE,WAAW,cAAc,MAAM,KAAK;AAAA,QAC1C;AAAA,QACA;AAAA,MAAA;AAKF,iBAAW,OAAO,MAAM;AACtB,cAAM,eAAe,MAAM,KAAK,qBAAqB,SAAS,SAAS,GAAG;AAC1E,YAAI,eAAe,GAAG;AACpB,iBAAO,MAAM,eAAe,YAAY,gCAAgC,GAAG,EAAE;AAAA,QAC/E;AAAA,MACF;AAGA,YAAM,cAAc,KAAK,GAAG,YAAY,CAAC,SAA2B;AAClE,iBAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,gBAAM,MAAM,KAAK,CAAC;AAClB,gBAAM,MAAM,IAAI,SAAS;AAGzB,gBAAM,SAAS,KAAK,WAAW,eAAe;AAAA,YAC5C,OAAO,SAAS;AAAA,YAChB,OAAO,SAAS;AAAA,YAChB;AAAA,YACA,IAAI;AAAA,YACJ,KAAK,UAAU,IAAI,QAAQ;AAAA,YAC3B;AAAA,aACA,oBAAI,KAAA,GAAO,YAAA;AAAA;AAAA,UAAY;AAEzB,gBAAM,QAAQ,OAAO;AAGrB,eAAK,WAAW,gBAAgB;AAAA,YAC9B,OAAO,KAAK;AAAA,YACZ,OAAO,SAAS;AAAA,YAChB,OAAO,SAAS;AAAA,YAChB,KAAK,UAAU,iBAAiB,CAAC,CAAC;AAAA,UAAA;AAAA,QAEtC;AAAA,MACF,CAAC;AAED,kBAAY,SAAS;AAAA,IACvB,SAAS,OAAO;AACd,YAAM,IAAI,gBAAgB,oCAAoC,KAAK;AAAA,IACrE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,gBAAgB,SAAiB,SAAkC;AACvE,QAAI;AACF,YAAM,oBAAoB,QAAQ,YAAA;AAClC,YAAM,SAAS,KAAK,WAAW,gBAAgB;AAAA,QAC7C,QAAQ,YAAA;AAAA,QACR,QAAQ,YAAA;AAAA;AAAA,QACR;AAAA,MAAA;AAEF,aAAO,OAAO;AAAA,IAChB,SAAS,OAAO;AACd,YAAM,IAAI,gBAAgB,8BAA8B,KAAK;AAAA,IAC/D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,qBACJ,SACA,SACA,KACiB;AACjB,QAAI;AACF,YAAM,oBAAoB,QAAQ,YAAA;AAClC,YAAM,SAAS,KAAK,WAAW,qBAAqB;AAAA,QAClD;AAAA,QACA,QAAQ,YAAA;AAAA,QACR,QAAQ,YAAA;AAAA;AAAA,QACR;AAAA,MAAA;AAEF,aAAO,OAAO;AAAA,IAChB,SAAS,OAAO;AACd,YAAM,IAAI,gBAAgB,qCAAqC,KAAK;AAAA,IACtE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ,IAAsC;AAClD,QAAI;AACF,YAAM,MAAM,KAAK,WAAW,QAAQ,IAAI,OAAO,EAAE,CAAC;AAClD,UAAI,CAAC,KAAK;AACR,eAAO;AAAA,MACT;AAEA,aAAO,wBAAwB,GAAG;AAAA,IACpC,SAAS,OAAO;AACd,YAAM,IAAI,gBAAgB,gCAAgC,EAAE,IAAI,KAAK;AAAA,IACvE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cACJ,SACA,SACA,OACA,OACqB;AACrB,QAAI;AACF,YAAM,eAAe,MAAM,KAAK,WAAW,WAAW,KAAK;AAC3D,YAAM,YAAY,KAAK,UAAU,YAAY;AAC7C,YAAM,WAAW,KAAK,eAAe,KAAK;AAC1C,YAAM,oBAAoB,QAAQ,YAAA;AAElC,YAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAsC5B;AAED,YAAM,aAAa,KAAK;AAAA,QACtB,QAAQ,YAAA;AAAA,QACR;AAAA,QACA,KAAK,UAAU,SAAS;AAAA,QACxB;AAAA,QACA,QAAQ,YAAA;AAAA,QACR;AAAA,QACA;AAAA;AAAA,QACA;AAAA,MAAA;AAIF,YAAM,gBAAgB,KAAK,YAAY,UAAU;AAGjD,YAAM,aAAa,cAChB,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS,EACxC,MAAM,GAAG,KAAK;AAEjB,aAAO,WAAW,IAAI,CAAC,SAAS;AAAA,QAC9B,GAAG,wBAAwB,GAAG;AAAA,QAC9B,UAAU;AAAA,UACR,GAAG,KAAK,MAAM,IAAI,QAAQ;AAAA,UAC1B,IAAI,IAAI;AAAA,UACR,OAAO,IAAI;AAAA,UACX,UAAU,IAAI;AAAA,UACd,UAAU,IAAI;AAAA,QAAA;AAAA,MAChB,EACA;AAAA,IACJ,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,mDAAmD,KAAK;AAAA,QACxD;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBACJ,SACA,SACA,IACA,OACqB;AACrB,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,QAAQ,EAAE;AACpC,UAAI,CAAC,QAAQ;AACX,eAAO,CAAA;AAAA,MACT;AAEA,YAAM,aAAc,OAAO,SAA8B,QAAQ,CAAA;AACjE,YAAM,YAAa,OAAO,SAA8B;AACxD,YAAM,oBAAoB,QAAQ,YAAA;AAElC,YAAM,SAAS,KAAK,WAAW,eAAe;AAAA,QAC5C,QAAQ,YAAA;AAAA,QACR;AAAA,QACA;AAAA,QACA,WAAW,SAAS;AAAA,QACpB,KAAK,UAAU,UAAU;AAAA,QACzB,OAAO,EAAE;AAAA,QACT;AAAA,MAAA;AAGF,aAAO,OAAO,IAAI,CAAC,QAAQ,wBAAwB,GAAG,CAAC;AAAA,IACzD,SAAS,OAAO;AACd,YAAM,IAAI,gBAAgB,sCAAsC,EAAE,IAAI,KAAK;AAAA,IAC7E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,2BACJ,SACA,SACA,IACA,OACqB;AACrB,QAAI;AACF,YAAM,YAAY,MAAM,KAAK,QAAQ,EAAE;AACvC,UAAI,CAAC,WAAW;AACd,eAAO,CAAA;AAAA,MACT;AAEA,YAAM,cAAc,UAAU;AAC9B,YAAM,oBAAoB,QAAQ,YAAA;AAElC,YAAM,SAAS,KAAK,WAAW,qBAAqB;AAAA,QAClD,QAAQ,YAAA;AAAA,QACR;AAAA,QACA,YAAY;AAAA,QACZ,OAAO,EAAE;AAAA,QACT,KAAK,UAAU,YAAY,IAAI;AAAA,QAC/B;AAAA,MAAA;AAGF,aAAO,OAAO,UAAU,IAAI,CAAC,QAAQ,wBAAwB,GAAG,CAAC;AAAA,IACnE,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,kDAAkD,EAAE;AAAA,QACpD;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,4BACJ,SACA,SACA,IACA,OACqB;AACrB,QAAI;AACF,YAAM,YAAY,MAAM,KAAK,QAAQ,EAAE;AACvC,UAAI,CAAC,WAAW;AACd,eAAO,CAAA;AAAA,MACT;AAEA,YAAM,cAAc,UAAU;AAC9B,YAAM,oBAAoB,QAAQ,YAAA;AAElC,YAAM,SAAS,KAAK,WAAW,sBAAsB;AAAA,QACnD,QAAQ,YAAA;AAAA,QACR;AAAA,QACA,YAAY;AAAA,QACZ,OAAO,EAAE;AAAA,QACT,KAAK,UAAU,YAAY,IAAI;AAAA,QAC/B;AAAA,MAAA;AAGF,aAAO,OAAO,IAAI,CAAC,QAAQ,wBAAwB,GAAG,CAAC;AAAA,IACzD,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,mDAAmD,EAAE;AAAA,QACrD;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBACJ,SACA,SACA,IAC0B;AAC1B,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,QAAQ,EAAE;AACnC,UAAI,CAAC,OAAO;AACV,eAAO;AAAA,MACT;AAEA,YAAM,gBAAgB,MAAM;AAC5B,YAAMC,QAAO,cAAc,QAAQ,CAAA;AACnC,YAAM,aAAaA,MAAK,MAAM,GAAG,EAAE;AAEnC,UAAI,WAAW,WAAW,GAAG;AAC3B,eAAO;AAAA,MACT;AAEA,YAAM,oBAAoB,QAAQ,YAAA;AAClC,YAAM,SAAS,KAAK,WAAW,eAAe;AAAA,QAC5C,QAAQ,YAAA;AAAA,QACR;AAAA,QACA,cAAc;AAAA,QACd,KAAK,UAAU,UAAU;AAAA,QACzB,OAAO,EAAE;AAAA,MAAA;AAGX,UAAI,CAAC,QAAQ;AACX,eAAO;AAAA,MACT;AAEA,aAAO,wBAAwB,MAAM;AAAA,IACvC,SAAS,OAAO;AACd,YAAM,IAAI,gBAAgB,sCAAsC,EAAE,IAAI,KAAK;AAAA,IAC7E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,gBACJ,SACA,SACA,KACqB;AACrB,QAAI,CAAC,IAAI,OAAQ,QAAO,CAAA;AACxB,QAAI;AACF,YAAM,oBAAoB,QAAQ,YAAA;AAElC,YAAM,eAAe,IAAI,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AAChD,YAAM,OAAO,KAAK,GAAG;AAAA,QACnB;AAAA;AAAA;AAAA;AAAA;AAAA,0BAKkB,YAAY;AAAA;AAAA,MAAA;AAGhC,YAAM,OAAO,KAAK;AAAA,QAChB,QAAQ,YAAA;AAAA,QACR;AAAA,QACA,GAAG;AAAA,MAAA;AAEL,aAAO,KAAK,IAAI,CAAC,QAAQ,wBAAwB,GAAG,CAAC;AAAA,IACvD,SAAS,OAAO;AACd,YAAM,IAAI,gBAAgB,oCAAoC,KAAK;AAAA,IACrE;AAAA,EACF;AACF;AC3mCO,MAAM,0BAA0B;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMT,iBAAiB,SAAiC;AACxD,YAAQ,WAAW,IAAI,YAAA;AAAA,EACzB;AAAA,EAEA,cAAc;AACZ,QAAI;AACJ,QAAI;AAGJ,UAAM,eAAe,QAAQ,IAAI;AACjC,QAAI,cAAc;AAChB,cAAQ;AACR,eAAS,KAAK,KAAK,OAAO,cAAc;AACxC,aAAO,MAAM,yDAAyD,KAAK,EAAE;AAAA,IAC/E,OAAO;AAEL,YAAM,cAAc,eAAA;AACpB,YAAM,WAAW,KAAK,KAAK,aAAa,QAAQ;AAChD,YAAM,YAAY,KAAK,KAAK,UAAU,cAAc;AACpD,YAAM,cAAc,GAAG,WAAW,SAAS;AAE3C,UAAI,aAAa;AACf,iBAAS;AACT,gBAAQ;AACR,eAAO,MAAM,kCAAkC,MAAM,EAAE;AAAA,MACzD,OAAO;AAEL,cAAM,gBAAgB,SAAS,mBAAmB,EAAE,QAAQ,IAAI;AAChE,gBAAQ,cAAc;AACtB,iBAAS,KAAK,KAAK,OAAO,cAAc;AACxC,eAAO,MAAM,yCAAyC,KAAK,EAAE;AAAA,MAC/D;AAAA,IACF;AAGA,QAAI;AACF,SAAG,UAAU,OAAO,EAAE,WAAW,MAAM;AAAA,IACzC,SAAS,OAAO;AAGd,aAAO,MAAM,2CAA2C,KAAK,KAAK,KAAK,EAAE;AAAA,IAC3E;AAEA,SAAK,QAAQ,IAAI,cAAc,MAAM;AACrC,SAAK,oBAAoB,IAAI,yBAAyB,KAAK,KAAK;AAEhE,UAAM,mBAAmB,IAAI;AAAA,MAC3B;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,iBAAiB,IAAI;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAGF,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAA4B;AAChC,UAAM,KAAK,MAAM,WAAA;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAA0B;AAC9B,WAAO,MAAM,6BAA6B;AAC1C,UAAM,KAAK,MAAM,SAAA;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,oBAAoB,UAA4D;AACpF,WAAO,KAAK,MAAM,oBAAoB,QAAQ;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBACJ,WACA,QACA,cACe;AACf,WAAO,KAAK,MAAM,oBAAoB,WAAW,QAAQ,YAAY;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBACJ,WACA,OACA,UACe;AACf,WAAO,KAAK,MAAM,sBAAsB,WAAW,OAAO,QAAQ;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBAAoB,WAAmB,SAAwC;AACnF,WAAO,KAAK,MAAM,oBAAoB,WAAW,OAAO;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,kBAAkB,WAAkD;AACxE,WAAO,KAAK,MAAM,kBAAkB,SAAS;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAc,KAAkC;AACpD,UAAM,aAAa;AAAA,MACjB,SAAS,IAAI,QAAQ,KAAA,EAAO,YAAA;AAAA,MAC5B,UAAU,IAAI,WAAW,IAAI,KAAA,EAAO,YAAA;AAAA,IAAY;AAElD,WAAO,KAAK,wBAAwB,WAAW,SAAS,WAAW,OAAO;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,gBAA2C;AAC/C,UAAM,SAAS,MAAM,KAAK,MAAM,qBAAA;AAChC,UAAM,YAA8B,CAAA;AACpC,eAAW,CAAC,SAAS,QAAQ,KAAK,QAAQ;AACxC,YAAM,KAAK,SAAS;AAAA,QAClB,CAAC,OACE;AAAA,UACC,IAAI,EAAE;AAAA,UACN,KAAK,EAAE,SAAS,SAAS,EAAE,QAAA;AAAA,UAC3B,QAAQ,EAAE;AAAA;AAAA,UAEV,UACE,EAAE,WAAW,cACT,SACA,EAAE,OAAO,EAAE,eAAe,UAAU,EAAE,iBAAA;AAAA,UAC5C,QAAQ,EAAE,WAAW,EAAE,eAAe,YAAY,EAAE,eAAA;AAAA,UACpD,WAAW,EAAE;AAAA,UACb,WAAW,EAAE,aAAa;AAAA,QAAA;AAAA,MAC5B;AAEJ,gBAAU,KAAK,EAAE,SAAS,UAAU,IAAI;AAAA,IAC1C;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,wBAAwB,KAA8C;AAC1E,WAAO,KAAK,MAAM,wBAAwB,GAAG;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,sBAAsB,SAAgC;AAC1D,WAAO,KAAK,uCAAuC,OAAO,EAAE;AAC5D,UAAM,oBAAoB,QAAQ,YAAA;AAGlC,UAAM,WAAW,MAAM,KAAK,aAAa,iBAAiB;AAC1D,UAAM,iBAAiB,MAAM,KAAK,OAAO,mBAAmB,EAAE;AAE9D,QAAI,SAAS,WAAW,KAAK,CAAC,gBAAgB;AAC5C,aAAO,KAAK,gBAAgB,OAAO,cAAc;AAGjD,YAAM,eAAe,MAAM,KAAK,cAAA;AAChC,YAAM,eAAe,aAAa,IAAI,CAAC,QAAQ,IAAI,OAAO;AAE1D,UAAI,cAAwB,CAAA;AAC5B,UAAI,aAAa,SAAS,GAAG;AAC3B,cAAM,OAAO,IAAI,KAAK,cAAc;AAAA;AAAA;AAAA;AAAA,UAIlC,WAAW;AAAA;AAAA,QAAA,CACZ;AACD,cAAM,UAAU,KAAK,OAAO,iBAAiB;AAE7C,sBAAc,QAAQ,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,WAAW,OAAO,IAAI;AAC7D,eAAO,KAAK,yBAAyB,YAAY,KAAK,IAAI,CAAC,EAAE;AAAA,MAC/D;AAEA,YAAM,IAAI,qBAAqB,SAAS,WAAW;AAAA,IACrD;AAEA,WAAO,KAAK,cAAc,OAAO,uBAAuB;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,SAAoC;AACrD,UAAM,WAAW,MAAM,KAAK,MAAM,oBAAoB,OAAO;AAC7D,WAAO,SAAS,OAAO,CAAC,MAAMD,gBAAO,MAAM,CAAC,CAAC;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,SAAiB,SAA2C;AACvE,UAAM,oBAAoB,KAAK,iBAAiB,OAAO;AACvD,WAAO,KAAK,MAAM,oBAAoB,SAAS,iBAAiB;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,gBACJ,SACA,eAC4B;AAC5B,UAAM,oBAAoB,GAAG,OAAO,GAAG,gBAAgB,IAAI,aAAa,KAAK,EAAE;AAC/E,WAAO,KAAK,+BAA+B,iBAAiB,EAAE;AAG9D,UAAM,iBAAiB,MAAM,KAAK,MAAM,oBAAoB,SAAS,EAAE;AACvE,UAAM,iBAAiB,MAAM,KAAK,aAAa,OAAO;AAEtD,QAAI,eAAe,WAAW,GAAG;AAC/B,UAAI,gBAAgB;AAClB,eAAO,KAAK,sCAAsC,OAAO,EAAE;AAC3D,eAAO,EAAE,WAAW,MAAM,gBAAgB,KAAA;AAAA,MAC5C;AAEA,aAAO,KAAK,mCAAmC,OAAO,EAAE;AAExD,YAAM,oBAAoB,MAAM,KAAK,MAAM,qBAAA;AAC3C,YAAM,iBAAiB,kBAAkB,IAAI,OAAO,KAAK,CAAA;AACzD,YAAM,IAAI,qBAAqB,SAAS,iBAAiB,IAAI,cAAc;AAAA,IAC7E;AAEA,QAAI,YAA2B;AAE/B,QAAI,CAAC,iBAAiB,kBAAkB,UAAU;AAChD,kBAAYA,gBAAO,cAAc,gBAAgB,GAAG;AAAA,IACtD,OAAO;AACL,YAAM,eAAe;AACrB,UAAI,CAAC,aAAa,KAAK,aAAa,GAAG;AACrC,eAAO,KAAK,sCAAsC,aAAa,EAAE;AAAA,MAEnE,OAAO;AAEL,YAAI,QAAQ;AACZ,YAAI,CAACA,gBAAO,WAAW,aAAa,GAAG;AAErC,kBAAQ,IAAI,aAAa;AAAA,QAC3B,WAAWA,gBAAO,MAAM,aAAa,GAAG;AAEtC,kBAAQ,GAAG,KAAK,SAAS,aAAa;AAAA,QACxC;AAEA,oBAAYA,gBAAO,cAAc,gBAAgB,KAAK;AAAA,MACxD;AAAA,IACF;AAEA,QAAI,WAAW;AACb,aAAO,KAAK,8BAA8B,SAAS,QAAQ,iBAAiB,EAAE;AAAA,IAChF,OAAO;AACL,aAAO,KAAK,4CAA4C,iBAAiB,EAAE;AAAA,IAC7E;AAKA,QAAI,CAAC,aAAa,CAAC,gBAAgB;AAEjC,YAAM,oBAAoB,MAAM,KAAK,MAAM,qBAAA;AAC3C,YAAM,iBAAiB,kBAAkB,IAAI,OAAO,KAAK,CAAA;AACzD,YAAM,IAAI,qBAAqB,SAAS,iBAAiB,IAAI,cAAc;AAAA,IAC7E;AAEA,WAAO,EAAE,WAAW,eAAA;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,mBAAmB,SAAiB,SAAwC;AAChF,UAAM,oBAAoB,KAAK,iBAAiB,OAAO;AACvD,WAAO;AAAA,MACL,mCAAmC,OAAO,IAAI,qBAAqB,cAAc;AAAA,IAAA;AAEnF,UAAM,QAAQ,MAAM,KAAK,MAAM,gBAAgB,SAAS,iBAAiB;AACzE,WAAO,KAAK,cAAc,KAAK,YAAY;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,YACJ,SACA,SACA,UACe;AACf,UAAM,oBAAoB,KAAK,iBAAiB,OAAO;AACvD,UAAM,MAAM,SAAS,SAAS;AAC9B,QAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,CAAC,IAAI,QAAQ;AAClD,YAAM,IAAI,WAAW,4CAA4C;AAAA,IACnE;AAEA,WAAO,KAAK,uBAAuB,SAAS,SAAS,KAAK,EAAE;AAE5D,QAAI,CAAC,SAAS,YAAY,QAAQ;AAChC,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AAGA,UAAM,SAAS,MAAM,KAAK,SAAS,UAAU,SAAS,WAAW;AAGjE,UAAM,YAAY,OAAO,IAAI,CAAC,WAAyB;AAAA,MACrD,aAAa,MAAM;AAAA,MACnB,UAAU;AAAA,QACR,GAAG,SAAS;AAAA,QACZ,OAAO,MAAM,QAAQ;AAAA,QACrB,MAAM,MAAM,QAAQ;AAAA,MAAA;AAAA,IACtB,EACA;AACF,WAAO,KAAK,2BAA2B,UAAU,MAAM,SAAS;AAGhE,UAAM,KAAK,MAAM,aAAa,SAAS,mBAAmB,SAAS;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YACJ,SACA,SACA,OACA,QAAQ,GACsB;AAC9B,UAAM,oBAAoB,KAAK,iBAAiB,OAAO;AACvD,WAAO,KAAK,kBAAkB,OAAO,SAAS,mBAAmB,OAAO,KAAK;AAAA,EAC/E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,wBAAwB,SAAiB,SAAkC;AAE/E,UAAM,oBAAoB,QAAQ,YAAA;AAClC,UAAM,oBAAoB,KAAK,iBAAiB,OAAO;AAGvD,UAAM,EAAE,UAAA,IAAc,MAAM,KAAK,MAAM;AAAA,MACrC;AAAA,MACA;AAAA,IAAA;AAGF,WAAO;AAAA,EACT;AACF;"}
|